Paste to upload

This commit is contained in:
2024-06-29 21:03:15 +08:00
parent e336d2372a
commit 49f999871a
9 changed files with 115 additions and 33 deletions

View File

@ -8,6 +8,10 @@ extension SolianExtenions on BuildContext {
));
}
void clearSnackbar() {
ScaffoldMessenger.of(this).clearSnackBars();
}
Future<void> showErrorDialog(dynamic exception) {
return showDialog<void>(
useRootNavigator: true,

View File

@ -99,12 +99,16 @@ class SolianMessages extends Translations {
'attachmentAddGalleryVideo': 'Gallery video',
'attachmentAddCameraPhoto': 'Capture photo',
'attachmentAddCameraVideo': 'Capture video',
'attachmentAddClipboard': 'Paste file',
'attachmentAddFile': 'Attach file',
'attachmentSetting': 'Adjust attachment',
'attachmentAlt': 'Alternative text',
'attachmentLoadFailed': 'Load Attachment Failed',
'attachmentLoadFailedCaption':
'Something went wrong during loading the attachment metadata...',
'attachmentUploading': 'Uploading @name...',
'attachmentUploadingWebMode':
'Uploading @name... Due to browser\'s limitation, calculate attachment information may cause some lag...',
'realm': 'Realm',
'realms': 'Realms',
'realmOrganizing': 'Organize a realm',
@ -325,11 +329,15 @@ class SolianMessages extends Translations {
'attachmentAddGalleryVideo': '相册视频',
'attachmentAddCameraPhoto': '拍摄图片',
'attachmentAddCameraVideo': '拍摄视频',
'attachmentAddClipboard': '粘贴文件',
'attachmentAddFile': '附加文件',
'attachmentSetting': '调整附件',
'attachmentAlt': '替代文字',
'attachmentLoadFailed': '加载失败',
'attachmentLoadFailedCaption': '有错误发生于加载附件元数据的过程中了…',
'attachmentUploading': '上传附件 @name 中…',
'attachmentUploadingWebMode':
'上传附件 @name 中… 由于浏览器单线程限制,计算所需资源可能会导致界面卡顿…',
'realm': '领域',
'realms': '领域',
'realmOrganizing': '组织领域',

View File

@ -1,19 +1,20 @@
import 'dart:async';
import 'dart:io';
import 'dart:isolate';
import 'dart:math' as math;
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path/path.dart' show basename;
import 'package:solian/exts.dart';
import 'package:solian/models/attachment.dart';
import 'package:solian/platform.dart';
import 'package:solian/providers/auth.dart';
import 'package:solian/providers/content/attachment.dart';
import 'package:super_clipboard/super_clipboard.dart';
import 'package:super_drag_and_drop/super_drag_and_drop.dart';
class AttachmentPublishPopup extends StatefulWidget {
@ -153,10 +154,48 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> {
setState(() => _isBusy = false);
}
void pasteFileToUpload() async {
final clipboard = SystemClipboard.instance;
if (clipboard == null) return;
final reader = await clipboard.read();
for (final format in Formats.standardFormats.whereType<FileFormat>()) {
if (reader.canProvide(format)) {
reader.getFile(format, (file) async {
final data = await file.readAll();
await uploadAttachment(
data,
file.fileName ?? 'unknown',
await calculateBytesSha256(data),
);
});
}
}
}
void handlePasteFile(ClipboardReadEvent event) async {
final reader = await event.getClipboardReader();
for (final format in Formats.standardFormats.whereType<FileFormat>()) {
if (reader.canProvide(format)) {
reader.getFile(format, (file) async {
final data = await file.readAll();
await uploadAttachment(
data,
file.fileName ?? 'unknown',
await calculateBytesSha256(data),
);
});
}
}
}
Future<void> uploadAttachment(Uint8List data, String path, String hash,
{double? ratio}) async {
final AttachmentProvider provider = Get.find();
try {
context.showSnackbar((PlatformInfo.isWeb
? 'attachmentUploadingWebMode'
: 'attachmentUploading')
.trParams({'name': basename(path)}));
final resp = await provider.createAttachment(
data,
path,
@ -167,6 +206,7 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> {
var result = Attachment.fromJson(resp.body);
setState(() => _attachments.add(result));
widget.onUpdate(_attachments.map((e) => e!.id).toList());
context.clearSnackbar();
} catch (err) {
rethrow;
}
@ -222,11 +262,13 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> {
void initState() {
super.initState();
revertMetadataList();
ClipboardEvents.instance?.registerPasteEventListener(handlePasteFile);
}
@override
void dispose() {
super.dispose();
ClipboardEvents.instance?.unregisterPasteEventListener(handlePasteFile);
}
@override
@ -256,11 +298,9 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> {
final data = await file.readAll();
await uploadAttachment(
data,
file.fileName ?? 'attachment',
file.fileName ?? 'unknown',
await calculateBytesSha256(data),
);
}, onError: (error) {
print('Error reading value $error');
});
}
}
@ -359,6 +399,13 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> {
alignment: WrapAlignment.center,
runAlignment: WrapAlignment.center,
children: [
if (PlatformInfo.isDesktop)
ElevatedButton.icon(
icon: const Icon(Icons.paste),
label: Text('attachmentAddClipboard'.tr),
style: const ButtonStyle(visualDensity: density),
onPressed: () => pasteFileToUpload(),
),
ElevatedButton.icon(
icon: const Icon(Icons.add_photo_alternate),
label: Text('attachmentAddGalleryPhoto'.tr),

View File

@ -134,15 +134,17 @@ class _PostItemState extends State<PostItem> {
size: 16,
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.75),
),
Text(
'postRepliedNotify'.trParams(
{'username': '@${widget.item.replyTo!.author.name}'},
),
style: TextStyle(
color:
Theme.of(context).colorScheme.onSurface.withOpacity(0.75),
),
).paddingOnly(left: 6),
Expanded(
child: Text(
'postRepliedNotify'.trParams(
{'username': '@${widget.item.replyTo!.author.name}'},
),
style: TextStyle(
color:
Theme.of(context).colorScheme.onSurface.withOpacity(0.75),
),
).paddingOnly(left: 6),
),
],
).paddingOnly(left: 12),
Card(
@ -167,15 +169,17 @@ class _PostItemState extends State<PostItem> {
size: 16,
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.75),
),
Text(
'postRepostedNotify'.trParams(
{'username': '@${widget.item.repostTo!.author.name}'},
),
style: TextStyle(
color:
Theme.of(context).colorScheme.onSurface.withOpacity(0.75),
),
).paddingOnly(left: 6),
Expanded(
child: Text(
'postRepostedNotify'.trParams(
{'username': '@${widget.item.repostTo!.author.name}'},
),
style: TextStyle(
color:
Theme.of(context).colorScheme.onSurface.withOpacity(0.75),
),
).paddingOnly(left: 6),
),
],
).paddingOnly(left: 12),
Card(