diff --git a/lib/features/composer/domain/state/upload_attachment_state.dart b/lib/features/composer/domain/state/upload_attachment_state.dart index 27f32c6df..75bb5e92a 100644 --- a/lib/features/composer/domain/state/upload_attachment_state.dart +++ b/lib/features/composer/domain/state/upload_attachment_state.dart @@ -1,4 +1,5 @@ import 'package:core/core.dart'; +import 'package:dartz/dartz.dart'; import 'package:model/model.dart'; class UploadAttachmentLoadingState extends UIState { @@ -22,6 +23,33 @@ class UploadAttachmentFailure extends FeatureFailure { UploadAttachmentFailure(this.exception); + @override + List get props => [exception]; +} + +class UploadAttachmentAllSuccess extends UIState { + final List> listResults; + + UploadAttachmentAllSuccess(this.listResults); + + @override + List get props => [listResults]; +} + +class UploadAttachmentHasSomeFailure extends UIState { + final List> listResults; + + UploadAttachmentHasSomeFailure(this.listResults); + + @override + List get props => [listResults]; +} + +class UploadAttachmentAllFailure extends FeatureFailure { + final exception; + + UploadAttachmentAllFailure(this.exception); + @override List get props => [exception]; } \ No newline at end of file diff --git a/lib/features/composer/domain/usecases/upload_attachment_interactor.dart b/lib/features/composer/domain/usecases/upload_attachment_interactor.dart index 4a2a5341a..60b0851fb 100644 --- a/lib/features/composer/domain/usecases/upload_attachment_interactor.dart +++ b/lib/features/composer/domain/usecases/upload_attachment_interactor.dart @@ -14,7 +14,6 @@ class UploadAttachmentInteractor { Stream> execute(FileInfo fileInfo, AccountId accountId, Uri uploadUrl) async* { try { - yield Right(UploadAttachmentLoadingState()); final result = await Future.wait( [_credentialRepository.getUserName(), _credentialRepository.getPassword()], eagerError: true diff --git a/lib/features/composer/domain/usecases/upload_mutiple_attachment_interactor.dart b/lib/features/composer/domain/usecases/upload_mutiple_attachment_interactor.dart new file mode 100644 index 000000000..26ea9cb90 --- /dev/null +++ b/lib/features/composer/domain/usecases/upload_mutiple_attachment_interactor.dart @@ -0,0 +1,36 @@ +import 'package:core/core.dart'; +import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:model/model.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'; + +class UploadMultipleAttachmentInteractor { + final UploadAttachmentInteractor _uploadAttachmentInteractor; + + UploadMultipleAttachmentInteractor(this._uploadAttachmentInteractor); + + Stream> execute(List listFiles, AccountId accountId, Uri uploadUrl) async* { + try { + yield Right(UploadAttachmentLoadingState()); + final listResult = await Future.wait( + listFiles.map((fileInfo) async { + final result = await _uploadAttachmentInteractor.execute(fileInfo, accountId, uploadUrl).toList(); + return result.first; + }) + ); + if (listResult.length == 1) { + yield listResult.first; + } else { + final listResultSuccess = listResult.where((either) => either.isRight()).toList(); + if (listResultSuccess.length == listResult.length) { + yield Right(UploadAttachmentHasSomeFailure(listResultSuccess)); + } else { + yield Right(UploadAttachmentAllSuccess(listResult)); + } + } + } catch (e) { + yield Left(UploadAttachmentAllFailure(e)); + } + } +} \ No newline at end of file diff --git a/lib/features/composer/presentation/composer_bindings.dart b/lib/features/composer/presentation/composer_bindings.dart index 815795509..95147efbb 100644 --- a/lib/features/composer/presentation/composer_bindings.dart +++ b/lib/features/composer/presentation/composer_bindings.dart @@ -26,6 +26,7 @@ import 'package:tmail_ui_user/features/composer/domain/usecases/upload_attachmen import 'package:tmail_ui_user/features/composer/domain/usecases/save_email_addresses_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/send_email_interactor.dart'; +import 'package:tmail_ui_user/features/composer/domain/usecases/upload_mutiple_attachment_interactor.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart'; import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; import 'package:tmail_ui_user/features/login/data/repository/credential_repository_impl.dart'; @@ -73,6 +74,7 @@ class ComposerBindings extends Bindings { Get.lazyPut(() => UploadAttachmentInteractor( Get.find(), Get.find())); + Get.lazyPut(() => UploadMultipleAttachmentInteractor(Get.find())); Get.lazyPut(() => ComposerController( Get.find(), Get.find(), @@ -85,6 +87,6 @@ class ComposerBindings extends Bindings { Get.find())); Get.find(), Get.find(), - Get.find())); + Get.find())); } } \ No newline at end of file diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index d9b848eb7..a3d8bac8f 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -27,9 +27,9 @@ import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete import 'package:tmail_ui_user/features/composer/domain/state/upload_attachment_state.dart'; import 'package:tmail_ui_user/features/composer/domain/state/search_email_address_state.dart'; import 'package:tmail_ui_user/features/composer/domain/state/send_email_state.dart'; -import 'package:tmail_ui_user/features/composer/domain/usecases/upload_attachment_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/save_email_addresses_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/send_email_interactor.dart'; +import 'package:tmail_ui_user/features/composer/domain/usecases/upload_mutiple_attachment_interactor.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart'; import 'package:tmail_ui_user/features/email/presentation/constants/email_constants.dart'; import 'package:tmail_ui_user/features/email/presentation/extensions/email_content_extension.dart'; @@ -60,7 +60,7 @@ class ComposerController extends BaseController { final HtmlMessagePurifier _htmlMessagePurifier; final HtmlEditorController htmlEditorController; final LocalFilePickerInteractor _localFilePickerInteractor; - final UploadAttachmentInteractor _uploadAttachmentInteractor; + final UploadMultipleAttachmentInteractor _uploadMultipleAttachmentInteractor; List listToEmailAddress = []; List listCcEmailAddress = []; @@ -82,7 +82,7 @@ class ComposerController extends BaseController { this._htmlMessagePurifier, this.htmlEditorController, this._localFilePickerInteractor, - this._uploadAttachmentInteractor, + this._uploadMultipleAttachmentInteractor, ); @override @@ -101,7 +101,8 @@ class ComposerController extends BaseController { _sendEmailFailure(failure); } else if (failure is LocalFilePickerFailure || failure is LocalFilePickerCancel) { _pickFileFailure(failure); - } else if (failure is UploadAttachmentFailure) { + } else if (failure is UploadAttachmentFailure + || failure is UploadAttachmentAllFailure) { _uploadAttachmentsFailure(failure); } }, @@ -110,7 +111,9 @@ class ComposerController extends BaseController { _sendEmailSuccess(success); } else if (success is LocalFilePickerSuccess) { _pickFileSuccess(success); - } else if (success is UploadAttachmentSuccess) { + } else if (success is UploadAttachmentSuccess + || success is UploadAttachmentAllSuccess + || success is UploadAttachmentHasSomeFailure) { _uploadAttachmentsSuccess(success); } }); @@ -366,16 +369,16 @@ class ComposerController extends BaseController { void _pickFileSuccess(Success success) { if (success is LocalFilePickerSuccess) { - _uploadAttachmentsAction(success.fileInfo); + _uploadAttachmentsAction(success.pickedFiles); } } - void _uploadAttachmentsAction(FileInfo fileInfo) async { + void _uploadAttachmentsAction(List pickedFiles) async { if (composerArguments.value != null) { final accountId = composerArguments.value!.session.accounts.keys.first; final uploadUrl = composerArguments.value!.session.getUploadUrl(accountId); - consumeState(_uploadAttachmentInteractor.execute(fileInfo, accountId, uploadUrl)); + consumeState(_uploadMultipleAttachmentInteractor.execute(pickedFiles, accountId, uploadUrl)); } } @@ -388,9 +391,25 @@ class ComposerController extends BaseController { void _uploadAttachmentsSuccess(Success success) { if (success is UploadAttachmentSuccess) { attachments.add(success.attachment); - if (Get.context != null) { - _appToast.showSuccessToast(AppLocalizations.of(Get.context!).attachments_uploaded_successfully); - } + } else if (success is UploadAttachmentAllSuccess) { + final listAttachment = success.listResults + .map((either) => either + .map((result) => (result as UploadAttachmentSuccess).attachment) + .toIterable().first) + .toList(); + + attachments.addAll(listAttachment); + } else if (success is UploadAttachmentHasSomeFailure) { + final listAttachment = success.listResults + .map((either) => either + .map((result) => (result as UploadAttachmentSuccess).attachment) + .toIterable().first) + .toList(); + + attachments.addAll(listAttachment); + } + if (Get.context != null) { + _appToast.showSuccessToast(AppLocalizations.of(Get.context!).attachments_uploaded_successfully); } } diff --git a/lib/features/upload/domain/state/local_file_picker_state.dart b/lib/features/upload/domain/state/local_file_picker_state.dart index 91cd536d1..e4a943b23 100644 --- a/lib/features/upload/domain/state/local_file_picker_state.dart +++ b/lib/features/upload/domain/state/local_file_picker_state.dart @@ -3,12 +3,12 @@ import 'package:core/core.dart'; import 'package:model/model.dart'; class LocalFilePickerSuccess extends UIState { - final FileInfo fileInfo; + final List pickedFiles; - LocalFilePickerSuccess(this.fileInfo); + LocalFilePickerSuccess(this.pickedFiles); @override - List get props => [fileInfo]; + List get props => [pickedFiles]; } class LocalFilePickerFailure extends FeatureFailure { diff --git a/lib/features/upload/domain/usecases/local_file_picker_interactor.dart b/lib/features/upload/domain/usecases/local_file_picker_interactor.dart index 742d682ab..f0088e74f 100644 --- a/lib/features/upload/domain/usecases/local_file_picker_interactor.dart +++ b/lib/features/upload/domain/usecases/local_file_picker_interactor.dart @@ -11,15 +11,14 @@ class LocalFilePickerInteractor { Stream> execute({FileType fileType = FileType.any}) async* { try { - final filesResult = await FilePicker.platform.pickFiles(type: fileType); + final filesResult = await FilePicker.platform.pickFiles(type: fileType, allowMultiple: true); if (filesResult != null && filesResult.files.isNotEmpty) { - final platformFile = filesResult.files.first; - final fileInfoResult = FileInfo( + final fileInfoResults = filesResult.files + .map((platformFile) => FileInfo( platformFile.name, platformFile.path ?? '', - platformFile.size, - ); - yield Right(LocalFilePickerSuccess(fileInfoResult)); + platformFile.size)).toList(); + yield Right(LocalFilePickerSuccess(fileInfoResults)); } else { yield Left(LocalFilePickerCancel()); }