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 acceptHeaderDefault = 'application/json';
static const contentTypeHeaderDefault = 'application/json'; static const contentTypeHeaderDefault = 'application/json';
static const pdfMimeType = 'application/pdf'; static const pdfMimeType = 'application/pdf';
static const textHtmlMimeType = 'text/html';
} }
+3 -3
View File
@@ -2,19 +2,19 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
class PrintAttachment with EquatableMixin { class PrintAttachment with EquatableMixin {
final String iconSvg; final String iconBase64Data;
final String name; final String name;
final String size; final String size;
PrintAttachment({ PrintAttachment({
required this.iconSvg, required this.iconBase64Data,
required this.name, required this.name,
required this.size required this.size
}); });
@override @override
List<Object?> get props => [ List<Object?> get props => [
iconSvg, iconBase64Data,
name, name,
size size
]; ];
+10
View File
@@ -1,7 +1,10 @@
import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:core/domain/exceptions/download_file_exception.dart'; import 'package:core/domain/exceptions/download_file_exception.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:flutter/services.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
class FileUtils { class FileUtils {
@@ -111,4 +114,11 @@ class FileUtils {
logError('FileUtils::removeFolder():EXCEPTION: $e'); 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> </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 = ''' static const String printDocumentCssStyle = '''
<!DOCTYPE html> <style>
<html> body,td,div,p,a,input {
<head> font-family: arial, sans-serif;
<meta name="viewport" content="width=device-width, initial-scale=1.0"> }
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head> body, td {
<body</body> font-size: 13px;
</html> }
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/presentation/extensions/html_extension.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:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:universal_html/html.dart' as html;
class HtmlUtils { class HtmlUtils {
static String customCssStyleHtmlEditor({TextDirection direction = TextDirection.ltr}) { static String customCssStyleHtmlEditor({TextDirection direction = TextDirection.ltr}) {
@@ -94,4 +96,31 @@ class HtmlUtils {
</html> </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 'dart:async';
import 'package:core/data/model/print_attachment.dart'; 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/presentation/resources/image_paths.dart';
import 'package:core/utils/app_logger.dart'; import 'package:core/utils/file_utils.dart';
import 'package:flutter/services.dart'; import 'package:core/utils/html/html_interaction.dart';
import 'package:htmltopdfwidgets/htmltopdfwidgets.dart' as pw; import 'package:core/utils/html/html_template.dart';
import 'package:intl/intl.dart'; import 'package:core/utils/html/html_utils.dart';
import 'package:printing/printing.dart'; import 'package:html/dom.dart';
import 'package:html/parser.dart';
class PrintUtils { class PrintUtils {
final pw.HTMLToPdf _htmlToPdf;
final ImagePaths _imagePaths; final ImagePaths _imagePaths;
final FileUtils _fileUtils;
PrintUtils(this._htmlToPdf, this._imagePaths); PrintUtils(this._imagePaths, this._fileUtils);
Future<pw.Widget> _createEmailContentWidget(String htmlDocument) async { Future<Element> _createUserInformationElement({
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,
required String appName, required String appName,
required String userName, required String userName,
required String title, }) async {
required String htmlDocument, 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 senderName,
required String senderEmailAddress, required String senderEmailAddress,
required String dateTime, 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 toPrefix,
required String ccPrefix, required String ccPrefix,
required String bccPrefix, required String bccPrefix,
required String replyToPrefix, required String replyToPrefix,
required String titleAttachment,
required pw.Widget contentWidget,
String? toAddress, String? toAddress,
String? ccAddress, String? ccAddress,
String? bccAddress, String? bccAddress,
String? replyToAddress, String? replyToAddress,
List<PrintAttachment>? listAttachment,
}) { }) {
return [ return Element.html('''
pw.Container( <tr>
padding: const pw.EdgeInsets.symmetric(horizontal: 32), <td colspan="2" style="padding-bottom: 4px;" class="recipient">
child: pw.Column( ${replyToAddress?.isNotEmpty == true ? _createRecipientHtmlTag(replyToPrefix, replyToAddress!) : ''}
crossAxisAlignment: pw.CrossAxisAlignment.start, ${toAddress?.isNotEmpty == true ? _createRecipientHtmlTag(toPrefix, toAddress!) : ''}
children: [ ${ccAddress?.isNotEmpty == true ? _createRecipientHtmlTag(ccPrefix, ccAddress!) : ''}
_createUserInformationRow( ${bccAddress?.isNotEmpty == true ? _createRecipientHtmlTag(bccPrefix, bccAddress!) : ''}
context: context, </td>
logoAppSvg: logoAppSvg, </tr>
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
))
]
]
)
)
];
} }
pw.Widget _createPageDivider() { Element _createEmailContentElement(String emailContent) {
return pw.Padding( return Element.html('''
padding: const pw.EdgeInsets.only(top: 12), <tr>
child: pw.Divider() <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({ String _createTitleAttachmentHtmlTag({
required pw.Context context, required int countAttachments,
required String appName, required String titleAttachment
required String title,
required String locale,
}) { }) {
final currentTime = DateFormat('MM/dd/yyyy, h:mm a', locale).format(DateTime.now()); return '<tr><td colspan="2"><b style="padding-left:3">$countAttachments $titleAttachment</b></td></tr>';
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)
)
)
),
]
)
);
} }
pw.Widget _createUserInformationRow({ String _createAttachmentHtmlTag(PrintAttachment printAttachment) {
required pw.Context context, return '''
required String logoAppSvg, <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 appName,
required String userName, required String userName,
}) { required String subject,
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 emailContent, required String emailContent,
required String senderName, required String senderName,
required String senderEmailAddress, required String senderEmailAddress,
@@ -331,69 +178,71 @@ class PrintUtils {
String? replyToAddress, String? replyToAddress,
List<PrintAttachment>? listAttachment, List<PrintAttachment>? listAttachment,
}) async { }) async {
return await Printing.layoutPdf(onLayout: (pw.PdfPageFormat format) async { Document document = parse(HtmlUtils.createTemplateHtmlDocument(title: '$appName - $subject'));
final googleInterRegularFont = await PdfGoogleFonts.interRegular();
final googleInterBoldFont = await PdfGoogleFonts.interBold();
final logoAppSvg = await rootBundle.loadString(_imagePaths.icTMailLogo);
final contentWidget = await _createEmailContentWidget(emailContent);
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( Element userInfoElement = await _createUserInformationElement(
pw.MultiPage( appName: appName,
pageFormat: pw.PdfPageFormat.standard, userName: userName);
orientation: pw.PageOrientation.portrait,
theme: pw.ThemeData( Element subjectElement = _createSubjectElement(subject);
defaultTextStyle: pw.TextStyle(
font: googleInterRegularFont, Element senderElement = _createSenderElement(
fontSize: 12, senderName: senderName,
color: pw.PdfColors.black senderEmailAddress: senderEmailAddress,
), dateTime: dateTime);
header3: pw.TextStyle(
font: googleInterBoldFont, Element recipientsElement = _createRecipientsElement(
fontSize: 14, toPrefix: toPrefix,
fontWeight: pw.FontWeight.bold, ccPrefix: ccPrefix,
color: pw.PdfColors.black bccPrefix: bccPrefix,
), replyToPrefix: replyToPrefix,
header4: pw.TextStyle( toAddress: toAddress,
font: googleInterBoldFont, ccAddress: ccAddress,
fontSize: 12, bccAddress: bccAddress,
fontWeight: pw.FontWeight.bold, replyToAddress: replyToAddress,
color: pw.PdfColors.black );
),
), Element emailContentElement = _createEmailContentElement(emailContent);
margin: pw.EdgeInsets.zero,
build: (context) => _createBodyWidgets( bodyContainerElement.append(userInfoElement);
context: context, bodyContainerElement.append(dividerElement);
contentWidget: contentWidget,
logoAppSvg: logoAppSvg, mainContentElement.append(subjectElement);
appName: appName, mainContentElement.append(dividerElement);
userName: userName,
title: title, messageBodyElement.append(senderElement);
htmlDocument: emailContent, messageBodyElement.append(recipientsElement);
senderName: senderName, messageBodyElement.append(emailContentElement);
senderEmailAddress: senderEmailAddress,
dateTime: dateTime, messageElement.append(messageBodyElement);
toPrefix: toPrefix,
ccPrefix: ccPrefix, mainContentElement.append(messageElement);
bccPrefix: toPrefix,
replyToPrefix: replyToPrefix, if (listAttachment?.isNotEmpty == true) {
toAddress: toAddress, Element attachmentsElement = _createAttachmentsElement(
ccAddress: ccAddress, titleAttachment: titleAttachment,
bccAddress: bccAddress, listAttachment: listAttachment!);
replyToAddress: replyToAddress, mainContentElement.append(dividerElement);
titleAttachment: titleAttachment, mainContentElement.append(attachmentsElement);
listAttachment: listAttachment }
),
header: (context) => _createPageHeader( bodyContainerElement.append(mainContentElement);
context: context,
appName: appName, document.body?.append(bodyContainerElement);
title: title,
locale: locale, Element scriptElement = Element.html(HtmlInteraction.scriptHandleInvokePrinterOnBrowser);
), document.body?.append(scriptElement);
)
); Element styleElement = Element.html(HtmlTemplate.printDocumentCssStyle);
return await pdfDocument.save(); document.head?.append(styleElement);
});
final htmlDocument = document.outerHtml;
HtmlUtils.openNewTabHtmlDocument(htmlDocument);
} }
} }
-96
View File
@@ -1,14 +1,6 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
archive:
dependency: transitive
description:
name: archive
sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
url: "https://pub.dev"
source: hosted
version: "3.4.10"
args: args:
dependency: transitive dependency: transitive
description: description:
@@ -25,22 +17,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.11.0" 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: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@@ -89,14 +65,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.18.0" version: "1.18.0"
convert:
dependency: transitive
description:
name: convert
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
cross_file: cross_file:
dependency: transitive dependency: transitive
description: description:
@@ -105,14 +73,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.3+7" version: "0.3.3+7"
crypto:
dependency: transitive
description:
name: crypto
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
source: hosted
version: "3.0.3"
csslib: csslib:
dependency: transitive dependency: transitive
description: description:
@@ -368,14 +328,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.15.3" version: "0.15.3"
htmltopdfwidgets:
dependency: "direct main"
description:
name: htmltopdfwidgets
sha256: "990e3002fbbfaf307555f1991bed61b6228d7053b7e1945815c7a68f56bbda81"
url: "https://pub.dev"
source: hosted
version: "1.0.3"
http: http:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -392,14 +344,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.2" version: "4.0.2"
image:
dependency: transitive
description:
name: image
sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e"
url: "https://pub.dev"
source: hosted
version: "4.1.7"
intl: intl:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -520,22 +464,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.1" 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: petitparser:
dependency: transitive dependency: transitive
description: description:
@@ -568,30 +496,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.9.1" 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: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
-4
View File
@@ -75,10 +75,6 @@ dependencies:
linkify: 5.0.0 linkify: 5.0.0
htmltopdfwidgets: 1.0.3
printing: 5.12.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
@@ -106,6 +106,7 @@ class ComposerBindings extends BaseBindings {
Get.lazyPut(() => PrintFileDataSourceImpl( Get.lazyPut(() => PrintFileDataSourceImpl(
Get.find<PrintUtils>(), Get.find<PrintUtils>(),
Get.find<ImagePaths>(), Get.find<ImagePaths>(),
Get.find<FileUtils>(),
Get.find<CacheExceptionThrower>() Get.find<CacheExceptionThrower>()
)); ));
Get.lazyPut(() => EmailHiveCacheDataSourceImpl( Get.lazyPut(() => EmailHiveCacheDataSourceImpl(
@@ -1,5 +1,5 @@
import 'package:tmail_ui_user/features/email/domain/model/email_print.dart'; import 'package:tmail_ui_user/features/email/domain/model/email_print.dart';
abstract class PrintFileDataSource { 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/data/model/print_attachment.dart';
import 'package:core/presentation/resources/image_paths.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:core/utils/print_utils.dart';
import 'package:filesize/filesize.dart'; import 'package:filesize/filesize.dart';
import 'package:flutter/services.dart';
import 'package:model/email/attachment.dart'; import 'package:model/email/attachment.dart';
import 'package:model/extensions/utc_date_extension.dart'; import 'package:model/extensions/utc_date_extension.dart';
import 'package:tmail_ui_user/features/email/data/datasource/print_file_datasource.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 PrintUtils _printUtils;
final ImagePaths _imagePaths; final ImagePaths _imagePaths;
final FileUtils _fileUtils;
final ExceptionThrower _exceptionThrower; final ExceptionThrower _exceptionThrower;
PrintFileDataSourceImpl( PrintFileDataSourceImpl(
this._printUtils, this._printUtils,
this._imagePaths, this._imagePaths,
this._fileUtils,
this._exceptionThrower this._exceptionThrower
); );
@override @override
Future<void> printEmailToPDF(EmailPrint emailPrint) { Future<void> printEmail(EmailPrint emailPrint) {
return Future.sync(() async { return Future.sync(() async {
final sender = emailPrint.emailInformation.from?.isNotEmpty == true final sender = emailPrint.emailInformation.from?.isNotEmpty == true
? emailPrint.emailInformation.from!.first ? emailPrint.emailInformation.from!.first
@@ -37,9 +38,9 @@ class PrintFileDataSourceImpl extends PrintFileDataSource {
if (emailPrint.attachments?.isNotEmpty == true) { if (emailPrint.attachments?.isNotEmpty == true) {
await Future.forEach<Attachment>(emailPrint.attachments ?? [], (attachment) async { 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( final printAttachment = PrintAttachment(
iconSvg: iconSvg, iconBase64Data: iconBase64Data,
name: attachment.name ?? '', name: attachment.name ?? '',
size: filesize(attachment.size?.value) 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, appName: emailPrint.appName,
userName: emailPrint.userName, userName: emailPrint.userName,
locale: emailPrint.locale, locale: emailPrint.locale,
title: emailPrint.emailInformation.subject ?? '', subject: emailPrint.emailInformation.subject ?? '',
emailContent: emailPrint.emailContent, emailContent: emailPrint.emailContent,
senderName: sender?.name ?? '', senderName: sender?.name ?? '',
senderEmailAddress: sender?.email ?? '', senderEmailAddress: sender?.email ?? '',
@@ -67,8 +68,6 @@ class PrintFileDataSourceImpl extends PrintFileDataSource {
titleAttachment: emailPrint.titleAttachment, titleAttachment: emailPrint.titleAttachment,
listAttachment: listPrintAttachment listAttachment: listPrintAttachment
); );
log('PrintFileDataSourceImpl::printEmailToPDF: RESULT = $result');
return result;
}).catchError(_exceptionThrower.throwException); }).catchError(_exceptionThrower.throwException);
} }
} }
@@ -235,6 +235,6 @@ class EmailRepositoryImpl extends EmailRepository {
@override @override
Future<void> printEmail(EmailPrint emailPrint) { Future<void> printEmail(EmailPrint emailPrint) {
return _printFileDataSource.printEmailToPDF(emailPrint); return _printFileDataSource.printEmail(emailPrint);
} }
} }
@@ -29,7 +29,6 @@ class PrintEmailInteractor {
final htmlContentTransformed = await emailRepository.transformHtmlEmailContent( final htmlContentTransformed = await emailRepository.transformHtmlEmailContent(
emailContent, emailContent,
TransformConfiguration.forPrintEmail()); TransformConfiguration.forPrintEmail());
log('PrintEmailInteractor::_transformHtmlEmailContent: htmlContentTransformed: $htmlContentTransformed');
return htmlContentTransformed; return htmlContentTransformed;
} catch (e) { } catch (e) {
logError('PrintEmailInteractor::_transformHtmlEmailContent: Exception: $e'); logError('PrintEmailInteractor::_transformHtmlEmailContent: Exception: $e');
@@ -105,6 +105,7 @@ class EmailBindings extends BaseBindings {
Get.lazyPut(() => PrintFileDataSourceImpl( Get.lazyPut(() => PrintFileDataSourceImpl(
Get.find<PrintUtils>(), Get.find<PrintUtils>(),
Get.find<ImagePaths>(), Get.find<ImagePaths>(),
Get.find<FileUtils>(),
Get.find<CacheExceptionThrower>() Get.find<CacheExceptionThrower>()
)); ));
Get.lazyPut(() => EmailHiveCacheDataSourceImpl( Get.lazyPut(() => EmailHiveCacheDataSourceImpl(
@@ -214,6 +214,7 @@ class MailboxDashBoardBindings extends BaseBindings {
Get.lazyPut(() => PrintFileDataSourceImpl( Get.lazyPut(() => PrintFileDataSourceImpl(
Get.find<PrintUtils>(), Get.find<PrintUtils>(),
Get.find<ImagePaths>(), Get.find<ImagePaths>(),
Get.find<FileUtils>(),
Get.find<CacheExceptionThrower>() Get.find<CacheExceptionThrower>()
)); ));
Get.lazyPut(() => MailboxDataSourceImpl( Get.lazyPut(() => MailboxDataSourceImpl(
@@ -70,6 +70,7 @@ class SendEmailInteractorBindings extends InteractorsBindings {
Get.lazyPut(() => PrintFileDataSourceImpl( Get.lazyPut(() => PrintFileDataSourceImpl(
Get.find<PrintUtils>(), Get.find<PrintUtils>(),
Get.find<ImagePaths>(), Get.find<ImagePaths>(),
Get.find<FileUtils>(),
Get.find<CacheExceptionThrower>(), Get.find<CacheExceptionThrower>(),
)); ));
Get.lazyPut(() => EmailHiveCacheDataSourceImpl( Get.lazyPut(() => EmailHiveCacheDataSourceImpl(
@@ -114,6 +114,7 @@ class FcmInteractorBindings extends InteractorsBindings {
Get.lazyPut(() => PrintFileDataSourceImpl( Get.lazyPut(() => PrintFileDataSourceImpl(
Get.find<PrintUtils>(), Get.find<PrintUtils>(),
Get.find<ImagePaths>(), Get.find<ImagePaths>(),
Get.find<FileUtils>(),
Get.find<CacheExceptionThrower>() Get.find<CacheExceptionThrower>()
)); ));
Get.lazyPut(() => EmailHiveCacheDataSourceImpl( 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:device_info_plus/device_info_plus.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:htmltopdfwidgets/htmltopdfwidgets.dart';
import 'package:shared_preferences/shared_preferences.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/features/sending_queue/presentation/utils/sending_queue_isolate_manager.dart';
import 'package:tmail_ui_user/main/utils/app_config.dart'; import 'package:tmail_ui_user/main/utils/app_config.dart';
@@ -62,8 +61,7 @@ class CoreBindings extends Bindings {
Get.put(CompressFileUtils()); Get.put(CompressFileUtils());
Get.put(AppConfigLoader()); Get.put(AppConfigLoader());
Get.put(FileUtils()); Get.put(FileUtils());
Get.put(HTMLToPdf()); Get.put(PrintUtils(Get.find<ImagePaths>(), Get.find<FileUtils>()));
Get.put(PrintUtils(Get.find<HTMLToPdf>(), Get.find<ImagePaths>()));
} }
void _bindingIsolate() { void _bindingIsolate() {
-56
View File
@@ -57,14 +57,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.0.0" 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: better_open_file:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -73,14 +65,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.6.4" version: "3.6.4"
bidi:
dependency: transitive
description:
name: bidi
sha256: "1a7d0c696324b2089f72e7671fd1f1f64fef44c980f3cebc84e803967c597b63"
url: "https://pub.dev"
source: hosted
version: "2.0.10"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@@ -1046,14 +1030,6 @@ packages:
url: "https://github.com/linagora/html-editor-enhanced.git" url: "https://github.com/linagora/html-editor-enhanced.git"
source: git source: git
version: "2.5.1" version: "2.5.1"
htmltopdfwidgets:
dependency: "direct main"
description:
name: htmltopdfwidgets
sha256: "990e3002fbbfaf307555f1991bed61b6228d7053b7e1945815c7a68f56bbda81"
url: "https://pub.dev"
source: hosted
version: "1.0.3"
http: http:
dependency: transitive dependency: transitive
description: description:
@@ -1358,22 +1334,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.0" 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: percent_indicator:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -1470,14 +1430,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.1" version: "1.5.1"
printing:
dependency: transitive
description:
name: printing
sha256: "1c99cab90ebcc1fff65831d264627d5b529359d563e53f33ab9b8117f2d280bc"
url: "https://pub.dev"
source: hosted
version: "5.12.0"
process: process:
dependency: transitive dependency: transitive
description: description:
@@ -1502,14 +1454,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.3" version: "1.2.3"
qr:
dependency: transitive
description:
name: qr
sha256: "64957a3930367bf97cc211a5af99551d630f2f4625e38af10edd6b19131b64b3"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
quiver: quiver:
dependency: transitive dependency: transitive
description: description:
-2
View File
@@ -231,8 +231,6 @@ dependencies:
mime: 1.0.4 mime: 1.0.4
htmltopdfwidgets: 1.0.3
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter 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_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/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/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/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/domain/usecases/view_attachment_for_web_interactor.dart';
import 'package:tmail_ui_user/features/email/presentation/controller/email_supervisor_controller.dart'; import 'package:tmail_ui_user/features/email/presentation/controller/email_supervisor_controller.dart';
@@ -69,6 +70,7 @@ const fallbackGenerators = {
MockSpec<ImagePaths>(), MockSpec<ImagePaths>(),
MockSpec<ResponsiveUtils>(), MockSpec<ResponsiveUtils>(),
MockSpec<Uuid>(), MockSpec<Uuid>(),
MockSpec<PrintEmailInteractor>(),
]) ])
void main() { void main() {
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();
@@ -99,6 +101,7 @@ void main() {
final imagePaths = MockImagePaths(); final imagePaths = MockImagePaths();
final responsiveUtils = MockResponsiveUtils(); final responsiveUtils = MockResponsiveUtils();
final uuid = MockUuid(); final uuid = MockUuid();
final printEmailInteractor = MockPrintEmailInteractor();
late SingleEmailController singleEmailController = SingleEmailController( late SingleEmailController singleEmailController = SingleEmailController(
getEmailContentInteractor, getEmailContentInteractor,
@@ -112,6 +115,7 @@ void main() {
getAllIdentitiesInteractor, getAllIdentitiesInteractor,
storeOpenedEmailInteractor, storeOpenedEmailInteractor,
viewAttachmentForWebInteractor, viewAttachmentForWebInteractor,
printEmailInteractor,
); );
final testAccountId = AccountId(Id('123')); final testAccountId = AccountId(Id('123'));