// Author: fruit.com.cn import 'package:flutter/material.dart'; import '../../../core/data/models/drama.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_dimens.dart'; import '../../../core/widgets/cover_play_count.dart'; import '../../../core/widgets/drama_cover_badge_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, ), ), const SizedBox(height: AppDimens.cardTitleTagGap), _tags(), ], ), ), ), ], ), ), ); } 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: 0, right: 0, child: DramaCoverBadgeRibbon( label: _badgeLabel(l10n, badge), color: _badgeColor(badge), keyPrefix: '${drama.id}-${badge.name}', ), ), const Positioned( left: 0, right: 0, bottom: 0, child: CoverBottomScrim(), ), Positioned( right: AppDimens.cardPlayCountRight, bottom: AppDimens.cardPlayCountBottom, child: CoverPlayCount( key: Key('home-feed-views-${drama.id}'), count: drama.viewCount, ), ), ], ), ), ); } 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; } String _badgeLabel(AppLocalizations l10n, HomeDramaBadge badge) { return switch (badge) { HomeDramaBadge.hot => l10n.homeBadgeHot, HomeDramaBadge.newRelease => l10n.homeBadgeNew, HomeDramaBadge.anime => l10n.homeBadgeAnime, }; } Color _badgeColor(HomeDramaBadge badge) { return switch (badge) { HomeDramaBadge.hot => AppColors.badgeHot, HomeDramaBadge.newRelease => AppColors.badgeNew, HomeDramaBadge.anime => AppColors.badgeAnime, }; } Widget _tags() { final tags = _cardTags(); if (tags.isEmpty) return const SizedBox.shrink(); return Text( tags.join(' | '), key: Key('home-feed-tags-${drama.id}'), maxLines: 1, overflow: TextOverflow.ellipsis, textAlign: TextAlign.left, style: const TextStyle( color: AppColors.textTertiary, fontSize: AppDimens.cardTagFont, 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); } }