From 23ecdfbf117b65fa4cbdce00a818d64fef3376e8 Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 30 Jul 2025 19:09:33 +0700 Subject: [PATCH] TF-2948 Apply new identity view for web Signed-off-by: dab246 --- assets/images/ic_delete_rule.svg | 6 +- assets/images/ic_radio.svg | 3 + assets/images/ic_radio_selected.svg | 4 + core/lib/core.dart | 1 + .../presentation/constants/constants_ui.dart | 1 + .../presentation/resources/image_paths.dart | 2 + .../transform_configuration.dart | 10 + .../html_content_viewer_on_web_widget.dart | 207 +++++++++++------- .../list/no_stretch_scroll_behavior.dart | 12 + core/lib/utils/html/html_interaction.dart | 20 ++ core/lib/utils/html/html_template.dart | 12 + .../mixin/scenario_utils_mixin.dart | 2 +- .../presentation/composer_bindings.dart | 2 +- .../presentation/bindings/email_bindings.dart | 2 +- .../identity_creator_bindings.dart | 4 +- .../identity_creator_controller.dart | 2 +- .../bindings/mailbox_dashboard_bindings.dart | 4 +- .../data/datasource/identity_data_source.dart | 3 +- .../identity_data_source_impl.dart | 22 +- .../repository/identity_repository_impl.dart | 5 +- .../domain/model/identity_signature.dart | 19 ++ .../repository/identity_repository.dart | 3 +- .../state/transform_html_signature_state.dart | 18 -- .../state/transform_list_signature_state.dart | 21 ++ ...reate_new_default_identity_interactor.dart | 2 +- .../edit_default_identity_interactor.dart | 2 +- .../get_all_identities_interactor.dart | 2 +- .../transform_html_signature_interactor.dart | 24 -- .../transform_list_signature_interactor.dart | 37 ++++ .../extensions/identity_extension.dart | 8 +- .../extensions/list_identity_extension.dart | 13 ++ .../identities/identities_controller.dart | 96 +++++--- .../identities/identities_view.dart | 145 ++++++++++++ .../identities/identity_bindings.dart | 8 +- .../identity_interactors_bindings.dart | 8 +- .../identities/utils/identity_utils.dart | 0 .../create_new_identity_button_widget.dart | 46 ++++ .../delete_identity_dialog_builder.dart | 0 .../widgets/identity_list_tile_builder.dart | 157 +++++++++++++ .../widgets/identity_loading_widget.dart | 32 +++ .../list_identity_item_actions_widget.dart | 82 +++++++ .../identities/widgets/signature_builder.dart | 55 +++++ .../widgets/signature_loading_widget.dart | 35 +++ .../manage_account_dashboard_bindings.dart | 4 +- .../manage_account_dashboard_controller.dart | 4 +- .../manage_account_dashboard_view.dart | 4 +- .../menu/settings/settings_view.dart | 4 +- .../presentation/model/account_menu_item.dart | 2 +- .../profiles/identities/identities_view.dart | 63 ------ .../widgets/identities_header_widget.dart | 52 ----- .../identities_radio_list_builder.dart | 152 ------------- .../widgets/identity_list_tile_builder.dart | 163 -------------- .../widgets/identity_loading_widget.dart | 45 ---- .../identities/widgets/signature_builder.dart | 59 ----- .../profiles/profiles_bindings.dart | 10 - .../presentation/profiles/profiles_view.dart | 44 ---- .../widgets/setting_explanation_widget.dart | 3 + .../widgets/setting_header_widget.dart | 5 + .../identity_creator_controller_test.dart | 2 +- .../identity/identity_utils_test.dart | 2 +- .../identities_controller_test.dart | 12 +- 61 files changed, 963 insertions(+), 804 deletions(-) create mode 100644 assets/images/ic_radio.svg create mode 100644 assets/images/ic_radio_selected.svg create mode 100644 core/lib/presentation/views/list/no_stretch_scroll_behavior.dart create mode 100644 lib/features/manage_account/domain/model/identity_signature.dart delete mode 100644 lib/features/manage_account/domain/state/transform_html_signature_state.dart create mode 100644 lib/features/manage_account/domain/state/transform_list_signature_state.dart delete mode 100644 lib/features/manage_account/domain/usecases/transform_html_signature_interactor.dart create mode 100644 lib/features/manage_account/domain/usecases/transform_list_signature_interactor.dart create mode 100644 lib/features/manage_account/presentation/extensions/list_identity_extension.dart rename lib/features/manage_account/presentation/{profiles => }/identities/identities_controller.dart (88%) create mode 100644 lib/features/manage_account/presentation/identities/identities_view.dart rename lib/features/manage_account/presentation/{profiles => }/identities/identity_bindings.dart (87%) rename lib/features/manage_account/presentation/{profiles => }/identities/identity_interactors_bindings.dart (96%) rename lib/features/manage_account/presentation/{profiles => }/identities/utils/identity_utils.dart (100%) create mode 100644 lib/features/manage_account/presentation/identities/widgets/create_new_identity_button_widget.dart rename lib/features/manage_account/presentation/{profiles => }/identities/widgets/delete_identity_dialog_builder.dart (100%) create mode 100644 lib/features/manage_account/presentation/identities/widgets/identity_list_tile_builder.dart create mode 100644 lib/features/manage_account/presentation/identities/widgets/identity_loading_widget.dart create mode 100644 lib/features/manage_account/presentation/identities/widgets/list_identity_item_actions_widget.dart create mode 100644 lib/features/manage_account/presentation/identities/widgets/signature_builder.dart create mode 100644 lib/features/manage_account/presentation/identities/widgets/signature_loading_widget.dart delete mode 100644 lib/features/manage_account/presentation/profiles/identities/identities_view.dart delete mode 100644 lib/features/manage_account/presentation/profiles/identities/widgets/identities_header_widget.dart delete mode 100644 lib/features/manage_account/presentation/profiles/identities/widgets/identities_radio_list_builder.dart delete mode 100644 lib/features/manage_account/presentation/profiles/identities/widgets/identity_list_tile_builder.dart delete mode 100644 lib/features/manage_account/presentation/profiles/identities/widgets/identity_loading_widget.dart delete mode 100644 lib/features/manage_account/presentation/profiles/identities/widgets/signature_builder.dart delete mode 100644 lib/features/manage_account/presentation/profiles/profiles_bindings.dart delete mode 100644 lib/features/manage_account/presentation/profiles/profiles_view.dart diff --git a/assets/images/ic_delete_rule.svg b/assets/images/ic_delete_rule.svg index af075917f..8b55020ed 100644 --- a/assets/images/ic_delete_rule.svg +++ b/assets/images/ic_delete_rule.svg @@ -1,5 +1,3 @@ - - - - + + diff --git a/assets/images/ic_radio.svg b/assets/images/ic_radio.svg new file mode 100644 index 000000000..9015c70bd --- /dev/null +++ b/assets/images/ic_radio.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/ic_radio_selected.svg b/assets/images/ic_radio_selected.svg new file mode 100644 index 000000000..7e0732c0c --- /dev/null +++ b/assets/images/ic_radio_selected.svg @@ -0,0 +1,4 @@ + + + + diff --git a/core/lib/core.dart b/core/lib/core.dart index 4a47e5aee..52f26a87d 100644 --- a/core/lib/core.dart +++ b/core/lib/core.dart @@ -69,6 +69,7 @@ export 'presentation/views/button/icon_button_web.dart'; export 'presentation/views/button/tmail_button_widget.dart'; export 'presentation/views/image/avatar_builder.dart'; export 'presentation/views/list/sliver_grid_delegate_fixed_height.dart'; +export 'presentation/views/list/no_stretch_scroll_behavior.dart'; export 'presentation/views/image/icon_builder.dart'; export 'presentation/views/context_menu/context_menu_action_builder.dart'; export 'presentation/views/context_menu/context_menu_builder.dart'; diff --git a/core/lib/presentation/constants/constants_ui.dart b/core/lib/presentation/constants/constants_ui.dart index b629e558e..db77e2f64 100644 --- a/core/lib/presentation/constants/constants_ui.dart +++ b/core/lib/presentation/constants/constants_ui.dart @@ -2,6 +2,7 @@ class ConstantsUI { static const String fontApp = 'Inter'; static const double htmlContentMaxHeight = 22000.0; static const double composerHtmlContentMaxHeight = 20000.0; + static const double htmlContentMinWidth = 300; static const double htmlContentMinHeight = 150; static const double htmlContentOffsetHeight = 30.0; } \ No newline at end of file diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart index 3bf6f4e14..208854141 100644 --- a/core/lib/presentation/resources/image_paths.dart +++ b/core/lib/presentation/resources/image_paths.dart @@ -232,6 +232,8 @@ class ImagePaths { String get icRefreshQuotas => _getImagePath('ic_refresh_quotas.svg'); String get icCreateFilter => _getImagePath('ic_create_filter.svg'); String get icArrowBack => _getImagePath('ic_arrow_back.svg'); + String get icRadio => _getImagePath('ic_radio.svg'); + String get icRadioSelected => _getImagePath('ic_radio_selected.svg'); String get icTwakeWorkplace => _getIconPath('icon_twp.png'); diff --git a/core/lib/presentation/utils/html_transformer/transform_configuration.dart b/core/lib/presentation/utils/html_transformer/transform_configuration.dart index b551bb5e8..00aa5c405 100644 --- a/core/lib/presentation/utils/html_transformer/transform_configuration.dart +++ b/core/lib/presentation/utils/html_transformer/transform_configuration.dart @@ -92,6 +92,16 @@ class TransformConfiguration { const RemoveMaxWidthInImageStyleTransformer(), ]); + factory TransformConfiguration.forSignatureIdentity() => TransformConfiguration.create( + customDomTransformers: [ + const RemoveScriptTransformer(), + const BlockQuotedTransformer(), + const BlockCodeTransformer(), + SanitizeHyperLinkTagInHtmlTransformer(useTooltip: PlatformInfo.isWeb), + const ImageTransformer(), + ], + ); + /// Provides easy access to a standard configuration that does not block external images. static TransformConfiguration standardConfiguration = TransformConfiguration( standardDomTransformers, diff --git a/core/lib/presentation/views/html_viewer/html_content_viewer_on_web_widget.dart b/core/lib/presentation/views/html_viewer/html_content_viewer_on_web_widget.dart index 0ec93703d..02c7a7f54 100644 --- a/core/lib/presentation/views/html_viewer/html_content_viewer_on_web_widget.dart +++ b/core/lib/presentation/views/html_viewer/html_content_viewer_on_web_widget.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:math' as math; +import 'package:core/presentation/constants/constants_ui.dart'; import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/utils/shims/dart_ui.dart' as ui; import 'package:core/utils/app_logger.dart'; @@ -9,6 +10,7 @@ import 'package:core/utils/html/html_interaction.dart'; import 'package:core/utils/html/html_template.dart'; import 'package:core/utils/html/html_utils.dart'; import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; import 'package:universal_html/html.dart' as html; typedef OnClickHyperLinkAction = Function(Uri?); @@ -34,6 +36,13 @@ class HtmlContentViewerOnWeb extends StatefulWidget { final bool keepWidthWhileLoading; final ScrollController? scrollController; final bool enableQuoteToggle; + final bool disableScrolling; + final bool keepAlive; + final double htmlContentMinHeight; + final double htmlContentMinWidth; + final double offsetHtmlContentHeight; + final double? viewMaxHeight; + final bool autoAdjustHeight; const HtmlContentViewerOnWeb({ Key? key, @@ -49,15 +58,21 @@ class HtmlContentViewerOnWeb extends StatefulWidget { this.contentPadding, this.scrollController, this.enableQuoteToggle = false, + this.keepAlive = false, + this.disableScrolling = false, + this.autoAdjustHeight = false, + this.htmlContentMinHeight = ConstantsUI.htmlContentMinHeight, + this.htmlContentMinWidth = ConstantsUI.htmlContentMinWidth, + this.offsetHtmlContentHeight = ConstantsUI.htmlContentOffsetHeight, + this.viewMaxHeight, }) : super(key: key); @override State createState() => _HtmlContentViewerOnWebState(); } -class _HtmlContentViewerOnWebState extends State { - - static const double _minWidth = 300; +class _HtmlContentViewerOnWebState extends State + with AutomaticKeepAliveClientMixin { /// The view ID for the IFrameElement. Must be unique. late String _createdViewId; /// The actual height of the content view, used to automatically set the height @@ -68,7 +83,7 @@ class _HtmlContentViewerOnWebState extends State { Future? _webInit; String? _htmlData; bool _isLoading = true; - double minHeight = 100; + late double minHeight; late final StreamSubscription _onMessageSubscription; bool _iframeLoaded = false; static const String iframeOnLoadMessage = 'iframeHasBeenLoaded'; @@ -80,7 +95,7 @@ class _HtmlContentViewerOnWebState extends State { super.initState(); _actualHeight = widget.heightContent; _actualWidth = widget.widthContent; - _createdViewId = _getRandString(10); + minHeight = widget.htmlContentMinHeight; _setUpWeb(); _onMessageSubscription = html.window.onMessage.listen(_handleMessageEvent); } @@ -119,6 +134,7 @@ class _HtmlContentViewerOnWebState extends State { bool _isScrollChangedEventTriggered(String? type) { return widget.scrollController != null && + widget.scrollController?.hasClients == true && type?.contains('toDart: $onScrollChangedEvent') == true; } @@ -144,8 +160,13 @@ class _HtmlContentViewerOnWebState extends State { void _handleContentHeightEvent(dynamic height) { final docHeight = height ?? _actualHeight; if (docHeight != null && mounted) { - final scrollHeightWithBuffer = docHeight + 30.0; - if (scrollHeightWithBuffer > minHeight) { + final scrollHeightWithBuffer = docHeight + widget.offsetHtmlContentHeight; + log('$runtimeType::_handleContentHeightEvent: ScrollHeightWithBuffer = $scrollHeightWithBuffer'); + bool isHeightChanged = widget.autoAdjustHeight + ? scrollHeightWithBuffer >= minHeight + : scrollHeightWithBuffer > minHeight; + + if (isHeightChanged) { setState(() { _actualHeight = scrollHeightWithBuffer; _isLoading = false; @@ -166,7 +187,7 @@ class _HtmlContentViewerOnWebState extends State { void _handleContentWidthEvent(dynamic width) { final docWidth = width ?? _actualWidth; if (docWidth != null && mounted && - docWidth > _minWidth && + docWidth > widget.htmlContentMinWidth && widget.allowResizeToDocumentSize) { setState(() => _actualWidth = docWidth); } @@ -196,7 +217,6 @@ class _HtmlContentViewerOnWebState extends State { log('_HtmlContentViewerOnWebState::didUpdateWidget():Old-Direction: ${oldWidget.direction} | Current-Direction: ${widget.direction}'); if (widget.contentHtml != oldWidget.contentHtml || widget.direction != oldWidget.direction) { - _createdViewId = _getRandString(10); _setUpWeb(); } @@ -247,10 +267,12 @@ class _HtmlContentViewerOnWebState extends State { } } - const resizeObserver = new ResizeObserver((entries) => { - var height = document.body.scrollHeight; - window.parent.postMessage(JSON.stringify({"view": "$_createdViewId", "type": "toDart: htmlHeight", "height": height}), "*"); - }); + ${!widget.autoAdjustHeight ? ''' + const resizeObserver = new ResizeObserver((entries) => { + var height = document.body.scrollHeight; + window.parent.postMessage(JSON.stringify({"view": "$_createdViewId", "type": "toDart: htmlHeight", "height": height}), "*"); + }); + ''' : ''} ${widget.mailtoDelegate != null ? ''' @@ -297,7 +319,7 @@ class _HtmlContentViewerOnWebState extends State { ''' : ''} - resizeObserver.observe(document.body); + ${!widget.autoAdjustHeight ? 'resizeObserver.observe(document.body);' : ''} } ${widget.scrollController != null ? ''' @@ -313,39 +335,30 @@ class _HtmlContentViewerOnWebState extends State { '''; - const scriptsDisableZoom = ''' - - '''; + final processedContent = widget.enableQuoteToggle + ? HtmlUtils.addQuoteToggle(content) + : content; + + final combinedCss = [ + HtmlTemplate.tooltipLinkCss, + if (widget.enableQuoteToggle) HtmlUtils.quoteToggleStyle, + if (widget.disableScrolling) HtmlTemplate.disableScrollingStyleCSS, + ].join(); + + final combinedScripts = [ + webViewActionScripts, + HtmlInteraction.scriptsDisableZoom, + HtmlInteraction.scriptsHandleLazyLoadingBackgroundImage, + HtmlInteraction.generateNormalizeImageScript(widget.widthContent), + if (widget.enableQuoteToggle) HtmlUtils.quoteToggleScript, + ].join(); final htmlTemplate = HtmlUtils.generateHtmlDocument( - content: widget.enableQuoteToggle - ? HtmlUtils.addQuoteToggle(content) - : content, + content: processedContent, minHeight: minHeight, - minWidth: _minWidth, - styleCSS: HtmlTemplate.tooltipLinkCss - + (widget.enableQuoteToggle ? HtmlUtils.quoteToggleStyle : ''), - javaScripts: webViewActionScripts - + scriptsDisableZoom - + HtmlInteraction.scriptsHandleLazyLoadingBackgroundImage - + HtmlInteraction.generateNormalizeImageScript(widget.widthContent) - + (widget.enableQuoteToggle ? HtmlUtils.quoteToggleScript : ''), + minWidth: widget.htmlContentMinWidth, + styleCSS: combinedCss, + javaScripts: combinedScripts, direction: widget.direction, contentPadding: widget.contentPadding, useDefaultFont: widget.useDefaultFont, @@ -355,6 +368,7 @@ class _HtmlContentViewerOnWebState extends State { } void _setUpWeb() { + _createdViewId = _getRandString(10); _htmlData = _generateHtmlDocument(widget.contentHtml); final iframe = html.IFrameElement() @@ -377,54 +391,76 @@ class _HtmlContentViewerOnWebState extends State { @override Widget build(BuildContext context) { - return LayoutBuilder(builder: (context, constraint) { - minHeight = math.min(constraint.maxHeight, minHeight); - final child = Stack( - children: [ - if (_htmlData?.isNotEmpty == false) - const SizedBox.shrink() - else - FutureBuilder( - future: _webInit, - builder: (context, snapshot) { - if (snapshot.hasData) { + super.build(context); + + if (widget.autoAdjustHeight) { + return _buildHtmlElementView(); + } else { + return LayoutBuilder( + builder: (_, constraint) { + minHeight = math.min(constraint.maxHeight, minHeight); + return _buildHtmlElementView(); + }, + ); + } + } + + Widget _buildHtmlElementView() { + log('$runtimeType::_buildHtmlElementView: ActualHeight: $_actualHeight'); + final child = Stack( + children: [ + if (_htmlData?.trim().isNotEmpty == true) + FutureBuilder( + future: _webInit, + builder: (_, snapshot) { + if (snapshot.hasData) { + final htmlView = HtmlElementView( + key: ValueKey('$_htmlData-${widget.key}'), + viewType: _createdViewId, + ); + + if (widget.viewMaxHeight != null) { + return Container( + height: _actualHeight, + width: _actualWidth, + color: Colors.greenAccent, + constraints: BoxConstraints( + maxHeight: widget.viewMaxHeight!, + ), + child: htmlView, + ); + } else { return SizedBox( height: _actualHeight, width: _actualWidth, - child: HtmlElementView( - key: ValueKey('$_htmlData-${widget.key}'), - viewType: _createdViewId, - ), + child: htmlView, ); - } else { - return const SizedBox.shrink(); } + } else { + return const SizedBox.shrink(); } + }, + ), + if (_isLoading) + const Align( + alignment: Alignment.topCenter, + child: Padding( + padding: EdgeInsets.all(16), + child: SizedBox( + width: 30, + height: 30, + child: CupertinoActivityIndicator(color: AppColor.colorLoading), + ), ), - if (_isLoading) - const Align( - alignment: Alignment.topCenter, - child: Padding( - padding: EdgeInsets.all(16), - child: SizedBox( - width: 30, - height: 30, - child: CupertinoActivityIndicator( - color: AppColor.colorLoading - ) - ) - ) - ) - ], - ); + ), + ], + ); - if (!widget.keepWidthWhileLoading) return child; - - return SizedBox( - width: _actualWidth, - child: child, - ); - }); + if (widget.keepWidthWhileLoading) { + return child; + } else { + return SizedBox(width: _actualWidth, child: child); + } } @override @@ -433,4 +469,7 @@ class _HtmlContentViewerOnWebState extends State { _onMessageSubscription.cancel(); super.dispose(); } + + @override + bool get wantKeepAlive => widget.keepAlive; } \ No newline at end of file diff --git a/core/lib/presentation/views/list/no_stretch_scroll_behavior.dart b/core/lib/presentation/views/list/no_stretch_scroll_behavior.dart new file mode 100644 index 000000000..2614cecf2 --- /dev/null +++ b/core/lib/presentation/views/list/no_stretch_scroll_behavior.dart @@ -0,0 +1,12 @@ +import 'package:flutter/material.dart'; + +class NoStretchScrollBehavior extends ScrollBehavior { + @override + Widget buildOverscrollIndicator( + BuildContext context, + Widget child, + ScrollableDetails details, + ) { + return child; + } +} diff --git a/core/lib/utils/html/html_interaction.dart b/core/lib/utils/html/html_interaction.dart index d7308fb20..1936bffd7 100644 --- a/core/lib/utils/html/html_interaction.dart +++ b/core/lib/utils/html/html_interaction.dart @@ -278,4 +278,24 @@ class HtmlInteraction { '''; } + + static const scriptsDisableZoom = ''' + + '''; } \ No newline at end of file diff --git a/core/lib/utils/html/html_template.dart b/core/lib/utils/html/html_template.dart index bbf05502b..e0c8e4035 100644 --- a/core/lib/utils/html/html_template.dart +++ b/core/lib/utils/html/html_template.dart @@ -250,4 +250,16 @@ class HtmlTemplate { } '''; + + static const String disableScrollingStyleCSS = ''' + html, body { + overflow: hidden; + overscroll-behavior: none; + scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* IE/Edge */ + } + ::-webkit-scrollbar { + display: none; + } + '''; } \ No newline at end of file diff --git a/integration_test/mixin/scenario_utils_mixin.dart b/integration_test/mixin/scenario_utils_mixin.dart index b24973d58..8611c89c8 100644 --- a/integration_test/mixin/scenario_utils_mixin.dart +++ b/integration_test/mixin/scenario_utils_mixin.dart @@ -25,7 +25,7 @@ import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_ident import 'package:tmail_ui_user/features/manage_account/domain/usecases/create_new_default_identity_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/create_new_identity_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/identity_interactors_bindings.dart'; import 'package:tmail_ui_user/features/thread/presentation/thread_controller.dart'; import 'package:tmail_ui_user/features/upload/domain/state/attachment_upload_state.dart'; import 'package:tmail_ui_user/main/error/capability_validator.dart'; diff --git a/lib/features/composer/presentation/composer_bindings.dart b/lib/features/composer/presentation/composer_bindings.dart index f21024ba4..7d7692100 100644 --- a/lib/features/composer/presentation/composer_bindings.dart +++ b/lib/features/composer/presentation/composer_bindings.dart @@ -55,8 +55,8 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/data/repository/compose import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/composer_cache_repository.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_composer_cache_by_id_on_web_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/identity_interactors_bindings.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/preferences/bindings/preferences_interactors_bindings.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/new_email_cache_manager.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/new_email_cache_worker_queue.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/opened_email_cache_manager.dart'; diff --git a/lib/features/email/presentation/bindings/email_bindings.dart b/lib/features/email/presentation/bindings/email_bindings.dart index eb0007a67..bd4482eab 100644 --- a/lib/features/email/presentation/bindings/email_bindings.dart +++ b/lib/features/email/presentation/bindings/email_bindings.dart @@ -50,7 +50,7 @@ import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_isolate_work 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/manage_account/domain/usecases/get_all_identities_interactor.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/identity_interactors_bindings.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/new_email_cache_manager.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/new_email_cache_worker_queue.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/opened_email_cache_manager.dart'; diff --git a/lib/features/identity_creator/presentation/identity_creator_bindings.dart b/lib/features/identity_creator/presentation/identity_creator_bindings.dart index b2f6ddadb..eff93c208 100644 --- a/lib/features/identity_creator/presentation/identity_creator_bindings.dart +++ b/lib/features/identity_creator/presentation/identity_creator_bindings.dart @@ -8,8 +8,8 @@ import 'package:tmail_ui_user/features/identity_creator/domain/usecase/save_iden import 'package:tmail_ui_user/features/identity_creator/presentation/identity_creator_controller.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/utils/identity_utils.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/identity_interactors_bindings.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/utils/identity_utils.dart'; import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart'; class IdentityCreatorBindings extends BaseBindings { diff --git a/lib/features/identity_creator/presentation/identity_creator_controller.dart b/lib/features/identity_creator/presentation/identity_creator_controller.dart index 1834177dc..35dbdb480 100644 --- a/lib/features/identity_creator/presentation/identity_creator_controller.dart +++ b/lib/features/identity_creator/presentation/identity_creator_controller.dart @@ -57,8 +57,8 @@ import 'package:tmail_ui_user/features/manage_account/domain/model/edit_identity 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/manage_account/presentation/extensions/identity_extension.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/utils/identity_utils.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/model/identity_action_type.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/utils/identity_utils.dart'; import 'package:tmail_ui_user/features/public_asset/domain/model/public_assets_in_identity_arguments.dart'; import 'package:tmail_ui_user/features/public_asset/presentation/model/public_asset_arguments.dart'; import 'package:tmail_ui_user/features/public_asset/presentation/public_asset_bindings.dart'; diff --git a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart index f59ced8b2..2dd4e0437 100644 --- a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart +++ b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart @@ -99,8 +99,8 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart'; import 'package:tmail_ui_user/features/manage_account/domain/repository/identity_repository.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/utils/identity_utils.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/identity_interactors_bindings.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/utils/identity_utils.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/new_email_cache_manager.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/new_email_cache_worker_queue.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/opened_email_cache_manager.dart'; diff --git a/lib/features/manage_account/data/datasource/identity_data_source.dart b/lib/features/manage_account/data/datasource/identity_data_source.dart index a7fc8f3bb..a7d44d279 100644 --- a/lib/features/manage_account/data/datasource/identity_data_source.dart +++ b/lib/features/manage_account/data/datasource/identity_data_source.dart @@ -5,6 +5,7 @@ import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/create_new_identity_request.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/edit_identity_request.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/identities_response.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/model/identity_signature.dart'; abstract class IdentityDataSource { Future getAllIdentities(Session session, AccountId accountId, {Properties? properties}); @@ -15,5 +16,5 @@ abstract class IdentityDataSource { Future editIdentity(Session session, AccountId accountId, EditIdentityRequest editIdentityRequest); - Future transformHtmlSignature(String signature); + Future transformHtmlSignature(IdentitySignature identitySignature); } \ No newline at end of file diff --git a/lib/features/manage_account/data/datasource_impl/identity_data_source_impl.dart b/lib/features/manage_account/data/datasource_impl/identity_data_source_impl.dart index 824148373..8587aeb52 100644 --- a/lib/features/manage_account/data/datasource_impl/identity_data_source_impl.dart +++ b/lib/features/manage_account/data/datasource_impl/identity_data_source_impl.dart @@ -1,11 +1,5 @@ -import 'package:core/presentation/utils/html_transformer/dom/block_code_transformers.dart'; -import 'package:core/presentation/utils/html_transformer/dom/block_quoted_transformers.dart'; -import 'package:core/presentation/utils/html_transformer/dom/image_transformers.dart'; -import 'package:core/presentation/utils/html_transformer/dom/sanitize_hyper_link_tag_in_html_transformers.dart'; -import 'package:core/presentation/utils/html_transformer/dom/script_transformers.dart'; import 'package:core/presentation/utils/html_transformer/html_transform.dart'; import 'package:core/presentation/utils/html_transformer/transform_configuration.dart'; -import 'package:core/utils/platform_info.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; @@ -15,6 +9,7 @@ import 'package:tmail_ui_user/features/manage_account/data/network/identity_api. import 'package:tmail_ui_user/features/manage_account/domain/model/create_new_identity_request.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/edit_identity_request.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/identities_response.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/model/identity_signature.dart'; import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart'; class IdentityDataSourceImpl extends IdentityDataSource { @@ -58,18 +53,13 @@ class IdentityDataSourceImpl extends IdentityDataSource { } @override - Future transformHtmlSignature(String signature) { + Future transformHtmlSignature(IdentitySignature identitySignature) { return Future.sync(() async { final signatureUnescape = await _htmlTransform.transformToHtml( - htmlContent: signature, - transformConfiguration: TransformConfiguration.create(customDomTransformers: [ - const RemoveScriptTransformer(), - const BlockQuotedTransformer(), - const BlockCodeTransformer(), - SanitizeHyperLinkTagInHtmlTransformer(useTooltip: PlatformInfo.isWeb), - const ImageTransformer(), - ])); - return signatureUnescape; + htmlContent: identitySignature.signature, + transformConfiguration: TransformConfiguration.forSignatureIdentity(), + ); + return identitySignature.newSignature(signatureUnescape); }).catchError(_exceptionThrower.throwException); } } \ No newline at end of file diff --git a/lib/features/manage_account/data/repository/identity_repository_impl.dart b/lib/features/manage_account/data/repository/identity_repository_impl.dart index dc47af63d..3a0fb2162 100644 --- a/lib/features/manage_account/data/repository/identity_repository_impl.dart +++ b/lib/features/manage_account/data/repository/identity_repository_impl.dart @@ -7,6 +7,7 @@ import 'package:tmail_ui_user/features/manage_account/data/datasource/identity_d import 'package:tmail_ui_user/features/manage_account/domain/model/create_new_identity_request.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/edit_identity_request.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/identities_response.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/model/identity_signature.dart'; import 'package:tmail_ui_user/features/manage_account/domain/repository/identity_repository.dart'; class IdentityRepositoryImpl extends IdentityRepository { @@ -36,7 +37,7 @@ class IdentityRepositoryImpl extends IdentityRepository { } @override - Future transformHtmlSignature(String signature) { - return _dataSource.transformHtmlSignature(signature); + Future transformHtmlSignature(IdentitySignature identitySignature) { + return _dataSource.transformHtmlSignature(identitySignature); } } \ No newline at end of file diff --git a/lib/features/manage_account/domain/model/identity_signature.dart b/lib/features/manage_account/domain/model/identity_signature.dart new file mode 100644 index 000000000..88bd980af --- /dev/null +++ b/lib/features/manage_account/domain/model/identity_signature.dart @@ -0,0 +1,19 @@ +import 'package:equatable/equatable.dart'; +import 'package:jmap_dart_client/jmap/identities/identity.dart'; + +class IdentitySignature with EquatableMixin { + final IdentityId identityId; + final String signature; + + IdentitySignature({required this.identityId, required this.signature}); + + IdentitySignature newSignature(String newSignature) { + return IdentitySignature( + identityId: identityId, + signature: newSignature + ); + } + + @override + List get props => [identityId, signature]; +} diff --git a/lib/features/manage_account/domain/repository/identity_repository.dart b/lib/features/manage_account/domain/repository/identity_repository.dart index 494ec9bbe..8dbce96c6 100644 --- a/lib/features/manage_account/domain/repository/identity_repository.dart +++ b/lib/features/manage_account/domain/repository/identity_repository.dart @@ -5,6 +5,7 @@ import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/create_new_identity_request.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/edit_identity_request.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/identities_response.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/model/identity_signature.dart'; abstract class IdentityRepository { Future getAllIdentities(Session session, AccountId accountId, {Properties? properties}); @@ -15,5 +16,5 @@ abstract class IdentityRepository { Future editIdentity(Session session, AccountId accountId, EditIdentityRequest editIdentityRequest); - Future transformHtmlSignature(String signature); + Future transformHtmlSignature(IdentitySignature identitySignature); } \ No newline at end of file diff --git a/lib/features/manage_account/domain/state/transform_html_signature_state.dart b/lib/features/manage_account/domain/state/transform_html_signature_state.dart deleted file mode 100644 index cbb795689..000000000 --- a/lib/features/manage_account/domain/state/transform_html_signature_state.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; - -class TransformHtmlSignatureLoading extends UIState {} - -class TransformHtmlSignatureSuccess extends UIState { - final String signature; - - TransformHtmlSignatureSuccess(this.signature); - - @override - List get props => [signature]; -} - -class TransformHtmlSignatureFailure extends FeatureFailure { - - TransformHtmlSignatureFailure(exception) : super(exception: exception); -} \ No newline at end of file diff --git a/lib/features/manage_account/domain/state/transform_list_signature_state.dart b/lib/features/manage_account/domain/state/transform_list_signature_state.dart new file mode 100644 index 000000000..5b5db1d69 --- /dev/null +++ b/lib/features/manage_account/domain/state/transform_list_signature_state.dart @@ -0,0 +1,21 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/model/identity_signature.dart'; + +class TransformListSignatureLoading extends LoadingState {} + +class TransformListSignatureSuccess extends UIState { + final List identitySignatures; + + TransformListSignatureSuccess(this.identitySignatures); + + @override + List get props => [identitySignatures]; +} + +class TransformListSignatureFailure extends FeatureFailure { + final List identitySignatures; + + TransformListSignatureFailure(exception, this.identitySignatures) + : super(exception: exception); +} diff --git a/lib/features/manage_account/domain/usecases/create_new_default_identity_interactor.dart b/lib/features/manage_account/domain/usecases/create_new_default_identity_interactor.dart index 5a31906fd..8bf97ac25 100644 --- a/lib/features/manage_account/domain/usecases/create_new_default_identity_interactor.dart +++ b/lib/features/manage_account/domain/usecases/create_new_default_identity_interactor.dart @@ -11,7 +11,7 @@ import 'package:tmail_ui_user/features/manage_account/domain/model/create_new_de import 'package:tmail_ui_user/features/manage_account/domain/model/create_new_identity_request.dart'; import 'package:tmail_ui_user/features/manage_account/domain/repository/identity_repository.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/create_new_default_identity_state.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/utils/identity_utils.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/utils/identity_utils.dart'; class CreateNewDefaultIdentityInteractor { final IdentityRepository _identityRepository; diff --git a/lib/features/manage_account/domain/usecases/edit_default_identity_interactor.dart b/lib/features/manage_account/domain/usecases/edit_default_identity_interactor.dart index bce595cf5..aab50275f 100644 --- a/lib/features/manage_account/domain/usecases/edit_default_identity_interactor.dart +++ b/lib/features/manage_account/domain/usecases/edit_default_identity_interactor.dart @@ -10,7 +10,7 @@ import 'package:tmail_ui_user/features/manage_account/domain/model/edit_default_ import 'package:tmail_ui_user/features/manage_account/domain/model/edit_identity_request.dart'; import 'package:tmail_ui_user/features/manage_account/domain/repository/identity_repository.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/edit_default_identity_state.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/utils/identity_utils.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/utils/identity_utils.dart'; class EditDefaultIdentityInteractor { final IdentityRepository _identityRepository; diff --git a/lib/features/manage_account/domain/usecases/get_all_identities_interactor.dart b/lib/features/manage_account/domain/usecases/get_all_identities_interactor.dart index 213a26cf9..f7389cd50 100644 --- a/lib/features/manage_account/domain/usecases/get_all_identities_interactor.dart +++ b/lib/features/manage_account/domain/usecases/get_all_identities_interactor.dart @@ -9,7 +9,7 @@ import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:tmail_ui_user/features/manage_account/domain/repository/identity_repository.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_identities_state.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/utils/identity_utils.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/utils/identity_utils.dart'; import 'package:tmail_ui_user/main/error/capability_validator.dart'; class GetAllIdentitiesInteractor { diff --git a/lib/features/manage_account/domain/usecases/transform_html_signature_interactor.dart b/lib/features/manage_account/domain/usecases/transform_html_signature_interactor.dart deleted file mode 100644 index b4e2f80cb..000000000 --- a/lib/features/manage_account/domain/usecases/transform_html_signature_interactor.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'dart:core'; - -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:dartz/dartz.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/repository/identity_repository.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/state/edit_identity_state.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/state/transform_html_signature_state.dart'; - -class TransformHtmlSignatureInteractor { - final IdentityRepository _identityRepository; - - TransformHtmlSignatureInteractor(this._identityRepository); - - Stream> execute(String signature) async* { - try { - yield Right(TransformHtmlSignatureLoading()); - final signatureUnescape = await _identityRepository.transformHtmlSignature(signature); - yield Right(TransformHtmlSignatureSuccess(signatureUnescape)); - } catch (exception) { - yield Left(EditIdentityFailure(exception)); - } - } -} \ No newline at end of file diff --git a/lib/features/manage_account/domain/usecases/transform_list_signature_interactor.dart b/lib/features/manage_account/domain/usecases/transform_list_signature_interactor.dart new file mode 100644 index 000000000..eb87e55c1 --- /dev/null +++ b/lib/features/manage_account/domain/usecases/transform_list_signature_interactor.dart @@ -0,0 +1,37 @@ +import 'dart:core'; + +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:core/utils/app_logger.dart'; +import 'package:dartz/dartz.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/model/identity_signature.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/repository/identity_repository.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/state/transform_list_signature_state.dart'; + +class TransformListSignatureInteractor { + final IdentityRepository _identityRepository; + + TransformListSignatureInteractor(this._identityRepository); + + Stream> execute(List identitySignatures) async* { + try { + yield Right(TransformListSignatureLoading()); + final newListIdentitySignature = await Future.wait( + identitySignatures.map(_transformHtmlSignature), + eagerError: true, + ); + yield Right(TransformListSignatureSuccess(newListIdentitySignature)); + } catch (exception) { + yield Left(TransformListSignatureFailure(exception, identitySignatures)); + } + } + + Future _transformHtmlSignature(IdentitySignature identitySignature) async { + try { + return await _identityRepository.transformHtmlSignature(identitySignature); + } catch (e) { + logError('TransformListSignatureInteractor::_transformHtmlSignature:Exception = $e'); + return identitySignature; + } + } +} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/extensions/identity_extension.dart b/lib/features/manage_account/presentation/extensions/identity_extension.dart index b06486910..c7cec5fe0 100644 --- a/lib/features/manage_account/presentation/extensions/identity_extension.dart +++ b/lib/features/manage_account/presentation/extensions/identity_extension.dart @@ -1,5 +1,5 @@ - import 'package:jmap_dart_client/jmap/identities/identity.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/model/identity_signature.dart'; extension IdentityExtension on Identity { @@ -12,4 +12,8 @@ extension IdentityExtension on Identity { return ''; } } -} \ No newline at end of file + + IdentitySignature? toIdentitySignature() => id != null + ? IdentitySignature(identityId: id!, signature: signatureAsString) + : null; +} diff --git a/lib/features/manage_account/presentation/extensions/list_identity_extension.dart b/lib/features/manage_account/presentation/extensions/list_identity_extension.dart new file mode 100644 index 000000000..968c696cc --- /dev/null +++ b/lib/features/manage_account/presentation/extensions/list_identity_extension.dart @@ -0,0 +1,13 @@ + +import 'package:jmap_dart_client/jmap/identities/identity.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/model/identity_signature.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/extensions/identity_extension.dart'; + +extension ListIdentityExtension on List { + + List toListIdentitySignature() { + return map((identity) => identity.toIdentitySignature()) + .nonNulls + .toList(); + } +} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/profiles/identities/identities_controller.dart b/lib/features/manage_account/presentation/identities/identities_controller.dart similarity index 88% rename from lib/features/manage_account/presentation/profiles/identities/identities_controller.dart rename to lib/features/manage_account/presentation/identities/identities_controller.dart index 5574cfce2..481849899 100644 --- a/lib/features/manage_account/presentation/profiles/identities/identities_controller.dart +++ b/lib/features/manage_account/presentation/identities/identities_controller.dart @@ -26,24 +26,26 @@ import 'package:tmail_ui_user/features/identity_creator/presentation/model/ident import 'package:tmail_ui_user/features/identity_creator/presentation/restore_identity_cache_interactor_bindings.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/create_new_identity_request.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/edit_identity_request.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/model/identity_signature.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/create_new_default_identity_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/create_new_identity_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/delete_identity_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/edit_identity_state.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/state/transform_html_signature_state.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/state/transform_list_signature_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/create_new_default_identity_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/create_new_identity_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/delete_identity_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/edit_default_identity_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/edit_identity_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/usecases/transform_html_signature_interactor.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/transform_list_signature_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/extensions/identity_extension.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/extensions/update_own_email_address_extension.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/extensions/list_identity_extension.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/widgets/delete_identity_dialog_builder.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/manage_account_dashboard_controller.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/model/identity_action_type.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/widgets/delete_identity_dialog_builder.dart'; import 'package:tmail_ui_user/features/public_asset/domain/extensions/string_to_public_asset_extension.dart'; import 'package:tmail_ui_user/features/public_asset/domain/model/public_assets_in_identity_arguments.dart'; import 'package:tmail_ui_user/features/public_asset/domain/state/remove_identity_from_public_assets_state.dart'; @@ -67,15 +69,19 @@ class IdentitiesController extends ReloadableController implements BeforeReconne final DeleteIdentityInteractor _deleteIdentityInteractor; final EditIdentityInteractor _editIdentityInteractor; final EditDefaultIdentityInteractor _editDefaultIdentityInteractor; - final TransformHtmlSignatureInteractor _transformHtmlSignatureInteractor; + final TransformListSignatureInteractor _transformListSignatureInteractor; final SaveIdentityCacheOnWebInteractor _saveIdentityCacheOnWebInteractor; final identitySelected = Rxn(); final signatureSelected = Rxn(); final listAllIdentities = [].obs; + final mapIdentitySignatures = {}.obs; + final identitiesViewState = Rx>(Right(UIState.idle)); + final signatureViewState = Rx>(Right(UIState.idle)); dynamic newIdentityArguments; - + ScrollController? listIdentityScrollController; + final _beforeReconnectManager = Get.find(); IdentitiesController( @@ -85,7 +91,7 @@ class IdentitiesController extends ReloadableController implements BeforeReconne this._editIdentityInteractor, this._createNewDefaultIdentityInteractor, this._editDefaultIdentityInteractor, - this._transformHtmlSignatureInteractor, + this._transformListSignatureInteractor, this._saveIdentityCacheOnWebInteractor ); @@ -94,14 +100,24 @@ class IdentitiesController extends ReloadableController implements BeforeReconne _registerObxStreamListener(); RestoreIdentityCacheInteractorBindings().dependencies(); _beforeReconnectManager.addListener(onBeforeReconnect); + if (PlatformInfo.isWeb) { + listIdentityScrollController = ScrollController(); + } super.onInit(); } + @override + void onReady() { + identitiesViewState.value = Right(GetAllIdentitiesLoading()); + super.onReady(); + } + @override void handleSuccessViewState(Success success) { - super.handleSuccessViewState(success); if (success is GetAllIdentitiesSuccess) { _handleGetAllIdentitiesSuccess(success); + } if (success is GetAllIdentitiesLoading) { + identitiesViewState.value = Right(success); } else if (success is CreateNewIdentitySuccess) { _createNewIdentitySuccess(success); } else if (success is CreateNewDefaultIdentitySuccess) { @@ -110,19 +126,25 @@ class IdentitiesController extends ReloadableController implements BeforeReconne _deleteIdentitySuccess(success); } else if (success is EditIdentitySuccess) { _editIdentitySuccess(success); - } else if (success is TransformHtmlSignatureSuccess) { - signatureSelected.value = success.signature; + } else if (success is TransformListSignatureLoading) { + signatureViewState.value = Right(success); + } else if (success is TransformListSignatureSuccess) { + signatureViewState.value = Right(success); + _syncMapIdentitySignatures(success.identitySignatures); } else if (success is RemoveIdentityFromPublicAssetsSuccessState) { _deleteIdentityAction(success.identityId); } else if (success is GetIdentityCacheOnWebSuccess) { _openIdentityEditorFromCache(success); + } else { + super.handleSuccessViewState(success); } } @override void handleFailureViewState(Failure failure) { - super.handleFailureViewState(failure); - if (failure is DeleteIdentityFailure) { + if (failure is GetAllIdentitiesFailure) { + identitiesViewState.value = Left(failure); + } else if (failure is DeleteIdentityFailure) { _deleteIdentityFailure(failure); } else if (failure is RemoveIdentityFromPublicAssetsFailureState) { _deleteIdentityAction(failure.identityId); @@ -134,6 +156,11 @@ class IdentitiesController extends ReloadableController implements BeforeReconne accountDashBoardController.synchronizeOwnEmailAddress( accountDashBoardController.sessionCurrent?.getUserDisplayName() ?? '', ); + } else if (failure is TransformListSignatureFailure) { + signatureViewState.value = Left(failure); + _syncMapIdentitySignatures(failure.identitySignatures); + } else { + super.handleFailureViewState(failure); } } @@ -164,29 +191,39 @@ class IdentitiesController extends ReloadableController implements BeforeReconne } void _handleGetAllIdentitiesSuccess(GetAllIdentitiesSuccess success) { + identitiesViewState.value = Right(success); + final listIdentities = success.identities ?? []; - - if (listIdentities.isNotEmpty) { - final newListIdentities = listIdentities - .where((identity) => identity.mayDelete == true && identity.name?.trim().isNotEmpty == true) - .toList(); - listAllIdentities.addAll(newListIdentities); - } - accountDashBoardController.updateOwnEmailAddressFromIdentities(listIdentities); - if (listAllIdentities.isNotEmpty) { - selectIdentity(listAllIdentities.first); - } + final newListIdentities = listIdentities.where(_validateIdentity).toList(); + + if (newListIdentities.isEmpty) return; + + listAllIdentities.addAll(newListIdentities); + + identitySelected.value = listAllIdentities.first; + + _transformSignature(); + } + + bool _validateIdentity(Identity identity) { + return identity.mayDelete == true && + identity.name?.trim().isNotEmpty == true; } - void selectIdentity(Identity? newIdentity) { - signatureSelected.value = null; - identitySelected.value = newIdentity; + void _transformSignature() { + final listIdentitySignature = listAllIdentities.toListIdentitySignature(); + consumeState(_transformListSignatureInteractor.execute(listIdentitySignature)); + } - if (newIdentity != null) { - consumeState(_transformHtmlSignatureInteractor.execute(newIdentity.signatureAsString)); - } + void _syncMapIdentitySignatures(List identitySignatures) { + mapIdentitySignatures.value = Map.fromEntries( + identitySignatures.map((identitySignature) => MapEntry( + identitySignature.identityId, + identitySignature.signature, + )), + ); } void goToCreateNewIdentity(BuildContext context) async { @@ -527,6 +564,9 @@ class IdentitiesController extends ReloadableController implements BeforeReconne RestoreIdentityCacheInteractorBindings().close(); newIdentityArguments = null; _beforeReconnectManager.removeListener(onBeforeReconnect); + if (PlatformInfo.isWeb) { + listIdentityScrollController?.dispose(); + } super.onClose(); } } \ No newline at end of file diff --git a/lib/features/manage_account/presentation/identities/identities_view.dart b/lib/features/manage_account/presentation/identities/identities_view.dart new file mode 100644 index 000000000..5e6fda633 --- /dev/null +++ b/lib/features/manage_account/presentation/identities/identities_view.dart @@ -0,0 +1,145 @@ +import 'package:core/presentation/utils/theme_utils.dart'; +import 'package:core/presentation/views/list/no_stretch_scroll_behavior.dart'; +import 'package:core/utils/platform_info.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/base/setting_detail_view_builder.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/identities_controller.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/widgets/create_new_identity_button_widget.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/widgets/identity_list_tile_builder.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/widgets/identity_loading_widget.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings_utils.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/model/account_menu_item.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/widgets/setting_explanation_widget.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/widgets/setting_header_widget.dart'; + +class IdentitiesView extends GetWidget { + + const IdentitiesView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final isDesktop = controller.responsiveUtils.isDesktop(context); + + return SettingDetailViewBuilder( + responsiveUtils: controller.responsiveUtils, + child: Container( + color: SettingsUtils.getContentBackgroundColor( + context, + controller.responsiveUtils, + ), + decoration: SettingsUtils.getBoxDecorationForContent( + context, + controller.responsiveUtils, + ), + width: double.infinity, + padding: isDesktop + ? const EdgeInsetsDirectional.only( + start: 30, + end: 30, + top: 22, + ) + : null, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (isDesktop) + ...[ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: SettingHeaderWidget( + menuItem: AccountMenuItem.profiles, + textStyle: ThemeUtils.textStyleInter600().copyWith( + color: Colors.black.withValues(alpha: 0.9), + ), + padding: const EdgeInsetsDirectional.only(end: 16), + ), + ), + CreateNewIdentityButtonWidget( + imagePaths: controller.imagePaths, + onCreateNewIdentityAction: () => + controller.goToCreateNewIdentity(context), + ), + ], + ), + const SizedBox(height: 14), + ] + else + ...[ + const SettingExplanationWidget( + menuItem: AccountMenuItem.profiles, + padding: EdgeInsetsDirectional.symmetric(horizontal: 16), + isCenter: true, + textAlign: TextAlign.center, + ), + Center( + child: CreateNewIdentityButtonWidget( + imagePaths: controller.imagePaths, + margin: const EdgeInsets.only(top: 24, bottom: 16), + onCreateNewIdentityAction: () => + controller.goToCreateNewIdentity(context), + ), + ), + ], + Obx(() => IdentityLoadingWidget( + identityViewState: controller.identitiesViewState.value, + )), + Expanded( + child: Obx(() { + final Widget listView = ListView.separated( + shrinkWrap: true, + itemCount: controller.listAllIdentities.length + 1, + controller: controller.listIdentityScrollController, + padding: const EdgeInsetsDirectional.only( + start: 24, + end: 24, + bottom: 24, + ), + itemBuilder: (context, index) { + if (index == controller.listAllIdentities.length) { + return const SizedBox.shrink(); + } + + final identity = controller.listAllIdentities[index]; + final isSelected = identity == controller.identitySelected.value; + return Obx(() => IdentityListTileBuilder( + imagePaths: controller.imagePaths, + identity: identity, + isSelected: isSelected, + mapIdentitySignatures: controller.mapIdentitySignatures, + signatureViewState: controller.signatureViewState.value, + isDesktop: isDesktop, + scrollController: controller.listIdentityScrollController, + onEditIdentityAction: (identitySelected) => + controller.goToEditIdentity(context, identitySelected), + onDeleteIdentityAction: (identitySelected) => + controller.openConfirmationDialogDeleteIdentityAction( + context, + identitySelected, + ), + )); + }, + separatorBuilder: (_, index) => Padding( + padding: const EdgeInsets.symmetric(vertical: 24), + child: Divider(color: Colors.black.withValues(alpha: 0.08)), + ), + ); + + if (PlatformInfo.isMobile) { + return ScrollConfiguration( + behavior: NoStretchScrollBehavior(), + child: listView, + ); + } else { + return listView; + } + }), + ), + ] + ), + ) + ); + } +} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/profiles/identities/identity_bindings.dart b/lib/features/manage_account/presentation/identities/identity_bindings.dart similarity index 87% rename from lib/features/manage_account/presentation/profiles/identities/identity_bindings.dart rename to lib/features/manage_account/presentation/identities/identity_bindings.dart index f7a2c70e5..993f37e64 100644 --- a/lib/features/manage_account/presentation/profiles/identities/identity_bindings.dart +++ b/lib/features/manage_account/presentation/identities/identity_bindings.dart @@ -6,9 +6,9 @@ import 'package:tmail_ui_user/features/manage_account/domain/usecases/delete_ide import 'package:tmail_ui_user/features/manage_account/domain/usecases/edit_default_identity_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/edit_identity_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/usecases/transform_html_signature_interactor.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identities_controller.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/transform_list_signature_interactor.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/identities_controller.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/identity_interactors_bindings.dart'; class IdentityBindings extends Bindings { @@ -23,7 +23,7 @@ class IdentityBindings extends Bindings { Get.find(), Get.find(), Get.find(), - Get.find(), + Get.find(), Get.find(), )); } diff --git a/lib/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart b/lib/features/manage_account/presentation/identities/identity_interactors_bindings.dart similarity index 96% rename from lib/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart rename to lib/features/manage_account/presentation/identities/identity_interactors_bindings.dart index 8a86fe8e1..de8943527 100644 --- a/lib/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart +++ b/lib/features/manage_account/presentation/identities/identity_interactors_bindings.dart @@ -17,8 +17,8 @@ import 'package:tmail_ui_user/features/manage_account/domain/usecases/delete_ide import 'package:tmail_ui_user/features/manage_account/domain/usecases/edit_default_identity_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/edit_identity_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/usecases/transform_html_signature_interactor.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/utils/identity_utils.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/transform_list_signature_interactor.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/utils/identity_utils.dart'; import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart'; import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart'; @@ -89,7 +89,7 @@ class IdentityInteractorsBindings extends InteractorsBindings { Get.find(tag: composerId) ), tag: composerId); Get.lazyPut( - () => TransformHtmlSignatureInteractor(Get.find(tag: composerId)), + () => TransformListSignatureInteractor(Get.find(tag: composerId)), tag: composerId, ); Get.lazyPut( @@ -138,7 +138,7 @@ class IdentityInteractorsBindings extends InteractorsBindings { Get.delete(tag: composerId); Get.delete(tag: composerId); Get.delete(tag: composerId); - Get.delete(tag: composerId); + Get.delete(tag: composerId); Get.delete(tag: composerId); } } \ No newline at end of file diff --git a/lib/features/manage_account/presentation/profiles/identities/utils/identity_utils.dart b/lib/features/manage_account/presentation/identities/utils/identity_utils.dart similarity index 100% rename from lib/features/manage_account/presentation/profiles/identities/utils/identity_utils.dart rename to lib/features/manage_account/presentation/identities/utils/identity_utils.dart diff --git a/lib/features/manage_account/presentation/identities/widgets/create_new_identity_button_widget.dart b/lib/features/manage_account/presentation/identities/widgets/create_new_identity_button_widget.dart new file mode 100644 index 000000000..f7c6e10d8 --- /dev/null +++ b/lib/features/manage_account/presentation/identities/widgets/create_new_identity_button_widget.dart @@ -0,0 +1,46 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/utils/theme_utils.dart'; +import 'package:core/presentation/views/button/tmail_button_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +typedef OnCreateNewIdentityAction = void Function(); + +class CreateNewIdentityButtonWidget extends StatelessWidget { + final ImagePaths imagePaths; + final OnCreateNewIdentityAction onCreateNewIdentityAction; + final EdgeInsetsGeometry? margin; + + const CreateNewIdentityButtonWidget({ + super.key, + required this.imagePaths, + required this.onCreateNewIdentityAction, + this.margin, + }); + + @override + Widget build(BuildContext context) { + return TMailButtonWidget( + text: AppLocalizations.of(context).createNewIdentity, + icon: imagePaths.icAddIdentity, + backgroundColor: AppColor.primaryMain, + borderRadius: 100, + height: 48, + maxWidth: 300, + margin: margin, + textStyle: ThemeUtils.textStyleM3LabelLarge(color: Colors.white), + padding: const EdgeInsetsDirectional.symmetric( + horizontal: 32, + ), + iconSize: 16, + iconColor: Colors.white, + iconSpace: 8, + maxLines: 1, + flexibleText: true, + textOverflow: TextOverflow.ellipsis, + mainAxisSize: MainAxisSize.min, + onTapActionCallback: onCreateNewIdentityAction, + ); + } +} diff --git a/lib/features/manage_account/presentation/profiles/identities/widgets/delete_identity_dialog_builder.dart b/lib/features/manage_account/presentation/identities/widgets/delete_identity_dialog_builder.dart similarity index 100% rename from lib/features/manage_account/presentation/profiles/identities/widgets/delete_identity_dialog_builder.dart rename to lib/features/manage_account/presentation/identities/widgets/delete_identity_dialog_builder.dart diff --git a/lib/features/manage_account/presentation/identities/widgets/identity_list_tile_builder.dart b/lib/features/manage_account/presentation/identities/widgets/identity_list_tile_builder.dart new file mode 100644 index 000000000..8cf438072 --- /dev/null +++ b/lib/features/manage_account/presentation/identities/widgets/identity_list_tile_builder.dart @@ -0,0 +1,157 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:core/presentation/utils/theme_utils.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:jmap_dart_client/jmap/identities/identity.dart'; +import 'package:model/extensions/list_email_address_extension.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/extensions/identity_extension.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/widgets/list_identity_item_actions_widget.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/widgets/signature_builder.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/widgets/signature_loading_widget.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +class IdentityListTileBuilder extends StatelessWidget { + + const IdentityListTileBuilder({ + Key? key, + required this.identity, + required this.mapIdentitySignatures, + required this.signatureViewState, + required this.imagePaths, + required this.onEditIdentityAction, + required this.onDeleteIdentityAction, + this.isDesktop = false, + this.isSelected = false, + this.scrollController, + }) : super(key: key); + + final Identity identity; + final Map mapIdentitySignatures; + final Either signatureViewState; + final ImagePaths imagePaths; + final bool isSelected; + final bool isDesktop; + final OnEditIdentityAction onEditIdentityAction; + final OnDeleteIdentityAction onDeleteIdentityAction; + final ScrollController? scrollController; + + @override + Widget build(BuildContext context) { + final selectedIcon = SvgPicture.asset( + isSelected ? imagePaths.icRadioSelected : imagePaths.icRadio, + width: 18, + height: 18, + fit: BoxFit.fill, + ); + + final signatureContent = mapIdentitySignatures[identity.id!] ?? + identity.signatureAsString; + + final identityContent = Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 8), + child: Text( + (identity.name ?? ''), + style: ThemeUtils.textStyleBodyBody1(color: Colors.black), + ), + ), + if (identity.email?.isNotEmpty == true) + Padding( + padding: const EdgeInsets.only(bottom: 8), + child: Text( + identity.email ?? '', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: AppColor.steelGray400, + ), + ), + ), + if (identity.replyTo?.isNotEmpty == true) + Padding( + padding: const EdgeInsets.only(bottom: 8), + child: Text( + '${AppLocalizations.of(context).reply_to.toUpperCase()}: ${identity.replyTo?.listEmailAddressToString(isFullEmailAddress: true)}', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: AppColor.steelGray400, + ), + ), + ), + if (identity.bcc?.isNotEmpty == true) + Padding( + padding: const EdgeInsets.only(bottom: 8), + child: Text( + '${AppLocalizations.of(context).bcc_email_address_prefix.toUpperCase()}: ${identity.bcc?.listEmailAddressToString(isFullEmailAddress: true)}', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: AppColor.steelGray400, + ), + ), + ), + if (signatureContent.trim().isNotEmpty) + ...[ + Text( + '--', + style: ThemeUtils.textStyleBodyBody2( + color: Colors.black, + ), + ), + SignatureLoadingWidget(signatureViewState: signatureViewState), + SignatureBuilder( + value: signatureContent, + width: isDesktop ? 280 : double.infinity, + scrollController: scrollController, + ), + ], + ], + ); + + if (isDesktop) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + selectedIcon, + Container( + width: 280, + padding: const EdgeInsetsDirectional.only(start: 18, end: 12), + child: identityContent, + ), + ListIdentityItemActionsWidget( + identity: identity, + imagePaths: imagePaths, + onEditIdentityAction: onEditIdentityAction, + onDeleteIdentityAction: onDeleteIdentityAction, + isDesktop: isDesktop, + ), + const Spacer(), + ], + ); + } else { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + selectedIcon, + const SizedBox(width: 18), + Expanded(child: identityContent), + ], + ), + const SizedBox(height: 24), + ListIdentityItemActionsWidget( + identity: identity, + imagePaths: imagePaths, + onEditIdentityAction: onEditIdentityAction, + onDeleteIdentityAction: onDeleteIdentityAction, + isDesktop: isDesktop, + ), + ], + ); + } + } +} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/identities/widgets/identity_loading_widget.dart b/lib/features/manage_account/presentation/identities/widgets/identity_loading_widget.dart new file mode 100644 index 000000000..93052fe8a --- /dev/null +++ b/lib/features/manage_account/presentation/identities/widgets/identity_loading_widget.dart @@ -0,0 +1,32 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flutter/material.dart'; +import 'package:tmail_ui_user/features/base/widget/circle_loading_widget.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_identities_state.dart'; + +class IdentityLoadingWidget extends StatelessWidget { + + final Either identityViewState; + + const IdentityLoadingWidget({ + super.key, + required this.identityViewState, + }); + + @override + Widget build(BuildContext context) { + return identityViewState.fold( + (failure) => const SizedBox.shrink(), + (success) { + if (success is GetAllIdentitiesLoading) { + return const Center( + child: CircleLoadingWidget(margin: EdgeInsets.all(16.0)), + ); + } else { + return const SizedBox.shrink(); + } + }, + ); + } +} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/identities/widgets/list_identity_item_actions_widget.dart b/lib/features/manage_account/presentation/identities/widgets/list_identity_item_actions_widget.dart new file mode 100644 index 000000000..5f35457d5 --- /dev/null +++ b/lib/features/manage_account/presentation/identities/widgets/list_identity_item_actions_widget.dart @@ -0,0 +1,82 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/utils/theme_utils.dart'; +import 'package:core/presentation/views/button/tmail_button_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:jmap_dart_client/jmap/identities/identity.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +typedef OnEditIdentityAction = Function(Identity identity); +typedef OnDeleteIdentityAction = Function(Identity identity); + +class ListIdentityItemActionsWidget extends StatelessWidget { + final Identity identity; + final ImagePaths imagePaths; + final OnEditIdentityAction onEditIdentityAction; + final OnDeleteIdentityAction onDeleteIdentityAction; + final bool isDesktop; + + const ListIdentityItemActionsWidget({ + super.key, + required this.identity, + required this.imagePaths, + required this.onEditIdentityAction, + required this.onDeleteIdentityAction, + this.isDesktop = true, + }); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + TMailButtonWidget( + icon: imagePaths.icCompose, + iconSize: 20, + text: AppLocalizations.of(context).edit, + backgroundColor: Colors.transparent, + iconColor: AppColor.primaryColor, + textStyle: ThemeUtils.textStyleInter500().copyWith( + fontSize: 13, + height: 1, + letterSpacing: 0.39, + color: AppColor.primaryColor, + ), + flexibleText: true, + minWidth: 100, + mainAxisSize: MainAxisSize.min, + maxLines: 1, + iconSpace: 4, + padding: const EdgeInsetsDirectional.symmetric( + vertical: 5, + horizontal: 8, + ), + onTapActionCallback: () => onEditIdentityAction(identity), + ), + TMailButtonWidget( + icon: imagePaths.icDeleteRule, + iconSize: 20, + text: AppLocalizations.of(context).delete, + backgroundColor: Colors.transparent, + iconColor: AppColor.primaryColor, + textStyle: ThemeUtils.textStyleInter500().copyWith( + fontSize: 13, + height: 1, + letterSpacing: 0.39, + color: AppColor.primaryColor, + ), + minWidth: 100, + flexibleText: true, + mainAxisSize: MainAxisSize.min, + maxLines: 1, + iconSpace: 4, + padding: const EdgeInsetsDirectional.symmetric( + vertical: 5, + horizontal: 8, + ), + onTapActionCallback: () => onDeleteIdentityAction(identity), + ), + ], + ); + } +} diff --git a/lib/features/manage_account/presentation/identities/widgets/signature_builder.dart b/lib/features/manage_account/presentation/identities/widgets/signature_builder.dart new file mode 100644 index 000000000..5e4e3f542 --- /dev/null +++ b/lib/features/manage_account/presentation/identities/widgets/signature_builder.dart @@ -0,0 +1,55 @@ +import 'package:core/presentation/views/html_viewer/html_content_viewer_on_web_widget.dart'; +import 'package:core/presentation/views/html_viewer/html_content_viewer_widget.dart'; +import 'package:core/utils/platform_info.dart'; +import 'package:flutter/material.dart'; +import 'package:tmail_ui_user/main/utils/app_utils.dart'; + +class SignatureBuilder extends StatelessWidget { + + const SignatureBuilder({ + Key? key, + required this.value, + this.height = 150, + this.width = 280, + this.scrollController, + }) : super(key: key); + + final String value; + final double width; + final double height; + final ScrollController? scrollController; + + @override + Widget build(BuildContext context) { + if (PlatformInfo.isWeb) { + return HtmlContentViewerOnWeb( + contentHtml: value, + widthContent: width, + heightContent: height, + contentPadding: 0, + viewMaxHeight: height, + htmlContentMinWidth: width, + htmlContentMinHeight: 0.0, + offsetHtmlContentHeight: 0.0, + allowResizeToDocumentSize: false, + direction: AppUtils.getCurrentDirection(context), + scrollController: scrollController, + keepAlive: true, + disableScrolling: true, + autoAdjustHeight: true, + ); + } else { + return HtmlContentViewer( + contentHtml: value, + initialWidth: width, + maxViewHeight: height, + contentPadding: 0, + htmlContentMinHeight: 0.0, + offsetHtmlContentHeight: 0.0, + direction: AppUtils.getCurrentDirection(context), + keepAlive: true, + disableScrolling: true, + ); + } + } +} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/identities/widgets/signature_loading_widget.dart b/lib/features/manage_account/presentation/identities/widgets/signature_loading_widget.dart new file mode 100644 index 000000000..57cd6b08a --- /dev/null +++ b/lib/features/manage_account/presentation/identities/widgets/signature_loading_widget.dart @@ -0,0 +1,35 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/state/transform_list_signature_state.dart'; + +class SignatureLoadingWidget extends StatelessWidget { + + final Either signatureViewState; + + const SignatureLoadingWidget({ + super.key, + required this.signatureViewState, + }); + + @override + Widget build(BuildContext context) { + return signatureViewState.fold( + (failure) => const SizedBox.shrink(), + (success) { + if (success is TransformListSignatureLoading) { + return const Center( + child: Padding( + padding: EdgeInsets.all(8), + child: CupertinoActivityIndicator(color: AppColor.colorLoading), + ), + ); + } else { + return const SizedBox.shrink(); + } + }, + ); + } +} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/manage_account_dashboard_bindings.dart b/lib/features/manage_account/presentation/manage_account_dashboard_bindings.dart index e698b5dab..020d066e3 100644 --- a/lib/features/manage_account/presentation/manage_account_dashboard_bindings.dart +++ b/lib/features/manage_account/presentation/manage_account_dashboard_bindings.dart @@ -6,10 +6,10 @@ import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_ import 'package:tmail_ui_user/features/manage_account/data/local/local_setting_cache_manager.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/presentation/identities/identity_bindings.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/manage_account_dashboard_controller.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/menu/manage_account_menu_bindings.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings/settings_bindings.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/profiles_bindings.dart'; import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart'; class ManageAccountDashBoardBindings extends BaseBindings { @@ -19,7 +19,7 @@ class ManageAccountDashBoardBindings extends BaseBindings { super.dependencies(); SettingsBindings().dependencies(); ManageAccountMenuBindings().dependencies(); - ProfileBindings().dependencies(); + IdentityBindings().dependencies(); } @override 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 effd11b37..3a9c02fcc 100644 --- a/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart +++ b/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart @@ -27,6 +27,7 @@ import 'package:tmail_ui_user/features/manage_account/presentation/extensions/ex import 'package:tmail_ui_user/features/manage_account/presentation/extensions/update_own_email_address_extension.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/identities/identity_bindings.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/language_and_region/language_and_region_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'; @@ -34,7 +35,6 @@ import 'package:tmail_ui_user/features/manage_account/presentation/model/manage_ import 'package:tmail_ui_user/features/manage_account/presentation/model/settings_page_level.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/notification/bindings/notification_binding.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/preferences/bindings/preferences_bindings.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/profiles_bindings.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/vacation_controller_bindings.dart'; import 'package:tmail_ui_user/main/error/capability_validator.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; @@ -196,7 +196,7 @@ class ManageAccountDashBoardController extends ReloadableController { void _bindingControllerMenuItemView(AccountMenuItem item) { switch (item) { case AccountMenuItem.profiles: - ProfileBindings().dependencies(); + IdentityBindings().dependencies(); break; case AccountMenuItem.languageAndRegion: LanguageAndRegionBindings().dependencies(); 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 3d2e6dffe..4556893ed 100644 --- a/lib/features/manage_account/presentation/manage_account_dashboard_view.dart +++ b/lib/features/manage_account/presentation/manage_account_dashboard_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/handle_profile_setting_action_type_click_extension.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/forward_view.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/identities_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/manage_account_dashboard_controller.dart'; @@ -18,7 +19,6 @@ import 'package:tmail_ui_user/features/manage_account/presentation/menu/manage_a import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings/settings_view.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/model/account_menu_item.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/preferences/preferences_view.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/profiles_view.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/vacation_view.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/widgets/vacation_notification_message_widget.dart'; @@ -113,7 +113,7 @@ class ManageAccountDashBoardView extends GetWidget { return Obx(() { switch(controller.manageAccountDashboardController.accountMenuItemSelected.value) { case AccountMenuItem.profiles: - return ProfilesView(responsiveUtils: controller.responsiveUtils); + return const IdentitiesView(); case AccountMenuItem.languageAndRegion: if (controller.manageAccountDashboardController.isLanguageSettingDisplayed) { return const LanguageAndRegionView(); 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 92383f871..2f08d8552 100644 --- a/lib/features/manage_account/presentation/model/account_menu_item.dart +++ b/lib/features/manage_account/presentation/model/account_menu_item.dart @@ -72,7 +72,7 @@ enum AccountMenuItem { String getExplanation(AppLocalizations appLocalizations) { switch(this) { case AccountMenuItem.profiles: - return appLocalizations.profilesSettingExplanation; + return appLocalizations.identitiesSettingExplanation; case AccountMenuItem.languageAndRegion: return appLocalizations.languageSubtitle; case AccountMenuItem.emailRules: diff --git a/lib/features/manage_account/presentation/profiles/identities/identities_view.dart b/lib/features/manage_account/presentation/profiles/identities/identities_view.dart deleted file mode 100644 index 7c3fc3d34..000000000 --- a/lib/features/manage_account/presentation/profiles/identities/identities_view.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; -import 'package:tmail_ui_user/features/base/mixin/popup_menu_widget_mixin.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings_utils.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identities_controller.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/widgets/identities_header_widget.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/widgets/identities_radio_list_builder.dart'; - -class IdentitiesView extends GetWidget with PopupMenuWidgetMixin, AppLoaderMixin { - - IdentitiesView({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Padding( - padding: SettingsUtils.getSettingContentPadding( - context, - controller.responsiveUtils, - ), - child: controller.responsiveUtils.isWebDesktop(context) - ? _buildIdentitiesViewWebDesktop(context) - : _buildIdentitiesViewMobile(context), - ); - } - - Widget _buildIdentitiesViewMobile(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - IdentitiesHeaderWidget( - onAddNewIdentityAction: () => controller.goToCreateNewIdentity(context), - ), - const SizedBox(height: 12), - IdentitiesRadioListBuilder( - controller: controller, - responsiveUtils: controller.responsiveUtils, - imagePaths: controller.imagePaths - ) - ], - ); - } - - Widget _buildIdentitiesViewWebDesktop(BuildContext context) { - return Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - width: 224, - child: IdentitiesHeaderWidget( - onAddNewIdentityAction: () => controller.goToCreateNewIdentity(context), - ) - ), - const SizedBox(width: 12), - Expanded(child: IdentitiesRadioListBuilder( - controller: controller, - responsiveUtils: controller.responsiveUtils, - imagePaths: controller.imagePaths - )), - ], - ); - } -} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/profiles/identities/widgets/identities_header_widget.dart b/lib/features/manage_account/presentation/profiles/identities/widgets/identities_header_widget.dart deleted file mode 100644 index ff744fd09..000000000 --- a/lib/features/manage_account/presentation/profiles/identities/widgets/identities_header_widget.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:core/presentation/extensions/color_extension.dart'; -import 'package:core/presentation/resources/image_paths.dart'; -import 'package:core/presentation/utils/theme_utils.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:tmail_ui_user/features/base/widget/material_text_icon_button.dart'; -import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; - -typedef OnAddNewIdentityAction = Function(); - -class IdentitiesHeaderWidget extends StatelessWidget { - - const IdentitiesHeaderWidget({ - Key? key, - required this.onAddNewIdentityAction, - }) : super(key: key); - - final OnAddNewIdentityAction onAddNewIdentityAction; - - @override - Widget build(BuildContext context) { - final imagePaths = Get.find(); - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - AppLocalizations.of(context).identities, - style: ThemeUtils.defaultTextStyleInterFont.copyWith( - fontSize: 17, - fontWeight: FontWeight.w500, - color: Colors.black)), - const SizedBox(height: 4), - Text( - AppLocalizations.of(context).identitiesSettingExplanation, - style: ThemeUtils.defaultTextStyleInterFont.copyWith( - fontSize: 15, - fontWeight: FontWeight.normal, - color: AppColor.colorSettingExplanation)), - const SizedBox(height: 24), - MaterialTextIconButton( - key: const Key('button_add_identity'), - label: AppLocalizations.of(context).createNewIdentity, - icon: imagePaths.icAddIdentity, - iconSize: 28, - minimumSize: const Size(double.infinity, 44), - onTap: onAddNewIdentityAction - ) - ] - ); - } -} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/profiles/identities/widgets/identities_radio_list_builder.dart b/lib/features/manage_account/presentation/profiles/identities/widgets/identities_radio_list_builder.dart deleted file mode 100644 index b0e61dd4f..000000000 --- a/lib/features/manage_account/presentation/profiles/identities/widgets/identities_radio_list_builder.dart +++ /dev/null @@ -1,152 +0,0 @@ -import 'package:core/presentation/extensions/color_extension.dart'; -import 'package:core/presentation/resources/image_paths.dart'; -import 'package:core/presentation/utils/responsive_utils.dart'; -import 'package:fading_edge_scrollview/fading_edge_scrollview.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get_state_manager/get_state_manager.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/state/transform_html_signature_state.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identities_controller.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/widgets/identity_list_tile_builder.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/widgets/identity_loading_widget.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/widgets/signature_builder.dart'; - -class IdentitiesRadioListBuilder extends StatelessWidget { - - final IdentitiesController controller; - final ResponsiveUtils responsiveUtils; - final ImagePaths imagePaths; - - const IdentitiesRadioListBuilder({ - Key? key, - required this.controller, - required this.responsiveUtils, - required this.imagePaths, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - height: responsiveUtils.isWebDesktop(context) ? 256 : null, - decoration: BoxDecoration( - border: Border.all(color: AppColor.attachmentFileBorderColor), - borderRadius: const BorderRadius.all(Radius.circular(16.0)), - color: Colors.white - ), - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(16.0)), - child: Stack( - children: [ - if (responsiveUtils.isWebDesktop(context)) - _buildIdentityViewHorizontal(context) - else - _buildIdentityViewVertical(context), - Obx(() => Align( - alignment: AlignmentDirectional.topCenter, - child: IdentityLoadingWidget( - identityViewState: controller.viewState.value, - settingViewState: controller.accountDashBoardController.viewState.value - ) - )) - ], - ) - ) - ); - } - - Widget _buildIdentityViewVertical(BuildContext context) { - return Obx(() => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if(controller.isSignatureShow) - ...[ - _buildListIdentityView(context), - Container(height: 1, color: AppColor.attachmentFileBorderColor), - Obx(() { - if (controller.isSignatureShow) { - return SignatureBuilder(controller.signatureSelected.value!); - } else { - return _buildLoadingView(); - } - }) - ] - else - _buildListIdentityView(context) - ], - )); - } - - Widget _buildIdentityViewHorizontal(BuildContext context) { - return Obx(() => Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (controller.isSignatureShow) - ...[ - _buildListIdentityView(context), - Container(width: 1, color: AppColor.attachmentFileBorderColor), - Expanded(child: Obx(() { - if (controller.signatureSelected.value != null) { - return SignatureBuilder(controller.signatureSelected.value!); - } else { - return _buildLoadingView(); - } - })) - ] - else - Expanded(child: _buildListIdentityView(context)) - ], - )); - } - - Widget _buildListIdentityView(BuildContext context) { - return Container( - key: const Key('identities_list'), - width: responsiveUtils.isWebDesktop(context) ? 320 : null, - height: 256, - padding: const EdgeInsets.only(left: 12, top: 12, bottom: 12), - child: Obx(() => FadingEdgeScrollView.fromScrollView( - gradientFractionOnStart: 0.3, - gradientFractionOnEnd: 0.3, - child: ListView.builder( - controller: ScrollController(), - padding: const EdgeInsets.only(right: 12.0), - itemCount: controller.listAllIdentities.length, - itemBuilder: ((context, index) { - return IdentityListTileBuilder( - imagePaths: imagePaths, - identity: controller.listAllIdentities[index], - identitySelected: controller.identitySelected.value, - onSelectIdentityAction: controller.selectIdentity, - onEditIdentityAction: (identitySelected) => - controller.goToEditIdentity(context, identitySelected), - onDeleteIdentityAction: (identitySelected) => - controller.openConfirmationDialogDeleteIdentityAction(context, identitySelected), - ); - }), - ), - )) - ); - } - - Widget _buildLoadingView() { - return Obx(() => controller.viewState.value.fold( - (failure) => const SizedBox.shrink(), - (success) { - if (success is TransformHtmlSignatureLoading) { - return const Align( - alignment: Alignment.topCenter, - child: Padding( - padding: EdgeInsets.all(16), - child: SizedBox( - width: 30, - height: 30, - child: CupertinoActivityIndicator(color: AppColor.colorLoading) - )) - ); - } else { - return const SizedBox.shrink(); - } - } - )); - } -} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/profiles/identities/widgets/identity_list_tile_builder.dart b/lib/features/manage_account/presentation/profiles/identities/widgets/identity_list_tile_builder.dart deleted file mode 100644 index 546c45ada..000000000 --- a/lib/features/manage_account/presentation/profiles/identities/widgets/identity_list_tile_builder.dart +++ /dev/null @@ -1,163 +0,0 @@ -import 'package:core/presentation/extensions/color_extension.dart'; -import 'package:core/presentation/resources/image_paths.dart'; -import 'package:core/presentation/utils/theme_utils.dart'; -import 'package:core/presentation/views/button/icon_button_web.dart'; -import 'package:core/presentation/views/text/text_overflow_builder.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:jmap_dart_client/jmap/identities/identity.dart'; -import 'package:model/extensions/list_email_address_extension.dart'; -import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; - -typedef OnSelectIdentityAction = Function(Identity? identitySelected); -typedef OnEditIdentityAction = Function(Identity identitySelected); -typedef OnDeleteIdentityAction = Function(Identity identitySelected); - -class IdentityListTileBuilder extends StatelessWidget { - - const IdentityListTileBuilder({ - Key? key, - required this.identity, - required this.identitySelected, - required this.imagePaths, - this.onSelectIdentityAction, - this.onEditIdentityAction, - this.onDeleteIdentityAction - }) : super(key: key); - - final Identity identity; - final Identity? identitySelected; - final ImagePaths imagePaths; - final OnSelectIdentityAction? onSelectIdentityAction; - final OnEditIdentityAction? onEditIdentityAction; - final OnDeleteIdentityAction? onDeleteIdentityAction; - - @override - Widget build(BuildContext context) { - return Material( - color: Colors.transparent, - child: Padding( - padding: const EdgeInsets.only(bottom: 2), - child: InkWell( - borderRadius: BorderRadius.circular(12.0), - onTap: () => onSelectIdentityAction?.call(identity), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12.0), - color: _isIdentitySelected - ? AppColor.colorItemSelected - : Colors.transparent, - ), - padding: const EdgeInsets.all(12), - child: Row( - children: [ - Radio( - value: identity, - splashRadius: 15, - groupValue: identitySelected, - activeColor: AppColor.primaryColor, - onChanged: onSelectIdentityAction, - ), - const SizedBox(width: 10), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only(bottom: 6), - child: TextOverflowBuilder( - (identity.name ?? ''), - style: ThemeUtils.defaultTextStyleInterFont.copyWith( - fontWeight: FontWeight.w500, - fontSize: 16, - color: Colors.black)), - ), - if (identity.email?.isNotEmpty == true) - _buildIconSVGWithTextLine(imagePaths.icEmail, identity.email), - if (identity.replyTo?.isNotEmpty == true) - _buildIconSVGWithTextLine( - imagePaths.icReplyTo, - identity.replyTo?.listEmailAddressToString(isFullEmailAddress: true) - ), - if (identity.bcc?.isNotEmpty == true) - _buildIconCharacterWithTextLine( - AppLocalizations.of(context).bcc_email_address_prefix, - identity.bcc?.listEmailAddressToString(isFullEmailAddress: true) - ), - ], - ) - ), - if(_isIdentitySelected) - ...[ - buildSVGIconButton( - icon: imagePaths.icEditRule, - iconSize: 24, - iconColor: AppColor.primaryColor, - onTap: () => onEditIdentityAction?.call(identity), - ), - buildSVGIconButton( - icon: imagePaths.icDeleteRule, - iconSize: 24, - iconColor: AppColor.colorDeletePermanentlyButton, - onTap: () => onDeleteIdentityAction?.call(identity), - ), - ] - ], - ), - ), - ), - ), - ); - } - - Widget _buildIconSVGWithTextLine(String imagePath, String? text) { - return Padding( - padding: const EdgeInsets.only(bottom: 4), - child: Row(children: [ - SizedBox( - width: 30, - child: Align( - alignment: Alignment.centerLeft, - child: SvgPicture.asset(imagePath, width: 15, height: 15))), - const SizedBox(width: 4), - Expanded(child: TextOverflowBuilder( - (text ?? ''), - style: ThemeUtils.defaultTextStyleInterFont.copyWith( - color: AppColor.colorEmailAddressFull, - fontWeight: FontWeight.normal, - fontSize: 13, - ), - )) - ]), - ); - } - - Widget _buildIconCharacterWithTextLine(String character, String? text) { - return Padding( - padding: const EdgeInsets.only(bottom: 4), - child: Row(children: [ - Container( - width: 30, - alignment: Alignment.centerLeft, - child: Text( - character, - style: ThemeUtils.defaultTextStyleInterFont.copyWith( - fontSize: 15, - fontWeight: FontWeight.normal, - decoration: TextDecoration.underline, - color: AppColor.colorTextButton))), - const SizedBox(width: 4), - Expanded(child: TextOverflowBuilder( - (text ?? ''), - style: ThemeUtils.defaultTextStyleInterFont.copyWith( - color: AppColor.colorEmailAddressFull, - fontWeight: FontWeight.normal, - fontSize: 13, - ), - )) - ]), - ); - } - - bool get _isIdentitySelected => identity.id == identitySelected?.id; -} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/profiles/identities/widgets/identity_loading_widget.dart b/lib/features/manage_account/presentation/profiles/identities/widgets/identity_loading_widget.dart deleted file mode 100644 index 062add041..000000000 --- a/lib/features/manage_account/presentation/profiles/identities/widgets/identity_loading_widget.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:dartz/dartz.dart'; -import 'package:flutter/material.dart'; -import 'package:tmail_ui_user/features/base/widget/circle_loading_widget.dart'; -import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_identities_state.dart'; - -class IdentityLoadingWidget extends StatelessWidget { - - final Either identityViewState; - final Either settingViewState; - - const IdentityLoadingWidget({ - super.key, - required this.identityViewState, - required this.settingViewState - }); - - @override - Widget build(BuildContext context) { - return settingViewState.fold( - (failure) => identityViewState.fold( - (failure) => const SizedBox.shrink(), - (success) { - if (success is GetAllIdentitiesLoading || success is GetSessionLoading) { - return const CircleLoadingWidget(margin: EdgeInsets.all(16.0)); - } else { - return const SizedBox.shrink(); - } - } - ), - (success) => identityViewState.fold( - (failure) => const SizedBox.shrink(), - (success) { - if (success is GetAllIdentitiesLoading || success is GetSessionLoading) { - return const CircleLoadingWidget(margin: EdgeInsets.all(16.0)); - } else { - return const SizedBox.shrink(); - } - } - ) - ); - } -} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/profiles/identities/widgets/signature_builder.dart b/lib/features/manage_account/presentation/profiles/identities/widgets/signature_builder.dart deleted file mode 100644 index 1cd7a3f90..000000000 --- a/lib/features/manage_account/presentation/profiles/identities/widgets/signature_builder.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:core/presentation/views/html_viewer/html_content_viewer_on_web_widget.dart'; -import 'package:core/presentation/views/html_viewer/html_content_viewer_widget.dart'; -import 'package:core/utils/platform_info.dart'; -import 'package:flutter/material.dart'; -import 'package:tmail_ui_user/main/utils/app_utils.dart'; - -class SignatureBuilder extends StatelessWidget { - - const SignatureBuilder( - this.signatureSelected, { - Key? key, - this.width, - this.height = 256 - }) : super(key: key); - - final String signatureSelected; - final double? width; - final double height; - - @override - Widget build(BuildContext context) { - return LayoutBuilder( - builder: (context, constraints) { - final signatureWidth = width ?? constraints.biggest.width; - final signatureHeight = height; - return Container( - width: signatureWidth, - color: Colors.white, - padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 16.0), - child: _buildSignature(context, signatureWidth, signatureHeight), - ); - } - ); - } - - Widget _buildSignature(BuildContext context, double width, double height) { - if (signatureSelected.isNotEmpty) { - if (PlatformInfo.isWeb) { - return HtmlContentViewerOnWeb( - contentHtml: signatureSelected, - widthContent: width, - heightContent: height, - allowResizeToDocumentSize: false, - direction: AppUtils.getCurrentDirection(context), - ); - } else { - return LayoutBuilder(builder: (context, constraints) { - return HtmlContentViewer( - contentHtml: signatureSelected, - initialWidth: constraints.maxWidth, - direction: AppUtils.getCurrentDirection(context), - ); - }); - } - } else { - return SizedBox(width: width, height: height); - } - } -} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/profiles/profiles_bindings.dart b/lib/features/manage_account/presentation/profiles/profiles_bindings.dart deleted file mode 100644 index e2b4f5477..000000000 --- a/lib/features/manage_account/presentation/profiles/profiles_bindings.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:get/get.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identity_bindings.dart'; - -class ProfileBindings extends Bindings { - - @override - void dependencies() { - IdentityBindings().dependencies(); - } -} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/profiles/profiles_view.dart b/lib/features/manage_account/presentation/profiles/profiles_view.dart deleted file mode 100644 index ddcac0ddc..000000000 --- a/lib/features/manage_account/presentation/profiles/profiles_view.dart +++ /dev/null @@ -1,44 +0,0 @@ - -import 'package:core/presentation/extensions/color_extension.dart'; -import 'package:core/presentation/utils/responsive_utils.dart'; -import 'package:flutter/material.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/base/setting_detail_view_builder.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/model/account_menu_item.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identities_view.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/widgets/setting_header_widget.dart'; - -class ProfilesView extends StatelessWidget { - - final ResponsiveUtils responsiveUtils; - - const ProfilesView({ - Key? key, - required this.responsiveUtils, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return SettingDetailViewBuilder( - responsiveUtils: responsiveUtils, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (responsiveUtils.isWebDesktop(context)) - ...[ - const SettingHeaderWidget(menuItem: AccountMenuItem.profiles), - const Divider(height: 1, color: AppColor.colorDividerHeaderSetting), - ], - Expanded(child: SingleChildScrollView( - physics: const ClampingScrollPhysics(), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - IdentitiesView() - ] - ) - )) - ] - ) - ); - } -} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/widgets/setting_explanation_widget.dart b/lib/features/manage_account/presentation/widgets/setting_explanation_widget.dart index de524a024..9a4f522f2 100644 --- a/lib/features/manage_account/presentation/widgets/setting_explanation_widget.dart +++ b/lib/features/manage_account/presentation/widgets/setting_explanation_widget.dart @@ -8,18 +8,21 @@ class SettingExplanationWidget extends StatelessWidget { final AccountMenuItem menuItem; final EdgeInsetsGeometry? padding; final bool isCenter; + final TextAlign? textAlign; const SettingExplanationWidget({ Key? key, required this.menuItem, this.isCenter = false, this.padding, + this.textAlign, }) : super(key: key); @override Widget build(BuildContext context) { Widget child = Text( menuItem.getExplanation(AppLocalizations.of(context)), + textAlign: textAlign, style: ThemeUtils.textStyleM3BodyMedium1 .copyWith(color: AppColor.gray424244.withValues(alpha: 0.64)), ); diff --git a/lib/features/manage_account/presentation/widgets/setting_header_widget.dart b/lib/features/manage_account/presentation/widgets/setting_header_widget.dart index 5c5cef9a4..6aec04950 100644 --- a/lib/features/manage_account/presentation/widgets/setting_header_widget.dart +++ b/lib/features/manage_account/presentation/widgets/setting_header_widget.dart @@ -26,6 +26,8 @@ class SettingHeaderWidget extends StatelessWidget { style: textStyle ?? ThemeUtils.textStyleInter600().copyWith( color: Colors.black, ), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), if (menuItem.getExplanation(appLocalizations).isNotEmpty) Padding( @@ -38,6 +40,8 @@ class SettingHeaderWidget extends StatelessWidget { letterSpacing: -0.15, color: AppColor.gray424244.withValues(alpha: 0.64), ), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), ), ]; @@ -46,6 +50,7 @@ class SettingHeaderWidget extends StatelessWidget { ? children.first : Column( crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: children, ); diff --git a/test/features/identity_creator/presentation/identity_creator_controller_test.dart b/test/features/identity_creator/presentation/identity_creator_controller_test.dart index 8bb4a67f9..a712dd8a8 100644 --- a/test/features/identity_creator/presentation/identity_creator_controller_test.dart +++ b/test/features/identity_creator/presentation/identity_creator_controller_test.dart @@ -40,8 +40,8 @@ import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_na import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/utils/identity_utils.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/model/identity_action_type.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/utils/identity_utils.dart'; import 'package:tmail_ui_user/features/public_asset/domain/model/public_assets_in_identity_arguments.dart'; import 'package:tmail_ui_user/features/public_asset/domain/usecase/create_public_asset_interactor.dart'; import 'package:tmail_ui_user/features/public_asset/domain/usecase/delete_public_assets_interactor.dart'; diff --git a/test/features/manage_account/identity/identity_utils_test.dart b/test/features/manage_account/identity/identity_utils_test.dart index ec7c2e8d5..343f07bfb 100644 --- a/test/features/manage_account/identity/identity_utils_test.dart +++ b/test/features/manage_account/identity/identity_utils_test.dart @@ -1,7 +1,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/utils/identity_utils.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/utils/identity_utils.dart'; void main() { final identityUtils = IdentityUtils(); diff --git a/test/features/manage_account/presentation/profiles/identities/identities_controller_test.dart b/test/features/manage_account/presentation/profiles/identities/identities_controller_test.dart index 8283b1c8c..fad87a0b0 100644 --- a/test/features/manage_account/presentation/profiles/identities/identities_controller_test.dart +++ b/test/features/manage_account/presentation/profiles/identities/identities_controller_test.dart @@ -29,9 +29,9 @@ import 'package:tmail_ui_user/features/manage_account/domain/usecases/edit_defau import 'package:tmail_ui_user/features/manage_account/domain/usecases/edit_identity_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/usecases/transform_html_signature_interactor.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/transform_list_signature_interactor.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/identities/identities_controller.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/manage_account_dashboard_controller.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identities_controller.dart'; import 'package:tmail_ui_user/features/public_asset/domain/usecase/add_identity_to_public_assets_interactor.dart'; import 'package:tmail_ui_user/features/public_asset/domain/usecase/clean_up_public_assets_interactor.dart'; import 'package:tmail_ui_user/features/public_asset/domain/usecase/delete_public_assets_interactor.dart'; @@ -80,7 +80,7 @@ const fallbackGenerators = { MockSpec(), MockSpec(), MockSpec(), - MockSpec(), + MockSpec(), MockSpec(fallbackGenerators: fallbackGenerators), MockSpec(), MockSpec(), @@ -95,7 +95,7 @@ void main() { late MockEditIdentityInteractor mockEditIdentityInteractor; late MockCreateNewDefaultIdentityInteractor mockCreateNewDefaultIdentityInteractor; late MockEditDefaultIdentityInteractor mockEditDefaultIdentityInteractor; - late MockTransformHtmlSignatureInteractor mockTransformHtmlSignatureInteractor; + late MockTransformListSignatureInteractor mockTransformListSignatureInteractor; late MockManageAccountDashBoardController mockManageAccountDashBoardController; late MockCachingManager mockCachingManager; @@ -165,7 +165,7 @@ void main() { mockEditIdentityInteractor = MockEditIdentityInteractor(); mockCreateNewDefaultIdentityInteractor = MockCreateNewDefaultIdentityInteractor(); mockEditDefaultIdentityInteractor = MockEditDefaultIdentityInteractor(); - mockTransformHtmlSignatureInteractor = MockTransformHtmlSignatureInteractor(); + mockTransformListSignatureInteractor = MockTransformListSignatureInteractor(); mockManageAccountDashBoardController = MockManageAccountDashBoardController(); Get.put(MockBeforeReconnectManager()); @@ -181,7 +181,7 @@ void main() { mockEditIdentityInteractor, mockCreateNewDefaultIdentityInteractor, mockEditDefaultIdentityInteractor, - mockTransformHtmlSignatureInteractor, + mockTransformListSignatureInteractor, mockSaveIdentityCacheOnWebInteractor); });