diff --git a/core/lib/presentation/views/dialog/downloading_file_dialog_builder.dart b/core/lib/presentation/views/dialog/downloading_file_dialog_builder.dart
index 41e8769a11..7a1805438c 100644
--- a/core/lib/presentation/views/dialog/downloading_file_dialog_builder.dart
+++ b/core/lib/presentation/views/dialog/downloading_file_dialog_builder.dart
@@ -1,7 +1,6 @@
-import 'package:core/core.dart';
+import 'package:core/presentation/extensions/color_extension.dart';
import 'package:flutter/cupertino.dart';
-import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
typedef OnCancelDownloadActionClick = void Function();
@@ -36,36 +35,78 @@ class DownloadingFileDialogBuilder {
}
Widget build() {
- return CupertinoAlertDialog(
+ return Dialog(
key: _key ?? const Key('DownloadingFileBuilder'),
- title: Text(_title, style: const TextStyle(fontSize: 17.0, color: Colors.black)),
- content: Padding(
- padding: const EdgeInsets.only(top: 16.0, left: 16.0, right: 16.0),
- child: Center(
- child: Column(
- children: [
- const SizedBox(
- width: 20.0,
- height: 20.0,
- child: CupertinoActivityIndicator()),
- const SizedBox(height: 16),
- Text(
+ backgroundColor: Colors.white,
+ shape: const RoundedRectangleBorder(
+ borderRadius: BorderRadius.all(Radius.circular(12))
+ ),
+ alignment: Alignment.center,
+ child: SizedBox(
+ width: 250,
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Padding(
+ padding: const EdgeInsetsDirectional.symmetric(
+ horizontal: 12,
+ vertical: 20
+ ),
+ child: Text(
+ _title,
+ style: const TextStyle(
+ fontSize: 17.0,
+ color: Colors.black,
+ fontWeight: FontWeight.bold
+ )
+ ),
+ ),
+ const SizedBox(
+ width: 20.0,
+ height: 20.0,
+ child: CupertinoActivityIndicator()
+ ),
+ const SizedBox(height: 16),
+ Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 16,
+ ),
+ child: Text(
_content,
- style: const TextStyle(fontSize: 13.0, color: Colors.black),
- softWrap: false,
- maxLines: 1)
- ],
- ),
- )),
- actions: [
- if (_actionText.isNotEmpty)
- Padding(
- padding: const EdgeInsets.only(bottom: kIsWeb ? 16 : 0, top: kIsWeb ? 16 : 0),
- child: TextButton(
- onPressed: () => _onCancelDownloadActionClick?.call(),
- child: Text(_actionText, style: const TextStyle(fontSize: 17.0, color: AppColor.appColor)),
- ))
- ],
+ style: const TextStyle(
+ fontSize: 13.0,
+ color: Colors.black,
+ ),
+ textAlign: TextAlign.center,
+ overflow: TextOverflow.ellipsis,
+ maxLines: 2
+ ),
+ ),
+ const SizedBox(height: 16),
+ if (_actionText.isNotEmpty && _onCancelDownloadActionClick != null)
+ ... [
+ const Divider(),
+ TextButton(
+ onPressed: _onCancelDownloadActionClick,
+ style: TextButton.styleFrom(
+ backgroundColor: Colors.white,
+ foregroundColor: AppColor.primaryColor,
+ overlayColor: Colors.black12,
+ fixedSize: const Size.fromWidth(250),
+ ),
+ child: Text(
+ _actionText,
+ style: const TextStyle(
+ fontSize: 17.0,
+ color: AppColor.primaryColor
+ )
+ ),
+ )
+ ]
+ ],
+ ),
+ ),
);
}
}
\ No newline at end of file
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 84f94739ab..1652964344 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -55,7 +55,7 @@
NSContactsUsageDescription
Used by Team-Mail for Email address auto-completion. You can easily to find the email address in your contact book to send the email.
NSPhotoLibraryAddUsageDescription
- To save photo to library, we need permission to access your photo library.
+ To save photos or videos to library, we need permission to access your photo library.
NSPhotoLibraryUsageDescription
To select and upload photo, we need permission to access your photo library.
UIBackgroundModes
diff --git a/lib/features/base/mixin/save_media_to_gallery_mixin.dart b/lib/features/base/mixin/save_media_to_gallery_mixin.dart
index 7af7a86460..8b8e46f174 100644
--- a/lib/features/base/mixin/save_media_to_gallery_mixin.dart
+++ b/lib/features/base/mixin/save_media_to_gallery_mixin.dart
@@ -15,7 +15,7 @@ import 'package:tmail_ui_user/main/permissions/storage_permission_service.dart';
typedef OnSaveCallbackAction = Function(bool isSuccess);
mixin SaveMediaToGalleryMixin {
- Future handleAndroidStoragePermission(BuildContext context) async {
+ Future _handleAndroidStoragePermission(BuildContext context) async {
if (await StoragePermissionService().isUserHaveToRequestStoragePermissionAndroid()) {
final permission = await Permission.storage.request();
@@ -32,8 +32,7 @@ mixin SaveMediaToGalleryMixin {
AppLocalizations.of(context).app_name,
),
),
- onAcceptButton: () =>
- StoragePermissionService().goToSettingsForPermissionActions(),
+ onAcceptButton: StoragePermissionService().goToSettingsForPermissionActions,
);
},
);
@@ -46,7 +45,7 @@ mixin SaveMediaToGalleryMixin {
}
}
- Future handlePhotoPermissionIOS(BuildContext context) async {
+ Future _handlePhotoPermissionIOS(BuildContext context) async {
final permissionStatus = await StoragePermissionService().requestPhotoAddOnlyPermissionIOS();
if (permissionStatus.isPermanentlyDenied && context.mounted) {
showDialog(
@@ -61,8 +60,7 @@ mixin SaveMediaToGalleryMixin {
AppLocalizations.of(context).app_name,
),
),
- onAcceptButton: () =>
- StoragePermissionService().goToSettingsForPermissionActions(),
+ onAcceptButton: StoragePermissionService().goToSettingsForPermissionActions,
);
},
);
@@ -72,36 +70,35 @@ mixin SaveMediaToGalleryMixin {
}
}
- Future saveMediaToGallery({
+ Future _saveMediaToGallery({
required File fileInDownloadsInApp,
required MediaType mediaType,
OnSaveCallbackAction? onSaveCallbackAction
}) async {
if (mediaType.isImageFile()) {
- await saveImageToGallery(file: fileInDownloadsInApp);
+ await _saveImageToGallery(file: fileInDownloadsInApp);
} else if (mediaType.isVideoFile()) {
- await saveVideoToGallery(file: fileInDownloadsInApp);
+ await _saveVideoToGallery(file: fileInDownloadsInApp);
} else {
return;
}
onSaveCallbackAction?.call(true);
}
- Future saveImageToGallery({
+ Future _saveImageToGallery({
required File file,
}) async {
log('SaveMediaToGalleryMixin::saveImageToGallery:file path: ${file.path}');
await Gal.putImage(file.path);
}
- Future saveVideoToGallery({
+ Future _saveVideoToGallery({
required File file,
}) async {
log('SaveMediaToGalleryMixin::saveVideoToGallery:file path: ${file.path}');
await Gal.putVideo(file.path);
}
-
Future saveToGallery({
required BuildContext context,
required String filePath,
@@ -110,9 +107,9 @@ mixin SaveMediaToGalleryMixin {
}) async {
try {
if (PlatformInfo.isAndroid) {
- await handleAndroidStoragePermission(context);
+ await _handleAndroidStoragePermission(context);
} else if (PlatformInfo.isIOS) {
- await handlePhotoPermissionIOS(context);
+ await _handlePhotoPermissionIOS(context);
} else {
return;
}
@@ -120,7 +117,7 @@ mixin SaveMediaToGalleryMixin {
final fileInDownloadsInApp = File(filePath);
if (context.mounted) {
- await saveMediaToGallery(
+ await _saveMediaToGallery(
mediaType: mediaType,
fileInDownloadsInApp: fileInDownloadsInApp,
onSaveCallbackAction: onSaveCallbackAction
diff --git a/lib/features/email/presentation/controller/single_email_controller.dart b/lib/features/email/presentation/controller/single_email_controller.dart
index 2b9e09d765..17ff10b8e3 100644
--- a/lib/features/email/presentation/controller/single_email_controller.dart
+++ b/lib/features/email/presentation/controller/single_email_controller.dart
@@ -6,7 +6,6 @@ import 'package:better_open_file/better_open_file.dart' as open_file;
import 'package:core/core.dart';
import 'package:dartz/dartz.dart';
import 'package:dio/dio.dart';
-import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
@@ -29,6 +28,7 @@ import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:share_plus/share_plus.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart';
+import 'package:tmail_ui_user/features/base/mixin/save_media_to_gallery_mixin.dart';
import 'package:tmail_ui_user/features/base/state/button_state.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart';
import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart';
@@ -112,7 +112,8 @@ import 'package:tmail_ui_user/main/routes/route_navigation.dart';
import 'package:tmail_ui_user/main/routes/route_utils.dart';
import 'package:tmail_ui_user/main/utils/app_utils.dart';
-class SingleEmailController extends BaseController with AppLoaderMixin {
+class SingleEmailController extends BaseController
+ with AppLoaderMixin, SaveMediaToGalleryMixin {
final mailboxDashBoardController = Get.find();
final emailSupervisorController = Get.find();
@@ -726,44 +727,83 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
}
}
- void exportAttachment(BuildContext context, Attachment attachment) {
+ void downloadAttachmentToPreview(BuildContext context, Attachment attachment) {
+ log('SingleEmailController::downloadAttachmentToPreview: Attachment = ${attachment.name}');
final cancelToken = CancelToken();
- _showDownloadingFileDialog(context, attachment, cancelToken: cancelToken);
- _exportAttachmentAction(attachment, cancelToken);
+
+ _showDownloadingFileDialog(
+ context: context,
+ attachment: attachment,
+ downloadTitle: AppLocalizations.of(context).preparing_to_export,
+ cancelToken: cancelToken);
+
+ _exportAttachmentAction(
+ attachment: attachment,
+ cancelToken: cancelToken,
+ isPreview: true);
+ }
+
+ void downloadAttachmentToSaveToStorage(BuildContext context, Attachment attachment) {
+ log('SingleEmailController::downloadAttachmentToSaveToStorage: Attachment = ${attachment.name}');
+ final cancelToken = CancelToken();
+
+ _showDownloadingFileDialog(
+ context: context,
+ attachment: attachment,
+ downloadTitle: attachment.type?.isSaveToGallerySupported() == true
+ ? AppLocalizations.of(context).preparing_to_save
+ : AppLocalizations.of(context).preparing_to_export,
+ cancelToken: cancelToken);
+
+ _exportAttachmentAction(
+ attachment: attachment,
+ cancelToken: cancelToken,
+ isPreview: attachment.type?.isSaveToGallerySupported() != true);
}
- void _showDownloadingFileDialog(BuildContext context, Attachment attachment, {CancelToken? cancelToken}) {
+ void _showDownloadingFileDialog({
+ required BuildContext context,
+ required Attachment attachment,
+ required String downloadTitle,
+ CancelToken? cancelToken,
+ }) {
+ final downloadDialog = DownloadingFileDialogBuilder()
+ ..key(const Key('downloading_file_dialog'))
+ ..title(downloadTitle)
+ ..content(AppLocalizations.of(context).downloading_file(attachment.name ?? ''));
+
if (cancelToken != null) {
- showCupertinoDialog(
- context: context,
- builder: (_) =>
- PointerInterceptor(child: (DownloadingFileDialogBuilder()
- ..key(const Key('downloading_file_dialog'))
- ..title(AppLocalizations.of(context).preparing_to_export)
- ..content(AppLocalizations.of(context).downloading_file(attachment.name ?? ''))
- ..actionText(AppLocalizations.of(context).cancel)
- ..addCancelDownloadActionClick(() {
- cancelToken.cancel([AppLocalizations.of(context).user_cancel_download_file]);
- popBack();
- }))
- .build()));
- } else {
- showCupertinoDialog(
- context: context,
- builder: (_) =>
- 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 ?? '')))
- .build()));
+ downloadDialog
+ ..actionText(AppLocalizations.of(context).cancel)
+ ..addCancelDownloadActionClick(() {
+ cancelToken.cancel([AppLocalizations.of(context).user_cancel_download_file]);
+ popBack();
+ });
}
+
+ Get.dialog(
+ barrierDismissible: false,
+ downloadDialog.build()
+ );
}
- void _exportAttachmentAction(Attachment attachment, CancelToken cancelToken) async {
+ void _exportAttachmentAction({
+ required Attachment attachment,
+ required CancelToken cancelToken,
+ bool isPreview = false,
+ }) async {
final accountId = mailboxDashBoardController.accountId.value;
- if (accountId != null && mailboxDashBoardController.sessionCurrent != null) {
- final baseDownloadUrl = mailboxDashBoardController.sessionCurrent!.getDownloadUrl(jmapUrl: dynamicUrlInterceptors.jmapUrl);
- consumeState(_exportAttachmentInteractor.execute(attachment, accountId, baseDownloadUrl, cancelToken));
+ final session = mailboxDashBoardController.sessionCurrent;
+ final baseDownloadUrl = session?.getDownloadUrl(jmapUrl: dynamicUrlInterceptors.jmapUrl);
+
+ if (accountId != null && session != null && baseDownloadUrl != null) {
+ consumeState(_exportAttachmentInteractor.execute(
+ attachment,
+ accountId,
+ baseDownloadUrl,
+ cancelToken,
+ isPreview
+ ));
}
}
@@ -779,13 +819,44 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
}
}
- void _exportAttachmentSuccessAction(ExportAttachmentSuccess success) async {
+ Future _exportAttachmentSuccessAction(ExportAttachmentSuccess success) async {
popBack();
- _openDownloadedPreviewWorkGroupDocument(success.downloadedResponse);
+
+ if (success.isPreview) {
+ await _openDownloadedDocument(success.downloadedResponse);
+ } else {
+ final mediaType = success.downloadedResponse.mediaType;
+ final filePath = success.downloadedResponse.filePath;
+
+ if (mediaType?.isSaveToGallerySupported() == true) {
+ await saveToGallery(
+ context: currentContext!,
+ filePath: filePath,
+ mediaType: mediaType!,
+ onSaveCallbackAction: (isSuccess) {
+ if (currentOverlayContext == null || currentContext == null) {
+ return;
+ }
+
+ if (isSuccess) {
+ appToast.showToastSuccessMessage(
+ currentOverlayContext!,
+ AppLocalizations.of(currentContext!).fileSavedToGallery);
+ } else {
+ appToast.showToastErrorMessage(
+ currentOverlayContext!,
+ AppLocalizations.of(currentContext!).saveFileToDownloadsError);
+ }
+ }
+ );
+ } else {
+ await _openDownloadedDocument(success.downloadedResponse);
+ }
+ }
}
- void _openDownloadedPreviewWorkGroupDocument(DownloadedResponse downloadedResponse) async {
- log('SingleEmailController::_openDownloadedPreviewWorkGroupDocument(): $downloadedResponse');
+ Future _openDownloadedDocument(DownloadedResponse downloadedResponse) async {
+ log('SingleEmailController::_openDownloadedDocument(): $downloadedResponse');
if (downloadedResponse.mediaType == null) {
await Share.shareXFiles([XFile(downloadedResponse.filePath)]);
}
@@ -796,7 +867,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
uti: Platform.isIOS ? downloadedResponse.mediaType!.getDocumentUti().value : null);
if (openResult.type != open_file.ResultType.done) {
- logError('SingleEmailController::_openDownloadedPreviewWorkGroupDocument(): no preview available');
+ logError('SingleEmailController::_openDownloadedDocument(): no preview available');
if (currentOverlayContext != null && currentContext != null) {
appToast.showToastErrorMessage(
currentOverlayContext!,
@@ -1796,7 +1867,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
if (PlatformInfo.isWeb) {
downloadAttachmentForWeb(attachment);
} else if (PlatformInfo.isMobile) {
- exportAttachment(context, attachment);
+ downloadAttachmentToSaveToStorage(context, attachment);
} else {
log('EmailView::handleDownloadAttachmentAction: THE PLATFORM IS SUPPORTED');
}
@@ -1810,7 +1881,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
downloadAttachmentForWeb(attachment);
}
} else if (PlatformInfo.isMobile) {
- exportAttachment(context, attachment);
+ downloadAttachmentToPreview(context, attachment);
} else {
log('EmailView::_handleViewAttachmentAction: THE PLATFORM IS SUPPORTED');
}
diff --git a/lib/main/permissions/permission_dialog.dart b/lib/main/permissions/permission_dialog.dart
index 9253b45143..a2fdcc269f 100644
--- a/lib/main/permissions/permission_dialog.dart
+++ b/lib/main/permissions/permission_dialog.dart
@@ -59,15 +59,12 @@ class _PermissionDialogState extends State
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
- if (widget.icon != null) ...[
- const SizedBox(
- height: 24.0,
- ),
- widget.icon!,
- ],
- const SizedBox(
- height: 16.0,
- ),
+ if (widget.icon != null)
+ ...[
+ const SizedBox(height: 24.0),
+ widget.icon!,
+ ],
+ const SizedBox(height: 16.0),
widget.explainTextRequestPermission,
const SizedBox(height: 24.0),
Expanded(
@@ -89,9 +86,9 @@ class _PermissionDialogState extends State
if (widget.onAcceptButton != null) {
widget.onAcceptButton!.call();
} else {
- await widget.permission.request().then(
- (value) => Navigator.of(context).pop(),
- );
+ await widget.permission
+ .request()
+ .then((value) => Navigator.of(context).pop());
}
},
),
@@ -129,8 +126,8 @@ class _PermissionTextButton extends StatelessWidget {
child: Text(
text,
style: Theme.of(context).textTheme.labelLarge?.copyWith(
- color: Theme.of(context).colorScheme.primary,
- ),
+ color: Theme.of(context).colorScheme.primary,
+ ),
),
),
),