TF-3454 Download all attachments as ZIP
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.1167 4.35833L15.9583 2.95833C15.7333 2.675 15.3917 2.5 15 2.5H5C4.60833 2.5 4.26667 2.675 4.03333 2.95833L2.88333 4.35833C2.64167 4.64167 2.5 5.01667 2.5 5.41667V15.8333C2.5 16.75 3.25 17.5 4.16667 17.5H15.8333C16.75 17.5 17.5 16.75 17.5 15.8333V5.41667C17.5 5.01667 17.3583 4.64167 17.1167 4.35833ZM5.2 4.16667H14.8L15.475 4.975H4.53333L5.2 4.16667ZM4.16667 15.8333V6.66667H15.8333V15.8333H4.16667ZM11.2083 8.33333H8.79167V10.8333H6.66667L10 14.1667L13.3333 10.8333H11.2083V8.33333Z" fill="#55687D"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 618 B |
@@ -31,6 +31,7 @@ class SupportedPreviewFileTypes {
|
||||
'application/vnd.ms-powerpoint'];
|
||||
|
||||
static const zipMimeTypes = [
|
||||
'application/zip',
|
||||
'application/x-tar',
|
||||
'application/x-gtar',
|
||||
'application/x-gzip',
|
||||
|
||||
@@ -222,6 +222,7 @@ class ImagePaths {
|
||||
String get icDeleteSelection => _getImagePath('ic_delete_selection.svg');
|
||||
String get icLogoTwakeWelcome => _getImagePath('ic_logo_twake_welcome.svg');
|
||||
String get icHelp => _getImagePath('ic_help.svg');
|
||||
String get icDownloadAll => _getImagePath('ic_download_all.svg');
|
||||
|
||||
String _getImagePath(String imageName) {
|
||||
return AssetsPaths.images + imageName;
|
||||
|
||||
@@ -99,6 +99,8 @@ PODS:
|
||||
- UniversalDetector2 (= 2.0.1)
|
||||
- flutter_downloader (0.0.1):
|
||||
- Flutter
|
||||
- flutter_file_dialog (0.0.1):
|
||||
- Flutter
|
||||
- flutter_image_compress_common (1.0.0):
|
||||
- Flutter
|
||||
- Mantle
|
||||
@@ -221,6 +223,7 @@ DEPENDENCIES:
|
||||
- flutter_appauth (from `.symlinks/plugins/flutter_appauth/ios`)
|
||||
- flutter_charset_detector_darwin (from `.symlinks/plugins/flutter_charset_detector_darwin/darwin`)
|
||||
- flutter_downloader (from `.symlinks/plugins/flutter_downloader/ios`)
|
||||
- flutter_file_dialog (from `.symlinks/plugins/flutter_file_dialog/ios`)
|
||||
- flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`)
|
||||
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
|
||||
- flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
|
||||
@@ -297,6 +300,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/flutter_charset_detector_darwin/darwin"
|
||||
flutter_downloader:
|
||||
:path: ".symlinks/plugins/flutter_downloader/ios"
|
||||
flutter_file_dialog:
|
||||
:path: ".symlinks/plugins/flutter_file_dialog/ios"
|
||||
flutter_image_compress_common:
|
||||
:path: ".symlinks/plugins/flutter_image_compress_common/ios"
|
||||
flutter_inappwebview_ios:
|
||||
@@ -363,6 +368,7 @@ SPEC CHECKSUMS:
|
||||
flutter_appauth: 1ce438877bc111c5d8f42da47729909290624886
|
||||
flutter_charset_detector_darwin: fb3692d6d72cb6afcce7b0dd1a9516be6e78556e
|
||||
flutter_downloader: b7301ae057deadd4b1650dc7c05375f10ff12c39
|
||||
flutter_file_dialog: 4c014a45b105709a27391e266c277d7e588e9299
|
||||
flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e
|
||||
flutter_inappwebview_ios: 97215cf7d4677db55df76782dbd2930c5e1c1ea0
|
||||
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
|
||||
|
||||
@@ -201,4 +201,24 @@ abstract class EmailDataSource {
|
||||
Future<void> storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer);
|
||||
|
||||
Future<EMLPreviewer> getPreviewEMLContentInMemory(String keyStored);
|
||||
|
||||
Future<void> downloadAllAttachmentsForWeb(
|
||||
AccountId accountId,
|
||||
EmailId emailId,
|
||||
String baseDownloadAllUrl,
|
||||
String outputFileName,
|
||||
AccountRequest accountRequest,
|
||||
DownloadTaskId taskId,
|
||||
StreamController<Either<Failure, Success>> onReceiveController,
|
||||
{CancelToken? cancelToken}
|
||||
);
|
||||
|
||||
Future<DownloadedResponse> exportAllAttachments(
|
||||
AccountId accountId,
|
||||
EmailId emailId,
|
||||
String baseDownloadAllUrl,
|
||||
String outputFileName,
|
||||
AccountRequest accountRequest,
|
||||
{CancelToken? cancelToken}
|
||||
);
|
||||
}
|
||||
@@ -540,4 +540,46 @@ class EmailDataSourceImpl extends EmailDataSource {
|
||||
Future<EMLPreviewer> getPreviewEMLContentInMemory(String keyStored) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> downloadAllAttachmentsForWeb(
|
||||
AccountId accountId,
|
||||
EmailId emailId,
|
||||
String baseDownloadAllUrl,
|
||||
String outputFileName,
|
||||
AccountRequest accountRequest,
|
||||
DownloadTaskId taskId,
|
||||
StreamController<Either<Failure, Success>> onReceiveController,
|
||||
{CancelToken? cancelToken}
|
||||
) => Future.sync(() async {
|
||||
return await emailAPI.downloadAllAttachmentsForWeb(
|
||||
accountId,
|
||||
emailId,
|
||||
baseDownloadAllUrl,
|
||||
outputFileName,
|
||||
accountRequest,
|
||||
taskId,
|
||||
onReceiveController,
|
||||
cancelToken: cancelToken,
|
||||
);
|
||||
}).catchError(_exceptionThrower.throwException);
|
||||
|
||||
@override
|
||||
Future<DownloadedResponse> exportAllAttachments(
|
||||
AccountId accountId,
|
||||
EmailId emailId,
|
||||
String baseDownloadAllUrl,
|
||||
String outputFileName,
|
||||
AccountRequest accountRequest, {
|
||||
CancelToken? cancelToken,
|
||||
}) => Future.sync(() async {
|
||||
return await emailAPI.exportAllAttachments(
|
||||
accountId,
|
||||
emailId,
|
||||
baseDownloadAllUrl,
|
||||
outputFileName,
|
||||
accountRequest,
|
||||
cancelToken: cancelToken,
|
||||
);
|
||||
}).catchError(_exceptionThrower.throwException);
|
||||
}
|
||||
@@ -563,4 +563,23 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
|
||||
Future<EMLPreviewer> getPreviewEMLContentInMemory(String keyStored) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> downloadAllAttachmentsForWeb(
|
||||
AccountId accountId,
|
||||
EmailId emailId,
|
||||
String baseDownloadAllUrl,
|
||||
String outputFileName,
|
||||
AccountRequest accountRequest,
|
||||
DownloadTaskId taskId,
|
||||
StreamController<Either<Failure, Success>> onReceiveController,
|
||||
{CancelToken? cancelToken}
|
||||
) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<DownloadedResponse> exportAllAttachments(AccountId accountId, EmailId emailId, String baseDownloadAllUrl, String outputFileName, AccountRequest accountRequest, {CancelToken? cancelToken}) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
@@ -254,4 +254,23 @@ class EmailLocalStorageDataSourceImpl extends EmailDataSource {
|
||||
Future<void> markAsForwarded(Session session, AccountId accountId, List<EmailId> emailIds) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> downloadAllAttachmentsForWeb(
|
||||
AccountId accountId,
|
||||
EmailId emailId,
|
||||
String baseDownloadAllUrl,
|
||||
String outputFileName,
|
||||
AccountRequest accountRequest,
|
||||
DownloadTaskId taskId,
|
||||
StreamController<Either<Failure, Success>> onReceiveController,
|
||||
{CancelToken? cancelToken}
|
||||
) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<DownloadedResponse> exportAllAttachments(AccountId accountId, EmailId emailId, String baseDownloadAllUrl, String outputFileName, AccountRequest accountRequest, {CancelToken? cancelToken}) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
@@ -252,4 +252,23 @@ class EmailSessionStorageDatasourceImpl extends EmailDataSource {
|
||||
Future<void> markAsForwarded(Session session, AccountId accountId, List<EmailId> emailIds) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> downloadAllAttachmentsForWeb(
|
||||
AccountId accountId,
|
||||
EmailId emailId,
|
||||
String baseDownloadAllUrl,
|
||||
String outputFileName,
|
||||
AccountRequest accountRequest,
|
||||
DownloadTaskId taskId,
|
||||
StreamController<Either<Failure, Success>> onReceiveController,
|
||||
{CancelToken? cancelToken}
|
||||
) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<DownloadedResponse> exportAllAttachments(AccountId accountId, EmailId emailId, String baseDownloadAllUrl, String outputFileName, AccountRequest accountRequest, {CancelToken? cancelToken}) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,7 @@ import 'package:model/download/download_task_id.dart';
|
||||
import 'package:model/email/attachment.dart';
|
||||
import 'package:model/email/mark_star_action.dart';
|
||||
import 'package:model/email/read_actions.dart';
|
||||
import 'package:model/extensions/account_id_extensions.dart';
|
||||
import 'package:model/extensions/email_extension.dart';
|
||||
import 'package:model/extensions/email_id_extensions.dart';
|
||||
import 'package:model/extensions/keyword_identifier_extension.dart';
|
||||
@@ -66,11 +67,13 @@ import 'package:tmail_ui_user/features/email/domain/extensions/email_id_extensio
|
||||
import 'package:tmail_ui_user/features/email/domain/model/event_action.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/model/restore_deleted_message_request.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/download_all_attachments_for_web_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart';
|
||||
import 'package:tmail_ui_user/main/error/capability_validator.dart';
|
||||
import 'package:uri/uri.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class EmailAPI with HandleSetErrorMixin {
|
||||
@@ -402,6 +405,88 @@ class EmailAPI with HandleSetErrorMixin {
|
||||
return bytesDownloaded;
|
||||
}
|
||||
|
||||
Future<void> downloadAllAttachmentsForWeb(
|
||||
AccountId accountId,
|
||||
EmailId emailId,
|
||||
String baseDownloadAllUrl,
|
||||
String outputFileName,
|
||||
AccountRequest accountRequest,
|
||||
DownloadTaskId taskId,
|
||||
StreamController<Either<Failure, Success>> onReceiveController,
|
||||
{CancelToken? cancelToken}
|
||||
) async {
|
||||
final authentication = accountRequest.authenticationType == AuthenticationType.oidc
|
||||
? accountRequest.bearerToken
|
||||
: accountRequest.basicAuth;
|
||||
final headerParam = _dioClient.getHeaders();
|
||||
headerParam[HttpHeaders.authorizationHeader] = authentication;
|
||||
headerParam[HttpHeaders.acceptHeader] = DioClient.jmapHeader;
|
||||
|
||||
final downloadAllUriTemplate = UriTemplate(Uri.decodeFull(baseDownloadAllUrl));
|
||||
final downloadAllUrl = downloadAllUriTemplate.expand({
|
||||
'accountId': accountId.asString,
|
||||
'emailId': emailId.asString,
|
||||
'name': outputFileName,
|
||||
});
|
||||
final downloadFileName = '$outputFileName.zip';
|
||||
|
||||
final bytesDownloaded = await _dioClient.get(
|
||||
downloadAllUrl,
|
||||
options: Options(
|
||||
headers: headerParam,
|
||||
responseType: ResponseType.bytes),
|
||||
onReceiveProgress: (downloaded, total) {
|
||||
log('EmailAPI::downloadFileForWeb(): downloaded = $downloaded | total: $total');
|
||||
double progress = 0;
|
||||
if (downloaded > 0 && total >= downloaded) {
|
||||
progress = (downloaded / total) * 100;
|
||||
}
|
||||
log('EmailAPI::downloadFileForWeb(): progress = ${progress.round()}%');
|
||||
onReceiveController.add(Right(DownloadingAllAttachmentsForWeb(
|
||||
taskId,
|
||||
downloadFileName,
|
||||
progress,
|
||||
downloaded,
|
||||
total > 0 ? total : 0,
|
||||
)));
|
||||
},
|
||||
cancelToken: cancelToken,
|
||||
);
|
||||
|
||||
_downloadManager.createAnchorElementDownloadFileWeb(
|
||||
bytesDownloaded,
|
||||
downloadFileName,
|
||||
);
|
||||
}
|
||||
|
||||
Future<DownloadedResponse> exportAllAttachments(
|
||||
AccountId accountId,
|
||||
EmailId emailId,
|
||||
String baseDownloadAllUrl,
|
||||
String outputFileName,
|
||||
AccountRequest accountRequest,
|
||||
{CancelToken? cancelToken}
|
||||
) async {
|
||||
final authentication = accountRequest.authenticationType == AuthenticationType.oidc
|
||||
? accountRequest.bearerToken
|
||||
: accountRequest.basicAuth;
|
||||
|
||||
final downloadAllUriTemplate = UriTemplate(Uri.decodeFull(baseDownloadAllUrl));
|
||||
final downloadAllUrl = downloadAllUriTemplate.expand({
|
||||
'accountId': accountId.asString,
|
||||
'emailId': emailId.asString,
|
||||
'name': outputFileName,
|
||||
});
|
||||
final downloadFileName = '$outputFileName.zip';
|
||||
|
||||
return _downloadManager.downloadFile(
|
||||
downloadAllUrl,
|
||||
getTemporaryDirectory(),
|
||||
downloadFileName,
|
||||
authentication,
|
||||
cancelToken: cancelToken);
|
||||
}
|
||||
|
||||
Future<({
|
||||
List<EmailId> emailIdsSuccess,
|
||||
Map<Id, SetError> mapErrors,
|
||||
|
||||
@@ -530,4 +530,41 @@ class EmailRepositoryImpl extends EmailRepository {
|
||||
htmlContent,
|
||||
configuration);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> downloadAllAttachmentsForWeb(
|
||||
AccountId accountId,
|
||||
EmailId emailId,
|
||||
String baseDownloadAllUrl,
|
||||
String outputFileName,
|
||||
AccountRequest accountRequest,
|
||||
DownloadTaskId taskId,
|
||||
StreamController<Either<Failure, Success>> onReceiveController,
|
||||
{CancelToken? cancelToken}
|
||||
) => emailDataSource[DataSourceType.network]!.downloadAllAttachmentsForWeb(
|
||||
accountId,
|
||||
emailId,
|
||||
baseDownloadAllUrl,
|
||||
outputFileName,
|
||||
accountRequest,
|
||||
taskId,
|
||||
onReceiveController,
|
||||
cancelToken: cancelToken,
|
||||
);
|
||||
|
||||
@override
|
||||
Future<DownloadedResponse> exportAllAttachments(
|
||||
AccountId accountId,
|
||||
EmailId emailId,
|
||||
String baseDownloadAllUrl,
|
||||
String outputFileName,
|
||||
AccountRequest accountRequest, {
|
||||
CancelToken? cancelToken,
|
||||
}) => emailDataSource[DataSourceType.network]!.exportAllAttachments(
|
||||
accountId,
|
||||
emailId,
|
||||
baseDownloadAllUrl,
|
||||
outputFileName,
|
||||
accountRequest,
|
||||
);
|
||||
}
|
||||
@@ -199,4 +199,24 @@ abstract class EmailRepository {
|
||||
Future<String> sanitizeHtmlContent(
|
||||
String htmlContent,
|
||||
TransformConfiguration configuration);
|
||||
|
||||
Future<void> downloadAllAttachmentsForWeb(
|
||||
AccountId accountId,
|
||||
EmailId emailId,
|
||||
String baseDownloadAllUrl,
|
||||
String outputFileName,
|
||||
AccountRequest accountRequest,
|
||||
DownloadTaskId taskId,
|
||||
StreamController<Either<Failure, Success>> onReceiveController,
|
||||
{CancelToken? cancelToken}
|
||||
);
|
||||
|
||||
Future<DownloadedResponse> exportAllAttachments(
|
||||
AccountId accountId,
|
||||
EmailId emailId,
|
||||
String baseDownloadAllUrl,
|
||||
String outputFileName,
|
||||
AccountRequest accountRequest,
|
||||
{CancelToken? cancelToken}
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import 'package:core/presentation/state/failure.dart';
|
||||
import 'package:core/presentation/state/success.dart';
|
||||
import 'package:model/download/download_task_id.dart';
|
||||
import 'package:model/email/attachment.dart';
|
||||
|
||||
class StartDownloadAllAttachmentsForWeb extends UIState {
|
||||
StartDownloadAllAttachmentsForWeb(this.taskId, this.attachment);
|
||||
|
||||
final DownloadTaskId taskId;
|
||||
final Attachment attachment;
|
||||
|
||||
@override
|
||||
List<Object> get props => [taskId, attachment];
|
||||
}
|
||||
|
||||
class DownloadingAllAttachmentsForWeb extends LoadingState {
|
||||
DownloadingAllAttachmentsForWeb(
|
||||
this.taskId,
|
||||
this.fileName,
|
||||
this.progress,
|
||||
this.downloaded,
|
||||
this.total,
|
||||
);
|
||||
|
||||
final DownloadTaskId taskId;
|
||||
final String fileName;
|
||||
final double progress;
|
||||
final int downloaded;
|
||||
final int total;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [taskId, fileName, progress, downloaded, total];
|
||||
}
|
||||
|
||||
class DownloadAllAttachmentsForWebSuccess extends UIState {
|
||||
DownloadAllAttachmentsForWebSuccess({required this.taskId});
|
||||
|
||||
final DownloadTaskId taskId;
|
||||
|
||||
@override
|
||||
List<Object> get props => [taskId];
|
||||
}
|
||||
|
||||
class DownloadAllAttachmentsForWebFailure extends FeatureFailure {
|
||||
DownloadAllAttachmentsForWebFailure({super.exception, required this.taskId});
|
||||
|
||||
final DownloadTaskId taskId;
|
||||
|
||||
@override
|
||||
List<Object> get props => [exception, taskId];
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import 'package:core/data/network/download/downloaded_response.dart';
|
||||
import 'package:core/presentation/state/failure.dart';
|
||||
import 'package:core/presentation/state/success.dart';
|
||||
|
||||
class ExportingAllAttachments extends LoadingState {}
|
||||
|
||||
class ExportAllAttachmentsSuccess extends UIState {
|
||||
ExportAllAttachmentsSuccess(this.downloadedResponse);
|
||||
|
||||
final DownloadedResponse downloadedResponse;
|
||||
|
||||
@override
|
||||
List<Object> get props => [downloadedResponse];
|
||||
}
|
||||
|
||||
class ExportAllAttachmentsFailure extends FeatureFailure {
|
||||
ExportAllAttachmentsFailure({super.exception});
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:core/presentation/state/failure.dart';
|
||||
import 'package:core/presentation/state/success.dart';
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/user_name.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||
import 'package:model/account/account_request.dart';
|
||||
import 'package:model/account/authentication_type.dart';
|
||||
import 'package:model/account/password.dart';
|
||||
import 'package:model/download/download_task_id.dart';
|
||||
import 'package:model/email/attachment.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/download_all_attachments_for_web_state.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart';
|
||||
|
||||
class DownloadAllAttachmentsForWebInteractor {
|
||||
const DownloadAllAttachmentsForWebInteractor(
|
||||
this._emailRepository,
|
||||
this._accountRepository,
|
||||
this._authenticationOIDCRepository,
|
||||
this.credentialRepository,
|
||||
);
|
||||
|
||||
final EmailRepository _emailRepository;
|
||||
final AccountRepository _accountRepository;
|
||||
final AuthenticationOIDCRepository _authenticationOIDCRepository;
|
||||
final CredentialRepository credentialRepository;
|
||||
|
||||
Stream<Either<Failure, Success>> execute(
|
||||
AccountId accountId,
|
||||
EmailId emailId,
|
||||
String baseDownloadAllUrl,
|
||||
Attachment attachment,
|
||||
DownloadTaskId taskId,
|
||||
StreamController<Either<Failure, Success>> onReceiveController,
|
||||
{CancelToken? cancelToken}
|
||||
) async* {
|
||||
try {
|
||||
yield Right(StartDownloadAllAttachmentsForWeb(
|
||||
taskId,
|
||||
attachment,
|
||||
));
|
||||
onReceiveController.add(Right(StartDownloadAllAttachmentsForWeb(
|
||||
taskId,
|
||||
attachment,
|
||||
)));
|
||||
final currentAccount = await _accountRepository.getCurrentAccount();
|
||||
AccountRequest accountRequest;
|
||||
if (currentAccount.authenticationType == AuthenticationType.oidc) {
|
||||
final tokenOidc = await _authenticationOIDCRepository.getStoredTokenOIDC(currentAccount.id);
|
||||
accountRequest = AccountRequest.withOidc(token: tokenOidc);
|
||||
} else {
|
||||
final authenticationInfoCache = await credentialRepository.getAuthenticationInfoStored();
|
||||
accountRequest = AccountRequest.withBasic(
|
||||
userName: UserName(authenticationInfoCache.username),
|
||||
password: Password(authenticationInfoCache.password),
|
||||
);
|
||||
}
|
||||
|
||||
await _emailRepository.downloadAllAttachmentsForWeb(
|
||||
accountId,
|
||||
emailId,
|
||||
baseDownloadAllUrl,
|
||||
attachment.name!,
|
||||
accountRequest,
|
||||
taskId,
|
||||
onReceiveController,
|
||||
cancelToken: cancelToken
|
||||
);
|
||||
|
||||
yield Right(DownloadAllAttachmentsForWebSuccess(taskId: taskId));
|
||||
} catch (e) {
|
||||
logError('DownloadAllAttachmentsForWebInteractor::execute():EXCEPTION: $e');
|
||||
yield Left(DownloadAllAttachmentsForWebFailure(exception: e, taskId: taskId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import 'package:core/presentation/state/failure.dart';
|
||||
import 'package:core/presentation/state/success.dart';
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/user_name.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||
import 'package:model/account/account_request.dart';
|
||||
import 'package:model/account/authentication_type.dart';
|
||||
import 'package:model/account/password.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/export_all_attachments_state.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart';
|
||||
|
||||
class ExportAllAttachmentsInteractor {
|
||||
const ExportAllAttachmentsInteractor(
|
||||
this._emailRepository,
|
||||
this._accountRepository,
|
||||
this._authenticationOIDCRepository,
|
||||
this.credentialRepository,
|
||||
);
|
||||
|
||||
final EmailRepository _emailRepository;
|
||||
final AccountRepository _accountRepository;
|
||||
final AuthenticationOIDCRepository _authenticationOIDCRepository;
|
||||
final CredentialRepository credentialRepository;
|
||||
|
||||
Stream<Either<Failure, Success>> execute(
|
||||
AccountId accountId,
|
||||
EmailId emailId,
|
||||
String baseDownloadAllUrl,
|
||||
String outputFileName,
|
||||
{CancelToken? cancelToken}
|
||||
) async* {
|
||||
try {
|
||||
yield Right(ExportingAllAttachments());
|
||||
final currentAccount = await _accountRepository.getCurrentAccount();
|
||||
AccountRequest accountRequest;
|
||||
if (currentAccount.authenticationType == AuthenticationType.oidc) {
|
||||
final tokenOidc = await _authenticationOIDCRepository.getStoredTokenOIDC(currentAccount.id);
|
||||
accountRequest = AccountRequest.withOidc(token: tokenOidc);
|
||||
} else {
|
||||
final authenticationInfoCache = await credentialRepository.getAuthenticationInfoStored();
|
||||
accountRequest = AccountRequest.withBasic(
|
||||
userName: UserName(authenticationInfoCache.username),
|
||||
password: Password(authenticationInfoCache.password),
|
||||
);
|
||||
}
|
||||
|
||||
final result = await _emailRepository.exportAllAttachments(
|
||||
accountId,
|
||||
emailId,
|
||||
baseDownloadAllUrl,
|
||||
outputFileName,
|
||||
accountRequest,
|
||||
cancelToken: cancelToken,
|
||||
);
|
||||
|
||||
yield Right(ExportAllAttachmentsSuccess(result));
|
||||
} catch (e) {
|
||||
logError('ExportAllAttachmentsInteractor::execute():EXCEPTION: $e');
|
||||
yield Left(ExportAllAttachmentsFailure(exception: e));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,10 @@ import 'package:tmail_ui_user/features/email/data/local/html_analyzer.dart';
|
||||
import 'package:tmail_ui_user/features/email/data/network/email_api.dart';
|
||||
import 'package:tmail_ui_user/features/email/data/repository/email_repository_impl.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/download_all_attachments_for_web_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/download_attachments_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/export_all_attachments_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/export_attachment_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/get_stored_email_state_interactor.dart';
|
||||
@@ -80,6 +82,8 @@ class EmailBindings extends BaseBindings {
|
||||
Get.find<ParseEmailByBlobIdInteractor>(),
|
||||
Get.find<PreviewEmailFromEmlFileInteractor>(),
|
||||
Get.find<GetHtmlContentFromAttachmentInteractor>(),
|
||||
Get.find<DownloadAllAttachmentsForWebInteractor>(),
|
||||
Get.find<ExportAllAttachmentsInteractor>(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -168,6 +172,16 @@ class EmailBindings extends BaseBindings {
|
||||
Get.lazyPut(() => PreviewEmailFromEmlFileInteractor(Get.find<EmailRepository>()));
|
||||
IdentityInteractorsBindings().dependencies();
|
||||
Get.lazyPut(() => GetHtmlContentFromAttachmentInteractor(Get.find<DownloadAttachmentForWebInteractor>()));
|
||||
Get.lazyPut(() => DownloadAllAttachmentsForWebInteractor(
|
||||
Get.find<EmailRepository>(),
|
||||
Get.find<AccountRepository>(),
|
||||
Get.find<AuthenticationOIDCRepository>(),
|
||||
Get.find<CredentialRepository>()));
|
||||
Get.lazyPut(() => ExportAllAttachmentsInteractor(
|
||||
Get.find<EmailRepository>(),
|
||||
Get.find<AccountRepository>(),
|
||||
Get.find<AuthenticationOIDCRepository>(),
|
||||
Get.find<CredentialRepository>()));
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -13,9 +13,11 @@ import 'package:dartz/dartz.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_file_dialog/flutter_file_dialog.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||
@@ -55,8 +57,10 @@ import 'package:tmail_ui_user/features/email/domain/state/calendar_event_accept_
|
||||
import 'package:tmail_ui_user/features/email/domain/state/calendar_event_maybe_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/calendar_event_reject_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/calendar_event_reply_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/download_all_attachments_for_web_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/download_attachments_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/export_all_attachments_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/export_attachment_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/get_html_content_from_attachment_state.dart';
|
||||
@@ -72,6 +76,8 @@ import 'package:tmail_ui_user/features/email/domain/state/send_receipt_to_sender
|
||||
import 'package:tmail_ui_user/features/email/domain/state/store_event_attendance_status_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/unsubscribe_email_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_accept_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/download_all_attachments_for_web_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/export_all_attachments_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_star_email_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/maybe_calendar_event_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_reject_interactor.dart';
|
||||
@@ -109,6 +115,7 @@ import 'package:tmail_ui_user/features/email/presentation/widgets/html_attachmen
|
||||
import 'package:tmail_ui_user/features/email/presentation/widgets/pdf_viewer/pdf_viewer.dart';
|
||||
import 'package:tmail_ui_user/features/email_previewer/email_previewer_dialog_view.dart';
|
||||
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
|
||||
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/action/mailbox_ui_action.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
|
||||
@@ -157,6 +164,8 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
||||
final ParseEmailByBlobIdInteractor _parseEmailByBlobIdInteractor;
|
||||
final PreviewEmailFromEmlFileInteractor _previewEmailFromEmlFileInteractor;
|
||||
final GetHtmlContentFromAttachmentInteractor _getHtmlContentFromAttachmentInteractor;
|
||||
final DownloadAllAttachmentsForWebInteractor _downloadAllAttachmentsForWebInteractor;
|
||||
final ExportAllAttachmentsInteractor _exportAllAttachmentsInteractor;
|
||||
|
||||
CreateNewEmailRuleFilterInteractor? _createNewEmailRuleFilterInteractor;
|
||||
SendReceiptToSenderInteractor? _sendReceiptToSenderInteractor;
|
||||
@@ -213,6 +222,8 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
||||
this._parseEmailByBlobIdInteractor,
|
||||
this._previewEmailFromEmlFileInteractor,
|
||||
this._getHtmlContentFromAttachmentInteractor,
|
||||
this._downloadAllAttachmentsForWebInteractor,
|
||||
this._exportAllAttachmentsInteractor,
|
||||
);
|
||||
|
||||
@override
|
||||
@@ -240,6 +251,8 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
||||
_handleMarkAsEmailReadCompleted(success.readActions);
|
||||
} else if (success is ExportAttachmentSuccess) {
|
||||
_exportAttachmentSuccessAction(success);
|
||||
} else if (success is ExportAllAttachmentsSuccess) {
|
||||
_exportAllAttachmentsSuccessAction(success);
|
||||
} else if (success is MoveToMailboxSuccess) {
|
||||
_moveToMailboxSuccess(success);
|
||||
} else if (success is MarkAsStarEmailSuccess) {
|
||||
@@ -250,6 +263,8 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
||||
_updateAttachmentsViewState(success.attachment.blobId, Right(success));
|
||||
} else if (success is DownloadingAttachmentForWeb) {
|
||||
_updateAttachmentsViewState(success.attachment.blobId, Right(success));
|
||||
} else if (success is DownloadAllAttachmentsForWebSuccess) {
|
||||
mailboxDashBoardController.deleteDownloadTask(success.taskId);
|
||||
} else if (success is GetAllIdentitiesSuccess) {
|
||||
_getAllIdentitiesSuccess(success);
|
||||
} else if (success is SendReceiptToSenderSuccess) {
|
||||
@@ -298,8 +313,12 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
||||
_downloadAttachmentsFailure(failure);
|
||||
} else if (failure is ExportAttachmentFailure) {
|
||||
_exportAttachmentFailureAction(failure);
|
||||
} else if (failure is ExportAllAttachmentsFailure) {
|
||||
_exportAllAttachmentsFailureAction(failure);
|
||||
} else if (failure is DownloadAttachmentForWebFailure) {
|
||||
_downloadAttachmentForWebFailureAction(failure);
|
||||
} else if (failure is DownloadAllAttachmentsForWebFailure) {
|
||||
_downloadAllAttachmentsForWebFailure(failure);
|
||||
} else if (failure is ParseCalendarEventFailure) {
|
||||
_handleParseCalendarEventFailure(failure);
|
||||
} else if (failure is GetEmailContentFailure) {
|
||||
@@ -445,15 +464,44 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
||||
log('SingleEmailController::DownloadingAttachmentForWeb(): $percent%');
|
||||
|
||||
emailSupervisorController.mailboxDashBoardController.updateDownloadTask(
|
||||
success.taskId,
|
||||
(currentTask) {
|
||||
final newTask = currentTask.copyWith(
|
||||
progress: success.progress,
|
||||
downloaded: success.downloaded,
|
||||
total: success.total);
|
||||
success.taskId,
|
||||
(currentTask) {
|
||||
final newTask = currentTask.copyWith(
|
||||
progress: success.progress,
|
||||
downloaded: success.downloaded,
|
||||
total: success.total);
|
||||
|
||||
return newTask;
|
||||
});
|
||||
return newTask;
|
||||
});
|
||||
} else if (success is StartDownloadAllAttachmentsForWeb) {
|
||||
emailSupervisorController.mailboxDashBoardController.addDownloadTask(
|
||||
DownloadTaskState(
|
||||
taskId: success.taskId,
|
||||
attachment: success.attachment,
|
||||
),
|
||||
);
|
||||
|
||||
if (currentOverlayContext != null && currentContext != null) {
|
||||
appToast.showToastMessage(
|
||||
currentOverlayContext!,
|
||||
AppLocalizations.of(currentContext!).your_download_has_started,
|
||||
leadingSVGIconColor: AppColor.primaryColor,
|
||||
leadingSVGIcon: imagePaths.icDownload);
|
||||
}
|
||||
} else if (success is DownloadingAllAttachmentsForWeb) {
|
||||
final percent = success.progress.round();
|
||||
log('SingleEmailController::DownloadingAttachmentForWeb(): $percent%');
|
||||
|
||||
emailSupervisorController.mailboxDashBoardController.updateDownloadTask(
|
||||
success.taskId,
|
||||
(currentTask) {
|
||||
final newTask = currentTask.copyWith(
|
||||
progress: success.progress,
|
||||
downloaded: success.downloaded,
|
||||
total: success.total);
|
||||
|
||||
return newTask;
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -804,11 +852,11 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
||||
|
||||
void exportAttachment(BuildContext context, Attachment attachment) {
|
||||
final cancelToken = CancelToken();
|
||||
_showDownloadingFileDialog(context, attachment, cancelToken: cancelToken);
|
||||
_showDownloadingFileDialog(context, attachment.name ?? '', cancelToken: cancelToken);
|
||||
_exportAttachmentAction(attachment, cancelToken);
|
||||
}
|
||||
|
||||
void _showDownloadingFileDialog(BuildContext context, Attachment attachment, {CancelToken? cancelToken}) {
|
||||
void _showDownloadingFileDialog(BuildContext context, String attachmentName, {CancelToken? cancelToken}) {
|
||||
if (cancelToken != null) {
|
||||
showCupertinoDialog(
|
||||
context: context,
|
||||
@@ -816,7 +864,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
||||
PointerInterceptor(child: (DownloadingFileDialogBuilder()
|
||||
..key(const Key('downloading_file_dialog'))
|
||||
..title(AppLocalizations.of(context).preparing_to_export)
|
||||
..content(AppLocalizations.of(context).downloading_file(attachment.name ?? ''))
|
||||
..content(AppLocalizations.of(context).downloading_file(attachmentName))
|
||||
..actionText(AppLocalizations.of(context).cancel)
|
||||
..addCancelDownloadActionClick(() {
|
||||
cancelToken.cancel([AppLocalizations.of(context).user_cancel_download_file]);
|
||||
@@ -830,7 +878,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
||||
PointerInterceptor(child: (DownloadingFileDialogBuilder()
|
||||
..key(const Key('downloading_file_for_web_dialog'))
|
||||
..title(AppLocalizations.of(context).preparing_to_save)
|
||||
..content(AppLocalizations.of(context).downloading_file(attachment.name ?? '')))
|
||||
..content(AppLocalizations.of(context).downloading_file(attachmentName)))
|
||||
.build()));
|
||||
}
|
||||
}
|
||||
@@ -854,6 +902,36 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
||||
}
|
||||
}
|
||||
|
||||
void exportAllAttachments(BuildContext context, String outputFileName) {
|
||||
final cancelToken = CancelToken();
|
||||
_showDownloadingFileDialog(context, outputFileName, cancelToken: cancelToken);
|
||||
_exportAllAttachmentsAction(outputFileName, cancelToken);
|
||||
}
|
||||
|
||||
void _exportAllAttachmentsAction(String outputFileName, CancelToken cancelToken) {
|
||||
if (accountId == null || session == null) {
|
||||
consumeState(Stream.value(Left(ExportAllAttachmentsFailure(
|
||||
exception: NotFoundSessionException(),
|
||||
))));
|
||||
return;
|
||||
}
|
||||
|
||||
final downloadAllSupported = session!.isDownloadAllSupported(accountId);
|
||||
if (!downloadAllSupported || _currentEmailId == null) {
|
||||
consumeState(Stream.value(Left(ExportAllAttachmentsFailure())));
|
||||
return;
|
||||
}
|
||||
|
||||
final baseDownloadAllUrl = session!.getDownloadAllCapability(accountId)!.endpoint!;
|
||||
consumeState(_exportAllAttachmentsInteractor.execute(
|
||||
accountId!,
|
||||
_currentEmailId!,
|
||||
baseDownloadAllUrl,
|
||||
outputFileName,
|
||||
cancelToken: cancelToken,
|
||||
));
|
||||
}
|
||||
|
||||
void _exportAttachmentFailureAction(ExportAttachmentFailure failure) {
|
||||
if (failure.exception is! CancelDownloadFileException) {
|
||||
popBack();
|
||||
@@ -871,6 +949,23 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
||||
_openDownloadedPreviewWorkGroupDocument(success.downloadedResponse);
|
||||
}
|
||||
|
||||
void _exportAllAttachmentsFailureAction(ExportAllAttachmentsFailure failure) {
|
||||
if (failure.exception is! CancelDownloadFileException) {
|
||||
popBack();
|
||||
|
||||
if (currentOverlayContext != null && currentContext != null) {
|
||||
appToast.showToastErrorMessage(
|
||||
currentOverlayContext!,
|
||||
AppLocalizations.of(currentContext!).attachment_download_failed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _exportAllAttachmentsSuccessAction(ExportAllAttachmentsSuccess success) {
|
||||
popBack();
|
||||
_saveDownloadedZipAttachments(success.downloadedResponse.filePath);
|
||||
}
|
||||
|
||||
void _openDownloadedPreviewWorkGroupDocument(DownloadedResponse downloadedResponse) async {
|
||||
log('SingleEmailController::_openDownloadedPreviewWorkGroupDocument(): $downloadedResponse');
|
||||
if (downloadedResponse.mediaType == null) {
|
||||
@@ -892,6 +987,11 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _saveDownloadedZipAttachments(String filePath) async {
|
||||
final params = SaveFileDialogParams(sourceFilePath: filePath);
|
||||
await FlutterFileDialog.saveFile(params: params);
|
||||
}
|
||||
|
||||
void downloadAttachmentForWeb(Attachment attachment) {
|
||||
if (accountId != null && session != null) {
|
||||
final generateTaskId = DownloadTaskId(uuid.v4());
|
||||
@@ -921,6 +1021,65 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
||||
}
|
||||
}
|
||||
|
||||
void downloadAllAttachmentsForWeb(String outputFileName) {
|
||||
final taskId = DownloadTaskId(uuid.v4());
|
||||
final session = this.session;
|
||||
final accountId = this.accountId;
|
||||
|
||||
if (accountId == null || session == null) {
|
||||
consumeState(Stream.value(Left(DownloadAllAttachmentsForWebFailure(
|
||||
exception: NotFoundSessionException(),
|
||||
taskId: taskId,
|
||||
))));
|
||||
return;
|
||||
}
|
||||
|
||||
final downloadAllSupported = session.isDownloadAllSupported(accountId);
|
||||
final emailId = _currentEmailId;
|
||||
|
||||
if (!downloadAllSupported || emailId == null) {
|
||||
consumeState(Stream.value(Left(DownloadAllAttachmentsForWebFailure(
|
||||
taskId: taskId,
|
||||
))));
|
||||
return;
|
||||
}
|
||||
|
||||
final baseDownloadAllUrl = session.getDownloadAllCapability(accountId)!.endpoint!;
|
||||
final downloadAttachment = Attachment(
|
||||
name: outputFileName,
|
||||
type: MediaType('application', 'zip'),
|
||||
);
|
||||
final cancelToken = CancelToken();
|
||||
consumeState(_downloadAllAttachmentsForWebInteractor.execute(
|
||||
accountId,
|
||||
emailId,
|
||||
baseDownloadAllUrl,
|
||||
downloadAttachment,
|
||||
taskId,
|
||||
_downloadProgressStateController,
|
||||
cancelToken: cancelToken,
|
||||
));
|
||||
}
|
||||
|
||||
bool isDownloadAllSupported() {
|
||||
return session?.isDownloadAllSupported(accountId) ?? false;
|
||||
}
|
||||
|
||||
bool downloadAllButtonIsEnabled() {
|
||||
return isDownloadAllSupported() && attachments.length > 1;
|
||||
}
|
||||
|
||||
void _downloadAllAttachmentsForWebFailure(
|
||||
DownloadAllAttachmentsForWebFailure failure,
|
||||
) {
|
||||
mailboxDashBoardController.deleteDownloadTask(failure.taskId);
|
||||
if (currentOverlayContext != null && currentContext != null) {
|
||||
appToast.showToastErrorMessage(
|
||||
currentOverlayContext!,
|
||||
AppLocalizations.of(currentContext!).attachment_download_failed);
|
||||
}
|
||||
}
|
||||
|
||||
void _downloadAttachmentForWebSuccessAction(DownloadAttachmentForWebSuccess success) {
|
||||
log('SingleEmailController::_downloadAttachmentForWebSuccessAction():');
|
||||
|
||||
@@ -1675,6 +1834,10 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
||||
..onCloseButtonAction(() => popBack())
|
||||
..onDownloadAttachmentFileAction((attachment) => handleDownloadAttachmentAction(context, attachment))
|
||||
..onViewAttachmentFileAction((attachment) => handleViewAttachmentAction(context, attachment))
|
||||
..onDownloadAllButtonAction(isDownloadAllSupported()
|
||||
? () => downloadAllAttachmentsForWeb('TwakeMail-${DateTime.now()}')
|
||||
: null
|
||||
)
|
||||
).show();
|
||||
} else {
|
||||
Get.dialog(
|
||||
@@ -1688,6 +1851,9 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
||||
onCloseButtonAction: () => popBack(),
|
||||
onDownloadAttachmentFileAction: (attachment) => handleDownloadAttachmentAction(context, attachment),
|
||||
onViewAttachmentFileAction: (attachment) => handleViewAttachmentAction(context, attachment),
|
||||
onDownloadAllButtonAction: isDownloadAllSupported()
|
||||
? () => downloadAllAttachmentsForWeb('TwakeMail-${DateTime.now()}')
|
||||
: null,
|
||||
)
|
||||
),
|
||||
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
|
||||
@@ -1995,6 +2161,19 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
||||
}
|
||||
}
|
||||
|
||||
void handleDownloadAllAttachmentsAction(
|
||||
BuildContext context,
|
||||
String outputFileName,
|
||||
) {
|
||||
if (PlatformInfo.isWeb) {
|
||||
downloadAllAttachmentsForWeb(outputFileName);
|
||||
} else if (PlatformInfo.isMobile) {
|
||||
exportAllAttachments(context, outputFileName);
|
||||
} else {
|
||||
log('EmailView::handleDownloadAllAttachmentsAction: THE PLATFORM IS SUPPORTED');
|
||||
}
|
||||
}
|
||||
|
||||
void handleViewAttachmentAction(BuildContext context, Attachment attachment) {
|
||||
if (PlatformInfo.isWeb && PlatformInfo.isCanvasKit && attachment.isPDFFile) {
|
||||
previewPDFFileAction(context, attachment);
|
||||
|
||||
@@ -339,6 +339,8 @@ class EmailView extends GetWidget<SingleEmailController> {
|
||||
downloadAttachmentAction: (attachment) => controller.handleDownloadAttachmentAction(context, attachment),
|
||||
viewAttachmentAction: (attachment) => controller.handleViewAttachmentAction(context, attachment),
|
||||
onTapShowAllAttachmentFile: () => controller.openAttachmentList(context, controller.attachments),
|
||||
showDownloadAllAttachmentsButton: controller.downloadAllButtonIsEnabled(),
|
||||
onTapDownloadAllButton: () => controller.handleDownloadAllAttachmentsAction(context, 'TwakeMail-${DateTime.now()}'),
|
||||
);
|
||||
} else {
|
||||
return const SizedBox.shrink();
|
||||
|
||||
@@ -42,9 +42,9 @@ class AttachmentListStyles {
|
||||
static const Color scrollbarThumbColor = AppColor.colorScrollbarThumbColor;
|
||||
static const Color scrollbarTrackBorderColor = Colors.transparent;
|
||||
static const Color separatorColor = AppColor.colorAttachmentBorder;
|
||||
static const Color downloadAllButtonColor = AppColor.primaryColor;
|
||||
static const Color downloadAllButtonColor = Colors.transparent;
|
||||
static const Color cancelButtonColor = Colors.transparent;
|
||||
static const Color downloadAllButtonTextColor = Colors.white;
|
||||
static const Color downloadAllButtonTextColor = AppColor.textButtonColor;
|
||||
static const Color cancelButtonTextColor = AppColor.primaryColor;
|
||||
static const Color cancelButtonBorderColor = AppColor.colorButtonBorder;
|
||||
static const Color barrierColor = AppColor.colorDefaultCupertinoActionSheet;
|
||||
|
||||
@@ -5,16 +5,16 @@ import 'package:flutter/material.dart';
|
||||
class EmailAttachmentsStyles {
|
||||
static const double headerTextSize = 15;
|
||||
static const double headerIconSize = 20;
|
||||
static const double headerSpace = 4;
|
||||
static const double headerSpace = 8;
|
||||
static const double marginHeader = 6;
|
||||
static const double buttonTextSize = 13;
|
||||
static const double buttonTextSize = 16;
|
||||
static const double buttonMoreAttachmentsTextSize = 14;
|
||||
static const double buttonBorderRadius = 8;
|
||||
static const double listSpace = 8;
|
||||
|
||||
static const Color headerTextColor = AppColor.colorTitleHeaderAttachment;
|
||||
static const Color headerIconColor = AppColor.colorAttachmentIcon;
|
||||
static const Color buttonTextColor = AppColor.primaryColor;
|
||||
static const Color buttonTextColor = AppColor.colorTextBody;
|
||||
static const Color buttonMoreAttachmentsTextColor = AppColor.colorLabelMoreAttachmentsButton;
|
||||
|
||||
static const FontWeight headerFontWeight = FontWeight.w400;
|
||||
@@ -24,4 +24,5 @@ class EmailAttachmentsStyles {
|
||||
static const EdgeInsetsGeometry padding = EdgeInsetsDirectional.only(start: 16, end: 16, bottom: 12);
|
||||
static const EdgeInsetsGeometry buttonPadding = EdgeInsets.symmetric(vertical: 8, horizontal: 12);
|
||||
static const EdgeInsetsGeometry moreButtonMargin = EdgeInsetsDirectional.only(bottom: 2, start: 8);
|
||||
static const attachmentsInfoPadding = EdgeInsetsDirectional.only(end: 8);
|
||||
}
|
||||
+9
-23
@@ -1,11 +1,10 @@
|
||||
import 'package:core/presentation/views/button/icon_button_web.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/styles/attachment/attachment_list_styles.dart';
|
||||
|
||||
class AttachmentListActionButtonBuilder extends StatelessWidget {
|
||||
final String? name;
|
||||
final TextStyle? textStyle;
|
||||
final Color? bgColor;
|
||||
final Color? borderColor;
|
||||
final Function? action;
|
||||
|
||||
const AttachmentListActionButtonBuilder({
|
||||
@@ -13,32 +12,19 @@ class AttachmentListActionButtonBuilder extends StatelessWidget {
|
||||
this.name,
|
||||
this.textStyle,
|
||||
this.bgColor,
|
||||
this.borderColor,
|
||||
this.action
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: () => action?.call(),
|
||||
child: Container(
|
||||
padding: AttachmentListStyles.buttonsPadding,
|
||||
decoration: BoxDecoration(
|
||||
color: bgColor,
|
||||
borderRadius: AttachmentListStyles.buttonRadius,
|
||||
border: Border.all(
|
||||
width: borderColor != null ? AttachmentListStyles.buttonBorderWidth : 0,
|
||||
color: borderColor ?? Colors.transparent
|
||||
)
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
name ?? '',
|
||||
textAlign: TextAlign.center,
|
||||
style: textStyle
|
||||
),
|
||||
),
|
||||
),
|
||||
return buildButtonWrapText(
|
||||
name ?? '',
|
||||
radius: 10,
|
||||
height: 44,
|
||||
textStyle: textStyle,
|
||||
bgColor: bgColor,
|
||||
onTap: () => action?.call(),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-4
@@ -1,3 +1,4 @@
|
||||
import 'package:core/presentation/extensions/color_extension.dart';
|
||||
import 'package:core/presentation/resources/image_paths.dart';
|
||||
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -118,13 +119,16 @@ class AttachmentListBottomSheetBodyBuilder extends StatelessWidget {
|
||||
Padding(
|
||||
padding: AttachmentListStyles.actionButtonsRowPadding,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
if (onDownloadAllButtonAction != null)
|
||||
AttachmentListActionButtonBuilder(
|
||||
name: AppLocalizations.of(context).downloadAll,
|
||||
bgColor: AttachmentListStyles.downloadAllButtonColor,
|
||||
textStyle: AttachmentListStyles.downloadAllButtonTextStyle
|
||||
bgColor: AppColor.primaryColor,
|
||||
textStyle: AttachmentListStyles.downloadAllButtonTextStyle.copyWith(
|
||||
color: Colors.white,
|
||||
),
|
||||
action: onDownloadAllButtonAction,
|
||||
),
|
||||
if (onDownloadAllButtonAction != null && onCancelButtonAction != null)
|
||||
const SizedBox(width: AttachmentListStyles.buttonsSpaceBetween),
|
||||
@@ -132,7 +136,6 @@ class AttachmentListBottomSheetBodyBuilder extends StatelessWidget {
|
||||
AttachmentListActionButtonBuilder(
|
||||
name: AppLocalizations.of(context).close,
|
||||
bgColor: AttachmentListStyles.cancelButtonColor,
|
||||
borderColor: AttachmentListStyles.cancelButtonBorderColor,
|
||||
textStyle: AttachmentListStyles.cancelButtonTextStyle
|
||||
)
|
||||
],
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ class AttachmentListBottomSheetBuilder {
|
||||
_statusBarHeight = Get.statusBarHeight / MediaQuery.of(_context).devicePixelRatio;
|
||||
}
|
||||
|
||||
void onDownloadAllButtonAction(OnDownloadAllButtonAction onDownloadAllButtonAction) {
|
||||
void onDownloadAllButtonAction(OnDownloadAllButtonAction? onDownloadAllButtonAction) {
|
||||
_onDownloadAllButtonAction = onDownloadAllButtonAction;
|
||||
}
|
||||
|
||||
|
||||
+7
-4
@@ -1,3 +1,4 @@
|
||||
import 'package:core/presentation/extensions/color_extension.dart';
|
||||
import 'package:core/presentation/resources/image_paths.dart';
|
||||
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -124,13 +125,16 @@ class AttachmentListDialogBodyBuilder extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
if (onDownloadAllButtonAction != null)
|
||||
AttachmentListActionButtonBuilder(
|
||||
name: AppLocalizations.of(context).downloadAll,
|
||||
bgColor: AttachmentListStyles.downloadAllButtonColor,
|
||||
textStyle: AttachmentListStyles.downloadAllButtonTextStyle
|
||||
bgColor: AppColor.primaryColor,
|
||||
textStyle: AttachmentListStyles.downloadAllButtonTextStyle.copyWith(
|
||||
color: Colors.white,
|
||||
),
|
||||
action: onDownloadAllButtonAction,
|
||||
),
|
||||
if (onDownloadAllButtonAction != null && onCancelButtonAction != null)
|
||||
const SizedBox(width: AttachmentListStyles.buttonsSpaceBetween),
|
||||
@@ -138,7 +142,6 @@ class AttachmentListDialogBodyBuilder extends StatelessWidget {
|
||||
AttachmentListActionButtonBuilder(
|
||||
name: AppLocalizations.of(context).close,
|
||||
bgColor: AttachmentListStyles.cancelButtonColor,
|
||||
borderColor: AttachmentListStyles.cancelButtonBorderColor,
|
||||
textStyle: AttachmentListStyles.cancelButtonTextStyle
|
||||
)
|
||||
],
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import 'package:core/presentation/extensions/color_extension.dart';
|
||||
import 'package:core/presentation/resources/image_paths.dart';
|
||||
import 'package:core/presentation/utils/responsive_utils.dart';
|
||||
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
||||
import 'package:core/utils/platform_info.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/styles/email_attachments_styles.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
|
||||
class AttachmentsInfo extends StatelessWidget {
|
||||
const AttachmentsInfo({
|
||||
super.key,
|
||||
required this.imagePaths,
|
||||
required this.numberOfAttachments,
|
||||
required this.totalSizeInfo,
|
||||
required this.responsiveUtils,
|
||||
this.onTapShowAllAttachmentFile,
|
||||
this.downloadAllEnabled = false,
|
||||
this.onTapDownloadAllButton,
|
||||
});
|
||||
|
||||
final ImagePaths imagePaths;
|
||||
final int numberOfAttachments;
|
||||
final String totalSizeInfo;
|
||||
final ResponsiveUtils responsiveUtils;
|
||||
final VoidCallback? onTapShowAllAttachmentFile;
|
||||
final bool downloadAllEnabled;
|
||||
final VoidCallback? onTapDownloadAllButton;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<Widget> attachmentsAmountAndSize = [
|
||||
SvgPicture.asset(
|
||||
imagePaths.icAttachment,
|
||||
width: EmailAttachmentsStyles.headerIconSize,
|
||||
height: EmailAttachmentsStyles.headerIconSize,
|
||||
colorFilter: EmailAttachmentsStyles.headerIconColor.asFilter(),
|
||||
fit: BoxFit.fill
|
||||
),
|
||||
const SizedBox(width: EmailAttachmentsStyles.headerSpace),
|
||||
Text(
|
||||
AppLocalizations.of(context).titleHeaderAttachment(
|
||||
numberOfAttachments,
|
||||
totalSizeInfo,
|
||||
),
|
||||
style: const TextStyle(
|
||||
fontSize: EmailAttachmentsStyles.headerTextSize,
|
||||
fontWeight: EmailAttachmentsStyles.headerFontWeight,
|
||||
color: EmailAttachmentsStyles.headerTextColor
|
||||
)
|
||||
),
|
||||
];
|
||||
|
||||
final showAllAttachments = (numberOfAttachments > 2)
|
||||
? TMailButtonWidget(
|
||||
text: AppLocalizations.of(context).showAll,
|
||||
backgroundColor: Colors.transparent,
|
||||
borderRadius: EmailAttachmentsStyles.buttonBorderRadius,
|
||||
padding: EmailAttachmentsStyles.buttonPadding,
|
||||
textStyle: const TextStyle(
|
||||
fontSize: EmailAttachmentsStyles.buttonTextSize,
|
||||
color: EmailAttachmentsStyles.buttonTextColor,
|
||||
fontWeight: EmailAttachmentsStyles.buttonFontWeight
|
||||
),
|
||||
onTapActionCallback: onTapShowAllAttachmentFile,
|
||||
)
|
||||
: const SizedBox.shrink();
|
||||
|
||||
List<Widget> downloadAllAttachments = downloadAllEnabled && !responsiveUtils.isMobile(context)
|
||||
? [
|
||||
const Spacer(),
|
||||
TMailButtonWidget(
|
||||
text: AppLocalizations.of(context).downloadAll,
|
||||
icon: imagePaths.icDownloadAll,
|
||||
iconAlignment: TextDirection.rtl,
|
||||
backgroundColor: Colors.transparent,
|
||||
borderRadius: EmailAttachmentsStyles.buttonBorderRadius,
|
||||
padding: EmailAttachmentsStyles.buttonPadding,
|
||||
textStyle: const TextStyle(
|
||||
fontSize: EmailAttachmentsStyles.buttonTextSize,
|
||||
color: EmailAttachmentsStyles.buttonTextColor,
|
||||
fontWeight: EmailAttachmentsStyles.buttonFontWeight
|
||||
),
|
||||
onTapActionCallback: onTapDownloadAllButton,
|
||||
),
|
||||
]
|
||||
: const [];
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
...attachmentsAmountAndSize,
|
||||
if (!PlatformInfo.isWeb) const Spacer(),
|
||||
showAllAttachments,
|
||||
...downloadAllAttachments,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
import 'package:core/presentation/action/action_callback_define.dart';
|
||||
import 'package:core/presentation/extensions/color_extension.dart';
|
||||
import 'package:core/presentation/resources/image_paths.dart';
|
||||
import 'package:core/presentation/utils/responsive_utils.dart';
|
||||
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
||||
import 'package:core/utils/platform_info.dart';
|
||||
import 'package:filesize/filesize.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:model/email/attachment.dart';
|
||||
import 'package:model/extensions/list_attachment_extension.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/styles/email_attachments_styles.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/widgets/attachment_item_widget.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/widgets/attachments_info.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/widgets/draggable_attachment_item_widget.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
|
||||
@@ -24,6 +23,8 @@ class EmailAttachmentsWidget extends StatelessWidget {
|
||||
final ResponsiveUtils responsiveUtils;
|
||||
final ImagePaths imagePaths;
|
||||
final OnTapActionCallback? onTapShowAllAttachmentFile;
|
||||
final bool showDownloadAllAttachmentsButton;
|
||||
final OnTapActionCallback? onTapDownloadAllButton;
|
||||
|
||||
const EmailAttachmentsWidget({
|
||||
super.key,
|
||||
@@ -35,6 +36,8 @@ class EmailAttachmentsWidget extends StatelessWidget {
|
||||
this.downloadAttachmentAction,
|
||||
this.viewAttachmentAction,
|
||||
this.onTapShowAllAttachmentFile,
|
||||
this.showDownloadAllAttachmentsButton = false,
|
||||
this.onTapDownloadAllButton,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -46,51 +49,14 @@ class EmailAttachmentsWidget extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(width: EmailAttachmentsStyles.headerSpace),
|
||||
SvgPicture.asset(
|
||||
imagePaths.icAttachment,
|
||||
width: EmailAttachmentsStyles.headerIconSize,
|
||||
height: EmailAttachmentsStyles.headerIconSize,
|
||||
colorFilter: EmailAttachmentsStyles.headerIconColor.asFilter(),
|
||||
fit: BoxFit.fill
|
||||
),
|
||||
const SizedBox(width: EmailAttachmentsStyles.headerSpace),
|
||||
Expanded(
|
||||
child: Text(
|
||||
AppLocalizations.of(context).titleHeaderAttachment(
|
||||
attachments.length,
|
||||
filesize(attachments.totalSize, 1)
|
||||
),
|
||||
style: const TextStyle(
|
||||
fontSize: EmailAttachmentsStyles.headerTextSize,
|
||||
fontWeight: EmailAttachmentsStyles.headerFontWeight,
|
||||
color: EmailAttachmentsStyles.headerTextColor
|
||||
)
|
||||
)
|
||||
),
|
||||
const SizedBox(width: EmailAttachmentsStyles.headerSpace),
|
||||
]
|
||||
)
|
||||
),
|
||||
if (attachments.length > 2)
|
||||
TMailButtonWidget(
|
||||
text: AppLocalizations.of(context).showAll,
|
||||
backgroundColor: Colors.transparent,
|
||||
borderRadius: EmailAttachmentsStyles.buttonBorderRadius,
|
||||
padding: EmailAttachmentsStyles.buttonPadding,
|
||||
textStyle: const TextStyle(
|
||||
fontSize: EmailAttachmentsStyles.buttonTextSize,
|
||||
color: EmailAttachmentsStyles.buttonTextColor,
|
||||
fontWeight: EmailAttachmentsStyles.buttonFontWeight
|
||||
),
|
||||
onTapActionCallback: onTapShowAllAttachmentFile,
|
||||
)
|
||||
],
|
||||
AttachmentsInfo(
|
||||
imagePaths: imagePaths,
|
||||
numberOfAttachments: attachments.length,
|
||||
totalSizeInfo: filesize(attachments.totalSize, 1),
|
||||
responsiveUtils: responsiveUtils,
|
||||
onTapShowAllAttachmentFile: onTapShowAllAttachmentFile,
|
||||
downloadAllEnabled: showDownloadAllAttachmentsButton,
|
||||
onTapDownloadAllButton: onTapDownloadAllButton,
|
||||
),
|
||||
const SizedBox(height: EmailAttachmentsStyles.marginHeader),
|
||||
Row(
|
||||
@@ -121,7 +87,7 @@ class EmailAttachmentsWidget extends StatelessWidget {
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
if (hideItemsCount > 0)
|
||||
if (hideItemsCount > 0 && !responsiveUtils.isMobile(context))
|
||||
TMailButtonWidget(
|
||||
text: AppLocalizations.of(context).moreAttachments(hideItemsCount),
|
||||
backgroundColor: Colors.transparent,
|
||||
@@ -137,6 +103,42 @@ class EmailAttachmentsWidget extends StatelessWidget {
|
||||
),
|
||||
]
|
||||
),
|
||||
const SizedBox(height: EmailAttachmentsStyles.marginHeader),
|
||||
if (responsiveUtils.isMobile(context))
|
||||
Row(
|
||||
children: [
|
||||
if (hideItemsCount > 0)
|
||||
TMailButtonWidget(
|
||||
text: AppLocalizations.of(context).moreAttachments(hideItemsCount),
|
||||
backgroundColor: Colors.transparent,
|
||||
borderRadius: EmailAttachmentsStyles.buttonBorderRadius,
|
||||
padding: EdgeInsets.zero,
|
||||
margin: EmailAttachmentsStyles.moreButtonMargin,
|
||||
textStyle: const TextStyle(
|
||||
fontSize: EmailAttachmentsStyles.buttonMoreAttachmentsTextSize,
|
||||
color: EmailAttachmentsStyles.buttonMoreAttachmentsTextColor,
|
||||
fontWeight: EmailAttachmentsStyles.buttonMoreAttachmentsFontWeight,
|
||||
),
|
||||
onTapActionCallback: onTapShowAllAttachmentFile,
|
||||
),
|
||||
const Spacer(),
|
||||
if (showDownloadAllAttachmentsButton)
|
||||
TMailButtonWidget(
|
||||
text: AppLocalizations.of(context).downloadAll,
|
||||
icon: imagePaths.icDownloadAll,
|
||||
iconAlignment: TextDirection.rtl,
|
||||
backgroundColor: Colors.transparent,
|
||||
borderRadius: EmailAttachmentsStyles.buttonBorderRadius,
|
||||
padding: EmailAttachmentsStyles.buttonPadding,
|
||||
textStyle: const TextStyle(
|
||||
fontSize: EmailAttachmentsStyles.buttonTextSize,
|
||||
color: EmailAttachmentsStyles.buttonTextColor,
|
||||
fontWeight: EmailAttachmentsStyles.buttonFontWeight
|
||||
),
|
||||
onTapActionCallback: onTapDownloadAllButton,
|
||||
),
|
||||
]
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -12,18 +12,22 @@ import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart
|
||||
import 'package:jmap_dart_client/jmap/core/capability/capability_properties.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
|
||||
import 'package:model/download_all/download_all_capability.dart';
|
||||
import 'package:model/model.dart';
|
||||
import 'package:tmail_ui_user/features/home/data/model/session_hive_obj.dart';
|
||||
import 'package:tmail_ui_user/features/home/domain/converter/session_account_converter.dart';
|
||||
import 'package:tmail_ui_user/features/home/domain/converter/session_capabilities_converter.dart';
|
||||
import 'package:tmail_ui_user/features/home/domain/converter/session_primary_account_converter.dart';
|
||||
import 'package:tmail_ui_user/main/error/capability_validator.dart';
|
||||
|
||||
extension SessionExtensions on Session {
|
||||
static final CapabilityIdentifier linagoraContactSupportCapability = CapabilityIdentifier(Uri.parse('com:linagora:params:jmap:contact:support'));
|
||||
static final CapabilityIdentifier linagoraDownloadAllCapability = CapabilityIdentifier(Uri.parse('com:linagora:params:downloadAll'));
|
||||
|
||||
static final Map<CapabilityIdentifier, CapabilityProperties Function(Map<String, dynamic>)> customMapCapabilitiesConverter = {
|
||||
linagoraContactSupportCapability: ContactSupportCapability.deserialize,
|
||||
tmailContactCapabilityIdentifier: AutocompleteCapability.deserialize,
|
||||
linagoraDownloadAllCapability: DownloadAllCapability.deserialize,
|
||||
};
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
@@ -94,4 +98,21 @@ extension SessionExtensions on Session {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
bool isDownloadAllSupported(AccountId? accountId) {
|
||||
if (accountId == null) return false;
|
||||
final isSupported = linagoraDownloadAllCapability.isSupported(this, accountId);
|
||||
if (!isSupported) return false;
|
||||
|
||||
final downloadAllCapability = getDownloadAllCapability(accountId);
|
||||
return downloadAllCapability?.endpoint?.isNotEmpty ?? false;
|
||||
}
|
||||
|
||||
DownloadAllCapability? getDownloadAllCapability(AccountId? accountId) {
|
||||
if (accountId == null) return null;
|
||||
return getCapabilityProperties<DownloadAllCapability>(
|
||||
accountId,
|
||||
linagoraDownloadAllCapability,
|
||||
);
|
||||
}
|
||||
}
|
||||
+10
-2
@@ -76,8 +76,8 @@ class DownloadTaskItemWidget extends StatelessWidget {
|
||||
DefaultTextStyle(
|
||||
style: const TextStyle(color: Colors.white, fontSize: 12),
|
||||
child: Text(
|
||||
'${ByteConverter(taskState.downloaded.toDouble()).toHumanReadable(SizeUnit.MB)}/'
|
||||
'${ByteConverter(taskState.total.toDouble()).toHumanReadable(SizeUnit.MB)}',
|
||||
'${ByteConverter(taskState.downloaded.toDouble()).toHumanReadable(SizeUnit.MB)}'
|
||||
'${_getTotalSizeText()}',
|
||||
softWrap: CommonTextStyle.defaultSoftWrap,
|
||||
overflow: CommonTextStyle.defaultTextOverFlow,
|
||||
maxLines: 1,
|
||||
@@ -96,4 +96,12 @@ class DownloadTaskItemWidget extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _getTotalSizeText() {
|
||||
if (taskState.total == 0) {
|
||||
return '';
|
||||
} else {
|
||||
return '/${ByteConverter(taskState.total.toDouble()).toHumanReadable(SizeUnit.MB)}';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import 'package:jmap_dart_client/jmap/core/capability/capability_properties.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'download_all_capability.g.dart';
|
||||
|
||||
@JsonSerializable(includeIfNull: false)
|
||||
class DownloadAllCapability extends CapabilityProperties {
|
||||
DownloadAllCapability({this.endpoint});
|
||||
|
||||
final String? endpoint;
|
||||
|
||||
factory DownloadAllCapability.fromJson(Map<String, dynamic> json) => _$DownloadAllCapabilityFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$DownloadAllCapabilityToJson(this);
|
||||
|
||||
factory DownloadAllCapability.deserialize(Map<String, dynamic> json) => _$DownloadAllCapabilityFromJson(json);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [endpoint];
|
||||
}
|
||||
+9
-1
@@ -775,6 +775,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.2"
|
||||
flutter_file_dialog:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_file_dialog
|
||||
sha256: "9344b8f07be6a1b6f9854b723fb0cf84a8094ba94761af1d213589d3cb087488"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
flutter_image_compress:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -2095,7 +2103,7 @@ packages:
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
uri:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: uri
|
||||
sha256: "889eea21e953187c6099802b7b4cf5219ba8f3518f604a1033064d45b1b8268a"
|
||||
|
||||
@@ -257,6 +257,10 @@ dependencies:
|
||||
|
||||
win32: 5.5.1
|
||||
|
||||
uri: 1.0.0
|
||||
|
||||
flutter_file_dialog: 3.0.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
@@ -28,6 +28,8 @@ import 'package:tmail_ui_user/features/email/domain/model/event_action.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/calendar_event_accept_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/parse_calendar_event_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_accept_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/download_all_attachments_for_web_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/export_all_attachments_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/get_html_content_from_attachment_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/maybe_calendar_event_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_reject_interactor.dart';
|
||||
@@ -110,6 +112,8 @@ const fallbackGenerators = {
|
||||
MockSpec<DioClient>(),
|
||||
MockSpec<GetHtmlContentFromAttachmentInteractor>(),
|
||||
MockSpec<TwakeAppManager>(),
|
||||
MockSpec<DownloadAllAttachmentsForWebInteractor>(),
|
||||
MockSpec<ExportAllAttachmentsInteractor>(),
|
||||
])
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
@@ -149,6 +153,9 @@ void main() {
|
||||
final getHtmlContentFromAttachmentInteractor = MockGetHtmlContentFromAttachmentInteractor();
|
||||
final mockTwakeAppManager = MockTwakeAppManager();
|
||||
|
||||
final downloadAllAttachmentsForWebInteractor = MockDownloadAllAttachmentsForWebInteractor();
|
||||
final exportAllAttachmentsInteractor = MockExportAllAttachmentsInteractor();
|
||||
|
||||
late SingleEmailController singleEmailController;
|
||||
|
||||
final testAccountId = AccountId(Id('123'));
|
||||
@@ -206,6 +213,8 @@ void main() {
|
||||
parseEmailByBlobIdInteractor,
|
||||
previewEmailFromEmlFileInteractor,
|
||||
getHtmlContentFromAttachmentInteractor,
|
||||
downloadAllAttachmentsForWebInteractor,
|
||||
exportAllAttachmentsInteractor,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/state.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/user_name.dart';
|
||||
import 'package:model/download_all/download_all_capability.dart';
|
||||
import 'package:model/support/contact_support_capability.dart';
|
||||
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
|
||||
|
||||
@@ -202,4 +203,227 @@ void main() {
|
||||
expect(result, isNull);
|
||||
});
|
||||
});
|
||||
|
||||
group('isDownloadAllSupported::test', () {
|
||||
test('SHOULD return false WHEN AccountId is null', () {
|
||||
// Arrange
|
||||
final session = Session(
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
UserName(''),
|
||||
Uri(),
|
||||
Uri(),
|
||||
Uri(),
|
||||
Uri(),
|
||||
State(''),
|
||||
);
|
||||
|
||||
// Act
|
||||
final result = session.isDownloadAllSupported(null);
|
||||
|
||||
// Assert
|
||||
expect(result, isFalse);
|
||||
});
|
||||
|
||||
test('SHOULD return false WHEN isSupported is false', () {
|
||||
// Arrange
|
||||
final session = Session(
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
UserName(''),
|
||||
Uri(),
|
||||
Uri(),
|
||||
Uri(),
|
||||
Uri(),
|
||||
State(''),
|
||||
);
|
||||
|
||||
// Act
|
||||
final result = session.isDownloadAllSupported(AccountFixtures.aliceAccountId);
|
||||
|
||||
// Assert
|
||||
expect(result, isFalse);
|
||||
});
|
||||
|
||||
test('SHOULD return false WHEN isSupported is true but endpoint is null', () {
|
||||
// Arrange
|
||||
final session = Session(
|
||||
{},
|
||||
{
|
||||
AccountFixtures.aliceAccountId: Account(
|
||||
AccountName('Alice'),
|
||||
true,
|
||||
false,
|
||||
{
|
||||
SessionExtensions.linagoraDownloadAllCapability: DownloadAllCapability()
|
||||
},
|
||||
)
|
||||
},
|
||||
{},
|
||||
UserName(''),
|
||||
Uri(),
|
||||
Uri(),
|
||||
Uri(),
|
||||
Uri(),
|
||||
State(''),
|
||||
);
|
||||
|
||||
// Act
|
||||
final result = session.isDownloadAllSupported(AccountFixtures.aliceAccountId);
|
||||
|
||||
// Assert
|
||||
expect(result, isFalse);
|
||||
});
|
||||
|
||||
test('SHOULD return false WHEN isSupported is true but endpoint is empty', () {
|
||||
// Arrange
|
||||
final session = Session(
|
||||
{},
|
||||
{
|
||||
AccountFixtures.aliceAccountId: Account(
|
||||
AccountName('Alice'),
|
||||
true,
|
||||
false,
|
||||
{
|
||||
SessionExtensions.linagoraDownloadAllCapability: DownloadAllCapability(endpoint: '')
|
||||
},
|
||||
)
|
||||
},
|
||||
{},
|
||||
UserName(''),
|
||||
Uri(),
|
||||
Uri(),
|
||||
Uri(),
|
||||
Uri(),
|
||||
State(''),
|
||||
);
|
||||
|
||||
// Act
|
||||
final result = session.isDownloadAllSupported(AccountFixtures.aliceAccountId);
|
||||
|
||||
// Assert
|
||||
expect(result, isFalse);
|
||||
});
|
||||
|
||||
test('SHOULD return true WHEN isSupported is true and endpoint is not empty', () {
|
||||
// Arrange
|
||||
final session = Session(
|
||||
{},
|
||||
{
|
||||
AccountFixtures.aliceAccountId: Account(
|
||||
AccountName('Alice'),
|
||||
true,
|
||||
false,
|
||||
{
|
||||
SessionExtensions.linagoraDownloadAllCapability: DownloadAllCapability(
|
||||
endpoint: 'https://example.com')
|
||||
},
|
||||
)
|
||||
},
|
||||
{},
|
||||
UserName(''),
|
||||
Uri(),
|
||||
Uri(),
|
||||
Uri(),
|
||||
Uri(),
|
||||
State(''),
|
||||
);
|
||||
|
||||
// Act
|
||||
final result = session.isDownloadAllSupported(AccountFixtures.aliceAccountId);
|
||||
|
||||
// Assert
|
||||
expect(result, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('getDownloadAllCapability::test', () {
|
||||
test('SHOULD return DownloadAllCapability WHEN DownloadAllCapability is available', () {
|
||||
// Arrange
|
||||
final session = Session(
|
||||
{},
|
||||
{
|
||||
AccountFixtures.aliceAccountId: Account(
|
||||
AccountName('Alice'),
|
||||
true,
|
||||
false,
|
||||
{
|
||||
SessionExtensions.linagoraDownloadAllCapability: DownloadAllCapability()
|
||||
},
|
||||
)
|
||||
},
|
||||
{},
|
||||
UserName(''),
|
||||
Uri(),
|
||||
Uri(),
|
||||
Uri(),
|
||||
Uri(),
|
||||
State(''),
|
||||
);
|
||||
|
||||
// Act
|
||||
final result = session.getDownloadAllCapability(AccountFixtures.aliceAccountId);
|
||||
|
||||
// Assert
|
||||
expect(result, isNotNull);
|
||||
});
|
||||
test('SHOULD return null WHEN DownloadAllCapability is not available', () {
|
||||
// Arrange
|
||||
final session = Session(
|
||||
{},
|
||||
{
|
||||
AccountFixtures.aliceAccountId: Account(
|
||||
AccountName('Alice'),
|
||||
true,
|
||||
false,
|
||||
{
|
||||
SessionExtensions.linagoraDownloadAllCapability: EmptyCapability()
|
||||
},
|
||||
)
|
||||
},
|
||||
{},
|
||||
UserName(''),
|
||||
Uri(),
|
||||
Uri(),
|
||||
Uri(),
|
||||
Uri(),
|
||||
State(''),
|
||||
);
|
||||
|
||||
// Act
|
||||
final result = session.getDownloadAllCapability(AccountFixtures.aliceAccountId);
|
||||
|
||||
// Assert
|
||||
expect(result, isNull);
|
||||
});
|
||||
test('SHOULD return null WHEN DownloadAllCapability is not supported', () {
|
||||
// Arrange
|
||||
final session = Session(
|
||||
{},
|
||||
{
|
||||
AccountFixtures.aliceAccountId: Account(
|
||||
AccountName('Alice'),
|
||||
true,
|
||||
false,
|
||||
{},
|
||||
)
|
||||
},
|
||||
{},
|
||||
UserName(''),
|
||||
Uri(),
|
||||
Uri(),
|
||||
Uri(),
|
||||
Uri(),
|
||||
State(''),
|
||||
);
|
||||
|
||||
// Act
|
||||
final result = session.getDownloadAllCapability(AccountFixtures.aliceAccountId);
|
||||
|
||||
// Assert
|
||||
expect(result, isNull);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user