From 1af90cd9e73450537a187c03cb44ba8b2d7bd763 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Fri, 22 Nov 2024 00:28:29 +0800 Subject: [PATCH] :sparkles: Paste to add attachment --- assets/translations/en.json | 5 +- assets/translations/zh.json | 5 +- ios/Podfile.lock | 6 +++ lib/controllers/post_write_controller.dart | 8 +++- lib/providers/sn_attachment.dart | 9 ++-- lib/screens/post/post_editor.dart | 45 ++++++++++++++++-- lib/widgets/chat/chat_message_input.dart | 46 ++++++++++++++++++- linux/flutter/generated_plugin_registrant.cc | 4 ++ linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 8 ++++ pubspec.yaml | 1 + .../flutter/generated_plugin_registrant.cc | 3 ++ windows/flutter/generated_plugins.cmake | 1 + 14 files changed, 132 insertions(+), 12 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index fc6af7b..e22c61f 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -171,5 +171,8 @@ "zero": "No attachments", "one": "{} attachment", "other": "{} attachments" - } + }, + "addAttachmentFromAlbum": "Add from album", + "addAttachmentFromClipboard": "Paste file", + "attachmentPastedImage": "Pasted Image" } diff --git a/assets/translations/zh.json b/assets/translations/zh.json index e8af7ec..4ce2c6b 100644 --- a/assets/translations/zh.json +++ b/assets/translations/zh.json @@ -171,5 +171,8 @@ "zero": "没有附件", "one": "{} 个附件", "other": "{} 个附件" - } + }, + "addAttachmentFromAlbum": "从相册中添加附件", + "addAttachmentFromClipboard": "粘贴附件", + "attachmentPastedImage" : "粘贴的图片" } diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e4fa17a..2eed149 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -202,6 +202,8 @@ PODS: - nanopb/encode (3.30910.0) - package_info_plus (0.4.5): - Flutter + - pasteboard (0.0.1): + - Flutter - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS @@ -247,6 +249,7 @@ DEPENDENCIES: - media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`) - media_kit_video (from `.symlinks/plugins/media_kit_video/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) + - pasteboard (from `.symlinks/plugins/pasteboard/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) @@ -316,6 +319,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/media_kit_video/ios" package_info_plus: :path: ".symlinks/plugins/package_info_plus/ios" + pasteboard: + :path: ".symlinks/plugins/pasteboard/ios" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/darwin" screen_brightness_ios: @@ -366,6 +371,7 @@ SPEC CHECKSUMS: media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 + pasteboard: 982969ebaa7c78af3e6cc7761e8f5e77565d9ce0 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 diff --git a/lib/controllers/post_write_controller.dart b/lib/controllers/post_write_controller.dart index b2c5798..88fc89a 100644 --- a/lib/controllers/post_write_controller.dart +++ b/lib/controllers/post_write_controller.dart @@ -86,7 +86,10 @@ class PostWriteMedia { if (file != null) { return file!; } else if (raw != null) { - return XFile.fromData(raw!, name: name); + return XFile.fromData( + raw!, + name: name, + ); } return null; } @@ -256,6 +259,9 @@ class PostWriteController extends ChangeNotifier { media.name, 'interactive', null, + mimetype: media.raw != null && media.type == PostWriteMediaType.image + ? 'image/png' + : null, ); final item = await attach.chunkedUploadParts( diff --git a/lib/providers/sn_attachment.dart b/lib/providers/sn_attachment.dart index aea412a..ab7142f 100644 --- a/lib/providers/sn_attachment.dart +++ b/lib/providers/sn_attachment.dart @@ -130,8 +130,9 @@ class SnAttachmentProvider { int size, String filename, String pool, - Map? metadata, - ) async { + Map? metadata, { + String? mimetype, + }) async { final fileAlt = filename.contains('.') ? filename.substring(0, filename.lastIndexOf('.')) : filename; @@ -139,8 +140,10 @@ class SnAttachmentProvider { filename.substring(filename.lastIndexOf('.') + 1).toLowerCase(); String? mimetypeOverride; - if (mimetypeOverrides.keys.contains(fileExt)) { + if (mimetype == null && mimetypeOverrides.keys.contains(fileExt)) { mimetypeOverride = mimetypeOverrides[fileExt]; + } else { + mimetypeOverride = mimetype; } final resp = await _sn.client.post('/cgi/uc/attachments/multipart', data: { diff --git a/lib/screens/post/post_editor.dart b/lib/screens/post/post_editor.dart index d42a272..580b0a7 100644 --- a/lib/screens/post/post_editor.dart +++ b/lib/screens/post/post_editor.dart @@ -9,6 +9,7 @@ import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; import 'package:material_symbols_icons/symbols.dart'; +import 'package:pasteboard/pasteboard.dart'; import 'package:styled_widget/styled_widget.dart'; import 'package:surface/controllers/post_write_controller.dart'; import 'package:surface/providers/sn_network.dart'; @@ -82,6 +83,18 @@ class _PostEditorScreenState extends State { ); } + void _pasteMedia() async { + final imageBytes = await Pasteboard.image; + if (imageBytes == null) return; + _writeController.addAttachments([ + PostWriteMedia.fromBytes( + imageBytes, + 'attachmentPastedImage'.tr(), + PostWriteMediaType.image, + ), + ]); + } + @override void dispose() { _writeController.dispose(); @@ -369,15 +382,39 @@ class _PostEditorScreenState extends State { scrollDirection: Axis.vertical, child: Row( children: [ - IconButton( - onPressed: _writeController.isBusy - ? null - : _selectMedia, + PopupMenuButton( icon: Icon( Symbols.add_photo_alternate, color: Theme.of(context).colorScheme.primary, ), + itemBuilder: (context) => [ + PopupMenuItem( + child: Row( + children: [ + const Icon(Symbols.photo_library), + const Gap(16), + Text('addAttachmentFromAlbum').tr(), + ], + ), + onTap: () { + _selectMedia(); + }, + ), + PopupMenuItem( + child: Row( + children: [ + const Icon(Symbols.content_paste), + const Gap(16), + Text('addAttachmentFromClipboard') + .tr(), + ], + ), + onTap: () { + _pasteMedia(); + }, + ), + ], ), ], ), diff --git a/lib/widgets/chat/chat_message_input.dart b/lib/widgets/chat/chat_message_input.dart index c40fe73..04c7301 100644 --- a/lib/widgets/chat/chat_message_input.dart +++ b/lib/widgets/chat/chat_message_input.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:image_picker/image_picker.dart'; import 'package:material_symbols_icons/symbols.dart'; +import 'package:pasteboard/pasteboard.dart'; import 'package:provider/provider.dart'; import 'package:styled_widget/styled_widget.dart'; import 'package:surface/controllers/chat_message_controller.dart'; @@ -72,6 +73,9 @@ class ChatMessageInputState extends State { media.name, 'interactive', null, + mimetype: media.raw != null && media.type == PostWriteMediaType.image + ? 'image/png' + : null, ); final item = await attach.chunkedUploadParts( @@ -133,6 +137,19 @@ class ChatMessageInputState extends State { setState(() {}); } + void _pasteMedia() async { + final imageBytes = await Pasteboard.image; + if (imageBytes == null) return; + _attachments.add( + PostWriteMedia.fromBytes( + imageBytes, + 'attachmentPastedImage'.tr(), + PostWriteMediaType.image, + ), + ); + setState(() {}); + } + @override void dispose() { _contentController.dispose(); @@ -266,12 +283,37 @@ class ChatMessageInputState extends State { ), ), const Gap(8), - IconButton( - onPressed: _isBusy ? null : _selectMedia, + PopupMenuButton( icon: Icon( Symbols.add_photo_alternate, color: Theme.of(context).colorScheme.primary, ), + itemBuilder: (context) => [ + PopupMenuItem( + child: Row( + children: [ + const Icon(Symbols.photo_library), + const Gap(16), + Text('addAttachmentFromAlbum').tr(), + ], + ), + onTap: () { + _selectMedia(); + }, + ), + PopupMenuItem( + child: Row( + children: [ + const Icon(Symbols.content_paste), + const Gap(16), + Text('addAttachmentFromClipboard').tr(), + ], + ), + onTap: () { + _pasteMedia(); + }, + ), + ], ), IconButton( onPressed: _isBusy ? null : _sendMessage, diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 7298075..2a32591 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include void fl_register_plugins(FlPluginRegistry* registry) { @@ -33,6 +34,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) media_kit_video_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitVideoPlugin"); media_kit_video_plugin_register_with_registrar(media_kit_video_registrar); + g_autoptr(FlPluginRegistrar) pasteboard_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin"); + pasteboard_plugin_register_with_registrar(pasteboard_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 33ddc1f..86ef1cf 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -9,6 +9,7 @@ list(APPEND FLUTTER_PLUGIN_LIST isar_flutter_libs media_kit_libs_linux media_kit_video + pasteboard url_launcher_linux ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 13a1aca..c569e94 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -17,6 +17,7 @@ import isar_flutter_libs import media_kit_libs_macos_video import media_kit_video import package_info_plus +import pasteboard import path_provider_foundation import screen_brightness_macos import shared_preferences_foundation @@ -37,6 +38,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin")) MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) + PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) diff --git a/pubspec.lock b/pubspec.lock index a1c1277..022e8ef 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1202,6 +1202,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" + pasteboard: + dependency: "direct main" + description: + name: pasteboard + sha256: "7bf733f3a00c7188ec1f2c6f0612854248b302cf91ef3611a2b7bb141c0f9d55" + url: "https://pub.dev" + source: hosted + version: "0.3.0" path: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 5460f46..c866d1b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -87,6 +87,7 @@ dependencies: media_kit: ^1.1.11 media_kit_video: ^1.2.5 media_kit_libs_video: ^1.0.5 + pasteboard: ^0.3.0 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index cfd4273..933b9fd 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -34,6 +35,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi")); MediaKitVideoPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("MediaKitVideoPluginCApi")); + PasteboardPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PasteboardPlugin")); ScreenBrightnessWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin")); UrlLauncherWindowsRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 30c0351..17c314a 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -11,6 +11,7 @@ list(APPEND FLUTTER_PLUGIN_LIST isar_flutter_libs media_kit_libs_windows_video media_kit_video + pasteboard screen_brightness_windows url_launcher_windows )