// Author: fruit.com.cn import 'package:flutter/material.dart'; import '../../../core/data/models/content_category.dart'; import '../../../core/data/models/drama.dart'; import '../../../core/data/services/category_controller.dart'; import '../../../core/data/services/home_feed_controller.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_dimens.dart'; import '../../../core/widgets/app_pull_to_refresh.dart'; import '../../../l10n/app_localizations.dart'; import '../../home/widgets/home_drama_classification.dart'; import 'recommend_section_card.dart'; const List _costumeNames = ['古装', 'costume', 'kostum']; enum RecommendSectionKind { hits, newReleases, costume } class RecommendSectionsView extends StatelessWidget { const RecommendSectionsView({ super.key, required this.feedController, required this.categories, required this.onOpenDrama, required this.onMore, }); final HomeFeedController feedController; final CategoryController categories; final ValueChanged onOpenDrama; final ValueChanged onMore; @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; final source = feedController.dramas; final hits = [...source] ..sort((a, b) => b.viewCount.compareTo(a.viewCount)); final newest = [...source] ..sort((a, b) { final left = publishedAtForDrama(a); final right = publishedAtForDrama(b); if (left == null && right == null) return b.id.compareTo(a.id); if (left == null) return 1; if (right == null) return -1; return right.compareTo(left); }); return AppPullToRefresh( key: const Key('recommend-sections-refresh'), onRefresh: () async { await Future.wait([feedController.refreshCurrent(), categories.load()]); }, child: ListView( key: const Key('recommend-sections-scroll'), physics: AppPullToRefresh.physics, padding: const EdgeInsets.only(bottom: AppDimens.spaceXl), children: [ _section( context, l10n, key: 'hits', title: l10n.weeklyHitsTitle, subtitle: l10n.weeklyHitsSubtitle, dramas: hits.take(10).toList(), onMore: () => onMore(RecommendSectionKind.hits), ), const SizedBox(height: AppDimens.spaceXl), _section( context, l10n, key: 'new', title: l10n.weeklyNewTitle, subtitle: l10n.weeklyNewSubtitle, dramas: newest.take(10).toList(), onMore: () => onMore(RecommendSectionKind.newReleases), ), const SizedBox(height: AppDimens.spaceXl), _section( context, l10n, key: 'costume', title: l10n.costumeAwardsTitle, subtitle: l10n.costumeAwardsSubtitle, dramas: _costumeDramas(source), onMore: () => onMore(RecommendSectionKind.costume), ), ], ), ); } List _costumeDramas(List source) { final category = _costumeCategory(); if (category != null) { final fromCategory = categories.dramasOf(category.id); if (fromCategory.isNotEmpty) return fromCategory.take(10).toList(); } return source .where( (drama) => _containsCostume(drama.category) || _containsCostume(drama.genre), ) .take(10) .toList(); } ContentCategory? _costumeCategory() { for (final category in categories.categories) { if (_containsCostume(category.name)) return category; } return null; } bool _containsCostume(String value) { final normalized = value.toLowerCase(); return _costumeNames.any(normalized.contains); } Widget _section( BuildContext context, AppLocalizations l10n, { required String key, required String title, required String subtitle, required List dramas, required VoidCallback onMore, }) { return Column( key: Key('recommend-section-$key'), crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric( horizontal: AppDimens.feedPageHorizontal, ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: const TextStyle( color: AppColors.textPrimary, fontSize: AppDimens.recommendSectionTitle, fontWeight: FontWeight.w700, ), ), const SizedBox(height: 2), Text( subtitle, style: const TextStyle( color: AppColors.textSecondary, fontSize: AppDimens.recommendSectionSubtitle, ), ), ], ), ), TextButton( key: Key('recommend-more-$key'), onPressed: onMore, style: TextButton.styleFrom( foregroundColor: AppColors.textSecondary, padding: const EdgeInsets.symmetric(horizontal: 4), minimumSize: const Size(0, 30), ), child: Row( children: [ Text( l10n.more, style: const TextStyle( color: AppColors.textSecondary, fontSize: AppDimens.recommendSectionMore, fontWeight: FontWeight.w500, ), ), const Icon(Icons.chevron_right_rounded, size: 16), ], ), ), ], ), ), const SizedBox(height: AppDimens.spaceMd), SizedBox( height: AppDimens.recommendRowHeight, child: ListView.separated( key: Key('recommend-row-$key'), scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric( horizontal: AppDimens.feedPageHorizontal, ), itemCount: dramas.length, separatorBuilder: (_, _) => const SizedBox(width: AppDimens.homeGridGap), itemBuilder: (context, index) => RecommendSectionCard( sectionKey: key, drama: dramas[index], onTap: () => onOpenDrama(dramas[index]), ), ), ), ], ); } }