diff --git a/lib/http/api.dart b/lib/http/api.dart index 6b58e7cb..d86ea690 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -845,4 +845,6 @@ class Api { static const String setPushSs = '${HttpString.tUrl}/link_setting/v1/link_setting/set_push_ss'; + + static const String dynReserve = '/x/dynamic/feed/reserve/click'; } diff --git a/lib/http/dynamics.dart b/lib/http/dynamics.dart index 81de1808..b1dcfe65 100644 --- a/lib/http/dynamics.dart +++ b/lib/http/dynamics.dart @@ -326,4 +326,29 @@ class DynamicsHttp { return Error(res.data['message']); } } + + static Future dynReserve({ + required reserveId, + required curBtnStatus, + required dynamicIdStr, + required reserveTotal, + }) async { + var res = await Request().post( + Api.dynReserve, + queryParameters: { + 'csrf': Accounts.main.csrf, + }, + data: { + 'reserve_id': reserveId, + 'cur_btn_status': curBtnStatus, + 'dynamic_id_str': dynamicIdStr, + 'reserve_total': reserveTotal, + }, + ); + if (res.data['code'] == 0) { + return {'status': true, 'data': res.data['data']}; + } else { + return {'status': false, 'msg': res.data['message']}; + } + } } diff --git a/lib/models/dynamics/result.dart b/lib/models/dynamics/result.dart index 6514aa2e..d2a1588c 100644 --- a/lib/models/dynamics/result.dart +++ b/lib/models/dynamics/result.dart @@ -459,9 +459,9 @@ class Reserve { this.upMid, }); - Map? button; - Map? desc1; - Map? desc2; + ReserveBtn? button; + Desc? desc1; + Desc? desc2; String? jumpUrl; int? reserveTotal; int? rid; @@ -471,9 +471,10 @@ class Reserve { int? upMid; Reserve.fromJson(Map json) { - button = json['button']; - desc1 = json['desc1']; - desc2 = json['desc2']; + button = + json['button'] == null ? null : ReserveBtn.fromJson(json['button']); + desc1 = json['desc1'] == null ? null : Desc.fromJson(json['desc1']); + desc2 = json['desc2'] == null ? null : Desc.fromJson(json['desc2']); jumpUrl = json['jump_url']; reserveTotal = json['reserve_total']; rid = json['rid']; @@ -485,6 +486,47 @@ class Reserve { } } +class ReserveBtn { + ReserveBtn({ + this.status, + this.type, + this.checkText, + this.uncheckText, + }); + + int? status; + int? type; + String? checkText; + String? uncheckText; + int? disable; + + ReserveBtn.fromJson(Map json) { + status = json['status']; + type = json['type']; + checkText = json['check']?['text'] ?? '已预约'; + uncheckText = json['uncheck']?['text'] ?? '预约'; + disable = json['uncheck']?['disable']; + } +} + +class Desc { + Desc({ + this.style, + this.text, + this.visible, + }); + + int? style; + String? text; + bool? visible; + + Desc.fromJson(Map json) { + style = json['style']; + text = json['text']; + visible = json['visible']; + } +} + class Good { Good({ this.headIcon, diff --git a/lib/pages/dynamics/widgets/additional_panel.dart b/lib/pages/dynamics/widgets/additional_panel.dart index d84205d8..e1b28263 100644 --- a/lib/pages/dynamics/widgets/additional_panel.dart +++ b/lib/pages/dynamics/widgets/additional_panel.dart @@ -1,8 +1,7 @@ import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; -import 'package:PiliPlus/http/search.dart'; +import 'package:PiliPlus/http/dynamics.dart'; import 'package:PiliPlus/models/dynamics/result.dart'; -import 'package:PiliPlus/utils/page_utils.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/app_scheme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -10,49 +9,19 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; Widget addWidget( ThemeData theme, DynamicItemModel item, BuildContext context, type, {floor = 1}) { - Map dynamicProperty = { - 'ADDITIONAL_TYPE_UGC': item.modules.moduleDynamic!.additional!.ugc, - // 直播预约 - 'ADDITIONAL_TYPE_RESERVE': item.modules.moduleDynamic!.additional!.reserve, - // 商品 - 'ADDITIONAL_TYPE_GOODS': item.modules.moduleDynamic!.additional!.goods, - // 比赛信息 - 'ADDITIONAL_TYPE_MATCH': item.modules.moduleDynamic!.additional!.match, - // 游戏信息 - 'ADDITIONAL_TYPE_COMMON': item.modules.moduleDynamic!.additional!.common, - }; - dynamic content = dynamicProperty[type]; Color bgColor = floor == 1 ? theme.dividerColor.withOpacity(0.08) : theme.colorScheme.surface; switch (type) { case 'ADDITIONAL_TYPE_UGC': + final content = item.modules.moduleDynamic!.additional!.ugc!; // 转发的投稿 return InkWell( - onTap: () async { - String text = content.jumpUrl; - RegExp bvRegex = RegExp(r'BV[0-9A-Za-z]{10}', caseSensitive: false); - Iterable matches = bvRegex.allMatches(text); - if (matches.isNotEmpty) { - Match match = matches.first; - String bvid = match.group(0)!; - String cover = content.cover; - try { - int cid = await SearchHttp.ab2c(bvid: bvid); - PageUtils.toVideoPage( - 'bvid=$bvid&cid=$cid', - arguments: { - 'pic': cover, - 'heroTag': Utils.makeHeroTag(bvid), - }, - ); - } catch (err) { - SmartDialog.showToast(err.toString()); - } - } else { - debugPrint("No match found."); - } - }, + onTap: content.jumpUrl == null + ? null + : () { + PiliScheme.routePushFromUrl(content.jumpUrl!); + }, child: Container( padding: const EdgeInsets.only(left: 12, top: 8, right: 12, bottom: 8), @@ -71,13 +40,13 @@ Widget addWidget( mainAxisAlignment: MainAxisAlignment.start, children: [ Text( - content.title, + content.title!, maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), Text( - content.descSecond, + content.descSecond!, style: TextStyle( color: theme.colorScheme.outline, fontSize: theme.textTheme.labelMedium!.fontSize, @@ -91,6 +60,7 @@ Widget addWidget( ), ); case 'ADDITIONAL_TYPE_RESERVE': + final content = item.modules.moduleDynamic!.additional!.reserve!; return content.state != -1 ? content.title != null ? Padding( @@ -102,30 +72,90 @@ Widget addWidget( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 10), color: bgColor, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + child: Row( children: [ - Text( - content.title, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - const SizedBox(height: 1), - Text.rich( - TextSpan( - style: TextStyle( - color: theme.colorScheme.outline, - fontSize: theme.textTheme.labelMedium!.fontSize, - ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (content.desc1 != null) - TextSpan(text: content.desc1['text']), - const TextSpan(text: ' '), - if (content.desc2 != null) - TextSpan(text: content.desc2['text']), + Text( + content.title!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 1), + Text.rich( + TextSpan( + style: TextStyle( + color: theme.colorScheme.outline, + fontSize: + theme.textTheme.labelMedium!.fontSize, + ), + children: [ + if (content.desc1 != null) + TextSpan(text: content.desc1!.text), + const TextSpan(text: ' '), + if (content.desc2 != null) + TextSpan(text: content.desc2!.text), + ], + ), + ) ], ), - ) + ), + if (content.button != null) + Builder( + builder: (context) { + final btn = content.button!; + final isReserved = btn.status == btn.type; + return FilledButton.tonal( + style: FilledButton.styleFrom( + foregroundColor: isReserved + ? theme.colorScheme.onSurfaceVariant + : null, + backgroundColor: isReserved + ? theme.colorScheme.onInverseSurface + : null, + visualDensity: VisualDensity.compact, + padding: const EdgeInsets.symmetric( + horizontal: 16), + tapTargetSize: + MaterialTapTargetSize.shrinkWrap, + ), + onPressed: btn.disable == 1 + ? null + : () async { + var res = + await DynamicsHttp.dynReserve( + reserveId: content.rid, + curBtnStatus: btn.status, + dynamicIdStr: item.idStr, + reserveTotal: content.reserveTotal, + ); + if (res['status']) { + content + ..desc2?.text = + res['data']['desc_update'] + ..reserveTotal = + res['data']['reserve_update'] + ..button!.status = res['data'] + ['final_btn_status']; + if (context.mounted) { + (context as Element?) + ?.markNeedsBuild(); + } + } else { + SmartDialog.showToast(res['msg']); + } + }, + child: Text( + isReserved + ? btn.checkText! + : btn.uncheckText!, + ), + ); + }, + ), ], ), ), @@ -134,11 +164,14 @@ Widget addWidget( : const SizedBox.shrink() : const SizedBox.shrink(); case 'ADDITIONAL_TYPE_GOODS': + // final content = item.modules.moduleDynamic!.additional!.goods; // 商品 return const SizedBox.shrink(); case 'ADDITIONAL_TYPE_MATCH': + // final content = item.modules.moduleDynamic!.additional!.match; return const SizedBox.shrink(); case 'ADDITIONAL_TYPE_COMMON': + // final content = item.modules.moduleDynamic!.additional!.common; return const SizedBox.shrink(); case 'ADDITIONAL_TYPE_VOTE': return const SizedBox.shrink(); diff --git a/lib/pages/dynamics_repost/view.dart b/lib/pages/dynamics_repost/view.dart index b56c9384..9cf989ee 100644 --- a/lib/pages/dynamics_repost/view.dart +++ b/lib/pages/dynamics_repost/view.dart @@ -82,16 +82,14 @@ class _RepostPanelState extends CommonPublishPageState { children: [ SizedBox(height: _isMax ? 16 : 10), _buildAppBar(theme), - if (_isMax) - Expanded(child: _buildEditPanel(theme)) - else - _buildEditPanel(theme), - if (_isMax.not) - ..._biuldDismiss(theme) - else ...[ + if (_isMax) ...[ + Expanded(child: _buildEditPanel(theme)), _buildToolbar, buildPanelContainer(Colors.transparent), - ] + ] else ...[ + _buildEditPanel(theme), + ..._biuldDismiss(theme), + ], ], ), ); diff --git a/lib/pages/fav/pgc/widget/item.dart b/lib/pages/fav/pgc/widget/item.dart index 1d582fdc..054900ce 100644 --- a/lib/pages/fav/pgc/widget/item.dart +++ b/lib/pages/fav/pgc/widget/item.dart @@ -3,6 +3,7 @@ import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/button/icon_button.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/models/bangumi/list.dart'; +import 'package:PiliPlus/models/common/badge_type.dart'; import 'package:PiliPlus/pages/common/multi_select_controller.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:flutter/material.dart'; @@ -70,6 +71,7 @@ class FavPgcItem extends StatelessWidget { right: 4, top: 4, text: item.badge, + size: PBadgeSize.small, fontSize: 10, padding: const EdgeInsets.symmetric( horizontal: 2,