TF-3454 Download all attachments as ZIP

This commit is contained in:
DatDang
2025-02-07 14:38:53 +07:00
committed by Dat H. Pham
parent 636df94458
commit f66e1460e4
34 changed files with 1175 additions and 100 deletions
@@ -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);
}
@@ -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),
);
}
}
@@ -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
)
],
@@ -33,7 +33,7 @@ class AttachmentListBottomSheetBuilder {
_statusBarHeight = Get.statusBarHeight / MediaQuery.of(_context).devicePixelRatio;
}
void onDownloadAllButtonAction(OnDownloadAllButtonAction onDownloadAllButtonAction) {
void onDownloadAllButtonAction(OnDownloadAllButtonAction? onDownloadAllButtonAction) {
_onDownloadAllButtonAction = onDownloadAllButtonAction;
}
@@ -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,
),
]
),
],
),
);