import 'dart:async'; import 'dart:io'; import 'dart:typed_data'; import 'package:get/get.dart'; import 'package:solian/models/attachment.dart'; import 'package:solian/providers/content/attachment.dart'; class AttachmentUploadTask { File file; String usage; Map? metadata; double progress = 0; bool isUploading = false; bool isCompleted = false; AttachmentUploadTask({ required this.file, required this.usage, this.metadata, }); } class AttachmentUploaderController extends GetxController { RxBool isUploading = false.obs; RxDouble progressOfUpload = 0.0.obs; RxList queueOfUpload = RxList.empty(growable: true); Timer? _progressSyncTimer; double _progressOfUpload = 0.0; void _syncProgress() { progressOfUpload.value = _progressOfUpload; queueOfUpload.refresh(); } void _startProgressSyncTimer() { if (_progressSyncTimer != null) { _progressSyncTimer!.cancel(); } _progressSyncTimer = Timer.periodic( const Duration(milliseconds: 500), (_) => _syncProgress(), ); } void _stopProgressSyncTimer() { if (_progressSyncTimer == null) return; _progressSyncTimer!.cancel(); _progressSyncTimer = null; } void enqueueTask(AttachmentUploadTask task) { if (isUploading.value) throw Exception('uploading blocked'); queueOfUpload.add(task); } void enqueueTaskBatch(Iterable tasks) { if (isUploading.value) throw Exception('uploading blocked'); queueOfUpload.addAll(tasks); } void dequeueTask(AttachmentUploadTask task) { if (isUploading.value) throw Exception('uploading blocked'); queueOfUpload.remove(task); } Future performSingleTask(int queueIndex) async { isUploading.value = true; progressOfUpload.value = 0; _startProgressSyncTimer(); queueOfUpload[queueIndex].isUploading = true; final task = queueOfUpload[queueIndex]; final result = await _rawUploadAttachment( await task.file.readAsBytes(), task.file.path, task.usage, null, onProgress: (value) { queueOfUpload[queueIndex].progress = value; _progressOfUpload = value; }, ); queueOfUpload.removeAt(queueIndex); _stopProgressSyncTimer(); _syncProgress(); isUploading.value = false; return result; } Future performUploadQueue({ required Function(Attachment item) onData, }) async { isUploading.value = true; progressOfUpload.value = 0; _startProgressSyncTimer(); for (var idx = 0; idx < queueOfUpload.length; idx++) { queueOfUpload[idx].isUploading = true; final task = queueOfUpload[idx]; final result = await _rawUploadAttachment( await task.file.readAsBytes(), task.file.path, task.usage, null, onProgress: (value) { queueOfUpload[idx].progress = value; _progressOfUpload = (idx + value) / queueOfUpload.length; }, ); _progressOfUpload = (idx + 1) / queueOfUpload.length; onData(result); queueOfUpload[idx].isUploading = false; queueOfUpload[idx].isCompleted = false; } queueOfUpload.clear(); _stopProgressSyncTimer(); _syncProgress(); isUploading.value = false; } Future uploadAttachmentWithCallback( Uint8List data, String path, String usage, Map? metadata, Function(Attachment) callback, ) async { if (isUploading.value) throw Exception('uploading blocked'); isUploading.value = true; final result = await _rawUploadAttachment( data, path, usage, metadata, onProgress: (progress) { progressOfUpload.value = progress; }, ); isUploading.value = false; callback(result); } Future uploadAttachment( Uint8List data, String path, String usage, Map? metadata, ) async { if (isUploading.value) throw Exception('uploading blocked'); isUploading.value = true; final result = await _rawUploadAttachment( data, path, usage, metadata, onProgress: (progress) { progressOfUpload.value = progress; }, ); isUploading.value = false; return result; } Future _rawUploadAttachment( Uint8List data, String path, String usage, Map? metadata, {Function(double)? onProgress}) async { final AttachmentProvider provider = Get.find(); try { final resp = await provider.createAttachment( data, path, usage, metadata, onProgress: onProgress, ); var result = Attachment.fromJson(resp.body); return result; } catch (err) { rethrow; } } }