Files
video/app/lib/providers/upload_provider.dart
2026-10-15 20:36:59 +08:00

117 lines
3.5 KiB
Dart

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:image_picker/image_picker.dart';
import '../core/network/api_exception.dart';
import '../services/upload_service.dart';
/// 上传发布流程步骤:选视频 → 填信息 → 发布中 → 完成
enum UploadStep { pick, edit, publishing, done }
/// 上传发布状态(《03-接口文档》5.5/5.6 三步链路)
class UploadState {
const UploadState({
this.step = UploadStep.pick,
this.file,
this.fileSize = 0,
this.progress = 0,
this.result,
this.error = '',
});
final UploadStep step;
final XFile? file; // 已选择的视频
final int fileSize; // 字节
final int progress; // 上传进度 0-100
final PublishResult? result;
final String error; // 发布失败文案(停留在 edit 步骤展示)
bool get publishing => step == UploadStep.publishing;
UploadState copyWith({
UploadStep? step,
XFile? file,
int? fileSize,
int? progress,
PublishResult? result,
String? error,
}) =>
UploadState(
step: step ?? this.step,
file: file ?? this.file,
fileSize: fileSize ?? this.fileSize,
progress: progress ?? this.progress,
result: result ?? this.result,
error: error ?? this.error,
);
}
class UploadNotifier extends StateNotifier<UploadState> {
UploadNotifier() : super(const UploadState());
final _picker = ImagePicker();
/// 从相册选择视频 → 进入填信息步骤
Future<void> pickVideo() async {
final file = await _picker.pickVideo(source: ImageSource.gallery);
if (file == null) return;
final size = await file.length();
state = UploadState(step: UploadStep.edit, file: file, fileSize: size);
}
/// 重新选择(回到选视频步骤)
void reset() => state = const UploadState();
/// 发布:获取凭证 → 上传文件(进度) → 创建视频
Future<void> submit({
required String title,
String description = '',
required int categoryId,
List<String> tags = const [],
}) async {
final file = state.file;
if (file == null || state.publishing) return;
state = state.copyWith(step: UploadStep.publishing, progress: 0, error: '');
try {
final token = await UploadService.fetchUploadToken(
fileName: file.name,
fileSize: state.fileSize,
);
await UploadService.uploadFile(
objectKey: token.objectKey,
filePath: file.path,
fileName: file.name,
onProgress: (sent, total) {
if (total > 0) {
state = state.copyWith(progress: (sent * 100) ~/ total);
}
},
);
final result = await UploadService.publish(
objectKey: token.objectKey,
title: title,
description: description,
categoryId: categoryId,
tags: tags,
);
state = state.copyWith(step: UploadStep.done, result: result);
} on ApiException catch (e) {
// 失败回到表单步骤,保留已填内容,展示服务端文案
state = state.copyWith(step: UploadStep.edit, error: e.message);
} catch (_) {
state = state.copyWith(step: UploadStep.edit, error: '网络异常,请稍后重试');
}
}
}
/// 上传发布(autoDispose:离开页面即重置流程)
final uploadProvider =
StateNotifierProvider.autoDispose<UploadNotifier, UploadState>(
(ref) => UploadNotifier(),
);
/// 分类列表(发布表单用)
final uploadCategoriesProvider =
FutureProvider.autoDispose<List<VideoCategory>>(
(ref) => UploadService.fetchCategories(),
);