diff --git a/core/lib/core.dart b/core/lib/core.dart index 8af345ccd..59a353a0c 100644 --- a/core/lib/core.dart +++ b/core/lib/core.dart @@ -42,7 +42,6 @@ export 'presentation/views/button/button_builder.dart'; export 'presentation/views/button/icon_button_web.dart'; export 'presentation/views/image/avatar_builder.dart'; export 'presentation/views/list/sliver_grid_delegate_fixed_height.dart'; -export 'presentation/views/text/text_form_field_builder.dart'; export 'presentation/views/image/icon_builder.dart'; export 'presentation/views/context_menu/context_menu_action_builder.dart'; export 'presentation/views/context_menu/context_menu_builder.dart'; diff --git a/core/lib/presentation/views/button/icon_button_web.dart b/core/lib/presentation/views/button/icon_button_web.dart index 5b2738a53..8709f42b4 100644 --- a/core/lib/presentation/views/button/icon_button_web.dart +++ b/core/lib/presentation/views/button/icon_button_web.dart @@ -96,6 +96,7 @@ Widget buildTextButton(String text, { double? width, double? height, Color? backgroundColor, + EdgeInsets? padding, double? radius, IconWebCallback? onTap, }) { @@ -106,6 +107,8 @@ Widget buildTextButton(String text, { style: ButtonStyle( backgroundColor: MaterialStateProperty.resolveWith((states) => backgroundColor ?? AppColor.colorTextButton), elevation: MaterialStateProperty.resolveWith((states) => 0), + padding: MaterialStateProperty.resolveWith( + (Set states) => padding ?? EdgeInsets.zero), shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(radius ?? 0)))), child: Text(text, style: textStyle ?? TextStyle(fontSize: 17, color: Colors.white, fontWeight: FontWeight.w500)), onPressed: () => onTap?.call() diff --git a/core/lib/presentation/views/text/text_form_field_builder.dart b/core/lib/presentation/views/text/text_form_field_builder.dart deleted file mode 100644 index 00245f831..000000000 --- a/core/lib/presentation/views/text/text_form_field_builder.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:flutter/material.dart'; - -typedef OnTapActionClick = void Function(); - -class TextFormFieldBuilder { - Key? _key; - ValueChanged? _onTextChange; - InputDecoration? _inputDecoration; - TextInputAction? _textInputAction; - TextStyle? _textStyle; - OnTapActionClick? _onTapActionClick; - - void key(Key key) { - _key = key; - } - - void onChange(ValueChanged onChange) { - _onTextChange = onChange; - } - - void textStyle(TextStyle style) { - _textStyle = style; - } - - void textDecoration(InputDecoration inputDecoration) { - _inputDecoration = inputDecoration; - } - - void textInputAction(TextInputAction inputAction) { - _textInputAction = inputAction; - } - - void addOnTapActionClick(OnTapActionClick onTapActionClick) { - _onTapActionClick = onTapActionClick; - } - - TextFormField build() { - return TextFormField( - key: _key ?? Key('text_form_field_builder'), - keyboardType: TextInputType.multiline, - maxLines: null, - onChanged: _onTextChange, - style: _textStyle, - decoration: _inputDecoration, - textInputAction: _textInputAction, - onTap: () { - if (_onTapActionClick != null) { - _onTapActionClick!(); - } - }, - ); - } -} \ No newline at end of file diff --git a/lib/features/identity_creator/presentation/identity_creator_bindings.dart b/lib/features/identity_creator/presentation/identity_creator_bindings.dart new file mode 100644 index 000000000..8dc0c10ed --- /dev/null +++ b/lib/features/identity_creator/presentation/identity_creator_bindings.dart @@ -0,0 +1,29 @@ +import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/base/base_bindings.dart'; +import 'package:tmail_ui_user/features/identity_creator/presentation/identity_creator_controller.dart'; +import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart'; + +class IdentityCreatorBindings extends BaseBindings { + + @override + void bindingsController() { + Get.lazyPut(() => IdentityCreatorController(Get.find())); + } + + @override + void bindingsDataSource() {} + + @override + void bindingsDataSourceImpl() {} + + @override + void bindingsInteractor() { + Get.lazyPut(() => VerifyNameInteractor()); + } + + @override + void bindingsRepository() {} + + @override + void bindingsRepositoryImpl() {} +} \ No newline at end of file diff --git a/lib/features/identity_creator/presentation/identity_creator_controller.dart b/lib/features/identity_creator/presentation/identity_creator_controller.dart new file mode 100644 index 000000000..e4c32d1dc --- /dev/null +++ b/lib/features/identity_creator/presentation/identity_creator_controller.dart @@ -0,0 +1,165 @@ + +import 'package:core/core.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:get/get.dart'; +import 'package:html_editor_enhanced/html_editor.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/identities/identity.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; +import 'package:model/model.dart'; +import 'package:tmail_ui_user/features/base/base_controller.dart'; +import 'package:tmail_ui_user/features/identity_creator/presentation/model/identity_creator_arguments.dart'; +import 'package:tmail_ui_user/features/identity_creator/presentation/model/signature_type.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/main/routes/route_navigation.dart'; + +class IdentityCreatorController extends BaseController { + + final VerifyNameInteractor _verifyNameInteractor; + + final noneEmailAddress = EmailAddress(null, 'None'); + final signatureType = SignatureType.plainText.obs; + final listEmailAddressDefault = [].obs; + final listEmailAddressOfReplyTo = [].obs; + final errorNameIdentity = Rxn(); + final emailOfIdentity = Rxn(); + final replyToOfIdentity = Rxn(); + final bccOfIdentity = Rxn(); + + final HtmlEditorController signatureHtmlEditorController = HtmlEditorController(processNewLineAsBr: true); + final TextEditingController signaturePlainEditorController = TextEditingController(); + final TextEditingController inputNameIdentityController = TextEditingController(); + final FocusNode inputNameIdentityFocusNode = FocusNode(); + + AccountId? accountId; + UserProfile? userProfile; + String? _nameIdentity; + String? _contentHtmlEditor; + + void updateNameIdentity(BuildContext context, String? value) { + _nameIdentity = value; + errorNameIdentity.value = _getErrorInputNameString(context); + } + + void updateContentHtmlEditor(String? text) => _contentHtmlEditor = text; + + String? get contentHtmlEditor => _contentHtmlEditor; + + IdentityCreatorController(this._verifyNameInteractor); + + @override + void onReady() { + _getArguments(); + _setDefaultValueForIdentity(); + super.onReady(); + } + + @override + void onClose() { + signaturePlainEditorController.dispose(); + inputNameIdentityFocusNode.dispose(); + inputNameIdentityController.dispose(); + super.onClose(); + } + + @override + void onDone() { + } + + @override + void onError(error) {} + + void _getArguments() { + final arguments = Get.arguments; + if (arguments is IdentityCreatorArguments) { + accountId = arguments.accountId; + userProfile = arguments.userProfile; + } + } + + void _setDefaultValueForIdentity() { + listEmailAddressOfReplyTo.add(noneEmailAddress); + bccOfIdentity.value = noneEmailAddress; + replyToOfIdentity.value = noneEmailAddress; + + if (userProfile != null && userProfile?.email.isNotEmpty == true) { + final userEmailAddress = EmailAddress(userProfile!.email, userProfile!.email); + listEmailAddressDefault.add(userEmailAddress); + listEmailAddressOfReplyTo.add(userEmailAddress); + emailOfIdentity.value = userEmailAddress; + } + } + + void selectSignatureType(SignatureType newSignatureType) { + signatureType.value = newSignatureType; + } + + void updateEmailOfIdentity(EmailAddress? newEmailAddress) { + emailOfIdentity.value = newEmailAddress; + } + + void updaterReplyToOfIdentity(EmailAddress? newEmailAddress) { + replyToOfIdentity.value = newEmailAddress; + } + + void updateBccOfIdentity(EmailAddress? newEmailAddress) { + bccOfIdentity.value = newEmailAddress; + } + + void createNewIdentity(BuildContext context) async { + final error = _getErrorInputNameString(context); + if (error?.isNotEmpty == true) { + log('IdentityCreatorController::createNewIdentity(): error: $error'); + errorNameIdentity.value = error; + return; + } + + final newIdentity = Identity( + name: _nameIdentity, + email: emailOfIdentity.value?.email, + replyTo: replyToOfIdentity.value != null && replyToOfIdentity.value != noneEmailAddress + ? {replyToOfIdentity.value!} + : null, + bcc: bccOfIdentity.value != null && bccOfIdentity.value != noneEmailAddress + ? {bccOfIdentity.value!} + : null, + textSignature: Signature(signaturePlainEditorController.text), + htmlSignature: Signature(contentHtmlEditor ?? '')); + + log('IdentityCreatorController::createNewIdentity(): $newIdentity'); + + _clearAll(); + FocusScope.of(context).unfocus(); + popBack(result: newIdentity); + } + + String? _getErrorInputNameString(BuildContext context) { + return _verifyNameInteractor.execute( + _nameIdentity, + [EmptyNameValidator()] + ).fold( + (failure) { + if (failure is VerifyNameFailure) { + return failure.getMessageIdentity(context); + } else { + return null; + } + }, + (success) => null + ); + } + + void _clearAll() { + signaturePlainEditorController.clear(); + inputNameIdentityController.clear(); + } + + void closeView(BuildContext context) { + _clearAll(); + FocusScope.of(context).unfocus(); + popBack(); + } +} \ No newline at end of file diff --git a/lib/features/identity_creator/presentation/identity_creator_view.dart b/lib/features/identity_creator/presentation/identity_creator_view.dart new file mode 100644 index 000000000..3de9c54cb --- /dev/null +++ b/lib/features/identity_creator/presentation/identity_creator_view.dart @@ -0,0 +1,459 @@ +import 'package:core/core.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get/get.dart'; +import 'package:html_editor_enhanced/html_editor.dart'; +import 'package:tmail_ui_user/features/identity_creator/presentation/identity_creator_controller.dart'; +import 'package:tmail_ui_user/features/identity_creator/presentation/model/signature_type.dart'; +import 'package:tmail_ui_user/features/identity_creator/presentation/widgets/identity_drop_list_field_builder.dart'; +import 'package:tmail_ui_user/features/identity_creator/presentation/widgets/identity_input_field_builder.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +class IdentityCreatorView extends GetWidget { + + final _imagePaths = Get.find(); + final _responsiveUtils = Get.find(); + + IdentityCreatorView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ResponsiveWidget( + responsiveUtils: _responsiveUtils, + mobile: Scaffold( + backgroundColor: Colors.white, + body: SafeArea( + child: ClipRRect( + borderRadius: _responsiveUtils.isMobile(context) && _responsiveUtils.isPortrait(context) + ? const BorderRadius.only(topRight: Radius.circular(14), topLeft: Radius.circular(14)) + : const BorderRadius.all(Radius.zero), + child: _buildBodyMobile(context) + ), + ) + ), + tablet: Scaffold( + backgroundColor: Colors.black38, + body: Align(alignment: Alignment.center, child: Card( + color: Colors.transparent, + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), + child: Container( + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(16))), + width: _responsiveUtils.getSizeScreenWidth(context) * 0.9, + height: _responsiveUtils.getSizeScreenHeight(context) * 0.9, + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(16)), + child: _buildBodyDesktop(context) + ) + ) + ) + ) + ), + tabletLarge: Scaffold( + backgroundColor: Colors.black38, + body: Align(alignment: Alignment.center, child: Card( + color: Colors.transparent, + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), + child: Container( + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(16))), + width: _responsiveUtils.getSizeScreenWidth(context) * 0.6, + height: _responsiveUtils.getSizeScreenHeight(context) * 0.9, + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(16)), + child: _buildBodyDesktop(context) + ) + ) + ) + ) + ), + desktop: Scaffold( + backgroundColor: Colors.black38, + body: Align(alignment: Alignment.center, child: Card( + color: Colors.transparent, + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), + child: Container( + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(16))), + width: _responsiveUtils.getSizeScreenWidth(context) * 0.4, + height: _responsiveUtils.getSizeScreenHeight(context) * 0.9, + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(16)), + child: _buildBodyDesktop(context) + ) + ) + ) + ) + ) + ); + } + + Widget _buildBodyDesktop(BuildContext context) { + return Stack( + children: [ + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Padding( + padding: const EdgeInsets.only(left: 24, top: 24), + child: Text(AppLocalizations.of(context).new_identity.inCaps, + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 24, color: Colors.black))), + const SizedBox(height: 8), + Expanded(child: SingleChildScrollView( + physics: const ClampingScrollPhysics(), + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Column(children: [ + Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Expanded(child: Obx(() => (IdentityInputFieldBuilder( + context, + _responsiveUtils, + AppLocalizations.of(context).name, + controller.errorNameIdentity.value, + editingController: controller.inputNameIdentityController, + focusNode: controller.inputNameIdentityFocusNode, + isMandatory: true) + ..addOnChangeInputNameAction((value) => controller.updateNameIdentity(context, value))) + .build())), + const SizedBox(width: 24), + Expanded(child: Obx(() => (IdentityDropListFieldBuilder( + _imagePaths, + AppLocalizations.of(context).email.inCaps, + controller.emailOfIdentity.value, + controller.listEmailAddressDefault) + ..addOnSelectEmailAddressDropListAction((emailAddress) => + controller.updateEmailOfIdentity(emailAddress))) + .build())), + ]), + const SizedBox(height: 24), + Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Expanded(child: Obx(() => (IdentityDropListFieldBuilder( + _imagePaths, + AppLocalizations.of(context).reply_to_address, + controller.replyToOfIdentity.value, + controller.listEmailAddressOfReplyTo) + ..addOnSelectEmailAddressDropListAction((emailAddress) => + controller.updaterReplyToOfIdentity(emailAddress))) + .build())), + const SizedBox(width: 24), + Expanded(child: Obx(() => (IdentityDropListFieldBuilder( + _imagePaths, + AppLocalizations.of(context).bcc_to_address, + controller.bccOfIdentity.value, + controller.listEmailAddressOfReplyTo) + ..addOnSelectEmailAddressDropListAction((emailAddress) => + controller.updateBccOfIdentity(emailAddress))) + .build())), + ]), + const SizedBox(height: 32), + Row(children: [ + Text(AppLocalizations.of(context).signature, + style: const TextStyle( + fontWeight: FontWeight.normal, + fontSize: 14, + color: AppColor.colorContentEmail)), + const Spacer(), + Obx(() => buildTextButton( + AppLocalizations.of(context).plain_text, + textStyle: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 14, + color: controller.signatureType.value == SignatureType.plainText + ? AppColor.colorContentEmail + : AppColor.colorHintSearchBar), + backgroundColor: controller.signatureType.value == SignatureType.plainText + ? AppColor.emailAddressChipColor + : Colors.transparent, + width: 85, + height: 30, + radius: 10, + onTap: () => controller.selectSignatureType(SignatureType.plainText))), + const SizedBox(width: 10), + Obx(() => buildTextButton( + AppLocalizations.of(context).html_template, + textStyle: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 14, + color: controller.signatureType.value == SignatureType.htmlTemplate + ? AppColor.colorContentEmail + : AppColor.colorHintSearchBar), + backgroundColor: controller.signatureType.value == SignatureType.htmlTemplate + ? AppColor.emailAddressChipColor + : Colors.transparent, + width: 110, + height: 30, + radius: 10, + onTap: () => controller.selectSignatureType(SignatureType.htmlTemplate))), + ]), + const SizedBox(height: 8), + Obx(() => Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + border: Border.all(color: AppColor.colorInputBorderCreateMailbox), + color: Colors.white), + padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16), + child: Stack( + children: [ + if (controller.signatureType.value == SignatureType.plainText) + SizedBox( + height: 230, + child: (TextFieldBuilder() + ..key(const Key('signature_plain_text_editor'),) + ..cursorColor(Colors.black) + ..addController(controller.signaturePlainEditorController) + ..textStyle(const TextStyle( + fontWeight: FontWeight.normal, + color: Colors.black, + fontSize: 16)) + ..maxLines(null)) + .build()) + else + HtmlEditor( + key: const Key('signature_html_editor'), + controller: controller.signatureHtmlEditorController, + htmlEditorOptions: const HtmlEditorOptions( + hint: '', + darkMode: false, + ), + blockQuotedContent: controller.contentHtmlEditor ?? '

', + htmlToolbarOptions: const HtmlToolbarOptions( + toolbarPosition: ToolbarPosition.custom, + ), + otherOptions: const OtherOptions(height: 230), + callbacks: Callbacks(onInit: () { + controller.signatureHtmlEditorController.setFullScreen(); + }, onChangeContent: (String? changed) { + controller.updateContentHtmlEditor(changed); + }, onFocus: () { + FocusManager.instance.primaryFocus?.unfocus(); + Future.delayed(const Duration(milliseconds: 500), () { + controller.signatureHtmlEditorController.setFocus(); + }); + }), + ) + ] + ), + )) + ]), + ), + )), + Container( + padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24), + alignment: Alignment.centerRight, + color: Colors.white, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + buildTextButton( + AppLocalizations.of(context).cancel, + textStyle: const TextStyle( + fontWeight: FontWeight.w500, + fontSize: 17, + color: AppColor.colorTextButton), + backgroundColor: AppColor.emailAddressChipColor, + width: 128, + height: 44, + radius: 10, + onTap: () => controller.closeView(context)), + const SizedBox(width: 12), + buildTextButton( + AppLocalizations.of(context).create, + width: 128, + height: 44, + radius: 10, + onTap: () => controller.createNewIdentity(context)), + ] + ), + ) + ]), + Positioned(top: 8, right: 8, + child: buildIconWeb( + icon: SvgPicture.asset(_imagePaths.icCloseMailbox, fit: BoxFit.fill), + tooltip: AppLocalizations.of(context).close, + onTap: () => controller.closeView(context))) + ] + ); + } + + Widget _buildBodyMobile(BuildContext context) { + return Stack( + children: [ + Column(children: [ + Padding( + padding: const EdgeInsets.only(top: 16), + child: Text(AppLocalizations.of(context).new_identity.inCaps, + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 21, color: Colors.black))), + const SizedBox(height: 8), + Expanded(child: SingleChildScrollView( + physics: const ClampingScrollPhysics(), + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Column(children: [ + Obx(() => (IdentityInputFieldBuilder( + context, + _responsiveUtils, + AppLocalizations.of(context).name, + controller.errorNameIdentity.value, + editingController: controller.inputNameIdentityController, + focusNode: controller.inputNameIdentityFocusNode, + isMandatory: true) + ..addOnChangeInputNameAction((value) => controller.updateNameIdentity(context, value))) + .build()), + const SizedBox(height: 24), + Obx(() => (IdentityDropListFieldBuilder( + _imagePaths, + AppLocalizations.of(context).email.inCaps, + controller.emailOfIdentity.value, + controller.listEmailAddressDefault) + ..addOnSelectEmailAddressDropListAction((emailAddress) => + controller.updateEmailOfIdentity(emailAddress))) + .build()), + const SizedBox(height: 24), + Obx(() => (IdentityDropListFieldBuilder( + _imagePaths, + AppLocalizations.of(context).reply_to_address, + controller.replyToOfIdentity.value, + controller.listEmailAddressOfReplyTo) + ..addOnSelectEmailAddressDropListAction((newEmailAddress) => + controller.updaterReplyToOfIdentity(newEmailAddress))) + .build()), + const SizedBox(height: 24), + Obx(() => (IdentityDropListFieldBuilder( + _imagePaths, + AppLocalizations.of(context).bcc_to_address, + controller.bccOfIdentity.value, + controller.listEmailAddressOfReplyTo) + ..addOnSelectEmailAddressDropListAction((newEmailAddress) => + controller.updateBccOfIdentity(newEmailAddress))) + .build()), + const SizedBox(height: 32), + Text(AppLocalizations.of(context).signature, + style: const TextStyle( + fontWeight: FontWeight.normal, + fontSize: 14, + color: AppColor.colorContentEmail)), + const SizedBox(height: 8), + Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + Obx(() => buildTextButton( + AppLocalizations.of(context).plain_text, + textStyle: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 14, + color: controller.signatureType.value == SignatureType.plainText + ? AppColor.colorContentEmail + : AppColor.colorHintSearchBar), + backgroundColor: controller.signatureType.value == SignatureType.plainText + ? AppColor.emailAddressChipColor + : Colors.transparent, + width: 85, + height: 30, + radius: 10, + onTap: () => controller.selectSignatureType(SignatureType.plainText))), + const SizedBox(width: 10), + Obx(() => buildTextButton( + AppLocalizations.of(context).html_template, + textStyle: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 14, + color: controller.signatureType.value == SignatureType.htmlTemplate + ? AppColor.colorContentEmail + : AppColor.colorHintSearchBar), + backgroundColor: controller.signatureType.value == SignatureType.htmlTemplate + ? AppColor.emailAddressChipColor + : Colors.transparent, + width: 110, + height: 30, + radius: 10, + onTap: () => controller.selectSignatureType(SignatureType.htmlTemplate))), + ]), + const SizedBox(height: 8), + Obx(() => Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + border: Border.all(color: AppColor.colorInputBorderCreateMailbox), + color: Colors.white), + padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16), + child: Stack( + children: [ + if (controller.signatureType.value == SignatureType.plainText) + SizedBox( + height: 230, + child: (TextFieldBuilder() + ..key(const Key('signature_plain_text_editor'),) + ..cursorColor(Colors.black) + ..addController(controller.signaturePlainEditorController) + ..textStyle(const TextStyle( + fontWeight: FontWeight.normal, + color: Colors.black, + fontSize: 16)) + ..maxLines(null)) + .build()) + else + HtmlEditor( + key: const Key('signature_html_editor'), + controller: controller.signatureHtmlEditorController, + htmlEditorOptions: const HtmlEditorOptions( + hint: '', + darkMode: false, + ), + blockQuotedContent: controller.contentHtmlEditor ?? '

', + htmlToolbarOptions: const HtmlToolbarOptions( + toolbarPosition: ToolbarPosition.custom, + ), + otherOptions: const OtherOptions(height: 230), + callbacks: Callbacks(onInit: () { + controller.signatureHtmlEditorController.setFullScreen(); + }, onChangeContent: (String? changed) { + controller.updateContentHtmlEditor(changed); + }, onFocus: () { + FocusManager.instance.primaryFocus?.unfocus(); + Future.delayed(const Duration(milliseconds: 500), () { + controller.signatureHtmlEditorController.setFocus(); + }); + }), + ) + ] + ), + )), + const SizedBox(height: 24), + Container( + alignment: Alignment.centerRight, + color: Colors.white, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + buildTextButton( + AppLocalizations.of(context).cancel, + textStyle: const TextStyle( + fontWeight: FontWeight.w500, + fontSize: 17, + color: AppColor.colorTextButton), + backgroundColor: AppColor.emailAddressChipColor, + width: 128, + height: 44, + radius: 10, + onTap: () => controller.closeView(context)), + const SizedBox(width: 12), + buildTextButton( + AppLocalizations.of(context).create, + width: 128, + height: 44, + radius: 10, + onTap: () => controller.createNewIdentity(context)), + ] + ), + ) + ]), + ), + )), + ]), + Positioned(top: 8, right: 8, + child: buildIconWeb( + icon: SvgPicture.asset(_imagePaths.icCloseMailbox, fit: BoxFit.fill), + tooltip: AppLocalizations.of(context).close, + onTap: () => controller.closeView(context))) + ] + ); + } +} \ No newline at end of file diff --git a/lib/features/identity_creator/presentation/model/identity_creator_arguments.dart b/lib/features/identity_creator/presentation/model/identity_creator_arguments.dart new file mode 100644 index 000000000..177ddef4a --- /dev/null +++ b/lib/features/identity_creator/presentation/model/identity_creator_arguments.dart @@ -0,0 +1,14 @@ + +import 'package:equatable/equatable.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:model/model.dart'; + +class IdentityCreatorArguments with EquatableMixin { + final AccountId accountId; + final UserProfile userProfile; + + IdentityCreatorArguments(this.accountId, this.userProfile); + + @override + List get props => [accountId, userProfile]; +} \ No newline at end of file diff --git a/lib/features/identity_creator/presentation/model/signature_type.dart b/lib/features/identity_creator/presentation/model/signature_type.dart new file mode 100644 index 000000000..9c88412b0 --- /dev/null +++ b/lib/features/identity_creator/presentation/model/signature_type.dart @@ -0,0 +1,5 @@ + +enum SignatureType { + plainText, + htmlTemplate +} \ No newline at end of file diff --git a/lib/features/identity_creator/presentation/widgets/identity_drop_list_field_builder.dart b/lib/features/identity_creator/presentation/widgets/identity_drop_list_field_builder.dart new file mode 100644 index 000000000..df85ea51b --- /dev/null +++ b/lib/features/identity_creator/presentation/widgets/identity_drop_list_field_builder.dart @@ -0,0 +1,82 @@ + +import 'package:core/core.dart'; +import 'package:dropdown_button2/dropdown_button2.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; + +typedef OnSelectEmailAddressDropListAction = Function(EmailAddress? emailAddress); + +class IdentityDropListFieldBuilder { + + final ImagePaths _imagePaths; + final String _label; + final EmailAddress? _emailAddressSelected; + final List _listEmailAddress; + + OnSelectEmailAddressDropListAction? onSelectItemDropList; + + IdentityDropListFieldBuilder( + this._imagePaths, + this._label, + this._emailAddressSelected, + this._listEmailAddress, + ); + + void addOnSelectEmailAddressDropListAction(OnSelectEmailAddressDropListAction action) { + onSelectItemDropList = action; + } + + Widget build() { + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text(_label, style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.normal, + color: AppColor.colorContentEmail)), + const SizedBox(height: 8), + DropdownButtonHideUnderline( + child: DropdownButton2( + isExpanded: true, + hint: Row( + children: [ + Expanded(child: Text( + _emailAddressSelected?.email ?? '', + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.normal, color: Colors.black), + maxLines: 1, + overflow: BuildUtils.isWeb ? null : TextOverflow.ellipsis, + )), + ], + ), + items: _listEmailAddress.map((item) => DropdownMenuItem( + value: item, + child: Text( + item.email ?? '', + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.normal, color: Colors.black), + maxLines: 1, + overflow: BuildUtils.isWeb ? null : TextOverflow.ellipsis, + ), + )).toList(), + value: _emailAddressSelected, + onChanged: (newEmailAddress) => onSelectItemDropList?.call(newEmailAddress), + icon: SvgPicture.asset(_imagePaths.icDropDown), + buttonPadding: const EdgeInsets.symmetric(horizontal: 12), + buttonDecoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + border: Border.all(color: AppColor.colorInputBorderCreateMailbox, width: 0.5), + color: AppColor.colorInputBackgroundCreateMailbox), + itemHeight: 44, + buttonHeight: 44, + selectedItemHighlightColor: Colors.black12, + itemPadding: const EdgeInsets.symmetric(horizontal: 12), + dropdownMaxHeight: 200, + dropdownDecoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: Colors.white), + dropdownElevation: 4, + scrollbarRadius: const Radius.circular(40), + scrollbarThickness: 6, + ), + ) + ]); + } +} \ No newline at end of file diff --git a/lib/features/identity_creator/presentation/widgets/identity_input_decoration_builder.dart b/lib/features/identity_creator/presentation/widgets/identity_input_decoration_builder.dart new file mode 100644 index 000000000..57e05916e --- /dev/null +++ b/lib/features/identity_creator/presentation/widgets/identity_input_decoration_builder.dart @@ -0,0 +1,37 @@ + +import 'package:core/core.dart'; +import 'package:flutter/material.dart'; + +class IdentityInputDecorationBuilder extends InputDecorationBuilder { + + @override + InputDecoration build() { + return InputDecoration( + enabledBorder: enabledBorder ?? const OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(10)), + borderSide: BorderSide(width: 1, color: AppColor.colorInputBorderCreateMailbox)), + focusedBorder: enabledBorder ?? const OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(10)), + borderSide: BorderSide(width: 1, color: AppColor.colorTextButton)), + errorBorder: errorBorder ?? const OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(10)), + borderSide: BorderSide(width: 1, color: AppColor.colorInputBorderErrorVerifyName)), + focusedErrorBorder: errorBorder ?? const OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(10)), + borderSide: BorderSide(width: 1, 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.colorHintInputCreateMailbox, fontSize: 16), + contentPadding: contentPadding ?? const EdgeInsets.symmetric(horizontal: 12, vertical: 12), + errorText: errorText, + errorStyle: errorTextStyle ?? const TextStyle(color: AppColor.colorInputBorderErrorVerifyName, fontSize: 13), + filled: true, + fillColor: errorText?.isNotEmpty == true + ? AppColor.colorInputBackgroundErrorVerifyName + : AppColor.colorInputBackgroundCreateMailbox); + } +} \ No newline at end of file diff --git a/lib/features/identity_creator/presentation/widgets/identity_input_field_builder.dart b/lib/features/identity_creator/presentation/widgets/identity_input_field_builder.dart new file mode 100644 index 000000000..71d62e45d --- /dev/null +++ b/lib/features/identity_creator/presentation/widgets/identity_input_field_builder.dart @@ -0,0 +1,59 @@ + +import 'package:core/core.dart'; +import 'package:flutter/material.dart'; +import 'package:tmail_ui_user/features/identity_creator/presentation/widgets/identity_input_decoration_builder.dart'; + +typedef OnChangeInputNameAction = Function(String? value); + +class IdentityInputFieldBuilder { + + final BuildContext _context; + final ResponsiveUtils _responsiveUtils; + final String _label; + final String? _error; + final TextEditingController? editingController; + final FocusNode? focusNode; + final TextInputType? inputType; + final bool isMandatory; + + OnChangeInputNameAction? onChangeInputNameAction; + + IdentityInputFieldBuilder( + this._context, + this._responsiveUtils, + this._label, + this._error, { + this.isMandatory = false, + this.editingController, + this.focusNode, + this.inputType + }); + + void addOnChangeInputNameAction(OnChangeInputNameAction action) { + onChangeInputNameAction = action; + } + + Widget build() { + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text(isMandatory ? '$_label*' : _label, style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.normal, + color: AppColor.colorContentEmail)), + const SizedBox(height: 8), + (TextFieldBuilder() + ..onChange((value) => onChangeInputNameAction?.call(value)) + ..textInputAction(TextInputAction.next) + ..addController(editingController ?? TextEditingController()) + ..addFocusNode(focusNode) + ..textStyle(const TextStyle(color: Colors.black, fontSize: 16)) + ..keyboardType(inputType ?? TextInputType.text) + ..textDecoration((IdentityInputDecorationBuilder() + ..setContentPadding(EdgeInsets.symmetric( + vertical: _responsiveUtils.isDesktop(_context) ? 16 : 12, + horizontal: 12)) + ..setErrorText(_error)) + .build())) + .build() + ]); + } +} \ No newline at end of file diff --git a/lib/features/mailbox_creator/presentation/extensions/validator_failure_extension.dart b/lib/features/mailbox_creator/presentation/extensions/validator_failure_extension.dart index cd9259699..fb8469425 100644 --- a/lib/features/mailbox_creator/presentation/extensions/validator_failure_extension.dart +++ b/lib/features/mailbox_creator/presentation/extensions/validator_failure_extension.dart @@ -24,4 +24,12 @@ extension ValicatorFailureExtension on VerifyNameFailure { return ''; } } + + String getMessageIdentity(BuildContext context) { + if (exception is EmptyNameException) { + return AppLocalizations.of(context).this_field_cannot_be_blank; + } else { + return ''; + } + } } \ No newline at end of file diff --git a/lib/features/manage_account/presentation/profiles/identities/identities_bindings.dart b/lib/features/manage_account/presentation/profiles/identities/identities_bindings.dart index 30c476e74..42fd28c44 100644 --- a/lib/features/manage_account/presentation/profiles/identities/identities_bindings.dart +++ b/lib/features/manage_account/presentation/profiles/identities/identities_bindings.dart @@ -6,14 +6,17 @@ import 'package:tmail_ui_user/features/manage_account/data/datasource_impl/manag import 'package:tmail_ui_user/features/manage_account/data/network/manage_account_api.dart'; import 'package:tmail_ui_user/features/manage_account/data/repository/manage_account_repository_impl.dart'; import 'package:tmail_ui_user/features/manage_account/domain/repository/manage_account_repository.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/create_new_identity_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/delete_identity_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identities_controller.dart'; +import 'package:uuid/uuid.dart'; class IdentitiesBindings extends BaseBindings { @override void dependencies() { + Get.lazyPut(() => const Uuid()); Get.lazyPut(() => ManageAccountAPI(Get.find())); super.dependencies(); } @@ -22,6 +25,8 @@ class IdentitiesBindings extends BaseBindings { void bindingsController() { Get.lazyPut(() => IdentitiesController( Get.find(), + Get.find(), + Get.find(), Get.find(), )); } @@ -39,6 +44,7 @@ class IdentitiesBindings extends BaseBindings { @override void bindingsInteractor() { Get.lazyPut(() => GetAllIdentitiesInteractor(Get.find())); + Get.lazyPut(() => CreateNewIdentityInteractor(Get.find())); Get.lazyPut(() => DeleteIdentityInteractor(Get.find())); } diff --git a/lib/features/manage_account/presentation/profiles/identities/identities_controller.dart b/lib/features/manage_account/presentation/profiles/identities/identities_controller.dart index c2b55c459..7aa0f605e 100644 --- a/lib/features/manage_account/presentation/profiles/identities/identities_controller.dart +++ b/lib/features/manage_account/presentation/profiles/identities/identities_controller.dart @@ -4,24 +4,37 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; +import 'package:tmail_ui_user/features/identity_creator/presentation/model/identity_creator_arguments.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/model/create_new_identity_request.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/state/create_new_identity_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/delete_identity_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_identities_state.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/create_new_identity_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/delete_identity_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/manage_account_dashboard_controller.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'; +import 'package:uuid/uuid.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; class IdentitiesController extends BaseController { final _accountDashBoardController = Get.find(); + final _appToast = Get.find(); + final _imagePaths = Get.find(); final _responsiveUtils = Get.find(); final _imagePaths = Get.find(); final _appToast = Get.find(); final GetAllIdentitiesInteractor _getAllIdentitiesInteractor; + final CreateNewIdentityInteractor _createNewIdentityInteractor; + final Uuid _uuid; final DeleteIdentityInteractor _deleteIdentityInteractor; final identitySelected = Rxn(); @@ -31,6 +44,11 @@ class IdentitiesController extends BaseController { this._getAllIdentitiesInteractor, this._deleteIdentityInteractor, ); + IdentitiesController( + this._getAllIdentitiesInteractor, + this._createNewIdentityInteractor, + this._uuid, + ); @override void onInit() { @@ -52,6 +70,8 @@ class IdentitiesController extends BaseController { listIdentities.value = success.identities ?? []; setIdentityDefault(); } + } else if (success is CreateNewIdentitySuccess) { + _createNewIdentitySuccess(success); } else if (success is DeleteIdentitySuccess) { _deleteIdentitySuccess(success); } @@ -89,6 +109,41 @@ class IdentitiesController extends BaseController { identitySelected.value = newIdentity; } + void goToCreateNewIdentity() async { + final accountId = _accountDashBoardController.accountId.value; + final userProfile = _accountDashBoardController.userProfile.value; + if (accountId != null && userProfile != null) { + final newIdentity = await push( + AppRoutes.IDENTITY_CREATOR, + arguments: IdentityCreatorArguments(accountId, userProfile)); + + if (newIdentity != null && newIdentity is Identity) { + final generateCreateId = Id(_uuid.v1()); + _createNewIdentityAction( + accountId, + CreateNewIdentityRequest(generateCreateId, newIdentity)); + } + } + } + + void _createNewIdentityAction(AccountId accountId, CreateNewIdentityRequest identityRequest) async { + consumeState(_createNewIdentityInteractor.execute(accountId, identityRequest)); + } + + void _createNewIdentitySuccess(CreateNewIdentitySuccess success) { + if (currentOverlayContext != null && currentContext != null) { + _appToast.showToastWithIcon( + currentOverlayContext!, + message: AppLocalizations.of(currentContext!).you_have_created_a_new_identity, + icon: _imagePaths.icSelected); + } + + final accountId = _accountDashBoardController.accountId.value; + if (accountId != null) { + _getAllIdentities(accountId); + } + } + void openConfirmationDialogDeleteIdentityAction(BuildContext context, Identity identity) { popBack(); diff --git a/lib/features/manage_account/presentation/profiles/identities/identities_view.dart b/lib/features/manage_account/presentation/profiles/identities/identities_view.dart index cae7efe4c..ff87e9416 100644 --- a/lib/features/manage_account/presentation/profiles/identities/identities_view.dart +++ b/lib/features/manage_account/presentation/profiles/identities/identities_view.dart @@ -53,6 +53,7 @@ class IdentitiesView extends GetWidget with PopupMenuWidge color: AppColor.colorInputBackgroundCreateMailbox), itemHeight: 44, buttonHeight: 44, + selectedItemHighlightColor: Colors.black12, itemPadding: const EdgeInsets.symmetric(horizontal: 12), dropdownMaxHeight: 200, dropdownDecoration: BoxDecoration( @@ -75,7 +76,7 @@ class IdentitiesView extends GetWidget with PopupMenuWidge ..radiusSplash(10) ..padding(const EdgeInsets.symmetric(vertical: 12)) ..textStyle(const TextStyle(fontSize: 17, color: Colors.white, fontWeight: FontWeight.w500)) - ..onPressActionClick(() => {}) + ..onPressActionClick(() => controller.goToCreateNewIdentity()) ..text(AppLocalizations.of(context).new_identity, isVertical: false)) .build() ]); @@ -99,7 +100,7 @@ class IdentitiesView extends GetWidget with PopupMenuWidge floatingActionButton: _responsiveUtils.isMobile(context) ? FloatingActionButton( key: const Key('add_new_identity'), - onPressed: () => {}, + onPressed: () => controller.goToCreateNewIdentity(), backgroundColor: AppColor.primaryColor, child: SvgPicture.asset(_imagePaths.icAddIdentity, width: 24, height: 24)) : null), diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 4ceb2ac70..41dcc74c9 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-05-05T15:05:07.334710", + "@@last_modified": "2022-05-05T09:06:54.039059", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -1126,6 +1126,54 @@ "placeholders_order": [], "placeholders": {} }, + "name": "Name", + "@name": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "reply_to_address": "Reply to address", + "@reply_to_address": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "bcc_to_address": "Bcc to address", + "@bcc_to_address": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "signature": "Signature", + "@signature": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "plain_text": "Plain text", + "@plain_text": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "html_template": "Html template", + "@html_template": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "create": "Create", + "@create": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "you_have_created_a_new_identity": "You have created a new identity", + "@you_have_created_a_new_identity": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, "delete_identity": "Delete identity", "@delete_identity": { "type": "text", diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index b97787b61..e8f8ef156 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -1179,6 +1179,54 @@ class AppLocalizations { name: 'new_identity'); } + String get name { + return Intl.message( + 'Name', + name: 'name'); + } + + String get reply_to_address { + return Intl.message( + 'Reply to address', + name: 'reply_to_address'); + } + + String get bcc_to_address { + return Intl.message( + 'Bcc to address', + name: 'bcc_to_address'); + } + + String get signature { + return Intl.message( + 'Signature', + name: 'signature'); + } + + String get plain_text { + return Intl.message( + 'Plain text', + name: 'plain_text'); + } + + String get html_template { + return Intl.message( + 'Html template', + name: 'html_template'); + } + + String get create { + return Intl.message( + 'Create', + name: 'create'); + } + + String get you_have_created_a_new_identity { + return Intl.message( + 'You have created a new identity', + name: 'you_have_created_a_new_identity'); + } + String get delete_identity { return Intl.message('Delete identity', name: 'delete_identity'); diff --git a/lib/main/pages/app_pages.dart b/lib/main/pages/app_pages.dart index 77f57dd7a..0da44727f 100644 --- a/lib/main/pages/app_pages.dart +++ b/lib/main/pages/app_pages.dart @@ -7,10 +7,12 @@ import 'package:tmail_ui_user/features/destination_picker/presentation/destinati import 'package:tmail_ui_user/features/email/presentation/email_view.dart' deferred as email; import 'package:tmail_ui_user/features/home/presentation/home_bindings.dart'; import 'package:tmail_ui_user/features/home/presentation/home_view.dart'; +import 'package:tmail_ui_user/features/identity_creator/presentation/identity_creator_bindings.dart'; import 'package:tmail_ui_user/features/login/presentation/login_bindings.dart'; import 'package:tmail_ui_user/features/login/presentation/login_view.dart' deferred as login; import 'package:tmail_ui_user/features/mailbox_creator/presentation/mailbox_creator_bindings.dart'; import 'package:tmail_ui_user/features/mailbox_creator/presentation/mailbox_creator_view.dart' deferred as mailbox_creator; +import 'package:tmail_ui_user/features/identity_creator/presentation/identity_creator_view.dart' deferred as identity_creator; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_view.dart' if (dart.library.html) 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart' deferred as mailbox_dashboard; @@ -61,5 +63,10 @@ class AppPages { name: AppRoutes.MANAGE_ACCOUNT, page: () => DeferredWidget(manage_account_dashboard.loadLibrary, () => manage_account_dashboard.ManageAccountDashBoardView()), binding: ManageAccountDashBoardBindings()), + GetPage( + name: AppRoutes.IDENTITY_CREATOR, + opaque: false, + page: () => DeferredWidget(identity_creator.loadLibrary, () => identity_creator.IdentityCreatorView()), + binding: IdentityCreatorBindings()), ]; } diff --git a/lib/main/routes/app_routes.dart b/lib/main/routes/app_routes.dart index 8aeef8e95..04d37c421 100644 --- a/lib/main/routes/app_routes.dart +++ b/lib/main/routes/app_routes.dart @@ -10,4 +10,5 @@ abstract class AppRoutes { static const DESTINATION_PICKER = '/destinationPicker'; static const MAILBOX_CREATOR = '/mailboxCreator'; static const MANAGE_ACCOUNT = '$MAILBOX_DASHBOARD/manage_account'; + static const IDENTITY_CREATOR = '/identity_creator'; } \ No newline at end of file