TF-1323 Fix can not attach image on mobile and web
This commit is contained in:
@@ -16,7 +16,7 @@ class ComposerRepositoryImpl extends ComposerRepository {
|
|||||||
this._composerDataSource);
|
this._composerDataSource);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
UploadAttachment uploadAttachment(FileInfo fileInfo, Uri uploadUri, {CancelToken? cancelToken}) {
|
Future<UploadAttachment> uploadAttachment(FileInfo fileInfo, Uri uploadUri, {CancelToken? cancelToken}) {
|
||||||
return _attachmentUploadDataSource.uploadAttachment(fileInfo, uploadUri, cancelToken: cancelToken);
|
return _attachmentUploadDataSource.uploadAttachment(fileInfo, uploadUri, cancelToken: cancelToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:model/model.dart';
|
import 'package:model/upload/file_info.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/domain/model/upload_attachment.dart';
|
import 'package:tmail_ui_user/features/upload/domain/model/upload_attachment.dart';
|
||||||
|
|
||||||
abstract class ComposerRepository {
|
abstract class ComposerRepository {
|
||||||
UploadAttachment uploadAttachment(FileInfo fileInfo, Uri uploadUri, {CancelToken? cancelToken});
|
Future<UploadAttachment> uploadAttachment(FileInfo fileInfo, Uri uploadUri, {CancelToken? cancelToken});
|
||||||
|
|
||||||
Future<String?> downloadImageAsBase64(String url, String cid, FileInfo fileInfo, {double? maxWidth, bool? compress});
|
Future<String?> downloadImageAsBase64(String url, String cid, FileInfo fileInfo, {double? maxWidth, bool? compress});
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
import 'package:core/presentation/state/failure.dart';
|
||||||
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
import 'package:tmail_ui_user/features/upload/domain/model/upload_attachment.dart';
|
||||||
|
|
||||||
|
class UploadAttachmentSuccess extends UIState {
|
||||||
|
|
||||||
|
final UploadAttachment uploadAttachment;
|
||||||
|
final bool isInline;
|
||||||
|
|
||||||
|
UploadAttachmentSuccess(this.uploadAttachment, {this.isInline = false});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [uploadAttachment, isInline];
|
||||||
|
}
|
||||||
|
|
||||||
|
class UploadAttachmentFailure extends FeatureFailure {
|
||||||
|
final dynamic exception;
|
||||||
|
final bool isInline;
|
||||||
|
|
||||||
|
UploadAttachmentFailure(this.exception, {this.isInline = false});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [exception, isInline];
|
||||||
|
}
|
||||||
@@ -1,14 +1,31 @@
|
|||||||
|
import 'package:core/presentation/state/failure.dart';
|
||||||
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:model/model.dart';
|
import 'package:model/upload/file_info.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/domain/repository/composer_repository.dart';
|
import 'package:tmail_ui_user/features/composer/domain/repository/composer_repository.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/domain/model/upload_attachment.dart';
|
import 'package:tmail_ui_user/features/composer/domain/state/upload_attachment_state.dart';
|
||||||
|
|
||||||
class UploadAttachmentInteractor {
|
class UploadAttachmentInteractor {
|
||||||
final ComposerRepository _composerRepository;
|
final ComposerRepository _composerRepository;
|
||||||
|
|
||||||
UploadAttachmentInteractor(this._composerRepository);
|
UploadAttachmentInteractor(this._composerRepository);
|
||||||
|
|
||||||
UploadAttachment execute(FileInfo fileInfo, Uri uploadUri, {CancelToken? cancelToken}) {
|
Stream<Either<Failure, Success>> execute(
|
||||||
return _composerRepository.uploadAttachment(fileInfo, uploadUri, cancelToken: cancelToken);
|
FileInfo fileInfo,
|
||||||
|
Uri uploadUri, {
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
bool isInline = false
|
||||||
|
}) async* {
|
||||||
|
try {
|
||||||
|
final uploadAttachment = await _composerRepository.uploadAttachment(
|
||||||
|
fileInfo,
|
||||||
|
uploadUri,
|
||||||
|
cancelToken: cancelToken
|
||||||
|
);
|
||||||
|
yield Right<Failure, Success>(UploadAttachmentSuccess(uploadAttachment, isInline: isInline));
|
||||||
|
} catch (e) {
|
||||||
|
yield Left<Failure, Success>(UploadAttachmentFailure(e, isInline: isInline));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,6 +51,7 @@ import 'package:tmail_ui_user/features/upload/presentation/controller/upload_con
|
|||||||
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/exceptions/remote_exception_thrower.dart';
|
import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
import 'package:worker_manager/worker_manager.dart';
|
import 'package:worker_manager/worker_manager.dart';
|
||||||
|
|
||||||
class ComposerBindings extends BaseBindings {
|
class ComposerBindings extends BaseBindings {
|
||||||
@@ -67,7 +68,11 @@ class ComposerBindings extends BaseBindings {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void bindingsDataSourceImpl() {
|
void bindingsDataSourceImpl() {
|
||||||
Get.lazyPut(() => AttachmentUploadDataSourceImpl(Get.find<FileUploader>()));
|
Get.lazyPut(() => AttachmentUploadDataSourceImpl(
|
||||||
|
Get.find<FileUploader>(),
|
||||||
|
Get.find<Uuid>(),
|
||||||
|
Get.find<RemoteExceptionThrower>()
|
||||||
|
));
|
||||||
Get.lazyPut(() => ComposerDataSourceImpl(Get.find<DownloadClient>(), Get.find<RemoteExceptionThrower>()));
|
Get.lazyPut(() => ComposerDataSourceImpl(Get.find<DownloadClient>(), Get.find<RemoteExceptionThrower>()));
|
||||||
Get.lazyPut(() => ContactDataSourceImpl(Get.find<CacheExceptionThrower>()));
|
Get.lazyPut(() => ContactDataSourceImpl(Get.find<CacheExceptionThrower>()));
|
||||||
Get.lazyPut(() => MailboxDataSourceImpl(
|
Get.lazyPut(() => MailboxDataSourceImpl(
|
||||||
|
|||||||
@@ -1445,7 +1445,7 @@ class ComposerController extends BaseController {
|
|||||||
final accountId = mailboxDashBoardController.accountId.value;
|
final accountId = mailboxDashBoardController.accountId.value;
|
||||||
if (session != null && accountId != null) {
|
if (session != null && accountId != null) {
|
||||||
final uploadUri = session.getUploadUri(accountId);
|
final uploadUri = session.getUploadUri(accountId);
|
||||||
uploadController.uploadInlineImage(pickedFile, uploadUri);
|
uploadController.uploadFileAction(pickedFile, uploadUri, isInline: true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (currentContext != null) {
|
if (currentContext != null) {
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ import 'package:model/upload/file_info.dart';
|
|||||||
import 'package:tmail_ui_user/features/upload/domain/model/upload_attachment.dart';
|
import 'package:tmail_ui_user/features/upload/domain/model/upload_attachment.dart';
|
||||||
|
|
||||||
abstract class AttachmentUploadDataSource {
|
abstract class AttachmentUploadDataSource {
|
||||||
UploadAttachment uploadAttachment(FileInfo fileInfo, Uri uploadUri, {CancelToken? cancelToken});
|
Future<UploadAttachment> uploadAttachment(FileInfo fileInfo, Uri uploadUri, {CancelToken? cancelToken});
|
||||||
}
|
}
|
||||||
@@ -5,29 +5,29 @@ import 'package:tmail_ui_user/features/upload/data/datasource/attachment_upload_
|
|||||||
import 'package:tmail_ui_user/features/upload/data/network/file_uploader.dart';
|
import 'package:tmail_ui_user/features/upload/data/network/file_uploader.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/domain/model/upload_attachment.dart';
|
import 'package:tmail_ui_user/features/upload/domain/model/upload_attachment.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/main/exceptions/exception_thrower.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class AttachmentUploadDataSourceImpl extends AttachmentUploadDataSource {
|
class AttachmentUploadDataSourceImpl extends AttachmentUploadDataSource {
|
||||||
|
|
||||||
final FileUploader _fileUploader;
|
final FileUploader _fileUploader;
|
||||||
|
final Uuid _uuid;
|
||||||
|
final ExceptionThrower _exceptionThrower;
|
||||||
|
|
||||||
AttachmentUploadDataSourceImpl(this._fileUploader);
|
AttachmentUploadDataSourceImpl(this._fileUploader, this._uuid, this._exceptionThrower);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
UploadAttachment uploadAttachment(FileInfo fileInfo, Uri uploadUri, {CancelToken? cancelToken}) {
|
Future<UploadAttachment> uploadAttachment(FileInfo fileInfo, Uri uploadUri, {CancelToken? cancelToken}) {
|
||||||
final attachmentUploadId = _generateAttachmentUploadId();
|
return Future.sync(() {
|
||||||
final uploadAttachment = UploadAttachment(
|
return UploadAttachment(
|
||||||
attachmentUploadId,
|
UploadTaskId(_uuid.v4()),
|
||||||
fileInfo,
|
fileInfo,
|
||||||
uploadUri,
|
uploadUri,
|
||||||
_fileUploader,
|
_fileUploader,
|
||||||
cancelToken: cancelToken)
|
cancelToken: cancelToken
|
||||||
..upload();
|
)..upload();
|
||||||
|
}).catchError((error) {
|
||||||
return uploadAttachment;
|
_exceptionThrower.throwException(error);
|
||||||
}
|
});
|
||||||
|
|
||||||
UploadTaskId _generateAttachmentUploadId() {
|
|
||||||
return UploadTaskId(const Uuid().v4());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,13 +3,18 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:core/core.dart';
|
import 'package:core/data/network/dio_client.dart';
|
||||||
|
import 'package:core/presentation/state/failure.dart';
|
||||||
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
import 'package:core/utils/app_logger.dart';
|
||||||
|
import 'package:core/utils/build_utils.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:model/email/attachment.dart';
|
import 'package:model/email/attachment.dart';
|
||||||
import 'package:model/upload/file_info.dart';
|
import 'package:model/upload/file_info.dart';
|
||||||
import 'package:model/upload/upload_response.dart';
|
import 'package:model/upload/upload_response.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/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:worker_manager/worker_manager.dart' as worker;
|
import 'package:worker_manager/worker_manager.dart' as worker;
|
||||||
@@ -36,21 +41,24 @@ class FileUploader {
|
|||||||
uploadUri,
|
uploadUri,
|
||||||
cancelToken: cancelToken);
|
cancelToken: cancelToken);
|
||||||
} else {
|
} else {
|
||||||
final attachmentUploaded = await _isolateExecutor.execute(
|
return await _isolateExecutor.execute(
|
||||||
arg1: UploadFileArguments(
|
arg1: UploadFileArguments(
|
||||||
_dioClient,
|
_dioClient,
|
||||||
uploadId,
|
uploadId,
|
||||||
fileInfo,
|
fileInfo,
|
||||||
uploadUri,
|
uploadUri,
|
||||||
cancelToken: cancelToken),
|
cancelToken: cancelToken
|
||||||
fun1: _handleUploadAttachmentAction,
|
),
|
||||||
notification: (value) {
|
fun1: _handleUploadAttachmentAction,
|
||||||
if (value is Success) {
|
notification: (value) {
|
||||||
log('FileUploader::uploadAttachment(): onUpdateProgress: $value');
|
if (value is Success) {
|
||||||
onSendController.add(Right(value));
|
log('FileUploader::uploadAttachment(): onUpdateProgress: $value');
|
||||||
}
|
onSendController.add(Right(value));
|
||||||
});
|
}
|
||||||
return attachmentUploaded;
|
}
|
||||||
|
)
|
||||||
|
.then((value) => value)
|
||||||
|
.catchError((error) => throw error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,80 +66,107 @@ class FileUploader {
|
|||||||
UploadFileArguments argsUpload,
|
UploadFileArguments argsUpload,
|
||||||
worker.TypeSendPort sendPort
|
worker.TypeSendPort sendPort
|
||||||
) async {
|
) async {
|
||||||
try {
|
final dioClient = argsUpload.dioClient;
|
||||||
final dioClient = argsUpload.dioClient;
|
final fileInfo = argsUpload.fileInfo;
|
||||||
final fileInfo = argsUpload.fileInfo;
|
final uploadUri = argsUpload.uploadUri;
|
||||||
final uploadUri = argsUpload.uploadUri;
|
final cancelToken = argsUpload.cancelToken;
|
||||||
final cancelToken = argsUpload.cancelToken;
|
|
||||||
|
|
||||||
final headerParam = dioClient.getHeaders();
|
final resultJson = await _invokeRequestToServer(
|
||||||
headerParam[HttpHeaders.contentTypeHeader] = fileInfo.mimeType;
|
dioClient,
|
||||||
headerParam[HttpHeaders.contentLengthHeader] = fileInfo.fileSize;
|
uploadUri,
|
||||||
|
fileInfo,
|
||||||
final resultJson = await argsUpload.dioClient.post(
|
cancelToken: cancelToken,
|
||||||
Uri.decodeFull(uploadUri.toString()),
|
onSendProgress: (count, total) {
|
||||||
options: Options(headers: headerParam),
|
log('FileUploader::_handleUploadAttachmentAction():onSendProgress: [${argsUpload.uploadId.id}] = $count');
|
||||||
data: fileInfo.readStream ?? File(fileInfo.filePath).openRead(),
|
sendPort.send(
|
||||||
cancelToken: cancelToken,
|
UploadingAttachmentUploadState(
|
||||||
onSendProgress: (progress, total) {
|
argsUpload.uploadId,
|
||||||
log('FileUploader::_handleUploadAttachmentAction():onSendProgress: [${argsUpload.uploadId.id}] = $progress');
|
count,
|
||||||
sendPort.send(UploadingAttachmentUploadState(
|
fileInfo.fileSize
|
||||||
argsUpload.uploadId,
|
)
|
||||||
progress,
|
);
|
||||||
fileInfo.fileSize));
|
|
||||||
});
|
|
||||||
|
|
||||||
if (cancelToken?.isCancelled == true) {
|
|
||||||
log('FileUploader::_handleUploadAttachmentAction(): cancelToken');
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
final decodeJson = resultJson is Map ? resultJson : jsonDecode(resultJson);
|
log('FileUploader::_handleUploadAttachmentAction():resultJson: $resultJson');
|
||||||
final uploadResponse = UploadResponse.fromJson(decodeJson);
|
if (cancelToken?.isCancelled == true) {
|
||||||
log('FileUploader::_handleUploadAttachmentAction(): ${uploadResponse.toString()}');
|
log('FileUploader::_handleUploadAttachmentAction(): upload is cancelled');
|
||||||
return uploadResponse.toAttachment(fileInfo.fileName);
|
|
||||||
} catch (e) {
|
|
||||||
log('FileUploader::_handleUploadAttachmentAction(): ERROR: $e');
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return _parsingResponse(resultJson: resultJson, fileName: fileInfo.fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Attachment?> _handleUploadAttachmentActionOnWeb(
|
Future<Attachment?> _handleUploadAttachmentActionOnWeb(
|
||||||
UploadTaskId uploadId,
|
UploadTaskId uploadId,
|
||||||
StreamController<Either<Failure, Success>> onSendController,
|
StreamController<Either<Failure, Success>> onSendController,
|
||||||
FileInfo fileInfo,
|
FileInfo fileInfo,
|
||||||
Uri uploadUri,
|
Uri uploadUri,
|
||||||
{CancelToken? cancelToken}
|
{CancelToken? cancelToken}
|
||||||
) async {
|
) async {
|
||||||
try {
|
final resultJson = await _invokeRequestToServer(
|
||||||
final headerParam = _dioClient.getHeaders();
|
_dioClient,
|
||||||
headerParam[HttpHeaders.contentTypeHeader] = fileInfo.mimeType;
|
uploadUri,
|
||||||
headerParam[HttpHeaders.contentLengthHeader] = fileInfo.fileSize;
|
fileInfo,
|
||||||
|
cancelToken: cancelToken,
|
||||||
final resultJson = await _dioClient.post(
|
onSendProgress: (count, total) {
|
||||||
Uri.decodeFull(uploadUri.toString()),
|
log('FileUploader::_handleUploadAttachmentActionOnWeb():onSendProgress: [${uploadId.id}] = $count');
|
||||||
options: Options(headers: headerParam),
|
onSendController.add(
|
||||||
data: fileInfo.readStream,
|
Right(UploadingAttachmentUploadState(
|
||||||
cancelToken: cancelToken,
|
uploadId,
|
||||||
onSendProgress: (progress, total) {
|
count,
|
||||||
log('FileUploader::_handleUploadAttachmentActionOnWeb():onSendProgress: [$uploadId] = $progress');
|
fileInfo.fileSize
|
||||||
onSendController.add(Right(UploadingAttachmentUploadState(
|
))
|
||||||
uploadId, progress, fileInfo.fileSize)));
|
);
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (cancelToken?.isCancelled == true) {
|
|
||||||
log('FileUploader::_handleUploadAttachmentAction(): cancelToken');
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
log('FileUploader::_handleUploadAttachmentActionOnWeb():resultJson: $resultJson');
|
||||||
|
|
||||||
|
if (cancelToken?.isCancelled == true) {
|
||||||
|
log('FileUploader::_handleUploadAttachmentActionOnWeb(): upload is cancelled');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _parsingResponse(resultJson: resultJson, fileName: fileInfo.fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<dynamic> _invokeRequestToServer(
|
||||||
|
DioClient dioClient,
|
||||||
|
Uri uploadUri,
|
||||||
|
FileInfo fileInfo, {
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
ProgressCallback? onSendProgress
|
||||||
|
}) {
|
||||||
|
final headerParam = dioClient.getHeaders();
|
||||||
|
headerParam[HttpHeaders.contentTypeHeader] = fileInfo.mimeType;
|
||||||
|
headerParam[HttpHeaders.contentLengthHeader] = fileInfo.fileSize;
|
||||||
|
|
||||||
|
final data = fileInfo.readStream ?? File(fileInfo.filePath).openRead();
|
||||||
|
|
||||||
|
if (cancelToken?.isCancelled == true) {
|
||||||
|
log('FileUploader::_invokeRequestToServer(): upload is cancelled');
|
||||||
|
return Future.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
return dioClient.post(
|
||||||
|
Uri.decodeFull(uploadUri.toString()),
|
||||||
|
options: Options(headers: headerParam),
|
||||||
|
data: data,
|
||||||
|
cancelToken: cancelToken,
|
||||||
|
onSendProgress: onSendProgress
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Attachment? _parsingResponse({dynamic resultJson, required String fileName}) {
|
||||||
|
log('FileUploader::_parsingResponse():resultJson: $resultJson');
|
||||||
|
if (resultJson != null) {
|
||||||
final decodeJson = resultJson is Map ? resultJson : jsonDecode(resultJson);
|
final decodeJson = resultJson is Map ? resultJson : jsonDecode(resultJson);
|
||||||
final uploadResponse = UploadResponse.fromJson(decodeJson);
|
final uploadResponse = UploadResponse.fromJson(decodeJson);
|
||||||
log('FileUploader::_handleUploadAttachmentActionOnWeb(): ${uploadResponse.toString()}');
|
log('FileUploader::_parsingResponse():uploadResponse: ${uploadResponse.toString()}');
|
||||||
return uploadResponse.toAttachment(fileInfo.fileName);
|
return uploadResponse.toAttachment(fileName);
|
||||||
} catch (e) {
|
} else {
|
||||||
log('FileUploader::_handleUploadAttachmentActionOnWeb(): $e');
|
logError('FileUploader::_parsingResponse(): DataResponseIsNullException');
|
||||||
return null;
|
throw DataResponseIsNullException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
class DataResponseIsNullException implements Exception {}
|
||||||
@@ -12,7 +12,7 @@ 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:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
|
||||||
class UploadAttachment extends Equatable {
|
class UploadAttachment with EquatableMixin {
|
||||||
|
|
||||||
final UploadTaskId uploadTaskId;
|
final UploadTaskId uploadTaskId;
|
||||||
final FileInfo fileInfo;
|
final FileInfo fileInfo;
|
||||||
@@ -35,31 +35,44 @@ class UploadAttachment extends Equatable {
|
|||||||
_progressStateController.add(flowUploadState);
|
_progressStateController.add(flowUploadState);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> upload() async {
|
void upload() async {
|
||||||
log('UploadFile::upload(): $uploadTaskId');
|
try {
|
||||||
_updateEvent(Right(PendingAttachmentUploadState(uploadTaskId, 0, fileInfo.fileSize)));
|
log('UploadFile::upload(): $uploadTaskId');
|
||||||
|
_updateEvent(Right(PendingAttachmentUploadState(uploadTaskId, 0, fileInfo.fileSize)));
|
||||||
|
|
||||||
final attachment = await fileUploader.uploadAttachment(
|
final attachment = await fileUploader.uploadAttachment(
|
||||||
uploadTaskId,
|
uploadTaskId,
|
||||||
_progressStateController,
|
_progressStateController,
|
||||||
fileInfo,
|
fileInfo,
|
||||||
uploadUri,
|
uploadUri,
|
||||||
cancelToken: cancelToken);
|
cancelToken: cancelToken
|
||||||
|
);
|
||||||
|
|
||||||
if (cancelToken?.isCancelled == true) {
|
if (cancelToken?.isCancelled == true) {
|
||||||
_updateEvent(Left(CancelAttachmentUploadState(uploadTaskId)));
|
_updateEvent(Left(CancelAttachmentUploadState(uploadTaskId)));
|
||||||
await _progressStateController.close();
|
await _progressStateController.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attachment != null) {
|
if (attachment != null) {
|
||||||
_updateEvent(Right(SuccessAttachmentUploadState(uploadTaskId, attachment, fileInfo)));
|
_updateEvent(Right(SuccessAttachmentUploadState(uploadTaskId, attachment, fileInfo)));
|
||||||
} else {
|
} else {
|
||||||
|
_updateEvent(Left(ErrorAttachmentUploadState(uploadTaskId)));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logError('UploadAttachment::upload():ERROR: $e');
|
||||||
_updateEvent(Left(ErrorAttachmentUploadState(uploadTaskId)));
|
_updateEvent(Left(ErrorAttachmentUploadState(uploadTaskId)));
|
||||||
|
} finally {
|
||||||
|
await _progressStateController.close();
|
||||||
}
|
}
|
||||||
await _progressStateController.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [uploadTaskId, fileInfo, uploadUri];
|
List<Object?> get props => [
|
||||||
|
uploadTaskId,
|
||||||
|
fileInfo,
|
||||||
|
uploadUri,
|
||||||
|
fileUploader,
|
||||||
|
cancelToken
|
||||||
|
];
|
||||||
}
|
}
|
||||||
@@ -15,10 +15,13 @@ import 'package:model/email/attachment.dart';
|
|||||||
import 'package:model/extensions/attachment_extension.dart';
|
import 'package:model/extensions/attachment_extension.dart';
|
||||||
import 'package:model/extensions/list_attachment_extension.dart';
|
import 'package:model/extensions/list_attachment_extension.dart';
|
||||||
import 'package:model/upload/file_info.dart';
|
import 'package:model/upload/file_info.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/base_controller.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/domain/state/upload_attachment_state.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/domain/usecases/upload_attachment_interactor.dart';
|
import 'package:tmail_ui_user/features/composer/domain/usecases/upload_attachment_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.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/features/upload/presentation/extensions/upload_attachment_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart';
|
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state_list.dart';
|
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state_list.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_status.dart';
|
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_status.dart';
|
||||||
@@ -26,7 +29,7 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
|||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class UploadController extends GetxController {
|
class UploadController extends BaseController {
|
||||||
|
|
||||||
final _imagePaths = Get.find<ImagePaths>();
|
final _imagePaths = Get.find<ImagePaths>();
|
||||||
final _appToast = Get.find<AppToast>();
|
final _appToast = Get.find<AppToast>();
|
||||||
@@ -125,6 +128,14 @@ class UploadController extends GetxController {
|
|||||||
if (failure is ErrorAttachmentUploadState) {
|
if (failure is ErrorAttachmentUploadState) {
|
||||||
uploadInlineViewState.value = Left(failure);
|
uploadInlineViewState.value = Left(failure);
|
||||||
_deleteInlineFileUploaded(failure.uploadId);
|
_deleteInlineFileUploaded(failure.uploadId);
|
||||||
|
if (currentContext != null && currentOverlayContext != null) {
|
||||||
|
_appToast.showToastWithIcon(currentOverlayContext!,
|
||||||
|
message: AppLocalizations.of(currentContext!).thisImageCannotBeAdded,
|
||||||
|
textColor: AppColor.toastErrorBackgroundColor,
|
||||||
|
iconColor: AppColor.toastErrorBackgroundColor,
|
||||||
|
icon: _imagePaths.icInsertImage
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(success) {
|
(success) {
|
||||||
@@ -185,28 +196,21 @@ class UploadController extends GetxController {
|
|||||||
_refreshListUploadAttachmentState();
|
_refreshListUploadAttachmentState();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> justUploadAttachmentsAction(List<FileInfo> uploadFiles, Uri uploadUri) async {
|
Future<void> justUploadAttachmentsAction(List<FileInfo> uploadFiles, Uri uploadUri) {
|
||||||
return Future.forEach<FileInfo>(uploadFiles, (uploadFile) async {
|
return Future.forEach<FileInfo>(uploadFiles, (uploadFile) async {
|
||||||
await _uploadFile(uploadFile, uploadUri);
|
await uploadFileAction(uploadFile, uploadUri);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _uploadFile(FileInfo uploadFile, Uri uploadUri) async {
|
Future<void> uploadFileAction(FileInfo uploadFile, Uri uploadUri, {bool isInline = false}) {
|
||||||
log('UploadAttachmentManager::_uploadFile(): ${uploadFile.fileName}');
|
log('UploadController::_uploadFile():fileName: ${uploadFile.fileName}');
|
||||||
|
consumeState(_uploadAttachmentInteractor.execute(
|
||||||
final cancelToken = CancelToken();
|
uploadFile,
|
||||||
final uploadAttachment = _uploadAttachmentInteractor.execute(
|
uploadUri,
|
||||||
uploadFile, uploadUri,
|
cancelToken: CancelToken(),
|
||||||
cancelToken: cancelToken);
|
isInline: isInline
|
||||||
|
));
|
||||||
_uploadingStateFiles.add(UploadFileState(
|
return Future.value();
|
||||||
uploadAttachment.uploadTaskId,
|
|
||||||
file: uploadFile,
|
|
||||||
cancelToken: cancelToken));
|
|
||||||
|
|
||||||
await _progressUploadStateStreamGroup.add(uploadAttachment.progressState);
|
|
||||||
|
|
||||||
_refreshListUploadAttachmentState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _refreshListUploadAttachmentState() {
|
void _refreshListUploadAttachmentState() {
|
||||||
@@ -258,19 +262,19 @@ class UploadController extends GetxController {
|
|||||||
bool hasEnoughMaxAttachmentSize({List<FileInfo>? listFiles}) {
|
bool hasEnoughMaxAttachmentSize({List<FileInfo>? listFiles}) {
|
||||||
final currentTotalAttachmentsSize = attachmentsUploaded.totalSize();
|
final currentTotalAttachmentsSize = attachmentsUploaded.totalSize();
|
||||||
final totalInlineAttachmentsSize = inlineAttachmentsUploaded.totalSize();
|
final totalInlineAttachmentsSize = inlineAttachmentsUploaded.totalSize();
|
||||||
log('ComposerController::_validateAttachmentsSize(): $currentTotalAttachmentsSize');
|
log('UploadController::_validateAttachmentsSize(): $currentTotalAttachmentsSize');
|
||||||
log('ComposerController::_validateAttachmentsSize(): totalInlineAttachmentsSize: $totalInlineAttachmentsSize');
|
log('UploadController::_validateAttachmentsSize(): totalInlineAttachmentsSize: $totalInlineAttachmentsSize');
|
||||||
num uploadedTotalSize = 0;
|
num uploadedTotalSize = 0;
|
||||||
if (listFiles != null && listFiles.isNotEmpty) {
|
if (listFiles != null && listFiles.isNotEmpty) {
|
||||||
final uploadedListSize = listFiles.map((file) => file.fileSize).toList();
|
final uploadedListSize = listFiles.map((file) => file.fileSize).toList();
|
||||||
uploadedTotalSize = uploadedListSize.reduce((sum, size) => sum + size);
|
uploadedTotalSize = uploadedListSize.reduce((sum, size) => sum + size);
|
||||||
log('ComposerController::_validateAttachmentsSize(): uploadedTotalSize: $uploadedTotalSize');
|
log('UploadController::_validateAttachmentsSize(): uploadedTotalSize: $uploadedTotalSize');
|
||||||
}
|
}
|
||||||
|
|
||||||
final totalSizeReadyToUpload = currentTotalAttachmentsSize +
|
final totalSizeReadyToUpload = currentTotalAttachmentsSize +
|
||||||
totalInlineAttachmentsSize +
|
totalInlineAttachmentsSize +
|
||||||
uploadedTotalSize;
|
uploadedTotalSize;
|
||||||
log('ComposerController::_validateAttachmentsSize(): totalSizeReadyToUpload: $totalSizeReadyToUpload');
|
log('UploadController::_validateAttachmentsSize(): totalSizeReadyToUpload: $totalSizeReadyToUpload');
|
||||||
|
|
||||||
final maxSizeAttachmentsPerEmail = _mailboxDashBoardController.maxSizeAttachmentsPerEmail?.value;
|
final maxSizeAttachmentsPerEmail = _mailboxDashBoardController.maxSizeAttachmentsPerEmail?.value;
|
||||||
if (maxSizeAttachmentsPerEmail != null) {
|
if (maxSizeAttachmentsPerEmail != null) {
|
||||||
@@ -285,23 +289,6 @@ class UploadController extends GetxController {
|
|||||||
.every((uploadFile) => uploadFile.uploadStatus.completed);
|
.every((uploadFile) => uploadFile.uploadStatus.completed);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> uploadInlineImage(FileInfo uploadFile, Uri uploadUri) async {
|
|
||||||
log('UploadAttachmentManager::uploadInlineImage(): ${uploadFile.fileName}');
|
|
||||||
|
|
||||||
final cancelToken = CancelToken();
|
|
||||||
final uploadAttachment = _uploadAttachmentInteractor.execute(
|
|
||||||
uploadFile,
|
|
||||||
uploadUri,
|
|
||||||
cancelToken: cancelToken);
|
|
||||||
|
|
||||||
_uploadingStateInlineFiles.add(UploadFileState(
|
|
||||||
uploadAttachment.uploadTaskId,
|
|
||||||
file: uploadFile,
|
|
||||||
cancelToken: cancelToken));
|
|
||||||
|
|
||||||
await _progressUploadInlineImageStateStreamGroup.add(uploadAttachment.progressState);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _deleteInlineFileUploaded(UploadTaskId uploadId) {
|
void _deleteInlineFileUploaded(UploadTaskId uploadId) {
|
||||||
_uploadingStateInlineFiles.deleteElementByUploadTaskId(uploadId);
|
_uploadingStateInlineFiles.deleteElementByUploadTaskId(uploadId);
|
||||||
}
|
}
|
||||||
@@ -350,4 +337,48 @@ class UploadController extends GetxController {
|
|||||||
|
|
||||||
return mapInlineAttachments;
|
return mapInlineAttachments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onDone() {
|
||||||
|
viewState.value.fold(_handleFailureViewState, _handleSuccessViewState);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleFailureViewState(Failure failure) async {
|
||||||
|
logError('UploadController::_handleFailureViewState():failure: $failure');
|
||||||
|
if (failure is UploadAttachmentFailure) {
|
||||||
|
if (failure.isInline) {
|
||||||
|
if (currentContext != null && currentOverlayContext != null) {
|
||||||
|
_appToast.showToastWithIcon(currentOverlayContext!,
|
||||||
|
message: AppLocalizations.of(currentContext!).thisImageCannotBeAdded,
|
||||||
|
textColor: AppColor.toastErrorBackgroundColor,
|
||||||
|
iconColor: AppColor.toastErrorBackgroundColor,
|
||||||
|
icon: _imagePaths.icInsertImage
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (currentContext != null && currentOverlayContext != null) {
|
||||||
|
_appToast.showToastWithIcon(currentOverlayContext!,
|
||||||
|
message: AppLocalizations.of(currentContext!).can_not_upload_this_file_as_attachments,
|
||||||
|
textColor: AppColor.toastErrorBackgroundColor,
|
||||||
|
iconColor: AppColor.toastErrorBackgroundColor,
|
||||||
|
icon: _imagePaths.icAttachment
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleSuccessViewState(Success success) async {
|
||||||
|
log('UploadController::_handleSuccessViewState():success: $success');
|
||||||
|
if (success is UploadAttachmentSuccess) {
|
||||||
|
if (success.isInline) {
|
||||||
|
_uploadingStateInlineFiles.add(success.uploadAttachment.toUploadFileState());
|
||||||
|
await _progressUploadInlineImageStateStreamGroup.add(success.uploadAttachment.progressState);
|
||||||
|
} else {
|
||||||
|
_uploadingStateFiles.add(success.uploadAttachment.toUploadFileState());
|
||||||
|
await _progressUploadStateStreamGroup.add(success.uploadAttachment.progressState);
|
||||||
|
_refreshListUploadAttachmentState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
import 'package:tmail_ui_user/features/upload/domain/model/upload_attachment.dart';
|
||||||
|
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart';
|
||||||
|
|
||||||
|
extension UploadAttachmentExtension on UploadAttachment {
|
||||||
|
|
||||||
|
UploadFileState toUploadFileState() {
|
||||||
|
return UploadFileState(
|
||||||
|
uploadTaskId,
|
||||||
|
file: fileInfo,
|
||||||
|
cancelToken: cancelToken
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"@@last_modified": "2023-02-03T11:24:09.273116",
|
"@@last_modified": "2023-02-03T16:28:53.514637",
|
||||||
"initializing_data": "Initializing data...",
|
"initializing_data": "Initializing data...",
|
||||||
"@initializing_data": {
|
"@initializing_data": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
@@ -2679,5 +2679,11 @@
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders_order": [],
|
"placeholders_order": [],
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"thisImageCannotBeAdded": "This image cannot be added.",
|
||||||
|
"@thisImageCannotBeAdded": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2754,7 +2754,7 @@ class AppLocalizations {
|
|||||||
name: 'saveEmailAsDraftFailureWithSetErrorTypeOverQuota',
|
name: 'saveEmailAsDraftFailureWithSetErrorTypeOverQuota',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String get mailBoxes {
|
String get mailBoxes {
|
||||||
return Intl.message(
|
return Intl.message(
|
||||||
'Mailboxes',
|
'Mailboxes',
|
||||||
@@ -2772,4 +2772,11 @@ class AppLocalizations {
|
|||||||
'Hide mailbox',
|
'Hide mailbox',
|
||||||
name: 'hideMailBoxes');
|
name: 'hideMailBoxes');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String get thisImageCannotBeAdded {
|
||||||
|
return Intl.message(
|
||||||
|
'This image cannot be added.',
|
||||||
|
name: 'thisImageCannotBeAdded'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
import 'package:core/core.dart';
|
|
||||||
import 'package:core/data/model/query/query_parameter.dart';
|
import 'package:core/data/model/query/query_parameter.dart';
|
||||||
import 'package:core/data/network/config/service_path.dart';
|
import 'package:core/data/network/config/service_path.dart';
|
||||||
import 'package:core/utils/app_logger.dart';
|
import 'package:core/utils/app_logger.dart';
|
||||||
|
import 'package:core/utils/build_utils.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||||
|
|||||||
Reference in New Issue
Block a user