TF-1194 apply new design identity to manage identity screen in mobile
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="mask0_7048_307397" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="2" y="1" width="20" height="21">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.38105 8.14411C8.90839 8.17036 8.54652 8.57482 8.57277 9.04747L9.00135 16.7618C9.0276 17.2344 9.43206 17.5963 9.90472 17.57C10.3774 17.5438 10.7393 17.1393 10.713 16.6667L10.2844 8.95238C10.2582 8.47973 9.85371 8.11785 9.38105 8.14411Z" fill="#007AFF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.619 8.14411C15.0917 8.17036 15.4535 8.57482 15.4273 9.04747L14.9987 16.7618C14.9724 17.2344 14.568 17.5963 14.0953 17.57C13.6227 17.5438 13.2608 17.1393 13.2871 16.6667L13.7156 8.95238C13.7419 8.47973 14.1463 8.11785 14.619 8.14411Z" fill="#007AFF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.00003 4.28564C2.52664 4.28564 2.14288 4.6694 2.14288 5.14279C2.14288 5.61617 2.52664 5.99993 3.00003 5.99993H3.93953L5.05169 16.9907C5.11497 17.6162 5.16741 18.1345 5.24362 18.5562C5.32307 18.9957 5.4386 19.3962 5.66241 19.7681C6.01147 20.348 6.52437 20.8116 7.13647 21.1005C7.52895 21.2857 7.93906 21.3604 8.38438 21.3951C8.8116 21.4285 9.33254 21.4285 9.96121 21.4285H14.0388C14.6675 21.4285 15.1885 21.4285 15.6157 21.3951C16.061 21.3604 16.4711 21.2857 16.8636 21.1005C17.4757 20.8116 17.9886 20.348 18.3376 19.7681C18.5615 19.3962 18.677 18.9957 18.7564 18.5562C18.8326 18.1345 18.8851 17.6162 18.9484 16.9908L20.0605 5.99993H21C21.4734 5.99993 21.8572 5.61617 21.8572 5.14279C21.8572 4.6694 21.4734 4.28564 21 4.28564H15.7616C15.3718 2.56794 13.8357 1.28564 12 1.28564C10.1644 1.28564 8.62823 2.56794 8.23846 4.28564H3.00003ZM6.75391 16.785C6.8214 17.452 6.86768 17.9033 6.93058 18.2513C6.99158 18.5888 7.05774 18.762 7.13115 18.884C7.30568 19.174 7.56213 19.4058 7.86818 19.5502C7.99692 19.611 8.17592 19.6594 8.51789 19.6861C8.87043 19.7136 9.32413 19.7142 9.99451 19.7142H14.0055C14.6759 19.7142 15.1296 19.7136 15.4822 19.6861C15.8241 19.6594 16.0031 19.611 16.1319 19.5502C16.4379 19.4058 16.6944 19.174 16.8689 18.884C16.9423 18.762 17.0085 18.5888 17.0695 18.2513C17.1324 17.9033 17.1786 17.452 17.2461 16.785L18.3375 5.99993H5.66257L6.75391 16.785ZM12 2.99993C11.1213 2.99993 10.3661 3.52883 10.0355 4.28564H13.9646C13.6339 3.52883 12.8787 2.99993 12 2.99993Z" fill="#007AFF"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_7048_307397)">
|
||||
<rect width="24" height="24" fill="#E64646"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -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)],
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,14 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
|
||||
_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) {
|
||||
|
||||
+95
-176
@@ -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<IdentitiesController> with PopupMenuWidgetMixin, AppLoaderMixin {
|
||||
|
||||
@@ -27,177 +17,106 @@ class IdentitiesView extends GetWidget<IdentitiesController> with PopupMenuWidge
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final buttonSelectIdentity = Row(children: [
|
||||
Expanded(child: Obx(() => DropDownButtonWidget<Identity>(
|
||||
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<PopupMenuEntry> _popupMenuIdentityActionTiles(BuildContext context, Identity identity) {
|
||||
List<Widget> _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<Widget> _bottomSheetIdentityActionTiles(BuildContext context, Identity identity) {
|
||||
return <Widget>[
|
||||
_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();
|
||||
}
|
||||
}
|
||||
+79
@@ -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<StatefulWidget> createState() => _IdentitiesRadioListBuilderState();
|
||||
}
|
||||
|
||||
class _IdentitiesRadioListBuilderState extends State<IdentitiesRadioListBuilder> {
|
||||
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!)),
|
||||
],
|
||||
),
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
+121
@@ -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<int>(
|
||||
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<Widget> _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;
|
||||
}
|
||||
}
|
||||
+65
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+20
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user