// Author: fruit.com.cn part of 'player_view.dart'; extension _PlayerViewActions on _PlayerPageState { Future _openSpeedSheet(BuildContext context) async { final selected = await showModalBottomSheet( context: context, backgroundColor: AppColors.transparent, showDragHandle: false, builder: (_) => PlayerSpeedSheet(selected: _playbackSpeed), ); if (selected == null || !mounted) return; _updatePlaybackSpeed(selected); await logic.setPlaybackSpeed(selected); } Future _openMore(BuildContext context, Episode episode) async { final l10n = AppLocalizations.of(context)!; await showModalBottomSheet( context: context, backgroundColor: AppColors.transparent, showDragHandle: false, isScrollControlled: true, builder: (_) => PlayerMoreSheet( l10n: l10n, state: PlayerMoreState( speed: _playbackSpeed, qualities: logic.qualities, selectedQuality: logic.selectedQuality, ), actions: PlayerMoreActions( onSpeedChanged: (value) { _updatePlaybackSpeed(value); unawaited(logic.setPlaybackSpeed(value)); }, onQualityChanged: (quality) => _switchQuality(context, l10n, quality), onClear: _hideChrome, onDownload: () => requestEpisodeDownload(context, episode.id), onPip: logic.pictureInPicture.isPlatformSupported ? () => unawaited(logic.enterPip(context)) : null, ), ), ); } Future _switchQuality( BuildContext context, AppLocalizations l10n, String quality, ) async { Navigator.of(context).pop(); final switched = await logic.selectQuality(quality); if (!context.mounted || !switched) return false; final label = quality.toUpperCase() == 'AUTO' ? l10n.qualityAuto : quality; showCenteredToast(context, l10n.qualitySwitched(label)); return true; } Future _openUnlockSheet( BuildContext context, int index, Episode episode, ) async { final wallet = logic.wallet; if (wallet == null) return; await showModalBottomSheet( context: context, backgroundColor: AppColors.transparent, showDragHandle: false, isScrollControlled: true, builder: (_) => PlayerUnlockSheet( l10n: AppLocalizations.of(context)!, episodeNumber: episode.number, coinCost: logic.blockedTicket?.unlockCoin ?? 0, balance: wallet.snapshot == null ? null : wallet.balance, onUnlock: () => _unlockEpisode(context, index), ), ); } Future _unlockEpisode(BuildContext context, int index) async { if (!await ensureSignedIn(context) || !context.mounted) return false; final ok = await logic.unlockEpisode(index); if (ok || !context.mounted) return ok; final l10n = AppLocalizations.of(context)!; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(errorText(l10n, logic.unlockError, l10n.unlockFailed)), ), ); return false; } Future _toggleLike( BuildContext context, InteractionController interactions, Episode episode, { bool likeOnly = false, }) async { unawaited(context.read().lightImpact()); if (interactions.isEpisodeBusy(episode.id) || (likeOnly && interactions.isEpisodeLiked(episode.id))) { return; } if (!await ensureSignedIn(context) || !context.mounted) return; if (interactions.isEpisodeBusy(episode.id) || (likeOnly && interactions.isEpisodeLiked(episode.id))) { return; } final result = await interactions.toggleEpisodeLike(episode.id); if (result.isSuccess || !context.mounted) return; showCenteredToast( context, result.errorMessage ?? AppLocalizations.of(context)!.likeFailed, ); } Future _toggleSave(BuildContext context, Drama drama) async { final favorites = context.read(); if (!await ensureSignedIn(context) || !context.mounted) return; unawaited(context.read().lightImpact()); final wasSaved = favorites.isSaved(drama.id); final ok = await favorites.toggle(drama.id, card: drama); if (!context.mounted) return; if (ok) { final nowSaved = favorites.isSaved(drama.id); if (nowSaved != wasSaved) { _applyFavoriteDelta(drama.id, nowSaved: nowSaved); } return; } ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(AppLocalizations.of(context)!.favoriteToggleFailed), ), ); } }