Compare commits

..

No commits in common. "b2a6ca72447d9f2ff0304dc209f04aef2959983d" and "8b3c45ab294d091116346c44d5328bb2e3b1ce14" have entirely different histories.

8 changed files with 49 additions and 109 deletions

View File

@ -115,10 +115,10 @@ class PostEditorController extends GetxController {
builder: (context) => AttachmentEditorPopup( builder: (context) => AttachmentEditorPopup(
usage: 'i.attachment', usage: 'i.attachment',
initialAttachments: attachments, initialAttachments: attachments,
onAdd: (int value) { onAdd: (value) {
attachments.add(value); attachments.add(value);
}, },
onRemove: (int value) { onRemove: (value) {
attachments.remove(value); attachments.remove(value);
}, },
), ),

View File

@ -1,4 +1,3 @@
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
@ -27,40 +26,11 @@ class AttachmentUploaderController extends GetxController {
RxDouble progressOfUpload = 0.0.obs; RxDouble progressOfUpload = 0.0.obs;
RxList<AttachmentUploadTask> queueOfUpload = RxList.empty(growable: true); 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) { void enqueueTask(AttachmentUploadTask task) {
if (isUploading.value) throw Exception('uploading blocked'); if (isUploading.value) throw Exception('uploading blocked');
queueOfUpload.add(task); queueOfUpload.add(task);
} }
void enqueueTaskBatch(Iterable<AttachmentUploadTask> tasks) {
if (isUploading.value) throw Exception('uploading blocked');
queueOfUpload.addAll(tasks);
}
void dequeueTask(AttachmentUploadTask task) { void dequeueTask(AttachmentUploadTask task) {
if (isUploading.value) throw Exception('uploading blocked'); if (isUploading.value) throw Exception('uploading blocked');
queueOfUpload.remove(task); queueOfUpload.remove(task);
@ -70,8 +40,8 @@ class AttachmentUploaderController extends GetxController {
isUploading.value = true; isUploading.value = true;
progressOfUpload.value = 0; progressOfUpload.value = 0;
_startProgressSyncTimer();
queueOfUpload[queueIndex].isUploading = true; queueOfUpload[queueIndex].isUploading = true;
queueOfUpload.refresh();
final task = queueOfUpload[queueIndex]; final task = queueOfUpload[queueIndex];
final result = await _rawUploadAttachment( final result = await _rawUploadAttachment(
@ -81,13 +51,13 @@ class AttachmentUploaderController extends GetxController {
null, null,
onProgress: (value) { onProgress: (value) {
queueOfUpload[queueIndex].progress = value; queueOfUpload[queueIndex].progress = value;
_progressOfUpload = value; queueOfUpload.refresh();
progressOfUpload.value = value;
}, },
); );
queueOfUpload.removeAt(queueIndex); queueOfUpload.removeAt(queueIndex);
_stopProgressSyncTimer(); queueOfUpload.refresh();
_syncProgress();
isUploading.value = false; isUploading.value = false;
@ -100,10 +70,9 @@ class AttachmentUploaderController extends GetxController {
isUploading.value = true; isUploading.value = true;
progressOfUpload.value = 0; progressOfUpload.value = 0;
_startProgressSyncTimer();
for (var idx = 0; idx < queueOfUpload.length; idx++) { for (var idx = 0; idx < queueOfUpload.length; idx++) {
queueOfUpload[idx].isUploading = true; queueOfUpload[idx].isUploading = true;
queueOfUpload.refresh();
final task = queueOfUpload[idx]; final task = queueOfUpload[idx];
final result = await _rawUploadAttachment( final result = await _rawUploadAttachment(
@ -113,20 +82,20 @@ class AttachmentUploaderController extends GetxController {
null, null,
onProgress: (value) { onProgress: (value) {
queueOfUpload[idx].progress = value; queueOfUpload[idx].progress = value;
_progressOfUpload = (idx + value) / queueOfUpload.length; queueOfUpload.refresh();
progressOfUpload.value = (idx + value) / queueOfUpload.length;
}, },
); );
_progressOfUpload = (idx + 1) / queueOfUpload.length; progressOfUpload.value = (idx + 1) / queueOfUpload.length;
onData(result); onData(result);
queueOfUpload[idx].isUploading = false; queueOfUpload[idx].isUploading = false;
queueOfUpload[idx].isCompleted = false; queueOfUpload[idx].isCompleted = false;
queueOfUpload.refresh();
} }
queueOfUpload.clear(); queueOfUpload.clear();
_stopProgressSyncTimer(); queueOfUpload.refresh();
_syncProgress();
isUploading.value = false; isUploading.value = false;
} }

View File

@ -8,7 +8,6 @@ import 'package:solian/controllers/post_editor_controller.dart';
import 'package:solian/exts.dart'; import 'package:solian/exts.dart';
import 'package:solian/models/post.dart'; import 'package:solian/models/post.dart';
import 'package:solian/models/realm.dart'; import 'package:solian/models/realm.dart';
import 'package:solian/providers/attachment_uploader.dart';
import 'package:solian/providers/auth.dart'; import 'package:solian/providers/auth.dart';
import 'package:solian/router.dart'; import 'package:solian/router.dart';
import 'package:solian/theme.dart'; import 'package:solian/theme.dart';
@ -57,14 +56,6 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
if (auth.isAuthorized.isFalse) return; if (auth.isAuthorized.isFalse) return;
if (_editorController.isEmpty) return; if (_editorController.isEmpty) return;
final AttachmentUploaderController uploader = Get.find();
if (uploader.queueOfUpload.any(
((x) => x.usage == 'i.attachment' && x.isUploading),
)) {
context.showErrorDialog('attachmentUploadInProgress'.tr);
return;
}
setState(() => _isBusy = true); setState(() => _isBusy = true);
final client = auth.configureClient('interactive'); final client = auth.configureClient('interactive');
@ -101,16 +92,15 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
} }
} }
void _cancelAction() { void cancelAction() {
_editorController.localClear(); _editorController.localClear();
AppRouter.instance.pop(); AppRouter.instance.pop();
} }
Post? get _editTo => _editorController.editTo.value; Post? get _editTo => _editorController.editTo.value;
Post? get _replyTo => _editorController.replyTo.value; Post? get _replyTo => _editorController.replyTo.value;
Post? get _repostTo => _editorController.repostTo.value; Post? get _repostTo => _editorController.repostTo.value;
Realm? get _realm => _editorController.realmZone.value;
@override @override
void initState() { void initState() {
@ -124,7 +114,7 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final notifyBannerActions = [ final notifyBannerActions = [
TextButton( TextButton(
onPressed: _cancelAction, onPressed: cancelAction,
child: Text('cancel'.tr), child: Text('cancel'.tr),
) )
]; ];

View File

@ -1,7 +1,7 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
abstract class ServiceFinder { abstract class ServiceFinder {
static const bool devFlag = true; static const bool devFlag = false;
static const String dealerUrl = static const String dealerUrl =
devFlag ? 'http://localhost:8442' : 'https://api.sn.solsynth.dev'; devFlag ? 'http://localhost:8442' : 'https://api.sn.solsynth.dev';

View File

@ -160,7 +160,6 @@ const i18nEnglish = {
'attachmentAutoUpload': 'Auto Upload', 'attachmentAutoUpload': 'Auto Upload',
'attachmentUploadQueue': 'Upload Queue', 'attachmentUploadQueue': 'Upload Queue',
'attachmentUploadQueueStart': 'Start All', 'attachmentUploadQueueStart': 'Start All',
'attachmentUploadInProgress': 'There are attachments being uploaded. Please wait until all attachments have been uploaded before proceeding...',
'attachmentAttached': 'Exists Files', 'attachmentAttached': 'Exists Files',
'attachmentUploadBlocked': 'Upload blocked, there is currently a task in progress...', 'attachmentUploadBlocked': 'Upload blocked, there is currently a task in progress...',
'attachmentAdd': 'Attach attachments', 'attachmentAdd': 'Attach attachments',

View File

@ -149,7 +149,6 @@ const i18nSimplifiedChinese = {
'attachmentAutoUpload': '自动上传', 'attachmentAutoUpload': '自动上传',
'attachmentUploadQueue': '上传队列', 'attachmentUploadQueue': '上传队列',
'attachmentUploadQueueStart': '整队上传', 'attachmentUploadQueueStart': '整队上传',
'attachmentUploadInProgress': '有附件正在上传,请等待所有附件上传完毕后再进行操作……',
'attachmentAttached': '已附附件', 'attachmentAttached': '已附附件',
'attachmentUploadBlocked': '上传受阻,当前已有任务进行中……', 'attachmentUploadBlocked': '上传受阻,当前已有任务进行中……',
'attachmentAdd': '附加附件', 'attachmentAdd': '附加附件',

View File

@ -56,10 +56,12 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
final medias = await _imagePicker.pickMultiImage(); final medias = await _imagePicker.pickMultiImage();
if (medias.isEmpty) return; if (medias.isEmpty) return;
_enqueueTaskBatch(medias.map((x) { for (final media in medias) {
final file = File(x.path); final file = File(media.path);
return AttachmentUploadTask(file: file, usage: widget.usage); _enqueueTask(
})); AttachmentUploadTask(file: file, usage: widget.usage),
);
}
} }
Future<void> _pickVideoToUpload() async { Future<void> _pickVideoToUpload() async {
@ -86,9 +88,11 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
List<File> files = result.paths.map((path) => File(path!)).toList(); List<File> files = result.paths.map((path) => File(path!)).toList();
_enqueueTaskBatch(files.map((x) { for (final file in files) {
return AttachmentUploadTask(file: x, usage: widget.usage); _enqueueTask(
})); AttachmentUploadTask(file: file, usage: widget.usage),
);
}
} }
Future<void> _takeMediaToUpload(bool isVideo) async { Future<void> _takeMediaToUpload(bool isVideo) async {
@ -155,11 +159,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
_isFirstTimeBusy = false; _isFirstTimeBusy = false;
return; return;
} else { } else {
_attachments = List.filled( _attachments = List.filled(widget.initialAttachments.length, null);
widget.initialAttachments.length,
null,
growable: true,
);
} }
setState(() => _isBusy = true); setState(() => _isBusy = true);
@ -239,9 +239,20 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
fontFamily: 'monospace', fontFamily: 'monospace',
), ),
), ),
Text( FutureBuilder(
'In queue #${index + 1}', 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), style: const TextStyle(fontSize: 12),
);
},
), ),
], ],
), ),
@ -399,13 +410,6 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
} }
} }
void _enqueueTaskBatch(Iterable<AttachmentUploadTask> tasks) {
_uploadController.enqueueTaskBatch(tasks);
if (_isAutoUpload) {
_startUploading();
}
}
void _startUploading() { void _startUploading() {
_uploadController.performUploadQueue(onData: (r) { _uploadController.performUploadQueue(onData: (r) {
widget.onAdd(r.id); widget.onAdd(r.id);
@ -429,10 +433,11 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
child: DropTarget( child: DropTarget(
onDragDone: (detail) async { onDragDone: (detail) async {
if (_uploadController.isUploading.value) return; if (_uploadController.isUploading.value) return;
_enqueueTaskBatch(detail.files.map((x) { for (final file in detail.files) {
final file = File(x.path); _enqueueTask(
return AttachmentUploadTask(file: file, usage: widget.usage); AttachmentUploadTask(file: File(file.path), usage: widget.usage),
})); );
}
}, },
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -450,12 +455,11 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
const SizedBox(width: 10), const SizedBox(width: 10),
Obx(() { Obx(() {
if (_uploadController.isUploading.value) { if (_uploadController.isUploading.value) {
return SizedBox( return const SizedBox(
width: 18, width: 18,
height: 18, height: 18,
child: CircularProgressIndicator( child: CircularProgressIndicator(
strokeWidth: 2.5, strokeWidth: 2.5,
value: _uploadController.progressOfUpload.value,
), ),
); );
} }

View File

@ -5,11 +5,9 @@ import 'package:solian/exts.dart';
import 'package:solian/models/account.dart'; import 'package:solian/models/account.dart';
import 'package:solian/models/channel.dart'; import 'package:solian/models/channel.dart';
import 'package:solian/models/event.dart'; import 'package:solian/models/event.dart';
import 'package:solian/providers/attachment_uploader.dart';
import 'package:solian/providers/auth.dart'; import 'package:solian/providers/auth.dart';
import 'package:solian/widgets/attachments/attachment_editor.dart'; import 'package:solian/widgets/attachments/attachment_editor.dart';
import 'package:solian/widgets/chat/chat_event.dart'; import 'package:solian/widgets/chat/chat_event.dart';
import 'package:badges/badges.dart' as badges;
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
class ChatMessageInput extends StatefulWidget { class ChatMessageInput extends StatefulWidget {
@ -82,14 +80,6 @@ class _ChatMessageInputState extends State<ChatMessageInput> {
final prof = auth.userProfile.value!; final prof = auth.userProfile.value!;
if (auth.isAuthorized.isFalse) return; if (auth.isAuthorized.isFalse) return;
final AttachmentUploaderController uploader = Get.find();
if (uploader.queueOfUpload.any(
((x) => x.usage == 'm.attachment' && x.isUploading),
)) {
context.showErrorDialog('attachmentUploadInProgress'.tr);
return;
}
Response resp; Response resp;
final mentionedUserNames = _findMentionedUsers(_textController.text); final mentionedUserNames = _findMentionedUsers(_textController.text);
@ -269,18 +259,7 @@ class _ChatMessageInputState extends State<ChatMessageInput> {
), ),
), ),
IconButton( IconButton(
icon: badges.Badge( icon: const Icon(Icons.attach_file),
badgeContent: Text(
_attachments.length.toString(),
style: const TextStyle(color: Colors.white),
),
showBadge: _attachments.isNotEmpty,
position: badges.BadgePosition.topEnd(
top: -12,
end: -8,
),
child: const Icon(Icons.file_present_rounded),
),
color: Colors.teal, color: Colors.teal,
onPressed: () => _editAttachments(), onPressed: () => _editAttachments(),
), ),