diff --git a/assets/images/ic_delete_outline.svg b/assets/images/ic_delete_outline.svg
new file mode 100644
index 000000000..214e3a8d8
--- /dev/null
+++ b/assets/images/ic_delete_outline.svg
@@ -0,0 +1,10 @@
+
diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart
index 3756610af..d22cddd97 100644
--- a/core/lib/presentation/extensions/color_extension.dart
+++ b/core/lib/presentation/extensions/color_extension.dart
@@ -73,6 +73,7 @@ extension AppColor on Color {
static const colorTextButton = Color(0xFF007AFF);
static const colorHintSearchBar = Color(0xFF818C99);
static const colorBgSearchBar = Color(0x99EBEDF0);
+ static const colorBgIdentityButton = Color(0x00EBEDF0);
static const colorShadowBgContentEmail = Color(0x14000000);
static const colorDividerMailbox = Color(0xFF99A2AD);
static const colorCollapseMailbox = Color(0xFFB8C1CC);
@@ -167,6 +168,7 @@ extension AppColor on Color {
static const colorTitleQuotasWarning = Color(0xFFF05C44);
static const colorProgressQuotasWarning = Color(0xFFFFA000);
static const colorOutOfStorageQuotasWarning = Color(0xffE64646);
+ static const colorThumbScrollBar = Color(0xFFAEB7C2);
static const mapGradientColor = [
[Color(0xFF21D4FD), Color(0xFFB721FF)],
diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart
index de00cbe38..7d9e3d0ee 100644
--- a/core/lib/presentation/resources/image_paths.dart
+++ b/core/lib/presentation/resources/image_paths.dart
@@ -26,6 +26,7 @@ class ImagePaths {
String get icSelected => _getImagePath('ic_selected.svg');
String get icUnSelected => _getImagePath('ic_unselected.svg');
String get icDelete => _getImagePath('ic_delete.svg');
+ String get icDeleteOutline => _getImagePath('ic_delete_outline.svg');
String get icSpam => _getImagePath('ic_spam.svg');
String get icMove => _getImagePath('ic_move.svg');
String get icReadToast => _getImagePath('ic_read_toast.svg');
diff --git a/core/lib/presentation/utils/theme_utils.dart b/core/lib/presentation/utils/theme_utils.dart
index 44ab20474..843d1d14a 100644
--- a/core/lib/presentation/utils/theme_utils.dart
+++ b/core/lib/presentation/utils/theme_utils.dart
@@ -12,6 +12,10 @@ class ThemeUtils {
appBarTheme: _appBarTheme,
textTheme: _textTheme,
visualDensity: VisualDensity.adaptivePlatformDensity,
+ scrollbarTheme: ScrollbarThemeData(
+ thickness: MaterialStateProperty.all(2.0),
+ radius: const Radius.circular(5.0),
+ thumbColor: MaterialStateProperty.all(AppColor.colorThumbScrollBar)),
);
}
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 24bfa928e..c420143d6 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
@@ -68,6 +68,14 @@ class _HtmlContentViewState extends State {
_htmlData = generateHtml(widget.contentHtml);
}
+ @override
+ void didUpdateWidget(covariant HtmlContentViewer oldWidget) {
+ super.didUpdateWidget(oldWidget);
+ if (widget.contentHtml != oldWidget.contentHtml) {
+ _htmlData = generateHtml(widget.contentHtml);
+ }
+ }
+
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
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 124abfaea..153c9ab50 100644
--- a/lib/features/manage_account/presentation/profiles/identities/identities_view.dart
+++ b/lib/features/manage_account/presentation/profiles/identities/identities_view.dart
@@ -1,22 +1,12 @@
-
-import 'package:core/presentation/extensions/color_extension.dart';
-import 'package:core/presentation/resources/image_paths.dart';
-import 'package:core/presentation/state/success.dart';
-import 'package:core/presentation/utils/responsive_utils.dart';
-import 'package:core/presentation/views/button/button_builder.dart';
-import 'package:core/presentation/views/responsive/responsive_widget.dart';
+import 'package:core/core.dart';
import 'package:flutter/material.dart';
-import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
-import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
-import 'package:jmap_dart_client/jmap/identities/identity.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/base/widget/drop_down_button_widget.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_bottom_sheet_action_tile_builder.dart';
-import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/widgets/identity_info_tile_builder.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/widgets/identities_radio_list_builder.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/widgets/identities_radio_list_builder_web.dart' as identities_listview_web;
class IdentitiesView extends GetWidget with PopupMenuWidgetMixin, AppLoaderMixin {
@@ -27,177 +17,106 @@ class IdentitiesView extends GetWidget with PopupMenuWidge
@override
Widget build(BuildContext context) {
- final buttonSelectIdentity = Row(children: [
- Expanded(child: Obx(() => DropDownButtonWidget(
- items: controller.listAllIdentities,
- itemSelected: controller.identitySelected.value,
- onChanged: (newIdentity) => controller.selectIdentity(newIdentity),
- supportSelectionIcon: true))),
- if (!_responsiveUtils.isMobile(context)) const SizedBox(width: 12),
- if (!_responsiveUtils.isMobile(context))
- (ButtonBuilder(_imagePaths.icAddIdentity)
- ..key(const Key('button_new_identity'))
- ..decoration(BoxDecoration(
- borderRadius: BorderRadius.circular(10),
- color: AppColor.colorTextButton))
- ..paddingIcon(const EdgeInsets.only(right: 8))
- ..iconColor(Colors.white)
- ..maxWidth(170)
- ..size(20)
- ..radiusSplash(10)
- ..padding(const EdgeInsets.symmetric(vertical: 12))
- ..textStyle(const TextStyle(fontSize: 17, color: Colors.white, fontWeight: FontWeight.w500))
- ..onPressActionClick(() => controller.goToCreateNewIdentity(context))
- ..text(AppLocalizations.of(context).new_identity, isVertical: false))
- .build()
- ]);
+ return ResponsiveWidget(
+ responsiveUtils: _responsiveUtils,
+ mobile: _buildIdentitiesViewMobile(context),
+ tablet: _buildIdentitiesViewMobile(context),
+ desktop: _buildIdentitiesViewWeb(context),
+ tabletLarge: _buildIdentitiesViewMobile(context),
+ );
+ }
- return LayoutBuilder(builder: (context, constraints) => ResponsiveWidget(
- responsiveUtils: _responsiveUtils,
- mobile: Scaffold(
- body: Container(
- margin: const EdgeInsets.symmetric(vertical: 16),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- buttonSelectIdentity,
- const SizedBox(height: 24),
- _buildLoadingView(),
- Expanded(child: Obx(() => MasonryGridView.count(
- key: const Key('list_identities'),
- crossAxisSpacing: 24.0,
- mainAxisSpacing: 24.0,
- crossAxisCount: _responsiveUtils.isDesktop(context) || _responsiveUtils.isTabletLarge(context)
- ? 2 : 1,
- itemCount: controller.listSelectedIdentities.length,
- itemBuilder: (context, index) =>
- IdentityInfoTileBuilder(_imagePaths, _responsiveUtils,
- controller.listSelectedIdentities[index],
- onMenuItemIdentityAction: (identity, position) =>
- _openIdentityMenuAction(context, identity, position))
- )))
- ]
- )
- ),
- floatingActionButton: _responsiveUtils.isMobile(context)
- ? FloatingActionButton(
- key: const Key('add_new_identity'),
- onPressed: () => controller.goToCreateNewIdentity(context),
- backgroundColor: AppColor.primaryColor,
- child: SvgPicture.asset(_imagePaths.icAddIdentity, width: 24, height: 24))
- : null),
- desktop: Container(
- margin: const EdgeInsets.symmetric(vertical: 24, horizontal: 16),
+ Widget _buildIdentitiesViewMobile(BuildContext context) {
+ return SingleChildScrollView(
+ padding: const EdgeInsets.only(left: 16.0, right: 16.0, top: 24.0, bottom: 24.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ for(Widget item in _buildIdentitiesTitles(context))
+ item,
+ _buildCreateIdentityButton(context),
+ IdentitiesRadioListBuilder(controller: controller),
+ ],
+ ),
+ );
+ }
+
+ Widget _buildIdentitiesViewWeb(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(vertical: 24.0, horizontal: 8.0),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Container(
+ width: 236,
+ padding: const EdgeInsets.only(right: 12.0),
child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- SizedBox(
- width: constraints.maxWidth / 2,
- child: buttonSelectIdentity),
- const SizedBox(height: 24),
- _buildLoadingView(),
- Expanded(child: Obx(() => MasonryGridView.count(
- key: const Key('list_identities'),
- crossAxisSpacing: 24.0,
- mainAxisSpacing: 24.0,
- crossAxisCount: _responsiveUtils.isDesktop(context) || _responsiveUtils.isTabletLarge(context)
- ? 2 : 1,
- itemCount: controller.listSelectedIdentities.length,
- itemBuilder: (context, index) =>
- IdentityInfoTileBuilder(_imagePaths, _responsiveUtils,
- controller.listSelectedIdentities[index],
- onMenuItemIdentityAction: (identity, position) =>
- _openIdentityMenuAction(context, identity, position))
- )))
- ]
- )
- )
- ));
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ for(Widget widget in _buildIdentitiesTitles(context))
+ widget,
+ _buildCreateIdentityButton(context),
+ ],
+ ),
+ ),
+ Expanded(
+ child: identities_listview_web.IdentitiesRadioListBuilder(controller: controller),
+ ),
+ ],
+ ),
+ );
}
- Widget _buildLoadingView() {
- return Obx(() => controller.viewState.value.fold(
- (failure) => const SizedBox.shrink(),
- (success) => success is LoadingState
- ? Padding(
- padding: const EdgeInsets.only(bottom: 24),
- child: loadingWidget)
- : const SizedBox.shrink()
- ));
- }
-
- void _openIdentityMenuAction(BuildContext context, Identity identity,
- RelativeRect? position) {
- if (_responsiveUtils.isScreenWithShortestSide(context)) {
- controller.openContextMenuAction(context, _bottomSheetIdentityActionTiles(context, identity));
- } else {
- controller.openPopupMenuAction(context, position, _popupMenuIdentityActionTiles(context, identity));
- }
- }
-
- List _popupMenuIdentityActionTiles(BuildContext context, Identity identity) {
+ List _buildIdentitiesTitles(BuildContext context) {
return [
- PopupMenuItem(
- padding: EdgeInsets.zero,
- child: popupItem(_imagePaths.icEdit,
- AppLocalizations.of(context).edit_identity,
- styleName: const TextStyle(
- fontWeight: FontWeight.normal,
- fontSize: 17,
- color: Colors.black),
- onCallbackAction: () => controller.goToEditIdentity(context, identity))),
- if (identity.mayDelete == true)
- PopupMenuItem(
- padding: EdgeInsets.zero,
- child: popupItem(_imagePaths.icDeleteComposer,
- AppLocalizations.of(context).delete_identity,
- colorIcon: AppColor.colorActionDeleteConfirmDialog,
- styleName: const TextStyle(
- fontWeight: FontWeight.normal,
- fontSize: 17,
- color: AppColor.colorActionDeleteConfirmDialog),
- onCallbackAction: () => controller.openConfirmationDialogDeleteIdentityAction(context, identity))),
+ Text(
+ AppLocalizations.of(context).identities.inCaps,
+ style: const TextStyle(
+ fontWeight: FontWeight.w500,
+ fontSize: 17,
+ color: Colors.black)),
+ const SizedBox(height: 4.0),
+ Text(
+ AppLocalizations.of(context).identities_description,
+ style: const TextStyle(
+ fontSize: 15,
+ fontWeight: FontWeight.w400,
+ color: AppColor.colorSettingExplanation),
+ ),
+ const SizedBox(height: 12.0),
];
}
- List _bottomSheetIdentityActionTiles(BuildContext context, Identity identity) {
- return [
- _editIdentityActionTile(context, identity),
- if (identity.mayDelete == true)
- _deleteIdentityActionTile(context, identity),
- ];
+ Widget _buildCreateIdentityButton(BuildContext context) {
+ return Row(
+ children: [
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.only(bottom: 12.0),
+ child: (ButtonBuilder(_imagePaths.icAddIdentity)
+ ..key(const Key('button_add_identity'))
+ ..decoration(BoxDecoration(
+ borderRadius: BorderRadius.circular(12),
+ color: AppColor.colorBorderIdentityInfo))
+ ..paddingIcon(const EdgeInsets.only(right: 12))
+ ..iconColor(AppColor.primaryColor)
+ ..size(28)
+ ..padding(const EdgeInsets.symmetric(vertical: 12))
+ ..textStyle(const TextStyle(
+ fontSize: 16,
+ color: AppColor.primaryColor,
+ fontWeight: FontWeight.w500,
+ ))
+ ..onPressActionClick(() => controller.goToCreateNewIdentity(context))
+ ..text(
+ AppLocalizations.of(context).create_new_identity,
+ isVertical: false,
+ ))
+ .build(),
+ ),
+ ),
+ ],
+ );
}
- Widget _deleteIdentityActionTile(BuildContext context, Identity identity) {
- return (IdentityBottomSheetActionTileBuilder(
- const Key('delete_identity_action'),
- SvgPicture.asset(_imagePaths.icDeleteComposer,
- color: AppColor.colorActionDeleteConfirmDialog),
- AppLocalizations.of(context).delete_identity,
- identity,
- iconLeftPadding: _responsiveUtils.isMobile(context)
- ? const EdgeInsets.only(left: 12, right: 16)
- : const EdgeInsets.only(right: 12),
- iconRightPadding: _responsiveUtils.isMobile(context)
- ? const EdgeInsets.only(right: 12)
- : EdgeInsets.zero)
- ..onActionClick((identity) => controller.openConfirmationDialogDeleteIdentityAction(context, identity)))
- .build();
- }
-
- Widget _editIdentityActionTile(BuildContext context, Identity identity) {
- return (IdentityBottomSheetActionTileBuilder(
- const Key('edit_identity_action'),
- SvgPicture.asset(_imagePaths.icEdit),
- AppLocalizations.of(context).edit_identity,
- identity,
- iconLeftPadding: _responsiveUtils.isMobile(context)
- ? const EdgeInsets.only(left: 12, right: 16)
- : const EdgeInsets.only(right: 12),
- iconRightPadding: _responsiveUtils.isMobile(context)
- ? const EdgeInsets.only(right: 12)
- : EdgeInsets.zero)
- ..onActionClick((identity) => controller.goToEditIdentity(context, identity)))
- .build();
- }
}
\ 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
new file mode 100644
index 000000000..5b19dab4d
--- /dev/null
+++ b/lib/features/manage_account/presentation/profiles/identities/widgets/identities_radio_list_builder.dart
@@ -0,0 +1,79 @@
+import 'package:core/presentation/extensions/color_extension.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.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/signature_of_identity_builder.dart';
+
+import 'identity_list_tile_builder.dart';
+
+class IdentitiesRadioListBuilder extends StatefulWidget {
+ const IdentitiesRadioListBuilder({
+ Key? key,
+ required this.controller,
+ }) : super(key: key);
+
+ final IdentitiesController controller;
+
+ @override
+ State createState() => _IdentitiesRadioListBuilderState();
+}
+
+class _IdentitiesRadioListBuilderState extends State {
+ late final controller = widget.controller;
+
+ @override
+ Widget build(BuildContext context) {
+ return Obx(() {
+ if (controller.listAllIdentities.isEmpty) {
+ return const SizedBox.shrink();
+ }
+
+ return Container(
+ foregroundDecoration: BoxDecoration(
+ border: Border.all(color: AppColor.colorBorderIdentityInfo, width: 1),
+ borderRadius: const BorderRadius.all(Radius.circular(16.0)),
+ ),
+ child: Obx(() => Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Container(
+ key: const Key('identities_checkbox_list'),
+ height: 256,
+ foregroundDecoration: BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
+ colors: [0, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 1].map((e)
+ => Colors.white.withOpacity(e.toDouble())).toList(),
+ stops: const [0.8, 0.82, 0.84, 0.86, 0.88, 0.90, 0.92, 0.94, 0.96, 0.98, 1],// Gradient from https://learnui.design/tools/gradient-generator.html
+ tileMode: TileMode.mirror,
+ ),
+ border: controller.isSignatureShow() ? const Border(bottom: BorderSide(color: AppColor.colorBorderIdentityInfo)): null,
+ ),
+ padding: const EdgeInsets.only(left: 12.0, bottom: 12.0, top: 12.0, right: 4.0),
+ child: Scrollbar(
+ child: MediaQuery.removePadding(
+ context: context,
+ removeTop: true,
+ child: ListView.builder(
+ padding: const EdgeInsets.only(right: 12.0),
+ itemCount: controller.listAllIdentities.value.length,
+ itemBuilder: ((context, index) {
+ return IdentityListTileBuilder(
+ controller: widget.controller,
+ index: index,
+ selected: widget.controller.selectedIndex.value,
+ );
+ }),
+ )
+ ),
+ ),
+ ),
+ if(controller.isSignatureShow())
+ Obx(() => SignatureOfIdentityBuilder(identity: controller.identitySelected.value!)),
+ ],
+ ),
+ ));
+ });
+ }
+}
\ 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
new file mode 100644
index 000000000..535091667
--- /dev/null
+++ b/lib/features/manage_account/presentation/profiles/identities/widgets/identity_list_tile_builder.dart
@@ -0,0 +1,121 @@
+import 'package:core/core.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:jmap_dart_client/jmap/identities/identity.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identities_controller.dart';
+
+class IdentityListTileBuilder extends StatelessWidget {
+ const IdentityListTileBuilder({
+ Key? key,
+ required this.controller,
+ required this.index,
+ required this.selected
+ }) : super(key: key);
+
+ final IdentitiesController controller;
+ final int index;
+ final int? selected;
+
+ @override
+ Widget build(BuildContext context) {
+ final imagePaths = controller.imagePaths;
+ final listAllIdentities = controller.listAllIdentities.value;
+ final identity = listAllIdentities[index];
+
+ return Material(
+ color: Colors.transparent,
+ child: InkWell(
+ borderRadius: BorderRadius.circular(12.0),
+ onTap: () => controller.selectIdentity(index),
+ child: Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(12.0),
+ color: isIdentitySelected(index) ? AppColor.colorItemSelected : Colors.transparent,
+ ),
+ padding: const EdgeInsets.only(top: 12.0, bottom: 12.0, right: 12.0),
+ child: Row(
+ children: [
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: BuildUtils.isWeb ? 8.0 : 0.0),
+ child: Radio(
+ value: index,
+ groupValue: selected,
+ onChanged: (selectedValue) => controller.selectIdentity(index),
+ ),
+ ),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ identity.name ?? '',
+ overflow: CommonTextStyle.defaultTextOverFlow,
+ softWrap: CommonTextStyle.defaultSoftWrap,
+ style: const TextStyle(
+ fontWeight: FontWeight.w500,
+ fontSize: 16,
+ color: Colors.black)),
+ _buildIconWithTextLine(imagePaths.icEmail, identity.email),
+ for(final widget in _buildReplyLines(identity, imagePaths))
+ widget
+ ],
+ )
+ ),
+ if(isIdentitySelected(index))...[
+ buildSVGIconButton(
+ padding: const EdgeInsets.only(left: 12.0, right: 12.0),
+ icon: imagePaths.icEdit,
+ iconSize: 24,
+ iconColor: AppColor.primaryColor,
+ onTap: () => controller.goToEditIdentity(context, controller.identitySelected.value!),
+ ),
+ buildSVGIconButton(
+ padding: const EdgeInsets.only(right: 0.0),
+ icon: imagePaths.icDeleteOutline,
+ iconSize: 24,
+ iconColor: AppColor.colorActionDeleteConfirmDialog,
+ onTap: () => controller.openConfirmationDialogDeleteIdentityAction(context, controller.identitySelected.value!),
+ ),
+ ]
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+
+ List _buildReplyLines(Identity? identity, ImagePaths imagePaths) {
+ return [
+ for(var identityReplyTo in identity?.replyTo?.toList() ?? [])
+ _buildIconWithTextLine(imagePaths.icReply, identityReplyTo.email)];
+ }
+
+ Widget _buildIconWithTextLine(String imagePath, String? text) {
+ return Row(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(right: 8.0),
+ child: SvgPicture.asset(
+ imagePath,
+ color: AppColor.primaryColor,
+ width: 16,
+ fit: BoxFit.fitHeight,
+ )),
+ // replacement character after ..., so wrap it inside richtext is workaround solution
+ Expanded(child: Text(
+ text ?? '', style: const TextStyle(
+ color: AppColor.colorEmailAddressFull,
+ fontWeight: FontWeight.w400,
+ fontSize: 13,
+ ),
+ overflow: CommonTextStyle.defaultTextOverFlow,
+ softWrap: CommonTextStyle.defaultSoftWrap,
+ )),
+ ]
+ );
+ }
+
+ bool isIdentitySelected(int index) {
+ return index == selected;
+ }
+}
\ 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
new file mode 100644
index 000000000..fa0b044a4
--- /dev/null
+++ b/lib/features/manage_account/presentation/profiles/identities/widgets/signature_builder.dart
@@ -0,0 +1,65 @@
+import 'package:core/core.dart';
+import 'package:flutter/material.dart';
+import 'package:html_unescape/html_unescape_small.dart';
+import 'package:jmap_dart_client/jmap/identities/identity.dart';
+
+class SignatureBuilder extends StatelessWidget {
+ SignatureBuilder({
+ Key? key,
+ this.width,
+ this.height,
+ this.htmlSignature,
+ this.textSignature,
+ }) : super(key: key);
+
+ final Signature? htmlSignature;
+ final Signature? textSignature;
+ 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 ?? constraints.biggest.height;
+ return Container(
+ width: signatureWidth,
+ padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 16.0),
+ decoration: const BoxDecoration(
+ color: Colors.white,
+ ),
+ child: _buildSignature(signatureWidth, signatureHeight),
+ );
+ }
+ );
+ }
+
+ Widget _buildSignature(double width, double height) {
+ Widget signature;
+ if (htmlSignature != null && htmlSignature!.value.isNotEmpty) {
+ final htmlSignatureDecoded = _decodeHtml(htmlSignature!.value);
+ if (BuildUtils.isWeb) {
+ signature = HtmlContentViewerOnWeb(
+ contentHtml: htmlSignatureDecoded,
+ widthContent: width,
+ heightContent: height,
+ controller: HtmlViewerControllerForWeb(),
+ allowResizeToDocumentSize: false);
+ } else {
+ signature = HtmlContentViewer(contentHtml: htmlSignatureDecoded, heightContent: height);
+ }
+ } else if (textSignature != null) {
+ signature = Text(textSignature!.value);
+ } else {
+ signature = SizedBox.fromSize(size: Size(width, height));
+ }
+ return signature;
+ }
+
+ String _decodeHtml(String htmlString) {
+ final unescape = HtmlUnescape();
+ return unescape.convert(htmlString);
+ }
+
+}
\ No newline at end of file
diff --git a/lib/features/manage_account/presentation/profiles/identities/widgets/signature_of_identity_builder.dart b/lib/features/manage_account/presentation/profiles/identities/widgets/signature_of_identity_builder.dart
new file mode 100644
index 000000000..06b3b477c
--- /dev/null
+++ b/lib/features/manage_account/presentation/profiles/identities/widgets/signature_of_identity_builder.dart
@@ -0,0 +1,20 @@
+import 'package:flutter/cupertino.dart';
+import 'package:jmap_dart_client/jmap/identities/identity.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/widgets/signature_builder.dart';
+
+class SignatureOfIdentityBuilder extends StatelessWidget {
+
+ SignatureOfIdentityBuilder({required this.identity});
+
+ final Identity identity;
+
+ @override
+ Widget build(BuildContext context) {
+ return SignatureBuilder(
+ height: 256,
+ htmlSignature: identity.htmlSignature,
+ textSignature: identity.textSignature
+ );
+ }
+}
+
diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart
index da402af62..796ac8cc2 100644
--- a/lib/main/localizations/app_localizations.dart
+++ b/lib/main/localizations/app_localizations.dart
@@ -1200,11 +1200,26 @@ class AppLocalizations {
name: 'profiles');
}
+ String get profiles_description {
+ return Intl.message('Info about you, and options to manage it',
+ name: 'profiles_description');
+ }
+
String get identities {
return Intl.message('Identities',
name: 'identities');
}
+ String get identities_description {
+ return Intl.message('Select the identity or email address you want to use to send an emails',
+ name: 'identities_description');
+ }
+
+ String get create_new_identity {
+ return Intl.message('Create new identity',
+ name: 'create_new_identity');
+ }
+
String get new_identity {
return Intl.message('New Identity',
name: 'new_identity');
@@ -2594,4 +2609,11 @@ class AppLocalizations {
name: 'openInNewTab',
);
}
+
+ String get regards {
+ return Intl.message(
+ 'Regards',
+ name: 'regards',
+ );
+ }
}
\ No newline at end of file
diff --git a/pubspec.lock b/pubspec.lock
index b7ca04234..bb7d6de08 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -789,6 +789,13 @@ packages:
url: "https://github.com/linagora/html-editor-enhanced.git"
source: git
version: "2.5.0"
+ html_unescape:
+ dependency: "direct main"
+ description:
+ name: html_unescape
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0"
http:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 63b060ade..2aa563671 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -207,6 +207,7 @@ dependencies:
# Use for flutter 3.0.5 https://github.com/EdsonBueno/focus_detector/pull/14
focus_detector:
git: https://github.com/ifnyas/focus_detector.git
+ html_unescape: ^2.0.0
dev_dependencies:
flutter_test: