diff --git a/lib/models/dynamics/result.dart b/lib/models/dynamics/result.dart index 1b21e813..10059e05 100644 --- a/lib/models/dynamics/result.dart +++ b/lib/models/dynamics/result.dart @@ -77,11 +77,6 @@ class Fallback { id: json['id'], type: json['type'], ); - - Map toJson() => { - 'id': id, - 'type': type, - }; } // 单个动态详情 @@ -241,12 +236,32 @@ class Button { String? icon; String? jumpUrl; String? text; + JumpStyle? jumpStyle; + int? status; + int? type; + Check? check; Button.fromJson(Map json) { handleType = json['handle_type']; icon = json['icon']; jumpUrl = json['jump_url']; text = json['text']; + jumpStyle = json['jump_style'] == null + ? null + : JumpStyle.fromJson(json['jump_style']); + status = json['status']; + type = json['type']; + check = json['check'] == null ? null : Check.fromJson(json['check']); + } +} + +class Check { + int? disable; + String? text; + + Check.fromJson(Map json) { + disable = json['disable']; + text = json['text']; } } @@ -357,12 +372,9 @@ class DynamicAddModel { Ugc? ugc; Reserve? reserve; Good? goods; - - /// TODO 比赛vs - String? match; - - /// TODO 游戏信息 - String? common; + UpowerLottery? upowerLottery; + AddCommon? common; + AddMatch? match; DynamicAddModel.fromJson(Map json) { type = json['type']; @@ -371,9 +383,200 @@ class DynamicAddModel { reserve = json['reserve'] != null ? Reserve.fromJson(json['reserve']) : null; goods = json['goods'] != null ? Good.fromJson(json['goods']) : null; + upowerLottery = json['upower_lottery'] != null + ? UpowerLottery.fromJson(json['upower_lottery']) + : null; + common = json['common'] != null ? AddCommon.fromJson(json['common']) : null; + match = json['match'] != null ? AddMatch.fromJson(json['match']) : null; } } +class AddMatch { + Button? button; + String? headText; + String? idStr; + String? jumpUrl; + MatchInfo? matchInfo; + + AddMatch({ + this.button, + this.headText, + this.idStr, + this.jumpUrl, + this.matchInfo, + }); + + factory AddMatch.fromJson(Map json) => AddMatch( + button: json["button"] == null ? null : Button.fromJson(json["button"]), + headText: json["head_text"], + idStr: json["id_str"], + jumpUrl: json["jump_url"], + matchInfo: json["match_info"] == null + ? null + : MatchInfo.fromJson(json["match_info"]), + ); +} + +class MatchInfo { + String? centerBottom; + List? centerTop; + TTeam? leftTeam; + TTeam? rightTeam; + int? status; + dynamic subTitle; + String? title; + + MatchInfo({ + this.centerBottom, + this.centerTop, + this.leftTeam, + this.rightTeam, + this.status, + this.subTitle, + this.title, + }); + + factory MatchInfo.fromJson(Map json) => MatchInfo( + centerBottom: json["center_bottom"], + centerTop: json["center_top"], + leftTeam: json["left_team"] == null + ? null + : TTeam.fromJson(json["left_team"]), + rightTeam: json["right_team"] == null + ? null + : TTeam.fromJson(json["right_team"]), + status: json["status"], + subTitle: json["sub_title"], + title: json["title"], + ); +} + +class TTeam { + int? id; + String? name; + String? pic; + + TTeam({ + this.id, + this.name, + this.pic, + }); + + factory TTeam.fromJson(Map json) => TTeam( + id: json["id"], + name: json["name"], + pic: json["pic"], + ); +} + +class AddCommon { + Button? button; + String? cover; + String? desc1; + String? desc2; + String? headText; + String? idStr; + String? jumpUrl; + int? style; + String? subType; + String? title; + + AddCommon({ + this.button, + this.cover, + this.desc1, + this.desc2, + this.headText, + this.idStr, + this.jumpUrl, + this.style, + this.subType, + this.title, + }); + + factory AddCommon.fromJson(Map json) => AddCommon( + button: json["button"] == null ? null : Button.fromJson(json["button"]), + cover: json["cover"], + desc1: json["desc1"], + desc2: json["desc2"], + headText: json["head_text"], + idStr: json["id_str"], + jumpUrl: json["jump_url"], + style: json["style"], + subType: json["sub_type"], + title: json["title"], + ); +} + +class UpowerLottery { + Button? button; + Desc? desc; + Hint? hint; + String? jumpUrl; + int? rid; + int? state; + String? title; + int? upMid; + int? upowerActionState; + int? upowerLevel; + + UpowerLottery({ + this.button, + this.desc, + this.hint, + this.jumpUrl, + this.rid, + this.state, + this.title, + this.upMid, + this.upowerActionState, + this.upowerLevel, + }); + + factory UpowerLottery.fromJson(Map json) => UpowerLottery( + button: json["button"] == null ? null : Button.fromJson(json["button"]), + desc: json["desc"] == null ? null : Desc.fromJson(json["desc"]), + hint: json["hint"] == null ? null : Hint.fromJson(json["hint"]), + jumpUrl: json["jump_url"], + rid: json["rid"], + state: json["state"], + title: json["title"], + upMid: json["up_mid"], + upowerActionState: json["upower_action_state"], + upowerLevel: json["upower_level"], + ); +} + +class Hint { + int? style; + String? text; + + Hint({ + this.style, + this.text, + }); + + factory Hint.fromJson(Map json) => Hint( + style: json["style"], + text: json["text"], + ); +} + +class JumpStyle { + String? iconUrl; + String? text; + + JumpStyle({ + this.iconUrl, + this.text, + }); + + factory JumpStyle.fromJson(Map json) => JumpStyle( + iconUrl: json["icon_url"], + text: json["text"], + ); +} + class Vote { Vote({ this.choiceCnt, @@ -452,6 +655,7 @@ class Reserve { this.button, this.desc1, this.desc2, + this.desc3, this.jumpUrl, this.reserveTotal, this.rid, @@ -464,6 +668,7 @@ class Reserve { ReserveBtn? button; Desc? desc1; Desc? desc2; + Desc? desc3; String? jumpUrl; int? reserveTotal; int? rid; @@ -477,6 +682,7 @@ class Reserve { 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']); + desc3 = json['desc3'] == null ? null : Desc.fromJson(json['desc3']); jumpUrl = json['jump_url']; reserveTotal = json['reserve_total']; rid = json['rid']; @@ -520,16 +726,19 @@ class Desc { this.style, this.text, this.visible, + this.jumpUrl, }); int? style; String? text; bool? visible; + String? jumpUrl; Desc.fromJson(Map json) { style = json['style']; text = json['text']; visible = json['visible']; + jumpUrl = json["jump_url"]; } } diff --git a/lib/pages/dynamics/widgets/additional_panel.dart b/lib/pages/dynamics/widgets/additional_panel.dart index 06226dc1..09f9014e 100644 --- a/lib/pages/dynamics/widgets/additional_panel.dart +++ b/lib/pages/dynamics/widgets/additional_panel.dart @@ -6,8 +6,10 @@ import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/pages/dynamics/widgets/vote.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; import 'package:PiliPlus/utils/utils.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; Widget addWidget( ThemeData theme, DynamicItemModel item, BuildContext context, type, @@ -102,22 +104,60 @@ Widget addWidget( TextSpan( style: TextStyle( color: theme.colorScheme.outline, - fontSize: theme - .textTheme.labelMedium!.fontSize, + fontSize: 13, ), children: [ - if (reserve.desc1 != null) + if (reserve.desc1?.text?.isNotEmpty == + true) TextSpan(text: reserve.desc1!.text), - const TextSpan(text: ' '), - if (reserve.desc2 != null) - TextSpan(text: reserve.desc2!.text), + if (reserve.desc2?.text?.isNotEmpty == + true) + TextSpan( + text: + ' ${reserve.desc2!.text}'), + if (reserve.desc3?.text?.isNotEmpty == + true) ...[ + const TextSpan(text: '\n'), + WidgetSpan( + alignment: + PlaceholderAlignment.middle, + child: Icon( + size: 17, + Icons.card_giftcard, + color: + theme.colorScheme.primary, + ), + ), + TextSpan( + text: ' ${reserve.desc3!.text}', + style: TextStyle( + color: + theme.colorScheme.primary, + ), + recognizer: + reserve.desc3!.jumpUrl == null + ? null + : (TapGestureRecognizer() + ..onTap = () { + Get.toNamed( + '/webview', + parameters: { + 'url': reserve + .desc3! + .jumpUrl! + }, + ); + }), + ), + ], ], ), ) ], ), ), - if (reserve.button != null) + if (reserve.button != null) ...[ + const SizedBox(width: 10), Builder( builder: (context) { final btn = reserve.button!; @@ -187,6 +227,7 @@ Widget addWidget( ); }, ), + ], ], ), ), @@ -195,6 +236,104 @@ Widget addWidget( ) : const SizedBox.shrink() : const SizedBox.shrink(); + case 'ADDITIONAL_TYPE_UPOWER_LOTTERY': + final content = item.modules.moduleDynamic!.additional!.upowerLottery!; + final borderRadius = floor == 1 ? null : StyleString.mdRadius; + return Padding( + padding: const EdgeInsets.only(top: 6), + child: Material( + color: bgColor, + borderRadius: borderRadius, + child: InkWell( + borderRadius: borderRadius, + onTap: content.jumpUrl == null + ? null + : () => PiliScheme.routePushFromUrl(content.jumpUrl!), + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Row( + children: [ + Expanded( + child: Column( + spacing: 2, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (content.title?.isNotEmpty == true) + Text(content.title!), + if (content.hint?.text?.isNotEmpty == true) + Text( + content.hint!.text!, + style: TextStyle( + color: theme.colorScheme.outline, + fontSize: 13, + ), + ), + if (content.desc?.text?.isNotEmpty == true) + Text.rich( + TextSpan( + children: [ + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Icon( + size: 17, + Icons.card_giftcard, + color: theme.colorScheme.primary, + ), + ), + TextSpan( + text: ' ${content.desc!.text!}', + style: TextStyle( + color: theme.colorScheme.primary, + fontSize: 13, + ), + recognizer: content.desc!.jumpUrl == null + ? null + : (TapGestureRecognizer() + ..onTap = () { + Get.toNamed( + '/webview', + parameters: { + 'url': content.desc!.jumpUrl! + }, + ); + }), + ), + ], + ), + ), + ], + ), + ), + if (content.button != null) ...[ + const SizedBox(width: 10), + FilledButton.tonal( + onPressed: content.button!.jumpUrl == null + ? null + : () => PiliScheme.routePushFromUrl( + content.button!.jumpUrl!, + ), + style: FilledButton.styleFrom( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + padding: const EdgeInsets.symmetric(horizontal: 10), + visualDensity: + const VisualDensity(horizontal: -2, vertical: -3), + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + child: Text(content.button!.jumpStyle?.text ?? + content.button!.check?.text ?? + ''), + ), + ], + ], + ), + ), + ), + ), + ); // 商品 case 'ADDITIONAL_TYPE_GOODS': final content = item.modules.moduleDynamic!.additional!.goods!; @@ -283,12 +422,6 @@ Widget addWidget( ); } 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': final vote = item.modules.moduleDynamic!.additional!.vote!; final borderRadius = floor == 1 ? null : StyleString.mdRadius; @@ -376,6 +509,204 @@ Widget addWidget( ), ), ); + case 'ADDITIONAL_TYPE_COMMON': + final content = item.modules.moduleDynamic!.additional!.common!; + final borderRadius = floor == 1 ? null : StyleString.mdRadius; + return Padding( + padding: const EdgeInsets.only(top: 6), + child: Material( + color: bgColor, + borderRadius: borderRadius, + child: InkWell( + borderRadius: borderRadius, + onTap: content.jumpUrl == null + ? null + : () => PiliScheme.routePushFromUrl(content.jumpUrl!), + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Row( + children: [ + if (content.cover?.isNotEmpty == true) ...[ + NetworkImgLayer( + width: 45, + height: 45, + src: content.cover, + radius: 6, + ), + const SizedBox(width: 10), + ], + Expanded( + child: Column( + spacing: 2, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (content.title?.isNotEmpty == true) + Text(content.title!), + if (content.desc1?.isNotEmpty == true) + Text( + content.desc1!, + style: TextStyle( + color: theme.colorScheme.outline, + fontSize: 13, + ), + ), + if (content.desc2?.isNotEmpty == true) + Text( + content.desc2!, + style: TextStyle( + color: theme.colorScheme.outline, + fontSize: 13, + ), + ), + ], + ), + ), + if (content.button?.jumpUrl?.isNotEmpty == true) ...[ + const SizedBox(width: 10), + FilledButton.tonal( + onPressed: () => PiliScheme.routePushFromUrl( + content.button!.jumpUrl!, + ), + style: FilledButton.styleFrom( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + padding: const EdgeInsets.symmetric(horizontal: 10), + visualDensity: + const VisualDensity(horizontal: -2, vertical: -3), + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + child: Text(content.button!.jumpStyle?.text ?? ''), + ), + ], + ], + ), + ), + ), + ), + ); + case 'ADDITIONAL_TYPE_MATCH': + final content = item.modules.moduleDynamic!.additional!.match!; + final borderRadius = floor == 1 ? null : StyleString.mdRadius; + return Padding( + padding: const EdgeInsets.only(top: 6), + child: Material( + color: bgColor, + borderRadius: borderRadius, + child: InkWell( + borderRadius: borderRadius, + onTap: content.jumpUrl == null + ? null + : () => PiliScheme.routePushFromUrl(content.jumpUrl!), + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Row( + children: [ + Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (content.matchInfo?.title?.isNotEmpty == true) + Text( + content.matchInfo!.title!, + style: const TextStyle(fontSize: 13), + ), + if (content.matchInfo?.subTitle?.isNotEmpty == true) + Text( + content.matchInfo!.subTitle!, + style: TextStyle( + fontSize: 13, + color: theme.colorScheme.outline, + ), + ), + ], + ), + const Spacer(), + if (content.matchInfo?.leftTeam != null) ...[ + Column( + mainAxisSize: MainAxisSize.min, + children: [ + NetworkImgLayer( + width: 30, + height: 30, + src: content.matchInfo!.leftTeam!.pic, + ), + const SizedBox(height: 5), + Text( + content.matchInfo!.leftTeam!.name!, + style: const TextStyle(fontSize: 13), + ) + ], + ), + const SizedBox(width: 16), + ], + Column( + children: [ + if (content.matchInfo?.centerTop?.isNotEmpty == true) + Container( + height: 35, + alignment: Alignment.center, + child: Text( + content.matchInfo!.centerTop!.join(' '), + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ), + if (content.matchInfo?.centerBottom?.isNotEmpty == true) + Text( + content.matchInfo!.centerBottom!, + style: TextStyle( + fontSize: 13, + color: theme.colorScheme.outline, + ), + ), + ], + ), + if (content.matchInfo?.rightTeam != null) ...[ + const SizedBox(width: 16), + Column( + mainAxisSize: MainAxisSize.min, + children: [ + NetworkImgLayer( + width: 30, + height: 30, + src: content.matchInfo!.rightTeam!.pic, + ), + const SizedBox(height: 5), + Text( + content.matchInfo!.rightTeam!.name!, + style: const TextStyle(fontSize: 13), + ) + ], + ), + ], + const Spacer(), + if (content.button != null) + FilledButton.tonal( + onPressed: () => PiliScheme.routePushFromUrl( + content.button!.jumpUrl!, + ), + style: FilledButton.styleFrom( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + padding: const EdgeInsets.symmetric(horizontal: 10), + visualDensity: + const VisualDensity(horizontal: -2, vertical: -3), + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + child: Text(content.button!.jumpStyle?.text ?? ''), + ), + ], + ), + ), + ), + ), + ); default: if (BuildConfig.isDebug) { return Padding( diff --git a/lib/pages/dynamics/widgets/content_panel.dart b/lib/pages/dynamics/widgets/content_panel.dart index b56c0145..972aee57 100644 --- a/lib/pages/dynamics/widgets/content_panel.dart +++ b/lib/pages/dynamics/widgets/content_panel.dart @@ -78,7 +78,7 @@ Widget content( ), ), if (richNodes != null) - source == 'detail' + source == 'detail' && floor == 1 ? SelectableText.rich( richNodes, style: isSave diff --git a/lib/pages/dynamics/widgets/video_panel.dart b/lib/pages/dynamics/widgets/video_panel.dart index 6ec24bf1..8c11c552 100644 --- a/lib/pages/dynamics/widgets/video_panel.dart +++ b/lib/pages/dynamics/widgets/video_panel.dart @@ -4,6 +4,7 @@ import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/models/common/badge_type.dart'; import 'package:PiliPlus/models/dynamics/result.dart'; +import 'package:PiliPlus/pages/dynamics/widgets/additional_panel.dart'; import 'package:PiliPlus/pages/dynamics/widgets/content_panel.dart'; import 'package:PiliPlus/pages/dynamics/widgets/rich_node_panel.dart'; import 'package:PiliPlus/utils/utils.dart'; @@ -201,6 +202,14 @@ Widget videoSeasonWidget( overflow: source == 'detail' ? null : TextOverflow.ellipsis, ), ), + if (item.modules.moduleDynamic?.additional != null) + addWidget( + theme, + item, + context, + item.modules.moduleDynamic?.additional?.type, + floor: floor, + ), ], ); } diff --git a/lib/pages/hot/view.dart b/lib/pages/hot/view.dart index 31ba75e2..aef7631d 100644 --- a/lib/pages/hot/view.dart +++ b/lib/pages/hot/view.dart @@ -109,7 +109,6 @@ class _HotPageState extends CommonPageState 'url': 'https://www.bilibili.com/h5/weekly-recommend' }, - arguments: {'off': false}, ), ), _buildEntranceItem( @@ -122,7 +121,6 @@ class _HotPageState extends CommonPageState 'url': 'https://www.bilibili.com/h5/good-history' }, - arguments: {'off': false}, ), ), ], diff --git a/lib/pages/webview/view.dart b/lib/pages/webview/view.dart index 87229ea2..63e7c1e9 100644 --- a/lib/pages/webview/view.dart +++ b/lib/pages/webview/view.dart @@ -33,7 +33,7 @@ class _WebviewPageState extends State { final RxString title = ''.obs; final RxDouble progress = 1.0.obs; bool? _inApp; - bool? _off; + bool _off = false; InAppWebViewController? _webViewController; @@ -282,7 +282,7 @@ class _WebviewPageState extends State { bool hasMatch = await PiliScheme.routePush( navigationAction.request.url?.uriValue ?? Uri(), selfHandle: true, - off: _off ?? true, + off: _off, ); // debugPrint('webview: [$url], [$hasMatch]'); if (hasMatch) { diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 6dc7f31e..be72b665 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -108,13 +108,15 @@ class PiliScheme { // to video reply String? oid = uriDigitRegExp.firstMatch(path)?.group(1); int? rpid = int.tryParse(queryParameters['comment_root_id']!); + String? commentSecondaryId = + queryParameters['comment_secondary_id']; if (oid != null && rpid != null) { Get.to( arguments: { 'oid': oid, 'rpid': rpid, 'type': 1, - 'id': queryParameters['comment_secondary_id'], + 'id': commentSecondaryId, }, () => Scaffold( resizeToAvoidBottomInset: false, @@ -142,9 +144,8 @@ class PiliScheme { source: 'routePush', replyType: 1, firstFloor: null, - id: queryParameters['comment_secondary_id'] != null - ? int.tryParse( - queryParameters['comment_secondary_id']!) + id: commentSecondaryId != null + ? int.tryParse(commentSecondaryId) : null, ), ), @@ -369,13 +370,15 @@ class PiliScheme { if (commentRootId != null) { String? dynId = uriDigitRegExp.firstMatch(path)?.group(1); int? rpid = int.tryParse(commentRootId); + final commentSecondaryId = + queryParameters['comment_secondary_id']; if (dynId != null && rpid != null) { Get.to( arguments: { 'oid': oid ?? dynId, 'rpid': rpid, 'type': businessId ?? 17, - 'id': queryParameters['comment_secondary_id'], + 'id': commentSecondaryId, }, () => Scaffold( resizeToAvoidBottomInset: false, @@ -399,9 +402,8 @@ class PiliScheme { source: 'routePush', replyType: businessId ?? 17, firstFloor: null, - id: queryParameters['comment_secondary_id'] != null - ? int.tryParse( - queryParameters['comment_secondary_id']!) + id: commentSecondaryId != null + ? int.tryParse(commentSecondaryId) : null, ), ), @@ -448,8 +450,7 @@ class PiliScheme { return false; case 'm.bilibili.com': // bilibili://m.bilibili.com/topic-detail?topic_id=1028161&frommodule=H5&h5awaken=xxx - final id = - RegExp(r'topic_id=(\d+)').firstMatch(uri.query)?.group(1); + final id = uri.queryParameters['topic_id']; if (id != null) { PageUtils.toDupNamed( '/dynTopic', @@ -589,27 +590,24 @@ class PiliScheme { launchURL(); return false; } - final String? area = pathSegments.first == 'mobile' - ? pathSegments.getOrNull(1) - : pathSegments.first; + final String? area = + pathSegments.first == 'mobile' || pathSegments.first == 'h5' + ? pathSegments.getOrNull(1) + : pathSegments.first; debugPrint('area: $area'); switch (area) { - case 'h5': - if (path.startsWith('/h5/note')) { - String? id = RegExp(r'cvid=(\d+)', caseSensitive: false) - .firstMatch(uri.query) - ?.group(1); - if (id != null) { - PageUtils.toDupNamed( - '/articlePage', - parameters: { - 'id': id, - 'type': 'read', - }, - off: off, - ); - return true; - } + case 'note': + String? id = uri.queryParameters['cvid']; + if (id != null) { + PageUtils.toDupNamed( + '/articlePage', + parameters: { + 'id': id, + 'type': 'read', + }, + off: off, + ); + return true; } launchURL(); return false; @@ -742,7 +740,7 @@ class PiliScheme { launchURL(); return false; case 'topic-detail': - String? id = RegExp(r'topic_id=(\d+)').firstMatch(uri.query)?.group(1); + String? id = uri.queryParameters['topic_id']; if (id != null) { PageUtils.toDupNamed( '/dynTopic', @@ -753,6 +751,58 @@ class PiliScheme { } launchURL(); return false; + case 'comment': + // https://www.bilibili.com/h5/comment/sub?oid=123456&pageType=1&root=87654321 + final queryParameters = uri.queryParameters; + String? oid = queryParameters['oid']; + String? root = queryParameters['root']; + String? pageType = queryParameters['pageType']; + if (oid != null && root != null && pageType != null) { + String? commentSecondaryId = queryParameters['comment_secondary_id']; + Get.to( + arguments: { + 'oid': oid, + 'rpid': root, + 'type': pageType, + 'id': commentSecondaryId, + }, + () => Scaffold( + resizeToAvoidBottomInset: false, + appBar: AppBar( + title: const Text('评论详情'), + actions: pageType == '1' + ? [ + IconButton( + tooltip: '前往', + onPressed: () { + videoPush(int.parse(oid), null); + }, + icon: const Icon(Icons.open_in_new), + ), + ] + : null, + ), + body: SafeArea( + top: false, + bottom: false, + child: VideoReplyReplyPanel( + enableSlide: false, + oid: int.parse(oid), + rpid: int.parse(root), + source: 'routePush', + replyType: int.parse(pageType), + firstFloor: null, + id: commentSecondaryId != null + ? int.tryParse(commentSecondaryId) + : null, + ), + ), + ), + ); + return true; + } + launchURL(); + return false; default: Map map = IdUtils.matchAvorBv(input: area?.split('?').first); if (map.isNotEmpty) {