From e4eca984838d3cecc97277158d8b8b05f48d321b Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 27 Apr 2022 15:19:52 +0700 Subject: [PATCH] TF-517 Implement identity for FromEmailAddress in Composer on browser --- assets/images/ic_edit_identity.svg | 4 + .../extensions/color_extension.dart | 1 + .../presentation/resources/image_paths.dart | 1 + .../presentation/composer_bindings.dart | 14 ++++ .../presentation/composer_controller.dart | 36 ++++++++- .../presentation/composer_view_web.dart | 81 +++++++++++++++++++ 6 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 assets/images/ic_edit_identity.svg diff --git a/assets/images/ic_edit_identity.svg b/assets/images/ic_edit_identity.svg new file mode 100644 index 000000000..73fd52680 --- /dev/null +++ b/assets/images/ic_edit_identity.svg @@ -0,0 +1,4 @@ + + + + diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart index aba28abd4..c2e1bc36c 100644 --- a/core/lib/presentation/extensions/color_extension.dart +++ b/core/lib/presentation/extensions/color_extension.dart @@ -111,6 +111,7 @@ extension AppColor on Color { static const colorBgMailboxSelected = Color(0xFF99E4E8EC); static const colorLoading = Color(0x2999A2AD); static const colorActionButtonHover = Color(0xFFA2AAB3); + static const colorBgMenuItemDropDownSelected = Color(0x80DEE2E7); 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 b992c096c..364c684f1 100644 --- a/core/lib/presentation/resources/image_paths.dart +++ b/core/lib/presentation/resources/image_paths.dart @@ -100,6 +100,7 @@ class ImagePaths { String get icMoreVertical => _getImagePath('ic_more_vertical.svg'); String get icEmail => _getImagePath('ic_email.svg'); String get icReplyTo => _getImagePath('ic_reply_to.svg'); + String get icEditIdentity => _getImagePath('ic_edit_identity.svg'); String _getImagePath(String imageName) { return AssetsPaths.images + imageName; diff --git a/lib/features/composer/presentation/composer_bindings.dart b/lib/features/composer/presentation/composer_bindings.dart index f2b0d5153..c96a952f0 100644 --- a/lib/features/composer/presentation/composer_bindings.dart +++ b/lib/features/composer/presentation/composer_bindings.dart @@ -37,8 +37,15 @@ import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; import 'package:tmail_ui_user/features/email/data/repository/email_repository_impl.dart'; import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; +import 'package:tmail_ui_user/features/manage_account/data/datasource/manage_account_datasource.dart'; +import 'package:tmail_ui_user/features/manage_account/data/datasource_impl/manage_account_datasource_impl.dart'; +import 'package:tmail_ui_user/features/manage_account/data/network/manage_account_api.dart'; +import 'package:tmail_ui_user/features/manage_account/data/repository/manage_account_repository_impl.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/repository/manage_account_repository.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/upload/domain/usecases/local_file_picker_interactor.dart'; import 'package:uuid/uuid.dart'; +import 'package:jmap_dart_client/http/http_client.dart' as jmap_http_client; class ComposerBindings extends BaseBindings { @override @@ -50,6 +57,7 @@ class ComposerBindings extends BaseBindings { void _bindingsUtils() { Get.lazyPut(() => EmailAddressDatabaseManager(Get.find())); Get.lazyPut(() => const Uuid()); + Get.lazyPut(() => ManageAccountAPI(Get.find())); } @override @@ -64,6 +72,7 @@ class ComposerBindings extends BaseBindings { Get.find(), Get.find() )); + Get.lazyPut(() => ManageAccountDataSourceImpl(Get.find())); } @override @@ -73,6 +82,7 @@ class ComposerBindings extends BaseBindings { Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); + Get.lazyPut(() => Get.find()); } @override @@ -90,6 +100,7 @@ class ComposerBindings extends BaseBindings { Get.find(), Get.find() )); + Get.lazyPut(() => ManageAccountRepositoryImpl(Get.find())); } @override @@ -98,6 +109,7 @@ class ComposerBindings extends BaseBindings { Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); + Get.lazyPut(() => Get.find()); } @override @@ -116,6 +128,7 @@ class ComposerBindings extends BaseBindings { Get.lazyPut(() => SaveEmailAsDraftsInteractor(Get.find())); Get.lazyPut(() => GetEmailContentInteractor(Get.find())); Get.lazyPut(() => UpdateEmailDraftsInteractor(Get.find())); + Get.lazyPut(() => GetAllIdentitiesInteractor(Get.find())); } @override @@ -132,6 +145,7 @@ class ComposerBindings extends BaseBindings { Get.find(), Get.find(), Get.find(), + Get.find(), )); } } \ No newline at end of file diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index e23d0bf13..618b8bcd9 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -15,6 +15,7 @@ import 'package:get/get.dart'; import 'package:html_editor_enhanced/html_editor.dart' as html_editor_browser; import 'package:http_parser/http_parser.dart'; import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_body_part.dart'; @@ -44,6 +45,8 @@ import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_i import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_identities_state.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/upload/domain/state/local_file_picker_state.dart'; import 'package:tmail_ui_user/features/upload/domain/usecases/local_file_picker_interactor.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; @@ -68,6 +71,8 @@ class ComposerController extends BaseController { final toAddressExpandMode = ExpandMode.EXPAND.obs; final ccAddressExpandMode = ExpandMode.EXPAND.obs; final bccAddressExpandMode = ExpandMode.EXPAND.obs; + final identitySelected = Rxn(); + final listIdentities = [].obs; final SendEmailInteractor _sendEmailInteractor; final SaveEmailAddressesInteractor _saveEmailAddressInteractor; @@ -80,6 +85,7 @@ class ComposerController extends BaseController { final SaveEmailAsDraftsInteractor _saveEmailAsDraftsInteractor; final GetEmailContentInteractor _getEmailContentInteractor; final UpdateEmailDraftsInteractor _updateEmailDraftsInteractor; + final GetAllIdentitiesInteractor _getAllIdentitiesInteractor; List listToEmailAddress = []; List listCcEmailAddress = []; @@ -126,6 +132,7 @@ class ComposerController extends BaseController { this._saveEmailAsDraftsInteractor, this._getEmailContentInteractor, this._updateEmailDraftsInteractor, + this._getAllIdentitiesInteractor, ); @override @@ -138,10 +145,10 @@ class ComposerController extends BaseController { @override void onReady() async { - super.onReady(); _initEmail(); - + _getAllIdentities(); Future.delayed(const Duration(milliseconds: 500), () => _checkContactPermission()); + super.onReady(); } @override @@ -173,6 +180,11 @@ class ComposerController extends BaseController { _uploadAttachmentsSuccess(success); } else if (success is GetEmailContentSuccess) { _getEmailContentSuccess(success); + } if (success is GetAllIdentitiesSuccess) { + if (success.identities?.isNotEmpty == true) { + listIdentities.value = success.identities ?? []; + setIdentityDefault(); + } } }); } @@ -216,6 +228,13 @@ class ComposerController extends BaseController { initialAttachments = arguments.attachments ?? []; } + void _getAllIdentities() { + final accountId = mailboxDashBoardController.accountId.value; + if (accountId != null) { + consumeState(_getAllIdentitiesInteractor.execute(accountId)); + } + } + String? getContentEmail(BuildContext context) { if (composerArguments.value != null) { switch(composerArguments.value!.emailActionType) { @@ -883,6 +902,19 @@ class ComposerController extends BaseController { } } + void setIdentityDefault() { + try { + final identityDefault = listIdentities.firstWhere((identity) => identity.mayDelete == false); + selectIdentity(identityDefault); + } catch (exception) { + selectIdentity(listIdentities.first); + } + } + + void selectIdentity(Identity? newIdentity) { + identitySelected.value = newIdentity; + } + void closeComposer() { FocusManager.instance.primaryFocus?.unfocus(); popBack(); diff --git a/lib/features/composer/presentation/composer_view_web.dart b/lib/features/composer/presentation/composer_view_web.dart index 4aff529f3..ef1443fb4 100644 --- a/lib/features/composer/presentation/composer_view_web.dart +++ b/lib/features/composer/presentation/composer_view_web.dart @@ -1,4 +1,5 @@ import 'package:core/core.dart'; +import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:file_picker/file_picker.dart'; import 'package:filesize/filesize.dart'; import 'package:flutter/cupertino.dart'; @@ -6,6 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:html_editor_enhanced/html_editor.dart' as html_editor_browser; +import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:model/model.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:tmail_ui_user/features/composer/domain/state/upload_attachment_state.dart'; @@ -37,6 +39,12 @@ class ComposerView extends GetWidget { children: [ Obx(() => _buildAppBarForMobile(context, controller.isEnableEmailSendButton.value)), const Divider(color: AppColor.colorDividerComposer, height: 1), + Obx(() => controller.identitySelected.value != null + ? _buildFromEmailAddress(context) + : const SizedBox.shrink()), + Obx(() => controller.identitySelected.value != null + ? const Divider(color: AppColor.colorDividerComposer, height: 1) + : const SizedBox.shrink()), _buildEmailAddress(context, constraints), const Divider(color: AppColor.colorDividerComposer, height: 1), Padding(padding: const EdgeInsets.symmetric(horizontal: 16), child: _buildSubjectEmail(context)), @@ -273,6 +281,12 @@ class ComposerView extends GetWidget { Expanded(child: Padding( padding: const EdgeInsets.only(left: 12), child: Column(children: [ + Obx(() => controller.identitySelected.value != null + ? _buildFromEmailAddress(context) + : const SizedBox.shrink()), + Obx(() => controller.identitySelected.value != null + ? const Divider(color: AppColor.colorDividerComposer, height: 1) + : const SizedBox.shrink()), _buildEmailAddress(context, constraints), const Divider(color: AppColor.colorDividerComposer, height: 1), Padding(padding: const EdgeInsets.only(right: 16), child: _buildSubjectEmail(context)), @@ -301,6 +315,73 @@ class ComposerView extends GetWidget { )); } + Widget _buildFromEmailAddress(BuildContext context) { + return Padding( + padding: EdgeInsets.only( + left: responsiveUtils.isMobile(context) ? 16 : 0, + top: 12, + bottom: 12), + child: Row(children: [ + Text('${AppLocalizations.of(context).from_email_address_prefix}:', + style: const TextStyle(fontSize: 15, color: AppColor.colorHintEmailAddressInput)), + const SizedBox(width: 12), + DropdownButtonHideUnderline( + child: DropdownButton2( + isExpanded: true, + customButton: SvgPicture.asset(imagePaths.icEditIdentity), + items: controller.listIdentities.map((item) => DropdownMenuItem( + value: item, + child: Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.symmetric(horizontal: 8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + color: item == controller.identitySelected.value ? AppColor.colorBgMenuItemDropDownSelected : Colors.transparent), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + item.name ?? '', + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.normal, color: Colors.black), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + Text( + item.email ?? '', + style: const TextStyle(fontSize: 13, fontWeight: FontWeight.normal, color: AppColor.colorHintSearchBar), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ) + ] + ), + ), + )).toList(), + onChanged: (newIdentity) => controller.selectIdentity(newIdentity), + itemPadding: const EdgeInsets.symmetric(horizontal: 8), + customItemsHeight: 55, + dropdownMaxHeight: 240, + dropdownWidth: 370, + dropdownDecoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: Colors.white), + dropdownElevation: 4, + scrollbarRadius: const Radius.circular(40), + scrollbarThickness: 6, + ), + ), + Expanded(child: Padding( + padding: const EdgeInsets.only(right: 8, left: 12), + child: Text( + controller.identitySelected.value?.email ?? '', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle(fontSize: 17, fontWeight: FontWeight.normal, color: AppColor.colorEmailAddressPrefix), + ))), + ]), + ); + } + Widget _buildEmailAddress(BuildContext context, BoxConstraints constraints) { log('ComposerView::_buildEmailAddress(): height: ${constraints.maxHeight}'); return ConstrainedBox(