TF-2520 Convert email object to html to perform print on browser

Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
dab246
2024-03-06 16:41:01 +07:00
committed by Dat H. Pham
parent ebd62ab78f
commit 632762bfcd
22 changed files with 309 additions and 531 deletions
+1
View File
@@ -2,4 +2,5 @@ class Constant {
static const acceptHeaderDefault = 'application/json';
static const contentTypeHeaderDefault = 'application/json';
static const pdfMimeType = 'application/pdf';
static const textHtmlMimeType = 'text/html';
}
+3 -3
View File
@@ -2,19 +2,19 @@
import 'package:equatable/equatable.dart';
class PrintAttachment with EquatableMixin {
final String iconSvg;
final String iconBase64Data;
final String name;
final String size;
PrintAttachment({
required this.iconSvg,
required this.iconBase64Data,
required this.name,
required this.size
});
@override
List<Object?> get props => [
iconSvg,
iconBase64Data,
name,
size
];
+10
View File
@@ -1,7 +1,10 @@
import 'dart:convert';
import 'dart:io';
import 'package:core/domain/exceptions/download_file_exception.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
class FileUtils {
@@ -111,4 +114,11 @@ class FileUtils {
logError('FileUtils::removeFolder():EXCEPTION: $e');
}
}
Future<String> convertImageAssetToBase64(String assetImage) async {
ByteData bytes = await rootBundle.load(assetImage);
final buffer = bytes.buffer;
final base64Data = base64Encode(Uint8List.view(buffer));
return base64Data;
}
}
@@ -91,4 +91,13 @@ class HtmlInteraction {
});
</script>
''';
static const String scriptHandleInvokePrinterOnBrowser = '''
<script type="text/javascript">
document.body.onload= function() {
document.body.offsetHeight;
window.print();
};
</script>
''';
}
+42 -9
View File
@@ -21,14 +21,47 @@ class HtmlTemplate {
}
''';
static const String sampleHtmlDocument = '''
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body</body>
</html>
static const String printDocumentCssStyle = '''
<style>
body,td,div,p,a,input {
font-family: arial, sans-serif;
}
body, td {
font-size: 13px;
}
a:link, a:active {
color: #1155CC;
text-decoration: none;
}
a:hover {
text-decoration: underline;
cursor: pointer;
}
a:visited{
color: #6611CC;
}
img {
border: 0px
}
pre {
white-space: pre;
white-space: -moz-pre-wrap;
white-space: -o-pre-wrap;
white-space: pre-wrap;
word-wrap: break-word;
max-width: 800px;
overflow: auto;
}
.logo {
position: relative;
}
</style>
''';
}
+29
View File
@@ -1,8 +1,10 @@
import 'package:core/data/constants/constant.dart';
import 'package:core/presentation/extensions/html_extension.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart';
import 'package:universal_html/html.dart' as html;
class HtmlUtils {
static String customCssStyleHtmlEditor({TextDirection direction = TextDirection.ltr}) {
@@ -94,4 +96,31 @@ class HtmlUtils {
</html>
''';
}
static String createTemplateHtmlDocument({String? title}) {
return '''
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
${title != null ? '<title>$title</title>' : ''}
</head>
<body></body>
</html>
''';
}
static String generateSVGImageData(String base64Data) => 'data:image/svg+xml;base64,$base64Data';
static void openNewTabHtmlDocument(String htmlDocument) {
final blob = html.Blob([htmlDocument], Constant.textHtmlMimeType);
final url = html.Url.createObjectUrlFromBlob(blob);
html.window.open(url, '_blank');
html.Url.revokeObjectUrl(url);
}
}
+195 -346
View File
@@ -2,319 +2,166 @@
import 'dart:async';
import 'package:core/data/model/print_attachment.dart';
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/utils/app_logger.dart';
import 'package:flutter/services.dart';
import 'package:htmltopdfwidgets/htmltopdfwidgets.dart' as pw;
import 'package:intl/intl.dart';
import 'package:printing/printing.dart';
import 'package:core/utils/file_utils.dart';
import 'package:core/utils/html/html_interaction.dart';
import 'package:core/utils/html/html_template.dart';
import 'package:core/utils/html/html_utils.dart';
import 'package:html/dom.dart';
import 'package:html/parser.dart';
class PrintUtils {
final pw.HTMLToPdf _htmlToPdf;
final ImagePaths _imagePaths;
final FileUtils _fileUtils;
PrintUtils(this._htmlToPdf, this._imagePaths);
PrintUtils(this._imagePaths, this._fileUtils);
Future<pw.Widget> _createEmailContentWidget(String htmlDocument) async {
try {
final List<pw.Widget> bodyWidgets = await _htmlToPdf.convert(htmlDocument);
return pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
...bodyWidgets
]
);
} catch (e) {
logError('PrintUtils::_createEmailContentWidget: Exception: $e');
return pw.Text(htmlDocument);
}
}
List<pw.Widget> _createBodyWidgets({
required pw.Context context,
required String logoAppSvg,
Future<Element> _createUserInformationElement({
required String appName,
required String userName,
required String title,
required String htmlDocument,
}) async {
final logoBase64Data = await _fileUtils.convertImageAssetToBase64(_imagePaths.icTMailLogo);
return Element.html('''
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr height="14px">
<td width="40"><img src="${HtmlUtils.generateSVGImageData(logoBase64Data)}" width="40" height="40" class="logo" /></td>
<td style="padding-left: 10px;font-size: 20px;color: #000;"><b>$appName</b></td>
<td align="right" style="color: #777;">
<b>$userName</b>
</td>
</tr>
</tbody>
</table>
''');
}
Element get dividerElement => Element.html('<hr />');
Element _createSubjectElement(String subject) {
return Element.html('''
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr>
<td>
<font size="+1"><b>$subject</b></font><br />
</td>
</tr>
</tbody>
</table>
''');
}
Element _createSenderElement({
required String senderName,
required String senderEmailAddress,
required String dateTime,
}) {
return Element.html('''
<tr>
<td>
<font size="-1"><b>$senderName </b>&lt;$senderEmailAddress&gt;</font>
</td>
<td align="right"><font size="-1">$dateTime</font></td>
</tr>
''');
}
String _createRecipientHtmlTag(String prefix, String emailAddress) {
Element element = Element.html('<div class="${prefix.toLowerCase()}"></div>');
element.text = '$prefix: $emailAddress';
return element.outerHtml;
}
Element _createRecipientsElement({
required String toPrefix,
required String ccPrefix,
required String bccPrefix,
required String replyToPrefix,
required String titleAttachment,
required pw.Widget contentWidget,
String? toAddress,
String? ccAddress,
String? bccAddress,
String? replyToAddress,
List<PrintAttachment>? listAttachment,
}) {
return [
pw.Container(
padding: const pw.EdgeInsets.symmetric(horizontal: 32),
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
_createUserInformationRow(
context: context,
logoAppSvg: logoAppSvg,
appName: appName,
userName: userName
),
_createPageDivider(),
if (title.isNotEmpty)
...[
_createTitleRow(
context: context,
title: title
),
_createPageDivider(),
],
pw.SizedBox(height: 12),
pw.Row(
children: [
pw.Expanded(
child: _createSenderWidget(
context: context,
name: senderName,
emailAddress: senderEmailAddress
)
),
_createDateTimeWidget(
context: context,
dateTime: dateTime
)
]
),
if (replyToAddress?.isNotEmpty == true)
_createRecipientRow(
prefix: replyToPrefix,
emailAddress: replyToAddress!
),
if (toAddress?.isNotEmpty == true)
_createRecipientRow(
prefix: toPrefix,
emailAddress: toAddress!
),
if (ccAddress?.isNotEmpty == true)
_createRecipientRow(
prefix: ccPrefix,
emailAddress: ccAddress!
),
if (bccAddress?.isNotEmpty == true)
_createRecipientRow(
prefix: bccPrefix,
emailAddress: bccAddress!
),
pw.Padding(
padding: const pw.EdgeInsets.symmetric(
horizontal: 12,
vertical: 24),
child: contentWidget
),
if (listAttachment?.isNotEmpty == true)
...[
_createPageDivider(),
pw.SizedBox(height: 12),
_createAttachmentTitle(
context: context,
title: titleAttachment,
totalAttachments: listAttachment!.length
),
pw.SizedBox(height: 4),
...listAttachment.map((attachment) => _createAttachmentRow(
context: context,
attachment: attachment
))
]
]
)
)
];
return Element.html('''
<tr>
<td colspan="2" style="padding-bottom: 4px;" class="recipient">
${replyToAddress?.isNotEmpty == true ? _createRecipientHtmlTag(replyToPrefix, replyToAddress!) : ''}
${toAddress?.isNotEmpty == true ? _createRecipientHtmlTag(toPrefix, toAddress!) : ''}
${ccAddress?.isNotEmpty == true ? _createRecipientHtmlTag(ccPrefix, ccAddress!) : ''}
${bccAddress?.isNotEmpty == true ? _createRecipientHtmlTag(bccPrefix, bccAddress!) : ''}
</td>
</tr>
''');
}
pw.Widget _createPageDivider() {
return pw.Padding(
padding: const pw.EdgeInsets.only(top: 12),
child: pw.Divider()
);
Element _createEmailContentElement(String emailContent) {
return Element.html('''
<tr>
<td colspan="2">
<table width="100%" cellpadding="12" cellspacing="0" border="0">
<tbody>
<tr>
<td>$emailContent</td>
</tr>
</tbody>
</table>
</td>
</tr>
''');
}
pw.Widget _createPageHeader({
required pw.Context context,
required String appName,
required String title,
required String locale,
String _createTitleAttachmentHtmlTag({
required int countAttachments,
required String titleAttachment
}) {
final currentTime = DateFormat('MM/dd/yyyy, h:mm a', locale).format(DateTime.now());
return pw.Padding(
padding: const pw.EdgeInsets.all(20),
child: pw.Row(
children: [
pw.Text(
currentTime,
style: pw.Theme.of(context).defaultTextStyle.copyWith(
fontSize: 8,
color: pw.PdfColor.fromInt(AppColor.colorTextBody.value)
)
),
pw.SizedBox(width: 16),
pw.Expanded(
child: pw.Text(
'$appName - $title',
textAlign: pw.TextAlign.center,
maxLines: 1,
style: pw.Theme.of(context).defaultTextStyle.copyWith(
fontSize: 8,
color: pw.PdfColor.fromInt(AppColor.colorTextBody.value)
)
)
),
]
)
);
return '<tr><td colspan="2"><b style="padding-left:3">$countAttachments $titleAttachment</b></td></tr>';
}
pw.Widget _createUserInformationRow({
required pw.Context context,
required String logoAppSvg,
String _createAttachmentHtmlTag(PrintAttachment printAttachment) {
return '''
<tr>
<td>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<a target="_blank" href="">
<img width="16" height="16" src="${HtmlUtils.generateSVGImageData(printAttachment.iconBase64Data)}" />
</a>
</td>
<td width="7"></td>
<td>
<b>${printAttachment.name}</b><br />
${printAttachment.size}
</td>
</tr>
</tbody>
</table>
</td>
</tr>
''';
}
Element _createAttachmentsElement({
required String titleAttachment,
required List<PrintAttachment> listAttachment
}) {
return Element.html('''
<table class="attachments" cellspacing="0" cellpadding="5" border="0">
<tbody>
${_createTitleAttachmentHtmlTag(countAttachments: listAttachment.length, titleAttachment: titleAttachment)}
${listAttachment.map((printAttachment) => _createAttachmentHtmlTag(printAttachment)).toList().join('')}
</tbody>
</table>
''');
}
Future<void> printEmail({
required String appName,
required String userName,
}) {
return pw.Row(
children: [
pw.SvgImage(
svg: logoAppSvg,
width: 32,
height: 32
),
pw.SizedBox(width: 12),
pw.Text(
appName,
style: pw.Theme.of(context).header0
),
pw.Spacer(),
pw.SizedBox(width: 12),
pw.Text(
userName,
style: pw.Theme.of(context).header4
)
]
);
}
pw.Widget _createTitleRow({
required pw.Context context,
required String title,
}) {
return pw.Padding(
padding: const pw.EdgeInsets.only(top: 12),
child: pw.Text(
title,
style: pw.Theme.of(context).header3
)
);
}
pw.Widget _createSenderWidget({
required pw.Context context,
required String name,
required String emailAddress,
}) {
return pw.Row(
children: [
if (name.isNotEmpty)
pw.Padding(
padding: const pw.EdgeInsetsDirectional.only(end: 4),
child: pw.Text(
name,
style: pw.Theme.of(context).header4
)
),
pw.Text('<$emailAddress>')
]
);
}
pw.Widget _createDateTimeWidget({
required pw.Context context,
required String dateTime,
}) {
return pw.Text(
dateTime,
style: pw.Theme.of(context).defaultTextStyle.copyWith(
fontSize: 10,
color: pw.PdfColor.fromInt(AppColor.colorTextBody.value)
)
);
}
pw.Widget _createRecipientRow({
required String prefix,
required String emailAddress,
}) {
return pw.Row(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text('$prefix:'),
pw.SizedBox(width: 4),
pw.Expanded(child: pw.Text(emailAddress))
]
);
}
pw.Widget _createAttachmentTitle({
required pw.Context context,
required String title,
required int totalAttachments,
}) {
return pw.Text(
'$totalAttachments $title',
style: pw.Theme.of(context).header4
);
}
pw.Widget _createAttachmentRow({
required pw.Context context,
required PrintAttachment attachment,
}) {
return pw.Padding(
padding: const pw.EdgeInsets.only(top: 8),
child: pw.Row(
children: [
pw.SvgImage(
svg: attachment.iconSvg,
width: 24,
height: 24
),
pw.SizedBox(width: 8),
pw.Expanded(
child: pw.Column(
mainAxisSize: pw.MainAxisSize.min,
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text(
attachment.name,
style: pw.Theme.of(context).header4
),
pw.Text(attachment.size)
]
)
),
]
)
);
}
Future<bool> printEmailToPDF({
required String appName,
required String userName,
required String title,
required String subject,
required String emailContent,
required String senderName,
required String senderEmailAddress,
@@ -331,69 +178,71 @@ class PrintUtils {
String? replyToAddress,
List<PrintAttachment>? listAttachment,
}) async {
return await Printing.layoutPdf(onLayout: (pw.PdfPageFormat format) async {
final googleInterRegularFont = await PdfGoogleFonts.interRegular();
final googleInterBoldFont = await PdfGoogleFonts.interBold();
final logoAppSvg = await rootBundle.loadString(_imagePaths.icTMailLogo);
final contentWidget = await _createEmailContentWidget(emailContent);
Document document = parse(HtmlUtils.createTemplateHtmlDocument(title: '$appName - $subject'));
final pdfDocument = pw.Document();
Element bodyContainerElement = Element.html('<div class="body-container"></div>');
Element mainContentElement = Element.html('<div class="main-content"></div>');
Element messageElement = Element.html('<table width="100%" cellpadding="0" cellspacing="0" border="0" class="message"></table>');
Element messageBodyElement = Element.html('<tbody></tbody>');
pdfDocument.addPage(
pw.MultiPage(
pageFormat: pw.PdfPageFormat.standard,
orientation: pw.PageOrientation.portrait,
theme: pw.ThemeData(
defaultTextStyle: pw.TextStyle(
font: googleInterRegularFont,
fontSize: 12,
color: pw.PdfColors.black
),
header3: pw.TextStyle(
font: googleInterBoldFont,
fontSize: 14,
fontWeight: pw.FontWeight.bold,
color: pw.PdfColors.black
),
header4: pw.TextStyle(
font: googleInterBoldFont,
fontSize: 12,
fontWeight: pw.FontWeight.bold,
color: pw.PdfColors.black
),
),
margin: pw.EdgeInsets.zero,
build: (context) => _createBodyWidgets(
context: context,
contentWidget: contentWidget,
logoAppSvg: logoAppSvg,
appName: appName,
userName: userName,
title: title,
htmlDocument: emailContent,
senderName: senderName,
senderEmailAddress: senderEmailAddress,
dateTime: dateTime,
toPrefix: toPrefix,
ccPrefix: ccPrefix,
bccPrefix: toPrefix,
replyToPrefix: replyToPrefix,
toAddress: toAddress,
ccAddress: ccAddress,
bccAddress: bccAddress,
replyToAddress: replyToAddress,
titleAttachment: titleAttachment,
listAttachment: listAttachment
),
header: (context) => _createPageHeader(
context: context,
appName: appName,
title: title,
locale: locale,
),
)
);
return await pdfDocument.save();
});
Element userInfoElement = await _createUserInformationElement(
appName: appName,
userName: userName);
Element subjectElement = _createSubjectElement(subject);
Element senderElement = _createSenderElement(
senderName: senderName,
senderEmailAddress: senderEmailAddress,
dateTime: dateTime);
Element recipientsElement = _createRecipientsElement(
toPrefix: toPrefix,
ccPrefix: ccPrefix,
bccPrefix: bccPrefix,
replyToPrefix: replyToPrefix,
toAddress: toAddress,
ccAddress: ccAddress,
bccAddress: bccAddress,
replyToAddress: replyToAddress,
);
Element emailContentElement = _createEmailContentElement(emailContent);
bodyContainerElement.append(userInfoElement);
bodyContainerElement.append(dividerElement);
mainContentElement.append(subjectElement);
mainContentElement.append(dividerElement);
messageBodyElement.append(senderElement);
messageBodyElement.append(recipientsElement);
messageBodyElement.append(emailContentElement);
messageElement.append(messageBodyElement);
mainContentElement.append(messageElement);
if (listAttachment?.isNotEmpty == true) {
Element attachmentsElement = _createAttachmentsElement(
titleAttachment: titleAttachment,
listAttachment: listAttachment!);
mainContentElement.append(dividerElement);
mainContentElement.append(attachmentsElement);
}
bodyContainerElement.append(mainContentElement);
document.body?.append(bodyContainerElement);
Element scriptElement = Element.html(HtmlInteraction.scriptHandleInvokePrinterOnBrowser);
document.body?.append(scriptElement);
Element styleElement = Element.html(HtmlTemplate.printDocumentCssStyle);
document.head?.append(styleElement);
final htmlDocument = document.outerHtml;
HtmlUtils.openNewTabHtmlDocument(htmlDocument);
}
}
-96
View File
@@ -1,14 +1,6 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
archive:
dependency: transitive
description:
name: archive
sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
url: "https://pub.dev"
source: hosted
version: "3.4.10"
args:
dependency: transitive
description:
@@ -25,22 +17,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.11.0"
barcode:
dependency: transitive
description:
name: barcode
sha256: "91b143666f7bb13636f716b6d4e412e372ab15ff7969799af8c9e30a382e9385"
url: "https://pub.dev"
source: hosted
version: "2.2.6"
bidi:
dependency: transitive
description:
name: bidi
sha256: "1a7d0c696324b2089f72e7671fd1f1f64fef44c980f3cebc84e803967c597b63"
url: "https://pub.dev"
source: hosted
version: "2.0.10"
boolean_selector:
dependency: transitive
description:
@@ -89,14 +65,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.18.0"
convert:
dependency: transitive
description:
name: convert
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
cross_file:
dependency: transitive
description:
@@ -105,14 +73,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.3.3+7"
crypto:
dependency: transitive
description:
name: crypto
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
source: hosted
version: "3.0.3"
csslib:
dependency: transitive
description:
@@ -368,14 +328,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.15.3"
htmltopdfwidgets:
dependency: "direct main"
description:
name: htmltopdfwidgets
sha256: "990e3002fbbfaf307555f1991bed61b6228d7053b7e1945815c7a68f56bbda81"
url: "https://pub.dev"
source: hosted
version: "1.0.3"
http:
dependency: "direct main"
description:
@@ -392,14 +344,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.2"
image:
dependency: transitive
description:
name: image
sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e"
url: "https://pub.dev"
source: hosted
version: "4.1.7"
intl:
dependency: "direct main"
description:
@@ -520,22 +464,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.1"
pdf:
dependency: transitive
description:
name: pdf
sha256: "243f05342fc0bdf140eba5b069398985cdbdd3dbb1d776cf43d5ea29cc570ba6"
url: "https://pub.dev"
source: hosted
version: "3.10.8"
pdf_widget_wrapper:
dependency: transitive
description:
name: pdf_widget_wrapper
sha256: e9d31fd7782ce28ae346b127ea7d1cd748d799bddee379f31191693610e23749
url: "https://pub.dev"
source: hosted
version: "1.0.1"
petitparser:
dependency: transitive
description:
@@ -568,30 +496,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.9.1"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29"
url: "https://pub.dev"
source: hosted
version: "3.7.4"
printing:
dependency: "direct main"
description:
name: printing
sha256: "1c99cab90ebcc1fff65831d264627d5b529359d563e53f33ab9b8117f2d280bc"
url: "https://pub.dev"
source: hosted
version: "5.12.0"
qr:
dependency: transitive
description:
name: qr
sha256: "64957a3930367bf97cc211a5af99551d630f2f4625e38af10edd6b19131b64b3"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
sky_engine:
dependency: transitive
description: flutter
-4
View File
@@ -75,10 +75,6 @@ dependencies:
linkify: 5.0.0
htmltopdfwidgets: 1.0.3
printing: 5.12.0
dev_dependencies:
flutter_test:
sdk: flutter
@@ -106,6 +106,7 @@ class ComposerBindings extends BaseBindings {
Get.lazyPut(() => PrintFileDataSourceImpl(
Get.find<PrintUtils>(),
Get.find<ImagePaths>(),
Get.find<FileUtils>(),
Get.find<CacheExceptionThrower>()
));
Get.lazyPut(() => EmailHiveCacheDataSourceImpl(
@@ -1,5 +1,5 @@
import 'package:tmail_ui_user/features/email/domain/model/email_print.dart';
abstract class PrintFileDataSource {
Future<void> printEmailToPDF(EmailPrint emailPrint);
Future<void> printEmail(EmailPrint emailPrint);
}
@@ -1,9 +1,8 @@
import 'package:core/data/model/print_attachment.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/file_utils.dart';
import 'package:core/utils/print_utils.dart';
import 'package:filesize/filesize.dart';
import 'package:flutter/services.dart';
import 'package:model/email/attachment.dart';
import 'package:model/extensions/utc_date_extension.dart';
import 'package:tmail_ui_user/features/email/data/datasource/print_file_datasource.dart';
@@ -15,16 +14,18 @@ class PrintFileDataSourceImpl extends PrintFileDataSource {
final PrintUtils _printUtils;
final ImagePaths _imagePaths;
final FileUtils _fileUtils;
final ExceptionThrower _exceptionThrower;
PrintFileDataSourceImpl(
this._printUtils,
this._imagePaths,
this._fileUtils,
this._exceptionThrower
);
@override
Future<void> printEmailToPDF(EmailPrint emailPrint) {
Future<void> printEmail(EmailPrint emailPrint) {
return Future.sync(() async {
final sender = emailPrint.emailInformation.from?.isNotEmpty == true
? emailPrint.emailInformation.from!.first
@@ -37,9 +38,9 @@ class PrintFileDataSourceImpl extends PrintFileDataSource {
if (emailPrint.attachments?.isNotEmpty == true) {
await Future.forEach<Attachment>(emailPrint.attachments ?? [], (attachment) async {
final iconSvg = await rootBundle.loadString(attachment.getIcon(_imagePaths));
final iconBase64Data = await _fileUtils.convertImageAssetToBase64(attachment.getIcon(_imagePaths));
final printAttachment = PrintAttachment(
iconSvg: iconSvg,
iconBase64Data: iconBase64Data,
name: attachment.name ?? '',
size: filesize(attachment.size?.value)
);
@@ -47,11 +48,11 @@ class PrintFileDataSourceImpl extends PrintFileDataSource {
});
}
final result = await _printUtils.printEmailToPDF(
return await _printUtils.printEmail(
appName: emailPrint.appName,
userName: emailPrint.userName,
locale: emailPrint.locale,
title: emailPrint.emailInformation.subject ?? '',
subject: emailPrint.emailInformation.subject ?? '',
emailContent: emailPrint.emailContent,
senderName: sender?.name ?? '',
senderEmailAddress: sender?.email ?? '',
@@ -67,8 +68,6 @@ class PrintFileDataSourceImpl extends PrintFileDataSource {
titleAttachment: emailPrint.titleAttachment,
listAttachment: listPrintAttachment
);
log('PrintFileDataSourceImpl::printEmailToPDF: RESULT = $result');
return result;
}).catchError(_exceptionThrower.throwException);
}
}
@@ -235,6 +235,6 @@ class EmailRepositoryImpl extends EmailRepository {
@override
Future<void> printEmail(EmailPrint emailPrint) {
return _printFileDataSource.printEmailToPDF(emailPrint);
return _printFileDataSource.printEmail(emailPrint);
}
}
@@ -29,7 +29,6 @@ class PrintEmailInteractor {
final htmlContentTransformed = await emailRepository.transformHtmlEmailContent(
emailContent,
TransformConfiguration.forPrintEmail());
log('PrintEmailInteractor::_transformHtmlEmailContent: htmlContentTransformed: $htmlContentTransformed');
return htmlContentTransformed;
} catch (e) {
logError('PrintEmailInteractor::_transformHtmlEmailContent: Exception: $e');
@@ -105,6 +105,7 @@ class EmailBindings extends BaseBindings {
Get.lazyPut(() => PrintFileDataSourceImpl(
Get.find<PrintUtils>(),
Get.find<ImagePaths>(),
Get.find<FileUtils>(),
Get.find<CacheExceptionThrower>()
));
Get.lazyPut(() => EmailHiveCacheDataSourceImpl(
@@ -214,6 +214,7 @@ class MailboxDashBoardBindings extends BaseBindings {
Get.lazyPut(() => PrintFileDataSourceImpl(
Get.find<PrintUtils>(),
Get.find<ImagePaths>(),
Get.find<FileUtils>(),
Get.find<CacheExceptionThrower>()
));
Get.lazyPut(() => MailboxDataSourceImpl(
@@ -70,6 +70,7 @@ class SendEmailInteractorBindings extends InteractorsBindings {
Get.lazyPut(() => PrintFileDataSourceImpl(
Get.find<PrintUtils>(),
Get.find<ImagePaths>(),
Get.find<FileUtils>(),
Get.find<CacheExceptionThrower>(),
));
Get.lazyPut(() => EmailHiveCacheDataSourceImpl(
@@ -114,6 +114,7 @@ class FcmInteractorBindings extends InteractorsBindings {
Get.lazyPut(() => PrintFileDataSourceImpl(
Get.find<PrintUtils>(),
Get.find<ImagePaths>(),
Get.find<FileUtils>(),
Get.find<CacheExceptionThrower>()
));
Get.lazyPut(() => EmailHiveCacheDataSourceImpl(
+1 -3
View File
@@ -10,7 +10,6 @@ import 'package:core/utils/print_utils.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:get/get.dart';
import 'package:htmltopdfwidgets/htmltopdfwidgets.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:tmail_ui_user/features/sending_queue/presentation/utils/sending_queue_isolate_manager.dart';
import 'package:tmail_ui_user/main/utils/app_config.dart';
@@ -62,8 +61,7 @@ class CoreBindings extends Bindings {
Get.put(CompressFileUtils());
Get.put(AppConfigLoader());
Get.put(FileUtils());
Get.put(HTMLToPdf());
Get.put(PrintUtils(Get.find<HTMLToPdf>(), Get.find<ImagePaths>()));
Get.put(PrintUtils(Get.find<ImagePaths>(), Get.find<FileUtils>()));
}
void _bindingIsolate() {
-56
View File
@@ -57,14 +57,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.0"
barcode:
dependency: transitive
description:
name: barcode
sha256: "91b143666f7bb13636f716b6d4e412e372ab15ff7969799af8c9e30a382e9385"
url: "https://pub.dev"
source: hosted
version: "2.2.6"
better_open_file:
dependency: "direct main"
description:
@@ -73,14 +65,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.6.4"
bidi:
dependency: transitive
description:
name: bidi
sha256: "1a7d0c696324b2089f72e7671fd1f1f64fef44c980f3cebc84e803967c597b63"
url: "https://pub.dev"
source: hosted
version: "2.0.10"
boolean_selector:
dependency: transitive
description:
@@ -1046,14 +1030,6 @@ packages:
url: "https://github.com/linagora/html-editor-enhanced.git"
source: git
version: "2.5.1"
htmltopdfwidgets:
dependency: "direct main"
description:
name: htmltopdfwidgets
sha256: "990e3002fbbfaf307555f1991bed61b6228d7053b7e1945815c7a68f56bbda81"
url: "https://pub.dev"
source: hosted
version: "1.0.3"
http:
dependency: transitive
description:
@@ -1358,22 +1334,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.0"
pdf:
dependency: transitive
description:
name: pdf
sha256: "243f05342fc0bdf140eba5b069398985cdbdd3dbb1d776cf43d5ea29cc570ba6"
url: "https://pub.dev"
source: hosted
version: "3.10.8"
pdf_widget_wrapper:
dependency: transitive
description:
name: pdf_widget_wrapper
sha256: e9d31fd7782ce28ae346b127ea7d1cd748d799bddee379f31191693610e23749
url: "https://pub.dev"
source: hosted
version: "1.0.1"
percent_indicator:
dependency: "direct main"
description:
@@ -1470,14 +1430,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.5.1"
printing:
dependency: transitive
description:
name: printing
sha256: "1c99cab90ebcc1fff65831d264627d5b529359d563e53f33ab9b8117f2d280bc"
url: "https://pub.dev"
source: hosted
version: "5.12.0"
process:
dependency: transitive
description:
@@ -1502,14 +1454,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.3"
qr:
dependency: transitive
description:
name: qr
sha256: "64957a3930367bf97cc211a5af99551d630f2f4625e38af10edd6b19131b64b3"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
quiver:
dependency: transitive
description:
-2
View File
@@ -231,8 +231,6 @@ dependencies:
mime: 1.0.4
htmltopdfwidgets: 1.0.3
dev_dependencies:
flutter_test:
sdk: flutter
@@ -21,6 +21,7 @@ import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_i
import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_star_email_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/move_to_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/print_email_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/store_opened_email_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/view_attachment_for_web_interactor.dart';
import 'package:tmail_ui_user/features/email/presentation/controller/email_supervisor_controller.dart';
@@ -69,6 +70,7 @@ const fallbackGenerators = {
MockSpec<ImagePaths>(),
MockSpec<ResponsiveUtils>(),
MockSpec<Uuid>(),
MockSpec<PrintEmailInteractor>(),
])
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
@@ -99,6 +101,7 @@ void main() {
final imagePaths = MockImagePaths();
final responsiveUtils = MockResponsiveUtils();
final uuid = MockUuid();
final printEmailInteractor = MockPrintEmailInteractor();
late SingleEmailController singleEmailController = SingleEmailController(
getEmailContentInteractor,
@@ -112,6 +115,7 @@ void main() {
getAllIdentitiesInteractor,
storeOpenedEmailInteractor,
viewAttachmentForWebInteractor,
printEmailInteractor,
);
final testAccountId = AccountId(Id('123'));