2024-08-01 15:10:19 +00:00
|
|
|
import 'dart:async';
|
2024-08-01 14:13:08 +00:00
|
|
|
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<String, dynamic>? metadata;
|
|
|
|
|
|
|
|
double progress = 0;
|
|
|
|
bool isUploading = false;
|
|
|
|
bool isCompleted = false;
|
2024-08-03 13:29:48 +00:00
|
|
|
dynamic error;
|
2024-08-01 14:13:08 +00:00
|
|
|
|
|
|
|
AttachmentUploadTask({
|
|
|
|
required this.file,
|
|
|
|
required this.usage,
|
|
|
|
this.metadata,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
class AttachmentUploaderController extends GetxController {
|
|
|
|
RxBool isUploading = false.obs;
|
|
|
|
RxDouble progressOfUpload = 0.0.obs;
|
|
|
|
RxList<AttachmentUploadTask> queueOfUpload = RxList.empty(growable: true);
|
|
|
|
|
2024-08-01 15:10:19 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-08-01 14:13:08 +00:00
|
|
|
void enqueueTask(AttachmentUploadTask task) {
|
|
|
|
if (isUploading.value) throw Exception('uploading blocked');
|
|
|
|
queueOfUpload.add(task);
|
|
|
|
}
|
|
|
|
|
2024-08-01 15:10:19 +00:00
|
|
|
void enqueueTaskBatch(Iterable<AttachmentUploadTask> tasks) {
|
|
|
|
if (isUploading.value) throw Exception('uploading blocked');
|
|
|
|
queueOfUpload.addAll(tasks);
|
|
|
|
}
|
|
|
|
|
2024-08-01 14:13:08 +00:00
|
|
|
void dequeueTask(AttachmentUploadTask task) {
|
|
|
|
if (isUploading.value) throw Exception('uploading blocked');
|
|
|
|
queueOfUpload.remove(task);
|
|
|
|
}
|
|
|
|
|
2024-08-03 13:29:48 +00:00
|
|
|
Future<Attachment?> performSingleTask(int queueIndex) async {
|
2024-08-01 14:13:08 +00:00
|
|
|
isUploading.value = true;
|
|
|
|
progressOfUpload.value = 0;
|
|
|
|
|
2024-08-01 15:10:19 +00:00
|
|
|
_startProgressSyncTimer();
|
2024-08-01 14:13:08 +00:00
|
|
|
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;
|
2024-08-01 15:10:19 +00:00
|
|
|
_progressOfUpload = value;
|
2024-08-01 14:13:08 +00:00
|
|
|
},
|
2024-08-03 13:29:48 +00:00
|
|
|
onError: (err) {
|
|
|
|
queueOfUpload[queueIndex].error = err;
|
|
|
|
queueOfUpload[queueIndex].isUploading = false;
|
|
|
|
},
|
2024-08-01 14:13:08 +00:00
|
|
|
);
|
|
|
|
|
2024-08-03 13:29:48 +00:00
|
|
|
if (queueOfUpload[queueIndex].error == null) {
|
|
|
|
queueOfUpload.removeAt(queueIndex);
|
|
|
|
}
|
2024-08-01 15:10:19 +00:00
|
|
|
_stopProgressSyncTimer();
|
|
|
|
_syncProgress();
|
2024-08-01 14:13:08 +00:00
|
|
|
|
|
|
|
isUploading.value = false;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> performUploadQueue({
|
|
|
|
required Function(Attachment item) onData,
|
|
|
|
}) async {
|
|
|
|
isUploading.value = true;
|
|
|
|
progressOfUpload.value = 0;
|
|
|
|
|
2024-08-01 15:10:19 +00:00
|
|
|
_startProgressSyncTimer();
|
|
|
|
|
2024-08-01 14:13:08 +00:00
|
|
|
for (var idx = 0; idx < queueOfUpload.length; idx++) {
|
2024-08-03 13:29:48 +00:00
|
|
|
if (queueOfUpload[idx].isUploading || queueOfUpload[idx].error != null) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2024-08-01 14:13:08 +00:00
|
|
|
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;
|
2024-08-01 15:10:19 +00:00
|
|
|
_progressOfUpload = (idx + value) / queueOfUpload.length;
|
2024-08-01 14:13:08 +00:00
|
|
|
},
|
2024-08-03 13:29:48 +00:00
|
|
|
onError: (err) {
|
|
|
|
queueOfUpload[idx].error = err;
|
|
|
|
queueOfUpload[idx].isUploading = false;
|
|
|
|
},
|
2024-08-01 14:13:08 +00:00
|
|
|
);
|
2024-08-01 15:10:19 +00:00
|
|
|
_progressOfUpload = (idx + 1) / queueOfUpload.length;
|
2024-08-03 13:29:48 +00:00
|
|
|
if (result != null) onData(result);
|
2024-08-01 14:13:08 +00:00
|
|
|
|
|
|
|
queueOfUpload[idx].isUploading = false;
|
2024-08-03 13:29:48 +00:00
|
|
|
queueOfUpload[idx].isCompleted = true;
|
2024-08-01 14:13:08 +00:00
|
|
|
}
|
|
|
|
|
2024-08-09 17:17:31 +00:00
|
|
|
queueOfUpload.removeWhere((x) => x.error == null);
|
2024-08-01 15:10:19 +00:00
|
|
|
_stopProgressSyncTimer();
|
|
|
|
_syncProgress();
|
|
|
|
|
2024-08-01 14:13:08 +00:00
|
|
|
isUploading.value = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> uploadAttachmentWithCallback(
|
|
|
|
Uint8List data,
|
|
|
|
String path,
|
2024-08-18 14:51:52 +00:00
|
|
|
String pool,
|
2024-08-01 14:13:08 +00:00
|
|
|
Map<String, dynamic>? metadata,
|
2024-08-03 13:29:48 +00:00
|
|
|
Function(Attachment?) callback,
|
2024-08-01 14:13:08 +00:00
|
|
|
) async {
|
|
|
|
if (isUploading.value) throw Exception('uploading blocked');
|
|
|
|
|
|
|
|
isUploading.value = true;
|
|
|
|
final result = await _rawUploadAttachment(
|
|
|
|
data,
|
|
|
|
path,
|
2024-08-18 14:51:52 +00:00
|
|
|
pool,
|
2024-08-01 14:13:08 +00:00
|
|
|
metadata,
|
|
|
|
onProgress: (progress) {
|
|
|
|
progressOfUpload.value = progress;
|
|
|
|
},
|
|
|
|
);
|
|
|
|
isUploading.value = false;
|
|
|
|
callback(result);
|
|
|
|
}
|
|
|
|
|
2024-08-03 13:29:48 +00:00
|
|
|
Future<Attachment?> uploadAttachment(
|
2024-08-01 14:13:08 +00:00
|
|
|
Uint8List data,
|
|
|
|
String path,
|
2024-08-18 14:51:52 +00:00
|
|
|
String pool,
|
2024-08-01 14:13:08 +00:00
|
|
|
Map<String, dynamic>? metadata,
|
|
|
|
) async {
|
|
|
|
if (isUploading.value) throw Exception('uploading blocked');
|
|
|
|
|
|
|
|
isUploading.value = true;
|
|
|
|
final result = await _rawUploadAttachment(
|
|
|
|
data,
|
|
|
|
path,
|
2024-08-18 14:51:52 +00:00
|
|
|
pool,
|
2024-08-01 14:13:08 +00:00
|
|
|
metadata,
|
|
|
|
onProgress: (progress) {
|
|
|
|
progressOfUpload.value = progress;
|
|
|
|
},
|
|
|
|
);
|
|
|
|
isUploading.value = false;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2024-08-03 13:29:48 +00:00
|
|
|
Future<Attachment?> _rawUploadAttachment(
|
2024-08-18 14:51:52 +00:00
|
|
|
Uint8List data, String path, String pool, Map<String, dynamic>? metadata,
|
2024-08-03 13:29:48 +00:00
|
|
|
{Function(double)? onProgress, Function(dynamic err)? onError}) async {
|
2024-08-01 14:13:08 +00:00
|
|
|
final AttachmentProvider provider = Get.find();
|
|
|
|
try {
|
2024-08-02 07:49:32 +00:00
|
|
|
final result = await provider.createAttachment(
|
2024-08-01 14:13:08 +00:00
|
|
|
data,
|
|
|
|
path,
|
2024-08-18 14:51:52 +00:00
|
|
|
pool,
|
2024-08-01 14:13:08 +00:00
|
|
|
metadata,
|
|
|
|
onProgress: onProgress,
|
|
|
|
);
|
|
|
|
return result;
|
|
|
|
} catch (err) {
|
2024-08-03 13:29:48 +00:00
|
|
|
if (onError != null) {
|
|
|
|
onError(err);
|
|
|
|
}
|
|
|
|
return null;
|
2024-08-01 14:13:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|