snap_wish/lib/presentation/providers/share_provider.dart
2025-09-17 13:32:25 +08:00

403 lines
12 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
import '../../domain/repositories/share_repository.dart';
import '../../data/datasources/share/share_intent_datasource.dart';
import '../../data/repositories/share_repository_impl.dart';
import '../../core/utils/logger.dart';
/// 分享状态类 - 管理分享相关的UI状态
/// 包含分享文件列表、处理状态、错误信息等
class ShareState {
/// 待处理的分享文件列表
final List<SharedMediaFile> pendingFiles;
/// 是否正在处理分享
final bool isProcessing;
/// 处理错误信息
final String? error;
/// 是否显示分享界面
final bool showShareUI;
/// 当前处理的批次索引
final int currentBatchIndex;
/// 总批次数
final int totalBatches;
/// 是否正在批量处理
final bool isBatchProcessing;
/// 是否有待处理的分享
bool get hasPendingShare => pendingFiles.isNotEmpty;
/// 构造函数 - 创建分享状态实例
const ShareState({
this.pendingFiles = const [],
this.isProcessing = false,
this.error,
this.showShareUI = false,
this.currentBatchIndex = 0,
this.totalBatches = 0,
this.isBatchProcessing = false,
});
/// 复制构造函数 - 创建状态副本并支持字段更新
ShareState copyWith({
List<SharedMediaFile>? pendingFiles,
bool? isProcessing,
String? error,
bool? showShareUI,
int? currentBatchIndex,
int? totalBatches,
bool? isBatchProcessing,
}) {
return ShareState(
pendingFiles: pendingFiles ?? this.pendingFiles,
isProcessing: isProcessing ?? this.isProcessing,
error: error,
showShareUI: showShareUI ?? this.showShareUI,
currentBatchIndex: currentBatchIndex ?? this.currentBatchIndex,
totalBatches: totalBatches ?? this.totalBatches,
isBatchProcessing: isBatchProcessing ?? this.isBatchProcessing,
);
}
}
/// 分享状态Notifier - 管理分享状态和业务逻辑
class ShareNotifier extends StateNotifier<ShareState> {
/// 分享仓库 - 处理分享相关的业务逻辑
final ShareRepository _shareRepository;
/// 分享数据流订阅 - 监听分享数据变化
StreamSubscription<List<SharedMediaFile>>? _shareSubscription;
ShareNotifier(this._shareRepository) : super(const ShareState()) {
_initializeShareListening();
}
/// 初始化分享监听 - 设置分享数据流监听
void _initializeShareListening() {
try {
Logger.info('初始化分享状态监听...');
// 监听分享数据流
_shareSubscription = _shareRepository.sharingStream?.listen(
handleSharedFiles,
onError: (error) {
Logger.error('分享数据流错误', error: error);
state = state.copyWith(error: '分享接收错误: $error');
},
);
// 检查当前是否有待处理的分享
_checkPendingShares();
Logger.info('分享状态监听初始化完成');
} catch (e) {
Logger.error('初始化分享监听失败', error: e);
state = state.copyWith(error: '初始化分享功能失败: $e');
}
}
/// 检查待处理的分享 - 检查当前是否有待处理的分享文件
void _checkPendingShares() {
try {
final pendingFiles = _shareRepository.getPendingShareFiles();
final hasShares = _shareRepository.hasPendingShare();
Logger.info('检查待处理分享: ${pendingFiles.length} 个文件');
if (hasShares && pendingFiles.isNotEmpty) {
state = state.copyWith(
pendingFiles: pendingFiles,
showShareUI: true,
error: null,
);
Logger.info('发现待处理分享,显示分享界面');
} else {
state = state.copyWith(
pendingFiles: [],
showShareUI: false,
error: null,
);
}
} catch (e) {
Logger.error('检查待处理分享失败', error: e);
state = state.copyWith(error: '检查分享状态失败: $e');
}
}
/// 处理接收到的分享文件 - 处理新的分享数据
/// [sharedFiles] 接收到的分享文件列表
Future<void> handleSharedFiles(List<SharedMediaFile> sharedFiles) async {
if (sharedFiles.isEmpty) {
Logger.warning('接收到的分享文件列表为空');
return;
}
try {
Logger.info('处理新的分享文件: ${sharedFiles.length}');
state = state.copyWith(
pendingFiles: sharedFiles,
showShareUI: true,
isProcessing: false,
error: null,
);
// 记录详细的文件信息
for (final file in sharedFiles) {
Logger.info('分享文件: ${file.path}, 类型: ${file.type}');
}
} catch (e) {
Logger.error('处理分享文件失败', error: e);
state = state.copyWith(
error: '处理分享文件失败: $e',
isProcessing: false,
);
}
}
/// 开始保存分享图片 - 开始批量保存分享图片,支持大量文件分批保存
/// [folderId] 目标文件夹ID可为空
/// [tags] 要添加的标签列表,可为空
/// [note] 备注内容,可为空
Future<void> startSavingSharedImages({
String? folderId,
List<String>? tags,
String? note,
}) async {
if (state.pendingFiles.isEmpty) {
Logger.warning('没有待保存的分享图片');
return;
}
try {
Logger.info('开始保存分享图片,共${state.pendingFiles.length}个文件');
// 初始化批量处理状态
final totalFiles = state.pendingFiles.length;
const batchSize = 10; // 每批处理10个文件
final totalBatches = (totalFiles / batchSize).ceil();
state = state.copyWith(
isProcessing: true,
isBatchProcessing: totalBatches > 1,
totalBatches: totalBatches,
currentBatchIndex: 0,
error: null,
);
if (totalBatches > 1) {
Logger.info('启用批量保存模式,共$totalBatches批,每批$batchSize个文件');
await _batchSaveSharedImages(folderId, tags, note, batchSize);
} else {
Logger.info('普通保存模式,直接处理$totalFiles个文件');
await _simulateSaveProcess(folderId, tags, note);
}
// 保存完成后清除分享数据
_shareRepository.clearCurrentShare();
state = state.copyWith(
pendingFiles: [],
isProcessing: false,
isBatchProcessing: false,
showShareUI: false,
currentBatchIndex: 0,
totalBatches: 0,
error: null,
);
Logger.info('分享图片保存完成');
} catch (e) {
Logger.error('保存分享图片失败', error: e);
state = state.copyWith(
isProcessing: false,
isBatchProcessing: false,
error: '保存图片失败: $e',
);
}
}
/// 批量保存分享图片 - 分批处理大量分享图片
/// [folderId] 目标文件夹ID
/// [tags] 标签列表
/// [note] 备注内容
/// [batchSize] 每批处理的大小
Future<void> _batchSaveSharedImages(
String? folderId,
List<String>? tags,
String? note,
int batchSize,
) async {
final batches = _createBatches(state.pendingFiles, batchSize);
Logger.info('开始分批保存,共${batches.length}');
for (int i = 0; i < batches.length; i++) {
final batch = batches[i];
final batchNumber = i + 1;
Logger.info('处理第$batchNumber批,共${batch.length}个文件');
// 更新当前批次状态
state = state.copyWith(currentBatchIndex: batchNumber);
// 模拟处理当前批次
await _simulateBatchSaveProcess(batch, folderId, tags, note, batchNumber);
// 批次间短暂延迟,避免系统过载
if (i < batches.length - 1) {
await Future.delayed(const Duration(milliseconds: 200));
}
}
Logger.info('批量保存完成');
}
/// 创建文件批次 - 将文件列表分成指定大小的批次
/// [files] 文件列表
/// [batchSize] 每批的大小
/// 返回批次列表
List<List<SharedMediaFile>> _createBatches(List<SharedMediaFile> files, int batchSize) {
final batches = <List<SharedMediaFile>>[];
for (int i = 0; i < files.length; i += batchSize) {
final end = (i + batchSize < files.length) ? i + batchSize : files.length;
batches.add(files.sublist(i, end));
}
return batches;
}
/// 模拟批量保存过程 - 处理单个批次的保存
/// [batch] 当前批次的文件
/// [folderId] 目标文件夹ID
/// [tags] 标签列表
/// [note] 备注内容
/// [batchNumber] 批次编号
Future<void> _simulateBatchSaveProcess(
List<SharedMediaFile> batch,
String? folderId,
List<String>? tags,
String? note,
int batchNumber,
) async {
// 模拟保存过程,实际实现中会调用真实的保存逻辑
await Future.delayed(const Duration(seconds: 1));
Logger.info('$batchNumber批次保存完成,处理了${batch.length}个文件');
}
/// 模拟保存过程 - 临时模拟保存过程(后续替换为真实逻辑)
/// [folderId] 目标文件夹ID
/// [tags] 标签列表
/// [note] 备注内容
Future<void> _simulateSaveProcess(
String? folderId,
List<String>? tags,
String? note,
) async {
// 模拟保存过程
await Future.delayed(const Duration(seconds: 2));
Logger.info('模拟保存完成 - 文件夹: $folderId, 标签: $tags, 备注: $note');
}
/// 取消分享保存 - 取消当前的分享保存操作
void cancelShareSaving() {
Logger.info('取消分享保存');
// 清除当前分享数据
_shareRepository.clearCurrentShare();
state = const ShareState(
pendingFiles: [],
isProcessing: false,
showShareUI: false,
error: null,
);
}
/// 关闭分享界面 - 关闭分享处理界面
void closeShareUI() {
Logger.info('关闭分享界面');
state = state.copyWith(
showShareUI: false,
error: null,
);
}
/// 清除错误状态 - 清除当前的错误信息
void clearError() {
if (state.error != null) {
state = state.copyWith(error: null);
}
}
/// 刷新分享状态 - 重新检查分享状态
void refreshShareStatus() {
Logger.info('刷新分享状态');
_checkPendingShares();
}
/// 获取分享文件数量 - 获取当前待处理的分享文件数量
int getShareFileCount() {
return _shareRepository.getShareFileCount();
}
/// 释放资源 - 清理分享相关资源
@override
Future<void> dispose() async {
Logger.info('释放分享状态资源');
// 调用父类的dispose方法StateNotifier的dispose不是异步的
super.dispose();
// 取消分享数据流订阅
await _shareSubscription?.cancel();
_shareSubscription = null;
// 释放分享仓库
await _shareRepository.dispose();
}
}
/// 分享数据源Provider - 提供分享数据源实例
final shareDataSourceProvider = Provider<ShareIntentDataSource>((ref) {
return ShareIntentDataSource();
});
/// 图片工具Provider - 提供图片工具实例
/// ImageUtils是静态工具类不需要实例化直接提供工具引用
final imageUtilsProvider = Provider<Object>((ref) {
return Object(); // 占位Provider实际使用ImageUtils静态方法
});
/// 文件工具Provider - 提供文件工具实例
/// FileUtils是静态工具类不需要实例化直接提供工具引用
final fileUtilsProvider = Provider<Object>((ref) {
return Object(); // 占位Provider实际使用FileUtils静态方法
});
/// 分享仓库Provider - 提供分享仓库实例
final shareRepositoryProvider = Provider<ShareRepository>((ref) {
final shareDataSource = ref.read(shareDataSourceProvider);
// ImageUtils和FileUtils是静态工具类直接传入null占位
return ShareRepositoryImpl(
shareDataSource: shareDataSource,
);
});
/// 分享状态Provider - 管理分享相关的UI状态
final shareProvider = StateNotifierProvider<ShareNotifier, ShareState>((ref) {
final shareRepository = ref.read(shareRepositoryProvider);
return ShareNotifier(shareRepository);
});