From ba0217f2fb438f56717169720535f083a22d9bc9 Mon Sep 17 00:00:00 2001 From: dab246 Date: Fri, 9 Dec 2022 19:18:37 +0700 Subject: [PATCH] TF-1152 Make better manage initial cursor position in composer --- .../presentation/composer_controller.dart | 74 +++++++++++++++---- .../composer/presentation/composer_view.dart | 3 + .../presentation/composer_view_web.dart | 5 +- .../widgets/email_address_input_builder.dart | 8 +- pubspec.lock | 6 +- pubspec.yaml | 2 +- 6 files changed, 77 insertions(+), 21 deletions(-) diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index 4868b2973..4b4a07be8 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -5,7 +5,6 @@ import 'dart:io'; import 'package:core/core.dart'; import 'package:dartz/dartz.dart'; import 'package:device_info_plus/device_info_plus.dart'; -import 'package:enough_html_editor/enough_html_editor.dart'; import 'package:file_picker/file_picker.dart'; import 'package:filesize/filesize.dart'; import 'package:fk_user_agent/fk_user_agent.dart'; @@ -115,6 +114,9 @@ class ComposerController extends BaseController { final ccEmailAddressController = TextEditingController(); final bccEmailAddressController = TextEditingController(); + FocusNode? subjectEmailInputFocusNode; + FocusNode? toAddressFocusNode; + final RichTextController keyboardRichTextController = RichTextController(); final ScrollController scrollController = ScrollController(); @@ -183,6 +185,7 @@ class ComposerController extends BaseController { @override void onInit() { super.onInit(); + createFocusNodeInput(); _listenWorker(); if (!BuildUtils.isWeb) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { @@ -216,6 +219,10 @@ class ComposerController extends BaseController { @override void dispose() { + subjectEmailInputFocusNode?.dispose(); + subjectEmailInputFocusNode = null; + toAddressFocusNode?.dispose(); + toAddressFocusNode = null; subjectEmailInputController.dispose(); toEmailAddressController.dispose(); ccEmailAddressController.dispose(); @@ -252,14 +259,7 @@ class ComposerController extends BaseController { } else if (success is GetEmailContentSuccess) { _getEmailContentSuccess(success); } else if (success is GetAllIdentitiesSuccess) { - if (success.identities?.isNotEmpty == true) { - listIdentities.value = success.identities! - .where((identity) => identity.mayDelete == true) - .toList(); - if (listIdentities.isNotEmpty) { - selectIdentity(listIdentities.first); - } - } + _handleGetAllIdentitiesSuccess(success); } else if (success is DownloadImageAsBase64Success) { if(kIsWeb) { richTextWebController.insertImage( @@ -305,6 +305,11 @@ class ComposerController extends BaseController { }); } + void createFocusNodeInput() { + toAddressFocusNode = FocusNode(); + subjectEmailInputFocusNode = FocusNode(); + } + void _initEmail() { final arguments = kIsWeb ? mailboxDashBoardController.routerArguments : Get.arguments; if (arguments is ComposerArguments) { @@ -321,6 +326,8 @@ class ComposerController extends BaseController { _initSubjectEmail(arguments); _initAttachments(arguments); } + + _autoFocusFieldWhenLauncher(); } void _initSubjectEmail(ComposerArguments arguments) { @@ -350,6 +357,20 @@ class ComposerController extends BaseController { } } + void _handleGetAllIdentitiesSuccess(GetAllIdentitiesSuccess success) async { + if (success.identities?.isNotEmpty == true) { + listIdentities.value = success.identities! + .where((identity) => identity.mayDelete == true) + .toList(); + + if (listIdentities.isNotEmpty) { + await selectIdentity(listIdentities.first); + } + } + + _autoFocusFieldWhenLauncher(); + } + String? getContentEmail(BuildContext context) { if (composerArguments.value != null) { switch(composerArguments.value!.emailActionType) { @@ -1076,8 +1097,10 @@ class ComposerController extends BaseController { void displayScreenTypeComposerAction(ScreenDisplayMode displayMode) { FocusManager.instance.primaryFocus?.unfocus(); + createFocusNodeInput(); _updateTextForEditor(); screenDisplayMode.value = displayMode; + _autoFocusFieldWhenLauncher(); } void _updateTextForEditor() async { @@ -1174,13 +1197,13 @@ class ComposerController extends BaseController { } } - void selectIdentity(Identity? newIdentity) { + Future selectIdentity(Identity? newIdentity) async { final formerIdentity = identitySelected.value; identitySelected.value = newIdentity; if (newIdentity != null) { - _applyIdentityForAllFieldComposer(formerIdentity, newIdentity); - FocusManager.instance.primaryFocus?.unfocus(); + await _applyIdentityForAllFieldComposer(formerIdentity, newIdentity); } + return Future.value(null); } bool get _isMobileApp { @@ -1189,7 +1212,10 @@ class ComposerController extends BaseController { && _responsiveUtils.isMobile(currentContext!); } - void _applyIdentityForAllFieldComposer(Identity? formerIdentity, Identity newIdentity) { + Future _applyIdentityForAllFieldComposer( + Identity? formerIdentity, + Identity newIdentity + ) async { if (formerIdentity != null) { // Remove former identity if (formerIdentity.bcc?.isNotEmpty == true) { @@ -1203,7 +1229,7 @@ class ComposerController extends BaseController { // Add new identity if (newIdentity.bcc?.isNotEmpty == true) { - _applyBccEmailAddressFromIdentity(newIdentity.bcc!); + await _applyBccEmailAddressFromIdentity(newIdentity.bcc!); } if (!_isMobileApp) { @@ -1213,9 +1239,11 @@ class ComposerController extends BaseController { _applySignature(newIdentity.textSignature!); } } + + return Future.value(null); } - void _applyBccEmailAddressFromIdentity(Set listEmailAddress) { + Future _applyBccEmailAddressFromIdentity(Set listEmailAddress) { if (!listEmailAddressType.contains(PrefixEmailAddress.bcc)) { listEmailAddressType.add(PrefixEmailAddress.bcc); } @@ -1224,6 +1252,8 @@ class ComposerController extends BaseController { ccAddressExpandMode.value = ExpandMode.COLLAPSE; bccAddressExpandMode.value = ExpandMode.COLLAPSE; _updateStatusEmailSendButton(); + + return Future.value(null); } void _removeBccEmailAddressFromFormerIdentity(Set listEmailAddress) { @@ -1368,4 +1398,18 @@ class ComposerController extends BaseController { hasRequestReadReceipt.toggle(); } + void _autoFocusFieldWhenLauncher() { + FocusManager.instance.primaryFocus?.unfocus(); + + if (listToEmailAddress.isEmpty) { + toAddressFocusNode?.requestFocus(); + } else if (subjectEmailInputController.text.isEmpty) { + subjectEmailInputFocusNode?.requestFocus(); + } else if (BuildUtils.isWeb) { + Future.delayed( + const Duration(milliseconds: 500), + richTextWebController.editorController.setFocus + ); + } + } } \ No newline at end of file diff --git a/lib/features/composer/presentation/composer_view.dart b/lib/features/composer/presentation/composer_view.dart index efa3d276a..d0a01a2c2 100644 --- a/lib/features/composer/presentation/composer_view.dart +++ b/lib/features/composer/presentation/composer_view.dart @@ -354,6 +354,8 @@ class ComposerView extends GetWidget controller.listEmailAddressType, expandMode: controller.toAddressExpandMode.value, controller: controller.toEmailAddressController, + focusNode: controller.toAddressFocusNode, + autoDisposeFocusNode: false, isInitial: controller.isInitialRecipient.value) ..addOnFocusEmailAddressChangeAction((prefixEmailAddress, focus) => controller.onEmailAddressFocusChange(prefixEmailAddress, focus)) ..addOnShowFullListEmailAddressAction((prefixEmailAddress) => controller.showFullEmailAddress(prefixEmailAddress)) @@ -431,6 +433,7 @@ class ComposerView extends GetWidget ..key(const Key('subject_email_input')) ..cursorColor(AppColor.colorTextButton) ..maxLines(responsiveUtils.isMobile(context) ? null : 1) + ..addFocusNode(controller.subjectEmailInputFocusNode) ..onChange((value) => controller.setSubjectEmail(value)) ..textStyle(const TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.normal)) ..textDecoration(const InputDecoration(contentPadding: EdgeInsets.zero, border: InputBorder.none)) diff --git a/lib/features/composer/presentation/composer_view_web.dart b/lib/features/composer/presentation/composer_view_web.dart index 84af12519..82e0932e8 100644 --- a/lib/features/composer/presentation/composer_view_web.dart +++ b/lib/features/composer/presentation/composer_view_web.dart @@ -473,6 +473,8 @@ class ComposerView extends GetWidget controller.listEmailAddressType, expandMode: controller.toAddressExpandMode.value, controller: controller.toEmailAddressController, + focusNode: controller.toAddressFocusNode, + autoDisposeFocusNode: false, isInitial: controller.isInitialRecipient.value) ..addOnFocusEmailAddressChangeAction((prefixEmailAddress, focus) => controller.onEmailAddressFocusChange(prefixEmailAddress, focus)) ..addOnShowFullListEmailAddressAction((prefixEmailAddress) => controller.showFullEmailAddress(prefixEmailAddress)) @@ -544,6 +546,7 @@ class ComposerView extends GetWidget child: (TextFieldBuilder() ..key(const Key('subject_email_input')) ..cursorColor(AppColor.colorTextButton) + ..addFocusNode(controller.subjectEmailInputFocusNode) ..onChange((value) => controller.setSubjectEmail(value)) ..textStyle(const TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.normal)) ..textDecoration(const InputDecoration(contentPadding: EdgeInsets.zero, border: InputBorder.none)) @@ -671,7 +674,7 @@ class ComposerView extends GetWidget child: HtmlEditor( key: const Key('composer_editor_web'), controller: controller.richTextWebController.editorController, - htmlEditorOptions: HtmlEditorOptions( + htmlEditorOptions: const HtmlEditorOptions( hint: '', darkMode: false, customBodyCssStyle: bodyCssStyleForEditor), 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 9be284dd8..e51c81f40 100644 --- a/lib/features/composer/presentation/widgets/email_address_input_builder.dart +++ b/lib/features/composer/presentation/widgets/email_address_input_builder.dart @@ -31,6 +31,8 @@ class EmailAddressInputBuilder { final List _listEmailAddressType; final TextEditingController? controller; final bool? isInitial; + final FocusNode? focusNode; + final bool autoDisposeFocusNode; List listEmailAddress = []; @@ -76,6 +78,8 @@ class EmailAddressInputBuilder { { this.isInitial, this.controller, + this.focusNode, + this.autoDisposeFocusNode = true, this.expandMode = ExpandMode.EXPAND, } ); @@ -127,12 +131,14 @@ class EmailAddressInputBuilder { child: TagEditor( length: newListEmailAddress.length, controller: controller, + focusNode: focusNode, + autoDisposeFocusNode: autoDisposeFocusNode, keyboardType: TextInputType.emailAddress, textInputAction: TextInputAction.done, hasAddButton: false, tagSpacing: 8, delimiters: const [' '], - autofocus: _prefixEmailAddress != PrefixEmailAddress.to, + autofocus: _prefixEmailAddress != PrefixEmailAddress.to && listEmailAddress.isEmpty, minTextFieldWidth: 20, resetTextOnSubmitted: true, suggestionsBoxElevation: _suggestionBoxRadius, diff --git a/pubspec.lock b/pubspec.lock index ea5eae131..1d846a2f7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -473,7 +473,7 @@ packages: name: firebase_core_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.2" firebase_messaging: dependency: "direct main" description: @@ -1366,14 +1366,14 @@ packages: name: super_tag_editor url: "https://pub.dartlang.org" source: hosted - version: "0.0.3+2" + version: "0.0.3+3" syncfusion_flutter_core: dependency: transitive description: name: syncfusion_flutter_core url: "https://pub.dartlang.org" source: hosted - version: "20.3.58" + version: "20.3.60" syncfusion_flutter_datepicker: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 4d84fa9c2..fe82a395b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -99,7 +99,7 @@ dependencies: filesize: 2.0.1 # super_tag_editor - super_tag_editor: 0.0.3+2 + super_tag_editor: 0.0.3+3 # uuid uuid: 3.0.4