From 95e50e436b37eca5ec82f9997b64482445f773ef Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Sun, 9 Mar 2025 21:12:16 +0800 Subject: [PATCH] mod: option show dyn actionbar Closes #412 Signed-off-by: bggRGjQaUbCoE --- lib/pages/dynamics/detail/controller.dart | 1 + lib/pages/dynamics/detail/view.dart | 451 ++++++++++--------- lib/pages/html/controller.dart | 23 +- lib/pages/html/view.dart | 501 ++++++++++++---------- lib/pages/setting/widgets/model.dart | 7 + lib/utils/storage.dart | 4 + 6 files changed, 556 insertions(+), 431 deletions(-) diff --git a/lib/pages/dynamics/detail/controller.dart b/lib/pages/dynamics/detail/controller.dart index 6636943b..a545b459 100644 --- a/lib/pages/dynamics/detail/controller.dart +++ b/lib/pages/dynamics/detail/controller.dart @@ -19,6 +19,7 @@ class DynamicDetailController extends ReplyController { int? floor; late final horizontalPreview = GStorage.horizontalPreview; + late final showDynActionBar = GStorage.showDynActionBar; @override dynamic get sourceId => diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index 09f3a14a..eac6be5b 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -458,211 +458,272 @@ class _DynamicDetailPageState extends State parent: _fabAnimationCtr!, curve: Curves.easeInOut, )), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Padding( - padding: const EdgeInsets.only(right: 14, bottom: 14), - child: FloatingActionButton( - heroTag: null, - onPressed: () { - feedBack(); - _dynamicDetailController.onReply( - context, - oid: _dynamicDetailController.oid, - replyType: ReplyType.values[replyType], - ); - }, - tooltip: '评论动态', - child: const Icon(Icons.reply), - ), - ), - Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - border: Border( - top: BorderSide( - color: Theme.of(context) - .colorScheme - .outline - .withOpacity(0.08), - ), - ), - ), - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Expanded( - child: Builder( - builder: (btnContext) => TextButton.icon( - onPressed: () { - showModalBottomSheet( - context: context, - isScrollControlled: true, - useSafeArea: true, - builder: (context) => RepostPanel( - item: _dynamicDetailController.item, - callback: () { - int count = int.tryParse( - _dynamicDetailController + child: Builder( + builder: (context) { + Widget button() => FloatingActionButton( + heroTag: null, + onPressed: () { + feedBack(); + _dynamicDetailController.onReply( + context, + oid: _dynamicDetailController.oid, + replyType: ReplyType.values[replyType], + ); + }, + tooltip: '评论动态', + child: const Icon(Icons.reply), + ); + return _dynamicDetailController.showDynActionBar.not + ? Align( + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only( + right: 14, + bottom: + MediaQuery.paddingOf(context).bottom + 14, + ), + child: button(), + ), + ) + : Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Padding( + padding: const EdgeInsets.only( + right: 14, bottom: 14), + child: button(), + ), + Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + border: Border( + top: BorderSide( + color: Theme.of(context) + .colorScheme + .outline + .withOpacity(0.08), + ), + ), + ), + padding: EdgeInsets.only( + bottom: + MediaQuery.paddingOf(context).bottom), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceAround, + children: [ + Expanded( + child: Builder( + builder: (btnContext) => + TextButton.icon( + onPressed: () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + useSafeArea: true, + builder: (context) => RepostPanel( + item: _dynamicDetailController + .item, + callback: () { + int count = int.tryParse( + _dynamicDetailController + .item + .modules + ?.moduleStat + ?.forward + ?.count ?? + '0') ?? + 0; + _dynamicDetailController + .item + .modules + ?.moduleStat ??= + ModuleStatModel(); + _dynamicDetailController + .item + .modules! + .moduleStat + ?.forward ??= ForWard(); + _dynamicDetailController + .item + .modules! + .moduleStat! + .forward! + .count = + (count + 1).toString(); + if (btnContext.mounted) { + (btnContext as Element?) + ?.markNeedsBuild(); + } + }, + ), + ); + }, + icon: Icon( + FontAwesomeIcons.shareFromSquare, + size: 16, + color: Theme.of(context) + .colorScheme + .outline, + semanticLabel: "转发", + ), + style: TextButton.styleFrom( + padding: const EdgeInsets.fromLTRB( + 15, 0, 15, 0), + foregroundColor: Theme.of(context) + .colorScheme + .outline, + ), + label: Text( + _dynamicDetailController .item .modules ?.moduleStat ?.forward - ?.count ?? - '0') ?? - 0; - _dynamicDetailController.item.modules - ?.moduleStat ??= ModuleStatModel(); - _dynamicDetailController.item.modules! - .moduleStat?.forward ??= ForWard(); - _dynamicDetailController - .item - .modules! - .moduleStat! - .forward! - .count = (count + 1).toString(); - if (btnContext.mounted) { - (btnContext as Element?) - ?.markNeedsBuild(); - } - }, + ?.count != + null + ? Utils.numFormat( + _dynamicDetailController + .item + .modules! + .moduleStat! + .forward! + .count) + : '转发', + ), + ), + ), ), - ); - }, - icon: Icon( - FontAwesomeIcons.shareFromSquare, - size: 16, - color: Theme.of(context).colorScheme.outline, - semanticLabel: "转发", - ), - style: TextButton.styleFrom( - padding: - const EdgeInsets.fromLTRB(15, 0, 15, 0), - foregroundColor: - Theme.of(context).colorScheme.outline, - ), - label: Text( - _dynamicDetailController.item.modules - ?.moduleStat?.forward?.count != - null - ? Utils.numFormat(_dynamicDetailController - .item - .modules! - .moduleStat! - .forward! - .count) - : '转发', - ), - ), - ), - ), - Expanded( - child: TextButton.icon( - onPressed: () { - Utils.shareText( - '${HttpString.dynamicShareBaseUrl}/${_dynamicDetailController.item.idStr}'); - }, - icon: Icon( - FontAwesomeIcons.shareNodes, - size: 16, - color: Theme.of(context).colorScheme.outline, - semanticLabel: "分享", - ), - style: TextButton.styleFrom( - padding: - const EdgeInsets.fromLTRB(15, 0, 15, 0), - foregroundColor: - Theme.of(context).colorScheme.outline, - ), - label: const Text('分享'), - ), - ), - Expanded( - child: Builder( - builder: (context) => TextButton.icon( - onPressed: () => Utils.onLikeDynamic( - _dynamicDetailController.item, - () { - if (context.mounted) { - (context as Element?)?.markNeedsBuild(); - } - }, - ), - icon: Icon( - _dynamicDetailController.item.modules - ?.moduleStat?.like?.status == - true - ? FontAwesomeIcons.solidThumbsUp - : FontAwesomeIcons.thumbsUp, - size: 16, - color: _dynamicDetailController.item.modules - ?.moduleStat?.like?.status == - true - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, - semanticLabel: _dynamicDetailController - .item - .modules - ?.moduleStat - ?.like - ?.status == - true - ? "已赞" - : "点赞", - ), - style: TextButton.styleFrom( - padding: - const EdgeInsets.fromLTRB(15, 0, 15, 0), - foregroundColor: - Theme.of(context).colorScheme.outline, - ), - label: AnimatedSwitcher( - duration: const Duration(milliseconds: 400), - transitionBuilder: (Widget child, - Animation animation) { - return ScaleTransition( - scale: animation, child: child); - }, - child: Text( - _dynamicDetailController.item.modules - ?.moduleStat?.like?.count != - null - ? Utils.numFormat( - _dynamicDetailController - .item - .modules! - .moduleStat! - .like! - .count) - : '点赞', - style: TextStyle( - color: _dynamicDetailController - .item - .modules - ?.moduleStat - ?.like - ?.status == - true - ? Theme.of(context) - .colorScheme - .primary - : Theme.of(context) + Expanded( + child: TextButton.icon( + onPressed: () { + Utils.shareText( + '${HttpString.dynamicShareBaseUrl}/${_dynamicDetailController.item.idStr}'); + }, + icon: Icon( + FontAwesomeIcons.shareNodes, + size: 16, + color: Theme.of(context) .colorScheme .outline, + semanticLabel: "分享", + ), + style: TextButton.styleFrom( + padding: const EdgeInsets.fromLTRB( + 15, 0, 15, 0), + foregroundColor: Theme.of(context) + .colorScheme + .outline, + ), + label: const Text('分享'), + ), ), - ), + Expanded( + child: Builder( + builder: (context) => TextButton.icon( + onPressed: () => Utils.onLikeDynamic( + _dynamicDetailController.item, + () { + if (context.mounted) { + (context as Element?) + ?.markNeedsBuild(); + } + }, + ), + icon: Icon( + _dynamicDetailController + .item + .modules + ?.moduleStat + ?.like + ?.status == + true + ? FontAwesomeIcons.solidThumbsUp + : FontAwesomeIcons.thumbsUp, + size: 16, + color: _dynamicDetailController + .item + .modules + ?.moduleStat + ?.like + ?.status == + true + ? Theme.of(context) + .colorScheme + .primary + : Theme.of(context) + .colorScheme + .outline, + semanticLabel: + _dynamicDetailController + .item + .modules + ?.moduleStat + ?.like + ?.status == + true + ? "已赞" + : "点赞", + ), + style: TextButton.styleFrom( + padding: const EdgeInsets.fromLTRB( + 15, 0, 15, 0), + foregroundColor: Theme.of(context) + .colorScheme + .outline, + ), + label: AnimatedSwitcher( + duration: const Duration( + milliseconds: 400), + transitionBuilder: (Widget child, + Animation animation) { + return ScaleTransition( + scale: animation, + child: child); + }, + child: Text( + _dynamicDetailController + .item + .modules + ?.moduleStat + ?.like + ?.count != + null + ? Utils.numFormat( + _dynamicDetailController + .item + .modules! + .moduleStat! + .like! + .count) + : '点赞', + style: TextStyle( + color: _dynamicDetailController + .item + .modules + ?.moduleStat + ?.like + ?.status == + true + ? Theme.of(context) + .colorScheme + .primary + : Theme.of(context) + .colorScheme + .outline, + ), + ), + ), + ), + ), + ), + ], ), ), - ), - ), - ], - ), - ), - ], + ], + ); + }, ), ), ), diff --git a/lib/pages/html/controller.dart b/lib/pages/html/controller.dart index 702f352e..c948a82a 100644 --- a/lib/pages/html/controller.dart +++ b/lib/pages/html/controller.dart @@ -25,6 +25,7 @@ class HtmlRenderController extends ReplyController { RxBool loaded = false.obs; late final horizontalPreview = GStorage.horizontalPreview; + late final showDynActionBar = GStorage.showDynActionBar; @override dynamic get sourceId => id; @@ -35,16 +36,20 @@ class HtmlRenderController extends ReplyController { id = Get.parameters['id']!; dynamicType = Get.parameters['dynamicType']!; type = dynamicType == 'picture' ? 11 : 12; - if (RegExp(r'^cv', caseSensitive: false).hasMatch(id)) { - UrlUtils.parseRedirectUrl('https://www.bilibili.com/read/$id/') - .then((url) { - if (url != null) { - _queryDyn(url.split('/').last); - } - }); - } else { - _queryDyn(id); + + if (showDynActionBar) { + if (RegExp(r'^cv', caseSensitive: false).hasMatch(id)) { + UrlUtils.parseRedirectUrl('https://www.bilibili.com/read/$id/') + .then((url) { + if (url != null) { + _queryDyn(url.split('/').last); + } + }); + } else { + _queryDyn(id); + } } + reqHtml(); } diff --git a/lib/pages/html/view.dart b/lib/pages/html/view.dart index e8f6aaf8..b638f273 100644 --- a/lib/pages/html/view.dart +++ b/lib/pages/html/view.dart @@ -448,20 +448,9 @@ class _HtmlRenderPageState extends State parent: fabAnimationCtr, curve: Curves.easeInOut, )), - child: Obx( - () => Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Padding( - padding: EdgeInsets.only( - right: 14, - bottom: 14 + - (_htmlRenderCtr.item.value.idStr != null - ? 0 - : MediaQuery.of(context).padding.bottom), - ), - child: FloatingActionButton( + child: Builder( + builder: (context) { + Widget button() => FloatingActionButton( heroTag: null, onPressed: () { feedBack(); @@ -473,229 +462,287 @@ class _HtmlRenderPageState extends State }, tooltip: '评论动态', child: const Icon(Icons.reply), - ), - ), - _htmlRenderCtr.item.value.idStr != null - ? Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - border: Border( - top: BorderSide( - color: Theme.of(context) - .colorScheme - .outline - .withOpacity(0.08), - ), - ), - ), + ); + return _htmlRenderCtr.showDynActionBar.not + ? Align( + alignment: Alignment.bottomRight, + child: Padding( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Expanded( - child: Builder( - builder: (btnContext) => TextButton.icon( - onPressed: () { - showModalBottomSheet( - context: context, - isScrollControlled: true, - useSafeArea: true, - builder: (context) => RepostPanel( - item: _htmlRenderCtr.item.value, - callback: () { - int count = int.tryParse( - _htmlRenderCtr + right: 14, + bottom: + MediaQuery.of(context).padding.bottom + 14, + ), + child: button(), + ), + ) + : Obx( + () => Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Padding( + padding: EdgeInsets.only( + right: 14, + bottom: 14 + + (_htmlRenderCtr.item.value.idStr != null + ? 0 + : MediaQuery.of(context) + .padding + .bottom), + ), + child: button(), + ), + _htmlRenderCtr.item.value.idStr != null + ? Container( + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .surface, + border: Border( + top: BorderSide( + color: Theme.of(context) + .colorScheme + .outline + .withOpacity(0.08), + ), + ), + ), + padding: EdgeInsets.only( + bottom: MediaQuery.paddingOf(context) + .bottom), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceAround, + children: [ + Expanded( + child: Builder( + builder: (btnContext) => + TextButton.icon( + onPressed: () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + useSafeArea: true, + builder: (context) => + RepostPanel( + item: _htmlRenderCtr + .item.value, + callback: () { + int count = int.tryParse( + _htmlRenderCtr + .item + .value + .modules + ?.moduleStat + ?.forward + ?.count ?? + '0') ?? + 0; + _htmlRenderCtr + .item + .value + .modules + ?.moduleStat + ?.forward! + .count = + (count + 1) + .toString(); + if (btnContext + .mounted) { + (btnContext + as Element?) + ?.markNeedsBuild(); + } + }, + ), + ); + }, + icon: Icon( + FontAwesomeIcons + .shareFromSquare, + size: 16, + color: Theme.of(context) + .colorScheme + .outline, + semanticLabel: "转发", + ), + style: TextButton.styleFrom( + padding: + const EdgeInsets.fromLTRB( + 15, 0, 15, 0), + foregroundColor: + Theme.of(context) + .colorScheme + .outline, + ), + label: Text( + _htmlRenderCtr .item .value .modules ?.moduleStat - ?.forward - ?.count ?? - '0') ?? - 0; - _htmlRenderCtr - .item - .value - .modules - ?.moduleStat - ?.forward! - .count = - (count + 1).toString(); - if (btnContext.mounted) { - (btnContext as Element?) - ?.markNeedsBuild(); - } - }, + ?.forward! + .count != + null + ? Utils.numFormat( + _htmlRenderCtr + .item + .value + .modules + ?.moduleStat + ?.forward! + .count) + : '转发', + ), + ), + ), ), - ); - }, - icon: Icon( - FontAwesomeIcons.shareFromSquare, - size: 16, - color: Theme.of(context) - .colorScheme - .outline, - semanticLabel: "转发", - ), - style: TextButton.styleFrom( - padding: const EdgeInsets.fromLTRB( - 15, 0, 15, 0), - foregroundColor: Theme.of(context) - .colorScheme - .outline, - ), - label: Text( - _htmlRenderCtr - .item - .value - .modules - ?.moduleStat - ?.forward! - .count != - null - ? Utils.numFormat(_htmlRenderCtr - .item - .value - .modules - ?.moduleStat - ?.forward! - .count) - : '转发', - ), - ), - ), - ), - Expanded( - child: TextButton.icon( - onPressed: () { - Utils.shareText( - '${HttpString.dynamicShareBaseUrl}/${_htmlRenderCtr.item.value.idStr}'); - }, - icon: Icon( - FontAwesomeIcons.shareNodes, - size: 16, - color: - Theme.of(context).colorScheme.outline, - semanticLabel: "分享", - ), - style: TextButton.styleFrom( - padding: const EdgeInsets.fromLTRB( - 15, 0, 15, 0), - foregroundColor: - Theme.of(context).colorScheme.outline, - ), - label: const Text('分享'), - ), - ), - Expanded( - child: Builder( - builder: (context) => TextButton.icon( - onPressed: () => Utils.onLikeDynamic( - _htmlRenderCtr.item.value, - () { - if (context.mounted) { - (context as Element?) - ?.markNeedsBuild(); - } - }, - ), - icon: Icon( - _htmlRenderCtr - .item - .value - .modules - ?.moduleStat - ?.like - ?.status == - true - ? FontAwesomeIcons.solidThumbsUp - : FontAwesomeIcons.thumbsUp, - size: 16, - color: _htmlRenderCtr - .item - .value - .modules - ?.moduleStat - ?.like - ?.status == - true - ? Theme.of(context) - .colorScheme - .primary - : Theme.of(context) - .colorScheme - .outline, - semanticLabel: _htmlRenderCtr - .item - .value - .modules - ?.moduleStat - ?.like - ?.status == - true - ? "已赞" - : "点赞", - ), - style: TextButton.styleFrom( - padding: const EdgeInsets.fromLTRB( - 15, 0, 15, 0), - foregroundColor: Theme.of(context) - .colorScheme - .outline, - ), - label: AnimatedSwitcher( - duration: - const Duration(milliseconds: 400), - transitionBuilder: (Widget child, - Animation animation) { - return ScaleTransition( - scale: animation, child: child); - }, - child: Text( - _htmlRenderCtr - .item - .value - .modules - ?.moduleStat - ?.like - ?.count != - null - ? Utils.numFormat(_htmlRenderCtr - .item - .value - .modules! - .moduleStat! - .like! - .count) - : '点赞', - style: TextStyle( - color: _htmlRenderCtr - .item - .value - .modules - ?.moduleStat - ?.like - ?.status == - true - ? Theme.of(context) - .colorScheme - .primary - : Theme.of(context) + Expanded( + child: TextButton.icon( + onPressed: () { + Utils.shareText( + '${HttpString.dynamicShareBaseUrl}/${_htmlRenderCtr.item.value.idStr}'); + }, + icon: Icon( + FontAwesomeIcons.shareNodes, + size: 16, + color: Theme.of(context) .colorScheme .outline, + semanticLabel: "分享", + ), + style: TextButton.styleFrom( + padding: + const EdgeInsets.fromLTRB( + 15, 0, 15, 0), + foregroundColor: + Theme.of(context) + .colorScheme + .outline, + ), + label: const Text('分享'), + ), ), - ), + Expanded( + child: Builder( + builder: (context) => + TextButton.icon( + onPressed: () => + Utils.onLikeDynamic( + _htmlRenderCtr.item.value, + () { + if (context.mounted) { + (context as Element?) + ?.markNeedsBuild(); + } + }, + ), + icon: Icon( + _htmlRenderCtr + .item + .value + .modules + ?.moduleStat + ?.like + ?.status == + true + ? FontAwesomeIcons + .solidThumbsUp + : FontAwesomeIcons + .thumbsUp, + size: 16, + color: _htmlRenderCtr + .item + .value + .modules + ?.moduleStat + ?.like + ?.status == + true + ? Theme.of(context) + .colorScheme + .primary + : Theme.of(context) + .colorScheme + .outline, + semanticLabel: _htmlRenderCtr + .item + .value + .modules + ?.moduleStat + ?.like + ?.status == + true + ? "已赞" + : "点赞", + ), + style: TextButton.styleFrom( + padding: + const EdgeInsets.fromLTRB( + 15, 0, 15, 0), + foregroundColor: + Theme.of(context) + .colorScheme + .outline, + ), + label: AnimatedSwitcher( + duration: const Duration( + milliseconds: 400), + transitionBuilder: + (Widget child, + Animation + animation) { + return ScaleTransition( + scale: animation, + child: child); + }, + child: Text( + _htmlRenderCtr + .item + .value + .modules + ?.moduleStat + ?.like + ?.count != + null + ? Utils.numFormat( + _htmlRenderCtr + .item + .value + .modules! + .moduleStat! + .like! + .count) + : '点赞', + style: TextStyle( + color: _htmlRenderCtr + .item + .value + .modules + ?.moduleStat + ?.like + ?.status == + true + ? Theme.of(context) + .colorScheme + .primary + : Theme.of(context) + .colorScheme + .outline, + ), + ), + ), + ), + ), + ), + ], ), - ), - ), - ), - ], - ), - ) - : const SizedBox.shrink(), - ], - ), + ) + : const SizedBox.shrink(), + ], + ), + ); + }, ), ), ), diff --git a/lib/pages/setting/widgets/model.dart b/lib/pages/setting/widgets/model.dart index 03e638c3..1f3e8cb5 100644 --- a/lib/pages/setting/widgets/model.dart +++ b/lib/pages/setting/widgets/model.dart @@ -2137,6 +2137,13 @@ List get extraSettings => [ setKey: SettingBoxKey.enableShrinkVideoSize, defaultVal: true, ), + SettingsModel( + settingsType: SettingsType.sw1tch, + title: '动态/专栏详情页展示底部操作栏', + leading: const Icon(Icons.more_horiz), + setKey: SettingBoxKey.showDynActionBar, + defaultVal: true, + ), SettingsModel( settingsType: SettingsType.sw1tch, enableFeedback: true, diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index d06673a1..cbad10f6 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -400,6 +400,9 @@ class GStorage { static bool get enableShrinkVideoSize => GStorage.setting .get(SettingBoxKey.enableShrinkVideoSize, defaultValue: true); + static bool get showDynActionBar => + GStorage.setting.get(SettingBoxKey.showDynActionBar, defaultValue: true); + static List get dynamicDetailRatio => List.from(setting .get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0])); @@ -657,6 +660,7 @@ class SettingBoxKey { slideDismissReplyPage = 'slideDismissReplyPage', showFSActionItem = 'showFSActionItem', enableShrinkVideoSize = 'enableShrinkVideoSize', + showDynActionBar = 'showDynActionBar', // Sponsor Block enableSponsorBlock = 'enableSponsorBlock',