From b2a6ca72447d9f2ff0304dc209f04aef2959983d Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Thu, 1 Aug 2024 23:10:19 +0800 Subject: [PATCH] :zap: Improve attachments queue performance --- lib/controllers/post_editor_controller.dart | 4 +- lib/providers/attachment_uploader.dart | 51 +++++++++++++---- .../attachments/attachment_editor.dart | 57 +++++++++---------- 3 files changed, 69 insertions(+), 43 deletions(-) diff --git a/lib/controllers/post_editor_controller.dart b/lib/controllers/post_editor_controller.dart index 1c5c346..5157107 100644 --- a/lib/controllers/post_editor_controller.dart +++ b/lib/controllers/post_editor_controller.dart @@ -115,10 +115,10 @@ class PostEditorController extends GetxController { builder: (context) => AttachmentEditorPopup( usage: 'i.attachment', initialAttachments: attachments, - onAdd: (value) { + onAdd: (int value) { attachments.add(value); }, - onRemove: (value) { + onRemove: (int value) { attachments.remove(value); }, ), diff --git a/lib/providers/attachment_uploader.dart b/lib/providers/attachment_uploader.dart index 0747463..3cedd94 100644 --- a/lib/providers/attachment_uploader.dart +++ b/lib/providers/attachment_uploader.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'dart:typed_data'; @@ -26,11 +27,40 @@ class AttachmentUploaderController extends GetxController { 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); @@ -40,8 +70,8 @@ class AttachmentUploaderController extends GetxController { isUploading.value = true; progressOfUpload.value = 0; + _startProgressSyncTimer(); queueOfUpload[queueIndex].isUploading = true; - queueOfUpload.refresh(); final task = queueOfUpload[queueIndex]; final result = await _rawUploadAttachment( @@ -51,13 +81,13 @@ class AttachmentUploaderController extends GetxController { null, onProgress: (value) { queueOfUpload[queueIndex].progress = value; - queueOfUpload.refresh(); - progressOfUpload.value = value; + _progressOfUpload = value; }, ); queueOfUpload.removeAt(queueIndex); - queueOfUpload.refresh(); + _stopProgressSyncTimer(); + _syncProgress(); isUploading.value = false; @@ -70,9 +100,10 @@ class AttachmentUploaderController extends GetxController { isUploading.value = true; progressOfUpload.value = 0; + _startProgressSyncTimer(); + for (var idx = 0; idx < queueOfUpload.length; idx++) { queueOfUpload[idx].isUploading = true; - queueOfUpload.refresh(); final task = queueOfUpload[idx]; final result = await _rawUploadAttachment( @@ -82,20 +113,20 @@ class AttachmentUploaderController extends GetxController { null, onProgress: (value) { queueOfUpload[idx].progress = value; - queueOfUpload.refresh(); - progressOfUpload.value = (idx + value) / queueOfUpload.length; + _progressOfUpload = (idx + value) / queueOfUpload.length; }, ); - progressOfUpload.value = (idx + 1) / queueOfUpload.length; + _progressOfUpload = (idx + 1) / queueOfUpload.length; onData(result); queueOfUpload[idx].isUploading = false; queueOfUpload[idx].isCompleted = false; - queueOfUpload.refresh(); } queueOfUpload.clear(); - queueOfUpload.refresh(); + _stopProgressSyncTimer(); + _syncProgress(); + isUploading.value = false; } diff --git a/lib/widgets/attachments/attachment_editor.dart b/lib/widgets/attachments/attachment_editor.dart index db40ef6..bc3589c 100644 --- a/lib/widgets/attachments/attachment_editor.dart +++ b/lib/widgets/attachments/attachment_editor.dart @@ -56,12 +56,10 @@ class _AttachmentEditorPopupState extends State { final medias = await _imagePicker.pickMultiImage(); if (medias.isEmpty) return; - for (final media in medias) { - final file = File(media.path); - _enqueueTask( - AttachmentUploadTask(file: file, usage: widget.usage), - ); - } + _enqueueTaskBatch(medias.map((x) { + final file = File(x.path); + return AttachmentUploadTask(file: file, usage: widget.usage); + })); } Future _pickVideoToUpload() async { @@ -88,11 +86,9 @@ class _AttachmentEditorPopupState extends State { List files = result.paths.map((path) => File(path!)).toList(); - for (final file in files) { - _enqueueTask( - AttachmentUploadTask(file: file, usage: widget.usage), - ); - } + _enqueueTaskBatch(files.map((x) { + return AttachmentUploadTask(file: x, usage: widget.usage); + })); } Future _takeMediaToUpload(bool isVideo) async { @@ -159,7 +155,11 @@ class _AttachmentEditorPopupState extends State { _isFirstTimeBusy = false; return; } else { - _attachments = List.filled(widget.initialAttachments.length, null); + _attachments = List.filled( + widget.initialAttachments.length, + null, + growable: true, + ); } setState(() => _isBusy = true); @@ -239,20 +239,9 @@ class _AttachmentEditorPopupState extends State { fontFamily: 'monospace', ), ), - FutureBuilder( - future: element.file.length(), - builder: (context, snapshot) { - if (!snapshot.hasData) { - return const Text( - '- Bytes', - style: TextStyle(fontSize: 12), - ); - } - return Text( - _formatBytes(snapshot.data!), - style: const TextStyle(fontSize: 12), - ); - }, + Text( + 'In queue #${index + 1}', + style: const TextStyle(fontSize: 12), ), ], ), @@ -410,6 +399,13 @@ class _AttachmentEditorPopupState extends State { } } + void _enqueueTaskBatch(Iterable tasks) { + _uploadController.enqueueTaskBatch(tasks); + if (_isAutoUpload) { + _startUploading(); + } + } + void _startUploading() { _uploadController.performUploadQueue(onData: (r) { widget.onAdd(r.id); @@ -433,11 +429,10 @@ class _AttachmentEditorPopupState extends State { child: DropTarget( onDragDone: (detail) async { if (_uploadController.isUploading.value) return; - for (final file in detail.files) { - _enqueueTask( - AttachmentUploadTask(file: File(file.path), usage: widget.usage), - ); - } + _enqueueTaskBatch(detail.files.map((x) { + final file = File(x.path); + return AttachmentUploadTask(file: file, usage: widget.usage); + })); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start,