diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 5ebb7f0c9b..f35800111c 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -48,7 +48,6 @@ import 'package:fluffychat/widgets/mixins/popup_context_menu_action_mixin.dart'; import 'package:fluffychat/widgets/mixins/popup_menu_widget_mixin.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import 'package:flutter/services.dart' as flutter; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; @@ -780,28 +779,24 @@ class ChatController extends State void copySingleEventAction() async { if (selectedEvents.length == 1) { final event = selectedEvents.first; - final matrixFile = event.getMatrixFile() ?? - await event.downloadAndDecryptAttachment( - getThumbnail: true, - ); - if (event.messageType == MessageTypes.Image) { - if (matrixFile.filePath != null) { - await Clipboard.instance.copyImageAsStream( - File(matrixFile.filePath!), - mimeType: event.mimeType, - ); - if (!PlatformInfos.isAndroid) { - TwakeSnackBar.show(context, L10n.of(context)!.copyImageSuccess); - } - } else if (matrixFile.bytes != null) { - await Clipboard.instance.copyImageAsBytes( - matrixFile.bytes!, - mimeType: event.mimeType, - ); - if (!PlatformInfos.isAndroid) { - TwakeSnackBar.show(context, L10n.of(context)!.copyImageSuccess); + if (event.messageType == MessageTypes.Image && PlatformInfos.isWeb) { + final matrixFile = event.getMatrixFile() ?? + await event.downloadAndDecryptAttachment( + getThumbnail: true, + ); + try { + if (matrixFile.filePath != null) { + await Clipboard.instance.copyImageAsStream( + File(matrixFile.filePath!), + mimeType: event.mimeType, + ); + } else if (matrixFile.bytes != null) { + await Clipboard.instance.copyImageAsBytes( + matrixFile.bytes!, + mimeType: event.mimeType, + ); } - } else { + } catch (e) { TwakeSnackBar.show(context, L10n.of(context)!.copyImageFailed); Logs().e( 'copySingleEventAction(): failed to copy file ${matrixFile.name}', @@ -814,11 +809,7 @@ class ChatController extends State } void copyEventsAction() { - flutter.Clipboard.setData( - flutter.ClipboardData( - text: _getSelectedEventString(), - ), - ); + Clipboard.instance.copyText(_getSelectedEventString()); showEmojiPickerNotifier.value = false; setState(() { diff --git a/lib/pages/chat/input_bar.dart b/lib/pages/chat/input_bar.dart index 166877a8c1..aee2996bc6 100644 --- a/lib/pages/chat/input_bar.dart +++ b/lib/pages/chat/input_bar.dart @@ -1,4 +1,3 @@ -import 'package:fluffychat/pages/chat/send_file_dialog.dart'; import 'package:fluffychat/presentation/enum/chat/popup_menu_item_web_enum.dart'; import 'package:fluffychat/presentation/extensions/text_editting_controller_extension.dart'; import 'package:fluffychat/presentation/mixins/paste_image_mixin.dart'; @@ -426,65 +425,40 @@ class InputBar extends StatelessWidget with PasteImageMixin { // it sets the types for the callback incorrectly onChanged!(text); }, - textCapitalization: TextCapitalization.sentences, - contentInsertionConfiguration: ContentInsertionConfiguration( - onContentInserted: (keyboardInsertContent) async { - if (room == null || !keyboardInsertContent.hasData) { - return; - } - await showDialog( - context: context, - useRootNavigator: PlatformInfos.isWeb, - builder: (context) { - return SendFileDialog( - room: room!, - files: [ - MatrixImageFile( - name: keyboardInsertContent.uri, - bytes: keyboardInsertContent.data, - ) - ], + contextMenuBuilder: !PlatformInfos.isWeb + ? ( + BuildContext contextMenucontext, + EditableTextState editableTextState, + ) { + return AdaptiveTextSelectionToolbar.editable( + anchors: editableTextState.contextMenuAnchors, + clipboardStatus: ClipboardStatus.pasteable, + onPaste: !PlatformInfos.isWeb + ? () async { + if (room == null) { + // FIXME: need to handle the case when in draft chat + return; + } + editableTextState + .pasteText(SelectionChangedCause.toolbar); + } + : null, + onCopy: () { + editableTextState + .copySelection(SelectionChangedCause.toolbar); + }, + onCut: () { + editableTextState + .cutSelection(SelectionChangedCause.toolbar); + }, + onSelectAll: () { + editableTextState + .selectAll(SelectionChangedCause.toolbar); + }, ); - }, - ); - }, - ), - contextMenuBuilder: ( - BuildContext contextMenucontext, - EditableTextState editableTextState, - ) { - return AdaptiveTextSelectionToolbar.editable( - anchors: editableTextState.contextMenuAnchors, - clipboardStatus: ClipboardStatus.pasteable, - onPaste: !PlatformInfos.isWeb - ? () async { - if (room == null) { - // FIXME: need to handle the case when in draft chat - return; - } - await Clipboard.instance.initReader(); - if (await Clipboard.instance - .isReadableImageFormat()) { - await pasteImage(context, room!); - } else { - editableTextState - .pasteText(SelectionChangedCause.toolbar); - } - } - : null, - onCopy: () { - editableTextState - .copySelection(SelectionChangedCause.toolbar); - }, - onCut: () { - editableTextState - .cutSelection(SelectionChangedCause.toolbar); - }, - onSelectAll: () { - editableTextState.selectAll(SelectionChangedCause.toolbar); - }, - ); - }, + } + : null, + textCapitalization: TextCapitalization.sentences, ), suggestionsCallback: getSuggestions, itemBuilder: (context, suggestion) => SuggestionTile( diff --git a/lib/pages/chat/send_file_dialog.dart b/lib/pages/chat/send_file_dialog.dart index 33893a16d7..0541f60f1f 100644 --- a/lib/pages/chat/send_file_dialog.dart +++ b/lib/pages/chat/send_file_dialog.dart @@ -1,5 +1,6 @@ import 'package:fluffychat/presentation/extensions/send_file_web_extension.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; +import 'package:fluffychat/utils/twake_snackbar.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -52,9 +53,7 @@ class SendFileDialogState extends State { shrinkImageMaxDimension: origImage ? null : 1600, ) .catchError((e) { - scaffoldMessenger.showSnackBar( - SnackBar(content: Text((e as Object).toLocalizedString(context))), - ); + TwakeSnackBar.show(context, (e as Object).toLocalizedString(context)); return null; }); } @@ -93,18 +92,6 @@ class SendFileDialogState extends State { fit: BoxFit.contain, ), ), - Row( - children: [ - Checkbox( - value: origImage, - onChanged: (v) => setState(() => origImage = v ?? false), - ), - InkWell( - onTap: () => setState(() => origImage = !origImage), - child: Text('${L10n.of(context)!.sendOriginal} ($sizeString)'), - ), - ], - ) ], ); } else { diff --git a/lib/presentation/extensions/text_editting_controller_extension.dart b/lib/presentation/extensions/text_editting_controller_extension.dart index 2d0c47aa54..c01d7290b7 100644 --- a/lib/presentation/extensions/text_editting_controller_extension.dart +++ b/lib/presentation/extensions/text_editting_controller_extension.dart @@ -1,15 +1,16 @@ import 'package:fluffychat/utils/clipboard.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart' as flutter; extension TextEdittingControllerExtension on TextEditingController { Future pasteText() async { final start = selection.start; final end = selection.end; + Clipboard.instance.initReader(); final pastedText = await Clipboard.instance.pasteText(); if (pastedText != null) { if (start == -1 || end == -1) { text = pastedText + text; + selection = TextSelection.collapsed(offset: text.length); return; } if (start == end) { @@ -19,6 +20,7 @@ extension TextEdittingControllerExtension on TextEditingController { } else { text = text.replaceRange(start, end, pastedText); } + selection = TextSelection.collapsed(offset: end + pastedText.length); } } @@ -26,11 +28,7 @@ extension TextEdittingControllerExtension on TextEditingController { final start = selection.start; final end = selection.end; if (start < end) { - await flutter.Clipboard.setData( - flutter.ClipboardData( - text: text.substring(start, end), - ), - ); + await Clipboard.instance.copyText(text.substring(start, end)); } } diff --git a/lib/presentation/mixins/paste_image_mixin.dart b/lib/presentation/mixins/paste_image_mixin.dart index e2271c79ba..99ad7f1056 100644 --- a/lib/presentation/mixins/paste_image_mixin.dart +++ b/lib/presentation/mixins/paste_image_mixin.dart @@ -11,7 +11,6 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; mixin PasteImageMixin { Future pasteImage(BuildContext context, Room room) async { - await Clipboard.instance.initReader(); if (!(await Clipboard.instance.isReadableImageFormat())) { TwakeSnackBar.show(context, L10n.of(context)!.fileFormatNotSupported); Logs().e('PasteImageMixin::pasteImage(): not readable image format'); diff --git a/lib/utils/clipboard.dart b/lib/utils/clipboard.dart index 8e104f31c2..95f9859aff 100644 --- a/lib/utils/clipboard.dart +++ b/lib/utils/clipboard.dart @@ -24,6 +24,12 @@ class Clipboard { Formats.svg, ]; + Future copyText(String text) async { + final item = DataWriterItem(); + item.add(Formats.plainText(text)); + await ClipboardWriter.instance.write([item]); + } + Future copyImageAsStream(File image, {String? mimeType}) async { final item = DataWriterItem(suggestedName: image.path); final imageStream = image.openRead(); @@ -31,21 +37,13 @@ class Clipboard { await imageStream.forEach((data) { item.add(getFormatFrom(mime)(Uint8List.fromList(data))); }); - try { - await ClipboardWriter.instance.write([item]); - } catch (e) { - Logs().e('Clipboard::copyImageAsStream(): $e'); - } + await ClipboardWriter.instance.write([item]); } Future copyImageAsBytes(Uint8List data, {String? mimeType}) async { - try { - final item = DataWriterItem(); - item.add(getFormatFrom(mimeType)(data)); - await ClipboardWriter.instance.write([item]); - } catch (e) { - Logs().e('Clipboard::copyImageAsBytes(): $e'); - } + final item = DataWriterItem(); + item.add(getFormatFrom(mimeType)(data)); + await ClipboardWriter.instance.write([item]); } Future initReader() async { @@ -53,7 +51,7 @@ class Clipboard { } Future pasteImageUsingStream() async { - _reader ??= await ClipboardReader.readClipboard(); + _reader = await ClipboardReader.readClipboard(); ClipboardImageInfo? imageInfo; final readableFormats = _reader!.getFormats(allImageFormatsSupported); @@ -90,7 +88,7 @@ class Clipboard { } Future? pasteImageUsingBytes() async { - _reader ??= await ClipboardReader.readClipboard(); + _reader = await ClipboardReader.readClipboard(); final readableFormats = _reader!.getFormats(allImageFormatsSupported); if (readableFormats.isEmpty != false && readableFormats.first is! SimpleFileFormat) { @@ -121,7 +119,7 @@ class Clipboard { } Future isReadableImageFormat() async { - _reader ??= await ClipboardReader.readClipboard(); + _reader = await ClipboardReader.readClipboard(); return _reader!.canProvide(Formats.png) || _reader!.canProvide(Formats.jpeg) || _reader!.canProvide(Formats.heic) || @@ -130,7 +128,7 @@ class Clipboard { } Future pasteText() async { - _reader ??= await ClipboardReader.readClipboard(); + _reader = await ClipboardReader.readClipboard(); String? copied; final readersFormat = _reader!.getFormats(Formats.standardFormats); diff --git a/lib/utils/platform_infos.dart b/lib/utils/platform_infos.dart index 8c8cd1a97c..a86bc94aba 100644 --- a/lib/utils/platform_infos.dart +++ b/lib/utils/platform_infos.dart @@ -30,10 +30,10 @@ abstract class PlatformInfos { static bool get platformCanRecord => (isMobile || isMacOS); static bool get isMacKeyboardPlatform => - isMacOS && + isMacOS || (kIsWeb && html.window.navigator.platform != null && - html.window.navigator.platform!.contains('mac')); + html.window.navigator.platform!.toLowerCase().contains('mac')); static String get clientName => '${AppConfig.applicationName} ${isWeb ? 'web' : Platform.operatingSystem}${kReleaseMode ? '' : 'Debug'}';