feat(home): 首页按内容类型分频道 Feed 并支持分页
This commit is contained in:
1 parent
4d0be47ee3
commit
ae61d9ec4c
22 files changed
+1001
-71
No files matched your search
@@ -100,6 +100,8 @@ class PlayerLogic extends ChangeNotifier {
|
||||
ValueChanged<Drama>? onDramaChanged;
|
||||
ValueChanged<bool>? onImmersiveChanged;
|
||||
bool _disposed = false;
|
||||
PlayerFactory? _sessionFactory;
|
||||
PlaybackCoordinator? _sessionCoordinator;
|
||||
|
||||
/// [start] 是否已经跑过:没跑过时 session/pages 还是 late 未初始化,
|
||||
/// dispose 不能直接碰它们(例如页面构建失败或纯逻辑测试)。
|
||||
@@ -109,6 +111,14 @@ class PlayerLogic extends ChangeNotifier {
|
||||
|
||||
bool get chromeHidden => immersive;
|
||||
|
||||
/// 当前 PageView 对应用户正在观看的短剧 id,尚未起播时为 null。
|
||||
String? get currentDramaId {
|
||||
if (!_started || visibleIndices.isEmpty) return null;
|
||||
final index = session.currentIndex;
|
||||
if (index < 0 || index >= queue.length) return null;
|
||||
return queue.entryAt(index).drama.id;
|
||||
}
|
||||
|
||||
Episode displayEpisode(Drama drama, Episode fallback) {
|
||||
final number = pickedEpisode[drama.id];
|
||||
if (number == null) return fallback;
|
||||
@@ -145,6 +155,8 @@ class PlayerLogic extends ChangeNotifier {
|
||||
required PlaybackCoordinator coordinator,
|
||||
}) {
|
||||
_started = true;
|
||||
_sessionFactory = factory;
|
||||
_sessionCoordinator = coordinator;
|
||||
pictureInPicture.setStateListener(onPipChanged);
|
||||
queue = feedDramas == null
|
||||
? PlaybackQueue.series(pageDrama)
|
||||
@@ -153,7 +165,53 @@ class PlayerLogic extends ChangeNotifier {
|
||||
pages = PageController(initialPage: initialIndex);
|
||||
// 票据解析后要把新地址写回这个列表(PlaybackSession 的 canPlay 读的就是它)
|
||||
sessionEpisodes = [for (final entry in queue.entries) entry.episode];
|
||||
session = PlaybackSession(
|
||||
session = _createSession(factory, coordinator);
|
||||
if (!active || visibleIndices.isEmpty) {
|
||||
session.setRouteVisible(false);
|
||||
}
|
||||
unawaited(loadResumePoint());
|
||||
unawaited(selectEpisode(initialIndex));
|
||||
}
|
||||
|
||||
/// 原地替换首页 feed 队列;频道变化时重置到第 1 个,刷新时尽量保留当前剧。
|
||||
void updateFeed(List<Drama> dramas, {String? keepDramaId}) {
|
||||
final currentId = currentDramaId;
|
||||
final nextQueue = PlaybackQueue.home(List<Drama>.unmodifiable(dramas));
|
||||
final nextVisible = List<int>.generate(nextQueue.length, (index) => index);
|
||||
final nextEpisodes = [for (final entry in nextQueue.entries) entry.episode];
|
||||
feedDramas = List<Drama>.unmodifiable(dramas);
|
||||
queue = nextQueue;
|
||||
visibleIndices = nextVisible;
|
||||
if (dramas.isNotEmpty) pageDrama = dramas.first;
|
||||
if (!_started) return;
|
||||
if (nextQueue.length == 0) {
|
||||
session.setRouteVisible(false);
|
||||
notify();
|
||||
return;
|
||||
}
|
||||
final keepIndex = keepDramaId != null && keepDramaId == currentId
|
||||
? nextQueue.entries.indexWhere((entry) => entry.drama.id == keepDramaId)
|
||||
: -1;
|
||||
if (keepIndex >= 0 && session.active != null) {
|
||||
sessionEpisodes
|
||||
..clear()
|
||||
..addAll(nextEpisodes);
|
||||
session.remapCurrentIndex(
|
||||
keepIndex,
|
||||
preloadIndex: nextVisibleIndex(keepIndex),
|
||||
);
|
||||
_jumpToFeedPage(keepIndex);
|
||||
notify();
|
||||
return;
|
||||
}
|
||||
_replaceSession(nextEpisodes, keepIndex >= 0 ? keepIndex : 0);
|
||||
}
|
||||
|
||||
PlaybackSession _createSession(
|
||||
PlayerFactory factory,
|
||||
PlaybackCoordinator coordinator,
|
||||
) {
|
||||
return PlaybackSession(
|
||||
episodes: sessionEpisodes,
|
||||
factory: factory,
|
||||
// 地址只由后端下发(票据或详情里的免费集签名地址),拿不到地址即视为锁定
|
||||
@@ -162,11 +220,35 @@ class PlayerLogic extends ChangeNotifier {
|
||||
activate: activatePlayer,
|
||||
coordinator: coordinator,
|
||||
);
|
||||
if (!active || visibleIndices.isEmpty) {
|
||||
session.setRouteVisible(false);
|
||||
}
|
||||
|
||||
void _replaceSession(List<Episode> nextEpisodes, int targetIndex) {
|
||||
final factory = _sessionFactory;
|
||||
final coordinator = _sessionCoordinator;
|
||||
if (factory == null || coordinator == null) return;
|
||||
unawaited(saveCurrentProgress());
|
||||
final previous = session;
|
||||
sessionEpisodes = List<Episode>.of(nextEpisodes);
|
||||
trackedPlayer = null;
|
||||
trackedEpisode = null;
|
||||
trackedDrama = null;
|
||||
session = _createSession(factory, coordinator);
|
||||
session.setRouteVisible(active && visibleIndices.isNotEmpty);
|
||||
unawaited(previous.close());
|
||||
_jumpToFeedPage(targetIndex);
|
||||
unawaited(selectEpisode(targetIndex));
|
||||
notify();
|
||||
}
|
||||
|
||||
void _jumpToFeedPage(int index) {
|
||||
if (!_started) return;
|
||||
if (pages.hasClients) {
|
||||
pages.jumpToPage(index);
|
||||
return;
|
||||
}
|
||||
unawaited(loadResumePoint());
|
||||
unawaited(selectEpisode(initialIndex));
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (pages.hasClients) pages.jumpToPage(index);
|
||||
});
|
||||
}
|
||||
|
||||
/// 服务端续看位置:进播放页时取一次,用于"定位到上次那一集的那一秒"。
|
||||
|
||||
Reference in new issue
Block a user