// Author: fruit.com.cn import 'package:flutter/material.dart'; import '../../../core/data/models/drama.dart'; import '../../../core/formatting/count_format.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_dimens.dart'; import '../../../core/widgets/drama_genre_ribbon.dart'; import '../../../core/widgets/remote_image.dart'; import '../../../l10n/app_localizations.dart'; import 'home_drama_classification.dart'; final RegExp _tagSplitter = RegExp(r'[,/、·]'); enum HomeDramaBadge { hot, newRelease, anime } class HomeFeedCard extends StatelessWidget { const HomeFeedCard({ super.key, required this.drama, required this.onTap, this.now, }); final Drama drama; final VoidCallback onTap; final DateTime? now; @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; return Material( key: Key('home-feed-card-${drama.id}'), color: AppColors.transparent, child: InkWell( borderRadius: BorderRadius.circular(AppDimens.cardCoverRadius), onTap: onTap, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ _cover(l10n), Expanded( child: Padding( padding: const EdgeInsets.only( top: AppDimens.cardCoverTitleGap, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Text( drama.title, key: Key('home-feed-title-${drama.id}'), maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.left, style: const TextStyle( color: AppColors.navSelected, fontSize: AppDimens.cardTitleFont, fontWeight: FontWeight.w500, height: AppDimens.cardTitleLineHeight, ), ), ], ), ), ), ], ), ), ); } Widget _cover(AppLocalizations l10n) { final badge = _badge(); return ClipRRect( key: Key('home-feed-cover-clip-${drama.id}'), borderRadius: BorderRadius.circular(AppDimens.cardCoverRadius), child: AspectRatio( aspectRatio: AppDimens.cardCoverAspectRatio, child: Stack( fit: StackFit.expand, children: [ RemoteImage( drama.posterUrl, key: Key('home-feed-cover-${drama.id}'), fit: BoxFit.cover, showFallbackIcon: true, fallbackColor: AppColors.contentSurface, ), if (badge != null) Positioned( top: AppDimens.spaceXs, left: AppDimens.spaceXs, child: _badgeChip(l10n, badge), ), Positioned( top: 0, right: 0, child: DramaGenreRibbon(tags: _cardTags(), keyPrefix: drama.id), ), Positioned( left: AppDimens.spaceXs, bottom: AppDimens.spaceXs, child: _views(), ), ], ), ), ); } HomeDramaBadge? _badge() { if (drama.dramaType.toUpperCase() == 'ANIME') { return HomeDramaBadge.anime; } if (isNewDrama(drama, now: now)) return HomeDramaBadge.newRelease; if (isHotDrama(drama)) { return HomeDramaBadge.hot; } return null; } Widget _badgeChip(AppLocalizations l10n, HomeDramaBadge badge) { final (label, color) = switch (badge) { HomeDramaBadge.hot => (l10n.homeBadgeHot, AppColors.badgeHot), HomeDramaBadge.newRelease => (l10n.homeBadgeNew, AppColors.badgeNew), HomeDramaBadge.anime => (l10n.homeBadgeAnime, AppColors.badgeAnime), }; return Container( key: Key('home-feed-badge-${badge.name}-${drama.id}'), padding: const EdgeInsets.symmetric( horizontal: AppDimens.cardBadgeHorizontalPadding, vertical: AppDimens.cardBadgeVerticalPadding, ), decoration: BoxDecoration( color: color, borderRadius: BorderRadius.circular(AppDimens.cardBadgeRadius), ), child: Text( label, style: const TextStyle( color: AppColors.navSelected, fontSize: AppDimens.cardBadgeFont, fontWeight: FontWeight.w700, letterSpacing: 0, ), ), ); } Widget _views() { return Container( key: Key('home-feed-views-${drama.id}'), padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2), decoration: BoxDecoration( color: AppColors.playerOverlayPill, borderRadius: BorderRadius.circular(AppDimens.pillRadius), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon( Icons.auto_awesome_rounded, size: AppDimens.cardViewsIcon, color: AppColors.navSelected, ), const SizedBox(width: 1), Text( formatPlayCount(drama.viewCount), style: const TextStyle( color: AppColors.navSelected, fontSize: AppDimens.cardViewsFont, fontWeight: FontWeight.w600, letterSpacing: 0, ), ), ], ), ); } List _cardTags() { final seen = {}; return [drama.genre, drama.category] .expand((value) => value.split(_tagSplitter)) .map((value) => value.trim()) .where((value) => value.isNotEmpty && seen.add(value)) .take(3) .toList(growable: false); } }