TF-295 Apply new UI for composer web browser
@@ -1,4 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13 2H6C5.46957 2 4.96086 2.21071 4.58579 2.58579C4.21071 2.96086 4 3.46957 4 4V20C4 20.5304 4.21071 21.0391 4.58579 21.4142C4.96086 21.7893 5.46957 22 6 22H18C18.5304 22 19.0391 21.7893 19.4142 21.4142C19.7893 21.0391 20 20.5304 20 20V9L13 2Z" stroke="#3840F7" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M13 2V9H20" stroke="#3840F7" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 544 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.3558 14.1983C17.7077 13.8473 18.2776 13.8479 18.6286 14.1998C18.9797 14.5517 18.979 15.1216 18.6271 15.4726L14.0643 20.0247C11.5536 22.5294 7.48776 22.5246 4.98298 20.0139C4.95572 19.9865 4.95572 19.9865 4.92868 19.9588C2.34868 17.3097 2.35833 13.0844 4.95042 10.4471L11.7862 3.49188C13.588 1.65866 16.5347 1.63314 18.3679 3.43489C18.407 3.47336 18.4455 3.51253 18.4833 3.55236L18.5148 3.58567C20.3243 5.49447 20.304 8.49133 18.4689 10.3755L11.5768 17.452C10.5166 18.5406 8.79579 18.6261 7.63278 17.6481L7.55711 17.5845C6.39786 16.6095 6.24841 14.8795 7.22332 13.7202C7.25904 13.6777 7.29605 13.6363 7.33428 13.5961L11.9727 8.71536C12.3151 8.35505 12.8847 8.34055 13.245 8.68296C13.6053 9.02537 13.6198 9.59503 13.2774 9.95534L8.63906 14.8361C8.62592 14.8499 8.6132 14.8641 8.60092 14.8787C8.26586 15.2772 8.31722 15.8718 8.71565 16.2068L8.79132 16.2705C9.23246 16.6415 9.88516 16.609 10.2873 16.1961L17.1795 9.11961C18.3404 7.92759 18.3532 6.03162 17.2085 4.82401L17.1769 4.79069C17.1538 4.76627 17.1302 4.74225 17.1062 4.71866C15.982 3.61374 14.1749 3.62939 13.07 4.75361L6.23418 11.7088C4.32817 13.6481 4.32107 16.755 6.2182 18.703C6.23766 18.7229 6.23766 18.7229 6.25728 18.7426C8.05994 20.5496 10.9861 20.553 12.793 18.7504L17.3558 14.1983Z" fill="#99A2AD"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.0056 6.82733C7.68016 6.50189 7.15252 6.50189 6.82709 6.82733C6.50165 7.15277 6.50165 7.6804 6.82709 8.00584L10.8212 11.9999L6.82709 15.994C6.50165 16.3194 6.50165 16.8471 6.82709 17.1725C7.15252 17.4979 7.68016 17.4979 8.0056 17.1725L11.9997 13.1784L15.9938 17.1725C16.3192 17.4979 16.8468 17.4979 17.1723 17.1725C17.4977 16.8471 17.4977 16.3194 17.1723 15.994L13.1782 11.9999L17.1723 8.00584C17.4977 7.6804 17.4977 7.15277 17.1723 6.82733C16.8468 6.50189 16.3192 6.50189 15.9938 6.82733L11.9997 10.8214L8.0056 6.82733Z" fill="#99A2AD"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 653 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="13" height="12" viewBox="0 0 13 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path opacity="0.24" d="M6.66699 12C9.9807 12 12.667 9.31371 12.667 6C12.667 2.68629 9.9807 0 6.66699 0C3.35328 0 0.666992 2.68629 0.666992 6C0.666992 9.31371 3.35328 12 6.66699 12Z" fill="black"/>
|
||||
<path d="M9.0352 3.6318C9.21093 3.80754 9.21093 4.09246 9.0352 4.2682L7.30349 6L9.0352 7.73181C9.19629 7.8929 9.20971 8.14574 9.07547 8.32212L9.0352 8.3682C8.85946 8.54394 8.57454 8.54394 8.3988 8.3682L6.66699 6.6365L4.93519 8.3682C4.75945 8.54394 4.47453 8.54394 4.29879 8.3682C4.12306 8.19247 4.12306 7.90754 4.29879 7.73181L6.03049 6L4.29879 4.2682C4.1377 4.10711 4.12428 3.85427 4.25852 3.67788L4.29879 3.6318C4.47453 3.45607 4.75945 3.45607 4.93519 3.6318L6.66699 5.3635L8.3988 3.6318C8.57454 3.45607 8.85946 3.45607 9.0352 3.6318Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 854 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5567 7.10185C13.0528 7.13286 13.4298 7.56015 13.3988 8.05624L12.8988 16.0562C12.8678 16.5523 12.4405 16.9294 11.9444 16.8983C11.4484 16.8673 11.0713 16.44 11.1023 15.944L11.6023 7.94396C11.6333 7.44787 12.0606 7.07085 12.5567 7.10185Z" fill="#959DAD"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.44445 7.10185C6.94836 7.13286 6.57133 7.56015 6.60234 8.05624L7.10234 16.0562C7.13334 16.5523 7.56064 16.9294 8.05673 16.8983C8.55281 16.8673 8.92984 16.44 8.89883 15.944L8.39883 7.94396C8.36783 7.44787 7.94053 7.07085 7.44445 7.10185Z" fill="#959DAD"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.00059 3.1001C0.50353 3.1001 0.100586 3.50304 0.100586 4.0001C0.100586 4.49715 0.50353 4.9001 1.00059 4.9001H2.18101L3.33564 17.2161C3.38098 17.6999 3.41978 18.1139 3.47866 18.4534C3.54079 18.8117 3.6356 19.1568 3.82955 19.4828C4.12375 19.9773 4.55842 20.3732 5.07825 20.6199C5.42094 20.7826 5.77339 20.8448 6.13588 20.8732C6.47946 20.9001 6.89528 20.9001 7.38125 20.9001H12.6199C13.1059 20.9001 13.5217 20.9001 13.8653 20.8732C14.2278 20.8448 14.5802 20.7826 14.9229 20.6199C15.4427 20.3732 15.8774 19.9773 16.1716 19.4828C16.3656 19.1568 16.4604 18.8117 16.5225 18.4534C16.5814 18.1139 16.6202 17.6999 16.6655 17.2161L17.8202 4.9001H19.0006C19.4976 4.9001 19.9006 4.49715 19.9006 4.0001C19.9006 3.50304 19.4976 3.1001 19.0006 3.1001H13.7962C13.3899 1.38008 11.8447 0.100098 10.0006 0.100098C8.15644 0.100098 6.61124 1.38008 6.20495 3.1001H1.00059ZM5.12466 17.0148C5.174 17.541 5.20681 17.8842 5.25219 18.1459C5.29568 18.3966 5.34007 18.5013 5.3765 18.5625C5.48809 18.7501 5.65297 18.9002 5.85014 18.9938C5.91452 19.0244 6.02283 19.0588 6.27655 19.0787C6.54136 19.0995 6.88605 19.1001 7.41462 19.1001H12.5866C13.1151 19.1001 13.4598 19.0995 13.7246 19.0787C13.9783 19.0588 14.0867 19.0244 14.151 18.9938C14.3482 18.9002 14.5131 18.7501 14.6247 18.5625C14.6611 18.5013 14.7055 18.3966 14.749 18.1459C14.7944 17.8842 14.8272 17.541 14.8765 17.0148L16.0123 4.9001H3.98891L5.12466 17.0148ZM10.0006 1.9001C9.16287 1.9001 8.43971 2.39061 8.10268 3.1001H11.8985C11.5615 2.39061 10.8383 1.9001 10.0006 1.9001Z" fill="#959DAD"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg width="29" height="38" viewBox="0 0 29 38" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M27.8914 10.7609L18.783 1.21875C18.2769 1.86935 17.2215 3.17056 17.048 3.17056C16.8312 3.17056 17.2649 7.07417 17.2649 7.29104C17.2649 7.46453 19.1444 10.5441 20.0842 12.0621L27.8914 10.7609Z"
|
||||
fill="white" stroke="#E0E0E0" stroke-width="0.433735" stroke-linejoin="round" />
|
||||
<path
|
||||
d="M17.9157 1H2.73494C1.77676 1 1 1.77676 1 2.73494V35.2651C1 36.2232 1.77676 37 2.73494 37H26.3735C27.3317 37 28.1084 36.2232 28.1084 35.2651V11.6265C28.1084 10.6683 27.3317 9.89157 26.3735 9.89157H21.3855C20.4274 9.89157 19.6506 9.11481 19.6506 8.15663V2.73494C19.6506 1.77676 18.8738 1 17.9157 1Z"
|
||||
fill="#FAFAFA" stroke="#E0E0E0" stroke-width="0.433735" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 792 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 4.9C14 4.40294 14.4029 4 14.9 4H19.1C19.5971 4 20 4.40294 20 4.9V9.10001C20 9.59706 19.5971 10 19.1 10C18.6029 10 18.2 9.59706 18.2 9.10001V7.07279L14.5364 10.7364C14.1849 11.0879 13.6151 11.0879 13.2636 10.7364C12.9121 10.3849 12.9121 9.81508 13.2636 9.46361L16.9272 5.8H14.9C14.4029 5.8 14 5.39706 14 4.9Z" fill="#99A2AD"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.7364 13.2636C11.0879 13.6151 11.0879 14.1849 10.7364 14.5364L7.07279 18.2H9.09818C9.59524 18.2 9.99818 18.6029 9.99818 19.1C9.99818 19.5971 9.59524 20 9.09818 20H4.9C4.40294 20 4 19.5971 4 19.1V14.9C4 14.403 4.40294 14 4.9 14C5.39706 14 5.8 14.403 5.8 14.9V16.9272L9.46361 13.2636C9.81508 12.9121 10.3849 12.9121 10.7364 13.2636Z" fill="#99A2AD"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 883 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="4" y="18.5" width="16" height="1.5" rx="0.75" fill="#99A2AD"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 175 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.73868 15.7541C5.05303 17.609 4.62218 18.9122 4.44611 19.6638C3.89277 22.026 3.49042 22.5581 5.55279 21.4355C7.61516 20.3129 17.5994 14.7519 19.8269 13.516C22.7306 11.905 22.7694 12.0309 19.6711 10.3204C17.3114 9.0176 7.4442 3.60166 5.55279 2.53825C3.66138 1.47483 3.89277 1.94771 4.44611 4.30986C4.62446 5.07122 5.06157 6.38609 5.75745 8.25446C6.24415 9.56121 7.37624 10.5202 8.74523 10.7854L14.5097 11.9019C14.5639 11.9128 14.599 11.9655 14.5882 12.0196C14.5802 12.0592 14.5493 12.0901 14.5097 12.0981L8.73224 13.2136C7.3588 13.4788 6.22367 14.4421 5.73868 15.7541Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 698 B |
@@ -32,6 +32,7 @@ export 'presentation/views/text/rich_text_builder.dart';
|
||||
export 'presentation/views/responsive/responsive_widget.dart';
|
||||
export 'presentation/views/list/tree_view.dart';
|
||||
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';
|
||||
|
||||
@@ -79,12 +79,12 @@ extension AppColor on Color {
|
||||
static const colorInputBorderCreateMailbox = Color(0x1F000000);
|
||||
static const colorInputBackgroundErrorVerifyName = Color(0xFFFAEBEB);
|
||||
static const colorInputBackgroundCreateMailbox = Color(0xFFF2F3F5);
|
||||
static const colorHintInputCreateMailbox= Color(0xFFA9B4C2);
|
||||
static const colorMessageConfirmDialog= Color(0xFF6D7885);
|
||||
static const colorActionDeleteConfirmDialog= Color(0xFFE64646);
|
||||
static const colorActionCancelDialog= Color(0xFF007AFF);
|
||||
static const colorMessageDialog= Color(0xFF222222);
|
||||
static const colorConfirmActionDialog= Color(0xFFF2F2F2);
|
||||
static const colorHintInputCreateMailbox = Color(0xFFA9B4C2);
|
||||
static const colorMessageConfirmDialog = Color(0xFF6D7885);
|
||||
static const colorActionDeleteConfirmDialog = Color(0xFFE64646);
|
||||
static const colorActionCancelDialog = Color(0xFF007AFF);
|
||||
static const colorMessageDialog = Color(0xFF222222);
|
||||
static const colorConfirmActionDialog = Color(0xFFF2F2F2);
|
||||
static const colorEmailAddress = Color(0xFF333333);
|
||||
static const colorHintEmailAddressInput = Color(0x993C3C43);
|
||||
static const colorDividerComposer = Color(0xFFC6C6C8);
|
||||
@@ -94,6 +94,10 @@ extension AppColor on Color {
|
||||
static const colorEmailAddressPrefix = Color(0xFF9AA7B6);
|
||||
static const colorEmailAddressTag = Color(0x146D7885);
|
||||
static const colorLineLeftEmailView = Color(0x2999A2AD);
|
||||
static const colorShadowComposer = Color(0x1F000000);
|
||||
static const colorBottomBarComposer = Color(0x5CEBEDF0);
|
||||
static const colorShadowComposerFullScreen = Color(0x33000000);
|
||||
static const colorCancelButton = Color(0xFFF2F2F2);
|
||||
|
||||
static const mapGradientColor = [
|
||||
[Color(0xFF21D4FD), Color(0xFFB721FF)],
|
||||
|
||||
@@ -10,7 +10,6 @@ class ImagePaths {
|
||||
String get icBack => _getImagePath('ic_back.svg');
|
||||
String get icEyeDisable => _getImagePath('ic_eye_disable.svg');
|
||||
String get icShare => _getImagePath('ic_share.svg');
|
||||
String get icAttachmentFile => _getImagePath('ic_attachment_file.svg');
|
||||
String get icMoreReceiver => _getImagePath('ic_more_receiver.svg');
|
||||
String get icComposerClose => _getImagePath('ic_composer_close.svg');
|
||||
String get icComposerSend => _getImagePath('ic_composer_send.svg');
|
||||
@@ -66,6 +65,14 @@ class ImagePaths {
|
||||
String get icDeleteEmail => _getImagePath('ic_delete_email.svg');
|
||||
String get icNewMessage => _getImagePath('ic_new_message.svg');
|
||||
String get icUnreadStatus => _getImagePath('ic_unread_status.svg');
|
||||
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 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 _getImagePath(String imageName) {
|
||||
return AssetsPaths.images + imageName;
|
||||
|
||||
@@ -15,7 +15,7 @@ class ResponsiveUtils {
|
||||
final double desktopVerticalMargin = 120.0;
|
||||
final double desktopHorizontalMargin = 200.0;
|
||||
|
||||
bool isMobileDevice(BuildContext context) => MediaQuery.of(context).size.shortestSide < 600;
|
||||
bool isMobileDevice(BuildContext context) => MediaQuery.of(context).size.shortestSide < minTabletWidth;
|
||||
|
||||
double getSizeWidthScreen(BuildContext context) => MediaQuery.of(context).size.width;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import 'package:core/core.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||
|
||||
typedef OnConfirmActionClick = void Function();
|
||||
typedef OnCancelActionClick = void Function();
|
||||
@@ -15,6 +16,9 @@ class ConfirmationDialogActionSheetBuilder {
|
||||
String? _cancelText;
|
||||
OnConfirmActionClick?_onConfirmActionClick;
|
||||
OnCancelActionClick? _onCancelActionClick;
|
||||
TextStyle? _styleConfirmButton;
|
||||
TextStyle? _styleCancelButton;
|
||||
TextStyle? _styleMessage;
|
||||
|
||||
ConfirmationDialogActionSheetBuilder(this._context);
|
||||
|
||||
@@ -32,11 +36,23 @@ class ConfirmationDialogActionSheetBuilder {
|
||||
_messageText = message;
|
||||
}
|
||||
|
||||
void styleMessage(TextStyle? style) {
|
||||
_styleMessage = style;
|
||||
}
|
||||
|
||||
void styleConfirmButton(TextStyle? style) {
|
||||
_styleConfirmButton = style;
|
||||
}
|
||||
|
||||
void styleCancelButton(TextStyle? style) {
|
||||
_styleCancelButton = style;
|
||||
}
|
||||
|
||||
void show() {
|
||||
showCupertinoModalPopup(
|
||||
context: _context,
|
||||
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
|
||||
builder: (context) => CupertinoActionSheet(
|
||||
builder: (context) => PointerInterceptor(child: CupertinoActionSheet(
|
||||
actions: [
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 10),
|
||||
@@ -45,27 +61,27 @@ class ConfirmationDialogActionSheetBuilder {
|
||||
child: Text(
|
||||
_messageText ?? '',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 14, color: AppColor.colorMessageConfirmDialog)),
|
||||
style: _styleMessage ?? TextStyle(fontSize: 14, color: AppColor.colorMessageConfirmDialog)),
|
||||
onPressed: () => {},
|
||||
)
|
||||
),
|
||||
Container(
|
||||
color: Colors.white,
|
||||
child: CupertinoActionSheetAction(
|
||||
child: Text(
|
||||
_confirmText ?? '',
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20, color: AppColor.colorActionDeleteConfirmDialog)),
|
||||
onPressed: () => _onConfirmActionClick?.call(),
|
||||
)
|
||||
color: Colors.white,
|
||||
child: CupertinoActionSheetAction(
|
||||
child: Text(
|
||||
_confirmText ?? '',
|
||||
style: _styleConfirmButton ?? TextStyle(fontWeight: FontWeight.w500, fontSize: 20, color: AppColor.colorActionDeleteConfirmDialog)),
|
||||
onPressed: () => _onConfirmActionClick?.call(),
|
||||
)
|
||||
),
|
||||
],
|
||||
cancelButton: CupertinoActionSheetAction(
|
||||
child: Text(
|
||||
_cancelText ?? '',
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20, color: AppColor.colorActionCancelDialog)),
|
||||
style: _styleCancelButton ?? TextStyle(fontWeight: FontWeight.w500, fontSize: 20, color: AppColor.colorActionCancelDialog)),
|
||||
onPressed: () => _onCancelActionClick?.call(),
|
||||
),
|
||||
)
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
typedef IconWebCallback = void Function();
|
||||
typedef IconWebHasPositionCallback = void Function(RelativeRect);
|
||||
|
||||
Widget buildIconWeb({
|
||||
required Widget icon,
|
||||
String? tooltip,
|
||||
IconWebCallback? onTap,
|
||||
EdgeInsets? iconPadding,
|
||||
double? iconSize,
|
||||
}) {
|
||||
return Material(
|
||||
type: MaterialType.circle,
|
||||
color: Colors.transparent,
|
||||
child: IconButton(icon: icon, iconSize: iconSize, padding: iconPadding ?? EdgeInsets.all(8.0), tooltip: tooltip, onPressed: onTap)
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildIconWebHasPosition(BuildContext context, {
|
||||
required Widget icon,
|
||||
String? tooltip,
|
||||
IconWebHasPositionCallback? onTap
|
||||
}) {
|
||||
return GestureDetector(
|
||||
onTapDown: (detail) {
|
||||
final screenSize = MediaQuery.of(context).size;
|
||||
final offset = detail.globalPosition;
|
||||
final position = RelativeRect.fromLTRB(
|
||||
offset.dx,
|
||||
offset.dy,
|
||||
screenSize.width - offset.dx,
|
||||
screenSize.height - offset.dy,
|
||||
);
|
||||
onTap?.call(position);
|
||||
},
|
||||
child: Tooltip(
|
||||
message: tooltip,
|
||||
child: icon,
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -16,6 +16,13 @@ class ConfirmDialogBuilder {
|
||||
String _confirmText = '';
|
||||
String _cancelText = '';
|
||||
Widget? _iconWidget;
|
||||
Color? _colorCancelButton;
|
||||
Color? _colorConfirmButton;
|
||||
TextStyle? _styleTextCancelButton;
|
||||
TextStyle? _styleTextConfirmButton;
|
||||
TextStyle? _styleTitle;
|
||||
TextStyle? _styleContent;
|
||||
double? _radiusButton;
|
||||
|
||||
OnConfirmButtonAction? _onConfirmButtonAction;
|
||||
OnCancelButtonAction? _onCancelButtonAction;
|
||||
@@ -39,6 +46,34 @@ class ConfirmDialogBuilder {
|
||||
_iconWidget = icon;
|
||||
}
|
||||
|
||||
void colorCancelButton(Color? color) {
|
||||
_colorCancelButton = color;
|
||||
}
|
||||
|
||||
void colorConfirmButton(Color? color) {
|
||||
_colorConfirmButton = color;
|
||||
}
|
||||
|
||||
void styleTextCancelButton(TextStyle? style) {
|
||||
_styleTextCancelButton = style;
|
||||
}
|
||||
|
||||
void styleTextConfirmButton(TextStyle? style) {
|
||||
_styleTextConfirmButton = style;
|
||||
}
|
||||
|
||||
void styleTitle(TextStyle? style) {
|
||||
_styleTitle = style;
|
||||
}
|
||||
|
||||
void styleContent(TextStyle? style) {
|
||||
_styleContent = style;
|
||||
}
|
||||
|
||||
void radiusButton(double? radius) {
|
||||
_radiusButton = radius;
|
||||
}
|
||||
|
||||
void onConfirmButtonAction(String confirmText, OnConfirmButtonAction? onConfirmButtonAction) {
|
||||
_confirmText = confirmText;
|
||||
_onConfirmButtonAction = onConfirmButtonAction;
|
||||
@@ -66,52 +101,57 @@ class ConfirmDialogBuilder {
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.all(Radius.circular(16))),
|
||||
child: Wrap(children: [
|
||||
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))),
|
||||
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))),
|
||||
if (_iconWidget != null)
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 24),
|
||||
alignment: Alignment.center,
|
||||
child: _iconWidget,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: Center(
|
||||
child: Text(
|
||||
_title,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 20.0, color: AppColor.colorActionDeleteConfirmDialog, fontWeight: FontWeight.w500)
|
||||
if (_title.isNotEmpty)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: Center(
|
||||
child: Text(
|
||||
_title,
|
||||
textAlign: TextAlign.center,
|
||||
style: _styleTitle ?? TextStyle(fontSize: 20.0, color: AppColor.colorActionDeleteConfirmDialog, fontWeight: FontWeight.w500)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
||||
child: Center(
|
||||
child: Text(_content,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 17.0, color: AppColor.colorMessageDialog)
|
||||
),
|
||||
if (_content.isNotEmpty)
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
||||
child: Center(
|
||||
child: Text(_content,
|
||||
textAlign: TextAlign.center,
|
||||
style: _styleContent ?? TextStyle(fontSize: 17.0, color: AppColor.colorMessageDialog)
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(bottom: 16, left: 16, right: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildButton(name: _cancelText, action: _onCancelButtonAction)
|
||||
),
|
||||
Expanded(child: _buildButton(
|
||||
name: _cancelText,
|
||||
bgColor: _colorCancelButton,
|
||||
radius: _radiusButton,
|
||||
textStyle: _styleTextCancelButton,
|
||||
action: _onCancelButtonAction)),
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: _buildButton(
|
||||
name: _confirmText,
|
||||
bgColor: AppColor.colorConfirmActionDialog,
|
||||
nameColor: AppColor.colorActionDeleteConfirmDialog,
|
||||
action: _onConfirmButtonAction)
|
||||
)
|
||||
Expanded(child: _buildButton(
|
||||
name: _confirmText,
|
||||
bgColor: _colorConfirmButton,
|
||||
radius: _radiusButton,
|
||||
textStyle: _styleTextConfirmButton,
|
||||
action: _onConfirmButtonAction))
|
||||
]
|
||||
))
|
||||
])
|
||||
@@ -120,7 +160,7 @@ class ConfirmDialogBuilder {
|
||||
}
|
||||
|
||||
Widget _buildButton({
|
||||
String? name, Color? nameColor, Color? bgColor, Function? action
|
||||
String? name, TextStyle? textStyle, Color? bgColor, double? radius, Function? action
|
||||
}) {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
@@ -133,7 +173,7 @@ class ConfirmDialogBuilder {
|
||||
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||
(Set<MaterialState> states) => bgColor ?? AppColor.colorTextButton),
|
||||
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderRadius: BorderRadius.circular(radius ?? 8),
|
||||
side: BorderSide(width: 0, color: bgColor ?? AppColor.colorTextButton),
|
||||
)),
|
||||
padding: MaterialStateProperty.resolveWith<EdgeInsets>(
|
||||
@@ -141,7 +181,7 @@ class ConfirmDialogBuilder {
|
||||
elevation: MaterialStateProperty.resolveWith<double>((Set<MaterialState> states) => 0)),
|
||||
child: Text(name ?? '',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: nameColor ?? Colors.white)),
|
||||
style: textStyle ?? TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: Colors.white)),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'dart:math';
|
||||
import 'package:core/presentation/extensions/color_extension.dart';
|
||||
import 'package:core/presentation/views/html_viewer/html_viewer_controller_for_web.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||
import 'dart:developer' as developer;
|
||||
import 'package:universal_html/html.dart' as html;
|
||||
import 'package:core/presentation/utils/shims/dart_ui.dart' as ui;
|
||||
@@ -221,10 +222,10 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
|
||||
future: webInit,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return HtmlElementView(
|
||||
return PointerInterceptor(child: HtmlElementView(
|
||||
key: ValueKey(htmlData),
|
||||
viewType: createdViewId,
|
||||
);
|
||||
));
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
|
||||
@@ -74,6 +74,8 @@ dependencies:
|
||||
|
||||
http: 0.13.4
|
||||
|
||||
pointer_interceptor: 0.9.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
@@ -127,6 +127,7 @@ class ComposerBindings extends BaseBindings {
|
||||
Get.find<GetAutoCompleteWithDeviceContactInteractor>(),
|
||||
Get.find<AppToast>(),
|
||||
Get.find<ImagePaths>(),
|
||||
Get.find<ResponsiveUtils>(),
|
||||
Get.find<Uuid>(),
|
||||
Get.find<DeviceInfoPlugin>(),
|
||||
Get.find<LocalFilePickerInteractor>(),
|
||||
|
||||
@@ -19,8 +19,10 @@ import 'package:jmap_dart_client/jmap/mail/email/email_body_part.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email_body_value.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/individual_header_identifier.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||
import 'package:model/model.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||
import 'package:tmail_ui_user/features/base/base_controller.dart';
|
||||
import 'package:tmail_ui_user/features/composer/domain/model/auto_complete_pattern.dart';
|
||||
import 'package:tmail_ui_user/features/composer/domain/model/contact_suggestion_source.dart';
|
||||
@@ -35,15 +37,16 @@ import 'package:tmail_ui_user/features/composer/domain/usecases/send_email_inter
|
||||
import 'package:tmail_ui_user/features/composer/domain/usecases/update_email_drafts_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/composer/domain/usecases/upload_mutiple_attachment_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_action.dart';
|
||||
import 'package:tmail_ui_user/features/upload/domain/state/local_file_picker_state.dart';
|
||||
import 'package:tmail_ui_user/features/upload/domain/usecases/local_file_picker_interactor.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class ComposerController extends BaseController {
|
||||
@@ -51,11 +54,14 @@ class ComposerController extends BaseController {
|
||||
final mailboxDashBoardController = Get.find<MailboxDashBoardController>();
|
||||
|
||||
final expandMode = ExpandMode.COLLAPSE.obs;
|
||||
final expandModeAttachments = ExpandMode.EXPAND.obs;
|
||||
final composerArguments = Rxn<ComposerArguments>();
|
||||
final isEnableEmailSendButton = false.obs;
|
||||
final isInitialRecipient = false.obs;
|
||||
final attachments = <Attachment>[].obs;
|
||||
final emailContents = <EmailContent>[].obs;
|
||||
final subjectEmail = Rxn<String>();
|
||||
final screenDisplayMode = ScreenDisplayMode.normal.obs;
|
||||
|
||||
final SendEmailInteractor _sendEmailInteractor;
|
||||
final SaveEmailAddressesInteractor _saveEmailAddressInteractor;
|
||||
@@ -63,6 +69,7 @@ class ComposerController extends BaseController {
|
||||
final GetAutoCompleteWithDeviceContactInteractor _getAutoCompleteWithDeviceContactInteractor;
|
||||
final AppToast _appToast;
|
||||
final ImagePaths _imagePaths;
|
||||
final ResponsiveUtils responsiveUtils;
|
||||
final Uuid _uuid;
|
||||
final LocalFilePickerInteractor _localFilePickerInteractor;
|
||||
final UploadMultipleAttachmentInteractor _uploadMultipleAttachmentInteractor;
|
||||
@@ -74,7 +81,6 @@ class ComposerController extends BaseController {
|
||||
List<EmailAddress> listToEmailAddress = <EmailAddress>[];
|
||||
List<EmailAddress> listCcEmailAddress = <EmailAddress>[];
|
||||
List<EmailAddress> listBccEmailAddress = <EmailAddress>[];
|
||||
String? _subjectEmail;
|
||||
ContactSuggestionSource _contactSuggestionSource = ContactSuggestionSource.localContact;
|
||||
HtmlEditorApi? htmlEditorApi;
|
||||
final HtmlEditorBrowser.HtmlEditorController htmlControllerBrowser = HtmlEditorBrowser.HtmlEditorController();
|
||||
@@ -85,8 +91,13 @@ class ComposerController extends BaseController {
|
||||
final bccEmailAddressController = TextEditingController();
|
||||
|
||||
List<Attachment> initialAttachments = <Attachment>[];
|
||||
String? _textEditor;
|
||||
|
||||
void setSubjectEmail(String subject) => _subjectEmail = subject;
|
||||
void setTextEditor(String? text) => _textEditor = text;
|
||||
|
||||
String? get textEditor => _textEditor;
|
||||
|
||||
void setSubjectEmail(String subject) => subjectEmail.value = subject;
|
||||
|
||||
Future<String> _getEmailBodyText({bool onlyText = false}) async {
|
||||
if (kIsWeb) {
|
||||
@@ -107,6 +118,7 @@ class ComposerController extends BaseController {
|
||||
this._getAutoCompleteWithDeviceContactInteractor,
|
||||
this._appToast,
|
||||
this._imagePaths,
|
||||
this.responsiveUtils,
|
||||
this._uuid,
|
||||
this._deviceInfoPlugin,
|
||||
this._localFilePickerInteractor,
|
||||
@@ -178,7 +190,7 @@ class ComposerController extends BaseController {
|
||||
}
|
||||
|
||||
void _initEmail() {
|
||||
final arguments = Get.arguments;
|
||||
final arguments = kIsWeb ? mailboxDashBoardController.routerArguments : Get.arguments;
|
||||
if (arguments is ComposerArguments) {
|
||||
composerArguments.value = arguments;
|
||||
if (arguments.emailActionType == EmailActionType.edit) {
|
||||
@@ -211,6 +223,14 @@ class ComposerController extends BaseController {
|
||||
return '';
|
||||
}
|
||||
|
||||
String? get initTextEditorComposer {
|
||||
if (composerArguments.value?.emailActionType == EmailActionType.compose) {
|
||||
return textEditor;
|
||||
} else {
|
||||
return textEditor ?? getContentEmail();
|
||||
}
|
||||
}
|
||||
|
||||
Tuple2<String, String>? _getHeaderEmailQuoted(String locale, ComposerArguments arguments) {
|
||||
if (arguments.presentationEmail != null) {
|
||||
final sentDate = arguments.presentationEmail?.sentAt;
|
||||
@@ -226,8 +246,9 @@ class ComposerController extends BaseController {
|
||||
}
|
||||
|
||||
void _initToEmailAddress(ComposerArguments arguments) {
|
||||
if (arguments.presentationEmail != null) {
|
||||
final userEmailAddress = EmailAddress(null, arguments.userProfile.email);
|
||||
final userProfile = mailboxDashBoardController.userProfile.value;
|
||||
if (arguments.presentationEmail != null && userProfile != null) {
|
||||
final userEmailAddress = EmailAddress(null, userProfile.email);
|
||||
|
||||
final recipients = arguments.presentationEmail!.generateRecipientsEmailAddressForComposer(arguments.emailActionType, arguments.mailboxRole);
|
||||
|
||||
@@ -302,12 +323,12 @@ class ComposerController extends BaseController {
|
||||
return emailQuotedHtml;
|
||||
}
|
||||
|
||||
Future<Email> _generateEmail(ComposerArguments arguments, {bool asDrafts = false}) async {
|
||||
Future<Email> _generateEmail(Map<Role, MailboxId> mapDefaultMailboxId, UserProfile userProfile, {bool asDrafts = false}) async {
|
||||
final generateEmailId = EmailId(Id(_uuid.v1()));
|
||||
final outboxMailboxId = arguments.mapMailboxId[PresentationMailbox.roleOutbox];
|
||||
final draftMailboxId = arguments.mapMailboxId[PresentationMailbox.roleDrafts];
|
||||
final outboxMailboxId = mapDefaultMailboxId[PresentationMailbox.roleOutbox];
|
||||
final draftMailboxId = mapDefaultMailboxId[PresentationMailbox.roleDrafts];
|
||||
final listFromEmailAddress = {
|
||||
EmailAddress(null, arguments.userProfile.email)
|
||||
EmailAddress(null, userProfile.email)
|
||||
};
|
||||
final generatePartId = PartId(_uuid.v1());
|
||||
final generateBlobId = Id(_uuid.v1());
|
||||
@@ -323,7 +344,7 @@ class ComposerController extends BaseController {
|
||||
cc: listCcEmailAddress.toSet(),
|
||||
bcc: listBccEmailAddress.toSet(),
|
||||
keywords: asDrafts ? {KeyWordIdentifier.emailDraft : true} : null,
|
||||
subject: _subjectEmail,
|
||||
subject: subjectEmail.value,
|
||||
htmlBody: {
|
||||
EmailBodyPart(
|
||||
partId: generatePartId,
|
||||
@@ -363,30 +384,44 @@ class ComposerController extends BaseController {
|
||||
clearFocusEditor(context);
|
||||
|
||||
if (isEnableEmailSendButton.value) {
|
||||
final arguments = composerArguments.value;
|
||||
if (arguments != null) {
|
||||
_saveEmailAddress();
|
||||
|
||||
final email = await _generateEmail(arguments);
|
||||
final accountId = arguments.session.accounts.keys.first;
|
||||
final sentMailboxId = arguments.mapMailboxId[PresentationMailbox.roleSent];
|
||||
final submissionCreateId = Id(_uuid.v1());
|
||||
|
||||
mailboxDashBoardController.consumeState(_sendEmailInteractor.execute(
|
||||
accountId,
|
||||
EmailRequest(
|
||||
email,
|
||||
submissionCreateId,
|
||||
mailboxIdSaved: sentMailboxId,
|
||||
emailIdDestroyed: arguments.emailActionType == EmailActionType.edit ? arguments.presentationEmail?.id : null)));
|
||||
if (subjectEmail.value?.isNotEmpty == true) {
|
||||
_handleSendMessages(context);
|
||||
} else {
|
||||
_showConfirmDialogSendEmail(context);
|
||||
}
|
||||
|
||||
popBack();
|
||||
} else {
|
||||
_appToast.showErrorToast(AppLocalizations.of(context).your_email_should_have_at_least_one_recipient);
|
||||
}
|
||||
}
|
||||
|
||||
void _handleSendMessages(BuildContext context) async {
|
||||
final arguments = composerArguments.value;
|
||||
final session = mailboxDashBoardController.sessionCurrent;
|
||||
final mapDefaultMailboxId = mailboxDashBoardController.mapDefaultMailboxId;
|
||||
final userProfile = mailboxDashBoardController.userProfile.value;
|
||||
if (arguments != null && session != null && mapDefaultMailboxId.isNotEmpty && userProfile != null) {
|
||||
_saveEmailAddress();
|
||||
|
||||
final email = await _generateEmail(mapDefaultMailboxId, userProfile);
|
||||
final accountId = session.accounts.keys.first;
|
||||
final sentMailboxId = mapDefaultMailboxId[PresentationMailbox.roleSent];
|
||||
final submissionCreateId = Id(_uuid.v1());
|
||||
|
||||
mailboxDashBoardController.consumeState(_sendEmailInteractor.execute(
|
||||
accountId,
|
||||
EmailRequest(email, submissionCreateId, mailboxIdSaved: sentMailboxId,
|
||||
emailIdDestroyed: arguments.emailActionType == EmailActionType.edit
|
||||
? arguments.presentationEmail?.id
|
||||
: null)));
|
||||
}
|
||||
|
||||
if (kIsWeb) {
|
||||
closeComposerWeb();
|
||||
} else {
|
||||
popBack();
|
||||
}
|
||||
}
|
||||
|
||||
void _saveEmailAddress() async {
|
||||
final listEmailAddressCanSave = Set<EmailAddress>();
|
||||
listEmailAddressCanSave.addAll(listToEmailAddress + listCcEmailAddress + listBccEmailAddress);
|
||||
@@ -460,10 +495,10 @@ class ComposerController extends BaseController {
|
||||
}
|
||||
|
||||
void _uploadAttachmentsAction(List<FileInfo> pickedFiles) async {
|
||||
if (composerArguments.value != null) {
|
||||
final accountId = composerArguments.value!.session.accounts.keys.first;
|
||||
final uploadUrl = composerArguments.value!.session.getUploadUrl(accountId);
|
||||
|
||||
final session = mailboxDashBoardController.sessionCurrent;
|
||||
if (session != null) {
|
||||
final accountId = session.accounts.keys.first;
|
||||
final uploadUrl = session.getUploadUrl(accountId);
|
||||
consumeState(_uploadMultipleAttachmentInteractor.execute(pickedFiles, accountId, uploadUrl));
|
||||
}
|
||||
}
|
||||
@@ -508,9 +543,9 @@ class ComposerController extends BaseController {
|
||||
final oldEmailBody = kIsWeb ? getContentEmail() : '\n${getContentEmail()}\n';
|
||||
final isEmailBodyChanged = !oldEmailBody.isSame(newEmailBody);
|
||||
|
||||
final newEmailSubject = _subjectEmail;
|
||||
final subjectEmail = arguments.presentationEmail?.getEmailTitle().trim() ?? '';
|
||||
final oldEmailSubject = arguments.emailActionType.getSubjectComposer(Get.context!, subjectEmail);
|
||||
final newEmailSubject = subjectEmail.value;
|
||||
final titleEmail = arguments.presentationEmail?.getEmailTitle().trim() ?? '';
|
||||
final oldEmailSubject = arguments.emailActionType.getSubjectComposer(Get.context!, titleEmail);
|
||||
final isEmailSubjectChanged = !oldEmailSubject.isSame(newEmailSubject);
|
||||
|
||||
final recipients = arguments.presentationEmail
|
||||
@@ -544,13 +579,18 @@ class ComposerController extends BaseController {
|
||||
clearFocusEditor(context);
|
||||
|
||||
final arguments = composerArguments.value;
|
||||
if (arguments != null && Get.context != null) {
|
||||
final mapDefaultMailboxId = mailboxDashBoardController.mapDefaultMailboxId;
|
||||
final userProfile = mailboxDashBoardController.userProfile.value;
|
||||
final session = mailboxDashBoardController.sessionCurrent;
|
||||
|
||||
if (arguments != null && Get.context != null && mapDefaultMailboxId.isNotEmpty
|
||||
&& userProfile != null && session != null) {
|
||||
final isChanged = await _isEmailChanged(arguments);
|
||||
if (isChanged) {
|
||||
_saveEmailAddress();
|
||||
|
||||
final newEmail = await _generateEmail(arguments, asDrafts: true);
|
||||
final accountId = arguments.session.accounts.keys.first;
|
||||
final newEmail = await _generateEmail(mapDefaultMailboxId, userProfile, asDrafts: true);
|
||||
final accountId = session.accounts.keys.first;
|
||||
final oldEmail = arguments.presentationEmail;
|
||||
|
||||
if (arguments.emailActionType == EmailActionType.edit && oldEmail != null) {
|
||||
@@ -563,10 +603,10 @@ class ComposerController extends BaseController {
|
||||
}
|
||||
|
||||
void _getEmailContentAction(ComposerArguments arguments) async {
|
||||
final baseDownloadUrl = arguments.session.getDownloadUrl();
|
||||
final accountId = arguments.session.accounts.keys.first;
|
||||
final baseDownloadUrl = mailboxDashBoardController.sessionCurrent?.getDownloadUrl();
|
||||
final accountId = mailboxDashBoardController.sessionCurrent?.accounts.keys.first;
|
||||
final emailId = arguments.presentationEmail?.id;
|
||||
if (emailId != null) {
|
||||
if (emailId != null && baseDownloadUrl != null && accountId != null) {
|
||||
consumeState(_getEmailContentInteractor.execute(accountId, emailId, baseDownloadUrl));
|
||||
}
|
||||
}
|
||||
@@ -594,7 +634,7 @@ class ComposerController extends BaseController {
|
||||
if (arguments.emailActionType == EmailActionType.edit) {
|
||||
return arguments.presentationEmail?.from?.first.emailAddress ?? '';
|
||||
} else {
|
||||
return arguments.userProfile.email;
|
||||
return mailboxDashBoardController.userProfile.value?.email ?? '';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
@@ -603,6 +643,69 @@ class ComposerController extends BaseController {
|
||||
void clearFocusEditor(BuildContext context) {
|
||||
if (!kIsWeb) {
|
||||
htmlEditorApi?.unfocus(context);
|
||||
htmlControllerBrowser.clearFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void closeComposerWeb() {
|
||||
mailboxDashBoardController.dispatchDashBoardAction(DashBoardAction.none);
|
||||
}
|
||||
|
||||
void minimizeComposer() {
|
||||
_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;
|
||||
}
|
||||
|
||||
void _updateTextForEditor() async {
|
||||
final textCurrent = await htmlControllerBrowser.getText();
|
||||
log('_updateTextForEditor() | textCurrent: $textCurrent');
|
||||
htmlControllerBrowser.insertHtml(textCurrent);
|
||||
}
|
||||
|
||||
void deleteComposer() {
|
||||
mailboxDashBoardController.dispatchDashBoardAction(DashBoardAction.none);
|
||||
}
|
||||
|
||||
void toggleDisplayAttachments() {
|
||||
final newExpandMode = expandModeAttachments.value == ExpandMode.COLLAPSE ? ExpandMode.EXPAND : ExpandMode.COLLAPSE;
|
||||
expandModeAttachments.value = newExpandMode;
|
||||
}
|
||||
|
||||
void _showConfirmDialogSendEmail(BuildContext context) {
|
||||
if (responsiveUtils.isMobileDevice(context)) {
|
||||
(ConfirmationDialogActionSheetBuilder(context)
|
||||
..messageText(AppLocalizations.of(context).message_confirm_send_email)
|
||||
..styleConfirmButton(TextStyle(fontSize: 20, fontWeight: FontWeight.normal, color: Colors.black))
|
||||
..onCancelAction(AppLocalizations.of(context).cancel, () => popBack())
|
||||
..onConfirmAction(AppLocalizations.of(context).send, () {
|
||||
popBack();
|
||||
_handleSendMessages(context);
|
||||
})).show();
|
||||
} else {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
|
||||
builder: (BuildContext context) => PointerInterceptor(child: (ConfirmDialogBuilder(_imagePaths)
|
||||
..key(Key('confirm_dialog_send_message'))
|
||||
..content(AppLocalizations.of(context).message_confirm_send_email)
|
||||
..colorConfirmButton(AppColor.colorTextButton)
|
||||
..colorCancelButton(AppColor.colorCancelButton)
|
||||
..styleContent(TextStyle(fontSize: 15, fontWeight: FontWeight.normal, color: AppColor.colorMessageDialog))
|
||||
..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, () {
|
||||
popBack();
|
||||
_handleSendMessages(context);
|
||||
})
|
||||
..onCancelButtonAction(AppLocalizations.of(context).cancel, () => popBack()))
|
||||
.build()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,529 @@
|
||||
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/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:model/model.dart';
|
||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||
import 'package:tmail_ui_user/features/composer/domain/state/upload_attachment_state.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_file_composer_builder.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/email_address_input_builder.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
|
||||
class ComposerView extends GetWidget<ComposerController> {
|
||||
|
||||
final responsiveUtils = Get.find<ResponsiveUtils>();
|
||||
final imagePaths = Get.find<ImagePaths>();
|
||||
final appToast = Get.find<AppToast>();
|
||||
final keyboardUtils = Get.find<KeyboardUtils>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveWidget(
|
||||
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))),
|
||||
]
|
||||
))
|
||||
)
|
||||
))
|
||||
),
|
||||
desktop: Obx(() {
|
||||
return Stack(children: [
|
||||
if (controller.screenDisplayMode == ScreenDisplayMode.normal)
|
||||
Positioned(right: 10, bottom: 10, child: Card(
|
||||
elevation: 20,
|
||||
color: Colors.transparent,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(20))),
|
||||
width: responsiveUtils.getSizeWidthScreen(context) * 0.5,
|
||||
height: responsiveUtils.getSizeHeightScreen(context) * 0.65,
|
||||
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)),
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
)),
|
||||
if (controller.screenDisplayMode == ScreenDisplayMode.minimize)
|
||||
Positioned(right: 10, bottom: 10, child: Card(
|
||||
elevation: 20,
|
||||
color: Colors.transparent,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: AppColor.colorTextButton, borderRadius: BorderRadius.all(Radius.circular(20))),
|
||||
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.closeComposerWeb())),
|
||||
]
|
||||
))
|
||||
)
|
||||
)),
|
||||
if (controller.screenDisplayMode == ScreenDisplayMode.fullScreen)
|
||||
Scaffold(
|
||||
backgroundColor: Colors.black38,
|
||||
body: Align(alignment: Alignment.center, child: Card(
|
||||
color: Colors.transparent,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(20))),
|
||||
width: responsiveUtils.getSizeWidthScreen(context) * 0.8,
|
||||
height: responsiveUtils.getSizeHeightScreen(context) * 0.85,
|
||||
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)),
|
||||
]
|
||||
))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
]);
|
||||
}),
|
||||
tablet: Scaffold(
|
||||
backgroundColor: Colors.black38,
|
||||
body: Align(alignment: Alignment.center, child: Card(
|
||||
color: Colors.transparent,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(20))),
|
||||
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)),
|
||||
]
|
||||
))
|
||||
)
|
||||
)
|
||||
))
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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.closeComposerWeb()),
|
||||
]
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAppBarForMobile(BuildContext context, bool isEnableSendButton) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(left: 10, right: 10, top: 10),
|
||||
color: Colors.white,
|
||||
child: Row(
|
||||
children: [
|
||||
buildIconWeb(
|
||||
icon: SvgPicture.asset(imagePaths.icCloseComposer, fit: BoxFit.fill),
|
||||
tooltip: AppLocalizations.of(context).close,
|
||||
onTap: () => controller.closeComposerWeb()),
|
||||
Spacer(),
|
||||
buildIconWeb(
|
||||
icon: SvgPicture.asset(imagePaths.icAttachmentsComposer, fit: BoxFit.fill),
|
||||
tooltip: AppLocalizations.of(context).pick_attachments,
|
||||
onTap: () => controller.openFilePickerByType(context, FileType.any)),
|
||||
buildIconWeb(
|
||||
icon: SvgPicture.asset(
|
||||
imagePaths.icSendComposer,
|
||||
color: isEnableSendButton ? AppColor.colorTextButton : AppColor.colorDisableMailboxCreateButton,
|
||||
fit: BoxFit.fill),
|
||||
tooltip: AppLocalizations.of(context).send,
|
||||
onTap: () => controller.sendEmailAction(context)),
|
||||
]
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBottomBar(BuildContext context, bool isEnableSendButton) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(left: 10, right: 20, top: 18, bottom: 18),
|
||||
color: AppColor.colorBottomBarComposer,
|
||||
child: Row(
|
||||
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)
|
||||
),
|
||||
)
|
||||
]
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTitleComposer(BuildContext context) {
|
||||
return Obx(() => Text(
|
||||
controller.subjectEmail.isNotEmpty == true
|
||||
? controller.subjectEmail.value ?? ''
|
||||
: AppLocalizations.of(context).new_message.capitalizeFirstEach,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
style: TextStyle(fontSize: 22, fontWeight: FontWeight.w500,
|
||||
color: controller.screenDisplayMode == ScreenDisplayMode.minimize ? Colors.white : Colors.black),
|
||||
));
|
||||
}
|
||||
|
||||
Widget _buildEmailAddress(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(bottom: 12),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
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()
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSubjectEmail(BuildContext context) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(right: 8, top: 16),
|
||||
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()
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEditorAndAttachments(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
physics: ClampingScrollPhysics(),
|
||||
child: Column(
|
||||
children: [
|
||||
Obx(() => controller.attachments.isNotEmpty
|
||||
? Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 4, horizontal: 10),
|
||||
child: _buildAttachmentsTitle(context, controller.attachments, controller.expandModeAttachments.value))
|
||||
: SizedBox.shrink()),
|
||||
Obx(() => controller.attachments.isEmpty
|
||||
? _buildAttachmentsLoadingView()
|
||||
: SizedBox.shrink()),
|
||||
Obx(() => controller.attachments.isNotEmpty
|
||||
? Padding(
|
||||
padding: EdgeInsets.only(bottom: 8, left: 10, right: 10),
|
||||
child: _buildAttachmentsList(context, controller.attachments, controller.expandModeAttachments.value))
|
||||
: SizedBox.shrink()),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
child: _buildComposerEditor(context)),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildComposerEditor(BuildContext context) {
|
||||
return Obx(() => HtmlEditorBrowser.HtmlEditor(
|
||||
key: Key('composer_editor_web'),
|
||||
controller: controller.htmlControllerBrowser,
|
||||
htmlEditorOptions: HtmlEditorBrowser.HtmlEditorOptions(
|
||||
hint: '',
|
||||
initialText: controller.initTextEditorComposer,
|
||||
autoAdjustHeight: true,
|
||||
darkMode: false,
|
||||
disableScrollbarEditor: true,
|
||||
),
|
||||
htmlToolbarOptions: HtmlEditorBrowser.HtmlToolbarOptions(
|
||||
toolbarPosition: HtmlEditorBrowser.ToolbarPosition.custom
|
||||
),
|
||||
otherOptions: HtmlEditorBrowser.OtherOptions(height: 550),
|
||||
callbacks: HtmlEditorBrowser.Callbacks(onBeforeCommand: (String? currentHtml) {
|
||||
controller.setTextEditor(currentHtml);
|
||||
}, onChangeContent: (String? changed) {
|
||||
controller.setTextEditor(changed);
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
Widget _buildAttachmentsLoadingView({EdgeInsets? padding, double? size}) {
|
||||
return Obx(() => controller.viewState.value.fold(
|
||||
(failure) => SizedBox.shrink(),
|
||||
(success) => success is UploadingAttachmentState
|
||||
? Center(child: Padding(
|
||||
padding: padding ?? EdgeInsets.all(10),
|
||||
child: SizedBox(
|
||||
width: size ?? 20,
|
||||
height: size ?? 20,
|
||||
child: CupertinoActivityIndicator(color: AppColor.colorTextButton))))
|
||||
: SizedBox.shrink()));
|
||||
}
|
||||
|
||||
Widget _buildAttachmentsTitle(BuildContext context, List<Attachment> attachments, ExpandMode expandModeAttachment) {
|
||||
return Row(
|
||||
children: [
|
||||
Text(
|
||||
'${AppLocalizations.of(context).attachments} (${filesize(attachments.totalSize(), 0)}):',
|
||||
style: TextStyle(fontSize: 12, color: AppColor.colorHintEmailAddressInput, fontWeight: FontWeight.normal)),
|
||||
_buildAttachmentsLoadingView(padding: EdgeInsets.only(left: 16), size: 16),
|
||||
Spacer(),
|
||||
Material(
|
||||
type: MaterialType.circle,
|
||||
color: Colors.transparent,
|
||||
child: TextButton(
|
||||
child: Text(
|
||||
expandModeAttachment == ExpandMode.EXPAND
|
||||
? AppLocalizations.of(context).hide
|
||||
: '${AppLocalizations.of(context).show_all} (${attachments.length})',
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 12, color: AppColor.colorTextButton)),
|
||||
onPressed: () => controller.toggleDisplayAttachments()
|
||||
)
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAttachmentsList(BuildContext context, List<Attachment> attachments, ExpandMode expandMode) {
|
||||
if (expandMode == ExpandMode.EXPAND) {
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
return GridView.builder(
|
||||
key: Key('list_attachment_full'),
|
||||
primary: false,
|
||||
shrinkWrap: true,
|
||||
itemCount: attachments.length,
|
||||
gridDelegate: SliverGridDelegateFixedHeight(
|
||||
height: 60,
|
||||
crossAxisCount: _getMaxItemRowListAttachment(context, constraints),
|
||||
crossAxisSpacing: 8.0,
|
||||
mainAxisSpacing: 8.0),
|
||||
itemBuilder: (context, index) =>
|
||||
(AttachmentFileComposerBuilder(context, imagePaths, attachments[index])
|
||||
..addOnDeleteAttachmentAction((attachment) => controller.removeAttachmentAction(attachment)))
|
||||
.build()
|
||||
);
|
||||
});
|
||||
} else {
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: SizedBox(
|
||||
height: 60,
|
||||
child: ListView.builder(
|
||||
key: Key('list_attachment_minimize'),
|
||||
shrinkWrap: true,
|
||||
physics: ClampingScrollPhysics(),
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: attachments.length,
|
||||
itemBuilder: (context, index) =>
|
||||
(AttachmentFileComposerBuilder(context, imagePaths, attachments[index],
|
||||
itemMargin: EdgeInsets.only(right: 8),
|
||||
maxWidth: _getMaxWidthItemListAttachment(context, constraints),
|
||||
maxHeight: 60)
|
||||
..addOnDeleteAttachmentAction((attachment) => controller.removeAttachmentAction(attachment)))
|
||||
.build()
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
int _getMaxItemRowListAttachment(BuildContext context, BoxConstraints constraints) {
|
||||
if (constraints.maxWidth < ResponsiveUtils.minTabletWidth) {
|
||||
return 2;
|
||||
} else if (constraints.maxWidth < ResponsiveUtils.minTabletLargeWidth) {
|
||||
return 3;
|
||||
} else {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
double _getMaxWidthItemListAttachment(BuildContext context, BoxConstraints constraints) {
|
||||
final currentWidth = constraints.maxWidth - 40;
|
||||
return currentWidth / _getMaxItemRowListAttachment(context, constraints);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
enum ScreenDisplayMode {
|
||||
fullScreen,
|
||||
minimize,
|
||||
normal
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
|
||||
import 'package:core/core.dart';
|
||||
import 'package:filesize/filesize.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:model/model.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
|
||||
typedef OnDeleteAttachmentAction = void Function(Attachment attachment);
|
||||
|
||||
class AttachmentFileComposerBuilder {
|
||||
|
||||
final BuildContext context;
|
||||
final ImagePaths imagePaths;
|
||||
final Attachment attachment;
|
||||
final double? maxWidth;
|
||||
final double? maxHeight;
|
||||
final EdgeInsets? itemMargin;
|
||||
|
||||
OnDeleteAttachmentAction? _onDeleteAttachmentAction;
|
||||
|
||||
Widget? buttonAction;
|
||||
|
||||
AttachmentFileComposerBuilder(
|
||||
this.context,
|
||||
this.imagePaths,
|
||||
this.attachment,
|
||||
{
|
||||
this.maxWidth,
|
||||
this.maxHeight,
|
||||
this.itemMargin,
|
||||
}
|
||||
);
|
||||
|
||||
void addOnDeleteAttachmentAction(OnDeleteAttachmentAction onDeleteAttachmentAction) {
|
||||
_onDeleteAttachmentAction = onDeleteAttachmentAction;
|
||||
}
|
||||
|
||||
Widget build() {
|
||||
return Theme(
|
||||
data: ThemeData(
|
||||
splashColor: Colors.transparent ,
|
||||
highlightColor: Colors.transparent),
|
||||
child: Container(
|
||||
margin: itemMargin ?? EdgeInsets.zero,
|
||||
padding: EdgeInsets.zero,
|
||||
alignment: Alignment.center,
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: AppColor.colorInputBorderCreateMailbox),
|
||||
color: Colors.white),
|
||||
child: Stack(children: [
|
||||
ListTile(
|
||||
contentPadding: EdgeInsets.only(right: 16),
|
||||
onTap: () {},
|
||||
leading: Transform(
|
||||
transform: Matrix4.translationValues(8.0, -3.0, 0.0),
|
||||
child: SvgPicture.asset(imagePaths.icFileAttachment, fit: BoxFit.fill)),
|
||||
title: Transform(
|
||||
transform: Matrix4.translationValues(-4.0, -6.0, 0.0),
|
||||
child: Text(
|
||||
attachment.name ?? '',
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: 12, color: AppColor.colorNameEmail, fontWeight: FontWeight.w500),
|
||||
)),
|
||||
subtitle: attachment.size != null && attachment.size?.value != 0
|
||||
? Transform(
|
||||
transform: Matrix4.translationValues(-4.0, -5.0, 0.0),
|
||||
child: Text(
|
||||
filesize(attachment.size?.value, 0),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: 10, color: AppColor.colorContentEmail, fontWeight: FontWeight.normal)))
|
||||
: null,
|
||||
),
|
||||
Positioned(right: -5, top: -5, child: buildIconWeb(
|
||||
icon: SvgPicture.asset(imagePaths.icDeleteAttachment, fit: BoxFit.fill),
|
||||
tooltip: AppLocalizations.of(context).delete,
|
||||
onTap: () => _onDeleteAttachmentAction?.call(attachment)))
|
||||
]),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ class EmailAddressInputBuilder {
|
||||
final List<EmailAddress> _listEmailAddress;
|
||||
final TextEditingController? controller;
|
||||
final bool? isInitial;
|
||||
final bool hasAvatar;
|
||||
|
||||
OnOpenExpandAddressActionClick? _onOpenExpandAddressActionClick;
|
||||
OnUpdateListEmailAddressAction? _onUpdateListEmailAddressAction;
|
||||
@@ -51,6 +52,7 @@ class EmailAddressInputBuilder {
|
||||
this._listEmailAddress,
|
||||
{
|
||||
this.isInitial,
|
||||
this.hasAvatar = false,
|
||||
this.controller,
|
||||
this.expandMode = ExpandMode.COLLAPSE,
|
||||
}
|
||||
@@ -69,7 +71,7 @@ class EmailAddressInputBuilder {
|
||||
Expanded(child: _buildTagEditor()),
|
||||
if (_prefixEmailAddress == PrefixEmailAddress.to)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 4),
|
||||
padding: EdgeInsets.only(top: 8),
|
||||
child: _buildButtonExpandAddress())
|
||||
]
|
||||
);
|
||||
@@ -79,12 +81,10 @@ class EmailAddressInputBuilder {
|
||||
return Material(
|
||||
type: MaterialType.circle,
|
||||
color: Colors.transparent,
|
||||
child: IconButton(
|
||||
icon: SvgPicture.asset(
|
||||
expandMode == ExpandMode.EXPAND ? _imagePaths.icExpandAddress : _imagePaths.icMoreReceiver,
|
||||
width: 20,
|
||||
height: 20,
|
||||
fit: BoxFit.fill),
|
||||
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()
|
||||
)
|
||||
);
|
||||
@@ -98,6 +98,7 @@ class EmailAddressInputBuilder {
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
textInputAction: TextInputAction.done,
|
||||
hasAddButton: false,
|
||||
tagSpacing: 8,
|
||||
resetTextOnSubmitted: true,
|
||||
textStyle: TextStyle(color: AppColor.colorEmailAddress, fontSize: 14, fontWeight: FontWeight.w500),
|
||||
onSubmitted: (value) {
|
||||
@@ -117,16 +118,22 @@ class EmailAddressInputBuilder {
|
||||
tagBuilder: (context, index) => Padding(
|
||||
padding: EdgeInsets.only(top: kIsWeb ? 8 : 0),
|
||||
child: Chip(
|
||||
labelPadding: EdgeInsets.only(left: 8, right: 8, bottom: 2),
|
||||
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,
|
||||
avatar: CircleAvatar(
|
||||
backgroundColor: AppColor.colorTextButton,
|
||||
child: Text(
|
||||
_listEmail[index].isNotEmpty ? _listEmail[index][0].toUpperCase() : '',
|
||||
style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w500))),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
side: BorderSide(width: 0, color: AppColor.emailAddressChipColor),
|
||||
),
|
||||
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);
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:core/core.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -33,6 +34,7 @@ import 'package:tmail_ui_user/features/email/presentation/widgets/email_address_
|
||||
import 'package:tmail_ui_user/features/email/presentation/widgets/email_address_dialog_builder.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_action.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';
|
||||
@@ -480,16 +482,16 @@ class EmailController extends BaseController {
|
||||
void composeEmailFromEmailAddress(EmailAddress emailAddress) {
|
||||
popBack();
|
||||
|
||||
if (canComposeEmail()) {
|
||||
push(
|
||||
AppRoutes.COMPOSER,
|
||||
arguments: ComposerArguments(
|
||||
emailActionType: EmailActionType.composeFromEmailAddress,
|
||||
emailAddress: emailAddress,
|
||||
mailboxRole: mailboxDashBoardController.selectedMailbox.value?.role,
|
||||
session: mailboxDashBoardController.sessionCurrent!,
|
||||
userProfile: mailboxDashBoardController.userProfile.value!,
|
||||
mapMailboxId: mailboxDashBoardController.mapDefaultMailboxId));
|
||||
final arguments = ComposerArguments(
|
||||
emailActionType: EmailActionType.composeFromEmailAddress,
|
||||
emailAddress: emailAddress,
|
||||
mailboxRole: mailboxDashBoardController.selectedMailbox.value?.role);
|
||||
if (kIsWeb) {
|
||||
if (mailboxDashBoardController.dashBoardAction != DashBoardAction.compose) {
|
||||
mailboxDashBoardController.dispatchDashBoardAction(DashBoardAction.compose, arguments: arguments);
|
||||
}
|
||||
} else {
|
||||
push(AppRoutes.COMPOSER, arguments: arguments);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,11 +499,6 @@ class EmailController extends BaseController {
|
||||
popBack();
|
||||
}
|
||||
|
||||
bool canComposeEmail() => mailboxDashBoardController.sessionCurrent != null
|
||||
&& mailboxDashBoardController.userProfile.value != null
|
||||
&& mailboxDashBoardController.mapDefaultMailboxId.isNotEmpty
|
||||
&& mailboxDashBoardController.selectedEmail.value != null;
|
||||
|
||||
void backToThreadView() {
|
||||
attachmentsExpandMode.value = ExpandMode.COLLAPSE;
|
||||
emailAddressExpandMode.value = ExpandMode.COLLAPSE;
|
||||
@@ -513,30 +510,36 @@ class EmailController extends BaseController {
|
||||
if (emailActionType == EmailActionType.compose) {
|
||||
composeEmailAction();
|
||||
} else {
|
||||
if (canComposeEmail()) {
|
||||
push(
|
||||
AppRoutes.COMPOSER,
|
||||
arguments: ComposerArguments(
|
||||
emailActionType: emailActionType,
|
||||
presentationEmail: mailboxDashBoardController.selectedEmail.value!,
|
||||
emailContents: emailContents,
|
||||
attachments: attachments,
|
||||
mailboxRole: mailboxDashBoardController.selectedMailbox.value?.role,
|
||||
session: mailboxDashBoardController.sessionCurrent!,
|
||||
userProfile: mailboxDashBoardController.userProfile.value!,
|
||||
mapMailboxId: mailboxDashBoardController.mapDefaultMailboxId));
|
||||
final arguments = ComposerArguments(
|
||||
emailActionType: emailActionType,
|
||||
presentationEmail: mailboxDashBoardController.selectedEmail.value!,
|
||||
emailContents: emailContents,
|
||||
attachments: attachments,
|
||||
mailboxRole: mailboxDashBoardController.selectedMailbox.value?.role);
|
||||
|
||||
if (kIsWeb) {
|
||||
if (mailboxDashBoardController.dashBoardAction != DashBoardAction.compose) {
|
||||
mailboxDashBoardController.dispatchDashBoardAction(DashBoardAction.compose, arguments: arguments);
|
||||
}
|
||||
if (Get.currentRoute == AppRoutes.EMAIL) {
|
||||
popBack();
|
||||
}
|
||||
} else {
|
||||
push(AppRoutes.COMPOSER, arguments: arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void composeEmailAction() {
|
||||
if (canComposeEmail()) {
|
||||
push(
|
||||
AppRoutes.COMPOSER,
|
||||
arguments: ComposerArguments(
|
||||
session: mailboxDashBoardController.sessionCurrent!,
|
||||
userProfile: mailboxDashBoardController.userProfile.value!,
|
||||
mapMailboxId: mailboxDashBoardController.mapDefaultMailboxId));
|
||||
if (kIsWeb) {
|
||||
if (mailboxDashBoardController.dashBoardAction != DashBoardAction.compose) {
|
||||
mailboxDashBoardController.dispatchDashBoardAction(DashBoardAction.compose, arguments: ComposerArguments());
|
||||
}
|
||||
if (Get.currentRoute == AppRoutes.EMAIL) {
|
||||
popBack();
|
||||
}
|
||||
} else {
|
||||
push(AppRoutes.COMPOSER, arguments: ComposerArguments());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,7 +216,7 @@ class EmailView extends GetView {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
padding: EdgeInsets.only(top: kIsWeb ? 10: 20),
|
||||
child: Text(
|
||||
'${prefixEmailAddress.asName(context)}:',
|
||||
style: TextStyle(fontSize: 14, color: AppColor.colorEmailAddressPrefix))),
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||
import 'package:model/model.dart';
|
||||
import 'package:tmail_ui_user/main/routes/router_arguments.dart';
|
||||
|
||||
class ComposerArguments with EquatableMixin {
|
||||
class ComposerArguments extends RouterArguments {
|
||||
final EmailActionType emailActionType;
|
||||
final PresentationEmail? presentationEmail;
|
||||
final List<EmailContent>? emailContents;
|
||||
final EmailAddress? emailAddress;
|
||||
final List<Attachment>? attachments;
|
||||
final Session session;
|
||||
final UserProfile userProfile;
|
||||
final Map<Role, MailboxId> mapMailboxId;
|
||||
final Role? mailboxRole;
|
||||
|
||||
ComposerArguments({
|
||||
@@ -23,9 +19,6 @@ class ComposerArguments with EquatableMixin {
|
||||
this.attachments,
|
||||
this.mailboxRole,
|
||||
this.emailAddress,
|
||||
required this.session,
|
||||
required this.userProfile,
|
||||
required this.mapMailboxId,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -35,8 +28,5 @@ class ComposerArguments with EquatableMixin {
|
||||
attachments,
|
||||
mailboxRole,
|
||||
emailAddress,
|
||||
session,
|
||||
userProfile,
|
||||
mapMailboxId,
|
||||
];
|
||||
}
|
||||
@@ -2,7 +2,9 @@
|
||||
import 'package:core/core.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:model/model.dart';
|
||||
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
||||
|
||||
typedef OnBackActionClick = void Function();
|
||||
typedef OnEmailActionClick = void Function(PresentationEmail, EmailActionType);
|
||||
@@ -51,9 +53,9 @@ class AppBarMailWidgetBuilder {
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
if ((!_responsiveUtils.isDesktop(_context) && !_responsiveUtils.isTabletLarge(_context)))
|
||||
if (_conditionShowBackButton())
|
||||
_buildBackButton(),
|
||||
if ((!_responsiveUtils.isDesktop(_context) && !_responsiveUtils.isTabletLarge(_context)))
|
||||
if (_conditionShowBackButton())
|
||||
Expanded(child: _buildMailboxName()),
|
||||
if (_presentationEmail != null) _buildListOptionButton(),
|
||||
]
|
||||
@@ -62,6 +64,16 @@ class AppBarMailWidgetBuilder {
|
||||
);
|
||||
}
|
||||
|
||||
bool _conditionShowBackButton() {
|
||||
if (Get.currentRoute == AppRoutes.MAILBOX_DASHBOARD) {
|
||||
return !_responsiveUtils.isDesktop(_context) && !_responsiveUtils.isTabletLarge(_context);
|
||||
} else if (Get.currentRoute == AppRoutes.EMAIL) {
|
||||
return _responsiveUtils.isMobileDevice(_context) || _responsiveUtils.isTablet(_context);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildBackButton() {
|
||||
return IconButton(
|
||||
icon: SvgPicture.asset(_imagePaths.icBack, width: 20, height: 20, color: AppColor.colorTextButton, fit: BoxFit.fill),
|
||||
|
||||
@@ -78,7 +78,7 @@ class AttachmentFileTileBuilder {
|
||||
}},
|
||||
leading: Transform(
|
||||
transform: Matrix4.translationValues(14.0, 2.0, 0.0),
|
||||
child: SvgPicture.asset(_imagePaths.icAttachmentFile, width: 24, height: 24, fit: BoxFit.fill)),
|
||||
child: SvgPicture.asset(_imagePaths.icFileAttachment, width: 24, height: 24, fit: BoxFit.fill)),
|
||||
title: Transform(
|
||||
transform: Matrix4.translationValues(-8.0, -8.0, 0.0),
|
||||
child: Text(
|
||||
|
||||
@@ -453,6 +453,8 @@ class MailboxController extends BaseMailboxController {
|
||||
..title(AppLocalizations.of(context).delete_mailboxes)
|
||||
..content(AppLocalizations.of(context).message_confirmation_dialog_delete_mailbox(presentationMailbox.name?.name ?? ''))
|
||||
..addIcon(SvgPicture.asset(_imagePaths.icRemoveDialog, fit: BoxFit.fill))
|
||||
..colorConfirmButton(AppColor.colorConfirmActionDialog)
|
||||
..styleTextConfirmButton(TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorActionDeleteConfirmDialog))
|
||||
..onCloseButtonAction(() => popBack())
|
||||
..onConfirmButtonAction(AppLocalizations.of(context).delete, () => _deleteMailboxAction(presentationMailbox))
|
||||
..onCancelButtonAction(AppLocalizations.of(context).cancel, () => popBack()))
|
||||
|
||||
@@ -11,16 +11,20 @@ import 'package:tmail_ui_user/features/base/base_controller.dart';
|
||||
import 'package:tmail_ui_user/features/composer/domain/state/save_email_as_drafts_state.dart';
|
||||
import 'package:tmail_ui_user/features/composer/domain/state/send_email_state.dart';
|
||||
import 'package:tmail_ui_user/features/composer/domain/state/update_email_drafts_state.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/composer_bindings.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/email_controller.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_user_profile_state.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/remove_email_drafts_state.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_user_profile_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_email_drafts_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_action.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/state/search_email_state.dart';
|
||||
import 'package:tmail_ui_user/features/thread/presentation/model/search_state.dart';
|
||||
import 'package:tmail_ui_user/features/thread/presentation/model/search_status.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:tmail_ui_user/main/routes/router_arguments.dart';
|
||||
|
||||
class MailboxDashBoardController extends BaseController {
|
||||
|
||||
@@ -36,6 +40,7 @@ class MailboxDashBoardController extends BaseController {
|
||||
final userProfile = Rxn<UserProfile>();
|
||||
final searchState = SearchState.initial().obs;
|
||||
final suggestionSearch = <String>[].obs;
|
||||
final dashBoardAction = DashBoardAction.none.obs;
|
||||
|
||||
SearchQuery? searchQuery;
|
||||
Session? sessionCurrent;
|
||||
@@ -43,6 +48,7 @@ class MailboxDashBoardController extends BaseController {
|
||||
Map<MailboxId, PresentationMailbox> mapMailbox = Map();
|
||||
TextEditingController searchInputController = TextEditingController();
|
||||
FocusNode searchFocus = FocusNode();
|
||||
RouterArguments? routerArguments;
|
||||
|
||||
MailboxDashBoardController(
|
||||
this._getUserProfileInteractor,
|
||||
@@ -225,6 +231,20 @@ class MailboxDashBoardController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
void dispatchDashBoardAction(DashBoardAction action, {RouterArguments? arguments}) {
|
||||
switch(action) {
|
||||
case DashBoardAction.none:
|
||||
routerArguments = null;
|
||||
Get.delete<ComposerController>();
|
||||
break;
|
||||
case DashBoardAction.compose:
|
||||
routerArguments = arguments;
|
||||
ComposerBindings().dependencies();
|
||||
break;
|
||||
}
|
||||
dashBoardAction.value = action;
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
searchInputController.dispose();
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import 'package:core/core.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/composer_view.dart'
|
||||
if (dart.library.html) 'package:tmail_ui_user/features/composer/presentation/composer_view_web.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/email_view.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_view.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_action.dart';
|
||||
import 'package:tmail_ui_user/features/thread/presentation/thread_view.dart';
|
||||
|
||||
class MailboxDashBoardView extends GetWidget<MailboxDashBoardController> {
|
||||
@@ -30,35 +33,40 @@ class MailboxDashBoardView extends GetWidget<MailboxDashBoardController> {
|
||||
desktop: SizedBox.shrink()
|
||||
),
|
||||
drawerEnableOpenDragGesture: !responsiveUtils.isDesktop(context),
|
||||
body: ResponsiveWidget(
|
||||
responsiveUtils: responsiveUtils,
|
||||
desktop: Container(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(flex: 1, child: MailboxView()),
|
||||
Expanded(flex: 1, child: ThreadView()),
|
||||
Expanded(flex: 2, child: EmailView()),
|
||||
],
|
||||
),
|
||||
),
|
||||
tabletLarge: Container(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: responsiveUtils.getSizeWidthScreen(context) * 0.35,
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: ThreadView()
|
||||
body: Stack(children: [
|
||||
ResponsiveWidget(
|
||||
responsiveUtils: responsiveUtils,
|
||||
desktop: Container(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(flex: 1, child: MailboxView()),
|
||||
Expanded(flex: 1, child: ThreadView()),
|
||||
Expanded(flex: 2, child: EmailView()),
|
||||
],
|
||||
),
|
||||
Expanded(child: EmailView()),
|
||||
],
|
||||
),
|
||||
),
|
||||
tablet: ThreadView(),
|
||||
mobile: ThreadView()),
|
||||
),
|
||||
tabletLarge: Container(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: responsiveUtils.getSizeWidthScreen(context) * 0.35,
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: ThreadView()
|
||||
),
|
||||
Expanded(child: EmailView()),
|
||||
],
|
||||
),
|
||||
),
|
||||
tablet: ThreadView(),
|
||||
mobile: ThreadView()),
|
||||
Obx(() => controller.dashBoardAction == DashBoardAction.compose
|
||||
? ComposerView()
|
||||
: SizedBox.shrink()),
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
enum DashBoardAction {
|
||||
none,
|
||||
compose
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:core/core.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||
@@ -28,6 +27,7 @@ import 'package:tmail_ui_user/features/email/presentation/model/composer_argumen
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/remove_email_drafts_state.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_action.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/state/get_all_email_state.dart';
|
||||
@@ -675,10 +675,6 @@ class ThreadController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
bool canComposeEmail() => mailboxDashBoardController.sessionCurrent != null
|
||||
&& mailboxDashBoardController.userProfile.value != null
|
||||
&& mailboxDashBoardController.mapDefaultMailboxId.isNotEmpty;
|
||||
|
||||
bool isSelectionEnabled() => currentSelectMode.value == SelectMode.ACTIVE;
|
||||
|
||||
void pressEmailSelectionAction(BuildContext context, EmailActionType actionType, List<PresentationEmail> selectionEmail) {
|
||||
@@ -709,27 +705,25 @@ class ThreadController extends BaseController {
|
||||
}
|
||||
|
||||
void editEmail(PresentationEmail presentationEmail) {
|
||||
if (canComposeEmail()) {
|
||||
push(
|
||||
AppRoutes.COMPOSER,
|
||||
arguments: ComposerArguments(
|
||||
emailActionType: EmailActionType.edit,
|
||||
presentationEmail: presentationEmail,
|
||||
mailboxRole: mailboxDashBoardController.selectedMailbox.value?.role,
|
||||
session: mailboxDashBoardController.sessionCurrent!,
|
||||
userProfile: mailboxDashBoardController.userProfile.value!,
|
||||
mapMailboxId: mailboxDashBoardController.mapDefaultMailboxId));
|
||||
final arguments = ComposerArguments(
|
||||
emailActionType: EmailActionType.edit,
|
||||
presentationEmail: presentationEmail,
|
||||
mailboxRole: mailboxDashBoardController.selectedMailbox.value?.role);
|
||||
|
||||
if (kIsWeb) {
|
||||
mailboxDashBoardController.dispatchDashBoardAction(DashBoardAction.compose, arguments: arguments);
|
||||
} else {
|
||||
push(AppRoutes.COMPOSER, arguments: arguments);
|
||||
}
|
||||
}
|
||||
|
||||
void composeEmailAction() {
|
||||
if (canComposeEmail()) {
|
||||
push(
|
||||
AppRoutes.COMPOSER,
|
||||
arguments: ComposerArguments(
|
||||
session: mailboxDashBoardController.sessionCurrent!,
|
||||
userProfile: mailboxDashBoardController.userProfile.value!,
|
||||
mapMailboxId: mailboxDashBoardController.mapDefaultMailboxId));
|
||||
if (kIsWeb) {
|
||||
if (mailboxDashBoardController.dashBoardAction != DashBoardAction.compose) {
|
||||
mailboxDashBoardController.dispatchDashBoardAction(DashBoardAction.compose, arguments: ComposerArguments());
|
||||
}
|
||||
} else {
|
||||
push(AppRoutes.COMPOSER, arguments: ComposerArguments());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -773,4 +773,47 @@ class AppLocalizations {
|
||||
'Email address copied to clipboard',
|
||||
name: 'email_address_copied_to_clipboard');
|
||||
}
|
||||
|
||||
String get minimize {
|
||||
return Intl.message(
|
||||
'Minimize',
|
||||
name: 'minimize');
|
||||
}
|
||||
|
||||
String get fullscreen {
|
||||
return Intl.message(
|
||||
'Fullscreen',
|
||||
name: 'fullscreen');
|
||||
}
|
||||
|
||||
String get close {
|
||||
return Intl.message(
|
||||
'Close',
|
||||
name: 'close');
|
||||
}
|
||||
|
||||
String get send {
|
||||
return Intl.message(
|
||||
'Send',
|
||||
name: 'send');
|
||||
}
|
||||
|
||||
String get attachments {
|
||||
return Intl.message(
|
||||
'Attachments',
|
||||
name: 'attachments');
|
||||
}
|
||||
|
||||
String get show_all {
|
||||
return Intl.message(
|
||||
'Show all',
|
||||
name: 'show_all');
|
||||
}
|
||||
|
||||
String get message_confirm_send_email {
|
||||
return Intl.message(
|
||||
'Are you sure to send messages without a subject?',
|
||||
name: 'message_confirm_send_email');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:get/get_navigation/src/routes/get_route.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/composer_bindings.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/composer_view.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/composer_view.dart'
|
||||
if (dart.library.html) 'package:tmail_ui_user/features/composer/presentation/composer_view_web.dart';
|
||||
import 'package:tmail_ui_user/features/destination_picker/presentation/destination_picker_bindings.dart';
|
||||
import 'package:tmail_ui_user/features/destination_picker/presentation/destination_picker_view.dart';
|
||||
import 'package:tmail_ui_user/features/home/presentation/home_bindings.dart';
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class RouterArguments with EquatableMixin {}
|
||||
@@ -125,11 +125,16 @@ dependencies:
|
||||
url: https://github.com/Enough-Software/enough_html_editor.git
|
||||
|
||||
# html_editor_enhanced: Compose email for Web Browser
|
||||
html_editor_enhanced: 2.4.0+1
|
||||
html_editor_enhanced:
|
||||
git:
|
||||
url: https://github.com/dab246/html-editor-enhanced.git
|
||||
ref: html_editor_for_web
|
||||
|
||||
# fk_user_agent
|
||||
fk_user_agent: 2.1.0
|
||||
|
||||
pointer_interceptor: 0.9.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||