⚡ Improve attachments queue performance
This commit is contained in:
		@@ -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);
 | 
			
		||||
        },
 | 
			
		||||
      ),
 | 
			
		||||
 
 | 
			
		||||
@@ -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<AttachmentUploadTask> 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<AttachmentUploadTask> 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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -56,12 +56,10 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
 | 
			
		||||
    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<void> _pickVideoToUpload() async {
 | 
			
		||||
@@ -88,11 +86,9 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
 | 
			
		||||
 | 
			
		||||
    List<File> 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<void> _takeMediaToUpload(bool isVideo) async {
 | 
			
		||||
@@ -159,7 +155,11 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
 | 
			
		||||
      _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<AttachmentEditorPopup> {
 | 
			
		||||
                            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<AttachmentEditorPopup> {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void _enqueueTaskBatch(Iterable<AttachmentUploadTask> 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<AttachmentEditorPopup> {
 | 
			
		||||
      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,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user