From 977e791bab96bb1fb19504a00754cc4f082646ca Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 1 Mar 2022 16:52:22 +0700 Subject: [PATCH] TF-282 Fix bug email address is cut off when displaying on the mobile app --- assets/images/ic_delete_email_address.svg | 4 + .../extensions/color_extension.dart | 9 +- .../presentation/resources/image_paths.dart | 1 + .../presentation/composer_controller.dart | 33 +-- .../composer/presentation/composer_view.dart | 86 +++++-- .../widgets/email_address_input_builder.dart | 228 ++++++++++-------- .../widgets/suggestions_box_controller.dart | 35 +++ .../top_bar_composer_widget_builder.dart | 15 +- lib/main/localizations/app_localizations.dart | 7 + pubspec.yaml | 8 +- 10 files changed, 260 insertions(+), 166 deletions(-) create mode 100644 assets/images/ic_delete_email_address.svg create mode 100644 lib/features/composer/presentation/widgets/suggestions_box_controller.dart diff --git a/assets/images/ic_delete_email_address.svg b/assets/images/ic_delete_email_address.svg new file mode 100644 index 000000000..1e1f7b061 --- /dev/null +++ b/assets/images/ic_delete_email_address.svg @@ -0,0 +1,4 @@ + + + + diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart index 36ced66b9..2a742715d 100644 --- a/core/lib/presentation/extensions/color_extension.dart +++ b/core/lib/presentation/extensions/color_extension.dart @@ -49,9 +49,9 @@ extension AppColor on Color { static const subjectEmailTextColorUnRead = Color(0xFF3840F7); static const dividerColor = Color(0xFFEAEAEA); static const bgComposer = Color(0xFFFBFBFF); - static const emailAddressChipColor = Color(0xFFF5F5F7); - static const enableSendEmailButtonColor = Color(0xFF3840F7); - static const disableSendEmailButtonColor = Color(0xFFACAFFF); + static const emailAddressChipColor = Color(0x0D001C3D); + static const enableSendEmailButtonColor = Color(0xFF007AFF); + static const disableSendEmailButtonColor = Color(0xFFA9B4C2); static const borderLeftEmailContentColor = Color(0xFFEFEFEF); static const toastBackgroundColor = Color(0xFFACAFFF); static const toastSuccessBackgroundColor = Color(0xFF04AA11); @@ -85,6 +85,9 @@ extension AppColor on Color { static const colorActionCancelDialog= Color(0xFF007AFF); static const colorMessageDialog= Color(0xFF222222); static const colorConfirmActionDialog= Color(0xFFF2F2F2); + static const colorEmailAddress = Color(0xFF333333); + static const colorHintEmailAddressInput = Color(0x993C3C43); + static const coloDividerComposer = Color(0xFFC6C6C8); static const mapGradientColor = [ [Color(0xFF21D4FD), Color(0xFFB721FF)], diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart index d859b7d2f..9ec7684b1 100644 --- a/core/lib/presentation/resources/image_paths.dart +++ b/core/lib/presentation/resources/image_paths.dart @@ -84,6 +84,7 @@ class ImagePaths { String get icRenameMailbox => _getImagePath('ic_rename_mailbox.svg'); String get icDeleteToast => _getImagePath('ic_delete_toast.svg'); String get icRemoveDialog => _getImagePath('ic_remove_dialog.svg'); + String get icDeleteEmailAddress => _getImagePath('ic_delete_email_address.svg'); String _getImagePath(String imageName) { return AssetsPaths.images + imageName; diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index 00c7c0956..876f5db22 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -9,7 +9,6 @@ import 'package:file_picker/file_picker.dart'; import 'package:fk_user_agent/fk_user_agent.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter_chips_input/flutter_chips_input.dart'; import 'package:get/get.dart'; import 'package:html_editor_enhanced/html_editor.dart' as HtmlEditorBrowser; import 'package:http_parser/http_parser.dart'; @@ -54,6 +53,7 @@ class ComposerController extends BaseController { final expandMode = ExpandMode.COLLAPSE.obs; final composerArguments = Rxn(); final isEnableEmailSendButton = false.obs; + final isInitialRecipient = false.obs; final attachments = [].obs; final emailContents = [].obs; @@ -79,9 +79,10 @@ class ComposerController extends BaseController { ContactSuggestionSource _contactSuggestionSource = ContactSuggestionSource.localContact; HtmlEditorApi? htmlEditorApi; final HtmlEditorBrowser.HtmlEditorController htmlControllerBrowser = HtmlEditorBrowser.HtmlEditorController(); - final keyToEmailAddress = GlobalKey(); - final keyCcEmailAddress = GlobalKey(); - final keyBccEmailAddress = GlobalKey(); + + final toEmailAddressController = TextEditingController(); + final ccEmailAddressController = TextEditingController(); + final bccEmailAddressController = TextEditingController(); List initialAttachments = []; @@ -132,6 +133,18 @@ class ComposerController extends BaseController { Future.delayed(Duration(milliseconds: 500), () => _checkContactPermission()); } + @override + void onClose() { + subjectEmailInputController.dispose(); + + toEmailAddressController.dispose(); + ccEmailAddressController.dispose(); + bccEmailAddressController.dispose(); + + htmlControllerBrowser.clearFocus(); + super.onClose(); + } + @override void onDone() { viewState.value.fold( @@ -238,16 +251,8 @@ class ComposerController extends BaseController { expandMode.value = ExpandMode.COLLAPSE; } - if (listToEmailAddress.isNotEmpty) { - keyToEmailAddress.currentState?.addMultipleValue(listToEmailAddress); - } - - if (listCcEmailAddress.isNotEmpty) { - keyCcEmailAddress.currentState?.addMultipleValue(listCcEmailAddress); - } - - if (listBccEmailAddress.isNotEmpty) { - keyBccEmailAddress.currentState?.addMultipleValue(listBccEmailAddress); + if (listToEmailAddress.isNotEmpty || listCcEmailAddress.isNotEmpty || listBccEmailAddress.isNotEmpty) { + isInitialRecipient.value = true; } } _updateStatusEmailSendButton(); diff --git a/lib/features/composer/presentation/composer_view.dart b/lib/features/composer/presentation/composer_view.dart index ee24991c6..a03647bc6 100644 --- a/lib/features/composer/presentation/composer_view.dart +++ b/lib/features/composer/presentation/composer_view.dart @@ -18,6 +18,7 @@ class ComposerView extends GetWidget { final responsiveUtils = Get.find(); final imagePaths = Get.find(); + final _appToast = Get.find(); final keyboardUtils = Get.find(); @override @@ -39,6 +40,7 @@ class ComposerView extends GetWidget { left: responsiveUtils.isMobileDevice(context) && responsiveUtils.isLandscape(context), child: Container( margin: EdgeInsets.zero, + padding: EdgeInsets.zero, alignment: Alignment.topCenter, child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -106,19 +108,26 @@ class ComposerView extends GetWidget { return Container( margin: EdgeInsets.zero, padding: EdgeInsets.only(top: 20), - color: AppColor.bgComposer, + color: Colors.white, alignment: Alignment.topCenter, child: Column( children: [ Padding( - padding: EdgeInsets.symmetric(horizontal: 24), + padding: EdgeInsets.only(left: 16), child: _buildEmailAddress(context)), + _buildDivider(), _buildSubjectEmail(context), - Divider(color: AppColor.dividerColor, height: 1) + _buildDivider(), ], ), ); } + + Widget _buildDivider(){ + return Padding( + padding: EdgeInsets.only(left: 16), + child: Divider(color: AppColor.coloDividerComposer, height: 1)); + } Widget _buildEmailAddress(BuildContext context) { return Column( @@ -126,7 +135,7 @@ class ComposerView extends GetWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: EdgeInsets.only(bottom: 8), + padding: EdgeInsets.only(bottom: 12), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -136,7 +145,7 @@ class ComposerView extends GetWidget { '${AppLocalizations.of(context).from_email_address_prefix}:', overflow: TextOverflow.ellipsis, maxLines: 1, - style: TextStyle(fontSize: 14, color: AppColor.nameUserColor, fontWeight: FontWeight.w500))), + style: TextStyle(fontSize: 15, color: AppColor.colorHintEmailAddressInput))), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -145,7 +154,7 @@ class ComposerView extends GetWidget { Obx(() => controller.composerArguments.value != null ? Text( '<${controller.getEmailAddressSender()}>', - style: TextStyle(fontSize: 14, color: AppColor.nameUserColor)) + style: TextStyle(fontSize: 14, color: AppColor.colorEmailAddress, fontWeight: FontWeight.w500)) : SizedBox.shrink() ) ], @@ -154,37 +163,50 @@ class ComposerView extends GetWidget { ] ) ), + Divider(color: AppColor.coloDividerComposer, height: 1), Obx(() => (EmailAddressInputBuilder( context, imagePaths, + _appToast, PrefixEmailAddress.to, controller.listToEmailAddress, - keyInput: controller.keyToEmailAddress, + controller: controller.toEmailAddressController, + isInitial: controller.isInitialRecipient.value, expandMode: controller.expandMode.value) ..addExpandAddressActionClick(() => controller.expandEmailAddressAction()) ..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress)) ..addOnSuggestionEmailAddress((word) => controller.getAutoCompleteSuggestion(word))) .build() ), + Obx(() => controller.expandMode.value == ExpandMode.EXPAND + ? Divider(color: AppColor.coloDividerComposer, height: 1) + : SizedBox.shrink()), Obx(() => controller.expandMode.value == ExpandMode.EXPAND ? (EmailAddressInputBuilder( context, imagePaths, + _appToast, PrefixEmailAddress.cc, controller.listCcEmailAddress, - keyInput: controller.keyCcEmailAddress) + controller: controller.ccEmailAddressController, + isInitial: controller.isInitialRecipient.value,) ..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress)) ..addOnSuggestionEmailAddress((word) => controller.getAutoCompleteSuggestion(word))) .build() : SizedBox.shrink() ), + Obx(() => controller.expandMode.value == ExpandMode.EXPAND + ? Divider(color: AppColor.coloDividerComposer, height: 1) + : SizedBox.shrink()), Obx(() => controller.expandMode.value == ExpandMode.EXPAND ? (EmailAddressInputBuilder( context, imagePaths, + _appToast, PrefixEmailAddress.bcc, controller.listBccEmailAddress, - keyInput: controller.keyBccEmailAddress) + controller: controller.bccEmailAddressController, + isInitial: controller.isInitialRecipient.value,) ..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress)) ..addOnSuggestionEmailAddress((word) => controller.getAutoCompleteSuggestion(word))) .build() @@ -196,22 +218,34 @@ class ComposerView extends GetWidget { Widget _buildSubjectEmail(BuildContext context) { return Padding( - padding: EdgeInsets.only(left: 24, right: 24, bottom: 10, top: 8), - child: (TextFieldBuilder() - ..key(Key('subject_email_input')) - ..textInputAction(TextInputAction.newline) - ..maxLines(null) - ..onChange((value) => controller.setSubjectEmail(value)) - ..textStyle(TextStyle(color: AppColor.nameUserColor, fontSize: 14, fontWeight: FontWeight.w500)) - ..textDecoration(InputDecoration( - hintText: AppLocalizations.of(context).subject_email, - hintStyle: TextStyle(color: AppColor.baseTextColor, fontSize: 14, fontWeight: FontWeight.w500), - contentPadding: EdgeInsets.zero, - filled: true, - border: InputBorder.none, - fillColor: AppColor.bgComposer)) - ..addController(controller.subjectEmailInputController)) - .build() + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.only(right: 8), + child: Text( + '${AppLocalizations.of(context).subject_email}:', + overflow: TextOverflow.ellipsis, + maxLines: 1, + style: TextStyle(fontSize: 15, color: AppColor.colorHintEmailAddressInput))), + Expanded( + child: (TextFieldBuilder() + ..key(Key('subject_email_input')) + ..textInputAction(TextInputAction.newline) + ..maxLines(null) + ..cursorColor(AppColor.colorTextButton) + ..onChange((value) => controller.setSubjectEmail(value)) + ..textStyle(TextStyle(color: AppColor.colorEmailAddress, fontSize: 15)) + ..textDecoration(InputDecoration( + contentPadding: EdgeInsets.zero, + border: InputBorder.none)) + ..addController(controller.subjectEmailInputController)) + .build() + ) + ] + ) ); } @@ -222,7 +256,7 @@ class ComposerView extends GetWidget { children: [ _buildEmailHeader(context), Container( - color: AppColor.primaryLightColor, + color: Colors.white, margin: EdgeInsets.zero, padding: EdgeInsets.zero, alignment: Alignment.topCenter, 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 2a08e01fd..b05fb4dba 100644 --- a/lib/features/composer/presentation/widgets/email_address_input_builder.dart +++ b/lib/features/composer/presentation/widgets/email_address_input_builder.dart @@ -1,29 +1,31 @@ +import 'dart:async'; + import 'package:core/core.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_chips_input/flutter_chips_input.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; +import 'package:material_tag_editor/tag_editor.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/prefix_email_address_extension.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; typedef OnOpenExpandAddressActionClick = void Function(); typedef OnSuggestionEmailAddress = Future> Function(String word); -typedef OnUpdateListEmailAddressAction = void Function( - PrefixEmailAddress prefixEmailAddress, - List listEmailAddress -); +typedef OnUpdateListEmailAddressAction = void Function(PrefixEmailAddress, List); class EmailAddressInputBuilder { - final Key? keyInput; final BuildContext _context; final ImagePaths _imagePaths; + final AppToast _appToast; final ExpandMode expandMode; final PrefixEmailAddress _prefixEmailAddress; final List _listEmailAddress; + final TextEditingController? controller; + final bool? isInitial; OnOpenExpandAddressActionClick? _onOpenExpandAddressActionClick; OnUpdateListEmailAddressAction? _onUpdateListEmailAddressAction; @@ -44,119 +46,135 @@ class EmailAddressInputBuilder { EmailAddressInputBuilder( this._context, this._imagePaths, + this._appToast, this._prefixEmailAddress, this._listEmailAddress, { - this.keyInput, + this.isInitial, + this.controller, this.expandMode = ExpandMode.COLLAPSE, } ); Widget build() { return Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.only(right: 8, top: 15), + child: Text( + '${_prefixEmailAddress.asName(_context)}:', + style: TextStyle(fontSize: 15, color: AppColor.colorHintEmailAddressInput))), + Expanded(child: _buildTagEditor()), + if (_prefixEmailAddress == PrefixEmailAddress.to) Padding( - padding: EdgeInsets.only(right: 8, top: _listEmailAddress.isNotEmpty ? 20 : 18), - child: Text( - '${_prefixEmailAddress.asName(_context)}:', - style: TextStyle(fontSize: 14, color: AppColor.nameUserColor, fontWeight: FontWeight.w500))), - Expanded(child: ChipsInput( - key: keyInput, - initialValue: _listEmailAddress, - textStyle: TextStyle(color: AppColor.nameUserColor, fontSize: 14, fontWeight: FontWeight.w500), - cursorColor: AppColor.primaryColor, - inputType: TextInputType.emailAddress, - decoration: InputDecoration( - border: InputBorder.none, - hintText: AppLocalizations.of(_context).hint_text_email_address, - hintMaxLines: 1, - hintStyle: TextStyle(color: AppColor.baseTextColor, fontSize: 14, fontWeight: FontWeight.w500)), - findSuggestions: (query) { - if (query.isNotEmpty && _onSuggestionEmailAddress != null) { - return _onSuggestionEmailAddress!(query); - } - return []; - }, - onChanged: (data) { - if (_onUpdateListEmailAddressAction != null) { - _onUpdateListEmailAddressAction!(_prefixEmailAddress, data); - } - }, - onChipInputActionDone: (state, value) { - if (GetUtils.isEmail(value)) { - state.selectSuggestion(EmailAddress(value, value)); - } - }, - onChipInputChangeFocusAction: (state, value) { - if (GetUtils.isEmail(value)) { - state.selectSuggestion(EmailAddress(value, value)); - } - }, - chipBuilder: (context, state, emailAddress) { - return InputChip( - key: ObjectKey(emailAddress), - label: Text(emailAddress.asString()), - labelStyle: TextStyle(color: AppColor.nameUserColor, fontSize: 12, fontWeight: FontWeight.w500), - backgroundColor: AppColor.emailAddressChipColor, - padding: EdgeInsets.all(3), - deleteIconColor: AppColor.baseTextColor, - avatar: CircleAvatar( - backgroundColor: AppColor.avatarColor, - child: Text( - emailAddress.asString().isNotEmpty ? emailAddress.asString()[0].toUpperCase() : '', - style: TextStyle(color: AppColor.appColor, fontSize: 12, fontWeight: FontWeight.w500))), - onDeleted: () => state.deleteChip(emailAddress), - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - ); - }, - suggestionBuilder: (context, state, emailAddress) { - return Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8), - border: Border.all(color: Colors.white), - color: Colors.white), - child: ListTile( - key: ObjectKey(emailAddress), - leading: CircleAvatar( - backgroundColor: AppColor.avatarColor, - child: Text( - emailAddress.asString().isNotEmpty ? emailAddress.asString()[0].toUpperCase() : '', - style: TextStyle(color: AppColor.appColor, fontSize: 16, fontWeight: FontWeight.w500))), - title: Text( - emailAddress.asString(), - style: TextStyle(color: AppColor.nameUserColor, fontSize: 14, fontWeight: FontWeight.w500)), - subtitle: emailAddress.displayName.isNotEmpty && emailAddress.emailAddress.isNotEmpty - ? Text( - emailAddress.emailAddress, - style: TextStyle(color: AppColor.baseTextColor, fontSize: 12, fontWeight: FontWeight.w500)) - : null, - onTap: () => state.selectSuggestion(emailAddress), - ), - ); - } - )), - if (_prefixEmailAddress == PrefixEmailAddress.to) - Padding( - padding: EdgeInsets.only(top: _listEmailAddress.isNotEmpty ? 6 : 3), - child: _buildButtonExpandAddress()) - ] + padding: EdgeInsets.only(top: 4), + child: _buildButtonExpandAddress()) + ] ); } Widget _buildButtonExpandAddress() { - return IconButton( - color: AppColor.baseTextColor, - icon: SvgPicture.asset( - expandMode == ExpandMode.EXPAND ? _imagePaths.icExpandAddress : _imagePaths.icMoreReceiver, - width: 20, - height: 20, - fit: BoxFit.fill), - onPressed: () { - if (_onOpenExpandAddressActionClick != null) { - _onOpenExpandAddressActionClick!(); - } - } + return Material( + type: MaterialType.circle, + color: Colors.transparent, + child: IconButton( + icon: SvgPicture.asset( + expandMode == ExpandMode.EXPAND ? _imagePaths.icExpandAddress : _imagePaths.icMoreReceiver, + width: 20, + height: 20, + fit: BoxFit.fill), + onPressed: () => _onOpenExpandAddressActionClick?.call() + ) + ); + } + + Widget _buildTagEditor() { + return StatefulBuilder(builder: (BuildContext context, StateSetter setState) { + return TagEditor( + length: _listEmail.length, + controller: controller, + keyboardType: TextInputType.emailAddress, + textInputAction: TextInputAction.done, + hasAddButton: false, + resetTextOnSubmitted: true, + textStyle: TextStyle(color: AppColor.colorEmailAddress, fontSize: 14, fontWeight: FontWeight.w500), + onSubmitted: (value) { + if (GetUtils.isEmail(value)) { + setState(() => _listEmailAddress.add(EmailAddress(value, value))); + _onUpdateListEmailAddressAction?.call(_prefixEmailAddress, _listEmailAddress); + } else { + _appToast.showErrorToast(AppLocalizations.of(context).email_address_is_not_in_the_correct_format); + } + }, + inputDecoration: InputDecoration( + border: InputBorder.none, + hintText: AppLocalizations.of(_context).hint_text_email_address, + hintMaxLines: 1, + hintStyle: TextStyle(color: AppColor.colorHintEmailAddressInput, fontSize: 14, fontWeight: FontWeight.w500) + ), + tagBuilder: (context, index) => Padding( + padding: EdgeInsets.only(top: kIsWeb ? 8 : 0), + child: Chip( + labelPadding: EdgeInsets.only(left: 8, right: 8, bottom: 2), + label: Text(_listEmail[index], maxLines: 1, overflow: TextOverflow.ellipsis), + deleteIcon: SvgPicture.asset(_imagePaths.icDeleteEmailAddress, fit: BoxFit.fill), + labelStyle: TextStyle(color: AppColor.colorEmailAddress, fontSize: 14, fontWeight: FontWeight.w500), + backgroundColor: AppColor.emailAddressChipColor, + avatar: CircleAvatar( + backgroundColor: AppColor.colorTextButton, + child: Text( + _listEmail[index].isNotEmpty ? _listEmail[index][0].toUpperCase() : '', + style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w500))), + onDeleted: () { + setState(() => _listEmailAddress.removeAt(index)); + _onUpdateListEmailAddressAction?.call(_prefixEmailAddress, _listEmailAddress); + }, + )), + onTagChanged: (String value) {}, + findSuggestions: (String query) { + if (query.isNotEmpty && _onSuggestionEmailAddress != null) { + return _onSuggestionEmailAddress!(query); + } + return []; + }, + suggestionBuilder: (context, tagEditorState, emailAddress) { + return _buildSuggestionItem(setState, context, tagEditorState, emailAddress); + }, + ); + }); + } + + List get _listEmail => _listEmailAddress.map((emailAddress) => emailAddress.asString()).toList(); + + Widget _buildSuggestionItem(StateSetter setState, BuildContext context, TagsEditorState tagEditorState, EmailAddress emailAddress) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.white), + color: Colors.white), + child: ListTile( + key: ObjectKey(emailAddress), + leading: CircleAvatar( + backgroundColor: AppColor.avatarColor, + child: Text( + emailAddress.asString().isNotEmpty ? emailAddress.asString()[0].toUpperCase() : '', + style: TextStyle(color: AppColor.appColor, fontSize: 16, fontWeight: FontWeight.w500))), + title: Text( + emailAddress.asString(), + style: TextStyle(color: AppColor.nameUserColor, fontSize: 14, fontWeight: FontWeight.w500)), + subtitle: emailAddress.displayName.isNotEmpty && emailAddress.emailAddress.isNotEmpty + ? Text( + emailAddress.emailAddress, + style: TextStyle(color: AppColor.baseTextColor, fontSize: 12, fontWeight: FontWeight.w500)) + : null, + onTap: () { + setState(() => _listEmailAddress.add(emailAddress)); + tagEditorState.selectSuggestion(emailAddress); + _onUpdateListEmailAddressAction?.call(_prefixEmailAddress, _listEmailAddress); + }, + ), ); } } \ No newline at end of file diff --git a/lib/features/composer/presentation/widgets/suggestions_box_controller.dart b/lib/features/composer/presentation/widgets/suggestions_box_controller.dart new file mode 100644 index 000000000..d16c83918 --- /dev/null +++ b/lib/features/composer/presentation/widgets/suggestions_box_controller.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; + +class SuggestionsBoxController { + final BuildContext context; + + OverlayEntry? overlayEntry; + + bool _isOpened = false; + + bool get isOpened => _isOpened; + + SuggestionsBoxController(this.context); + + void open() { + if (_isOpened) return; + assert(overlayEntry != null); + Overlay.of(context)?.insert(overlayEntry!); + _isOpened = true; + } + + void close() { + if (!_isOpened) return; + assert(overlayEntry != null); + overlayEntry!.remove(); + _isOpened = false; + } + + void toggle() { + if (_isOpened) { + close(); + } else { + open(); + } + } +} diff --git a/lib/features/composer/presentation/widgets/top_bar_composer_widget_builder.dart b/lib/features/composer/presentation/widgets/top_bar_composer_widget_builder.dart index 325f6cd4c..a6dde007d 100644 --- a/lib/features/composer/presentation/widgets/top_bar_composer_widget_builder.dart +++ b/lib/features/composer/presentation/widgets/top_bar_composer_widget_builder.dart @@ -68,16 +68,9 @@ class TopBarComposerWidgetBuilder { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ - // IconButton( - // key: Key('button_file_share'), - // color: AppColor.baseTextColor, - // icon: SvgPicture.asset(_imagePaths.icComposerFileShare, color: AppColor.baseTextColor, fit: BoxFit.fill), - // onPressed: () {} - // ), IconButton( key: Key('button_attachment'), - color: AppColor.baseTextColor, - icon: SvgPicture.asset(_imagePaths.icShare, color: AppColor.baseTextColor, fit: BoxFit.fill), + icon: SvgPicture.asset(_imagePaths.icShare, fit: BoxFit.fill), onPressed: () { if (_onAttachFileActionClick != null) { _onAttachFileActionClick!(); @@ -98,12 +91,6 @@ class TopBarComposerWidgetBuilder { } ), SizedBox(width: 8) - // IconButton( - // key: Key('button_menu_composer'), - // color: AppColor.baseTextColor, - // icon: SvgPicture.asset(_imagePaths.icComposerMenu, color: AppColor.baseTextColor, fit: BoxFit.fill), - // onPressed: () {} - // ) ] ); } diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index 90a805f0d..c961c6448 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -722,4 +722,11 @@ class AppLocalizations { name: 'there_is_already_folder_with_the_same_name', ); } + + String get email_address_is_not_in_the_correct_format { + return Intl.message( + 'Email address is not in the correct format', + name: 'email_address_is_not_in_the_correct_format', + ); + } } \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index e5f15eb10..0eb28f40a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -80,11 +80,11 @@ dependencies: # file_size filesize: 2.0.1 - # flutter_chips_input - flutter_chips_input: + # material_tag_editor + material_tag_editor: git: - url: https://github.com/dab246/flutter_chips_input.git - ref: master + url: https://github.com/dab246/material_tag_editor.git + ref: feature/support_suggestion_box # uuid uuid: 3.0.4