diff --git a/packages/harbour/lib/l10n/en.arb b/packages/harbour/lib/l10n/en.arb index 58a39dbd..1fd12b38 100644 --- a/packages/harbour/lib/l10n/en.arb +++ b/packages/harbour/lib/l10n/en.arb @@ -42,6 +42,7 @@ "retry": "Retry", "showSlashHide": "Show/Hide", "exit": "Exit", + "disabled": "Disabled", "settings": "Settings", "settingsForApp": "Settings - {name}", "@settingsForApp": { @@ -135,9 +136,33 @@ "filesChooseFolder": "Choose folder", "filesAddToFavorites": "Add to favorites", "filesRemoveFromFavorites": "Remove from favorites", + "filesConfirmUploadSizeWarning": "Are you sure you want to upload a file that is bigger than {warningSize} ({actualSize})?", + "@filesConfirmUploadSizeWarning": { + "placeholders": { + "warningSize": { + "type": "String" + }, + "actualSize": { + "type": "String" + } + } + }, + "filesConfirmDownloadSizeWarning": "Are you sure you want to download a file that is bigger than {warningSize} ({actualSize})?", + "@filesConfirmDownloadSizeWarning": { + "placeholders": { + "warningSize": { + "type": "String" + }, + "actualSize": { + "type": "String" + } + } + }, "filesOptionsShowPreviews": "Show previews for files", "filesOptionsUploadQueueParallelism": "Upload queue parallelism", "filesOptionsDownloadQueueParallelism": "Download queue parallelism", + "filesOptionsUploadSizeWarning": "Upload size warning", + "filesOptionsDownloadSizeWarning": "Download size warning", "newsName": "News", "newsAddFeed": "Add feed", "newsFolder": "Folder", diff --git a/packages/harbour/lib/l10n/localizations.dart b/packages/harbour/lib/l10n/localizations.dart index e700b019..c96332a9 100644 --- a/packages/harbour/lib/l10n/localizations.dart +++ b/packages/harbour/lib/l10n/localizations.dart @@ -257,6 +257,12 @@ abstract class AppLocalizations { /// **'Exit'** String get exit; + /// No description provided for @disabled. + /// + /// In en, this message translates to: + /// **'Disabled'** + String get disabled; + /// No description provided for @settings. /// /// In en, this message translates to: @@ -569,6 +575,18 @@ abstract class AppLocalizations { /// **'Remove from favorites'** String get filesRemoveFromFavorites; + /// No description provided for @filesConfirmUploadSizeWarning. + /// + /// In en, this message translates to: + /// **'Are you sure you want to upload a file that is bigger than {warningSize} ({actualSize})?'** + String filesConfirmUploadSizeWarning(String warningSize, String actualSize); + + /// No description provided for @filesConfirmDownloadSizeWarning. + /// + /// In en, this message translates to: + /// **'Are you sure you want to download a file that is bigger than {warningSize} ({actualSize})?'** + String filesConfirmDownloadSizeWarning(String warningSize, String actualSize); + /// No description provided for @filesOptionsShowPreviews. /// /// In en, this message translates to: @@ -587,6 +605,18 @@ abstract class AppLocalizations { /// **'Download queue parallelism'** String get filesOptionsDownloadQueueParallelism; + /// No description provided for @filesOptionsUploadSizeWarning. + /// + /// In en, this message translates to: + /// **'Upload size warning'** + String get filesOptionsUploadSizeWarning; + + /// No description provided for @filesOptionsDownloadSizeWarning. + /// + /// In en, this message translates to: + /// **'Download size warning'** + String get filesOptionsDownloadSizeWarning; + /// No description provided for @newsName. /// /// In en, this message translates to: diff --git a/packages/harbour/lib/l10n/localizations_en.dart b/packages/harbour/lib/l10n/localizations_en.dart index a25e7175..ab02b417 100644 --- a/packages/harbour/lib/l10n/localizations_en.dart +++ b/packages/harbour/lib/l10n/localizations_en.dart @@ -95,6 +95,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get exit => 'Exit'; + @override + String get disabled => 'Disabled'; + @override String get settings => 'Settings'; @@ -261,6 +264,16 @@ class AppLocalizationsEn extends AppLocalizations { @override String get filesRemoveFromFavorites => 'Remove from favorites'; + @override + String filesConfirmUploadSizeWarning(String warningSize, String actualSize) { + return 'Are you sure you want to upload a file that is bigger than $warningSize ($actualSize)?'; + } + + @override + String filesConfirmDownloadSizeWarning(String warningSize, String actualSize) { + return 'Are you sure you want to download a file that is bigger than $warningSize ($actualSize)?'; + } + @override String get filesOptionsShowPreviews => 'Show previews for files'; @@ -270,6 +283,12 @@ class AppLocalizationsEn extends AppLocalizations { @override String get filesOptionsDownloadQueueParallelism => 'Download queue parallelism'; + @override + String get filesOptionsUploadSizeWarning => 'Upload size warning'; + + @override + String get filesOptionsDownloadSizeWarning => 'Download size warning'; + @override String get newsName => 'News'; diff --git a/packages/harbour/lib/src/apps/files/app.dart b/packages/harbour/lib/src/apps/files/app.dart index 601dcca5..8a4440dc 100644 --- a/packages/harbour/lib/src/apps/files/app.dart +++ b/packages/harbour/lib/src/apps/files/app.dart @@ -17,6 +17,7 @@ import 'package:image_picker/image_picker.dart'; import 'package:intersperse/intersperse.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:nextcloud/nextcloud.dart'; +import 'package:path/path.dart' as p; import 'package:provider/provider.dart'; import 'package:rxdart/rxdart.dart'; import 'package:settings/settings.dart'; diff --git a/packages/harbour/lib/src/apps/files/dialogs/choose_create.dart b/packages/harbour/lib/src/apps/files/dialogs/choose_create.dart index e5c6c2a5..ba0a8c2e 100644 --- a/packages/harbour/lib/src/apps/files/dialogs/choose_create.dart +++ b/packages/harbour/lib/src/apps/files/dialogs/choose_create.dart @@ -1,6 +1,6 @@ part of '../app.dart'; -class FilesChooseCreateDialog extends StatelessWidget { +class FilesChooseCreateDialog extends StatefulWidget { const FilesChooseCreateDialog({ required this.bloc, required this.basePath, @@ -10,16 +10,40 @@ class FilesChooseCreateDialog extends StatelessWidget { final FilesBloc bloc; final List basePath; - Future upload(final FileType type) async { + @override + State createState() => _FilesChooseCreateDialogState(); +} + +class _FilesChooseCreateDialogState extends State { + Future uploadFromPick(final FileType type) async { final result = await FilePicker.platform.pickFiles( allowMultiple: true, type: type, ); if (result != null) { for (final file in result.files) { - bloc.uploadFile([...basePath, file.name], file.path!); + await upload(File(file.path!)); + } + } + } + + Future upload(final File file) async { + final sizeWarning = widget.bloc.options.uploadSizeWarning.value; + if (sizeWarning != null) { + final stat = file.statSync(); + if (stat.size > sizeWarning) { + if (!(await showConfirmationDialog( + context, + AppLocalizations.of(context).filesConfirmUploadSizeWarning( + filesize(sizeWarning), + filesize(stat.size), + ), + ))) { + return; + } } } + widget.bloc.uploadFile([...widget.basePath, p.basename(file.path)], file.path); } @override @@ -32,9 +56,11 @@ class FilesChooseCreateDialog extends StatelessWidget { ), title: Text(AppLocalizations.of(context).filesUploadFiles), onTap: () async { - Navigator.of(context).pop(); + await uploadFromPick(FileType.any); - await upload(FileType.any); + if (mounted) { + Navigator.of(context).pop(); + } }, ), ListTile( @@ -44,9 +70,11 @@ class FilesChooseCreateDialog extends StatelessWidget { ), title: Text(AppLocalizations.of(context).filesUploadImages), onTap: () async { - Navigator.of(context).pop(); + await uploadFromPick(FileType.image); - await upload(FileType.image); + if (mounted) { + Navigator.of(context).pop(); + } }, ), if (Provider.of(context, listen: false).canUseCamera) ...[ @@ -62,7 +90,7 @@ class FilesChooseCreateDialog extends StatelessWidget { final picker = ImagePicker(); final result = await picker.pickImage(source: ImageSource.camera); if (result != null) { - bloc.uploadFile([...basePath, result.name], result.path); + await upload(File(result.path)); } }, ), @@ -81,7 +109,7 @@ class FilesChooseCreateDialog extends StatelessWidget { builder: (final context) => const FilesCreateFolderDialog(), ); if (result != null) { - bloc.browser.createFolder([...basePath, ...result]); + widget.bloc.browser.createFolder([...widget.basePath, ...result]); } }, ), diff --git a/packages/harbour/lib/src/apps/files/options.dart b/packages/harbour/lib/src/apps/files/options.dart index 1696fe92..8cd4a424 100644 --- a/packages/harbour/lib/src/apps/files/options.dart +++ b/packages/harbour/lib/src/apps/files/options.dart @@ -9,6 +9,8 @@ class FilesAppSpecificOptions extends NextcloudAppSpecificOptions { showPreviewsOption, uploadQueueParallelism, downloadQueueParallelism, + uploadSizeWarning, + downloadSizeWarning, ]; } @@ -25,7 +27,7 @@ class FilesAppSpecificOptions extends NextcloudAppSpecificOptions { ); late final uploadQueueParallelism = SelectOption( - storage: super.storage, + storage: storage, category: generalCategory, key: 'upload-queue-parallelism', label: (final context) => AppLocalizations.of(context).filesOptionsUploadQueueParallelism, @@ -38,7 +40,7 @@ class FilesAppSpecificOptions extends NextcloudAppSpecificOptions { ); late final downloadQueueParallelism = SelectOption( - storage: super.storage, + storage: storage, category: generalCategory, key: 'download-queue-parallelism', label: (final context) => AppLocalizations.of(context).filesOptionsDownloadQueueParallelism, @@ -49,4 +51,39 @@ class FilesAppSpecificOptions extends NextcloudAppSpecificOptions { }, }), ); + + late final _sizeWarningValues = { + null: (final context) => AppLocalizations.of(context).disabled, + for (final i in [ + 1, + 10, + 100, + 1024, + 2 * 2024, + 6 * 1024, + 10 * 1024, + ]) ...{ + _mb(i): (final _) => filesize(_mb(i)), + }, + }; + + int _mb(final int i) => i * 1024 * 1024; + + late final uploadSizeWarning = SelectOption( + storage: storage, + category: generalCategory, + key: 'upload-size-warning', + label: (final context) => AppLocalizations.of(context).filesOptionsUploadSizeWarning, + defaultValue: BehaviorSubject.seeded(_mb(10)), + values: BehaviorSubject.seeded(_sizeWarningValues), + ); + + late final downloadSizeWarning = SelectOption( + storage: storage, + category: generalCategory, + key: 'download-size-warning', + label: (final context) => AppLocalizations.of(context).filesOptionsDownloadSizeWarning, + defaultValue: BehaviorSubject.seeded(_mb(10)), + values: BehaviorSubject.seeded(_sizeWarningValues), + ); } diff --git a/packages/harbour/lib/src/apps/files/pages/main.dart b/packages/harbour/lib/src/apps/files/pages/main.dart index 9ed78141..b17e6286 100644 --- a/packages/harbour/lib/src/apps/files/pages/main.dart +++ b/packages/harbour/lib/src/apps/files/pages/main.dart @@ -26,7 +26,19 @@ class _FilesMainPageState extends State { Widget build(BuildContext context) => FilesBrowserView( bloc: widget.bloc.browser, filesBloc: widget.bloc, - onPickFile: (final details) { + onPickFile: (final details) async { + final sizeWarning = widget.bloc.options.downloadSizeWarning.value; + if (sizeWarning != null && details.size > sizeWarning) { + if (!(await showConfirmationDialog( + context, + AppLocalizations.of(context).filesConfirmDownloadSizeWarning( + filesize(sizeWarning), + filesize(details.size), + ), + ))) { + return; + } + } widget.bloc.openFile(details.path, details.etag!, details.mimeType); }, ); diff --git a/packages/harbour/lib/src/apps/files/widgets/browser_view.dart b/packages/harbour/lib/src/apps/files/widgets/browser_view.dart index 84ed7248..801a3e48 100644 --- a/packages/harbour/lib/src/apps/files/widgets/browser_view.dart +++ b/packages/harbour/lib/src/apps/files/widgets/browser_view.dart @@ -425,6 +425,18 @@ class _FilesBrowserViewState extends State { } break; case _FileAction.sync: + final sizeWarning = widget.bloc.options.downloadSizeWarning.value; + if (sizeWarning != null && details.size > sizeWarning) { + if (!(await showConfirmationDialog( + context, + AppLocalizations.of(context).filesConfirmDownloadSizeWarning( + filesize(sizeWarning), + filesize(details.size), + ), + ))) { + return; + } + } widget.filesBloc.syncFile(details.path); break; case _FileAction.delete: