TF-1194 Refactor and optimize widget

This commit is contained in:
dab246
2022-12-28 14:13:11 +07:00
committed by Dat H. Pham
parent f664a7fc05
commit d9d0b71d35
18 changed files with 489 additions and 434 deletions
-10
View File
@@ -1,10 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 2.4 KiB

@@ -169,6 +169,7 @@ extension AppColor on Color {
static const colorProgressQuotasWarning = Color(0xFFFFA000);
static const colorOutOfStorageQuotasWarning = Color(0xffE64646);
static const colorThumbScrollBar = Color(0xFFAEB7C2);
static const colorCreateNewIdentityButton = Color(0xFFEBEDF0);
static const mapGradientColor = [
[Color(0xFF21D4FD), Color(0xFFB721FF)],
@@ -26,7 +26,6 @@ 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');
@@ -36,6 +36,7 @@ class SettingsFirstLevelView extends GetWidget<SettingsController> {
SettingFirstLevelTileBuilder(
AppLocalizations.of(context).profiles,
AccountMenuItem.profiles.getIcon(_imagePaths),
subtitle: AppLocalizations.of(context).profilesSettingExplanation,
() => controller.selectSettings(AccountMenuItem.profiles)
),
Divider(
@@ -43,7 +43,6 @@ class IdentitiesController extends BaseController {
final DeleteIdentityInteractor _deleteIdentityInteractor;
final EditIdentityInteractor _editIdentityInteractor;
final selectedIndex = Rxn<int>();
final identitySelected = Rxn<Identity>();
final listAllIdentities = <Identity>[].obs;
@@ -71,28 +70,22 @@ class IdentitiesController extends BaseController {
@override
void onDone() {
viewState.value.fold(
(failure) {
if (failure is DeleteIdentityFailure) {
_deleteIdentityFailure(failure);
}
},
(success) {
if (success is GetAllIdentitiesSuccess) {
if (success.identities?.isNotEmpty == true) {
final newListIdentities = success.identities!
.where((identity) => identity.mayDelete == true)
.toList();
listAllIdentities.addAll(newListIdentities);
selectIdentity(0);
}
} else if (success is CreateNewIdentitySuccess) {
_createNewIdentitySuccess(success);
} else if (success is DeleteIdentitySuccess) {
_deleteIdentitySuccess(success);
} else if (success is EditIdentitySuccess) {
_editIdentitySuccess(success);
}
(failure) {
if (failure is DeleteIdentityFailure) {
_deleteIdentityFailure(failure);
}
},
(success) {
if (success is GetAllIdentitiesSuccess) {
_handleGetAllIdentitiesSuccess(success);
} else if (success is CreateNewIdentitySuccess) {
_createNewIdentitySuccess(success);
} else if (success is DeleteIdentitySuccess) {
_deleteIdentitySuccess(success);
} else if (success is EditIdentitySuccess) {
_editIdentitySuccess(success);
}
}
);
}
@@ -117,7 +110,7 @@ class IdentitiesController extends BaseController {
}
void _refreshAllIdentities() {
selectedIndex.value = null;
identitySelected.value = null;
listAllIdentities.clear();
final accountId = _accountDashBoardController.accountId.value;
@@ -126,11 +119,21 @@ class IdentitiesController extends BaseController {
}
}
void selectIdentity(int? index) {
selectedIndex.value = index;
if (selectedIndex.value != null) {
identitySelected.value = listAllIdentities[selectedIndex.value!];
void _handleGetAllIdentitiesSuccess(GetAllIdentitiesSuccess success) {
if (success.identities?.isNotEmpty == true) {
final newListIdentities = success.identities!
.where((identity) => identity.mayDelete == true)
.toList();
listAllIdentities.addAll(newListIdentities);
}
if (listAllIdentities.isNotEmpty) {
selectIdentity(listAllIdentities.first);
}
}
void selectIdentity(Identity? newIdentity) {
identitySelected.value = newIdentity;
}
void goToCreateNewIdentity(BuildContext context) async {
@@ -226,6 +229,8 @@ class IdentitiesController extends BaseController {
}
void _deleteIdentityAction(Identity identity) {
popBack();
final accountId = _accountDashBoardController.accountId.value;
if (accountId != null && identity.id != null) {
consumeState(_deleteIdentityInteractor.execute(accountId, identity.id!));
@@ -319,7 +324,5 @@ class IdentitiesController extends BaseController {
ImagePaths get imagePaths => _imagePaths;
bool isSignatureShow() {
return selectedIndex.value!= null && selectedIndex.value! >= 0;
}
bool get isSignatureShow => identitySelected.value != null;
}
@@ -1,12 +1,12 @@
import 'package:core/core.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/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/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/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 {
@@ -17,106 +17,52 @@ class IdentitiesView extends GetWidget<IdentitiesController> with PopupMenuWidge
@override
Widget build(BuildContext context) {
return ResponsiveWidget(
responsiveUtils: _responsiveUtils,
mobile: _buildIdentitiesViewMobile(context),
tablet: _buildIdentitiesViewMobile(context),
desktop: _buildIdentitiesViewWeb(context),
tabletLarge: _buildIdentitiesViewMobile(context),
return Container(
color: Colors.transparent,
margin: const EdgeInsets.all(24),
child: _responsiveUtils.isWebDesktop(context)
? _buildIdentitiesViewWebDesktop(context)
: _buildIdentitiesViewMobile(context),
);
}
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: [
for(Widget widget in _buildIdentitiesTitles(context))
widget,
_buildCreateIdentityButton(context),
],
),
),
Expanded(
child: identities_listview_web.IdentitiesRadioListBuilder(controller: controller),
),
],
),
);
}
List<Widget> _buildIdentitiesTitles(BuildContext context) {
return [
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),
];
}
Widget _buildCreateIdentityButton(BuildContext context) {
return Row(
return Column(
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(),
),
IdentitiesHeaderWidget(
imagePaths: _imagePaths,
responsiveUtils: _responsiveUtils,
onAddNewIdentityAction: () => controller.goToCreateNewIdentity(context),
),
const SizedBox(height: 12),
IdentitiesRadioListBuilder(
controller: controller,
responsiveUtils: _responsiveUtils,
imagePaths: _imagePaths
)
],
);
}
Widget _buildIdentitiesViewWebDesktop(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 224,
child: IdentitiesHeaderWidget(
imagePaths: _imagePaths,
responsiveUtils: _responsiveUtils,
onAddNewIdentityAction: () => controller.goToCreateNewIdentity(context),
)
),
const SizedBox(width: 12),
Expanded(child: IdentitiesRadioListBuilder(
controller: controller,
responsiveUtils: _responsiveUtils,
imagePaths: _imagePaths
)),
],
);
}
}
@@ -0,0 +1,62 @@
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:core/presentation/views/button/button_builder.dart';
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnAddNewIdentityAction = Function();
class IdentitiesHeaderWidget extends StatelessWidget {
const IdentitiesHeaderWidget({
Key? key,
required this.imagePaths,
required this.responsiveUtils,
this.onAddNewIdentityAction,
}) : super(key: key);
final ImagePaths imagePaths;
final ResponsiveUtils responsiveUtils;
final OnAddNewIdentityAction? onAddNewIdentityAction;
@override
Widget build(BuildContext context) {
return Container(
color: Colors.transparent,
child: 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),
(ButtonBuilder(imagePaths.icAddIdentity)
..key(const Key('button_add_identity'))
..decoration(BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: AppColor.colorCreateNewIdentityButton))
..paddingIcon(const EdgeInsets.only(right: 8))
..iconColor(AppColor.colorTextButton)
..size(28)
..radiusSplash(12)
..padding(const EdgeInsets.symmetric(vertical: 10))
..textStyle(const TextStyle(
fontSize: 16,
color: AppColor.colorTextButton,
fontWeight: FontWeight.w500))
..onPressActionClick(() => onAddNewIdentityAction?.call())
..text(AppLocalizations.of(context).createNewIdentity, isVertical: false)
).build()
]),
);
}
}
@@ -1,79 +1,105 @@
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/material.dart';
import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart';
import 'package:get/get_state_manager/get_state_manager.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/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);
class IdentitiesRadioListBuilder extends StatelessWidget {
final IdentitiesController controller;
final ResponsiveUtils responsiveUtils;
final ImagePaths imagePaths;
@override
State<StatefulWidget> createState() => _IdentitiesRadioListBuilderState();
}
class _IdentitiesRadioListBuilderState extends State<IdentitiesRadioListBuilder> {
late final controller = widget.controller;
const IdentitiesRadioListBuilder({
Key? key,
required this.controller,
required this.responsiveUtils,
required this.imagePaths,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Obx(() {
if (controller.listAllIdentities.isEmpty) {
return const SizedBox.shrink();
}
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: responsiveUtils.isWebDesktop(context)
? _buildIdentityViewHorizontal(context)
: _buildIdentityViewVertical(context))
);
}
return Container(
foregroundDecoration: BoxDecoration(
border: Border.all(color: AppColor.colorBorderIdentityInfo, width: 1),
borderRadius: const BorderRadius.all(Radius.circular(16.0)),
Widget _buildIdentityViewVertical(BuildContext context) {
return Obx(() => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if(controller.isSignatureShow)
...[
_buildListIdentityView(context),
Container(height: 1, color: AppColor.attachmentFileBorderColor),
Obx(() => SignatureOfIdentityBuilder(identity: controller.identitySelected.value!))
]
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(() => SignatureOfIdentityBuilder(identity: controller.identitySelected.value!)),
)
]
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,
shouldDisposeScrollController: true,
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),
);
}),
),
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!)),
],
),
));
});
))
);
}
}
@@ -1,82 +0,0 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:flutter/material.dart';
import 'package:get/get_state_manager/get_state_manager.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<IdentitiesRadioListBuilder> createState() => _IdentitiesRadioListBuilderState();
}
class _IdentitiesRadioListBuilderState extends State<IdentitiesRadioListBuilder> {
int? selected;
late final allIdentities = widget.controller.listAllIdentities.value;
@override
Widget build(BuildContext context) {
return Container(
height: 256,
foregroundDecoration: BoxDecoration(
border: Border.all(color: AppColor.colorBorderIdentityInfo, width: 1),
borderRadius: const BorderRadius.all(Radius.circular(16.0)),
),
child: Obx(() => Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if(widget.controller.isSignatureShow())...[
_buildListViewContainer(),
Expanded(
child: Obx(() => SignatureOfIdentityBuilder(identity: widget.controller.identitySelected.value!)),
)
] else
Expanded(child: _buildListViewContainer())
],
)),
);
}
Widget _buildListViewContainer() {
return Container(
key: const Key('identities_checkbox_list'),
width: 320,
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: widget.controller.isSignatureShow() ? const Border(right: BorderSide(color: AppColor.colorBorderIdentityInfo)): null,
),
padding: const EdgeInsets.only(left: 12.0, bottom: 12.0, top: 12.0, right: 4.0),
child: Scrollbar(
controller: ScrollController(),
radius: const Radius.circular(5.0),
thickness: 2.0,
child: Obx(() => ListView.builder(
padding: const EdgeInsets.only(right: 12.0),
itemCount: widget.controller.listAllIdentities.value.length,
itemBuilder: ((context, index) {
return IdentityListTileBuilder(
controller: widget.controller,
index: index,
selected: widget.controller.selectedIndex.value,
);
}),
),
))
);
}
}
@@ -1,121 +1,167 @@
import 'package:core/core.dart';
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/style_utils.dart';
import 'package:core/presentation/views/button/icon_button_web.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';
import 'package:model/extensions/list_email_address_extension.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnSelectIdentityAction = Function(Identity? identitySelected);
typedef OnEditIdentityAction = Function(Identity identitySelected);
typedef OnDeleteIdentityAction = Function(Identity identitySelected);
class IdentityListTileBuilder extends StatelessWidget {
const IdentityListTileBuilder({
Key? key,
required this.controller,
required this.index,
required this.selected
required this.identity,
required this.identitySelected,
required this.imagePaths,
this.onSelectIdentityAction,
this.onEditIdentityAction,
this.onDeleteIdentityAction
}) : super(key: key);
final IdentitiesController controller;
final int index;
final int? selected;
final Identity identity;
final Identity? identitySelected;
final ImagePaths imagePaths;
final OnSelectIdentityAction? onSelectIdentityAction;
final OnEditIdentityAction? onEditIdentityAction;
final OnDeleteIdentityAction? onDeleteIdentityAction;
@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),
child: Padding(
padding: const EdgeInsets.only(bottom: 2),
child: InkWell(
borderRadius: BorderRadius.circular(12.0),
onTap: () => onSelectIdentityAction?.call(identity),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
color: _isIdentitySelected
? AppColor.colorItemSelected
: Colors.transparent,
),
padding: const EdgeInsets.all(12),
child: Row(
children: [
Radio<Identity>(
value: identity,
splashRadius: 15,
groupValue: identitySelected,
activeColor: AppColor.primaryColor,
onChanged: onSelectIdentityAction,
),
),
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!),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 6),
child: Text(
identity.name ?? '',
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap,
style: const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16,
color: Colors.black)),
),
if (identity.email?.isNotEmpty == true)
_buildIconSVGWithTextLine(imagePaths.icEmail, identity.email),
if (identity.replyTo?.isNotEmpty == true)
_buildIconSVGWithTextLine(
imagePaths.icReplyTo,
identity.replyTo?.listEmailAddressToString(isFullEmailAddress: true)
),
if (identity.bcc?.isNotEmpty == true)
_buildIconCharacterWithTextLine(
AppLocalizations.of(context).bcc_email_address_prefix,
identity.bcc?.listEmailAddressToString(isFullEmailAddress: true)
),
],
)
),
buildSVGIconButton(
padding: const EdgeInsets.only(right: 0.0),
icon: imagePaths.icDeleteOutline,
iconSize: 24,
iconColor: AppColor.colorActionDeleteConfirmDialog,
onTap: () => controller.openConfirmationDialogDeleteIdentityAction(context, controller.identitySelected.value!),
),
]
],
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),
),
]
],
),
),
),
),
);
}
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
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: Text(
text ?? '', style: const TextStyle(
text ?? '',
style: const TextStyle(
color: AppColor.colorEmailAddressFull,
fontWeight: FontWeight.w400,
fontWeight: FontWeight.normal,
fontSize: 13,
),
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap,
)),
]
softWrap: CommonTextStyle.defaultSoftWrap
))
]),
);
}
bool isIdentitySelected(int index) {
return index == selected;
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: Text(text ?? '',
style: const TextStyle(
color: AppColor.colorEmailAddressFull,
fontWeight: FontWeight.normal,
fontSize: 13,
),
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap
))
]),
);
}
bool get _isIdentitySelected => identity.id == identitySelected?.id;
}
@@ -1,10 +1,14 @@
import 'package:core/core.dart';
import 'package:core/presentation/views/html_viewer/html_content_viewer_on_web_widget.dart';
import 'package:core/presentation/views/html_viewer/html_content_viewer_widget.dart';
import 'package:core/presentation/views/html_viewer/html_viewer_controller_for_web.dart';
import 'package:core/utils/build_utils.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({
const SignatureBuilder({
Key? key,
this.width,
this.height,
@@ -61,5 +65,4 @@ class SignatureBuilder extends StatelessWidget {
final unescape = HtmlUnescape();
return unescape.convert(htmlString);
}
}
@@ -4,7 +4,7 @@ import 'package:tmail_ui_user/features/manage_account/presentation/profiles/iden
class SignatureOfIdentityBuilder extends StatelessWidget {
SignatureOfIdentityBuilder({required this.identity});
const SignatureOfIdentityBuilder({Key? key, required this.identity}) : super(key: key);
final Identity identity;
@@ -1,15 +1,18 @@
import 'package:core/core.dart';
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/menu/settings_utils.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/profiles_controller.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/profiles/widgets/profiles_header_widget.dart';
class ProfilesView extends GetWidget<ProfilesController> {
final _responsiveUtils = Get.find<ResponsiveUtils>();
final _imagePaths = Get.find<ImagePaths>();
ProfilesView({Key? key}) : super(key: key);
@@ -17,52 +20,31 @@ class ProfilesView extends GetWidget<ProfilesController> {
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: SettingsUtils.getBackgroundColor(context, _responsiveUtils),
body: ResponsiveWidget(
responsiveUtils: _responsiveUtils,
mobile: IdentitiesView(),
desktop: _buildProfilesWeb(context))
);
}
Widget _buildProfilesWeb(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(16.0)),
border: Border.all(color: AppColor.colorBorderIdentityInfo),
color: Colors.white,
),
margin: const EdgeInsets.all(16.0),
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for(var widget in _buildProfilesTitle(context))
widget,
const Divider(color: AppColor.lineItemListColor, height: 0.5, thickness: 0.2),
Expanded(child: IdentitiesView()),
],
),
);
}
List<Widget> _buildProfilesTitle(BuildContext context) {
return [
Text(
AppLocalizations.of(context).profiles.inCaps,
style: const TextStyle(
fontWeight: FontWeight.w700,
fontSize: 20,
fontFamily: 'Inter',
color: Colors.black)),
const SizedBox(height: 4.0),
Text(
AppLocalizations.of(context).profiles_description,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: AppColor.colorContentEmail),
body: Container(
width: double.infinity,
height: double.infinity,
color: SettingsUtils.getContentBackgroundColor(context, _responsiveUtils),
decoration: SettingsUtils.getBoxDecorationForContent(context, _responsiveUtils),
margin: SettingsUtils.getMarginViewForForwardSettingDetails(context, _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()
])
))
],
),
const SizedBox(height: 16.0),
];
),
);
}
}
@@ -0,0 +1,40 @@
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:tmail_ui_user/main/localizations/app_localizations.dart';
class ProfilesHeaderWidget extends StatelessWidget {
const ProfilesHeaderWidget({
Key? key,
required this.imagePaths,
required this.responsiveUtils,
}) : super(key: key);
final ImagePaths imagePaths;
final ResponsiveUtils responsiveUtils;
@override
Widget build(BuildContext context) {
return Container(
color: Colors.transparent,
padding: const EdgeInsets.only(top: 16, left: 16, right: 16, bottom: 15),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(
AppLocalizations.of(context).profiles,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.black)),
const SizedBox(height: 4),
Text(
AppLocalizations.of(context).profilesSettingExplanation,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: AppColor.colorSettingExplanation)),
]),
);
}
}
+25 -1
View File
@@ -1,5 +1,5 @@
{
"@@last_modified": "2022-11-18T14:33:10.063321",
"@@last_modified": "2022-12-28T14:09:51.971658",
"initializing_data": "Initializing data...",
"@initializing_data": {
"type": "text",
@@ -1166,12 +1166,30 @@
"placeholders_order": [],
"placeholders": {}
},
"profilesSettingExplanation": "Info about you, and options to manage it",
"@profilesSettingExplanation": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"identities": "Identities",
"@identities": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"identitiesSettingExplanation": "Select the identity or email address you want to use to send an emails",
"@identitiesSettingExplanation": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"createNewIdentity": "Create new identity",
"@createNewIdentity": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"new_identity": "New Identity",
"@new_identity": {
"type": "text",
@@ -2527,5 +2545,11 @@
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"regards": "Regards",
"@regards": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
}
}
+13 -9
View File
@@ -1200,9 +1200,11 @@ class AppLocalizations {
name: 'profiles');
}
String get profiles_description {
return Intl.message('Info about you, and options to manage it',
name: 'profiles_description');
String get profilesSettingExplanation {
return Intl.message(
'Info about you, and options to manage it',
name: 'profilesSettingExplanation'
);
}
String get identities {
@@ -1210,14 +1212,16 @@ class AppLocalizations {
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 identitiesSettingExplanation {
return Intl.message(
'Select the identity or email address you want to use to send an emails',
name: 'identitiesSettingExplanation');
}
String get create_new_identity {
return Intl.message('Create new identity',
name: 'create_new_identity');
String get createNewIdentity {
return Intl.message(
'Create new identity',
name: 'createNewIdentity');
}
String get new_identity {
+7
View File
@@ -411,6 +411,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
fading_edge_scrollview:
dependency: "direct main"
description:
name: fading_edge_scrollview
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
fake_async:
dependency: transitive
description:
+4 -1
View File
@@ -207,7 +207,10 @@ 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
html_unescape: 2.0.0
fading_edge_scrollview: 3.0.0
dev_dependencies:
flutter_test: