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/common/widgets/progress_bar/video_progress_indicator.dart'; import 'package:PiliPlus/http/search.dart'; import 'package:PiliPlus/http/user.dart'; import 'package:PiliPlus/models/common/badge_type.dart'; import 'package:PiliPlus/models/common/history_business_type.dart'; import 'package:PiliPlus/models_new/history/list.dart'; import 'package:PiliPlus/pages/common/multi_select_controller.dart'; import 'package:PiliPlus/pages/history/base_controller.dart'; import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; class HistoryItem extends StatelessWidget { final HistoryItemModel item; final dynamic ctr; final Function? onChoose; final Function(dynamic kid, dynamic business) onDelete; const HistoryItem({ super.key, required this.item, this.ctr, this.onChoose, required this.onDelete, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); int aid = item.history.oid!; String bvid = item.history.bvid ?? IdUtils.av2bv(aid); return Material( type: MaterialType.transparency, child: InkWell( onTap: () async { if (ctr is MultiSelectController || ctr is HistoryBaseController) { if (ctr.enableMultiSelect.value) { onChoose?.call(); return; } } if (item.history.business?.contains('article') == true) { PageUtils.toDupNamed( '/articlePage', parameters: { 'id': item.history.business == 'article-list' ? '${item.history.cid}' : '${item.history.oid}', 'type': 'read', }, ); } else if (item.history.business == 'live') { if (item.liveStatus == 1) { Get.toNamed('/liveRoom?roomid=${item.history.oid}'); } else { SmartDialog.showToast('直播未开播'); } } else if (item.history.business == 'pgc') { PageUtils.viewPgc(epId: item.history.epid); } else { int? cid = item.history.cid ?? await SearchHttp.ab2c( aid: aid, bvid: bvid, part: item.history.page, ); if (cid != null) { PageUtils.toVideoPage( 'bvid=$bvid&cid=$cid', arguments: { 'heroTag': Utils.makeHeroTag(aid), 'pic': item.cover, }, ); } } }, onLongPress: () { if (ctr is MultiSelectController || ctr is HistoryBaseController) { if (!ctr.enableMultiSelect.value) { ctr.enableMultiSelect.value = true; onChoose?.call(); } return; } imageSaveDialog( title: item.title, cover: item.cover, bvid: bvid, ); }, child: Stack( clipBehavior: Clip.none, children: [ Padding( padding: const EdgeInsets.symmetric( horizontal: StyleString.safeSpace, vertical: 5, ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ AspectRatio( aspectRatio: StyleString.aspectRatio, child: LayoutBuilder( builder: (context, boxConstraints) { double maxWidth = boxConstraints.maxWidth; double maxHeight = boxConstraints.maxHeight; return Stack( clipBehavior: Clip.none, children: [ NetworkImgLayer( src: item.cover?.isNotEmpty == true ? item.cover : item.covers?.firstOrNull ?? '', width: maxWidth, height: maxHeight, ), if (!HistoryBusinessType.hiddenDurationType .contains(item.history.business)) PBadge( text: item.progress == -1 ? '已看完' : '${DurationUtil.formatDuration(item.progress)}/${DurationUtil.formatDuration(item.duration)}', right: 6.0, bottom: 8.0, type: PBadgeType.gray, ), // 右上角 if (HistoryBusinessType.showBadge .contains(item.history.business) || item.history.business == HistoryBusinessType.live.type) PBadge( text: item.badge, top: 6.0, right: 6.0, type: item.history.business == HistoryBusinessType.live.type && item.liveStatus != 1 ? PBadgeType.gray : PBadgeType.primary, ), if (item.duration != null && item.duration != 0 && item.progress != null && item.progress != 0) Positioned( left: 0, right: 0, bottom: 0, child: videoProgressIndicator( item.progress == -1 ? 1 : item.progress! / item.duration!, ), ), Positioned.fill( child: AnimatedOpacity( opacity: item.checked == true ? 1 : 0, duration: const Duration(milliseconds: 200), child: Container( alignment: Alignment.center, decoration: BoxDecoration( borderRadius: StyleString.mdRadius, color: Colors.black.withValues(alpha: 0.6), ), child: SizedBox( width: 34, height: 34, child: AnimatedScale( scale: item.checked == true ? 1 : 0, duration: const Duration(milliseconds: 250), curve: Curves.easeInOut, child: IconButton( tooltip: '取消选择', style: ButtonStyle( padding: WidgetStateProperty.all( EdgeInsets.zero), backgroundColor: WidgetStateProperty.resolveWith( (states) { return theme.colorScheme.surface .withValues(alpha: 0.8); }, ), ), onPressed: () { feedBack(); onChoose?.call(); }, icon: Icon(Icons.done_all_outlined, color: theme.colorScheme.primary), ), ), ), ), ), ), ], ); }, ), ), const SizedBox(width: 10), content(theme), ], ), ), Positioned( right: 12, bottom: 0, child: SizedBox( width: 29, height: 29, child: PopupMenuButton( padding: EdgeInsets.zero, tooltip: '功能菜单', icon: Icon( Icons.more_vert_outlined, color: theme.colorScheme.outline, size: 18, ), position: PopupMenuPosition.under, itemBuilder: (BuildContext context) => >[ if (item.authorMid != null && item.authorName?.isNotEmpty == true) PopupMenuItem( onTap: () => Get.toNamed('/member?mid=${item.authorMid}'), height: 35, child: Row( children: [ const Icon(MdiIcons.accountCircleOutline, size: 16), const SizedBox(width: 6), Text( '访问:${item.authorName}', style: const TextStyle(fontSize: 13), ) ], ), ), if (item.history.business != 'pgc' && item.badge != '番剧' && item.tagName?.contains('动画') != true && item.history.business != 'live' && item.history.business?.contains('article') != true) PopupMenuItem( onTap: () async { var res = await UserHttp.toViewLater( bvid: item.history.bvid); SmartDialog.showToast(res['msg']); }, height: 35, child: const Row( children: [ Icon(Icons.watch_later_outlined, size: 16), SizedBox(width: 6), Text('稍后再看', style: TextStyle(fontSize: 13)) ], ), ), PopupMenuItem( onTap: () => onDelete(item.kid, item.history.business), height: 35, child: const Row( children: [ Icon(Icons.close_outlined, size: 16), SizedBox(width: 6), Text('删除记录', style: TextStyle(fontSize: 13)) ], ), ), ], ), ), ), ], ), ), ); } Widget content(ThemeData theme) { return Expanded( child: Column( spacing: 2, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.title!, style: TextStyle( fontSize: theme.textTheme.bodyMedium!.fontSize, height: 1.42, letterSpacing: 0.3, ), maxLines: item.videos! > 1 ? 1 : 2, overflow: TextOverflow.ellipsis, ), if (item.history.business == 'pgc' && item.showTitle?.isNotEmpty == true) Text( item.showTitle!, style: TextStyle( fontSize: 13, color: theme.colorScheme.outline, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), const Spacer(), if (item.authorName?.isNotEmpty == true) Text( item.authorName!, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: theme.textTheme.labelMedium!.fontSize, color: theme.colorScheme.outline, ), ), Text( DateUtil.chatFormat(item.viewAt!, isHistory: true), style: TextStyle( fontSize: theme.textTheme.labelMedium!.fontSize, color: theme.colorScheme.outline, ), ), ], ), ); } }