TF-332 Fix when nothing is change tmail still making jmap request
This commit is contained in:
@@ -12,7 +12,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:html_editor_enhanced/html_editor.dart' as html_editor_browser;
|
import 'package:html_editor_enhanced/html_editor.dart' as editor_web;
|
||||||
import 'package:http_parser/http_parser.dart';
|
import 'package:http_parser/http_parser.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/identities/identity.dart';
|
import 'package:jmap_dart_client/jmap/identities/identity.dart';
|
||||||
@@ -62,7 +62,6 @@ class ComposerController extends BaseController {
|
|||||||
final composerArguments = Rxn<ComposerArguments>();
|
final composerArguments = Rxn<ComposerArguments>();
|
||||||
final isEnableEmailSendButton = false.obs;
|
final isEnableEmailSendButton = false.obs;
|
||||||
final isInitialRecipient = false.obs;
|
final isInitialRecipient = false.obs;
|
||||||
final emailContents = Rxn<List<EmailContent>>();
|
|
||||||
final listEmailAddressType = <PrefixEmailAddress>[].obs;
|
final listEmailAddressType = <PrefixEmailAddress>[].obs;
|
||||||
final subjectEmail = Rxn<String>();
|
final subjectEmail = Rxn<String>();
|
||||||
final screenDisplayMode = ScreenDisplayMode.normal.obs;
|
final screenDisplayMode = ScreenDisplayMode.normal.obs;
|
||||||
@@ -71,6 +70,7 @@ class ComposerController extends BaseController {
|
|||||||
final bccAddressExpandMode = ExpandMode.EXPAND.obs;
|
final bccAddressExpandMode = ExpandMode.EXPAND.obs;
|
||||||
final identitySelected = Rxn<Identity>();
|
final identitySelected = Rxn<Identity>();
|
||||||
final listIdentities = <Identity>[].obs;
|
final listIdentities = <Identity>[].obs;
|
||||||
|
final emailContentsViewState = Rx<Either<Failure, Success>>(Right(UIState.idle));
|
||||||
|
|
||||||
final SendEmailInteractor _sendEmailInteractor;
|
final SendEmailInteractor _sendEmailInteractor;
|
||||||
final GetAutoCompleteInteractor _getAutoCompleteInteractor;
|
final GetAutoCompleteInteractor _getAutoCompleteInteractor;
|
||||||
@@ -88,8 +88,7 @@ class ComposerController extends BaseController {
|
|||||||
List<EmailAddress> listBccEmailAddress = <EmailAddress>[];
|
List<EmailAddress> listBccEmailAddress = <EmailAddress>[];
|
||||||
ContactSuggestionSource _contactSuggestionSource = ContactSuggestionSource.tMailContact;
|
ContactSuggestionSource _contactSuggestionSource = ContactSuggestionSource.tMailContact;
|
||||||
HtmlEditorApi? htmlEditorApi;
|
HtmlEditorApi? htmlEditorApi;
|
||||||
final html_editor_browser.HtmlEditorController htmlControllerBrowser =
|
final htmlControllerBrowser = editor_web.HtmlEditorController(processNewLineAsBr: true);
|
||||||
html_editor_browser.HtmlEditorController(processNewLineAsBr: true);
|
|
||||||
|
|
||||||
final subjectEmailInputController = TextEditingController();
|
final subjectEmailInputController = TextEditingController();
|
||||||
final toEmailAddressController = TextEditingController();
|
final toEmailAddressController = TextEditingController();
|
||||||
@@ -98,6 +97,7 @@ class ComposerController extends BaseController {
|
|||||||
|
|
||||||
List<Attachment> initialAttachments = <Attachment>[];
|
List<Attachment> initialAttachments = <Attachment>[];
|
||||||
String? _textEditorWeb;
|
String? _textEditorWeb;
|
||||||
|
List<EmailContent>? _emailContents;
|
||||||
|
|
||||||
void setTextEditorWeb(String? text) => _textEditorWeb = text;
|
void setTextEditorWeb(String? text) => _textEditorWeb = text;
|
||||||
|
|
||||||
@@ -105,21 +105,34 @@ class ComposerController extends BaseController {
|
|||||||
|
|
||||||
void setSubjectEmail(String subject) => subjectEmail.value = subject;
|
void setSubjectEmail(String subject) => subjectEmail.value = subject;
|
||||||
|
|
||||||
Future<String> _getEmailBodyText({bool changedEmail = false}) async {
|
Future<String> _getEmailBodyText(BuildContext context, {
|
||||||
if (kIsWeb) {
|
bool changedEmail = false
|
||||||
return await htmlControllerBrowser.getText();
|
}) async {
|
||||||
} else {
|
if (BuildUtils.isWeb) {
|
||||||
if (changedEmail) {
|
var contentHtml = '';
|
||||||
return await htmlEditorApi?.getText() ?? '';
|
if (_responsiveUtils.isWebDesktop(context) &&
|
||||||
|
screenDisplayMode.value == ScreenDisplayMode.minimize) {
|
||||||
|
contentHtml = textEditorWeb ?? '';
|
||||||
} else {
|
} else {
|
||||||
final content = await htmlEditorApi?.getText() ?? '';
|
contentHtml = await htmlControllerBrowser.getText();
|
||||||
if (_isMobileApp && identitySelected.value?.textSignature?.value.isNotEmpty == true) {
|
}
|
||||||
final newContent = '$content${identitySelected.value?.textSignature?.value.toSignatureBlock()}';
|
log('ComposerController::_getEmailBodyText():WEB: contentHtml: $contentHtml');
|
||||||
log('ComposerController::_generateEmail()_MOBILE: $newContent');
|
final newContentHtml = contentHtml.removeEditorStartTag();
|
||||||
return newContent;
|
log('ComposerController::_getEmailBodyText():WEB: newContentHtml: $newContentHtml');
|
||||||
} else {
|
return newContentHtml;
|
||||||
return content;
|
} else {
|
||||||
}
|
final contentHtml = await htmlEditorApi?.getText() ?? '';
|
||||||
|
log('ComposerController::_getEmailBodyText():MOBILE: $contentHtml');
|
||||||
|
final newContentHtml = contentHtml.removeEditorStartTag();
|
||||||
|
if (changedEmail) {
|
||||||
|
return newContentHtml;
|
||||||
|
} else if (_isMobileApp && identitySelected.value?.textSignature?.value.isNotEmpty == true) {
|
||||||
|
final contentHtmlWithSignature =
|
||||||
|
'$newContentHtml${identitySelected.value?.textSignature?.value.toSignatureBlock()}';
|
||||||
|
log('ComposerController::_getEmailBodyText():MOBILE:SIGNATURE: $contentHtmlWithSignature');
|
||||||
|
return contentHtmlWithSignature;
|
||||||
|
} else {
|
||||||
|
return newContentHtml;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,12 +188,24 @@ class ComposerController extends BaseController {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onData(Either<Failure, Success> newState) {
|
||||||
|
super.onData(newState);
|
||||||
|
newState.map((success) async {
|
||||||
|
if (success is GetEmailContentLoading) {
|
||||||
|
emailContentsViewState.value = Right(success);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onDone() {
|
void onDone() {
|
||||||
viewState.value.fold(
|
viewState.value.fold(
|
||||||
(failure) {
|
(failure) {
|
||||||
if (failure is LocalFilePickerFailure || failure is LocalFilePickerCancel) {
|
if (failure is LocalFilePickerFailure || failure is LocalFilePickerCancel) {
|
||||||
_pickFileFailure(failure);
|
_pickFileFailure(failure);
|
||||||
|
} else if (failure is GetEmailContentFailure) {
|
||||||
|
emailContentsViewState.value = Left(failure);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(success) {
|
(success) {
|
||||||
@@ -218,7 +243,7 @@ class ComposerController extends BaseController {
|
|||||||
if (arguments.emailActionType == EmailActionType.edit) {
|
if (arguments.emailActionType == EmailActionType.edit) {
|
||||||
_getEmailContentAction(arguments);
|
_getEmailContentAction(arguments);
|
||||||
}
|
}
|
||||||
_initToEmailAddress(arguments);
|
_initEmailAddress(arguments);
|
||||||
_initSubjectEmail(arguments);
|
_initSubjectEmail(arguments);
|
||||||
_initAttachments(arguments);
|
_initAttachments(arguments);
|
||||||
}
|
}
|
||||||
@@ -338,7 +363,7 @@ class ComposerController extends BaseController {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initToEmailAddress(ComposerArguments arguments) {
|
void _initEmailAddress(ComposerArguments arguments) {
|
||||||
final userProfile = mailboxDashBoardController.userProfile.value;
|
final userProfile = mailboxDashBoardController.userProfile.value;
|
||||||
if (arguments.presentationEmail != null && userProfile != null) {
|
if (arguments.presentationEmail != null && userProfile != null) {
|
||||||
final userEmailAddress = EmailAddress(null, userProfile.email);
|
final userEmailAddress = EmailAddress(null, userProfile.email);
|
||||||
@@ -419,8 +444,12 @@ class ComposerController extends BaseController {
|
|||||||
return emailQuotedHtml;
|
return emailQuotedHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Email> _generateEmail(Map<Role, MailboxId> mapDefaultMailboxId,
|
Future<Email> _generateEmail(
|
||||||
UserProfile userProfile, {bool asDrafts = false}) async {
|
BuildContext context,
|
||||||
|
Map<Role, MailboxId> mapDefaultMailboxId,
|
||||||
|
UserProfile userProfile,
|
||||||
|
{bool asDrafts = false}
|
||||||
|
) async {
|
||||||
final generateEmailId = EmailId(Id(_uuid.v1()));
|
final generateEmailId = EmailId(Id(_uuid.v1()));
|
||||||
final outboxMailboxId = mapDefaultMailboxId[PresentationMailbox.roleOutbox];
|
final outboxMailboxId = mapDefaultMailboxId[PresentationMailbox.roleOutbox];
|
||||||
final draftMailboxId = mapDefaultMailboxId[PresentationMailbox.roleDrafts];
|
final draftMailboxId = mapDefaultMailboxId[PresentationMailbox.roleDrafts];
|
||||||
@@ -437,7 +466,7 @@ class ComposerController extends BaseController {
|
|||||||
final generatePartId = PartId(_uuid.v1());
|
final generatePartId = PartId(_uuid.v1());
|
||||||
final generateBlobId = Id(_uuid.v1());
|
final generateBlobId = Id(_uuid.v1());
|
||||||
|
|
||||||
var emailBodyText = await _getEmailBodyText();
|
var emailBodyText = await _getEmailBodyText(context);
|
||||||
log('ComposerController::_generateEmail(): $emailBodyText');
|
log('ComposerController::_generateEmail(): $emailBodyText');
|
||||||
final userAgent = await userAgentPlatform;
|
final userAgent = await userAgentPlatform;
|
||||||
log('ComposerController::_generateEmail(): userAgent: $userAgent');
|
log('ComposerController::_generateEmail(): userAgent: $userAgent');
|
||||||
@@ -561,7 +590,7 @@ class ComposerController extends BaseController {
|
|||||||
if (arguments != null && session != null && mapDefaultMailboxId.isNotEmpty
|
if (arguments != null && session != null && mapDefaultMailboxId.isNotEmpty
|
||||||
&& userProfile != null) {
|
&& userProfile != null) {
|
||||||
|
|
||||||
final email = await _generateEmail(mapDefaultMailboxId, userProfile);
|
final email = await _generateEmail(context, mapDefaultMailboxId, userProfile);
|
||||||
final accountId = session.accounts.keys.first;
|
final accountId = session.accounts.keys.first;
|
||||||
final sentMailboxId = mapDefaultMailboxId[PresentationMailbox.roleSent];
|
final sentMailboxId = mapDefaultMailboxId[PresentationMailbox.roleSent];
|
||||||
final submissionCreateId = Id(const Uuid().v1());
|
final submissionCreateId = Id(const Uuid().v1());
|
||||||
@@ -666,14 +695,17 @@ class ComposerController extends BaseController {
|
|||||||
uploadController.deleteFileUploaded(uploadId);
|
uploadController.deleteFileUploaded(uploadId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _isEmailChanged(BuildContext context, ComposerArguments arguments) async {
|
Future<bool> _isEmailChanged(
|
||||||
final currentTextInComposer = await _getEmailBodyText(changedEmail: true);
|
BuildContext context,
|
||||||
final newEmailBody = currentTextInComposer.removeEditorDefaultSpace();
|
ComposerArguments arguments,
|
||||||
|
) async {
|
||||||
|
final newEmailBody = await _getEmailBodyText(context, changedEmail: true);
|
||||||
log('ComposerController::_isEmailChanged(): newEmailBody: $newEmailBody');
|
log('ComposerController::_isEmailChanged(): newEmailBody: $newEmailBody');
|
||||||
var oldEmailBody = '';
|
var oldEmailBody = getContentEmail(context) ?? '';
|
||||||
final contentEmail = getContentEmail(context);
|
log('ComposerController::_isEmailChanged(): getContentEmail: $oldEmailBody');
|
||||||
if (arguments.emailActionType != EmailActionType.compose && contentEmail != null && contentEmail.isNotEmpty) {
|
if (arguments.emailActionType != EmailActionType.compose &&
|
||||||
oldEmailBody = kIsWeb ? contentEmail : '\n$contentEmail\n';
|
oldEmailBody.isNotEmpty) {
|
||||||
|
oldEmailBody = BuildUtils.isWeb ? oldEmailBody : '\n$oldEmailBody\n';
|
||||||
}
|
}
|
||||||
if (BuildUtils.isWeb) {
|
if (BuildUtils.isWeb) {
|
||||||
if (identitySelected.value?.htmlSignature?.value.isNotEmpty == true) {
|
if (identitySelected.value?.htmlSignature?.value.isNotEmpty == true) {
|
||||||
@@ -733,7 +765,11 @@ class ComposerController extends BaseController {
|
|||||||
final isChanged = await _isEmailChanged(context, arguments);
|
final isChanged = await _isEmailChanged(context, arguments);
|
||||||
log('ComposerController::saveEmailAsDrafts(): saveEmailAsDrafts isChanged: $isChanged');
|
log('ComposerController::saveEmailAsDrafts(): saveEmailAsDrafts isChanged: $isChanged');
|
||||||
if (isChanged) {
|
if (isChanged) {
|
||||||
final newEmail = await _generateEmail(mapDefaultMailboxId, userProfile, asDrafts: true);
|
final newEmail = await _generateEmail(
|
||||||
|
context,
|
||||||
|
mapDefaultMailboxId,
|
||||||
|
userProfile,
|
||||||
|
asDrafts: true);
|
||||||
final accountId = session.accounts.keys.first;
|
final accountId = session.accounts.keys.first;
|
||||||
final oldEmail = arguments.presentationEmail;
|
final oldEmail = arguments.presentationEmail;
|
||||||
|
|
||||||
@@ -764,21 +800,15 @@ class ComposerController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _getEmailContentSuccess(GetEmailContentSuccess success) {
|
void _getEmailContentSuccess(GetEmailContentSuccess success) {
|
||||||
emailContents.value = success.emailContents;
|
|
||||||
if (success.attachments.isNotEmpty) {
|
if (success.attachments.isNotEmpty) {
|
||||||
initialAttachments = success.attachments;
|
initialAttachments = success.attachments;
|
||||||
uploadController.initializeUploadAttachments(success.attachments);
|
uploadController.initializeUploadAttachments(success.attachments);
|
||||||
}
|
}
|
||||||
|
emailContentsViewState.value = Right(success);
|
||||||
|
_emailContents = success.emailContents;
|
||||||
}
|
}
|
||||||
|
|
||||||
String? getEmailContentDraftsAsHtml() {
|
String? getEmailContentDraftsAsHtml() => _emailContents?.asHtmlString;
|
||||||
final listContents = emailContents.value;
|
|
||||||
if (listContents != null) {
|
|
||||||
return listContents.asHtmlString;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String getEmailAddressSender() {
|
String getEmailAddressSender() {
|
||||||
final arguments = composerArguments.value;
|
final arguments = composerArguments.value;
|
||||||
|
|||||||
@@ -10,15 +10,17 @@ import 'package:flutter_svg/flutter_svg.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:jmap_dart_client/jmap/identities/identity.dart';
|
import 'package:jmap_dart_client/jmap/identities/identity.dart';
|
||||||
import 'package:model/model.dart';
|
import 'package:model/model.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/domain/state/upload_attachment_state.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/composer_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_file_composer_builder.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/features/composer/presentation/widgets/email_address_input_builder.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/presentation/extensions/list_upload_file_state_extension.dart';
|
import 'package:tmail_ui_user/features/upload/presentation/extensions/list_upload_file_state_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart';
|
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
class ComposerView extends GetWidget<ComposerController> {
|
class ComposerView extends GetWidget<ComposerController> with AppLoaderMixin {
|
||||||
|
|
||||||
final responsiveUtils = Get.find<ResponsiveUtils>();
|
final responsiveUtils = Get.find<ResponsiveUtils>();
|
||||||
final imagePaths = Get.find<ImagePaths>();
|
final imagePaths = Get.find<ImagePaths>();
|
||||||
@@ -490,25 +492,59 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
|
|
||||||
Widget _buildComposerEditor(BuildContext context) {
|
Widget _buildComposerEditor(BuildContext context) {
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
if (controller.composerArguments.value?.emailActionType == EmailActionType.compose) {
|
final argsComposer = controller.composerArguments.value;
|
||||||
return HtmlEditor(
|
|
||||||
key: const Key('composer_editor'),
|
if (argsComposer == null) {
|
||||||
minHeight: 550,
|
return const SizedBox.shrink();
|
||||||
initialContent: ''.addEditorDefaultSpace(),
|
}
|
||||||
onCreated: (editorApi) => controller.htmlEditorApi = editorApi);
|
|
||||||
} else {
|
switch(argsComposer.emailActionType) {
|
||||||
final message = controller.getContentEmail(context);
|
case EmailActionType.compose:
|
||||||
return message != null && message.isNotEmpty
|
case EmailActionType.composeFromEmailAddress:
|
||||||
? HtmlEditor(
|
return _buildHtmlEditor(HtmlExtension.editorStartTags);
|
||||||
key: const Key('composer_editor'),
|
case EmailActionType.edit:
|
||||||
minHeight: 550,
|
return controller.emailContentsViewState.value.fold(
|
||||||
onCreated: (editorApi) => controller.htmlEditorApi = editorApi,
|
(failure) => _buildHtmlEditor(HtmlExtension.editorStartTags),
|
||||||
initialContent: message)
|
(success) {
|
||||||
: const SizedBox.shrink();
|
if (success is GetEmailContentLoading) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: loadingWidget,
|
||||||
|
);
|
||||||
|
} else if (success is GetEmailContentSuccess) {
|
||||||
|
var contentHtml = success.emailContents.asHtmlString;
|
||||||
|
if (contentHtml.isEmpty == true) {
|
||||||
|
contentHtml = HtmlExtension.editorStartTags;
|
||||||
|
}
|
||||||
|
return _buildHtmlEditor(contentHtml);
|
||||||
|
} else {
|
||||||
|
return _buildHtmlEditor(HtmlExtension.editorStartTags);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
case EmailActionType.reply:
|
||||||
|
case EmailActionType.replyAll:
|
||||||
|
case EmailActionType.forward:
|
||||||
|
var contentHtml = controller.getEmailContentQuotedAsHtml(
|
||||||
|
context,
|
||||||
|
argsComposer);
|
||||||
|
if (contentHtml.isEmpty == true) {
|
||||||
|
contentHtml = HtmlExtension.editorStartTags;
|
||||||
|
}
|
||||||
|
return _buildHtmlEditor(contentHtml);
|
||||||
|
default:
|
||||||
|
return _buildHtmlEditor(HtmlExtension.editorStartTags);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildHtmlEditor(String initialContent) {
|
||||||
|
return HtmlEditor(
|
||||||
|
key: const Key('composer_editor'),
|
||||||
|
minHeight: 550,
|
||||||
|
initialContent: initialContent,
|
||||||
|
onCreated: (editorApi) => controller.htmlEditorApi = editorApi);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildAttachmentsLoadingView({EdgeInsets? padding, double? size}) {
|
Widget _buildAttachmentsLoadingView({EdgeInsets? padding, double? size}) {
|
||||||
return Obx(() => controller.viewState.value.fold(
|
return Obx(() => controller.viewState.value.fold(
|
||||||
(failure) => const SizedBox.shrink(),
|
(failure) => const SizedBox.shrink(),
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ import 'package:flutter/cupertino.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:html_editor_enhanced/html_editor.dart' as html_editor_browser;
|
import 'package:html_editor_enhanced/html_editor.dart' as editor_web;
|
||||||
import 'package:jmap_dart_client/jmap/identities/identity.dart';
|
import 'package:jmap_dart_client/jmap/identities/identity.dart';
|
||||||
import 'package:model/model.dart';
|
import 'package:model/model.dart';
|
||||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/domain/state/upload_attachment_state.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/composer_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart';
|
||||||
@@ -17,9 +18,10 @@ import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_
|
|||||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/email_address_input_builder.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/email_address_input_builder.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/presentation/extensions/list_upload_file_state_extension.dart';
|
import 'package:tmail_ui_user/features/upload/presentation/extensions/list_upload_file_state_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart';
|
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
class ComposerView extends GetWidget<ComposerController> {
|
class ComposerView extends GetWidget<ComposerController> with AppLoaderMixin {
|
||||||
|
|
||||||
final responsiveUtils = Get.find<ResponsiveUtils>();
|
final responsiveUtils = Get.find<ResponsiveUtils>();
|
||||||
final imagePaths = Get.find<ImagePaths>();
|
final imagePaths = Get.find<ImagePaths>();
|
||||||
@@ -53,7 +55,12 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
const Divider(color: AppColor.colorDividerComposer, height: 1),
|
const Divider(color: AppColor.colorDividerComposer, height: 1),
|
||||||
Padding(padding: const EdgeInsets.symmetric(horizontal: 16), child: _buildListButton(context)),
|
Padding(padding: const EdgeInsets.symmetric(horizontal: 16), child: _buildListButton(context)),
|
||||||
const Divider(color: AppColor.colorDividerComposer, height: 1),
|
const Divider(color: AppColor.colorDividerComposer, height: 1),
|
||||||
Expanded(child: _buildEditorAndAttachments(context)),
|
Expanded(child: Column(
|
||||||
|
children: [
|
||||||
|
_buildAttachmentsWidget(context),
|
||||||
|
_buildEditorForm(context)
|
||||||
|
]
|
||||||
|
)),
|
||||||
]
|
]
|
||||||
)))
|
)))
|
||||||
)
|
)
|
||||||
@@ -92,7 +99,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
padding: const EdgeInsets.only(left: 10),
|
padding: const EdgeInsets.only(left: 10),
|
||||||
child: buildIconWeb(
|
child: buildIconWeb(
|
||||||
icon: SvgPicture.asset(imagePaths.icCloseMailbox, fit: BoxFit.fill),
|
icon: SvgPicture.asset(imagePaths.icCloseMailbox, fit: BoxFit.fill),
|
||||||
tooltip: AppLocalizations.of(context).close,
|
tooltip: AppLocalizations.of(context).saveAndClose,
|
||||||
onTap: () => controller.saveEmailAsDrafts(context)
|
onTap: () => controller.saveEmailAsDrafts(context)
|
||||||
)),
|
)),
|
||||||
buildIconWeb(
|
buildIconWeb(
|
||||||
@@ -147,23 +154,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
),
|
)
|
||||||
tabletLarge: Scaffold(
|
|
||||||
backgroundColor: Colors.black38,
|
|
||||||
body: Align(alignment: Alignment.center, child: Card(
|
|
||||||
color: Colors.transparent,
|
|
||||||
shadowColor: Colors.transparent,
|
|
||||||
child: Container(
|
|
||||||
decoration: const BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(24))),
|
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(24)),
|
|
||||||
child: LayoutBuilder(builder: (context, constraints) =>
|
|
||||||
PointerInterceptor(child: _buildBodyForDesktop(context, constraints)))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
))
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +163,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
children: [
|
children: [
|
||||||
buildIconWeb(
|
buildIconWeb(
|
||||||
icon: SvgPicture.asset(imagePaths.icCloseMailbox, fit: BoxFit.fill),
|
icon: SvgPicture.asset(imagePaths.icCloseMailbox, fit: BoxFit.fill),
|
||||||
tooltip: AppLocalizations.of(context).close,
|
tooltip: AppLocalizations.of(context).saveAndClose,
|
||||||
onTap: () => controller.saveEmailAsDrafts(context)),
|
onTap: () => controller.saveEmailAsDrafts(context)),
|
||||||
if (responsiveUtils.isDesktop(context))
|
if (responsiveUtils.isDesktop(context))
|
||||||
Obx(() => buildIconWeb(
|
Obx(() => buildIconWeb(
|
||||||
@@ -204,7 +195,7 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
children: [
|
children: [
|
||||||
buildIconWeb(
|
buildIconWeb(
|
||||||
icon: SvgPicture.asset(imagePaths.icClose, width: 30, height: 30, fit: BoxFit.fill),
|
icon: SvgPicture.asset(imagePaths.icClose, width: 30, height: 30, fit: BoxFit.fill),
|
||||||
tooltip: AppLocalizations.of(context).close,
|
tooltip: AppLocalizations.of(context).saveAndClose,
|
||||||
iconPadding: EdgeInsets.zero,
|
iconPadding: EdgeInsets.zero,
|
||||||
onTap: () => controller.saveEmailAsDrafts(context)),
|
onTap: () => controller.saveEmailAsDrafts(context)),
|
||||||
Expanded(child: _buildTitleComposer(context)),
|
Expanded(child: _buildTitleComposer(context)),
|
||||||
@@ -287,8 +278,15 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
])),
|
])),
|
||||||
const Divider(color: AppColor.colorDividerComposer, height: 1),
|
const Divider(color: AppColor.colorDividerComposer, height: 1),
|
||||||
Expanded(child: Padding(
|
Expanded(child: Padding(
|
||||||
padding: EdgeInsets.only(left: responsiveUtils.isMobile(context) ? 16 : 60, right: responsiveUtils.isMobile(context) ? 16 : 25),
|
padding: EdgeInsets.only(
|
||||||
child: _buildEditorAndAttachments(context))),
|
left: responsiveUtils.isMobile(context) ? 16 : 60,
|
||||||
|
right: responsiveUtils.isMobile(context) ? 16 : 25),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_buildAttachmentsWidget(context),
|
||||||
|
_buildEditorForm(context)
|
||||||
|
]
|
||||||
|
))),
|
||||||
const Divider(color: AppColor.colorDividerComposer, height: 1),
|
const Divider(color: AppColor.colorDividerComposer, height: 1),
|
||||||
Obx(() => _buildBottomBar(context, controller.isEnableEmailSendButton.value)),
|
Obx(() => _buildBottomBar(context, controller.isEnableEmailSendButton.value)),
|
||||||
]);
|
]);
|
||||||
@@ -489,105 +487,137 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildEditorAndAttachments(BuildContext context) {
|
Widget _buildEditorForm(BuildContext context) {
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
final uploadAttachments = controller.uploadController.listUploadAttachments;
|
final argsComposer = controller.composerArguments.value;
|
||||||
|
|
||||||
return Column(
|
if (argsComposer == null) {
|
||||||
children: [
|
return const SizedBox.shrink();
|
||||||
if (uploadAttachments.isNotEmpty)
|
}
|
||||||
...[
|
|
||||||
Padding(
|
final currentTextEditor = controller.textEditorWeb;
|
||||||
padding: EdgeInsets.only(
|
|
||||||
top: 4,
|
switch(argsComposer.emailActionType) {
|
||||||
bottom: 4,
|
case EmailActionType.compose:
|
||||||
left: responsiveUtils.isMobile(context) ? 16 : 20,
|
case EmailActionType.composeFromEmailAddress:
|
||||||
right: responsiveUtils.isMobile(context) ? 16: 0),
|
return _buildHtmlEditor(
|
||||||
child: _buildAttachmentsTitle(context,
|
context,
|
||||||
uploadAttachments,
|
currentTextEditor ?? HtmlExtension.editorStartTags);
|
||||||
controller.expandModeAttachments.value)),
|
case EmailActionType.edit:
|
||||||
_buildAttachmentsLoadingView(),
|
return controller.emailContentsViewState.value.fold(
|
||||||
Padding(
|
(failure) => _buildHtmlEditor(
|
||||||
padding: EdgeInsets.only(
|
context,
|
||||||
bottom: 8,
|
currentTextEditor ?? HtmlExtension.editorStartTags),
|
||||||
left: responsiveUtils.isMobile(context) ? 16 : 10,
|
(success) {
|
||||||
right: responsiveUtils.isMobile(context) ? 16 : 10),
|
if (success is GetEmailContentLoading) {
|
||||||
child: _buildAttachmentsList(context,
|
return Padding(
|
||||||
uploadAttachments,
|
padding: const EdgeInsets.all(16.0),
|
||||||
controller.expandModeAttachments.value))
|
child: loadingWidget,
|
||||||
],
|
);
|
||||||
if (controller.composerArguments.value != null)
|
} else if (success is GetEmailContentSuccess) {
|
||||||
_buildComposeEditor(context)
|
var contentHtml = success.emailContents.asHtmlString;
|
||||||
]
|
if (contentHtml.isEmpty == true) {
|
||||||
);
|
contentHtml = HtmlExtension.editorStartTags;
|
||||||
|
}
|
||||||
|
return _buildHtmlEditor(context, currentTextEditor ?? contentHtml);
|
||||||
|
} else {
|
||||||
|
return _buildHtmlEditor(
|
||||||
|
context,
|
||||||
|
currentTextEditor ?? HtmlExtension.editorStartTags);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
case EmailActionType.reply:
|
||||||
|
case EmailActionType.replyAll:
|
||||||
|
case EmailActionType.forward:
|
||||||
|
var contentHtml = controller.getEmailContentQuotedAsHtml(
|
||||||
|
context,
|
||||||
|
argsComposer);
|
||||||
|
if (contentHtml.isEmpty == true) {
|
||||||
|
contentHtml = HtmlExtension.editorStartTags;
|
||||||
|
}
|
||||||
|
return _buildHtmlEditor(context, currentTextEditor ?? contentHtml);
|
||||||
|
default:
|
||||||
|
return _buildHtmlEditor(
|
||||||
|
context,
|
||||||
|
currentTextEditor ?? HtmlExtension.editorStartTags);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildComposeEditor(BuildContext context) {
|
Widget _buildHtmlEditor(BuildContext context, String initContent) {
|
||||||
if (controller.composerArguments.value?.emailActionType == EmailActionType.compose) {
|
log('ComposerView::_buildHtmlEditor(): initContent: $initContent');
|
||||||
final initContent = controller.textEditorWeb ?? ''.addEditorDefaultSpace();
|
|
||||||
return Expanded(child: Padding(
|
return Expanded(
|
||||||
padding: EdgeInsets.symmetric(horizontal: responsiveUtils.isMobile(context) ? 8 : 10),
|
child: Padding(
|
||||||
child: _buildEditor(context, initContent)));
|
padding: EdgeInsets.symmetric(
|
||||||
} else if (controller.composerArguments.value?.emailActionType == EmailActionType.edit) {
|
horizontal: responsiveUtils.isMobile(context) ? 8 : 10),
|
||||||
final initContent = controller.textEditorWeb ?? controller.getEmailContentDraftsAsHtml();
|
child: editor_web.HtmlEditor(
|
||||||
if (initContent != null) {
|
key: const Key('composer_editor_web'),
|
||||||
return Expanded(child: Padding(
|
controller: controller.htmlControllerBrowser,
|
||||||
padding: EdgeInsets.symmetric(horizontal: responsiveUtils.isMobile(context) ? 8 : 10),
|
htmlEditorOptions: const editor_web.HtmlEditorOptions(
|
||||||
child: _buildEditor(context, initContent)));
|
hint: '',
|
||||||
} else {
|
darkMode: false,
|
||||||
return const Padding(
|
),
|
||||||
padding: EdgeInsets.all(16),
|
blockQuotedContent: initContent,
|
||||||
child: SizedBox(
|
htmlToolbarOptions: const editor_web.HtmlToolbarOptions(
|
||||||
width: 30,
|
toolbarPosition: editor_web.ToolbarPosition.custom),
|
||||||
height: 30,
|
otherOptions: const editor_web.OtherOptions(height: 550),
|
||||||
child: CupertinoActivityIndicator(color: AppColor.colorLoading)));
|
callbacks: editor_web.Callbacks(
|
||||||
}
|
onBeforeCommand: (String? currentHtml) {
|
||||||
} else {
|
log('ComposerView::_buildComposerEditor(): onBeforeCommand : $currentHtml');
|
||||||
final initContent = controller.textEditorWeb ?? controller.getEmailContentQuotedAsHtml(context, controller.composerArguments.value!);
|
controller.setTextEditorWeb(currentHtml);
|
||||||
return Expanded(child: Padding(
|
}, onChangeContent: (String? changed) {
|
||||||
padding: EdgeInsets.symmetric(horizontal: responsiveUtils.isMobile(context) ? 8 : 10),
|
log('ComposerView::_buildComposerEditor(): onChangeContent : $changed');
|
||||||
child: _buildEditor(context, initContent)));
|
controller.setTextEditorWeb(changed);
|
||||||
}
|
}, onInit: () {
|
||||||
|
log('ComposerView::_buildComposerEditor(): onInit');
|
||||||
|
controller.setTextEditorWeb(initContent);
|
||||||
|
controller.setFullScreenEditor();
|
||||||
|
}, onFocus: () {
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus();
|
||||||
|
Future.delayed(const Duration(milliseconds: 500), () {
|
||||||
|
controller.htmlControllerBrowser.setFocus();
|
||||||
|
});
|
||||||
|
}, onBlur: () {
|
||||||
|
controller.onEditorFocusChange(false);
|
||||||
|
}, onMouseDown: () {
|
||||||
|
controller.onEditorFocusChange(true);
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildEditor(BuildContext context, String initContent) {
|
Widget _buildAttachmentsWidget(BuildContext context) {
|
||||||
log('ComposerView::_buildEditor(): initContent: $initContent');
|
return Obx(() {
|
||||||
|
final attachments = controller.uploadController.listUploadAttachments;
|
||||||
return html_editor_browser.HtmlEditor(
|
if (attachments.isNotEmpty) {
|
||||||
key: const Key('composer_editor_web'),
|
return Column(children: [
|
||||||
controller: controller.htmlControllerBrowser,
|
Padding(
|
||||||
htmlEditorOptions: const html_editor_browser.HtmlEditorOptions(
|
padding: EdgeInsets.only(
|
||||||
hint: '',
|
top: 4,
|
||||||
darkMode: false,
|
bottom: 4,
|
||||||
),
|
left: responsiveUtils.isMobile(context) ? 16 : 20,
|
||||||
blockQuotedContent: initContent,
|
right: responsiveUtils.isMobile(context) ? 16: 0),
|
||||||
htmlToolbarOptions: const html_editor_browser.HtmlToolbarOptions(
|
child: _buildAttachmentsTitle(
|
||||||
toolbarPosition: html_editor_browser.ToolbarPosition.custom
|
context,
|
||||||
),
|
attachments,
|
||||||
otherOptions: const html_editor_browser.OtherOptions(height: 550),
|
controller.expandModeAttachments.value)),
|
||||||
callbacks: html_editor_browser.Callbacks(
|
_buildAttachmentsLoadingView(),
|
||||||
onBeforeCommand: (String? currentHtml) {
|
Padding(
|
||||||
log('ComposerView::_buildComposerEditor(): onBeforeCommand : $currentHtml');
|
padding: EdgeInsets.only(
|
||||||
controller.setTextEditorWeb(currentHtml);
|
bottom: 8,
|
||||||
}, onChangeContent: (String? changed) {
|
left: responsiveUtils.isMobile(context) ? 16 : 10,
|
||||||
log('ComposerView::_buildComposerEditor(): onChangeContent : $changed');
|
right: responsiveUtils.isMobile(context) ? 16 : 10),
|
||||||
controller.setTextEditorWeb(changed);
|
child: _buildAttachmentsList(
|
||||||
}, onInit: () {
|
context,
|
||||||
log('ComposerView::_buildComposerEditor(): onInit');
|
attachments,
|
||||||
controller.setFullScreenEditor();
|
controller.expandModeAttachments.value))
|
||||||
}, onFocus: () {
|
]);
|
||||||
FocusManager.instance.primaryFocus?.unfocus();
|
} else {
|
||||||
Future.delayed(const Duration(milliseconds: 500), () {
|
return const SizedBox.shrink();
|
||||||
controller.htmlControllerBrowser.setFocus();
|
}
|
||||||
});
|
});
|
||||||
}, onBlur: () {
|
|
||||||
controller.onEditorFocusChange(false);
|
|
||||||
}, onMouseDown: () {
|
|
||||||
controller.onEditorFocusChange(true);
|
|
||||||
}
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildAttachmentsLoadingView({EdgeInsets? padding, double? size}) {
|
Widget _buildAttachmentsLoadingView({EdgeInsets? padding, double? size}) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"@@last_modified": "2022-07-05T19:24:56.876056",
|
"@@last_modified": "2022-07-11T12:17:57.489417",
|
||||||
"initializing_data": "Initializing data...",
|
"initializing_data": "Initializing data...",
|
||||||
"@initializing_data": {
|
"@initializing_data": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
@@ -1567,5 +1567,11 @@
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders_order": [],
|
"placeholders_order": [],
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"saveAndClose": "Save & close",
|
||||||
|
"@saveAndClose": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1608,4 +1608,10 @@ class AppLocalizations {
|
|||||||
name: 'messageDialogSendEmailUploadingAttachment'
|
name: 'messageDialogSendEmailUploadingAttachment'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String get saveAndClose {
|
||||||
|
return Intl.message(
|
||||||
|
'Save & close',
|
||||||
|
name: 'saveAndClose');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user