diff --git a/lib/common/widgets/overlay_pop.dart b/lib/common/widgets/overlay_pop.dart index fe9b9377..4f0a3899 100644 --- a/lib/common/widgets/overlay_pop.dart +++ b/lib/common/widgets/overlay_pop.dart @@ -62,6 +62,7 @@ class OverlayPop extends StatelessWidget { Expanded( child: Text( videoItem.title! as String, + style: Theme.of(context).textTheme.titleSmall, ), ), const SizedBox(width: 4), diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 0d96f7b7..9916aa7a 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:pilipala/utils/feed_back.dart'; import '../../models/model_rec_video_item.dart'; +import 'overlay_pop.dart'; import 'stat/danmu.dart'; import 'stat/view.dart'; import '../../http/dynamics.dart'; @@ -19,15 +21,11 @@ import 'network_img_layer.dart'; class VideoCardV extends StatelessWidget { final dynamic videoItem; final int crossAxisCount; - final Function()? longPress; - final Function()? longPressEnd; const VideoCardV({ Key? key, required this.videoItem, required this.crossAxisCount, - this.longPress, - this.longPressEnd, }) : super(key: key); bool isStringNumeric(String str) { @@ -127,64 +125,56 @@ class VideoCardV extends StatelessWidget { @override Widget build(BuildContext context) { String heroTag = Utils.makeHeroTag(videoItem.id); - return Card( - elevation: 0, - clipBehavior: Clip.hardEdge, - margin: EdgeInsets.zero, - child: GestureDetector( - onLongPress: () { - if (longPress != null) { - longPress!(); - } - }, - // onLongPressEnd: (details) { - // if (longPressEnd != null) { - // longPressEnd!(); - // } - // }, - child: InkWell( - onTap: () async => onPushDetail(heroTag), - child: Column( - children: [ - AspectRatio( - aspectRatio: StyleString.aspectRatio, - child: LayoutBuilder(builder: (context, boxConstraints) { - double maxWidth = boxConstraints.maxWidth; - double maxHeight = boxConstraints.maxHeight; - return Stack( - children: [ - Hero( - tag: heroTag, - child: NetworkImgLayer( - src: videoItem.pic, - width: maxWidth, - height: maxHeight, - ), - ), - if (videoItem.duration > 0) - if (crossAxisCount == 1) ...[ - PBadge( - bottom: 10, - right: 10, - text: Utils.timeFormat(videoItem.duration), - ) - ] else ...[ - PBadge( - bottom: 6, - right: 7, - size: 'small', - type: 'gray', - text: Utils.timeFormat(videoItem.duration), - ) - ], - ], - ); - }), - ), - VideoContent(videoItem: videoItem, crossAxisCount: crossAxisCount) - ], + return InkWell( + onTap: () async => onPushDetail(heroTag), + onLongPress: () { + SmartDialog.show( + builder: (context) => OverlayPop( + videoItem: videoItem, + closeFn: () => SmartDialog.dismiss(), ), - ), + ); + }, + borderRadius: BorderRadius.circular(16), + child: Column( + children: [ + AspectRatio( + aspectRatio: StyleString.aspectRatio, + child: LayoutBuilder(builder: (context, boxConstraints) { + double maxWidth = boxConstraints.maxWidth; + double maxHeight = boxConstraints.maxHeight; + return Stack( + children: [ + Hero( + tag: heroTag, + child: NetworkImgLayer( + src: videoItem.pic, + width: maxWidth, + height: maxHeight, + ), + ), + if (videoItem.duration > 0) + if (crossAxisCount == 1) ...[ + PBadge( + bottom: 10, + right: 10, + text: Utils.timeFormat(videoItem.duration), + ) + ] else ...[ + PBadge( + bottom: 6, + right: 7, + size: 'small', + type: 'gray', + text: Utils.timeFormat(videoItem.duration), + ) + ], + ], + ); + }), + ), + VideoContent(videoItem: videoItem, crossAxisCount: crossAxisCount) + ], ), ); } @@ -196,122 +186,90 @@ class VideoContent extends StatelessWidget { const VideoContent( {Key? key, required this.videoItem, required this.crossAxisCount}) : super(key: key); + + Widget _buildBadge(String text, String type, [double fs = 12]) { + return PBadge( + text: text, + stack: 'normal', + size: 'small', + type: type, + fs: fs, + ); + } + @override Widget build(BuildContext context) { - return Expanded( - flex: crossAxisCount == 1 ? 0 : 1, - child: Padding( - padding: crossAxisCount == 1 - ? const EdgeInsets.fromLTRB(9, 9, 9, 4) - : const EdgeInsets.fromLTRB(5, 8, 5, 4), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Expanded( - child: Text( - videoItem.title, - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - ), - if (videoItem.goto == 'av' && crossAxisCount == 1) ...[ - const SizedBox(width: 10), - VideoPopupMenu( - size: 32, - iconSize: 18, - videoItem: videoItem, - ), - ], - ], - ), - if (crossAxisCount > 1) ...[ - const SizedBox(height: 2), - VideoStat( - videoItem: videoItem, - crossAxisCount: crossAxisCount, - ), - ], - if (crossAxisCount == 1) const SizedBox(height: 4), - Row( - children: [ - if (videoItem.goto == 'bangumi') ...[ - PBadge( - text: videoItem.bangumiBadge, - stack: 'normal', - size: 'small', - type: 'line', - fs: 9, - ) - ], - if (videoItem.rcmdReason != null && - videoItem.rcmdReason.content != '') ...[ - PBadge( - text: videoItem.rcmdReason.content, - stack: 'normal', - size: 'small', - type: 'color', - ) - ], - if (videoItem.goto == 'picture') ...[ - const PBadge( - text: '动态', - stack: 'normal', - size: 'small', - type: 'line', - fs: 9, - ) - ], - if (videoItem.isFollowed == 1) ...[ - const PBadge( - text: '已关注', - stack: 'normal', - size: 'small', - type: 'color', - ) - ], - Expanded( - flex: crossAxisCount == 1 ? 0 : 1, - child: Text( - videoItem.owner.name, - maxLines: 1, - style: TextStyle( - fontSize: - Theme.of(context).textTheme.labelMedium!.fontSize, - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - if (crossAxisCount == 1) ...[ - Text( - ' • ', - style: TextStyle( - fontSize: - Theme.of(context).textTheme.labelMedium!.fontSize, - color: Theme.of(context).colorScheme.outline, - ), - ), - VideoStat( - videoItem: videoItem, - crossAxisCount: crossAxisCount, - ), - const Spacer(), - ], - if (videoItem.goto == 'av' && crossAxisCount != 1) ...[ - VideoPopupMenu( - size: 24, - iconSize: 14, - videoItem: videoItem, - ), - ] else ...[ - const SizedBox(height: 24) - ] - ], - ), + return Padding( + padding: crossAxisCount == 1 + ? const EdgeInsets.fromLTRB(9, 9, 9, 4) + : const EdgeInsets.fromLTRB(5, 8, 5, 4), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + videoItem.title, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + if (crossAxisCount > 1) ...[ + const SizedBox(height: 2), + VideoStat(videoItem: videoItem, crossAxisCount: crossAxisCount), ], - ), + if (crossAxisCount == 1) const SizedBox(height: 4), + Row( + children: [ + if (videoItem.goto == 'bangumi') + _buildBadge(videoItem.bangumiBadge, 'line', 9), + if (videoItem.rcmdReason?.content != null && + videoItem.rcmdReason.content != '') + _buildBadge(videoItem.rcmdReason.content, 'color'), + if (videoItem.goto == 'picture') _buildBadge('动态', 'line', 9), + if (videoItem.isFollowed == 1) _buildBadge('已关注', 'color'), + Expanded( + flex: crossAxisCount == 1 ? 0 : 1, + child: Text( + videoItem.owner.name, + maxLines: 1, + style: TextStyle( + fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, + color: Theme.of(context).colorScheme.outline, + ), + ), + ), + if (crossAxisCount == 1) ...[ + const SizedBox(width: 10), + VideoStat( + videoItem: videoItem, + crossAxisCount: crossAxisCount, + ), + const Spacer(), + ], + if (videoItem.goto == 'av') + SizedBox( + width: 24, + height: 24, + child: IconButton( + onPressed: () { + feedBack(); + showModalBottomSheet( + context: context, + useRootNavigator: true, + isScrollControlled: true, + builder: (context) { + return MorePanel(videoItem: videoItem); + }, + ); + }, + icon: Icon( + Icons.more_vert_outlined, + color: Theme.of(context).colorScheme.outline, + size: 14, + ), + ), + ) + ], + ), + ], ), ); } @@ -331,15 +289,9 @@ class VideoStat extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - StatView( - theme: 'gray', - view: videoItem.stat.view, - ), + StatView(theme: 'gray', view: videoItem.stat.view), const SizedBox(width: 8), - StatDanMu( - theme: 'gray', - danmu: videoItem.stat.danmu, - ), + StatDanMu(theme: 'gray', danmu: videoItem.stat.danmu), if (videoItem is RecVideoItemModel) ...[ crossAxisCount > 1 ? const Spacer() : const SizedBox(width: 8), RichText( @@ -358,99 +310,98 @@ class VideoStat extends StatelessWidget { } } -class VideoPopupMenu extends StatelessWidget { - final double? size; - final double? iconSize; +class MorePanel extends StatelessWidget { final dynamic videoItem; + const MorePanel({super.key, required this.videoItem}); - const VideoPopupMenu({ - Key? key, - required this.size, - required this.iconSize, - required this.videoItem, - }) : super(key: key); + Future menuActionHandler(String type) async { + switch (type) { + case 'block': + blockUser(); + break; + case 'watchLater': + var res = await UserHttp.toViewLater(bvid: videoItem.bvid as String); + SmartDialog.showToast(res['msg']); + Get.back(); + break; + default: + } + } + + void blockUser() async { + SmartDialog.show( + useSystem: true, + animationType: SmartAnimationType.centerFade_otherSlide, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('提示'), + content: Text('确定拉黑:${videoItem.owner.name}(${videoItem.owner.mid})?' + '\n\n注:被拉黑的Up可以在隐私设置-黑名单管理中解除'), + actions: [ + TextButton( + onPressed: () => SmartDialog.dismiss(), + child: Text( + '点错了', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + var res = await VideoHttp.relationMod( + mid: videoItem.owner.mid, + act: 5, + reSrc: 11, + ); + SmartDialog.dismiss(); + SmartDialog.showToast(res['msg'] ?? '成功'); + }, + child: const Text('确认'), + ) + ], + ); + }, + ); + } @override Widget build(BuildContext context) { - return SizedBox( - width: size, - height: size, - child: PopupMenuButton( - padding: EdgeInsets.zero, - icon: Icon( - Icons.more_vert_outlined, - color: Theme.of(context).colorScheme.outline, - size: iconSize, - ), - position: PopupMenuPosition.under, - // constraints: const BoxConstraints(maxHeight: 35), - onSelected: (String type) {}, - itemBuilder: (BuildContext context) => >[ - PopupMenuItem( - onTap: () async { - var res = - await UserHttp.toViewLater(bvid: videoItem.bvid as String); - SmartDialog.showToast(res['msg']); - }, - value: 'pause', - height: 40, - child: const Row( - children: [ - Icon(Icons.watch_later_outlined, size: 16), - SizedBox(width: 6), - Text('稍后再看', style: TextStyle(fontSize: 13)) - ], + return Container( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + InkWell( + onTap: () => Get.back(), + child: Container( + height: 35, + padding: const EdgeInsets.only(bottom: 2), + child: Center( + child: Container( + width: 32, + height: 3, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.outline, + borderRadius: const BorderRadius.all(Radius.circular(3))), + ), + ), ), ), - const PopupMenuDivider(), - PopupMenuItem( - onTap: () async { - SmartDialog.show( - useSystem: true, - animationType: SmartAnimationType.centerFade_otherSlide, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('提示'), - content: Text( - '确定拉黑:${videoItem.owner.name}(${videoItem.owner.mid})?' - '\n\n注:被拉黑的Up可以在隐私设置-黑名单管理中解除'), - actions: [ - TextButton( - onPressed: () => SmartDialog.dismiss(), - child: Text( - '点错了', - style: TextStyle( - color: Theme.of(context).colorScheme.outline), - ), - ), - TextButton( - onPressed: () async { - var res = await VideoHttp.relationMod( - mid: videoItem.owner.mid, - act: 5, - reSrc: 11, - ); - SmartDialog.dismiss(); - SmartDialog.showToast(res['msg'] ?? '成功'); - }, - child: const Text('确认'), - ) - ], - ); - }, - ); - }, - value: 'pause', - height: 40, - child: Row( - children: [ - const Icon(Icons.block, size: 16), - const SizedBox(width: 6), - Text('拉黑:${videoItem.owner.name}', - style: const TextStyle(fontSize: 13)) - ], + ListTile( + onTap: () async => await menuActionHandler('block'), + minLeadingWidth: 0, + leading: const Icon(Icons.block, size: 19), + title: Text( + '拉黑up主 「${videoItem.owner.name}」', + style: Theme.of(context).textTheme.titleSmall, ), ), + ListTile( + onTap: () async => await menuActionHandler('watchLater'), + minLeadingWidth: 0, + leading: const Icon(Icons.watch_later_outlined, size: 19), + title: + Text('添加至稍后再看', style: Theme.of(context).textTheme.titleSmall), + ), ], ), ); diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index cc228f6b..a25389bd 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -214,6 +214,34 @@ class UserInfoWidget extends StatelessWidget { final VoidCallback? callback; final HomeController? ctr; + Widget buildLoggedInWidget(context) { + return Stack( + children: [ + NetworkImgLayer( + type: 'avatar', + width: 34, + height: 34, + src: userFace, + ), + Positioned.fill( + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: () => callback?.call(), + splashColor: Theme.of(context) + .colorScheme + .primaryContainer + .withOpacity(0.3), + borderRadius: const BorderRadius.all( + Radius.circular(50), + ), + ), + ), + ) + ], + ); + } + @override Widget build(BuildContext context) { return Row( @@ -231,31 +259,7 @@ class UserInfoWidget extends StatelessWidget { const SizedBox(width: 8), Obx( () => userLogin.value - ? Stack( - children: [ - NetworkImgLayer( - type: 'avatar', - width: 34, - height: 34, - src: userFace, - ), - Positioned.fill( - child: Material( - color: Colors.transparent, - child: InkWell( - onTap: () => callback?.call(), - splashColor: Theme.of(context) - .colorScheme - .primaryContainer - .withOpacity(0.3), - borderRadius: const BorderRadius.all( - Radius.circular(50), - ), - ), - ), - ) - ], - ) + ? buildLoggedInWidget(context) : DefaultUser(callback: () => callback!()), ), ], @@ -402,30 +406,27 @@ class SearchBar extends StatelessWidget { color: colorScheme.onSecondaryContainer.withOpacity(0.05), child: InkWell( splashColor: colorScheme.primaryContainer.withOpacity(0.3), - onTap: () => Get.toNamed( - '/search', - parameters: {'hintText': ctr!.defaultSearch.value}, - ), - child: Row( - children: [ - const SizedBox(width: 14), - Icon( - Icons.search_outlined, - color: colorScheme.onSecondaryContainer, - ), - const SizedBox(width: 10), - Obx( - () => Expanded( - child: Text( + onTap: () => Get.toNamed('/search', + parameters: {'hintText': ctr!.defaultSearch.value}), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 14), + child: Row( + children: [ + Icon( + Icons.search_outlined, + color: colorScheme.onSecondaryContainer, + ), + const SizedBox(width: 10), + Obx( + () => Text( ctr!.defaultSearch.value, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle(color: colorScheme.outline), ), ), - ), - const SizedBox(width: 15), - ], + ], + ), ), ), ), diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart index acc1e654..67567870 100644 --- a/lib/pages/rcmd/view.dart +++ b/lib/pages/rcmd/view.dart @@ -5,9 +5,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/skeleton/video_card_v.dart'; -import 'package:pilipala/common/widgets/animated_dialog.dart'; import 'package:pilipala/common/widgets/http_error.dart'; -import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/common/widgets/video_card_v.dart'; import 'package:pilipala/utils/main_stream.dart'; @@ -118,16 +116,6 @@ class _RcmdPageState extends State ); } - OverlayEntry _createPopupDialog(videoItem) { - return OverlayEntry( - builder: (context) => AnimatedDialog( - closeFn: _rcmdController.popupDialog?.remove, - child: OverlayPop( - videoItem: videoItem, closeFn: _rcmdController.popupDialog?.remove), - ), - ); - } - Widget contentGrid(ctr, videoList) { // double maxWidth = Get.size.width; // int baseWidth = 500; @@ -158,14 +146,6 @@ class _RcmdPageState extends State ? VideoCardV( videoItem: videoList[index], crossAxisCount: crossAxisCount, - longPress: () { - _rcmdController.popupDialog = - _createPopupDialog(videoList[index]); - Overlay.of(context).insert(_rcmdController.popupDialog!); - }, - longPressEnd: () { - _rcmdController.popupDialog?.remove(); - }, ) : const VideoCardVSkeleton(); }, diff --git a/lib/pages/video/detail/introduction/widgets/intro_detail.dart b/lib/pages/video/detail/introduction/widgets/intro_detail.dart index 1e9bb842..3ec92023 100644 --- a/lib/pages/video/detail/introduction/widgets/intro_detail.dart +++ b/lib/pages/video/detail/introduction/widgets/intro_detail.dart @@ -23,16 +23,34 @@ class IntroDetail extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 4), - GestureDetector( - onTap: () { - Clipboard.setData(ClipboardData(text: videoDetail!.bvid!)); - SmartDialog.showToast('已复制'); - }, - child: Text( - videoDetail!.bvid!, - style: TextStyle( - fontSize: 13, color: Theme.of(context).colorScheme.primary), - ), + Row( + children: [ + GestureDetector( + onTap: () { + Clipboard.setData(ClipboardData(text: videoDetail!.bvid!)); + SmartDialog.showToast('已复制'); + }, + child: Text( + videoDetail!.bvid!, + style: TextStyle( + fontSize: 13, + color: Theme.of(context).colorScheme.primary), + ), + ), + const SizedBox(width: 10), + GestureDetector( + onTap: () { + Clipboard.setData(ClipboardData(text: videoDetail!.bvid!)); + SmartDialog.showToast('已复制'); + }, + child: Text( + videoDetail!.aid!.toString(), + style: TextStyle( + fontSize: 13, + color: Theme.of(context).colorScheme.primary), + ), + ) + ], ), const SizedBox(height: 4), Text.rich(