diff --git a/lib/models/dynamics/result.dart b/lib/models/dynamics/result.dart index 9aa94fd1..fff6ead6 100644 --- a/lib/models/dynamics/result.dart +++ b/lib/models/dynamics/result.dart @@ -29,7 +29,6 @@ class DynamicItemModel { DynamicItemModel? orig; String? type; bool? visible; - bool? isForwarded; // opus Fallback? fallback; @@ -41,7 +40,7 @@ class DynamicItemModel { ? ItemModulesModel() : ItemModulesModel.fromJson(json['modules']); if (json['orig'] != null) { - orig = DynamicItemModel.fromJson(json['orig'])..isForwarded = true; + orig = DynamicItemModel.fromJson(json['orig']); } type = json['type']; visible = json['visible']; diff --git a/lib/pages/dynamics/widgets/additional_panel.dart b/lib/pages/dynamics/widgets/additional_panel.dart index 09f9014e..381a3a63 100644 --- a/lib/pages/dynamics/widgets/additional_panel.dart +++ b/lib/pages/dynamics/widgets/additional_panel.dart @@ -11,9 +11,9 @@ 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, +Widget addWidget(ThemeData theme, DynamicItemModel item, BuildContext context, {floor = 1}) { + final type = item.modules.moduleDynamic?.additional?.type; late final Color bgColor = floor == 1 ? theme.dividerColor.withValues(alpha: 0.08) : theme.colorScheme.surface; diff --git a/lib/pages/dynamics/widgets/article_panel.dart b/lib/pages/dynamics/widgets/article_panel.dart deleted file mode 100644 index 9d1d2119..00000000 --- a/lib/pages/dynamics/widgets/article_panel.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:PiliPlus/common/constants.dart'; -import 'package:PiliPlus/models/dynamics/result.dart'; -import 'package:PiliPlus/pages/dynamics/widgets/pic_panel.dart'; -import 'package:PiliPlus/utils/utils.dart'; -import 'package:flutter/material.dart'; - -Widget articlePanel( - ThemeData theme, - String? source, - DynamicItemModel item, - BuildContext context, - Function(List, int)? callback, { - floor = 1, -}) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: StyleString.safeSpace), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (floor == 2) ...[ - Row( - children: [ - Text( - '@${item.modules.moduleAuthor!.name}', - style: TextStyle(color: theme.colorScheme.primary), - ), - const SizedBox(width: 6), - Text( - Utils.dateFormat(item.modules.moduleAuthor!.pubTs), - style: TextStyle( - color: theme.colorScheme.outline, - fontSize: theme.textTheme.labelSmall!.fontSize), - ), - ], - ), - const SizedBox(height: 8), - ], - Text( - item.modules.moduleDynamic!.major!.opus!.title!, - style: theme.textTheme.titleMedium! - .copyWith(fontWeight: FontWeight.bold), - ), - const SizedBox(height: 2), - if (item.modules.moduleDynamic?.major?.opus?.summary?.text != - 'undefined') ...[ - Text( - item.modules.moduleDynamic!.major!.opus!.summary!.richTextNodes! - .first.text!, - maxLines: source == 'detail' ? null : 4, - style: const TextStyle(height: 1.55), - overflow: source == 'detail' ? null : TextOverflow.ellipsis, - ), - const SizedBox(height: 2), - ], - picWidget(item, context, callback) - ], - ), - ); -} diff --git a/lib/pages/dynamics/widgets/blocked_item.dart b/lib/pages/dynamics/widgets/blocked_item.dart new file mode 100644 index 00000000..0cdc218b --- /dev/null +++ b/lib/pages/dynamics/widgets/blocked_item.dart @@ -0,0 +1,15 @@ +import 'package:PiliPlus/models/dynamics/result.dart' show ModuleBlocked; +import 'package:PiliPlus/pages/article/widgets/opus_content.dart' + show moduleBlockedItem; +import 'package:flutter/material.dart'; + +Widget blockedItem(ThemeData theme, ModuleBlocked moduleBlocked) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 1), + child: LayoutBuilder( + builder: (context, constraints) { + return moduleBlockedItem(theme, moduleBlocked, constraints.maxWidth); + }, + ), + ); +} diff --git a/lib/pages/dynamics/widgets/content_panel.dart b/lib/pages/dynamics/widgets/content_panel.dart index 972aee57..a6a3d002 100644 --- a/lib/pages/dynamics/widgets/content_panel.dart +++ b/lib/pages/dynamics/widgets/content_panel.dart @@ -72,7 +72,11 @@ Widget content( ], ), style: TextStyle( - fontSize: source == 'detail' && !isSave ? 16 : 15, + fontSize: floor != 1 + ? 14 + : source == 'detail' && !isSave + ? 16 + : 15, color: theme.colorScheme.primary, ), ), @@ -86,10 +90,12 @@ Widget content( : const TextStyle(fontSize: 16), ) : Text.rich( - style: const TextStyle(fontSize: 15), + style: floor == 1 + ? const TextStyle(fontSize: 15) + : const TextStyle(fontSize: 14), richNodes, - maxLines: 6, - overflow: TextOverflow.ellipsis, + maxLines: isSave ? null : 6, + overflow: isSave ? null : TextOverflow.ellipsis, ), if (item.modules.moduleDynamic?.major?.opus?.pics?.isNotEmpty == true) Text.rich(picsNodes()), diff --git a/lib/pages/dynamics/widgets/dynamic_panel.dart b/lib/pages/dynamics/widgets/dynamic_panel.dart index a588c5a5..1036164a 100644 --- a/lib/pages/dynamics/widgets/dynamic_panel.dart +++ b/lib/pages/dynamics/widgets/dynamic_panel.dart @@ -1,9 +1,11 @@ import 'package:PiliPlus/common/widgets/image/image_save.dart'; import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/pages/dynamics/widgets/action_panel.dart'; +import 'package:PiliPlus/pages/dynamics/widgets/additional_panel.dart'; import 'package:PiliPlus/pages/dynamics/widgets/author_panel.dart'; +import 'package:PiliPlus/pages/dynamics/widgets/blocked_item.dart'; import 'package:PiliPlus/pages/dynamics/widgets/content_panel.dart'; -import 'package:PiliPlus/pages/dynamics/widgets/forward_panel.dart'; +import 'package:PiliPlus/pages/dynamics/widgets/module_panel.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:flutter/material.dart'; @@ -68,7 +70,11 @@ class DynamicPanel extends StatelessWidget { if (item.modules.moduleDynamic!.desc != null || item.modules.moduleDynamic!.major != null) content(theme, isSave, context, item, source, callback), - forWard(theme, isSave, item, context, source, callback), + module(theme, isSave, item, context, source, callback), + if (item.modules.moduleDynamic?.additional != null) + addWidget(theme, item, context), + if (item.modules.moduleDynamic?.major?.blocked != null) + blockedItem(theme, item.modules.moduleDynamic!.major!.blocked!), const SizedBox(height: 2), if (source == null) ActionPanel(item: item), if (source == 'detail' && !isSave) const SizedBox(height: 12), diff --git a/lib/pages/dynamics/widgets/forward_panel.dart b/lib/pages/dynamics/widgets/forward_panel.dart deleted file mode 100644 index ec7d2d0f..00000000 --- a/lib/pages/dynamics/widgets/forward_panel.dart +++ /dev/null @@ -1,540 +0,0 @@ -// 转发 -import 'package:PiliPlus/common/widgets/badge.dart'; -import 'package:PiliPlus/common/widgets/custom_icon.dart'; -import 'package:PiliPlus/common/widgets/image/image_save.dart'; -import 'package:PiliPlus/common/widgets/image/image_view.dart'; -import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; -import 'package:PiliPlus/models/common/image_type.dart'; -import 'package:PiliPlus/models/dynamics/result.dart'; -import 'package:PiliPlus/pages/article/widgets/opus_content.dart'; -import 'package:PiliPlus/pages/dynamics/widgets/additional_panel.dart'; -import 'package:PiliPlus/pages/dynamics/widgets/article_panel.dart'; -import 'package:PiliPlus/pages/dynamics/widgets/live_panel.dart'; -import 'package:PiliPlus/pages/dynamics/widgets/live_panel_sub.dart'; -import 'package:PiliPlus/pages/dynamics/widgets/live_rcmd_panel.dart'; -import 'package:PiliPlus/pages/dynamics/widgets/pic_panel.dart'; -import 'package:PiliPlus/pages/dynamics/widgets/rich_node_panel.dart'; -import 'package:PiliPlus/pages/dynamics/widgets/video_panel.dart'; -import 'package:PiliPlus/utils/extension.dart'; -import 'package:PiliPlus/utils/page_utils.dart'; -import 'package:PiliPlus/utils/utils.dart'; -import 'package:flutter/material.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:get/get.dart'; - -InlineSpan picsNodes( - List pics, - Function(List, int)? callback, -) { - return WidgetSpan( - child: LayoutBuilder( - builder: (context, constraints) => imageView( - constraints.maxWidth, - pics - .map( - (item) => ImageModel( - width: item.width, - height: item.height, - url: item.url ?? '', - liveUrl: item.liveUrl, - ), - ) - .toList(), - callback: callback, - ), - ), - ); -} - -Widget _blockedItem(ThemeData theme, ModuleBlocked moduleBlocked) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 1), - child: LayoutBuilder( - builder: (context, constraints) { - return moduleBlockedItem(theme, moduleBlocked, constraints.maxWidth); - }, - ), - ); -} - -Widget forWard( - ThemeData theme, - bool isSave, - DynamicItemModel item, - BuildContext context, - String? source, - Function(List, int)? callback, { - floor = 1, -}) { - switch (item.type) { - // 图文 - case 'DYNAMIC_TYPE_DRAW': - TextSpan? richNodes = richNode(theme, item, context); - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (floor == 2) ...[ - Row( - children: [ - GestureDetector( - onTap: () => Get.toNamed( - '/member?mid=${item.modules.moduleAuthor!.mid}', - arguments: {'face': item.modules.moduleAuthor!.face}), - child: Text( - '@${item.modules.moduleAuthor!.name}', - style: TextStyle(color: theme.colorScheme.primary), - ), - ), - const SizedBox(width: 6), - Text( - Utils.dateFormat(item.modules.moduleAuthor!.pubTs), - style: TextStyle( - color: theme.colorScheme.outline, - fontSize: theme.textTheme.labelSmall!.fontSize), - ), - ], - ), - const SizedBox(height: 5), - if (item.modules.moduleDynamic?.topic != null) ...[ - GestureDetector( - onTap: () => Get.toNamed( - '/dynTopic', - parameters: { - 'id': item.modules.moduleDynamic!.topic!.id!.toString(), - 'name': item.modules.moduleDynamic!.topic!.name!, - }, - ), - child: Text.rich( - TextSpan( - children: [ - WidgetSpan( - alignment: PlaceholderAlignment.bottom, - child: Padding( - padding: const EdgeInsets.only(right: 4), - child: Icon( - size: 18, - CustomIcon.topic_tag, - color: theme.colorScheme.primary, - ), - ), - ), - TextSpan(text: item.modules.moduleDynamic!.topic!.name), - ], - ), - style: TextStyle(color: theme.colorScheme.primary), - ), - ), - const SizedBox(height: 5), - ], - if (richNodes != null) - Text.rich( - richNodes, - // 被转发状态(floor=2) 隐藏 - maxLines: isSave - ? null - : source == 'detail' && floor != 2 - ? null - : 4, - overflow: isSave - ? null - : source == 'detail' && floor != 2 - ? null - : TextOverflow.ellipsis, - ), - if (item.modules.moduleDynamic?.major?.opus?.pics?.isNotEmpty == - true) - Text.rich( - picsNodes( - item.modules.moduleDynamic!.major!.opus!.pics!, callback), - ), - const SizedBox(height: 4), - ], - Padding( - padding: floor == 2 - ? EdgeInsets.zero - : const EdgeInsets.only(left: 12, right: 12), - child: picWidget(item, context, callback), - ), - - /// 附加内容 商品信息、直播预约等等 - if (item.modules.moduleDynamic?.additional != null) - addWidget( - theme, - item, - context, - item.modules.moduleDynamic?.additional?.type, - floor: floor, - ), - if (item.modules.moduleDynamic?.major?.blocked != null) - _blockedItem(theme, item.modules.moduleDynamic!.major!.blocked!), - ], - ); - // 视频 - case 'DYNAMIC_TYPE_AV': - return videoSeasonWidget( - theme, isSave, source, item, context, 'archive', callback, - floor: floor); - // 文章 - case 'DYNAMIC_TYPE_ARTICLE': - return item.isForwarded == true - ? articlePanel(theme, source, item, context, callback, floor: floor) - : item.modules.moduleDynamic?.major?.blocked != null - ? _blockedItem(theme, item.modules.moduleDynamic!.major!.blocked!) - : const SizedBox.shrink(); - // 转发 - case 'DYNAMIC_TYPE_FORWARD': - final isNoneMajor = - item.orig?.modules.moduleDynamic?.major?.type == 'MAJOR_TYPE_NONE'; - return InkWell( - onTap: isNoneMajor - ? null - : () => PageUtils.pushDynDetail(item.orig!, floor + 1), - onLongPress: isNoneMajor - ? null - : () { - late String? title, cover; - late var origMajor = item.orig?.modules.moduleDynamic?.major; - late var major = item.modules.moduleDynamic?.major; - switch (item.orig?.type) { - case 'DYNAMIC_TYPE_AV': - title = origMajor?.archive?.title; - cover = origMajor?.archive?.cover; - break; - case 'DYNAMIC_TYPE_UGC_SEASON': - title = origMajor?.ugcSeason?.title; - cover = origMajor?.ugcSeason?.cover; - break; - case 'DYNAMIC_TYPE_PGC' || 'DYNAMIC_TYPE_PGC_UNION': - title = origMajor?.pgc?.title; - cover = origMajor?.pgc?.cover; - break; - case 'DYNAMIC_TYPE_LIVE_RCMD': - title = major?.liveRcmd?.title; - cover = major?.liveRcmd?.cover; - break; - case 'DYNAMIC_TYPE_LIVE': - title = major?.live?.title; - cover = major?.live?.cover; - break; - default: - return; - } - imageSaveDialog( - title: title, - cover: cover, - ); - }, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8), - color: theme.dividerColor.withValues(alpha: 0.08), - child: forWard(theme, isSave, item.orig!, context, source, callback, - floor: floor + 1), - ), - ); - // 直播 - case 'DYNAMIC_TYPE_LIVE_RCMD': - return liveRcmdPanel(theme, source, item, context, floor: floor); - // 直播 - case 'DYNAMIC_TYPE_LIVE': - return livePanel(theme, source, item, context, floor: floor); - // 合集 - case 'DYNAMIC_TYPE_UGC_SEASON': - return videoSeasonWidget( - theme, isSave, source, item, context, 'ugcSeason', callback); - case 'DYNAMIC_TYPE_WORD': - late TextSpan? richNodes = richNode(theme, item, context); - return floor == 2 - ? Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - GestureDetector( - onTap: () => Get.toNamed( - '/member?mid=${item.modules.moduleAuthor?.mid}', - arguments: {'face': item.modules.moduleAuthor?.face}), - child: Text( - '@${item.modules.moduleAuthor?.name}', - style: TextStyle(color: theme.colorScheme.primary), - ), - ), - const SizedBox(width: 6), - Text( - Utils.dateFormat(item.modules.moduleAuthor?.pubTs), - style: TextStyle( - color: theme.colorScheme.outline, - fontSize: theme.textTheme.labelSmall!.fontSize), - ), - ], - ), - const SizedBox(height: 8), - if (richNodes != null) - Text.rich( - richNodes, - // 被转发状态(floor=2) 隐藏 - maxLines: isSave - ? null - : source == 'detail' && floor != 2 - ? null - : 4, - overflow: isSave - ? null - : source == 'detail' && floor != 2 - ? null - : TextOverflow.ellipsis, - ), - ], - ) - : item.modules.moduleDynamic?.additional != null - ? addWidget( - theme, - item, - context, - item.modules.moduleDynamic!.additional!.type, - floor: floor, - ) - : item.modules.moduleDynamic?.major?.blocked != null - ? _blockedItem( - theme, item.modules.moduleDynamic!.major!.blocked!) - : const SizedBox.shrink(); - case 'DYNAMIC_TYPE_PGC': - return videoSeasonWidget( - theme, isSave, source, item, context, 'pgc', callback, - floor: floor); - case 'DYNAMIC_TYPE_PGC_UNION': - return videoSeasonWidget( - theme, isSave, source, item, context, 'pgc', callback, - floor: floor); - // 直播结束 - case 'DYNAMIC_TYPE_NONE': - return Row( - children: [ - const Icon( - FontAwesomeIcons.ghost, - size: 14, - ), - const SizedBox(width: 4), - Text(item.modules.moduleDynamic!.major!.none!.tips!) - ], - ); - // 课堂 - case 'DYNAMIC_TYPE_COURSES_SEASON': - return SizedBox( - width: double.infinity, - child: Padding( - padding: floor == 1 - ? const EdgeInsets.symmetric(horizontal: 12) - : EdgeInsets.zero, - child: Text( - "课堂:${item.modules.moduleDynamic!.major!.courses!['title']}", - ), - ), - ); - // 活动 - case 'DYNAMIC_TYPE_COMMON_SQUARE': - return InkWell( - onTap: () { - try { - String url = item.modules.moduleDynamic!.major!.common!['jump_url']; - if (url.contains('bangumi/play') && PageUtils.viewPgcFromUri(url)) { - return; - } - PageUtils.handleWebview(url, inApp: true); - } catch (_) {} - }, - child: Container( - width: double.infinity, - padding: - const EdgeInsets.only(left: 12, top: 10, right: 12, bottom: 10), - color: theme.dividerColor.withValues(alpha: 0.08), - child: Row( - children: [ - NetworkImgLayer( - radius: 8, - width: 45, - height: 45, - src: item.modules.moduleDynamic!.major!.common!['cover'], - ), - const SizedBox(width: 10), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - item.modules.moduleDynamic!.major!.common!['title'], - style: TextStyle( - color: theme.colorScheme.primary, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - const SizedBox(height: 2), - Text( - item.modules.moduleDynamic!.major!.common!['desc'], - style: TextStyle( - color: theme.colorScheme.outline, - fontSize: theme.textTheme.labelMedium!.fontSize, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ], - ), - ), - ], - ), - ), - ); - case 'DYNAMIC_TYPE_MUSIC': - final Map music = item.modules.moduleDynamic!.major!.music!; - return InkWell( - onTap: () => PageUtils.handleWebview("https:${music['jump_url']}"), - child: Container( - width: double.infinity, - padding: - const EdgeInsets.only(left: 12, top: 10, right: 12, bottom: 10), - color: theme.dividerColor.withValues(alpha: 0.08), - child: Row( - children: [ - NetworkImgLayer( - radius: 8, - width: 45, - height: 45, - src: music['cover'], - ), - const SizedBox(width: 10), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - music['title'], - style: TextStyle( - color: theme.colorScheme.primary, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - const SizedBox(height: 2), - Text( - music['label'], - style: TextStyle( - color: theme.colorScheme.outline, - fontSize: theme.textTheme.labelMedium!.fontSize, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ], - ) - ], - ), - ), - ); - case 'DYNAMIC_TYPE_MEDIALIST': - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (floor == 2) ...[ - GestureDetector( - onTap: () => - Get.toNamed('/member?mid=${item.modules.moduleAuthor!.mid}'), - child: Row( - children: [ - NetworkImgLayer( - width: 28, - height: 28, - type: ImageType.avatar, - src: item.modules.moduleAuthor!.face, - ), - const SizedBox(width: 10), - Text( - item.modules.moduleAuthor!.name!, - style: TextStyle( - color: item.modules.moduleAuthor!.vip != null && - item.modules.moduleAuthor!.vip!.status > 0 && - item.modules.moduleAuthor!.vip!.type == 2 - ? context.vipColor - : theme.colorScheme.onSurface, - fontSize: theme.textTheme.titleMedium!.fontSize, - ), - ), - ], - ), - ), - const SizedBox(height: 10), - ], - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (floor == 1) const SizedBox(width: 12), - Stack( - clipBehavior: Clip.none, - children: [ - Hero( - tag: item.modules.moduleDynamic!.major!.medialist!['cover'], - child: NetworkImgLayer( - width: 180, - height: 110, - src: item - .modules.moduleDynamic!.major!.medialist!['cover'], - ), - ), - PBadge( - right: 6, - top: 6, - text: item.modules.moduleDynamic!.major!.medialist!['badge'] - ?['text'], - ) - ], - ), - const SizedBox(width: 14), - Expanded( - child: SizedBox( - height: 110, - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 4), - Text( - item.modules.moduleDynamic!.major!.medialist!['title'], - style: TextStyle( - fontSize: theme.textTheme.titleMedium!.fontSize, - fontWeight: FontWeight.bold), - ), - if (item.modules.moduleDynamic?.major - ?.medialist?['sub_title'] != - null) ...[ - const Spacer(), - Text( - item.modules.moduleDynamic!.major! - .medialist!['sub_title'], - style: TextStyle( - fontSize: theme.textTheme.labelLarge!.fontSize, - color: theme.colorScheme.outline), - ), - ], - ], - ), - ), - ), - if (floor == 1) const SizedBox(width: 12), - ], - ), - ], - ); - - case 'DYNAMIC_TYPE_SUBSCRIPTION_NEW' - when item.modules.moduleDynamic?.major?.type == - 'MAJOR_TYPE_SUBSCRIPTION_NEW': - return livePanelSub(theme, source, item, context, floor: floor); - default: - return Padding( - padding: floor == 1 - ? const EdgeInsets.symmetric(horizontal: 12) - : EdgeInsets.zero, - child: Text('暂未支持的类型: \n${item.idStr}\n${item.type}'), - ); - } -} diff --git a/lib/pages/dynamics/widgets/live_panel.dart b/lib/pages/dynamics/widgets/live_panel.dart index 14eebc63..5efebd03 100644 --- a/lib/pages/dynamics/widgets/live_panel.dart +++ b/lib/pages/dynamics/widgets/live_panel.dart @@ -1,8 +1,6 @@ import 'package:PiliPlus/common/widgets/image/image_save.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/models/dynamics/result.dart'; -import 'package:PiliPlus/pages/dynamics/widgets/rich_node_panel.dart'; -import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -17,53 +15,9 @@ Widget livePanel( if (content == null) { return const SizedBox.shrink(); } - late final authorStyle = TextStyle(color: theme.colorScheme.primary); - late TextSpan? richNodes = richNode(theme, item, context); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (floor == 2) ...[ - Row( - children: [ - GestureDetector( - onTap: () => Get.toNamed( - '/member?mid=${item.modules.moduleAuthor!.mid}', - arguments: {'face': item.modules.moduleAuthor!.face}), - child: Text( - '@${item.modules.moduleAuthor!.name}', - style: authorStyle, - ), - ), - const SizedBox(width: 6), - Text( - Utils.dateFormat(item.modules.moduleAuthor?.pubTs), - style: TextStyle( - color: theme.colorScheme.outline, - fontSize: theme.textTheme.labelSmall!.fontSize, - ), - ), - ], - ), - ], - const SizedBox(height: 4), - if (item.modules.moduleDynamic?.topic != null) ...[ - Padding( - padding: floor == 2 - ? EdgeInsets.zero - : const EdgeInsets.only(left: 12, right: 12), - child: Text( - '#${item.modules.moduleDynamic!.topic!.name}', - style: authorStyle, - ), - ), - const SizedBox(height: 6), - ], - if (floor == 2 && - item.modules.moduleDynamic?.desc != null && - richNodes != null) ...[ - Text.rich(richNodes), - const SizedBox(height: 6), - ], GestureDetector( behavior: HitTestBehavior.opaque, onTap: () => Get.toNamed('/liveRoom?roomid=${content.live?.id}'), diff --git a/lib/pages/dynamics/widgets/live_panel_sub.dart b/lib/pages/dynamics/widgets/live_panel_sub.dart index 7b62c337..c9a5e546 100644 --- a/lib/pages/dynamics/widgets/live_panel_sub.dart +++ b/lib/pages/dynamics/widgets/live_panel_sub.dart @@ -3,8 +3,6 @@ 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/rich_node_panel.dart'; -import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -20,62 +18,9 @@ Widget livePanelSub( if (subItem == null || content == null) { return const SizedBox.shrink(); } - late TextSpan? richNodes = richNode(theme, item, context); - late final authorStyle = TextStyle(color: theme.colorScheme.primary); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (floor == 2) ...[ - Row( - children: [ - GestureDetector( - onTap: () => Get.toNamed( - '/member?mid=${item.modules.moduleAuthor?.mid}', - arguments: {'face': item.modules.moduleAuthor?.face}, - ), - child: Text( - '@${item.modules.moduleAuthor?.name}', - style: authorStyle, - ), - ), - const SizedBox(width: 6), - Text( - Utils.dateFormat(item.modules.moduleAuthor?.pubTs), - style: TextStyle( - color: theme.colorScheme.outline, - fontSize: theme.textTheme.labelSmall!.fontSize, - ), - ), - ], - ), - ], - const SizedBox(height: 4), - if (item.modules.moduleDynamic?.topic != null) ...[ - GestureDetector( - onTap: () => Get.toNamed( - '/dynTopic', - parameters: { - 'id': item.modules.moduleDynamic!.topic!.id!.toString(), - 'name': item.modules.moduleDynamic!.topic!.name!, - }, - ), - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: StyleString.safeSpace), - child: Text( - '#${item.modules.moduleDynamic!.topic!.name}', - style: authorStyle, - ), - ), - ), - const SizedBox(height: 6), - ], - if (floor == 2 && - item.modules.moduleDynamic?.desc != null && - richNodes != null) ...[ - Text.rich(richNodes), - const SizedBox(height: 6), - ], Padding( padding: const EdgeInsets.symmetric(horizontal: StyleString.safeSpace), child: GestureDetector( diff --git a/lib/pages/dynamics/widgets/live_rcmd_panel.dart b/lib/pages/dynamics/widgets/live_rcmd_panel.dart index 6f88cfb4..987bb280 100644 --- a/lib/pages/dynamics/widgets/live_rcmd_panel.dart +++ b/lib/pages/dynamics/widgets/live_rcmd_panel.dart @@ -3,11 +3,8 @@ 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/rich_node_panel.dart'; import 'package:PiliPlus/utils/page_utils.dart'; -import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; -import 'package:get/get.dart'; Widget liveRcmdPanel( ThemeData theme, @@ -20,62 +17,9 @@ Widget liveRcmdPanel( if (liveRcmd == null) { return const SizedBox.shrink(); } - late TextSpan? richNodes = richNode(theme, item, context); - late final authorStyle = TextStyle(color: theme.colorScheme.primary); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (floor == 2) ...[ - Row( - children: [ - GestureDetector( - onTap: () => Get.toNamed( - '/member?mid=${item.modules.moduleAuthor?.mid}', - arguments: {'face': item.modules.moduleAuthor?.face}, - ), - child: Text( - '@${item.modules.moduleAuthor?.name}', - style: authorStyle, - ), - ), - const SizedBox(width: 6), - Text( - Utils.dateFormat(item.modules.moduleAuthor?.pubTs), - style: TextStyle( - color: theme.colorScheme.outline, - fontSize: theme.textTheme.labelSmall!.fontSize, - ), - ), - ], - ), - ], - const SizedBox(height: 4), - if (item.modules.moduleDynamic?.topic != null) ...[ - GestureDetector( - onTap: () => Get.toNamed( - '/dynTopic', - parameters: { - 'id': item.modules.moduleDynamic!.topic!.id!.toString(), - 'name': item.modules.moduleDynamic!.topic!.name!, - }, - ), - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: StyleString.safeSpace), - child: Text( - '#${item.modules.moduleDynamic!.topic!.name}', - style: authorStyle, - ), - ), - ), - const SizedBox(height: 6), - ], - if (floor == 2 && - item.modules.moduleDynamic?.desc != null && - richNodes != null) ...[ - Text.rich(richNodes), - const SizedBox(height: 6), - ], Padding( padding: const EdgeInsets.symmetric(horizontal: StyleString.safeSpace), child: GestureDetector( diff --git a/lib/pages/dynamics/widgets/module_panel.dart b/lib/pages/dynamics/widgets/module_panel.dart new file mode 100644 index 00000000..c2652943 --- /dev/null +++ b/lib/pages/dynamics/widgets/module_panel.dart @@ -0,0 +1,360 @@ +// 转发 +import 'package:PiliPlus/common/constants.dart'; +import 'package:PiliPlus/common/widgets/badge.dart'; +import 'package:PiliPlus/common/widgets/image/image_save.dart'; +import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; +import 'package:PiliPlus/models/dynamics/result.dart'; +import 'package:PiliPlus/pages/article/widgets/opus_content.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/live_panel.dart'; +import 'package:PiliPlus/pages/dynamics/widgets/live_panel_sub.dart'; +import 'package:PiliPlus/pages/dynamics/widgets/live_rcmd_panel.dart'; +import 'package:PiliPlus/pages/dynamics/widgets/video_panel.dart'; +import 'package:PiliPlus/utils/page_utils.dart'; +import 'package:PiliPlus/utils/utils.dart'; +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:get/get.dart'; + +Widget blockedItem(ThemeData theme, ModuleBlocked moduleBlocked) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 1), + child: LayoutBuilder( + builder: (context, constraints) { + return moduleBlockedItem(theme, moduleBlocked, constraints.maxWidth); + }, + ), + ); +} + +Widget module( + ThemeData theme, + bool isSave, + DynamicItemModel item, + BuildContext context, + String? source, + Function(List, int)? callback, { + floor = 1, +}) { + switch (item.type) { + // 图文 + case 'DYNAMIC_TYPE_DRAW': + // 文章 + case 'DYNAMIC_TYPE_ARTICLE': + case 'DYNAMIC_TYPE_WORD': + return const SizedBox.shrink(); + // 视频 + case 'DYNAMIC_TYPE_AV': + return videoSeasonWidget( + theme, isSave, source, item, context, 'archive', callback, + floor: floor); + // 转发 + case 'DYNAMIC_TYPE_FORWARD': + final orig = item.orig!; + final isNoneMajor = + orig.modules.moduleDynamic?.major?.type == 'MAJOR_TYPE_NONE'; + return InkWell( + onTap: + isNoneMajor ? null : () => PageUtils.pushDynDetail(orig, floor + 1), + onLongPress: isNoneMajor + ? null + : () { + late String? title, cover; + late var origMajor = orig.modules.moduleDynamic?.major; + late var major = item.modules.moduleDynamic?.major; + switch (orig.type) { + case 'DYNAMIC_TYPE_AV': + title = origMajor?.archive?.title; + cover = origMajor?.archive?.cover; + break; + case 'DYNAMIC_TYPE_UGC_SEASON': + title = origMajor?.ugcSeason?.title; + cover = origMajor?.ugcSeason?.cover; + break; + case 'DYNAMIC_TYPE_PGC' || 'DYNAMIC_TYPE_PGC_UNION': + title = origMajor?.pgc?.title; + cover = origMajor?.pgc?.cover; + break; + case 'DYNAMIC_TYPE_LIVE_RCMD': + title = major?.liveRcmd?.title; + cover = major?.liveRcmd?.cover; + break; + case 'DYNAMIC_TYPE_LIVE': + title = major?.live?.title; + cover = major?.live?.cover; + break; + default: + return; + } + imageSaveDialog( + title: title, + cover: cover, + ); + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8), + color: theme.dividerColor.withValues(alpha: 0.08), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + GestureDetector( + onTap: () => Get.toNamed( + '/member?mid=${orig.modules.moduleAuthor!.mid}', + arguments: {'face': orig.modules.moduleAuthor!.face}), + child: Text( + '@${orig.modules.moduleAuthor!.name}', + style: TextStyle(color: theme.colorScheme.primary), + ), + ), + const SizedBox(width: 6), + Text( + Utils.dateFormat(orig.modules.moduleAuthor!.pubTs), + style: TextStyle( + color: theme.colorScheme.outline, + fontSize: theme.textTheme.labelSmall!.fontSize), + ), + ], + ), + const SizedBox(height: 5), + content(theme, isSave, context, orig, source, callback, + floor: floor + 1), + module(theme, isSave, orig, context, source, callback, + floor: floor + 1), + if (orig.modules.moduleDynamic?.additional != null) + addWidget(theme, orig, context, floor: floor), + if (orig.modules.moduleDynamic?.major?.blocked != null) + blockedItem(theme, orig.modules.moduleDynamic!.major!.blocked!), + ], + ), + ), + ); + // 直播 + case 'DYNAMIC_TYPE_LIVE_RCMD': + return liveRcmdPanel(theme, source, item, context, floor: floor); + // 直播 + case 'DYNAMIC_TYPE_LIVE': + return livePanel(theme, source, item, context, floor: floor); + // 合集 + case 'DYNAMIC_TYPE_UGC_SEASON': + return videoSeasonWidget( + theme, isSave, source, item, context, 'ugcSeason', callback); + case 'DYNAMIC_TYPE_PGC': + return videoSeasonWidget( + theme, isSave, source, item, context, 'pgc', callback, + floor: floor); + case 'DYNAMIC_TYPE_PGC_UNION': + return videoSeasonWidget( + theme, isSave, source, item, context, 'pgc', callback, + floor: floor); + // 直播结束 + case 'DYNAMIC_TYPE_NONE': + return Row( + children: [ + const Icon( + FontAwesomeIcons.ghost, + size: 14, + ), + const SizedBox(width: 4), + Text(item.modules.moduleDynamic!.major!.none!.tips!) + ], + ); + // 课堂 + case 'DYNAMIC_TYPE_COURSES_SEASON': + return SizedBox( + width: double.infinity, + child: Padding( + padding: floor == 1 + ? const EdgeInsets.symmetric(horizontal: 12) + : EdgeInsets.zero, + child: Text( + "课堂:${item.modules.moduleDynamic!.major!.courses!['title']}", + ), + ), + ); + // 活动 + case 'DYNAMIC_TYPE_COMMON_SQUARE': + return Material( + color: floor == 1 + ? theme.dividerColor.withValues(alpha: 0.08) + : theme.colorScheme.surface, + shape: floor == 1 + ? null + : const RoundedRectangleBorder(borderRadius: StyleString.mdRadius), + child: InkWell( + borderRadius: floor == 1 ? null : StyleString.mdRadius, + onTap: () { + try { + String url = + item.modules.moduleDynamic!.major!.common!['jump_url']; + if (url.contains('bangumi/play') && + PageUtils.viewPgcFromUri(url)) { + return; + } + PageUtils.handleWebview(url, inApp: true); + } catch (_) {} + }, + child: Padding( + padding: + const EdgeInsets.only(left: 12, top: 10, right: 12, bottom: 10), + child: Row( + children: [ + NetworkImgLayer( + radius: 8, + width: 45, + height: 45, + src: item.modules.moduleDynamic!.major!.common!['cover'], + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.modules.moduleDynamic!.major!.common!['title'], + style: TextStyle( + color: theme.colorScheme.primary, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 2), + Text( + item.modules.moduleDynamic!.major!.common!['desc'], + style: TextStyle( + color: theme.colorScheme.outline, + fontSize: theme.textTheme.labelMedium!.fontSize, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + ], + ), + ), + ), + ); + case 'DYNAMIC_TYPE_MUSIC': + final Map music = item.modules.moduleDynamic!.major!.music!; + return InkWell( + onTap: () => PageUtils.handleWebview("https:${music['jump_url']}"), + child: Container( + width: double.infinity, + padding: + const EdgeInsets.only(left: 12, top: 10, right: 12, bottom: 10), + color: theme.dividerColor.withValues(alpha: 0.08), + child: Row( + children: [ + NetworkImgLayer( + radius: 8, + width: 45, + height: 45, + src: music['cover'], + ), + const SizedBox(width: 10), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + music['title'], + style: TextStyle( + color: theme.colorScheme.primary, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 2), + Text( + music['label'], + style: TextStyle( + color: theme.colorScheme.outline, + fontSize: theme.textTheme.labelMedium!.fontSize, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ) + ], + ), + ), + ); + case 'DYNAMIC_TYPE_MEDIALIST': + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (floor == 1) const SizedBox(width: 12), + Stack( + clipBehavior: Clip.none, + children: [ + Hero( + tag: item.modules.moduleDynamic!.major!.medialist!['cover'], + child: NetworkImgLayer( + width: 180, + height: 110, + src: item.modules.moduleDynamic!.major!.medialist!['cover'], + ), + ), + PBadge( + right: 6, + top: 6, + text: item.modules.moduleDynamic!.major!.medialist!['badge'] + ?['text'], + ) + ], + ), + const SizedBox(width: 14), + Expanded( + child: SizedBox( + height: 110, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 4), + Text( + item.modules.moduleDynamic!.major!.medialist!['title'], + style: TextStyle( + fontSize: theme.textTheme.titleMedium!.fontSize, + fontWeight: FontWeight.bold), + ), + if (item.modules.moduleDynamic?.major + ?.medialist?['sub_title'] != + null) ...[ + const Spacer(), + Text( + item.modules.moduleDynamic!.major! + .medialist!['sub_title'], + style: TextStyle( + fontSize: theme.textTheme.labelLarge!.fontSize, + color: theme.colorScheme.outline), + ), + ], + ], + ), + ), + ), + if (floor == 1) const SizedBox(width: 12), + ], + ); + + case 'DYNAMIC_TYPE_SUBSCRIPTION_NEW' + when item.modules.moduleDynamic?.major?.type == + 'MAJOR_TYPE_SUBSCRIPTION_NEW': + return livePanelSub(theme, source, item, context, floor: floor); + + default: + return Padding( + padding: floor == 1 + ? const EdgeInsets.symmetric(horizontal: 12) + : EdgeInsets.zero, + child: Text('暂未支持的类型: \n${item.idStr}\n${item.type}'), + ); + } +} diff --git a/lib/pages/dynamics/widgets/pic_panel.dart b/lib/pages/dynamics/widgets/pic_panel.dart deleted file mode 100644 index 529baf22..00000000 --- a/lib/pages/dynamics/widgets/pic_panel.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:PiliPlus/common/widgets/image/image_view.dart'; -import 'package:PiliPlus/models/dynamics/result.dart'; -import 'package:flutter/material.dart'; - -Widget picWidget( - DynamicItemModel item, - BuildContext context, - Function(List, int)? callback, -) { - if (item.modules.moduleDynamic?.major?.draw?.items == null || - item.modules.moduleDynamic?.major?.type == 'MAJOR_TYPE_OPUS') { - /// fix 图片跟rich_node_panel重复 - // pictures = item.modules.moduleDynamic.major.opus.pics; - return const SizedBox.shrink(); - } - return LayoutBuilder( - builder: (context, constraints) => imageView( - constraints.maxWidth, - (item.modules.moduleDynamic!.major!.draw!.items as List) - .map( - (item) => ImageModel( - width: item.width, - height: item.height, - url: item.url ?? '', - liveUrl: item.liveUrl, - ), - ) - .toList(), - callback: callback, - ), - ); -} diff --git a/lib/pages/dynamics/widgets/rich_node_panel.dart b/lib/pages/dynamics/widgets/rich_node_panel.dart index 2f95eada..682adc46 100644 --- a/lib/pages/dynamics/widgets/rich_node_panel.dart +++ b/lib/pages/dynamics/widgets/rich_node_panel.dart @@ -33,8 +33,7 @@ TextSpan? richNode( spanChildren.add( TextSpan( text: '${item.modules.moduleDynamic!.major!.opus!.title!}\n', - style: theme.textTheme.titleMedium! - .copyWith(fontWeight: FontWeight.bold), + style: const TextStyle(fontWeight: FontWeight.bold), ), ); } diff --git a/lib/pages/dynamics/widgets/video_panel.dart b/lib/pages/dynamics/widgets/video_panel.dart index 8c11c552..14e15831 100644 --- a/lib/pages/dynamics/widgets/video_panel.dart +++ b/lib/pages/dynamics/widgets/video_panel.dart @@ -4,12 +4,8 @@ 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'; import 'package:flutter/material.dart'; -import 'package:get/get.dart'; Widget videoSeasonWidget( ThemeData theme, @@ -61,8 +57,6 @@ Widget videoSeasonWidget( return const SizedBox.shrink(); } - TextSpan? richNodes = richNode(theme, item, context); - Widget buildCover() { return LayoutBuilder( builder: (context, box) { @@ -149,46 +143,13 @@ Widget videoSeasonWidget( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - if (floor == 2) ...[ - Row( - children: [ - GestureDetector( - onTap: () => Get.toNamed( - '/member?mid=${item.modules.moduleAuthor!.mid}', - arguments: {'face': item.modules.moduleAuthor!.face}, - ), - child: Text( - '@${item.modules.moduleAuthor!.name}', - style: TextStyle(color: theme.colorScheme.primary), - ), - ), - const SizedBox(width: 6), - if (item.modules.moduleAuthor?.pubTs != null) - Text( - Utils.dateFormat(item.modules.moduleAuthor!.pubTs), - style: TextStyle( - color: theme.colorScheme.outline, - fontSize: theme.textTheme.labelSmall!.fontSize, - ), - ), - ], - ), - const SizedBox(height: 6), - content(theme, isSave, context, item, source, null, floor: 2), - if (itemContent.desc != null && richNodes != null) ...[ - Text.rich(richNodes), - const SizedBox(height: 6), - ], - ], if (itemContent.cover != null) - if (item.isForwarded == true) - buildCover() - else - Padding( - padding: - const EdgeInsets.symmetric(horizontal: StyleString.safeSpace), - child: buildCover(), - ), + Padding( + padding: floor == 1 + ? const EdgeInsets.symmetric(horizontal: StyleString.safeSpace) + : EdgeInsets.zero, + child: buildCover(), + ), const SizedBox(height: 6), if (itemContent.title != null) Padding( @@ -202,14 +163,6 @@ 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, - ), ], ); }