From 1aeebca96b9c7e98b4183148eeef80b5f0995c54 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Sat, 21 Jan 2023 22:33:42 +0100 Subject: [PATCH] Fixed saved view model, slightly changed sorting dialog, fixed open document in system viewer not working because file has folder-like structure. --- .../sort_field_localization_mapper.dart | 22 ++ .../bloc/document_details_cubit.dart | 3 +- .../sort_field_selection_bottom_sheet.dart | 192 +++++++++--------- .../view/widgets/sort_documents_button.dart | 99 +++++---- lib/features/home/view/home_page.dart | 5 +- .../view/widget/bottom_navigation_bar.dart | 64 ------ lib/l10n/intl_cs.arb | 4 + lib/l10n/intl_de.arb | 4 + lib/l10n/intl_en.arb | 4 + lib/l10n/intl_tr.arb | 4 + 10 files changed, 182 insertions(+), 219 deletions(-) create mode 100644 lib/core/translation/sort_field_localization_mapper.dart delete mode 100644 lib/features/home/view/widget/bottom_navigation_bar.dart diff --git a/lib/core/translation/sort_field_localization_mapper.dart b/lib/core/translation/sort_field_localization_mapper.dart new file mode 100644 index 0000000..c2b1f0b --- /dev/null +++ b/lib/core/translation/sort_field_localization_mapper.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:paperless_api/paperless_api.dart'; +import 'package:paperless_mobile/generated/l10n.dart'; + +String translateSortField(BuildContext context, SortField sortField) { + switch (sortField) { + case SortField.archiveSerialNumber: + return S.of(context).documentArchiveSerialNumberPropertyShortLabel; + case SortField.correspondentName: + return S.of(context).documentCorrespondentPropertyLabel; + case SortField.title: + return S.of(context).documentTitlePropertyLabel; + case SortField.documentType: + return S.of(context).documentDocumentTypePropertyLabel; + case SortField.created: + return S.of(context).documentCreatedPropertyLabel; + case SortField.added: + return S.of(context).documentAddedPropertyLabel; + case SortField.modified: + return S.of(context).documentModifiedPropertyLabel; + } +} diff --git a/lib/features/document_details/bloc/document_details_cubit.dart b/lib/features/document_details/bloc/document_details_cubit.dart index 7f504bd..71ed062 100644 --- a/lib/features/document_details/bloc/document_details_cubit.dart +++ b/lib/features/document_details/bloc/document_details_cubit.dart @@ -53,7 +53,8 @@ class DocumentDetailsCubit extends Cubit { final metaData = await _api.getMetaData(state.document); final docBytes = await _api.download(state.document); File f = File('${downloadDir.path}/${metaData.mediaFilename}'); - f.writeAsBytes(docBytes); + f.createSync(recursive: true); + f.writeAsBytesSync(docBytes); return OpenFilex.open(f.path, type: "application/pdf") .then((value) => value.type); } diff --git a/lib/features/documents/view/widgets/search/sort_field_selection_bottom_sheet.dart b/lib/features/documents/view/widgets/search/sort_field_selection_bottom_sheet.dart index 73e6dee..a261cfc 100644 --- a/lib/features/documents/view/widgets/search/sort_field_selection_bottom_sheet.dart +++ b/lib/features/documents/view/widgets/search/sort_field_selection_bottom_sheet.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:paperless_api/paperless_api.dart'; +import 'package:paperless_mobile/core/translation/sort_field_localization_mapper.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart'; import 'package:paperless_mobile/features/labels/bloc/label_state.dart'; @@ -8,9 +10,9 @@ import 'package:paperless_mobile/generated/l10n.dart'; class SortFieldSelectionBottomSheet extends StatefulWidget { final SortOrder initialSortOrder; - final SortField initialSortField; + final SortField? initialSortField; - final Future Function(SortField field, SortOrder order) onSubmit; + final Future Function(SortField? field, SortOrder order) onSubmit; const SortFieldSelectionBottomSheet({ super.key, @@ -26,7 +28,7 @@ class SortFieldSelectionBottomSheet extends StatefulWidget { class _SortFieldSelectionBottomSheetState extends State { - late SortField _currentSortField; + late SortField? _currentSortField; late SortOrder _currentSortOrder; @override @@ -39,61 +41,90 @@ class _SortFieldSelectionBottomSheetState @override Widget build(BuildContext context) { return ClipRRect( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - S.of(context).documentsPageOrderByLabel, - style: Theme.of(context).textTheme.bodySmall, - textAlign: TextAlign.start, - ), - TextButton( - child: Text(S.of(context).documentFilterApplyFilterLabel), - onPressed: () { - widget.onSubmit( - _currentSortField, - _currentSortOrder, - ); - Navigator.pop(context); - }, - ), - ], - ).paddedSymmetrically(horizontal: 16, vertical: 8.0), - Column( - children: [ - _buildSortOption(SortField.archiveSerialNumber), - BlocBuilder, LabelState>( - builder: (context, state) { - return _buildSortOption( - SortField.correspondentName, - enabled: state.labels.values.fold( - false, - (previousValue, element) => - previousValue || (element.documentCount ?? 0) > 0), - ); - }, - ), - _buildSortOption(SortField.title), - BlocBuilder, LabelState>( - builder: (context, state) { - return _buildSortOption( - SortField.documentType, - enabled: state.labels.values.fold( - false, - (previousValue, element) => - previousValue || (element.documentCount ?? 0) > 0), - ); - }, - ), - _buildSortOption(SortField.created), - _buildSortOption(SortField.added), - _buildSortOption(SortField.modified), - ], - ), - ], + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + S.of(context).documentsPageOrderByLabel, + style: Theme.of(context).textTheme.bodySmall, + textAlign: TextAlign.start, + ), + TextButton( + child: Text(S.of(context).documentFilterApplyFilterLabel), + onPressed: () { + widget.onSubmit( + _currentSortField, + _currentSortOrder, + ); + Navigator.pop(context); + }, + ), + ], + ).paddedOnly(left: 16, right: 16, top: 8), + Column( + children: [ + _buildSortOption(SortField.archiveSerialNumber), + BlocBuilder, + LabelState>( + builder: (context, state) { + return _buildSortOption( + SortField.correspondentName, + enabled: state.labels.values.fold( + false, + (previousValue, element) => + previousValue || + (element.documentCount ?? 0) > 0), + ); + }, + ), + _buildSortOption(SortField.title), + BlocBuilder, LabelState>( + builder: (context, state) { + return _buildSortOption( + SortField.documentType, + enabled: state.labels.values.fold( + false, + (previousValue, element) => + previousValue || + (element.documentCount ?? 0) > 0), + ); + }, + ), + _buildSortOption(SortField.created), + _buildSortOption(SortField.added), + _buildSortOption(SortField.modified), + const SizedBox(height: 16), + Center( + child: SegmentedButton( + multiSelectionEnabled: false, + showSelectedIcon: false, + segments: [ + ButtonSegment( + icon: const FaIcon(FontAwesomeIcons.arrowDownAZ), + value: SortOrder.descending, + label: Text(S.of(context).sortDocumentDescending), + ), + ButtonSegment( + icon: const FaIcon(FontAwesomeIcons.arrowUpZA), + value: SortOrder.ascending, + label: Text(S.of(context).sortDocumentAscending), + ), + ], + emptySelectionAllowed: false, + selected: {_currentSortOrder}, + onSelectionChanged: (selection) { + setState(() => _currentSortOrder = selection.first); + }, + ), + ).paddedOnly(bottom: 16), + ], + ), + ], + ), ), ); } @@ -101,47 +132,10 @@ class _SortFieldSelectionBottomSheetState Widget _buildSortOption(SortField field, {bool enabled = true}) { return ListTile( enabled: enabled, - contentPadding: const EdgeInsets.symmetric(horizontal: 32), - title: Text( - _localizedSortField(field), - ), - trailing: _currentSortField == field - ? _buildOrderIcon(_currentSortOrder) - : null, - onTap: () { - setState(() { - _currentSortOrder = (_currentSortField == field - ? _currentSortOrder.toggle() - : SortOrder.descending); - _currentSortField = field; - }); - }, + contentPadding: const EdgeInsets.only(left: 32, right: 16), + title: Text(translateSortField(context, field)), + trailing: _currentSortField == field ? const Icon(Icons.done) : null, + onTap: () => setState(() => _currentSortField = field), ); } - - Widget _buildOrderIcon(SortOrder order) { - if (order == SortOrder.ascending) { - return const Icon(Icons.arrow_upward); - } - return const Icon(Icons.arrow_downward); - } - - String _localizedSortField(SortField sortField) { - switch (sortField) { - case SortField.archiveSerialNumber: - return S.of(context).documentArchiveSerialNumberPropertyShortLabel; - case SortField.correspondentName: - return S.of(context).documentCorrespondentPropertyLabel; - case SortField.title: - return S.of(context).documentTitlePropertyLabel; - case SortField.documentType: - return S.of(context).documentDocumentTypePropertyLabel; - case SortField.created: - return S.of(context).documentCreatedPropertyLabel; - case SortField.added: - return S.of(context).documentAddedPropertyLabel; - case SortField.modified: - return S.of(context).documentModifiedPropertyLabel; - } - } } diff --git a/lib/features/documents/view/widgets/sort_documents_button.dart b/lib/features/documents/view/widgets/sort_documents_button.dart index e935e47..cccc276 100644 --- a/lib/features/documents/view/widgets/sort_documents_button.dart +++ b/lib/features/documents/view/widgets/sort_documents_button.dart @@ -16,60 +16,55 @@ class SortDocumentsButton extends StatelessWidget { Widget build(BuildContext context) { return IconButton( icon: const Icon(Icons.sort), - onPressed: () => _onOpenSortBottomSheet(context), - ); - } - - void _onOpenSortBottomSheet(BuildContext context) { - showModalBottomSheet( - elevation: 2, - context: context, - isScrollControlled: true, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), - ), - ), - builder: (_) => BlocProvider.value( - value: context.read(), - child: FractionallySizedBox( - heightFactor: .6, - child: MultiBlocProvider( - providers: [ - BlocProvider( - create: (context) => LabelCubit( - context.read< - LabelRepository>(), - ), - ), - BlocProvider( - create: (context) => LabelCubit( - context.read< - LabelRepository>(), - ), - ), - ], - child: BlocBuilder( - builder: (context, state) { - return SortFieldSelectionBottomSheet( - initialSortField: state.filter.sortField, - initialSortOrder: state.filter.sortOrder, - onSubmit: (field, order) => - context.read().updateCurrentFilter( - (filter) => filter.copyWith( - sortField: field, - sortOrder: order, - ), - ), - ); - }, + onPressed: () { + showModalBottomSheet( + elevation: 2, + context: context, + isScrollControlled: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16), ), ), - ), - ), + builder: (_) => BlocProvider.value( + value: context.read(), + child: MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => LabelCubit( + context.read< + LabelRepository>(), + ), + ), + BlocProvider( + create: (context) => LabelCubit( + context.read< + LabelRepository>(), + ), + ), + ], + child: BlocBuilder( + builder: (context, state) { + return SortFieldSelectionBottomSheet( + initialSortField: state.filter.sortField, + initialSortOrder: state.filter.sortOrder, + onSubmit: (field, order) => + context.read().updateCurrentFilter( + (filter) => filter.copyWith( + sortField: field, + sortOrder: order, + ), + ), + ); + }, + ), + ), + ), + ); + }, ); } } diff --git a/lib/features/home/view/home_page.dart b/lib/features/home/view/home_page.dart index 4ec53af..ac8ce5a 100644 --- a/lib/features/home/view/home_page.dart +++ b/lib/features/home/view/home_page.dart @@ -8,19 +8,18 @@ import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart'; import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart'; import 'package:paperless_mobile/core/global/constants.dart'; -import 'package:paperless_mobile/core/translation/error_code_localization_mapper.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/saved_view_repository.dart'; import 'package:paperless_mobile/core/repository/state/impl/correspondent_repository_state.dart'; import 'package:paperless_mobile/core/repository/state/impl/document_type_repository_state.dart'; import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart'; import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart'; +import 'package:paperless_mobile/core/translation/error_code_localization_mapper.dart'; import 'package:paperless_mobile/features/document_upload/cubit/document_upload_cubit.dart'; import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart'; import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart'; import 'package:paperless_mobile/features/documents/view/pages/documents_page.dart'; import 'package:paperless_mobile/features/home/view/route_description.dart'; -import 'package:paperless_mobile/features/home/view/widget/bottom_navigation_bar.dart'; import 'package:paperless_mobile/features/home/view/widget/app_drawer.dart'; import 'package:paperless_mobile/features/labels/view/pages/labels_page.dart'; import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart'; @@ -31,8 +30,8 @@ import 'package:paperless_mobile/features/sharing/share_intent_queue.dart'; import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart'; import 'package:paperless_mobile/generated/l10n.dart'; import 'package:paperless_mobile/util.dart'; -import 'package:receive_sharing_intent/receive_sharing_intent.dart'; import 'package:path/path.dart' as p; +import 'package:receive_sharing_intent/receive_sharing_intent.dart'; import 'package:responsive_builder/responsive_builder.dart'; class HomePage extends StatefulWidget { diff --git a/lib/features/home/view/widget/bottom_navigation_bar.dart b/lib/features/home/view/widget/bottom_navigation_bar.dart deleted file mode 100644 index 2b9a27a..0000000 --- a/lib/features/home/view/widget/bottom_navigation_bar.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:paperless_mobile/generated/l10n.dart'; - -class BottomNavBar extends StatelessWidget { - final int selectedIndex; - final void Function(int) onNavigationChanged; - - const BottomNavBar( - {Key? key, - required this.selectedIndex, - required this.onNavigationChanged}) - : super(key: key); - - @override - Widget build(BuildContext context) { - return NavigationBar( - elevation: 4.0, - onDestinationSelected: onNavigationChanged, - selectedIndex: selectedIndex, - destinations: [ - NavigationDestination( - icon: const Icon(Icons.description_outlined), - selectedIcon: Icon( - Icons.description, - color: Theme.of(context).colorScheme.primary, - ), - label: S.of(context).bottomNavDocumentsPageLabel, - ), - NavigationDestination( - icon: const Icon(Icons.document_scanner_outlined), - selectedIcon: Icon( - Icons.document_scanner, - color: Theme.of(context).colorScheme.primary, - ), - label: S.of(context).bottomNavScannerPageLabel, - ), - NavigationDestination( - icon: const Icon(Icons.sell_outlined), - selectedIcon: Icon( - Icons.sell, - color: Theme.of(context).colorScheme.primary, - ), - label: S.of(context).bottomNavLabelsPageLabel, - ), - NavigationDestination( - icon: const Icon(Icons.inbox_outlined), - selectedIcon: Icon( - Icons.inbox, - color: Theme.of(context).colorScheme.primary, - ), - label: S.of(context).bottomNavInboxPageLabel, - ), - NavigationDestination( - icon: const Icon(Icons.settings_outlined), - selectedIcon: Icon( - Icons.settings, - color: Theme.of(context).colorScheme.primary, - ), - label: S.of(context).appDrawerSettingsLabel, - ), - ], - ); - } -} diff --git a/lib/l10n/intl_cs.arb b/lib/l10n/intl_cs.arb index 204cc91..76222dc 100644 --- a/lib/l10n/intl_cs.arb +++ b/lib/l10n/intl_cs.arb @@ -584,6 +584,10 @@ "@settingsThemeModeLightLabel": {}, "settingsThemeModeSystemLabel": "Systémový", "@settingsThemeModeSystemLabel": {}, + "sortDocumentAscending": "Ascending", + "@sortDocumentAscending": {}, + "sortDocumentDescending": "Descending", + "@sortDocumentDescending": {}, "storagePathParameterDayLabel": "den", "@storagePathParameterDayLabel": {}, "storagePathParameterMonthLabel": "měsíc", diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index d2ddc85..4738b2d 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -584,6 +584,10 @@ "@settingsThemeModeLightLabel": {}, "settingsThemeModeSystemLabel": "System", "@settingsThemeModeSystemLabel": {}, + "sortDocumentAscending": "Aufsteigend", + "@sortDocumentAscending": {}, + "sortDocumentDescending": "Absteigend", + "@sortDocumentDescending": {}, "storagePathParameterDayLabel": "Tag", "@storagePathParameterDayLabel": {}, "storagePathParameterMonthLabel": "Monat", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 1ba5eed..5243d9d 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -584,6 +584,10 @@ "@settingsThemeModeLightLabel": {}, "settingsThemeModeSystemLabel": "System", "@settingsThemeModeSystemLabel": {}, + "sortDocumentAscending": "Ascending", + "@sortDocumentAscending": {}, + "sortDocumentDescending": "Descending", + "@sortDocumentDescending": {}, "storagePathParameterDayLabel": "day", "@storagePathParameterDayLabel": {}, "storagePathParameterMonthLabel": "month", diff --git a/lib/l10n/intl_tr.arb b/lib/l10n/intl_tr.arb index 17651e8..17f7ae1 100644 --- a/lib/l10n/intl_tr.arb +++ b/lib/l10n/intl_tr.arb @@ -584,6 +584,10 @@ "@settingsThemeModeLightLabel": {}, "settingsThemeModeSystemLabel": "Sistem", "@settingsThemeModeSystemLabel": {}, + "sortDocumentAscending": "Ascending", + "@sortDocumentAscending": {}, + "sortDocumentDescending": "Descending", + "@sortDocumentDescending": {}, "storagePathParameterDayLabel": "gün", "@storagePathParameterDayLabel": {}, "storagePathParameterMonthLabel": "ay",