diff --git a/core/lib/data/network/config/authorization_interceptors.dart b/core/lib/data/network/config/authorization_interceptors.dart index c6ea7b05c..4c152a449 100644 --- a/core/lib/data/network/config/authorization_interceptors.dart +++ b/core/lib/data/network/config/authorization_interceptors.dart @@ -1,10 +1,13 @@ import 'dart:convert'; +import 'dart:io'; import 'package:dio/dio.dart'; class AuthorizationInterceptors extends InterceptorsWrapper { String? _authorization; + String getBasicAuth() => _authorization ?? ''; + void changeAuthorization(String? userName, String? password) { _authorization = base64Encode(utf8.encode('$userName:$password')); } @@ -12,7 +15,7 @@ class AuthorizationInterceptors extends InterceptorsWrapper { @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) { if (_authorization != null) { - options.headers['Authorization'] = 'Basic $_authorization'; + options.headers[HttpHeaders.authorizationHeader] = 'Basic $_authorization'; } super.onRequest(options, handler); } diff --git a/core/lib/data/network/download/download_client.dart b/core/lib/data/network/download/download_client.dart index 0a3ea69e9..82a35c496 100644 --- a/core/lib/data/network/download/download_client.dart +++ b/core/lib/data/network/download/download_client.dart @@ -12,11 +12,11 @@ class DownloadClient { Future downloadFile( String url, - String baseAuth, + String basicAuth, CancelToken? cancelToken, ) async { final headerParam = _dioClient.getHeaders(); - headerParam[HttpHeaders.authorizationHeader] = baseAuth; + headerParam[HttpHeaders.authorizationHeader] = basicAuth; headerParam[HttpHeaders.acceptHeader] = DioClient.jmapHeader; final responseBody = await _dioClient.get( diff --git a/core/lib/data/network/download/download_manager.dart b/core/lib/data/network/download/download_manager.dart index 9fa023768..b467db980 100644 --- a/core/lib/data/network/download/download_manager.dart +++ b/core/lib/data/network/download/download_manager.dart @@ -14,14 +14,14 @@ class DownloadManager { String downloadUrl, Future directoryToSave, String filename, - String baseAuth, + String basicAuth, {CancelToken? cancelToken} ) async { final streamController = StreamController(); try { await Future.wait([ - _downloadClient.downloadFile(downloadUrl, baseAuth, cancelToken), + _downloadClient.downloadFile(downloadUrl, basicAuth, cancelToken), directoryToSave ]).then((values) { final fileStream = (values[0] as ResponseBody).stream; diff --git a/lib/features/base/base_controller.dart b/lib/features/base/base_controller.dart index 123b49b7a..deaeffd16 100644 --- a/lib/features/base/base_controller.dart +++ b/lib/features/base/base_controller.dart @@ -25,6 +25,10 @@ abstract class BaseController extends GetxController { ); } + void clearState() { + viewState.value = Right(UIState.idle); + } + void onData(Either newState) { viewState.value = newState; } diff --git a/lib/features/composer/data/network/composer_api.dart b/lib/features/composer/data/network/composer_api.dart index dc5fee9a8..f99685f51 100644 --- a/lib/features/composer/data/network/composer_api.dart +++ b/lib/features/composer/data/network/composer_api.dart @@ -14,14 +14,13 @@ class ComposerAPI { Future uploadAttachment(UploadRequest uploadRequest) async { final resultJson = await _dioClient.post( Uri.decodeFull(uploadRequest.uploadUrl.toString()), - options: Options(headers: _buildHeaderRequestParam(uploadRequest.accountRequest.basicAuth())), + options: Options(headers: _buildHeaderRequestParam()), data: File(uploadRequest.fileInfo.filePath).readAsBytesSync()); return UploadResponse.fromJson(resultJson); } - Map _buildHeaderRequestParam(String basicAuth) { + Map _buildHeaderRequestParam() { final headerParam = _dioClient.getHeaders(); - headerParam[HttpHeaders.authorizationHeader] = basicAuth; headerParam[HttpHeaders.acceptHeader] = DioClient.jmapHeader; return headerParam; } diff --git a/lib/features/composer/domain/state/send_email_state.dart b/lib/features/composer/domain/state/send_email_state.dart index d1b19abed..e517615a2 100644 --- a/lib/features/composer/domain/state/send_email_state.dart +++ b/lib/features/composer/domain/state/send_email_state.dart @@ -1,5 +1,12 @@ import 'package:core/core.dart'; +class SendingEmailState extends UIState { + SendingEmailState(); + + @override + List get props => []; +} + class SendEmailSuccess extends UIState { SendEmailSuccess(); diff --git a/lib/features/composer/domain/state/upload_attachment_state.dart b/lib/features/composer/domain/state/upload_attachment_state.dart index 75bb5e92a..b35d08755 100644 --- a/lib/features/composer/domain/state/upload_attachment_state.dart +++ b/lib/features/composer/domain/state/upload_attachment_state.dart @@ -2,8 +2,8 @@ import 'package:core/core.dart'; import 'package:dartz/dartz.dart'; import 'package:model/model.dart'; -class UploadAttachmentLoadingState extends UIState { - UploadAttachmentLoadingState(); +class UploadingAttachmentState extends UIState { + UploadingAttachmentState(); @override List get props => []; @@ -27,28 +27,37 @@ class UploadAttachmentFailure extends FeatureFailure { List get props => [exception]; } -class UploadAttachmentAllSuccess extends UIState { +class UploadMultipleAttachmentAllSuccess extends UIState { final List> listResults; - UploadAttachmentAllSuccess(this.listResults); + UploadMultipleAttachmentAllSuccess(this.listResults); @override List get props => [listResults]; } -class UploadAttachmentHasSomeFailure extends UIState { +class UploadMultipleAttachmentHasSomeFailure extends UIState { final List> listResults; - UploadAttachmentHasSomeFailure(this.listResults); + UploadMultipleAttachmentHasSomeFailure(this.listResults); @override List get props => [listResults]; } -class UploadAttachmentAllFailure extends FeatureFailure { +class UploadMultipleAttachmentAllFailure extends FeatureFailure { + final List> listResults; + + UploadMultipleAttachmentAllFailure(this.listResults); + + @override + List get props => [listResults]; +} + +class UploadMultipleAttachmentFailure extends FeatureFailure { final exception; - UploadAttachmentAllFailure(this.exception); + UploadMultipleAttachmentFailure(this.exception); @override List get props => [exception]; diff --git a/lib/features/composer/domain/usecases/send_email_interactor.dart b/lib/features/composer/domain/usecases/send_email_interactor.dart index 18074116a..a830615f4 100644 --- a/lib/features/composer/domain/usecases/send_email_interactor.dart +++ b/lib/features/composer/domain/usecases/send_email_interactor.dart @@ -12,15 +12,15 @@ class SendEmailInteractor { Stream> execute(AccountId accountId, EmailRequest emailRequest) async* { try { - yield Right(LoadingState()); + yield Right(SendingEmailState()); final result = await emailRepository.sendEmail(accountId, emailRequest); if (result) { yield Right(SendEmailSuccess()); } else { - yield Left(SendEmailFailure(result)); + yield Left(SendEmailFailure(result)); } } catch (e) { - yield Left(SendEmailFailure(e)); + yield Left(SendEmailFailure(e)); } } } \ 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 60b0851fb..5f521d7ad 100644 --- a/lib/features/composer/domain/usecases/upload_attachment_interactor.dart +++ b/lib/features/composer/domain/usecases/upload_attachment_interactor.dart @@ -4,23 +4,16 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/composer/domain/repository/composer_repository.dart'; import 'package:tmail_ui_user/features/composer/domain/state/upload_attachment_state.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; class UploadAttachmentInteractor { final ComposerRepository _composerRepository; - final CredentialRepository _credentialRepository; - UploadAttachmentInteractor(this._composerRepository, this._credentialRepository); + UploadAttachmentInteractor(this._composerRepository); Stream> execute(FileInfo fileInfo, AccountId accountId, Uri uploadUrl) async* { try { - final result = await Future.wait( - [_credentialRepository.getUserName(), _credentialRepository.getPassword()], - eagerError: true - ).then((List responses) async { - final accountRequest = AccountRequest(responses.first, responses.last); - return await _composerRepository.uploadAttachment(UploadRequest(uploadUrl, accountId, fileInfo, accountRequest)); - }); + final result = await _composerRepository.uploadAttachment( + UploadRequest(uploadUrl, accountId, fileInfo)); yield Right(UploadAttachmentSuccess(result)); } catch (e) { yield Left(UploadAttachmentFailure(e)); diff --git a/lib/features/composer/domain/usecases/upload_mutiple_attachment_interactor.dart b/lib/features/composer/domain/usecases/upload_mutiple_attachment_interactor.dart index 26ea9cb90..64e75b971 100644 --- a/lib/features/composer/domain/usecases/upload_mutiple_attachment_interactor.dart +++ b/lib/features/composer/domain/usecases/upload_mutiple_attachment_interactor.dart @@ -12,7 +12,7 @@ class UploadMultipleAttachmentInteractor { Stream> execute(List listFiles, AccountId accountId, Uri uploadUrl) async* { try { - yield Right(UploadAttachmentLoadingState()); + yield Right(UploadingAttachmentState()); final listResult = await Future.wait( listFiles.map((fileInfo) async { final result = await _uploadAttachmentInteractor.execute(fileInfo, accountId, uploadUrl).toList(); @@ -24,13 +24,15 @@ class UploadMultipleAttachmentInteractor { } else { final listResultSuccess = listResult.where((either) => either.isRight()).toList(); if (listResultSuccess.length == listResult.length) { - yield Right(UploadAttachmentHasSomeFailure(listResultSuccess)); + yield Right(UploadMultipleAttachmentAllSuccess(listResultSuccess)); + } else if (listResultSuccess.length == 0) { + yield Left(UploadMultipleAttachmentAllFailure(listResult)); } else { - yield Right(UploadAttachmentAllSuccess(listResult)); + yield Right(UploadMultipleAttachmentHasSomeFailure(listResultSuccess)); } } } catch (e) { - yield Left(UploadAttachmentAllFailure(e)); + yield Left(UploadMultipleAttachmentFailure(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 95147efbb..d7b4adb8a 100644 --- a/lib/features/composer/presentation/composer_bindings.dart +++ b/lib/features/composer/presentation/composer_bindings.dart @@ -3,7 +3,6 @@ import 'package:core/presentation/utils/app_toast.dart'; import 'package:flutter/cupertino.dart'; import 'package:get/get.dart'; import 'package:html_editor_enhanced/html_editor.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:tmail_ui_user/features/composer/data/datasource/autocomplete_datasource.dart'; import 'package:tmail_ui_user/features/composer/data/datasource/composer_datasource.dart'; import 'package:tmail_ui_user/features/composer/data/datasource/contact_datasource.dart'; @@ -28,9 +27,6 @@ import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete 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'; -import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; import 'package:tmail_ui_user/features/upload/domain/usecases/local_file_picker_interactor.dart'; import 'package:uuid/uuid.dart'; @@ -46,7 +42,6 @@ class ComposerBindings extends Bindings { DataSourceType.local: Get.find(), })); Get.lazyPut(() => Get.find()); - Get.lazyPut(() => SendEmailInteractor(Get.find())); Get.lazyPut(() => SaveEmailAddressesInteractor(Get.find())); Get.lazyPut(() => AutoCompleteDataSourceImpl()); Get.lazyPut(() => Get.find()); @@ -69,11 +64,8 @@ class ComposerBindings extends Bindings { Get.find(), Get.find())); Get.lazyPut(() => LocalFilePickerInteractor()); - Get.lazyPut(() => CredentialRepositoryImpl(Get.find())); - Get.lazyPut(() => Get.find()); Get.lazyPut(() => UploadAttachmentInteractor( - Get.find(), - Get.find())); + Get.find())); Get.lazyPut(() => UploadMultipleAttachmentInteractor(Get.find())); Get.lazyPut(() => ComposerController( Get.find(), @@ -84,8 +76,7 @@ class ComposerBindings extends Bindings { Get.find(), Get.find(), Get.find(), - Get.find())); - Get.find(), + Get.find(), Get.find(), Get.find())); } diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index a3d8bac8f..8601c4adb 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -25,8 +25,6 @@ import 'package:tmail_ui_user/features/composer/domain/state/get_autocomplete_st import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete_with_device_contact_interactor.dart'; 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/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'; @@ -35,6 +33,7 @@ import 'package:tmail_ui_user/features/email/presentation/constants/email_consta import 'package:tmail_ui_user/features/email/presentation/extensions/email_content_extension.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/features/email/presentation/model/message_content.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/upload/domain/state/local_file_picker_state.dart'; import 'package:tmail_ui_user/features/upload/domain/usecases/local_file_picker_interactor.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; @@ -43,6 +42,8 @@ import 'package:uuid/uuid.dart'; class ComposerController extends BaseController { + final mailboxDashBoardController = Get.find(); + final expandMode = ExpandMode.COLLAPSE.obs; final composerArguments = Rxn(); final isEnableEmailSendButton = false.obs; @@ -58,7 +59,6 @@ class ComposerController extends BaseController { final HtmlEditorController composerEditorController; final TextEditingController subjectEmailInputController; final HtmlMessagePurifier _htmlMessagePurifier; - final HtmlEditorController htmlEditorController; final LocalFilePickerInteractor _localFilePickerInteractor; final UploadMultipleAttachmentInteractor _uploadMultipleAttachmentInteractor; @@ -80,7 +80,6 @@ class ComposerController extends BaseController { this.composerEditorController, this.subjectEmailInputController, this._htmlMessagePurifier, - this.htmlEditorController, this._localFilePickerInteractor, this._uploadMultipleAttachmentInteractor, ); @@ -97,23 +96,19 @@ class ComposerController extends BaseController { void onDone() { viewState.value.fold( (failure) { - if (failure is SendEmailFailure) { - _sendEmailFailure(failure); - } else if (failure is LocalFilePickerFailure || failure is LocalFilePickerCancel) { + if (failure is LocalFilePickerFailure || failure is LocalFilePickerCancel) { _pickFileFailure(failure); } else if (failure is UploadAttachmentFailure - || failure is UploadAttachmentAllFailure) { + || failure is UploadMultipleAttachmentAllFailure) { _uploadAttachmentsFailure(failure); } }, (success) { - if (success is SendEmailSuccess) { - _sendEmailSuccess(success); - } else if (success is LocalFilePickerSuccess) { + if (success is LocalFilePickerSuccess) { _pickFileSuccess(success); } else if (success is UploadAttachmentSuccess - || success is UploadAttachmentAllSuccess - || success is UploadAttachmentHasSomeFailure) { + || success is UploadMultipleAttachmentAllSuccess + || success is UploadMultipleAttachmentHasSomeFailure) { _uploadAttachmentsSuccess(success); } }); @@ -283,40 +278,39 @@ class ComposerController extends BaseController { )}, bodyValues: { generatePartId: EmailBodyValue(emailBodyText, false, false) - } + }, + attachments: attachments.isNotEmpty ? _generateAttachments() : null ); } + Set _generateAttachments() { + return attachments.map((attachment) => + attachment.toEmailBodyPart(Attachment.dispositionAttachment)).toSet(); + } + void sendEmailAction(BuildContext context) async { if (isEnableEmailSendButton.value) { - _appToast.showToast(AppLocalizations.of(context).your_email_being_sent); + _saveEmailAddress(); final email = await generateEmail(); final accountId = composerArguments.value!.session.accounts.keys.first; final sentMailboxId = composerArguments.value!.mapMailboxId[PresentationMailbox.roleSent]; final submissionCreateId = Id(_uuid.v1()); - consumeState(_sendEmailInteractor.execute(accountId, EmailRequest(email, submissionCreateId, mailboxIdSaved: sentMailboxId))); + mailboxDashBoardController.consumeState(_sendEmailInteractor.execute( + accountId, + EmailRequest(email, submissionCreateId, mailboxIdSaved: sentMailboxId))); + + popBack(); } else { _appToast.showErrorToast(AppLocalizations.of(context).your_email_should_have_at_least_one_recipient); } } - void _sendEmailSuccess(Success success) { - _saveEmailAddress(); - _appToast.showSuccessToast(AppLocalizations.of(Get.context!).message_sent); - popBack(); - } - - void _sendEmailFailure(Failure failure) { - _appToast.showErrorToast(AppLocalizations.of(Get.context!).error_message_sent); - popBack(); - } - - void _saveEmailAddress() { + void _saveEmailAddress() async { final listEmailAddressCanSave = Set(); listEmailAddressCanSave.addAll(listToEmailAddress + listCcEmailAddress + listBccEmailAddress); - _saveEmailAddressInteractor.execute(listEmailAddressCanSave.toList()); + await _saveEmailAddressInteractor.execute(listEmailAddressCanSave.toList()); } void _checkContactPermission() async { @@ -391,20 +385,20 @@ class ComposerController extends BaseController { void _uploadAttachmentsSuccess(Success success) { if (success is UploadAttachmentSuccess) { attachments.add(success.attachment); - } else if (success is UploadAttachmentAllSuccess) { - final listAttachment = success.listResults + } else if (success is UploadMultipleAttachmentAllSuccess) { + final listAttachment = success.listResults.where((either) => either.isRight()) .map((either) => either .map((result) => (result as UploadAttachmentSuccess).attachment) .toIterable().first) .toList(); attachments.addAll(listAttachment); - } else if (success is UploadAttachmentHasSomeFailure) { + } else if (success is UploadMultipleAttachmentHasSomeFailure) { final listAttachment = success.listResults - .map((either) => either - .map((result) => (result as UploadAttachmentSuccess).attachment) - .toIterable().first) - .toList(); + .map((either) => either + .map((result) => (result as UploadAttachmentSuccess).attachment) + .toIterable().first) + .toList(); attachments.addAll(listAttachment); } @@ -418,6 +412,6 @@ class ComposerController extends BaseController { } void backToEmailViewAction() { - Get.back(); + popBack(); } } \ No newline at end of file diff --git a/lib/features/composer/presentation/composer_view.dart b/lib/features/composer/presentation/composer_view.dart index 15bc75940..e2fb37a12 100644 --- a/lib/features/composer/presentation/composer_view.dart +++ b/lib/features/composer/presentation/composer_view.dart @@ -6,7 +6,6 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:html_editor_enhanced/html_editor.dart'; import 'package:model/email/attachment.dart'; -import 'package:model/email/email_action_type.dart'; import 'package:tmail_ui_user/features/composer/domain/state/upload_attachment_state.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/email_address_composer_widget_builder.dart'; @@ -62,7 +61,6 @@ class ComposerView extends GetWidget { ); } - Widget _buildEmailHeader(BuildContext context) { List _pickAttachmentsActionTiles(BuildContext context) { return [ _pickPhotoAndVideoAction(context), @@ -89,7 +87,7 @@ class ComposerView extends GetWidget { .build(); } - Widget _buildEmailHeaderField(BuildContext context) { + Widget _buildEmailHeader(BuildContext context) { return Container( margin: EdgeInsets.zero, padding: EdgeInsets.only(top: 20), @@ -152,18 +150,16 @@ class ComposerView extends GetWidget { _buildEmailHeader(context), Padding( padding: EdgeInsets.only(bottom: 30, top: 16, left: 16, right: 16), - child: _buildComposerEditer(context), - ) - child: _buildEmailBodyEditorQuoted(context), + child: _buildComposerEditor(context), ), - _buildLoadingView(), + _buildAttachmentsLoadingView(), _buildAttachments(context), ] ) ); } - Widget _buildComposerEditer(BuildContext context) { + Widget _buildComposerEditor(BuildContext context) { return HtmlEditor( key: Key('email_body_editor_quoted'), controller: controller.composerEditorController, @@ -182,10 +178,10 @@ class ComposerView extends GetWidget { ); } - Widget _buildLoadingView() { + Widget _buildAttachmentsLoadingView() { return Obx(() => controller.viewState.value.fold( (failure) => SizedBox.shrink(), - (success) => success is UploadAttachmentLoadingState + (success) => success is UploadingAttachmentState ? Center(child: Padding( padding: EdgeInsets.only(top: 8), child: SizedBox( diff --git a/lib/features/email/data/datasource_impl/email_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_datasource_impl.dart index abed73c6e..047bbfcbb 100644 --- a/lib/features/email/data/datasource_impl/email_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_datasource_impl.dart @@ -1,10 +1,7 @@ import 'package:dio/dio.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; -import 'package:model/email/read_actions.dart'; -import 'package:model/account/account_request.dart'; -import 'package:model/download/download_task_id.dart'; -import 'package:model/email/attachment.dart'; +import 'package:model/model.dart'; import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'; import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; diff --git a/lib/features/email/data/network/email_api.dart b/lib/features/email/data/network/email_api.dart index d992abc43..5d4bd7980 100644 --- a/lib/features/email/data/network/email_api.dart +++ b/lib/features/email/data/network/email_api.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:convert'; import 'dart:io'; import 'package:core/core.dart'; @@ -155,14 +154,12 @@ class EmailAPI { throw DeviceNotSupportedException(); } - final basicAuth = 'Basic ' + base64Encode(utf8.encode('${accountRequest.userName.userName}:${accountRequest.password.value}')); - final taskIds = await Future.wait( attachments.map((attachment) async => await FlutterDownloader.enqueue( url: attachment.getDownloadUrl(baseDownloadUrl, accountId), savedDir: externalStorageDirPath, headers: { - HttpHeaders.authorizationHeader: basicAuth, + HttpHeaders.authorizationHeader: accountRequest.basicAuth, HttpHeaders.acceptHeader: DioClient.jmapHeader }, fileName: attachment.name, @@ -182,13 +179,11 @@ class EmailAPI { AccountRequest accountRequest, CancelToken cancelToken ) async { - final basicAuth = 'Basic ' + base64Encode(utf8.encode('${accountRequest.userName.userName}:${accountRequest.password.value}')); - return _downloadManager.downloadFile( attachment.getDownloadUrl(baseDownloadUrl, accountId), getTemporaryDirectory(), attachment.name ?? '', - basicAuth, + accountRequest.basicAuth, cancelToken: cancelToken); } } \ No newline at end of file diff --git a/lib/features/email/data/repository/email_repository_impl.dart b/lib/features/email/data/repository/email_repository_impl.dart index ee9042774..93ccf915b 100644 --- a/lib/features/email/data/repository/email_repository_impl.dart +++ b/lib/features/email/data/repository/email_repository_impl.dart @@ -1,11 +1,8 @@ -import 'package:dio/src/cancel_token.dart'; +import 'package:dio/dio.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; -import 'package:model/email/read_actions.dart'; -import 'package:model/account/account_request.dart'; -import 'package:model/download/download_task_id.dart'; -import 'package:model/email/attachment.dart'; +import 'package:model/model.dart'; import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'; import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart'; import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; diff --git a/lib/features/email/presentation/email_bindings.dart b/lib/features/email/presentation/email_bindings.dart index 91b464f84..aced7f897 100644 --- a/lib/features/email/presentation/email_bindings.dart +++ b/lib/features/email/presentation/email_bindings.dart @@ -1,6 +1,7 @@ import 'package:core/core.dart'; import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:tmail_ui_user/features/composer/domain/usecases/send_email_interactor.dart'; import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart'; import 'package:tmail_ui_user/features/email/data/datasource_impl/email_datasource_impl.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; @@ -21,6 +22,7 @@ class EmailBindings extends Bindings { Get.lazyPut(() => Get.find()); Get.lazyPut(() => EmailRepositoryImpl(Get.find())); Get.lazyPut(() => Get.find()); + Get.put(SendEmailInteractor(Get.find())); Get.lazyPut(() => GetEmailContentInteractor(Get.find())); Get.lazyPut(() => MarkAsEmailReadInteractor(Get.find())); Get.lazyPut(() => CredentialRepositoryImpl(Get.find())); diff --git a/lib/features/email/presentation/email_controller.dart b/lib/features/email/presentation/email_controller.dart index 46d7426b5..42403aef6 100644 --- a/lib/features/email/presentation/email_controller.dart +++ b/lib/features/email/presentation/email_controller.dart @@ -204,7 +204,7 @@ class EmailController extends BaseController { ..actionText(AppLocalizations.of(context).cancel) ..addCancelDownloadActionClick(() { cancelToken.cancel([AppLocalizations.of(context).user_cancel_download_file]); - Get.back(); + popBack(); })) .build()); } diff --git a/lib/features/email/presentation/extensions/email_content_extension.dart b/lib/features/email/presentation/extensions/email_content_extension.dart index 6b1be40a9..9631f2ed6 100644 --- a/lib/features/email/presentation/extensions/email_content_extension.dart +++ b/lib/features/email/presentation/extensions/email_content_extension.dart @@ -3,7 +3,6 @@ import 'package:tmail_ui_user/features/email/presentation/constants/email_consta import 'package:model/model.dart'; import 'package:tmail_ui_user/features/email/presentation/model/message_content.dart'; import 'package:tmail_ui_user/features/email/presentation/model/text_format.dart'; -import 'package:tmail_ui_user/features/email/presentation/extensions/email_body_part_extension.dart'; extension EmailContentExtension on EmailContent { diff --git a/lib/features/email/presentation/widgets/attachment_file_tile_builder.dart b/lib/features/email/presentation/widgets/attachment_file_tile_builder.dart index f54ab317e..be2aa2656 100644 --- a/lib/features/email/presentation/widgets/attachment_file_tile_builder.dart +++ b/lib/features/email/presentation/widgets/attachment_file_tile_builder.dart @@ -99,20 +99,21 @@ class AttachmentFileTileBuilder { : null, trailing: buttonAction != null ? Transform( - transform: Matrix4.translationValues(6.0, heightItem != null ? -6.0 : 0.0, 0.0), + transform: Matrix4.translationValues(-5.0, heightItem != null ? -6.0 : 0.0, 0.0), child: buttonAction) : null ), - Transform( - transform: Matrix4.translationValues(5.0, 5.0, 0.0), - child: IconButton( - icon: SvgPicture.asset(_imagePaths.icDownload, width: 24, height: 24, fit: BoxFit.fill), - onPressed: () { - if (_onDownloadAttachmentFileActionClick != null) { - _onDownloadAttachmentFileActionClick!(_attachment); + if (buttonAction == null) + Transform( + transform: Matrix4.translationValues(5.0, 5.0, 0.0), + child: IconButton( + icon: SvgPicture.asset(_imagePaths.icDownload, width: 24, height: 24, fit: BoxFit.fill), + onPressed: () { + if (_onDownloadAttachmentFileActionClick != null) { + _onDownloadAttachmentFileActionClick!(_attachment); + } } - } - )), + )), if (_attachmentSize > _limitDisplayAttachment && _expandMode == ExpandMode.COLLAPSE) _buildItemBackground() ] ) diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart index 79632a05c..47865d54a 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart @@ -1,3 +1,4 @@ +import 'package:core/core.dart'; import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:tmail_ui_user/features/email/presentation/email_bindings.dart'; @@ -15,8 +16,11 @@ class MailboxDashBoardBindings extends Bindings { Get.lazyPut(() => CredentialRepositoryImpl(Get.find())); Get.lazyPut(() => Get.find()); Get.lazyPut(() => GetUserProfileInteractor(Get.find())); + Get.lazyPut(() => AppToast()); - Get.put(MailboxDashBoardController(Get.find())); + Get.put(MailboxDashBoardController( + Get.find(), + Get.find())); MailboxBindings().dependencies(); ThreadBindings().dependencies(); diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart index 09d3490c2..bbb358d31 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart @@ -1,3 +1,5 @@ +import 'package:core/core.dart'; +import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:get/get.dart'; @@ -6,13 +8,16 @@ import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; +import 'package:tmail_ui_user/features/composer/domain/state/send_email_state.dart'; import 'package:tmail_ui_user/features/email/presentation/email_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_user_profile_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_user_profile_interactor.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; class MailboxDashBoardController extends BaseController { final GetUserProfileInteractor _getUserProfileInteractor; + final AppToast _appToast; final scaffoldKey = GlobalKey(); final selectedMailbox = Rxn(); @@ -23,7 +28,7 @@ class MailboxDashBoardController extends BaseController { Session? sessionCurrent; Map mapMailboxId = Map(); - MailboxDashBoardController(this._getUserProfileInteractor); + MailboxDashBoardController(this._getUserProfileInteractor, this._appToast); @override void onReady() { @@ -33,14 +38,41 @@ class MailboxDashBoardController extends BaseController { } @override - void onDone() { + void onData(Either newState) { + super.onData(newState); viewState.value.map((success) { - if (success is GetUserProfileSuccess) { - userProfile.value = success.userProfile; + if (success is SendingEmailState) { + if (Get.context != null) { + _appToast.showToast(AppLocalizations.of(Get.context!).your_email_being_sent); + } } }); } + @override + void onDone() { + viewState.value.fold( + (failure) { + if (failure is SendEmailFailure) { + if (Get.context != null) { + _appToast.showSuccessToast(AppLocalizations.of(Get.context!).error_message_sent); + clearState(); + } + } + }, + (success) { + if (success is GetUserProfileSuccess) { + userProfile.value = success.userProfile; + } else if (success is SendEmailSuccess) { + if (Get.context != null) { + _appToast.showSuccessToast(AppLocalizations.of(Get.context!).message_sent); + clearState(); + } + } + } + ); + } + @override void onError(error) { } diff --git a/model/lib/account/account_request.dart b/model/lib/account/account_request.dart index 773d27b89..b23bdf5f3 100644 --- a/model/lib/account/account_request.dart +++ b/model/lib/account/account_request.dart @@ -14,7 +14,7 @@ class AccountRequest with EquatableMixin { 'password': password.value, }; - String basicAuth() => 'Basic ${base64Encode(utf8.encode('${userName.userName}:${password.value}'))}'; + String get basicAuth => 'Basic ${base64Encode(utf8.encode('${userName.userName}:${password.value}'))}'; @override List get props => [userName, password]; diff --git a/model/lib/email/attachment.dart b/model/lib/email/attachment.dart index ebca713a1..d10b0acdd 100644 --- a/model/lib/email/attachment.dart +++ b/model/lib/email/attachment.dart @@ -9,12 +9,15 @@ import 'package:uri/uri.dart'; class Attachment with EquatableMixin { + static final String dispositionAttachment = 'attachment'; + final PartId? partId; final Id? blobId; final UnsignedInt? size; final String? name; final MediaType? type; final String? cid; + final String? disposition; Attachment({ this.partId, @@ -22,7 +25,8 @@ class Attachment with EquatableMixin { this.size, this.name, this.type, - this.cid + this.cid, + this.disposition, }); String getDownloadUrl(String baseDownloadUrl, AccountId accountId) { @@ -37,5 +41,5 @@ class Attachment with EquatableMixin { } @override - List get props => [partId, blobId, size, name, type, cid]; + List get props => [partId, blobId, size, name, type, cid, disposition]; } \ No newline at end of file diff --git a/model/lib/extensions/attachment_extension.dart b/model/lib/extensions/attachment_extension.dart new file mode 100644 index 000000000..783b11dba --- /dev/null +++ b/model/lib/extensions/attachment_extension.dart @@ -0,0 +1,12 @@ + +import 'package:jmap_dart_client/jmap/mail/email/email_body_part.dart'; +import 'package:model/model.dart'; + +extension AttachmentExtension on Attachment { + EmailBodyPart toEmailBodyPart(String disposition) => EmailBodyPart( + blobId: blobId, + size: size, + name: name, + type: type, + disposition: disposition); +} \ No newline at end of file diff --git a/lib/features/email/presentation/extensions/email_body_part_extension.dart b/model/lib/extensions/email_body_part_extension.dart similarity index 86% rename from lib/features/email/presentation/extensions/email_body_part_extension.dart rename to model/lib/extensions/email_body_part_extension.dart index e354021a2..6c5b2c332 100644 --- a/lib/features/email/presentation/extensions/email_body_part_extension.dart +++ b/model/lib/extensions/email_body_part_extension.dart @@ -8,5 +8,6 @@ extension EmailBodyPartExtension on EmailBodyPart { size: size, name: name, type: type, - cid: cid); + cid: cid, + disposition: disposition); } diff --git a/model/lib/model.dart b/model/lib/model.dart index 8754fd07e..9dccc63b6 100644 --- a/model/lib/model.dart +++ b/model/lib/model.dart @@ -35,6 +35,8 @@ export 'extensions/user_profile_extension.dart'; export 'extensions/presentation_email_extension.dart'; export 'extensions/keyword_identifier_extension.dart'; export 'extensions/presentation_mailbox_extension.dart'; +export 'extensions/email_body_part_extension.dart'; +export 'extensions/attachment_extension.dart'; // Download export 'download/download_task_id.dart'; diff --git a/model/lib/upload/upload_request.dart b/model/lib/upload/upload_request.dart index ab8fbff3a..d84d4d374 100644 --- a/model/lib/upload/upload_request.dart +++ b/model/lib/upload/upload_request.dart @@ -8,10 +8,9 @@ class UploadRequest with EquatableMixin { final Uri uploadUrl; final FileInfo fileInfo; final AccountId accountId; - final AccountRequest accountRequest; - UploadRequest(this.uploadUrl, this.accountId, this.fileInfo, this.accountRequest); + UploadRequest(this.uploadUrl, this.accountId, this.fileInfo); @override - List get props => [uploadUrl, accountId, fileInfo, accountRequest]; + List get props => [uploadUrl, accountId, fileInfo]; } \ No newline at end of file diff --git a/model/lib/upload/upload_response.dart b/model/lib/upload/upload_response.dart index 4d2e65571..d503ad699 100644 --- a/model/lib/upload/upload_response.dart +++ b/model/lib/upload/upload_response.dart @@ -34,7 +34,7 @@ class UploadResponse with EquatableMixin { } extension UploadResponseExtension on UploadResponse { - Attachment toAttachmentFile(String nameFile) { - return Attachment(blobId: blobId, size: UnsignedInt(size), name: nameFile, type: type); + Attachment toAttachmentFile(String nameFile, {MediaType? mediaType}) { + return Attachment(blobId: blobId, size: UnsignedInt(size), name: nameFile, type: mediaType ?? type); } } \ No newline at end of file