From 2ae09adca2c71a2505694167e2f59eb6a3a3c684 Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 4 Dec 2024 14:54:40 +0700 Subject: [PATCH 1/4] TF-2948 Apply new identity view for web --- assets/images/ic_delete_rule.svg | 6 +- .../extensions/color_extension.dart | 1 + .../transform_configuration.dart | 10 + .../html_content_viewer_on_web_widget.dart | 109 +++++--- core/lib/utils/html/html_utils.dart | 5 +- .../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 ++ .../transform_html_signature_interactor.dart | 24 -- .../transform_list_signature_interactor.dart | 37 +++ .../base/setting_detail_view_builder.dart | 30 ++- .../extensions/identity_extension.dart | 6 + .../extensions/list_identity_extension.dart | 11 + .../manage_account_dashboard_bindings.dart | 4 +- .../manage_account_dashboard_controller.dart | 4 +- .../manage_account_dashboard_view.dart | 4 +- .../menu/settings/settings_view.dart | 6 +- .../identities/identities_controller.dart | 75 ++++-- .../profiles/identities/identities_view.dart | 120 +++++---- .../identities/identity_bindings.dart | 4 +- .../identity_interactors_bindings.dart | 4 +- .../widgets/identities_header_widget.dart | 51 ---- .../identities_radio_list_builder.dart | 152 ----------- .../widgets/identity_list_tile_builder.dart | 254 ++++++++++-------- .../widgets/identity_loading_widget.dart | 39 ++- .../identities/widgets/signature_builder.dart | 65 ++--- .../widgets/signature_loading_widget.dart | 35 +++ .../profiles/profiles_bindings.dart | 10 - .../presentation/profiles/profiles_view.dart | 43 --- lib/l10n/intl_messages.arb | 10 +- lib/main/localizations/app_localizations.dart | 8 +- .../identities_controller_test.dart | 10 +- 36 files changed, 579 insertions(+), 649 deletions(-) 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 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 create mode 100644 lib/features/manage_account/presentation/profiles/identities/widgets/signature_loading_widget.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 af075917f3..8b55020edf 100644 --- a/assets/images/ic_delete_rule.svg +++ b/assets/images/ic_delete_rule.svg @@ -1,5 +1,3 @@ - - - - + + diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart index 1ca116c7f0..78119915aa 100644 --- a/core/lib/presentation/extensions/color_extension.dart +++ b/core/lib/presentation/extensions/color_extension.dart @@ -234,6 +234,7 @@ extension AppColor on Color { static const colorStarredSearchFilterIcon = Color(0xFFFFCC00); static const colorMobileSearchFilterButton = Color(0xFFEBEDF0); static const colorContactViewClearFilterButton = Color(0x001C3D0D); + static const colorDisableRadioButton = Color(0xFF86888B); static const mapGradientColor = [ [Color(0xFF21D4FD), Color(0xFFB721FF)], diff --git a/core/lib/presentation/utils/html_transformer/transform_configuration.dart b/core/lib/presentation/utils/html_transformer/transform_configuration.dart index 36cf86077a..8ae9798471 100644 --- a/core/lib/presentation/utils/html_transformer/transform_configuration.dart +++ b/core/lib/presentation/utils/html_transformer/transform_configuration.dart @@ -86,6 +86,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 c3ac17d675..c722ff6c55 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 @@ -17,6 +17,10 @@ class HtmlContentViewerOnWeb extends StatefulWidget { final double widthContent; final double heightContent; final TextDirection? direction; + final double? minWidth; + final double? maxHeight; + final double? contentPadding; + final bool adjustHeight; /// Handler for mailto: links final Function(Uri?)? mailtoDelegate; @@ -30,6 +34,10 @@ class HtmlContentViewerOnWeb extends StatefulWidget { required this.widthContent, required this.heightContent, this.allowResizeToDocumentSize = true, + this.adjustHeight = false, + this.minWidth, + this.maxHeight, + this.contentPadding, this.mailtoDelegate, this.direction, }) : super(key: key); @@ -40,7 +48,7 @@ class HtmlContentViewerOnWeb extends StatefulWidget { class _HtmlContentViewerOnWebState extends State { - static const double _minWidth = 300; + static const double _defaultMinWidth = 300; /// 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 @@ -48,6 +56,8 @@ class _HtmlContentViewerOnWebState extends State { /// The actual width of the content view, used to automatically set the width late double _actualWidth; + late double _minWidth; + Future? _webInit; String? _htmlData; bool _isLoading = true; @@ -61,6 +71,7 @@ class _HtmlContentViewerOnWebState extends State { super.initState(); _actualHeight = widget.heightContent; _actualWidth = widget.widthContent; + _minWidth = widget.minWidth ?? _defaultMinWidth; _createdViewId = _getRandString(10); _setUpWeb(); @@ -78,8 +89,9 @@ class _HtmlContentViewerOnWebState extends State { if (data['type'] != null && data['type'].contains('toDart: htmlHeight')) { final docHeight = data['height'] ?? _actualHeight; if (docHeight != null && mounted) { - final scrollHeightWithBuffer = docHeight + 30.0; - if (scrollHeightWithBuffer > minHeight) { + final bottomPadding = widget.adjustHeight ? 0 : 30.0; + final scrollHeightWithBuffer = docHeight + bottomPadding; + if (scrollHeightWithBuffer > minHeight || widget.adjustHeight) { setState(() { _actualHeight = scrollHeightWithBuffer; _isLoading = false; @@ -214,11 +226,13 @@ class _HtmlContentViewerOnWebState extends State { final htmlTemplate = HtmlUtils.generateHtmlDocument( content: content, - minHeight: minHeight, + minHeight: widget.adjustHeight ? 0 : minHeight, minWidth: _minWidth, styleCSS: HtmlTemplate.tooltipLinkCss, javaScripts: webViewActionScripts + scriptsDisableZoom + HtmlInteraction.scriptsHandleLazyLoadingBackgroundImage, - direction: widget.direction); + direction: widget.direction, + contentPadding: widget.contentPadding + ); return htmlTemplate; } @@ -246,47 +260,58 @@ class _HtmlContentViewerOnWebState extends State { @override Widget build(BuildContext context) { - return LayoutBuilder(builder: (context, constraint) { - minHeight = math.max(constraint.maxHeight, minHeight); - return Stack( - children: [ - if (_htmlData?.isNotEmpty == false) - const SizedBox.shrink() - else - FutureBuilder( - future: _webInit, - builder: (context, snapshot) { - if (snapshot.hasData) { - return SizedBox( - height: _actualHeight, - width: _actualWidth, - child: HtmlElementView( - key: ValueKey(_htmlData), - viewType: _createdViewId, - ), - ); - } else { - return const SizedBox.shrink(); - } + return Stack( + children: [ + if (_htmlData?.isNotEmpty == false) + const SizedBox.shrink() + else + FutureBuilder( + future: _webInit, + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const SizedBox.shrink(); + } + + if (widget.adjustHeight) { + return Container( + height: _actualHeight, + width: _actualWidth, + constraints: widget.maxHeight != null + ? BoxConstraints(maxHeight: widget.maxHeight!) + : null, + child: HtmlElementView( + key: ValueKey(_htmlData), + viewType: _createdViewId, + ), + ); } - ), - if (_isLoading) - const Align( - alignment: Alignment.topCenter, - child: Padding( - padding: EdgeInsets.all(16), - child: SizedBox( - width: 30, - height: 30, - child: CupertinoActivityIndicator( - color: AppColor.colorLoading - ) + + return SizedBox( + height: _actualHeight, + width: _actualWidth, + child: HtmlElementView( + key: ValueKey(_htmlData), + viewType: _createdViewId, + ), + ); + } + ), + if (_isLoading) + const Align( + alignment: Alignment.topCenter, + child: Padding( + padding: EdgeInsets.all(16), + child: SizedBox( + width: 30, + height: 30, + child: CupertinoActivityIndicator( + color: AppColor.colorLoading ) ) ) - ], - ); - }); + ) + ], + ); } @override diff --git a/core/lib/utils/html/html_utils.dart b/core/lib/utils/html/html_utils.dart index 63a212074f..4e07ea23ba 100644 --- a/core/lib/utils/html/html_utils.dart +++ b/core/lib/utils/html/html_utils.dart @@ -91,7 +91,8 @@ class HtmlUtils { String? styleCSS, String? javaScripts, bool hideScrollBar = true, - TextDirection? direction + TextDirection? direction, + double? contentPadding, }) { return ''' @@ -117,7 +118,7 @@ class HtmlUtils { ${styleCSS ?? ''} - +
$content
${javaScripts ?? ''} 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 a7fc8f3bba..a7d44d279e 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 824148373a..8587aeb523 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 dc47af63da..3a0fb2162b 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 0000000000..88bd980afe --- /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 494ec9bbe1..8dbce96c6a 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 cbb7956895..0000000000 --- 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 0000000000..5b5db1d69f --- /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/transform_html_signature_interactor.dart b/lib/features/manage_account/domain/usecases/transform_html_signature_interactor.dart deleted file mode 100644 index b4e2f80cbf..0000000000 --- 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 0000000000..eb87e55c12 --- /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/base/setting_detail_view_builder.dart b/lib/features/manage_account/presentation/base/setting_detail_view_builder.dart index 56d6770542..d65da84c03 100644 --- a/lib/features/manage_account/presentation/base/setting_detail_view_builder.dart +++ b/lib/features/manage_account/presentation/base/setting_detail_view_builder.dart @@ -20,20 +20,26 @@ class SettingDetailViewBuilder extends StatelessWidget { @override Widget build(BuildContext context) { + Widget childWidget = Container( + width: double.infinity, + height: double.infinity, + color: SettingsUtils.getContentBackgroundColor(context, responsiveUtils), + decoration: SettingsUtils.getBoxDecorationForContent(context, responsiveUtils), + margin: SettingsUtils.getMarginSettingDetailsView(context, responsiveUtils), + padding: padding, + child: child, + ); + + if (onTapGestureDetector != null) { + childWidget = GestureDetector( + onTap: onTapGestureDetector, + child: childWidget, + ); + } + return Scaffold( backgroundColor: SettingsUtils.getBackgroundColor(context, responsiveUtils), - body: GestureDetector( - onTap: onTapGestureDetector, - child: Container( - width: double.infinity, - height: double.infinity, - color: SettingsUtils.getContentBackgroundColor(context, responsiveUtils), - decoration: SettingsUtils.getBoxDecorationForContent(context, responsiveUtils), - margin: SettingsUtils.getMarginSettingDetailsView(context, responsiveUtils), - padding: padding, - child: child, - ), - ), + body: childWidget, ); } } \ 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 b06486910a..9d74f5b32e 100644 --- a/lib/features/manage_account/presentation/extensions/identity_extension.dart +++ b/lib/features/manage_account/presentation/extensions/identity_extension.dart @@ -1,5 +1,6 @@ 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 +13,9 @@ extension IdentityExtension on Identity { return ''; } } + + IdentitySignature toIdentitySignature() => IdentitySignature( + identityId: id!, + signature: signatureAsString, + ); } \ No newline at end of file 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 0000000000..e1d33e883b --- /dev/null +++ b/lib/features/manage_account/presentation/extensions/list_identity_extension.dart @@ -0,0 +1,11 @@ + +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()).toList(); + } +} \ 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 a964d83246..ad9f63522b 100644 --- a/lib/features/manage_account/presentation/manage_account_dashboard_bindings.dart +++ b/lib/features/manage_account/presentation/manage_account_dashboard_bindings.dart @@ -8,7 +8,7 @@ import 'package:tmail_ui_user/features/manage_account/domain/repository/manage_a 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/features/manage_account/presentation/profiles/identities/identity_bindings.dart'; import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart'; class ManageAccountDashBoardBindings extends BaseBindings { @@ -18,7 +18,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 ae57d913aa..a64e6b3c8c 100644 --- a/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart +++ b/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart @@ -31,7 +31,7 @@ import 'package:tmail_ui_user/features/manage_account/presentation/model/account import 'package:tmail_ui_user/features/manage_account/presentation/model/manage_account_arguments.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/model/settings_page_level.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/notification/bindings/notification_binding.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/profiles_bindings.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identity_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'; @@ -175,7 +175,7 @@ class ManageAccountDashBoardController extends ReloadableController with UserSet 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 bfe29c01ab..f653a6abe7 100644 --- a/lib/features/manage_account/presentation/manage_account_dashboard_view.dart +++ b/lib/features/manage_account/presentation/manage_account_dashboard_view.dart @@ -17,7 +17,7 @@ import 'package:tmail_ui_user/features/manage_account/presentation/manage_accoun import 'package:tmail_ui_user/features/manage_account/presentation/menu/manage_account_menu_view.dart'; 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/profiles/profiles_view.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identities_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'; @@ -108,7 +108,7 @@ class ManageAccountDashBoardView extends GetWidget { return Obx(() { switch(controller.manageAccountDashboardController.accountMenuItemSelected.value) { case AccountMenuItem.profiles: - return SafeArea( + return const SafeArea( top: false, - child: ProfilesView()); + child: IdentitiesView()); case AccountMenuItem.languageAndRegion: return const SafeArea( top: false, diff --git a/lib/features/manage_account/presentation/profiles/identities/identities_controller.dart b/lib/features/manage_account/presentation/profiles/identities/identities_controller.dart index 9d8f66de44..7a0ed8b754 100644 --- a/lib/features/manage_account/presentation/profiles/identities/identities_controller.dart +++ b/lib/features/manage_account/presentation/profiles/identities/identities_controller.dart @@ -26,20 +26,22 @@ 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/list_identity_extension.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'; @@ -66,12 +68,15 @@ 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; @@ -84,7 +89,7 @@ class IdentitiesController extends ReloadableController implements BeforeReconne this._editIdentityInteractor, this._createNewDefaultIdentityInteractor, this._editDefaultIdentityInteractor, - this._transformHtmlSignatureInteractor, + this._transformListSignatureInteractor, this._saveIdentityCacheOnWebInteractor ); @@ -98,9 +103,10 @@ class IdentitiesController extends ReloadableController implements BeforeReconne @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) { @@ -109,19 +115,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); @@ -129,6 +141,11 @@ class IdentitiesController extends ReloadableController implements BeforeReconne _deleteIdentityAction(failure.identityId); } else if (failure is GetIdentityCacheOnWebFailure) { _removeIdentityCache(); + } else if (failure is TransformListSignatureFailure) { + signatureViewState.value = Left(failure); + _syncMapIdentitySignatures(failure.identitySignatures); + } else { + super.handleFailureViewState(failure); } } @@ -159,25 +176,35 @@ class IdentitiesController extends ReloadableController implements BeforeReconne } void _handleGetAllIdentitiesSuccess(GetAllIdentitiesSuccess success) { - if (success.identities?.isNotEmpty == true) { - final newListIdentities = success.identities! - .where((identity) => identity.mayDelete == true && identity.name?.trim().isNotEmpty == true) - .toList(); - listAllIdentities.addAll(newListIdentities); - } + identitiesViewState.value = Right(success); + final newListIdentities = success.identities?.where(_validateIdentity).toList() ?? []; - if (listAllIdentities.isNotEmpty) { - selectIdentity(listAllIdentities.first); - } + if (newListIdentities.isEmpty) return; + + listAllIdentities.addAll(newListIdentities); + + identitySelected.value = listAllIdentities.first; + + _transformSignature(); } - void selectIdentity(Identity? newIdentity) { - signatureSelected.value = null; - identitySelected.value = newIdentity; + bool _validateIdentity(Identity identity) { + return identity.mayDelete == true && + identity.name?.trim().isNotEmpty == true; + } - if (newIdentity != null) { - consumeState(_transformHtmlSignatureInteractor.execute(newIdentity.signatureAsString)); - } + void _transformSignature() { + final listIdentitySignature = listAllIdentities.toListIdentitySignature(); + consumeState(_transformListSignatureInteractor.execute(listIdentitySignature)); + } + + void _syncMapIdentitySignatures(List identitySignatures) { + mapIdentitySignatures.value = Map.fromEntries( + identitySignatures.map((identitySignature) => MapEntry( + identitySignature.identityId, + identitySignature.signature, + )), + ); } void goToCreateNewIdentity(BuildContext context) async { diff --git a/lib/features/manage_account/presentation/profiles/identities/identities_view.dart b/lib/features/manage_account/presentation/profiles/identities/identities_view.dart index 476ce7f539..1784aa3d17 100644 --- a/lib/features/manage_account/presentation/profiles/identities/identities_view.dart +++ b/lib/features/manage_account/presentation/profiles/identities/identities_view.dart @@ -1,59 +1,85 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/views/button/tmail_button_widget.dart'; 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/base/setting_detail_view_builder.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'; +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/widgets/profiles_header_widget.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -class IdentitiesView extends GetWidget with PopupMenuWidgetMixin, AppLoaderMixin { +class IdentitiesView extends GetWidget { - IdentitiesView({Key? key}) : super(key: key); + const IdentitiesView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(24), - 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 - ) - ], - ); - } + return SettingDetailViewBuilder( + responsiveUtils: controller.responsiveUtils, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (controller.responsiveUtils.isWebDesktop(context)) + ...[ + ProfilesHeaderWidget( + imagePaths: controller.imagePaths, + responsiveUtils: controller.responsiveUtils, + ), + const Divider(color: AppColor.colorDivider), + ], + Obx(() => IdentityLoadingWidget( + identityViewState: controller.identitiesViewState.value, + )), + Flexible( + child: Obx(() => ListView.separated( + shrinkWrap: true, + itemCount: controller.listAllIdentities.length + 1, + padding: const EdgeInsetsDirectional.only(start: 40, end: 40, bottom: 8), + itemBuilder: (context, index) { + if (index == controller.listAllIdentities.length) { + return Divider(color: Colors.black.withOpacity(.01)); + } - 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 - )), - ], + return Obx(() => IdentityListTileBuilder( + imagePaths: controller.imagePaths, + identity: controller.listAllIdentities[index], + identitySelected: controller.identitySelected.value, + mapIdentitySignatures: controller.mapIdentitySignatures, + signatureViewState: controller.signatureViewState.value, + onEditIdentityAction: (identitySelected) => + controller.goToEditIdentity(context, identitySelected), + onDeleteIdentityAction: (identitySelected) => + controller.openConfirmationDialogDeleteIdentityAction(context, identitySelected), + )); + }, + separatorBuilder: (_, __) => + Divider(color: Colors.black.withOpacity(.08)), + )), + ), + TMailButtonWidget( + key: const Key('create_new_profile_button'), + text: AppLocalizations.of(context).createNewProfile, + icon: controller.imagePaths.icAddNewFolder, + backgroundColor: AppColor.colorTextButton, + borderRadius: 10, + margin: const EdgeInsetsDirectional.symmetric(horizontal: 55, vertical: 16), + textStyle: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: Colors.white, + fontSize: 17, + fontWeight: FontWeight.w500, + ), + padding: const EdgeInsetsDirectional.symmetric(vertical: 8, horizontal: 16), + iconSize: 24, + iconSpace: 2, + maxLines: 1, + flexibleText: true, + mainAxisSize: MainAxisSize.min, + iconColor: Colors.white, + onTapActionCallback: () => controller.goToCreateNewIdentity(context), + ), + ] + ) ); } } \ 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/profiles/identities/identity_bindings.dart index f7a2c70e5a..326b655baf 100644 --- a/lib/features/manage_account/presentation/profiles/identities/identity_bindings.dart +++ b/lib/features/manage_account/presentation/profiles/identities/identity_bindings.dart @@ -6,7 +6,7 @@ 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/domain/usecases/transform_list_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'; @@ -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/profiles/identities/identity_interactors_bindings.dart index 7e3addd7dd..73a06fb2b0 100644 --- a/lib/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart +++ b/lib/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart @@ -17,7 +17,7 @@ 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/domain/usecases/transform_list_signature_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/profiles/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'; @@ -65,7 +65,7 @@ class IdentityInteractorsBindings extends InteractorsBindings { Get.lazyPut(() => EditDefaultIdentityInteractor( Get.find(), Get.find())); - Get.lazyPut(() => TransformHtmlSignatureInteractor(Get.find())); + Get.lazyPut(() => TransformListSignatureInteractor(Get.find())); Get.lazyPut(() => SaveIdentityCacheOnWebInteractor(Get.find())); } 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 2173a705ca..0000000000 --- a/lib/features/manage_account/presentation/profiles/identities/widgets/identities_header_widget.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:core/presentation/extensions/color_extension.dart'; -import 'package:core/presentation/resources/image_paths.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: const TextStyle( - fontSize: 17, - fontWeight: FontWeight.w500, - color: Colors.black)), - const SizedBox(height: 4), - Text( - AppLocalizations.of(context).identitiesSettingExplanation, - style: const TextStyle( - 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 b0e61dd4f0..0000000000 --- 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 index 0d7183ca29..1d4a16b4b3 100644 --- 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 @@ -1,11 +1,16 @@ +import 'package:core/presentation/extensions/capitalize_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/resources/image_paths.dart'; -import 'package:core/presentation/views/button/icon_button_web.dart'; -import 'package:core/presentation/views/text/text_overflow_builder.dart'; +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:core/presentation/views/button/tmail_button_widget.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/profiles/identities/widgets/signature_builder.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/widgets/signature_loading_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; typedef OnSelectIdentityAction = Function(Identity? identitySelected); @@ -18,6 +23,8 @@ class IdentityListTileBuilder extends StatelessWidget { Key? key, required this.identity, required this.identitySelected, + required this.mapIdentitySignatures, + required this.signatureViewState, required this.imagePaths, this.onSelectIdentityAction, this.onEditIdentityAction, @@ -26,6 +33,8 @@ class IdentityListTileBuilder extends StatelessWidget { final Identity identity; final Identity? identitySelected; + final Map mapIdentitySignatures; + final Either signatureViewState; final ImagePaths imagePaths; final OnSelectIdentityAction? onSelectIdentityAction; final OnEditIdentityAction? onEditIdentityAction; @@ -33,130 +42,143 @@ class IdentityListTileBuilder extends StatelessWidget { @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( + return Padding( + padding: const EdgeInsetsDirectional.symmetric(horizontal: 8, vertical: 24), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Radio( + value: identity, + splashRadius: 18, + groupValue: identitySelected, + fillColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return AppColor.primaryColor; + } + return AppColor.colorDisableRadioButton; + }), + onChanged: onSelectIdentityAction, + ), + const SizedBox(width: 10), + SizedBox( + width: 280, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Radio( - value: identity, - splashRadius: 15, - groupValue: identitySelected, - activeColor: AppColor.primaryColor, - onChanged: onSelectIdentityAction, + Padding( + padding: const EdgeInsets.only(bottom: 3), + child: Text( + (identity.name ?? ''), + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.w500, + fontSize: 16, + color: Colors.black, + ), + ), ), - const SizedBox(width: 10), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only(bottom: 6), - child: TextOverflowBuilder( - (identity.name ?? ''), - style: const TextStyle( - fontWeight: FontWeight.w500, - fontSize: 16, - color: Colors.black)), + if (identity.email?.isNotEmpty == true) + Padding( + padding: const EdgeInsets.only(bottom: 3), + child: Text( + identity.email ?? '', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: AppColor.colorEmailAddressFull, + fontWeight: FontWeight.normal, + fontSize: 13, ), - 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), + ), + if (identity.replyTo?.isNotEmpty == true) + Padding( + padding: const EdgeInsets.only(bottom: 3), + child: Text( + '${AppLocalizations.of(context).reply_to.capitalizeFirstEach}: ${identity.replyTo?.listEmailAddressToString(isFullEmailAddress: true)}', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: AppColor.colorEmailAddressFull, + fontWeight: FontWeight.normal, + fontSize: 13, + ), + ), + ), + if (identity.bcc?.isNotEmpty == true) + Text( + '${AppLocalizations.of(context).bcc_email_address_prefix}: ${identity.bcc?.listEmailAddressToString(isFullEmailAddress: true)}', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: AppColor.colorEmailAddressFull, + fontWeight: FontWeight.normal, + fontSize: 13, + ), + ), + if (identity.signatureAsString.isNotEmpty) + ...[ + Padding( + padding: const EdgeInsets.symmetric(vertical: 3), + child: Text( + '--', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Colors.black, + fontWeight: FontWeight.normal, + fontSize: 15, + ), + ), ), - ] + SignatureLoadingWidget(signatureViewState: signatureViewState), + SignatureBuilder( + value: mapIdentitySignatures[identity.id!] ?? identity.signatureAsString, + ) + ], ], + ) + ), + TMailButtonWidget( + icon: imagePaths.icCompose, + iconSize: 20, + text: AppLocalizations.of(context).edit, + backgroundColor: Colors.transparent, + iconColor: AppColor.primaryColor, + textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.w500, + fontSize: 13, + color: AppColor.primaryColor, + ), + flexibleText: true, + minWidth: 100, + mainAxisSize: MainAxisSize.min, + maxLines: 1, + iconSpace: 4, + padding: const EdgeInsetsDirectional.symmetric( + vertical: 5, + horizontal: 8, ), + onTapActionCallback: () => onEditIdentityAction?.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: const TextStyle( - color: AppColor.colorEmailAddressFull, - fontWeight: FontWeight.normal, - fontSize: 13, + TMailButtonWidget( + icon: imagePaths.icDeleteRule, + iconSize: 20, + text: AppLocalizations.of(context).delete, + backgroundColor: Colors.transparent, + iconColor: AppColor.primaryColor, + textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.w500, + fontSize: 13, + color: AppColor.primaryColor, + ), + minWidth: 100, + flexibleText: true, + mainAxisSize: MainAxisSize.min, + maxLines: 1, + iconSpace: 4, + padding: const EdgeInsetsDirectional.symmetric( + vertical: 5, + horizontal: 8, + ), + onTapActionCallback: () => onDeleteIdentityAction?.call(identity), ), - )) - ]), + const Spacer(), + ], + ), ); } - 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: const TextStyle( - fontSize: 15, - fontWeight: FontWeight.normal, - decoration: TextDecoration.underline, - color: AppColor.colorTextButton))), - const SizedBox(width: 4), - Expanded(child: TextOverflowBuilder( - (text ?? ''), - style: const TextStyle( - 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 index 151ff60588..77899b83be 100644 --- 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 @@ -1,45 +1,36 @@ +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/material.dart'; -import 'package:tmail_ui_user/features/base/widget/circle_loading_widget.dart'; +import 'package:flutter/cupertino.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(padding: EdgeInsets.all(16.0)); - } else { - return const SizedBox.shrink(); - } + return identityViewState.fold( + (failure) => const SizedBox.shrink(), + (success) { + if (success is GetAllIdentitiesLoading || success is GetSessionLoading) { + return const Center( + child: Padding( + padding: EdgeInsets.all(16), + child: CupertinoActivityIndicator(color: AppColor.colorLoading), + ), + ); + } else { + return const SizedBox.shrink(); } - ), - (success) => identityViewState.fold( - (failure) => const SizedBox.shrink(), - (success) { - if (success is GetAllIdentitiesLoading || success is GetSessionLoading) { - return const CircleLoadingWidget(padding: 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 index 1cd7a3f904..2ed0407e2d 100644 --- a/lib/features/manage_account/presentation/profiles/identities/widgets/signature_builder.dart +++ b/lib/features/manage_account/presentation/profiles/identities/widgets/signature_builder.dart @@ -6,54 +6,41 @@ import 'package:tmail_ui_user/main/utils/app_utils.dart'; class SignatureBuilder extends StatelessWidget { - const SignatureBuilder( - this.signatureSelected, { + const SignatureBuilder({ Key? key, - this.width, - this.height = 256 + required this.value, + this.height = 150, + this.width = 280, }) : super(key: key); - final String signatureSelected; - final double? width; + final String value; + 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), - ); - } - ); - } + if (value.isEmpty) { + return const SizedBox.shrink(); + } - 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), - ); - }); - } + if (PlatformInfo.isWeb) { + return HtmlContentViewerOnWeb( + contentHtml: value, + widthContent: width, + heightContent: height, + contentPadding: 0, + maxHeight: height, + minWidth: height, + allowResizeToDocumentSize: false, + adjustHeight: true, + direction: AppUtils.getCurrentDirection(context), + ); } else { - return SizedBox(width: width, height: height); + return HtmlContentViewer( + contentHtml: value, + initialWidth: width, + direction: AppUtils.getCurrentDirection(context), + ); } } } \ No newline at end of file diff --git a/lib/features/manage_account/presentation/profiles/identities/widgets/signature_loading_widget.dart b/lib/features/manage_account/presentation/profiles/identities/widgets/signature_loading_widget.dart new file mode 100644 index 0000000000..57cd6b08a7 --- /dev/null +++ b/lib/features/manage_account/presentation/profiles/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/profiles/profiles_bindings.dart b/lib/features/manage_account/presentation/profiles/profiles_bindings.dart deleted file mode 100644 index e2b4f5477e..0000000000 --- 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 b743916ca1..0000000000 --- a/lib/features/manage_account/presentation/profiles/profiles_view.dart +++ /dev/null @@ -1,43 +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: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/profiles/identities/identities_view.dart'; -import 'package:tmail_ui_user/features/manage_account/presentation/profiles/widgets/profiles_header_widget.dart'; - -class ProfilesView extends StatelessWidget { - - final _responsiveUtils = Get.find(); - final _imagePaths = Get.find(); - - ProfilesView({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SettingDetailViewBuilder( - responsiveUtils: _responsiveUtils, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (_responsiveUtils.isWebDesktop(context)) - ...[ - ProfilesHeaderWidget(imagePaths: _imagePaths, responsiveUtils: _responsiveUtils), - Container(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/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 74f755910b..9dd177e2a6 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2024-11-25T10:57:37.546143", + "@@last_modified": "2024-12-04T13:48:34.001337", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -1206,7 +1206,7 @@ "placeholders_order": [], "placeholders": {} }, - "profilesSettingExplanation": "Info about you, and options to manage it.", + "profilesSettingExplanation": "Create the multiply profiles, signatures and forwarding rules for your email", "@profilesSettingExplanation": { "type": "text", "placeholders_order": [], @@ -4135,5 +4135,11 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "createNewProfile": "Create new profile", + "@createNewProfile": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index 44f25ca989..123690fe94 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -1240,7 +1240,7 @@ class AppLocalizations { String get profilesSettingExplanation { return Intl.message( - 'Info about you, and options to manage it.', + 'Create the multiply profiles, signatures and forwarding rules for your email', name: 'profilesSettingExplanation' ); } @@ -4345,4 +4345,10 @@ class AppLocalizations { name: 'createTwakeIdFailed', ); } + + String get createNewProfile { + return Intl.message( + 'Create new profile', + name: 'createNewProfile'); + } } 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 d185c0c3e7..debc23b2e2 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,7 +29,7 @@ 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/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'; @@ -78,7 +78,7 @@ const fallbackGenerators = { MockSpec(), MockSpec(), MockSpec(), - MockSpec(), + MockSpec(), MockSpec(fallbackGenerators: fallbackGenerators), MockSpec(), MockSpec(), @@ -93,7 +93,7 @@ void main() { late MockEditIdentityInteractor mockEditIdentityInteractor; late MockCreateNewDefaultIdentityInteractor mockCreateNewDefaultIdentityInteractor; late MockEditDefaultIdentityInteractor mockEditDefaultIdentityInteractor; - late MockTransformHtmlSignatureInteractor mockTransformHtmlSignatureInteractor; + late MockTransformListSignatureInteractor mockTransformListSignatureInteractor; late MockManageAccountDashBoardController mockManageAccountDashBoardController; late MockCachingManager mockCachingManager; @@ -160,7 +160,7 @@ void main() { mockEditIdentityInteractor = MockEditIdentityInteractor(); mockCreateNewDefaultIdentityInteractor = MockCreateNewDefaultIdentityInteractor(); mockEditDefaultIdentityInteractor = MockEditDefaultIdentityInteractor(); - mockTransformHtmlSignatureInteractor = MockTransformHtmlSignatureInteractor(); + mockTransformListSignatureInteractor = MockTransformListSignatureInteractor(); mockManageAccountDashBoardController = MockManageAccountDashBoardController(); Get.put(MockBeforeReconnectManager()); @@ -176,7 +176,7 @@ void main() { mockEditIdentityInteractor, mockCreateNewDefaultIdentityInteractor, mockEditDefaultIdentityInteractor, - mockTransformHtmlSignatureInteractor, + mockTransformListSignatureInteractor, mockSaveIdentityCacheOnWebInteractor); }); From 1654db143392edddc4fd0c8531a7d451e0190047 Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 4 Dec 2024 16:00:54 +0700 Subject: [PATCH 2/4] TF-2948 Apply new identity view for mobile --- .../html_content_viewer_widget.dart | 18 +- .../profiles/identities/identities_view.dart | 89 +++++-- .../identities/styles/identities_style.dart | 22 ++ .../widgets/identity_list_tile_builder.dart | 218 ++++++++++-------- .../identities/widgets/signature_builder.dart | 2 + 5 files changed, 225 insertions(+), 124 deletions(-) create mode 100644 lib/features/manage_account/presentation/profiles/identities/styles/identities_style.dart diff --git a/core/lib/presentation/views/html_viewer/html_content_viewer_widget.dart b/core/lib/presentation/views/html_viewer/html_content_viewer_widget.dart index 964a841495..eac3fcb179 100644 --- a/core/lib/presentation/views/html_viewer/html_content_viewer_widget.dart +++ b/core/lib/presentation/views/html_viewer/html_content_viewer_widget.dart @@ -6,9 +6,9 @@ import 'package:core/utils/app_logger.dart'; import 'package:core/utils/html/html_interaction.dart'; import 'package:core/utils/html/html_utils.dart'; import 'package:core/utils/platform_info.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:url_launcher/url_launcher.dart' as launcher; import 'package:url_launcher/url_launcher_string.dart'; @@ -22,6 +22,8 @@ class HtmlContentViewer extends StatefulWidget { final String contentHtml; final double? initialWidth; final TextDirection? direction; + final double? contentPadding; + final bool adjustHeight; final OnLoadWidthHtmlViewerAction? onLoadWidthHtmlViewer; final OnMailtoDelegateAction? onMailtoDelegateAction; @@ -30,8 +32,10 @@ class HtmlContentViewer extends StatefulWidget { const HtmlContentViewer({ Key? key, required this.contentHtml, + this.adjustHeight = false, this.initialWidth, this.direction, + this.contentPadding, this.onLoadWidthHtmlViewer, this.onMailtoDelegateAction, this.onScrollHorizontalEnd @@ -44,12 +48,13 @@ class HtmlContentViewer extends StatefulWidget { class _HtmlContentViewState extends State { static const double _minHeight = 100.0; - static const double _offsetHeight = 30.0; + static const double _defaultOffsetHeight = 30.0; late InAppWebViewController _webViewController; late double _actualHeight; late Set> _gestureRecognizers; late String _customScripts; + late double _offsetHeight; final _loadingBarNotifier = ValueNotifier(true); @@ -63,6 +68,7 @@ class _HtmlContentViewState extends State { @override void initState() { super.initState(); + _offsetHeight = widget.adjustHeight ? 0 : _defaultOffsetHeight; if (PlatformInfo.isAndroid) { _gestureRecognizers = { Factory(() => LongPressGestureRecognizer()), @@ -96,12 +102,14 @@ class _HtmlContentViewState extends State { _htmlData = HtmlUtils.generateHtmlDocument( content: widget.contentHtml, direction: widget.direction, - javaScripts: _customScripts + javaScripts: _customScripts, + contentPadding: widget.contentPadding, ); } @override Widget build(BuildContext context) { + log('_HtmlContentViewState::build:_actualHeight = $_actualHeight'); return Stack(children: [ if (_htmlData == null) const SizedBox.shrink() @@ -165,7 +173,7 @@ class _HtmlContentViewState extends State { ) async { final maxContentHeight = math.max(oldContentSize.height, newContentSize.height); log('_HtmlContentViewState::_onContentSizeChanged:maxContentHeight: $maxContentHeight'); - if (maxContentHeight > _actualHeight && !_loadingBarNotifier.value && mounted) { + if ((maxContentHeight > _actualHeight || widget.adjustHeight) && !_loadingBarNotifier.value && mounted) { log('_HtmlContentViewState::_onContentSizeChanged:HEIGHT_UPDATED: $maxContentHeight'); setState(() { _actualHeight = maxContentHeight + _offsetHeight; @@ -186,7 +194,7 @@ class _HtmlContentViewState extends State { void _onHandleContentSizeChangedEvent(List parameters) async { final maxContentHeight = await _webViewController.evaluateJavascript(source: 'document.body.scrollHeight'); log('_HtmlContentViewState::_onHandleContentSizeChangedEvent:maxContentHeight: $maxContentHeight'); - if (maxContentHeight is num && maxContentHeight > _actualHeight && !_loadingBarNotifier.value && mounted) { + if (maxContentHeight is num && (maxContentHeight > _actualHeight || widget.adjustHeight) && !_loadingBarNotifier.value && mounted) { log('_HtmlContentViewState::_onHandleContentSizeChangedEvent:HEIGHT_UPDATED: $maxContentHeight'); setState(() { _actualHeight = maxContentHeight + _offsetHeight; diff --git a/lib/features/manage_account/presentation/profiles/identities/identities_view.dart b/lib/features/manage_account/presentation/profiles/identities/identities_view.dart index 1784aa3d17..b20cb8043e 100644 --- a/lib/features/manage_account/presentation/profiles/identities/identities_view.dart +++ b/lib/features/manage_account/presentation/profiles/identities/identities_view.dart @@ -4,6 +4,7 @@ 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/profiles/identities/identities_controller.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/styles/identities_style.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/widgets/profiles_header_widget.dart'; @@ -27,7 +28,49 @@ class IdentitiesView extends GetWidget { responsiveUtils: controller.responsiveUtils, ), const Divider(color: AppColor.colorDivider), - ], + ] + else + Padding( + padding: IdentitiesStyle.getPadding(context, controller.responsiveUtils), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsetsDirectional.only(top: 24), + child: Text( + AppLocalizations.of(context).profilesSettingExplanation, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + fontSize: 13, + fontWeight: FontWeight.w400, + color: AppColor.colorSettingExplanation, + ), + ), + ), + TMailButtonWidget( + key: const Key('create_new_profile_button'), + text: AppLocalizations.of(context).createNewProfile, + icon: controller.imagePaths.icAddNewFolder, + backgroundColor: AppColor.colorTextButton, + borderRadius: 10, + margin: const EdgeInsetsDirectional.symmetric(vertical: 24), + textStyle: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: Colors.white, + fontSize: 17, + fontWeight: FontWeight.w500, + ), + padding: const EdgeInsetsDirectional.symmetric(vertical: 8), + iconSize: 24, + iconSpace: 2, + maxLines: 1, + flexibleText: true, + iconColor: Colors.white, + onTapActionCallback: () => controller.goToCreateNewIdentity(context), + ), + Divider(color: Colors.black.withOpacity(.08)), + ], + ), + ), Obx(() => IdentityLoadingWidget( identityViewState: controller.identitiesViewState.value, )), @@ -35,7 +78,7 @@ class IdentitiesView extends GetWidget { child: Obx(() => ListView.separated( shrinkWrap: true, itemCount: controller.listAllIdentities.length + 1, - padding: const EdgeInsetsDirectional.only(start: 40, end: 40, bottom: 8), + padding: IdentitiesStyle.getListViewPadding(context, controller.responsiveUtils), itemBuilder: (context, index) { if (index == controller.listAllIdentities.length) { return Divider(color: Colors.black.withOpacity(.01)); @@ -43,6 +86,7 @@ class IdentitiesView extends GetWidget { return Obx(() => IdentityListTileBuilder( imagePaths: controller.imagePaths, + responsiveUtils: controller.responsiveUtils, identity: controller.listAllIdentities[index], identitySelected: controller.identitySelected.value, mapIdentitySignatures: controller.mapIdentitySignatures, @@ -57,27 +101,28 @@ class IdentitiesView extends GetWidget { Divider(color: Colors.black.withOpacity(.08)), )), ), - TMailButtonWidget( - key: const Key('create_new_profile_button'), - text: AppLocalizations.of(context).createNewProfile, - icon: controller.imagePaths.icAddNewFolder, - backgroundColor: AppColor.colorTextButton, - borderRadius: 10, - margin: const EdgeInsetsDirectional.symmetric(horizontal: 55, vertical: 16), - textStyle: Theme.of(context).textTheme.bodyLarge?.copyWith( - color: Colors.white, - fontSize: 17, - fontWeight: FontWeight.w500, + if (controller.responsiveUtils.isWebDesktop(context)) + TMailButtonWidget( + key: const Key('create_new_profile_button'), + text: AppLocalizations.of(context).createNewProfile, + icon: controller.imagePaths.icAddNewFolder, + backgroundColor: AppColor.colorTextButton, + borderRadius: 10, + margin: const EdgeInsetsDirectional.symmetric(horizontal: 55, vertical: 16), + textStyle: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: Colors.white, + fontSize: 17, + fontWeight: FontWeight.w500, + ), + padding: const EdgeInsetsDirectional.symmetric(vertical: 8, horizontal: 16), + iconSize: 24, + iconSpace: 2, + maxLines: 1, + flexibleText: true, + mainAxisSize: MainAxisSize.min, + iconColor: Colors.white, + onTapActionCallback: () => controller.goToCreateNewIdentity(context), ), - padding: const EdgeInsetsDirectional.symmetric(vertical: 8, horizontal: 16), - iconSize: 24, - iconSpace: 2, - maxLines: 1, - flexibleText: true, - mainAxisSize: MainAxisSize.min, - iconColor: Colors.white, - onTapActionCallback: () => controller.goToCreateNewIdentity(context), - ), ] ) ); diff --git a/lib/features/manage_account/presentation/profiles/identities/styles/identities_style.dart b/lib/features/manage_account/presentation/profiles/identities/styles/identities_style.dart new file mode 100644 index 0000000000..1c8a778abb --- /dev/null +++ b/lib/features/manage_account/presentation/profiles/identities/styles/identities_style.dart @@ -0,0 +1,22 @@ +import 'package:core/presentation/utils/responsive_utils.dart'; +import 'package:flutter/material.dart'; + +class IdentitiesStyle { + static EdgeInsetsGeometry getPadding(BuildContext context, ResponsiveUtils responsiveUtils) { + if (responsiveUtils.isMobile(context) || responsiveUtils.isLandscapeMobile(context)) { + return const EdgeInsets.symmetric(horizontal: 24); + } else { + return const EdgeInsets.symmetric(horizontal: 32); + } + } + + static EdgeInsetsGeometry getListViewPadding(BuildContext context, ResponsiveUtils responsiveUtils) { + if (responsiveUtils.isWebDesktop(context)) { + return const EdgeInsetsDirectional.only(start: 40, end: 40, bottom: 8); + } else if (responsiveUtils.isMobile(context) || responsiveUtils.isLandscapeMobile(context)) { + return const EdgeInsets.symmetric(horizontal: 16); + } else { + return const EdgeInsets.symmetric(horizontal: 24); + } + } +} \ 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 index 1d4a16b4b3..afc0e68aa1 100644 --- 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 @@ -3,6 +3,7 @@ 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/responsive_utils.dart'; import 'package:core/presentation/views/button/tmail_button_widget.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; @@ -26,6 +27,7 @@ class IdentityListTileBuilder extends StatelessWidget { required this.mapIdentitySignatures, required this.signatureViewState, required this.imagePaths, + required this.responsiveUtils, this.onSelectIdentityAction, this.onEditIdentityAction, this.onDeleteIdentityAction @@ -36,20 +38,92 @@ class IdentityListTileBuilder extends StatelessWidget { final Map mapIdentitySignatures; final Either signatureViewState; final ImagePaths imagePaths; + final ResponsiveUtils responsiveUtils; final OnSelectIdentityAction? onSelectIdentityAction; final OnEditIdentityAction? onEditIdentityAction; final OnDeleteIdentityAction? onDeleteIdentityAction; @override Widget build(BuildContext context) { + final listIdentitiesWidget = Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 3), + child: Text( + (identity.name ?? ''), + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.w500, + fontSize: 16, + color: Colors.black, + ), + ), + ), + if (identity.email?.isNotEmpty == true) + Padding( + padding: const EdgeInsets.only(bottom: 3), + child: Text( + identity.email ?? '', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: AppColor.colorEmailAddressFull, + fontWeight: FontWeight.normal, + fontSize: 13, + ), + ), + ), + if (identity.replyTo?.isNotEmpty == true) + Padding( + padding: const EdgeInsets.only(bottom: 3), + child: Text( + '${AppLocalizations.of(context).reply_to.capitalizeFirstEach}: ${identity.replyTo?.listEmailAddressToString(isFullEmailAddress: true)}', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: AppColor.colorEmailAddressFull, + fontWeight: FontWeight.normal, + fontSize: 13, + ), + ), + ), + if (identity.bcc?.isNotEmpty == true) + Text( + '${AppLocalizations.of(context).bcc_email_address_prefix}: ${identity.bcc?.listEmailAddressToString(isFullEmailAddress: true)}', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: AppColor.colorEmailAddressFull, + fontWeight: FontWeight.normal, + fontSize: 13, + ), + ), + if (identity.signatureAsString.isNotEmpty) + ...[ + Padding( + padding: const EdgeInsets.symmetric(vertical: 3), + child: Text( + '--', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Colors.black, + fontWeight: FontWeight.normal, + fontSize: 15, + ), + ), + ), + SignatureLoadingWidget(signatureViewState: signatureViewState), + SignatureBuilder( + value: mapIdentitySignatures[identity.id!] ?? identity.signatureAsString, + width: responsiveUtils.isWebDesktop(context) ? 280 : double.infinity, + ) + ], + ], + ); + return Padding( - padding: const EdgeInsetsDirectional.symmetric(horizontal: 8, vertical: 24), + padding: responsiveUtils.isWebDesktop(context) + ? const EdgeInsetsDirectional.symmetric(horizontal: 8, vertical: 24) + : const EdgeInsetsDirectional.symmetric(vertical: 24), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Radio( value: identity, - splashRadius: 18, + splashRadius: 24, groupValue: identitySelected, fillColor: WidgetStateProperty.resolveWith((states) { if (states.contains(WidgetState.selected)) { @@ -59,122 +133,72 @@ class IdentityListTileBuilder extends StatelessWidget { }), onChanged: onSelectIdentityAction, ), - const SizedBox(width: 10), - SizedBox( - width: 280, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only(bottom: 3), - child: Text( - (identity.name ?? ''), - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w500, - fontSize: 16, - color: Colors.black, - ), - ), - ), - if (identity.email?.isNotEmpty == true) - Padding( - padding: const EdgeInsets.only(bottom: 3), - child: Text( - identity.email ?? '', - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: AppColor.colorEmailAddressFull, - fontWeight: FontWeight.normal, - fontSize: 13, - ), - ), - ), - if (identity.replyTo?.isNotEmpty == true) - Padding( - padding: const EdgeInsets.only(bottom: 3), - child: Text( - '${AppLocalizations.of(context).reply_to.capitalizeFirstEach}: ${identity.replyTo?.listEmailAddressToString(isFullEmailAddress: true)}', - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: AppColor.colorEmailAddressFull, - fontWeight: FontWeight.normal, - fontSize: 13, - ), - ), - ), - if (identity.bcc?.isNotEmpty == true) - Text( - '${AppLocalizations.of(context).bcc_email_address_prefix}: ${identity.bcc?.listEmailAddressToString(isFullEmailAddress: true)}', - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: AppColor.colorEmailAddressFull, - fontWeight: FontWeight.normal, - fontSize: 13, - ), - ), - if (identity.signatureAsString.isNotEmpty) - ...[ - Padding( - padding: const EdgeInsets.symmetric(vertical: 3), - child: Text( - '--', - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Colors.black, - fontWeight: FontWeight.normal, - fontSize: 15, - ), - ), - ), - SignatureLoadingWidget(signatureViewState: signatureViewState), - SignatureBuilder( - value: mapIdentitySignatures[identity.id!] ?? identity.signatureAsString, - ) - ], - ], + const SizedBox(width: 5), + if (responsiveUtils.isWebDesktop(context)) + SizedBox( + width: 280, + child: listIdentitiesWidget, ) - ), + else + Expanded(child: listIdentitiesWidget), TMailButtonWidget( icon: imagePaths.icCompose, iconSize: 20, - text: AppLocalizations.of(context).edit, + text: responsiveUtils.isWebDesktop(context) + ? AppLocalizations.of(context).edit + : '', backgroundColor: Colors.transparent, iconColor: AppColor.primaryColor, - textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w500, - fontSize: 13, - color: AppColor.primaryColor, - ), - flexibleText: true, - minWidth: 100, + textStyle: responsiveUtils.isWebDesktop(context) + ? Theme.of(context).textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.w500, + fontSize: 13, + color: AppColor.primaryColor, + ) + : null, + flexibleText: responsiveUtils.isWebDesktop(context), + minWidth: responsiveUtils.isWebDesktop(context) ? 100 : 0, mainAxisSize: MainAxisSize.min, maxLines: 1, iconSpace: 4, - padding: const EdgeInsetsDirectional.symmetric( - vertical: 5, - horizontal: 8, - ), + padding: responsiveUtils.isWebDesktop(context) + ? const EdgeInsetsDirectional.symmetric( + vertical: 5, + horizontal: 8, + ) + : null, onTapActionCallback: () => onEditIdentityAction?.call(identity), ), TMailButtonWidget( icon: imagePaths.icDeleteRule, iconSize: 20, - text: AppLocalizations.of(context).delete, + text: responsiveUtils.isWebDesktop(context) + ? AppLocalizations.of(context).delete + : '', backgroundColor: Colors.transparent, iconColor: AppColor.primaryColor, - textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w500, - fontSize: 13, - color: AppColor.primaryColor, - ), - minWidth: 100, - flexibleText: true, + textStyle: responsiveUtils.isWebDesktop(context) + ? Theme.of(context).textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.w500, + fontSize: 13, + color: AppColor.primaryColor, + ) + : null, + flexibleText: responsiveUtils.isWebDesktop(context), + minWidth: responsiveUtils.isWebDesktop(context) ? 100 : 0, mainAxisSize: MainAxisSize.min, maxLines: 1, iconSpace: 4, - padding: const EdgeInsetsDirectional.symmetric( - vertical: 5, - horizontal: 8, - ), + padding: responsiveUtils.isWebDesktop(context) + ? const EdgeInsetsDirectional.symmetric( + vertical: 5, + horizontal: 8, + ) + : null, onTapActionCallback: () => onDeleteIdentityAction?.call(identity), ), - const Spacer(), + if (responsiveUtils.isWebDesktop(context)) + const Spacer(), ], ), ); 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 index 2ed0407e2d..514d74ae2d 100644 --- a/lib/features/manage_account/presentation/profiles/identities/widgets/signature_builder.dart +++ b/lib/features/manage_account/presentation/profiles/identities/widgets/signature_builder.dart @@ -39,6 +39,8 @@ class SignatureBuilder extends StatelessWidget { return HtmlContentViewer( contentHtml: value, initialWidth: width, + adjustHeight: true, + contentPadding: 0, direction: AppUtils.getCurrentDirection(context), ); } From 4bc211c7d46c15cc3c15126106d16ab775c410d0 Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 4 Dec 2024 16:32:30 +0700 Subject: [PATCH 3/4] TF-2948 Set identity as default --- .../presentation/identity_creator_view.dart | 14 +++++++++---- .../identities/identities_controller.dart | 20 ++++++++++++++++++- .../profiles/identities/identities_view.dart | 1 + .../widgets/identity_list_tile_builder.dart | 3 ++- model/lib/extensions/identity_extension.dart | 5 +++-- 5 files changed, 35 insertions(+), 8 deletions(-) diff --git a/lib/features/identity_creator/presentation/identity_creator_view.dart b/lib/features/identity_creator/presentation/identity_creator_view.dart index 2d633c0e0d..f9c8ac6ec7 100644 --- a/lib/features/identity_creator/presentation/identity_creator_view.dart +++ b/lib/features/identity_creator/presentation/identity_creator_view.dart @@ -472,10 +472,16 @@ class IdentityCreatorView extends GetWidget ), ] ), - _buildHtmlEditorWeb( - context, - controller.contentHtmlEditor, - maxWidth), + ConstrainedBox( + constraints: BoxConstraints( + maxWidth: maxWidth, + maxHeight: 300, + ), + child: _buildHtmlEditorWeb( + context, + controller.contentHtmlEditor, + maxWidth), + ), ], ); } else { diff --git a/lib/features/manage_account/presentation/profiles/identities/identities_controller.dart b/lib/features/manage_account/presentation/profiles/identities/identities_controller.dart index 7a0ed8b754..8a638b7e9a 100644 --- a/lib/features/manage_account/presentation/profiles/identities/identities_controller.dart +++ b/lib/features/manage_account/presentation/profiles/identities/identities_controller.dart @@ -12,7 +12,9 @@ import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart'; +import 'package:model/extensions/identity_extension.dart'; import 'package:model/extensions/identity_request_dto_extension.dart'; import 'package:tmail_ui_user/features/base/before_reconnect_handler.dart'; import 'package:tmail_ui_user/features/base/before_reconnect_manager.dart'; @@ -72,7 +74,6 @@ class IdentitiesController extends ReloadableController implements BeforeReconne final SaveIdentityCacheOnWebInteractor _saveIdentityCacheOnWebInteractor; final identitySelected = Rxn(); - final signatureSelected = Rxn(); final listAllIdentities = [].obs; final mapIdentitySignatures = {}.obs; final identitiesViewState = Rx>(Right(UIState.idle)); @@ -540,6 +541,23 @@ class IdentitiesController extends ReloadableController implements BeforeReconne )); } + void setIdentityAsDefault(Identity? identity) { + final accountId = accountDashBoardController.accountId.value; + final session = accountDashBoardController.sessionCurrent; + + if (identity == null || accountId == null || session == null) return; + + identitySelected.value = identity; + + final editIdentityRequest = EditIdentityRequest( + identityId: identity.id!, + identityRequest: identity.toIdentityRequest(sortOrder: UnsignedInt(0)), + isDefaultIdentity: true, + ); + + _editIdentityAction(session, accountId, editIdentityRequest); + } + @override Future onBeforeReconnect() => _saveIdentityCacheOnWebAction(newIdentityArguments); diff --git a/lib/features/manage_account/presentation/profiles/identities/identities_view.dart b/lib/features/manage_account/presentation/profiles/identities/identities_view.dart index b20cb8043e..976638cae7 100644 --- a/lib/features/manage_account/presentation/profiles/identities/identities_view.dart +++ b/lib/features/manage_account/presentation/profiles/identities/identities_view.dart @@ -91,6 +91,7 @@ class IdentitiesView extends GetWidget { identitySelected: controller.identitySelected.value, mapIdentitySignatures: controller.mapIdentitySignatures, signatureViewState: controller.signatureViewState.value, + onSelectIdentityAction: controller.setIdentityAsDefault, onEditIdentityAction: (identitySelected) => controller.goToEditIdentity(context, identitySelected), onDeleteIdentityAction: (identitySelected) => 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 index afc0e68aa1..35699ba625 100644 --- 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 @@ -5,6 +5,7 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:core/presentation/views/button/tmail_button_widget.dart'; +import 'package:core/utils/platform_info.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart'; @@ -123,7 +124,7 @@ class IdentityListTileBuilder extends StatelessWidget { children: [ Radio( value: identity, - splashRadius: 24, + splashRadius: PlatformInfo.isWeb ? 18 : 24, groupValue: identitySelected, fillColor: WidgetStateProperty.resolveWith((states) { if (states.contains(WidgetState.selected)) { diff --git a/model/lib/extensions/identity_extension.dart b/model/lib/extensions/identity_extension.dart index 983654f5c0..29bfaaca11 100644 --- a/model/lib/extensions/identity_extension.dart +++ b/model/lib/extensions/identity_extension.dart @@ -1,4 +1,5 @@ +import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:model/identity/identity_request_dto.dart'; @@ -6,12 +7,12 @@ import 'package:model/identity/identity_request_dto.dart'; extension IdentityExtension on Identity { EmailAddress toEmailAddressNoName() => EmailAddress(null, email); - IdentityRequestDto toIdentityRequest() => IdentityRequestDto( + IdentityRequestDto toIdentityRequest({UnsignedInt? sortOrder}) => IdentityRequestDto( name: name, replyTo: replyTo, bcc: bcc, textSignature: textSignature, htmlSignature: htmlSignature, - sortOrder: sortOrder + sortOrder: sortOrder ?? this.sortOrder ); } \ No newline at end of file From eaa8fd53d19591aad57aeeef989de2806e6b9814 Mon Sep 17 00:00:00 2001 From: dab246 Date: Thu, 5 Dec 2024 10:30:37 +0700 Subject: [PATCH 4/4] fixup! TF-2948 Set identity as default --- .../identities_controller_test.dart | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) 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 debc23b2e2..e2e76be46b 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 @@ -230,10 +230,25 @@ void main() { 'should only show identities with name not empty', () async { // arrange - final identity1 = Identity(name: '', mayDelete: true); - final identity2 = Identity(mayDelete: true); - final identity3 = Identity(name: 'valid name', mayDelete: true); - final identity4 = Identity(name: ' ', mayDelete: true); + final identity1 = Identity( + id: IdentityId(Id('identity1')), + name: '', + mayDelete: true, + ); + final identity2 = Identity( + id: IdentityId(Id('identity2')), + mayDelete: true, + ); + final identity3 = Identity( + id: IdentityId(Id('identity3')), + name: 'valid name', + mayDelete: true, + ); + final identity4 = Identity( + id: IdentityId(Id('identity4')), + name: ' ', + mayDelete: true, + ); when(mockGetAllIdentitiesInteractor.execute(any, any, properties: anyNamed('properties'))) .thenAnswer((_) => Stream.value(Right(GetAllIdentitiesSuccess( [identity1, identity2, identity3, identity4],