fix(player): 解锁状态登录期内共享,重进播放页不再丢解锁
解锁记录原本是播放页实例的私有集合,退出再进入会先闪一次锁定态,还会 被 selectLocked 提前释放播放器。改为进程内共享的 UnlockedEpisodeRegistry, 由全局注入并在登录态变化时清空;播放器三处渲染分支统一走同一判定, 会话锁态只约束当前页,避免相邻预加载页被误判为锁定。
This commit is contained in:
1 parent
a4d08e7c1e
commit
57f84c45bd
6 files changed
+103
-18
No files matched your search
@@ -47,6 +47,7 @@ import 'core/data/services/playlist_controller.dart';
|
|||||||
import 'core/data/services/event_tracker.dart';
|
import 'core/data/services/event_tracker.dart';
|
||||||
import 'core/data/services/playback_position_cache.dart';
|
import 'core/data/services/playback_position_cache.dart';
|
||||||
import 'core/data/services/playback_session.dart';
|
import 'core/data/services/playback_session.dart';
|
||||||
|
import 'core/data/services/unlocked_episode_registry.dart';
|
||||||
import 'core/data/services/video_player_port.dart';
|
import 'core/data/services/video_player_port.dart';
|
||||||
import 'core/data/services/wallet_controller.dart';
|
import 'core/data/services/wallet_controller.dart';
|
||||||
import 'core/navigation/app_route_observer.dart';
|
import 'core/navigation/app_route_observer.dart';
|
||||||
@@ -207,6 +208,10 @@ class _LumaDramaAppState extends State<LumaDramaApp> {
|
|||||||
|
|
||||||
late final LocaleController _localeController = LocaleController();
|
late final LocaleController _localeController = LocaleController();
|
||||||
|
|
||||||
|
/// 登录期内共享的「已解锁收费集」:播放页进出、feed 与全屏切换都要沿用。
|
||||||
|
late final UnlockedEpisodeRegistry _unlockedEpisodes =
|
||||||
|
UnlockedEpisodeRegistry();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -226,6 +231,8 @@ class _LumaDramaAppState extends State<LumaDramaApp> {
|
|||||||
if (_auth.isSignedIn == _wasSignedIn) return;
|
if (_auth.isSignedIn == _wasSignedIn) return;
|
||||||
_wasSignedIn = _auth.isSignedIn;
|
_wasSignedIn = _auth.isSignedIn;
|
||||||
_interactionController.clear();
|
_interactionController.clear();
|
||||||
|
// 解锁结论属于账号:换人登录后不能沿用上一个人的可播判断。
|
||||||
|
_unlockedEpisodes.clear();
|
||||||
if (_auth.isSignedIn) {
|
if (_auth.isSignedIn) {
|
||||||
unawaited(_favoriteController.load());
|
unawaited(_favoriteController.load());
|
||||||
unawaited(_walletController.load());
|
unawaited(_walletController.load());
|
||||||
@@ -271,6 +278,7 @@ class _LumaDramaAppState extends State<LumaDramaApp> {
|
|||||||
Provider<CatalogApi>.value(value: _catalogApi),
|
Provider<CatalogApi>.value(value: _catalogApi),
|
||||||
Provider<PlaybackRepository>.value(value: _playback),
|
Provider<PlaybackRepository>.value(value: _playback),
|
||||||
Provider<PlaybackPositionCache>.value(value: _playbackPositionCache),
|
Provider<PlaybackPositionCache>.value(value: _playbackPositionCache),
|
||||||
|
Provider<UnlockedEpisodeRegistry>.value(value: _unlockedEpisodes),
|
||||||
Provider<WatchHistoryRepository>.value(value: _watchHistory),
|
Provider<WatchHistoryRepository>.value(value: _watchHistory),
|
||||||
Provider<FavoriteRepository>.value(value: _favorites),
|
Provider<FavoriteRepository>.value(value: _favorites),
|
||||||
Provider<CommentRepository>.value(value: _commentRepository),
|
Provider<CommentRepository>.value(value: _commentRepository),
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// Author: fruit.com.cn
|
||||||
|
|
||||||
|
/// 本次登录期内已确认可播的收费集。
|
||||||
|
///
|
||||||
|
/// 解锁本身是服务端行为(`POST /app/episodes/{id}/unlock` 成功即生效),
|
||||||
|
/// 客户端只需要记住「这一集不用再挡」。若不记住,退出播放页再进来会先闪
|
||||||
|
/// 一次锁定态,且会被 [PlaybackSession.selectLocked] 提前释放播放器。
|
||||||
|
///
|
||||||
|
/// 生命周期跟随账号:登录态变化时由 `app.dart` 调用 [clear],避免换账号后
|
||||||
|
/// 沿用上一个人的解锁结论。
|
||||||
|
class UnlockedEpisodeRegistry {
|
||||||
|
final Set<String> _episodeIds = <String>{};
|
||||||
|
|
||||||
|
bool contains(String episodeId) => _episodeIds.contains(episodeId);
|
||||||
|
|
||||||
|
void markUnlocked(String episodeId) {
|
||||||
|
if (episodeId.isEmpty) return;
|
||||||
|
_episodeIds.add(episodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() => _episodeIds.clear();
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import '../../core/data/repositories/playback_repository.dart';
|
|||||||
import '../../core/data/services/android_picture_in_picture.dart';
|
import '../../core/data/services/android_picture_in_picture.dart';
|
||||||
import '../../core/data/services/download_controller.dart';
|
import '../../core/data/services/download_controller.dart';
|
||||||
import '../../core/data/services/playback_position_cache.dart';
|
import '../../core/data/services/playback_position_cache.dart';
|
||||||
|
import '../../core/data/services/unlocked_episode_registry.dart';
|
||||||
import '../../core/data/services/wallet_controller.dart';
|
import '../../core/data/services/wallet_controller.dart';
|
||||||
import 'player_logic.dart';
|
import 'player_logic.dart';
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ class PlayerBinding {
|
|||||||
required WatchHistoryRepository history,
|
required WatchHistoryRepository history,
|
||||||
required PlaybackRepository playback,
|
required PlaybackRepository playback,
|
||||||
required PlaybackPositionCache positionCache,
|
required PlaybackPositionCache positionCache,
|
||||||
|
UnlockedEpisodeRegistry? unlockedEpisodes,
|
||||||
WalletController? wallet,
|
WalletController? wallet,
|
||||||
DownloadController? downloads,
|
DownloadController? downloads,
|
||||||
bool Function()? isSignedIn,
|
bool Function()? isSignedIn,
|
||||||
@@ -20,6 +22,7 @@ class PlayerBinding {
|
|||||||
history: history,
|
history: history,
|
||||||
playback: playback,
|
playback: playback,
|
||||||
positionCache: positionCache,
|
positionCache: positionCache,
|
||||||
|
unlockedEpisodes: unlockedEpisodes,
|
||||||
pictureInPicture: AndroidPictureInPicture(),
|
pictureInPicture: AndroidPictureInPicture(),
|
||||||
wallet: wallet,
|
wallet: wallet,
|
||||||
downloads: downloads,
|
downloads: downloads,
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import '../../core/data/services/platform_share_service.dart';
|
|||||||
import '../../core/data/services/playback_position_cache.dart';
|
import '../../core/data/services/playback_position_cache.dart';
|
||||||
import '../../core/data/services/playback_queue.dart';
|
import '../../core/data/services/playback_queue.dart';
|
||||||
import '../../core/data/services/playback_session.dart';
|
import '../../core/data/services/playback_session.dart';
|
||||||
|
import '../../core/data/services/unlocked_episode_registry.dart';
|
||||||
import '../../core/data/services/video_player_port.dart';
|
import '../../core/data/services/video_player_port.dart';
|
||||||
import '../../core/data/services/wallet_controller.dart';
|
import '../../core/data/services/wallet_controller.dart';
|
||||||
import '../../core/formatting/localized_content.dart';
|
import '../../core/formatting/localized_content.dart';
|
||||||
@@ -64,11 +65,13 @@ class PlayerLogic extends ChangeNotifier {
|
|||||||
required this.pictureInPicture,
|
required this.pictureInPicture,
|
||||||
this.wallet,
|
this.wallet,
|
||||||
this.downloads,
|
this.downloads,
|
||||||
|
UnlockedEpisodeRegistry? unlockedEpisodes,
|
||||||
PlaybackPositionCache? positionCache,
|
PlaybackPositionCache? positionCache,
|
||||||
Future<void> Function()? waitForFrame,
|
Future<void> Function()? waitForFrame,
|
||||||
DateTime Function()? clock,
|
DateTime Function()? clock,
|
||||||
bool Function()? isSignedIn,
|
bool Function()? isSignedIn,
|
||||||
}) : positionCache = positionCache ?? PlaybackPositionCache(),
|
}) : unlockedEpisodes = unlockedEpisodes ?? UnlockedEpisodeRegistry(),
|
||||||
|
positionCache = positionCache ?? PlaybackPositionCache(),
|
||||||
_waitForFrame =
|
_waitForFrame =
|
||||||
waitForFrame ?? (() => WidgetsBinding.instance.endOfFrame),
|
waitForFrame ?? (() => WidgetsBinding.instance.endOfFrame),
|
||||||
_clock = clock ?? DateTime.now,
|
_clock = clock ?? DateTime.now,
|
||||||
@@ -79,6 +82,9 @@ class PlayerLogic extends ChangeNotifier {
|
|||||||
final AndroidPictureInPicture pictureInPicture;
|
final AndroidPictureInPicture pictureInPicture;
|
||||||
final WalletController? wallet;
|
final WalletController? wallet;
|
||||||
final DownloadController? downloads;
|
final DownloadController? downloads;
|
||||||
|
|
||||||
|
/// 已确认可播的收费集(登录期内共享):退出播放页再进来不必重新挡一次。
|
||||||
|
final UnlockedEpisodeRegistry unlockedEpisodes;
|
||||||
final PlaybackPositionCache positionCache;
|
final PlaybackPositionCache positionCache;
|
||||||
final Future<void> Function() _waitForFrame;
|
final Future<void> Function() _waitForFrame;
|
||||||
final DateTime Function() _clock;
|
final DateTime Function() _clock;
|
||||||
@@ -169,14 +175,15 @@ class PlayerLogic extends ChangeNotifier {
|
|||||||
|
|
||||||
/// 当前设备是否为 iOS 真机环境(面板据此显示自动小窗开关)。
|
/// 当前设备是否为 iOS 真机环境(面板据此显示自动小窗开关)。
|
||||||
bool get iosDevice => !kIsWeb && Platform.isIOS;
|
bool get iosDevice => !kIsWeb && Platform.isIOS;
|
||||||
|
|
||||||
/// PiP 状态是全局单通道回调,须广播给所有存活的 PlayerLogic:
|
/// PiP 状态是全局单通道回调,须广播给所有存活的 PlayerLogic:
|
||||||
/// 推荐页每页一个 logic,若只保留最后一次注册的 handler,
|
/// 推荐页每页一个 logic,若只保留最后一次注册的 handler,
|
||||||
/// 相邻预加载页会盖掉正在播放页的注册,导致小窗 UI 不隐藏且无声音。
|
/// 相邻预加载页会盖掉正在播放页的注册,导致小窗 UI 不隐藏且无声音。
|
||||||
static final Set<PlayerLogic> _pipSubscribers = <PlayerLogic>{};
|
static final Set<PlayerLogic> _pipSubscribers = <PlayerLogic>{};
|
||||||
|
|
||||||
/// 「记住上次选集」:dramaId → 集号。只在用户从选集面板主动挑集时写入
|
/// 「记住上次选集」:dramaId → 集号。只在用户从选集面板主动挑集时写入
|
||||||
/// (见 [_recordPickedEpisode]),feed 里同一部剧据此显示上次选中的集。
|
/// (见 [_recordPickedEpisode]),feed 里同一部剧据此显示上次选中的集。
|
||||||
static final Map<String, int> pickedEpisode = {};
|
static final Map<String, int> pickedEpisode = {};
|
||||||
final Set<String> _unlockedEpisodeIds = {};
|
|
||||||
final Map<String, EpisodeStats> _episodeStats = {};
|
final Map<String, EpisodeStats> _episodeStats = {};
|
||||||
final Map<String, DateTime> _episodeStatsFetchedAt = {};
|
final Map<String, DateTime> _episodeStatsFetchedAt = {};
|
||||||
final Map<String, Future<void>> _episodeStatsInFlight = {};
|
final Map<String, Future<void>> _episodeStatsInFlight = {};
|
||||||
@@ -562,7 +569,7 @@ class PlayerLogic extends ChangeNotifier {
|
|||||||
final localPath = await downloads?.findLocalPath(episode.id);
|
final localPath = await downloads?.findLocalPath(episode.id);
|
||||||
if (request != pageRequest) return false;
|
if (request != pageRequest) return false;
|
||||||
if (localPath != null) {
|
if (localPath != null) {
|
||||||
_unlockedEpisodeIds.add(episode.id);
|
unlockedEpisodes.markUnlocked(episode.id);
|
||||||
blockedTicket = null;
|
blockedTicket = null;
|
||||||
blockedEpisodeId = null;
|
blockedEpisodeId = null;
|
||||||
qualities = const [];
|
qualities = const [];
|
||||||
@@ -587,7 +594,7 @@ class PlayerLogic extends ChangeNotifier {
|
|||||||
final playUrl = preferred ?? ticket.playUrl;
|
final playUrl = preferred ?? ticket.playUrl;
|
||||||
if (preferred == null) selectedQuality = 'AUTO';
|
if (preferred == null) selectedQuality = 'AUTO';
|
||||||
if (ticket.accessible && playUrl.isNotEmpty) {
|
if (ticket.accessible && playUrl.isNotEmpty) {
|
||||||
_unlockedEpisodeIds.add(episode.id);
|
unlockedEpisodes.markUnlocked(episode.id);
|
||||||
// 签名地址原样写回,不做任何拼接/改写(AppConfig.mediaUrl 只补 host)
|
// 签名地址原样写回,不做任何拼接/改写(AppConfig.mediaUrl 只补 host)
|
||||||
sessionEpisodes[index] = episode.withVideoUrl(
|
sessionEpisodes[index] = episode.withVideoUrl(
|
||||||
playUrl,
|
playUrl,
|
||||||
@@ -860,7 +867,7 @@ class PlayerLogic extends ChangeNotifier {
|
|||||||
unlockError = service.unlockError;
|
unlockError = service.unlockError;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_unlockedEpisodeIds.add(episode.id);
|
unlockedEpisodes.markUnlocked(episode.id);
|
||||||
final ready = await ensureTicket(index, forceRefresh: true);
|
final ready = await ensureTicket(index, forceRefresh: true);
|
||||||
if (ready) {
|
if (ready) {
|
||||||
await _selectVisible(index);
|
await _selectVisible(index);
|
||||||
@@ -1688,7 +1695,16 @@ class PlayerLogic extends ChangeNotifier {
|
|||||||
/// 收费集只有在本会话确认可播或解锁成功后才解除门禁。
|
/// 收费集只有在本会话确认可播或解锁成功后才解除门禁。
|
||||||
bool isEpisodeLocked(Episode episode) {
|
bool isEpisodeLocked(Episode episode) {
|
||||||
return (episode.locked || !episode.isFree) &&
|
return (episode.locked || !episode.isFree) &&
|
||||||
!_unlockedEpisodeIds.contains(episode.id);
|
!unlockedEpisodes.contains(episode.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 单页锁定判定:剧集自身锁定 + 会话锁态(只约束当前页)+ 后端拒绝。
|
||||||
|
///
|
||||||
|
/// 播放页的多个渲染分支都必须走这里,否则相邻预加载页会被会话锁态误判成锁定。
|
||||||
|
bool isEpisodeBlockedFor(Episode episode, int index) {
|
||||||
|
return isEpisodeLocked(episode) ||
|
||||||
|
(index == session.currentIndex && session.isLocked) ||
|
||||||
|
blockedEpisodeId == episode.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 按可见队列选择一集,并预热上一集与下一集。
|
/// 按可见队列选择一集,并预热上一集与下一集。
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import '../../core/data/services/playback_position_cache.dart';
|
|||||||
import '../../core/data/services/playback_session.dart';
|
import '../../core/data/services/playback_session.dart';
|
||||||
import '../../core/data/services/sprite_thumbnail_axis.dart';
|
import '../../core/data/services/sprite_thumbnail_axis.dart';
|
||||||
import '../../core/data/services/playback_thumbnail_service.dart';
|
import '../../core/data/services/playback_thumbnail_service.dart';
|
||||||
|
import '../../core/data/services/unlocked_episode_registry.dart';
|
||||||
import '../../core/data/services/video_player_port.dart';
|
import '../../core/data/services/video_player_port.dart';
|
||||||
import '../../core/data/services/wallet_controller.dart';
|
import '../../core/data/services/wallet_controller.dart';
|
||||||
import '../../core/formatting/error_text.dart';
|
import '../../core/formatting/error_text.dart';
|
||||||
@@ -167,6 +168,7 @@ class _PlayerPageState extends State<PlayerPage>
|
|||||||
history: context.read<WatchHistoryRepository>(),
|
history: context.read<WatchHistoryRepository>(),
|
||||||
playback: context.read<PlaybackRepository>(),
|
playback: context.read<PlaybackRepository>(),
|
||||||
positionCache: context.read<PlaybackPositionCache>(),
|
positionCache: context.read<PlaybackPositionCache>(),
|
||||||
|
unlockedEpisodes: _readUnlockedEpisodes(context),
|
||||||
wallet: widget.wallet,
|
wallet: widget.wallet,
|
||||||
downloads: widget.downloads,
|
downloads: widget.downloads,
|
||||||
isSignedIn: () => context.read<AuthController>().isSignedIn,
|
isSignedIn: () => context.read<AuthController>().isSignedIn,
|
||||||
@@ -349,6 +351,16 @@ class _PlayerPageState extends State<PlayerPage>
|
|||||||
setState(() => _playbackSpeed = value);
|
setState(() => _playbackSpeed = value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 全局解锁登记表(登录期内共享);直接构造播放页的测试没有 Provider,
|
||||||
|
/// 返回 null 让 PlayerLogic 退化成页面自用的一份。
|
||||||
|
UnlockedEpisodeRegistry? _readUnlockedEpisodes(BuildContext context) {
|
||||||
|
try {
|
||||||
|
return context.read<UnlockedEpisodeRegistry>();
|
||||||
|
} on ProviderNotFoundException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _hideChrome() {
|
void _hideChrome() {
|
||||||
if (!_chromeVisible) return;
|
if (!_chromeVisible) return;
|
||||||
setState(() => _chromeVisible = false);
|
setState(() => _chromeVisible = false);
|
||||||
@@ -956,10 +968,7 @@ class _PlayerPageState extends State<PlayerPage>
|
|||||||
}
|
}
|
||||||
final episode = _resolvedEpisode(index);
|
final episode = _resolvedEpisode(index);
|
||||||
final selected = index == currentIndex;
|
final selected = index == currentIndex;
|
||||||
final locked =
|
final locked = logic.isEpisodeBlockedFor(episode, index);
|
||||||
logic.isEpisodeLocked(episode) ||
|
|
||||||
(selected && logic.session.isLocked) ||
|
|
||||||
logic.blockedEpisodeId == episode.id;
|
|
||||||
final port = locked
|
final port = locked
|
||||||
? null
|
? null
|
||||||
: (selected ? logic.session.active : null) ??
|
: (selected ? logic.session.active : null) ??
|
||||||
@@ -1042,10 +1051,7 @@ class _PlayerPageState extends State<PlayerPage>
|
|||||||
final drama = entry.drama;
|
final drama = entry.drama;
|
||||||
final episode = _resolvedEpisode(index);
|
final episode = _resolvedEpisode(index);
|
||||||
final selected = index == logic.session.currentIndex;
|
final selected = index == logic.session.currentIndex;
|
||||||
final locked =
|
final locked = logic.isEpisodeBlockedFor(episode, index);
|
||||||
logic.isEpisodeLocked(episode) ||
|
|
||||||
logic.session.isLocked ||
|
|
||||||
logic.blockedEpisodeId == episode.id;
|
|
||||||
// Selection changes before the prepared slot is promoted. Use its controller
|
// Selection changes before the prepared slot is promoted. Use its controller
|
||||||
// throughout the handoff instead of briefly falling back to a placeholder.
|
// throughout the handoff instead of briefly falling back to a placeholder.
|
||||||
final port = locked
|
final port = locked
|
||||||
@@ -1294,10 +1300,7 @@ class _PlayerPageState extends State<PlayerPage>
|
|||||||
final drama = entry.drama;
|
final drama = entry.drama;
|
||||||
final episode = _resolvedEpisode(index);
|
final episode = _resolvedEpisode(index);
|
||||||
final selected = index == logic.session.currentIndex;
|
final selected = index == logic.session.currentIndex;
|
||||||
final locked =
|
final locked = logic.isEpisodeBlockedFor(episode, index);
|
||||||
logic.isEpisodeLocked(episode) ||
|
|
||||||
logic.session.isLocked ||
|
|
||||||
logic.blockedEpisodeId == episode.id;
|
|
||||||
final port = locked
|
final port = locked
|
||||||
? null
|
? null
|
||||||
: (selected ? logic.session.active : null) ??
|
: (selected ? logic.session.active : null) ??
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import 'package:fruitshorts/core/data/repositories/commerce_repository.dart';
|
|||||||
import 'package:fruitshorts/core/data/repositories/playback_repository.dart';
|
import 'package:fruitshorts/core/data/repositories/playback_repository.dart';
|
||||||
import 'package:fruitshorts/core/data/services/android_picture_in_picture.dart';
|
import 'package:fruitshorts/core/data/services/android_picture_in_picture.dart';
|
||||||
import 'package:fruitshorts/core/data/services/playback_session.dart';
|
import 'package:fruitshorts/core/data/services/playback_session.dart';
|
||||||
|
import 'package:fruitshorts/core/data/services/unlocked_episode_registry.dart';
|
||||||
import 'package:fruitshorts/core/data/services/wallet_controller.dart';
|
import 'package:fruitshorts/core/data/services/wallet_controller.dart';
|
||||||
import 'package:fruitshorts/pages/player/player_logic.dart';
|
import 'package:fruitshorts/pages/player/player_logic.dart';
|
||||||
import 'package:fruitshorts/pages/player/player_view.dart';
|
import 'package:fruitshorts/pages/player/player_view.dart';
|
||||||
@@ -120,6 +121,36 @@ void main() {
|
|||||||
logic.dispose();
|
logic.dispose();
|
||||||
await _drain(logic);
|
await _drain(logic);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should_keep_episode_unlocked_when_reentering_player_page', () async {
|
||||||
|
final registry = UnlockedEpisodeRegistry();
|
||||||
|
registry.markUnlocked('locked-1');
|
||||||
|
final playback = FakePlaybackRepository(
|
||||||
|
ticket: const PlayTicket(
|
||||||
|
episodeId: 'locked-1',
|
||||||
|
accessible: true,
|
||||||
|
accessType: 'ENTITLED',
|
||||||
|
playUrl: '/files/locked-1.mp4',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
// 重进播放页 = 新建 PlayerLogic,但沿用同一张解锁登记表。
|
||||||
|
final logic = _logic(playback, unlockedEpisodes: registry);
|
||||||
|
logic.attach(
|
||||||
|
const PlayerPage(drama: _lockedDrama, initialIndex: 0),
|
||||||
|
isMounted: () => true,
|
||||||
|
);
|
||||||
|
logic.start(
|
||||||
|
factory: (_) => _NoopPlayer(),
|
||||||
|
coordinator: PlaybackCoordinator(),
|
||||||
|
);
|
||||||
|
await _drain(logic);
|
||||||
|
|
||||||
|
expect(logic.isEpisodeLocked(logic.sessionEpisodes.single), isFalse);
|
||||||
|
expect(logic.session.active, isNotNull);
|
||||||
|
|
||||||
|
logic.dispose();
|
||||||
|
await _drain(logic);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerLogic _logic(
|
PlayerLogic _logic(
|
||||||
@@ -127,11 +158,13 @@ PlayerLogic _logic(
|
|||||||
bool signedIn = true,
|
bool signedIn = true,
|
||||||
bool Function()? isSignedIn,
|
bool Function()? isSignedIn,
|
||||||
WalletController? wallet,
|
WalletController? wallet,
|
||||||
|
UnlockedEpisodeRegistry? unlockedEpisodes,
|
||||||
}) {
|
}) {
|
||||||
return PlayerLogic(
|
return PlayerLogic(
|
||||||
history: FakeWatchHistory(),
|
history: FakeWatchHistory(),
|
||||||
playback: playback,
|
playback: playback,
|
||||||
wallet: wallet,
|
wallet: wallet,
|
||||||
|
unlockedEpisodes: unlockedEpisodes,
|
||||||
pictureInPicture: AndroidPictureInPicture(
|
pictureInPicture: AndroidPictureInPicture(
|
||||||
channel: const MethodChannel('test/player_locked_auth_flow/pip'),
|
channel: const MethodChannel('test/player_locked_auth_flow/pip'),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in new issue
Block a user