From 006d805304a63a7033780ae061021b9f50c8f2a2 Mon Sep 17 00:00:00 2001 From: HuyNguyen Date: Wed, 12 Apr 2023 00:40:22 +0700 Subject: [PATCH] TF-1544: Support can `TAB` another field in composer (cherry picked from commit 38f24da4295f4e6231eb77af18b2181496ad78f8) --- .../presentation/composer_controller.dart | 35 ++++++++++++++++++- .../presentation/composer_view_web.dart | 20 +++++++---- .../widgets/email_address_input_builder.dart | 17 +++++++++ 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index a809a65b6..4844177c4 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -12,6 +12,7 @@ import 'package:filesize/filesize.dart'; import 'package:fk_user_agent/fk_user_agent.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:http_parser/http_parser.dart'; @@ -346,7 +347,15 @@ class ComposerController extends BaseController { void createFocusNodeInput() { toAddressFocusNode = FocusNode(); - subjectEmailInputFocusNode = FocusNode(); + subjectEmailInputFocusNode = FocusNode( + onKey: (focus, event) { + if (event is RawKeyDownEvent && event.logicalKey == LogicalKeyboardKey.tab) { + richTextWebController.editorController.setFocus(); + return KeyEventResult.handled; + } + return KeyEventResult.ignored; + } + ); ccAddressFocusNode = FocusNode(); bccAddressFocusNode = FocusNode(); @@ -1198,9 +1207,11 @@ class ComposerController extends BaseController { updateListEmailAddress(prefixEmailAddress, []); switch(prefixEmailAddress) { case PrefixEmailAddress.cc: + ccAddressFocusNode = FocusNode(); ccEmailAddressController.clear(); break; case PrefixEmailAddress.bcc: + bccAddressFocusNode = FocusNode(); bccEmailAddressController.clear(); break; default: @@ -1632,4 +1643,26 @@ class ComposerController extends BaseController { Navigator.maybePop(context); onEditorFocusChange(true); } + + FocusNode? getNextFocusOfToEmailAddress() { + if (listEmailAddressType.contains(PrefixEmailAddress.cc) == true) { + return ccAddressFocusNode; + } else if (listEmailAddressType.contains(PrefixEmailAddress.bcc) == true) { + return bccAddressFocusNode; + } else { + return subjectEmailInputFocusNode; + } + } + + FocusNode? getNextFocusOfCcEmailAddress() { + if (listEmailAddressType.contains(PrefixEmailAddress.bcc) == true) { + return bccAddressFocusNode; + } else { + return subjectEmailInputFocusNode; + } + } + + void handleFocusNextAddressAction() { + _autoCreateEmailTag(); + } } \ No newline at end of file diff --git a/lib/features/composer/presentation/composer_view_web.dart b/lib/features/composer/presentation/composer_view_web.dart index 8ee74998d..33745ac42 100644 --- a/lib/features/composer/presentation/composer_view_web.dart +++ b/lib/features/composer/presentation/composer_view_web.dart @@ -579,13 +579,15 @@ class ComposerView extends GetWidget focusNode: controller.toAddressFocusNode, autoDisposeFocusNode: false, isInitial: controller.isInitialRecipient.value, - keyTagEditor: controller.keyToEmailTagEditor + keyTagEditor: controller.keyToEmailTagEditor, + nextFocusNode: controller.getNextFocusOfToEmailAddress() ) ..addOnFocusEmailAddressChangeAction((prefixEmailAddress, focus) => controller.onEmailAddressFocusChange(prefixEmailAddress, focus)) ..addOnShowFullListEmailAddressAction((prefixEmailAddress) => controller.showFullEmailAddress(prefixEmailAddress)) ..addOnAddEmailAddressTypeAction((prefixEmailAddress) => controller.addEmailAddressType(prefixEmailAddress)) ..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress)) - ..addOnSuggestionEmailAddress(controller.getAutoCompleteSuggestion)) + ..addOnSuggestionEmailAddress(controller.getAutoCompleteSuggestion) + ..addOnFocusNextAddressAction(controller.handleFocusNextAddressAction)) .build() )), Obx(() => controller.listEmailAddressType.contains(PrefixEmailAddress.cc) == true @@ -604,13 +606,16 @@ class ComposerView extends GetWidget expandMode: controller.ccAddressExpandMode.value, controller: controller.ccEmailAddressController, isInitial: controller.isInitialRecipient.value, - keyTagEditor: controller.keyCcEmailTagEditor + keyTagEditor: controller.keyCcEmailTagEditor, + focusNode: controller.ccAddressFocusNode, + nextFocusNode: controller.getNextFocusOfCcEmailAddress() ) ..addOnFocusEmailAddressChangeAction((prefixEmailAddress, focus) => controller.onEmailAddressFocusChange(prefixEmailAddress, focus)) ..addOnShowFullListEmailAddressAction((prefixEmailAddress) => controller.showFullEmailAddress(prefixEmailAddress)) ..addOnDeleteEmailAddressTypeAction((prefixEmailAddress) => controller.deleteEmailAddressType(prefixEmailAddress)) ..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress)) - ..addOnSuggestionEmailAddress(controller.getAutoCompleteSuggestion)) + ..addOnSuggestionEmailAddress(controller.getAutoCompleteSuggestion) + ..addOnFocusNextAddressAction(controller.handleFocusNextAddressAction)) .build()) : const SizedBox.shrink() ), @@ -630,13 +635,16 @@ class ComposerView extends GetWidget expandMode: controller.bccAddressExpandMode.value, controller: controller.bccEmailAddressController, isInitial: controller.isInitialRecipient.value, - keyTagEditor: controller.keyBccEmailTagEditor + keyTagEditor: controller.keyBccEmailTagEditor, + focusNode: controller.bccAddressFocusNode, + nextFocusNode: controller.subjectEmailInputFocusNode ) ..addOnFocusEmailAddressChangeAction((prefixEmailAddress, focus) => controller.onEmailAddressFocusChange(prefixEmailAddress, focus)) ..addOnShowFullListEmailAddressAction((prefixEmailAddress) => controller.showFullEmailAddress(prefixEmailAddress)) ..addOnDeleteEmailAddressTypeAction((prefixEmailAddress) => controller.deleteEmailAddressType(prefixEmailAddress)) ..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress)) - ..addOnSuggestionEmailAddress(controller.getAutoCompleteSuggestion)) + ..addOnSuggestionEmailAddress(controller.getAutoCompleteSuggestion) + ..addOnFocusNextAddressAction(controller.handleFocusNextAddressAction)) .build()) : const SizedBox.shrink() ), diff --git a/lib/features/composer/presentation/widgets/email_address_input_builder.dart b/lib/features/composer/presentation/widgets/email_address_input_builder.dart index bd07a7bf4..89be8e115 100644 --- a/lib/features/composer/presentation/widgets/email_address_input_builder.dart +++ b/lib/features/composer/presentation/widgets/email_address_input_builder.dart @@ -5,6 +5,7 @@ import 'package:collection/collection.dart'; import 'package:core/core.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; @@ -21,6 +22,7 @@ typedef OnAddEmailAddressTypeAction = void Function(PrefixEmailAddress); typedef OnDeleteEmailAddressTypeAction = void Function(PrefixEmailAddress); typedef OnShowFullListEmailAddressAction = void Function(PrefixEmailAddress); typedef OnFocusEmailAddressChangeAction = void Function(PrefixEmailAddress, bool); +typedef OnFocusNextAddressAction = void Function(); class EmailAddressInputBuilder { @@ -35,6 +37,7 @@ class EmailAddressInputBuilder { final FocusNode? focusNode; final bool autoDisposeFocusNode; final GlobalKey? keyTagEditor; + final FocusNode? nextFocusNode; List listEmailAddress = []; @@ -44,6 +47,7 @@ class EmailAddressInputBuilder { OnDeleteEmailAddressTypeAction? _onDeleteEmailAddressTypeAction; OnShowFullListEmailAddressAction? _onShowFullListEmailAddressAction; OnFocusEmailAddressChangeAction? _onFocusEmailAddressChangeAction; + OnFocusNextAddressAction? _onFocusNextAddressAction; Timer? _gapBetweenTagChangedAndFindSuggestion; bool lastTagFocused = false; @@ -72,6 +76,10 @@ class EmailAddressInputBuilder { _onFocusEmailAddressChangeAction = onFocusEmailAddressChangeAction; } + void addOnFocusNextAddressAction(OnFocusNextAddressAction onFocusNextAddressAction) { + _onFocusNextAddressAction = onFocusNextAddressAction; + } + EmailAddressInputBuilder( this._context, this._imagePaths, @@ -85,6 +93,7 @@ class EmailAddressInputBuilder { this.autoDisposeFocusNode = true, this.expandMode = ExpandMode.EXPAND, this.keyTagEditor, + this.nextFocusNode, } ); @@ -132,6 +141,14 @@ class EmailAddressInputBuilder { final newListEmailAddress = _isCollapse ? listEmailAddress.sublist(0, 1) : listEmailAddress; return FocusScope(child: Focus( onFocusChange: (focus) => _onFocusEmailAddressChangeAction?.call(_prefixEmailAddress, focus), + onKey: (focusNode, event) { + if (event is RawKeyDownEvent && event.logicalKey == LogicalKeyboardKey.tab) { + nextFocusNode?.requestFocus(); + _onFocusNextAddressAction?.call(); + return KeyEventResult.handled; + } + return KeyEventResult.ignored; + }, child: TagEditor( key: keyTagEditor, length: newListEmailAddress.length,