feat(library): 收藏与观看历史支持分页
This commit is contained in:
1 parent
63d3b0f7ca
commit
235b50598a
24 files changed
+1015
-105
No files matched your search
@@ -0,0 +1,23 @@
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import 'json_numbers.dart';
|
||||
|
||||
/// 服务端标准分页响应 `{total, list}`。
|
||||
class PageResult<T> {
|
||||
const PageResult({this.total = 0, this.list = const []});
|
||||
|
||||
final int total;
|
||||
final List<T> list;
|
||||
|
||||
/// 解析分页壳,条目由调用方提供具体解析函数。
|
||||
factory PageResult.fromJson(
|
||||
Map<String, dynamic> json,
|
||||
T Function(Map<String, dynamic>) parseItem,
|
||||
) {
|
||||
final items = (json['list'] as List? ?? const [])
|
||||
.whereType<Map<String, dynamic>>()
|
||||
.map(parseItem)
|
||||
.toList(growable: false);
|
||||
return PageResult(total: jsonToInt(json['total']), list: items);
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,10 @@ class WatchProgress {
|
||||
required this.episodeId,
|
||||
required this.position,
|
||||
required this.duration,
|
||||
this.dramaTitle,
|
||||
this.dramaCover,
|
||||
this.episodeNumber,
|
||||
this.totalEpisodes,
|
||||
this.finished = false,
|
||||
this.playCount = 0,
|
||||
this.updatedAt,
|
||||
@@ -19,6 +23,10 @@ class WatchProgress {
|
||||
|
||||
final String dramaId;
|
||||
final String episodeId;
|
||||
final String? dramaTitle;
|
||||
final String? dramaCover;
|
||||
final int? episodeNumber;
|
||||
final int? totalEpisodes;
|
||||
final Duration position;
|
||||
final Duration duration;
|
||||
final bool finished;
|
||||
@@ -37,6 +45,14 @@ class WatchProgress {
|
||||
return WatchProgress(
|
||||
dramaId: (json['dramaId'] ?? '').toString(),
|
||||
episodeId: (json['episodeId'] ?? '').toString(),
|
||||
dramaTitle: json['dramaTitle']?.toString(),
|
||||
dramaCover: json['dramaCover']?.toString(),
|
||||
episodeNumber: json['episodeNumber'] == null
|
||||
? null
|
||||
: jsonToInt(json['episodeNumber']),
|
||||
totalEpisodes: json['totalEpisodes'] == null
|
||||
? null
|
||||
: jsonToInt(json['totalEpisodes']),
|
||||
position: Duration(milliseconds: jsonToInt(json['positionMs'])),
|
||||
duration: Duration(milliseconds: jsonToInt(json['durationMs'])),
|
||||
finished: json['finished'] == true,
|
||||
@@ -48,6 +64,10 @@ class WatchProgress {
|
||||
Map<String, dynamic> toJson() => {
|
||||
'dramaId': dramaId,
|
||||
'episodeId': episodeId,
|
||||
if (dramaTitle != null) 'dramaTitle': dramaTitle,
|
||||
if (dramaCover != null) 'dramaCover': dramaCover,
|
||||
if (episodeNumber != null) 'episodeNumber': episodeNumber,
|
||||
if (totalEpisodes != null) 'totalEpisodes': totalEpisodes,
|
||||
'positionMs': position.inMilliseconds,
|
||||
'durationMs': duration.inMilliseconds,
|
||||
'finished': finished,
|
||||
|
||||
@@ -19,8 +19,10 @@ class WatchRecord {
|
||||
|
||||
String get dramaId => progress.dramaId;
|
||||
|
||||
/// 详情中按 episodeId 定位集号;缺失时返回 0。
|
||||
/// 优先使用分页接口集号,其次从已加载详情定位。
|
||||
int get episodeNumber {
|
||||
final pageEpisode = progress.episodeNumber;
|
||||
if (pageEpisode != null && pageEpisode > 0) return pageEpisode;
|
||||
final episodes = drama?.episodes ?? const <Episode>[];
|
||||
for (var index = 0; index < episodes.length; index++) {
|
||||
if (episodes[index].id == progress.episodeId) return index + 1;
|
||||
@@ -28,6 +30,24 @@ class WatchRecord {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// 服务端分页标题优先,详情标题作为回退。
|
||||
String get displayTitle {
|
||||
final pageTitle = progress.dramaTitle?.trim() ?? '';
|
||||
return pageTitle.isNotEmpty ? pageTitle : drama?.title.trim() ?? '';
|
||||
}
|
||||
|
||||
/// 服务端分页封面优先,详情封面作为回退。
|
||||
String get displayCover {
|
||||
final pageCover = progress.dramaCover?.trim() ?? '';
|
||||
return pageCover.isNotEmpty ? pageCover : drama?.posterUrl ?? '';
|
||||
}
|
||||
|
||||
/// 分页接口已带展示字段时无需等待详情加载。
|
||||
bool get hasDisplayMetadata =>
|
||||
displayTitle.isNotEmpty ||
|
||||
progress.dramaCover?.trim().isNotEmpty == true ||
|
||||
(progress.episodeNumber ?? 0) > 0;
|
||||
|
||||
/// 生成卡片加载中、成功或失败后的新状态。
|
||||
WatchRecord copyWith({
|
||||
Drama? drama,
|
||||
|
||||
Reference in new issue
Block a user