// Author: fruit.com.cn import 'package:flutter/material.dart'; import '../../../core/data/models/drama.dart'; import '../../../core/data/models/watch_progress.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_dimens.dart'; import '../../../core/widgets/remote_image.dart'; import '../../../l10n/app_localizations.dart'; const double _railHeight = 96; const double _cardWidth = 286; const double _coverWidth = 64; const double _playSize = 34; /// One drama/record pair rendered by the continuation rail. typedef HomeContinueItem = ({Drama drama, WatchProgress record}); /// Home continuation cards for records that still have unwatched time. class HomeContinueWatching extends StatelessWidget { const HomeContinueWatching({ super.key, required this.items, required this.onPlay, }); final List items; final void Function(Drama drama, WatchProgress record) onPlay; @override Widget build(BuildContext context) { if (items.isEmpty) return const SizedBox.shrink(); return SizedBox( key: const Key('home-continue-watching'), height: _railHeight, child: ListView.separated( scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric( horizontal: AppDimens.pageHorizontal, ), itemCount: items.length, separatorBuilder: (_, _) => const SizedBox(width: AppDimens.spaceSm), itemBuilder: (context, index) { final item = items[index]; return _ContinueCard( item: item, onPlay: () => onPlay(item.drama, item.record), ); }, ), ); } } class _ContinueCard extends StatelessWidget { const _ContinueCard({required this.item, required this.onPlay}); final HomeContinueItem item; final VoidCallback onPlay; @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; return Container( width: _cardWidth, padding: const EdgeInsets.all(AppDimens.spaceSm), decoration: BoxDecoration( color: AppColors.card, borderRadius: BorderRadius.circular(AppDimens.cardRadius), ), child: Row( children: [ ClipRRect( borderRadius: BorderRadius.circular(AppDimens.innerRadius), child: RemoteImage( item.drama.posterUrl, width: _coverWidth, height: _railHeight - AppDimens.spaceLg, ), ), const SizedBox(width: AppDimens.spaceSm), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.drama.title, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( color: AppColors.onSurface, fontSize: AppDimens.previewCardTitle, fontWeight: FontWeight.w700, ), ), const SizedBox(height: AppDimens.spaceXs), Text( _progressText(l10n, item.drama, item.record), key: Key('home-continue-progress-${item.drama.id}'), maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( color: AppColors.muted, fontSize: AppDimens.cardMeta, ), ), ], ), ), const SizedBox(width: AppDimens.spaceSm), IconButton( key: Key('home-continue-play-${item.drama.id}'), onPressed: onPlay, style: IconButton.styleFrom( backgroundColor: AppColors.blue, foregroundColor: AppColors.onPrimary, fixedSize: const Size.square(_playSize), padding: EdgeInsets.zero, ), icon: const Icon( Icons.play_arrow_rounded, size: AppDimens.accountIcon, ), ), ], ), ); } String _progressText( AppLocalizations l10n, Drama drama, WatchProgress record, ) { final episode = drama.episodes.where( (value) => value.id == record.episodeId, ); final number = episode.isEmpty ? 1 : episode.first.number; final remaining = record.duration - record.position; final minutes = remaining.inSeconds <= 0 ? 0 : (remaining.inSeconds / Duration.secondsPerMinute).ceil(); return l10n.episodeRemaining(number, minutes); } }