From 35fa43ed6bbcd53f9e11ebeb5b4bc99f8702b6b4 Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 4 Nov 2025 11:38:59 +0700 Subject: [PATCH] TF-4011 Support `Tab` shortcut key to switch focus between tag & input field --- .../presentation/composer_view_web.dart | 1485 +++++++++-------- .../widgets/recipient_composer_widget.dart | 21 +- pubspec.lock | 4 +- pubspec.yaml | 2 +- 4 files changed, 787 insertions(+), 725 deletions(-) diff --git a/lib/features/composer/presentation/composer_view_web.dart b/lib/features/composer/presentation/composer_view_web.dart index 72e3361ac..973a84e21 100644 --- a/lib/features/composer/presentation/composer_view_web.dart +++ b/lib/features/composer/presentation/composer_view_web.dart @@ -1,6 +1,7 @@ import 'package:core/presentation/views/responsive/responsive_widget.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:model/email/prefix_email_address.dart'; @@ -73,512 +74,540 @@ class ComposerView extends GetWidget { responsiveUtils: controller.responsiveUtils, mobile: MobileResponsiveContainerView( childBuilder: (_, constraints) { - return GestureDetector( - onTap: controller.clickOutsideComposer, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Obx(() => MobileResponsiveAppBarComposerWidget( - imagePaths: controller.imagePaths, - isCodeViewEnabled: controller.richTextWebController!.codeViewEnabled, - isFormattingOptionsEnabled: controller.richTextWebController!.isFormattingOptionsEnabled, - openRichToolbarAction: controller.richTextWebController!.toggleFormattingOptions, - isSendButtonEnabled: controller.isEnableEmailSendButton.value, - hasRequestReadReceipt: controller.hasRequestReadReceipt.value, - isMarkAsImportant: controller.isMarkAsImportant.value, - isEmailChanged: controller.isEmailChanged.value, - menuMoreOptionController: controller.menuMoreOptionController!, - onCloseViewAction: () => controller.handleClickCloseComposer(context), - attachFileAction: () => controller.openFilePickerByType(context, FileType.any), - insertImageAction: () => controller.insertImage(context, constraints.maxWidth), - sendMessageAction: () => controller.handleClickSendButton(context), - toggleCodeViewAction: controller.richTextWebController!.toggleCodeView, - printDraftAction: () => controller.printDraft(context), - toggleRequestReadReceiptAction: () => controller.toggleRequestReadReceipt(context), - toggleMarkAsImportantAction: () => controller.toggleMarkAsImportant(context), - saveToDraftsAction: () => controller.handleClickSaveAsDraftsButton(context), - saveToTemplateAction: () => controller.handleClickSaveAsTemplateButton(context), - deleteComposerAction: controller.handleClickDeleteComposer, - )), - ConstrainedBox( - constraints: BoxConstraints( - maxHeight: ComposerStyle.getMaxHeightEmailAddressWidget( - context, - constraints, - controller.responsiveUtils - ) + return Shortcuts( + shortcuts: { + LogicalKeySet(LogicalKeyboardKey.tab): + const DoNothingAndStopPropagationIntent(), + }, + child: Actions( + actions: { + DoNothingAndStopPropagationIntent: + CallbackAction( + onInvoke: (_) => null ), - child: SingleChildScrollView( - controller: controller.scrollControllerEmailAddress, - child: Obx(() => Column( - children: [ - if (controller.fromRecipientState.value == PrefixRecipientState.enabled) - Tooltip( - message: controller.identitySelected.value?.email ?? '', - child: FromComposerMobileWidget( - selectedIdentity: controller.identitySelected.value, - imagePaths: controller.imagePaths, - responsiveUtils: controller.responsiveUtils, - margin: ComposerStyle.mobileRecipientMargin, - padding: ComposerStyle.mobileRecipientPadding, - onTap: () => controller.openSelectIdentityBottomSheet(context) - ), - ), - if (controller.recipientsCollapsedState.value == PrefixRecipientState.enabled) - RecipientsCollapsedComposerWidget( - listEmailAddress: controller.allListEmailAddressWithoutReplyTo, - margin: ComposerStyle.mobileRecipientMargin, - onShowAllRecipientsAction: controller.showFullRecipients, - ), - if (controller.toRecipientState.value == PrefixRecipientState.enabled) - _buildRecipientComposerWidget( - composerId: composerId, - prefix: PrefixEmailAddress.to, - controller: controller, - maxWidth: constraints.maxWidth, - listEmailAddress: controller.listToEmailAddress, - textController: controller.toEmailAddressController, - focusNode: controller.toAddressFocusNode, - focusNodeKeyboard: controller.toAddressFocusNodeKeyboard, - keyTagEditor: controller.keyToEmailTagEditor, - nextFocusNode: controller.getNextFocusOfToEmailAddress(), - isMobile: true, - ), - if (controller.ccRecipientState.value == PrefixRecipientState.enabled) - _buildRecipientComposerWidget( - composerId: composerId, - prefix: PrefixEmailAddress.cc, - controller: controller, - maxWidth: constraints.maxWidth, - listEmailAddress: controller.listCcEmailAddress, - textController: controller.ccEmailAddressController, - focusNode: controller.ccAddressFocusNode, - focusNodeKeyboard: controller.ccAddressFocusNodeKeyboard, - keyTagEditor: controller.keyCcEmailTagEditor, - nextFocusNode: controller.getNextFocusOfCcEmailAddress(), - isMobile: true, - ), - if (controller.bccRecipientState.value == PrefixRecipientState.enabled) - _buildRecipientComposerWidget( - composerId: composerId, - prefix: PrefixEmailAddress.bcc, - controller: controller, - maxWidth: constraints.maxWidth, - listEmailAddress: controller.listBccEmailAddress, - textController: controller.bccEmailAddressController, - focusNode: controller.bccAddressFocusNode, - focusNodeKeyboard: controller.bccAddressFocusNodeKeyboard, - keyTagEditor: controller.keyBccEmailTagEditor, - nextFocusNode: controller.getNextFocusOfBccEmailAddress(), - isMobile: true, - ), - if (controller.replyToRecipientState.value == PrefixRecipientState.enabled) - _buildRecipientComposerWidget( - composerId: composerId, - prefix: PrefixEmailAddress.replyTo, - controller: controller, - maxWidth: constraints.maxWidth, - listEmailAddress: controller.listReplyToEmailAddress, - textController: controller.replyToEmailAddressController, - focusNode: controller.replyToAddressFocusNode, - focusNodeKeyboard: controller.replyToAddressFocusNodeKeyboard, - keyTagEditor: controller.keyReplyToEmailTagEditor, - nextFocusNode: controller.subjectEmailInputFocusNode, - isMobile: true, - ), - ], + }, + child: GestureDetector( + onTap: controller.clickOutsideComposer, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Obx(() => MobileResponsiveAppBarComposerWidget( + imagePaths: controller.imagePaths, + isCodeViewEnabled: controller.richTextWebController!.codeViewEnabled, + isFormattingOptionsEnabled: controller.richTextWebController!.isFormattingOptionsEnabled, + openRichToolbarAction: controller.richTextWebController!.toggleFormattingOptions, + isSendButtonEnabled: controller.isEnableEmailSendButton.value, + hasRequestReadReceipt: controller.hasRequestReadReceipt.value, + isMarkAsImportant: controller.isMarkAsImportant.value, + isEmailChanged: controller.isEmailChanged.value, + menuMoreOptionController: controller.menuMoreOptionController!, + onCloseViewAction: () => controller.handleClickCloseComposer(context), + attachFileAction: () => controller.openFilePickerByType(context, FileType.any), + insertImageAction: () => controller.insertImage(context, constraints.maxWidth), + sendMessageAction: () => controller.handleClickSendButton(context), + toggleCodeViewAction: controller.richTextWebController!.toggleCodeView, + printDraftAction: () => controller.printDraft(context), + toggleRequestReadReceiptAction: () => controller.toggleRequestReadReceipt(context), + toggleMarkAsImportantAction: () => controller.toggleMarkAsImportant(context), + saveToDraftsAction: () => controller.handleClickSaveAsDraftsButton(context), + saveToTemplateAction: () => controller.handleClickSaveAsTemplateButton(context), + deleteComposerAction: controller.handleClickDeleteComposer, )), - ) - ), - SubjectComposerWidget( - focusNode: controller.subjectEmailInputFocusNode, - textController: controller.subjectEmailInputController, - onTextChange: controller.setSubjectEmail, - padding: ComposerStyle.mobileSubjectPadding, - margin: ComposerStyle.mobileSubjectMargin, - ), - Expanded( - child: Stack( - children: [ - Column( + ConstrainedBox( + constraints: BoxConstraints( + maxHeight: ComposerStyle.getMaxHeightEmailAddressWidget( + context, + constraints, + controller.responsiveUtils + ) + ), + child: SingleChildScrollView( + controller: controller.scrollControllerEmailAddress, + child: Obx(() => Column( + children: [ + if (controller.fromRecipientState.value == PrefixRecipientState.enabled) + Tooltip( + message: controller.identitySelected.value?.email ?? '', + child: FromComposerMobileWidget( + selectedIdentity: controller.identitySelected.value, + imagePaths: controller.imagePaths, + responsiveUtils: controller.responsiveUtils, + margin: ComposerStyle.mobileRecipientMargin, + padding: ComposerStyle.mobileRecipientPadding, + onTap: () => controller.openSelectIdentityBottomSheet(context) + ), + ), + if (controller.recipientsCollapsedState.value == PrefixRecipientState.enabled) + RecipientsCollapsedComposerWidget( + listEmailAddress: controller.allListEmailAddressWithoutReplyTo, + margin: ComposerStyle.mobileRecipientMargin, + onShowAllRecipientsAction: controller.showFullRecipients, + ), + if (controller.toRecipientState.value == PrefixRecipientState.enabled) + _buildRecipientComposerWidget( + composerId: composerId, + prefix: PrefixEmailAddress.to, + controller: controller, + maxWidth: constraints.maxWidth, + listEmailAddress: controller.listToEmailAddress, + textController: controller.toEmailAddressController, + focusNode: controller.toAddressFocusNode, + focusNodeKeyboard: controller.toAddressFocusNodeKeyboard, + keyTagEditor: controller.keyToEmailTagEditor, + nextFocusNode: controller.getNextFocusOfToEmailAddress(), + isMobile: true, + ), + if (controller.ccRecipientState.value == PrefixRecipientState.enabled) + _buildRecipientComposerWidget( + composerId: composerId, + prefix: PrefixEmailAddress.cc, + controller: controller, + maxWidth: constraints.maxWidth, + listEmailAddress: controller.listCcEmailAddress, + textController: controller.ccEmailAddressController, + focusNode: controller.ccAddressFocusNode, + focusNodeKeyboard: controller.ccAddressFocusNodeKeyboard, + keyTagEditor: controller.keyCcEmailTagEditor, + nextFocusNode: controller.getNextFocusOfCcEmailAddress(), + isMobile: true, + ), + if (controller.bccRecipientState.value == PrefixRecipientState.enabled) + _buildRecipientComposerWidget( + composerId: composerId, + prefix: PrefixEmailAddress.bcc, + controller: controller, + maxWidth: constraints.maxWidth, + listEmailAddress: controller.listBccEmailAddress, + textController: controller.bccEmailAddressController, + focusNode: controller.bccAddressFocusNode, + focusNodeKeyboard: controller.bccAddressFocusNodeKeyboard, + keyTagEditor: controller.keyBccEmailTagEditor, + nextFocusNode: controller.getNextFocusOfBccEmailAddress(), + isMobile: true, + ), + if (controller.replyToRecipientState.value == PrefixRecipientState.enabled) + _buildRecipientComposerWidget( + composerId: composerId, + prefix: PrefixEmailAddress.replyTo, + controller: controller, + maxWidth: constraints.maxWidth, + listEmailAddress: controller.listReplyToEmailAddress, + textController: controller.replyToEmailAddressController, + focusNode: controller.replyToAddressFocusNode, + focusNodeKeyboard: controller.replyToAddressFocusNodeKeyboard, + keyTagEditor: controller.keyReplyToEmailTagEditor, + nextFocusNode: controller.subjectEmailInputFocusNode, + isMobile: true, + ), + ], + )), + ) + ), + SubjectComposerWidget( + focusNode: controller.subjectEmailInputFocusNode, + textController: controller.subjectEmailInputController, + onTextChange: controller.setSubjectEmail, + padding: ComposerStyle.mobileSubjectPadding, + margin: ComposerStyle.mobileSubjectMargin, + ), + Expanded( + child: Stack( children: [ - Expanded( - child: LayoutBuilder( - builder: (_, constraintsEditor) { - return Obx(() => WebEditorView( - key: controller.responsiveContainerKey, - editorController: controller.richTextWebController!.editorController, - arguments: controller.composerArguments.value, - contentViewState: controller.emailContentsViewState.value, - currentWebContent: controller.textEditorWeb, - onInitial: controller.handleInitHtmlEditorWeb, - onChangeContent: controller.onChangeTextEditorWeb, - onFocus: controller.handleOnFocusHtmlEditorWeb, - onEditorSettings: controller.richTextWebController!.onEditorSettingsChange, - onEditorTextSizeChanged: controller.richTextWebController!.onEditorTextSizeChanged, - height: constraintsEditor.maxHeight, - horizontalPadding: ComposerStyle.mobileEditorHorizontalPadding, - onDragEnter: controller.handleOnDragEnterHtmlEditorWeb, - onDragOver: controller.handleOnDragOverHtmlEditorWeb, - onPasteImageSuccessAction: (listFileUpload) => controller.handleOnPasteImageSuccessAction( - context: context, - maxWidth: constraintsEditor.maxWidth, - listFileUpload: listFileUpload - ), - onPasteImageFailureAction: (listFileUpload, base64, uploadError) => - controller.handleOnPasteImageFailureAction( - context: context, - listFileUpload: listFileUpload, - base64: base64, - uploadError: uploadError + Column( + children: [ + Expanded( + child: LayoutBuilder( + builder: (_, constraintsEditor) { + return Obx(() => WebEditorView( + key: controller.responsiveContainerKey, + editorController: controller.richTextWebController!.editorController, + arguments: controller.composerArguments.value, + contentViewState: controller.emailContentsViewState.value, + currentWebContent: controller.textEditorWeb, + onInitial: controller.handleInitHtmlEditorWeb, + onChangeContent: controller.onChangeTextEditorWeb, + onFocus: controller.handleOnFocusHtmlEditorWeb, + onEditorSettings: controller.richTextWebController!.onEditorSettingsChange, + onEditorTextSizeChanged: controller.richTextWebController!.onEditorTextSizeChanged, + height: constraintsEditor.maxHeight, + horizontalPadding: ComposerStyle.mobileEditorHorizontalPadding, + onDragEnter: controller.handleOnDragEnterHtmlEditorWeb, + onDragOver: controller.handleOnDragOverHtmlEditorWeb, + onPasteImageSuccessAction: (listFileUpload) => controller.handleOnPasteImageSuccessAction( + context: context, + maxWidth: constraintsEditor.maxWidth, + listFileUpload: listFileUpload + ), + onPasteImageFailureAction: (listFileUpload, base64, uploadError) => + controller.handleOnPasteImageFailureAction( + context: context, + listFileUpload: listFileUpload, + base64: base64, + uploadError: uploadError + ), + onInitialContentLoadComplete: controller.onInitialContentLoadCompleteWeb, + onKeyDownEditorAction: controller.onKeyDownEditorAction, + )); + } + ), + ), + Obx(() { + if (controller.uploadController.listUploadAttachments.isNotEmpty) { + return AttachmentComposerWidget( + listFileUploaded: controller.uploadController.listUploadAttachments, + isCollapsed: controller.isAttachmentCollapsed, + onDeleteAttachmentAction: controller.deleteAttachmentUploaded, + onToggleExpandAttachmentAction: (isCollapsed) => controller.isAttachmentCollapsed = isCollapsed, + ); + } else { + return const SizedBox.shrink(); + } + }), + Obx(() { + if (controller.richTextWebController!.isFormattingOptionsEnabled) { + return ToolbarRichTextWidget( + richTextController: controller.richTextWebController!, + imagePaths: controller.imagePaths, + padding: ComposerStyle.richToolbarPadding, + decoration: const BoxDecoration( + color: ComposerStyle.richToolbarColor, + boxShadow: ComposerStyle.richToolbarShadow, ), - onInitialContentLoadComplete: controller.onInitialContentLoadCompleteWeb, - onKeyDownEditorAction: controller.onKeyDownEditorAction, - )); - } - ), + isMobile: controller.responsiveUtils.isMobile(context), + ); + } else { + return const SizedBox.shrink(); + } + }) + ], ), + Align( + alignment: AlignmentDirectional.topCenter, + child: Obx(() => InsertImageLoadingBarWidget( + uploadInlineViewState: controller.uploadController.uploadInlineViewState.value, + viewState: controller.viewState.value, + margin: ComposerStyle.insertImageLoadingBarMargin, + )), + ), + iframeOverlay, Obx(() { - if (controller.uploadController.listUploadAttachments.isNotEmpty) { - return AttachmentComposerWidget( - listFileUploaded: controller.uploadController.listUploadAttachments, - isCollapsed: controller.isAttachmentCollapsed, - onDeleteAttachmentAction: controller.deleteAttachmentUploaded, - onToggleExpandAttachmentAction: (isCollapsed) => controller.isAttachmentCollapsed = isCollapsed, + if (controller.mailboxDashBoardController.isAttachmentDraggableAppActive) { + return Positioned.fill( + child: PointerInterceptor( + child: AttachmentDropZoneWidget( + imagePaths: controller.imagePaths, + onAttachmentDropZoneListener: controller.onAttachmentDropZoneListener, + ) + ), ); } else { return const SizedBox.shrink(); } }), Obx(() { - if (controller.richTextWebController!.isFormattingOptionsEnabled) { - return ToolbarRichTextWidget( - richTextController: controller.richTextWebController!, - imagePaths: controller.imagePaths, - padding: ComposerStyle.richToolbarPadding, - decoration: const BoxDecoration( - color: ComposerStyle.richToolbarColor, - boxShadow: ComposerStyle.richToolbarShadow, + if (controller.mailboxDashBoardController.isLocalFileDraggableAppActive) { + return Positioned.fill( + child: PointerInterceptor( + child: LocalFileDropZoneWidget( + imagePaths: controller.imagePaths, + onLocalFileDropZoneListener: (details) => + controller.onLocalFileDropZoneListener( + context: context, + details: details, + maxWidth: constraints.maxWidth, + ), + ) ), - isMobile: controller.responsiveUtils.isMobile(context), ); } else { return const SizedBox.shrink(); } - }) + }), ], ), - Align( - alignment: AlignmentDirectional.topCenter, - child: Obx(() => InsertImageLoadingBarWidget( - uploadInlineViewState: controller.uploadController.uploadInlineViewState.value, - viewState: controller.viewState.value, - margin: ComposerStyle.insertImageLoadingBarMargin, - )), - ), - iframeOverlay, - Obx(() { - if (controller.mailboxDashBoardController.isAttachmentDraggableAppActive) { - return Positioned.fill( - child: PointerInterceptor( - child: AttachmentDropZoneWidget( - imagePaths: controller.imagePaths, - onAttachmentDropZoneListener: controller.onAttachmentDropZoneListener, - ) - ), - ); - } else { - return const SizedBox.shrink(); - } - }), - Obx(() { - if (controller.mailboxDashBoardController.isLocalFileDraggableAppActive) { - return Positioned.fill( - child: PointerInterceptor( - child: LocalFileDropZoneWidget( - imagePaths: controller.imagePaths, - onLocalFileDropZoneListener: (details) => - controller.onLocalFileDropZoneListener( - context: context, - details: details, - maxWidth: constraints.maxWidth, - ), - ) - ), - ); - } else { - return const SizedBox.shrink(); - } - }), - ], - ), + ), + ] ), - ] + ), ), ); } ), desktop: Obx(() => DesktopResponsiveContainerView( childBuilder: (_, constraints) { - return GestureDetector( - onTap: controller.clickOutsideComposer, - child: Column(children: [ - Obx(() => DesktopAppBarComposerWidget( - imagePaths: controller.imagePaths, - emailSubject: controller.subjectEmail.value ?? '', - displayMode: controller.screenDisplayMode.value, - onCloseViewAction: () => controller.handleClickCloseComposer(context), - onChangeDisplayModeAction: controller.displayScreenTypeComposerAction, - )), - ConstrainedBox( - constraints: BoxConstraints( - maxHeight: ComposerStyle.getMaxHeightEmailAddressWidget( - context, - constraints, - controller.responsiveUtils - ) - ), - child: SingleChildScrollView( - controller: controller.scrollControllerEmailAddress, - child: Obx(() => Column( - children: [ - if (controller.fromRecipientState.value == PrefixRecipientState.enabled) - FromComposerDropDownWidget( - items: controller.listFromIdentities, - itemSelected: controller.identitySelected.value, - dropdownKey: controller.identityDropdownKey, - imagePaths: controller.imagePaths, - padding: ComposerStyle.desktopRecipientPadding, - margin: ComposerStyle.desktopRecipientMargin, - onChangeIdentity: controller.onChangeIdentity, - ), - if (controller.recipientsCollapsedState.value == PrefixRecipientState.enabled) - RecipientsCollapsedComposerWidget( - listEmailAddress: controller.allListEmailAddressWithoutReplyTo, - margin: ComposerStyle.desktopRecipientMargin, - onShowAllRecipientsAction: controller.showFullRecipients, - ), - if (controller.toRecipientState.value == PrefixRecipientState.enabled) - _buildRecipientComposerWidget( - composerId: composerId, - prefix: PrefixEmailAddress.to, - controller: controller, - maxWidth: constraints.maxWidth, - listEmailAddress: controller.listToEmailAddress, - textController: controller.toEmailAddressController, - focusNode: controller.toAddressFocusNode, - focusNodeKeyboard: controller.toAddressFocusNodeKeyboard, - keyTagEditor: controller.keyToEmailTagEditor, - nextFocusNode: controller.getNextFocusOfToEmailAddress(), - ), - if (controller.ccRecipientState.value == PrefixRecipientState.enabled) - _buildRecipientComposerWidget( - composerId: composerId, - prefix: PrefixEmailAddress.cc, - controller: controller, - maxWidth: constraints.maxWidth, - listEmailAddress: controller.listCcEmailAddress, - textController: controller.ccEmailAddressController, - focusNode: controller.ccAddressFocusNode, - focusNodeKeyboard: controller.ccAddressFocusNodeKeyboard, - keyTagEditor: controller.keyCcEmailTagEditor, - nextFocusNode: controller.getNextFocusOfCcEmailAddress(), - ), - if (controller.bccRecipientState.value == PrefixRecipientState.enabled) - _buildRecipientComposerWidget( - composerId: composerId, - prefix: PrefixEmailAddress.bcc, - controller: controller, - maxWidth: constraints.maxWidth, - listEmailAddress: controller.listBccEmailAddress, - textController: controller.bccEmailAddressController, - focusNode: controller.bccAddressFocusNode, - focusNodeKeyboard: controller.bccAddressFocusNodeKeyboard, - keyTagEditor: controller.keyBccEmailTagEditor, - nextFocusNode: controller.getNextFocusOfBccEmailAddress(), - ), - if (controller.replyToRecipientState.value == PrefixRecipientState.enabled) - _buildRecipientComposerWidget( - composerId: composerId, - prefix: PrefixEmailAddress.replyTo, - controller: controller, - maxWidth: constraints.maxWidth, - listEmailAddress: controller.listReplyToEmailAddress, - textController: controller.replyToEmailAddressController, - focusNode: controller.replyToAddressFocusNode, - focusNodeKeyboard: controller.replyToAddressFocusNodeKeyboard, - keyTagEditor: controller.keyReplyToEmailTagEditor, - nextFocusNode: controller.subjectEmailInputFocusNode, - ), - ], + return Shortcuts( + shortcuts: { + LogicalKeySet(LogicalKeyboardKey.tab): + const DoNothingAndStopPropagationIntent(), + }, + child: Actions( + actions: { + DoNothingAndStopPropagationIntent: + CallbackAction( + onInvoke: (_) => null + ), + }, + child: GestureDetector( + onTap: controller.clickOutsideComposer, + child: Column(children: [ + Obx(() => DesktopAppBarComposerWidget( + imagePaths: controller.imagePaths, + emailSubject: controller.subjectEmail.value ?? '', + displayMode: controller.screenDisplayMode.value, + onCloseViewAction: () => controller.handleClickCloseComposer(context), + onChangeDisplayModeAction: controller.displayScreenTypeComposerAction, )), - ) - ), - SubjectComposerWidget( - focusNode: controller.subjectEmailInputFocusNode, - textController: controller.subjectEmailInputController, - onTextChange: controller.setSubjectEmail, - padding: ComposerStyle.desktopSubjectPadding, - margin: ComposerStyle.desktopSubjectMargin, - ), - Expanded( - child: Stack( - children: [ - Column( + ConstrainedBox( + constraints: BoxConstraints( + maxHeight: ComposerStyle.getMaxHeightEmailAddressWidget( + context, + constraints, + controller.responsiveUtils + ) + ), + child: SingleChildScrollView( + controller: controller.scrollControllerEmailAddress, + child: Obx(() => Column( + children: [ + if (controller.fromRecipientState.value == PrefixRecipientState.enabled) + FromComposerDropDownWidget( + items: controller.listFromIdentities, + itemSelected: controller.identitySelected.value, + dropdownKey: controller.identityDropdownKey, + imagePaths: controller.imagePaths, + padding: ComposerStyle.desktopRecipientPadding, + margin: ComposerStyle.desktopRecipientMargin, + onChangeIdentity: controller.onChangeIdentity, + ), + if (controller.recipientsCollapsedState.value == PrefixRecipientState.enabled) + RecipientsCollapsedComposerWidget( + listEmailAddress: controller.allListEmailAddressWithoutReplyTo, + margin: ComposerStyle.desktopRecipientMargin, + onShowAllRecipientsAction: controller.showFullRecipients, + ), + if (controller.toRecipientState.value == PrefixRecipientState.enabled) + _buildRecipientComposerWidget( + composerId: composerId, + prefix: PrefixEmailAddress.to, + controller: controller, + maxWidth: constraints.maxWidth, + listEmailAddress: controller.listToEmailAddress, + textController: controller.toEmailAddressController, + focusNode: controller.toAddressFocusNode, + focusNodeKeyboard: controller.toAddressFocusNodeKeyboard, + keyTagEditor: controller.keyToEmailTagEditor, + nextFocusNode: controller.getNextFocusOfToEmailAddress(), + ), + if (controller.ccRecipientState.value == PrefixRecipientState.enabled) + _buildRecipientComposerWidget( + composerId: composerId, + prefix: PrefixEmailAddress.cc, + controller: controller, + maxWidth: constraints.maxWidth, + listEmailAddress: controller.listCcEmailAddress, + textController: controller.ccEmailAddressController, + focusNode: controller.ccAddressFocusNode, + focusNodeKeyboard: controller.ccAddressFocusNodeKeyboard, + keyTagEditor: controller.keyCcEmailTagEditor, + nextFocusNode: controller.getNextFocusOfCcEmailAddress(), + ), + if (controller.bccRecipientState.value == PrefixRecipientState.enabled) + _buildRecipientComposerWidget( + composerId: composerId, + prefix: PrefixEmailAddress.bcc, + controller: controller, + maxWidth: constraints.maxWidth, + listEmailAddress: controller.listBccEmailAddress, + textController: controller.bccEmailAddressController, + focusNode: controller.bccAddressFocusNode, + focusNodeKeyboard: controller.bccAddressFocusNodeKeyboard, + keyTagEditor: controller.keyBccEmailTagEditor, + nextFocusNode: controller.getNextFocusOfBccEmailAddress(), + ), + if (controller.replyToRecipientState.value == PrefixRecipientState.enabled) + _buildRecipientComposerWidget( + composerId: composerId, + prefix: PrefixEmailAddress.replyTo, + controller: controller, + maxWidth: constraints.maxWidth, + listEmailAddress: controller.listReplyToEmailAddress, + textController: controller.replyToEmailAddressController, + focusNode: controller.replyToAddressFocusNode, + focusNodeKeyboard: controller.replyToAddressFocusNodeKeyboard, + keyTagEditor: controller.keyReplyToEmailTagEditor, + nextFocusNode: controller.subjectEmailInputFocusNode, + ), + ], + )), + ) + ), + SubjectComposerWidget( + focusNode: controller.subjectEmailInputFocusNode, + textController: controller.subjectEmailInputController, + onTextChange: controller.setSubjectEmail, + padding: ComposerStyle.desktopSubjectPadding, + margin: ComposerStyle.desktopSubjectMargin, + ), + Expanded( + child: Stack( children: [ - Expanded( - child: Container( - decoration: const BoxDecoration( - border: Border( - bottom: BorderSide( - color: ComposerStyle.borderColor, - width: 1 + Column( + children: [ + Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide( + color: ComposerStyle.borderColor, + width: 1 + ) + ), + color: ComposerStyle.backgroundEditorColor + ), + child: Column( + children: [ + Expanded( + child: LayoutBuilder( + builder: (_, constraintsEditor) { + return Obx(() { + return WebEditorView( + key: controller.responsiveContainerKey, + editorController: controller.richTextWebController!.editorController, + arguments: controller.composerArguments.value, + contentViewState: controller.emailContentsViewState.value, + currentWebContent: controller.textEditorWeb, + onInitial: controller.handleInitHtmlEditorWeb, + onChangeContent: controller.onChangeTextEditorWeb, + onFocus: controller.handleOnFocusHtmlEditorWeb, + onEditorSettings: controller.richTextWebController?.onEditorSettingsChange, + onEditorTextSizeChanged: controller.richTextWebController?.onEditorTextSizeChanged, + height: constraintsEditor.maxHeight, + horizontalPadding: ComposerStyle.desktopEditorHorizontalPadding, + onDragEnter: controller.handleOnDragEnterHtmlEditorWeb, + onDragOver: controller.handleOnDragOverHtmlEditorWeb, + onPasteImageSuccessAction: (listFileUpload) => controller.handleOnPasteImageSuccessAction( + context: context, + maxWidth: constraintsEditor.maxWidth, + listFileUpload: listFileUpload + ), + onPasteImageFailureAction: (listFileUpload, base64, uploadError) => + controller.handleOnPasteImageFailureAction( + context: context, + listFileUpload: listFileUpload, + base64: base64, + uploadError: uploadError + ), + onInitialContentLoadComplete: controller.onInitialContentLoadCompleteWeb, + onKeyDownEditorAction: controller.onKeyDownEditorAction, + ); + }); + } + ), + ), + Obx(() { + if (controller.uploadController.listUploadAttachments.isNotEmpty) { + return AttachmentComposerWidget( + listFileUploaded: controller.uploadController.listUploadAttachments, + isCollapsed: controller.isAttachmentCollapsed, + onDeleteAttachmentAction: controller.deleteAttachmentUploaded, + onToggleExpandAttachmentAction: (isCollapsed) => controller.isAttachmentCollapsed = isCollapsed, + ); + } else { + return const SizedBox.shrink(); + } + }), + Obx(() { + if (controller.richTextWebController!.isFormattingOptionsEnabled) { + return ToolbarRichTextWidget( + richTextController: controller.richTextWebController!, + imagePaths: controller.imagePaths, + padding: ComposerStyle.richToolbarPadding, + decoration: const BoxDecoration( + color: ComposerStyle.richToolbarColor, + boxShadow: ComposerStyle.richToolbarShadow, + ), + isMobile: controller.responsiveUtils.isMobile(context), + ); + } else { + return const SizedBox.shrink(); + } + }) + ], + ), + ), + ), + Obx(() => BottomBarComposerWidget( + imagePaths: controller.imagePaths, + isCodeViewEnabled: controller.richTextWebController!.codeViewEnabled, + isFormattingOptionsEnabled: controller.richTextWebController!.isFormattingOptionsEnabled, + hasReadReceipt: controller.hasRequestReadReceipt.value, + isMarkAsImportant: controller.isMarkAsImportant.value, + openRichToolbarAction: controller.richTextWebController!.toggleFormattingOptions, + attachFileAction: () => controller.openFilePickerByType(context, FileType.any), + insertImageAction: () => controller.insertImage(context, constraints.maxWidth), + deleteComposerAction: controller.handleClickDeleteComposer, + saveToDraftAction: () => controller.handleClickSaveAsDraftsButton(context), + sendMessageAction: () => controller.handleClickSendButton(context), + isEmailChanged: controller.isEmailChanged.isTrue, + toggleCodeViewAction: controller.richTextWebController!.toggleCodeView, + menuMoreOptionController: controller.menuMoreOptionController!, + onPopupMenuChanged: controller.onPopupMenuChanged, + printDraftAction: () => controller.printDraft(context), + toggleRequestReadReceiptAction: () => controller.toggleRequestReadReceipt(context), + toggleMarkAsImportantAction: () => controller.toggleMarkAsImportant(context), + saveAsTemplateAction: () => controller.handleClickSaveAsTemplateButton(context), + )), + ], + ), + Align( + alignment: AlignmentDirectional.topCenter, + child: Obx(() => InsertImageLoadingBarWidget( + uploadInlineViewState: controller.uploadController.uploadInlineViewState.value, + viewState: controller.viewState.value, + margin: ComposerStyle.insertImageLoadingBarMargin, + )), + ), + iframeOverlay, + Obx(() { + if (controller.mailboxDashBoardController.isAttachmentDraggableAppActive) { + return Positioned.fill( + child: PointerInterceptor( + child: AttachmentDropZoneWidget( + imagePaths: controller.imagePaths, + onAttachmentDropZoneListener: + controller.onAttachmentDropZoneListener, ) ), - color: ComposerStyle.backgroundEditorColor - ), - child: Column( - children: [ - Expanded( - child: LayoutBuilder( - builder: (_, constraintsEditor) { - return Obx(() { - return WebEditorView( - key: controller.responsiveContainerKey, - editorController: controller.richTextWebController!.editorController, - arguments: controller.composerArguments.value, - contentViewState: controller.emailContentsViewState.value, - currentWebContent: controller.textEditorWeb, - onInitial: controller.handleInitHtmlEditorWeb, - onChangeContent: controller.onChangeTextEditorWeb, - onFocus: controller.handleOnFocusHtmlEditorWeb, - onEditorSettings: controller.richTextWebController?.onEditorSettingsChange, - onEditorTextSizeChanged: controller.richTextWebController?.onEditorTextSizeChanged, - height: constraintsEditor.maxHeight, - horizontalPadding: ComposerStyle.desktopEditorHorizontalPadding, - onDragEnter: controller.handleOnDragEnterHtmlEditorWeb, - onDragOver: controller.handleOnDragOverHtmlEditorWeb, - onPasteImageSuccessAction: (listFileUpload) => controller.handleOnPasteImageSuccessAction( - context: context, - maxWidth: constraintsEditor.maxWidth, - listFileUpload: listFileUpload - ), - onPasteImageFailureAction: (listFileUpload, base64, uploadError) => - controller.handleOnPasteImageFailureAction( - context: context, - listFileUpload: listFileUpload, - base64: base64, - uploadError: uploadError - ), - onInitialContentLoadComplete: controller.onInitialContentLoadCompleteWeb, - onKeyDownEditorAction: controller.onKeyDownEditorAction, - ); - }); - } - ), - ), - Obx(() { - if (controller.uploadController.listUploadAttachments.isNotEmpty) { - return AttachmentComposerWidget( - listFileUploaded: controller.uploadController.listUploadAttachments, - isCollapsed: controller.isAttachmentCollapsed, - onDeleteAttachmentAction: controller.deleteAttachmentUploaded, - onToggleExpandAttachmentAction: (isCollapsed) => controller.isAttachmentCollapsed = isCollapsed, - ); - } else { - return const SizedBox.shrink(); - } - }), - Obx(() { - if (controller.richTextWebController!.isFormattingOptionsEnabled) { - return ToolbarRichTextWidget( - richTextController: controller.richTextWebController!, - imagePaths: controller.imagePaths, - padding: ComposerStyle.richToolbarPadding, - decoration: const BoxDecoration( - color: ComposerStyle.richToolbarColor, - boxShadow: ComposerStyle.richToolbarShadow, - ), - isMobile: controller.responsiveUtils.isMobile(context), - ); - } else { - return const SizedBox.shrink(); - } - }) - ], - ), - ), - ), - Obx(() => BottomBarComposerWidget( - imagePaths: controller.imagePaths, - isCodeViewEnabled: controller.richTextWebController!.codeViewEnabled, - isFormattingOptionsEnabled: controller.richTextWebController!.isFormattingOptionsEnabled, - hasReadReceipt: controller.hasRequestReadReceipt.value, - isMarkAsImportant: controller.isMarkAsImportant.value, - openRichToolbarAction: controller.richTextWebController!.toggleFormattingOptions, - attachFileAction: () => controller.openFilePickerByType(context, FileType.any), - insertImageAction: () => controller.insertImage(context, constraints.maxWidth), - deleteComposerAction: controller.handleClickDeleteComposer, - saveToDraftAction: () => controller.handleClickSaveAsDraftsButton(context), - sendMessageAction: () => controller.handleClickSendButton(context), - isEmailChanged: controller.isEmailChanged.isTrue, - toggleCodeViewAction: controller.richTextWebController!.toggleCodeView, - menuMoreOptionController: controller.menuMoreOptionController!, - onPopupMenuChanged: controller.onPopupMenuChanged, - printDraftAction: () => controller.printDraft(context), - toggleRequestReadReceiptAction: () => controller.toggleRequestReadReceipt(context), - toggleMarkAsImportantAction: () => controller.toggleMarkAsImportant(context), - saveAsTemplateAction: () => controller.handleClickSaveAsTemplateButton(context), - )), + ); + } else { + return const SizedBox.shrink(); + } + }), + Obx(() { + if (controller.mailboxDashBoardController.isLocalFileDraggableAppActive) { + return Positioned.fill( + child: PointerInterceptor( + child: LocalFileDropZoneWidget( + imagePaths: controller.imagePaths, + onLocalFileDropZoneListener: (details) => + controller.onLocalFileDropZoneListener( + context: context, + details: details, + maxWidth: constraints.maxWidth, + ), + ) + ), + ); + } else { + return const SizedBox.shrink(); + } + }), ], ), - Align( - alignment: AlignmentDirectional.topCenter, - child: Obx(() => InsertImageLoadingBarWidget( - uploadInlineViewState: controller.uploadController.uploadInlineViewState.value, - viewState: controller.viewState.value, - margin: ComposerStyle.insertImageLoadingBarMargin, - )), - ), - iframeOverlay, - Obx(() { - if (controller.mailboxDashBoardController.isAttachmentDraggableAppActive) { - return Positioned.fill( - child: PointerInterceptor( - child: AttachmentDropZoneWidget( - imagePaths: controller.imagePaths, - onAttachmentDropZoneListener: - controller.onAttachmentDropZoneListener, - ) - ), - ); - } else { - return const SizedBox.shrink(); - } - }), - Obx(() { - if (controller.mailboxDashBoardController.isLocalFileDraggableAppActive) { - return Positioned.fill( - child: PointerInterceptor( - child: LocalFileDropZoneWidget( - imagePaths: controller.imagePaths, - onLocalFileDropZoneListener: (details) => - controller.onLocalFileDropZoneListener( - context: context, - details: details, - maxWidth: constraints.maxWidth, - ), - ) - ), - ); - } else { - return const SizedBox.shrink(); - } - }), - ], - ), + ), + ]), ), - ]), + ), ); }, composerManager: controller.mailboxDashBoardController.composerManager, @@ -591,260 +620,274 @@ class ComposerView extends GetWidget { )), tablet: TabletResponsiveContainerView( childBuilder: (_, constraints) { - return GestureDetector( - onTap: controller.clickOutsideComposer, - child: Column(children: [ - Obx(() => DesktopAppBarComposerWidget( - imagePaths: controller.imagePaths, - emailSubject: controller.subjectEmail.value ?? '', - onCloseViewAction: () => controller.handleClickCloseComposer(context), - )), - ConstrainedBox( - constraints: BoxConstraints( - maxHeight: ComposerStyle.getMaxHeightEmailAddressWidget( - context, - constraints, - controller.responsiveUtils - ) - ), - child: SingleChildScrollView( - controller: controller.scrollControllerEmailAddress, - child: Obx(() => Column( - children: [ - if (controller.fromRecipientState.value == PrefixRecipientState.enabled) - FromComposerDropDownWidget( - items: controller.listFromIdentities, - itemSelected: controller.identitySelected.value, - dropdownKey: controller.identityDropdownKey, - imagePaths: controller.imagePaths, - padding: ComposerStyle.tabletRecipientPadding, - margin: ComposerStyle.tabletRecipientMargin, - onChangeIdentity: controller.onChangeIdentity, - ), - if (controller.recipientsCollapsedState.value == PrefixRecipientState.enabled) - RecipientsCollapsedComposerWidget( - listEmailAddress: controller.allListEmailAddressWithoutReplyTo, - margin: ComposerStyle.desktopRecipientMargin, - onShowAllRecipientsAction: controller.showFullRecipients, - ), - if (controller.toRecipientState.value == PrefixRecipientState.enabled) - _buildRecipientComposerWidget( - composerId: composerId, - prefix: PrefixEmailAddress.to, - controller: controller, - maxWidth: constraints.maxWidth, - listEmailAddress: controller.listToEmailAddress, - textController: controller.toEmailAddressController, - focusNode: controller.toAddressFocusNode, - focusNodeKeyboard: controller.toAddressFocusNodeKeyboard, - keyTagEditor: controller.keyToEmailTagEditor, - nextFocusNode: controller.getNextFocusOfToEmailAddress(), - ), - if (controller.ccRecipientState.value == PrefixRecipientState.enabled) - _buildRecipientComposerWidget( - composerId: composerId, - prefix: PrefixEmailAddress.cc, - controller: controller, - maxWidth: constraints.maxWidth, - listEmailAddress: controller.listCcEmailAddress, - textController: controller.ccEmailAddressController, - focusNode: controller.ccAddressFocusNode, - focusNodeKeyboard: controller.ccAddressFocusNodeKeyboard, - keyTagEditor: controller.keyCcEmailTagEditor, - nextFocusNode: controller.getNextFocusOfCcEmailAddress(), - ), - if (controller.bccRecipientState.value == PrefixRecipientState.enabled) - _buildRecipientComposerWidget( - composerId: composerId, - prefix: PrefixEmailAddress.bcc, - controller: controller, - maxWidth: constraints.maxWidth, - listEmailAddress: controller.listBccEmailAddress, - textController: controller.bccEmailAddressController, - focusNode: controller.bccAddressFocusNode, - focusNodeKeyboard: controller.bccAddressFocusNodeKeyboard, - keyTagEditor: controller.keyBccEmailTagEditor, - nextFocusNode: controller.getNextFocusOfBccEmailAddress(), - ), - if (controller.replyToRecipientState.value == PrefixRecipientState.enabled) - _buildRecipientComposerWidget( - composerId: composerId, - prefix: PrefixEmailAddress.replyTo, - controller: controller, - maxWidth: constraints.maxWidth, - listEmailAddress: controller.listReplyToEmailAddress, - textController: controller.replyToEmailAddressController, - focusNode: controller.replyToAddressFocusNode, - focusNodeKeyboard: controller.replyToAddressFocusNodeKeyboard, - keyTagEditor: controller.keyReplyToEmailTagEditor, - nextFocusNode: controller.subjectEmailInputFocusNode, - ), - ], + return Shortcuts( + shortcuts: { + LogicalKeySet(LogicalKeyboardKey.tab): + const DoNothingAndStopPropagationIntent(), + }, + child: Actions( + actions: { + DoNothingAndStopPropagationIntent: + CallbackAction( + onInvoke: (_) => null + ), + }, + child: GestureDetector( + onTap: controller.clickOutsideComposer, + child: Column(children: [ + Obx(() => DesktopAppBarComposerWidget( + imagePaths: controller.imagePaths, + emailSubject: controller.subjectEmail.value ?? '', + onCloseViewAction: () => controller.handleClickCloseComposer(context), )), - ) - ), - SubjectComposerWidget( - focusNode: controller.subjectEmailInputFocusNode, - textController: controller.subjectEmailInputController, - onTextChange: controller.setSubjectEmail, - padding: ComposerStyle.tabletSubjectPadding, - margin: ComposerStyle.tabletSubjectMargin, - ), - Expanded( - child: Stack( - children: [ - Column( + ConstrainedBox( + constraints: BoxConstraints( + maxHeight: ComposerStyle.getMaxHeightEmailAddressWidget( + context, + constraints, + controller.responsiveUtils + ) + ), + child: SingleChildScrollView( + controller: controller.scrollControllerEmailAddress, + child: Obx(() => Column( + children: [ + if (controller.fromRecipientState.value == PrefixRecipientState.enabled) + FromComposerDropDownWidget( + items: controller.listFromIdentities, + itemSelected: controller.identitySelected.value, + dropdownKey: controller.identityDropdownKey, + imagePaths: controller.imagePaths, + padding: ComposerStyle.tabletRecipientPadding, + margin: ComposerStyle.tabletRecipientMargin, + onChangeIdentity: controller.onChangeIdentity, + ), + if (controller.recipientsCollapsedState.value == PrefixRecipientState.enabled) + RecipientsCollapsedComposerWidget( + listEmailAddress: controller.allListEmailAddressWithoutReplyTo, + margin: ComposerStyle.desktopRecipientMargin, + onShowAllRecipientsAction: controller.showFullRecipients, + ), + if (controller.toRecipientState.value == PrefixRecipientState.enabled) + _buildRecipientComposerWidget( + composerId: composerId, + prefix: PrefixEmailAddress.to, + controller: controller, + maxWidth: constraints.maxWidth, + listEmailAddress: controller.listToEmailAddress, + textController: controller.toEmailAddressController, + focusNode: controller.toAddressFocusNode, + focusNodeKeyboard: controller.toAddressFocusNodeKeyboard, + keyTagEditor: controller.keyToEmailTagEditor, + nextFocusNode: controller.getNextFocusOfToEmailAddress(), + ), + if (controller.ccRecipientState.value == PrefixRecipientState.enabled) + _buildRecipientComposerWidget( + composerId: composerId, + prefix: PrefixEmailAddress.cc, + controller: controller, + maxWidth: constraints.maxWidth, + listEmailAddress: controller.listCcEmailAddress, + textController: controller.ccEmailAddressController, + focusNode: controller.ccAddressFocusNode, + focusNodeKeyboard: controller.ccAddressFocusNodeKeyboard, + keyTagEditor: controller.keyCcEmailTagEditor, + nextFocusNode: controller.getNextFocusOfCcEmailAddress(), + ), + if (controller.bccRecipientState.value == PrefixRecipientState.enabled) + _buildRecipientComposerWidget( + composerId: composerId, + prefix: PrefixEmailAddress.bcc, + controller: controller, + maxWidth: constraints.maxWidth, + listEmailAddress: controller.listBccEmailAddress, + textController: controller.bccEmailAddressController, + focusNode: controller.bccAddressFocusNode, + focusNodeKeyboard: controller.bccAddressFocusNodeKeyboard, + keyTagEditor: controller.keyBccEmailTagEditor, + nextFocusNode: controller.getNextFocusOfBccEmailAddress(), + ), + if (controller.replyToRecipientState.value == PrefixRecipientState.enabled) + _buildRecipientComposerWidget( + composerId: composerId, + prefix: PrefixEmailAddress.replyTo, + controller: controller, + maxWidth: constraints.maxWidth, + listEmailAddress: controller.listReplyToEmailAddress, + textController: controller.replyToEmailAddressController, + focusNode: controller.replyToAddressFocusNode, + focusNodeKeyboard: controller.replyToAddressFocusNodeKeyboard, + keyTagEditor: controller.keyReplyToEmailTagEditor, + nextFocusNode: controller.subjectEmailInputFocusNode, + ), + ], + )), + ) + ), + SubjectComposerWidget( + focusNode: controller.subjectEmailInputFocusNode, + textController: controller.subjectEmailInputController, + onTextChange: controller.setSubjectEmail, + padding: ComposerStyle.tabletSubjectPadding, + margin: ComposerStyle.tabletSubjectMargin, + ), + Expanded( + child: Stack( children: [ - Expanded( - child: Container( - decoration: const BoxDecoration( - border: Border( - bottom: BorderSide( - color: ComposerStyle.borderColor, - width: 1 + Column( + children: [ + Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide( + color: ComposerStyle.borderColor, + width: 1 + ) + ), + color: ComposerStyle.backgroundEditorColor + ), + child: Column( + children: [ + Expanded( + child: LayoutBuilder( + builder: (_, constraintsBody) { + return Obx(() => WebEditorView( + key: controller.responsiveContainerKey, + editorController: controller.richTextWebController!.editorController, + arguments: controller.composerArguments.value, + contentViewState: controller.emailContentsViewState.value, + currentWebContent: controller.textEditorWeb, + onInitial: controller.handleInitHtmlEditorWeb, + onChangeContent: controller.onChangeTextEditorWeb, + onFocus: controller.handleOnFocusHtmlEditorWeb, + onEditorSettings: controller.richTextWebController!.onEditorSettingsChange, + onEditorTextSizeChanged: controller.richTextWebController!.onEditorTextSizeChanged, + height: constraintsBody.maxHeight, + horizontalPadding: ComposerStyle.desktopEditorHorizontalPadding, + onDragEnter: controller.handleOnDragEnterHtmlEditorWeb, + onDragOver: controller.handleOnDragOverHtmlEditorWeb, + onPasteImageSuccessAction: (listFileUpload) => controller.handleOnPasteImageSuccessAction( + context: context, + maxWidth: constraintsBody.maxWidth, + listFileUpload: listFileUpload + ), + onPasteImageFailureAction: (listFileUpload, base64, uploadError) => + controller.handleOnPasteImageFailureAction( + context: context, + listFileUpload: listFileUpload, + base64: base64, + uploadError: uploadError + ), + onInitialContentLoadComplete: controller.onInitialContentLoadCompleteWeb, + onKeyDownEditorAction: controller.onKeyDownEditorAction, + )); + } + ), + ), + Obx(() { + if (controller.uploadController.listUploadAttachments.isNotEmpty) { + return AttachmentComposerWidget( + listFileUploaded: controller.uploadController.listUploadAttachments, + isCollapsed: controller.isAttachmentCollapsed, + onDeleteAttachmentAction: controller.deleteAttachmentUploaded, + onToggleExpandAttachmentAction: (isCollapsed) => controller.isAttachmentCollapsed = isCollapsed, + ); + } else { + return const SizedBox.shrink(); + } + }), + Obx(() { + if (controller.richTextWebController!.isFormattingOptionsEnabled) { + return ToolbarRichTextWidget( + richTextController: controller.richTextWebController!, + imagePaths: controller.imagePaths, + padding: ComposerStyle.richToolbarPadding, + decoration: const BoxDecoration( + color: ComposerStyle.richToolbarColor, + boxShadow: ComposerStyle.richToolbarShadow, + ), + isMobile: controller.responsiveUtils.isMobile(context), + ); + } else { + return const SizedBox.shrink(); + } + }) + ], + ), + ), + ), + Obx(() => BottomBarComposerWidget( + imagePaths: controller.imagePaths, + isCodeViewEnabled: controller.richTextWebController!.codeViewEnabled, + isFormattingOptionsEnabled: controller.richTextWebController!.isFormattingOptionsEnabled, + hasReadReceipt: controller.hasRequestReadReceipt.value, + isMarkAsImportant: controller.isMarkAsImportant.value, + openRichToolbarAction: controller.richTextWebController!.toggleFormattingOptions, + attachFileAction: () => controller.openFilePickerByType(context, FileType.any), + insertImageAction: () => controller.insertImage(context, constraints.maxWidth), + deleteComposerAction: controller.handleClickDeleteComposer, + saveToDraftAction: () => controller.handleClickSaveAsDraftsButton(context), + sendMessageAction: () => controller.handleClickSendButton(context), + isEmailChanged: controller.isEmailChanged.isTrue, + toggleCodeViewAction: controller.richTextWebController!.toggleCodeView, + menuMoreOptionController: controller.menuMoreOptionController!, + onPopupMenuChanged: controller.onPopupMenuChanged, + printDraftAction: () => controller.printDraft(context), + toggleRequestReadReceiptAction: () => controller.toggleRequestReadReceipt(context), + toggleMarkAsImportantAction: () => controller.toggleMarkAsImportant(context), + saveAsTemplateAction: () => controller.handleClickSaveAsTemplateButton(context), + )), + ], + ), + Align( + alignment: AlignmentDirectional.topCenter, + child: Obx(() => InsertImageLoadingBarWidget( + uploadInlineViewState: controller.uploadController.uploadInlineViewState.value, + viewState: controller.viewState.value, + margin: ComposerStyle.insertImageLoadingBarMargin, + )), + ), + iframeOverlay, + Obx(() { + if (controller.mailboxDashBoardController.isAttachmentDraggableAppActive) { + return Positioned.fill( + child: PointerInterceptor( + child: AttachmentDropZoneWidget( + imagePaths: controller.imagePaths, + onAttachmentDropZoneListener: controller.onAttachmentDropZoneListener, ) ), - color: ComposerStyle.backgroundEditorColor - ), - child: Column( - children: [ - Expanded( - child: LayoutBuilder( - builder: (_, constraintsBody) { - return Obx(() => WebEditorView( - key: controller.responsiveContainerKey, - editorController: controller.richTextWebController!.editorController, - arguments: controller.composerArguments.value, - contentViewState: controller.emailContentsViewState.value, - currentWebContent: controller.textEditorWeb, - onInitial: controller.handleInitHtmlEditorWeb, - onChangeContent: controller.onChangeTextEditorWeb, - onFocus: controller.handleOnFocusHtmlEditorWeb, - onEditorSettings: controller.richTextWebController!.onEditorSettingsChange, - onEditorTextSizeChanged: controller.richTextWebController!.onEditorTextSizeChanged, - height: constraintsBody.maxHeight, - horizontalPadding: ComposerStyle.desktopEditorHorizontalPadding, - onDragEnter: controller.handleOnDragEnterHtmlEditorWeb, - onDragOver: controller.handleOnDragOverHtmlEditorWeb, - onPasteImageSuccessAction: (listFileUpload) => controller.handleOnPasteImageSuccessAction( - context: context, - maxWidth: constraintsBody.maxWidth, - listFileUpload: listFileUpload - ), - onPasteImageFailureAction: (listFileUpload, base64, uploadError) => - controller.handleOnPasteImageFailureAction( - context: context, - listFileUpload: listFileUpload, - base64: base64, - uploadError: uploadError - ), - onInitialContentLoadComplete: controller.onInitialContentLoadCompleteWeb, - onKeyDownEditorAction: controller.onKeyDownEditorAction, - )); - } - ), - ), - Obx(() { - if (controller.uploadController.listUploadAttachments.isNotEmpty) { - return AttachmentComposerWidget( - listFileUploaded: controller.uploadController.listUploadAttachments, - isCollapsed: controller.isAttachmentCollapsed, - onDeleteAttachmentAction: controller.deleteAttachmentUploaded, - onToggleExpandAttachmentAction: (isCollapsed) => controller.isAttachmentCollapsed = isCollapsed, - ); - } else { - return const SizedBox.shrink(); - } - }), - Obx(() { - if (controller.richTextWebController!.isFormattingOptionsEnabled) { - return ToolbarRichTextWidget( - richTextController: controller.richTextWebController!, - imagePaths: controller.imagePaths, - padding: ComposerStyle.richToolbarPadding, - decoration: const BoxDecoration( - color: ComposerStyle.richToolbarColor, - boxShadow: ComposerStyle.richToolbarShadow, - ), - isMobile: controller.responsiveUtils.isMobile(context), - ); - } else { - return const SizedBox.shrink(); - } - }) - ], - ), - ), - ), - Obx(() => BottomBarComposerWidget( - imagePaths: controller.imagePaths, - isCodeViewEnabled: controller.richTextWebController!.codeViewEnabled, - isFormattingOptionsEnabled: controller.richTextWebController!.isFormattingOptionsEnabled, - hasReadReceipt: controller.hasRequestReadReceipt.value, - isMarkAsImportant: controller.isMarkAsImportant.value, - openRichToolbarAction: controller.richTextWebController!.toggleFormattingOptions, - attachFileAction: () => controller.openFilePickerByType(context, FileType.any), - insertImageAction: () => controller.insertImage(context, constraints.maxWidth), - deleteComposerAction: controller.handleClickDeleteComposer, - saveToDraftAction: () => controller.handleClickSaveAsDraftsButton(context), - sendMessageAction: () => controller.handleClickSendButton(context), - isEmailChanged: controller.isEmailChanged.isTrue, - toggleCodeViewAction: controller.richTextWebController!.toggleCodeView, - menuMoreOptionController: controller.menuMoreOptionController!, - onPopupMenuChanged: controller.onPopupMenuChanged, - printDraftAction: () => controller.printDraft(context), - toggleRequestReadReceiptAction: () => controller.toggleRequestReadReceipt(context), - toggleMarkAsImportantAction: () => controller.toggleMarkAsImportant(context), - saveAsTemplateAction: () => controller.handleClickSaveAsTemplateButton(context), - )), + ); + } else { + return const SizedBox.shrink(); + } + }), + Obx(() { + if (controller.mailboxDashBoardController.isLocalFileDraggableAppActive) { + return Positioned.fill( + child: PointerInterceptor( + child: LocalFileDropZoneWidget( + imagePaths: controller.imagePaths, + onLocalFileDropZoneListener: (details) => + controller.onLocalFileDropZoneListener( + context: context, + details: details, + maxWidth: constraints.maxWidth, + ), + ) + ), + ); + } else { + return const SizedBox.shrink(); + } + }), ], ), - Align( - alignment: AlignmentDirectional.topCenter, - child: Obx(() => InsertImageLoadingBarWidget( - uploadInlineViewState: controller.uploadController.uploadInlineViewState.value, - viewState: controller.viewState.value, - margin: ComposerStyle.insertImageLoadingBarMargin, - )), - ), - iframeOverlay, - Obx(() { - if (controller.mailboxDashBoardController.isAttachmentDraggableAppActive) { - return Positioned.fill( - child: PointerInterceptor( - child: AttachmentDropZoneWidget( - imagePaths: controller.imagePaths, - onAttachmentDropZoneListener: controller.onAttachmentDropZoneListener, - ) - ), - ); - } else { - return const SizedBox.shrink(); - } - }), - Obx(() { - if (controller.mailboxDashBoardController.isLocalFileDraggableAppActive) { - return Positioned.fill( - child: PointerInterceptor( - child: LocalFileDropZoneWidget( - imagePaths: controller.imagePaths, - onLocalFileDropZoneListener: (details) => - controller.onLocalFileDropZoneListener( - context: context, - details: details, - maxWidth: constraints.maxWidth, - ), - ) - ), - ); - } else { - return const SizedBox.shrink(); - } - }), - ], - ), - ) - ]), + ) + ]), + ), + ), ); }, ) diff --git a/lib/features/composer/presentation/widgets/recipient_composer_widget.dart b/lib/features/composer/presentation/widgets/recipient_composer_widget.dart index c81c017b8..f1a8f3dd7 100644 --- a/lib/features/composer/presentation/widgets/recipient_composer_widget.dart +++ b/lib/features/composer/presentation/widgets/recipient_composer_widget.dart @@ -134,6 +134,9 @@ class _RecipientComposerWidgetState extends State { void initState() { super.initState(); _currentListEmailAddress = widget.listEmailAddress; + if (PlatformInfo.isWeb) { + widget.focusNodeKeyboard?.addListener(_onFocusKeyboardListener); + } } @override @@ -144,6 +147,20 @@ class _RecipientComposerWidgetState extends State { } } + @override + void dispose() { + if (PlatformInfo.isWeb) { + widget.focusNodeKeyboard?.removeListener(_onFocusKeyboardListener); + } + super.dispose(); + } + + void _onFocusKeyboardListener() { + if (widget.focusNodeKeyboard?.hasFocus != true && mounted) { + setState(() => _tagIndexFocused = -1); + } + } + @override Widget build(BuildContext context) { bool isMobileResponsive = _responsiveUtils.isMobile(context); @@ -406,7 +423,9 @@ class _RecipientComposerWidgetState extends State { } KeyEventResult _recipientInputOnKeyListener(FocusNode node, KeyEvent event) { - if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.tab) { + if (widget.focusNode?.hasFocus == true && + event is KeyDownEvent && + event.logicalKey == LogicalKeyboardKey.tab) { widget.nextFocusNode?.requestFocus(); widget.onFocusNextAddressAction?.call(); return KeyEventResult.handled; diff --git a/pubspec.lock b/pubspec.lock index 4fb694a5a..b054b9502 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -2170,10 +2170,10 @@ packages: dependency: "direct main" description: name: super_tag_editor - sha256: "83823674fa1048eb0411d169c9072028fa80ba6998ef18b357c29f58b835ea75" + sha256: "7f7c1cb38e98e9cfa92fb074d4d5f5f53846d6bffcc96e05cb2191c159dba28d" url: "https://pub.dev" source: hosted - version: "0.4.1" + version: "1.1.0" syncfusion_flutter_core: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 9c1951332..da68f830c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -110,7 +110,7 @@ dependencies: ref: master ### Dependencies from pub.dev ### - super_tag_editor: 0.4.1 + super_tag_editor: 1.1.0 external_app_launcher: 4.0.3