diff --git a/assets/images/ic_mailbox_visibility.svg b/assets/images/ic_mailbox_visibility.svg new file mode 100644 index 000000000..87feb0a80 --- /dev/null +++ b/assets/images/ic_mailbox_visibility.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart index 8e1b00117..7084a8f7b 100644 --- a/core/lib/presentation/resources/image_paths.dart +++ b/core/lib/presentation/resources/image_paths.dart @@ -174,6 +174,7 @@ class ImagePaths { String get icDeleteMailbox => _getImagePath('ic_delete_mailbox.svg'); String get icHideMailbox => _getImagePath('ic_hide_mailbox.svg'); String get icShowMailbox => _getImagePath('ic_show_mailbox.svg'); + String get icMailboxVisibility => _getImagePath('ic_mailbox_visibility.svg'); String _getImagePath(String imageName) { return AssetsPaths.images + imageName; diff --git a/lib/features/base/base_controller.dart b/lib/features/base/base_controller.dart index 43005ef6a..44eca34d6 100644 --- a/lib/features/base/base_controller.dart +++ b/lib/features/base/base_controller.dart @@ -27,6 +27,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/bindings/c import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/bindings/tmail_autocomplete_bindings.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/email_rules/bindings/email_rules_interactor_bindings.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/forward/bindings/forwarding_interactors_bindings.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/mailbox_visibility/bindings/mailbox_visibility_interactor_bindings.dart'; import 'package:tmail_ui_user/features/push_notification/domain/exceptions/fcm_exception.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/bindings/fcm_interactor_bindings.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/config/fcm_configuration.dart'; @@ -163,6 +164,10 @@ abstract class BaseController extends GetxController } } + void injectMailboxVisibilityBindings() { + MailboxVisibilityInteractorBindings().dependencies(); + } + void injectFCMBindings(Session? session, AccountId? accountId) async { try { requireCapability(session!, accountId!, [FirebaseCapability.fcmIdentifier]); diff --git a/lib/features/manage_account/presentation/mailbox_visibility/bindings/mailbox_visibility_bindings.dart b/lib/features/manage_account/presentation/mailbox_visibility/bindings/mailbox_visibility_bindings.dart new file mode 100644 index 000000000..07e7a578e --- /dev/null +++ b/lib/features/manage_account/presentation/mailbox_visibility/bindings/mailbox_visibility_bindings.dart @@ -0,0 +1,20 @@ +import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree_builder.dart'; +import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_controller.dart'; + +class MailboxVisibilityBindings extends Bindings { + + @override + void dependencies() { + Get.lazyPut(() => MailboxVisibilityController( + Get.find(), + Get.find() + )); + _bindingsUtils(); + } + + void _bindingsUtils() { + Get.lazyPut(() => TreeBuilder()); + } +} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/mailbox_visibility/bindings/mailbox_visibility_interactor_bindings.dart b/lib/features/manage_account/presentation/mailbox_visibility/bindings/mailbox_visibility_interactor_bindings.dart new file mode 100644 index 000000000..176e8774b --- /dev/null +++ b/lib/features/manage_account/presentation/mailbox_visibility/bindings/mailbox_visibility_interactor_bindings.dart @@ -0,0 +1,63 @@ +import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/base/interactors_bindings.dart'; +import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart'; +import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart'; +import 'package:tmail_ui_user/features/caching/state_cache_client.dart'; +import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart'; +import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart'; +import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/mailbox_cache_datasource_impl.dart'; +import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/mailbox_datasource_impl.dart'; +import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/state_datasource_impl.dart'; +import 'package:tmail_ui_user/features/mailbox/data/local/mailbox_cache_manager.dart'; +import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_api.dart'; +import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_isolate_worker.dart'; +import 'package:tmail_ui_user/features/mailbox/data/repository/mailbox_repository_impl.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/get_all_mailbox_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/refresh_all_mailbox_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/subscribe_mailbox_interactor.dart'; +import 'package:core/data/model/source_type/data_source_type.dart'; + +class MailboxVisibilityInteractorBindings extends InteractorsBindings { + + @override + void bindingsDataSource() { + Get.lazyPut(() => Get.find()); + Get.lazyPut(() => Get.find()); + } + + @override + void bindingsDataSourceImpl() { + Get.lazyPut(() => MailboxDataSourceImpl( + Get.find(), + Get.find(), + Get.find())); + Get.lazyPut(() => MailboxCacheDataSourceImpl( + Get.find(), + Get.find())); + Get.lazyPut(() => StateDataSourceImpl(Get.find(), Get.find())); + } + + @override + void bindingsInteractor() { + Get.lazyPut(() => GetAllMailboxInteractor(Get.find())); + Get.lazyPut(() => RefreshAllMailboxInteractor(Get.find())); + Get.lazyPut(() => SubscribeMailboxInteractor(Get.find())); + } + + @override + void bindingsRepository() { + Get.lazyPut(() => Get.find()); + } + + @override + void bindingsRepositoryImpl() { + Get.lazyPut(() => MailboxRepositoryImpl( + { + DataSourceType.network: Get.find(), + DataSourceType.local: Get.find() + }, + Get.find(), + )); + } +} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_controller.dart b/lib/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_controller.dart new file mode 100644 index 000000000..07e9b6a05 --- /dev/null +++ b/lib/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_controller.dart @@ -0,0 +1,197 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/utils/app_toast.dart'; +import 'package:core/presentation/utils/responsive_utils.dart'; +import 'package:core/utils/app_logger.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get/get.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:model/mailbox/expand_mode.dart'; +import 'package:model/mailbox/presentation_mailbox.dart'; +import 'package:tmail_ui_user/features/base/base_mailbox_controller.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_subscribe_action_state.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_subscribe_state.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/subscribe_mailbox_request.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/state/get_all_mailboxes_state.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/state/refresh_changes_all_mailboxes_state.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/state/subscribe_mailbox_state.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/get_all_mailbox_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/refresh_all_mailbox_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/subscribe_mailbox_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_categories.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_categories_expand_mode.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree_builder.dart'; +import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/manage_account_dashboard_controller.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; +import 'package:tmail_ui_user/main/routes/route_navigation.dart'; + +class MailboxVisibilityController extends BaseMailboxController { + GetAllMailboxInteractor? _getAllMailboxInteractor; + RefreshAllMailboxInteractor? _refreshAllMailboxInteractor; + SubscribeMailboxInteractor? _subscribeMailboxInteractor; + final _accountDashBoardController = Get.find(); + final _appToast = Get.find(); + final _imagePaths = Get.find(); + final _responsiveUtils = Get.find(); + final mailboxCategoriesExpandMode = MailboxCategoriesExpandMode.initial().obs; + final mailboxListScrollController = ScrollController(); + + Map mapDefaultMailboxIdByRole = {}; + Map mapMailboxById = {}; + PresentationMailbox? outboxMailbox; + + jmap.State? _currentMailboxState; + + MailboxVisibilityController( + TreeBuilder treeBuilder, + VerifyNameInteractor verifyNameInteractor, + ) : super(treeBuilder, verifyNameInteractor); + + @override + void onInit() { + super.onInit(); + try { + _getAllMailboxInteractor = Get.find(); + _refreshAllMailboxInteractor = Get.find(); + _subscribeMailboxInteractor = Get.find(); + } catch (e) { + logError('MailboxVisibilityController::onInit(): ${e.toString()}'); + } + } + + @override + void onDone() { + viewState.value.fold((failure) {}, (success) { + if (success is GetAllMailboxSuccess) { + _currentMailboxState = success.currentMailboxState; + _buildMailboxTreeHasSubscribed(success.mailboxList); + } else if (success is RefreshChangesAllMailboxSuccess) { + _currentMailboxState = success.currentMailboxState; + _refreshMailboxTreeHasSubscribed(success.mailboxList); + } else if (success is SubscribeMailboxSuccess) { + _subscribeMailboxSuccess(success); + } + }); + + } + + @override + void onReady() { + final _session = _accountDashBoardController.sessionCurrent.value; + final _accountId = _accountDashBoardController.accountId.value; + if(_session != null && _accountId != null) { + getAllMailboxAction(_session, _accountId); + } + super.onReady(); + } + + void getAllMailboxAction(Session session, AccountId accountId) async { + if(_getAllMailboxInteractor != null){ + consumeState(_getAllMailboxInteractor!.execute(session, accountId)); + } + } + + void refreshMailboxChanges({jmap.State? currentMailboxState}) { + log('MailboxVisibilityController::refreshMailboxChanges(): currentMailboxState: $currentMailboxState'); + final newMailboxState = currentMailboxState ?? _currentMailboxState; + log('MailboxVisibilityController::refreshMailboxChanges(): newMailboxState: $newMailboxState'); + final session = _accountDashBoardController.sessionCurrent.value; + final accountId = _accountDashBoardController.accountId.value; + if (accountId != null && session != null && newMailboxState != null) { + consumeState(_refreshAllMailboxInteractor!.execute(session, accountId, newMailboxState)); + } + } + + void _buildMailboxTreeHasSubscribed(List mailboxList) async { + final _mailboxList = mailboxList; + await buildTree(_mailboxList); + } + + void _refreshMailboxTreeHasSubscribed(List mailboxList) async { + final _mailboxList = mailboxList; + await refreshTree(_mailboxList); + } + + void subscribeMailbox(MailboxNode mailboxNode) { + final _mailboxSubscribeState = mailboxNode.item.isSubscribedMailbox + ? MailboxSubscribeState.disabled : MailboxSubscribeState.enabled; + final _mailboxSubscribeStateAction = mailboxNode.item.isSubscribedMailbox + ? MailboxSubscribeAction.unSubscribe : MailboxSubscribeAction.subscribe; + if (mailboxNode.item.isSubscribedMailbox) { + _subscribeMailboxAction( + SubscribeMailboxRequest( + mailboxNode.item.id, + _mailboxSubscribeState, + _mailboxSubscribeStateAction, + ) + ); + } + } + + void _subscribeMailboxAction(SubscribeMailboxRequest subscribeMailboxRequest) { + final _accountId = _accountDashBoardController.accountId.value; + if (_accountId != null) { + consumeState(_subscribeMailboxInteractor!.execute(_accountId, subscribeMailboxRequest)); + } + } + + void toggleMailboxCategories(MailboxCategories categories) { + switch(categories) { + case MailboxCategories.exchange: + final newExpandMode = mailboxCategoriesExpandMode.value.defaultMailbox == ExpandMode.EXPAND ? ExpandMode.COLLAPSE : ExpandMode.EXPAND; + mailboxCategoriesExpandMode.value.defaultMailbox = newExpandMode; + mailboxCategoriesExpandMode.refresh(); + break; + case MailboxCategories.personalMailboxes: + final newExpandMode = mailboxCategoriesExpandMode.value.personalMailboxes == ExpandMode.EXPAND ? ExpandMode.COLLAPSE : ExpandMode.EXPAND; + mailboxCategoriesExpandMode.value.personalMailboxes = newExpandMode; + mailboxCategoriesExpandMode.refresh(); + break; + case MailboxCategories.teamMailboxes: + final newExpandMode = mailboxCategoriesExpandMode.value.teamMailboxes == ExpandMode.EXPAND ? ExpandMode.COLLAPSE : ExpandMode.EXPAND; + mailboxCategoriesExpandMode.value.teamMailboxes = newExpandMode; + mailboxCategoriesExpandMode.refresh(); + break; + case MailboxCategories.appGrid: + break; + } + } + + void _subscribeMailboxSuccess(SubscribeMailboxSuccess subscribeMailboxSuccess) { + if (subscribeMailboxSuccess.subscribeAction == MailboxSubscribeAction.unSubscribe + && currentOverlayContext != null + && currentContext != null) { + _appToast.showBottomToast( + currentOverlayContext!, + AppLocalizations.of(currentContext!).toastMsgHideMailboxSuccess, + actionName: AppLocalizations.of(currentContext!).undo, + onActionClick: () { + _subscribeMailboxAction( + SubscribeMailboxRequest( + subscribeMailboxSuccess.mailboxId, + MailboxSubscribeState.enabled, + MailboxSubscribeAction.undo) + ); + }, + leadingIcon: SvgPicture.asset( + _imagePaths.icFolderMailbox, + width: 24, + height: 24, + color: Colors.white, + fit: BoxFit.fill), + backgroundColor: AppColor.toastSuccessBackgroundColor, + textColor: Colors.white, + textActionColor: Colors.white, + actionIcon: SvgPicture.asset(_imagePaths.icUndo), + maxWidth: _responsiveUtils.getMaxWidthToast(currentContext!)); + } + + refreshMailboxChanges(currentMailboxState: subscribeMailboxSuccess.currentMailboxState); + } +} diff --git a/lib/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_view.dart b/lib/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_view.dart new file mode 100644 index 000000000..1764fc2ca --- /dev/null +++ b/lib/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_view.dart @@ -0,0 +1,196 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:core/presentation/utils/responsive_utils.dart'; +import 'package:core/presentation/views/button/icon_button_web.dart'; +import 'package:core/presentation/views/list/tree_view.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get/get.dart'; +import 'package:model/mailbox/expand_mode.dart'; +import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_categories.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_controller.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/mailbox_visibility/widgets/mailbox_visibility_folder_tile_builder.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/mailbox_visibility/widgets/mailbox_visibility_header_widget.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings_utils.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +class MailboxVisibilityView extends GetWidget with AppLoaderMixin { + final _responsiveUtils = Get.find(); + final _imagePaths = Get.find(); + + MailboxVisibilityView({Key? key}) : super(key: key); + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: _responsiveUtils.isWebDesktop(context) + ? AppColor.colorBgDesktop + : Colors.white, + body: Container( + width: double.infinity, + margin: _responsiveUtils.isWebDesktop(context) + ? const EdgeInsets.all(24) + : EdgeInsets.symmetric(horizontal: SettingsUtils.getHorizontalPadding(context, _responsiveUtils)), + color: _responsiveUtils.isWebDesktop(context) ? null : Colors.white, + decoration: _responsiveUtils.isWebDesktop(context) + ? BoxDecoration( + borderRadius: BorderRadius.circular(20), + border: Border.all(color: AppColor.colorBorderBodyThread, width: 1), + color: Colors.white) + : null, + child: ClipRRect( + borderRadius: BorderRadius.circular( + _responsiveUtils.isWebDesktop(context) ? 20 : 0), + child: Padding( + padding: const EdgeInsets.only(top: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (_responsiveUtils.isWebDesktop(context))...[ + const MailboxVisibilityHeaderWidget(), + _buildLoadingView(), + const Padding( + padding: EdgeInsets.symmetric(vertical: 16), + child: Divider(color: AppColor.colorDividerMailbox, height: 0.5, thickness: 0.2), + ), + ], + Expanded(child: _buildListMailbox(context)), + ] + ), + ), + ), + ), + ); + } + + Widget _buildLoadingView() { + return Obx(() => controller.viewState.value.fold( + (failure) => const SizedBox.shrink(), + (success) => success is LoadingState + ? const Center( + child: Padding( + padding: EdgeInsets.only(top: 16), + child: SizedBox( + height: 24, + width: 24, + child: CupertinoActivityIndicator( + color: AppColor.colorTextButton, + ), + )), + ) + : const SizedBox.shrink())); + } + + Widget _buildListMailbox(BuildContext context) { + return SingleChildScrollView( + controller: controller.mailboxListScrollController, + key: const PageStorageKey('mailbox_list'), + physics: const ClampingScrollPhysics(), + child: Column(children: [ + Obx(() => controller.defaultMailboxIsNotEmpty + ? _buildMailboxCategory(context, MailboxCategories.exchange, controller.defaultRootNode) + : const SizedBox.shrink()), + const SizedBox(height: 13), + Obx(() => controller.teamMailboxesIsNotEmpty + ? _buildMailboxCategory(context, MailboxCategories.teamMailboxes, controller.teamMailboxesRootNode) + : const SizedBox.shrink()), + const SizedBox(height: 8), + Obx(() => controller.personalMailboxIsNotEmpty + ? _buildMailboxCategory(context, MailboxCategories.personalMailboxes, controller.personalRootNode) + : const SizedBox.shrink()), + + ]) + ); + } + + Widget _buildHeaderMailboxCategory(BuildContext context, MailboxCategories categories) { + return Padding( + padding: const EdgeInsets.only(top: 10), + child: Row(children: [ + buildIconWeb( + splashRadius: 5, + iconPadding: EdgeInsets.zero, + minSize: 12, + icon: SvgPicture.asset( + categories.getExpandMode(controller.mailboxCategoriesExpandMode.value) == ExpandMode.EXPAND + ? _imagePaths.icExpandFolder + : _imagePaths.icCollapseFolder, + color: AppColor.primaryColor, + fit: BoxFit.fill), + tooltip: AppLocalizations.of(context).collapse, + onTap: () => controller.toggleMailboxCategories(categories)), + Expanded(child: Text(categories.getTitle(context), + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontSize: 17, + color: Colors.black, + fontWeight: FontWeight.bold))), + ])); + } + + Widget _buildMailboxCategory(BuildContext context, MailboxCategories categories, MailboxNode mailboxNode) { + if (categories == MailboxCategories.exchange) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: _responsiveUtils.isDesktop(context) ? 24 : 0), + child: _buildBodyMailboxCategory(context, categories, mailboxNode), + ); + } + return Padding( + padding: EdgeInsets.symmetric(horizontal: _responsiveUtils.isDesktop(context) ? 24 : 0), + child: Column(children: [ + _buildHeaderMailboxCategory(context, categories), + AnimatedContainer( + duration: const Duration(milliseconds: 400), + child: categories.getExpandMode(controller.mailboxCategoriesExpandMode.value) == ExpandMode.EXPAND + ? _buildBodyMailboxCategory(context, categories, mailboxNode) + : const Offstage()) + ]), + ); + } + + Widget _buildBodyMailboxCategory( + BuildContext context, + MailboxCategories categories, + MailboxNode mailboxNode, + ) { + final lastNode = mailboxNode.childrenItems?.last; + + return Container( + padding: EdgeInsets.only( + right: _responsiveUtils.isDesktop(context) ? 0 : 12, + left: _responsiveUtils.isDesktop(context) ? 0 : 12), + child: TreeView( + key: Key('${categories.keyValue}_mailbox_list'), + children: _buildListChildTileWidget( + context, + mailboxNode, + lastNode: lastNode))); + } + + List _buildListChildTileWidget( + BuildContext context, + MailboxNode parentNode, + {MailboxNode? lastNode} + ) { + return parentNode.childrenItems + ?.map((mailboxNode) => mailboxNode.hasChildren() + ? TreeViewChild( + context, + key: const Key('children_tree_mailbox_child'), + isExpanded: mailboxNode.expandMode == ExpandMode.EXPAND, + parent: Obx(() => controller.defaultMailboxIsNotEmpty ? (MailBoxVisibilityFolderTileBuilder(context, _imagePaths, mailboxNode, lastNode: lastNode) + ..addOnExpandFolderActionClick((mailboxNode) => controller.toggleMailboxFolder(mailboxNode)) + ..addOnSubscribeMailboxActionClick((mailboxNode) => controller.subscribeMailbox(mailboxNode))) + .build(context) : const SizedBox.shrink()) , + children: _buildListChildTileWidget(context, mailboxNode) + ).build() + : Obx(() => controller.defaultMailboxIsNotEmpty ? (MailBoxVisibilityFolderTileBuilder(context, _imagePaths, mailboxNode, lastNode: lastNode) + ..addOnExpandFolderActionClick((mailboxNode) => controller.toggleMailboxFolder(mailboxNode)) + ..addOnSubscribeMailboxActionClick((mailboxNode) => controller.subscribeMailbox(mailboxNode))) + .build(context) : const SizedBox.shrink())).toList() ?? []; + } +} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/mailbox_visibility/widgets/mailbox_visibility_folder_tile_builder.dart b/lib/features/manage_account/presentation/mailbox_visibility/widgets/mailbox_visibility_folder_tile_builder.dart new file mode 100644 index 000000000..8bcd7762c --- /dev/null +++ b/lib/features/manage_account/presentation/mailbox_visibility/widgets/mailbox_visibility_folder_tile_builder.dart @@ -0,0 +1,258 @@ +import 'package:core/core.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:model/mailbox/expand_mode.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/extensions/presentation_mailbox_extension.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +typedef OnExpandFolderActionClick = void Function(MailboxNode); +typedef OnSubscribeMailboxActionClick = void Function(MailboxNode); + +class MailBoxVisibilityFolderTileBuilder { + final MailboxNode _mailboxNode; + final BuildContext _context; + final ImagePaths _imagePaths; + final MailboxNode? lastNode; + final MailboxActions? mailboxActions; + + OnSubscribeMailboxActionClick? _onSubscribeMailboxActionClick; + OnExpandFolderActionClick? _onExpandFolderActionClick; + bool isHoverItem = false; + + MailBoxVisibilityFolderTileBuilder( + this._context, + this._imagePaths, + this._mailboxNode, { + this.lastNode, + this.mailboxActions, + }); + + void addOnExpandFolderActionClick(OnExpandFolderActionClick onExpandFolderActionClick) { + _onExpandFolderActionClick = onExpandFolderActionClick; + } + + void addOnSubscribeMailboxActionClick(OnSubscribeMailboxActionClick onSubscribeMailboxActionClick) { + _onSubscribeMailboxActionClick = onSubscribeMailboxActionClick; + } + + Widget build(BuildContext context) => _buildMailboxItem(context); + + Widget _buildMailboxItem(BuildContext context) { + if (BuildUtils.isWeb) { + return Theme( + data: ThemeData( + splashColor: Colors.transparent, + highlightColor: Colors.transparent), + child: StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return InkWell( + onTap: () {}, + onHover: (value) => setState(() => isHoverItem = value), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: backgroundColorItem), + padding: EdgeInsets.only( + left: _mailboxNode.item.hasRole() ? 0 : 4, + right: 4, + top: 8, + bottom: 8), + margin: const EdgeInsets.only(bottom: 4), + child: Row( + children: [ + _buildLeadingMailboxItem(), + const SizedBox(width: 4), + Expanded(child: _buildTitleFolderItem()), + if (!_mailboxNode.item.hasRole()) + _buildSubscribeMailboxItem(context, _mailboxNode), + const SizedBox(width: 32), + ]), + ), + ); + }), + ); + } else { + return ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(14)), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + padding: EdgeInsets.symmetric( + vertical: _mailboxNode.hasChildren() ? 8 : 15), + child: Row( + crossAxisAlignment: + _mailboxNode.item.isTeamMailboxes + ? CrossAxisAlignment.start + : CrossAxisAlignment.center, + children: [ + if (_mailboxNode.item.isTeamMailboxes) + const SizedBox(width: 16), + _buildLeadingMailboxItem(), + const SizedBox(width: 8), + Expanded(child: _buildTitleFolderItem()), + if (!_mailboxNode.item.hasRole()) + _buildSubscribeMailboxItem(context, _mailboxNode), + ]), + ), + ])); + } + } + + Widget _buildLeadingMailboxItem() { + if (BuildUtils.isWeb) { + return Row(mainAxisSize: MainAxisSize.min, children: [ + if (_mailboxNode.hasChildren() && _mailboxNode.item.isPersonal) + Row( + children: [ + const SizedBox(width: 8), + buildIconWeb( + icon: SvgPicture.asset( + _mailboxNode.expandMode == ExpandMode.EXPAND + ? _imagePaths.icExpandFolder + : _imagePaths.icCollapseFolder, + color: _mailboxNode.expandMode == ExpandMode.EXPAND + ? AppColor.colorExpandMailbox + : AppColor.colorCollapseMailbox, + fit: BoxFit.fill), + minSize: 12, + splashRadius: 10, + iconPadding: EdgeInsets.zero, + tooltip: _mailboxNode.expandMode == ExpandMode.EXPAND + ? AppLocalizations.of(_context).collapse + : AppLocalizations.of(_context).expand, + onTap: () => _onExpandFolderActionClick?.call(_mailboxNode)), + ], + ) + else + SizedBox(width: _mailboxNode.item.isPersonal ? 32 : 24), + Transform( + transform: Matrix4.translationValues(-4.0, 0.0, 0.0), + child: _buildLeadingIconTeamMailboxes()), + ]); + } else { + return Row(mainAxisSize: MainAxisSize.min, children: [ + if (_mailboxNode.hasChildren()) + Row( + children: [ + SizedBox(width: _mailboxNode.item.hasRole() ? 0 : 0), + if (!_mailboxNode.item.isTeamMailboxes) + buildIconWeb( + icon: SvgPicture.asset( + _mailboxNode.expandMode == ExpandMode.EXPAND + ? _imagePaths.icExpandFolder + : _imagePaths.icCollapseFolder, + color: _mailboxNode.expandMode == ExpandMode.EXPAND + ? AppColor.colorExpandMailbox + : AppColor.colorCollapseMailbox, + fit: BoxFit.fill), + minSize: 12, + splashRadius: 10, + iconPadding: EdgeInsets.zero, + tooltip: _mailboxNode.expandMode == ExpandMode.EXPAND + ? AppLocalizations.of(_context).collapse + : AppLocalizations.of(_context).expand, + onTap: () => _onExpandFolderActionClick?.call(_mailboxNode)), + ], + ) + else + const SizedBox(width: 24), + _buildLeadingIconTeamMailboxes(), + ]); + } + } + + Widget _buildLeadingIconTeamMailboxes() { + if (!_mailboxNode.item.isPersonal) { + return _buildLeadingIconForChildOfTeamMailboxes(); + } else { + return _buildMailboxIcon(); + } + } + + Widget _buildLeadingIconForChildOfTeamMailboxes() { + if (_mailboxNode.item.hasParentId()) { + return _buildMailboxIcon(); + } else { + return const SizedBox(); + } + } + + Color get backgroundColorItem { + if (BuildUtils.isWeb && isHoverItem) { + return AppColor.colorBgMailboxSelected; + } else { + return Colors.white; + } + } + + Widget _buildTitleFolderItem() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + _mailboxNode.item.name?.name ?? '', + maxLines: 1, + softWrap: CommonTextStyle.defaultSoftWrap, + overflow: CommonTextStyle.defaultTextOverFlow, + style: TextStyle( + fontSize: _mailboxNode.item.isTeamMailboxes ? 16 : 15, + color: _mailboxNode.item.isSubscribedMailbox + ? _getColorTextTitleFolderItem() + : AppColor.colorItemAlreadySelected, + fontWeight: _mailboxNode.item.isTeamMailboxes + ? FontWeight.w500 + : FontWeight.normal), + ), + if (_mailboxNode.item.isTeamMailboxes) + Text( + _mailboxNode.item.emailTeamMailBoxes ?? '', + maxLines: 1, + softWrap: CommonTextStyle.defaultSoftWrap, + overflow: CommonTextStyle.defaultTextOverFlow, + style: const TextStyle( + fontSize: 13, + color: AppColor.colorEmailAddressFull, + fontWeight: FontWeight.w400), + ), + ], + ); + } + + Color _getColorTextTitleFolderItem() { + if (_mailboxNode.item.isTeamMailboxes) { + return Colors.black; + } else { + return AppColor.colorNameEmail; + } + } + + Widget _buildMailboxIcon() { + return SvgPicture.asset(_mailboxNode.item.getMailboxIcon(_imagePaths), + width: BuildUtils.isWeb ? 20 : 24, + height: BuildUtils.isWeb ? 20 : 24, + color: !_mailboxNode.item.isSubscribedMailbox + ? AppColor.colorDeleteContactIcon + : null, + fit: BoxFit.fill); + } + + Widget _buildSubscribeMailboxItem(BuildContext context, MailboxNode _mailboxNode) { + return InkWell( + onTap: () => _onSubscribeMailboxActionClick?.call(_mailboxNode), + child: Text( + _mailboxNode.item.isSubscribedMailbox + ? AppLocalizations.of(context).hide + : AppLocalizations.of(context).show, + maxLines: 1, + softWrap: CommonTextStyle.defaultSoftWrap, + overflow: CommonTextStyle.defaultTextOverFlow, + style: const TextStyle( + fontSize: 15, color: Colors.blue, fontWeight: FontWeight.normal), + ), + ); + } +} diff --git a/lib/features/manage_account/presentation/mailbox_visibility/widgets/mailbox_visibility_header_widget.dart b/lib/features/manage_account/presentation/mailbox_visibility/widgets/mailbox_visibility_header_widget.dart new file mode 100644 index 000000000..db732aeb4 --- /dev/null +++ b/lib/features/manage_account/presentation/mailbox_visibility/widgets/mailbox_visibility_header_widget.dart @@ -0,0 +1,30 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:flutter/material.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +class MailboxVisibilityHeaderWidget extends StatelessWidget { + + const MailboxVisibilityHeaderWidget({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(AppLocalizations.of(context).mailboxVisibility, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.black)), + const SizedBox(height: 4), + Text(AppLocalizations.of(context).mailboxVisibilitySubtitle, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: AppColor.colorSettingExplanation)) + ]), + ); + } +} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart b/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart index 5f2a3d00f..d5dfc4c54 100644 --- a/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart +++ b/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart @@ -26,6 +26,7 @@ import 'package:tmail_ui_user/features/manage_account/presentation/action/dashbo import 'package:tmail_ui_user/features/manage_account/presentation/email_rules/bindings/email_rules_bindings.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/extensions/vacation_response_extension.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/forward/bindings/forward_bindings.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/mailbox_visibility/bindings/mailbox_visibility_bindings.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/model/account_menu_item.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/model/manage_account_arguments.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/model/settings_page_level.dart'; @@ -103,6 +104,7 @@ class ManageAccountDashBoardController extends ReloadableController { injectAutoCompleteBindings(sessionCurrent.value, accountId.value); injectForwardBindings(sessionCurrent.value, accountId.value); injectRuleFilterBindings(sessionCurrent.value, accountId.value); + injectMailboxVisibilityBindings(); injectVacationBindings(sessionCurrent.value, accountId.value); _getVacationResponse(); } @@ -117,6 +119,7 @@ class ManageAccountDashBoardController extends ReloadableController { injectAutoCompleteBindings(sessionCurrent.value, accountId.value); injectForwardBindings(sessionCurrent.value, accountId.value); injectRuleFilterBindings(sessionCurrent.value, accountId.value); + injectMailboxVisibilityBindings(); injectVacationBindings(sessionCurrent.value, accountId.value); _getVacationResponse(); if (arguments.menuSettingCurrent != null) { @@ -182,12 +185,15 @@ class ManageAccountDashBoardController extends ReloadableController { void selectAccountMenuItem(AccountMenuItem newAccountMenuItem) { clearInputFormView(); - if(newAccountMenuItem == AccountMenuItem.emailRules) { + if (newAccountMenuItem == AccountMenuItem.emailRules) { EmailRulesBindings().dependencies(); } - if(newAccountMenuItem == AccountMenuItem.forward) { + if (newAccountMenuItem == AccountMenuItem.forward) { ForwardBindings().dependencies(); } + if (newAccountMenuItem == AccountMenuItem.mailboxVisibility) { + MailboxVisibilityBindings().dependencies(); + } accountMenuItemSelected.value = newAccountMenuItem; if (isMenuDrawerOpen) { closeMenuDrawer(); @@ -205,12 +211,15 @@ class ManageAccountDashBoardController extends ReloadableController { } void _goToSettingMenuCurrent(AccountMenuItem accountMenuItem) { - if(accountMenuItem == AccountMenuItem.emailRules) { + if (accountMenuItem == AccountMenuItem.emailRules) { EmailRulesBindings().dependencies(); } - if(accountMenuItem == AccountMenuItem.forward) { + if (accountMenuItem == AccountMenuItem.forward) { ForwardBindings().dependencies(); } + if (accountMenuItem == AccountMenuItem.mailboxVisibility) { + MailboxVisibilityBindings().dependencies(); + } accountMenuItemSelected.value = accountMenuItem; if (currentContext != null && !_responsiveUtils.isDesktop(currentContext!)) { diff --git a/lib/features/manage_account/presentation/manage_account_dashboard_view.dart b/lib/features/manage_account/presentation/manage_account_dashboard_view.dart index 3f8b66810..cf2460d97 100644 --- a/lib/features/manage_account/presentation/manage_account_dashboard_view.dart +++ b/lib/features/manage_account/presentation/manage_account_dashboard_view.dart @@ -10,6 +10,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mixin/user_setting_popup_menu_mixin.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/extensions/vacation_response_extension.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_view.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/widgets/vacation_notification_message_widget.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/email_rules/email_rules_view.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/forward/forward_view.dart'; @@ -179,6 +180,8 @@ class ManageAccountDashBoardView extends GetWidget { return const SizedBox.shrink(); } }), + Column(children: [ + SettingFirstLevelTileBuilder( + AccountMenuItem.mailboxVisibility.getName(context), + AccountMenuItem.mailboxVisibility.getIcon(_imagePaths), + subtitle: AppLocalizations.of(context).mailboxVisibilitySubtitle, + () => controller.selectSettings(AccountMenuItem.mailboxVisibility) + ), + Divider( + color: AppColor.colorDividerComposer, + height: 1, + indent: SettingsUtils.getHorizontalPadding(context, _responsiveUtils), + endIndent: SettingsUtils.getHorizontalPadding(context, _responsiveUtils) + ), + ]), SettingFirstLevelTileBuilder( AccountMenuItem.languageAndRegion.getName(context), AccountMenuItem.languageAndRegion.getIcon(_imagePaths), diff --git a/lib/features/manage_account/presentation/menu/settings/settings_view.dart b/lib/features/manage_account/presentation/menu/settings/settings_view.dart index bd2e0028a..c49e02d3c 100644 --- a/lib/features/manage_account/presentation/menu/settings/settings_view.dart +++ b/lib/features/manage_account/presentation/menu/settings/settings_view.dart @@ -11,6 +11,7 @@ import 'package:tmail_ui_user/features/manage_account/presentation/email_rules/e import 'package:tmail_ui_user/features/manage_account/presentation/extensions/vacation_response_extension.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/forward/forward_view.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/language_and_region/language_and_region_view.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_view.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings/settings_controller.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings/settings_first_level_view.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings_utils.dart'; @@ -193,13 +194,13 @@ class SettingsView extends GetWidget { case AccountMenuItem.languageAndRegion: return LanguageAndRegionView(); case AccountMenuItem.emailRules: - if(controller.manageAccountDashboardController.checkAvailableRuleFilterInSession()){ + if (controller.manageAccountDashboardController.checkAvailableRuleFilterInSession()) { return EmailRulesView(); } else { return const SizedBox.shrink(); } case AccountMenuItem.forward: - if(controller.manageAccountDashboardController.checkAvailableForwardInSession()){ + if (controller.manageAccountDashboardController.checkAvailableForwardInSession()) { return ForwardView(); } else { return const SizedBox.shrink(); @@ -210,6 +211,8 @@ class SettingsView extends GetWidget { } else { return const SizedBox.shrink(); } + case AccountMenuItem.mailboxVisibility: + return MailboxVisibilityView(); default: return const SizedBox.shrink(); } diff --git a/lib/features/manage_account/presentation/model/account_menu_item.dart b/lib/features/manage_account/presentation/model/account_menu_item.dart index fef3d31b5..265cb243b 100644 --- a/lib/features/manage_account/presentation/model/account_menu_item.dart +++ b/lib/features/manage_account/presentation/model/account_menu_item.dart @@ -9,6 +9,7 @@ enum AccountMenuItem { emailRules, forward, vacation, + mailboxVisibility, none; String getIcon(ImagePaths imagePaths) { @@ -23,6 +24,8 @@ enum AccountMenuItem { return imagePaths.icForward; case AccountMenuItem.vacation: return imagePaths.icVacation; + case AccountMenuItem.mailboxVisibility: + return imagePaths.icMailboxVisibility; case AccountMenuItem.none: return imagePaths.icProfiles; } @@ -40,6 +43,8 @@ enum AccountMenuItem { return AppLocalizations.of(context).forwarding; case AccountMenuItem.vacation: return AppLocalizations.of(context).vacation; + case AccountMenuItem.mailboxVisibility: + return AppLocalizations.of(context).mailboxVisibility; case AccountMenuItem.none: return AppLocalizations.of(context).profiles; } diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index e8b7dff44..9b4e9c70e 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -2805,4 +2805,17 @@ class AppLocalizations { 'This mailbox is already displayed in your primary mailbox', name: 'toastMessageShowMailboxSuccess'); } + String get mailboxVisibility { + return Intl.message( + 'Mailbox visibility', + name: 'mailboxVisibility', + ); + } + + String get mailboxVisibilitySubtitle { + return Intl.message( + 'Show/ hide your mailboxes, including your personal and team mailboxes.', + name: 'mailboxVisibilitySubtitle', + ); + } } \ No newline at end of file diff --git a/model/lib/mailbox/presentation_mailbox.dart b/model/lib/mailbox/presentation_mailbox.dart index 2c4da5dd2..cb888097d 100644 --- a/model/lib/mailbox/presentation_mailbox.dart +++ b/model/lib/mailbox/presentation_mailbox.dart @@ -89,6 +89,8 @@ class PresentationMailbox with EquatableMixin { bool get isOutbox => name == lowerCaseOutboxMailboxName || role == roleOutbox; + bool get isSubscribedMailbox => isSubscribed != null && isSubscribed?.value == true; + bool matchCountingRules() { if (isTrash || isDrafts || isTemplates || isSent) { return false;