Files
ReelShort/lib/pages/drama_detail/drama_detail_logic.dart
T

130 lines
3.7 KiB
Dart

// Author: fruit.com.cn
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../core/data/models/drama.dart';
import '../../core/data/models/watch_progress.dart';
import '../../core/data/remote/api_client.dart';
import '../../core/data/repositories/watch_history_repository.dart';
import '../../core/data/repositories/playback_repository.dart';
import '../../core/data/services/favorite_controller.dart';
import '../../core/data/services/download_controller.dart';
import '../../core/data/services/wallet_controller.dart';
import '../player/player_view.dart';
class DramaDetailLogic extends ChangeNotifier {
DramaDetailLogic({
required this.drama,
required this.history,
required this.playback,
required this.favorites,
});
/// 先用列表卡片渲染,取到详情后换成带完整剧集的版本。
Drama drama;
final WatchHistoryRepository history;
final PlaybackRepository playback;
final FavoriteController favorites;
bool loading = false;
bool failed = false;
/// 接口给出的失败原因;为空时界面回退到本地文案。
String? failureMessage;
int? continueIndex;
WatchProgress? progress;
String? progressError;
bool _disposed = false;
void notify() {
if (!_disposed) notifyListeners();
}
/// 列表卡片只带一集可播预览,进详情时补一次完整剧集列表。
Future<void> load(Future<Drama> Function() fetch) async {
if (drama.detailLoaded) {
await loadContinue();
return;
}
loading = true;
failed = false;
failureMessage = null;
notify();
try {
drama = await fetch();
await loadContinue();
} on ApiException catch (exception) {
failed = true;
failureMessage = exception.message;
} on DioException {
// 断网/超时:交给 l10n 文案,不把 HTTP 细节丢给用户
failed = true;
failureMessage = 'networkError';
} catch (_) {
failed = true;
} finally {
loading = false;
notify();
}
}
Future<void> loadContinue() async {
try {
final record = await history.progressFor(drama.id);
if (_disposed) return;
progress = record;
progressError = null;
continueIndex = record == null
? null
: _indexOf(record.episodeId) ?? continueIndex;
notify();
} catch (exception) {
if (_disposed) return;
// 拿不到续看位置不影响看第一集,但要让用户知道(可重试)
progressError = exception is ApiException
? exception.message
: 'progressError';
notify();
}
}
int? _indexOf(String episodeId) {
final index = drama.episodes.indexWhere(
(episode) => episode.id == episodeId,
);
return index >= 0 ? index : null;
}
/// 该集的续看进度(0~1),用于剧集行上的进度条;没有记录返回 null。
double? progressFractionFor(String episodeId) {
final record = progress;
if (record == null || record.episodeId != episodeId) return null;
if (!record.hasPosition) return null;
return record.fraction;
}
Future<void> openPlayer(BuildContext context, int index) async {
await Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => PlayerPage(
drama: drama,
initialIndex: index,
wallet: context.read<WalletController>(),
downloads: context.read<DownloadController>(),
),
),
);
await loadContinue();
}
/// 收藏切换:乐观更新失败时会回滚,由调用方给出提示。
Future<bool> toggleFavorite() => favorites.toggle(drama.id, card: drama);
@override
void dispose() {
_disposed = true;
super.dispose();
}
}