feat(app): 完成 P0 钱包解锁、下载落盘与手机号登录

This commit is contained in:
Zhou Weidong committed 2026-10-19 23:46:47 +08:00
1 parent de941c841c
commit 136ee74618
47 files changed
+3264 -70

No files matched your search

+62 -3
View File
@@ -14,10 +14,12 @@ import '../../core/data/repositories/playback_repository.dart';
import '../../core/data/repositories/watch_history_repository.dart';
import '../../core/data/services/android_picture_in_picture.dart';
import '../../core/data/services/catalog_controller.dart';
import '../../core/data/services/download_controller.dart';
import '../../core/data/services/platform_share_service.dart';
import '../../core/data/services/playback_queue.dart';
import '../../core/data/services/playback_session.dart';
import '../../core/data/services/video_player_port.dart';
import '../../core/data/services/wallet_controller.dart';
import '../../core/formatting/localized_content.dart';
import '../../core/theme/app_colors.dart';
import '../../l10n/app_localizations.dart';
@@ -37,11 +39,15 @@ class PlayerLogic extends ChangeNotifier {
required this.history,
required this.playback,
required this.pictureInPicture,
this.wallet,
this.downloads,
});
final WatchHistoryRepository history;
final PlaybackRepository playback;
final AndroidPictureInPicture pictureInPicture;
final WalletController? wallet;
final DownloadController? downloads;
late PageController pages;
late PlaybackQueue queue;
@@ -75,6 +81,10 @@ class PlayerLogic extends ChangeNotifier {
bool pipPreparing = false;
final Map<String, int> pickedEpisode = {};
/// 解锁中的集 id 与失败文案;空表示没有进行中的解锁。
String? unlockingEpisodeId;
String? unlockError;
bool Function() isMounted = () => false;
bool homeMode = false;
bool embedded = false;
@@ -194,7 +204,7 @@ class PlayerLogic extends ChangeNotifier {
/// 票据门:起播前先拿票(命中未过期缓存就不会重复请求),
/// 拿不到地址就交给 [blockedTicket] 让 UI 提示解锁/暂无片源。
Future<bool> ensureTicket(int index) async {
Future<bool> ensureTicket(int index, {bool forceRefresh = false}) async {
if (index < 0 || index >= sessionEpisodes.length) return false;
final episode = sessionEpisodes[index];
final request = pageRequest;
@@ -202,7 +212,18 @@ class PlayerLogic extends ChangeNotifier {
ticketError = null;
notify();
try {
final ticket = await playback.ticketFor(episode.id);
final localPath = await downloads?.findLocalPath(episode.id);
if (request != pageRequest) return false;
if (localPath != null) {
blockedTicket = null;
blockedEpisodeId = null;
sessionEpisodes[index] = episode.withVideoUrl(_fileUrl(localPath));
return true;
}
final ticket = await playback.ticketFor(
episode.id,
forceRefresh: forceRefresh,
);
if (request != pageRequest) return false;
blockedTicket = null;
blockedEpisodeId = null;
@@ -231,6 +252,9 @@ class PlayerLogic extends ChangeNotifier {
}
}
String _fileUrl(String path) =>
path.startsWith('file:') ? path : Uri.file(path).toString();
/// 重试当前集的票据(失败态的"重试"按钮)。
Future<void> retryTicket() async {
ticketError = null;
@@ -238,6 +262,36 @@ class PlayerLogic extends ChangeNotifier {
unawaited(selectEpisode(session.currentIndex));
}
/// 金币解锁当前集;成功后强制刷新票据并直接起播。
Future<bool> unlockEpisode(int index) async {
final service = wallet;
if (service == null || index < 0 || index >= sessionEpisodes.length) {
return false;
}
final episode = sessionEpisodes[index];
unlockingEpisodeId = episode.id;
unlockError = null;
notify();
try {
final result = await service.unlock(episode.id);
if (result == null || !result.unlocked) {
unlockError = service.unlockError;
return false;
}
final ready = await ensureTicket(index, forceRefresh: true);
if (ready) {
await session.select(
index,
preloadIndex: nextVisibleIndex(index),
);
}
return ready;
} finally {
unlockingEpisodeId = null;
if (isMounted()) notify();
}
}
void onPageUpdated(PlayerPage previous) {
if (previous.active != active) {
session.setRouteVisible(active && visibleIndices.isNotEmpty);
@@ -562,7 +616,12 @@ class PlayerLogic extends ChangeNotifier {
if (!context.mounted) return;
await Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => PlayerPage(drama: drama, initialIndex: index),
builder: (_) => PlayerPage(
drama: drama,
initialIndex: index,
wallet: wallet,
downloads: downloads,
),
),
);
if (isMounted()) session.setRouteVisible(active);