add
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
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(),
|
||||
);
|
||||
Reference in New Issue
Block a user