TF-3514 Detect base64 image to transfer it to cid attachment
Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import 'dart:typed_data';
|
|||||||
|
|
||||||
import 'package:core/utils/app_logger.dart';
|
import 'package:core/utils/app_logger.dart';
|
||||||
import 'package:core/domain/exceptions/string_exception.dart';
|
import 'package:core/domain/exceptions/string_exception.dart';
|
||||||
|
import 'package:http_parser/http_parser.dart';
|
||||||
|
|
||||||
class StringConvert {
|
class StringConvert {
|
||||||
static const String separatorPattern = r'[ ,;]+';
|
static const String separatorPattern = r'[ ,;]+';
|
||||||
@@ -73,4 +74,29 @@ class StringConvert {
|
|||||||
static String toUrlScheme(String hostScheme) {
|
static String toUrlScheme(String hostScheme) {
|
||||||
return '$hostScheme://';
|
return '$hostScheme://';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Uint8List convertBase64ImageTagToBytes(String base64ImageTag) {
|
||||||
|
if (!base64ImageTag.contains('base64,')) {
|
||||||
|
throw ArgumentError('The string is not valid Base64 data from an <img> tag.');
|
||||||
|
}
|
||||||
|
|
||||||
|
final base64Data = base64ImageTag.split(',').last;
|
||||||
|
|
||||||
|
return base64Decode(base64Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static MediaType? getMediaTypeFromBase64ImageTag(String base64ImageTag) {
|
||||||
|
try {
|
||||||
|
if (!base64ImageTag.startsWith("data:") || !base64ImageTag.contains(";base64,")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final mimeType = base64ImageTag.split(";")[0].split(":")[1];
|
||||||
|
log('StringConvert::getMediaTypeFromBase64ImageTag:mimeType = $mimeType');
|
||||||
|
return MediaType.parse(mimeType);
|
||||||
|
} catch (e) {
|
||||||
|
logError('StringConvert::getMimeTypeFromBase64ImageTag:Exception = $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,15 +54,14 @@ class ComposerRepositoryImpl extends ComposerRepository {
|
|||||||
String emailContent = createEmailRequest.emailContent;
|
String emailContent = createEmailRequest.emailContent;
|
||||||
Set<EmailBodyPart> emailAttachments = Set.from(createEmailRequest.createAttachments());
|
Set<EmailBodyPart> emailAttachments = Set.from(createEmailRequest.createAttachments());
|
||||||
|
|
||||||
if (createEmailRequest.inlineAttachments?.isNotEmpty == true) {
|
final tupleContentInlineAttachments = await _replaceImageBase64ToImageCID(
|
||||||
final tupleContentInlineAttachments = await _replaceImageBase64ToImageCID(
|
emailContent: emailContent,
|
||||||
emailContent: emailContent,
|
inlineAttachments: createEmailRequest.inlineAttachments ?? {},
|
||||||
inlineAttachments: createEmailRequest.inlineAttachments!
|
uploadUri: createEmailRequest.uploadUri,
|
||||||
);
|
);
|
||||||
|
|
||||||
emailContent = tupleContentInlineAttachments.value1;
|
emailContent = tupleContentInlineAttachments.value1;
|
||||||
emailAttachments.addAll(tupleContentInlineAttachments.value2);
|
emailAttachments.addAll(tupleContentInlineAttachments.value2);
|
||||||
}
|
|
||||||
|
|
||||||
emailContent = await _removeCollapsedExpandedSignatureEffect(emailContent: emailContent);
|
emailContent = await _removeCollapsedExpandedSignatureEffect(emailContent: emailContent);
|
||||||
|
|
||||||
@@ -83,18 +82,23 @@ class ComposerRepositoryImpl extends ComposerRepository {
|
|||||||
|
|
||||||
Future<Tuple2<String, Set<EmailBodyPart>>> _replaceImageBase64ToImageCID({
|
Future<Tuple2<String, Set<EmailBodyPart>>> _replaceImageBase64ToImageCID({
|
||||||
required String emailContent,
|
required String emailContent,
|
||||||
required Map<String, Attachment> inlineAttachments
|
required Map<String, Attachment> inlineAttachments,
|
||||||
|
required Uri? uploadUri,
|
||||||
}) {
|
}) {
|
||||||
try {
|
try {
|
||||||
return _htmlDataSource.replaceImageBase64ToImageCID(
|
return _htmlDataSource.replaceImageBase64ToImageCID(
|
||||||
emailContent: emailContent,
|
emailContent: emailContent,
|
||||||
inlineAttachments: inlineAttachments);
|
inlineAttachments: inlineAttachments,
|
||||||
|
uploadUri: uploadUri,
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError('ComposerRepositoryImpl::_replaceImageBase64ToImageCID: Exception: $e');
|
logError('ComposerRepositoryImpl::_replaceImageBase64ToImageCID: Exception: $e');
|
||||||
return Future.value(
|
return Future.value(
|
||||||
Tuple2(
|
Tuple2(
|
||||||
emailContent,
|
emailContent,
|
||||||
inlineAttachments.values.toList().toEmailBodyPart(charset: Constant.base64Charset)
|
inlineAttachments.isNotEmpty
|
||||||
|
? inlineAttachments.values.toList().toEmailBodyPart(charset: Constant.base64Charset)
|
||||||
|
: {},
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,29 +65,13 @@ import 'package:tmail_ui_user/features/upload/data/network/file_uploader.dart';
|
|||||||
import 'package:tmail_ui_user/features/upload/domain/usecases/local_file_picker_interactor.dart';
|
import 'package:tmail_ui_user/features/upload/domain/usecases/local_file_picker_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/domain/usecases/local_image_picker_interactor.dart';
|
import 'package:tmail_ui_user/features/upload/domain/usecases/local_image_picker_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/presentation/controller/upload_controller.dart';
|
import 'package:tmail_ui_user/features/upload/presentation/controller/upload_controller.dart';
|
||||||
import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart';
|
|
||||||
import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart';
|
import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart';
|
||||||
import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart';
|
import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/ios_sharing_manager.dart';
|
import 'package:tmail_ui_user/main/utils/ios_sharing_manager.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
import 'package:worker_manager/worker_manager.dart';
|
|
||||||
|
|
||||||
class ComposerBindings extends BaseBindings {
|
class ComposerBindings extends BaseBindings {
|
||||||
|
|
||||||
@override
|
|
||||||
void dependencies() {
|
|
||||||
_bindingsUtils();
|
|
||||||
super.dependencies();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _bindingsUtils() {
|
|
||||||
Get.lazyPut(() => FileUploader(
|
|
||||||
Get.find<DioClient>(tag: BindingTag.isolateTag),
|
|
||||||
Get.find<Executor>(),
|
|
||||||
Get.find<FileUtils>(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void bindingsDataSourceImpl() {
|
void bindingsDataSourceImpl() {
|
||||||
Get.lazyPut(() => AttachmentUploadDataSourceImpl(
|
Get.lazyPut(() => AttachmentUploadDataSourceImpl(
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import 'package:html/parser.dart';
|
|||||||
import 'package:html_editor_enhanced/html_editor.dart';
|
import 'package:html_editor_enhanced/html_editor.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/properties/properties.dart';
|
import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||||
import 'package:jmap_dart_client/jmap/identities/identity.dart';
|
import 'package:jmap_dart_client/jmap/identities/identity.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||||
@@ -474,24 +475,35 @@ class ComposerController extends BaseController
|
|||||||
mailboxDashBoardController.sessionCurrent!.username);
|
mailboxDashBoardController.sessionCurrent!.username);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Uri? _getUploadUriFromSession(Session session, AccountId accountId) {
|
||||||
|
try {
|
||||||
|
return session.getUploadUri(accountId, jmapUrl: dynamicUrlInterceptors.jmapUrl);
|
||||||
|
} catch (e) {
|
||||||
|
logError('ComposerController::_getUploadUriFromSession:Exception = $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<CreateEmailRequest?> _generateCreateEmailRequest() async {
|
Future<CreateEmailRequest?> _generateCreateEmailRequest() async {
|
||||||
if (composerArguments.value == null ||
|
final arguments = composerArguments.value;
|
||||||
mailboxDashBoardController.sessionCurrent == null ||
|
final session = mailboxDashBoardController.sessionCurrent;
|
||||||
mailboxDashBoardController.accountId.value == null
|
final accountId = mailboxDashBoardController.accountId.value;
|
||||||
) {
|
|
||||||
|
if (arguments == null || session == null || accountId == null) {
|
||||||
log('ComposerController::_generateCreateEmailRequest: SESSION or ACCOUNT_ID or ARGUMENTS is NULL');
|
log('ComposerController::_generateCreateEmailRequest: SESSION or ACCOUNT_ID or ARGUMENTS is NULL');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final emailContent = await getContentInEditor();
|
final emailContent = await getContentInEditor();
|
||||||
|
final uploadUri = _getUploadUriFromSession(session, accountId);
|
||||||
|
|
||||||
return CreateEmailRequest(
|
return CreateEmailRequest(
|
||||||
session: mailboxDashBoardController.sessionCurrent!,
|
session: session,
|
||||||
accountId: mailboxDashBoardController.accountId.value!,
|
accountId: accountId,
|
||||||
emailActionType: composerArguments.value!.emailActionType,
|
emailActionType: arguments.emailActionType,
|
||||||
subject: subjectEmail.value ?? '',
|
subject: subjectEmail.value ?? '',
|
||||||
emailContent: emailContent,
|
emailContent: emailContent,
|
||||||
fromSender: composerArguments.value!.presentationEmail?.from ?? {},
|
fromSender: arguments.presentationEmail?.from ?? {},
|
||||||
toRecipients: listToEmailAddress.toSet(),
|
toRecipients: listToEmailAddress.toSet(),
|
||||||
ccRecipients: listCcEmailAddress.toSet(),
|
ccRecipients: listCcEmailAddress.toSet(),
|
||||||
bccRecipients: listBccEmailAddress.toSet(),
|
bccRecipients: listBccEmailAddress.toSet(),
|
||||||
@@ -505,12 +517,13 @@ class ComposerController extends BaseController
|
|||||||
sentMailboxId: getSentMailboxIdForComposer(),
|
sentMailboxId: getSentMailboxIdForComposer(),
|
||||||
draftsMailboxId: getDraftMailboxIdForComposer(),
|
draftsMailboxId: getDraftMailboxIdForComposer(),
|
||||||
draftsEmailId: getDraftEmailId(),
|
draftsEmailId: getDraftEmailId(),
|
||||||
answerForwardEmailId: composerArguments.value!.presentationEmail?.id,
|
answerForwardEmailId: arguments.presentationEmail?.id,
|
||||||
unsubscribeEmailId: composerArguments.value!.previousEmailId,
|
unsubscribeEmailId: arguments.previousEmailId,
|
||||||
messageId: composerArguments.value!.messageId,
|
messageId: arguments.messageId,
|
||||||
references: composerArguments.value!.references,
|
references: arguments.references,
|
||||||
emailSendingQueue: composerArguments.value!.sendingEmail,
|
emailSendingQueue: arguments.sendingEmail,
|
||||||
displayMode: screenDisplayMode.value
|
displayMode: screenDisplayMode.value,
|
||||||
|
uploadUri: uploadUri,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1043,10 +1056,11 @@ class ComposerController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _handleSendMessages(BuildContext context) async {
|
void _handleSendMessages(BuildContext context) async {
|
||||||
if (composerArguments.value == null ||
|
final arguments = composerArguments.value;
|
||||||
mailboxDashBoardController.sessionCurrent == null ||
|
final session = mailboxDashBoardController.sessionCurrent;
|
||||||
mailboxDashBoardController.accountId.value == null
|
final accountId = mailboxDashBoardController.accountId.value;
|
||||||
) {
|
|
||||||
|
if (arguments == null || session == null || accountId == null) {
|
||||||
log('ComposerController::_handleSendMessages: SESSION or ACCOUNT_ID or ARGUMENTS is NULL');
|
log('ComposerController::_handleSendMessages: SESSION or ACCOUNT_ID or ARGUMENTS is NULL');
|
||||||
_sendButtonState = ButtonState.enabled;
|
_sendButtonState = ButtonState.enabled;
|
||||||
_closeComposerAction(closeOverlays: true);
|
_closeComposerAction(closeOverlays: true);
|
||||||
@@ -1058,9 +1072,14 @@ class ComposerController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
final emailContent = await getContentInEditor();
|
final emailContent = await getContentInEditor();
|
||||||
|
final uploadUri = _getUploadUriFromSession(session, accountId);
|
||||||
final cancelToken = CancelToken();
|
final cancelToken = CancelToken();
|
||||||
final resultState = await _showSendingMessageDialog(
|
final resultState = await _showSendingMessageDialog(
|
||||||
|
session: session,
|
||||||
|
accountId: accountId,
|
||||||
|
arguments: arguments,
|
||||||
emailContent: emailContent,
|
emailContent: emailContent,
|
||||||
|
uploadUri: uploadUri,
|
||||||
cancelToken: cancelToken
|
cancelToken: cancelToken
|
||||||
);
|
);
|
||||||
log('ComposerController::_handleSendMessages: resultState = $resultState');
|
log('ComposerController::_handleSendMessages: resultState = $resultState');
|
||||||
@@ -1080,18 +1099,22 @@ class ComposerController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<dynamic> _showSendingMessageDialog({
|
Future<dynamic> _showSendingMessageDialog({
|
||||||
|
required Session session,
|
||||||
|
required AccountId accountId,
|
||||||
|
required ComposerArguments arguments,
|
||||||
required String emailContent,
|
required String emailContent,
|
||||||
CancelToken? cancelToken
|
required Uri? uploadUri,
|
||||||
|
CancelToken? cancelToken,
|
||||||
}) {
|
}) {
|
||||||
final childWidget = PointerInterceptor(
|
final childWidget = PointerInterceptor(
|
||||||
child: SendingMessageDialogView(
|
child: SendingMessageDialogView(
|
||||||
createEmailRequest: CreateEmailRequest(
|
createEmailRequest: CreateEmailRequest(
|
||||||
session: mailboxDashBoardController.sessionCurrent!,
|
session: session,
|
||||||
accountId: mailboxDashBoardController.accountId.value!,
|
accountId: accountId,
|
||||||
emailActionType: composerArguments.value!.emailActionType,
|
emailActionType: arguments.emailActionType,
|
||||||
subject: subjectEmail.value ?? '',
|
subject: subjectEmail.value ?? '',
|
||||||
emailContent: emailContent,
|
emailContent: emailContent,
|
||||||
fromSender: composerArguments.value!.presentationEmail?.from ?? {},
|
fromSender: arguments.presentationEmail?.from ?? {},
|
||||||
toRecipients: listToEmailAddress.toSet(),
|
toRecipients: listToEmailAddress.toSet(),
|
||||||
ccRecipients: listCcEmailAddress.toSet(),
|
ccRecipients: listCcEmailAddress.toSet(),
|
||||||
bccRecipients: listBccEmailAddress.toSet(),
|
bccRecipients: listBccEmailAddress.toSet(),
|
||||||
@@ -1104,12 +1127,13 @@ class ComposerController extends BaseController
|
|||||||
outboxMailboxId: getOutboxMailboxIdForComposer(),
|
outboxMailboxId: getOutboxMailboxIdForComposer(),
|
||||||
sentMailboxId: getSentMailboxIdForComposer(),
|
sentMailboxId: getSentMailboxIdForComposer(),
|
||||||
draftsEmailId: getDraftEmailId(),
|
draftsEmailId: getDraftEmailId(),
|
||||||
answerForwardEmailId: composerArguments.value!.presentationEmail?.id,
|
answerForwardEmailId: arguments.presentationEmail?.id,
|
||||||
unsubscribeEmailId: composerArguments.value!.previousEmailId,
|
unsubscribeEmailId: arguments.previousEmailId,
|
||||||
messageId: composerArguments.value!.messageId,
|
messageId: arguments.messageId,
|
||||||
references: composerArguments.value!.references,
|
references: arguments.references,
|
||||||
emailSendingQueue: composerArguments.value!.sendingEmail,
|
emailSendingQueue: arguments.sendingEmail,
|
||||||
displayMode: screenDisplayMode.value
|
displayMode: screenDisplayMode.value,
|
||||||
|
uploadUri: uploadUri,
|
||||||
),
|
),
|
||||||
createNewAndSendEmailInteractor: _createNewAndSendEmailInteractor,
|
createNewAndSendEmailInteractor: _createNewAndSendEmailInteractor,
|
||||||
onCancelSendingEmailAction: _handleCancelSendingMessage,
|
onCancelSendingEmailAction: _handleCancelSendingMessage,
|
||||||
@@ -1350,9 +1374,13 @@ class ComposerController extends BaseController
|
|||||||
|
|
||||||
_saveToDraftButtonState = ButtonState.disabled;
|
_saveToDraftButtonState = ButtonState.disabled;
|
||||||
|
|
||||||
if (composerArguments.value == null ||
|
final arguments = composerArguments.value;
|
||||||
mailboxDashBoardController.sessionCurrent == null ||
|
final session = mailboxDashBoardController.sessionCurrent;
|
||||||
mailboxDashBoardController.accountId.value == null ||
|
final accountId = mailboxDashBoardController.accountId.value;
|
||||||
|
|
||||||
|
if (arguments == null ||
|
||||||
|
session == null ||
|
||||||
|
accountId == null ||
|
||||||
getDraftMailboxIdForComposer() == null
|
getDraftMailboxIdForComposer() == null
|
||||||
) {
|
) {
|
||||||
log('ComposerController::handleClickSaveAsDraftsButton: SESSION or ACCOUNT_ID or ARGUMENTS is NULL');
|
log('ComposerController::handleClickSaveAsDraftsButton: SESSION or ACCOUNT_ID or ARGUMENTS is NULL');
|
||||||
@@ -1361,9 +1389,14 @@ class ComposerController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
final emailContent = await getContentInEditor();
|
final emailContent = await getContentInEditor();
|
||||||
|
final uploadUri = _getUploadUriFromSession(session, accountId);
|
||||||
final cancelToken = CancelToken();
|
final cancelToken = CancelToken();
|
||||||
final resultState = await _showSavingMessageToDraftsDialog(
|
final resultState = await _showSavingMessageToDraftsDialog(
|
||||||
|
session: session,
|
||||||
|
accountId: accountId,
|
||||||
|
arguments: arguments,
|
||||||
emailContent: emailContent,
|
emailContent: emailContent,
|
||||||
|
uploadUri: uploadUri,
|
||||||
draftEmailId: _emailIdEditing,
|
draftEmailId: _emailIdEditing,
|
||||||
cancelToken: cancelToken
|
cancelToken: cancelToken
|
||||||
);
|
);
|
||||||
@@ -2291,9 +2324,13 @@ class ComposerController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _handleSaveMessageToDraft(BuildContext context) async {
|
void _handleSaveMessageToDraft(BuildContext context) async {
|
||||||
if (composerArguments.value == null ||
|
final arguments = composerArguments.value;
|
||||||
mailboxDashBoardController.sessionCurrent == null ||
|
final session = mailboxDashBoardController.sessionCurrent;
|
||||||
mailboxDashBoardController.accountId.value == null ||
|
final accountId = mailboxDashBoardController.accountId.value;
|
||||||
|
|
||||||
|
if (arguments == null ||
|
||||||
|
session == null ||
|
||||||
|
accountId == null ||
|
||||||
getDraftMailboxIdForComposer() == null
|
getDraftMailboxIdForComposer() == null
|
||||||
) {
|
) {
|
||||||
log('ComposerController::_handleSaveMessageToDraft: SESSION or ACCOUNT_ID or ARGUMENTS is NULL');
|
log('ComposerController::_handleSaveMessageToDraft: SESSION or ACCOUNT_ID or ARGUMENTS is NULL');
|
||||||
@@ -2305,11 +2342,16 @@ class ComposerController extends BaseController
|
|||||||
popBack();
|
popBack();
|
||||||
|
|
||||||
final emailContent = await getContentInEditor();
|
final emailContent = await getContentInEditor();
|
||||||
|
final uploadUri = _getUploadUriFromSession(session, accountId);
|
||||||
final draftEmailId = getDraftEmailId();
|
final draftEmailId = getDraftEmailId();
|
||||||
log('ComposerController::_handleSaveMessageToDraft: draftEmailId = $draftEmailId');
|
log('ComposerController::_handleSaveMessageToDraft: draftEmailId = $draftEmailId');
|
||||||
final cancelToken = CancelToken();
|
final cancelToken = CancelToken();
|
||||||
final resultState = await _showSavingMessageToDraftsDialog(
|
final resultState = await _showSavingMessageToDraftsDialog(
|
||||||
|
session: session,
|
||||||
|
accountId: accountId,
|
||||||
|
arguments: arguments,
|
||||||
emailContent: emailContent,
|
emailContent: emailContent,
|
||||||
|
uploadUri: uploadUri,
|
||||||
draftEmailId: draftEmailId,
|
draftEmailId: draftEmailId,
|
||||||
cancelToken: cancelToken
|
cancelToken: cancelToken
|
||||||
);
|
);
|
||||||
@@ -2346,19 +2388,23 @@ class ComposerController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<dynamic> _showSavingMessageToDraftsDialog({
|
Future<dynamic> _showSavingMessageToDraftsDialog({
|
||||||
|
required Session session,
|
||||||
|
required AccountId accountId,
|
||||||
|
required ComposerArguments arguments,
|
||||||
required String emailContent,
|
required String emailContent,
|
||||||
|
required Uri? uploadUri,
|
||||||
EmailId? draftEmailId,
|
EmailId? draftEmailId,
|
||||||
CancelToken? cancelToken,
|
CancelToken? cancelToken,
|
||||||
}) {
|
}) {
|
||||||
final childWidget = PointerInterceptor(
|
final childWidget = PointerInterceptor(
|
||||||
child: SavingMessageDialogView(
|
child: SavingMessageDialogView(
|
||||||
createEmailRequest: CreateEmailRequest(
|
createEmailRequest: CreateEmailRequest(
|
||||||
session: mailboxDashBoardController.sessionCurrent!,
|
session: session,
|
||||||
accountId: mailboxDashBoardController.accountId.value!,
|
accountId: accountId,
|
||||||
emailActionType: composerArguments.value!.emailActionType,
|
emailActionType: arguments.emailActionType,
|
||||||
subject: subjectEmail.value ?? '',
|
subject: subjectEmail.value ?? '',
|
||||||
emailContent: emailContent,
|
emailContent: emailContent,
|
||||||
fromSender: composerArguments.value!.presentationEmail?.from ?? {},
|
fromSender: arguments.presentationEmail?.from ?? {},
|
||||||
toRecipients: listToEmailAddress.toSet(),
|
toRecipients: listToEmailAddress.toSet(),
|
||||||
ccRecipients: listCcEmailAddress.toSet(),
|
ccRecipients: listCcEmailAddress.toSet(),
|
||||||
bccRecipients: listBccEmailAddress.toSet(),
|
bccRecipients: listBccEmailAddress.toSet(),
|
||||||
@@ -2371,12 +2417,13 @@ class ComposerController extends BaseController
|
|||||||
sentMailboxId: getSentMailboxIdForComposer(),
|
sentMailboxId: getSentMailboxIdForComposer(),
|
||||||
draftsMailboxId: getDraftMailboxIdForComposer(),
|
draftsMailboxId: getDraftMailboxIdForComposer(),
|
||||||
draftsEmailId: draftEmailId,
|
draftsEmailId: draftEmailId,
|
||||||
answerForwardEmailId: composerArguments.value!.presentationEmail?.id,
|
answerForwardEmailId: arguments.presentationEmail?.id,
|
||||||
unsubscribeEmailId: composerArguments.value!.previousEmailId,
|
unsubscribeEmailId: arguments.previousEmailId,
|
||||||
messageId: composerArguments.value!.messageId,
|
messageId: arguments.messageId,
|
||||||
references: composerArguments.value!.references,
|
references: arguments.references,
|
||||||
emailSendingQueue: composerArguments.value!.sendingEmail,
|
emailSendingQueue: arguments.sendingEmail,
|
||||||
displayMode: screenDisplayMode.value
|
displayMode: screenDisplayMode.value,
|
||||||
|
uploadUri: uploadUri,
|
||||||
),
|
),
|
||||||
createNewAndSaveEmailToDraftsInteractor: _createNewAndSaveEmailToDraftsInteractor,
|
createNewAndSaveEmailToDraftsInteractor: _createNewAndSaveEmailToDraftsInteractor,
|
||||||
onCancelSavingEmailToDraftsAction: _handleCancelSavingMessageToDrafts,
|
onCancelSavingEmailToDraftsAction: _handleCancelSavingMessageToDrafts,
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ class CreateEmailRequest with EquatableMixin {
|
|||||||
final MessageIdsHeaderValue? references;
|
final MessageIdsHeaderValue? references;
|
||||||
final SendingEmail? emailSendingQueue;
|
final SendingEmail? emailSendingQueue;
|
||||||
final ScreenDisplayMode displayMode;
|
final ScreenDisplayMode displayMode;
|
||||||
|
final Uri? uploadUri;
|
||||||
|
|
||||||
CreateEmailRequest({
|
CreateEmailRequest({
|
||||||
required this.session,
|
required this.session,
|
||||||
@@ -64,7 +65,8 @@ class CreateEmailRequest with EquatableMixin {
|
|||||||
this.messageId,
|
this.messageId,
|
||||||
this.references,
|
this.references,
|
||||||
this.emailSendingQueue,
|
this.emailSendingQueue,
|
||||||
this.displayMode = ScreenDisplayMode.normal
|
this.displayMode = ScreenDisplayMode.normal,
|
||||||
|
this.uploadUri,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -94,5 +96,6 @@ class CreateEmailRequest with EquatableMixin {
|
|||||||
references,
|
references,
|
||||||
emailSendingQueue,
|
emailSendingQueue,
|
||||||
displayMode,
|
displayMode,
|
||||||
|
uploadUri,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,8 @@ abstract class HtmlDataSource {
|
|||||||
|
|
||||||
Future<Tuple2<String, Set<EmailBodyPart>>> replaceImageBase64ToImageCID({
|
Future<Tuple2<String, Set<EmailBodyPart>>> replaceImageBase64ToImageCID({
|
||||||
required String emailContent,
|
required String emailContent,
|
||||||
required Map<String, Attachment> inlineAttachments
|
required Map<String, Attachment> inlineAttachments,
|
||||||
|
required Uri? uploadUri,
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<String> removeCollapsedExpandedSignatureEffect({required String emailContent});
|
Future<String> removeCollapsedExpandedSignatureEffect({required String emailContent});
|
||||||
|
|||||||
@@ -42,12 +42,14 @@ class HtmlDataSourceImpl extends HtmlDataSource {
|
|||||||
@override
|
@override
|
||||||
Future<Tuple2<String, Set<EmailBodyPart>>> replaceImageBase64ToImageCID({
|
Future<Tuple2<String, Set<EmailBodyPart>>> replaceImageBase64ToImageCID({
|
||||||
required String emailContent,
|
required String emailContent,
|
||||||
required Map<String, Attachment> inlineAttachments
|
required Map<String, Attachment> inlineAttachments,
|
||||||
|
required Uri? uploadUri,
|
||||||
}) {
|
}) {
|
||||||
return Future.sync(() async {
|
return Future.sync(() async {
|
||||||
return await _htmlAnalyzer.replaceImageBase64ToImageCID(
|
return await _htmlAnalyzer.replaceImageBase64ToImageCID(
|
||||||
emailContent: emailContent,
|
emailContent: emailContent,
|
||||||
inlineAttachments: inlineAttachments
|
inlineAttachments: inlineAttachments,
|
||||||
|
uploadUri: uploadUri,
|
||||||
);
|
);
|
||||||
}).catchError(_exceptionThrower.throwException);
|
}).catchError(_exceptionThrower.throwException);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:core/data/constants/constant.dart';
|
import 'package:core/data/constants/constant.dart';
|
||||||
import 'package:core/presentation/utils/html_transformer/html_transform.dart';
|
import 'package:core/presentation/utils/html_transformer/html_transform.dart';
|
||||||
@@ -5,6 +7,7 @@ import 'package:core/presentation/utils/html_transformer/text/persist_preformatt
|
|||||||
import 'package:core/presentation/utils/html_transformer/text/sanitize_autolink_html_transformers.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:core/utils/app_logger.dart';
|
import 'package:core/utils/app_logger.dart';
|
||||||
|
import 'package:core/utils/string_convert.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:html/parser.dart';
|
import 'package:html/parser.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email_body_part.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email_body_part.dart';
|
||||||
@@ -12,13 +15,21 @@ import 'package:model/email/attachment.dart';
|
|||||||
import 'package:model/email/email_content.dart';
|
import 'package:model/email/email_content.dart';
|
||||||
import 'package:model/email/email_content_type.dart';
|
import 'package:model/email/email_content_type.dart';
|
||||||
import 'package:model/extensions/attachment_extension.dart';
|
import 'package:model/extensions/attachment_extension.dart';
|
||||||
|
import 'package:model/upload/file_info.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/extensions/list_attachments_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/model/event_action.dart';
|
import 'package:tmail_ui_user/features/email/domain/model/event_action.dart';
|
||||||
|
import 'package:tmail_ui_user/features/upload/data/network/file_uploader.dart';
|
||||||
|
import 'package:tmail_ui_user/features/upload/domain/model/upload_task_id.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class HtmlAnalyzer {
|
class HtmlAnalyzer {
|
||||||
|
static const String cidPrefixKey = 'cid:';
|
||||||
|
|
||||||
final HtmlTransform _htmlTransform;
|
final HtmlTransform _htmlTransform;
|
||||||
|
final FileUploader _fileUploader;
|
||||||
|
final Uuid _uuid;
|
||||||
|
|
||||||
HtmlAnalyzer(this._htmlTransform);
|
HtmlAnalyzer(this._htmlTransform, this._fileUploader, this._uuid);
|
||||||
|
|
||||||
Future<EmailContent> transformEmailContent(
|
Future<EmailContent> transformEmailContent(
|
||||||
EmailContent emailContent,
|
EmailContent emailContent,
|
||||||
@@ -112,35 +123,71 @@ class HtmlAnalyzer {
|
|||||||
|
|
||||||
Future<Tuple2<String, Set<EmailBodyPart>>> replaceImageBase64ToImageCID({
|
Future<Tuple2<String, Set<EmailBodyPart>>> replaceImageBase64ToImageCID({
|
||||||
required String emailContent,
|
required String emailContent,
|
||||||
required Map<String, Attachment> inlineAttachments
|
required Map<String, Attachment> inlineAttachments,
|
||||||
|
required Uri? uploadUri,
|
||||||
}) async {
|
}) async {
|
||||||
final document = parse(emailContent);
|
final document = parse(emailContent);
|
||||||
final listImgTag = document.querySelectorAll('img[src^="data:image/"][id^="cid:"]');
|
final listImgTag = document.querySelectorAll('img[src^="data:image/"]');
|
||||||
|
log('HtmlAnalyzer::replaceImageBase64ToImageCID:listImgTagLength = ${listImgTag.length} | inlineAttachments = ${inlineAttachments.length}');
|
||||||
|
|
||||||
final listInlineAttachment = await Future.wait(listImgTag.map((imgTag) async {
|
if (listImgTag.isEmpty) {
|
||||||
final idImg = imgTag.attributes['id'];
|
return Tuple2(
|
||||||
final cid = idImg!.replaceFirst('cid:', '').trim();
|
emailContent,
|
||||||
imgTag.attributes['src'] = 'cid:$cid';
|
inlineAttachments.isNotEmpty
|
||||||
imgTag.attributes.remove('id');
|
? inlineAttachments.values.toList().toEmailBodyPart(charset: Constant.base64Charset)
|
||||||
return cid;
|
: {},
|
||||||
})).then((listCid) {
|
);
|
||||||
final listInlineAttachment = listCid
|
}
|
||||||
.map((cid) {
|
|
||||||
if (inlineAttachments.containsKey(cid)) {
|
|
||||||
return inlineAttachments[cid]!.toEmailBodyPart(charset: Constant.base64Charset);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.whereNotNull()
|
|
||||||
.toSet();
|
|
||||||
|
|
||||||
return listInlineAttachment;
|
final Set<EmailBodyPart> inlineAttachmentsSet = {};
|
||||||
});
|
final List<Future<void>> asyncTasks = [];
|
||||||
|
|
||||||
|
for (final imgTag in listImgTag) {
|
||||||
|
late final LinkedHashMap<Object, String> attributes = imgTag.attributes;
|
||||||
|
final idImg = attributes['id'];
|
||||||
|
final imageSrc = attributes['src'];
|
||||||
|
|
||||||
|
if (imageSrc?.isEmpty ?? true) continue;
|
||||||
|
|
||||||
|
if (idImg?.startsWith(cidPrefixKey) == true) {
|
||||||
|
final cid = idImg!.substring(cidPrefixKey.length).trim();
|
||||||
|
attributes['src'] = '$cidPrefixKey$cid';
|
||||||
|
attributes.remove('id');
|
||||||
|
|
||||||
|
final attachment = inlineAttachments[cid];
|
||||||
|
log('HtmlAnalyzer::replaceImageBase64ToImageCID:attachment = $attachment');
|
||||||
|
if (attachment != null) {
|
||||||
|
inlineAttachmentsSet.add(attachment.toEmailBodyPart(charset: Constant.base64Charset));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uploadUri == null) continue;
|
||||||
|
|
||||||
|
asyncTasks.add(_retrieveAttachmentFromUpload(
|
||||||
|
uploadUri: uploadUri,
|
||||||
|
base64ImageTag: imageSrc!,
|
||||||
|
).then((newAttachment) {
|
||||||
|
if (newAttachment == null) return;
|
||||||
|
|
||||||
|
final newInlineAttachment = newAttachment.toAttachmentWithDisposition(
|
||||||
|
disposition: ContentDisposition.inline,
|
||||||
|
cid: _uuid.v1(),
|
||||||
|
);
|
||||||
|
final newCid = newInlineAttachment.cid;
|
||||||
|
inlineAttachments[newCid!] = newInlineAttachment;
|
||||||
|
attributes['src'] = '$cidPrefixKey$newCid';
|
||||||
|
|
||||||
|
inlineAttachmentsSet.add(newInlineAttachment.toEmailBodyPart(charset: Constant.base64Charset));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asyncTasks.isNotEmpty) {
|
||||||
|
await Future.wait(asyncTasks);
|
||||||
|
}
|
||||||
|
|
||||||
final newContent = document.body?.innerHtml ?? emailContent;
|
final newContent = document.body?.innerHtml ?? emailContent;
|
||||||
|
return Tuple2(newContent, inlineAttachmentsSet);
|
||||||
return Tuple2(newContent, listInlineAttachment);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> removeCollapsedExpandedSignatureEffect({required String emailContent}) async {
|
Future<String> removeCollapsedExpandedSignatureEffect({required String emailContent}) async {
|
||||||
@@ -162,4 +209,32 @@ class HtmlAnalyzer {
|
|||||||
log('HtmlAnalyzer::removeCollapsedExpandedSignatureEffect: AFTER = $newContent');
|
log('HtmlAnalyzer::removeCollapsedExpandedSignatureEffect: AFTER = $newContent');
|
||||||
return newContent;
|
return newContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Attachment?> _retrieveAttachmentFromUpload({
|
||||||
|
required Uri uploadUri,
|
||||||
|
required String base64ImageTag,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
final imageBytes = StringConvert.convertBase64ImageTagToBytes(base64ImageTag);
|
||||||
|
final mediaType = StringConvert.getMediaTypeFromBase64ImageTag(base64ImageTag);
|
||||||
|
log('HtmlAnalyzer::_retrieveAttachmentFromUpload: mimeType = ${mediaType?.mimeType} | imageBytesLength = ${imageBytes.length}');
|
||||||
|
final generateId = _uuid.v1();
|
||||||
|
final fileInfo = FileInfo.fromBytes(
|
||||||
|
bytes: imageBytes,
|
||||||
|
name: '$generateId.${mediaType?.subtype ?? 'png'}',
|
||||||
|
type: mediaType?.mimeType,
|
||||||
|
);
|
||||||
|
|
||||||
|
final attachment = await _fileUploader.uploadAttachment(
|
||||||
|
UploadTaskId(generateId),
|
||||||
|
fileInfo,
|
||||||
|
uploadUri,
|
||||||
|
);
|
||||||
|
log('HtmlAnalyzer::_retrieveAttachmentFromUpload:Attachment = $attachment');
|
||||||
|
return attachment;
|
||||||
|
} catch (e) {
|
||||||
|
logError('HtmlAnalyzer::_retrieveAttachmentFromUpload:Exception = $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,7 @@ extension DetailedEmailHiveCacheExtension on DetailedEmailHiveCache {
|
|||||||
emailId: EmailId(Id(emailId)),
|
emailId: EmailId(Id(emailId)),
|
||||||
createdTime: timeSaved,
|
createdTime: timeSaved,
|
||||||
attachments: attachments?.toAttachment(),
|
attachments: attachments?.toAttachment(),
|
||||||
|
emailContentPath: emailContentPath,
|
||||||
headers: headers?.toSetEmailHeader(),
|
headers: headers?.toSetEmailHeader(),
|
||||||
keywords: keywords != null
|
keywords: keywords != null
|
||||||
? Map.fromIterables(keywords!.keys.map((value) => KeyWordIdentifier(value)), keywords!.values)
|
? Map.fromIterables(keywords!.keys.map((value) => KeyWordIdentifier(value)), keywords!.values)
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ class GetEmailContentInteractor {
|
|||||||
additionalProperties: additionalProperties);
|
additionalProperties: additionalProperties);
|
||||||
final listAttachments = email.allAttachments.getListAttachmentsDisplayedOutside(email.htmlBodyAttachments);
|
final listAttachments = email.allAttachments.getListAttachmentsDisplayedOutside(email.htmlBodyAttachments);
|
||||||
final listInlineImages = email.allAttachments.listAttachmentsDisplayedInContent;
|
final listInlineImages = email.allAttachments.listAttachmentsDisplayedInContent;
|
||||||
|
log('GetEmailContentInteractor::_getContentEmailFromServer: listAttachments = ${listAttachments.length} | listInlineImages = ${listInlineImages.length}');
|
||||||
if (email.emailContentList.isNotEmpty) {
|
if (email.emailContentList.isNotEmpty) {
|
||||||
final mapCidImageDownloadUrl = listInlineImages.toMapCidImageDownloadUrl(
|
final mapCidImageDownloadUrl = listInlineImages.toMapCidImageDownloadUrl(
|
||||||
accountId: accountId,
|
accountId: accountId,
|
||||||
@@ -109,8 +109,8 @@ class GetEmailContentInteractor {
|
|||||||
{Properties? additionalProperties}
|
{Properties? additionalProperties}
|
||||||
) async* {
|
) async* {
|
||||||
try {
|
try {
|
||||||
log('GetEmailContentInteractor::_getStoredOpenedEmail(): CALLED');
|
|
||||||
final detailedEmail = await emailRepository.getStoredOpenedEmail(session, accountId, emailId);
|
final detailedEmail = await emailRepository.getStoredOpenedEmail(session, accountId, emailId);
|
||||||
|
log('GetEmailContentInteractor::_getStoredOpenedEmail: attachments = ${detailedEmail.attachments?.length} | inlineImages = ${detailedEmail.inlineImages?.length}');
|
||||||
yield Right<Failure, Success>(GetEmailContentFromCacheSuccess(
|
yield Right<Failure, Success>(GetEmailContentFromCacheSuccess(
|
||||||
htmlEmailContent: detailedEmail.htmlEmailContent ?? '',
|
htmlEmailContent: detailedEmail.htmlEmailContent ?? '',
|
||||||
attachments: detailedEmail.attachments ?? [],
|
attachments: detailedEmail.attachments ?? [],
|
||||||
@@ -144,8 +144,8 @@ class GetEmailContentInteractor {
|
|||||||
{Properties? additionalProperties}
|
{Properties? additionalProperties}
|
||||||
) async* {
|
) async* {
|
||||||
try {
|
try {
|
||||||
log('GetEmailContentInteractor::_getStoredNewEmail():CALLED');
|
|
||||||
final detailedEmail = await emailRepository.getStoredNewEmail(session, accountId, emailId);
|
final detailedEmail = await emailRepository.getStoredNewEmail(session, accountId, emailId);
|
||||||
|
log('GetEmailContentInteractor::_getStoredNewEmail: attachments = ${detailedEmail.attachments?.length} | inlineImages = ${detailedEmail.inlineImages?.length}');
|
||||||
yield Right<Failure, Success>(GetEmailContentFromCacheSuccess(
|
yield Right<Failure, Success>(GetEmailContentFromCacheSuccess(
|
||||||
htmlEmailContent: detailedEmail.htmlEmailContent ?? '',
|
htmlEmailContent: detailedEmail.htmlEmailContent ?? '',
|
||||||
attachments: detailedEmail.attachments ?? [],
|
attachments: detailedEmail.attachments ?? [],
|
||||||
|
|||||||
@@ -686,6 +686,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
emailId: currentEmail!.id!,
|
emailId: currentEmail!.id!,
|
||||||
createdTime: currentEmail?.receivedAt?.value ?? DateTime.now(),
|
createdTime: currentEmail?.receivedAt?.value ?? DateTime.now(),
|
||||||
attachments: success.attachments,
|
attachments: success.attachments,
|
||||||
|
inlineImages: success.inlineImages,
|
||||||
headers: success.emailCurrent?.headers,
|
headers: success.emailCurrent?.headers,
|
||||||
keywords: success.emailCurrent?.keywords,
|
keywords: success.emailCurrent?.keywords,
|
||||||
htmlEmailContent: success.htmlEmailContent,
|
htmlEmailContent: success.htmlEmailContent,
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import 'package:core/data/network/dio_client.dart';
|
|
||||||
import 'package:core/data/network/download/download_client.dart';
|
import 'package:core/data/network/download/download_client.dart';
|
||||||
import 'package:core/utils/application_manager.dart';
|
import 'package:core/utils/application_manager.dart';
|
||||||
import 'package:core/utils/file_utils.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:jmap_dart_client/http/http_client.dart';
|
import 'package:jmap_dart_client/http/http_client.dart';
|
||||||
import 'package:tmail_ui_user/features/base/base_bindings.dart';
|
import 'package:tmail_ui_user/features/base/base_bindings.dart';
|
||||||
@@ -29,7 +27,6 @@ import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart';
|
|||||||
import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart';
|
import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart';
|
||||||
import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart';
|
import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
import 'package:worker_manager/worker_manager.dart';
|
|
||||||
|
|
||||||
class PublicAssetBindings extends BaseBindings {
|
class PublicAssetBindings extends BaseBindings {
|
||||||
PublicAssetBindings(this._publicAssetArguments);
|
PublicAssetBindings(this._publicAssetArguments);
|
||||||
@@ -38,12 +35,6 @@ class PublicAssetBindings extends BaseBindings {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dependencies() {
|
void dependencies() {
|
||||||
Get.lazyPut(
|
|
||||||
() => FileUploader(
|
|
||||||
Get.find<DioClient>(tag: BindingTag.isolateTag),
|
|
||||||
Get.find<Executor>(),
|
|
||||||
Get.find<FileUtils>()),
|
|
||||||
tag: BindingTag.publicAssetBindingsTag);
|
|
||||||
Get.lazyPut(
|
Get.lazyPut(
|
||||||
() => PublicAssetApi(Get.find<HttpClient>(), Get.find<Uuid>()),
|
() => PublicAssetApi(Get.find<HttpClient>(), Get.find<Uuid>()),
|
||||||
tag: BindingTag.publicAssetBindingsTag);
|
tag: BindingTag.publicAssetBindingsTag);
|
||||||
@@ -82,7 +73,7 @@ class PublicAssetBindings extends BaseBindings {
|
|||||||
void bindingsDataSourceImpl() {
|
void bindingsDataSourceImpl() {
|
||||||
Get.lazyPut(
|
Get.lazyPut(
|
||||||
() => AttachmentUploadDataSourceImpl(
|
() => AttachmentUploadDataSourceImpl(
|
||||||
Get.find<FileUploader>(tag: BindingTag.publicAssetBindingsTag),
|
Get.find<FileUploader>(),
|
||||||
Get.find<Uuid>(),
|
Get.find<Uuid>(),
|
||||||
Get.find<RemoteExceptionThrower>()),
|
Get.find<RemoteExceptionThrower>()),
|
||||||
tag: BindingTag.publicAssetBindingsTag);
|
tag: BindingTag.publicAssetBindingsTag);
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
import 'package:core/data/network/dio_client.dart';
|
import 'package:core/data/network/dio_client.dart';
|
||||||
import 'package:core/utils/file_utils.dart';
|
import 'package:core/utils/file_utils.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:model/upload/file_info.dart';
|
||||||
import 'package:tmail_ui_user/features/base/isolate/background_isolate_binary_messenger/background_isolate_binary_messenger.dart';
|
import 'package:tmail_ui_user/features/base/isolate/background_isolate_binary_messenger/background_isolate_binary_messenger.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/domain/model/mobile_file_upload.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';
|
||||||
|
|
||||||
class UploadFileArguments with EquatableMixin {
|
class UploadFileArguments with EquatableMixin {
|
||||||
@@ -11,7 +11,7 @@ class UploadFileArguments with EquatableMixin {
|
|||||||
final DioClient dioClient;
|
final DioClient dioClient;
|
||||||
final FileUtils fileUtils;
|
final FileUtils fileUtils;
|
||||||
final UploadTaskId uploadId;
|
final UploadTaskId uploadId;
|
||||||
final MobileFileUpload mobileFileUpload;
|
final FileInfo fileInfo;
|
||||||
final Uri uploadUri;
|
final Uri uploadUri;
|
||||||
final RootIsolateToken isolateToken;
|
final RootIsolateToken isolateToken;
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ class UploadFileArguments with EquatableMixin {
|
|||||||
this.dioClient,
|
this.dioClient,
|
||||||
this.fileUtils,
|
this.fileUtils,
|
||||||
this.uploadId,
|
this.uploadId,
|
||||||
this.mobileFileUpload,
|
this.fileInfo,
|
||||||
this.uploadUri,
|
this.uploadUri,
|
||||||
this.isolateToken,
|
this.isolateToken,
|
||||||
);
|
);
|
||||||
@@ -29,7 +29,7 @@ class UploadFileArguments with EquatableMixin {
|
|||||||
dioClient,
|
dioClient,
|
||||||
fileUtils,
|
fileUtils,
|
||||||
uploadId,
|
uploadId,
|
||||||
mobileFileUpload,
|
fileInfo,
|
||||||
uploadUri,
|
uploadUri,
|
||||||
isolateToken,
|
isolateToken,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import 'package:tmail_ui_user/features/base/isolate/background_isolate_binary_me
|
|||||||
import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart';
|
import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/data/model/upload_file_arguments.dart';
|
import 'package:tmail_ui_user/features/upload/data/model/upload_file_arguments.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/domain/exceptions/upload_exception.dart';
|
import 'package:tmail_ui_user/features/upload/domain/exceptions/upload_exception.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/domain/extensions/file_info_extension.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';
|
||||||
import 'package:tmail_ui_user/features/upload/domain/state/attachment_upload_state.dart';
|
import 'package:tmail_ui_user/features/upload/domain/state/attachment_upload_state.dart';
|
||||||
import 'package:tmail_ui_user/main/exceptions/isolate_exception.dart';
|
import 'package:tmail_ui_user/main/exceptions/isolate_exception.dart';
|
||||||
@@ -43,31 +42,33 @@ class FileUploader {
|
|||||||
|
|
||||||
Future<Attachment> uploadAttachment(
|
Future<Attachment> uploadAttachment(
|
||||||
UploadTaskId uploadId,
|
UploadTaskId uploadId,
|
||||||
StreamController<Either<Failure, Success>> onSendController,
|
|
||||||
FileInfo fileInfo,
|
FileInfo fileInfo,
|
||||||
Uri uploadUri,
|
Uri uploadUri,
|
||||||
{CancelToken? cancelToken}
|
{
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
StreamController<Either<Failure, Success>>? onSendController,
|
||||||
|
}
|
||||||
) async {
|
) async {
|
||||||
if (PlatformInfo.isWeb) {
|
if (PlatformInfo.isWeb) {
|
||||||
return _handleUploadAttachmentActionOnWeb(
|
return _handleUploadAttachmentActionOnWeb(
|
||||||
uploadId,
|
uploadId,
|
||||||
onSendController,
|
|
||||||
fileInfo,
|
fileInfo,
|
||||||
uploadUri,
|
uploadUri,
|
||||||
cancelToken: cancelToken);
|
cancelToken: cancelToken,
|
||||||
|
onSendController: onSendController,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
final rootIsolateToken = RootIsolateToken.instance;
|
final rootIsolateToken = RootIsolateToken.instance;
|
||||||
if (rootIsolateToken == null) {
|
if (rootIsolateToken == null) {
|
||||||
throw CanNotGetRootIsolateToken();
|
throw CanNotGetRootIsolateToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
final mobileFileUpload = fileInfo.toMobileFileUpload();
|
|
||||||
return await _isolateExecutor.execute(
|
return await _isolateExecutor.execute(
|
||||||
arg1: UploadFileArguments(
|
arg1: UploadFileArguments(
|
||||||
_dioClient,
|
_dioClient,
|
||||||
_fileUtils,
|
_fileUtils,
|
||||||
uploadId,
|
uploadId,
|
||||||
mobileFileUpload,
|
fileInfo,
|
||||||
uploadUri,
|
uploadUri,
|
||||||
rootIsolateToken,
|
rootIsolateToken,
|
||||||
),
|
),
|
||||||
@@ -75,7 +76,7 @@ class FileUploader {
|
|||||||
notification: (value) {
|
notification: (value) {
|
||||||
if (value is Success) {
|
if (value is Success) {
|
||||||
log('FileUploader::uploadAttachment(): onUpdateProgress: $value');
|
log('FileUploader::uploadAttachment(): onUpdateProgress: $value');
|
||||||
onSendController.add(Right(value));
|
onSendController?.add(Right(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -94,12 +95,15 @@ class FileUploader {
|
|||||||
await HiveCacheConfig.instance.setUp();
|
await HiveCacheConfig.instance.setUp();
|
||||||
|
|
||||||
final headerParam = argsUpload.dioClient.getHeaders();
|
final headerParam = argsUpload.dioClient.getHeaders();
|
||||||
headerParam[HttpHeaders.contentTypeHeader] = argsUpload.mobileFileUpload.mimeType;
|
headerParam[HttpHeaders.contentTypeHeader] = argsUpload.fileInfo.mimeType;
|
||||||
headerParam[HttpHeaders.contentLengthHeader] = argsUpload.mobileFileUpload.fileSize;
|
headerParam[HttpHeaders.contentLengthHeader] = argsUpload.fileInfo.fileSize;
|
||||||
|
|
||||||
final mapExtra = <String, dynamic>{
|
final mapExtra = <String, dynamic>{
|
||||||
uploadAttachmentExtraKey: {
|
uploadAttachmentExtraKey: {
|
||||||
filePathExtraKey: argsUpload.mobileFileUpload.filePath,
|
if (argsUpload.fileInfo.filePath?.isNotEmpty == true)
|
||||||
|
filePathExtraKey: argsUpload.fileInfo.filePath,
|
||||||
|
if (argsUpload.fileInfo.bytes?.isNotEmpty == true)
|
||||||
|
streamDataExtraKey: BodyBytesStream.fromBytes(argsUpload.fileInfo.bytes!),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -109,31 +113,39 @@ class FileUploader {
|
|||||||
headers: headerParam,
|
headers: headerParam,
|
||||||
extra: mapExtra
|
extra: mapExtra
|
||||||
),
|
),
|
||||||
data: File(argsUpload.mobileFileUpload.filePath).openRead(),
|
data: argsUpload.fileInfo.filePath?.isNotEmpty == true
|
||||||
|
? File(argsUpload.fileInfo.filePath!).openRead()
|
||||||
|
: argsUpload.fileInfo.bytes != null
|
||||||
|
? BodyBytesStream.fromBytes(argsUpload.fileInfo.bytes!)
|
||||||
|
: null,
|
||||||
onSendProgress: (count, total) {
|
onSendProgress: (count, total) {
|
||||||
log('FileUploader::_handleUploadAttachmentAction():onSendProgress: FILE[${argsUpload.uploadId.id}] : { PROGRESS = $count | TOTAL = $total}');
|
log('FileUploader::_handleUploadAttachmentAction():onSendProgress: FILE[${argsUpload.uploadId.id}] : { PROGRESS = $count | TOTAL = $total}');
|
||||||
sendPort.send(
|
sendPort.send(
|
||||||
UploadingAttachmentUploadState(
|
UploadingAttachmentUploadState(
|
||||||
argsUpload.uploadId,
|
argsUpload.uploadId,
|
||||||
count,
|
count,
|
||||||
argsUpload.mobileFileUpload.fileSize
|
argsUpload.fileInfo.fileSize
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
log('FileUploader::_handleUploadAttachmentAction(): RESULT_JSON = $resultJson');
|
log('FileUploader::_handleUploadAttachmentAction(): RESULT_JSON = $resultJson');
|
||||||
if (argsUpload.mobileFileUpload.mimeType == FileUtils.TEXT_PLAIN_MIME_TYPE) {
|
if (argsUpload.fileInfo.mimeType == FileUtils.TEXT_PLAIN_MIME_TYPE) {
|
||||||
final fileCharset = await argsUpload.fileUtils.getCharsetFromBytes(
|
final fileBytes = argsUpload.fileInfo.filePath?.isNotEmpty == true
|
||||||
File(argsUpload.mobileFileUpload.filePath).readAsBytesSync()
|
? File(argsUpload.fileInfo.filePath!).readAsBytesSync()
|
||||||
);
|
: argsUpload.fileInfo.bytes;
|
||||||
|
|
||||||
|
final fileCharset = fileBytes != null
|
||||||
|
? await argsUpload.fileUtils.getCharsetFromBytes(fileBytes)
|
||||||
|
: null;
|
||||||
return _parsingResponse(
|
return _parsingResponse(
|
||||||
resultJson: resultJson,
|
resultJson: resultJson,
|
||||||
fileName: argsUpload.mobileFileUpload.fileName,
|
fileName: argsUpload.fileInfo.fileName,
|
||||||
fileCharset: fileCharset.toLowerCase());
|
fileCharset: fileCharset?.toLowerCase());
|
||||||
} else {
|
} else {
|
||||||
return _parsingResponse(
|
return _parsingResponse(
|
||||||
resultJson: resultJson,
|
resultJson: resultJson,
|
||||||
fileName: argsUpload.mobileFileUpload.fileName);
|
fileName: argsUpload.fileInfo.fileName);
|
||||||
}
|
}
|
||||||
} on DioError catch (exception) {
|
} on DioError catch (exception) {
|
||||||
logError('FileUploader::_handleUploadAttachmentAction():DioError: $exception');
|
logError('FileUploader::_handleUploadAttachmentAction():DioError: $exception');
|
||||||
@@ -149,10 +161,12 @@ class FileUploader {
|
|||||||
|
|
||||||
Future<Attachment> _handleUploadAttachmentActionOnWeb(
|
Future<Attachment> _handleUploadAttachmentActionOnWeb(
|
||||||
UploadTaskId uploadId,
|
UploadTaskId uploadId,
|
||||||
StreamController<Either<Failure, Success>> onSendController,
|
|
||||||
FileInfo fileInfo,
|
FileInfo fileInfo,
|
||||||
Uri uploadUri,
|
Uri uploadUri,
|
||||||
{CancelToken? cancelToken}
|
{
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
StreamController<Either<Failure, Success>>? onSendController,
|
||||||
|
}
|
||||||
) async {
|
) async {
|
||||||
final headerParam = _dioClient.getHeaders();
|
final headerParam = _dioClient.getHeaders();
|
||||||
headerParam[HttpHeaders.contentTypeHeader] = fileInfo.mimeType;
|
headerParam[HttpHeaders.contentTypeHeader] = fileInfo.mimeType;
|
||||||
@@ -160,7 +174,8 @@ class FileUploader {
|
|||||||
|
|
||||||
final mapExtra = <String, dynamic>{
|
final mapExtra = <String, dynamic>{
|
||||||
uploadAttachmentExtraKey: {
|
uploadAttachmentExtraKey: {
|
||||||
streamDataExtraKey: BodyBytesStream.fromBytes(fileInfo.bytes!),
|
if (fileInfo.bytes?.isNotEmpty == true)
|
||||||
|
streamDataExtraKey: BodyBytesStream.fromBytes(fileInfo.bytes!),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -174,7 +189,7 @@ class FileUploader {
|
|||||||
cancelToken: cancelToken,
|
cancelToken: cancelToken,
|
||||||
onSendProgress: (count, total) {
|
onSendProgress: (count, total) {
|
||||||
log('FileUploader::_handleUploadAttachmentActionOnWeb():onSendProgress: FILE[${uploadId.id}] : { PROGRESS = $count | TOTAL = $total}');
|
log('FileUploader::_handleUploadAttachmentActionOnWeb():onSendProgress: FILE[${uploadId.id}] : { PROGRESS = $count | TOTAL = $total}');
|
||||||
onSendController.add(
|
onSendController?.add(
|
||||||
Right(UploadingAttachmentUploadState(
|
Right(UploadingAttachmentUploadState(
|
||||||
uploadId,
|
uploadId,
|
||||||
count,
|
count,
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
|
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:model/upload/file_info.dart';
|
import 'package:model/upload/file_info.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/domain/model/mobile_file_upload.dart';
|
|
||||||
|
|
||||||
extension FileInfoExtension on FileInfo {
|
extension FileInfoExtension on FileInfo {
|
||||||
MobileFileUpload toMobileFileUpload() => MobileFileUpload(fileName, filePath!, fileSize, mimeType);
|
|
||||||
|
|
||||||
FileInfo withInline() => FileInfo(
|
FileInfo withInline() => FileInfo(
|
||||||
fileName: fileName,
|
fileName: fileName,
|
||||||
fileSize: fileSize,
|
fileSize: fileSize,
|
||||||
filePath: filePath,
|
filePath: filePath,
|
||||||
bytes: bytes,
|
bytes: bytes,
|
||||||
readStream: readStream,
|
|
||||||
type: type,
|
type: type,
|
||||||
isInline: true,
|
isInline: true,
|
||||||
isShared: isShared);
|
isShared: isShared);
|
||||||
@@ -21,6 +17,5 @@ extension FileInfoExtension on FileInfo {
|
|||||||
path: filePath,
|
path: filePath,
|
||||||
size: fileSize,
|
size: fileSize,
|
||||||
bytes: bytes,
|
bytes: bytes,
|
||||||
readStream: readStream,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,5 @@ extension PlatformFileExtension on PlatformFile {
|
|||||||
fileSize: size,
|
fileSize: size,
|
||||||
filePath: PlatformInfo.isWeb ? '' : path ?? '',
|
filePath: PlatformInfo.isWeb ? '' : path ?? '',
|
||||||
bytes: bytes,
|
bytes: bytes,
|
||||||
readStream: readStream
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
|
|
||||||
import 'package:equatable/equatable.dart';
|
|
||||||
|
|
||||||
class MobileFileUpload with EquatableMixin {
|
|
||||||
final String fileName;
|
|
||||||
final String filePath;
|
|
||||||
final int fileSize;
|
|
||||||
final String mimeType;
|
|
||||||
|
|
||||||
MobileFileUpload(
|
|
||||||
this.fileName,
|
|
||||||
this.filePath,
|
|
||||||
this.fileSize,
|
|
||||||
this.mimeType
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [
|
|
||||||
fileName,
|
|
||||||
filePath,
|
|
||||||
fileSize,
|
|
||||||
mimeType,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
@@ -42,10 +42,10 @@ class UploadAttachment with EquatableMixin {
|
|||||||
|
|
||||||
final attachment = await fileUploader.uploadAttachment(
|
final attachment = await fileUploader.uploadAttachment(
|
||||||
uploadTaskId,
|
uploadTaskId,
|
||||||
_progressStateController,
|
|
||||||
fileInfo,
|
fileInfo,
|
||||||
uploadUri,
|
uploadUri,
|
||||||
cancelToken: cancelToken
|
cancelToken: cancelToken,
|
||||||
|
onSendController: _progressStateController,
|
||||||
);
|
);
|
||||||
|
|
||||||
log('UploadAttachment::upload: ATTACHMENT_UPLOADED = $attachment');
|
log('UploadAttachment::upload: ATTACHMENT_UPLOADED = $attachment');
|
||||||
|
|||||||
@@ -193,24 +193,24 @@ class UploadController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void initializeUploadAttachments(List<Attachment> attachments) {
|
void initializeUploadAttachments(List<Attachment> attachments) {
|
||||||
final listUploadFilesState = attachments
|
_uploadingStateFiles.addAll(
|
||||||
.map((attachment) => UploadFileState(
|
attachments.map((attachment) => UploadFileState(
|
||||||
UploadTaskId(attachment.blobId!.value),
|
UploadTaskId(attachment.blobId!.value),
|
||||||
uploadStatus: UploadFileStatus.succeed,
|
uploadStatus: UploadFileStatus.succeed,
|
||||||
attachment: attachment))
|
attachment: attachment
|
||||||
.toList();
|
))
|
||||||
_uploadingStateFiles.addAll(listUploadFilesState);
|
);
|
||||||
_refreshListUploadAttachmentState();
|
_refreshListUploadAttachmentState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void initializeUploadInlineAttachments(List<Attachment> inlineAttachments) {
|
void initializeUploadInlineAttachments(List<Attachment> inlineAttachments) {
|
||||||
final listUploadInlineImagesState = inlineAttachments
|
_uploadingStateInlineFiles.addAll(
|
||||||
.map((inlineAttachment) => UploadFileState(
|
inlineAttachments.map((inlineAttachment) => UploadFileState(
|
||||||
UploadTaskId(inlineAttachment.blobId!.value),
|
UploadTaskId(inlineAttachment.blobId!.value),
|
||||||
uploadStatus: UploadFileStatus.succeed,
|
uploadStatus: UploadFileStatus.succeed,
|
||||||
attachment: inlineAttachment))
|
attachment: inlineAttachment,
|
||||||
.toList();
|
))
|
||||||
_uploadingStateInlineFiles.addAll(listUploadInlineImagesState);
|
);
|
||||||
_refreshListUploadAttachmentState();
|
_refreshListUploadAttachmentState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,45 +428,44 @@ class UploadController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<Attachment> get inlineAttachmentsUploaded {
|
List<Attachment> get inlineAttachmentsUploaded {
|
||||||
if (_uploadingStateInlineFiles.uploadingStateFiles.isEmpty) {
|
return _uploadingStateInlineFiles.uploadingStateFiles.fold<List<Attachment>>(
|
||||||
return List.empty();
|
[],
|
||||||
}
|
(list, fileState) {
|
||||||
return _uploadingStateInlineFiles.uploadingStateFiles
|
final attachment = fileState?.attachment;
|
||||||
.whereNotNull()
|
if (attachment?.cid != null) {
|
||||||
.map((fileState) => fileState.attachment)
|
list.add(attachment!);
|
||||||
.whereNotNull()
|
}
|
||||||
.where((attachment) => attachment.cid != null)
|
return list;
|
||||||
.toList();
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<FileInfo> get inlineAttachmentsPicked {
|
List<FileInfo> get inlineAttachmentsPicked {
|
||||||
if (_uploadingStateInlineFiles.uploadingStateFiles.isEmpty) {
|
return _uploadingStateInlineFiles.uploadingStateFiles.fold<List<FileInfo>>(
|
||||||
return List.empty();
|
[],
|
||||||
}
|
(list, fileState) {
|
||||||
return _uploadingStateInlineFiles.uploadingStateFiles
|
final file = fileState?.file;
|
||||||
.whereNotNull()
|
if (file != null) {
|
||||||
.map((fileState) => fileState.file)
|
list.add(file);
|
||||||
.whereNotNull()
|
}
|
||||||
.toList();
|
return list;
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Attachment> get mapInlineAttachments {
|
Map<String, Attachment> get mapInlineAttachments {
|
||||||
final inlineAttachments = _uploadingStateInlineFiles.uploadingStateFiles
|
final mapInlineAttachments = _uploadingStateInlineFiles.uploadingStateFiles.fold<Map<String, Attachment>>(
|
||||||
.whereNotNull()
|
{},
|
||||||
.map((fileState) => fileState.attachment)
|
(map, fileState) {
|
||||||
.whereNotNull()
|
final attachment = fileState?.attachment;
|
||||||
.where((attachment) => attachment.cid != null)
|
if (attachment?.cid != null) {
|
||||||
.toList();
|
map[attachment!.cid!] = attachment;
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
log('UploadController::mapInlineAttachments(): $inlineAttachments');
|
log('UploadController::mapInlineAttachments(): Found ${mapInlineAttachments.length} inline attachments.');
|
||||||
|
|
||||||
if (inlineAttachments.isEmpty) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
final mapInlineAttachments = {
|
|
||||||
for (var item in inlineAttachments) item.cid!: item
|
|
||||||
};
|
|
||||||
|
|
||||||
return mapInlineAttachments;
|
return mapInlineAttachments;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class UploadFileStateList {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
UploadFileStateList addAll(List<UploadFileState> elements) {
|
UploadFileStateList addAll(Iterable<UploadFileState> elements) {
|
||||||
_uploadingStateFiles.addAll(elements);
|
_uploadingStateFiles.addAll(elements);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,10 +37,12 @@ import 'package:tmail_ui_user/features/push_notification/data/network/web_socket
|
|||||||
import 'package:tmail_ui_user/features/quotas/data/network/quotas_api.dart';
|
import 'package:tmail_ui_user/features/quotas/data/network/quotas_api.dart';
|
||||||
import 'package:tmail_ui_user/features/server_settings/data/network/server_settings_api.dart';
|
import 'package:tmail_ui_user/features/server_settings/data/network/server_settings_api.dart';
|
||||||
import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart';
|
import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart';
|
||||||
|
import 'package:tmail_ui_user/features/upload/data/network/file_uploader.dart';
|
||||||
import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart';
|
import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart';
|
||||||
import 'package:tmail_ui_user/main/exceptions/send_email_exception_thrower.dart';
|
import 'package:tmail_ui_user/main/exceptions/send_email_exception_thrower.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/ios_sharing_manager.dart';
|
import 'package:tmail_ui_user/main/utils/ios_sharing_manager.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
import 'package:worker_manager/worker_manager.dart';
|
||||||
|
|
||||||
class NetworkBindings extends Bindings {
|
class NetworkBindings extends Bindings {
|
||||||
|
|
||||||
@@ -84,6 +86,7 @@ class NetworkBindings extends Bindings {
|
|||||||
Get.find<OIDCHttpClient>(),
|
Get.find<OIDCHttpClient>(),
|
||||||
Get.find<MailboxCacheManager>(),
|
Get.find<MailboxCacheManager>(),
|
||||||
));
|
));
|
||||||
|
Get.put(Executor());
|
||||||
}
|
}
|
||||||
|
|
||||||
void _bindingInterceptors() {
|
void _bindingInterceptors() {
|
||||||
@@ -126,6 +129,11 @@ class NetworkBindings extends Bindings {
|
|||||||
Get.put(ServerSettingsAPI(Get.find<HttpClient>()));
|
Get.put(ServerSettingsAPI(Get.find<HttpClient>()));
|
||||||
Get.put(WebSocketApi(Get.find<DioClient>()));
|
Get.put(WebSocketApi(Get.find<DioClient>()));
|
||||||
Get.put(LinagoraEcosystemApi(Get.find<DioClient>()));
|
Get.put(LinagoraEcosystemApi(Get.find<DioClient>()));
|
||||||
|
Get.put(FileUploader(
|
||||||
|
Get.find<DioClient>(),
|
||||||
|
Get.find<Executor>(),
|
||||||
|
Get.find<FileUtils>(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _bindingConnection() {
|
void _bindingConnection() {
|
||||||
@@ -140,7 +148,11 @@ class NetworkBindings extends Bindings {
|
|||||||
void _bindingTransformer() {
|
void _bindingTransformer() {
|
||||||
Get.put(const HtmlEscape());
|
Get.put(const HtmlEscape());
|
||||||
Get.put(HtmlTransform(Get.find<DioClient>(), Get.find<HtmlEscape>()));
|
Get.put(HtmlTransform(Get.find<DioClient>(), Get.find<HtmlEscape>()));
|
||||||
Get.put(HtmlAnalyzer(Get.find<HtmlTransform>()));
|
Get.put(HtmlAnalyzer(
|
||||||
|
Get.find<HtmlTransform>(),
|
||||||
|
Get.find<FileUploader>(),
|
||||||
|
Get.find<Uuid>(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _bindingServices() {
|
void _bindingServices() {
|
||||||
|
|||||||
@@ -87,7 +87,6 @@ class NetworkIsolateBindings extends Bindings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _bindingIsolateWorker() {
|
void _bindingIsolateWorker() {
|
||||||
Get.put(Executor());
|
|
||||||
Get.put(ThreadIsolateWorker(
|
Get.put(ThreadIsolateWorker(
|
||||||
Get.find<ThreadAPI>(tag: BindingTag.isolateTag),
|
Get.find<ThreadAPI>(tag: BindingTag.isolateTag),
|
||||||
Get.find<EmailAPI>(tag: BindingTag.isolateTag),
|
Get.find<EmailAPI>(tag: BindingTag.isolateTag),
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ class FileInfo with EquatableMixin {
|
|||||||
final int fileSize;
|
final int fileSize;
|
||||||
final String? filePath;
|
final String? filePath;
|
||||||
final Uint8List? bytes;
|
final Uint8List? bytes;
|
||||||
final Stream<List<int>>? readStream;
|
|
||||||
final String? type;
|
final String? type;
|
||||||
final bool? isInline;
|
final bool? isInline;
|
||||||
final bool? isShared;
|
final bool? isShared;
|
||||||
@@ -17,7 +16,6 @@ class FileInfo with EquatableMixin {
|
|||||||
required this.fileSize,
|
required this.fileSize,
|
||||||
this.filePath,
|
this.filePath,
|
||||||
this.bytes,
|
this.bytes,
|
||||||
this.readStream,
|
|
||||||
this.type,
|
this.type,
|
||||||
this.isInline,
|
this.isInline,
|
||||||
this.isShared,
|
this.isShared,
|
||||||
@@ -32,7 +30,7 @@ class FileInfo with EquatableMixin {
|
|||||||
}) {
|
}) {
|
||||||
return FileInfo(
|
return FileInfo(
|
||||||
fileName: name ?? '',
|
fileName: name ?? '',
|
||||||
fileSize: size ?? 0,
|
fileSize: size ?? bytes.length,
|
||||||
bytes: bytes,
|
bytes: bytes,
|
||||||
type: type,
|
type: type,
|
||||||
isInline: isInline,
|
isInline: isInline,
|
||||||
@@ -59,7 +57,6 @@ class FileInfo with EquatableMixin {
|
|||||||
filePath,
|
filePath,
|
||||||
fileSize,
|
fileSize,
|
||||||
bytes,
|
bytes,
|
||||||
readStream,
|
|
||||||
type,
|
type,
|
||||||
isInline,
|
isInline,
|
||||||
isShared,
|
isShared,
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller
|
|||||||
import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart';
|
import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/upload/data/network/file_uploader.dart';
|
||||||
import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart';
|
import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart';
|
||||||
import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart';
|
import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/toast_manager.dart';
|
import 'package:tmail_ui_user/main/utils/toast_manager.dart';
|
||||||
@@ -114,6 +115,7 @@ const fallbackGenerators = {
|
|||||||
MockSpec<TwakeAppManager>(),
|
MockSpec<TwakeAppManager>(),
|
||||||
MockSpec<DownloadAllAttachmentsForWebInteractor>(),
|
MockSpec<DownloadAllAttachmentsForWebInteractor>(),
|
||||||
MockSpec<ExportAllAttachmentsInteractor>(),
|
MockSpec<ExportAllAttachmentsInteractor>(),
|
||||||
|
MockSpec<FileUploader>(),
|
||||||
])
|
])
|
||||||
void main() {
|
void main() {
|
||||||
TestWidgetsFlutterBinding.ensureInitialized();
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
@@ -405,10 +407,14 @@ void main() {
|
|||||||
final calendarEventRepository = CalendarEventRepositoryImpl(
|
final calendarEventRepository = CalendarEventRepositoryImpl(
|
||||||
{DataSourceType.network :calendarEventDataSource},
|
{DataSourceType.network :calendarEventDataSource},
|
||||||
HtmlDataSourceImpl(
|
HtmlDataSourceImpl(
|
||||||
HtmlAnalyzer(HtmlTransform(
|
HtmlAnalyzer(
|
||||||
MockDioClient(),
|
HtmlTransform(
|
||||||
const HtmlEscape(),
|
MockDioClient(),
|
||||||
)),
|
const HtmlEscape(),
|
||||||
|
),
|
||||||
|
MockFileUploader(),
|
||||||
|
MockUuid(),
|
||||||
|
),
|
||||||
CacheExceptionThrower(),
|
CacheExceptionThrower(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import 'package:tmail_ui_user/features/manage_account/presentation/profiles/iden
|
|||||||
import 'package:tmail_ui_user/features/public_asset/domain/model/public_assets_in_identity_arguments.dart';
|
import 'package:tmail_ui_user/features/public_asset/domain/model/public_assets_in_identity_arguments.dart';
|
||||||
import 'package:tmail_ui_user/features/public_asset/domain/usecase/create_public_asset_interactor.dart';
|
import 'package:tmail_ui_user/features/public_asset/domain/usecase/create_public_asset_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/public_asset/domain/usecase/delete_public_assets_interactor.dart';
|
import 'package:tmail_ui_user/features/public_asset/domain/usecase/delete_public_assets_interactor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/upload/data/network/file_uploader.dart';
|
||||||
import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart';
|
import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart';
|
||||||
import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart';
|
import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart';
|
||||||
import 'package:tmail_ui_user/main/universal_import/html_stub.dart';
|
import 'package:tmail_ui_user/main/universal_import/html_stub.dart';
|
||||||
@@ -78,6 +79,7 @@ import 'identity_creator_controller_test.mocks.dart';
|
|||||||
MockSpec<DioClient>(),
|
MockSpec<DioClient>(),
|
||||||
MockSpec<Executor>(),
|
MockSpec<Executor>(),
|
||||||
MockSpec<FileUtils>(),
|
MockSpec<FileUtils>(),
|
||||||
|
MockSpec<FileUploader>(),
|
||||||
MockSpec<RemoteExceptionThrower>(),
|
MockSpec<RemoteExceptionThrower>(),
|
||||||
MockSpec<DownloadClient>(),
|
MockSpec<DownloadClient>(),
|
||||||
MockSpec<HtmlAnalyzer>(),
|
MockSpec<HtmlAnalyzer>(),
|
||||||
@@ -163,6 +165,7 @@ void main() {
|
|||||||
Get.put<DioClient>(MockDioClient(), tag: BindingTag.isolateTag);
|
Get.put<DioClient>(MockDioClient(), tag: BindingTag.isolateTag);
|
||||||
Get.put<Executor>(MockExecutor());
|
Get.put<Executor>(MockExecutor());
|
||||||
Get.put<FileUtils>(MockFileUtils());
|
Get.put<FileUtils>(MockFileUtils());
|
||||||
|
Get.put<FileUploader>(MockFileUploader());
|
||||||
Get.put<RemoteExceptionThrower>(MockRemoteExceptionThrower());
|
Get.put<RemoteExceptionThrower>(MockRemoteExceptionThrower());
|
||||||
Get.put<DownloadClient>(MockDownloadClient());
|
Get.put<DownloadClient>(MockDownloadClient());
|
||||||
Get.put<HtmlAnalyzer>(MockHtmlAnalyzer());
|
Get.put<HtmlAnalyzer>(MockHtmlAnalyzer());
|
||||||
|
|||||||
Reference in New Issue
Block a user