Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions lib/pages/collect/collect_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:kazumi/modules/bangumi/bangumi_item.dart';
import 'package:kazumi/modules/collect/collect_module.dart';
import 'package:kazumi/modules/collect/collect_change_module.dart';
import 'package:kazumi/modules/collect/collect_type.dart';
import 'package:kazumi/request/bangumi.dart';
import 'package:kazumi/utils/storage.dart';
import 'package:kazumi/utils/webdav.dart';
import 'package:kazumi/repositories/collect_crud_repository.dart';
Expand All @@ -20,6 +21,21 @@ abstract class _CollectController with Store {
final _collectCrudRepository = Modular.get<ICollectCrudRepository>();
final _collectRepository = Modular.get<ICollectRepository>();

// 时间表缓存:key=番剧ID, value=星期(0-6)
@observable
ObservableMap<int, int> _watchingCalendarWeekdayById = ObservableMap<int, int>();

// 构造函数:初始化时从缓存加载时间表数据
_CollectController() {
_loadCachedCalendar();
}

@observable
bool isWatchingCalendarLoading = false;

@observable
bool isWatchingCalendarReady = false;

Box setting = GStorage.setting;
List<BangumiItem> get favorites => _collectCrudRepository.getFavorites();

Expand All @@ -32,6 +48,86 @@ abstract class _CollectController with Store {
collectibles.addAll(_collectCrudRepository.getAllCollectibles());
}

/// 从缓存加载时间表数据
void _loadCachedCalendar() {
try {
final cachedData = setting.get(SettingBoxKey.cachedWatchingCalendar);
if (cachedData != null && cachedData is Map) {
_watchingCalendarWeekdayById = ObservableMap<int, int>.of(
Map<int, int>.from(cachedData),
);
if (_watchingCalendarWeekdayById.isNotEmpty) {
isWatchingCalendarReady = true;
KazumiLogger().d('Loaded cached watching calendar with ${_watchingCalendarWeekdayById.length} items');
}
}
} catch (e) {
KazumiLogger().e('Failed to load cached calendar', error: e);
}
}

/// 缓存时间表数据到本地
Future<void> _saveCachedCalendar() async {
try {
await setting.put(
SettingBoxKey.cachedWatchingCalendar,
Map<int, int>.from(_watchingCalendarWeekdayById),
);
KazumiLogger().d('Saved watching calendar cache with ${_watchingCalendarWeekdayById.length} items');
} catch (e) {
KazumiLogger().e('Failed to save calendar cache', error: e);
}
}

@action
Future<void> loadWatchingCalendar() async {
if (isWatchingCalendarLoading) {
return;
}
Comment on lines +82 to +86
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loadWatchingCalendar() is introduced but not called anywhere in the codebase (no references outside this controller). As a result, cachedWatchingCalendar will never be populated/updated and the “在看” weekday grouping will only use the airWeekday fallback. Consider triggering this refresh from CollectPage.initState() (or when entering the “在看” tab) so the calendar mapping and cache are actually used.

Copilot uses AI. Check for mistakes.
isWatchingCalendarLoading = true;
try {
final calendar = await BangumiHTTP.getCalendar();
if (calendar.isEmpty) {
KazumiLogger().w('Resolve watching calendar failed: empty calendar');
return;
}
final map = <int, int>{};
for (int weekdayIndex = 0;
weekdayIndex < calendar.length && weekdayIndex < 7;
weekdayIndex++) {
for (final item in calendar[weekdayIndex]) {
map[item.id] = weekdayIndex;
}
}
if (map.isEmpty) {
KazumiLogger().w('Resolve watching calendar failed: empty map');
return;
}
_watchingCalendarWeekdayById = ObservableMap<int, int>.of(map);
isWatchingCalendarReady = true;
// 加载成功后缓存到本地
await _saveCachedCalendar();
} catch (e) {
KazumiLogger().e('Resolve watching calendar failed', error: e);
// 失败时不阻塞 UI:继续使用 airWeekday 的降级分组
isWatchingCalendarReady = false;
_watchingCalendarWeekdayById = ObservableMap<int, int>();
} finally {
isWatchingCalendarLoading = false;
}
}

/// 确保时间表已加载(用于刷新缓存)
Future<void> ensureWatchingCalendarLoaded() async {
if (isWatchingCalendarLoading) {
return;
}
if (isWatchingCalendarReady && _watchingCalendarWeekdayById.isNotEmpty) {
return;
}
await loadWatchingCalendar();
}
Comment on lines +120 to +129
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ensureWatchingCalendarLoaded 的注释写“用于刷新缓存”,但当前实现只要 isWatchingCalendarReady && _watchingCalendarWeekdayById.isNotEmpty 就直接 return,不会触发刷新。建议:要么更新注释为“确保已加载(优先使用缓存)”,要么增加可选参数(如 forceRefresh)以支持主动刷新。

Copilot uses AI. Check for mistakes.

int getCollectType(BangumiItem bangumiItem) {
return _collectCrudRepository.getCollectType(bangumiItem.id);
}
Expand Down Expand Up @@ -130,4 +226,54 @@ abstract class _CollectController with Store {
.where((item) => !excludeIds.contains(item.id))
.toList();
}

/// 将"在看"番剧按周数分组
///
/// 返回 Map<int, List<CollectedBangumi>>
/// key: 0-6 代表周一到周日, 7 代表老番(不在时间表中)
Map<int, List<CollectedBangumi>> getWatchingBangumiByWeekday() {
Comment on lines +230 to +234
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里的注释多处写“按周数分组”,但实际逻辑是按“周几/星期”分组(0-6 对应周一到周日,7 为老番)。建议把注释改成“按星期/周几分组”,避免误导后续维护。

Copilot uses AI. Check for mistakes.
// 初始化 8 个分组 (周一到周日 + 老番)
Map<int, List<CollectedBangumi>> weekdayGroups = {
0: [], // 周一
1: [], // 周二
2: [], // 周三
3: [], // 周四
4: [], // 周五
5: [], // 周六
6: [], // 周日
7: [], // 老番
};

// 过滤出"在看"类型的番剧
final watchingList = collectibles.where((item) => item.type == 1).toList();

Comment on lines +247 to +249
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hard-codes the “在看” collect type as 1. Since CollectType is already available, prefer CollectType.watching.value (or CollectType.fromValue(item.type) == CollectType.watching) to avoid magic numbers and keep the logic consistent if values ever change.

Copilot uses AI. Check for mistakes.
// 按周数分组:优先使用时间表匹配;不在时间表的统一归为老番
for (var collected in watchingList) {
final id = collected.bangumiItem.id;
final calendarWeekdayIndex = _watchingCalendarWeekdayById[id];
if (calendarWeekdayIndex != null && calendarWeekdayIndex >= 0 && calendarWeekdayIndex <= 6) {
weekdayGroups[calendarWeekdayIndex]!.add(collected);
} else {
// 降级:时间表未就绪时,先按 airWeekday 分组;否则视为老番
if (!isWatchingCalendarReady && _watchingCalendarWeekdayById.isEmpty) {
final weekday = collected.bangumiItem.airWeekday;
if (weekday >= 1 && weekday <= 7) {
weekdayGroups[weekday - 1]!.add(collected);
} else {
weekdayGroups[7]!.add(collected);
}
} else {
Comment on lines +255 to +265
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

当前分组逻辑在“时间表已就绪(isWatchingCalendarReady==true 且缓存非空)但某个在看条目不在 _watchingCalendarWeekdayById 里”时,会直接归入“老番”。这会导致缓存缺少新条目/新季度时分组明显不准。建议:对 calendarWeekdayIndex == null 的情况也按 bangumiItem.airWeekday 做一次降级分组(无效再归入老番),或在该场景触发一次后台刷新日历映射。

Suggested change
weekdayGroups[calendarWeekdayIndex]!.add(collected);
} else {
// 降级:时间表未就绪时,先按 airWeekday 分组;否则视为老番
if (!isWatchingCalendarReady && _watchingCalendarWeekdayById.isEmpty) {
final weekday = collected.bangumiItem.airWeekday;
if (weekday >= 1 && weekday <= 7) {
weekdayGroups[weekday - 1]!.add(collected);
} else {
weekdayGroups[7]!.add(collected);
}
} else {
// 首选:使用时间表中的星期分组
weekdayGroups[calendarWeekdayIndex]!.add(collected);
} else {
// 降级:当某个在看条目不在日历映射中时,尝试使用 airWeekday 分组(无效再视为老番)
final weekday = collected.bangumiItem.airWeekday;
if (weekday >= 1 && weekday <= 7) {
weekdayGroups[weekday - 1]!.add(collected);
} else {
// airWeekday 无效时才视为老番

Copilot uses AI. Check for mistakes.
weekdayGroups[7]!.add(collected);
}
}
}

// 对每个分组按时间排序
for (var group in weekdayGroups.values) {
group.sort((a, b) => b.time.millisecondsSinceEpoch
.compareTo(a.time.millisecondsSinceEpoch));
Comment on lines +271 to +274
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getWatchingBangumiByWeekday() 每次调用都会对 8 个分组执行 sort。该方法在页面 build 期间会被调用,重复排序会带来不必要的开销。建议把分组结果做成 @computed/缓存(当 collectibles 或时间表映射变化时再重算),或至少只对非空分组排序。

Suggested change
// 对每个分组按时间排序
for (var group in weekdayGroups.values) {
group.sort((a, b) => b.time.millisecondsSinceEpoch
.compareTo(a.time.millisecondsSinceEpoch));
// 对每个分组按时间排序(仅对非空分组执行排序)
for (var group in weekdayGroups.values) {
if (group.isNotEmpty) {
group.sort((a, b) => b.time.millisecondsSinceEpoch
.compareTo(a.time.millisecondsSinceEpoch));
}

Copilot uses AI. Check for mistakes.
}

return weekdayGroups;
}
}
63 changes: 63 additions & 0 deletions lib/pages/collect/collect_controller.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading