Files
ReelShort/lib/pages/player/player_view_actions.dart

189 lines
6.0 KiB
Dart

// Author: fruit.com.cn
part of 'player_view.dart';
extension _PlayerViewActions on _PlayerPageState {
Future<void> _openSpeedSheet(BuildContext context) async {
final selected = await showModalBottomSheet<double>(
context: context,
backgroundColor: AppColors.transparent,
showDragHandle: false,
builder: (_) => PlayerSpeedSheet(selected: _playbackSpeed),
);
if (selected == null || !mounted) return;
_updatePlaybackSpeed(selected);
await logic.setPlaybackSpeed(selected);
}
Future<void> _openMore(BuildContext context, Episode episode) async {
final l10n = AppLocalizations.of(context)!;
await showModalBottomSheet<void>(
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),
// iOS 不显示手动「画中画」入口(走下方「退出后自动小窗」开关)。
onPip: logic.pictureInPicture.isPlatformSupported && !logic.iosDevice
? () => unawaited(logic.enterPip(context))
: null,
autoPipEnabled: logic.iosAutoPipEnabled,
onAutoPipChanged: logic.iosDevice
? (value) => unawaited(logic.setIosAutoPip(value))
: null,
),
),
);
}
Future<bool> _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<void> _openUnlockSheet(
BuildContext context,
int index,
Episode episode,
) async {
final wallet = logic.wallet;
if (wallet == null) return;
if (!context.read<AuthController>().isSignedIn &&
!await ensureSignedIn(context)) {
return;
}
if (!context.mounted) return;
await showModalBottomSheet<bool>(
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<bool> _unlockEpisode(BuildContext context, int index) async {
if (!context.read<AuthController>().isSignedIn) 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<void> _toggleLike(
BuildContext context,
InteractionController interactions,
Episode episode, {
bool likeOnly = false,
}) async {
final busyBefore = interactions.isEpisodeBusy(episode.id);
final likedBefore = interactions.isEpisodeLiked(episode.id);
if (kDebugMode) {
debugPrint(
'[player-like] phase=toggle-start '
'episodeId=${episode.id} likeOnly=$likeOnly '
'busy=$busyBefore liked=$likedBefore',
);
}
unawaited(context.read<Haptics>().lightImpact());
if (busyBefore || (likeOnly && likedBefore)) {
if (kDebugMode) {
debugPrint(
'[player-like] phase=toggle-skipped '
'episodeId=${episode.id} '
'reason=${busyBefore ? 'busy' : 'already-liked'}',
);
}
return;
}
final result = await interactions.toggleEpisodeLike(episode.id);
if (kDebugMode) {
debugPrint(
'[player-like] phase=toggle-result '
'episodeId=${episode.id} success=${result.isSuccess} '
'liked=${interactions.isEpisodeLiked(episode.id)} '
'count=${interactions.episodeLikeCount(episode.id)} '
'error=${result.errorMessage ?? '-'}',
);
}
if (result.isSuccess) {
final likeCount = interactions.episodeLikeCount(episode.id);
if (likeCount != null) {
logic.applyEpisodeLikeState(
episode.id,
liked: interactions.isEpisodeLiked(episode.id),
likeCount: likeCount,
);
}
_refreshLikeState();
return;
}
if (!context.mounted) return;
if (result.errorCode == 200007) {
await ensureSignedIn(context);
return;
}
showCenteredToast(
context,
result.errorMessage ?? AppLocalizations.of(context)!.likeFailed,
);
}
Future<void> _toggleSave(BuildContext context, Drama drama) async {
final favorites = context.read<FavoriteController>();
unawaited(context.read<Haptics>().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) {
logic.markDramaFavoriteChanged(drama.id);
}
return;
}
if (favorites.lastErrorCode == 200007) {
await ensureSignedIn(context);
return;
}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.favoriteToggleFailed),
),
);
}
}