TF-1118 Apply new design for Forwarding on web

This commit is contained in:
dab246
2022-11-04 00:33:59 +07:00
committed by Dat H. Pham
parent bd78732fe9
commit b153de5c90
22 changed files with 936 additions and 414 deletions
+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 24C18.6274 24 24 18.6274 24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 5.37258 0 12C0 18.6274 5.37258 24 12 24ZM5 11C4.44772 11 4 11.4477 4 12C4 12.5523 4.44772 13 5 13H19C19.5523 13 20 12.5523 20 12C20 11.4477 19.5523 11 19 11H5Z" fill="#007AFF"/>
</svg>

After

Width:  |  Height:  |  Size: 403 B

@@ -133,7 +133,7 @@ extension AppColor on Color {
static const colorBackgroundSnackBar = Color(0xFF343438);
static const colorBackgroundHeaderListRuleFilter = Color(0xFFFAF7F7);
static const colorBorderListRuleFilter = Color(0xFFE7E8EC);
static const colorBackgroundHeaderListForwards = Color(0xFFFAF7F7);
static const colorBackgroundHeaderListForwards = Color(0xFFF8F9FA);
static const colorBorderListForwardsFilter = Color(0xFFE7E8EC);
static const colorBackgroundFieldConditionRulesFilter = Color(0xFFF2F3F5);
static const colorDividerRuleFilter = Color(0xFFE7E8EC);
@@ -157,6 +157,9 @@ extension AppColor on Color {
static const colorDividerDestinationPicker = Color(0x1F000000);
static const colorItemAlreadySelected = Color(0xFF818C99);
static const colorItemSelected = Color(0xFFF2F3F5);
static const colorBorderSettingContentWeb = Color(0xFFE7E8EC);
static const colorDividerHeaderSetting = Color(0xFFE4E4E4);
static const colorSettingExplanation = Color(0xFF686E76);
static const mapGradientColor = [
[Color(0xFF21D4FD), Color(0xFFB721FF)],
@@ -19,8 +19,17 @@ extension StringExtension on String {
}
} else {
final regexLetter = RegExp("([A-Za-z])");
final firstLetter = regexLetter.firstMatch(trim())?.group(0);
return firstLetter != null ? '${firstLetter.toUpperCase()}${firstLetter.toUpperCase()}' : '';
final listMatch = regexLetter.allMatches(trim()).toList();
if (listMatch.length > 1) {
final firstLetter = listMatch[0].group(0);
final secondLetter = listMatch[1].group(0);
return firstLetter != null && secondLetter != null
? '${firstLetter.toUpperCase()}${secondLetter.toUpperCase()}'
: '';
} else {
final firstLetter = listMatch[0].group(0);
return firstLetter != null ? firstLetter.toUpperCase() : '';
}
}
}
}
@@ -169,6 +169,7 @@ class ImagePaths {
String get icCreateNewFolder => _getImagePath('ic_create_new_folder.svg');
String get icOlder => _getImagePath('ic_older.svg');
String get icNewer => _getImagePath('ic_newer.svg');
String get icCancelSelection => _getImagePath('ic_cancel_selection.svg');
String _getImagePath(String imageName) {
return AssetsPaths.images + imageName;
@@ -21,7 +21,12 @@ mixin MessageDialogActionMixin {
String? cancelTitle,
bool hasCancelButton = true,
bool showAsBottomSheet = false,
Widget? icon
Widget? icon,
TextStyle? titleStyle,
TextStyle? actionStyle,
TextStyle? cancelStyle,
Color? actionButtonColor,
Color? cancelButtonColor,
}
) {
if (_responsiveUtils.isMobile(context)) {
@@ -40,16 +45,16 @@ mixin MessageDialogActionMixin {
..addIcon(icon)
..margin(const EdgeInsets.symmetric(vertical: 42, horizontal: 16))
..widthDialog(_responsiveUtils.getSizeScreenWidth(context))
..colorConfirmButton(AppColor.colorTextButton)
..colorCancelButton(AppColor.colorCancelButton)
..colorConfirmButton(actionButtonColor ?? AppColor.colorTextButton)
..colorCancelButton(cancelButtonColor ?? AppColor.colorCancelButton)
..paddingTitle(icon != null ? const EdgeInsets.only(top: 34) : EdgeInsets.zero)
..marginIcon(EdgeInsets.zero)
..paddingContent(const EdgeInsets.only(left: 44, right: 44, bottom: 24, top: 8))
..paddingButton(hasCancelButton ? null : const EdgeInsets.only(bottom: 16, left: 44, right: 44))
..styleTitle(const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black))
..styleTitle(titleStyle ?? const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black))
..styleContent(const TextStyle(fontSize: 14, fontWeight: FontWeight.normal, color: AppColor.colorContentEmail))
..styleTextCancelButton(const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton))
..styleTextConfirmButton(const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: Colors.white))
..styleTextCancelButton(cancelStyle ?? const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton))
..styleTextConfirmButton(actionStyle ?? const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: Colors.white))
..onConfirmButtonAction(actionName, () {
popBack();
onConfirmAction?.call();
@@ -76,16 +81,16 @@ mixin MessageDialogActionMixin {
..title(title ?? '')
..content(message)
..addIcon(icon)
..colorConfirmButton(AppColor.colorTextButton)
..colorCancelButton(AppColor.colorCancelButton)
..colorConfirmButton(actionButtonColor ?? AppColor.colorTextButton)
..colorCancelButton(cancelButtonColor ?? AppColor.colorCancelButton)
..paddingTitle(icon != null ? const EdgeInsets.only(top: 34) : EdgeInsets.zero)
..marginIcon(EdgeInsets.zero)
..paddingContent(const EdgeInsets.only(left: 44, right: 44, bottom: 24, top: 8))
..paddingButton(hasCancelButton ? null : const EdgeInsets.only(bottom: 16, left: 44, right: 44))
..styleTitle(const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black))
..styleTitle(titleStyle ?? const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black))
..styleContent(const TextStyle(fontSize: 14, fontWeight: FontWeight.normal, color: AppColor.colorContentEmail))
..styleTextCancelButton(const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton))
..styleTextConfirmButton(const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: Colors.white))
..styleTextCancelButton(cancelStyle ?? const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton))
..styleTextConfirmButton(actionStyle ?? const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: Colors.white))
..onConfirmButtonAction(actionName, () {
popBack();
onConfirmAction?.call();
@@ -50,4 +50,14 @@ extension ValicatorFailureExtension on VerifyNameFailure {
return '';
}
}
String getMessageForwarding(BuildContext context) {
if (exception is EmptyNameException) {
return AppLocalizations.of(context).this_field_cannot_be_blank;
} else if (exception is EmailAddressInvalidException) {
return AppLocalizations.of(context).incorrectEmailFormat;
} else {
return '';
}
}
}
@@ -1,12 +1,13 @@
import 'package:forward/forward/tmail_forward.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/recipient_forward.dart';
extension TMailForwardExtension on TMailForward {
List<RecipientForward> get listRecipientForward {
return forwards
.map((emailAddress) => RecipientForward(emailAddress))
.map((emailAddress) => RecipientForward(EmailAddress(emailAddress, emailAddress)))
.toList();
}
}
@@ -1,10 +1,12 @@
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/forward/forward_controller.dart';
class ForwardBindings extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => ForwardController());
Get.lazyPut(() => VerifyNameInteractor());
Get.lazyPut(() => ForwardController(Get.find<VerifyNameInteractor>()));
}
}
@@ -0,0 +1,97 @@
import 'package:contact/contact/model/capability_contact.dart';
import 'package:core/utils/app_logger.dart';
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:model/autocomplete/auto_complete_pattern.dart';
import 'package:tmail_ui_user/features/composer/domain/model/contact_suggestion_source.dart';
import 'package:tmail_ui_user/features/composer/domain/state/get_autocomplete_state.dart';
import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete_interactor.dart';
import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete_with_device_contact_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/bindings/contact_autocomplete_bindings.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/bindings/tmail_autocomplete_bindings.dart';
import 'package:tmail_ui_user/main/error/capability_validator.dart';
class ForwardRecipientController {
final ContactSuggestionSource _contactSuggestionSource = ContactSuggestionSource.tMailContact;
final TextEditingController inputEmailForwardController = TextEditingController();
GetAutoCompleteWithDeviceContactInteractor? _getAutoCompleteWithDeviceContactInteractor;
GetAutoCompleteInteractor? _getAutoCompleteInteractor;
AccountId? _accountId;
Session? _session;
EmailAddress? _emailAddressSelected;
ForwardRecipientController({AccountId? accountId, Session? session}) {
_accountId = accountId;
_session = session;
injectAutoCompleteBindings(_session, _accountId);
}
EmailAddress? get emailAddressSelected => _emailAddressSelected;
void onClose() {
clearAll();
_disposeWidget();
}
void injectAutoCompleteBindings(Session? session, AccountId? accountId) {
try {
ContactAutoCompleteBindings().dependencies();
requireCapability(session!, accountId!, [tmailContactCapabilityIdentifier]);
TMailAutoCompleteBindings().dependencies();
_getAutoCompleteWithDeviceContactInteractor = Get.find<GetAutoCompleteWithDeviceContactInteractor>();
_getAutoCompleteInteractor = Get.find<GetAutoCompleteInteractor>();
} catch (e) {
logError('ForwardRecipientController::injectAutoCompleteBindings(): exception: $e');
}
}
Future<List<EmailAddress>> getAutoCompleteSuggestion(String word) async {
log('ForwardRecipientController::getAutoCompleteSuggestion(): $word');
if (_contactSuggestionSource == ContactSuggestionSource.all) {
if (_getAutoCompleteWithDeviceContactInteractor == null || _getAutoCompleteInteractor == null) {
log('ForwardRecipientController::getAutoCompleteSuggestion(): _getAutoCompleteWithDeviceContactInteractor is NULL');
return <EmailAddress>[];
} else {
return await _getAutoCompleteWithDeviceContactInteractor!
.execute(AutoCompletePattern(word: word, accountId: _accountId))
.then((value) => value.fold(
(failure) => <EmailAddress>[],
(success) => success is GetAutoCompleteSuccess
? success.listEmailAddress
: <EmailAddress>[]));
}
} else {
if (_getAutoCompleteInteractor == null) {
log('ForwardRecipientController::getAutoCompleteSuggestion(): _getAutoCompleteInteractor is NULL');
return <EmailAddress>[];
} else {
return await _getAutoCompleteInteractor!
.execute(AutoCompletePattern(word: word, accountId: _accountId))
.then((value) => value.fold(
(failure) => <EmailAddress>[],
(success) => success is GetAutoCompleteSuccess
? success.listEmailAddress
: <EmailAddress>[]));
}
}
}
void selectEmailAddress(EmailAddress? emailAddress) {
_emailAddressSelected = emailAddress;
}
void clearAll() {
inputEmailForwardController.clear();
_emailAddressSelected = null;
}
void _disposeWidget() {
inputEmailForwardController.dispose();
}
}
@@ -1,11 +1,7 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/app_toast.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/views/bottom_popup/confirmation_dialog_action_sheet_builder.dart';
import 'package:core/presentation/views/dialog/confirmation_dialog_builder.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/build_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:forward/forward/tmail_forward.dart';
@@ -13,9 +9,12 @@ import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:model/extensions/email_address_extension.dart';
import 'package:model/mailbox/select_mode.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/emails_forward_creator/presentation/model/email_forward_creator_arguments.dart';
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/email_address_validator.dart';
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/empty_name_validator.dart';
import 'package:tmail_ui_user/features/mailbox_creator/domain/state/verify_name_view_state.dart';
import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_creator/presentation/extensions/validator_failure_extension.dart';
import 'package:tmail_ui_user/features/manage_account/domain/model/add_recipients_in_forwarding_request.dart';
import 'package:tmail_ui_user/features/manage_account/domain/model/delete_recipient_in_forwarding_request.dart';
import 'package:tmail_ui_user/features/manage_account/domain/model/edit_local_copy_in_forwarding_request.dart';
@@ -29,33 +28,39 @@ import 'package:tmail_ui_user/features/manage_account/domain/usecases/delete_rec
import 'package:tmail_ui_user/features/manage_account/domain/usecases/edit_local_copy_in_forwarding_interactor.dart';
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_forward_interactor.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/tmail_forward_extension.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/forward/controller/forward_recipient_controller.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/manage_account_dashboard_controller.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/recipient_forward.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
class ForwardController extends BaseController {
final VerifyNameInteractor _verifyNameInteractor;
GetForwardInteractor? _getForwardInteractor;
DeleteRecipientInForwardingInteractor? _deleteRecipientInForwardingInteractor;
AddRecipientsInForwardingInteractor? _addRecipientsInForwardingInteractor;
EditLocalCopyInForwardingInteractor? _editLocalCopyInForwardingInteractor;
final accountDashBoardController = Get.find<ManageAccountDashBoardController>();
final _responsiveUtils = Get.find<ResponsiveUtils>();
final _imagePaths = Get.find<ImagePaths>();
final _appToast = Get.find<AppToast>();
final selectionMode = Rx<SelectMode>(SelectMode.INACTIVE);
final listRecipientForward = RxList<RecipientForward>();
final currentForward = Rxn<TMailForward>();
final listForwards = <String>[].obs;
final errorRecipientInputText = RxnString();
bool get currentForwardLocalCopyState => currentForward.value?.localCopy ?? false;
ForwardController();
late ForwardRecipientController recipientController;
ForwardController(this._verifyNameInteractor) {
recipientController = ForwardRecipientController(
accountId: accountDashBoardController.accountId.value,
session: accountDashBoardController.sessionCurrent.value);
}
@override
void onInit() {
@@ -70,6 +75,12 @@ class ForwardController extends BaseController {
}
}
@override
void onClose() {
recipientController.onClose();
super.onClose();
}
@override
void onDone() {
viewState.value.fold(
@@ -105,39 +116,28 @@ class ForwardController extends BaseController {
}
void deleteRecipients(BuildContext context, String emailAddress) {
if (_responsiveUtils.isMobile(context)) {
(ConfirmationDialogActionSheetBuilder(context)
..messageText(AppLocalizations.of(context)
.messageConfirmationDialogDeleteRecipientForward(emailAddress))
..onCancelAction(AppLocalizations.of(context).cancel, () =>
popBack())
..onConfirmAction(AppLocalizations.of(context).delete, () =>
_handleDeleteRecipientAction({emailAddress})))
.show();
} else {
showDialog(
context: context,
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
builder: (BuildContext context) =>
PointerInterceptor(child: (ConfirmDialogBuilder(_imagePaths)
..title(AppLocalizations.of(context).deleteRecipient)
..content(AppLocalizations.of(context)
.messageConfirmationDialogDeleteRecipientForward(emailAddress))
..addIcon(SvgPicture.asset(_imagePaths.icRemoveDialog,
fit: BoxFit.fill))
..marginIcon(EdgeInsets.zero)
..colorConfirmButton(AppColor.colorConfirmActionDialog)
..styleTextConfirmButton(const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w500,
color: AppColor.colorActionDeleteConfirmDialog))
..onCloseButtonAction(() => popBack())
..onConfirmButtonAction(AppLocalizations.of(context).delete, () =>
_handleDeleteRecipientAction({emailAddress}))
..onCancelButtonAction(AppLocalizations.of(context).cancel, () =>
popBack()))
.build()));
}
showConfirmDialogAction(currentContext!,
title: AppLocalizations.of(context).deleteRecipient,
AppLocalizations.of(context).messageConfirmationDialogDeleteRecipientForward(emailAddress),
AppLocalizations.of(currentContext!).remove,
onConfirmAction: () => _handleDeleteRecipientAction({emailAddress}),
showAsBottomSheet: true,
icon: SvgPicture.asset(_imagePaths.icDeleteDialogIdentity, fit: BoxFit.fill),
titleStyle: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: AppColor.colorDeletePermanentlyButton),
actionStyle: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w500,
color: Colors.white),
cancelStyle: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w500,
color: AppColor.colorTextButton),
actionButtonColor: AppColor.colorDeletePermanentlyButton,
cancelButtonColor: AppColor.colorButtonCancelDialog,
);
}
void _handleDeleteRecipientAction(Set<String> listRecipients) {
@@ -201,39 +201,28 @@ class ForwardController extends BaseController {
}
void deleteMultipleRecipients(BuildContext context, Set<String> listEmailAddress) {
if (_responsiveUtils.isMobile(context)) {
(ConfirmationDialogActionSheetBuilder(context)
..messageText(AppLocalizations.of(context).messageConfirmationDialogDeleteAllRecipientForward)
..onCancelAction(AppLocalizations.of(context).cancel, () =>
popBack())
..onConfirmAction(AppLocalizations.of(context).delete, () {
_handleDeleteRecipientAction(listEmailAddress);
}))
.show();
} else {
showDialog(
context: context,
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
builder: (BuildContext context) =>
PointerInterceptor(child: (ConfirmDialogBuilder(_imagePaths)
..title(AppLocalizations.of(context).deleteRecipient)
..content(AppLocalizations.of(context).messageConfirmationDialogDeleteAllRecipientForward)
..addIcon(SvgPicture.asset(_imagePaths.icRemoveDialog,
fit: BoxFit.fill))
..marginIcon(EdgeInsets.zero)
..colorConfirmButton(AppColor.colorConfirmActionDialog)
..styleTextConfirmButton(const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w500,
color: AppColor.colorActionDeleteConfirmDialog))
..onCloseButtonAction(() => popBack())
..onConfirmButtonAction(AppLocalizations.of(context).delete, () {
_handleDeleteRecipientAction(listEmailAddress);
})
..onCancelButtonAction(AppLocalizations.of(context).cancel, () =>
popBack()))
.build()));
}
showConfirmDialogAction(currentContext!,
title: AppLocalizations.of(context).deleteRecipient,
AppLocalizations.of(context).messageConfirmationDialogDeleteAllRecipientForward,
AppLocalizations.of(currentContext!).remove,
onConfirmAction: () => _handleDeleteRecipientAction(listEmailAddress),
showAsBottomSheet: true,
icon: SvgPicture.asset(_imagePaths.icDeleteDialogIdentity, fit: BoxFit.fill),
titleStyle: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: AppColor.colorDeletePermanentlyButton),
actionStyle: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w500,
color: Colors.white),
cancelStyle: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w500,
color: AppColor.colorTextButton),
actionButtonColor: AppColor.colorDeletePermanentlyButton,
cancelButtonColor: AppColor.colorButtonCancelDialog,
);
}
void selectAllRecipientForward() {
@@ -246,26 +235,19 @@ class ForwardController extends BaseController {
.toList();
}
void goToAddEmailsForward(BuildContext context) async {
void addRecipientAction(BuildContext context) {
final accountId = accountDashBoardController.accountId.value;
final session = accountDashBoardController.sessionCurrent.value;
if (accountId != null && session != null) {
final arguments = EmailForwardCreatorArguments(accountId, session);
final emailAddressSelected = recipientController.emailAddressSelected;
if (BuildUtils.isWeb) {
showDialogEmailForwardCreator(
context: context,
arguments: arguments,
onAddEmailForward: (listEmailAddress) => _handleAddRecipients(accountId, listEmailAddress));
} else {
final newListEmailAddress = await push(
AppRoutes.emailsForwardCreator,
arguments: arguments);
updateErrorRecipientInputText(context, emailAddressSelected?.email);
if (newListEmailAddress is List<EmailAddress> && newListEmailAddress.isNotEmpty) {
_handleAddRecipients(accountId, newListEmailAddress);
}
}
if (errorRecipientInputText.value != null) {
return;
}
log('ForwardController::addRecipientAction():emailAddressSelected: $emailAddressSelected');
if (emailAddressSelected != null && accountId != null) {
_handleAddRecipients(accountId, [emailAddressSelected]);
}
}
@@ -291,6 +273,8 @@ class ForwardController extends BaseController {
currentForward.value = success.forward;
listRecipientForward.value = currentForward.value!.listRecipientForward;
recipientController.clearAll();
}
void handleEditLocalCopy() {
@@ -320,4 +304,24 @@ class ForwardController extends BaseController {
currentForward.value = success.forward;
listRecipientForward.value = currentForward.value!.listRecipientForward;
}
void updateErrorRecipientInputText(BuildContext context, String? emailAddress) {
errorRecipientInputText.value = _getErrorRecipientInputString(context, emailAddress);
}
String? _getErrorRecipientInputString(BuildContext context, String? emailAddress) {
return _verifyNameInteractor.execute(
emailAddress,
[EmptyNameValidator(), EmailAddressValidator()]
).fold(
(failure) {
if (failure is VerifyNameFailure) {
return failure.getMessageForwarding(context);
} else {
return null;
}
},
(success) => null
);
}
}
@@ -1,11 +1,20 @@
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/state/success.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/utils/style_utils.dart';
import 'package:core/presentation/views/button/button_builder.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/forward/forward_controller.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/forward/widgets/forward_header_widget.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/forward/widgets/list_email_forward_widget.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/forward/widgets/recipient_input_field_builder.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings_utils.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ForwardView extends GetWidget<ForwardController> with AppLoaderMixin {
final _responsiveUtils = Get.find<ResponsiveUtils>();
@@ -16,38 +25,162 @@ class ForwardView extends GetWidget<ForwardController> with AppLoaderMixin {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: _responsiveUtils.isWebDesktop(context)
? AppColor.colorBgDesktop
: Colors.white,
backgroundColor: SettingsUtils.getBackgroundColor(context, _responsiveUtils),
body: Container(
width: double.infinity,
height: double.infinity,
color: SettingsUtils.getContentBackgroundColor(context, _responsiveUtils),
decoration: SettingsUtils.getBoxDecorationForContent(context, _responsiveUtils),
margin: SettingsUtils.getMarginViewForSettingDetails(context, _responsiveUtils),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ForwardHeaderWidget(
imagePaths: _imagePaths,
responsiveUtils: _responsiveUtils,
),
SizedBox(height: _responsiveUtils.isWebDesktop(context) ? 24 : 16),
_buildLoadingView(),
ListEmailForwardsWidget()
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ForwardHeaderWidget(imagePaths: _imagePaths, responsiveUtils: _responsiveUtils),
Container(height: 1, color: AppColor.colorDividerHeaderSetting),
Expanded(child: SingleChildScrollView(
child: Column(children: [
_buildLoadingView(),
Obx(() {
if (controller.currentForward.value != null) {
return Column(children: [
_buildKeepLocalSwitchButton(context),
_buildAddRecipientsFormWidget(context),
ListEmailForwardsWidget()
]);
} else {
return const SizedBox.shrink();
}
})
])
))
],
),
),
);
}
Widget _buildKeepLocalSwitchButton(BuildContext context) {
return Container(
color: Colors.transparent,
padding: const EdgeInsets.only(left: 24, top: 24, right: 24, bottom: 11),
child: Row(children: [
Obx(() {
return InkWell(
onTap: controller.handleEditLocalCopy,
child: SvgPicture.asset(
controller.currentForwardLocalCopyState
? _imagePaths.icSwitchOn
: _imagePaths.icSwitchOff,
fit: BoxFit.fill,
width: 24,
height: 24)
);
}),
const SizedBox(width: 16),
Expanded(
child: Text(
AppLocalizations.of(context).keepLocalCopyForwardLabel,
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black)
),
)
]),
);
}
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()
? Padding(
padding: const EdgeInsets.all(24),
child: loadingWidget)
: const SizedBox.shrink()
));
}
Widget _buildAddRecipientInputField() {
return Obx(() => RecipientInputFieldBuilder(
editingController: controller.recipientController.inputEmailForwardController,
onSelectedSuggestionAction: (newEmailAddress) {
controller.recipientController.selectEmailAddress(newEmailAddress);
controller.recipientController.inputEmailForwardController.text = newEmailAddress.email ?? '';
},
onRecipientInputTextChange: (pattern) {
if (pattern.trim().isNotEmpty) {
controller.recipientController.selectEmailAddress(EmailAddress(null, pattern.trim()));
} else {
controller.recipientController.selectEmailAddress(null);
}
controller.errorRecipientInputText.value = null;
},
onSummitedCallbackAction: (pattern) {
if (pattern.trim().isNotEmpty) {
controller.recipientController.selectEmailAddress(EmailAddress(null, pattern.trim()));
} else {
controller.recipientController.selectEmailAddress(null);
}
},
onSuggestionCallbackAction: controller.recipientController.getAutoCompleteSuggestion,
errorText: controller.errorRecipientInputText.value,
));
}
Widget _buildAddRecipientButton(BuildContext context) {
return (ButtonBuilder(_imagePaths.icAddIdentity)
..key(const Key('button_add_recipient'))
..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.addRecipientAction(context))
..text(AppLocalizations.of(context).addRecipientButton, isVertical: false)
).build();
}
Widget _buildAddRecipientsFormWidget(BuildContext context) {
if (_responsiveUtils.isPortraitMobile(context)) {
return Container(
color: Colors.transparent,
width: double.infinity,
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildAddRecipientInputField(),
const SizedBox(height: 16),
_buildAddRecipientButton(context)
],
),
);
} else {
return Container(
color: Colors.transparent,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(child: _buildAddRecipientInputField()),
Padding(
padding: const EdgeInsets.only(left: 24, right: 12),
child: _buildAddRecipientButton(context))
],
),
);
}
}
}
@@ -2,6 +2,7 @@ import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:model/extensions/email_address_extension.dart';
import 'package:model/mailbox/select_mode.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/forward/forward_controller.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/recipient_forward.dart';
@@ -13,10 +14,12 @@ class EmailForwardItemWidget extends StatelessWidget {
final RecipientForward recipientForward;
final SelectMode selectionMode;
final bool isLast;
EmailForwardItemWidget({
Key? key,
required this.recipientForward,
this.isLast = false,
this.selectionMode = SelectMode.INACTIVE,
}) : super(key: key);
@@ -30,32 +33,70 @@ class EmailForwardItemWidget extends StatelessWidget {
}
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),
color: Colors.white,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: recipientForward.selectMode == SelectMode.ACTIVE
? AppColor.colorItemEmailSelectedDesktop
: Colors.white,
borderRadius: BorderRadius.only(
bottomLeft: isLast ? const Radius.circular(12) : Radius.zero,
bottomRight: isLast ? const Radius.circular(12) : Radius.zero,
)
),
child: Row(crossAxisAlignment: CrossAxisAlignment.center, children: [
Container(
width: 32,
height: 32,
color: Colors.transparent,
alignment: Alignment.center,
child: _buildAvatarIcon()),
const SizedBox(width: 10),
Expanded(child: Text(recipientForward.emailAddress,
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
color: Colors.black))),
width: 54,
height: 54,
color: Colors.transparent,
alignment: Alignment.center,
child: _buildAvatarIcon()
),
const SizedBox(width: 12),
Expanded(child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (recipientForward.emailAddress.displayName.isNotEmpty)
Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Text(
recipientForward.emailAddress.displayName,
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap,
maxLines: 1,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black
)
),
),
Text(
recipientForward.emailAddress.email ?? '',
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap,
maxLines: 1,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: AppColor.colorContentEmail
)
)
],
)),
const SizedBox(width: 12),
buildIconWeb(
icon: SvgPicture.asset(
_imagePaths.icDeleteEmailForward,
fit: BoxFit.fill,
width: 18,
height: 18,
),
onTap: () =>
_emailForwardController.deleteRecipients(context, recipientForward.emailAddress)),
icon: SvgPicture.asset(
_imagePaths.icDeleteEmailForward,
fit: BoxFit.fill,
width: 18,
height: 18,
),
onTap: () => _emailForwardController.deleteRecipients(
context,
recipientForward.emailAddress.email ?? ''
)
),
]),
),
);
@@ -64,22 +105,25 @@ class EmailForwardItemWidget extends StatelessWidget {
Widget _buildAvatarIcon() {
if (selectionMode == SelectMode.ACTIVE) {
return Container(
alignment: Alignment.center,
child: SvgPicture.asset(
recipientForward.selectMode == SelectMode.ACTIVE
? _imagePaths.icSelected
: _imagePaths.icUnSelected,
width: 24,
height: 24));
alignment: Alignment.center,
child: SvgPicture.asset(
recipientForward.selectMode == SelectMode.ACTIVE
? _imagePaths.icSelected
: _imagePaths.icUnSelected,
width: 24,
height: 24
)
);
} else {
return CircleAvatar(
backgroundColor: AppColor.colorTextButton,
radius: 16,
child: Text(recipientForward.emailAddress[0].toUpperCase(),
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w500)));
return (AvatarBuilder()
..text(recipientForward.emailAddress.asString().firstLetterToUpperCase)
..size(54)
..addTextStyle(const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 20,
color: Colors.white))
..avatarColor(recipientForward.emailAddress.avatarColors)
).build();
}
}
}
@@ -1,8 +1,14 @@
import 'package:core/core.dart';
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/extensions/string_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:core/presentation/views/image/avatar_builder.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:html_editor_enhanced/utils/utils.dart';
import 'package:model/extensions/email_address_extension.dart';
import 'package:model/mailbox/select_mode.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/forward/forward_controller.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/recipient_forward.dart';
@@ -11,10 +17,12 @@ class EmailForwardItemWidget extends StatefulWidget {
final RecipientForward recipientForward;
final SelectMode selectionMode;
final bool isLast;
const EmailForwardItemWidget({
Key? key,
required this.recipientForward,
this.isLast = false,
this.selectionMode = SelectMode.INACTIVE,
}) : super(key: key);
@@ -28,48 +36,97 @@ class _EmailForwardItemWidgetState extends State<EmailForwardItemWidget> {
final _emailForwardController = Get.find<ForwardController>();
bool isHoverIcon = false;
bool isHoverItem = false;
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),
color: Colors.white,
child: Row(crossAxisAlignment: CrossAxisAlignment.center, children: [
InkWell(
onTap: () {
if (isHoverIcon) {
_emailForwardController.selectRecipientForward(widget.recipientForward);
}
},
onHover: (value) {
setState(mounted, this.setState, () {
isHoverIcon = value;
});
},
child: Container(
width: 32,
height: 32,
return InkWell(
onTap: () => {},
onHover: (value) {
setState(mounted, this.setState, () {
isHoverItem = value;
});
},
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: widget.recipientForward.selectMode == SelectMode.ACTIVE
? AppColor.colorItemEmailSelectedDesktop
: Colors.white,
borderRadius: BorderRadius.only(
bottomLeft: widget.isLast ? const Radius.circular(12) : Radius.zero,
bottomRight: widget.isLast ? const Radius.circular(12) : Radius.zero,
)
),
child: Row(crossAxisAlignment: CrossAxisAlignment.center, children: [
InkWell(
onTap: () {
if (isHoverIcon) {
_emailForwardController.selectRecipientForward(widget.recipientForward);
}
},
onHover: (value) {
setState(mounted, this.setState, () {
isHoverIcon = value;
});
},
child: Container(
width: 54,
height: 54,
color: Colors.transparent,
alignment: Alignment.center,
child: _buildAvatarIcon())),
const SizedBox(width: 10),
Expanded(child: Text(widget.recipientForward.emailAddress,
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
color: Colors.black))),
buildIconWeb(
icon: SvgPicture.asset(
_imagePaths.icDeleteEmailForward,
fit: BoxFit.fill,
width: 18,
height: 18,
child: _buildAvatarIcon()
)
),
const SizedBox(width: 12),
Expanded(child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (widget.recipientForward.emailAddress.displayName.isNotEmpty)
Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Text(
widget.recipientForward.emailAddress.displayName,
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap,
maxLines: 1,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black
)
),
),
Text(
widget.recipientForward.emailAddress.email ?? '',
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap,
maxLines: 1,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: AppColor.colorContentEmail
)
)
],
)),
const SizedBox(width: 12),
if (isHoverItem)
buildIconWeb(
icon: SvgPicture.asset(
_imagePaths.icDeleteEmailForward,
fit: BoxFit.fill,
width: 18,
height: 18,
),
onTap: () => _emailForwardController.deleteRecipients(
context,
widget.recipientForward.emailAddress.email ?? ''
)
),
onTap: () =>
_emailForwardController.deleteRecipients(context, widget.recipientForward.emailAddress)),
]),
]),
),
);
}
@@ -84,14 +141,15 @@ class _EmailForwardItemWidgetState extends State<EmailForwardItemWidget> {
width: 24,
height: 24));
} else {
return CircleAvatar(
backgroundColor: AppColor.colorTextButton,
radius: 16,
child: Text(widget.recipientForward.emailAddress[0].toUpperCase(),
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w500)));
return (AvatarBuilder()
..text(widget.recipientForward.emailAddress.asString().firstLetterToUpperCase)
..size(54)
..addTextStyle(const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 20,
color: Colors.white))
..avatarColor(widget.recipientForward.emailAddress.avatarColors)
).build();
}
}
}
@@ -2,13 +2,10 @@ 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/utils/style_utils.dart';
import 'package:core/presentation/views/button/button_builder.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/forward/forward_controller.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ForwardHeaderWidget extends GetWidget<ForwardController> {
class ForwardHeaderWidget extends StatelessWidget {
const ForwardHeaderWidget({
Key? key,
required this.imagePaths,
@@ -21,97 +18,29 @@ class ForwardHeaderWidget extends GetWidget<ForwardController> {
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: AppColor.colorBackgroundWrapIconStyleCode,
borderRadius: BorderRadius.circular(16),
),
color: Colors.transparent,
padding: const EdgeInsets.only(top: 24, left: 24, right: 24, bottom: 11),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(AppLocalizations.of(context).forwarding,
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.bold,
color: Colors.black)),
const SizedBox(height: 24),
_buildButtonAddNewEmailsForward(context),
const SizedBox(height: 24),
Obx(() => _buildButtonSwitchKeepLocalCopy(context)),
Text(
AppLocalizations.of(context).forwarding,
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap,
maxLines: 1,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black)),
const SizedBox(height: 8),
Text(
AppLocalizations.of(context).forwardingSettingExplanation,
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap,
maxLines: 3,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: AppColor.colorSettingExplanation)),
]),
);
}
Widget _buildButtonAddNewEmailsForward(BuildContext context) {
if (!responsiveUtils.isMobile(context)) {
return (ButtonBuilder(imagePaths.icAddEmailForward)
..key(const Key('button_add_emails_forward'))
..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.goToAddEmailsForward(context))
..text(
AppLocalizations.of(context).addRecipients,
isVertical: false,
))
.build();
} else {
return (ButtonBuilder(imagePaths.icAddEmailForward)
..key(const Key('button_add_emails_forward'))
..decoration(BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: AppColor.colorTextButton))
..paddingIcon(const EdgeInsets.only(right: 8))
..iconColor(Colors.white)
..size(20)
..radiusSplash(10)
..padding(const EdgeInsets.symmetric(vertical: 12))
..textStyle(const TextStyle(
fontSize: 17,
color: Colors.white,
fontWeight: FontWeight.w500,
))
..onPressActionClick(() => controller.goToAddEmailsForward(context))
..text(
AppLocalizations.of(context).addRecipients,
isVertical: false,
))
.build();
}
}
Widget _buildButtonSwitchKeepLocalCopy(BuildContext context) {
if(controller.currentForward.value != null) {
return SizedBox(
width: 250,
child: SwitchListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 0),
title: Text(AppLocalizations.of(context).keepLocalCopyForwardLabel,
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
color: Colors.black)),
value: controller.currentForwardLocalCopyState,
onChanged: (_) {
controller.handleEditLocalCopy();
},
controlAffinity: ListTileControlAffinity.trailing,
),
);
} else {
return const SizedBox.shrink();
}
}
}
@@ -6,79 +6,29 @@ import 'package:model/mailbox/select_mode.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/forward/forward_controller.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/forward/widgets/email_forward_item_widget.dart'
if (dart.library.html) 'package:tmail_ui_user/features/manage_account/presentation/forward/widgets/email_forward_item_widget_for_web.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings_utils.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/recipient_forward.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ListEmailForwardsWidget extends GetWidget<ForwardController> {
final _imagePaths = Get.find<ImagePaths>();
final _responsiveUtils = Get.find<ResponsiveUtils>();
ListEmailForwardsWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: AppColor.colorBackgroundWrapIconStyleCode,
borderRadius: BorderRadius.circular(16),
border: Border.all(
width: 1,
color: AppColor.colorBorderListForwardsFilter)
),
decoration: SettingsUtils.getBoxDecorationForListRecipient(context, _responsiveUtils),
margin: SettingsUtils.getPaddingListRecipientForwarding(context, _responsiveUtils),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
borderRadius: BorderRadius.circular(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Obx(() => Container(
width: double.infinity,
decoration: const BoxDecoration(
color: AppColor.colorBackgroundHeaderListForwards,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16)),
),
padding: controller.selectionMode.value == SelectMode.INACTIVE
? const EdgeInsets.symmetric(vertical: 17, horizontal: 24)
: const EdgeInsets.symmetric(vertical: 13, horizontal: 24),
child: Row(children: [
Expanded(child: Text(
AppLocalizations.of(context).headerRecipients,
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: AppColor.colorTextButtonHeaderThread))),
Obx(() {
if (controller.selectionMode.value == SelectMode.ACTIVE) {
return _buildListButtonSelection(context);
} else {
if (controller.listRecipientForward.isNotEmpty) {
return (ButtonBuilder(_imagePaths.icSelectAll)
..decoration(BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: AppColor.colorButtonHeaderThread))
..paddingIcon(const EdgeInsets.only(right: 8))
..size(16)
..radiusSplash(10)
..padding(const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8))
..textStyle(const TextStyle(
fontSize: 12,
color: AppColor.colorTextButtonHeaderThread))
..onPressActionClick(() => controller.selectAllRecipientForward())
..text(AppLocalizations.of(context).select_all, isVertical: false))
.build();
} else {
return const SizedBox.shrink();
}
}
})
]),
)),
_buildTitleHeader(context),
const Divider(
color: AppColor.lineItemListColor,
height: 1,
@@ -92,13 +42,20 @@ class ListEmailForwardsWidget extends GetWidget<ForwardController> {
final recipientForward = controller.listRecipientForward[index];
return EmailForwardItemWidget(
recipientForward: recipientForward,
isLast: index == controller.listRecipientForward.length - 1,
selectionMode: controller.selectionMode.value);
},
separatorBuilder: (context, index) => const Divider(
color: AppColor.lineItemListColor,
height: 1,
thickness: 0.2,
),
separatorBuilder: (context, index) {
if (index != controller.listRecipientForward.length - 1) {
return const Divider(
color: AppColor.lineItemListColor,
height: 1,
thickness: 0.2,
);
} else {
return const SizedBox.shrink();
}
},
);
}),
]),
@@ -106,26 +63,76 @@ class ListEmailForwardsWidget extends GetWidget<ForwardController> {
);
}
Widget _buildListButtonSelection(BuildContext context) {
return Row(mainAxisSize: MainAxisSize.min, children: [
buildIconWeb(
icon: SvgPicture.asset(
_imagePaths.icCloseComposer,
color: AppColor.colorTextButton,
fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).cancel,
onTap: () => controller.cancelSelectionMode()),
const SizedBox(width: 5),
buildIconWeb(
icon: SvgPicture.asset(
_imagePaths.icDelete,
color: AppColor.colorActionDeleteConfirmDialog,
fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).delete_all,
onTap: () =>
controller.deleteMultipleRecipients(
context,
controller.listRecipientForwardSelected.listEmailAddress))
]);
Widget _buildTitleHeader(BuildContext context) {
return Obx(() => Container(
width: double.infinity,
decoration: const BoxDecoration(
color: AppColor.colorBackgroundHeaderListForwards,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16)
),
),
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12),
child: Row(children: [
_buildSelectAllButton(),
const SizedBox(width: 12),
Expanded(child: Text(
AppLocalizations.of(context).headerRecipients,
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: AppColor.colorTextButtonHeaderThread
)
)),
const SizedBox(width: 12),
if (controller.listRecipientForwardSelected.isNotEmpty)
_buildDeleteAllButton(context)
]),
));
}
Widget _buildSelectAllButton() {
return Material(
child: InkWell(
customBorder: const CircleBorder(),
child: Padding(
padding: const EdgeInsets.all(16),
child: SvgPicture.asset(
controller.selectionMode.value == SelectMode.ACTIVE
? _imagePaths.icCancelSelection
: _imagePaths.icUnSelected,
width: 24,
height: 24
),
),
onTap: () {
if (controller.selectionMode.value == SelectMode.ACTIVE) {
controller.cancelSelectionMode();
} else {
controller.selectAllRecipientForward();
}
},
),
color: Colors.transparent,
);
}
Widget _buildDeleteAllButton(BuildContext context) {
return buildIconWeb(
icon: SvgPicture.asset(
_imagePaths.icDeleteEmailForward,
fit: BoxFit.fill,
width: 18,
height: 18,
),
tooltip: AppLocalizations.of(context).delete_all,
onTap: () => controller.deleteMultipleRecipients(
context,
controller.listRecipientForwardSelected.listEmailAddress
)
);
}
}
@@ -0,0 +1,41 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/views/text/input_decoration_builder.dart';
import 'package:core/utils/build_utils.dart';
import 'package:flutter/material.dart';
class RecipientInputDecorationBuilder extends InputDecorationBuilder {
@override
InputDecoration build() {
return InputDecoration(
enabledBorder: enabledBorder ?? const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
borderSide: BorderSide(width: 0.5, color: AppColor.colorInputBorderCreateMailbox)),
focusedBorder: enabledBorder ?? const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
borderSide: BorderSide(width: 0.5, color: AppColor.colorTextButton)),
errorBorder: errorBorder ?? const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
borderSide: BorderSide(width: 0.5, color: AppColor.colorInputBorderErrorVerifyName)),
focusedErrorBorder: errorBorder ?? const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
borderSide: BorderSide(width: 0.5, color: AppColor.colorInputBorderErrorVerifyName)),
prefixText: prefixText,
labelText: labelText,
floatingLabelBehavior: FloatingLabelBehavior.never,
labelStyle: labelStyle ?? const TextStyle(color: Colors.black, fontSize: 16),
hintText: hintText,
isDense: true,
hintStyle: hintStyle ?? const TextStyle(color: AppColor.colorEmailAddressFull, fontSize: 16),
contentPadding: const EdgeInsets.symmetric(
vertical: BuildUtils.isWeb ? 16 : 12,
horizontal: 12),
errorText: errorText,
errorStyle: errorTextStyle ?? const TextStyle(color: AppColor.colorInputBorderErrorVerifyName, fontSize: 13),
filled: true,
fillColor: errorText?.isNotEmpty == true
? AppColor.colorInputBackgroundErrorVerifyName
: AppColor.colorInputBackgroundCreateMailbox);
}
}
@@ -0,0 +1,81 @@
import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/forward/widgets/recipient_input_decoration_builder.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnSelectedSuggestionAction = Function(EmailAddress emailAddress);
typedef OnSuggestionCallbackAction = Function(String pattern);
typedef OnSummitedCallbackAction = Function(String pattern);
typedef OnRecipientInputTextChange = Function(String pattern);
class RecipientInputFieldBuilder extends StatelessWidget {
final TextEditingController? editingController;
final OnSelectedSuggestionAction? onSelectedSuggestionAction;
final OnSuggestionCallbackAction? onSuggestionCallbackAction;
final OnSummitedCallbackAction? onSummitedCallbackAction;
final OnRecipientInputTextChange? onRecipientInputTextChange;
final String? errorText;
const RecipientInputFieldBuilder({
Key? key,
this.editingController,
this.onSelectedSuggestionAction,
this.onSuggestionCallbackAction,
this.onSummitedCallbackAction,
this.onRecipientInputTextChange,
this.errorText
}) : super(key: key);
@override
Widget build(BuildContext context) {
return TypeAheadFormField<EmailAddress>(
textFieldConfiguration: TextFieldConfiguration(
controller: editingController,
onSubmitted: (pattern) {
if (onSummitedCallbackAction != null) {
onSummitedCallbackAction!(pattern);
}
},
onChanged: onRecipientInputTextChange,
textInputAction: TextInputAction.done,
decoration: (RecipientInputDecorationBuilder()
..setHintText(AppLocalizations.of(context).recipientsLabel)
..setErrorText(errorText)
).build()
),
debounceDuration: const Duration(milliseconds: 500),
suggestionsCallback: (pattern) async {
if (onSuggestionCallbackAction != null) {
return onSuggestionCallbackAction!(pattern);
} else {
return [];
}
},
itemBuilder: (BuildContext context, emailAddress) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
child: Text(emailAddress.email ?? '',
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: Colors.black)),
);
},
onSuggestionSelected: (emailSelected) {
if (onSelectedSuggestionAction != null) {
onSelectedSuggestionAction!(emailSelected);
}
},
suggestionsBoxDecoration: SuggestionsBoxDecoration(
borderRadius: BorderRadius.circular(14)
),
noItemsFoundBuilder: (context) => const SizedBox(),
hideOnEmpty: true,
hideOnError: true,
hideOnLoading: true,
);
}
}
@@ -1,6 +1,7 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/utils/build_utils.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
class SettingsUtils {
static double getHorizontalPadding(BuildContext context, ResponsiveUtils responsiveUtils) {
@@ -30,7 +31,7 @@ class SettingsUtils {
static EdgeInsets getMarginViewForSettingDetails(BuildContext context, ResponsiveUtils responsiveUtils) {
if (BuildUtils.isWeb) {
if (responsiveUtils.isDesktop(context)) {
return const EdgeInsets.only(left: 16, top: 16, right: 24, bottom: 24);
return const EdgeInsets.all(16);
} else if (responsiveUtils.isTabletLarge(context) ||
responsiveUtils.isTablet(context)) {
return const EdgeInsets.only(right: 32, top: 16, bottom: 16, left: 32);
@@ -49,4 +50,50 @@ class SettingsUtils {
}
}
}
static BoxDecoration? getBoxDecorationForContent(BuildContext context, ResponsiveUtils responsiveUtils) {
if (responsiveUtils.isWebDesktop(context)) {
return BoxDecoration(
borderRadius: BorderRadius.circular(16),
border: Border.all(color: AppColor.colorBorderSettingContentWeb, width: 1),
color: Colors.white);
} else {
return null;
}
}
static Color? getContentBackgroundColor(BuildContext context, ResponsiveUtils responsiveUtils) {
if (responsiveUtils.isWebDesktop(context)) {
return null;
} else {
return Colors.white;
}
}
static Color? getBackgroundColor(BuildContext context, ResponsiveUtils responsiveUtils) {
if (responsiveUtils.isWebDesktop(context)) {
return AppColor.colorBgDesktop;
} else {
return Colors.white;
}
}
static EdgeInsets getPaddingListRecipientForwarding(BuildContext context, ResponsiveUtils responsiveUtils) {
if (responsiveUtils.isPortraitMobile(context)) {
return const EdgeInsets.all(12);
} else {
return const EdgeInsets.symmetric(horizontal: 24, vertical: 12);
}
}
static BoxDecoration? getBoxDecorationForListRecipient(BuildContext context, ResponsiveUtils responsiveUtils) {
if (responsiveUtils.isWebDesktop(context)) {
return BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColor.colorBorderSettingContentWeb, width: 1),
color: Colors.white);
} else {
return null;
}
}
}
@@ -1,9 +1,10 @@
import 'package:equatable/equatable.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:model/mailbox/select_mode.dart';
class RecipientForward with EquatableMixin {
final String emailAddress;
final EmailAddress emailAddress;
final SelectMode selectMode;
RecipientForward(this.emailAddress, {this.selectMode = SelectMode.INACTIVE});
@@ -35,5 +36,5 @@ extension RecipientForwardExtension on RecipientForward {
extension ListRecipientForwardExtension on List<RecipientForward> {
Set<String> get listEmailAddress =>
map((recipient) => recipient.emailAddress).toSet();
map((recipient) => recipient.emailAddress.email!).toSet();
}
@@ -16,23 +16,21 @@ class ProfilesView extends GetWidget<ProfilesController> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: _responsiveUtils.isWebDesktop(context)
? AppColor.colorBgDesktop
: Colors.white,
backgroundColor: SettingsUtils.getBackgroundColor(context, _responsiveUtils),
body: Container(
margin: _responsiveUtils.isWebDesktop(context)
? const EdgeInsets.all(24)
? const EdgeInsets.all(16)
: EdgeInsets.symmetric(horizontal: SettingsUtils.getHorizontalPadding(context, _responsiveUtils)),
color: _responsiveUtils.isWebDesktop(context) ? null : Colors.white,
decoration: _responsiveUtils.isWebDesktop(context)
? BoxDecoration(
borderRadius: BorderRadius.circular(20),
border: Border.all(color: AppColor.colorBorderBodyThread, width: 1),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: AppColor.colorBorderSettingContentWeb, width: 1),
color: Colors.white)
: null,
child: ClipRRect(
borderRadius: BorderRadius.circular(
_responsiveUtils.isWebDesktop(context) ? 20 : 0),
_responsiveUtils.isWebDesktop(context) ? 16 : 0),
child: Padding(
padding: const EdgeInsets.only(left: 10, right: 10, bottom: 10),
child: DefaultTabController(
+33 -9
View File
@@ -1,5 +1,5 @@
{
"@@last_modified": "2022-10-28T13:12:30.889722",
"@@last_modified": "2022-11-04T00:32:44.924510",
"initializing_data": "Initializing data...",
"@initializing_data": {
"type": "text",
@@ -2022,7 +2022,7 @@
"emailAddress": {}
}
},
"deleteRecipient": "Delete recipient",
"deleteRecipient": "Remove recipients",
"@deleteRecipient": {
"type": "text",
"placeholders_order": [],
@@ -2034,7 +2034,7 @@
"placeholders_order": [],
"placeholders": {}
},
"messageConfirmationDialogDeleteAllRecipientForward": "Do you want to delete all email?",
"messageConfirmationDialogDeleteAllRecipientForward": "Are you sure you want to remove those recipients? Doing this will remove them from the email chain.",
"@messageConfirmationDialogDeleteAllRecipientForward": {
"type": "text",
"placeholders_order": [],
@@ -2046,7 +2046,7 @@
"placeholders_order": [],
"placeholders": {}
},
"recipientsLabel": "Name or Email address",
"recipientsLabel": "Enter name or email address",
"@recipientsLabel": {
"type": "text",
"placeholders_order": [],
@@ -2070,7 +2070,7 @@
"placeholders_order": [],
"placeholders": {}
},
"keepLocalCopyForwardLabel": "Keep local copy",
"keepLocalCopyForwardLabel": "Keep a copy of the email in Inbox",
"@keepLocalCopyForwardLabel": {
"type": "text",
"placeholders_order": [],
@@ -2400,14 +2400,14 @@
"placeholders_order": [],
"placeholders": {}
},
"newer": "Newer",
"@newer": {
"createNewMailbox": "Create new mailbox",
"@createNewMailbox": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"createNewMailbox": "Create new mailbox",
"@createNewMailbox": {
"newer": "Newer",
"@newer": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
@@ -2417,5 +2417,29 @@
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"forwardingSettingExplanation": "Allows a new recipient to see the email sent if they were not originally included in the email chain.",
"@forwardingSettingExplanation": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"addRecipientButton": "Add recipient",
"@addRecipientButton": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"incorrectEmailFormat": "Incorrect email format",
"@incorrectEmailFormat": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"remove": "Remove",
"@remove": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
}
}
+28 -4
View File
@@ -2083,7 +2083,7 @@ class AppLocalizations {
String get deleteRecipient {
return Intl.message(
'Delete recipient',
'Remove recipients',
name: 'deleteRecipient');
}
@@ -2095,7 +2095,7 @@ class AppLocalizations {
String get messageConfirmationDialogDeleteAllRecipientForward {
return Intl.message(
'Do you want to delete all email?',
'Are you sure you want to remove those recipients? Doing this will remove them from the email chain.',
name: 'messageConfirmationDialogDeleteAllRecipientForward'
);
}
@@ -2109,7 +2109,7 @@ class AppLocalizations {
String get recipientsLabel {
return Intl.message(
'Name or Email address',
'Enter name or email address',
name: 'recipientsLabel',
);
}
@@ -2134,7 +2134,7 @@ class AppLocalizations {
String get keepLocalCopyForwardLabel {
return Intl.message(
'Keep local copy',
'Keep a copy of the email in Inbox',
name: 'keepLocalCopyForwardLabel',
);
}
@@ -2487,4 +2487,28 @@ class AppLocalizations {
'Older',
name: 'older');
}
String get forwardingSettingExplanation {
return Intl.message(
'Allows a new recipient to see the email sent if they were not originally included in the email chain.',
name: 'forwardingSettingExplanation');
}
String get addRecipientButton {
return Intl.message(
'Add recipient',
name: 'addRecipientButton');
}
String get incorrectEmailFormat {
return Intl.message(
'Incorrect email format',
name: 'incorrectEmailFormat');
}
String get remove {
return Intl.message(
'Remove',
name: 'remove');
}
}