diff --git a/assets/images/ic_add_email_address.svg b/assets/images/ic_add_email_address.svg
new file mode 100644
index 000000000..c3026c8c2
--- /dev/null
+++ b/assets/images/ic_add_email_address.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/ic_chevron_up.svg b/assets/images/ic_chevron_up.svg
new file mode 100644
index 000000000..fcc429730
--- /dev/null
+++ b/assets/images/ic_chevron_up.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/ic_close.svg b/assets/images/ic_close.svg
new file mode 100644
index 000000000..cb1b7c340
--- /dev/null
+++ b/assets/images/ic_close.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/ic_empty.svg b/assets/images/ic_empty.svg
new file mode 100644
index 000000000..17ec58775
--- /dev/null
+++ b/assets/images/ic_empty.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/ic_fullscreen_exit.svg b/assets/images/ic_fullscreen_exit.svg
new file mode 100644
index 000000000..509276675
--- /dev/null
+++ b/assets/images/ic_fullscreen_exit.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/ic_minimize.svg b/assets/images/ic_minimize.svg
new file mode 100644
index 000000000..999a094a3
--- /dev/null
+++ b/assets/images/ic_minimize.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/ic_minimize_composer.svg b/assets/images/ic_minimize_composer.svg
deleted file mode 100644
index ac0e2f5f4..000000000
--- a/assets/images/ic_minimize_composer.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/assets/images/ic_send_disable.svg b/assets/images/ic_send_disable.svg
new file mode 100644
index 000000000..d17fb2804
--- /dev/null
+++ b/assets/images/ic_send_disable.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/ic_send_mobile_disable.svg b/assets/images/ic_send_mobile_disable.svg
deleted file mode 100644
index 64f85c188..000000000
--- a/assets/images/ic_send_mobile_disable.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/assets/images/ic_send_toast_error.svg b/assets/images/ic_send_toast_error.svg
new file mode 100644
index 000000000..129c34279
--- /dev/null
+++ b/assets/images/ic_send_toast_error.svg
@@ -0,0 +1,4 @@
+
diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart
index 3646c1b42..3c44a6be3 100644
--- a/core/lib/presentation/extensions/color_extension.dart
+++ b/core/lib/presentation/extensions/color_extension.dart
@@ -43,7 +43,7 @@ extension AppColor on Color {
static const attachmentFileBorderColor = Color(0xFFEAEAEA);
static const attachmentFileNameColor = Color(0xFF182952);
static const attachmentFileSizeColor = Color(0xFF7E869B);
- static const avatarColor = Color(0xFFE6E5FF);
+ static const avatarColor = Color(0xFFF8F8F8);
static const avatarTextColor = Color(0xFF3840F7);
static const sentTimeTextColorUnRead = Color(0xFF182952);
static const subjectEmailTextColorUnRead = Color(0xFF3840F7);
@@ -103,6 +103,9 @@ extension AppColor on Color {
static const colorBorderBodyThread = Color(0x5CB8C1CC);
static const colorBgDesktop = Color(0xFFF6F6F6);
static const colorItemEmailSelectedDesktop = Color(0x0F007AFF);
+ static const colorAvatar = Color(0xFFDE5E5E);
+ static const colorFocusButton = Color(0x14818C99);
+ static const colorBorderEmailAddressInvalid = Color(0xFFFF3347);
static const mapGradientColor = [
[Color(0xFF21D4FD), Color(0xFFB721FF)],
diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart
index 30ddadeeb..73ca23672 100644
--- a/core/lib/presentation/resources/image_paths.dart
+++ b/core/lib/presentation/resources/image_paths.dart
@@ -68,18 +68,24 @@ class ImagePaths {
String get icAttachmentsComposer => _getImagePath('ic_attachments_composer.svg');
String get icCloseComposer => _getImagePath('ic_close_composer.svg');
String get icFullScreenComposer => _getImagePath('ic_fullscreen_composer.svg');
- String get icMinimizeComposer => _getImagePath('ic_minimize_composer.svg');
+ String get icMinimize => _getImagePath('ic_minimize.svg');
String get icSendComposer => _getImagePath('ic_send_composer.svg');
String get icDeleteComposer => _getImagePath('ic_delete_composer.svg');
String get icFileAttachment => _getImagePath('ic_file_attachment.svg');
String get icDeleteAttachment => _getImagePath('ic_delete_attachment.svg');
String get icSendMobile => _getImagePath('ic_send_mobile.svg');
- String get icSendMobileDisable => _getImagePath('ic_send_mobile_disable.svg');
+ String get icSendDisable => _getImagePath('ic_send_disable.svg');
String get icArrowDown => _getImagePath('ic_arrow_down.svg');
String get icFilterWeb => _getImagePath('ic_filter_web.svg');
String get icMarkAllAsRead => _getImagePath('ic_mark_all_as_read.svg');
String get icRefresh => _getImagePath('ic_refresh.svg');
String get icSelectAll => _getImagePath('ic_select_all.svg');
+ String get icFullScreenExit => _getImagePath('ic_fullscreen_exit.svg');
+ String get icChevronUp => _getImagePath('ic_chevron_up.svg');
+ String get icAddEmailAddress => _getImagePath('ic_add_email_address.svg');
+ String get icClose => _getImagePath('ic_close.svg');
+ String get icSendToastError => _getImagePath('ic_send_toast_error.svg');
+ String get icEmpty => _getImagePath('ic_empty.svg');
String _getImagePath(String imageName) {
return AssetsPaths.images + imageName;
diff --git a/core/lib/presentation/views/button/icon_button_web.dart b/core/lib/presentation/views/button/icon_button_web.dart
index d01a36db1..a83456b83 100644
--- a/core/lib/presentation/views/button/icon_button_web.dart
+++ b/core/lib/presentation/views/button/icon_button_web.dart
@@ -1,4 +1,5 @@
+import 'package:core/core.dart';
import 'package:flutter/material.dart';
typedef IconWebCallback = void Function();
@@ -20,7 +21,7 @@ Widget buildIconWeb({
iconSize: iconSize,
padding: iconPadding ?? EdgeInsets.all(8.0),
splashRadius: splashRadius ?? 20,
- tooltip: tooltip,
+ tooltip: tooltip ?? '',
onPressed: onTap)
);
}
@@ -50,4 +51,47 @@ Widget buildIconWebHasPosition(BuildContext context, {
child: icon,
)
);
-}
\ No newline at end of file
+}
+
+Widget buildTextCircleButton(String text, {
+ TextStyle? textStyle,
+ IconWebCallback? onTap,
+}) {
+ return Material(
+ shape: CircleBorder(),
+ color: Colors.transparent,
+ child: TextButton(
+ child: Text(
+ text,
+ style: textStyle ?? TextStyle(fontWeight: FontWeight.normal, fontSize: 15, color: AppColor.lineItemListColor)),
+ style: ButtonStyle(
+ overlayColor: MaterialStateProperty.resolveWith((Set states) => AppColor.colorFocusButton),
+ shape: MaterialStateProperty.all(CircleBorder()),
+ padding: MaterialStateProperty.resolveWith((Set states) => EdgeInsets.all(5)),
+ elevation: MaterialStateProperty.resolveWith((Set states) => 0)),
+ onPressed: () => onTap?.call()
+ )
+ );
+}
+
+Widget buildTextButton(String text, {
+ TextStyle? textStyle,
+ double? width,
+ double? height,
+ Color? backgroundColor,
+ double? radius,
+ IconWebCallback? onTap,
+}) {
+ return SizedBox(
+ width: width ?? double.infinity,
+ height: height ?? 40,
+ child: ElevatedButton(
+ style: ButtonStyle(
+ backgroundColor: MaterialStateProperty.resolveWith((states) => backgroundColor ?? AppColor.colorTextButton),
+ elevation: MaterialStateProperty.resolveWith((states) => 0),
+ 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/dialog/confirmation_dialog_builder.dart b/core/lib/presentation/views/dialog/confirmation_dialog_builder.dart
index ac19397f1..49a5b60c7 100644
--- a/core/lib/presentation/views/dialog/confirmation_dialog_builder.dart
+++ b/core/lib/presentation/views/dialog/confirmation_dialog_builder.dart
@@ -23,6 +23,10 @@ class ConfirmDialogBuilder {
TextStyle? _styleTitle;
TextStyle? _styleContent;
double? _radiusButton;
+ EdgeInsets? _paddingTitle;
+ EdgeInsets? _paddingContent;
+ EdgeInsets? _paddingButton;
+ EdgeInsets? _marginIcon;
OnConfirmButtonAction? _onConfirmButtonAction;
OnCancelButtonAction? _onCancelButtonAction;
@@ -74,6 +78,22 @@ class ConfirmDialogBuilder {
_radiusButton = radius;
}
+ void paddingTitle(EdgeInsets? value) {
+ _paddingTitle = value;
+ }
+
+ void paddingContent(EdgeInsets? value) {
+ _paddingContent = value;
+ }
+
+ void paddingButton(EdgeInsets? value) {
+ _paddingButton = value;
+ }
+
+ void marginIcon(EdgeInsets? value) {
+ _marginIcon = value;
+ }
+
void onConfirmButtonAction(String confirmText, OnConfirmButtonAction? onConfirmButtonAction) {
_confirmText = confirmText;
_onConfirmButtonAction = onConfirmButtonAction;
@@ -104,19 +124,21 @@ class ConfirmDialogBuilder {
if (_onCloseButtonAction != null)
Align(
alignment: Alignment.centerRight,
- child: IconButton(
- padding: EdgeInsets.only(top: 16, right: 16),
- onPressed: () => _onCloseButtonAction?.call(),
- icon: SvgPicture.asset(_imagePath.icCloseMailbox, width: 30, height: 30, fit: BoxFit.fill))),
+ child: Padding(
+ padding: EdgeInsets.only(top: 8, right: 8),
+ child: buildIconWeb(
+ icon: SvgPicture.asset(_imagePath.icCloseMailbox, fit: BoxFit.fill),
+ onTap: () => _onCloseButtonAction?.call())
+ )),
if (_iconWidget != null)
Container(
- margin: EdgeInsets.only(top: 24),
+ margin: _marginIcon ?? EdgeInsets.only(top: 24),
alignment: Alignment.center,
child: _iconWidget,
),
if (_title.isNotEmpty)
Padding(
- padding: EdgeInsets.only(top: 12),
+ padding: _paddingTitle ?? EdgeInsets.only(top: 12),
child: Center(
child: Text(
_title,
@@ -127,7 +149,7 @@ class ConfirmDialogBuilder {
),
if (_content.isNotEmpty)
Padding(
- padding: EdgeInsets.symmetric(horizontal: 16, vertical: 24),
+ padding: _paddingContent ?? EdgeInsets.symmetric(horizontal: 16, vertical: 24),
child: Center(
child: Text(_content,
textAlign: TextAlign.center,
@@ -136,22 +158,24 @@ class ConfirmDialogBuilder {
),
),
Padding(
- padding: EdgeInsets.only(bottom: 16, left: 16, right: 16),
+ padding: _paddingButton ?? EdgeInsets.only(bottom: 16, left: 16, right: 16),
child: Row(
children: [
- Expanded(child: _buildButton(
+ if (_cancelText.isNotEmpty)
+ Expanded(child: _buildButton(
name: _cancelText,
bgColor: _colorCancelButton,
radius: _radiusButton,
textStyle: _styleTextCancelButton,
action: _onCancelButtonAction)),
- SizedBox(width: 16),
- Expanded(child: _buildButton(
- name: _confirmText,
- bgColor: _colorConfirmButton,
- radius: _radiusButton,
- textStyle: _styleTextConfirmButton,
- action: _onConfirmButtonAction))
+ if (_confirmText.isNotEmpty && _cancelText.isNotEmpty) SizedBox(width: 16),
+ if (_confirmText.isNotEmpty)
+ Expanded(child: _buildButton(
+ name: _confirmText,
+ bgColor: _colorConfirmButton,
+ radius: _radiusButton,
+ textStyle: _styleTextConfirmButton,
+ action: _onConfirmButtonAction))
]
))
])
diff --git a/lib/features/composer/presentation/composer_bindings.dart b/lib/features/composer/presentation/composer_bindings.dart
index 0ddc7dbce..139784b2c 100644
--- a/lib/features/composer/presentation/composer_bindings.dart
+++ b/lib/features/composer/presentation/composer_bindings.dart
@@ -125,9 +125,6 @@ class ComposerBindings extends BaseBindings {
Get.find(),
Get.find(),
Get.find(),
- Get.find(),
- Get.find(),
- Get.find(),
Get.find(),
Get.find(),
Get.find(),
diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart
index 9bcd73e59..461db1c84 100644
--- a/lib/features/composer/presentation/composer_controller.dart
+++ b/lib/features/composer/presentation/composer_controller.dart
@@ -9,6 +9,7 @@ import 'package:file_picker/file_picker.dart';
import 'package:fk_user_agent/fk_user_agent.dart';
import 'package:flutter/foundation.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' as HtmlEditorBrowser;
import 'package:http_parser/http_parser.dart';
@@ -52,7 +53,10 @@ import 'package:uuid/uuid.dart';
class ComposerController extends BaseController {
final mailboxDashBoardController = Get.find();
-
+ final _appToast = Get.find();
+ final _imagePaths = Get.find();
+ final _responsiveUtils = Get.find();
+
final expandMode = ExpandMode.COLLAPSE.obs;
final expandModeMobile = ExpandMode.EXPAND.obs;
final expandModeAttachments = ExpandMode.EXPAND.obs;
@@ -61,16 +65,17 @@ class ComposerController extends BaseController {
final isInitialRecipient = false.obs;
final attachments = [].obs;
final emailContents = Rxn>();
+ final listEmailAddressType = [].obs;
final subjectEmail = Rxn();
final screenDisplayMode = ScreenDisplayMode.normal.obs;
+ final toAddressExpandMode = ExpandMode.EXPAND.obs;
+ final ccAddressExpandMode = ExpandMode.EXPAND.obs;
+ final bccAddressExpandMode = ExpandMode.EXPAND.obs;
final SendEmailInteractor _sendEmailInteractor;
final SaveEmailAddressesInteractor _saveEmailAddressInteractor;
final GetAutoCompleteInteractor _getAutoCompleteInteractor;
final GetAutoCompleteWithDeviceContactInteractor _getAutoCompleteWithDeviceContactInteractor;
- final AppToast _appToast;
- final ImagePaths _imagePaths;
- final ResponsiveUtils responsiveUtils;
final Uuid _uuid;
final LocalFilePickerInteractor _localFilePickerInteractor;
final UploadMultipleAttachmentInteractor _uploadMultipleAttachmentInteractor;
@@ -90,6 +95,7 @@ class ComposerController extends BaseController {
final toEmailAddressController = TextEditingController();
final ccEmailAddressController = TextEditingController();
final bccEmailAddressController = TextEditingController();
+ final toEmailAddressFocusNode = FocusNode();
List initialAttachments = [];
String? _textEditorWeb;
@@ -117,9 +123,6 @@ class ComposerController extends BaseController {
this._saveEmailAddressInteractor,
this._getAutoCompleteInteractor,
this._getAutoCompleteWithDeviceContactInteractor,
- this._appToast,
- this._imagePaths,
- this.responsiveUtils,
this._uuid,
this._deviceInfoPlugin,
this._localFilePickerInteractor,
@@ -151,6 +154,7 @@ class ComposerController extends BaseController {
toEmailAddressController.dispose();
ccEmailAddressController.dispose();
bccEmailAddressController.dispose();
+ toEmailAddressFocusNode.dispose();
super.onClose();
}
@@ -241,11 +245,6 @@ class ComposerController extends BaseController {
return null;
}
- void expandEmailAddressAction() {
- final newExpandMode = expandMode.value == ExpandMode.COLLAPSE ? ExpandMode.EXPAND : ExpandMode.COLLAPSE;
- expandMode.value = newExpandMode;
- }
-
void _initToEmailAddress(ComposerArguments arguments) {
final userProfile = mailboxDashBoardController.userProfile.value;
if (arguments.presentationEmail != null && userProfile != null) {
@@ -266,6 +265,14 @@ class ComposerController extends BaseController {
if (listToEmailAddress.isNotEmpty || listCcEmailAddress.isNotEmpty || listBccEmailAddress.isNotEmpty) {
isInitialRecipient.value = true;
}
+
+ if (listCcEmailAddress.isNotEmpty) {
+ listEmailAddressType.add(PrefixEmailAddress.cc);
+ }
+
+ if (listBccEmailAddress.isNotEmpty) {
+ listEmailAddressType.add(PrefixEmailAddress.bcc);
+ }
} else if (arguments.emailAddress != null) {
listToEmailAddress.add(arguments.emailAddress!);
isInitialRecipient.value = true;
@@ -377,15 +384,45 @@ class ComposerController extends BaseController {
void sendEmailAction(BuildContext context) async {
clearFocusEditor(context);
- if (isEnableEmailSendButton.value) {
- if (subjectEmail.value?.isNotEmpty == true) {
- _handleSendMessages(context);
- } else {
- _showConfirmDialogSendEmail(context);
- }
- } else {
- _appToast.showErrorToast(AppLocalizations.of(context).your_email_should_have_at_least_one_recipient);
+ if (!isEnableEmailSendButton.value) {
+ _showConfirmDialogSendEmailFailed(
+ context,
+ AppLocalizations.of(context).message_dialog_send_email_without_recipient,
+ AppLocalizations.of(context).add_recipients,
+ () => toEmailAddressFocusNode.requestFocus(),
+ hasCancelButton: false);
+ return;
}
+
+ final allListEmailAddress = listToEmailAddress + listCcEmailAddress + listBccEmailAddress;
+ final listEmailAddressInvalid = allListEmailAddress
+ .where((emailAddress) => !GetUtils.isEmail(emailAddress.emailAddress))
+ .toList();
+
+ if (listEmailAddressInvalid.isNotEmpty) {
+ _showConfirmDialogSendEmailFailed(
+ context,
+ AppLocalizations.of(context).message_dialog_send_email_with_email_address_invalid,
+ AppLocalizations.of(context).fix_email_addresses,
+ () {
+ toAddressExpandMode.value = ExpandMode.EXPAND;
+ ccAddressExpandMode.value = ExpandMode.EXPAND;
+ bccAddressExpandMode.value = ExpandMode.EXPAND;
+ },
+ hasCancelButton: false);
+ return;
+ }
+
+ if (subjectEmail.value == null || subjectEmail.isEmpty == true) {
+ _showConfirmDialogSendEmailFailed(
+ context,
+ AppLocalizations.of(context).message_dialog_send_email_without_a_subject,
+ AppLocalizations.of(context).send_anyway,
+ () => _handleSendMessages(context));
+ return;
+ }
+
+ _handleSendMessages(context);
}
void _handleSendMessages(BuildContext context) async {
@@ -646,23 +683,20 @@ class ComposerController extends BaseController {
if (!kIsWeb) {
htmlEditorApi?.unfocus(context);
htmlControllerBrowser.clearFocus();
+ } else {
+ FocusManager.instance.primaryFocus?.unfocus();
}
}
void closeComposerWeb() {
+ FocusManager.instance.primaryFocus?.unfocus();
mailboxDashBoardController.dispatchDashBoardAction(DashBoardAction.none);
}
- void minimizeComposer() {
+ void displayScreenTypeComposerAction(ScreenDisplayMode displayMode) {
+ FocusManager.instance.primaryFocus?.unfocus();
_updateTextForEditor();
- final newDisplayMode = screenDisplayMode.value == ScreenDisplayMode.minimize ? ScreenDisplayMode.normal : ScreenDisplayMode.minimize;
- screenDisplayMode.value = newDisplayMode;
- }
-
- void setFullScreenComposer() {
- _updateTextForEditor();
- final newDisplayMode = screenDisplayMode.value == ScreenDisplayMode.fullScreen ? ScreenDisplayMode.normal : ScreenDisplayMode.fullScreen;
- screenDisplayMode.value = newDisplayMode;
+ screenDisplayMode.value = displayMode;
}
void _updateTextForEditor() async {
@@ -671,6 +705,7 @@ class ComposerController extends BaseController {
}
void deleteComposer() {
+ FocusManager.instance.primaryFocus?.unfocus();
mailboxDashBoardController.dispatchDashBoardAction(DashBoardAction.none);
}
@@ -679,15 +714,16 @@ class ComposerController extends BaseController {
expandModeAttachments.value = newExpandMode;
}
- void _showConfirmDialogSendEmail(BuildContext context) {
- if (responsiveUtils.isMobileDevice(context)) {
+ void _showConfirmDialogSendEmailFailed(BuildContext context, String message,
+ String actionName, Function? onConfirmAction, {bool hasCancelButton = true}) {
+ if (_responsiveUtils.isMobile(context)) {
(ConfirmationDialogActionSheetBuilder(context)
- ..messageText(AppLocalizations.of(context).message_confirm_send_email)
+ ..messageText(message)
..styleConfirmButton(TextStyle(fontSize: 20, fontWeight: FontWeight.normal, color: Colors.black))
..onCancelAction(AppLocalizations.of(context).cancel, () => popBack())
- ..onConfirmAction(AppLocalizations.of(context).send, () {
+ ..onConfirmAction(actionName, () {
popBack();
- _handleSendMessages(context);
+ onConfirmAction?.call();
})).show();
} else {
showDialog(
@@ -695,17 +731,25 @@ class ComposerController extends BaseController {
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
builder: (BuildContext context) => PointerInterceptor(child: (ConfirmDialogBuilder(_imagePaths)
..key(Key('confirm_dialog_send_message'))
- ..content(AppLocalizations.of(context).message_confirm_send_email)
+ ..title(AppLocalizations.of(context).sending_failed)
+ ..content(message)
+ ..addIcon(SvgPicture.asset(_imagePaths.icSendToastError, fit: BoxFit.fill))
..colorConfirmButton(AppColor.colorTextButton)
..colorCancelButton(AppColor.colorCancelButton)
- ..styleContent(TextStyle(fontSize: 15, fontWeight: FontWeight.normal, color: AppColor.colorMessageDialog))
+ ..paddingTitle(EdgeInsets.only(top: 34))
+ ..marginIcon(EdgeInsets.zero)
+ ..paddingContent(EdgeInsets.only(left: 44, right: 44, bottom: 24, top: 8))
+ ..paddingButton(hasCancelButton ? null : EdgeInsets.only(bottom: 16, left: 44, right: 44))
+ ..styleTitle(TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black))
+ ..styleContent(TextStyle(fontSize: 14, fontWeight: FontWeight.normal, color: AppColor.colorContentEmail))
..styleTextCancelButton(TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton))
..styleTextConfirmButton(TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: Colors.white))
- ..onConfirmButtonAction(AppLocalizations.of(context).send, () {
+ ..onConfirmButtonAction(actionName, () {
popBack();
- _handleSendMessages(context);
+ onConfirmAction?.call();
})
- ..onCancelButtonAction(AppLocalizations.of(context).cancel, () => popBack()))
+ ..onCancelButtonAction(hasCancelButton ? AppLocalizations.of(context).cancel : '', () => popBack())
+ ..onCloseButtonAction(() => popBack()))
.build()));
}
}
@@ -714,6 +758,73 @@ class ComposerController extends BaseController {
expandModeMobile.value = expandMode;
}
+ void addEmailAddressType(PrefixEmailAddress prefixEmailAddress) {
+ listEmailAddressType.add(prefixEmailAddress);
+ }
+
+ void deleteEmailAddressType(PrefixEmailAddress prefixEmailAddress) {
+ listEmailAddressType.remove(prefixEmailAddress);
+ updateListEmailAddress(prefixEmailAddress, List.empty());
+ }
+
+ void onSubjectEmailFocusChange(bool isFocus) {
+ log('ComposerController::onSubjectEmailFocusChange(): Focus: $isFocus');
+ if (isFocus) {
+ toAddressExpandMode.value = ExpandMode.COLLAPSE;
+ ccAddressExpandMode.value = ExpandMode.COLLAPSE;
+ bccAddressExpandMode.value = ExpandMode.COLLAPSE;
+ }
+ }
+
+ void onEditorFocusChange(bool isFocus) {
+ log('ComposerController::onEditorFocusChange(): Focus: $isFocus');
+ if (isFocus) {
+ toAddressExpandMode.value = ExpandMode.COLLAPSE;
+ ccAddressExpandMode.value = ExpandMode.COLLAPSE;
+ bccAddressExpandMode.value = ExpandMode.COLLAPSE;
+ }
+ }
+
+ void showFullEmailAddress(PrefixEmailAddress prefixEmailAddress) {
+ switch(prefixEmailAddress) {
+ case PrefixEmailAddress.to:
+ toAddressExpandMode.value = ExpandMode.EXPAND;
+ break;
+ case PrefixEmailAddress.cc:
+ ccAddressExpandMode.value = ExpandMode.EXPAND;
+ break;
+ case PrefixEmailAddress.bcc:
+ bccAddressExpandMode.value = ExpandMode.EXPAND;
+ break;
+ default:
+ break;
+ }
+ }
+
+ void onEmailAddressFocusChange(PrefixEmailAddress prefixEmailAddress, bool isFocus) {
+ if (isFocus) {
+ switch(prefixEmailAddress) {
+ case PrefixEmailAddress.to:
+ toAddressExpandMode.value = ExpandMode.EXPAND;
+ ccAddressExpandMode.value = ExpandMode.COLLAPSE;
+ bccAddressExpandMode.value = ExpandMode.COLLAPSE;
+ break;
+ case PrefixEmailAddress.cc:
+ ccAddressExpandMode.value = ExpandMode.EXPAND;
+ toAddressExpandMode.value = ExpandMode.COLLAPSE;
+ bccAddressExpandMode.value = ExpandMode.COLLAPSE;
+ break;
+ case PrefixEmailAddress.bcc:
+ bccAddressExpandMode.value = ExpandMode.EXPAND;
+ toAddressExpandMode.value = ExpandMode.COLLAPSE;
+ ccAddressExpandMode.value = ExpandMode.COLLAPSE;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
void closeComposer(BuildContext context) {
popBack();
}
diff --git a/lib/features/composer/presentation/composer_view.dart b/lib/features/composer/presentation/composer_view.dart
index 43f5ee53b..937848c26 100644
--- a/lib/features/composer/presentation/composer_view.dart
+++ b/lib/features/composer/presentation/composer_view.dart
@@ -18,7 +18,6 @@ class ComposerView extends GetWidget {
final responsiveUtils = Get.find();
final imagePaths = Get.find();
- final _appToast = Get.find();
final keyboardUtils = Get.find();
@override
@@ -128,7 +127,7 @@ class ComposerView extends GetWidget {
onTap: () => controller.openPickAttachmentMenu(context, _pickAttachmentsActionTiles(context))),
if (expandModeMobile == ExpandMode.COLLAPSE)
buildIconWeb(
- icon: SvgPicture.asset(isEnableSendButton ? imagePaths.icSendMobile : imagePaths.icSendMobileDisable, fit: BoxFit.fill),
+ icon: SvgPicture.asset(isEnableSendButton ? imagePaths.icSendMobile : imagePaths.icSendDisable, fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).send,
onTap: () => controller.sendEmailAction(context)),
]);
@@ -145,7 +144,7 @@ class ComposerView extends GetWidget {
buildIconWeb(
iconSize: 36,
iconPadding: EdgeInsets.zero,
- icon: SvgPicture.asset(isEnableSendButton ? imagePaths.icSendMobile : imagePaths.icSendMobileDisable, width: 36, height: 36, fit: BoxFit.fill),
+ icon: SvgPicture.asset(isEnableSendButton ? imagePaths.icSendMobile : imagePaths.icSendDisable, width: 36, height: 36, fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).send,
onTap: () => controller.sendEmailAction(context)),
]);
@@ -182,13 +181,11 @@ class ComposerView extends GetWidget {
Obx(() => (EmailAddressInputBuilder(
context,
imagePaths,
- _appToast,
PrefixEmailAddress.to,
controller.listToEmailAddress,
+ controller.listEmailAddressType,
controller: controller.toEmailAddressController,
- isInitial: controller.isInitialRecipient.value,
- expandMode: controller.expandMode.value)
- ..addExpandAddressActionClick(() => controller.expandEmailAddressAction())
+ isInitial: controller.isInitialRecipient.value)
..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress))
..addOnSuggestionEmailAddress((word) => controller.getAutoCompleteSuggestion(word)))
.build()
@@ -200,9 +197,9 @@ class ComposerView extends GetWidget {
? (EmailAddressInputBuilder(
context,
imagePaths,
- _appToast,
PrefixEmailAddress.cc,
controller.listCcEmailAddress,
+ controller.listEmailAddressType,
controller: controller.ccEmailAddressController,
isInitial: controller.isInitialRecipient.value,)
..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress))
@@ -217,9 +214,9 @@ class ComposerView extends GetWidget {
? (EmailAddressInputBuilder(
context,
imagePaths,
- _appToast,
PrefixEmailAddress.bcc,
controller.listBccEmailAddress,
+ controller.listEmailAddressType,
controller: controller.bccEmailAddressController,
isInitial: controller.isInitialRecipient.value,)
..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress))
diff --git a/lib/features/composer/presentation/composer_view_web.dart b/lib/features/composer/presentation/composer_view_web.dart
index 68d385d3e..e943b7cb3 100644
--- a/lib/features/composer/presentation/composer_view_web.dart
+++ b/lib/features/composer/presentation/composer_view_web.dart
@@ -2,6 +2,7 @@ import 'package:core/core.dart';
import 'package:file_picker/file_picker.dart';
import 'package:filesize/filesize.dart';
import 'package:flutter/cupertino.dart';
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
@@ -28,87 +29,78 @@ class ComposerView extends GetWidget {
responsiveUtils: responsiveUtils,
mobile: Scaffold(
backgroundColor: Colors.white,
- body: Align(alignment: Alignment.center, child: Card(
- color: Colors.transparent,
- child: Container(
- color: Colors.white,
- child: PointerInterceptor(child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Obx(() => _buildAppBarForMobile(context, controller.isEnableEmailSendButton.value)),
- SizedBox(height: 16),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: _buildEmailAddress(context)),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: Divider(color: AppColor.colorDividerComposer, height: 1)),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: _buildSubjectEmail(context)),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: Divider(color: AppColor.colorDividerComposer, height: 1)),
- Expanded(child: Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: _buildEditorAndAttachments(context))),
- ]
- ))
- )
- ))
+ body: Container(
+ color: Colors.white,
+ child: LayoutBuilder(builder: (context, constraints) => PointerInterceptor(child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Obx(() => _buildAppBarForMobile(context, controller.isEnableEmailSendButton.value)),
+ Divider(color: AppColor.colorDividerComposer, height: 1),
+ _buildEmailAddress(context, constraints),
+ Divider(color: AppColor.colorDividerComposer, height: 1),
+ Padding(padding: EdgeInsets.symmetric(horizontal: 16), child: _buildSubjectEmail(context)),
+ Divider(color: AppColor.colorDividerComposer, height: 1),
+ Padding(padding: EdgeInsets.symmetric(horizontal: 16), child: _buildListButton(context)),
+ Divider(color: AppColor.colorDividerComposer, height: 1),
+ Expanded(child: _buildEditorAndAttachments(context)),
+ ]
+ )))
+ )
),
desktop: Obx(() {
return Stack(children: [
if (controller.screenDisplayMode == ScreenDisplayMode.normal)
- Positioned(right: 10, bottom: 10, child: Card(
+ Positioned(right: 5, bottom: 5, child: Card(
elevation: 20,
color: Colors.transparent,
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))),
child: Container(
- decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(20))),
+ decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(24))),
width: responsiveUtils.getSizeScreenWidth(context) * 0.55,
- height: responsiveUtils.getSizeScreenHeight(context) * 0.75,
- child: PointerInterceptor(child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- SizedBox(height: 5),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: _buildAppBar(context)),
- SizedBox(height: 10),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: _buildTitleComposer(context)),
- SizedBox(height: 16),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: _buildEmailAddress(context)),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: Divider(color: AppColor.colorDividerComposer, height: 1)),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: _buildSubjectEmail(context)),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: Divider(color: AppColor.colorDividerComposer, height: 1)),
- Expanded(child: Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: _buildEditorAndAttachments(context))),
- Obx(() => _buildBottomBar(context, controller.isEnableEmailSendButton.value)),
- ]
- )
+ height: responsiveUtils.getSizeScreenHeight(context) * 0.7,
+ child: ClipRRect(
+ borderRadius: BorderRadius.all(Radius.circular(24)),
+ child: LayoutBuilder(builder: (context, constraints) =>
+ PointerInterceptor(child: _buildBodyForDesktop(context, constraints)))
)
)
)),
if (controller.screenDisplayMode == ScreenDisplayMode.minimize)
- Positioned(right: 10, bottom: 10, child: Card(
+ Positioned(right: 5, bottom: 5, child: Card(
elevation: 20,
color: Colors.transparent,
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))),
child: Container(
- decoration: BoxDecoration(color: AppColor.colorTextButton, borderRadius: BorderRadius.all(Radius.circular(20))),
+ decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(16))),
width: 500,
height: 50,
- child: PointerInterceptor(child: Row(
- children: [
- Expanded(child: Padding(
- padding: EdgeInsets.only(left: 20, right: 16),
- child: _buildTitleComposer(context),
- )),
- buildIconWeb(
- icon: SvgPicture.asset(imagePaths.icMinimizeComposer, color: Colors.white, fit: BoxFit.fill),
- tooltip: AppLocalizations.of(context).minimize,
- onTap: () => controller.minimizeComposer()),
- buildIconWeb(
- icon: SvgPicture.asset(imagePaths.icFullScreenComposer, color: Colors.white, fit: BoxFit.fill),
- tooltip: AppLocalizations.of(context).fullscreen,
- onTap: () => controller.setFullScreenComposer()),
- Padding(
- padding: EdgeInsets.only(right: 8),
- child: buildIconWeb(
- icon: SvgPicture.asset(imagePaths.icCloseComposer, color: Colors.white, fit: BoxFit.fill),
- tooltip: AppLocalizations.of(context).close,
- onTap: () {
- controller.saveEmailAsDrafts(context);
- controller.closeComposerWeb();
- })),
- ]
- ))
+ child: ClipRRect(
+ borderRadius: BorderRadius.all(Radius.circular(16)),
+ child: PointerInterceptor(child: Row(children: [
+ Padding(
+ padding: EdgeInsets.only(left: 10),
+ child: buildIconWeb(
+ icon: SvgPicture.asset(imagePaths.icCloseMailbox, fit: BoxFit.fill),
+ tooltip: AppLocalizations.of(context).close,
+ onTap: () {
+ controller.saveEmailAsDrafts(context);
+ controller.closeComposerWeb();
+ }
+ )),
+ buildIconWeb(
+ icon: SvgPicture.asset(imagePaths.icFullScreenComposer, fit: BoxFit.fill),
+ tooltip: AppLocalizations.of(context).fullscreen,
+ onTap: () => controller.displayScreenTypeComposerAction(ScreenDisplayMode.fullScreen)),
+ buildIconWeb(
+ icon: SvgPicture.asset(imagePaths.icChevronUp, fit: BoxFit.fill),
+ tooltip: AppLocalizations.of(context).show,
+ onTap: () => controller.displayScreenTypeComposerAction(ScreenDisplayMode.normal)),
+ Expanded(child: Padding(
+ padding: EdgeInsets.only(left: 16, right: 80),
+ child: _buildTitleComposer(context),
+ )),
+ ]))
+ )
)
)),
if (controller.screenDisplayMode == ScreenDisplayMode.fullScreen)
@@ -116,29 +108,15 @@ class ComposerView extends GetWidget {
backgroundColor: Colors.black38,
body: Align(alignment: Alignment.center, child: Card(
color: Colors.transparent,
- shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))),
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))),
child: Container(
- decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(20))),
+ decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(24))),
width: responsiveUtils.getSizeScreenWidth(context) * 0.85,
height: responsiveUtils.getSizeScreenHeight(context) * 0.9,
child: ClipRRect(
- borderRadius: BorderRadius.all(Radius.circular(20)),
- child: PointerInterceptor(child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- SizedBox(height: 5),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: _buildAppBar(context)),
- SizedBox(height: 10),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: _buildTitleComposer(context)),
- SizedBox(height: 16),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: _buildEmailAddress(context)),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: Divider(color: AppColor.colorDividerComposer, height: 1)),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: _buildSubjectEmail(context)),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: Divider(color: AppColor.colorDividerComposer, height: 1)),
- Expanded(child: Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: _buildEditorAndAttachments(context))),
- Obx(() => _buildBottomBar(context, controller.isEnableEmailSendButton.value)),
- ]
- ))
+ borderRadius: BorderRadius.all(Radius.circular(24)),
+ child: LayoutBuilder(builder: (context, constraints) =>
+ PointerInterceptor(child: _buildBodyForDesktop(context, constraints)))
)
)
)
@@ -150,27 +128,30 @@ class ComposerView extends GetWidget {
backgroundColor: Colors.black38,
body: Align(alignment: Alignment.center, child: Card(
color: Colors.transparent,
+ shadowColor: Colors.transparent,
child: Container(
- decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(20))),
+ decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(24))),
margin: EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: ClipRRect(
- borderRadius: BorderRadius.all(Radius.circular(20)),
- child: PointerInterceptor(child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- SizedBox(height: 5),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: _buildAppBar(context)),
- SizedBox(height: 10),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: _buildTitleComposer(context)),
- SizedBox(height: 16),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: _buildEmailAddress(context)),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: Divider(color: AppColor.colorDividerComposer, height: 1)),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: _buildSubjectEmail(context)),
- Padding(padding: EdgeInsets.symmetric(horizontal: 20), child: Divider(color: AppColor.colorDividerComposer, height: 1)),
- Expanded(child: Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: _buildEditorAndAttachments(context))),
- Obx(() => _buildBottomBar(context, controller.isEnableEmailSendButton.value)),
- ]
- ))
+ borderRadius: BorderRadius.all(Radius.circular(24)),
+ child: LayoutBuilder(builder: (context, constraints) =>
+ PointerInterceptor(child: _buildBodyForDesktop(context, constraints)))
+ )
+ )
+ ))
+ ),
+ tabletLarge: Scaffold(
+ backgroundColor: Colors.black38,
+ body: Align(alignment: Alignment.center, child: Card(
+ color: Colors.transparent,
+ shadowColor: Colors.transparent,
+ child: Container(
+ decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(24))),
+ margin: EdgeInsets.symmetric(horizontal: 16, vertical: 20),
+ child: ClipRRect(
+ borderRadius: BorderRadius.all(Radius.circular(24)),
+ child: LayoutBuilder(builder: (context, constraints) =>
+ PointerInterceptor(child: _buildBodyForDesktop(context, constraints)))
)
)
))
@@ -179,55 +160,55 @@ class ComposerView extends GetWidget {
}
Widget _buildAppBar(BuildContext context) {
- return Transform(
- transform: Matrix4.translationValues(15.0, 0.0, 0.0),
- child: Row(
- children: [
- Spacer(),
- if (responsiveUtils.isDesktop(context))
- buildIconWeb(
- icon: SvgPicture.asset(imagePaths.icMinimizeComposer, fit: BoxFit.fill),
- tooltip: AppLocalizations.of(context).minimize,
- onTap: () => controller.minimizeComposer()),
- if (responsiveUtils.isDesktop(context))
- buildIconWeb(
- icon: SvgPicture.asset(imagePaths.icFullScreenComposer, fit: BoxFit.fill),
- tooltip: AppLocalizations.of(context).fullscreen,
- onTap: () => controller.setFullScreenComposer()),
- buildIconWeb(
- icon: SvgPicture.asset(imagePaths.icCloseComposer, fit: BoxFit.fill),
- tooltip: AppLocalizations.of(context).close,
- onTap: () {
- controller.saveEmailAsDrafts(context);
- controller.closeComposerWeb();
- }),
- ]
- ),
+ return Row(
+ children: [
+ buildIconWeb(
+ icon: SvgPicture.asset(imagePaths.icCloseMailbox, fit: BoxFit.fill),
+ tooltip: AppLocalizations.of(context).close,
+ onTap: () {
+ controller.saveEmailAsDrafts(context);
+ controller.closeComposerWeb();
+ }),
+ if (responsiveUtils.isDesktop(context))
+ Obx(() => buildIconWeb(
+ icon: SvgPicture.asset(
+ controller.screenDisplayMode == ScreenDisplayMode.fullScreen
+ ? imagePaths.icFullScreenExit
+ : imagePaths.icFullScreenComposer,
+ fit: BoxFit.fill),
+ tooltip: AppLocalizations.of(context).fullscreen,
+ onTap: () => controller.displayScreenTypeComposerAction(controller.screenDisplayMode == ScreenDisplayMode.fullScreen
+ ? ScreenDisplayMode.normal
+ : ScreenDisplayMode.fullScreen))),
+ if (responsiveUtils.isDesktop(context))
+ buildIconWeb(
+ icon: SvgPicture.asset(imagePaths.icMinimize, fit: BoxFit.fill),
+ tooltip: AppLocalizations.of(context).minimize,
+ onTap: () => controller.displayScreenTypeComposerAction(ScreenDisplayMode.minimize)),
+ Expanded(child: _buildTitleComposer(context)),
+ SizedBox(width: 100),
+ ]
);
}
Widget _buildAppBarForMobile(BuildContext context, bool isEnableSendButton) {
return Container(
- padding: EdgeInsets.only(left: 10, right: 10, top: 10),
+ padding: EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 10),
color: Colors.white,
child: Row(
children: [
buildIconWeb(
- icon: SvgPicture.asset(imagePaths.icCloseComposer, fit: BoxFit.fill),
+ icon: SvgPicture.asset(imagePaths.icClose, width: 30, height: 30, fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).close,
+ iconPadding: EdgeInsets.zero,
onTap: () {
controller.saveEmailAsDrafts(context);
controller.closeComposerWeb();
}),
- Spacer(),
- buildIconWeb(
- icon: SvgPicture.asset(imagePaths.icAttachmentsComposer, fit: BoxFit.fill),
- tooltip: AppLocalizations.of(context).pick_attachments,
- onTap: () => controller.openFilePickerByType(context, FileType.any)),
+ Expanded(child: _buildTitleComposer(context)),
buildIconWeb(
icon: SvgPicture.asset(
- imagePaths.icSendComposer,
- color: isEnableSendButton ? AppColor.colorTextButton : AppColor.colorDisableMailboxCreateButton,
+ isEnableSendButton ? imagePaths.icSendMobile : imagePaths.icSendDisable,
fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).send,
onTap: () => controller.sendEmailAction(context)),
@@ -238,133 +219,157 @@ class ComposerView extends GetWidget {
Widget _buildBottomBar(BuildContext context, bool isEnableSendButton) {
return Container(
- padding: EdgeInsets.only(left: 10, right: 20, top: 18, bottom: 18),
- color: AppColor.colorBottomBarComposer,
- child: Row(
+ padding: EdgeInsets.symmetric(vertical: 16),
+ color: Colors.white,
+ child: Row(mainAxisAlignment: MainAxisAlignment.center,
children: [
- buildIconWeb(
- icon: SvgPicture.asset(imagePaths.icDeleteComposer, fit: BoxFit.fill),
- tooltip: AppLocalizations.of(context).delete,
- onTap: () => controller.deleteComposer()),
- Spacer(),
- buildIconWeb(
- icon: SvgPicture.asset(imagePaths.icAttachmentsComposer, fit: BoxFit.fill),
- tooltip: AppLocalizations.of(context).pick_attachments,
- onTap: () => controller.openFilePickerByType(context, FileType.any)),
- SizedBox(width: 16),
- SizedBox(
- width: 102,
- height: 36,
- child: ElevatedButton(
- style: ButtonStyle(
- backgroundColor: MaterialStateProperty.resolveWith(
- (states) => isEnableSendButton ? AppColor.colorTextButton : AppColor.colorDisableMailboxCreateButton),
- padding: MaterialStateProperty.resolveWith((states) => EdgeInsets.symmetric(horizontal: 16, vertical: 10)),
- elevation: MaterialStateProperty.resolveWith((states) => 0),
- shape: MaterialStateProperty.all(RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(12),
- side: BorderSide(width: 0, color: isEnableSendButton ? AppColor.colorTextButton : AppColor.colorDisableMailboxCreateButton)))),
- child: Row(mainAxisSize: MainAxisSize.min, children: [
- Text(AppLocalizations.of(context).send.toUpperCase(), style: TextStyle(fontSize: 17, color: Colors.white, fontWeight: FontWeight.w500)),
- SizedBox(width: 5),
- SvgPicture.asset(imagePaths.icSendComposer, width: 18, height: 18, fit: BoxFit.fill),
- ]),
- onPressed: () => controller.sendEmailAction(context)
- ),
- )
+ buildTextButton(
+ AppLocalizations.of(context).cancel,
+ textStyle: TextStyle(fontWeight: FontWeight.w500, fontSize: 17, color: AppColor.lineItemListColor),
+ backgroundColor: AppColor.emailAddressChipColor,
+ width: 150,
+ height: 44,
+ radius: 10,
+ onTap: () => controller.closeComposerWeb()),
+ SizedBox(width: 12),
+ buildTextButton(
+ AppLocalizations.of(context).save_to_drafts,
+ textStyle: TextStyle(fontWeight: FontWeight.w500, fontSize: 17, color: AppColor.colorTextButton),
+ backgroundColor: AppColor.emailAddressChipColor,
+ width: 150,
+ height: 44,
+ radius: 10,
+ onTap: () {
+ controller.saveEmailAsDrafts(context);
+ controller.closeComposerWeb();
+ }),
+ SizedBox(width: 12),
+ buildTextButton(
+ AppLocalizations.of(context).send,
+ width: 150,
+ height: 44,
+ radius: 10,
+ onTap: () => controller.sendEmailAction(context)),
]
),
);
}
+ Widget _buildBodyForDesktop(BuildContext context, BoxConstraints constraints) {
+ return Column(children: [
+ Padding(padding: EdgeInsets.only(left: 20, right: 20, top: 8), child: _buildAppBar(context)),
+ Padding(padding: EdgeInsets.only(top: 8), child: Divider(color: AppColor.colorDividerComposer, height: 1)),
+ Padding(
+ padding: EdgeInsets.only(left: 16),
+ child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
+ Padding(padding: EdgeInsets.only(top: 20),
+ child: (AvatarBuilder()
+ ..text('${controller.mailboxDashBoardController.userProfile.value?.getAvatarText() ?? ''}')
+ ..size(56)
+ ..addTextStyle(TextStyle(fontWeight: FontWeight.w600, fontSize: 28, color: Colors.white))
+ ..backgroundColor(AppColor.colorAvatar))
+ .build()),
+ Expanded(child: Padding(
+ padding: EdgeInsets.only(left: 12),
+ child: Column(children: [
+ _buildEmailAddress(context, constraints),
+ Divider(color: AppColor.colorDividerComposer, height: 1),
+ Padding(padding: EdgeInsets.only(right: 16), child: _buildSubjectEmail(context)),
+ Divider(color: AppColor.colorDividerComposer, height: 1),
+ _buildListButton(context),
+ ]),
+ ))
+ ])),
+ Divider(color: AppColor.colorDividerComposer, height: 1),
+ Expanded(child: Padding(
+ padding: EdgeInsets.only(left: responsiveUtils.isMobile(context) ? 16 : 60, right: responsiveUtils.isMobile(context) ? 16 : 25),
+ child: _buildEditorAndAttachments(context))),
+ Divider(color: AppColor.colorDividerComposer, height: 1),
+ Obx(() => _buildBottomBar(context, controller.isEnableEmailSendButton.value)),
+ ]);
+ }
+
Widget _buildTitleComposer(BuildContext context) {
return Obx(() => Text(
controller.subjectEmail.isNotEmpty == true
? controller.subjectEmail.value ?? ''
: AppLocalizations.of(context).new_message.capitalizeFirstEach,
- overflow: TextOverflow.ellipsis,
+ overflow: kIsWeb ? null : TextOverflow.ellipsis,
maxLines: 1,
- style: TextStyle(fontSize: 22, fontWeight: FontWeight.w500,
- color: controller.screenDisplayMode == ScreenDisplayMode.minimize ? Colors.white : Colors.black),
+ textAlign: TextAlign.center,
+ style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black),
));
}
- Widget _buildEmailAddress(BuildContext context) {
- return Column(
- children: [
- Padding(
- padding: EdgeInsets.only(bottom: 12),
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.start,
+ Widget _buildEmailAddress(BuildContext context, BoxConstraints constraints) {
+ log('ComposerView::_buildEmailAddress(): height: ${constraints.maxHeight}');
+ return ConstrainedBox(
+ constraints: BoxConstraints(maxHeight: _getMaxHeightEmailAddressWidget(context, constraints)),
+ child: SingleChildScrollView(
+ child: Column(
children: [
- Padding(
- padding: EdgeInsets.only(right: 8),
- child: Text(
- '${AppLocalizations.of(context).from_email_address_prefix}:',
- overflow: TextOverflow.ellipsis,
- maxLines: 1,
- style: TextStyle(fontSize: 15, color: AppColor.colorHintEmailAddressInput))),
- Expanded(
- child: Obx(() => controller.composerArguments.value != null
- ? Text(
- '<${controller.getEmailAddressSender()}>',
- style: TextStyle(fontSize: 16, color: AppColor.colorContentEmail, fontWeight: FontWeight.normal))
- : SizedBox.shrink()
- )
- )
- ]
- )
- ),
- Divider(color: AppColor.colorDividerComposer, height: 1),
- Obx(() => (EmailAddressInputBuilder(
- context,
- imagePaths,
- appToast,
- PrefixEmailAddress.to,
- controller.listToEmailAddress,
- controller: controller.toEmailAddressController,
- isInitial: controller.isInitialRecipient.value,
- expandMode: controller.expandMode.value)
- ..addExpandAddressActionClick(() => controller.expandEmailAddressAction())
- ..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress))
- ..addOnSuggestionEmailAddress((word) => controller.getAutoCompleteSuggestion(word)))
- .build()
- ),
- Obx(() => controller.expandMode.value == ExpandMode.EXPAND
- ? Divider(color: AppColor.colorDividerComposer, height: 1)
- : SizedBox.shrink()),
- Obx(() => controller.expandMode.value == ExpandMode.EXPAND
- ? (EmailAddressInputBuilder(
- context,
- imagePaths,
- appToast,
- PrefixEmailAddress.cc,
- controller.listCcEmailAddress,
- controller: controller.ccEmailAddressController,
- isInitial: controller.isInitialRecipient.value,)
- ..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress))
- ..addOnSuggestionEmailAddress((word) => controller.getAutoCompleteSuggestion(word)))
- .build()
- : SizedBox.shrink()
- ),
- Obx(() => controller.expandMode.value == ExpandMode.EXPAND
- ? Divider(color: AppColor.colorDividerComposer, height: 1)
- : SizedBox.shrink()),
- Obx(() => controller.expandMode.value == ExpandMode.EXPAND
- ? (EmailAddressInputBuilder(
- context,
- imagePaths,
- appToast,
- PrefixEmailAddress.bcc,
- controller.listBccEmailAddress,
- controller: controller.bccEmailAddressController,
- isInitial: controller.isInitialRecipient.value,)
- ..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress))
- ..addOnSuggestionEmailAddress((word) => controller.getAutoCompleteSuggestion(word)))
- .build()
- : SizedBox.shrink()
- ),
- ],
+ Obx(() => Padding(
+ padding: EdgeInsets.only(left: responsiveUtils.isMobile(context) ? 16 : 0),
+ child: (EmailAddressInputBuilder(context, imagePaths,
+ PrefixEmailAddress.to,
+ controller.listToEmailAddress,
+ controller.listEmailAddressType,
+ expandMode: controller.toAddressExpandMode.value,
+ controller: controller.toEmailAddressController,
+ focusNode: controller.toEmailAddressFocusNode,
+ isInitial: controller.isInitialRecipient.value)
+ ..addOnFocusEmailAddressChangeAction((prefixEmailAddress, focus) => controller.onEmailAddressFocusChange(prefixEmailAddress, focus))
+ ..addOnShowFullListEmailAddressAction((prefixEmailAddress) => controller.showFullEmailAddress(prefixEmailAddress))
+ ..addOnAddEmailAddressTypeAction((prefixEmailAddress) => controller.addEmailAddressType(prefixEmailAddress))
+ ..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress))
+ ..addOnSuggestionEmailAddress((word) => controller.getAutoCompleteSuggestion(word)))
+ .build()
+ )),
+ Obx(() => controller.listEmailAddressType.contains(PrefixEmailAddress.cc) == true
+ ? Divider(color: AppColor.colorDividerComposer, height: 1)
+ : SizedBox.shrink()),
+ Obx(() => controller.listEmailAddressType.contains(PrefixEmailAddress.cc) == true
+ ? Padding(
+ padding: EdgeInsets.only(left: responsiveUtils.isMobile(context) ? 16 : 0),
+ child: (EmailAddressInputBuilder(context, imagePaths,
+ PrefixEmailAddress.cc,
+ controller.listCcEmailAddress,
+ controller.listEmailAddressType,
+ expandMode: controller.ccAddressExpandMode.value,
+ controller: controller.ccEmailAddressController,
+ isInitial: controller.isInitialRecipient.value,)
+ ..addOnFocusEmailAddressChangeAction((prefixEmailAddress, focus) => controller.onEmailAddressFocusChange(prefixEmailAddress, focus))
+ ..addOnShowFullListEmailAddressAction((prefixEmailAddress) => controller.showFullEmailAddress(prefixEmailAddress))
+ ..addOnDeleteEmailAddressTypeAction((prefixEmailAddress) => controller.deleteEmailAddressType(prefixEmailAddress))
+ ..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress))
+ ..addOnSuggestionEmailAddress((word) => controller.getAutoCompleteSuggestion(word)))
+ .build())
+ : SizedBox.shrink()
+ ),
+ Obx(() => controller.listEmailAddressType.contains(PrefixEmailAddress.bcc) == true
+ ? Divider(color: AppColor.colorDividerComposer, height: 1)
+ : SizedBox.shrink()),
+ Obx(() => controller.listEmailAddressType.contains(PrefixEmailAddress.bcc) == true
+ ? Padding(
+ padding: EdgeInsets.only(left: responsiveUtils.isMobile(context) ? 16 : 0),
+ child: (EmailAddressInputBuilder(context, imagePaths,
+ PrefixEmailAddress.bcc,
+ controller.listBccEmailAddress,
+ controller.listEmailAddressType,
+ expandMode: controller.bccAddressExpandMode.value,
+ controller: controller.bccEmailAddressController,
+ isInitial: controller.isInitialRecipient.value,)
+ ..addOnFocusEmailAddressChangeAction((prefixEmailAddress, focus) => controller.onEmailAddressFocusChange(prefixEmailAddress, focus))
+ ..addOnShowFullListEmailAddressAction((prefixEmailAddress) => controller.showFullEmailAddress(prefixEmailAddress))
+ ..addOnDeleteEmailAddressTypeAction((prefixEmailAddress) => controller.deleteEmailAddressType(prefixEmailAddress))
+ ..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress))
+ ..addOnSuggestionEmailAddress((word) => controller.getAutoCompleteSuggestion(word)))
+ .build())
+ : SizedBox.shrink()
+ ),
+ ],
+ ),
+ )
);
}
@@ -377,61 +382,74 @@ class ComposerView extends GetWidget {
child: Text(
'${AppLocalizations.of(context).subject_email}:',
style: TextStyle(fontSize: 15, color: AppColor.colorHintEmailAddressInput))),
- Expanded(child: (TextFieldBuilder()
- ..key(Key('subject_email_input'))
- ..cursorColor(AppColor.colorTextButton)
- ..onChange((value) => controller.setSubjectEmail(value))
- ..textStyle(TextStyle(color: AppColor.colorEmailAddress, fontSize: 15))
- ..textDecoration(InputDecoration(contentPadding: EdgeInsets.zero, border: InputBorder.none))
- ..addController(controller.subjectEmailInputController))
- .build()
+ Expanded(
+ child: FocusScope(child: Focus(
+ onFocusChange: (focus) => controller.onSubjectEmailFocusChange(focus),
+ child: (TextFieldBuilder()
+ ..key(Key('subject_email_input'))
+ ..cursorColor(AppColor.colorTextButton)
+ ..onChange((value) => controller.setSubjectEmail(value))
+ ..textStyle(TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.normal))
+ ..textDecoration(InputDecoration(contentPadding: EdgeInsets.zero, border: InputBorder.none))
+ ..addController(controller.subjectEmailInputController))
+ .build(),
+ ))
)
]
);
}
+ Widget _buildListButton(BuildContext context) {
+ return Transform(
+ transform: Matrix4.translationValues(-10.0, 0.0, 0.0),
+ child: Padding(
+ padding: EdgeInsets.symmetric(vertical: 5),
+ child: Row(children: [
+ buildIconWeb(
+ icon: SvgPicture.asset(imagePaths.icAttachmentsComposer, color: AppColor.colorTextButton, fit: BoxFit.fill),
+ tooltip: AppLocalizations.of(context).attach_file,
+ onTap: () => controller.openFilePickerByType(context, FileType.any)),
+ ])
+ )
+ );
+ }
+
Widget _buildEditorAndAttachments(BuildContext context) {
return Column(
children: [
Obx(() => controller.attachments.isNotEmpty
? Padding(
- padding: EdgeInsets.symmetric(vertical: 4, horizontal: 10),
+ padding: EdgeInsets.only(top: 4, bottom: 4, left: responsiveUtils.isMobile(context) ? 16 : 20, right: responsiveUtils.isMobile(context) ? 16: 0),
child: _buildAttachmentsTitle(context, controller.attachments, controller.expandModeAttachments.value))
: SizedBox.shrink()),
Obx(() => controller.attachments.isEmpty
? _buildAttachmentsLoadingView()
: SizedBox.shrink()),
- Obx(() => controller.attachments.isNotEmpty && controller.expandModeAttachments.value == ExpandMode.COLLAPSE
- ? Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(color: AppColor.colorDividerComposer, height: 1))
- : SizedBox.shrink()),
Obx(() => controller.attachments.isNotEmpty
? Padding(
- padding: EdgeInsets.only(bottom: 8, left: 10, right: 10),
+ padding: EdgeInsets.only(bottom: 8, left: responsiveUtils.isMobile(context) ? 16 : 10, right: responsiveUtils.isMobile(context) ? 16 : 10),
child: _buildAttachmentsList(context, controller.attachments, controller.expandModeAttachments.value))
: SizedBox.shrink()),
Obx(() {
if (controller.composerArguments.value != null) {
if (controller.composerArguments.value?.emailActionType == EmailActionType.compose) {
final initContent = controller.textEditorWeb ?? '';
- log('ComposerView::_buildEditorAndAttachments(): initContent: $initContent');
return Expanded(child: Padding(
- padding: EdgeInsets.symmetric(horizontal: 10),
+ padding: EdgeInsets.symmetric(horizontal: responsiveUtils.isMobile(context) ? 8 : 10),
child: _buildEditor(context, initContent)));
} else if (controller.composerArguments.value?.emailActionType == EmailActionType.edit) {
- final initContent = controller.getEmailContentDraftsAsHtml();
- log('ComposerView::_buildEditorAndAttachments(): initContent: $initContent');
+ final initContent = controller.textEditorWeb ?? controller.getEmailContentDraftsAsHtml();
if (initContent != null) {
return Expanded(child: Padding(
- padding: EdgeInsets.symmetric(horizontal: 10),
+ padding: EdgeInsets.symmetric(horizontal: responsiveUtils.isMobile(context) ? 8 : 10),
child: _buildEditor(context, initContent)));
} else {
return SizedBox.shrink();
}
} else {
- final initContent = controller.getEmailContentQuotedAsHtml(context, controller.composerArguments.value!);
- log('ComposerView::_buildEditorAndAttachments(): initContent: $initContent');
+ final initContent = controller.textEditorWeb ?? controller.getEmailContentQuotedAsHtml(context, controller.composerArguments.value!);
return Expanded(child: Padding(
- padding: EdgeInsets.symmetric(horizontal: 10),
+ padding: EdgeInsets.symmetric(horizontal: responsiveUtils.isMobile(context) ? 8 : 10),
child: _buildEditor(context, initContent)));
}
} else {
@@ -443,11 +461,13 @@ class ComposerView extends GetWidget {
}
Widget _buildEditor(BuildContext context, String initContent) {
+ log('ComposerView::_buildEditor(): initContent: $initContent');
+
return HtmlEditorBrowser.HtmlEditor(
key: Key('composer_editor_web'),
controller: controller.htmlControllerBrowser,
htmlEditorOptions: HtmlEditorBrowser.HtmlEditorOptions(
- hint: '',
+ hint: '${AppLocalizations.of(context).hint_compose_email}',
initialText: initContent,
darkMode: false,
),
@@ -460,7 +480,7 @@ class ComposerView extends GetWidget {
log('ComposerView::_buildComposerEditor(): onBeforeCommand');
controller.setTextEditorWeb(currentHtml);
}, onChangeContent: (String? changed) {
- log('ComposerView::_buildComposerEditor(): onChangeContent');
+ log('ComposerView::_buildComposerEditor(): onChangeContent : $changed');
controller.setTextEditorWeb(changed);
}, onInit: () {
log('ComposerView::_buildComposerEditor(): onInit');
@@ -470,6 +490,10 @@ class ComposerView extends GetWidget {
Future.delayed(const Duration(milliseconds: 500), () {
controller.htmlControllerBrowser.setFocus();
});
+ }, onBlur: () {
+ controller.onEditorFocusChange(false);
+ }, onMouseDown: () {
+ controller.onEditorFocusChange(true);
}
),
);
@@ -555,4 +579,16 @@ class ComposerView extends GetWidget {
final currentWidth = constraints.maxWidth - 40;
return currentWidth / _getMaxItemRowListAttachment(context, constraints);
}
+
+ double _getMaxHeightEmailAddressWidget(BuildContext context, BoxConstraints constraints) {
+ if (responsiveUtils.isDesktop(context)) {
+ if (controller.screenDisplayMode == ScreenDisplayMode.normal) {
+ return constraints.maxHeight > 0 ? constraints.maxHeight * 0.25 : 150.0;
+ } else {
+ return constraints.maxHeight > 0 ? constraints.maxHeight * 0.3 : 150.0;
+ }
+ } else {
+ return constraints.maxHeight > 0 ? constraints.maxHeight * 0.4 : 150.0;
+ }
+ }
}
\ No newline at end of file
diff --git a/lib/features/composer/presentation/widgets/email_address_input_builder.dart b/lib/features/composer/presentation/widgets/email_address_input_builder.dart
index 4226e99c2..bc939d32e 100644
--- a/lib/features/composer/presentation/widgets/email_address_input_builder.dart
+++ b/lib/features/composer/presentation/widgets/email_address_input_builder.dart
@@ -12,29 +12,32 @@ import 'package:model/model.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/prefix_email_address_extension.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
-typedef OnOpenExpandAddressActionClick = void Function();
typedef OnSuggestionEmailAddress = Future> Function(String word);
typedef OnUpdateListEmailAddressAction = void Function(PrefixEmailAddress, List);
+typedef OnAddEmailAddressTypeAction = void Function(PrefixEmailAddress);
+typedef OnDeleteEmailAddressTypeAction = void Function(PrefixEmailAddress);
+typedef OnShowFullListEmailAddressAction = void Function(PrefixEmailAddress);
+typedef OnFocusEmailAddressChangeAction = void Function(PrefixEmailAddress, bool);
class EmailAddressInputBuilder {
final BuildContext _context;
final ImagePaths _imagePaths;
- final AppToast _appToast;
final ExpandMode expandMode;
final PrefixEmailAddress _prefixEmailAddress;
- List _listEmailAddress;
+ final List _listEmailAddressType;
final TextEditingController? controller;
+ final FocusNode? focusNode;
final bool? isInitial;
- final bool hasAvatar;
- OnOpenExpandAddressActionClick? _onOpenExpandAddressActionClick;
+ List _listEmailAddress;
+
OnUpdateListEmailAddressAction? _onUpdateListEmailAddressAction;
OnSuggestionEmailAddress? _onSuggestionEmailAddress;
-
- void addExpandAddressActionClick(OnOpenExpandAddressActionClick onOpenExpandAddressActionClick) {
- _onOpenExpandAddressActionClick = onOpenExpandAddressActionClick;
- }
+ OnAddEmailAddressTypeAction? _onAddEmailAddressTypeAction;
+ OnDeleteEmailAddressTypeAction? _onDeleteEmailAddressTypeAction;
+ OnShowFullListEmailAddressAction? _onShowFullListEmailAddressAction;
+ OnFocusEmailAddressChangeAction? _onFocusEmailAddressChangeAction;
void addOnUpdateListEmailAddressAction(OnUpdateListEmailAddressAction onUpdateListEmailAddressAction) {
_onUpdateListEmailAddressAction = onUpdateListEmailAddressAction;
@@ -44,124 +47,174 @@ class EmailAddressInputBuilder {
_onSuggestionEmailAddress = onSuggestionEmailAddress;
}
+ void addOnAddEmailAddressTypeAction(OnAddEmailAddressTypeAction onAddEmailAddressTypeAction) {
+ _onAddEmailAddressTypeAction = onAddEmailAddressTypeAction;
+ }
+
+ void addOnDeleteEmailAddressTypeAction(OnDeleteEmailAddressTypeAction onDeleteEmailAddressTypeAction) {
+ _onDeleteEmailAddressTypeAction = onDeleteEmailAddressTypeAction;
+ }
+
+ void addOnShowFullListEmailAddressAction(OnShowFullListEmailAddressAction onShowFullListEmailAddressAction) {
+ _onShowFullListEmailAddressAction = onShowFullListEmailAddressAction;
+ }
+
+ void addOnFocusEmailAddressChangeAction(OnFocusEmailAddressChangeAction onFocusEmailAddressChangeAction) {
+ _onFocusEmailAddressChangeAction = onFocusEmailAddressChangeAction;
+ }
+
EmailAddressInputBuilder(
this._context,
this._imagePaths,
- this._appToast,
this._prefixEmailAddress,
this._listEmailAddress,
+ this._listEmailAddressType,
{
this.isInitial,
- this.hasAvatar = false,
this.controller,
- this.expandMode = ExpandMode.COLLAPSE,
+ this.focusNode,
+ this.expandMode = ExpandMode.EXPAND,
}
);
Widget build() {
return Row(
- crossAxisAlignment: CrossAxisAlignment.start,
- mainAxisAlignment: MainAxisAlignment.center,
children: [
- Padding(
- padding: EdgeInsets.only(right: 8, top: 15),
- child: Text(
- '${_prefixEmailAddress.asName(_context)}:',
- style: TextStyle(fontSize: 15, color: AppColor.colorHintEmailAddressInput))),
- Expanded(child: _buildTagEditor()),
+ Text('${_prefixEmailAddress.asName(_context)}:',
+ style: TextStyle(fontSize: 15, color: AppColor.colorHintEmailAddressInput)),
+ buildIconWeb(
+ icon: SvgPicture.asset(_imagePaths.icAddEmailAddress, fit: BoxFit.fill),
+ tooltip: AppLocalizations.of(_context).email_address_suggestion,
+ onTap: () => {}),
+ Expanded(child: Padding(
+ padding: EdgeInsets.only(right: _listEmailAddressType.isNotEmpty ? 16 : 0),
+ child: _buildTagEditor())),
if (_prefixEmailAddress == PrefixEmailAddress.to)
- Padding(
- padding: EdgeInsets.only(top: kIsWeb ? 8 : 0),
- child: _buildButtonExpandAddress())
+ Row(children: [
+ if (!_listEmailAddressType.contains(PrefixEmailAddress.cc))
+ Transform(
+ transform: Matrix4.translationValues(
+ _listEmailAddressType.contains(PrefixEmailAddress.bcc) ? 0.0 : 16.0,
+ 0.0, 0.0),
+ child: buildTextCircleButton(AppLocalizations.of(_context).cc_email_address_prefix,
+ textStyle: TextStyle(
+ fontSize: 15,
+ fontWeight: FontWeight.normal,
+ decoration: TextDecoration.underline,
+ color: AppColor.lineItemListColor),
+ onTap: () => _onAddEmailAddressTypeAction?.call(PrefixEmailAddress.cc))),
+ if (!_listEmailAddressType.contains(PrefixEmailAddress.bcc))
+ buildTextCircleButton(AppLocalizations.of(_context).bcc_email_address_prefix,
+ textStyle: TextStyle(
+ fontSize: 15,
+ fontWeight: FontWeight.normal,
+ decoration: TextDecoration.underline,
+ color: AppColor.lineItemListColor),
+ onTap: () => _onAddEmailAddressTypeAction?.call(PrefixEmailAddress.bcc)),
+ ]),
+ if (_prefixEmailAddress != PrefixEmailAddress.to)
+ buildIconWeb(
+ icon: SvgPicture.asset(_imagePaths.icCloseComposer, fit: BoxFit.fill),
+ onTap: () => _onDeleteEmailAddressTypeAction?.call(_prefixEmailAddress))
]
);
}
- Widget _buildButtonExpandAddress() {
- return Material(
- type: MaterialType.circle,
- color: Colors.transparent,
- child: TextButton(
- child: Text(
- expandMode == ExpandMode.EXPAND ? AppLocalizations.of(_context).hide : AppLocalizations.of(_context).details,
- style: TextStyle(fontWeight: FontWeight.normal, fontSize: 16, color: AppColor.colorTextButton)),
- onPressed: () => _onOpenExpandAddressActionClick?.call()
- )
- );
- }
-
Widget _buildTagEditor() {
return StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
- return TagEditor(
- length: _listEmail.length,
- controller: controller,
- keyboardType: TextInputType.emailAddress,
- textInputAction: TextInputAction.done,
- hasAddButton: false,
- tagSpacing: 8,
- delimiters: [' '],
- resetTextOnSubmitted: true,
- textStyle: TextStyle(color: AppColor.colorEmailAddress, fontSize: 14, fontWeight: FontWeight.w500),
- onSubmitted: (value) {
- if (GetUtils.isEmail(value)) {
- setState(() => _listEmailAddress.add(EmailAddress(value, value)));
- _onUpdateListEmailAddressAction?.call(_prefixEmailAddress, _listEmailAddress);
- } else {
- _appToast.showErrorToast(AppLocalizations.of(context).email_address_is_not_in_the_correct_format);
- }
- },
- inputDecoration: InputDecoration(
- border: InputBorder.none,
- hintText: AppLocalizations.of(_context).hint_text_email_address,
- hintMaxLines: 1,
- hintStyle: TextStyle(color: AppColor.colorHintEmailAddressInput, fontSize: 14, fontWeight: FontWeight.w500)
- ),
- tagBuilder: (context, index) => Padding(
- padding: EdgeInsets.only(top: kIsWeb ? 8 : 0),
- child: Chip(
- labelPadding: EdgeInsets.only(left: 12, right: 12, bottom: 2),
- label: Text(_listEmail[index], maxLines: 1, overflow: TextOverflow.ellipsis),
- deleteIcon: SvgPicture.asset(_imagePaths.icDeleteEmailAddress, fit: BoxFit.fill),
- labelStyle: TextStyle(color: AppColor.colorEmailAddress, fontSize: 14, fontWeight: FontWeight.w500),
- backgroundColor: AppColor.emailAddressChipColor,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(10),
- side: BorderSide(width: 0, color: AppColor.emailAddressChipColor),
+ final newListEmailAddress = _isCollapse ? _listEmailAddress.sublist(0, 1) : _listEmailAddress;
+ return FocusScope(child: Focus(
+ onFocusChange: (focus) => _onFocusEmailAddressChangeAction?.call(_prefixEmailAddress, focus),
+ child: TagEditor(
+ length: newListEmailAddress.length,
+ controller: controller,
+ focusNode: focusNode,
+ keyboardType: TextInputType.emailAddress,
+ textInputAction: TextInputAction.done,
+ hasAddButton: false,
+ tagSpacing: 8,
+ delimiters: [' '],
+ minTextFieldWidth: 20,
+ resetTextOnSubmitted: true,
+ textStyle: TextStyle(color: AppColor.colorEmailAddress, fontSize: 14, fontWeight: FontWeight.w500),
+ onSubmitted: (value) {
+ setState(() => _listEmailAddress.add(EmailAddress(null, value)));
+ _onUpdateListEmailAddressAction?.call(_prefixEmailAddress, _listEmailAddress);
+ },
+ inputDecoration: InputDecoration(border: InputBorder.none),
+ tagBuilder: (context, index) => Stack(alignment: Alignment.centerRight, children: [
+ Padding(
+ padding: EdgeInsets.only(top: kIsWeb ? 8 : 0, right: _isCollapse ? 50 : 0),
+ child: InkWell(
+ onTap: () => _isCollapse ? _onShowFullListEmailAddressAction?.call(_prefixEmailAddress) : null,
+ child: Chip(
+ labelPadding: EdgeInsets.only(left: 12, right: 12, bottom: 2),
+ label: Text(newListEmailAddress[index].asString(), maxLines: 1, overflow: kIsWeb ? null : TextOverflow.ellipsis),
+ deleteIcon: SvgPicture.asset(_imagePaths.icClose, fit: BoxFit.fill),
+ labelStyle: TextStyle(color: Colors.black, fontSize: 17, fontWeight: FontWeight.normal),
+ backgroundColor: _isEmailAddressValid(_listEmailAddress[index].emailAddress) ? AppColor.colorEmailAddressTag : Colors.white,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(10),
+ side: BorderSide(
+ width: _isEmailAddressValid(_listEmailAddress[index].emailAddress) ? 0 : 1,
+ color: _isEmailAddressValid(_listEmailAddress[index].emailAddress) ? AppColor.colorEmailAddressTag : AppColor.colorBorderEmailAddressInvalid),
+ ),
+ avatar: newListEmailAddress[index].displayName.isNotEmpty
+ ? CircleAvatar(
+ backgroundColor: AppColor.colorTextButton,
+ child: Text(
+ _listEmailAddress[index].displayName[0].toUpperCase(),
+ style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w500)))
+ : null,
+ onDeleted: () {
+ setState(() => _listEmailAddress.removeAt(index));
+ _onUpdateListEmailAddressAction?.call(_prefixEmailAddress, _listEmailAddress);
+ },
+ )
+ )
),
- avatar: hasAvatar == true
- ? CircleAvatar(
- backgroundColor: AppColor.colorTextButton,
- child: Text(
- _listEmail[index].isNotEmpty ? _listEmail[index][0].toUpperCase() : '',
- style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w500)))
- : null,
- onDeleted: () {
- setState(() => _listEmailAddress.removeAt(index));
- _onUpdateListEmailAddressAction?.call(_prefixEmailAddress, _listEmailAddress);
- },
- )),
- onTagChanged: (String value) {
- if (GetUtils.isEmail(value)) {
- setState(() => _listEmailAddress.add(EmailAddress(value, value)));
- _onUpdateListEmailAddressAction?.call(_prefixEmailAddress, _listEmailAddress);
- } else {
- _appToast.showErrorToast(AppLocalizations.of(context).email_address_is_not_in_the_correct_format);
- }
- },
- findSuggestions: (String query) {
- if (query.isNotEmpty && _onSuggestionEmailAddress != null) {
- return _onSuggestionEmailAddress!(query);
- }
- return [];
- },
- suggestionBuilder: (context, tagEditorState, emailAddress) {
- return _buildSuggestionItem(setState, context, tagEditorState, emailAddress);
- },
- );
+ if (_isCollapse) _buildCounter(context, _listEmailAddress.length - newListEmailAddress.length),
+ ]),
+ onTagChanged: (String value) {
+ setState(() => _listEmailAddress.add(EmailAddress(null, value)));
+ _onUpdateListEmailAddressAction?.call(_prefixEmailAddress, _listEmailAddress);
+ },
+ findSuggestions: (String query) {
+ if (query.isNotEmpty && _onSuggestionEmailAddress != null) {
+ return _onSuggestionEmailAddress!(query);
+ }
+ return [];
+ },
+ suggestionBuilder: (context, tagEditorState, emailAddress) {
+ return _buildSuggestionItem(setState, context, tagEditorState, emailAddress);
+ },
+ )
+ ));
});
}
- List get _listEmail => _listEmailAddress.map((emailAddress) => emailAddress.asString()).toList();
+ Widget _buildCounter(BuildContext context, int count) {
+ return Padding(
+ padding: EdgeInsets.only(left: 8, top: kIsWeb ? 8 : 0),
+ child: InkWell(
+ onTap: () => _onShowFullListEmailAddressAction?.call(_prefixEmailAddress),
+ child: Chip(
+ labelPadding: EdgeInsets.symmetric(horizontal: 8),
+ label: Text('+$count', maxLines: 1, overflow: kIsWeb ? null : TextOverflow.ellipsis),
+ labelStyle: TextStyle(color: Colors.black, fontSize: 17, fontWeight: FontWeight.normal),
+ backgroundColor: AppColor.colorEmailAddressTag,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(10),
+ side: BorderSide(width: 0, color: AppColor.colorEmailAddressTag),
+ ),
+ ),
+ ),
+ );
+ }
+
+ bool get _isCollapse {
+ return _listEmailAddress.length > 1 && expandMode == ExpandMode.COLLAPSE;
+ }
Widget _buildSuggestionItem(StateSetter setState, BuildContext context, TagsEditorState tagEditorState, EmailAddress emailAddress) {
return Container(
@@ -175,14 +228,14 @@ class EmailAddressInputBuilder {
backgroundColor: AppColor.avatarColor,
child: Text(
emailAddress.asString().isNotEmpty ? emailAddress.asString()[0].toUpperCase() : '',
- style: TextStyle(color: AppColor.appColor, fontSize: 16, fontWeight: FontWeight.w500))),
+ style: TextStyle(color: Colors.black, fontSize: 16, fontWeight: FontWeight.w600))),
title: Text(
emailAddress.asString(),
- style: TextStyle(color: AppColor.nameUserColor, fontSize: 14, fontWeight: FontWeight.w500)),
+ style: TextStyle(color: Colors.black, fontSize: 16, fontWeight: FontWeight.normal)),
subtitle: emailAddress.displayName.isNotEmpty && emailAddress.emailAddress.isNotEmpty
? Text(
- emailAddress.emailAddress,
- style: TextStyle(color: AppColor.baseTextColor, fontSize: 12, fontWeight: FontWeight.w500))
+ emailAddress.emailAddress,
+ style: TextStyle(color: AppColor.colorHintSearchBar, fontSize: 13, fontWeight: FontWeight.normal))
: null,
onTap: () {
setState(() => _listEmailAddress.add(emailAddress));
@@ -192,4 +245,6 @@ class EmailAddressInputBuilder {
),
);
}
+
+ bool _isEmailAddressValid(String emailAddress) => GetUtils.isEmail(emailAddress);
}
\ No newline at end of file
diff --git a/lib/features/composer/presentation/widgets/suggestions_box_controller.dart b/lib/features/composer/presentation/widgets/suggestions_box_controller.dart
deleted file mode 100644
index d16c83918..000000000
--- a/lib/features/composer/presentation/widgets/suggestions_box_controller.dart
+++ /dev/null
@@ -1,35 +0,0 @@
-import 'package:flutter/material.dart';
-
-class SuggestionsBoxController {
- final BuildContext context;
-
- OverlayEntry? overlayEntry;
-
- bool _isOpened = false;
-
- bool get isOpened => _isOpened;
-
- SuggestionsBoxController(this.context);
-
- void open() {
- if (_isOpened) return;
- assert(overlayEntry != null);
- Overlay.of(context)?.insert(overlayEntry!);
- _isOpened = true;
- }
-
- void close() {
- if (!_isOpened) return;
- assert(overlayEntry != null);
- overlayEntry!.remove();
- _isOpened = false;
- }
-
- void toggle() {
- if (_isOpened) {
- close();
- } else {
- open();
- }
- }
-}
diff --git a/lib/features/mailbox/presentation/mailbox_controller.dart b/lib/features/mailbox/presentation/mailbox_controller.dart
index 22ba0ffb3..de7a1fd03 100644
--- a/lib/features/mailbox/presentation/mailbox_controller.dart
+++ b/lib/features/mailbox/presentation/mailbox_controller.dart
@@ -10,6 +10,7 @@ import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/core/state.dart' as jmapState;
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/model.dart';
+import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:rxdart/transformers.dart';
import 'package:tmail_ui_user/features/base/base_mailbox_controller.dart';
import 'package:tmail_ui_user/features/composer/domain/state/save_email_as_drafts_state.dart';
@@ -457,7 +458,7 @@ class MailboxController extends BaseMailboxController {
showDialog(
context: context,
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
- builder: (BuildContext context) => (ConfirmDialogBuilder(_imagePaths)
+ builder: (BuildContext context) => PointerInterceptor(child: (ConfirmDialogBuilder(_imagePaths)
..key(Key('confirm_dialog_delete_mailbox'))
..title(AppLocalizations.of(context).delete_mailboxes)
..content(AppLocalizations.of(context).message_confirmation_dialog_delete_mailbox(presentationMailbox.name?.name ?? ''))
@@ -467,7 +468,7 @@ class MailboxController extends BaseMailboxController {
..onCloseButtonAction(() => popBack())
..onConfirmButtonAction(AppLocalizations.of(context).delete, () => _deleteMailboxAction(presentationMailbox))
..onCancelButtonAction(AppLocalizations.of(context).cancel, () => popBack()))
- .build());
+ .build()));
}
}
diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart
index bf652e908..ad01f0734 100644
--- a/lib/main/localizations/app_localizations.dart
+++ b/lib/main/localizations/app_localizations.dart
@@ -207,10 +207,10 @@ class AppLocalizations {
);
}
- String get your_email_should_have_at_least_one_recipient {
+ String get message_dialog_send_email_without_recipient {
return Intl.message(
'Your email should have at least one recipient',
- name: 'your_email_should_have_at_least_one_recipient',
+ name: 'message_dialog_send_email_without_recipient',
);
}
@@ -810,10 +810,10 @@ class AppLocalizations {
name: 'show_all');
}
- String get message_confirm_send_email {
+ String get message_dialog_send_email_without_a_subject {
return Intl.message(
'Are you sure to send messages without a subject?',
- name: 'message_confirm_send_email');
+ name: 'message_dialog_send_email_without_a_subject');
}
String get app_name {
@@ -888,4 +888,72 @@ class AppLocalizations {
'Collapse',
name: 'collapse');
}
+
+ String get save_to_drafts {
+ return Intl.message(
+ 'Save to drafts',
+ name: 'save_to_drafts',
+ );
+ }
+
+ String get hint_compose_email {
+ return Intl.message(
+ 'Start composing a letter...',
+ name: 'hint_compose_email');
+ }
+
+ String get email_address_suggestion {
+ return Intl.message(
+ 'Email address suggestion',
+ name: 'email_address_suggestion',
+ );
+ }
+
+ String get attach_file {
+ return Intl.message(
+ 'Attach file',
+ name: 'attach_file');
+ }
+
+ String get show {
+ return Intl.message(
+ 'Show',
+ name: 'show');
+ }
+
+ String get add_recipients {
+ return Intl.message(
+ 'Add recipients',
+ name: 'add_recipients');
+ }
+
+ String get sending_failed {
+ return Intl.message(
+ 'Sending failed',
+ name: 'sending_failed');
+ }
+
+ String get send_anyway {
+ return Intl.message(
+ 'Send anyway',
+ name: 'send_anyway');
+ }
+
+ String get empty_subject {
+ return Intl.message(
+ 'Empty subject',
+ name: 'empty_subject');
+ }
+
+ String get message_dialog_send_email_with_email_address_invalid {
+ return Intl.message(
+ 'Check the correctness of email addresses and try again',
+ name: 'message_dialog_send_email_with_email_address_invalid');
+ }
+
+ String get fix_email_addresses {
+ return Intl.message(
+ 'Fix email addresses',
+ name: 'fix_email_addresses');
+ }
}
\ No newline at end of file