feat(recommend): 剧单更多进入两列详情页

This commit is contained in:
Zhou Weidong committed 2026-10-23 03:18:24 +08:00
1 parent f4cf7bc2dd
commit 9b1afbdb36
4 files changed
+408 -43

No files matched your search

@@ -14,6 +14,7 @@ import '../../home/widgets/home_drama_classification.dart';
import 'recommend_section_card.dart';
const List<String> _costumeNames = ['古装', 'costume', 'kostum'];
const int _sectionPreviewCount = 10;
enum RecommendSectionKind { hits, newReleases, costume }
@@ -35,17 +36,14 @@ class RecommendSectionsView extends StatelessWidget {
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);
});
final hits = recommendSectionDramas(
kind: RecommendSectionKind.hits,
source: source,
);
final newest = recommendSectionDramas(
kind: RecommendSectionKind.newReleases,
source: source,
);
return AppPullToRefresh(
key: const Key('recommend-sections-refresh'),
onRefresh: () async {
@@ -62,7 +60,7 @@ class RecommendSectionsView extends StatelessWidget {
key: 'hits',
title: l10n.weeklyHitsTitle,
subtitle: l10n.weeklyHitsSubtitle,
dramas: hits.take(10).toList(),
dramas: hits.take(_sectionPreviewCount).toList(),
onMore: () => onMore(RecommendSectionKind.hits),
),
const SizedBox(height: AppDimens.spaceXl),
@@ -72,7 +70,7 @@ class RecommendSectionsView extends StatelessWidget {
key: 'new',
title: l10n.weeklyNewTitle,
subtitle: l10n.weeklyNewSubtitle,
dramas: newest.take(10).toList(),
dramas: newest.take(_sectionPreviewCount).toList(),
onMore: () => onMore(RecommendSectionKind.newReleases),
),
const SizedBox(height: AppDimens.spaceXl),
@@ -82,7 +80,11 @@ class RecommendSectionsView extends StatelessWidget {
key: 'costume',
title: l10n.costumeAwardsTitle,
subtitle: l10n.costumeAwardsSubtitle,
dramas: _costumeDramas(source),
dramas: recommendSectionDramas(
kind: RecommendSectionKind.costume,
source: source,
categories: categories,
).take(_sectionPreviewCount).toList(),
onMore: () => onMore(RecommendSectionKind.costume),
),
],
@@ -90,33 +92,6 @@ class RecommendSectionsView extends StatelessWidget {
);
}
List<Drama> _costumeDramas(List<Drama> 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, {
@@ -208,3 +183,54 @@ class RecommendSectionsView extends StatelessWidget {
);
}
}
/// 剧单分区的完整片单,横滑预览和更多页共用同一排序。
List<Drama> recommendSectionDramas({
required RecommendSectionKind kind,
required List<Drama> source,
CategoryController? categories,
}) {
switch (kind) {
case RecommendSectionKind.hits:
return [...source]..sort((a, b) => b.viewCount.compareTo(a.viewCount));
case RecommendSectionKind.newReleases:
return [...source]..sort(_newestFirst);
case RecommendSectionKind.costume:
return _costumeDramas(source, categories);
}
}
int _newestFirst(Drama leftDrama, Drama rightDrama) {
final left = publishedAtForDrama(leftDrama);
final right = publishedAtForDrama(rightDrama);
if (left == null && right == null) return rightDrama.id.compareTo(leftDrama.id);
if (left == null) return 1;
if (right == null) return -1;
return right.compareTo(left);
}
List<Drama> _costumeDramas(List<Drama> source, CategoryController? categories) {
final category = _costumeCategory(categories);
if (category != null) {
final fromCategory = categories!.dramasOf(category.id);
if (fromCategory.isNotEmpty) return fromCategory;
}
return source.where(_isCostumeDrama).toList(growable: false);
}
ContentCategory? _costumeCategory(CategoryController? categories) {
if (categories == null) return null;
for (final category in categories.categories) {
if (_containsCostume(category.name)) return category;
}
return null;
}
bool _isCostumeDrama(Drama drama) {
return _containsCostume(drama.category) || _containsCostume(drama.genre);
}
bool _containsCostume(String value) {
final normalized = value.toLowerCase();
return _costumeNames.any(normalized.contains);
}