TF-1961 Transform html email content in composer
(cherry picked from commit f5470c130744242195b265a0ea4aad044ad8e740)
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
|
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
/// Transforms plain text messages.
|
/// Transforms plain text messages.
|
||||||
abstract class TextTransformer {
|
abstract class TextTransformer {
|
||||||
const TextTransformer();
|
const TextTransformer();
|
||||||
|
|
||||||
String process(String text);
|
String process(String text, HtmlEscape htmlEscape);
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,9 @@ import 'package:html/dom.dart';
|
|||||||
|
|
||||||
class ImageTransformer extends DomTransformer {
|
class ImageTransformer extends DomTransformer {
|
||||||
|
|
||||||
const ImageTransformer();
|
final bool useLoadingAttribute;
|
||||||
|
|
||||||
|
const ImageTransformer({this.useLoadingAttribute = false});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> process({
|
Future<void> process({
|
||||||
@@ -40,14 +42,19 @@ class ImageTransformer extends DomTransformer {
|
|||||||
);
|
);
|
||||||
imageElement.attributes['src'] = imageBase64 ?? src;
|
imageElement.attributes['src'] = imageBase64 ?? src;
|
||||||
} else if (src.startsWith('https://') || src.startsWith('http://')) {
|
} else if (src.startsWith('https://') || src.startsWith('http://')) {
|
||||||
final classAttribute = imageElement.attributes['class'];
|
if (useLoadingAttribute) {
|
||||||
if (classAttribute != null) {
|
imageElement.attributes['loading'] = 'lazy';
|
||||||
imageElement.attributes['class'] = '$classAttribute lazy-loading';
|
|
||||||
} else {
|
} else {
|
||||||
imageElement.attributes['class'] = 'lazy-loading';
|
final classAttribute = imageElement.attributes['class'];
|
||||||
|
if (classAttribute != null) {
|
||||||
|
imageElement.attributes['class'] = '$classAttribute lazy-loading';
|
||||||
|
} else {
|
||||||
|
imageElement.attributes['class'] = 'lazy-loading';
|
||||||
|
}
|
||||||
|
imageElement.attributes['data-src'] = src;
|
||||||
|
imageElement.attributes.remove('src');
|
||||||
|
imageElement.attributes.remove('loading');
|
||||||
}
|
}
|
||||||
imageElement.attributes['data-src'] = src;
|
|
||||||
imageElement.attributes.remove('src');
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
import 'package:core/data/network/dio_client.dart';
|
||||||
|
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
|
||||||
|
import 'package:core/presentation/utils/html_transformer/html_template.dart';
|
||||||
|
import 'package:core/utils/app_logger.dart';
|
||||||
|
import 'package:html/dom.dart';
|
||||||
|
|
||||||
|
class RemoveTooltipLinkTransformer extends DomTransformer {
|
||||||
|
|
||||||
|
const RemoveTooltipLinkTransformer();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> process({
|
||||||
|
required Document document,
|
||||||
|
Map<String, String>? mapUrlDownloadCID,
|
||||||
|
DioClient? dioClient
|
||||||
|
}) async {
|
||||||
|
final linkElements = document.querySelectorAll('a.$nameClassToolTip');
|
||||||
|
await Future.wait(linkElements.map((linkElement) async {
|
||||||
|
final classAttribute = linkElement.attributes['class'];
|
||||||
|
if (classAttribute != null) {
|
||||||
|
final newClassAttribute = classAttribute.replaceFirst(nameClassToolTip, '');
|
||||||
|
linkElement.attributes['class'] = newClassAttribute;
|
||||||
|
}
|
||||||
|
final listSpanTag = linkElement.querySelectorAll('span.tooltiptext');
|
||||||
|
log('RemoveTooltipLinkTransformer::process:listSpanTag: ${listSpanTag.length}');
|
||||||
|
if (listSpanTag.isNotEmpty) {
|
||||||
|
for (var element in listSpanTag) {
|
||||||
|
element.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import 'package:core/data/network/dio_client.dart';
|
||||||
|
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
|
||||||
|
import 'package:html/dom.dart';
|
||||||
|
|
||||||
|
class ReplaceLazyLoadImageTransformer extends DomTransformer {
|
||||||
|
|
||||||
|
const ReplaceLazyLoadImageTransformer();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> process({
|
||||||
|
required Document document,
|
||||||
|
Map<String, String>? mapUrlDownloadCID,
|
||||||
|
DioClient? dioClient
|
||||||
|
}) async {
|
||||||
|
final imageElements = document.querySelectorAll('img.lazy-loading');
|
||||||
|
await Future.wait(imageElements.map((imageElement) async {
|
||||||
|
final classAttribute = imageElement.attributes['class'];
|
||||||
|
if (classAttribute != null) {
|
||||||
|
final newClassAttribute = classAttribute.replaceFirst('lazy-loading', '');
|
||||||
|
imageElement.attributes['class'] = newClassAttribute;
|
||||||
|
}
|
||||||
|
final dataSrc = imageElement.attributes['data-src'];
|
||||||
|
if (dataSrc != null) {
|
||||||
|
imageElement.attributes['src'] = dataSrc;
|
||||||
|
imageElement.attributes.remove('data-src');
|
||||||
|
}
|
||||||
|
imageElement.attributes['loading'] = 'lazy';
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,23 +1,26 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:core/core.dart';
|
import 'package:core/core.dart';
|
||||||
import 'package:core/presentation/utils/html_transformer/message_content_transformer.dart';
|
import 'package:core/presentation/utils/html_transformer/message_content_transformer.dart';
|
||||||
|
|
||||||
class HtmlTransform {
|
class HtmlTransform {
|
||||||
|
|
||||||
final DioClient _dioClient;
|
final DioClient _dioClient;
|
||||||
|
final HtmlEscape _htmlEscape;
|
||||||
|
|
||||||
HtmlTransform(this._dioClient);
|
HtmlTransform(this._dioClient, this._htmlEscape);
|
||||||
|
|
||||||
/// Transforms this message to HTML code.
|
/// Transforms this message to HTML code.
|
||||||
Future<String> transformToHtml({
|
Future<String> transformToHtml({
|
||||||
required String contentHtml,
|
required String htmlContent,
|
||||||
Map<String, String>? mapUrlDownloadCID,
|
Map<String, String>? mapCidImageDownloadUrl,
|
||||||
TransformConfiguration? transformConfiguration,
|
TransformConfiguration? transformConfiguration,
|
||||||
}) async {
|
}) async {
|
||||||
transformConfiguration ??= TransformConfiguration.create();
|
transformConfiguration ??= TransformConfiguration.create();
|
||||||
final transformer = MessageContentTransformer(transformConfiguration, _dioClient);
|
final transformer = MessageContentTransformer(transformConfiguration, _dioClient, _htmlEscape);
|
||||||
final document = await transformer.toDocument(
|
final document = await transformer.toDocument(
|
||||||
message: contentHtml,
|
message: htmlContent,
|
||||||
mapUrlDownloadCID: mapUrlDownloadCID
|
mapUrlDownloadCID: mapCidImageDownloadUrl
|
||||||
);
|
);
|
||||||
return document.outerHtml;
|
return document.outerHtml;
|
||||||
}
|
}
|
||||||
@@ -28,7 +31,7 @@ class HtmlTransform {
|
|||||||
TransformConfiguration? transformConfiguration
|
TransformConfiguration? transformConfiguration
|
||||||
}) {
|
}) {
|
||||||
transformConfiguration ??= TransformConfiguration.create();
|
transformConfiguration ??= TransformConfiguration.create();
|
||||||
final transformer = MessageContentTransformer(transformConfiguration, _dioClient);
|
final transformer = MessageContentTransformer(transformConfiguration, _dioClient, _htmlEscape);
|
||||||
final message = transformer.toMessage(content);
|
final message = transformer.toMessage(content);
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:core/data/network/dio_client.dart';
|
import 'package:core/data/network/dio_client.dart';
|
||||||
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
|
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
|
||||||
import 'package:html/dom.dart';
|
import 'package:html/dom.dart';
|
||||||
@@ -5,23 +7,28 @@ import 'package:html/parser.dart' show parse;
|
|||||||
|
|
||||||
/// Transforms messages
|
/// Transforms messages
|
||||||
class MessageContentTransformer {
|
class MessageContentTransformer {
|
||||||
/// The configuration used for the transformation
|
/// The _configuration used for the transformation
|
||||||
final TransformConfiguration configuration;
|
final TransformConfiguration _configuration;
|
||||||
final DioClient dioClient;
|
final DioClient _dioClient;
|
||||||
|
final HtmlEscape _htmlEscape;
|
||||||
|
|
||||||
MessageContentTransformer(this.configuration, this.dioClient);
|
MessageContentTransformer(
|
||||||
|
this._configuration,
|
||||||
|
this._dioClient,
|
||||||
|
this._htmlEscape
|
||||||
|
);
|
||||||
|
|
||||||
Future<void> _transformDocument({
|
Future<void> _transformDocument({
|
||||||
required Document document,
|
required Document document,
|
||||||
Map<String, String>? mapUrlDownloadCID
|
Map<String, String>? mapUrlDownloadCID
|
||||||
}) async {
|
}) async {
|
||||||
await Future.wait([
|
await Future.wait([
|
||||||
if (configuration.domTransformers.isNotEmpty)
|
if (_configuration.domTransformers.isNotEmpty)
|
||||||
...configuration.domTransformers.map((domTransformer) async =>
|
..._configuration.domTransformers.map((domTransformer) async =>
|
||||||
domTransformer.process(
|
domTransformer.process(
|
||||||
document: document,
|
document: document,
|
||||||
mapUrlDownloadCID: mapUrlDownloadCID,
|
mapUrlDownloadCID: mapUrlDownloadCID,
|
||||||
dioClient: dioClient
|
dioClient: _dioClient
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
@@ -40,9 +47,9 @@ class MessageContentTransformer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _transformMessage(String message) {
|
String _transformMessage(String message) {
|
||||||
if (configuration.textTransformers.isNotEmpty) {
|
if (_configuration.textTransformers.isNotEmpty) {
|
||||||
for (var transformer in configuration.textTransformers) {
|
for (var transformer in _configuration.textTransformers) {
|
||||||
message = transformer.process(message);
|
message = transformer.process(message, _htmlEscape);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return message;
|
return message;
|
||||||
|
|||||||
+2
-4
@@ -6,10 +6,8 @@ import 'package:core/presentation/utils/html_transformer/base/text_transformer.d
|
|||||||
|
|
||||||
class SanitizeAutolinkHtmlTransformers extends TextTransformer {
|
class SanitizeAutolinkHtmlTransformers extends TextTransformer {
|
||||||
|
|
||||||
final HtmlEscape htmlEscape;
|
const SanitizeAutolinkHtmlTransformers();
|
||||||
|
|
||||||
SanitizeAutolinkHtmlTransformers(this.htmlEscape);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String process(String text) => SanitizeAutolinkFilter(htmlEscape).process(text);
|
String process(String text, HtmlEscape htmlEscape) => SanitizeAutolinkFilter(htmlEscape).process(text);
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,16 @@
|
|||||||
|
|
||||||
|
import 'package:core/core.dart';
|
||||||
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
|
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
|
||||||
import 'package:core/presentation/utils/html_transformer/base/text_transformer.dart';
|
import 'package:core/presentation/utils/html_transformer/base/text_transformer.dart';
|
||||||
import 'package:core/presentation/utils/html_transformer/dom/add_target_blank_in_tag_a_transformers.dart';
|
import 'package:core/presentation/utils/html_transformer/dom/add_target_blank_in_tag_a_transformers.dart';
|
||||||
import 'package:core/presentation/utils/html_transformer/dom/blockcode_transformers.dart';
|
import 'package:core/presentation/utils/html_transformer/dom/blockcode_transformers.dart';
|
||||||
import 'package:core/presentation/utils/html_transformer/dom/blockquoted_transformers.dart';
|
import 'package:core/presentation/utils/html_transformer/dom/blockquoted_transformers.dart';
|
||||||
import 'package:core/presentation/utils/html_transformer/dom/image_transformers.dart';
|
import 'package:core/presentation/utils/html_transformer/dom/image_transformers.dart';
|
||||||
|
import 'package:core/presentation/utils/html_transformer/dom/remove_tooltip_link_transformers.dart';
|
||||||
|
import 'package:core/presentation/utils/html_transformer/dom/replace_lazy_load_image_transformer.dart';
|
||||||
import 'package:core/presentation/utils/html_transformer/dom/script_transformers.dart';
|
import 'package:core/presentation/utils/html_transformer/dom/script_transformers.dart';
|
||||||
import 'package:core/presentation/utils/html_transformer/dom/sigature_transformers.dart';
|
import 'package:core/presentation/utils/html_transformer/dom/sigature_transformers.dart';
|
||||||
|
import 'package:core/presentation/utils/html_transformer/text/sanitize_autolink_html_transformers.dart';
|
||||||
|
|
||||||
/// Contains the configuration for all transformations.
|
/// Contains the configuration for all transformations.
|
||||||
class TransformConfiguration {
|
class TransformConfiguration {
|
||||||
@@ -25,6 +29,45 @@ class TransformConfiguration {
|
|||||||
this.textTransformers
|
this.textTransformers
|
||||||
);
|
);
|
||||||
|
|
||||||
|
factory TransformConfiguration.forReplyForwardEmail() => TransformConfiguration.create(
|
||||||
|
customDomTransformers: [
|
||||||
|
const ReplaceLazyLoadImageTransformer(),
|
||||||
|
if (PlatformInfo.isWeb)
|
||||||
|
const RemoveTooltipLinkTransformer(),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
factory TransformConfiguration.forDraftsEmail() => TransformConfiguration.create(
|
||||||
|
customDomTransformers: [
|
||||||
|
const RemoveScriptTransformer(),
|
||||||
|
const BlockQuotedTransformer(),
|
||||||
|
const BlockCodeTransformer(),
|
||||||
|
const AddTargetBlankInTagATransformer(),
|
||||||
|
const ImageTransformer(useLoadingAttribute: true),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
factory TransformConfiguration.forComposeEmailPlatformWeb() => TransformConfiguration.create(
|
||||||
|
customDomTransformers: [
|
||||||
|
const RemoveScriptTransformer(),
|
||||||
|
const BlockQuotedTransformer(),
|
||||||
|
const BlockCodeTransformer(),
|
||||||
|
const AddTargetBlankInTagATransformer(),
|
||||||
|
const ImageTransformer(useLoadingAttribute: true)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
factory TransformConfiguration.forPreviewEmailPlatformWeb() => TransformConfiguration.create(
|
||||||
|
customDomTransformers: [
|
||||||
|
const RemoveScriptTransformer(),
|
||||||
|
const BlockQuotedTransformer(),
|
||||||
|
const BlockCodeTransformer(),
|
||||||
|
const AddTargetBlankInTagATransformer(),
|
||||||
|
const ImageTransformer(),
|
||||||
|
const AddTooltipLinkTransformer(),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
/// Provides easy access to a standard configuration that does not block external images.
|
/// Provides easy access to a standard configuration that does not block external images.
|
||||||
static const TransformConfiguration standardConfiguration = TransformConfiguration(
|
static const TransformConfiguration standardConfiguration = TransformConfiguration(
|
||||||
standardDomTransformers,
|
standardDomTransformers,
|
||||||
@@ -61,13 +104,7 @@ class TransformConfiguration {
|
|||||||
ImageTransformer(),
|
ImageTransformer(),
|
||||||
];
|
];
|
||||||
|
|
||||||
static const List<DomTransformer> domTransformersForDraftEmail = [
|
static const List<TextTransformer> standardTextTransformers = [
|
||||||
RemoveScriptTransformer(),
|
SanitizeAutolinkHtmlTransformers()
|
||||||
BlockQuotedTransformer(),
|
|
||||||
BlockCodeTransformer(),
|
|
||||||
AddTargetBlankInTagATransformer(),
|
|
||||||
ImageTransformer(),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
static const List<TextTransformer> standardTextTransformers = [];
|
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@ import 'package:tmail_ui_user/features/session/domain/usecases/get_session_inter
|
|||||||
import 'package:tmail_ui_user/main/error/capability_validator.dart';
|
import 'package:tmail_ui_user/main/error/capability_validator.dart';
|
||||||
|
|
||||||
abstract class ReloadableController extends BaseController {
|
abstract class ReloadableController extends BaseController {
|
||||||
final DynamicUrlInterceptors _dynamicUrlInterceptors = Get.find<DynamicUrlInterceptors>();
|
final DynamicUrlInterceptors dynamicUrlInterceptors = Get.find<DynamicUrlInterceptors>();
|
||||||
final GetSessionInteractor _getSessionInteractor = Get.find<GetSessionInteractor>();
|
final GetSessionInteractor _getSessionInteractor = Get.find<GetSessionInteractor>();
|
||||||
final GetAuthenticatedAccountInteractor _getAuthenticatedAccountInteractor;
|
final GetAuthenticatedAccountInteractor _getAuthenticatedAccountInteractor;
|
||||||
final UpdateAuthenticationAccountInteractor _updateAuthenticationAccountInteractor;
|
final UpdateAuthenticationAccountInteractor _updateAuthenticationAccountInteractor;
|
||||||
@@ -72,8 +72,8 @@ abstract class ReloadableController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _setUpInterceptors(GetCredentialViewState credentialViewState) {
|
void _setUpInterceptors(GetCredentialViewState credentialViewState) {
|
||||||
_dynamicUrlInterceptors.setJmapUrl(credentialViewState.baseUrl.origin);
|
dynamicUrlInterceptors.setJmapUrl(credentialViewState.baseUrl.origin);
|
||||||
_dynamicUrlInterceptors.changeBaseUrl(credentialViewState.baseUrl.origin);
|
dynamicUrlInterceptors.changeBaseUrl(credentialViewState.baseUrl.origin);
|
||||||
authorizationInterceptors.setBasicAuthorization(
|
authorizationInterceptors.setBasicAuthorization(
|
||||||
credentialViewState.userName.value,
|
credentialViewState.userName.value,
|
||||||
credentialViewState.password.value,
|
credentialViewState.password.value,
|
||||||
@@ -100,10 +100,10 @@ abstract class ReloadableController extends BaseController {
|
|||||||
void _handleGetSessionSuccess(GetSessionSuccess success) {
|
void _handleGetSessionSuccess(GetSessionSuccess success) {
|
||||||
final session = success.session;
|
final session = success.session;
|
||||||
final personalAccount = session.personalAccount;
|
final personalAccount = session.personalAccount;
|
||||||
final apiUrl = session.getQualifiedApiUrl(baseUrl: _dynamicUrlInterceptors.jmapUrl);
|
final apiUrl = session.getQualifiedApiUrl(baseUrl: dynamicUrlInterceptors.jmapUrl);
|
||||||
log('ReloadableController::_handleGetSessionSuccess():apiUrl: $apiUrl');
|
log('ReloadableController::_handleGetSessionSuccess():apiUrl: $apiUrl');
|
||||||
if (apiUrl.isNotEmpty) {
|
if (apiUrl.isNotEmpty) {
|
||||||
_dynamicUrlInterceptors.changeBaseUrl(apiUrl);
|
dynamicUrlInterceptors.changeBaseUrl(apiUrl);
|
||||||
updateAuthenticationAccount(session, personalAccount.accountId, session.username);
|
updateAuthenticationAccount(session, personalAccount.accountId, session.username);
|
||||||
handleReloaded(session);
|
handleReloaded(session);
|
||||||
} else {
|
} else {
|
||||||
@@ -120,8 +120,8 @@ abstract class ReloadableController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _setUpInterceptorsOidc(GetStoredTokenOidcSuccess tokenOidcSuccess) {
|
void _setUpInterceptorsOidc(GetStoredTokenOidcSuccess tokenOidcSuccess) {
|
||||||
_dynamicUrlInterceptors.setJmapUrl(tokenOidcSuccess.baseUrl.toString());
|
dynamicUrlInterceptors.setJmapUrl(tokenOidcSuccess.baseUrl.toString());
|
||||||
_dynamicUrlInterceptors.changeBaseUrl(tokenOidcSuccess.baseUrl.toString());
|
dynamicUrlInterceptors.changeBaseUrl(tokenOidcSuccess.baseUrl.toString());
|
||||||
authorizationInterceptors.setTokenAndAuthorityOidc(
|
authorizationInterceptors.setTokenAndAuthorityOidc(
|
||||||
newToken: tokenOidcSuccess.tokenOidc.toToken(),
|
newToken: tokenOidcSuccess.tokenOidc.toToken(),
|
||||||
newConfig: tokenOidcSuccess.oidcConfiguration);
|
newConfig: tokenOidcSuccess.oidcConfiguration);
|
||||||
@@ -140,7 +140,7 @@ abstract class ReloadableController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void updateAuthenticationAccount(Session session, AccountId accountId, UserName userName) {
|
void updateAuthenticationAccount(Session session, AccountId accountId, UserName userName) {
|
||||||
final apiUrl = session.getQualifiedApiUrl(baseUrl: _dynamicUrlInterceptors.jmapUrl);
|
final apiUrl = session.getQualifiedApiUrl(baseUrl: dynamicUrlInterceptors.jmapUrl);
|
||||||
log('ReloadableController::updateAuthenticationAccount():apiUrl: $apiUrl');
|
log('ReloadableController::updateAuthenticationAccount():apiUrl: $apiUrl');
|
||||||
if (apiUrl.isNotEmpty) {
|
if (apiUrl.isNotEmpty) {
|
||||||
consumeState(_updateAuthenticationAccountInteractor.execute(accountId, apiUrl, userName));
|
consumeState(_updateAuthenticationAccountInteractor.execute(accountId, apiUrl, userName));
|
||||||
|
|||||||
@@ -4,17 +4,35 @@ import 'package:tmail_ui_user/features/base/styles/cupertino_loading_widget_styl
|
|||||||
class CupertinoLoadingWidget extends StatelessWidget {
|
class CupertinoLoadingWidget extends StatelessWidget {
|
||||||
|
|
||||||
final double? size;
|
final double? size;
|
||||||
|
final EdgeInsetsGeometry? padding;
|
||||||
|
|
||||||
const CupertinoLoadingWidget({super.key, this.size});
|
const CupertinoLoadingWidget({super.key, this.size, this.padding});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Center(
|
if (padding != null) {
|
||||||
child: SizedBox(
|
return Padding(
|
||||||
width: size ?? CupertinoLoadingWidgetStyles.size,
|
padding: padding!,
|
||||||
height: size ?? CupertinoLoadingWidgetStyles.size,
|
child: Center(
|
||||||
child: const CupertinoActivityIndicator(color: CupertinoLoadingWidgetStyles.progressColor)
|
child: SizedBox(
|
||||||
)
|
width: size ?? CupertinoLoadingWidgetStyles.size,
|
||||||
);
|
height: size ?? CupertinoLoadingWidgetStyles.size,
|
||||||
|
child: const CupertinoActivityIndicator(
|
||||||
|
color: CupertinoLoadingWidgetStyles.progressColor
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Center(
|
||||||
|
child: SizedBox(
|
||||||
|
width: size ?? CupertinoLoadingWidgetStyles.size,
|
||||||
|
height: size ?? CupertinoLoadingWidgetStyles.size,
|
||||||
|
child: const CupertinoActivityIndicator(
|
||||||
|
color: CupertinoLoadingWidgetStyles.progressColor
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ import 'package:core/presentation/utils/app_toast.dart';
|
|||||||
import 'package:core/presentation/utils/responsive_utils.dart';
|
import 'package:core/presentation/utils/responsive_utils.dart';
|
||||||
import 'package:core/presentation/utils/style_utils.dart';
|
import 'package:core/presentation/utils/style_utils.dart';
|
||||||
import 'package:core/presentation/views/button/icon_button_web.dart';
|
import 'package:core/presentation/views/button/icon_button_web.dart';
|
||||||
|
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
||||||
import 'package:core/presentation/views/list/sliver_grid_delegate_fixed_height.dart';
|
import 'package:core/presentation/views/list/sliver_grid_delegate_fixed_height.dart';
|
||||||
import 'package:core/presentation/views/text/text_field_builder.dart';
|
import 'package:core/presentation/views/text/text_field_builder.dart';
|
||||||
import 'package:core/presentation/views/text/text_overflow_builder.dart';
|
import 'package:core/presentation/views/text/text_overflow_builder.dart';
|
||||||
@@ -321,22 +322,17 @@ abstract class BaseComposerView extends GetWidget<ComposerController>
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Material(
|
TMailButtonWidget.fromText(
|
||||||
type: MaterialType.circle,
|
text: expandModeAttachment == ExpandMode.EXPAND
|
||||||
color: Colors.transparent,
|
? AppLocalizations.of(context).hide
|
||||||
child: TextButton(
|
: '${AppLocalizations.of(context).showAll} (${uploadFilesState.length})',
|
||||||
onPressed: controller.toggleDisplayAttachments,
|
onTapActionCallback: controller.toggleDisplayAttachments,
|
||||||
child: Text(
|
textStyle: const TextStyle(
|
||||||
expandModeAttachment == ExpandMode.EXPAND
|
fontWeight: FontWeight.w500,
|
||||||
? AppLocalizations.of(context).hide
|
fontSize: 12,
|
||||||
: '${AppLocalizations.of(context).showAll} (${uploadFilesState.length})',
|
color: AppColor.colorTextButton
|
||||||
style: const TextStyle(
|
),
|
||||||
fontWeight: FontWeight.w500,
|
backgroundColor: Colors.transparent,
|
||||||
fontSize: 12,
|
|
||||||
color: AppColor.colorTextButton
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import 'package:tmail_ui_user/features/email/data/network/email_api.dart';
|
|||||||
import 'package:tmail_ui_user/features/email/data/repository/email_repository_impl.dart';
|
import 'package:tmail_ui_user/features/email/data/repository/email_repository_impl.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
|
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/usecases/transform_html_email_content_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart';
|
import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart';
|
import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/mailbox_cache_datasource_impl.dart';
|
import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/mailbox_cache_datasource_impl.dart';
|
||||||
@@ -163,6 +164,7 @@ class ComposerBindings extends BaseBindings {
|
|||||||
Get.lazyPut(() => RemoveComposerCacheOnWebInteractor(Get.find<ComposerCacheRepository>()));
|
Get.lazyPut(() => RemoveComposerCacheOnWebInteractor(Get.find<ComposerCacheRepository>()));
|
||||||
Get.lazyPut(() => SaveComposerCacheOnWebInteractor(Get.find<ComposerCacheRepository>()));
|
Get.lazyPut(() => SaveComposerCacheOnWebInteractor(Get.find<ComposerCacheRepository>()));
|
||||||
Get.lazyPut(() => DownloadImageAsBase64Interactor(Get.find<ComposerRepository>()));
|
Get.lazyPut(() => DownloadImageAsBase64Interactor(Get.find<ComposerRepository>()));
|
||||||
|
Get.lazyPut(() => TransformHtmlEmailContentInteractor(Get.find<EmailRepository>()));
|
||||||
|
|
||||||
IdentityInteractorsBindings().dependencies();
|
IdentityInteractorsBindings().dependencies();
|
||||||
}
|
}
|
||||||
@@ -184,6 +186,7 @@ class ComposerBindings extends BaseBindings {
|
|||||||
Get.find<SaveComposerCacheOnWebInteractor>(),
|
Get.find<SaveComposerCacheOnWebInteractor>(),
|
||||||
Get.find<RichTextWebController>(),
|
Get.find<RichTextWebController>(),
|
||||||
Get.find<DownloadImageAsBase64Interactor>(),
|
Get.find<DownloadImageAsBase64Interactor>(),
|
||||||
|
Get.find<TransformHtmlEmailContentInteractor>(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,8 +48,11 @@ import 'package:tmail_ui_user/features/composer/presentation/extensions/email_ac
|
|||||||
import 'package:tmail_ui_user/features/composer/presentation/model/image_source.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/model/image_source.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/model/inline_image.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/model/inline_image.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';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
|
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/state/transform_html_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/domain/usecases/get_email_content_interactor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/usecases/transform_html_email_content_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart';
|
import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_composer_cache_on_web_interactor.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_composer_cache_on_web_interactor.dart';
|
||||||
@@ -60,6 +63,7 @@ import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_id
|
|||||||
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/identity_extension.dart';
|
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/identity_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/network_connection/presentation/network_connection_controller.dart';
|
import 'package:tmail_ui_user/features/network_connection/presentation/network_connection_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/sending_queue/domain/extensions/sending_email_extension.dart';
|
import 'package:tmail_ui_user/features/sending_queue/domain/extensions/sending_email_extension.dart';
|
||||||
|
import 'package:tmail_ui_user/features/sending_queue/domain/model/sending_email.dart';
|
||||||
import 'package:tmail_ui_user/features/sending_queue/presentation/model/sending_email_action_type.dart';
|
import 'package:tmail_ui_user/features/sending_queue/presentation/model/sending_email_action_type.dart';
|
||||||
import 'package:tmail_ui_user/features/sending_queue/presentation/model/sending_email_arguments.dart';
|
import 'package:tmail_ui_user/features/sending_queue/presentation/model/sending_email_arguments.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/domain/model/upload_task_id.dart';
|
import 'package:tmail_ui_user/features/upload/domain/model/upload_task_id.dart';
|
||||||
@@ -95,7 +99,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 emailContentsViewState = Rxn<Either<Failure, Success>>();
|
||||||
final hasRequestReadReceipt = false.obs;
|
final hasRequestReadReceipt = false.obs;
|
||||||
|
|
||||||
final LocalFilePickerInteractor _localFilePickerInteractor;
|
final LocalFilePickerInteractor _localFilePickerInteractor;
|
||||||
@@ -109,6 +113,7 @@ class ComposerController extends BaseController {
|
|||||||
final SaveComposerCacheOnWebInteractor _saveComposerCacheOnWebInteractor;
|
final SaveComposerCacheOnWebInteractor _saveComposerCacheOnWebInteractor;
|
||||||
final RichTextWebController richTextWebController;
|
final RichTextWebController richTextWebController;
|
||||||
final DownloadImageAsBase64Interactor _downloadImageAsBase64Interactor;
|
final DownloadImageAsBase64Interactor _downloadImageAsBase64Interactor;
|
||||||
|
final TransformHtmlEmailContentInteractor _transformHtmlEmailContentInteractor;
|
||||||
|
|
||||||
GetAutoCompleteWithDeviceContactInteractor? _getAutoCompleteWithDeviceContactInteractor;
|
GetAutoCompleteWithDeviceContactInteractor? _getAutoCompleteWithDeviceContactInteractor;
|
||||||
GetAutoCompleteInteractor? _getAutoCompleteInteractor;
|
GetAutoCompleteInteractor? _getAutoCompleteInteractor;
|
||||||
@@ -145,7 +150,6 @@ class ComposerController extends BaseController {
|
|||||||
List<Attachment> initialAttachments = <Attachment>[];
|
List<Attachment> initialAttachments = <Attachment>[];
|
||||||
String? _textEditorWeb;
|
String? _textEditorWeb;
|
||||||
String? _initTextEditor;
|
String? _initTextEditor;
|
||||||
String? _emailContents;
|
|
||||||
double? maxWithEditor;
|
double? maxWithEditor;
|
||||||
late Worker uploadInlineImageWorker;
|
late Worker uploadInlineImageWorker;
|
||||||
|
|
||||||
@@ -201,6 +205,7 @@ class ComposerController extends BaseController {
|
|||||||
this._saveComposerCacheOnWebInteractor,
|
this._saveComposerCacheOnWebInteractor,
|
||||||
this.richTextWebController,
|
this.richTextWebController,
|
||||||
this._downloadImageAsBase64Interactor,
|
this._downloadImageAsBase64Interactor,
|
||||||
|
this._transformHtmlEmailContentInteractor,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -264,7 +269,9 @@ class ComposerController extends BaseController {
|
|||||||
@override
|
@override
|
||||||
void handleSuccessViewState(Success success) {
|
void handleSuccessViewState(Success success) {
|
||||||
super.handleSuccessViewState(success);
|
super.handleSuccessViewState(success);
|
||||||
if (success is GetEmailContentLoading) {
|
if (success is GetEmailContentLoading ||
|
||||||
|
success is TransformHtmlEmailContentLoading ||
|
||||||
|
success is TransformHtmlEmailContentSuccess) {
|
||||||
emailContentsViewState.value = Right(success);
|
emailContentsViewState.value = Right(success);
|
||||||
} else if (success is LocalFilePickerSuccess) {
|
} else if (success is LocalFilePickerSuccess) {
|
||||||
_pickFileSuccess(success);
|
_pickFileSuccess(success);
|
||||||
@@ -299,7 +306,8 @@ class ComposerController extends BaseController {
|
|||||||
super.handleFailureViewState(failure);
|
super.handleFailureViewState(failure);
|
||||||
if (failure is LocalFilePickerFailure || failure is LocalFilePickerCancel) {
|
if (failure is LocalFilePickerFailure || failure is LocalFilePickerCancel) {
|
||||||
_pickFileFailure(failure);
|
_pickFileFailure(failure);
|
||||||
} else if (failure is GetEmailContentFailure) {
|
} else if (failure is GetEmailContentFailure ||
|
||||||
|
failure is TransformHtmlEmailContentFailure) {
|
||||||
emailContentsViewState.value = Left(failure);
|
emailContentsViewState.value = Left(failure);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -387,39 +395,105 @@ class ComposerController extends BaseController {
|
|||||||
: Get.arguments;
|
: Get.arguments;
|
||||||
if (arguments is ComposerArguments) {
|
if (arguments is ComposerArguments) {
|
||||||
composerArguments.value = arguments;
|
composerArguments.value = arguments;
|
||||||
|
|
||||||
injectAutoCompleteBindings(
|
injectAutoCompleteBindings(
|
||||||
mailboxDashBoardController.sessionCurrent,
|
mailboxDashBoardController.sessionCurrent,
|
||||||
mailboxDashBoardController.accountId.value);
|
mailboxDashBoardController.accountId.value
|
||||||
|
);
|
||||||
|
|
||||||
if (arguments.emailActionType == EmailActionType.edit) {
|
switch(arguments.emailActionType) {
|
||||||
_getEmailContentAction(arguments);
|
case EmailActionType.editDraft:
|
||||||
|
_initEmailAddress(
|
||||||
|
presentationEmail: arguments.presentationEmail!,
|
||||||
|
actionType: EmailActionType.editDraft
|
||||||
|
);
|
||||||
|
_initSubjectEmail(
|
||||||
|
presentationEmail: arguments.presentationEmail!,
|
||||||
|
actionType: EmailActionType.editDraft
|
||||||
|
);
|
||||||
|
_getEmailContentFromEmailId(
|
||||||
|
emailId: arguments.presentationEmail!.id!,
|
||||||
|
isDraftEmail: arguments.presentationEmail!.isDraft
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case EmailActionType.editSendingEmail:
|
||||||
|
_initEmailAddress(
|
||||||
|
presentationEmail: arguments.sendingEmail!.presentationEmail,
|
||||||
|
actionType: EmailActionType.editSendingEmail
|
||||||
|
);
|
||||||
|
_initSubjectEmail(
|
||||||
|
presentationEmail: arguments.sendingEmail!.presentationEmail,
|
||||||
|
actionType: EmailActionType.editSendingEmail
|
||||||
|
);
|
||||||
|
_getEmailContentFromSendingEmail(arguments.sendingEmail!);
|
||||||
|
break;
|
||||||
|
case EmailActionType.composeFromContentShared:
|
||||||
|
_getEmailContentFromContentShared(arguments.emailContents!);
|
||||||
|
break;
|
||||||
|
case EmailActionType.composeFromFileShared:
|
||||||
|
_addAttachmentFromFileShare(arguments.listSharedMediaFile!);
|
||||||
|
break;
|
||||||
|
case EmailActionType.composeFromEmailAddress:
|
||||||
|
listToEmailAddress.add(arguments.emailAddress!);
|
||||||
|
isInitialRecipient.value = true;
|
||||||
|
toAddressExpandMode.value = ExpandMode.COLLAPSE;
|
||||||
|
_updateStatusEmailSendButton();
|
||||||
|
break;
|
||||||
|
case EmailActionType.reply:
|
||||||
|
case EmailActionType.replyAll:
|
||||||
|
_initEmailAddress(
|
||||||
|
presentationEmail: arguments.presentationEmail!,
|
||||||
|
actionType: arguments.emailActionType,
|
||||||
|
mailboxRole: arguments.presentationEmail!.mailboxContain?.role ?? mailboxDashBoardController.selectedMailbox.value?.role
|
||||||
|
);
|
||||||
|
_initSubjectEmail(
|
||||||
|
presentationEmail: arguments.presentationEmail!,
|
||||||
|
actionType: arguments.emailActionType
|
||||||
|
);
|
||||||
|
_transformHtmlEmailContent(arguments.emailContents);
|
||||||
|
break;
|
||||||
|
case EmailActionType.forward:
|
||||||
|
_initSubjectEmail(
|
||||||
|
presentationEmail: arguments.presentationEmail!,
|
||||||
|
actionType: arguments.emailActionType
|
||||||
|
);
|
||||||
|
_initAttachments(arguments.attachments ?? []);
|
||||||
|
_transformHtmlEmailContent(arguments.emailContents);
|
||||||
|
break;
|
||||||
|
case EmailActionType.reopenComposerBrowser:
|
||||||
|
_initEmailAddress(
|
||||||
|
presentationEmail: arguments.presentationEmail!,
|
||||||
|
actionType: EmailActionType.reopenComposerBrowser
|
||||||
|
);
|
||||||
|
_initSubjectEmail(
|
||||||
|
presentationEmail: arguments.presentationEmail!,
|
||||||
|
actionType: EmailActionType.reopenComposerBrowser
|
||||||
|
);
|
||||||
|
_initAttachments(arguments.attachments ?? []);
|
||||||
|
_getEmailContentFromSessionStorageBrowser(arguments.emailContents!);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_initEmailAddress(arguments);
|
|
||||||
_initSubjectEmail(arguments);
|
|
||||||
_initAttachments(arguments);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_autoFocusFieldWhenLauncher();
|
_autoFocusFieldWhenLauncher();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initSubjectEmail(ComposerArguments arguments) {
|
void _initSubjectEmail({
|
||||||
if (currentContext != null) {
|
required PresentationEmail presentationEmail,
|
||||||
final subjectEmail = arguments.presentationEmail?.getEmailTitle().trim() ?? '';
|
required EmailActionType actionType
|
||||||
final newSubject = arguments.emailActionType.getSubjectComposer(currentContext!, subjectEmail);
|
}) {
|
||||||
setSubjectEmail(newSubject);
|
final subjectEmail = presentationEmail.getEmailTitle().trim();
|
||||||
subjectEmailInputController.text = newSubject;
|
final newSubject = actionType.getSubjectComposer(currentContext, subjectEmail);
|
||||||
}
|
setSubjectEmail(newSubject);
|
||||||
|
subjectEmailInputController.text = newSubject;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initAttachments(ComposerArguments arguments) {
|
void _initAttachments(List<Attachment> attachments) {
|
||||||
if (arguments.attachments?.isNotEmpty == true) {
|
if (attachments.isNotEmpty) {
|
||||||
initialAttachments = arguments.attachments!;
|
initialAttachments = attachments;
|
||||||
uploadController.initializeUploadAttachments(
|
uploadController.initializeUploadAttachments(attachments.listAttachmentsDisplayedOutSide);
|
||||||
arguments.attachments!.listAttachmentsDisplayedOutSide);
|
|
||||||
}
|
|
||||||
if (PlatformInfo.isWeb) {
|
|
||||||
expandModeAttachments.value = ExpandMode.EXPAND;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,7 +512,6 @@ class ComposerController extends BaseController {
|
|||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
if (listIdentities.isNotEmpty) {
|
if (listIdentities.isNotEmpty) {
|
||||||
_initTextEditor = null;
|
|
||||||
await selectIdentity(listIdentities.first);
|
await selectIdentity(listIdentities.first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -446,113 +519,49 @@ class ComposerController extends BaseController {
|
|||||||
_autoFocusFieldWhenLauncher();
|
_autoFocusFieldWhenLauncher();
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _getHeaderEmailQuoted(BuildContext context, ComposerArguments arguments) {
|
void _initEmailAddress({
|
||||||
final presentationEmail = arguments.presentationEmail;
|
required PresentationEmail presentationEmail,
|
||||||
if (presentationEmail != null) {
|
required EmailActionType actionType,
|
||||||
final locale = Localizations.localeOf(context).toLanguageTag();
|
Role? mailboxRole,
|
||||||
log('ComposerController::_getHeaderEmailQuoted(): emailActionType: ${arguments.emailActionType}');
|
}) {
|
||||||
switch(arguments.emailActionType) {
|
final recipients = presentationEmail.generateRecipientsEmailAddressForComposer(
|
||||||
case EmailActionType.reply:
|
emailActionType: actionType,
|
||||||
case EmailActionType.replyAll:
|
mailboxRole: mailboxRole
|
||||||
final receivedAt = presentationEmail.receivedAt;
|
);
|
||||||
final emailAddress = presentationEmail.from.listEmailAddressToString(isFullEmailAddress: true);
|
log('ComposerController::_initEmailAddress:recipients: $recipients');
|
||||||
return AppLocalizations.of(context).header_email_quoted(
|
|
||||||
receivedAt.formatDateToLocal(pattern: 'MMM d, y h:mm a', locale: locale),
|
|
||||||
emailAddress);
|
|
||||||
case EmailActionType.forward:
|
|
||||||
var headerQuoted = '------- ${AppLocalizations.of(context).forwarded_message} -------'.addNewLineTag();
|
|
||||||
|
|
||||||
final subject = presentationEmail.subject ?? '';
|
|
||||||
final receivedAt = presentationEmail.receivedAt;
|
|
||||||
final fromEmailAddress = presentationEmail.from.listEmailAddressToString(isFullEmailAddress: true);
|
|
||||||
final toEmailAddress = presentationEmail.to.listEmailAddressToString(isFullEmailAddress: true);
|
|
||||||
final ccEmailAddress = presentationEmail.cc.listEmailAddressToString(isFullEmailAddress: true);
|
|
||||||
final bccEmailAddress = presentationEmail.bcc.listEmailAddressToString(isFullEmailAddress: true);
|
|
||||||
|
|
||||||
if (subject.isNotEmpty) {
|
|
||||||
headerQuoted = headerQuoted
|
|
||||||
.append('${AppLocalizations.of(context).subject_email}: ')
|
|
||||||
.append(subject)
|
|
||||||
.addNewLineTag();
|
|
||||||
}
|
|
||||||
if (receivedAt != null) {
|
|
||||||
headerQuoted = headerQuoted
|
|
||||||
.append('${AppLocalizations.of(context).date}: ')
|
|
||||||
.append(receivedAt.formatDateToLocal(pattern: 'MMM d, y h:mm a', locale: locale))
|
|
||||||
.addNewLineTag();
|
|
||||||
}
|
|
||||||
if (fromEmailAddress.isNotEmpty) {
|
|
||||||
headerQuoted = headerQuoted
|
|
||||||
.append('${AppLocalizations.of(context).from_email_address_prefix}: ')
|
|
||||||
.append(fromEmailAddress)
|
|
||||||
.addNewLineTag();
|
|
||||||
}
|
|
||||||
if (toEmailAddress.isNotEmpty) {
|
|
||||||
headerQuoted = headerQuoted
|
|
||||||
.append('${AppLocalizations.of(context).to_email_address_prefix}: ')
|
|
||||||
.append(toEmailAddress)
|
|
||||||
.addNewLineTag();
|
|
||||||
}
|
|
||||||
if (ccEmailAddress.isNotEmpty) {
|
|
||||||
headerQuoted = headerQuoted
|
|
||||||
.append('${AppLocalizations.of(context).cc_email_address_prefix}: ')
|
|
||||||
.append(ccEmailAddress)
|
|
||||||
.addNewLineTag();
|
|
||||||
}
|
|
||||||
if (bccEmailAddress.isNotEmpty) {
|
|
||||||
headerQuoted = headerQuoted
|
|
||||||
.append('${AppLocalizations.of(context).bcc_email_address_prefix}: ')
|
|
||||||
.append(bccEmailAddress)
|
|
||||||
.addNewLineTag();
|
|
||||||
}
|
|
||||||
|
|
||||||
return headerQuoted;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _initEmailAddress(ComposerArguments arguments) {
|
|
||||||
final userProfile = mailboxDashBoardController.userProfile.value;
|
final userProfile = mailboxDashBoardController.userProfile.value;
|
||||||
if (arguments.presentationEmail != null && userProfile != null) {
|
if (userProfile != null) {
|
||||||
final userEmailAddress = EmailAddress(null, userProfile.email);
|
final isSender = presentationEmail.from.asList().every((element) => element.email == userProfile.email);
|
||||||
final recipients = arguments.presentationEmail!.generateRecipientsEmailAddressForComposer(
|
|
||||||
arguments.emailActionType,
|
|
||||||
arguments.mailboxRole);
|
|
||||||
|
|
||||||
final isSender = arguments.presentationEmail!.from.asList().every((element) => element.email == userEmailAddress.email);
|
|
||||||
|
|
||||||
if (isSender) {
|
if (isSender) {
|
||||||
listToEmailAddress = List.from(recipients.value1);
|
listToEmailAddress = List.from(recipients.value1.toSet());
|
||||||
listCcEmailAddress = List.from(recipients.value2);
|
listCcEmailAddress = List.from(recipients.value2.toSet());
|
||||||
listBccEmailAddress = List.from(recipients.value3);
|
listBccEmailAddress = List.from(recipients.value3.toSet());
|
||||||
} else {
|
} else {
|
||||||
listToEmailAddress = List.from(recipients.value1.toSet().filterEmailAddress(userEmailAddress));
|
listToEmailAddress = List.from(recipients.value1.toSet().filterEmailAddress(userProfile.email));
|
||||||
listCcEmailAddress = List.from(recipients.value2.toSet().filterEmailAddress(userEmailAddress));
|
listCcEmailAddress = List.from(recipients.value2.toSet().filterEmailAddress(userProfile.email));
|
||||||
listBccEmailAddress = List.from(recipients.value3.toSet().filterEmailAddress(userEmailAddress));
|
listBccEmailAddress = List.from(recipients.value3.toSet().filterEmailAddress(userProfile.email));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
listToEmailAddress = List.from(recipients.value1.toSet());
|
||||||
|
listCcEmailAddress = List.from(recipients.value2.toSet());
|
||||||
|
listBccEmailAddress = List.from(recipients.value3.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
if (listToEmailAddress.isNotEmpty || listCcEmailAddress.isNotEmpty || listBccEmailAddress.isNotEmpty) {
|
if (listToEmailAddress.isNotEmpty || listCcEmailAddress.isNotEmpty || listBccEmailAddress.isNotEmpty) {
|
||||||
isInitialRecipient.value = true;
|
|
||||||
toAddressExpandMode.value = ExpandMode.COLLAPSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listCcEmailAddress.isNotEmpty) {
|
|
||||||
listEmailAddressType.add(PrefixEmailAddress.cc);
|
|
||||||
ccAddressExpandMode.value = ExpandMode.COLLAPSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listBccEmailAddress.isNotEmpty) {
|
|
||||||
listEmailAddressType.add(PrefixEmailAddress.bcc);
|
|
||||||
bccAddressExpandMode.value = ExpandMode.COLLAPSE;
|
|
||||||
}
|
|
||||||
} else if (arguments.emailAddress != null) {
|
|
||||||
listToEmailAddress.add(arguments.emailAddress!);
|
|
||||||
isInitialRecipient.value = true;
|
isInitialRecipient.value = true;
|
||||||
toAddressExpandMode.value = ExpandMode.COLLAPSE;
|
toAddressExpandMode.value = ExpandMode.COLLAPSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (listCcEmailAddress.isNotEmpty) {
|
||||||
|
listEmailAddressType.add(PrefixEmailAddress.cc);
|
||||||
|
ccAddressExpandMode.value = ExpandMode.COLLAPSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listBccEmailAddress.isNotEmpty) {
|
||||||
|
listEmailAddressType.add(PrefixEmailAddress.bcc);
|
||||||
|
bccAddressExpandMode.value = ExpandMode.COLLAPSE;
|
||||||
|
}
|
||||||
|
|
||||||
_updateStatusEmailSendButton();
|
_updateStatusEmailSendButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,13 +595,21 @@ class ComposerController extends BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String getEmailContentQuotedAsHtml(BuildContext context, ComposerArguments arguments) {
|
String getEmailContentQuotedAsHtml({
|
||||||
final headerEmailQuoted = _getHeaderEmailQuoted(context, arguments);
|
required BuildContext context,
|
||||||
|
required String emailContent,
|
||||||
|
required EmailActionType emailActionType,
|
||||||
|
required PresentationEmail presentationEmail,
|
||||||
|
}) {
|
||||||
|
final headerEmailQuoted = emailActionType.getHeaderEmailQuoted(
|
||||||
|
context: context,
|
||||||
|
presentationEmail: presentationEmail
|
||||||
|
);
|
||||||
log('ComposerController::getEmailContentQuotedAsHtml(): headerEmailQuoted: $headerEmailQuoted');
|
log('ComposerController::getEmailContentQuotedAsHtml(): headerEmailQuoted: $headerEmailQuoted');
|
||||||
final headerEmailQuotedAsHtml = headerEmailQuoted != null ? headerEmailQuoted.addBlockTag('cite') : '';
|
final headerEmailQuotedAsHtml = headerEmailQuoted != null
|
||||||
|
? headerEmailQuoted.addCiteTag()
|
||||||
final trustAsHtml = arguments.emailContents ?? '';
|
: '';
|
||||||
final emailQuotedHtml = '${HtmlExtension.editorStartTags}$headerEmailQuotedAsHtml${trustAsHtml.addBlockQuoteTag()}';
|
final emailQuotedHtml = '${HtmlExtension.editorStartTags}$headerEmailQuotedAsHtml${emailContent.addBlockQuoteTag()}';
|
||||||
|
|
||||||
return emailQuotedHtml;
|
return emailQuotedHtml;
|
||||||
}
|
}
|
||||||
@@ -789,13 +806,13 @@ class ComposerController extends BaseController {
|
|||||||
|
|
||||||
if (arguments != null && accountId != null && userProfile != null && session != null) {
|
if (arguments != null && accountId != null && userProfile != null && session != null) {
|
||||||
final createdEmail = await _generateEmail(context, userProfile, outboxMailboxId: outboxMailboxId);
|
final createdEmail = await _generateEmail(context, userProfile, outboxMailboxId: outboxMailboxId);
|
||||||
final emailRequest = arguments.sendingEmail != null
|
final emailRequest = arguments.emailActionType == EmailActionType.editSendingEmail
|
||||||
? arguments.sendingEmail!.toEmailRequest(newEmail: createdEmail)
|
? arguments.sendingEmail!.toEmailRequest(newEmail: createdEmail)
|
||||||
: EmailRequest(
|
: EmailRequest(
|
||||||
email: createdEmail,
|
email: createdEmail,
|
||||||
sentMailboxId: sentMailboxId,
|
sentMailboxId: sentMailboxId,
|
||||||
identityId: identitySelected.value?.id,
|
identityId: identitySelected.value?.id,
|
||||||
emailIdDestroyed: arguments.emailActionType == EmailActionType.edit
|
emailIdDestroyed: arguments.emailActionType == EmailActionType.editDraft
|
||||||
? arguments.presentationEmail?.id
|
? arguments.presentationEmail?.id
|
||||||
: null,
|
: null,
|
||||||
emailIdAnsweredOrForwarded: arguments.presentationEmail?.id,
|
emailIdAnsweredOrForwarded: arguments.presentationEmail?.id,
|
||||||
@@ -952,36 +969,40 @@ class ComposerController extends BaseController {
|
|||||||
uploadController.deleteFileUploaded(uploadId);
|
uploadController.deleteFileUploaded(uploadId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _isEmailChanged(
|
Future<bool> _isEmailChanged({
|
||||||
BuildContext context,
|
required BuildContext context,
|
||||||
ComposerArguments arguments,
|
required EmailActionType emailActionType,
|
||||||
) async {
|
PresentationEmail? presentationEmail,
|
||||||
|
Role? mailboxRole,
|
||||||
|
}) async {
|
||||||
final newEmailBody = await _getEmailBodyText(context, changedEmail: true);
|
final newEmailBody = await _getEmailBodyText(context, changedEmail: true);
|
||||||
log('ComposerController::_isEmailChanged(): newEmailBody: $newEmailBody');
|
log('ComposerController::_isEmailChanged(): newEmailBody: $newEmailBody');
|
||||||
var oldEmailBody = _initTextEditor ?? '';
|
final oldEmailBody = _initTextEditor ?? '';
|
||||||
log('ComposerController::_isEmailChanged(): oldEmailBody: $oldEmailBody');
|
log('ComposerController::_isEmailChanged(): oldEmailBody: $oldEmailBody');
|
||||||
final isEmailBodyChanged = !oldEmailBody.trim().isSame(newEmailBody.trim());
|
final isEmailBodyChanged = !oldEmailBody.trim().isSame(newEmailBody.trim());
|
||||||
log('ComposerController::_isEmailChanged(): isEmailBodyChanged: $isEmailBodyChanged');
|
log('ComposerController::_isEmailChanged(): isEmailBodyChanged: $isEmailBodyChanged');
|
||||||
|
|
||||||
final newEmailSubject = subjectEmail.value ?? '';
|
final newEmailSubject = subjectEmail.value ?? '';
|
||||||
final titleEmail = arguments.presentationEmail?.getEmailTitle().trim() ?? '';
|
final oldEmailSubject = emailActionType == EmailActionType.editDraft
|
||||||
final oldEmailSubject = arguments.emailActionType == EmailActionType.edit ? titleEmail : '';
|
? presentationEmail?.getEmailTitle().trim() ?? ''
|
||||||
|
: '';
|
||||||
final isEmailSubjectChanged = !oldEmailSubject.trim().isSame(newEmailSubject.trim());
|
final isEmailSubjectChanged = !oldEmailSubject.trim().isSame(newEmailSubject.trim());
|
||||||
|
|
||||||
final recipients = arguments.presentationEmail
|
final recipients = presentationEmail
|
||||||
?.generateRecipientsEmailAddressForComposer(arguments.emailActionType, arguments.mailboxRole)
|
?.generateRecipientsEmailAddressForComposer(
|
||||||
?? const Tuple3(<EmailAddress>[], <EmailAddress>[], <EmailAddress>[]);
|
emailActionType: emailActionType,
|
||||||
|
mailboxRole: mailboxRole
|
||||||
|
) ?? const Tuple3(<EmailAddress>[], <EmailAddress>[], <EmailAddress>[]);
|
||||||
|
|
||||||
final newToEmailAddress = listToEmailAddress;
|
final newToEmailAddress = listToEmailAddress;
|
||||||
final oldToEmailAddress = arguments.emailActionType == EmailActionType.edit ? recipients.value1 : [];
|
final oldToEmailAddress = emailActionType == EmailActionType.editDraft ? recipients.value1 : [];
|
||||||
final isToEmailAddressChanged = !oldToEmailAddress.isSame(newToEmailAddress);
|
final isToEmailAddressChanged = !oldToEmailAddress.isSame(newToEmailAddress);
|
||||||
|
|
||||||
final newCcEmailAddress = listCcEmailAddress;
|
final newCcEmailAddress = listCcEmailAddress;
|
||||||
final oldCcEmailAddress = arguments.emailActionType == EmailActionType.edit ? recipients.value2 : [];
|
final oldCcEmailAddress = emailActionType == EmailActionType.editDraft ? recipients.value2 : [];
|
||||||
final isCcEmailAddressChanged = !oldCcEmailAddress.isSame(newCcEmailAddress);
|
final isCcEmailAddressChanged = !oldCcEmailAddress.isSame(newCcEmailAddress);
|
||||||
|
|
||||||
final newBccEmailAddress = listBccEmailAddress;
|
final newBccEmailAddress = listBccEmailAddress;
|
||||||
final oldBccEmailAddress = arguments.emailActionType == EmailActionType.edit ? recipients.value3 : [];
|
final oldBccEmailAddress = emailActionType == EmailActionType.editDraft ? recipients.value3 : [];
|
||||||
final isBccEmailAddressChanged = !oldBccEmailAddress.isSame(newBccEmailAddress);
|
final isBccEmailAddressChanged = !oldBccEmailAddress.isSame(newBccEmailAddress);
|
||||||
|
|
||||||
final isAttachmentsChanged = !initialAttachments.isSame(uploadController.attachmentsUploaded.toList());
|
final isAttachmentsChanged = !initialAttachments.isSame(uploadController.attachmentsUploaded.toList());
|
||||||
@@ -999,36 +1020,60 @@ class ComposerController extends BaseController {
|
|||||||
clearFocusEditor(context);
|
clearFocusEditor(context);
|
||||||
|
|
||||||
final arguments = composerArguments.value;
|
final arguments = composerArguments.value;
|
||||||
final draftMailboxId = mailboxDashBoardController.mapDefaultMailboxIdByRole[PresentationMailbox.roleDrafts];
|
|
||||||
final userProfile = mailboxDashBoardController.userProfile.value;
|
final userProfile = mailboxDashBoardController.userProfile.value;
|
||||||
final accountId = mailboxDashBoardController.accountId.value;
|
final accountId = mailboxDashBoardController.accountId.value;
|
||||||
final session = mailboxDashBoardController.sessionCurrent;
|
final session = mailboxDashBoardController.sessionCurrent;
|
||||||
|
|
||||||
if (arguments != null && userProfile != null && accountId != null && session != null) {
|
if (arguments == null ||
|
||||||
final isChanged = await _isEmailChanged(context, arguments);
|
userProfile == null ||
|
||||||
if (isChanged && context.mounted) {
|
session == null ||
|
||||||
final newEmail = await _generateEmail(
|
accountId == null
|
||||||
context,
|
) {
|
||||||
userProfile,
|
if (PlatformInfo.isWeb) {
|
||||||
asDrafts: true,
|
mailboxDashBoardController.closeComposerOverlay();
|
||||||
draftMailboxId: draftMailboxId);
|
} else {
|
||||||
final oldEmail = arguments.presentationEmail;
|
if (canPop) popBack();
|
||||||
|
|
||||||
if (arguments.emailActionType == EmailActionType.edit && oldEmail != null && oldEmail.id != null) {
|
|
||||||
mailboxDashBoardController.consumeState(
|
|
||||||
_updateEmailDraftsInteractor.execute(
|
|
||||||
session,
|
|
||||||
accountId,
|
|
||||||
newEmail,
|
|
||||||
oldEmail.id!
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
mailboxDashBoardController.consumeState(_saveEmailAsDraftsInteractor.execute(session, accountId, newEmail));
|
|
||||||
}
|
|
||||||
|
|
||||||
uploadController.clearInlineFileUploaded();
|
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final isChanged = await _isEmailChanged(
|
||||||
|
context: context,
|
||||||
|
emailActionType: arguments.emailActionType,
|
||||||
|
presentationEmail: arguments.presentationEmail,
|
||||||
|
mailboxRole: arguments.mailboxRole
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isChanged && context.mounted) {
|
||||||
|
final draftMailboxId = mailboxDashBoardController.mapDefaultMailboxIdByRole[PresentationMailbox.roleDrafts];
|
||||||
|
|
||||||
|
final newEmail = await _generateEmail(
|
||||||
|
context,
|
||||||
|
userProfile,
|
||||||
|
asDrafts: true,
|
||||||
|
draftMailboxId: draftMailboxId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (arguments.emailActionType == EmailActionType.editDraft) {
|
||||||
|
mailboxDashBoardController.consumeState(
|
||||||
|
_updateEmailDraftsInteractor.execute(
|
||||||
|
session,
|
||||||
|
accountId,
|
||||||
|
newEmail,
|
||||||
|
arguments.presentationEmail!.id!
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
mailboxDashBoardController.consumeState(
|
||||||
|
_saveEmailAsDraftsInteractor.execute(
|
||||||
|
session,
|
||||||
|
accountId,
|
||||||
|
newEmail
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadController.clearInlineFileUploaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PlatformInfo.isWeb) {
|
if (PlatformInfo.isWeb) {
|
||||||
@@ -1087,90 +1132,109 @@ class ComposerController extends BaseController {
|
|||||||
return listFileInfo;
|
return listFileInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _getEmailContentAction(ComposerArguments arguments) async {
|
void _addAttachmentFromFileShare(List<SharedMediaFile> listSharedMediaFile) {
|
||||||
|
final listImageSharedMediaFile = listSharedMediaFile.where((element) => element.type == SharedMediaType.IMAGE);
|
||||||
final listSharedMediaFile = arguments.listSharedMediaFile;
|
final listFileAttachmentSharedMediaFile = listSharedMediaFile.where((element) => element.type != SharedMediaType.IMAGE);
|
||||||
if (listSharedMediaFile != null && listSharedMediaFile.isNotEmpty) {
|
if (listImageSharedMediaFile.isNotEmpty) {
|
||||||
final listImageSharedMediaFile = listSharedMediaFile.where((element) => element.type == SharedMediaType.IMAGE);
|
final listInlineImage = covertListSharedMediaFileToInlineImage(listSharedMediaFile);
|
||||||
final listFileAttachmentSharedMediaFile = listSharedMediaFile.where((element) => element.type != SharedMediaType.IMAGE);
|
for (var e in listInlineImage) {
|
||||||
if (listImageSharedMediaFile.isNotEmpty) {
|
_uploadInlineAttachmentsAction(e.fileInfo!);
|
||||||
final listInlineImage = covertListSharedMediaFileToInlineImage(arguments.listSharedMediaFile!);
|
|
||||||
for (var e in listInlineImage) {
|
|
||||||
_uploadInlineAttachmentsAction(e.fileInfo!);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (listFileAttachmentSharedMediaFile.isNotEmpty) {
|
}
|
||||||
final listFile = covertListSharedMediaFileToFileInfo(arguments.listSharedMediaFile!);
|
if (listFileAttachmentSharedMediaFile.isNotEmpty) {
|
||||||
if (uploadController.hasEnoughMaxAttachmentSize(listFiles: listFile)) {
|
final listFile = covertListSharedMediaFileToFileInfo(listSharedMediaFile);
|
||||||
_uploadAttachmentsAction(listFile);
|
if (uploadController.hasEnoughMaxAttachmentSize(listFiles: listFile)) {
|
||||||
} else {
|
_uploadAttachmentsAction(listFile);
|
||||||
if (currentContext != null) {
|
} else {
|
||||||
showConfirmDialogAction(
|
if (currentContext != null) {
|
||||||
currentContext!,
|
showConfirmDialogAction(
|
||||||
AppLocalizations.of(currentContext!).message_dialog_upload_attachments_exceeds_maximum_size(
|
currentContext!,
|
||||||
filesize(mailboxDashBoardController.maxSizeAttachmentsPerEmail?.value ?? 0, 0)),
|
AppLocalizations.of(currentContext!).message_dialog_upload_attachments_exceeds_maximum_size(
|
||||||
AppLocalizations.of(currentContext!).got_it,
|
filesize(mailboxDashBoardController.maxSizeAttachmentsPerEmail?.value ?? 0, 0)),
|
||||||
onConfirmAction: () => {},
|
AppLocalizations.of(currentContext!).got_it,
|
||||||
title: AppLocalizations.of(currentContext!).maximum_files_size,
|
title: AppLocalizations.of(currentContext!).maximum_files_size,
|
||||||
hasCancelButton: false,
|
hasCancelButton: false,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (arguments.emailContents != null && arguments.emailContents!.isNotEmpty && arguments.sendingEmail != null) {
|
void _getEmailContentFromSendingEmail(SendingEmail sendingEmail) {
|
||||||
_emailContents = arguments.emailContents;
|
consumeState(Stream.value(
|
||||||
emailContentsViewState.value = Right(
|
Right(GetEmailContentSuccess(
|
||||||
GetEmailContentSuccess(
|
htmlEmailContent: sendingEmail.presentationEmail.emailContentList.asHtmlString,
|
||||||
emailContent: _emailContents!,
|
attachments: sendingEmail.email.allAttachments,
|
||||||
emailContentDisplayed: '',
|
emailCurrent: sendingEmail.email
|
||||||
attachments: [],
|
))
|
||||||
emailCurrent: arguments.presentationEmail?.toEmail()
|
));
|
||||||
)
|
}
|
||||||
);
|
|
||||||
} else {
|
void _getEmailContentFromSessionStorageBrowser(String content) {
|
||||||
final session = mailboxDashBoardController.sessionCurrent;
|
consumeState(Stream.value(
|
||||||
final baseDownloadUrl = mailboxDashBoardController.sessionCurrent?.getDownloadUrl(jmapUrl: _dynamicUrlInterceptors.jmapUrl);
|
Right(GetEmailContentSuccess(
|
||||||
final accountId = mailboxDashBoardController.sessionCurrent?.accounts.keys.first;
|
htmlEmailContent: content,
|
||||||
final emailId = arguments.presentationEmail?.id;
|
attachments: [],
|
||||||
if (session != null && emailId != null && baseDownloadUrl != null && accountId != null) {
|
))
|
||||||
consumeState(_getEmailContentInteractor.execute(
|
));
|
||||||
session,
|
}
|
||||||
accountId,
|
|
||||||
emailId,
|
void _getEmailContentFromContentShared(String content) {
|
||||||
baseDownloadUrl,
|
consumeState(Stream.value(
|
||||||
composeEmail: true,
|
Right(GetEmailContentSuccess(
|
||||||
draftsEmail: arguments.presentationEmail?.isDraft ?? false
|
htmlEmailContent: content,
|
||||||
));
|
attachments: [],
|
||||||
|
))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _getEmailContentFromEmailId({required EmailId emailId, bool isDraftEmail = false}) {
|
||||||
|
final session = mailboxDashBoardController.sessionCurrent;
|
||||||
|
final accountId = mailboxDashBoardController.accountId.value;
|
||||||
|
if (session != null && accountId != null) {
|
||||||
|
TransformConfiguration transformConfiguration = TransformConfiguration.standardConfiguration;
|
||||||
|
if (isDraftEmail) {
|
||||||
|
transformConfiguration = TransformConfiguration.forDraftsEmail();
|
||||||
|
} else if (PlatformInfo.isWeb) {
|
||||||
|
transformConfiguration = TransformConfiguration.forComposeEmailPlatformWeb();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
consumeState(_getEmailContentInteractor.execute(
|
||||||
|
session,
|
||||||
|
accountId,
|
||||||
|
emailId,
|
||||||
|
mailboxDashBoardController.baseDownloadUrl,
|
||||||
|
transformConfiguration
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _getEmailContentOffLineSuccess(GetEmailContentFromCacheSuccess success) {
|
void _getEmailContentOffLineSuccess(GetEmailContentFromCacheSuccess success) {
|
||||||
if (success.attachments.isNotEmpty) {
|
_initAttachments(success.attachments);
|
||||||
initialAttachments = success.attachments;
|
|
||||||
uploadController.initializeUploadAttachments(
|
|
||||||
success.attachments.listAttachmentsDisplayedOutSide);
|
|
||||||
}
|
|
||||||
emailContentsViewState.value = Right(success);
|
emailContentsViewState.value = Right(success);
|
||||||
_emailContents = success.emailContent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _getEmailContentSuccess(GetEmailContentSuccess success) {
|
void _getEmailContentSuccess(GetEmailContentSuccess success) {
|
||||||
if (success.attachments.isNotEmpty) {
|
_initAttachments(success.attachments);
|
||||||
initialAttachments = success.attachments;
|
|
||||||
uploadController.initializeUploadAttachments(
|
|
||||||
success.attachments.listAttachmentsDisplayedOutSide);
|
|
||||||
}
|
|
||||||
emailContentsViewState.value = Right(success);
|
emailContentsViewState.value = Right(success);
|
||||||
_emailContents = success.emailContent;
|
}
|
||||||
|
|
||||||
|
void _transformHtmlEmailContent(String? emailContent) {
|
||||||
|
emailContentsViewState(Right(TransformHtmlEmailContentLoading()));
|
||||||
|
if (emailContent?.isEmpty == true) {
|
||||||
|
consumeState(Stream.value(Left(TransformHtmlEmailContentFailure(EmptyEmailContentException()))));
|
||||||
|
} else {
|
||||||
|
consumeState(_transformHtmlEmailContentInteractor.execute(
|
||||||
|
emailContent!,
|
||||||
|
TransformConfiguration.forReplyForwardEmail()
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String getEmailAddressSender() {
|
String getEmailAddressSender() {
|
||||||
final arguments = composerArguments.value;
|
final arguments = composerArguments.value;
|
||||||
if (arguments != null) {
|
if (arguments != null) {
|
||||||
if (arguments.emailActionType == EmailActionType.edit) {
|
if (arguments.emailActionType == EmailActionType.editDraft) {
|
||||||
return arguments.presentationEmail?.from?.first.emailAddress ?? '';
|
return arguments.presentationEmail?.from?.first.emailAddress ?? '';
|
||||||
} else {
|
} else {
|
||||||
return mailboxDashBoardController.userProfile.value?.email ?? '';
|
return mailboxDashBoardController.userProfile.value?.email ?? '';
|
||||||
|
|||||||
@@ -15,9 +15,11 @@ import 'package:get/get.dart';
|
|||||||
import 'package:model/email/email_action_type.dart';
|
import 'package:model/email/email_action_type.dart';
|
||||||
import 'package:rich_text_composer/rich_text_composer.dart';
|
import 'package:rich_text_composer/rich_text_composer.dart';
|
||||||
import 'package:rich_text_composer/views/widgets/rich_text_keyboard_toolbar.dart';
|
import 'package:rich_text_composer/views/widgets/rich_text_keyboard_toolbar.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/cupertino_loading_widget.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/base_composer_view.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/base_composer_view.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/styles/composer_style.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/styles/composer_style.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
|
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/state/transform_html_email_content_state.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/app_utils.dart';
|
import 'package:tmail_ui_user/main/utils/app_utils.dart';
|
||||||
|
|
||||||
@@ -276,38 +278,96 @@ class ComposerView extends BaseComposerView {
|
|||||||
switch(argsComposer.emailActionType) {
|
switch(argsComposer.emailActionType) {
|
||||||
case EmailActionType.compose:
|
case EmailActionType.compose:
|
||||||
case EmailActionType.composeFromEmailAddress:
|
case EmailActionType.composeFromEmailAddress:
|
||||||
return _buildHtmlEditor(context, initialContent: HtmlExtension.editorStartTags);
|
case EmailActionType.composeFromFileShared:
|
||||||
case EmailActionType.edit:
|
return _buildHtmlEditor(
|
||||||
return controller.emailContentsViewState.value.fold(
|
context,
|
||||||
(failure) => _buildHtmlEditor(context, initialContent: HtmlExtension.editorStartTags),
|
initialContent: HtmlExtension.editorStartTags
|
||||||
|
);
|
||||||
|
case EmailActionType.editDraft:
|
||||||
|
case EmailActionType.editSendingEmail:
|
||||||
|
case EmailActionType.composeFromContentShared:
|
||||||
|
case EmailActionType.reopenComposerBrowser:
|
||||||
|
final emailContentsViewState = controller.emailContentsViewState.value;
|
||||||
|
if (emailContentsViewState == null) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
return emailContentsViewState.fold(
|
||||||
|
(failure) => _buildHtmlEditor(
|
||||||
|
context,
|
||||||
|
initialContent: HtmlExtension.editorStartTags
|
||||||
|
),
|
||||||
(success) {
|
(success) {
|
||||||
if (success is GetEmailContentLoading) {
|
if (success is GetEmailContentLoading) {
|
||||||
return Padding(
|
return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0));
|
||||||
padding: const EdgeInsets.all(16.0),
|
|
||||||
child: loadingWidget,
|
|
||||||
);
|
|
||||||
} else if (success is GetEmailContentSuccess) {
|
} else if (success is GetEmailContentSuccess) {
|
||||||
var contentHtml = success.emailContent;
|
var contentHtml = success.htmlEmailContent;
|
||||||
if (contentHtml.isEmpty == true) {
|
if (contentHtml.isEmpty == true) {
|
||||||
contentHtml = HtmlExtension.editorStartTags;
|
contentHtml = HtmlExtension.editorStartTags;
|
||||||
}
|
}
|
||||||
return _buildHtmlEditor(context, initialContent: contentHtml);
|
return _buildHtmlEditor(
|
||||||
|
context,
|
||||||
|
initialContent: contentHtml
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return _buildHtmlEditor(context, initialContent: HtmlExtension.editorStartTags);
|
return _buildHtmlEditor(
|
||||||
|
context,
|
||||||
|
initialContent: HtmlExtension.editorStartTags
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
case EmailActionType.reply:
|
case EmailActionType.reply:
|
||||||
case EmailActionType.replyAll:
|
case EmailActionType.replyAll:
|
||||||
case EmailActionType.forward:
|
case EmailActionType.forward:
|
||||||
var contentHtml = controller.getEmailContentQuotedAsHtml(
|
final emailContentsViewState = controller.emailContentsViewState.value;
|
||||||
context,
|
if (emailContentsViewState == null) {
|
||||||
argsComposer);
|
return const SizedBox.shrink();
|
||||||
if (contentHtml.isEmpty == true) {
|
|
||||||
contentHtml = HtmlExtension.editorStartTags;
|
|
||||||
}
|
}
|
||||||
return _buildHtmlEditor(context, initialContent: contentHtml);
|
return emailContentsViewState.fold(
|
||||||
|
(failure) {
|
||||||
|
final emailContentQuoted = controller.getEmailContentQuotedAsHtml(
|
||||||
|
context: context,
|
||||||
|
emailContent: '',
|
||||||
|
emailActionType: argsComposer.emailActionType,
|
||||||
|
presentationEmail: argsComposer.presentationEmail!
|
||||||
|
);
|
||||||
|
return _buildHtmlEditor(
|
||||||
|
context,
|
||||||
|
initialContent: emailContentQuoted
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(success) {
|
||||||
|
if (success is TransformHtmlEmailContentLoading) {
|
||||||
|
return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0));
|
||||||
|
} else if (success is TransformHtmlEmailContentSuccess) {
|
||||||
|
final emailContentQuoted = controller.getEmailContentQuotedAsHtml(
|
||||||
|
context: context,
|
||||||
|
emailContent: success.htmlContent,
|
||||||
|
emailActionType: argsComposer.emailActionType,
|
||||||
|
presentationEmail: argsComposer.presentationEmail!
|
||||||
|
);
|
||||||
|
return _buildHtmlEditor(
|
||||||
|
context,
|
||||||
|
initialContent: emailContentQuoted
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
final emailContentQuoted = controller.getEmailContentQuotedAsHtml(
|
||||||
|
context: context,
|
||||||
|
emailContent: '',
|
||||||
|
emailActionType: argsComposer.emailActionType,
|
||||||
|
presentationEmail: argsComposer.presentationEmail!
|
||||||
|
);
|
||||||
|
return _buildHtmlEditor(
|
||||||
|
context,
|
||||||
|
initialContent: emailContentQuoted
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return _buildHtmlEditor(context, initialContent: HtmlExtension.editorStartTags);
|
return _buildHtmlEditor(
|
||||||
|
context,
|
||||||
|
initialContent: HtmlExtension.editorStartTags
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,14 @@ import 'package:flutter_svg/flutter_svg.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:model/email/email_action_type.dart';
|
import 'package:model/email/email_action_type.dart';
|
||||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/cupertino_loading_widget.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/base_composer_view.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/base_composer_view.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';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/styles/composer_style.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/styles/composer_style.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/email_editor_widget.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/email_editor_widget.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/toolbar_rich_text_builder.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/toolbar_rich_text_builder.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
|
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/state/transform_html_email_content_state.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/app_utils.dart';
|
import 'package:tmail_ui_user/main/utils/app_utils.dart';
|
||||||
|
|
||||||
@@ -385,46 +387,96 @@ class ComposerView extends BaseComposerView {
|
|||||||
switch(argsComposer.emailActionType) {
|
switch(argsComposer.emailActionType) {
|
||||||
case EmailActionType.compose:
|
case EmailActionType.compose:
|
||||||
case EmailActionType.composeFromEmailAddress:
|
case EmailActionType.composeFromEmailAddress:
|
||||||
|
case EmailActionType.composeFromFileShared:
|
||||||
return _buildHtmlEditor(
|
return _buildHtmlEditor(
|
||||||
context,
|
context,
|
||||||
currentTextEditor ?? HtmlExtension.editorStartTags);
|
currentTextEditor ?? HtmlExtension.editorStartTags
|
||||||
case EmailActionType.edit:
|
);
|
||||||
return controller.emailContentsViewState.value.fold(
|
case EmailActionType.editDraft:
|
||||||
|
case EmailActionType.editSendingEmail:
|
||||||
|
case EmailActionType.composeFromContentShared:
|
||||||
|
case EmailActionType.reopenComposerBrowser:
|
||||||
|
final emailContentsViewState = controller.emailContentsViewState.value;
|
||||||
|
if (emailContentsViewState == null) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
return emailContentsViewState.fold(
|
||||||
(failure) => _buildHtmlEditor(
|
(failure) => _buildHtmlEditor(
|
||||||
context,
|
context,
|
||||||
currentTextEditor ?? HtmlExtension.editorStartTags),
|
currentTextEditor ?? HtmlExtension.editorStartTags
|
||||||
|
),
|
||||||
(success) {
|
(success) {
|
||||||
if (success is GetEmailContentLoading) {
|
if (success is GetEmailContentLoading) {
|
||||||
return Padding(
|
return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0));
|
||||||
padding: const EdgeInsets.all(16.0),
|
|
||||||
child: loadingWidget,
|
|
||||||
);
|
|
||||||
} else if (success is GetEmailContentSuccess) {
|
} else if (success is GetEmailContentSuccess) {
|
||||||
var contentHtml = success.emailContent;
|
var contentHtml = success.htmlEmailContent;
|
||||||
if (contentHtml.isEmpty == true) {
|
if (contentHtml.isEmpty == true) {
|
||||||
contentHtml = HtmlExtension.editorStartTags;
|
contentHtml = HtmlExtension.editorStartTags;
|
||||||
}
|
}
|
||||||
return _buildHtmlEditor(context, currentTextEditor ?? contentHtml);
|
return _buildHtmlEditor(
|
||||||
|
context,
|
||||||
|
currentTextEditor ?? contentHtml
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return _buildHtmlEditor(
|
return _buildHtmlEditor(
|
||||||
context,
|
context,
|
||||||
currentTextEditor ?? HtmlExtension.editorStartTags);
|
currentTextEditor ?? HtmlExtension.editorStartTags
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
case EmailActionType.reply:
|
case EmailActionType.reply:
|
||||||
case EmailActionType.replyAll:
|
case EmailActionType.replyAll:
|
||||||
case EmailActionType.forward:
|
case EmailActionType.forward:
|
||||||
var contentHtml = controller.getEmailContentQuotedAsHtml(
|
final emailContentsViewState = controller.emailContentsViewState.value;
|
||||||
context,
|
if (emailContentsViewState == null) {
|
||||||
argsComposer);
|
return const SizedBox.shrink();
|
||||||
if (contentHtml.isEmpty == true) {
|
|
||||||
contentHtml = HtmlExtension.editorStartTags;
|
|
||||||
}
|
}
|
||||||
return _buildHtmlEditor(context, currentTextEditor ?? contentHtml);
|
return emailContentsViewState.fold(
|
||||||
|
(failure) {
|
||||||
|
final emailContentQuoted = controller.getEmailContentQuotedAsHtml(
|
||||||
|
context: context,
|
||||||
|
emailContent: '',
|
||||||
|
emailActionType: argsComposer.emailActionType,
|
||||||
|
presentationEmail: argsComposer.presentationEmail!
|
||||||
|
);
|
||||||
|
return _buildHtmlEditor(
|
||||||
|
context,
|
||||||
|
currentTextEditor ?? emailContentQuoted
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(success) {
|
||||||
|
if (success is TransformHtmlEmailContentLoading) {
|
||||||
|
return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0));
|
||||||
|
} else if (success is TransformHtmlEmailContentSuccess) {
|
||||||
|
final emailContentQuoted = controller.getEmailContentQuotedAsHtml(
|
||||||
|
context: context,
|
||||||
|
emailContent: success.htmlContent,
|
||||||
|
emailActionType: argsComposer.emailActionType,
|
||||||
|
presentationEmail: argsComposer.presentationEmail!
|
||||||
|
);
|
||||||
|
return _buildHtmlEditor(
|
||||||
|
context,
|
||||||
|
currentTextEditor ?? emailContentQuoted
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
final emailContentQuoted = controller.getEmailContentQuotedAsHtml(
|
||||||
|
context: context,
|
||||||
|
emailContent: '',
|
||||||
|
emailActionType: argsComposer.emailActionType,
|
||||||
|
presentationEmail: argsComposer.presentationEmail!
|
||||||
|
);
|
||||||
|
return _buildHtmlEditor(
|
||||||
|
context,
|
||||||
|
currentTextEditor ?? emailContentQuoted
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return _buildHtmlEditor(
|
return _buildHtmlEditor(
|
||||||
context,
|
context,
|
||||||
currentTextEditor ?? HtmlExtension.editorStartTags);
|
currentTextEditor ?? HtmlExtension.editorStartTags
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,35 @@
|
|||||||
|
|
||||||
|
import 'package:core/presentation/extensions/html_extension.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:model/email/email_action_type.dart';
|
import 'package:model/email/email_action_type.dart';
|
||||||
|
import 'package:model/email/presentation_email.dart';
|
||||||
|
import 'package:model/extensions/list_email_address_extension.dart';
|
||||||
|
import 'package:model/extensions/utc_date_extension.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
extension EmailActionTypeExtension on EmailActionType {
|
extension EmailActionTypeExtension on EmailActionType {
|
||||||
String getSubjectComposer(BuildContext context, String subject) {
|
String getSubjectComposer(BuildContext? context, String subject) {
|
||||||
switch(this) {
|
switch(this) {
|
||||||
case EmailActionType.reply:
|
case EmailActionType.reply:
|
||||||
case EmailActionType.replyAll:
|
case EmailActionType.replyAll:
|
||||||
if (subject.toLowerCase().startsWith('re:')) {
|
if (subject.toLowerCase().startsWith('re:')) {
|
||||||
return subject;
|
return subject;
|
||||||
} else {
|
} else {
|
||||||
return '${AppLocalizations.of(context).prefix_reply_email} $subject';
|
return context != null
|
||||||
|
? '${AppLocalizations.of(context).prefix_reply_email} $subject'
|
||||||
|
: subject;
|
||||||
}
|
}
|
||||||
case EmailActionType.forward:
|
case EmailActionType.forward:
|
||||||
if (subject.toLowerCase().startsWith('fwd:')) {
|
if (subject.toLowerCase().startsWith('fwd:')) {
|
||||||
return subject;
|
return subject;
|
||||||
} else {
|
} else {
|
||||||
return '${AppLocalizations.of(context).prefix_forward_email} $subject';
|
return context != null
|
||||||
|
? '${AppLocalizations.of(context).prefix_forward_email} $subject'
|
||||||
|
: subject;
|
||||||
}
|
}
|
||||||
case EmailActionType.edit:
|
case EmailActionType.editDraft:
|
||||||
|
case EmailActionType.editSendingEmail:
|
||||||
|
case EmailActionType.reopenComposerBrowser:
|
||||||
return subject;
|
return subject;
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
@@ -40,4 +50,71 @@ extension EmailActionTypeExtension on EmailActionType {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String? getHeaderEmailQuoted({
|
||||||
|
required BuildContext context,
|
||||||
|
required PresentationEmail presentationEmail
|
||||||
|
}) {
|
||||||
|
final locale = Localizations.localeOf(context).toLanguageTag();
|
||||||
|
switch(this) {
|
||||||
|
case EmailActionType.reply:
|
||||||
|
case EmailActionType.replyAll:
|
||||||
|
final receivedAt = presentationEmail.receivedAt;
|
||||||
|
final emailAddress = presentationEmail.from.listEmailAddressToString(isFullEmailAddress: true);
|
||||||
|
return AppLocalizations.of(context).header_email_quoted(
|
||||||
|
receivedAt.formatDateToLocal(pattern: 'MMM d, y h:mm a', locale: locale),
|
||||||
|
emailAddress
|
||||||
|
);
|
||||||
|
case EmailActionType.forward:
|
||||||
|
var headerQuoted = '------- ${AppLocalizations.of(context).forwarded_message} -------'.addNewLineTag();
|
||||||
|
|
||||||
|
final subject = presentationEmail.subject ?? '';
|
||||||
|
final receivedAt = presentationEmail.receivedAt;
|
||||||
|
final fromEmailAddress = presentationEmail.from.listEmailAddressToString(isFullEmailAddress: true);
|
||||||
|
final toEmailAddress = presentationEmail.to.listEmailAddressToString(isFullEmailAddress: true);
|
||||||
|
final ccEmailAddress = presentationEmail.cc.listEmailAddressToString(isFullEmailAddress: true);
|
||||||
|
final bccEmailAddress = presentationEmail.bcc.listEmailAddressToString(isFullEmailAddress: true);
|
||||||
|
|
||||||
|
if (subject.isNotEmpty) {
|
||||||
|
headerQuoted = headerQuoted
|
||||||
|
.append('${AppLocalizations.of(context).subject_email}: ')
|
||||||
|
.append(subject)
|
||||||
|
.addNewLineTag();
|
||||||
|
}
|
||||||
|
if (receivedAt != null) {
|
||||||
|
headerQuoted = headerQuoted
|
||||||
|
.append('${AppLocalizations.of(context).date}: ')
|
||||||
|
.append(receivedAt.formatDateToLocal(pattern: 'MMM d, y h:mm a', locale: locale))
|
||||||
|
.addNewLineTag();
|
||||||
|
}
|
||||||
|
if (fromEmailAddress.isNotEmpty) {
|
||||||
|
headerQuoted = headerQuoted
|
||||||
|
.append('${AppLocalizations.of(context).from_email_address_prefix}: ')
|
||||||
|
.append(fromEmailAddress)
|
||||||
|
.addNewLineTag();
|
||||||
|
}
|
||||||
|
if (toEmailAddress.isNotEmpty) {
|
||||||
|
headerQuoted = headerQuoted
|
||||||
|
.append('${AppLocalizations.of(context).to_email_address_prefix}: ')
|
||||||
|
.append(toEmailAddress)
|
||||||
|
.addNewLineTag();
|
||||||
|
}
|
||||||
|
if (ccEmailAddress.isNotEmpty) {
|
||||||
|
headerQuoted = headerQuoted
|
||||||
|
.append('${AppLocalizations.of(context).cc_email_address_prefix}: ')
|
||||||
|
.append(ccEmailAddress)
|
||||||
|
.addNewLineTag();
|
||||||
|
}
|
||||||
|
if (bccEmailAddress.isNotEmpty) {
|
||||||
|
headerQuoted = headerQuoted
|
||||||
|
.append('${AppLocalizations.of(context).bcc_email_address_prefix}: ')
|
||||||
|
.append(bccEmailAddress)
|
||||||
|
.addNewLineTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
return headerQuoted;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -99,7 +99,7 @@ class ComposerStyle {
|
|||||||
if (responsiveUtils.isPortraitMobile(context) || responsiveUtils.isLandscapeMobile(context)) {
|
if (responsiveUtils.isPortraitMobile(context) || responsiveUtils.isLandscapeMobile(context)) {
|
||||||
return const EdgeInsetsDirectional.symmetric(horizontal: 16);
|
return const EdgeInsetsDirectional.symmetric(horizontal: 16);
|
||||||
} else {
|
} else {
|
||||||
return const EdgeInsetsDirectional.only(start: 88, end: 48);
|
return const EdgeInsetsDirectional.only(start: 88, end: 48, top: 8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
|
|
||||||
|
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
|
||||||
import 'package:model/model.dart';
|
import 'package:model/model.dart';
|
||||||
|
|
||||||
abstract class HtmlDataSource {
|
abstract class HtmlDataSource {
|
||||||
Future<EmailContent> transformEmailContent(
|
Future<EmailContent> transformEmailContent(
|
||||||
EmailContent emailContent,
|
EmailContent emailContent,
|
||||||
Map<String, String> mapUrlDownloadCID,
|
Map<String, String> mapCidImageDownloadUrl,
|
||||||
{bool draftsEmail = false}
|
TransformConfiguration transformConfiguration
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<EmailContent> addTooltipWhenHoverOnLink(EmailContent emailContent);
|
Future<String> transformHtmlEmailContent(
|
||||||
|
String htmlContent,
|
||||||
|
TransformConfiguration configuration
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
|
||||||
import 'package:model/email/email_content.dart';
|
import 'package:model/email/email_content.dart';
|
||||||
import 'package:tmail_ui_user/features/email/data/datasource/html_datasource.dart';
|
import 'package:tmail_ui_user/features/email/data/datasource/html_datasource.dart';
|
||||||
import 'package:tmail_ui_user/features/email/data/local/html_analyzer.dart';
|
import 'package:tmail_ui_user/features/email/data/local/html_analyzer.dart';
|
||||||
@@ -13,22 +14,25 @@ class HtmlDataSourceImpl extends HtmlDataSource {
|
|||||||
@override
|
@override
|
||||||
Future<EmailContent> transformEmailContent(
|
Future<EmailContent> transformEmailContent(
|
||||||
EmailContent emailContent,
|
EmailContent emailContent,
|
||||||
Map<String, String>? mapUrlDownloadCID,
|
Map<String, String> mapCidImageDownloadUrl,
|
||||||
{bool draftsEmail = false}
|
TransformConfiguration transformConfiguration
|
||||||
) {
|
) {
|
||||||
return Future.sync(() async {
|
return Future.sync(() async {
|
||||||
return await _htmlAnalyzer.transformEmailContent(
|
return await _htmlAnalyzer.transformEmailContent(
|
||||||
emailContent,
|
emailContent,
|
||||||
mapUrlDownloadCID,
|
mapCidImageDownloadUrl,
|
||||||
draftsEmail: draftsEmail
|
transformConfiguration
|
||||||
);
|
);
|
||||||
}).catchError(_exceptionThrower.throwException);
|
}).catchError(_exceptionThrower.throwException);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<EmailContent> addTooltipWhenHoverOnLink(EmailContent emailContent) {
|
Future<String> transformHtmlEmailContent(String htmlContent, TransformConfiguration configuration) {
|
||||||
return Future.sync(() async {
|
return Future.sync(() async {
|
||||||
return await _htmlAnalyzer.addTooltipWhenHoverOnLink(emailContent);
|
return await _htmlAnalyzer.transformHtmlEmailContent(
|
||||||
|
htmlContent,
|
||||||
|
configuration
|
||||||
|
);
|
||||||
}).catchError(_exceptionThrower.throwException);
|
}).catchError(_exceptionThrower.throwException);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,5 @@
|
|||||||
|
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:core/data/network/dio_client.dart';
|
|
||||||
import 'package:core/presentation/utils/html_transformer/dom/add_tooltip_link_transformers.dart';
|
|
||||||
import 'package:core/presentation/utils/html_transformer/html_transform.dart';
|
import 'package:core/presentation/utils/html_transformer/html_transform.dart';
|
||||||
import 'package:core/presentation/utils/html_transformer/text/sanitize_autolink_html_transformers.dart';
|
|
||||||
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
|
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
|
||||||
import 'package:html/parser.dart';
|
import 'package:html/parser.dart';
|
||||||
import 'package:model/email/email_content.dart';
|
import 'package:model/email/email_content.dart';
|
||||||
@@ -14,32 +8,27 @@ import 'package:model/email/email_content_type.dart';
|
|||||||
class HtmlAnalyzer {
|
class HtmlAnalyzer {
|
||||||
|
|
||||||
final HtmlTransform _htmlTransform;
|
final HtmlTransform _htmlTransform;
|
||||||
final HtmlEscape _htmlEscape;
|
|
||||||
|
|
||||||
HtmlAnalyzer(this._htmlTransform, this._htmlEscape);
|
HtmlAnalyzer(this._htmlTransform);
|
||||||
|
|
||||||
Future<EmailContent> transformEmailContent(
|
Future<EmailContent> transformEmailContent(
|
||||||
EmailContent emailContent,
|
EmailContent emailContent,
|
||||||
Map<String, String>? mapUrlDownloadCID,
|
Map<String, String> mapCidImageDownloadUrl,
|
||||||
{bool draftsEmail = false}
|
TransformConfiguration transformConfiguration
|
||||||
) async {
|
) async {
|
||||||
switch(emailContent.type) {
|
switch(emailContent.type) {
|
||||||
case EmailContentType.textHtml:
|
case EmailContentType.textHtml:
|
||||||
final htmlContent = await _htmlTransform.transformToHtml(
|
final htmlContent = await _htmlTransform.transformToHtml(
|
||||||
contentHtml: emailContent.content,
|
htmlContent: emailContent.content,
|
||||||
mapUrlDownloadCID: mapUrlDownloadCID,
|
mapCidImageDownloadUrl: mapCidImageDownloadUrl,
|
||||||
transformConfiguration: draftsEmail
|
transformConfiguration: transformConfiguration
|
||||||
? TransformConfiguration.create(customDomTransformers: TransformConfiguration.domTransformersForDraftEmail)
|
|
||||||
: null
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return EmailContent(emailContent.type, htmlContent);
|
return EmailContent(emailContent.type, htmlContent);
|
||||||
case EmailContentType.textPlain:
|
case EmailContentType.textPlain:
|
||||||
final message = _htmlTransform.transformToTextPlain(
|
final message = _htmlTransform.transformToTextPlain(
|
||||||
content: emailContent.content,
|
content: emailContent.content,
|
||||||
transformConfiguration: TransformConfiguration.create(
|
transformConfiguration: transformConfiguration
|
||||||
customTextTransformers: [SanitizeAutolinkHtmlTransformers(_htmlEscape)]
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
return EmailContent(emailContent.type, message);
|
return EmailContent(emailContent.type, message);
|
||||||
default:
|
default:
|
||||||
@@ -47,21 +36,6 @@ class HtmlAnalyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<EmailContent> addTooltipWhenHoverOnLink(EmailContent emailContent) async {
|
|
||||||
switch(emailContent.type) {
|
|
||||||
case EmailContentType.textHtml:
|
|
||||||
final htmlContent = await _htmlTransform.transformToHtml(
|
|
||||||
contentHtml: emailContent.content,
|
|
||||||
transformConfiguration: TransformConfiguration.create(
|
|
||||||
customDomTransformers: [const AddTooltipLinkTransformer()]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return EmailContent(emailContent.type, htmlContent);
|
|
||||||
default:
|
|
||||||
return emailContent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<String>> getListLinkCalendarEvent(String emailContents) async {
|
Future<List<String>> getListLinkCalendarEvent(String emailContents) async {
|
||||||
final document = parse(emailContents);
|
final document = parse(emailContents);
|
||||||
final linkElements = document.querySelectorAll('a.part-button');
|
final linkElements = document.querySelectorAll('a.part-button');
|
||||||
@@ -71,4 +45,15 @@ class HtmlAnalyzer {
|
|||||||
.toList();
|
.toList();
|
||||||
return listLink;
|
return listLink;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String> transformHtmlEmailContent(
|
||||||
|
String htmlContent,
|
||||||
|
TransformConfiguration configuration
|
||||||
|
) async {
|
||||||
|
final htmlContentTransformed = await _htmlTransform.transformToHtml(
|
||||||
|
htmlContent: htmlContent,
|
||||||
|
transformConfiguration: configuration
|
||||||
|
);
|
||||||
|
return htmlContentTransformed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ import 'package:core/data/model/source_type/data_source_type.dart';
|
|||||||
import 'package:core/data/network/download/downloaded_response.dart';
|
import 'package:core/data/network/download/downloaded_response.dart';
|
||||||
import 'package:core/presentation/state/failure.dart';
|
import 'package:core/presentation/state/failure.dart';
|
||||||
import 'package:core/presentation/state/success.dart';
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
@@ -109,22 +110,16 @@ class EmailRepositoryImpl extends EmailRepository {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<EmailContent>> transformEmailContent(
|
Future<List<EmailContent>> transformEmailContent(
|
||||||
List<EmailContent> emailContents,
|
List<EmailContent> emailContents,
|
||||||
List<Attachment> attachmentInlines,
|
Map<String, String> mapCidImageDownloadUrl,
|
||||||
String? baseUrlDownload,
|
TransformConfiguration transformConfiguration
|
||||||
AccountId accountId,
|
) async {
|
||||||
{bool draftsEmail = false}
|
|
||||||
) async {
|
|
||||||
final mapUrlDownloadCID = {
|
|
||||||
for (var attachment in attachmentInlines)
|
|
||||||
attachment.cid! : attachment.getDownloadUrl(baseUrlDownload!, accountId)
|
|
||||||
};
|
|
||||||
return await Future.wait(emailContents
|
return await Future.wait(emailContents
|
||||||
.map((emailContent) async {
|
.map((emailContent) async {
|
||||||
return await _htmlDataSource.transformEmailContent(
|
return await _htmlDataSource.transformEmailContent(
|
||||||
emailContent,
|
emailContent,
|
||||||
mapUrlDownloadCID,
|
mapCidImageDownloadUrl,
|
||||||
draftsEmail: draftsEmail
|
transformConfiguration,
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.toList());
|
.toList());
|
||||||
@@ -173,13 +168,6 @@ class EmailRepositoryImpl extends EmailRepository {
|
|||||||
return emailDataSource[DataSourceType.network]!.deleteEmailPermanently(session, accountId, emailId);
|
return emailDataSource[DataSourceType.network]!.deleteEmailPermanently(session, accountId, emailId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<EmailContent>> addTooltipWhenHoverOnLink(List<EmailContent> emailContents) {
|
|
||||||
return Future.wait(emailContents
|
|
||||||
.map((emailContent) => _htmlDataSource.addTooltipWhenHoverOnLink(emailContent))
|
|
||||||
.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<jmap.State?> getEmailState(Session session, AccountId accountId) {
|
Future<jmap.State?> getEmailState(Session session, AccountId accountId) {
|
||||||
return _stateDataSource.getState(accountId, session.username, StateType.email);
|
return _stateDataSource.getState(accountId, session.username, StateType.email);
|
||||||
@@ -219,4 +207,9 @@ class EmailRepositoryImpl extends EmailRepository {
|
|||||||
Future<DetailedEmail> getStoredNewEmail(Session session, AccountId accountId, EmailId emailId) {
|
Future<DetailedEmail> getStoredNewEmail(Session session, AccountId accountId, EmailId emailId) {
|
||||||
return emailDataSource[DataSourceType.hiveCache]!.getStoredNewEmail(session, accountId, emailId);
|
return emailDataSource[DataSourceType.hiveCache]!.getStoredNewEmail(session, accountId, emailId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> transformHtmlEmailContent(String htmlContent, TransformConfiguration configuration) {
|
||||||
|
return _htmlDataSource.transformHtmlEmailContent(htmlContent, configuration);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,4 +3,6 @@ class CannotDeleteOldEmailException implements Exception {}
|
|||||||
|
|
||||||
class NotFoundEmailException implements Exception {}
|
class NotFoundEmailException implements Exception {}
|
||||||
|
|
||||||
class NotFoundEmailContentException implements Exception {}
|
class NotFoundEmailContentException implements Exception {}
|
||||||
|
|
||||||
|
class EmptyEmailContentException implements Exception {}
|
||||||
@@ -4,6 +4,7 @@ import 'dart:typed_data';
|
|||||||
import 'package:core/data/network/download/downloaded_response.dart';
|
import 'package:core/data/network/download/downloaded_response.dart';
|
||||||
import 'package:core/presentation/state/failure.dart';
|
import 'package:core/presentation/state/failure.dart';
|
||||||
import 'package:core/presentation/state/success.dart';
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
@@ -69,14 +70,10 @@ abstract class EmailRepository {
|
|||||||
|
|
||||||
Future<List<EmailContent>> transformEmailContent(
|
Future<List<EmailContent>> transformEmailContent(
|
||||||
List<EmailContent> emailContents,
|
List<EmailContent> emailContents,
|
||||||
List<Attachment> attachmentInlines,
|
Map<String, String> mapCidImageDownloadUrl,
|
||||||
String? baseUrlDownload,
|
TransformConfiguration transformConfiguration
|
||||||
AccountId accountId,
|
|
||||||
{bool draftsEmail = false}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<List<EmailContent>> addTooltipWhenHoverOnLink(List<EmailContent> emailContents);
|
|
||||||
|
|
||||||
Future<Email> saveEmailAsDrafts(Session session, AccountId accountId, Email email);
|
Future<Email> saveEmailAsDrafts(Session session, AccountId accountId, Email email);
|
||||||
|
|
||||||
Future<bool> removeEmailDrafts(Session session, AccountId accountId, EmailId emailId);
|
Future<bool> removeEmailDrafts(Session session, AccountId accountId, EmailId emailId);
|
||||||
@@ -102,4 +99,9 @@ abstract class EmailRepository {
|
|||||||
Future<DetailedEmail> getStoredOpenedEmail(Session session, AccountId accountId, EmailId emailId);
|
Future<DetailedEmail> getStoredOpenedEmail(Session session, AccountId accountId, EmailId emailId);
|
||||||
|
|
||||||
Future<DetailedEmail> getStoredNewEmail(Session session, AccountId accountId, EmailId emailId);
|
Future<DetailedEmail> getStoredNewEmail(Session session, AccountId accountId, EmailId emailId);
|
||||||
|
|
||||||
|
Future<String> transformHtmlEmailContent(
|
||||||
|
String htmlContent,
|
||||||
|
TransformConfiguration configuration
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -6,41 +6,38 @@ import 'package:model/email/attachment.dart';
|
|||||||
class GetEmailContentLoading extends LoadingState {}
|
class GetEmailContentLoading extends LoadingState {}
|
||||||
|
|
||||||
class GetEmailContentSuccess extends UIState {
|
class GetEmailContentSuccess extends UIState {
|
||||||
final String emailContent;
|
final String htmlEmailContent;
|
||||||
final String emailContentDisplayed;
|
|
||||||
final List<Attachment> attachments;
|
final List<Attachment> attachments;
|
||||||
final Email? emailCurrent;
|
final Email? emailCurrent;
|
||||||
|
|
||||||
GetEmailContentSuccess({
|
GetEmailContentSuccess({
|
||||||
required this.emailContent,
|
required this.htmlEmailContent,
|
||||||
required this.emailContentDisplayed,
|
|
||||||
required this.attachments,
|
|
||||||
required this.emailCurrent
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [
|
|
||||||
emailContent,
|
|
||||||
emailContentDisplayed,
|
|
||||||
attachments,
|
|
||||||
emailCurrent
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
class GetEmailContentFromCacheSuccess extends UIState {
|
|
||||||
final String emailContent;
|
|
||||||
final List<Attachment> attachments;
|
|
||||||
final Email? emailCurrent;
|
|
||||||
|
|
||||||
GetEmailContentFromCacheSuccess({
|
|
||||||
required this.emailContent,
|
|
||||||
required this.attachments,
|
required this.attachments,
|
||||||
this.emailCurrent
|
this.emailCurrent
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
emailContent,
|
htmlEmailContent,
|
||||||
|
attachments,
|
||||||
|
emailCurrent
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
class GetEmailContentFromCacheSuccess extends UIState {
|
||||||
|
final String htmlEmailContent;
|
||||||
|
final List<Attachment> attachments;
|
||||||
|
final Email? emailCurrent;
|
||||||
|
|
||||||
|
GetEmailContentFromCacheSuccess({
|
||||||
|
required this.htmlEmailContent,
|
||||||
|
required this.attachments,
|
||||||
|
this.emailCurrent
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [
|
||||||
|
htmlEmailContent,
|
||||||
attachments,
|
attachments,
|
||||||
emailCurrent,
|
emailCurrent,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import 'package:core/presentation/state/failure.dart';
|
||||||
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
|
||||||
|
class TransformHtmlEmailContentLoading extends LoadingState {}
|
||||||
|
|
||||||
|
class TransformHtmlEmailContentSuccess extends UIState {
|
||||||
|
final String htmlContent;
|
||||||
|
|
||||||
|
TransformHtmlEmailContentSuccess(this.htmlContent);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [htmlContent];
|
||||||
|
}
|
||||||
|
|
||||||
|
class TransformHtmlEmailContentFailure extends FeatureFailure {
|
||||||
|
|
||||||
|
TransformHtmlEmailContentFailure(dynamic exception) : super(exception: exception);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
import 'package:core/presentation/state/failure.dart';
|
import 'package:core/presentation/state/failure.dart';
|
||||||
import 'package:core/presentation/state/success.dart';
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
|
||||||
import 'package:core/utils/app_logger.dart';
|
import 'package:core/utils/app_logger.dart';
|
||||||
import 'package:core/utils/platform_info.dart';
|
import 'package:core/utils/platform_info.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
@@ -20,19 +21,16 @@ class GetEmailContentInteractor {
|
|||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
EmailId emailId,
|
EmailId emailId,
|
||||||
String? baseDownloadUrl,
|
String baseDownloadUrl,
|
||||||
{
|
TransformConfiguration transformConfiguration,
|
||||||
bool composeEmail = false,
|
|
||||||
bool draftsEmail = false
|
|
||||||
}
|
|
||||||
) async* {
|
) async* {
|
||||||
try {
|
try {
|
||||||
yield Right<Failure, Success>(GetEmailContentLoading());
|
yield Right<Failure, Success>(GetEmailContentLoading());
|
||||||
|
|
||||||
if (PlatformInfo.isMobile) {
|
if (PlatformInfo.isMobile) {
|
||||||
yield* _getStoredOpenedEmail(session, accountId, emailId, baseDownloadUrl, composeEmail: composeEmail, draftsEmail: draftsEmail);
|
yield* _getStoredOpenedEmail(session, accountId, emailId, baseDownloadUrl, transformConfiguration);
|
||||||
} else {
|
} else {
|
||||||
yield* _getContentEmailFromServer(session, accountId, emailId, baseDownloadUrl, composeEmail: composeEmail, draftsEmail: draftsEmail);
|
yield* _getContentEmailFromServer(session, accountId, emailId, baseDownloadUrl, transformConfiguration);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('GetEmailContentInteractor::execute(): exception = $e');
|
log('GetEmailContentInteractor::execute(): exception = $e');
|
||||||
@@ -44,38 +42,31 @@ class GetEmailContentInteractor {
|
|||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
EmailId emailId,
|
EmailId emailId,
|
||||||
String? baseDownloadUrl,
|
String baseDownloadUrl,
|
||||||
{
|
TransformConfiguration transformConfiguration,
|
||||||
bool composeEmail = false,
|
|
||||||
bool draftsEmail = false
|
|
||||||
}
|
|
||||||
) async* {
|
) async* {
|
||||||
try {
|
try {
|
||||||
final email = await emailRepository.getEmailContent(session, accountId, emailId);
|
final email = await emailRepository.getEmailContent(session, accountId, emailId);
|
||||||
|
|
||||||
if (email.emailContentList.isNotEmpty) {
|
if (email.emailContentList.isNotEmpty) {
|
||||||
|
final mapCidImageDownloadUrl = email.attachmentsWithCid.toMapCidImageDownloadUrl(
|
||||||
|
accountId: accountId,
|
||||||
|
downloadUrl: baseDownloadUrl
|
||||||
|
);
|
||||||
final newEmailContents = await emailRepository.transformEmailContent(
|
final newEmailContents = await emailRepository.transformEmailContent(
|
||||||
email.emailContentList,
|
email.emailContentList,
|
||||||
email.allAttachments.listAttachmentsDisplayedInContent,
|
mapCidImageDownloadUrl,
|
||||||
baseDownloadUrl,
|
transformConfiguration
|
||||||
accountId,
|
|
||||||
draftsEmail: draftsEmail
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final newEmailContentsDisplayed = PlatformInfo.isWeb && !composeEmail
|
|
||||||
? await emailRepository.addTooltipWhenHoverOnLink(newEmailContents)
|
|
||||||
: newEmailContents;
|
|
||||||
|
|
||||||
yield Right<Failure, Success>(GetEmailContentSuccess(
|
yield Right<Failure, Success>(GetEmailContentSuccess(
|
||||||
emailContent: newEmailContents.asHtmlString,
|
htmlEmailContent: newEmailContents.asHtmlString,
|
||||||
emailContentDisplayed: newEmailContentsDisplayed.asHtmlString,
|
|
||||||
attachments: email.allAttachments,
|
attachments: email.allAttachments,
|
||||||
emailCurrent: email
|
emailCurrent: email
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
yield Right<Failure, Success>(GetEmailContentSuccess(
|
yield Right<Failure, Success>(GetEmailContentSuccess(
|
||||||
emailContent: '',
|
htmlEmailContent: '',
|
||||||
emailContentDisplayed: '',
|
|
||||||
attachments: email.allAttachments,
|
attachments: email.allAttachments,
|
||||||
emailCurrent: email
|
emailCurrent: email
|
||||||
));
|
));
|
||||||
@@ -90,17 +81,14 @@ class GetEmailContentInteractor {
|
|||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
EmailId emailId,
|
EmailId emailId,
|
||||||
String? baseDownloadUrl,
|
String baseDownloadUrl,
|
||||||
{
|
TransformConfiguration transformConfiguration,
|
||||||
bool composeEmail = false,
|
|
||||||
bool draftsEmail = false
|
|
||||||
}
|
|
||||||
) async* {
|
) async* {
|
||||||
try {
|
try {
|
||||||
log('GetEmailContentInteractor::_getStoredOpenedEmail(): CALLED');
|
log('GetEmailContentInteractor::_getStoredOpenedEmail(): CALLED');
|
||||||
final detailedEmail = await emailRepository.getStoredOpenedEmail(session, accountId, emailId);
|
final detailedEmail = await emailRepository.getStoredOpenedEmail(session, accountId, emailId);
|
||||||
yield Right<Failure, Success>(GetEmailContentFromCacheSuccess(
|
yield Right<Failure, Success>(GetEmailContentFromCacheSuccess(
|
||||||
emailContent: detailedEmail.htmlEmailContent ?? "",
|
htmlEmailContent: detailedEmail.htmlEmailContent ?? '',
|
||||||
attachments: detailedEmail.attachments ?? [],
|
attachments: detailedEmail.attachments ?? [],
|
||||||
emailCurrent: Email(
|
emailCurrent: Email(
|
||||||
id: emailId,
|
id: emailId,
|
||||||
@@ -115,8 +103,8 @@ class GetEmailContentInteractor {
|
|||||||
accountId,
|
accountId,
|
||||||
emailId,
|
emailId,
|
||||||
baseDownloadUrl,
|
baseDownloadUrl,
|
||||||
composeEmail: composeEmail,
|
transformConfiguration
|
||||||
draftsEmail: draftsEmail);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,17 +112,14 @@ class GetEmailContentInteractor {
|
|||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
EmailId emailId,
|
EmailId emailId,
|
||||||
String? baseDownloadUrl,
|
String baseDownloadUrl,
|
||||||
{
|
TransformConfiguration transformConfiguration,
|
||||||
bool composeEmail = false,
|
|
||||||
bool draftsEmail = false
|
|
||||||
}
|
|
||||||
) async* {
|
) async* {
|
||||||
try {
|
try {
|
||||||
log('GetEmailContentInteractor::_getStoredNewEmail():CALLED');
|
log('GetEmailContentInteractor::_getStoredNewEmail():CALLED');
|
||||||
final detailedEmail = await emailRepository.getStoredNewEmail(session, accountId, emailId);
|
final detailedEmail = await emailRepository.getStoredNewEmail(session, accountId, emailId);
|
||||||
yield Right<Failure, Success>(GetEmailContentFromCacheSuccess(
|
yield Right<Failure, Success>(GetEmailContentFromCacheSuccess(
|
||||||
emailContent: detailedEmail.htmlEmailContent ?? "",
|
htmlEmailContent: detailedEmail.htmlEmailContent ?? '',
|
||||||
attachments: detailedEmail.attachments ?? [],
|
attachments: detailedEmail.attachments ?? [],
|
||||||
emailCurrent: Email(
|
emailCurrent: Email(
|
||||||
id: emailId,
|
id: emailId,
|
||||||
@@ -149,8 +134,8 @@ class GetEmailContentInteractor {
|
|||||||
accountId,
|
accountId,
|
||||||
emailId,
|
emailId,
|
||||||
baseDownloadUrl,
|
baseDownloadUrl,
|
||||||
composeEmail: composeEmail,
|
transformConfiguration
|
||||||
draftsEmail: draftsEmail);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import 'package:core/presentation/state/failure.dart';
|
import 'package:core/presentation/state/failure.dart';
|
||||||
import 'package:core/presentation/state/success.dart';
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
|
||||||
|
import 'package:core/utils/platform_info.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/extensions/utc_date_extension.dart';
|
import 'package:jmap_dart_client/jmap/core/extensions/utc_date_extension.dart';
|
||||||
@@ -23,7 +25,7 @@ class GetListDetailedEmailByIdInteractor {
|
|||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
Set<EmailId> emailIds,
|
Set<EmailId> emailIds,
|
||||||
String? baseDownloadUrl,
|
String baseDownloadUrl,
|
||||||
{Set<Comparator>? sort}
|
{Set<Comparator>? sort}
|
||||||
) async* {
|
) async* {
|
||||||
try {
|
try {
|
||||||
@@ -57,17 +59,27 @@ class GetListDetailedEmailByIdInteractor {
|
|||||||
Future<Tuple2<Email, DetailedEmail>> _parsingEmailToDetailedEmail(
|
Future<Tuple2<Email, DetailedEmail>> _parsingEmailToDetailedEmail(
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
Email email,
|
Email email,
|
||||||
String? baseDownloadUrl
|
String baseDownloadUrl
|
||||||
) async {
|
) async {
|
||||||
String? htmlEmailContent;
|
String? htmlEmailContent;
|
||||||
|
|
||||||
final listEmailContent = email.emailContentList;
|
final listEmailContent = email.emailContentList;
|
||||||
if (listEmailContent.isNotEmpty) {
|
if (listEmailContent.isNotEmpty) {
|
||||||
|
final mapCidImageDownloadUrl = email.attachmentsWithCid.toMapCidImageDownloadUrl(
|
||||||
|
accountId: accountId,
|
||||||
|
downloadUrl: baseDownloadUrl
|
||||||
|
);
|
||||||
|
TransformConfiguration transformConfiguration = TransformConfiguration.standardConfiguration;
|
||||||
|
if (email.isDraft) {
|
||||||
|
transformConfiguration = TransformConfiguration.forDraftsEmail();
|
||||||
|
} else if (PlatformInfo.isWeb) {
|
||||||
|
transformConfiguration = TransformConfiguration.forPreviewEmailPlatformWeb();
|
||||||
|
}
|
||||||
final newEmailContents = await _emailRepository.transformEmailContent(
|
final newEmailContents = await _emailRepository.transformEmailContent(
|
||||||
listEmailContent,
|
email.emailContentList,
|
||||||
email.allAttachments.listAttachmentsDisplayedInContent,
|
mapCidImageDownloadUrl,
|
||||||
baseDownloadUrl,
|
transformConfiguration
|
||||||
accountId);
|
);
|
||||||
|
|
||||||
htmlEmailContent = newEmailContents.asHtmlString;
|
htmlEmailContent = newEmailContents.asHtmlString;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
import 'package:core/presentation/state/failure.dart';
|
||||||
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/state/transform_html_email_content_state.dart';
|
||||||
|
|
||||||
|
class TransformHtmlEmailContentInteractor {
|
||||||
|
final EmailRepository emailRepository;
|
||||||
|
|
||||||
|
TransformHtmlEmailContentInteractor(this.emailRepository);
|
||||||
|
|
||||||
|
Stream<Either<Failure, Success>> execute(
|
||||||
|
String htmlContent,
|
||||||
|
TransformConfiguration configuration
|
||||||
|
) async* {
|
||||||
|
try {
|
||||||
|
yield Right<Failure, Success>(TransformHtmlEmailContentLoading());
|
||||||
|
final htmlContentTransformed = await emailRepository.transformHtmlEmailContent(htmlContent, configuration);
|
||||||
|
yield Right<Failure, Success>(TransformHtmlEmailContentSuccess(htmlContentTransformed));
|
||||||
|
} catch (e) {
|
||||||
|
yield Left<Failure, Success>(TransformHtmlEmailContentFailure(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -360,22 +360,33 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _getEmailContentAction(EmailId emailId) async {
|
void _getEmailContentAction(EmailId emailId) async {
|
||||||
final session = mailboxDashBoardController.sessionCurrent;
|
|
||||||
final accountId = mailboxDashBoardController.accountId.value;
|
|
||||||
final baseDownloadUrl = mailboxDashBoardController.sessionCurrent?.getDownloadUrl(jmapUrl: _dynamicUrlInterceptors.jmapUrl);
|
|
||||||
final emailLoaded = emailSupervisorController.getEmailInQueueByEmailId(emailId);
|
final emailLoaded = emailSupervisorController.getEmailInQueueByEmailId(emailId);
|
||||||
|
|
||||||
if (emailLoaded != null) {
|
if (emailLoaded != null) {
|
||||||
consumeState(Stream.value(Right<Failure, Success>(
|
consumeState(Stream.value(Right<Failure, Success>(
|
||||||
GetEmailContentSuccess(
|
GetEmailContentSuccess(
|
||||||
emailContent: emailLoaded.originalContent,
|
htmlEmailContent: emailLoaded.htmlContent,
|
||||||
emailContentDisplayed: emailLoaded.displayedContent,
|
|
||||||
attachments: emailLoaded.attachments,
|
attachments: emailLoaded.attachments,
|
||||||
emailCurrent: emailLoaded.emailCurrent
|
emailCurrent: emailLoaded.emailCurrent
|
||||||
)
|
)
|
||||||
)));
|
)));
|
||||||
} else if (session != null && accountId != null && baseDownloadUrl != null) {
|
} else {
|
||||||
consumeState(_getEmailContentInteractor.execute(session, accountId, emailId, baseDownloadUrl));
|
final session = mailboxDashBoardController.sessionCurrent;
|
||||||
|
final accountId = mailboxDashBoardController.accountId.value;
|
||||||
|
if (session != null && accountId != null) {
|
||||||
|
final baseDownloadUrl = mailboxDashBoardController.sessionCurrent?.getDownloadUrl(jmapUrl: _dynamicUrlInterceptors.jmapUrl) ?? '';
|
||||||
|
TransformConfiguration transformConfiguration = PlatformInfo.isWeb
|
||||||
|
? TransformConfiguration.forPreviewEmailPlatformWeb()
|
||||||
|
: TransformConfiguration.standardConfiguration;
|
||||||
|
|
||||||
|
consumeState(_getEmailContentInteractor.execute(
|
||||||
|
session,
|
||||||
|
accountId,
|
||||||
|
emailId,
|
||||||
|
baseDownloadUrl,
|
||||||
|
transformConfiguration
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,8 +398,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
emailSupervisorController.popEmailQueue(success.emailCurrent?.id);
|
emailSupervisorController.popEmailQueue(success.emailCurrent?.id);
|
||||||
|
|
||||||
_currentEmailLoaded = EmailLoaded(
|
_currentEmailLoaded = EmailLoaded(
|
||||||
originalContent: success.emailContent,
|
htmlContent: success.htmlEmailContent,
|
||||||
displayedContent: success.emailContent,
|
|
||||||
attachments: List.of(success.attachments),
|
attachments: List.of(success.attachments),
|
||||||
emailCurrent: success.emailCurrent,
|
emailCurrent: success.emailCurrent,
|
||||||
);
|
);
|
||||||
@@ -401,10 +411,10 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
_parseCalendarEventAction(
|
_parseCalendarEventAction(
|
||||||
accountId: mailboxDashBoardController.accountId.value!,
|
accountId: mailboxDashBoardController.accountId.value!,
|
||||||
blobIds: success.attachments.calendarEventBlobIds,
|
blobIds: success.attachments.calendarEventBlobIds,
|
||||||
emailContents: success.emailContent
|
emailContents: success.htmlEmailContent
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
emailContents.value = success.emailContent;
|
emailContents.value = success.htmlEmailContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
final isShowMessageReadReceipt = success.emailCurrent?.hasReadReceipt(mailboxDashBoardController.mapMailboxById) == true;
|
final isShowMessageReadReceipt = success.emailCurrent?.hasReadReceipt(mailboxDashBoardController.mapMailboxById) == true;
|
||||||
@@ -422,8 +432,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
emailSupervisorController.popEmailQueue(success.emailCurrent?.id);
|
emailSupervisorController.popEmailQueue(success.emailCurrent?.id);
|
||||||
|
|
||||||
_currentEmailLoaded = EmailLoaded(
|
_currentEmailLoaded = EmailLoaded(
|
||||||
originalContent: success.emailContent,
|
htmlContent: success.htmlEmailContent,
|
||||||
displayedContent: success.emailContentDisplayed,
|
|
||||||
attachments: List.of(success.attachments),
|
attachments: List.of(success.attachments),
|
||||||
emailCurrent: success.emailCurrent,
|
emailCurrent: success.emailCurrent,
|
||||||
);
|
);
|
||||||
@@ -436,10 +445,10 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
_parseCalendarEventAction(
|
_parseCalendarEventAction(
|
||||||
accountId: mailboxDashBoardController.accountId.value!,
|
accountId: mailboxDashBoardController.accountId.value!,
|
||||||
blobIds: success.attachments.calendarEventBlobIds,
|
blobIds: success.attachments.calendarEventBlobIds,
|
||||||
emailContents: success.emailContent
|
emailContents: success.htmlEmailContent
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
emailContents.value = success.emailContentDisplayed;
|
emailContents.value = success.htmlEmailContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PlatformInfo.isMobile) {
|
if (PlatformInfo.isMobile) {
|
||||||
@@ -449,7 +458,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
attachments: success.attachments,
|
attachments: success.attachments,
|
||||||
headers: currentEmail?.emailHeader?.toSet(),
|
headers: currentEmail?.emailHeader?.toSet(),
|
||||||
keywords: currentEmail?.keywords,
|
keywords: currentEmail?.keywords,
|
||||||
htmlEmailContent: success.emailContentDisplayed
|
htmlEmailContent: success.htmlEmailContent
|
||||||
);
|
);
|
||||||
|
|
||||||
_storeOpenedEmailAction(
|
_storeOpenedEmailAction(
|
||||||
@@ -978,13 +987,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
|
|
||||||
void composeEmailFromEmailAddress(EmailAddress emailAddress) {
|
void composeEmailFromEmailAddress(EmailAddress emailAddress) {
|
||||||
popBack();
|
popBack();
|
||||||
|
mailboxDashBoardController.goToComposer(ComposerArguments.fromEmailAddress(emailAddress));
|
||||||
final arguments = ComposerArguments(
|
|
||||||
emailActionType: EmailActionType.composeFromEmailAddress,
|
|
||||||
emailAddress: emailAddress,
|
|
||||||
mailboxRole: mailboxDashBoardController.selectedMailbox.value?.role);
|
|
||||||
|
|
||||||
mailboxDashBoardController.goToComposer(arguments);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void openMailToLink(Uri? uri) {
|
void openMailToLink(Uri? uri) {
|
||||||
@@ -993,12 +996,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
log('SingleEmailController::openMailToLink(): address: $address');
|
log('SingleEmailController::openMailToLink(): address: $address');
|
||||||
if (address.isNotEmpty) {
|
if (address.isNotEmpty) {
|
||||||
final emailAddress = EmailAddress(null, address);
|
final emailAddress = EmailAddress(null, address);
|
||||||
final arguments = ComposerArguments(
|
mailboxDashBoardController.goToComposer(ComposerArguments.fromEmailAddress(emailAddress));
|
||||||
emailActionType: EmailActionType.composeFromEmailAddress,
|
|
||||||
emailAddress: emailAddress,
|
|
||||||
mailboxRole: mailboxDashBoardController.selectedMailbox.value?.role);
|
|
||||||
|
|
||||||
mailboxDashBoardController.goToComposer(arguments);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1158,19 +1156,43 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pressEmailAction(EmailActionType emailActionType) {
|
void pressEmailAction(
|
||||||
if (emailActionType == EmailActionType.compose) {
|
EmailActionType emailActionType,
|
||||||
mailboxDashBoardController.goToComposer(ComposerArguments());
|
PresentationEmail presentationEmail
|
||||||
} else {
|
) {
|
||||||
final arguments = ComposerArguments(
|
switch(emailActionType) {
|
||||||
emailActionType: emailActionType,
|
case EmailActionType.compose:
|
||||||
presentationEmail: mailboxDashBoardController.selectedEmail.value!,
|
mailboxDashBoardController.goToComposer(ComposerArguments());
|
||||||
emailContents: _currentEmailLoaded?.originalContent,
|
break;
|
||||||
attachments: emailActionType == EmailActionType.forward ? attachments : null,
|
case EmailActionType.reply:
|
||||||
mailboxRole: mailboxDashBoardController.selectedMailbox.value?.role
|
mailboxDashBoardController.goToComposer(
|
||||||
);
|
ComposerArguments.replyEmail(
|
||||||
|
presentationEmail: presentationEmail,
|
||||||
mailboxDashBoardController.goToComposer(arguments);
|
content: _currentEmailLoaded?.htmlContent ?? '',
|
||||||
|
mailboxRole: presentationEmail.mailboxContain?.role
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case EmailActionType.replyAll:
|
||||||
|
mailboxDashBoardController.goToComposer(
|
||||||
|
ComposerArguments.replyAllEmail(
|
||||||
|
presentationEmail: presentationEmail,
|
||||||
|
content: _currentEmailLoaded?.htmlContent ?? '',
|
||||||
|
mailboxRole: presentationEmail.mailboxContain?.role
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case EmailActionType.forward:
|
||||||
|
mailboxDashBoardController.goToComposer(
|
||||||
|
ComposerArguments.forwardEmail(
|
||||||
|
presentationEmail: presentationEmail,
|
||||||
|
content: _currentEmailLoaded?.htmlContent ?? '',
|
||||||
|
attachments: attachments
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1276,7 +1298,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
|
|
||||||
void _handleParseCalendarEventFailure(ParseCalendarEventFailure failure) {
|
void _handleParseCalendarEventFailure(ParseCalendarEventFailure failure) {
|
||||||
emailLoadedViewState.value = Left<Failure, Success>(failure);
|
emailLoadedViewState.value = Left<Failure, Success>(failure);
|
||||||
emailContents.value = _currentEmailLoaded?.displayedContent;
|
emailContents.value = _currentEmailLoaded?.htmlContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _enableScrollPageView() {
|
void _enableScrollPageView() {
|
||||||
@@ -1289,11 +1311,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
|
|
||||||
void openNewComposerAction(String mailTo) {
|
void openNewComposerAction(String mailTo) {
|
||||||
final emailAddress = EmailAddress(mailTo, mailTo);
|
final emailAddress = EmailAddress(mailTo, mailTo);
|
||||||
final arguments = ComposerArguments(
|
mailboxDashBoardController.goToComposer(ComposerArguments.fromEmailAddress(emailAddress));
|
||||||
emailActionType: EmailActionType.composeFromEmailAddress,
|
|
||||||
emailAddress: emailAddress,
|
|
||||||
mailboxRole: mailboxDashBoardController.selectedMailbox.value?.role
|
|
||||||
);
|
|
||||||
mailboxDashBoardController.goToComposer(arguments);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
|||||||
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||||
import 'package:model/model.dart';
|
import 'package:model/model.dart';
|
||||||
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox_dashboard/data/model/composer_cache.dart';
|
||||||
import 'package:tmail_ui_user/features/sending_queue/domain/model/sending_email.dart';
|
import 'package:tmail_ui_user/features/sending_queue/domain/model/sending_email.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/router_arguments.dart';
|
import 'package:tmail_ui_user/main/routes/router_arguments.dart';
|
||||||
|
|
||||||
@@ -26,6 +27,84 @@ class ComposerArguments extends RouterArguments {
|
|||||||
this.sendingEmail
|
this.sendingEmail
|
||||||
});
|
});
|
||||||
|
|
||||||
|
factory ComposerArguments.fromSendingEmail(SendingEmail sendingEmail) =>
|
||||||
|
ComposerArguments(
|
||||||
|
emailActionType: EmailActionType.editSendingEmail,
|
||||||
|
sendingEmail: sendingEmail
|
||||||
|
);
|
||||||
|
|
||||||
|
factory ComposerArguments.fromContentShared(String content) =>
|
||||||
|
ComposerArguments(
|
||||||
|
emailActionType: EmailActionType.composeFromContentShared,
|
||||||
|
emailContents: content
|
||||||
|
);
|
||||||
|
|
||||||
|
factory ComposerArguments.fromFileShared(List<SharedMediaFile> filesShared) =>
|
||||||
|
ComposerArguments(
|
||||||
|
emailActionType: EmailActionType.composeFromFileShared,
|
||||||
|
listSharedMediaFile: filesShared
|
||||||
|
);
|
||||||
|
|
||||||
|
factory ComposerArguments.fromEmailAddress(EmailAddress emailAddress) =>
|
||||||
|
ComposerArguments(
|
||||||
|
emailActionType: EmailActionType.composeFromEmailAddress,
|
||||||
|
emailAddress: emailAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
factory ComposerArguments.editDraftEmail(PresentationEmail presentationEmail) =>
|
||||||
|
ComposerArguments(
|
||||||
|
emailActionType: EmailActionType.editDraft,
|
||||||
|
presentationEmail: presentationEmail
|
||||||
|
);
|
||||||
|
|
||||||
|
factory ComposerArguments.fromSessionStorageBrowser(ComposerCache composerCache) =>
|
||||||
|
ComposerArguments(
|
||||||
|
emailActionType: EmailActionType.reopenComposerBrowser,
|
||||||
|
presentationEmail: PresentationEmail(
|
||||||
|
id: composerCache.id,
|
||||||
|
subject: composerCache.subject,
|
||||||
|
from: composerCache.from,
|
||||||
|
to: composerCache.to,
|
||||||
|
cc: composerCache.cc,
|
||||||
|
bcc: composerCache.bcc,
|
||||||
|
),
|
||||||
|
emailContents: composerCache.emailContentList.asHtmlString,
|
||||||
|
);
|
||||||
|
|
||||||
|
factory ComposerArguments.replyEmail({
|
||||||
|
required PresentationEmail presentationEmail,
|
||||||
|
required String content,
|
||||||
|
Role? mailboxRole,
|
||||||
|
}) => ComposerArguments(
|
||||||
|
emailActionType: EmailActionType.reply,
|
||||||
|
presentationEmail: presentationEmail,
|
||||||
|
emailContents: content,
|
||||||
|
mailboxRole: mailboxRole
|
||||||
|
);
|
||||||
|
|
||||||
|
factory ComposerArguments.replyAllEmail({
|
||||||
|
required PresentationEmail presentationEmail,
|
||||||
|
required String content,
|
||||||
|
Role? mailboxRole,
|
||||||
|
}) => ComposerArguments(
|
||||||
|
emailActionType: EmailActionType.replyAll,
|
||||||
|
presentationEmail: presentationEmail,
|
||||||
|
emailContents: content,
|
||||||
|
mailboxRole: mailboxRole
|
||||||
|
);
|
||||||
|
|
||||||
|
factory ComposerArguments.forwardEmail({
|
||||||
|
required PresentationEmail presentationEmail,
|
||||||
|
required String content,
|
||||||
|
required List<Attachment> attachments,
|
||||||
|
}) => ComposerArguments(
|
||||||
|
emailActionType: EmailActionType.forward,
|
||||||
|
presentationEmail: presentationEmail,
|
||||||
|
emailContents: content,
|
||||||
|
attachments: attachments,
|
||||||
|
mailboxRole: presentationEmail.mailboxContain?.role
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
emailActionType,
|
emailActionType,
|
||||||
|
|||||||
@@ -3,22 +3,19 @@ import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
|||||||
import 'package:model/email/attachment.dart';
|
import 'package:model/email/attachment.dart';
|
||||||
|
|
||||||
class EmailLoaded with EquatableMixin {
|
class EmailLoaded with EquatableMixin {
|
||||||
final String originalContent;
|
final String htmlContent;
|
||||||
final String displayedContent;
|
|
||||||
final List<Attachment> attachments;
|
final List<Attachment> attachments;
|
||||||
final Email? emailCurrent;
|
final Email? emailCurrent;
|
||||||
|
|
||||||
EmailLoaded({
|
EmailLoaded({
|
||||||
required this.originalContent,
|
required this.htmlContent,
|
||||||
required this.displayedContent,
|
|
||||||
required this.attachments,
|
required this.attachments,
|
||||||
this.emailCurrent,
|
this.emailCurrent,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
originalContent,
|
htmlContent,
|
||||||
displayedContent,
|
|
||||||
attachments,
|
attachments,
|
||||||
emailCurrent
|
emailCurrent
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ import 'package:model/email/presentation_email.dart';
|
|||||||
import 'package:model/extensions/presentation_email_extension.dart';
|
import 'package:model/extensions/presentation_email_extension.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
typedef OnPressEmailActionClick = void Function(EmailActionType emailActionType);
|
typedef OnPressEmailActionClick = void Function(
|
||||||
|
EmailActionType emailActionType,
|
||||||
|
PresentationEmail presentationEmail
|
||||||
|
);
|
||||||
|
|
||||||
class BottomBarMailWidgetBuilder extends StatelessWidget {
|
class BottomBarMailWidgetBuilder extends StatelessWidget {
|
||||||
|
|
||||||
@@ -60,7 +63,7 @@ class BottomBarMailWidgetBuilder extends StatelessWidget {
|
|||||||
color: AppColor.colorTextButton
|
color: AppColor.colorTextButton
|
||||||
),
|
),
|
||||||
verticalDirection: responsiveUtils.isPortraitMobile(context),
|
verticalDirection: responsiveUtils.isPortraitMobile(context),
|
||||||
onTapActionCallback: () => onPressEmailActionClick?.call(EmailActionType.replyAll),
|
onTapActionCallback: () => onPressEmailActionClick?.call(EmailActionType.replyAll, _presentationEmail),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -79,7 +82,7 @@ class BottomBarMailWidgetBuilder extends StatelessWidget {
|
|||||||
color: AppColor.colorTextButton
|
color: AppColor.colorTextButton
|
||||||
),
|
),
|
||||||
verticalDirection: responsiveUtils.isPortraitMobile(context),
|
verticalDirection: responsiveUtils.isPortraitMobile(context),
|
||||||
onTapActionCallback: () => onPressEmailActionClick?.call(EmailActionType.reply),
|
onTapActionCallback: () => onPressEmailActionClick?.call(EmailActionType.reply, _presentationEmail),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -98,7 +101,7 @@ class BottomBarMailWidgetBuilder extends StatelessWidget {
|
|||||||
color: AppColor.colorTextButton
|
color: AppColor.colorTextButton
|
||||||
),
|
),
|
||||||
verticalDirection: responsiveUtils.isPortraitMobile(context),
|
verticalDirection: responsiveUtils.isPortraitMobile(context),
|
||||||
onTapActionCallback: () => onPressEmailActionClick?.call(EmailActionType.forward),
|
onTapActionCallback: () => onPressEmailActionClick?.call(EmailActionType.forward, _presentationEmail),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -117,7 +120,7 @@ class BottomBarMailWidgetBuilder extends StatelessWidget {
|
|||||||
color: AppColor.colorTextButton
|
color: AppColor.colorTextButton
|
||||||
),
|
),
|
||||||
verticalDirection: responsiveUtils.isPortraitMobile(context),
|
verticalDirection: responsiveUtils.isPortraitMobile(context),
|
||||||
onTapActionCallback: () => onPressEmailActionClick?.call(EmailActionType.compose),
|
onTapActionCallback: () => onPressEmailActionClick?.call(EmailActionType.compose, _presentationEmail),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
+6
-10
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:core/core.dart';
|
import 'package:core/core.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
import 'package:model/model.dart';
|
import 'package:model/model.dart';
|
||||||
@@ -11,13 +12,9 @@ class SessionStorageComposerDatasourceImpl
|
|||||||
@override
|
@override
|
||||||
ComposerCache getComposerCacheOnWeb() {
|
ComposerCache getComposerCacheOnWeb() {
|
||||||
try {
|
try {
|
||||||
final result = html.window.sessionStorage.entries
|
final result = html.window.sessionStorage.entries.firstWhereOrNull((e) => e.key == EmailActionType.reopenComposerBrowser.name);
|
||||||
.where((e) => e.key == EmailActionType.edit.name)
|
if (result != null) {
|
||||||
.toList();
|
final emailCache = ComposerCache.fromJson(jsonDecode(result.value));
|
||||||
if (result.isNotEmpty) {
|
|
||||||
final jsonHandle =
|
|
||||||
json.decode(result.first.value) as Map<String, dynamic>;
|
|
||||||
final emailCache = ComposerCache.fromJson(jsonHandle);
|
|
||||||
return emailCache;
|
return emailCache;
|
||||||
} else {
|
} else {
|
||||||
throw NotFoundInWebSessionException();
|
throw NotFoundInWebSessionException();
|
||||||
@@ -30,8 +27,7 @@ class SessionStorageComposerDatasourceImpl
|
|||||||
@override
|
@override
|
||||||
void removeComposerCacheOnWeb() {
|
void removeComposerCacheOnWeb() {
|
||||||
try {
|
try {
|
||||||
html.window.sessionStorage
|
html.window.sessionStorage.removeWhere((key, value) => key == EmailActionType.reopenComposerBrowser.name);
|
||||||
.removeWhere((key, value) => key == EmailActionType.edit.name);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw NotFoundInWebSessionException(errorMessage: e.toString());
|
throw NotFoundInWebSessionException(errorMessage: e.toString());
|
||||||
}
|
}
|
||||||
@@ -41,7 +37,7 @@ class SessionStorageComposerDatasourceImpl
|
|||||||
void saveComposerCacheOnWeb(Email email) {
|
void saveComposerCacheOnWeb(Email email) {
|
||||||
try {
|
try {
|
||||||
Map<String, String> entries = {
|
Map<String, String> entries = {
|
||||||
EmailActionType.edit.name: json.encode(email.toJson())
|
EmailActionType.reopenComposerBrowser.name: email.asString()
|
||||||
};
|
};
|
||||||
html.window.sessionStorage.addAll(entries);
|
html.window.sessionStorage.addAll(entries);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
+25
-49
@@ -204,7 +204,8 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
ComposerArguments? composerArguments;
|
ComposerArguments? composerArguments;
|
||||||
NavigationRouter? navigationRouter;
|
NavigationRouter? navigationRouter;
|
||||||
|
|
||||||
late StreamSubscription _emailReceiveManagerStreamSubscription;
|
late StreamSubscription _emailAddressStreamSubscription;
|
||||||
|
late StreamSubscription _emailContentStreamSubscription;
|
||||||
late StreamSubscription _fileReceiveManagerStreamSubscription;
|
late StreamSubscription _fileReceiveManagerStreamSubscription;
|
||||||
|
|
||||||
final StreamController<Either<Failure, Success>> _progressStateController =
|
final StreamController<Either<Failure, Success>> _progressStateController =
|
||||||
@@ -267,19 +268,7 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
(failure) {},
|
(failure) {},
|
||||||
(success) {
|
(success) {
|
||||||
if(success is GetComposerCacheSuccess){
|
if(success is GetComposerCacheSuccess){
|
||||||
final ComposerArguments composerArguments = ComposerArguments(
|
openComposerOverlay(ComposerArguments.fromSessionStorageBrowser(success.composerCache));
|
||||||
emailActionType: EmailActionType.edit,
|
|
||||||
presentationEmail: PresentationEmail(
|
|
||||||
id: success.composerCache.id,
|
|
||||||
subject: success.composerCache.subject,
|
|
||||||
from: success.composerCache.from,
|
|
||||||
to: success.composerCache.to,
|
|
||||||
cc: success.composerCache.cc,
|
|
||||||
bcc: success.composerCache.bcc,
|
|
||||||
),
|
|
||||||
emailContents: success.composerCache.emailContentList.asHtmlString,
|
|
||||||
);
|
|
||||||
openComposerOverlay(composerArguments);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -373,49 +362,33 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _registerPendingEmailAddress() {
|
void _registerPendingEmailAddress() {
|
||||||
_emailReceiveManagerStreamSubscription =
|
_emailAddressStreamSubscription =
|
||||||
_emailReceiveManager.pendingEmailAddressInfo.stream.listen((emailAddress) {
|
_emailReceiveManager.pendingEmailAddressInfo.stream.listen((emailAddress) {
|
||||||
log('MailboxDashBoardController::_registerPendingEmailAddress(): ${emailAddress?.email}');
|
if (emailAddress?.email?.isNotEmpty == true) {
|
||||||
if (emailAddress != null && emailAddress.email?.isNotEmpty == true) {
|
_emailReceiveManager.clearPendingEmailAddress();
|
||||||
_emailReceiveManager.clearPendingEmailAddress();
|
goToComposer(ComposerArguments.fromEmailAddress(emailAddress!));
|
||||||
final arguments = ComposerArguments(
|
}
|
||||||
emailActionType: EmailActionType.composeFromEmailAddress,
|
});
|
||||||
emailAddress: emailAddress,
|
|
||||||
mailboxRole: selectedMailbox.value?.role);
|
|
||||||
goToComposer(arguments);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _registerPendingEmailContents() {
|
void _registerPendingEmailContents() {
|
||||||
_emailReceiveManagerStreamSubscription =
|
_emailContentStreamSubscription =
|
||||||
_emailReceiveManager.pendingEmailContentInfo.stream.listen((emailContent) {
|
_emailReceiveManager.pendingEmailContentInfo.stream.listen((emailContent) {
|
||||||
log('MailboxDashBoardController::_registerPendingEmailContents(): ${emailContent?.content}');
|
if (emailContent?.content.isNotEmpty == true) {
|
||||||
if (emailContent != null && emailContent.content.isNotEmpty == true) {
|
|
||||||
_emailReceiveManager.clearPendingEmailContent();
|
_emailReceiveManager.clearPendingEmailContent();
|
||||||
final arguments = ComposerArguments(
|
goToComposer(ComposerArguments.fromContentShared([emailContent!].asHtmlString));
|
||||||
emailActionType: EmailActionType.edit,
|
}
|
||||||
emailContents: [emailContent].asHtmlString,
|
});
|
||||||
mailboxRole: selectedMailbox.value?.role);
|
|
||||||
goToComposer(arguments);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _registerPendingFileInfo() {
|
void _registerPendingFileInfo() {
|
||||||
_fileReceiveManagerStreamSubscription =
|
_fileReceiveManagerStreamSubscription =
|
||||||
_emailReceiveManager.pendingFileInfo.stream.listen((listFile) {
|
_emailReceiveManager.pendingFileInfo.stream.listen((listFile) {
|
||||||
log('MailboxDashBoardController::_registerPendingFileInfo(): ${listFile.length}');
|
if (listFile.isNotEmpty) {
|
||||||
if (listFile.isNotEmpty && sessionCurrent != null) {
|
_emailReceiveManager.clearPendingFileInfo();
|
||||||
_emailReceiveManager.clearPendingFileInfo();
|
goToComposer(ComposerArguments.fromFileShared(listFile));
|
||||||
final arguments = ComposerArguments(
|
}
|
||||||
emailActionType: EmailActionType.edit,
|
});
|
||||||
mailboxRole: selectedMailbox.value?.role,
|
|
||||||
listSharedMediaFile: listFile,
|
|
||||||
);
|
|
||||||
goToComposer(arguments);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _registerStreamListener() {
|
void _registerStreamListener() {
|
||||||
@@ -2071,11 +2044,14 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
void selectAllEmailAction() {
|
void selectAllEmailAction() {
|
||||||
dispatchAction(SelectionAllEmailAction());
|
dispatchAction(SelectionAllEmailAction());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String get baseDownloadUrl => sessionCurrent?.getDownloadUrl(jmapUrl: dynamicUrlInterceptors.jmapUrl) ?? '';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
_emailReceiveManager.closeEmailReceiveManagerStream();
|
_emailReceiveManager.closeEmailReceiveManagerStream();
|
||||||
_emailReceiveManagerStreamSubscription.cancel();
|
_emailAddressStreamSubscription.cancel();
|
||||||
|
_emailContentStreamSubscription.cancel();
|
||||||
_fileReceiveManagerStreamSubscription.cancel();
|
_fileReceiveManagerStreamSubscription.cancel();
|
||||||
_progressStateController.close();
|
_progressStateController.close();
|
||||||
_refreshActionEventController.close();
|
_refreshActionEventController.close();
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class IdentityDataSourceImpl extends IdentityDataSource {
|
|||||||
Future<String> transformHtmlSignature(String signature) {
|
Future<String> transformHtmlSignature(String signature) {
|
||||||
return Future.sync(() async {
|
return Future.sync(() async {
|
||||||
final signatureUnescape = await _htmlTransform.transformToHtml(
|
final signatureUnescape = await _htmlTransform.transformToHtml(
|
||||||
contentHtml: signature,
|
htmlContent: signature,
|
||||||
transformConfiguration: TransformConfiguration.create(customDomTransformers: [
|
transformConfiguration: TransformConfiguration.create(customDomTransformers: [
|
||||||
const RemoveScriptTransformer(),
|
const RemoveScriptTransformer(),
|
||||||
const BlockQuotedTransformer(),
|
const BlockQuotedTransformer(),
|
||||||
|
|||||||
@@ -664,7 +664,7 @@ class SearchEmailController extends BaseController
|
|||||||
switch(actionType) {
|
switch(actionType) {
|
||||||
case EmailActionType.preview:
|
case EmailActionType.preview:
|
||||||
if (mailboxContain?.isDrafts == true) {
|
if (mailboxContain?.isDrafts == true) {
|
||||||
editEmail(selectedEmail);
|
editDraftEmail(selectedEmail);
|
||||||
} else {
|
} else {
|
||||||
previewEmail(selectedEmail);
|
previewEmail(selectedEmail);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ import 'package:get/get.dart';
|
|||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/user_name.dart';
|
import 'package:jmap_dart_client/jmap/core/user_name.dart';
|
||||||
import 'package:model/email/email_action_type.dart';
|
|
||||||
import 'package:model/extensions/list_email_content_extension.dart';
|
|
||||||
import 'package:model/mailbox/select_mode.dart';
|
import 'package:model/mailbox/select_mode.dart';
|
||||||
import 'package:tmail_ui_user/features/base/base_controller.dart';
|
import 'package:tmail_ui_user/features/base/base_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_mixin.dart';
|
import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_mixin.dart';
|
||||||
@@ -270,16 +268,7 @@ class SendingQueueController extends BaseController with MessageDialogActionMixi
|
|||||||
|
|
||||||
void _editSendingEmailAction(SendingEmail sendingEmail) {
|
void _editSendingEmailAction(SendingEmail sendingEmail) {
|
||||||
disableSelectionMode();
|
disableSelectionMode();
|
||||||
|
dashboardController?.goToComposer(ComposerArguments.fromSendingEmail(sendingEmail));
|
||||||
final arguments = ComposerArguments(
|
|
||||||
emailActionType: EmailActionType.edit,
|
|
||||||
presentationEmail: sendingEmail.presentationEmail,
|
|
||||||
mailboxRole: dashboardController?.selectedMailbox.value?.role,
|
|
||||||
emailContents: sendingEmail.presentationEmail.emailContentList.asHtmlString,
|
|
||||||
sendingEmail: sendingEmail
|
|
||||||
);
|
|
||||||
|
|
||||||
dashboardController?.goToComposer(arguments);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _resendSendingEmailAction(List<SendingEmail> listSendingEmails) async {
|
void _resendSendingEmailAction(List<SendingEmail> listSendingEmails) async {
|
||||||
|
|||||||
@@ -37,13 +37,8 @@ mixin EmailActionController {
|
|||||||
final responsiveUtils = Get.find<ResponsiveUtils>();
|
final responsiveUtils = Get.find<ResponsiveUtils>();
|
||||||
final imagePaths = Get.find<ImagePaths>();
|
final imagePaths = Get.find<ImagePaths>();
|
||||||
|
|
||||||
void editEmail(PresentationEmail presentationEmail) {
|
void editDraftEmail(PresentationEmail presentationEmail) {
|
||||||
final arguments = ComposerArguments(
|
mailboxDashBoardController.goToComposer(ComposerArguments.editDraftEmail(presentationEmail));
|
||||||
emailActionType: EmailActionType.edit,
|
|
||||||
presentationEmail: presentationEmail,
|
|
||||||
mailboxRole: mailboxDashBoardController.selectedMailbox.value?.role);
|
|
||||||
|
|
||||||
mailboxDashBoardController.goToComposer(arguments);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void previewEmail(PresentationEmail presentationEmail) {
|
void previewEmail(PresentationEmail presentationEmail) {
|
||||||
|
|||||||
@@ -873,7 +873,7 @@ class ThreadController extends BaseController with EmailActionController {
|
|||||||
switch(actionType) {
|
switch(actionType) {
|
||||||
case EmailActionType.preview:
|
case EmailActionType.preview:
|
||||||
if (mailboxContain?.isDrafts == true) {
|
if (mailboxContain?.isDrafts == true) {
|
||||||
editEmail(selectedEmail);
|
editDraftEmail(selectedEmail);
|
||||||
} else {
|
} else {
|
||||||
previewEmail(selectedEmail);
|
previewEmail(selectedEmail);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ class NetworkBindings extends Bindings {
|
|||||||
|
|
||||||
void _bindingTransformer() {
|
void _bindingTransformer() {
|
||||||
Get.put(const HtmlEscape());
|
Get.put(const HtmlEscape());
|
||||||
Get.put(HtmlTransform(Get.find<DioClient>()));
|
Get.put(HtmlTransform(Get.find<DioClient>(), Get.find<HtmlEscape>()));
|
||||||
Get.put(HtmlAnalyzer(Get.find<HtmlTransform>(), Get.find<HtmlEscape>()));
|
Get.put(HtmlAnalyzer(Get.find<HtmlTransform>()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,8 +9,12 @@ enum EmailActionType {
|
|||||||
markAsStarred,
|
markAsStarred,
|
||||||
unMarkAsStarred,
|
unMarkAsStarred,
|
||||||
moveToMailbox,
|
moveToMailbox,
|
||||||
edit,
|
editDraft,
|
||||||
|
editSendingEmail,
|
||||||
|
composeFromContentShared,
|
||||||
|
composeFromFileShared,
|
||||||
composeFromEmailAddress,
|
composeFromEmailAddress,
|
||||||
|
reopenComposerBrowser,
|
||||||
moveToTrash,
|
moveToTrash,
|
||||||
deletePermanently,
|
deletePermanently,
|
||||||
preview,
|
preview,
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ extension EmailExtension on Email {
|
|||||||
|
|
||||||
bool get hasMdnSent => keywords?.containsKey(KeyWordIdentifier.mdnSent) == true;
|
bool get hasMdnSent => keywords?.containsKey(KeyWordIdentifier.mdnSent) == true;
|
||||||
|
|
||||||
|
bool get isDraft => keywords?.containsKey(KeyWordIdentifier.emailDraft) == true;
|
||||||
|
|
||||||
bool get withAttachments => hasAttachment == true;
|
bool get withAttachments => hasAttachment == true;
|
||||||
|
|
||||||
bool hasReadReceipt(Map<MailboxId, PresentationMailbox> mapMailbox) {
|
bool hasReadReceipt(Map<MailboxId, PresentationMailbox> mapMailbox) {
|
||||||
@@ -127,6 +129,13 @@ extension EmailExtension on Email {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Attachment> get attachmentsWithCid {
|
||||||
|
return attachments
|
||||||
|
?.where((element) => element.disposition != null && element.cid?.isNotEmpty == true)
|
||||||
|
.map((item) => item.toAttachment())
|
||||||
|
.toList() ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
PresentationMailbox? findMailboxContain(Map<MailboxId, PresentationMailbox> mapMailbox) {
|
PresentationMailbox? findMailboxContain(Map<MailboxId, PresentationMailbox> mapMailbox) {
|
||||||
final newMailboxIds = mailboxIds;
|
final newMailboxIds = mailboxIds;
|
||||||
newMailboxIds?.removeWhere((key, value) => !value);
|
newMailboxIds?.removeWhere((key, value) => !value);
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import 'package:model/model.dart';
|
|
||||||
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
|
import 'package:model/email/attachment.dart';
|
||||||
|
|
||||||
extension ListAttachmentExtension on List<Attachment> {
|
extension ListAttachmentExtension on List<Attachment> {
|
||||||
|
|
||||||
@@ -20,4 +22,15 @@ extension ListAttachmentExtension on List<Attachment> {
|
|||||||
return where((attachment) => attachment.hasCid())
|
return where((attachment) => attachment.hasCid())
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, String> toMapCidImageDownloadUrl({
|
||||||
|
required AccountId accountId,
|
||||||
|
required String downloadUrl
|
||||||
|
}) {
|
||||||
|
final mapUrlDownloadCID = {
|
||||||
|
for (var attachment in listAttachmentsDisplayedInContent)
|
||||||
|
attachment.cid! : attachment.getDownloadUrl(downloadUrl, accountId)
|
||||||
|
};
|
||||||
|
return mapUrlDownloadCID;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -29,9 +29,9 @@ extension ListEmailAddressExtension on Set<EmailAddress>? {
|
|||||||
|
|
||||||
int numberEmailAddress() => this != null ? this!.length : 0;
|
int numberEmailAddress() => this != null ? this!.length : 0;
|
||||||
|
|
||||||
List<EmailAddress> filterEmailAddress(EmailAddress emailAddressNotExist) {
|
List<EmailAddress> filterEmailAddress(String emailAddressNotExist) {
|
||||||
return this != null
|
return this != null
|
||||||
? this!.where((emailAddress) => emailAddress.email != emailAddressNotExist.email).toList()
|
? this!.where((emailAddress) => emailAddress.email != emailAddressNotExist).toList()
|
||||||
: List.empty();
|
: List.empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,10 +110,10 @@ extension PresentationEmailExtension on PresentationEmail {
|
|||||||
return allEmailAddress.isNotEmpty ? allEmailAddress.join(', ') : '';
|
return allEmailAddress.isNotEmpty ? allEmailAddress.join(', ') : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple3<List<EmailAddress>, List<EmailAddress>, List<EmailAddress>> generateRecipientsEmailAddressForComposer(
|
Tuple3<List<EmailAddress>, List<EmailAddress>, List<EmailAddress>> generateRecipientsEmailAddressForComposer({
|
||||||
EmailActionType? emailActionType,
|
required EmailActionType emailActionType,
|
||||||
Role? mailboxRole
|
Role? mailboxRole
|
||||||
) {
|
}) {
|
||||||
switch(emailActionType) {
|
switch(emailActionType) {
|
||||||
case EmailActionType.reply:
|
case EmailActionType.reply:
|
||||||
if (mailboxRole == PresentationMailbox.roleSent) {
|
if (mailboxRole == PresentationMailbox.roleSent) {
|
||||||
@@ -128,10 +128,8 @@ extension PresentationEmailExtension on PresentationEmail {
|
|||||||
} else {
|
} else {
|
||||||
return Tuple3(to.asList() + from.asList(), cc.asList(), bcc.asList());
|
return Tuple3(to.asList() + from.asList(), cc.asList(), bcc.asList());
|
||||||
}
|
}
|
||||||
case EmailActionType.edit:
|
|
||||||
return Tuple3(to.asList(), cc.asList(), bcc.asList());
|
|
||||||
default:
|
default:
|
||||||
return const Tuple3([], [], []);
|
return Tuple3(to.asList(), cc.asList(), bcc.asList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user