Check directory exists when downloading

This commit is contained in:
Hillel Coren 2023-10-22 17:54:54 +03:00
parent ac91c104d7
commit a301b0e839
6 changed files with 69 additions and 45 deletions

View File

@ -34,9 +34,9 @@ import 'package:invoiceninja_flutter/redux/vendor/vendor_actions.dart';
import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.dart'; import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.dart';
import 'package:invoiceninja_flutter/utils/completers.dart'; import 'package:invoiceninja_flutter/utils/completers.dart';
import 'package:invoiceninja_flutter/utils/dialogs.dart'; import 'package:invoiceninja_flutter/utils/dialogs.dart';
import 'package:invoiceninja_flutter/utils/files.dart';
import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:invoiceninja_flutter/utils/platforms.dart'; import 'package:invoiceninja_flutter/utils/platforms.dart';
import 'package:path_provider/path_provider.dart';
import 'package:pinch_zoom/pinch_zoom.dart'; import 'package:pinch_zoom/pinch_zoom.dart';
import 'package:printing/printing.dart'; import 'package:printing/printing.dart';
@ -321,7 +321,7 @@ void handleDocumentAction(
} }
final store = StoreProvider.of<AppState>(context!); final store = StoreProvider.of<AppState>(context!);
final localization = AppLocalization.of(context); final localization = AppLocalization.of(context)!;
final documentIds = documents.map((document) => document.id).toList(); final documentIds = documents.map((document) => document.id).toList();
final document = store.state.documentState.map[documentIds.first]!; final document = store.state.documentState.map[documentIds.first]!;
@ -331,19 +331,19 @@ void handleDocumentAction(
break; break;
case EntityAction.restore: case EntityAction.restore:
final message = documentIds.length > 1 final message = documentIds.length > 1
? localization!.restoredDocuments ? localization.restoredDocuments
.replaceFirst(':value', ':count') .replaceFirst(':value', ':count')
.replaceFirst(':count', documentIds.length.toString()) .replaceFirst(':count', documentIds.length.toString())
: localization!.restoredDocument; : localization.restoredDocument;
store.dispatch(RestoreDocumentRequest( store.dispatch(RestoreDocumentRequest(
snackBarCompleter<Null>(message), documentIds)); snackBarCompleter<Null>(message), documentIds));
break; break;
case EntityAction.archive: case EntityAction.archive:
final message = documentIds.length > 1 final message = documentIds.length > 1
? localization!.archivedDocuments ? localization.archivedDocuments
.replaceFirst(':value', ':count') .replaceFirst(':value', ':count')
.replaceFirst(':count', documentIds.length.toString()) .replaceFirst(':count', documentIds.length.toString())
: localization!.archivedDocument; : localization.archivedDocument;
store.dispatch(ArchiveDocumentRequest( store.dispatch(ArchiveDocumentRequest(
snackBarCompleter<Null>(message), documentIds)); snackBarCompleter<Null>(message), documentIds));
break; break;
@ -386,7 +386,7 @@ void handleDocumentAction(
DownloadDocumentsRequest( DownloadDocumentsRequest(
documentIds: documentIds, documentIds: documentIds,
completer: snackBarCompleter<Null>( completer: snackBarCompleter<Null>(
localization!.exportedData, localization.exportedData,
), ),
), ),
); );
@ -402,7 +402,7 @@ void handleDocumentAction(
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Navigator.of(context).pop(), onPressed: () => Navigator.of(context).pop(),
child: Text(localization!.close.toUpperCase())), child: Text(localization.close.toUpperCase())),
], ],
content: document.isImage content: document.isImage
? PinchZoom( ? PinchZoom(
@ -440,30 +440,29 @@ void handleDocumentAction(
WebUtils.downloadBinaryFile(document!.name, document.data!); WebUtils.downloadBinaryFile(document!.name, document.data!);
} }
} else { } else {
final directory = await (isDesktopOS() final directory = await getAppDownloadDirectory();
? getDownloadsDirectory() as FutureOr<file.Directory> if (directory != null) {
: getApplicationDocumentsDirectory());
String filePath = String filePath =
'${directory.path}${file.Platform.pathSeparator}${document!.name}'; '$directory/${file.Platform.pathSeparator}${document!.name}';
if (file.File(filePath).existsSync()) { if (file.File(filePath).existsSync()) {
final extension = document.name.split('.').last; final extension = document.name.split('.').last;
final timestamp = DateTime.now().millisecondsSinceEpoch; final timestamp = DateTime.now().millisecondsSinceEpoch;
filePath = filePath = filePath.replaceFirst(
filePath.replaceFirst('.$extension', '_$timestamp.$extension'); '.$extension', '_$timestamp.$extension');
} }
await File(filePath).writeAsBytes(document.data!); await File(filePath).writeAsBytes(document.data!);
if (isDesktopOS()) { if (isDesktopOS()) {
showToast(localization!.fileSavedInPath showToast(localization.fileSavedInPath
.replaceFirst(':path', directory.path)); .replaceFirst(':path', directory));
} else { } else {
await Share.shareXFiles([XFile(filePath)]); await Share.shareXFiles([XFile(filePath)]);
} }
} }
} }
}
if (document.data == null) { if (document.data == null) {
store.dispatch(LoadDocumentData( store.dispatch(LoadDocumentData(
documentId: document.id, documentId: document.id,

View File

@ -21,8 +21,8 @@ import 'package:invoiceninja_flutter/ui/app/buttons/elevated_button.dart';
import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart'; import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart';
import 'package:invoiceninja_flutter/ui/app/forms/date_picker.dart'; import 'package:invoiceninja_flutter/ui/app/forms/date_picker.dart';
import 'package:invoiceninja_flutter/ui/app/multiselect.dart'; import 'package:invoiceninja_flutter/ui/app/multiselect.dart';
import 'package:invoiceninja_flutter/utils/files.dart';
import 'package:invoiceninja_flutter/utils/formatting.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart';
import 'package:path_provider/path_provider.dart';
import 'package:printing/printing.dart'; import 'package:printing/printing.dart';
// Project imports: // Project imports:
@ -352,16 +352,14 @@ class _ClientPdfViewState extends State<ClientPdfView> {
WebUtils.downloadBinaryFile( WebUtils.downloadBinaryFile(
fileName, _response!.bodyBytes); fileName, _response!.bodyBytes);
} else { } else {
final directory = await (isDesktopOS() final directory = await getAppDownloadDirectory();
? getDownloadsDirectory()
: getApplicationDocumentsDirectory());
if (directory == null) { if (directory == null) {
return; return;
} }
String filePath = String filePath =
'${directory.path}${file.Platform.pathSeparator}$fileName'; '$directory${file.Platform.pathSeparator}$fileName';
if (file.File(filePath).existsSync()) { if (file.File(filePath).existsSync()) {
final timestamp = final timestamp =
@ -375,7 +373,7 @@ class _ClientPdfViewState extends State<ClientPdfView> {
if (isDesktopOS()) { if (isDesktopOS()) {
showToast(localization.fileSavedInPath showToast(localization.fileSavedInPath
.replaceFirst(':path', directory.path)); .replaceFirst(':path', directory));
} else { } else {
await Share.shareXFiles([XFile(filePath)]); await Share.shareXFiles([XFile(filePath)]);
} }

View File

@ -14,7 +14,7 @@ import 'package:http/http.dart' as http;
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:invoiceninja_flutter/main_app.dart'; import 'package:invoiceninja_flutter/main_app.dart';
import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart'; import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart';
import 'package:path_provider/path_provider.dart'; import 'package:invoiceninja_flutter/utils/files.dart';
import 'package:printing/printing.dart'; import 'package:printing/printing.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
@ -269,16 +269,15 @@ class _InvoicePdfViewState extends State<InvoicePdfView> {
WebUtils.downloadBinaryFile( WebUtils.downloadBinaryFile(
fileName, _response!.bodyBytes); fileName, _response!.bodyBytes);
} else { } else {
final directory = await (isDesktopOS() final directory =
? getDownloadsDirectory() await getAppDownloadDirectory();
: getApplicationDocumentsDirectory());
if (directory == null) { if (directory == null) {
return; return;
} }
String filePath = String filePath =
'${directory.path}${file.Platform.pathSeparator}$fileName'; '$directory${file.Platform.pathSeparator}$fileName';
if (file.File(filePath).existsSync()) { if (file.File(filePath).existsSync()) {
final timestamp = final timestamp =
@ -293,7 +292,7 @@ class _InvoicePdfViewState extends State<InvoicePdfView> {
if (isDesktopOS()) { if (isDesktopOS()) {
showToast(localization.fileSavedInPath showToast(localization.fileSavedInPath
.replaceFirst(':path', directory.path)); .replaceFirst(':path', directory));
} else { } else {
await Share.shareXFiles([XFile(filePath)]); await Share.shareXFiles([XFile(filePath)]);
} }

View File

@ -18,9 +18,9 @@ import 'package:invoiceninja_flutter/ui/reports/recurring_expense_report.dart';
import 'package:invoiceninja_flutter/ui/reports/recurring_invoice_report.dart'; import 'package:invoiceninja_flutter/ui/reports/recurring_invoice_report.dart';
import 'package:invoiceninja_flutter/ui/reports/transaction_report.dart'; import 'package:invoiceninja_flutter/ui/reports/transaction_report.dart';
import 'package:invoiceninja_flutter/ui/reports/vendor_report.dart'; import 'package:invoiceninja_flutter/ui/reports/vendor_report.dart';
import 'package:invoiceninja_flutter/utils/files.dart';
import 'package:invoiceninja_flutter/utils/platforms.dart'; import 'package:invoiceninja_flutter/utils/platforms.dart';
import 'package:memoize/memoize.dart'; import 'package:memoize/memoize.dart';
import 'package:path_provider/path_provider.dart';
import 'package:redux/redux.dart'; import 'package:redux/redux.dart';
// Project imports: // Project imports:
@ -511,22 +511,19 @@ class ReportsScreenVM {
if (kIsWeb) { if (kIsWeb) {
WebUtils.downloadTextFile(filename, csvData); WebUtils.downloadTextFile(filename, csvData);
} else { } else {
final directory = await (isDesktopOS() final directory = await getAppDownloadDirectory();
? getDownloadsDirectory()
: getApplicationDocumentsDirectory());
if (directory == null) { if (directory == null) {
return; return;
} }
final filePath = final filePath = directory + file.Platform.pathSeparator + filename;
directory.path + file.Platform.pathSeparator + filename;
final csvFile = file.File(filePath); final csvFile = file.File(filePath);
await csvFile.writeAsString(csvData); await csvFile.writeAsString(csvData);
if (isDesktopOS()) { if (isDesktopOS()) {
showToast(localization!.fileSavedInPath showToast(localization!.fileSavedInPath
.replaceFirst(':path', directory.path)); .replaceFirst(':path', directory));
} else { } else {
await Share.shareXFiles([XFile(filePath)]); await Share.shareXFiles([XFile(filePath)]);
} }

View File

@ -7,6 +7,10 @@ import 'package:flutter/foundation.dart';
// Package imports: // Package imports:
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:invoiceninja_flutter/main_app.dart';
import 'package:invoiceninja_flutter/utils/dialogs.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
// Project imports: // Project imports:
@ -77,3 +81,24 @@ Future<List<MultipartFile>?> _pickFiles({
return null; return null;
} }
Future<String?> getAppDownloadDirectory() async {
final directory = await (isDesktopOS()
? getDownloadsDirectory()
: getApplicationDocumentsDirectory());
if (directory == null) {
return null;
}
if (!Directory(directory.path).existsSync()) {
showErrorDialog(
message: AppLocalization.of(navigatorKey.currentContext!)!
.directoryDoesNotExist
.replaceFirst(':value', directory.path));
return null;
}
return directory.path;
}

View File

@ -18,6 +18,8 @@ mixin LocalizationsProvider on LocaleCodeAware {
static final Map<String, Map<String, String>> _localizedValues = { static final Map<String, Map<String, String>> _localizedValues = {
'en': { 'en': {
// STARTER: lang key - do not remove comment // STARTER: lang key - do not remove comment
'directory_does_not_exist':
'The download directory does not exist :value',
'user_logged_in_notification': 'User Logged in Notification', 'user_logged_in_notification': 'User Logged in Notification',
'user_logged_in_notification_help': 'user_logged_in_notification_help':
'Send an email when logging in from a new location', 'Send an email when logging in from a new location',
@ -109898,6 +109900,10 @@ mixin LocalizationsProvider on LocaleCodeAware {
_localizedValues[localeCode]!['user_logged_in_notification_help'] ?? _localizedValues[localeCode]!['user_logged_in_notification_help'] ??
_localizedValues['en']!['user_logged_in_notification_help']!; _localizedValues['en']!['user_logged_in_notification_help']!;
String get directoryDoesNotExist =>
_localizedValues[localeCode]!['directory_does_not_exist'] ??
_localizedValues['en']!['directory_does_not_exist']!;
// STARTER: lang field - do not remove comment // STARTER: lang field - do not remove comment
String lookup(String? key) { String lookup(String? key) {