TF-1152 Make better manage initial cursor position in composer

This commit is contained in:
dab246
2022-12-09 19:18:37 +07:00
committed by Dat H. Pham
parent d9c0ded54d
commit ba0217f2fb
6 changed files with 77 additions and 21 deletions
@@ -5,7 +5,6 @@ import 'dart:io';
import 'package:core/core.dart'; import 'package:core/core.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:device_info_plus/device_info_plus.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:file_picker/file_picker.dart';
import 'package:filesize/filesize.dart'; import 'package:filesize/filesize.dart';
import 'package:fk_user_agent/fk_user_agent.dart'; import 'package:fk_user_agent/fk_user_agent.dart';
@@ -115,6 +114,9 @@ class ComposerController extends BaseController {
final ccEmailAddressController = TextEditingController(); final ccEmailAddressController = TextEditingController();
final bccEmailAddressController = TextEditingController(); final bccEmailAddressController = TextEditingController();
FocusNode? subjectEmailInputFocusNode;
FocusNode? toAddressFocusNode;
final RichTextController keyboardRichTextController = RichTextController(); final RichTextController keyboardRichTextController = RichTextController();
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
@@ -183,6 +185,7 @@ class ComposerController extends BaseController {
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
createFocusNodeInput();
_listenWorker(); _listenWorker();
if (!BuildUtils.isWeb) { if (!BuildUtils.isWeb) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
@@ -216,6 +219,10 @@ class ComposerController extends BaseController {
@override @override
void dispose() { void dispose() {
subjectEmailInputFocusNode?.dispose();
subjectEmailInputFocusNode = null;
toAddressFocusNode?.dispose();
toAddressFocusNode = null;
subjectEmailInputController.dispose(); subjectEmailInputController.dispose();
toEmailAddressController.dispose(); toEmailAddressController.dispose();
ccEmailAddressController.dispose(); ccEmailAddressController.dispose();
@@ -252,14 +259,7 @@ class ComposerController extends BaseController {
} else if (success is GetEmailContentSuccess) { } else if (success is GetEmailContentSuccess) {
_getEmailContentSuccess(success); _getEmailContentSuccess(success);
} else if (success is GetAllIdentitiesSuccess) { } else if (success is GetAllIdentitiesSuccess) {
if (success.identities?.isNotEmpty == true) { _handleGetAllIdentitiesSuccess(success);
listIdentities.value = success.identities!
.where((identity) => identity.mayDelete == true)
.toList();
if (listIdentities.isNotEmpty) {
selectIdentity(listIdentities.first);
}
}
} else if (success is DownloadImageAsBase64Success) { } else if (success is DownloadImageAsBase64Success) {
if(kIsWeb) { if(kIsWeb) {
richTextWebController.insertImage( richTextWebController.insertImage(
@@ -305,6 +305,11 @@ class ComposerController extends BaseController {
}); });
} }
void createFocusNodeInput() {
toAddressFocusNode = FocusNode();
subjectEmailInputFocusNode = FocusNode();
}
void _initEmail() { void _initEmail() {
final arguments = kIsWeb ? mailboxDashBoardController.routerArguments : Get.arguments; final arguments = kIsWeb ? mailboxDashBoardController.routerArguments : Get.arguments;
if (arguments is ComposerArguments) { if (arguments is ComposerArguments) {
@@ -321,6 +326,8 @@ class ComposerController extends BaseController {
_initSubjectEmail(arguments); _initSubjectEmail(arguments);
_initAttachments(arguments); _initAttachments(arguments);
} }
_autoFocusFieldWhenLauncher();
} }
void _initSubjectEmail(ComposerArguments arguments) { 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) { String? getContentEmail(BuildContext context) {
if (composerArguments.value != null) { if (composerArguments.value != null) {
switch(composerArguments.value!.emailActionType) { switch(composerArguments.value!.emailActionType) {
@@ -1076,8 +1097,10 @@ class ComposerController extends BaseController {
void displayScreenTypeComposerAction(ScreenDisplayMode displayMode) { void displayScreenTypeComposerAction(ScreenDisplayMode displayMode) {
FocusManager.instance.primaryFocus?.unfocus(); FocusManager.instance.primaryFocus?.unfocus();
createFocusNodeInput();
_updateTextForEditor(); _updateTextForEditor();
screenDisplayMode.value = displayMode; screenDisplayMode.value = displayMode;
_autoFocusFieldWhenLauncher();
} }
void _updateTextForEditor() async { void _updateTextForEditor() async {
@@ -1174,13 +1197,13 @@ class ComposerController extends BaseController {
} }
} }
void selectIdentity(Identity? newIdentity) { Future<void> selectIdentity(Identity? newIdentity) async {
final formerIdentity = identitySelected.value; final formerIdentity = identitySelected.value;
identitySelected.value = newIdentity; identitySelected.value = newIdentity;
if (newIdentity != null) { if (newIdentity != null) {
_applyIdentityForAllFieldComposer(formerIdentity, newIdentity); await _applyIdentityForAllFieldComposer(formerIdentity, newIdentity);
FocusManager.instance.primaryFocus?.unfocus();
} }
return Future.value(null);
} }
bool get _isMobileApp { bool get _isMobileApp {
@@ -1189,7 +1212,10 @@ class ComposerController extends BaseController {
&& _responsiveUtils.isMobile(currentContext!); && _responsiveUtils.isMobile(currentContext!);
} }
void _applyIdentityForAllFieldComposer(Identity? formerIdentity, Identity newIdentity) { Future<void> _applyIdentityForAllFieldComposer(
Identity? formerIdentity,
Identity newIdentity
) async {
if (formerIdentity != null) { if (formerIdentity != null) {
// Remove former identity // Remove former identity
if (formerIdentity.bcc?.isNotEmpty == true) { if (formerIdentity.bcc?.isNotEmpty == true) {
@@ -1203,7 +1229,7 @@ class ComposerController extends BaseController {
// Add new identity // Add new identity
if (newIdentity.bcc?.isNotEmpty == true) { if (newIdentity.bcc?.isNotEmpty == true) {
_applyBccEmailAddressFromIdentity(newIdentity.bcc!); await _applyBccEmailAddressFromIdentity(newIdentity.bcc!);
} }
if (!_isMobileApp) { if (!_isMobileApp) {
@@ -1213,9 +1239,11 @@ class ComposerController extends BaseController {
_applySignature(newIdentity.textSignature!); _applySignature(newIdentity.textSignature!);
} }
} }
return Future.value(null);
} }
void _applyBccEmailAddressFromIdentity(Set<EmailAddress> listEmailAddress) { Future<void> _applyBccEmailAddressFromIdentity(Set<EmailAddress> listEmailAddress) {
if (!listEmailAddressType.contains(PrefixEmailAddress.bcc)) { if (!listEmailAddressType.contains(PrefixEmailAddress.bcc)) {
listEmailAddressType.add(PrefixEmailAddress.bcc); listEmailAddressType.add(PrefixEmailAddress.bcc);
} }
@@ -1224,6 +1252,8 @@ class ComposerController extends BaseController {
ccAddressExpandMode.value = ExpandMode.COLLAPSE; ccAddressExpandMode.value = ExpandMode.COLLAPSE;
bccAddressExpandMode.value = ExpandMode.COLLAPSE; bccAddressExpandMode.value = ExpandMode.COLLAPSE;
_updateStatusEmailSendButton(); _updateStatusEmailSendButton();
return Future.value(null);
} }
void _removeBccEmailAddressFromFormerIdentity(Set<EmailAddress> listEmailAddress) { void _removeBccEmailAddressFromFormerIdentity(Set<EmailAddress> listEmailAddress) {
@@ -1368,4 +1398,18 @@ class ComposerController extends BaseController {
hasRequestReadReceipt.toggle(); 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
);
}
}
} }
@@ -354,6 +354,8 @@ class ComposerView extends GetWidget<ComposerController>
controller.listEmailAddressType, controller.listEmailAddressType,
expandMode: controller.toAddressExpandMode.value, expandMode: controller.toAddressExpandMode.value,
controller: controller.toEmailAddressController, controller: controller.toEmailAddressController,
focusNode: controller.toAddressFocusNode,
autoDisposeFocusNode: false,
isInitial: controller.isInitialRecipient.value) isInitial: controller.isInitialRecipient.value)
..addOnFocusEmailAddressChangeAction((prefixEmailAddress, focus) => controller.onEmailAddressFocusChange(prefixEmailAddress, focus)) ..addOnFocusEmailAddressChangeAction((prefixEmailAddress, focus) => controller.onEmailAddressFocusChange(prefixEmailAddress, focus))
..addOnShowFullListEmailAddressAction((prefixEmailAddress) => controller.showFullEmailAddress(prefixEmailAddress)) ..addOnShowFullListEmailAddressAction((prefixEmailAddress) => controller.showFullEmailAddress(prefixEmailAddress))
@@ -431,6 +433,7 @@ class ComposerView extends GetWidget<ComposerController>
..key(const Key('subject_email_input')) ..key(const Key('subject_email_input'))
..cursorColor(AppColor.colorTextButton) ..cursorColor(AppColor.colorTextButton)
..maxLines(responsiveUtils.isMobile(context) ? null : 1) ..maxLines(responsiveUtils.isMobile(context) ? null : 1)
..addFocusNode(controller.subjectEmailInputFocusNode)
..onChange((value) => controller.setSubjectEmail(value)) ..onChange((value) => controller.setSubjectEmail(value))
..textStyle(const TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.normal)) ..textStyle(const TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.normal))
..textDecoration(const InputDecoration(contentPadding: EdgeInsets.zero, border: InputBorder.none)) ..textDecoration(const InputDecoration(contentPadding: EdgeInsets.zero, border: InputBorder.none))
@@ -473,6 +473,8 @@ class ComposerView extends GetWidget<ComposerController>
controller.listEmailAddressType, controller.listEmailAddressType,
expandMode: controller.toAddressExpandMode.value, expandMode: controller.toAddressExpandMode.value,
controller: controller.toEmailAddressController, controller: controller.toEmailAddressController,
focusNode: controller.toAddressFocusNode,
autoDisposeFocusNode: false,
isInitial: controller.isInitialRecipient.value) isInitial: controller.isInitialRecipient.value)
..addOnFocusEmailAddressChangeAction((prefixEmailAddress, focus) => controller.onEmailAddressFocusChange(prefixEmailAddress, focus)) ..addOnFocusEmailAddressChangeAction((prefixEmailAddress, focus) => controller.onEmailAddressFocusChange(prefixEmailAddress, focus))
..addOnShowFullListEmailAddressAction((prefixEmailAddress) => controller.showFullEmailAddress(prefixEmailAddress)) ..addOnShowFullListEmailAddressAction((prefixEmailAddress) => controller.showFullEmailAddress(prefixEmailAddress))
@@ -544,6 +546,7 @@ class ComposerView extends GetWidget<ComposerController>
child: (TextFieldBuilder() child: (TextFieldBuilder()
..key(const Key('subject_email_input')) ..key(const Key('subject_email_input'))
..cursorColor(AppColor.colorTextButton) ..cursorColor(AppColor.colorTextButton)
..addFocusNode(controller.subjectEmailInputFocusNode)
..onChange((value) => controller.setSubjectEmail(value)) ..onChange((value) => controller.setSubjectEmail(value))
..textStyle(const TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.normal)) ..textStyle(const TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.normal))
..textDecoration(const InputDecoration(contentPadding: EdgeInsets.zero, border: InputBorder.none)) ..textDecoration(const InputDecoration(contentPadding: EdgeInsets.zero, border: InputBorder.none))
@@ -671,7 +674,7 @@ class ComposerView extends GetWidget<ComposerController>
child: HtmlEditor( child: HtmlEditor(
key: const Key('composer_editor_web'), key: const Key('composer_editor_web'),
controller: controller.richTextWebController.editorController, controller: controller.richTextWebController.editorController,
htmlEditorOptions: HtmlEditorOptions( htmlEditorOptions: const HtmlEditorOptions(
hint: '', hint: '',
darkMode: false, darkMode: false,
customBodyCssStyle: bodyCssStyleForEditor), customBodyCssStyle: bodyCssStyleForEditor),
@@ -31,6 +31,8 @@ class EmailAddressInputBuilder {
final List<PrefixEmailAddress> _listEmailAddressType; final List<PrefixEmailAddress> _listEmailAddressType;
final TextEditingController? controller; final TextEditingController? controller;
final bool? isInitial; final bool? isInitial;
final FocusNode? focusNode;
final bool autoDisposeFocusNode;
List<EmailAddress> listEmailAddress = <EmailAddress>[]; List<EmailAddress> listEmailAddress = <EmailAddress>[];
@@ -76,6 +78,8 @@ class EmailAddressInputBuilder {
{ {
this.isInitial, this.isInitial,
this.controller, this.controller,
this.focusNode,
this.autoDisposeFocusNode = true,
this.expandMode = ExpandMode.EXPAND, this.expandMode = ExpandMode.EXPAND,
} }
); );
@@ -127,12 +131,14 @@ class EmailAddressInputBuilder {
child: TagEditor<SuggestionEmailAddress>( child: TagEditor<SuggestionEmailAddress>(
length: newListEmailAddress.length, length: newListEmailAddress.length,
controller: controller, controller: controller,
focusNode: focusNode,
autoDisposeFocusNode: autoDisposeFocusNode,
keyboardType: TextInputType.emailAddress, keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.done, textInputAction: TextInputAction.done,
hasAddButton: false, hasAddButton: false,
tagSpacing: 8, tagSpacing: 8,
delimiters: const [' '], delimiters: const [' '],
autofocus: _prefixEmailAddress != PrefixEmailAddress.to, autofocus: _prefixEmailAddress != PrefixEmailAddress.to && listEmailAddress.isEmpty,
minTextFieldWidth: 20, minTextFieldWidth: 20,
resetTextOnSubmitted: true, resetTextOnSubmitted: true,
suggestionsBoxElevation: _suggestionBoxRadius, suggestionsBoxElevation: _suggestionBoxRadius,
+3 -3
View File
@@ -473,7 +473,7 @@ packages:
name: firebase_core_web name: firebase_core_web
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.1" version: "2.0.2"
firebase_messaging: firebase_messaging:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -1366,14 +1366,14 @@ packages:
name: super_tag_editor name: super_tag_editor
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.0.3+2" version: "0.0.3+3"
syncfusion_flutter_core: syncfusion_flutter_core:
dependency: transitive dependency: transitive
description: description:
name: syncfusion_flutter_core name: syncfusion_flutter_core
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "20.3.58" version: "20.3.60"
syncfusion_flutter_datepicker: syncfusion_flutter_datepicker:
dependency: transitive dependency: transitive
description: description:
+1 -1
View File
@@ -99,7 +99,7 @@ dependencies:
filesize: 2.0.1 filesize: 2.0.1
# super_tag_editor # super_tag_editor
super_tag_editor: 0.0.3+2 super_tag_editor: 0.0.3+3
# uuid # uuid
uuid: 3.0.4 uuid: 3.0.4