Fix appear save draft dialog when reload editing composer
Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
@@ -39,6 +39,7 @@ class DownloadClient {
|
|||||||
String cid,
|
String cid,
|
||||||
String fileExtension,
|
String fileExtension,
|
||||||
String fileName,
|
String fileName,
|
||||||
|
String mimeType,
|
||||||
{
|
{
|
||||||
String? filePath,
|
String? filePath,
|
||||||
double? maxWidth,
|
double? maxWidth,
|
||||||
@@ -58,10 +59,14 @@ class DownloadClient {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final imageType = mimeType == 'application/octet-stream'
|
||||||
|
? 'image/$fileExtension'
|
||||||
|
: mimeType;
|
||||||
|
|
||||||
if (PlatformInfo.isWeb) {
|
if (PlatformInfo.isWeb) {
|
||||||
final base64Uri = encodeToBase64Uri({
|
final base64Uri = encodeToBase64Uri({
|
||||||
'bytesData': bytesData,
|
'bytesData': bytesData,
|
||||||
'mimeType': 'image/$fileExtension',
|
'mimeType': imageType,
|
||||||
'cid': cid,
|
'cid': cid,
|
||||||
'fileName': fileName,
|
'fileName': fileName,
|
||||||
'maxWidth': maxWidth
|
'maxWidth': maxWidth
|
||||||
@@ -76,7 +81,7 @@ class DownloadClient {
|
|||||||
|
|
||||||
final base64Uri = await compute(encodeToBase64Uri, {
|
final base64Uri = await compute(encodeToBase64Uri, {
|
||||||
'bytesData': bytesDataCompressed,
|
'bytesData': bytesDataCompressed,
|
||||||
'mimeType': 'image/$fileExtension',
|
'mimeType': imageType,
|
||||||
'cid': cid,
|
'cid': cid,
|
||||||
'fileName': fileName,
|
'fileName': fileName,
|
||||||
'maxWidth': maxWidth
|
'maxWidth': maxWidth
|
||||||
@@ -86,7 +91,7 @@ class DownloadClient {
|
|||||||
} else {
|
} else {
|
||||||
final base64Uri = await compute(encodeToBase64Uri, {
|
final base64Uri = await compute(encodeToBase64Uri, {
|
||||||
'bytesData': bytesData,
|
'bytesData': bytesData,
|
||||||
'mimeType': 'image/$fileExtension',
|
'mimeType': imageType,
|
||||||
'cid': cid,
|
'cid': cid,
|
||||||
'fileName': fileName,
|
'fileName': fileName,
|
||||||
'maxWidth': maxWidth
|
'maxWidth': maxWidth
|
||||||
@@ -113,7 +118,8 @@ class DownloadClient {
|
|||||||
if (fileName.contains('.')) {
|
if (fileName.contains('.')) {
|
||||||
fileName = fileName.split('.').first;
|
fileName = fileName.split('.').first;
|
||||||
}
|
}
|
||||||
final base64Uri = '<img src="${HtmlUtils.convertBase64ToImageResourceData(base64Data: base64Data, mimeType: mimeType)}" alt="$fileName" id="cid:$cid" style="max-width: $maxWidth;" />';
|
mimeType = HtmlUtils.validateHtmlImageResourceMimeType(mimeType);
|
||||||
|
final base64Uri = '<img src="${HtmlUtils.convertBase64ToImageResourceData(base64Data: base64Data, mimeType: mimeType)}" alt="$fileName" style="max-width: $maxWidth;" data-mimetype="$mimeType" id="cid:$cid" />';
|
||||||
return base64Uri;
|
return base64Uri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,12 +45,13 @@ class ImageTransformer extends DomTransformer {
|
|||||||
final src = imageElement.attributes['src'];
|
final src = imageElement.attributes['src'];
|
||||||
|
|
||||||
if (src == null) return;
|
if (src == null) return;
|
||||||
|
final mimeType = imageElement.attributes['data-mimetype'];
|
||||||
if (src.startsWith('cid:') && mapUrlDownloadCID != null) {
|
if (src.startsWith('cid:') && mapUrlDownloadCID != null) {
|
||||||
final imageBase64 = await _convertCidToBase64Image(
|
final imageBase64 = await _convertCidToBase64Image(
|
||||||
dioClient: dioClient,
|
dioClient: dioClient,
|
||||||
mapUrlDownloadCID: mapUrlDownloadCID,
|
mapUrlDownloadCID: mapUrlDownloadCID,
|
||||||
imageSource: src
|
imageSource: src,
|
||||||
|
mimeType: mimeType,
|
||||||
);
|
);
|
||||||
imageElement.attributes['src'] = imageBase64 ?? src;
|
imageElement.attributes['src'] = imageBase64 ?? src;
|
||||||
imageElement.attributes['id'] ??= src;
|
imageElement.attributes['id'] ??= src;
|
||||||
@@ -68,7 +69,8 @@ class ImageTransformer extends DomTransformer {
|
|||||||
Future<String?> _convertCidToBase64Image({
|
Future<String?> _convertCidToBase64Image({
|
||||||
required DioClient dioClient,
|
required DioClient dioClient,
|
||||||
required Map<String, String> mapUrlDownloadCID,
|
required Map<String, String> mapUrlDownloadCID,
|
||||||
required String imageSource
|
required String imageSource,
|
||||||
|
required String? mimeType,
|
||||||
}) async {
|
}) async {
|
||||||
final cid = imageSource.replaceFirst('cid:', '').trim();
|
final cid = imageSource.replaceFirst('cid:', '').trim();
|
||||||
final urlDownloadCid = mapUrlDownloadCID[cid];
|
final urlDownloadCid = mapUrlDownloadCID[cid];
|
||||||
@@ -79,7 +81,8 @@ class ImageTransformer extends DomTransformer {
|
|||||||
final imgBase64Uri = await loadAsyncNetworkImageToBase64(
|
final imgBase64Uri = await loadAsyncNetworkImageToBase64(
|
||||||
dioClient,
|
dioClient,
|
||||||
compressFileUtils,
|
compressFileUtils,
|
||||||
urlDownloadCid
|
urlDownloadCid,
|
||||||
|
mimeType,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (imgBase64Uri.isEmpty) return null;
|
if (imgBase64Uri.isEmpty) return null;
|
||||||
@@ -90,7 +93,8 @@ class ImageTransformer extends DomTransformer {
|
|||||||
Future<String> loadAsyncNetworkImageToBase64(
|
Future<String> loadAsyncNetworkImageToBase64(
|
||||||
DioClient dioClient,
|
DioClient dioClient,
|
||||||
CompressFileUtils compressFileUtils,
|
CompressFileUtils compressFileUtils,
|
||||||
String imageUrl
|
String imageUrl,
|
||||||
|
String? mimeType,
|
||||||
) async {
|
) async {
|
||||||
try {
|
try {
|
||||||
var responseData = await dioClient.get(
|
var responseData = await dioClient.get(
|
||||||
@@ -99,10 +103,16 @@ class ImageTransformer extends DomTransformer {
|
|||||||
|
|
||||||
if (responseData != null) {
|
if (responseData != null) {
|
||||||
if (PlatformInfo.isWeb) {
|
if (PlatformInfo.isWeb) {
|
||||||
return encodeToBase64Uri(responseData);
|
return encodeToBase64Uri({
|
||||||
|
'bytesData': responseData,
|
||||||
|
'mimeType': mimeType,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
final bytesCompressed = await compressFileUtils.compressBytesDataImage(responseData);
|
final bytesCompressed = await compressFileUtils.compressBytesDataImage(responseData);
|
||||||
final base64Uri = await compute(encodeToBase64Uri, bytesCompressed);
|
final base64Uri = await compute(encodeToBase64Uri, {
|
||||||
|
'bytesData': bytesCompressed,
|
||||||
|
'mimeType': mimeType,
|
||||||
|
});
|
||||||
return base64Uri;
|
return base64Uri;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -113,12 +123,12 @@ class ImageTransformer extends DomTransformer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static String encodeToBase64Uri(dynamic bytesData) {
|
static String encodeToBase64Uri(Map<String, dynamic> entryParam) {
|
||||||
final base64Data = base64Encode(bytesData);
|
final base64Data = base64Encode(entryParam['bytesData']);
|
||||||
if (!base64Data.endsWith('==')) {
|
if (!base64Data.endsWith('==')) {
|
||||||
base64Data.append('==');
|
base64Data.append('==');
|
||||||
}
|
}
|
||||||
final base64Uri = 'data:image/jpeg;base64,$base64Data';
|
final mimeType = entryParam['mimeType'] ?? 'image/jpeg';
|
||||||
return base64Uri;
|
return 'data:$mimeType;base64,$base64Data';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
@@ -11,6 +11,7 @@ class StandardizeHtmlSanitizingTransformers extends TextTransformer {
|
|||||||
'bgcolor',
|
'bgcolor',
|
||||||
'id',
|
'id',
|
||||||
'class',
|
'class',
|
||||||
|
'data-mimetype',
|
||||||
];
|
];
|
||||||
|
|
||||||
static const List<String> mailAllowedHtmlTags = [
|
static const List<String> mailAllowedHtmlTags = [
|
||||||
|
|||||||
@@ -99,13 +99,10 @@ class HtmlUtils {
|
|||||||
required String base64Data,
|
required String base64Data,
|
||||||
required String mimeType
|
required String mimeType
|
||||||
}) {
|
}) {
|
||||||
log('HtmlUtils::convertBase64ToImageResourceData:');
|
|
||||||
mimeType = validateHtmlImageResourceMimeType(mimeType);
|
|
||||||
if (!base64Data.endsWith('==')) {
|
if (!base64Data.endsWith('==')) {
|
||||||
base64Data.append('==');
|
base64Data.append('==');
|
||||||
}
|
}
|
||||||
final imageResource = 'data:$mimeType;base64,$base64Data';
|
return 'data:$mimeType;base64,$base64Data';
|
||||||
return imageResource;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static String generateHtmlDocument({
|
static String generateHtmlDocument({
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class ComposerDataSourceImpl extends ComposerDataSource {
|
|||||||
cid,
|
cid,
|
||||||
fileInfo.fileExtension,
|
fileInfo.fileExtension,
|
||||||
fileInfo.fileName,
|
fileInfo.fileName,
|
||||||
|
fileInfo.mimeType,
|
||||||
filePath: fileInfo.filePath,
|
filePath: fileInfo.filePath,
|
||||||
maxWidth: maxWidth,
|
maxWidth: maxWidth,
|
||||||
compress: compress);
|
compress: compress);
|
||||||
|
|||||||
@@ -113,4 +113,14 @@ class ComposerRepositoryImpl extends ComposerRepository {
|
|||||||
return Future.value(emailContent);
|
return Future.value(emailContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> removeStyleLazyLoadDisplayInlineImages({required String emailContent}) {
|
||||||
|
try {
|
||||||
|
return _htmlDataSource.removeStyleLazyLoadDisplayInlineImages(emailContent: emailContent);
|
||||||
|
} catch (e) {
|
||||||
|
logError('ComposerRepositoryImpl::removeStyleLazyLoadDisplayInlineImages: Exception: $e');
|
||||||
|
return Future.value(emailContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -17,4 +17,6 @@ abstract class ComposerRepository {
|
|||||||
Future<String?> downloadImageAsBase64(String url, String cid, FileInfo fileInfo, {double? maxWidth, bool? compress});
|
Future<String?> downloadImageAsBase64(String url, String cid, FileInfo fileInfo, {double? maxWidth, bool? compress});
|
||||||
|
|
||||||
Future<String> removeCollapsedExpandedSignatureEffect({required String emailContent});
|
Future<String> removeCollapsedExpandedSignatureEffect({required String emailContent});
|
||||||
|
|
||||||
|
Future<String> removeStyleLazyLoadDisplayInlineImages({required String emailContent});
|
||||||
}
|
}
|
||||||
@@ -1112,6 +1112,13 @@ class ComposerController extends BaseController
|
|||||||
emailContent = await _composerRepository.removeCollapsedExpandedSignatureEffect(
|
emailContent = await _composerRepository.removeCollapsedExpandedSignatureEffect(
|
||||||
emailContent: emailContent,
|
emailContent: emailContent,
|
||||||
);
|
);
|
||||||
|
if (emailIdEditing != null &&
|
||||||
|
savedActionType == EmailActionType.compose &&
|
||||||
|
currentEmailActionType == EmailActionType.reopenComposerBrowser) {
|
||||||
|
emailContent = await _composerRepository.removeStyleLazyLoadDisplayInlineImages(
|
||||||
|
emailContent: emailContent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final savedEmailDraft = SavedEmailDraft(
|
final savedEmailDraft = SavedEmailDraft(
|
||||||
subject: subjectEmail.value ?? '',
|
subject: subjectEmail.value ?? '',
|
||||||
|
|||||||
@@ -282,8 +282,9 @@ class RichTextWebController extends BaseRichTextController {
|
|||||||
void insertImageAsBase64({required PlatformFile platformFile, int? maxWidth}) {
|
void insertImageAsBase64({required PlatformFile platformFile, int? maxWidth}) {
|
||||||
if (platformFile.bytes != null) {
|
if (platformFile.bytes != null) {
|
||||||
final base64Data = base64Encode(platformFile.bytes!);
|
final base64Data = base64Encode(platformFile.bytes!);
|
||||||
|
final mimeType = HtmlUtils.validateHtmlImageResourceMimeType('image/${platformFile.extension}');
|
||||||
editorController.insertHtml(
|
editorController.insertHtml(
|
||||||
'<img src="${HtmlUtils.convertBase64ToImageResourceData(base64Data: base64Data, mimeType: 'image/${platformFile.extension}')}" data-filename="${platformFile.name}" alt="Image in my signature" style="max-width: ${maxWidth != null ? '${maxWidth}px' : '100%'};"/>'
|
'<img src="${HtmlUtils.convertBase64ToImageResourceData(base64Data: base64Data, mimeType: mimeType)}" data-filename="${platformFile.name}" alt="Image in my signature" style="max-width: ${maxWidth != null ? '${maxWidth}px' : '100%'};" data-mimetype="$mimeType"/>'
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
logError("RichTextWebController::insertImageAsBase64: bytes is null");
|
logError("RichTextWebController::insertImageAsBase64: bytes is null");
|
||||||
|
|||||||
@@ -23,4 +23,6 @@ abstract class HtmlDataSource {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Future<String> removeCollapsedExpandedSignatureEffect({required String emailContent});
|
Future<String> removeCollapsedExpandedSignatureEffect({required String emailContent});
|
||||||
|
|
||||||
|
Future<String> removeStyleLazyLoadDisplayInlineImages({required String emailContent});
|
||||||
}
|
}
|
||||||
@@ -62,4 +62,13 @@ class HtmlDataSourceImpl extends HtmlDataSource {
|
|||||||
);
|
);
|
||||||
}).catchError(_exceptionThrower.throwException);
|
}).catchError(_exceptionThrower.throwException);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> removeStyleLazyLoadDisplayInlineImages({required String emailContent}) {
|
||||||
|
return Future.sync(() async {
|
||||||
|
return await _htmlAnalyzer.removeStyleLazyLoadDisplayInlineImages(
|
||||||
|
emailContent: emailContent,
|
||||||
|
);
|
||||||
|
}).catchError(_exceptionThrower.throwException);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -237,4 +237,30 @@ class HtmlAnalyzer {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String> removeStyleLazyLoadDisplayInlineImages({required String emailContent}) async {
|
||||||
|
try {
|
||||||
|
final document = parse(emailContent);
|
||||||
|
final imgElements = document.querySelectorAll('img[style], img[loading]');
|
||||||
|
await Future.wait(imgElements.map((img) async {
|
||||||
|
String? style = img.attributes['style'];
|
||||||
|
if (style != null) {
|
||||||
|
style = style.replaceAll(RegExp(r'display\s*:\s*inline;?'), '').trim();
|
||||||
|
if (style.isEmpty) {
|
||||||
|
img.attributes.remove('style');
|
||||||
|
} else {
|
||||||
|
img.attributes['style'] = style;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (img.attributes['loading'] == 'lazy') {
|
||||||
|
img.attributes.remove('loading');
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return document.body?.innerHtml ?? emailContent;
|
||||||
|
} catch (e) {
|
||||||
|
logError('HtmlAnalyzer::removeStyleLazyLoadDisplayInlineImages:Exception = $e');
|
||||||
|
return emailContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -186,7 +186,7 @@ class PublicAssetController extends BaseController {
|
|||||||
newlyPickedPublicAssetIds.add(publicAsset.id!);
|
newlyPickedPublicAssetIds.add(publicAsset.id!);
|
||||||
final imageTag = '<img '
|
final imageTag = '<img '
|
||||||
'src="${publicAsset.publicURI!}" '
|
'src="${publicAsset.publicURI!}" '
|
||||||
'style="max-width: 100%" '
|
'style="max-width: 100%;" '
|
||||||
'public-asset-id="${publicAsset.id!.value}">';
|
'public-asset-id="${publicAsset.id!.value}">';
|
||||||
if (PlatformInfo.isWeb) {
|
if (PlatformInfo.isWeb) {
|
||||||
Get
|
Get
|
||||||
|
|||||||
Reference in New Issue
Block a user