From 1f335119b372f3bbc7568dd56e153c2071bb35c4 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Mon, 24 Apr 2023 01:14:20 +0200 Subject: [PATCH] feat: Add hive type adapters to api models, migrate to freezed --- lib/core/config/hive/hive_config.dart | 16 +- .../database/tables}/global_settings.dart | 0 .../database/tables}/user_account.dart | 2 +- lib/core/database/tables/user_app_state.dart | 22 + .../database/tables}/user_credentials.dart | 0 .../database/tables}/user_settings.dart | 4 - lib/core/service/status_service.dart | 2 +- .../widgets/bulk_edit_label_bottom_sheet.dart | 26 +- .../widgets/document_download_button.dart | 2 +- .../view/document_edit_page.dart | 116 ++- .../cubit/document_search_cubit.dart | 17 +- .../view/sliver_search_bar.dart | 13 +- .../document_upload_preparation_page.dart | 58 +- .../documents/cubit/documents_cubit.dart | 30 +- .../documents/view/pages/documents_page.dart | 136 ++-- lib/features/home/view/home_page.dart | 19 +- .../view/widget/verify_identity_page.dart | 10 +- lib/features/inbox/cubit/inbox_cubit.dart | 22 +- .../view/widgets/fullscreen_tags_form.dart | 29 +- .../tags/view/widgets/tags_form_field.dart | 67 +- .../labels/view/pages/labels_page.dart | 87 +-- .../view/widgets/fullscreen_label_form.dart | 62 +- .../labels/view/widgets/label_form_field.dart | 33 +- .../labels/view/widgets/label_item.dart | 14 +- .../cubit/linked_documents_cubit.dart | 5 +- .../login/cubit/authentication_cubit.dart | 137 ++-- lib/features/login/view/login_page.dart | 2 +- .../cubit/document_paging_bloc_mixin.dart | 33 +- .../cubit/saved_view_details_cubit.dart | 3 + .../view/dialogs/account_settings_dialog.dart | 2 +- .../settings/view/manage_accounts_page.dart | 24 +- .../biometric_authentication_setting.dart | 4 +- .../widgets/color_scheme_option_setting.dart | 2 +- .../view/widgets/global_settings_builder.dart | 2 +- .../widgets/language_selection_setting.dart | 2 +- .../settings/view/widgets/user_avatar.dart | 2 +- .../view/widgets/user_settings_builder.dart | 14 +- .../cubit/similar_documents_cubit.dart | 6 +- lib/main.dart | 45 +- .../lib/config/hive/hive_type_ids.dart | 47 +- packages/paperless_api/lib/paperless_api.dart | 1 + .../converters/tags_query_json_converter.dart | 34 - .../lib/src/models/document_filter.dart | 17 +- .../lib/src/models/filter_rule_model.dart | 134 ++-- .../paperless_api/lib/src/models/models.dart | 2 +- .../absolute_date_range_query.dart | 8 +- .../date_range_queries/date_range_query.dart | 1 + .../date_range_queries/date_range_unit.dart | 9 + .../relative_date_range_query.dart | 5 + .../unset_date_range_query.dart | 8 +- .../query_parameters/id_query_parameter.dart | 178 ++--- .../id_query_parameter.freezed.dart | 686 ++++++++++++++++++ .../private/tags_query_type.dart | 11 - .../models/query_parameters/query_type.dart | 10 + .../models/query_parameters/sort_field.dart | 13 + .../models/query_parameters/sort_order.dart | 7 + .../tags_query/any_assigned_tags_query.dart | 46 -- .../tags_query/exclude_tag_id_query.dart | 15 - .../tags_query/ids_tags_query.dart | 94 --- .../tags_query/include_tag_id_query.dart | 15 - .../only_not_assigned_tags_query.dart | 22 - .../tags_query/tag_id_query.dart | 35 - .../tags_query/tags_queries.dart | 7 - .../tags_query/tags_query.dart | 65 +- .../tags_query/tags_query.freezed.dart | 566 +++++++++++++++ .../models/query_parameters/text_query.dart | 14 +- .../paperless_api/test/saved_view_test.dart | 34 +- 67 files changed, 2075 insertions(+), 1079 deletions(-) rename lib/{features/settings/model => core/database/tables}/global_settings.dart (100%) rename lib/{features/login/model => core/database/tables}/user_account.dart (88%) create mode 100644 lib/core/database/tables/user_app_state.dart rename lib/{features/login/model => core/database/tables}/user_credentials.dart (100%) rename lib/{features/settings/model => core/database/tables}/user_settings.dart (80%) delete mode 100644 packages/paperless_api/lib/src/converters/tags_query_json_converter.dart create mode 100644 packages/paperless_api/lib/src/models/query_parameters/id_query_parameter.freezed.dart delete mode 100644 packages/paperless_api/lib/src/models/query_parameters/private/tags_query_type.dart delete mode 100644 packages/paperless_api/lib/src/models/query_parameters/tags_query/any_assigned_tags_query.dart delete mode 100644 packages/paperless_api/lib/src/models/query_parameters/tags_query/exclude_tag_id_query.dart delete mode 100644 packages/paperless_api/lib/src/models/query_parameters/tags_query/ids_tags_query.dart delete mode 100644 packages/paperless_api/lib/src/models/query_parameters/tags_query/include_tag_id_query.dart delete mode 100644 packages/paperless_api/lib/src/models/query_parameters/tags_query/only_not_assigned_tags_query.dart delete mode 100644 packages/paperless_api/lib/src/models/query_parameters/tags_query/tag_id_query.dart delete mode 100644 packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_queries.dart create mode 100644 packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.freezed.dart diff --git a/lib/core/config/hive/hive_config.dart b/lib/core/config/hive/hive_config.dart index 3205853..7b5df09 100644 --- a/lib/core/config/hive/hive_config.dart +++ b/lib/core/config/hive/hive_config.dart @@ -1,21 +1,23 @@ import 'package:hive_flutter/adapters.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/config/hive/custom_adapters/theme_mode_adapter.dart'; +import 'package:paperless_mobile/core/database/tables/global_settings.dart'; +import 'package:paperless_mobile/core/database/tables/user_app_state.dart'; +import 'package:paperless_mobile/core/database/tables/user_credentials.dart'; import 'package:paperless_mobile/features/login/model/authentication_information.dart'; import 'package:paperless_mobile/features/login/model/client_certificate.dart'; -import 'package:paperless_mobile/features/login/model/user_account.dart'; -import 'package:paperless_mobile/features/login/model/user_credentials.dart'; -import 'package:paperless_mobile/features/settings/model/global_settings.dart'; +import 'package:paperless_mobile/core/database/tables/user_account.dart'; import 'package:paperless_mobile/features/settings/model/color_scheme_option.dart'; -import 'package:paperless_mobile/features/settings/model/user_settings.dart'; +import 'package:paperless_mobile/core/database/tables/user_settings.dart'; class HiveBoxes { HiveBoxes._(); static const globalSettings = 'globalSettings'; - static const userSettings = 'userSettings'; static const authentication = 'authentication'; static const userCredentials = 'userCredentials'; static const userAccount = 'userAccount'; + static const userAppState = 'userAppState'; + static const userSettings = 'userSettings'; } class HiveTypeIds { @@ -28,9 +30,11 @@ class HiveTypeIds { static const clientCertificate = 5; static const userCredentials = 6; static const userAccount = 7; + static const userAppState = 8; } void registerHiveAdapters() { + registerPaperlessApiHiveTypeAdapters(); Hive.registerAdapter(ColorSchemeOptionAdapter()); Hive.registerAdapter(ThemeModeAdapter()); Hive.registerAdapter(GlobalSettingsAdapter()); @@ -39,7 +43,7 @@ void registerHiveAdapters() { Hive.registerAdapter(UserSettingsAdapter()); Hive.registerAdapter(UserCredentialsAdapter()); Hive.registerAdapter(UserAccountAdapter()); - Hive.registerAdapter(DocumentFilterAdapter()); + Hive.registerAdapter(UserAppStateAdapter()); } extension HiveSingleValueBox on Box { diff --git a/lib/features/settings/model/global_settings.dart b/lib/core/database/tables/global_settings.dart similarity index 100% rename from lib/features/settings/model/global_settings.dart rename to lib/core/database/tables/global_settings.dart diff --git a/lib/features/login/model/user_account.dart b/lib/core/database/tables/user_account.dart similarity index 88% rename from lib/features/login/model/user_account.dart rename to lib/core/database/tables/user_account.dart index f253f5c..8d53261 100644 --- a/lib/features/login/model/user_account.dart +++ b/lib/core/database/tables/user_account.dart @@ -1,6 +1,6 @@ import 'package:hive_flutter/adapters.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart'; -import 'package:paperless_mobile/features/settings/model/user_settings.dart'; +import 'package:paperless_mobile/core/database/tables/user_settings.dart'; part 'user_account.g.dart'; diff --git a/lib/core/database/tables/user_app_state.dart b/lib/core/database/tables/user_app_state.dart new file mode 100644 index 0000000..02e7399 --- /dev/null +++ b/lib/core/database/tables/user_app_state.dart @@ -0,0 +1,22 @@ +import 'package:hive_flutter/adapters.dart'; +import 'package:paperless_api/paperless_api.dart'; +import 'package:paperless_mobile/core/config/hive/hive_config.dart'; +part 'user_app_state.g.dart'; + +@HiveType(typeId: HiveTypeIds.userAppState) +class UserAppState extends HiveObject { + @HiveField(0) + final String userId; + + @HiveField(1) + DocumentFilter currentDocumentFilter; + + @HiveField(2) + List documentSearchHistory; + + UserAppState({ + required this.userId, + this.currentDocumentFilter = const DocumentFilter(), + this.documentSearchHistory = const [], + }); +} diff --git a/lib/features/login/model/user_credentials.dart b/lib/core/database/tables/user_credentials.dart similarity index 100% rename from lib/features/login/model/user_credentials.dart rename to lib/core/database/tables/user_credentials.dart diff --git a/lib/features/settings/model/user_settings.dart b/lib/core/database/tables/user_settings.dart similarity index 80% rename from lib/features/settings/model/user_settings.dart rename to lib/core/database/tables/user_settings.dart index cd4d965..f501c46 100644 --- a/lib/features/settings/model/user_settings.dart +++ b/lib/core/database/tables/user_settings.dart @@ -9,11 +9,7 @@ class UserSettings with HiveObjectMixin { @HiveField(0) bool isBiometricAuthenticationEnabled; - @HiveField(1) - DocumentFilter currentDocumentFilter; - UserSettings({ this.isBiometricAuthenticationEnabled = false, - required this.currentDocumentFilter, }); } diff --git a/lib/core/service/status_service.dart b/lib/core/service/status_service.dart index cd662f6..4e42b3d 100644 --- a/lib/core/service/status_service.dart +++ b/lib/core/service/status_service.dart @@ -9,7 +9,7 @@ import 'package:paperless_mobile/core/bloc/document_status_cubit.dart'; import 'package:paperless_mobile/core/model/document_processing_status.dart'; import 'package:paperless_mobile/features/login/model/authentication_information.dart'; import 'package:paperless_mobile/constants.dart'; -import 'package:paperless_mobile/features/login/model/user_credentials.dart'; +import 'package:paperless_mobile/core/database/tables/user_credentials.dart'; import 'package:web_socket_channel/io.dart'; abstract class StatusService { diff --git a/lib/features/document_bulk_action/view/widgets/bulk_edit_label_bottom_sheet.dart b/lib/features/document_bulk_action/view/widgets/bulk_edit_label_bottom_sheet.dart index f450ea1..670c3be 100644 --- a/lib/features/document_bulk_action/view/widgets/bulk_edit_label_bottom_sheet.dart +++ b/lib/features/document_bulk_action/view/widgets/bulk_edit_label_bottom_sheet.dart @@ -8,8 +8,7 @@ import 'package:paperless_mobile/features/document_bulk_action/cubit/document_bu import 'package:paperless_mobile/features/labels/view/widgets/label_form_field.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; -typedef LabelOptionsSelector = Map Function( - DocumentBulkActionState state); +typedef LabelOptionsSelector = Map Function(DocumentBulkActionState state); class BulkEditLabelBottomSheet extends StatefulWidget { final String title; @@ -18,7 +17,7 @@ class BulkEditLabelBottomSheet extends StatefulWidget { final LabelOptionsSelector availableOptionsSelector; final void Function(int? selectedId) onSubmit; final int? initialValue; - + const BulkEditLabelBottomSheet({ super.key, required this.title, @@ -30,19 +29,16 @@ class BulkEditLabelBottomSheet extends StatefulWidget { }); @override - State> createState() => - _BulkEditLabelBottomSheetState(); + State> createState() => _BulkEditLabelBottomSheetState(); } -class _BulkEditLabelBottomSheetState - extends State> { +class _BulkEditLabelBottomSheetState extends State> { final _formKey = GlobalKey(); @override Widget build(BuildContext context) { return Padding( - padding: - EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), child: BlocBuilder( builder: (context, state) { return Padding( @@ -59,8 +55,7 @@ class _BulkEditLabelBottomSheetState FormBuilder( key: _formKey, child: LabelFormField( - initialValue: - IdQueryParameter.fromId(widget.initialValue), + initialValue: IdQueryParameter.fromId(widget.initialValue), name: "labelFormField", options: widget.availableOptionsSelector(state), labelText: widget.formFieldLabel, @@ -75,12 +70,11 @@ class _BulkEditLabelBottomSheetState const SizedBox(width: 16), FilledButton( onPressed: () { - if (_formKey.currentState?.saveAndValidate() ?? - false) { - final value = _formKey.currentState - ?.getRawValue('labelFormField') + if (_formKey.currentState?.saveAndValidate() ?? false) { + final value = _formKey.currentState?.getRawValue('labelFormField') as IdQueryParameter?; - widget.onSubmit(value?.id); + widget + .onSubmit(value?.maybeWhen(fromId: (id) => id, orElse: () => null)); } }, child: Text(S.of(context)!.apply), diff --git a/lib/features/document_details/view/widgets/document_download_button.dart b/lib/features/document_details/view/widgets/document_download_button.dart index 07b4628..fa80279 100644 --- a/lib/features/document_details/view/widgets/document_download_button.dart +++ b/lib/features/document_details/view/widgets/document_download_button.dart @@ -5,7 +5,7 @@ import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/features/document_details/cubit/document_details_cubit.dart'; import 'package:paperless_mobile/features/document_details/view/dialogs/select_file_type_dialog.dart'; -import 'package:paperless_mobile/features/settings/model/global_settings.dart'; +import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/helpers/message_helpers.dart'; diff --git a/lib/features/document_edit/view/document_edit_page.dart b/lib/features/document_edit/view/document_edit_page.dart index b139786..03b3208 100644 --- a/lib/features/document_edit/view/document_edit_page.dart +++ b/lib/features/document_edit/view/document_edit_page.dart @@ -50,8 +50,8 @@ class _DocumentEditPageState extends State { @override void initState() { super.initState(); - _filteredSuggestions = widget.suggestions - ?.documentDifference(context.read().state.document); + _filteredSuggestions = + widget.suggestions?.documentDifference(context.read().state.document); } @override @@ -95,16 +95,14 @@ class _DocumentEditPageState extends State { ListView( children: [ _buildTitleFormField(state.document.title).padded(), - _buildCreatedAtFormField(state.document.created) - .padded(), + _buildCreatedAtFormField(state.document.created).padded(), // Correspondent form field Column( children: [ LabelFormField( showAnyAssignedOption: false, showNotAssignedOption: false, - addLabelPageBuilder: (initialValue) => - RepositoryProvider.value( + addLabelPageBuilder: (initialValue) => RepositoryProvider.value( value: context.read(), child: AddCorrespondentPage( initialName: initialValue, @@ -112,30 +110,20 @@ class _DocumentEditPageState extends State { ), addLabelText: S.of(context)!.addCorrespondent, labelText: S.of(context)!.correspondent, - options: context - .watch() - .state - .correspondents, + options: context.watch().state.correspondents, initialValue: IdQueryParameter.fromId( state.document.correspondent, ), name: fkCorrespondent, prefixIcon: const Icon(Icons.person_outlined), ), - if (_filteredSuggestions - ?.hasSuggestedCorrespondents ?? - false) + if (_filteredSuggestions?.hasSuggestedCorrespondents ?? false) _buildSuggestionsSkeleton( - suggestions: - _filteredSuggestions!.correspondents, - itemBuilder: (context, itemData) => - ActionChip( - label: Text( - state.correspondents[itemData]!.name), + suggestions: _filteredSuggestions!.correspondents, + itemBuilder: (context, itemData) => ActionChip( + label: Text(state.correspondents[itemData]!.name), onPressed: () { - _formKey - .currentState?.fields[fkCorrespondent] - ?.didChange( + _formKey.currentState?.fields[fkCorrespondent]?.didChange( IdQueryParameter.fromId(itemData), ); }, @@ -149,8 +137,7 @@ class _DocumentEditPageState extends State { LabelFormField( showAnyAssignedOption: false, showNotAssignedOption: false, - addLabelPageBuilder: (currentInput) => - RepositoryProvider.value( + addLabelPageBuilder: (currentInput) => RepositoryProvider.value( value: context.read(), child: AddDocumentTypePage( initialName: currentInput, @@ -158,26 +145,18 @@ class _DocumentEditPageState extends State { ), addLabelText: S.of(context)!.addDocumentType, labelText: S.of(context)!.documentType, - initialValue: IdQueryParameter.fromId( - state.document.documentType), + initialValue: IdQueryParameter.fromId(state.document.documentType), options: state.documentTypes, name: _DocumentEditPageState.fkDocumentType, - prefixIcon: - const Icon(Icons.description_outlined), + prefixIcon: const Icon(Icons.description_outlined), ), - if (_filteredSuggestions - ?.hasSuggestedDocumentTypes ?? - false) + if (_filteredSuggestions?.hasSuggestedDocumentTypes ?? false) _buildSuggestionsSkeleton( - suggestions: - _filteredSuggestions!.documentTypes, - itemBuilder: (context, itemData) => - ActionChip( - label: Text( - state.documentTypes[itemData]!.name), - onPressed: () => _formKey - .currentState?.fields[fkDocumentType] - ?.didChange( + suggestions: _filteredSuggestions!.documentTypes, + itemBuilder: (context, itemData) => ActionChip( + label: Text(state.documentTypes[itemData]!.name), + onPressed: () => + _formKey.currentState?.fields[fkDocumentType]?.didChange( IdQueryParameter.fromId(itemData), ), ), @@ -190,17 +169,14 @@ class _DocumentEditPageState extends State { LabelFormField( showAnyAssignedOption: false, showNotAssignedOption: false, - addLabelPageBuilder: (initialValue) => - RepositoryProvider.value( + addLabelPageBuilder: (initialValue) => RepositoryProvider.value( value: context.read(), - child: AddStoragePathPage( - initalName: initialValue), + child: AddStoragePathPage(initalName: initialValue), ), addLabelText: S.of(context)!.addStoragePath, labelText: S.of(context)!.storagePath, options: state.storagePaths, - initialValue: IdQueryParameter.fromId( - state.document.storagePath), + initialValue: IdQueryParameter.fromId(state.document.storagePath), name: fkStoragePath, prefixIcon: const Icon(Icons.folder_outlined), ), @@ -213,8 +189,8 @@ class _DocumentEditPageState extends State { allowOnlySelection: true, allowCreation: true, allowExclude: false, - initialValue: IdsTagsQuery.included( - state.document.tags, + initialValue: TagsQuery.ids( + include: state.document.tags, ), ).padded(), if (_filteredSuggestions?.tags @@ -223,8 +199,7 @@ class _DocumentEditPageState extends State { .isNotEmpty ?? false) _buildSuggestionsSkeleton( - suggestions: - (_filteredSuggestions?.tags.toSet() ?? {}), + suggestions: (_filteredSuggestions?.tags.toSet() ?? {}), itemBuilder: (context, itemData) { final tag = state.tags[itemData]!; return ActionChip( @@ -234,17 +209,15 @@ class _DocumentEditPageState extends State { ), backgroundColor: tag.color, onPressed: () { - final currentTags = _formKey.currentState - ?.fields[fkTags]?.value as TagsQuery; - if (currentTags is IdsTagsQuery) { - _formKey.currentState?.fields[fkTags] - ?.didChange((IdsTagsQuery.fromIds( - {...currentTags.ids, itemData}))); - } else { - _formKey.currentState?.fields[fkTags] - ?.didChange((IdsTagsQuery.fromIds( - {itemData}))); - } + final currentTags = + _formKey.currentState?.fields[fkTags]?.value as TagsQuery; + _formKey.currentState?.fields[fkTags]?.didChange( + currentTags.maybeWhen( + ids: (include, exclude) => TagsQuery.ids( + include: [...include, itemData], exclude: exclude), + orElse: () => TagsQuery.ids(include: [itemData]), + ), + ); }, ); }, @@ -282,13 +255,14 @@ class _DocumentEditPageState extends State { if (_formKey.currentState?.saveAndValidate() ?? false) { final values = _formKey.currentState!.value; var mergedDocument = document.copyWith( - title: values[fkTitle], - created: values[fkCreatedDate], - documentType: () => (values[fkDocumentType] as IdQueryParameter).id, - correspondent: () => (values[fkCorrespondent] as IdQueryParameter).id, - storagePath: () => (values[fkStoragePath] as IdQueryParameter).id, - tags: (values[fkTags] as IdsTagsQuery).includedIds, - content: values[fkContent]); + title: values[fkTitle], + created: values[fkCreatedDate], + documentType: () => (values[fkDocumentType] as SetIdQueryParameter).id, + correspondent: () => (values[fkCorrespondent] as SetIdQueryParameter).id, + storagePath: () => (values[fkStoragePath] as SetIdQueryParameter).id, + tags: (values[fkTags] as IdsTagsQuery).include, + content: values[fkContent], + ); setState(() { _isSubmitLoading = true; }); @@ -342,8 +316,7 @@ class _DocumentEditPageState extends State { suggestions: _filteredSuggestions!.dates, itemBuilder: (context, itemData) => ActionChip( label: Text(DateFormat.yMMMd().format(itemData)), - onPressed: () => _formKey.currentState?.fields[fkCreatedDate] - ?.didChange(itemData), + onPressed: () => _formKey.currentState?.fields[fkCreatedDate]?.didChange(itemData), ), ), ], @@ -372,8 +345,7 @@ class _DocumentEditPageState extends State { itemBuilder: (context, index) => ColoredChipWrapper( child: itemBuilder(context, suggestions.elementAt(index)), ), - separatorBuilder: (BuildContext context, int index) => - const SizedBox(width: 4.0), + separatorBuilder: (BuildContext context, int index) => const SizedBox(width: 4.0), ), ), ], diff --git a/lib/features/document_search/cubit/document_search_cubit.dart b/lib/features/document_search/cubit/document_search_cubit.dart index 8dfe803..355e453 100644 --- a/lib/features/document_search/cubit/document_search_cubit.dart +++ b/lib/features/document_search/cubit/document_search_cubit.dart @@ -3,18 +3,17 @@ import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart'; -import 'package:paperless_mobile/features/login/model/user_account.dart'; +import 'package:paperless_mobile/core/database/tables/user_account.dart'; import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart'; -import 'package:paperless_mobile/features/settings/model/user_settings.dart'; +import 'package:paperless_mobile/core/database/tables/user_settings.dart'; import 'package:paperless_mobile/features/settings/model/view_type.dart'; part 'document_search_state.dart'; part 'document_search_cubit.g.dart'; -class DocumentSearchCubit extends HydratedCubit - with DocumentPagingBlocMixin { +class DocumentSearchCubit extends HydratedCubit with DocumentPagingBlocMixin { @override final PaperlessDocumentsApi api; @@ -58,8 +57,7 @@ class DocumentSearchCubit extends HydratedCubit state.copyWith( searchHistory: [ query, - ...state.searchHistory - .whereNot((previousQuery) => previousQuery == query) + ...state.searchHistory.whereNot((previousQuery) => previousQuery == query) ], ), ); @@ -72,9 +70,7 @@ class DocumentSearchCubit extends HydratedCubit void removeHistoryEntry(String entry) { emit( state.copyWith( - searchHistory: state.searchHistory - .whereNot((element) => element == entry) - .toList(), + searchHistory: state.searchHistory.whereNot((element) => element == entry).toList(), ), ); } @@ -121,6 +117,5 @@ class DocumentSearchCubit extends HydratedCubit } @override - // TODO: implement account - UserAccount get account => throw UnimplementedError(); + Future onFilterUpdated(DocumentFilter filter) async {} } diff --git a/lib/features/document_search/view/sliver_search_bar.dart b/lib/features/document_search/view/sliver_search_bar.dart index e4eb87e..cd7d3cd 100644 --- a/lib/features/document_search/view/sliver_search_bar.dart +++ b/lib/features/document_search/view/sliver_search_bar.dart @@ -5,10 +5,9 @@ import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.da import 'package:paperless_mobile/core/bloc/paperless_server_information_state.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/delegate/customizable_sliver_persistent_header_delegate.dart'; -import 'package:paperless_mobile/core/widgets/material/search/m3_search_bar.dart' - as s; +import 'package:paperless_mobile/core/widgets/material/search/m3_search_bar.dart' as s; import 'package:paperless_mobile/features/document_search/view/document_search_page.dart'; -import 'package:paperless_mobile/features/login/model/user_account.dart'; +import 'package:paperless_mobile/core/database/tables/user_account.dart'; import 'package:paperless_mobile/features/settings/view/dialogs/account_settings_dialog.dart'; import 'package:paperless_mobile/features/settings/view/manage_accounts_page.dart'; import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; @@ -46,14 +45,10 @@ class SliverSearchBar extends StatelessWidget { icon: GlobalSettingsBuilder( builder: (context, settings) { return ValueListenableBuilder( - valueListenable: - Hive.box(HiveBoxes.userAccount) - .listenable(), + valueListenable: Hive.box(HiveBoxes.userAccount).listenable(), builder: (context, box, _) { final account = box.get(settings.currentLoggedInUser!)!; - return UserAvatar( - userId: settings.currentLoggedInUser!, - account: account); + return UserAvatar(userId: settings.currentLoggedInUser!, account: account); }, ); }, diff --git a/lib/features/document_upload/view/document_upload_preparation_page.dart b/lib/features/document_upload/view/document_upload_preparation_page.dart index 0ccfd69..881fe2b 100644 --- a/lib/features/document_upload/view/document_upload_preparation_page.dart +++ b/lib/features/document_upload/view/document_upload_preparation_page.dart @@ -41,12 +41,10 @@ class DocumentUploadPreparationPage extends StatefulWidget { }) : super(key: key); @override - State createState() => - _DocumentUploadPreparationPageState(); + State createState() => _DocumentUploadPreparationPageState(); } -class _DocumentUploadPreparationPageState - extends State { +class _DocumentUploadPreparationPageState extends State { static const fkFileName = "filename"; static final fileNameDateFormat = DateFormat("yyyy_MM_ddTHH_mm_ss"); @@ -73,8 +71,7 @@ class _DocumentUploadPreparationPageState title: Text(S.of(context)!.prepareDocument), bottom: _isUploadLoading ? const PreferredSize( - child: LinearProgressIndicator(), - preferredSize: Size.fromHeight(4.0)) + child: LinearProgressIndicator(), preferredSize: Size.fromHeight(4.0)) : null, ), floatingActionButton: Visibility( @@ -95,8 +92,7 @@ class _DocumentUploadPreparationPageState FormBuilderTextField( autovalidateMode: AutovalidateMode.always, name: DocumentModel.titleKey, - initialValue: - widget.title ?? "scan_${fileNameDateFormat.format(_now)}", + initialValue: widget.title ?? "scan_${fileNameDateFormat.format(_now)}", validator: (value) { if (value?.trim().isEmpty ?? true) { return S.of(context)!.thisFieldIsRequired; @@ -108,22 +104,18 @@ class _DocumentUploadPreparationPageState suffixIcon: IconButton( icon: const Icon(Icons.close), onPressed: () { - _formKey.currentState?.fields[DocumentModel.titleKey] - ?.didChange(""); + _formKey.currentState?.fields[DocumentModel.titleKey]?.didChange(""); if (_syncTitleAndFilename) { - _formKey.currentState?.fields[fkFileName] - ?.didChange(""); + _formKey.currentState?.fields[fkFileName]?.didChange(""); } }, ), errorText: _errors[DocumentModel.titleKey], ), onChanged: (value) { - final String transformedValue = - _formatFilename(value ?? ''); + final String transformedValue = _formatFilename(value ?? ''); if (_syncTitleAndFilename) { - _formKey.currentState?.fields[fkFileName] - ?.didChange(transformedValue); + _formKey.currentState?.fields[fkFileName]?.didChange(transformedValue); } }, ), @@ -138,12 +130,10 @@ class _DocumentUploadPreparationPageState suffixText: widget.fileExtension, suffixIcon: IconButton( icon: const Icon(Icons.clear), - onPressed: () => _formKey.currentState?.fields[fkFileName] - ?.didChange(''), + onPressed: () => _formKey.currentState?.fields[fkFileName]?.didChange(''), ), ), - initialValue: widget.filename ?? - "scan_${fileNameDateFormat.format(_now)}", + initialValue: widget.filename ?? "scan_${fileNameDateFormat.format(_now)}", ), // Synchronize title and filename SwitchListTile( @@ -153,13 +143,10 @@ class _DocumentUploadPreparationPageState () => _syncTitleAndFilename = value, ); if (_syncTitleAndFilename) { - final String transformedValue = _formatFilename(_formKey - .currentState - ?.fields[DocumentModel.titleKey] - ?.value as String); + final String transformedValue = _formatFilename( + _formKey.currentState?.fields[DocumentModel.titleKey]?.value as String); if (_syncTitleAndFilename) { - _formKey.currentState?.fields[fkFileName] - ?.didChange(transformedValue); + _formKey.currentState?.fields[fkFileName]?.didChange(transformedValue); } } }, @@ -184,8 +171,7 @@ class _DocumentUploadPreparationPageState ? IconButton( icon: const Icon(Icons.close), onPressed: () { - _formKey.currentState! - .fields[DocumentModel.createdKey] + _formKey.currentState!.fields[DocumentModel.createdKey] ?.didChange(null); }, ) @@ -196,8 +182,7 @@ class _DocumentUploadPreparationPageState LabelFormField( showAnyAssignedOption: false, showNotAssignedOption: false, - addLabelPageBuilder: (initialName) => - RepositoryProvider.value( + addLabelPageBuilder: (initialName) => RepositoryProvider.value( value: context.read(), child: AddCorrespondentPage(initialName: initialName), ), @@ -211,8 +196,7 @@ class _DocumentUploadPreparationPageState LabelFormField( showAnyAssignedOption: false, showNotAssignedOption: false, - addLabelPageBuilder: (initialName) => - RepositoryProvider.value( + addLabelPageBuilder: (initialName) => RepositoryProvider.value( value: context.read(), child: AddDocumentTypePage(initialName: initialName), ), @@ -252,10 +236,9 @@ class _DocumentUploadPreparationPageState final createdAt = fv[DocumentModel.createdKey] as DateTime?; final title = fv[DocumentModel.titleKey] as String; - final docType = fv[DocumentModel.documentTypeKey] as IdQueryParameter; + final docType = fv[DocumentModel.documentTypeKey] as SetIdQueryParameter; final tags = fv[DocumentModel.tagsKey] as IdsTagsQuery; - final correspondent = - fv[DocumentModel.correspondentKey] as IdQueryParameter; + final correspondent = fv[DocumentModel.correspondentKey] as SetIdQueryParameter; final taskId = await cubit.upload( widget.fileBytes, @@ -266,7 +249,7 @@ class _DocumentUploadPreparationPageState title: title, documentType: docType.id, correspondent: correspondent.id, - tags: tags.ids, + tags: tags.include, createdAt: createdAt, ); showSnackBar( @@ -283,8 +266,7 @@ class _DocumentUploadPreparationPageState setState(() => _errors = errors); } catch (unknownError, stackTrace) { debugPrint(unknownError.toString()); - showErrorMessage( - context, const PaperlessServerException.unknown(), stackTrace); + showErrorMessage(context, const PaperlessServerException.unknown(), stackTrace); } finally { setState(() { _isUploadLoading = false; diff --git a/lib/features/documents/cubit/documents_cubit.dart b/lib/features/documents/cubit/documents_cubit.dart index 6baedda..2d9b558 100644 --- a/lib/features/documents/cubit/documents_cubit.dart +++ b/lib/features/documents/cubit/documents_cubit.dart @@ -4,9 +4,10 @@ import 'package:flutter/foundation.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:paperless_api/paperless_api.dart'; +import 'package:paperless_mobile/core/database/tables/user_app_state.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart'; -import 'package:paperless_mobile/features/login/model/user_account.dart'; +import 'package:paperless_mobile/core/database/tables/user_account.dart'; import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart'; import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart'; import 'package:paperless_mobile/features/settings/model/view_type.dart'; @@ -14,8 +15,7 @@ import 'package:paperless_mobile/features/settings/model/view_type.dart'; part 'documents_cubit.g.dart'; part 'documents_state.dart'; -class DocumentsCubit extends HydratedCubit - with DocumentPagingBlocMixin { +class DocumentsCubit extends HydratedCubit with DocumentPagingBlocMixin { @override final PaperlessDocumentsApi api; @@ -24,24 +24,21 @@ class DocumentsCubit extends HydratedCubit @override final DocumentChangedNotifier notifier; - @override - final UserAccount account; + final UserAppState _userState; DocumentsCubit( this.api, this.notifier, this._labelRepository, - this.account, - ) : super(DocumentsState(filter: account.settings.currentDocumentFilter)) { + this._userState, + ) : super(DocumentsState(filter: _userState.currentDocumentFilter)) { notifier.addListener( this, onUpdated: (document) { replace(document); emit( state.copyWith( - selection: state.selection - .map((e) => e.id == document.id ? document : e) - .toList(), + selection: state.selection.map((e) => e.id == document.id ? document : e).toList(), ), ); }, @@ -49,8 +46,7 @@ class DocumentsCubit extends HydratedCubit remove(document); emit( state.copyWith( - selection: - state.selection.where((e) => e.id != document.id).toList(), + selection: state.selection.where((e) => e.id != document.id).toList(), ), ); }, @@ -84,9 +80,7 @@ class DocumentsCubit extends HydratedCubit if (state.selectedIds.contains(model.id)) { emit( state.copyWith( - selection: state.selection - .where((element) => element.id != model.id) - .toList(), + selection: state.selection.where((element) => element.id != model.id).toList(), ), ); } else { @@ -129,4 +123,10 @@ class DocumentsCubit extends HydratedCubit void setViewType(ViewType viewType) { emit(state.copyWith(viewType: viewType)); } + + @override + Future onFilterUpdated(DocumentFilter filter) async { + _userState.currentDocumentFilter = filter; + await _userState.save(); + } } diff --git a/lib/features/documents/view/pages/documents_page.dart b/lib/features/documents/view/pages/documents_page.dart index d985485..f463465 100644 --- a/lib/features/documents/view/pages/documents_page.dart +++ b/lib/features/documents/view/pages/documents_page.dart @@ -1,4 +1,5 @@ import 'package:badges/badges.dart' as b; +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:paperless_api/paperless_api.dart'; @@ -41,12 +42,9 @@ class DocumentsPage extends StatefulWidget { State createState() => _DocumentsPageState(); } -class _DocumentsPageState extends State - with SingleTickerProviderStateMixin { - final SliverOverlapAbsorberHandle searchBarHandle = - SliverOverlapAbsorberHandle(); - final SliverOverlapAbsorberHandle tabBarHandle = - SliverOverlapAbsorberHandle(); +class _DocumentsPageState extends State with SingleTickerProviderStateMixin { + final SliverOverlapAbsorberHandle searchBarHandle = SliverOverlapAbsorberHandle(); + final SliverOverlapAbsorberHandle tabBarHandle = SliverOverlapAbsorberHandle(); late final TabController _tabController; int _currentTab = 0; @@ -83,8 +81,7 @@ class _DocumentsPageState extends State @override Widget build(BuildContext context) { return BlocListener( - listenWhen: (previous, current) => - !previous.isSuccess && current.isSuccess, + listenWhen: (previous, current) => !previous.isSuccess && current.isSuccess, listener: (context, state) { showSnackBar( context, @@ -101,8 +98,7 @@ class _DocumentsPageState extends State }, child: BlocConsumer( listenWhen: (previous, current) => - previous != ConnectivityState.connected && - current == ConnectivityState.connected, + previous != ConnectivityState.connected && current == ConnectivityState.connected, listener: (context, state) { try { context.read().reload(); @@ -150,11 +146,7 @@ class _DocumentsPageState extends State resizeToAvoidBottomInset: true, body: WillPopScope( onWillPop: () async { - if (context - .read() - .state - .selection - .isNotEmpty) { + if (context.read().state.selection.isNotEmpty) { context.read().resetSelection(); } return false; @@ -189,8 +181,7 @@ class _DocumentsPageState extends State } return SliverPersistentHeader( pinned: true, - delegate: - CustomizableSliverPersistentHeaderDelegate( + delegate: CustomizableSliverPersistentHeaderDelegate( minExtent: kTextTabBarHeight, maxExtent: kTextTabBarHeight, child: ColoredTabBar( @@ -214,22 +205,15 @@ class _DocumentsPageState extends State if (metrics.maxScrollExtent == 0) { return true; } - final desiredTab = - (metrics.pixels / metrics.maxScrollExtent) - .round(); - if (metrics.axis == Axis.horizontal && - _currentTab != desiredTab) { + final desiredTab = (metrics.pixels / metrics.maxScrollExtent).round(); + if (metrics.axis == Axis.horizontal && _currentTab != desiredTab) { setState(() => _currentTab = desiredTab); } return false; }, child: TabBarView( controller: _tabController, - physics: context - .watch() - .state - .selection - .isNotEmpty + physics: context.watch().state.selection.isNotEmpty ? const NeverScrollableScrollPhysics() : null, children: [ @@ -297,19 +281,13 @@ class _DocumentsPageState extends State final currState = context.read().state; final max = notification.metrics.maxScrollExtent; - if (max == 0 || - _currentTab != 0 || - currState.isLoading || - currState.isLastPageLoaded) { + if (max == 0 || _currentTab != 0 || currState.isLoading || currState.isLastPageLoaded) { return false; } final offset = notification.metrics.pixels; if (offset >= max * 0.7) { - context - .read() - .loadMore() - .onError( + context.read().loadMore().onError( (error, stackTrace) => showErrorMessage( context, error, @@ -344,8 +322,7 @@ class _DocumentsPageState extends State return SliverAdaptiveDocumentsView( viewType: state.viewType, onTap: _openDetails, - onSelected: - context.read().toggleDocumentSelection, + onSelected: context.read().toggleDocumentSelection, hasInternetConnection: connectivityState.isConnected, onTagSelected: _addTagToFilter, onCorrespondentSelected: _addCorrespondentToFilter, @@ -436,8 +413,7 @@ class _DocumentsPageState extends State snapSizes: const [0.9, 1], initialChildSize: .9, maxChildSize: 1, - builder: (context, controller) => - BlocBuilder( + builder: (context, controller) => BlocBuilder( builder: (context, state) { return DocumentFilterPanel( initialFilter: context.read().state.filter, @@ -458,9 +434,7 @@ class _DocumentsPageState extends State if (filterIntent.shouldReset) { await context.read().resetFilter(); } else { - await context - .read() - .updateFilter(filter: filterIntent.filter!); + await context.read().updateFilter(filter: filterIntent.filter!); } } on PaperlessServerException catch (error, stackTrace) { showErrorMessage(context, error, stackTrace); @@ -480,20 +454,21 @@ class _DocumentsPageState extends State void _addTagToFilter(int tagId) { try { - final tagsQuery = - context.read().state.filter.tags is IdsTagsQuery - ? context.read().state.filter.tags as IdsTagsQuery - : const IdsTagsQuery(); - if (tagsQuery.includedIds.contains(tagId)) { + final tagsQuery = context.read().state.filter.tags is IdsTagsQuery + ? context.read().state.filter.tags as IdsTagsQuery + : const IdsTagsQuery(); + if (tagsQuery.include.contains(tagId)) { context.read().updateCurrentFilter( (filter) => filter.copyWith( - tags: tagsQuery.withIdsRemoved([tagId]), + tags: tagsQuery.copyWith( + include: tagsQuery.include.whereNot((id) => id == tagId), + exclude: tagsQuery.exclude.whereNot((id) => id == tagId)), ), ); } else { context.read().updateCurrentFilter( (filter) => filter.copyWith( - tags: tagsQuery.withIdQueriesAdded([IncludeTagIdQuery(tagId)]), + tags: tagsQuery.copyWith(include: [...tagsQuery.include, tagId]), ), ); } @@ -505,16 +480,17 @@ class _DocumentsPageState extends State void _addCorrespondentToFilter(int? correspondentId) { final cubit = context.read(); try { - if (cubit.state.filter.correspondent.id == correspondentId) { - cubit.updateCurrentFilter( - (filter) => - filter.copyWith(correspondent: const IdQueryParameter.unset()), - ); - } else { - cubit.updateCurrentFilter( - (filter) => filter.copyWith( - correspondent: IdQueryParameter.fromId(correspondentId)), - ); + final correspondent = cubit.state.filter.correspondent; + if (correspondent is SetIdQueryParameter) { + if (correspondent.id == correspondentId) { + cubit.updateCurrentFilter( + (filter) => filter.copyWith(correspondent: const IdQueryParameter.unset()), + ); + } else { + cubit.updateCurrentFilter( + (filter) => filter.copyWith(correspondent: IdQueryParameter.fromId(correspondentId)), + ); + } } } on PaperlessServerException catch (error, stackTrace) { showErrorMessage(context, error, stackTrace); @@ -524,16 +500,17 @@ class _DocumentsPageState extends State void _addDocumentTypeToFilter(int? documentTypeId) { final cubit = context.read(); try { - if (cubit.state.filter.documentType.id == documentTypeId) { - cubit.updateCurrentFilter( - (filter) => - filter.copyWith(documentType: const IdQueryParameter.unset()), - ); - } else { - cubit.updateCurrentFilter( - (filter) => filter.copyWith( - documentType: IdQueryParameter.fromId(documentTypeId)), - ); + final documentType = cubit.state.filter.documentType; + if (documentType is SetIdQueryParameter) { + if (documentType.id == documentTypeId) { + cubit.updateCurrentFilter( + (filter) => filter.copyWith(documentType: const IdQueryParameter.unset()), + ); + } else { + cubit.updateCurrentFilter( + (filter) => filter.copyWith(documentType: IdQueryParameter.fromId(documentTypeId)), + ); + } } } on PaperlessServerException catch (error, stackTrace) { showErrorMessage(context, error, stackTrace); @@ -543,16 +520,17 @@ class _DocumentsPageState extends State void _addStoragePathToFilter(int? pathId) { final cubit = context.read(); try { - if (cubit.state.filter.correspondent.id == pathId) { - cubit.updateCurrentFilter( - (filter) => - filter.copyWith(storagePath: const IdQueryParameter.unset()), - ); - } else { - cubit.updateCurrentFilter( - (filter) => - filter.copyWith(storagePath: IdQueryParameter.fromId(pathId)), - ); + final path = cubit.state.filter.documentType; + if (path is SetIdQueryParameter) { + if (path.id == pathId) { + cubit.updateCurrentFilter( + (filter) => filter.copyWith(storagePath: const IdQueryParameter.unset()), + ); + } else { + cubit.updateCurrentFilter( + (filter) => filter.copyWith(storagePath: IdQueryParameter.fromId(pathId)), + ); + } } } on PaperlessServerException catch (error, stackTrace) { showErrorMessage(context, error, stackTrace); diff --git a/lib/features/home/view/home_page.dart b/lib/features/home/view/home_page.dart index 5c577d0..77b00ca 100644 --- a/lib/features/home/view/home_page.dart +++ b/lib/features/home/view/home_page.dart @@ -11,6 +11,7 @@ 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/config/hive/hive_config.dart'; +import 'package:paperless_mobile/core/database/tables/user_app_state.dart'; import 'package:paperless_mobile/core/global/constants.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/saved_view_repository.dart'; @@ -28,7 +29,7 @@ import 'package:paperless_mobile/features/inbox/view/pages/inbox_page.dart'; import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart'; import 'package:paperless_mobile/features/labels/view/pages/labels_page.dart'; import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart'; -import 'package:paperless_mobile/features/login/model/user_account.dart'; +import 'package:paperless_mobile/core/database/tables/user_account.dart'; import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart'; import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart'; import 'package:paperless_mobile/features/sharing/share_intent_queue.dart'; @@ -245,7 +246,7 @@ class _HomePageState extends State with WidgetsBindingObserver { context.read(), context.read(), context.read(), - Hive.box(HiveBoxes.userAccount).get(userId)!, + Hive.box(HiveBoxes.userAppState).get(userId)!, )..reload(), ), BlocProvider( @@ -280,8 +281,7 @@ class _HomePageState extends State with WidgetsBindingObserver { listeners: [ BlocListener( //Only re-initialize data if the connectivity changed from not connected to connected - listenWhen: (previous, current) => - current == ConnectivityState.connected, + listenWhen: (previous, current) => current == ConnectivityState.connected, listener: (context, state) { _initializeData(context); }, @@ -290,9 +290,7 @@ class _HomePageState extends State with WidgetsBindingObserver { listener: (context, state) { if (state.task != null) { // Handle local notifications on task change (only when app is running for now). - context - .read() - .notifyTaskChanged(state.task!); + context.read().notifyTaskChanged(state.task!); } }, ), @@ -305,9 +303,7 @@ class _HomePageState extends State with WidgetsBindingObserver { children: [ NavigationRail( labelType: NavigationRailLabelType.all, - destinations: destinations - .map((e) => e.toNavigationRailDestination()) - .toList(), + destinations: destinations.map((e) => e.toNavigationRailDestination()).toList(), selectedIndex: _currentIndex, onDestinationSelected: _onNavigationChanged, ), @@ -325,8 +321,7 @@ class _HomePageState extends State with WidgetsBindingObserver { elevation: 4.0, selectedIndex: _currentIndex, onDestinationSelected: _onNavigationChanged, - destinations: - destinations.map((e) => e.toNavigationDestination()).toList(), + destinations: destinations.map((e) => e.toNavigationDestination()).toList(), ), body: routes[_currentIndex], ); diff --git a/lib/features/home/view/widget/verify_identity_page.dart b/lib/features/home/view/widget/verify_identity_page.dart index c9c193e..45e1e42 100644 --- a/lib/features/home/view/widget/verify_identity_page.dart +++ b/lib/features/home/view/widget/verify_identity_page.dart @@ -5,7 +5,7 @@ import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/saved_view_repository.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart'; -import 'package:paperless_mobile/features/settings/model/global_settings.dart'; +import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/features/settings/view/widgets/user_settings_builder.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; @@ -32,9 +32,7 @@ class VerifyIdentityPage extends StatelessWidget { return Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text(S - .of(context)! - .useTheConfiguredBiometricFactorToAuthenticate) + Text(S.of(context)!.useTheConfiguredBiometricFactorToAuthenticate) .paddedSymmetrically(horizontal: 16), const Icon( Icons.fingerprint, @@ -56,9 +54,7 @@ class VerifyIdentityPage extends StatelessWidget { ), ), ElevatedButton( - onPressed: () => context - .read() - .restoreSessionState(), + onPressed: () => context.read().restoreSessionState(), child: Text(S.of(context)!.verifyIdentity), ), ], diff --git a/lib/features/inbox/cubit/inbox_cubit.dart b/lib/features/inbox/cubit/inbox_cubit.dart index 3e15ca4..aadad67 100644 --- a/lib/features/inbox/cubit/inbox_cubit.dart +++ b/lib/features/inbox/cubit/inbox_cubit.dart @@ -13,8 +13,7 @@ import 'package:paperless_mobile/features/paged_document_view/cubit/document_pag part 'inbox_cubit.g.dart'; part 'inbox_state.dart'; -class InboxCubit extends HydratedCubit - with DocumentPagingBlocMixin { +class InboxCubit extends HydratedCubit with DocumentPagingBlocMixin { final LabelRepository _labelRepository; final PaperlessDocumentsApi _documentsApi; @@ -39,10 +38,7 @@ class InboxCubit extends HydratedCubit this, onDeleted: remove, onUpdated: (document) { - if (document.tags - .toSet() - .intersection(state.inboxTags.toSet()) - .isEmpty) { + if (document.tags.toSet().intersection(state.inboxTags.toSet()).isEmpty) { remove(document); emit(state.copyWith(itemsInInboxCount: state.itemsInInboxCount - 1)); } else { @@ -101,7 +97,7 @@ class InboxCubit extends HydratedCubit updateFilter( filter: DocumentFilter( sortField: SortField.added, - tags: IdsTagsQuery.fromIds(inboxTags), + tags: TagsQuery.ids(include: inboxTags), ), ); } @@ -131,7 +127,7 @@ class InboxCubit extends HydratedCubit updateFilter( filter: DocumentFilter( sortField: SortField.added, - tags: IdsTagsQuery.fromIds(inboxTags), + tags: TagsQuery.ids(include: inboxTags), ), ); } @@ -141,8 +137,7 @@ class InboxCubit extends HydratedCubit /// from the inbox. /// Future> removeFromInbox(DocumentModel document) async { - final tagsToRemove = - document.tags.toSet().intersection(state.inboxTags.toSet()); + final tagsToRemove = document.tags.toSet().intersection(state.inboxTags.toSet()); final updatedTags = {...document.tags}..removeAll(tagsToRemove); final updatedDocument = await api.update( @@ -196,8 +191,8 @@ class InboxCubit extends HydratedCubit Future assignAsn(DocumentModel document) async { if (document.archiveSerialNumber == null) { final int asn = await _documentsApi.findNextAsn(); - final updatedDocument = await _documentsApi - .update(document.copyWith(archiveSerialNumber: () => asn)); + final updatedDocument = + await _documentsApi.update(document.copyWith(archiveSerialNumber: () => asn)); replace(updatedDocument); } @@ -222,4 +217,7 @@ class InboxCubit extends HydratedCubit _labelRepository.removeListener(this); return super.close(); } + + @override + Future onFilterUpdated(DocumentFilter filter) async {} } diff --git a/lib/features/labels/tags/view/widgets/fullscreen_tags_form.dart b/lib/features/labels/tags/view/widgets/fullscreen_tags_form.dart index 2305558..3cbda0c 100644 --- a/lib/features/labels/tags/view/widgets/fullscreen_tags_form.dart +++ b/lib/features/labels/tags/view/widgets/fullscreen_tags_form.dart @@ -44,12 +44,12 @@ class _FullscreenTagsFormState extends State { _options = widget.options.values.toList(); final value = widget.initialValue; if (value is IdsTagsQuery) { - _include = value.includedIds.toList(); - _exclude = value.excludedIds.toList(); + _include = value.include.toList(); + _exclude = value.include.toList(); } else if (value is AnyAssignedTagsQuery) { _include = value.tagIds.toList(); _anyAssigned = true; - } else if (value is OnlyNotAssignedTagsQuery) { + } else if (value is NotAssignedTagsQuery) { _notAssigned = true; } _textEditingController.addListener(() => setState(() { @@ -113,28 +113,24 @@ class _FullscreenTagsFormState extends State { icon: const Icon(Icons.done), onPressed: () { if (widget.allowOnlySelection) { - widget.onSubmit(returnValue: IdsTagsQuery.included(_include)); + widget.onSubmit(returnValue: TagsQuery.ids(include: _include)); return; } late final TagsQuery query; if (_notAssigned) { - query = const OnlyNotAssignedTagsQuery(); + query = const TagsQuery.notAssigned(); } else if (_anyAssigned) { - query = AnyAssignedTagsQuery(tagIds: _include); + query = TagsQuery.anyAssigned(tagIds: _include); } else { - query = IdsTagsQuery([ - for (var id in _include) IncludeTagIdQuery(id), - for (var id in _exclude) ExcludeTagIdQuery(id), - ]); + query = TagsQuery.ids(include: _include, exclude: _exclude); } widget.onSubmit(returnValue: query); }, ), ], bottom: PreferredSize( - preferredSize: !widget.allowOnlySelection - ? const Size.fromHeight(32) - : const Size.fromHeight(1), + preferredSize: + !widget.allowOnlySelection ? const Size.fromHeight(32) : const Size.fromHeight(1), child: Column( children: [ Divider(color: theme.colorScheme.outline), @@ -237,8 +233,7 @@ class _FullscreenTagsFormState extends State { yield _buildNotAssignedOption(); } - var matches = _options - .where((e) => e.name.trim().toLowerCase().contains(normalizedQuery)); + var matches = _options.where((e) => e.name.trim().toLowerCase().contains(normalizedQuery)); if (matches.isEmpty && widget.allowCreation) { yield Text(S.of(context)!.noItemsFound); yield TextButton( @@ -304,9 +299,7 @@ class SelectableTagWidget extends StatelessWidget { Widget build(BuildContext context) { return ListTile( title: Text(tag.name), - trailing: excluded - ? const Icon(Icons.close) - : (selected ? const Icon(Icons.done) : null), + trailing: excluded ? const Icon(Icons.close) : (selected ? const Icon(Icons.done) : null), leading: CircleAvatar( backgroundColor: tag.color, child: (tag.isInboxTag) diff --git a/lib/features/labels/tags/view/widgets/tags_form_field.dart b/lib/features/labels/tags/view/widgets/tags_form_field.dart index f70fd69..31cd29e 100644 --- a/lib/features/labels/tags/view/widgets/tags_form_field.dart +++ b/lib/features/labels/tags/view/widgets/tags_form_field.dart @@ -1,6 +1,7 @@ import 'dart:developer'; import 'package:animations/animations.dart'; +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:paperless_api/paperless_api.dart'; @@ -32,9 +33,9 @@ class TagsFormField extends StatelessWidget { initialValue: initialValue, builder: (field) { final values = _generateOptions(context, field.value, field).toList(); - final isEmpty = (field.value is IdsTagsQuery && - (field.value as IdsTagsQuery).ids.isEmpty) || - field.value == null; + final isEmpty = + (field.value is IdsTagsQuery && (field.value as IdsTagsQuery).include.isEmpty) || + field.value == null; bool anyAssigned = field.value is AnyAssignedTagsQuery; return OpenContainer( middleColor: Theme.of(context).colorScheme.background, @@ -59,8 +60,7 @@ class TagsFormField extends StatelessWidget { height: 32, child: ListView.separated( scrollDirection: Axis.horizontal, - separatorBuilder: (context, index) => - const SizedBox(width: 4), + separatorBuilder: (context, index) => const SizedBox(width: 4), itemBuilder: (context, index) => values[index], itemCount: values.length, ), @@ -93,33 +93,56 @@ class TagsFormField extends StatelessWidget { ) sync* { if (query == null) { yield Container(); - } else if (query is IdsTagsQuery) { - for (final e in query.queries) { - yield _buildTagIdQueryWidget(context, e, field); - } - } else if (query is OnlyNotAssignedTagsQuery) { - yield _buildNotAssignedTagWidget(context, field); - } else if (query is AnyAssignedTagsQuery) { - for (final e in query.tagIds) { - yield _buildAnyAssignedTagWidget(context, e, field, query); + } else { + final widgets = query.map( + ids: (value) => [ + for (var inc in value.include) _buildTagIdQueryWidget(context, inc, field, false), + for (var exc in value.exclude) _buildTagIdQueryWidget(context, exc, field, true), + ], + anyAssigned: (value) => [ + for (var id in value.tagIds) _buildAnyAssignedTagWidget(context, id, field, value), + ], + notAssigned: (value) => [_buildNotAssignedTagWidget(context, field)], + ); + for (var child in widgets) { + yield child; } } } Widget _buildTagIdQueryWidget( BuildContext context, - TagIdQuery e, + int id, FormFieldState field, + bool exclude, ) { assert(field.value is IdsTagsQuery); final formValue = field.value as IdsTagsQuery; - final tag = options[e.id]!; + final tag = options[id]!; return QueryTagChip( - onDeleted: () => field.didChange(formValue.withIdsRemoved([e.id])), + onDeleted: () => field.didChange(formValue.copyWith( + include: formValue.include.whereNot((element) => element == id), + exclude: formValue.exclude.whereNot((element) => element == id), + )), onSelected: allowExclude - ? () => field.didChange(formValue.withIdQueryToggled(e.id)) + ? () { + if (formValue.include.contains(id)) { + field.didChange( + formValue.copyWith( + include: formValue.include.whereNot((element) => element == id), + exclude: [...formValue.exclude, id], + ), + ); + } else if (formValue.exclude.contains(id)) {} + field.didChange( + formValue.copyWith( + include: [...formValue.include, id], + exclude: formValue.exclude.whereNot((element) => element == id), + ), + ); + } : null, - exclude: e is ExcludeTagIdQuery, + exclude: exclude, backgroundColor: tag.color, foregroundColor: tag.textColor, labelText: tag.name, @@ -147,9 +170,11 @@ class TagsFormField extends StatelessWidget { ) { return QueryTagChip( onDeleted: () { - final updatedQuery = query.withRemoved([e]); + final updatedQuery = query.copyWith( + tagIds: query.tagIds.whereNot((element) => element == e), + ); if (updatedQuery.tagIds.isEmpty) { - field.didChange(const IdsTagsQuery()); + field.didChange(const TagsQuery.ids()); } else { field.didChange(updatedQuery); } diff --git a/lib/features/labels/view/pages/labels_page.dart b/lib/features/labels/view/pages/labels_page.dart index 769b20a..991b952 100644 --- a/lib/features/labels/view/pages/labels_page.dart +++ b/lib/features/labels/view/pages/labels_page.dart @@ -27,12 +27,9 @@ class LabelsPage extends StatefulWidget { State createState() => _LabelsPageState(); } -class _LabelsPageState extends State - with SingleTickerProviderStateMixin { - final SliverOverlapAbsorberHandle searchBarHandle = - SliverOverlapAbsorberHandle(); - final SliverOverlapAbsorberHandle tabBarHandle = - SliverOverlapAbsorberHandle(); +class _LabelsPageState extends State with SingleTickerProviderStateMixin { + final SliverOverlapAbsorberHandle searchBarHandle = SliverOverlapAbsorberHandle(); + final SliverOverlapAbsorberHandle tabBarHandle = SliverOverlapAbsorberHandle(); late final TabController _tabController; int _currentIndex = 0; @@ -82,33 +79,25 @@ class _LabelsPageState extends State Tab( icon: Icon( Icons.person_outline, - color: Theme.of(context) - .colorScheme - .onPrimaryContainer, + color: Theme.of(context).colorScheme.onPrimaryContainer, ), ), Tab( icon: Icon( Icons.description_outlined, - color: Theme.of(context) - .colorScheme - .onPrimaryContainer, + color: Theme.of(context).colorScheme.onPrimaryContainer, ), ), Tab( icon: Icon( Icons.label_outline, - color: Theme.of(context) - .colorScheme - .onPrimaryContainer, + color: Theme.of(context).colorScheme.onPrimaryContainer, ), ), Tab( icon: Icon( Icons.folder_open, - color: Theme.of(context) - .colorScheme - .onPrimaryContainer, + color: Theme.of(context).colorScheme.onPrimaryContainer, ), ), ], @@ -126,20 +115,17 @@ class _LabelsPageState extends State return true; } final desiredTab = - ((metrics.pixels / metrics.maxScrollExtent) * - (_tabController.length - 1)) + ((metrics.pixels / metrics.maxScrollExtent) * (_tabController.length - 1)) .round(); - if (metrics.axis == Axis.horizontal && - _currentIndex != desiredTab) { + if (metrics.axis == Axis.horizontal && _currentIndex != desiredTab) { setState(() => _currentIndex = desiredTab); } return true; }, child: RefreshIndicator( edgeOffset: kTextTabBarHeight, - notificationPredicate: (notification) => - connectedState.isConnected, + notificationPredicate: (notification) => connectedState.isConnected, onRefresh: () => [ context.read().reloadCorrespondents, context.read().reloadDocumentTypes, @@ -157,20 +143,14 @@ class _LabelsPageState extends State SliverOverlapInjector(handle: searchBarHandle), SliverOverlapInjector(handle: tabBarHandle), LabelTabView( - labels: context - .watch() - .state - .correspondents, + labels: context.watch().state.correspondents, filterBuilder: (label) => DocumentFilter( - correspondent: - IdQueryParameter.fromId(label.id), + correspondent: IdQueryParameter.fromId(label.id), pageSize: label.documentCount ?? 0, ), onEdit: _openEditCorrespondentPage, - emptyStateActionButtonLabel: - S.of(context)!.addNewCorrespondent, - emptyStateDescription: - S.of(context)!.noCorrespondentsSetUp, + emptyStateActionButtonLabel: S.of(context)!.addNewCorrespondent, + emptyStateDescription: S.of(context)!.noCorrespondentsSetUp, onAddNew: _openAddCorrespondentPage, ), ], @@ -184,20 +164,14 @@ class _LabelsPageState extends State SliverOverlapInjector(handle: searchBarHandle), SliverOverlapInjector(handle: tabBarHandle), LabelTabView( - labels: context - .watch() - .state - .documentTypes, + labels: context.watch().state.documentTypes, filterBuilder: (label) => DocumentFilter( - documentType: - IdQueryParameter.fromId(label.id), + documentType: IdQueryParameter.fromId(label.id), pageSize: label.documentCount ?? 0, ), onEdit: _openEditDocumentTypePage, - emptyStateActionButtonLabel: - S.of(context)!.addNewDocumentType, - emptyStateDescription: - S.of(context)!.noDocumentTypesSetUp, + emptyStateActionButtonLabel: S.of(context)!.addNewDocumentType, + emptyStateDescription: S.of(context)!.noDocumentTypesSetUp, onAddNew: _openAddDocumentTypePage, ), ], @@ -211,10 +185,9 @@ class _LabelsPageState extends State SliverOverlapInjector(handle: searchBarHandle), SliverOverlapInjector(handle: tabBarHandle), LabelTabView( - labels: - context.watch().state.tags, + labels: context.watch().state.tags, filterBuilder: (label) => DocumentFilter( - tags: IdsTagsQuery.fromIds([label.id!]), + tags: TagsQuery.ids(include: [label.id!]), pageSize: label.documentCount ?? 0, ), onEdit: _openEditTagPage, @@ -227,10 +200,8 @@ class _LabelsPageState extends State ) : null, ), - emptyStateActionButtonLabel: - S.of(context)!.addNewTag, - emptyStateDescription: - S.of(context)!.noTagsSetUp, + emptyStateActionButtonLabel: S.of(context)!.addNewTag, + emptyStateDescription: S.of(context)!.noTagsSetUp, onAddNew: _openAddTagPage, ), ], @@ -244,21 +215,15 @@ class _LabelsPageState extends State SliverOverlapInjector(handle: searchBarHandle), SliverOverlapInjector(handle: tabBarHandle), LabelTabView( - labels: context - .watch() - .state - .storagePaths, + labels: context.watch().state.storagePaths, onEdit: _openEditStoragePathPage, filterBuilder: (label) => DocumentFilter( - storagePath: - IdQueryParameter.fromId(label.id), + storagePath: IdQueryParameter.fromId(label.id), pageSize: label.documentCount ?? 0, ), contentBuilder: (path) => Text(path.path), - emptyStateActionButtonLabel: - S.of(context)!.addNewStoragePath, - emptyStateDescription: - S.of(context)!.noStoragePathsSetUp, + emptyStateActionButtonLabel: S.of(context)!.addNewStoragePath, + emptyStateDescription: S.of(context)!.noStoragePathsSetUp, onAddNew: _openAddStoragePathPage, ), ], diff --git a/lib/features/labels/view/widgets/fullscreen_label_form.dart b/lib/features/labels/view/widgets/fullscreen_label_form.dart index fe55559..a8c3829 100644 --- a/lib/features/labels/view/widgets/fullscreen_label_form.dart +++ b/lib/features/labels/view/widgets/fullscreen_label_form.dart @@ -28,10 +28,10 @@ class FullscreenLabelForm extends StatefulWidget { this.addNewLabelText, this.autofocus = true, }) : assert( - !(initialValue?.onlyAssigned ?? false) || showAnyAssignedOption, + !(initialValue?.isOnlyAssigned() ?? false) || showAnyAssignedOption, ), assert( - !(initialValue?.onlyNotAssigned ?? false) || showNotAssignedOption, + !(initialValue?.isOnlyNotAssigned() ?? false) || showNotAssignedOption, ), assert((addNewLabelText != null) == (onCreateNewLabel != null)); @@ -39,8 +39,7 @@ class FullscreenLabelForm extends StatefulWidget { State createState() => _FullscreenLabelFormState(); } -class _FullscreenLabelFormState - extends State> { +class _FullscreenLabelFormState extends State> { bool _showClearIcon = false; final _textEditingController = TextEditingController(); final _focusNode = FocusNode(); @@ -80,7 +79,12 @@ class _FullscreenLabelFormState FocusScope.of(context).unfocus(); final index = AutocompleteHighlightedOption.of(context); final value = index.isNegative ? null : options.elementAt(index); - widget.onSubmit(returnValue: IdQueryParameter.fromId(value?.id)); + widget.onSubmit( + returnValue: IdQueryParameter.fromId( + value?.whenOrNull( + fromId: (id) => id, + ), + )); }, autofocus: true, style: theme.textTheme.bodyLarge?.apply( @@ -124,11 +128,9 @@ class _FullscreenLabelFormState itemCount: options.length, itemBuilder: (BuildContext context, int index) { final option = options.elementAt(index); - final highlight = - AutocompleteHighlightedOption.of(context) == index; + final highlight = AutocompleteHighlightedOption.of(context) == index; if (highlight) { - SchedulerBinding.instance - .addPostFrameCallback((Duration timeStamp) { + SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) { Scrollable.ensureVisible( context, alignment: 0, @@ -183,7 +185,8 @@ class _FullscreenLabelFormState } for (final option in widget.options.values) { // Don't include the initial value in the selection - if (option.id == widget.initialValue?.id) { + final initialValue = widget.initialValue; + if (initialValue is SetIdQueryParameter && option.id == initialValue.id) { continue; } yield IdQueryParameter.fromId(option.id); @@ -191,8 +194,8 @@ class _FullscreenLabelFormState } } else { // Show filtered options, if no matching option is found, always show not assigned and any assigned (if enabled) and proceed. - final matches = widget.options.values - .where((e) => e.name.trim().toLowerCase().contains(normalizedQuery)); + final matches = + widget.options.values.where((e) => e.name.trim().toLowerCase().contains(normalizedQuery)); if (matches.isNotEmpty) { for (final match in matches) { yield IdQueryParameter.fromId(match.id); @@ -218,33 +221,18 @@ class _FullscreenLabelFormState } String? _buildHintText() { - if (widget.initialValue?.isSet ?? false) { - return widget.options[widget.initialValue!.id]!.name; - } - if (widget.initialValue?.onlyNotAssigned ?? false) { - return S.of(context)!.notAssigned; - } - if (widget.initialValue?.onlyAssigned ?? false) { - return S.of(context)!.anyAssigned; - } - - return S.of(context)!.startTyping; + return widget.initialValue?.when( + unset: () => S.of(context)!.startTyping, + notAssigned: () => S.of(context)!.notAssigned, + anyAssigned: () => S.of(context)!.anyAssigned, + fromId: (id) => widget.options[id]!.name, + ); } Widget _buildOptionWidget(IdQueryParameter option, bool highlight) { void onTap() => widget.onSubmit(returnValue: option); - late final String title; - if (option.isSet) { - title = widget.options[option.id]!.name; - } - if (option.onlyNotAssigned) { - title = S.of(context)!.notAssigned; - } - if (option.onlyAssigned) { - title = S.of(context)!.anyAssigned; - } - if (option.isUnset) { + if (option.isUnset()) { return Center( child: Column( children: [ @@ -258,6 +246,12 @@ class _FullscreenLabelFormState ), ); } + + final title = option.whenOrNull( + notAssigned: () => S.of(context)!.notAssigned, + anyAssigned: () => S.of(context)!.anyAssigned, + fromId: (id) => widget.options[id]!.name, + )!; // Never null, since we already return on unset before return ListTile( selected: highlight, selectedTileColor: Theme.of(context).focusColor, diff --git a/lib/features/labels/view/widgets/label_form_field.dart b/lib/features/labels/view/widgets/label_form_field.dart index 4851fe2..7aaf09f 100644 --- a/lib/features/labels/view/widgets/label_form_field.dart +++ b/lib/features/labels/view/widgets/label_form_field.dart @@ -45,20 +45,19 @@ class LabelFormField extends StatelessWidget { }) : super(key: key); String _buildText(BuildContext context, IdQueryParameter? value) { - if (value?.isSet ?? false) { - return options[value!.id]!.name; - } else if (value?.onlyNotAssigned ?? false) { - return S.of(context)!.notAssigned; - } else if (value?.onlyAssigned ?? false) { - return S.of(context)!.anyAssigned; - } - return ''; + return value?.when( + unset: () => '', + notAssigned: () => S.of(context)!.notAssigned, + anyAssigned: () => S.of(context)!.anyAssigned, + fromId: (id) => options[id]!.name, + ) ?? + ''; } @override Widget build(BuildContext context) { - final isEnabled = options.values.any((e) => (e.documentCount ?? 0) > 0) || - addLabelPageBuilder != null; + final isEnabled = + options.values.any((e) => (e.documentCount ?? 0) > 0) || addLabelPageBuilder != null; return FormBuilderField( name: name, initialValue: initialValue, @@ -68,8 +67,9 @@ class LabelFormField extends StatelessWidget { final controller = TextEditingController( text: _buildText(context, field.value), ); - final displayedSuggestions = - suggestions.whereNot((e) => e.id == field.value?.id).toList(); + final displayedSuggestions = suggestions + .whereNot((e) => e.id == field.value?.maybeWhen(fromId: (id) => id, orElse: () => -1)) + .toList(); return Column( children: [ @@ -93,8 +93,7 @@ class LabelFormField extends StatelessWidget { suffixIcon: controller.text.isNotEmpty ? IconButton( icon: const Icon(Icons.clear), - onPressed: () => - field.didChange(const IdQueryParameter.unset()), + onPressed: () => field.didChange(const IdQueryParameter.unset()), ) : null, ), @@ -107,8 +106,7 @@ class LabelFormField extends StatelessWidget { ? (initialName) { return Navigator.of(context).push( MaterialPageRoute( - builder: (context) => - addLabelPageBuilder!(initialName), + builder: (context) => addLabelPageBuilder!(initialName), ), ); } @@ -139,8 +137,7 @@ class LabelFormField extends StatelessWidget { scrollDirection: Axis.horizontal, itemCount: displayedSuggestions.length, itemBuilder: (context, index) { - final suggestion = - displayedSuggestions.elementAt(index); + final suggestion = displayedSuggestions.elementAt(index); return ColoredChipWrapper( child: ActionChip( label: Text(suggestion.name), diff --git a/lib/features/labels/view/widgets/label_item.dart b/lib/features/labels/view/widgets/label_item.dart index 47b6264..860aa89 100644 --- a/lib/features/labels/view/widgets/label_item.dart +++ b/lib/features/labels/view/widgets/label_item.dart @@ -5,8 +5,8 @@ import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/features/linked_documents/cubit/linked_documents_cubit.dart'; import 'package:paperless_mobile/features/linked_documents/view/linked_documents_page.dart'; -import 'package:paperless_mobile/features/login/model/user_account.dart'; -import 'package:paperless_mobile/features/settings/model/global_settings.dart'; +import 'package:paperless_mobile/core/database/tables/user_account.dart'; +import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/helpers/format_helpers.dart'; class LabelItem extends StatelessWidget { @@ -46,10 +46,9 @@ class LabelItem extends StatelessWidget { onPressed: (label.documentCount ?? 0) == 0 ? null : () { - final currentUser = - Hive.box(HiveBoxes.globalSettings) - .getValue()! - .currentLoggedInUser!; + final currentUser = Hive.box(HiveBoxes.globalSettings) + .getValue()! + .currentLoggedInUser!; final filter = filterBuilder(label); Navigator.push( context, @@ -60,8 +59,7 @@ class LabelItem extends StatelessWidget { context.read(), context.read(), context.read(), - Hive.box(HiveBoxes.userAccount) - .get(currentUser)!, + Hive.box(HiveBoxes.userAccount).get(currentUser)!, ), child: const LinkedDocumentsPage(), ), diff --git a/lib/features/linked_documents/cubit/linked_documents_cubit.dart b/lib/features/linked_documents/cubit/linked_documents_cubit.dart index 9c166e8..50116f1 100644 --- a/lib/features/linked_documents/cubit/linked_documents_cubit.dart +++ b/lib/features/linked_documents/cubit/linked_documents_cubit.dart @@ -3,7 +3,7 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart'; -import 'package:paperless_mobile/features/login/model/user_account.dart'; +import 'package:paperless_mobile/core/database/tables/user_account.dart'; import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart'; import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart'; import 'package:paperless_mobile/features/settings/model/view_type.dart'; @@ -73,4 +73,7 @@ class LinkedDocumentsCubit extends HydratedCubit Map? toJson(LinkedDocumentsState state) { return state.toJson(); } + + @override + Future onFilterUpdated(DocumentFilter filter) async {} } diff --git a/lib/features/login/cubit/authentication_cubit.dart b/lib/features/login/cubit/authentication_cubit.dart index 0dc82e2..caa4fba 100644 --- a/lib/features/login/cubit/authentication_cubit.dart +++ b/lib/features/login/cubit/authentication_cubit.dart @@ -7,17 +7,18 @@ import 'package:hive_flutter/adapters.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart'; +import 'package:paperless_mobile/core/database/tables/user_app_state.dart'; import 'package:paperless_mobile/core/interceptor/dio_http_error_interceptor.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/security/session_manager.dart'; import 'package:paperless_mobile/features/login/model/client_certificate.dart'; import 'package:paperless_mobile/features/login/model/login_form_credentials.dart'; -import 'package:paperless_mobile/features/login/model/user_account.dart'; -import 'package:paperless_mobile/features/login/model/user_credentials.dart'; +import 'package:paperless_mobile/core/database/tables/user_account.dart'; +import 'package:paperless_mobile/core/database/tables/user_credentials.dart'; import 'package:paperless_mobile/features/login/services/authentication_service.dart'; -import 'package:paperless_mobile/features/settings/model/global_settings.dart'; -import 'package:paperless_mobile/features/settings/model/user_settings.dart'; +import 'package:paperless_mobile/core/database/tables/global_settings.dart'; +import 'package:paperless_mobile/core/database/tables/user_settings.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; part 'authentication_state.dart'; @@ -58,33 +59,33 @@ class AuthenticationCubit extends Cubit { clientCertificate: clientCertificate, authToken: token, ); + final userAccountBox = Hive.box(HiveBoxes.userAccount); + final userStateBox = Hive.box(HiveBoxes.userAppState); final userId = "${credentials.username}@$serverUrl"; - // If it is first time login, create settings for this user. - final userAccountBox = Hive.box(HiveBoxes.userAccount); - - final fullName = await _fetchFullName(); - if (!userAccountBox.containsKey(userId)) { - userAccountBox.put( - userId, - UserAccount( - id: userId, - settings: UserSettings( - currentDocumentFilter: DocumentFilter(), - ), - serverUrl: serverUrl, - username: credentials.username!, - fullName: fullName, - ), - ); + if (userAccountBox.containsKey(userId)) { + throw Exception("User with id $userId already exists!"); } - // Mark logged in user as currently active user. - final globalSettings = - Hive.box(HiveBoxes.globalSettings).getValue()!; - globalSettings.currentLoggedInUser = userId; - globalSettings.save(); + final fullName = await _fetchFullName(); + // Create user account + await userAccountBox.put( + userId, + UserAccount( + id: userId, + settings: UserSettings(), + serverUrl: serverUrl, + username: credentials.username!, + fullName: fullName, + ), + ); + + // Create user state + await userStateBox.put( + userId, + UserAppState(userId: userId), + ); // Save credentials in encrypted box final userCredentialsBox = await _getUserCredentialsBox(); @@ -96,21 +97,25 @@ class AuthenticationCubit extends Cubit { ), ); userCredentialsBox.close(); + + // Mark logged in user as currently active user. + final globalSettings = Hive.box(HiveBoxes.globalSettings).getValue()!; + globalSettings.currentLoggedInUser = userId; + await globalSettings.save(); + emit( AuthenticationState( isAuthenticated: true, username: credentials.username, userId: userId, fullName: fullName, - //TODO: Query ui settings with full name and add as parameter here... ), ); } /// Switches to another account if it exists. Future switchAccount(String userId) async { - final globalSettings = - Hive.box(HiveBoxes.globalSettings).getValue()!; + final globalSettings = Hive.box(HiveBoxes.globalSettings).getValue()!; if (globalSettings.currentLoggedInUser == userId) { return; } @@ -124,8 +129,8 @@ class AuthenticationCubit extends Cubit { final account = userAccountBox.get(userId)!; if (account.settings.isBiometricAuthenticationEnabled) { - final authenticated = await _localAuthService - .authenticateLocalUser("Authenticate to switch your account."); + final authenticated = + await _localAuthService.authenticateLocalUser("Authenticate to switch your account."); if (!authenticated) { debugPrint("User not authenticated."); return; @@ -172,6 +177,7 @@ class AuthenticationCubit extends Cubit { final userId = "${credentials.username}@$serverUrl"; final userAccountsBox = Hive.box(HiveBoxes.userAccount); + final userStateBox = Hive.box(HiveBoxes.userAppState); if (userAccountsBox.containsKey(userId)) { throw Exception("User already exists"); @@ -202,12 +208,18 @@ class AuthenticationCubit extends Cubit { username: credentials.username!, settings: UserSettings( isBiometricAuthenticationEnabled: enableBiometricAuthentication, - currentDocumentFilter: DocumentFilter(), ), fullName: fullName, ), ); + await userStateBox.put( + userId, + UserAppState( + userId: userId, + ), + ); + final userCredentialsBox = await _getUserCredentialsBox(); await userCredentialsBox.put( userId, @@ -221,14 +233,16 @@ class AuthenticationCubit extends Cubit { } Future removeAccount(String userId) async { - final globalSettings = - Hive.box(HiveBoxes.globalSettings).getValue()!; - final currentUser = globalSettings.currentLoggedInUser; + final globalSettings = Hive.box(HiveBoxes.globalSettings).getValue()!; final userAccountBox = Hive.box(HiveBoxes.userAccount); final userCredentialsBox = await _getUserCredentialsBox(); + final userAppStateBox = Hive.box(HiveBoxes.userAppState); + final currentUser = globalSettings.currentLoggedInUser; await userAccountBox.delete(userId); + await userAppStateBox.delete(userId); await userCredentialsBox.delete(userId); + await userAccountBox.close(); if (currentUser == userId) { return logout(); @@ -239,54 +253,49 @@ class AuthenticationCubit extends Cubit { /// Performs a conditional hydration based on the local authentication success. /// Future restoreSessionState() async { - final globalSettings = - Hive.box(HiveBoxes.globalSettings).getValue()!; + final globalSettings = Hive.box(HiveBoxes.globalSettings).getValue()!; final userId = globalSettings.currentLoggedInUser; if (userId == null) { // If there is nothing to restore, we can quit here. return; } - final userAccount = - Hive.box(HiveBoxes.userAccount).get(userId)!; + final userAccount = Hive.box(HiveBoxes.userAccount).get(userId)!; if (userAccount.settings.isBiometricAuthenticationEnabled) { - final localAuthSuccess = await _localAuthService - .authenticateLocalUser("Authenticate to log back in"); //TODO: INTL + final localAuthSuccess = + await _localAuthService.authenticateLocalUser("Authenticate to log back in"); //TODO: INTL if (!localAuthSuccess) { - emit( - const AuthenticationState(showBiometricAuthenticationScreen: true)); + emit(const AuthenticationState(showBiometricAuthenticationScreen: true)); return; } } final userCredentialsBox = await _getUserCredentialsBox(); + final authentication = userCredentialsBox.get(globalSettings.currentLoggedInUser!); - final authentication = - userCredentialsBox.get(globalSettings.currentLoggedInUser!); - if (authentication != null) { - _dioWrapper.updateSettings( - clientCertificate: authentication.clientCertificate, - authToken: authentication.token, - baseUrl: userAccount.serverUrl, - serverInformation: PaperlessServerInformationModel(), - ); - emit( - AuthenticationState( - isAuthenticated: true, - showBiometricAuthenticationScreen: false, - username: userAccount.username, - ), - ); - } else { - throw Exception( - "User should be authenticated but no authentication information was found."); + await userCredentialsBox.close(); + + if (authentication == null) { + throw Exception("User should be authenticated but no authentication information was found."); } + _dioWrapper.updateSettings( + clientCertificate: authentication.clientCertificate, + authToken: authentication.token, + baseUrl: userAccount.serverUrl, + serverInformation: PaperlessServerInformationModel(), + ); + emit( + AuthenticationState( + isAuthenticated: true, + showBiometricAuthenticationScreen: false, + username: userAccount.username, + ), + ); } Future logout() async { await _resetExternalState(); - final globalSettings = - Hive.box(HiveBoxes.globalSettings).getValue()!; + final globalSettings = Hive.box(HiveBoxes.globalSettings).getValue()!; globalSettings ..currentLoggedInUser = null ..save(); diff --git a/lib/features/login/view/login_page.dart b/lib/features/login/view/login_page.dart index c8a1b9f..a7263f0 100644 --- a/lib/features/login/view/login_page.dart +++ b/lib/features/login/view/login_page.dart @@ -12,7 +12,7 @@ import 'package:paperless_mobile/features/login/view/widgets/form_fields/client_ import 'package:paperless_mobile/features/login/view/widgets/form_fields/server_address_form_field.dart'; import 'package:paperless_mobile/features/login/view/widgets/form_fields/user_credentials_form_field.dart'; import 'package:paperless_mobile/features/login/view/widgets/login_pages/server_connection_page.dart'; -import 'package:paperless_mobile/features/settings/model/global_settings.dart'; +import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/helpers/message_helpers.dart'; import 'widgets/login_pages/server_login_page.dart'; diff --git a/lib/features/paged_document_view/cubit/document_paging_bloc_mixin.dart b/lib/features/paged_document_view/cubit/document_paging_bloc_mixin.dart index 6d954d5..9d68333 100644 --- a/lib/features/paged_document_view/cubit/document_paging_bloc_mixin.dart +++ b/lib/features/paged_document_view/cubit/document_paging_bloc_mixin.dart @@ -2,7 +2,6 @@ import 'package:collection/collection.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; -import 'package:paperless_mobile/features/login/model/user_account.dart'; import 'paged_documents_state.dart'; @@ -10,11 +9,11 @@ import 'paged_documents_state.dart'; /// Mixin which can be used on cubits that handle documents. /// This implements all paging and filtering logic. /// -mixin DocumentPagingBlocMixin - on BlocBase { +mixin DocumentPagingBlocMixin on BlocBase { PaperlessDocumentsApi get api; DocumentChangedNotifier get notifier; - UserAccount get account; + + Future onFilterUpdated(DocumentFilter filter); Future loadMore() async { if (state.isLastPageLoaded) { @@ -30,8 +29,7 @@ mixin DocumentPagingBlocMixin value: [...state.value, result], )); } finally { - account.settings.currentDocumentFilter = newFilter; - account.save(); + await onFilterUpdated(newFilter); emit(state.copyWithPaged(isLoading: false)); } } @@ -52,8 +50,7 @@ mixin DocumentPagingBlocMixin hasLoaded: true, )); } finally { - account.settings.currentDocumentFilter = filter; - account.save(); + await onFilterUpdated(filter); emit(state.copyWithPaged(isLoading: false)); } } @@ -66,13 +63,11 @@ mixin DocumentPagingBlocMixin ) async => updateFilter(filter: transformFn(state.filter)); - Future resetFilter() { + Future resetFilter() async { final filter = DocumentFilter.initial.copyWith( sortField: state.filter.sortField, sortOrder: state.filter.sortOrder, ); - account.settings.currentDocumentFilter = filter; - account.save(); return updateFilter(filter: filter); } @@ -90,8 +85,7 @@ mixin DocumentPagingBlocMixin )); } } finally { - account.settings.currentDocumentFilter = filter; - account.save(); + await onFilterUpdated(filter); if (!isClosed) { emit(state.copyWithPaged(isLoading: false)); } @@ -132,8 +126,7 @@ mixin DocumentPagingBlocMixin if (index != -1) { final foundPage = state.value[index]; final replacementPage = foundPage.copyWith( - results: foundPage.results - ..removeWhere((element) => element.id == document.id), + results: foundPage.results..removeWhere((element) => element.id == document.id), ); final newCount = foundPage.count - 1; emit( @@ -141,8 +134,7 @@ mixin DocumentPagingBlocMixin value: state.value .mapIndexed( (currIndex, element) => - (currIndex == index ? replacementPage : element) - .copyWith(count: newCount), + (currIndex == index ? replacementPage : element).copyWith(count: newCount), ) .toList(), ), @@ -165,14 +157,11 @@ mixin DocumentPagingBlocMixin if (pageIndex != -1) { final foundPage = state.value[pageIndex]; final replacementPage = foundPage.copyWith( - results: foundPage.results - .map((doc) => doc.id == document.id ? document : doc) - .toList(), + results: foundPage.results.map((doc) => doc.id == document.id ? document : doc).toList(), ); final newState = state.copyWithPaged( value: state.value - .mapIndexed((currIndex, element) => - currIndex == pageIndex ? replacementPage : element) + .mapIndexed((currIndex, element) => currIndex == pageIndex ? replacementPage : element) .toList(), ); emit(newState); diff --git a/lib/features/saved_view_details/cubit/saved_view_details_cubit.dart b/lib/features/saved_view_details/cubit/saved_view_details_cubit.dart index db5a01f..1bddcb1 100644 --- a/lib/features/saved_view_details/cubit/saved_view_details_cubit.dart +++ b/lib/features/saved_view_details/cubit/saved_view_details_cubit.dart @@ -69,4 +69,7 @@ class SavedViewDetailsCubit extends HydratedCubit Map? toJson(SavedViewDetailsState state) { return state.toJson(); } + + @override + Future onFilterUpdated(DocumentFilter filter) async {} } diff --git a/lib/features/settings/view/dialogs/account_settings_dialog.dart b/lib/features/settings/view/dialogs/account_settings_dialog.dart index 9bf9aae..8cfdb1d 100644 --- a/lib/features/settings/view/dialogs/account_settings_dialog.dart +++ b/lib/features/settings/view/dialogs/account_settings_dialog.dart @@ -10,7 +10,7 @@ import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/widgets/hint_card.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart'; -import 'package:paperless_mobile/features/login/model/user_account.dart'; +import 'package:paperless_mobile/core/database/tables/user_account.dart'; import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/helpers/message_helpers.dart'; diff --git a/lib/features/settings/view/manage_accounts_page.dart b/lib/features/settings/view/manage_accounts_page.dart index 996b6f2..2f5df23 100644 --- a/lib/features/settings/view/manage_accounts_page.dart +++ b/lib/features/settings/view/manage_accounts_page.dart @@ -9,9 +9,9 @@ import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart'; import 'package:paperless_mobile/features/login/model/login_form_credentials.dart'; -import 'package:paperless_mobile/features/login/model/user_account.dart'; +import 'package:paperless_mobile/core/database/tables/user_account.dart'; import 'package:paperless_mobile/features/login/view/login_page.dart'; -import 'package:paperless_mobile/features/settings/model/global_settings.dart'; +import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/features/settings/view/dialogs/switch_account_dialog.dart'; import 'package:paperless_mobile/features/settings/view/pages/switching_accounts_page.dart'; import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; @@ -26,13 +26,11 @@ class ManageAccountsPage extends StatelessWidget { return GlobalSettingsBuilder( builder: (context, globalSettings) { return ValueListenableBuilder( - valueListenable: - Hive.box(HiveBoxes.userAccount).listenable(), + valueListenable: Hive.box(HiveBoxes.userAccount).listenable(), builder: (context, box, _) { final userIds = box.keys.toList().cast(); final otherAccounts = userIds - .whereNot( - (element) => element == globalSettings.currentLoggedInUser) + .whereNot((element) => element == globalSettings.currentLoggedInUser) .toList(); return SimpleDialog( insetPadding: EdgeInsets.all(24), @@ -51,11 +49,8 @@ class ManageAccountsPage extends StatelessWidget { borderRadius: BorderRadius.circular(24), ), children: [ - _buildAccountTile( - context, - globalSettings.currentLoggedInUser!, - box.get(globalSettings.currentLoggedInUser!)!, - globalSettings), + _buildAccountTile(context, globalSettings.currentLoggedInUser!, + box.get(globalSettings.currentLoggedInUser!)!, globalSettings), // if (otherAccounts.isNotEmpty) Text("Other accounts"), Column( children: [ @@ -188,8 +183,7 @@ class ManageAccountsPage extends StatelessWidget { MaterialPageRoute( builder: (context) => LoginPage( titleString: "Add account", //TODO: INTL - onSubmit: (context, username, password, serverUrl, - clientCertificate) async { + onSubmit: (context, username, password, serverUrl, clientCertificate) async { final userId = await context.read().addAccount( credentials: LoginFormCredentials( username: username, @@ -202,8 +196,8 @@ class ManageAccountsPage extends StatelessWidget { ); final shoudSwitch = await showDialog( context: context, - builder: (context) => SwitchAccountDialog( - username: username, serverUrl: serverUrl), + builder: (context) => + SwitchAccountDialog(username: username, serverUrl: serverUrl), ) ?? false; if (shoudSwitch) { diff --git a/lib/features/settings/view/widgets/biometric_authentication_setting.dart b/lib/features/settings/view/widgets/biometric_authentication_setting.dart index 329fd84..139e49d 100644 --- a/lib/features/settings/view/widgets/biometric_authentication_setting.dart +++ b/lib/features/settings/view/widgets/biometric_authentication_setting.dart @@ -3,8 +3,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hive_flutter/adapters.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/features/login/services/authentication_service.dart'; -import 'package:paperless_mobile/features/settings/model/global_settings.dart'; -import 'package:paperless_mobile/features/settings/model/user_settings.dart'; +import 'package:paperless_mobile/core/database/tables/global_settings.dart'; +import 'package:paperless_mobile/core/database/tables/user_settings.dart'; import 'package:paperless_mobile/features/settings/view/widgets/user_settings_builder.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:provider/provider.dart'; diff --git a/lib/features/settings/view/widgets/color_scheme_option_setting.dart b/lib/features/settings/view/widgets/color_scheme_option_setting.dart index 818ccab..af6f3b0 100644 --- a/lib/features/settings/view/widgets/color_scheme_option_setting.dart +++ b/lib/features/settings/view/widgets/color_scheme_option_setting.dart @@ -7,7 +7,7 @@ import 'package:paperless_mobile/constants.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/translation/color_scheme_option_localization_mapper.dart'; import 'package:paperless_mobile/core/widgets/hint_card.dart'; -import 'package:paperless_mobile/features/settings/model/global_settings.dart'; +import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/features/settings/model/color_scheme_option.dart'; import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; import 'package:paperless_mobile/features/settings/view/widgets/radio_settings_dialog.dart'; diff --git a/lib/features/settings/view/widgets/global_settings_builder.dart b/lib/features/settings/view/widgets/global_settings_builder.dart index b20a5b1..e8425d3 100644 --- a/lib/features/settings/view/widgets/global_settings_builder.dart +++ b/lib/features/settings/view/widgets/global_settings_builder.dart @@ -3,7 +3,7 @@ import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter/src/widgets/placeholder.dart'; import 'package:hive_flutter/adapters.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart'; -import 'package:paperless_mobile/features/settings/model/global_settings.dart'; +import 'package:paperless_mobile/core/database/tables/global_settings.dart'; class GlobalSettingsBuilder extends StatelessWidget { final Widget Function(BuildContext context, GlobalSettings settings) builder; diff --git a/lib/features/settings/view/widgets/language_selection_setting.dart b/lib/features/settings/view/widgets/language_selection_setting.dart index 30a1ad7..2539364 100644 --- a/lib/features/settings/view/widgets/language_selection_setting.dart +++ b/lib/features/settings/view/widgets/language_selection_setting.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hive_flutter/adapters.dart'; -import 'package:paperless_mobile/features/settings/model/global_settings.dart'; +import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/features/settings/view/widgets/radio_settings_dialog.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; diff --git a/lib/features/settings/view/widgets/user_avatar.dart b/lib/features/settings/view/widgets/user_avatar.dart index fc8ac8c..0339cbd 100644 --- a/lib/features/settings/view/widgets/user_avatar.dart +++ b/lib/features/settings/view/widgets/user_avatar.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:paperless_mobile/features/login/model/user_account.dart'; +import 'package:paperless_mobile/core/database/tables/user_account.dart'; class UserAvatar extends StatelessWidget { final String userId; diff --git a/lib/features/settings/view/widgets/user_settings_builder.dart b/lib/features/settings/view/widgets/user_settings_builder.dart index 2f7c287..5b78f44 100644 --- a/lib/features/settings/view/widgets/user_settings_builder.dart +++ b/lib/features/settings/view/widgets/user_settings_builder.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:hive_flutter/adapters.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart'; -import 'package:paperless_mobile/features/login/model/user_account.dart'; -import 'package:paperless_mobile/features/settings/model/global_settings.dart'; -import 'package:paperless_mobile/features/settings/model/user_settings.dart'; +import 'package:paperless_mobile/core/database/tables/user_account.dart'; +import 'package:paperless_mobile/core/database/tables/global_settings.dart'; +import 'package:paperless_mobile/core/database/tables/user_settings.dart'; class UserAccountBuilder extends StatelessWidget { final Widget Function( @@ -19,12 +19,10 @@ class UserAccountBuilder extends StatelessWidget { @override Widget build(BuildContext context) { return ValueListenableBuilder>( - valueListenable: - Hive.box(HiveBoxes.userAccount).listenable(), + valueListenable: Hive.box(HiveBoxes.userAccount).listenable(), builder: (context, accountBox, _) { - final currentUser = Hive.box(HiveBoxes.globalSettings) - .getValue()! - .currentLoggedInUser; + final currentUser = + Hive.box(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser; if (currentUser != null) { final account = accountBox.get(currentUser); return builder(context, account); diff --git a/lib/features/similar_documents/cubit/similar_documents_cubit.dart b/lib/features/similar_documents/cubit/similar_documents_cubit.dart index 3aea2c3..7899889 100644 --- a/lib/features/similar_documents/cubit/similar_documents_cubit.dart +++ b/lib/features/similar_documents/cubit/similar_documents_cubit.dart @@ -7,8 +7,7 @@ import 'package:paperless_mobile/features/paged_document_view/cubit/paged_docume part 'similar_documents_state.dart'; -class SimilarDocumentsCubit extends Cubit - with DocumentPagingBlocMixin { +class SimilarDocumentsCubit extends Cubit with DocumentPagingBlocMixin { final int documentId; @override @@ -60,4 +59,7 @@ class SimilarDocumentsCubit extends Cubit _labelRepository.removeListener(this); return super.close(); } + + @override + Future onFilterUpdated(DocumentFilter filter) async {} } diff --git a/lib/main.dart b/lib/main.dart index 46fe95c..f7eef58 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -20,6 +20,7 @@ import 'package:paperless_mobile/constants.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/config/hive/hive_config.dart'; +import 'package:paperless_mobile/core/database/tables/user_app_state.dart'; import 'package:paperless_mobile/core/interceptor/dio_http_error_interceptor.dart'; import 'package:paperless_mobile/core/interceptor/language_header.interceptor.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; @@ -35,12 +36,11 @@ import 'package:paperless_mobile/features/home/view/widget/verify_identity_page. import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart'; import 'package:paperless_mobile/features/login/model/client_certificate.dart'; import 'package:paperless_mobile/features/login/model/login_form_credentials.dart'; -import 'package:paperless_mobile/features/login/model/user_account.dart'; +import 'package:paperless_mobile/core/database/tables/user_account.dart'; import 'package:paperless_mobile/features/login/services/authentication_service.dart'; import 'package:paperless_mobile/features/login/view/login_page.dart'; import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart'; -import 'package:paperless_mobile/features/settings/model/global_settings.dart'; -import 'package:paperless_mobile/features/settings/model/user_settings.dart'; +import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; import 'package:paperless_mobile/features/sharing/share_intent_queue.dart'; import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart'; @@ -54,8 +54,7 @@ import 'package:receive_sharing_intent/receive_sharing_intent.dart'; String get defaultPreferredLocaleSubtag { String preferredLocale = Platform.localeName.split("_").first; - if (!S.supportedLocales - .any((locale) => locale.languageCode == preferredLocale)) { + if (!S.supportedLocales.any((locale) => locale.languageCode == preferredLocale)) { preferredLocale = 'en'; } return preferredLocale; @@ -63,14 +62,13 @@ String get defaultPreferredLocaleSubtag { Future _initHive() async { await Hive.initFlutter(); - //TODO: REMOVE! - // await getApplicationDocumentsDirectory() - // .then((value) => value.delete(recursive: true)); + // //TODO: REMOVE! + // await getApplicationDocumentsDirectory().then((value) => value.delete(recursive: true)); registerHiveAdapters(); await Hive.openBox(HiveBoxes.userAccount); - final globalSettingsBox = - await Hive.openBox(HiveBoxes.globalSettings); + await Hive.openBox(HiveBoxes.userAppState); + final globalSettingsBox = await Hive.openBox(HiveBoxes.globalSettings); if (!globalSettingsBox.hasValue) { await globalSettingsBox.setValue( @@ -155,8 +153,7 @@ void main() async { //Update language header in interceptor on language change. globalSettingsBox.listenable().addListener(() { - languageHeaderInterceptor.preferredLocaleSubtag = - globalSettings.preferredLocaleSubtag; + languageHeaderInterceptor.preferredLocaleSubtag = globalSettings.preferredLocaleSubtag; }); runApp( @@ -180,8 +177,7 @@ void main() async { Provider.value( value: connectivityStatusService, ), - Provider.value( - value: localNotificationService), + Provider.value(value: localNotificationService), Provider.value(value: DocumentChangedNotifier()), ], child: MultiRepositoryProvider( @@ -211,8 +207,7 @@ class PaperlessMobileEntrypoint extends StatefulWidget { }) : super(key: key); @override - State createState() => - _PaperlessMobileEntrypointState(); + State createState() => _PaperlessMobileEntrypointState(); } class _PaperlessMobileEntrypointState extends State { @@ -247,8 +242,7 @@ class _PaperlessMobileEntrypointState extends State { GlobalWidgetsLocalizations.delegate, ], routes: { - DocumentDetailsRoute.routeName: (context) => - const DocumentDetailsRoute(), + DocumentDetailsRoute.routeName: (context) => const DocumentDetailsRoute(), }, home: const AuthenticationWrapper(), ); @@ -283,11 +277,9 @@ class _AuthenticationWrapperState extends State { } initializeDateFormatting(); // For sharing files coming from outside the app while the app is still opened - ReceiveSharingIntent.getMediaStream() - .listen(ShareIntentQueue.instance.addAll); + ReceiveSharingIntent.getMediaStream().listen(ShareIntentQueue.instance.addAll); // For sharing files coming from outside the app while the app is closed - ReceiveSharingIntent.getInitialMedia() - .then(ShareIntentQueue.instance.addAll); + ReceiveSharingIntent.getInitialMedia().then(ShareIntentQueue.instance.addAll); } Future _setOptimalDisplayMode() async { @@ -299,8 +291,7 @@ class _AuthenticationWrapperState extends State { .toList() ..sort((a, b) => b.refreshRate.compareTo(a.refreshRate)); - final DisplayMode mostOptimalMode = - sameResolution.isNotEmpty ? sameResolution.first : active; + final DisplayMode mostOptimalMode = sameResolution.isNotEmpty ? sameResolution.first : active; debugPrint('Setting refresh rate to ${mostOptimalMode.refreshRate}'); await FlutterDisplayMode.setPreferredMode(mostOptimalMode); @@ -351,14 +342,12 @@ class _AuthenticationWrapperState extends State { ) async { try { await context.read().login( - credentials: - LoginFormCredentials(username: username, password: password), + credentials: LoginFormCredentials(username: username, password: password), serverUrl: serverUrl, clientCertificate: clientCertificate, ); // Show onboarding after first login! - final globalSettings = - Hive.box(HiveBoxes.globalSettings).getValue()!; + final globalSettings = Hive.box(HiveBoxes.globalSettings).getValue()!; if (globalSettings.showOnboarding) { Navigator.push( context, diff --git a/packages/paperless_api/lib/config/hive/hive_type_ids.dart b/packages/paperless_api/lib/config/hive/hive_type_ids.dart index 546833b..254ac8b 100644 --- a/packages/paperless_api/lib/config/hive/hive_type_ids.dart +++ b/packages/paperless_api/lib/config/hive/hive_type_ids.dart @@ -1,4 +1,49 @@ +import 'package:hive/hive.dart'; +import 'package:paperless_api/paperless_api.dart'; + class PaperlessApiHiveTypeIds { PaperlessApiHiveTypeIds._(); - static const int documentFilter = 1000; + static const int documentFilter = 100; + static const int idQueryParameter = 101; + static const int tagsQuery = 102; + static const int anyAssignedTagsQuery = 103; + static const int tagIdQuery = 104; + static const int includeTagIdQuery = 105; + static const int idsTagsQuery = 106; + static const int excludeTagIdQuery = 107; + static const int sortField = 108; + static const int sortOrder = 109; + static const int absoluteDateRangeQuery = 110; + static const int relativeDateRangeQuery = 111; + static const int dateRangeUnit = 112; + static const int unsetDateRangeQuery = 113; + static const int textQuery = 114; + static const int queryType = 115; + static const int unsetIdQueryParameter = 116; + static const int notAssignedIdQueryParameter = 117; + static const int anyAssignedIdQueryParameter = 118; + static const int setIdQueryParameter = 119; + static const int notAssignedTagsQuery = 120; +} + +void registerPaperlessApiHiveTypeAdapters() { + Hive.registerAdapter(DocumentFilterAdapter()); + // TagsQuery + Hive.registerAdapter(AnyAssignedTagsQueryAdapter()); + Hive.registerAdapter(NotAssignedTagsQueryAdapter()); + Hive.registerAdapter(IdsTagsQueryAdapter()); + + Hive.registerAdapter(SortFieldAdapter()); + Hive.registerAdapter(SortOrderAdapter()); + Hive.registerAdapter(AbsoluteDateRangeQueryAdapter()); + Hive.registerAdapter(RelativeDateRangeQueryAdapter()); + Hive.registerAdapter(DateRangeUnitAdapter()); + Hive.registerAdapter(UnsetDateRangeQueryAdapter()); + Hive.registerAdapter(TextQueryAdapter()); + Hive.registerAdapter(QueryTypeAdapter()); + // IdQueryParameter + Hive.registerAdapter(SetIdQueryParameterAdapter()); + Hive.registerAdapter(UnsetIdQueryParameterAdapter()); + Hive.registerAdapter(AnyAssignedIdQueryParameterAdapter()); + Hive.registerAdapter(NotAssignedIdQueryParameterAdapter()); } diff --git a/packages/paperless_api/lib/paperless_api.dart b/packages/paperless_api/lib/paperless_api.dart index 1ef273c..444403f 100644 --- a/packages/paperless_api/lib/paperless_api.dart +++ b/packages/paperless_api/lib/paperless_api.dart @@ -3,3 +3,4 @@ library paperless_api; export 'src/models/models.dart'; export 'src/modules/modules.dart'; export 'src/converters/converters.dart'; +export 'config/hive/hive_type_ids.dart'; diff --git a/packages/paperless_api/lib/src/converters/tags_query_json_converter.dart b/packages/paperless_api/lib/src/converters/tags_query_json_converter.dart deleted file mode 100644 index 643215a..0000000 --- a/packages/paperless_api/lib/src/converters/tags_query_json_converter.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:json_annotation/json_annotation.dart'; -import 'package:paperless_api/src/models/query_parameters/tags_query/any_assigned_tags_query.dart'; -import 'package:paperless_api/src/models/query_parameters/tags_query/ids_tags_query.dart'; -import 'package:paperless_api/src/models/query_parameters/tags_query/only_not_assigned_tags_query.dart'; - -import '../models/query_parameters/tags_query/tags_query.dart'; - -class TagsQueryJsonConverter - extends JsonConverter> { - const TagsQueryJsonConverter(); - @override - TagsQuery fromJson(Map json) { - final type = json['type'] as String; - final data = json['data'] as Map; - switch (type) { - case 'OnlyNotAssignedTagsQuery': - return const OnlyNotAssignedTagsQuery(); - case 'AnyAssignedTagsQuery': - return AnyAssignedTagsQuery.fromJson(data); - case 'IdsTagsQuery': - return IdsTagsQuery.fromJson(data); - default: - throw Exception('Error parsing TagsQuery: Unknown type $type'); - } - } - - @override - Map toJson(TagsQuery object) { - return { - 'type': object.runtimeType.toString(), - 'data': object.toJson(), - }; - } -} diff --git a/packages/paperless_api/lib/src/models/document_filter.dart b/packages/paperless_api/lib/src/models/document_filter.dart index 807df70..6f6012f 100644 --- a/packages/paperless_api/lib/src/models/document_filter.dart +++ b/packages/paperless_api/lib/src/models/document_filter.dart @@ -4,11 +4,10 @@ import 'package:hive/hive.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:paperless_api/config/hive/hive_type_ids.dart'; import 'package:paperless_api/paperless_api.dart'; -import 'package:paperless_api/src/converters/tags_query_json_converter.dart'; +import 'package:paperless_api/src/models/query_parameters/tags_query/tags_query.dart'; part 'document_filter.g.dart'; -@TagsQueryJsonConverter() @DateRangeQueryJsonConverter() @JsonSerializable(explicitToJson: true) @HiveType(typeId: PaperlessApiHiveTypeIds.documentFilter) @@ -48,8 +47,6 @@ class DocumentFilter extends Equatable { final DateRangeQuery modified; @HiveField(12) final TextQuery query; - - /// Query documents similar to the document with this id. @HiveField(13) final int? moreLike; @@ -58,7 +55,7 @@ class DocumentFilter extends Equatable { this.correspondent = const IdQueryParameter.unset(), this.storagePath = const IdQueryParameter.unset(), this.asnQuery = const IdQueryParameter.unset(), - this.tags = const IdsTagsQuery(), + this.tags = const TagsQuery.ids(), this.sortField = SortField.created, this.sortOrder = SortOrder.descending, this.page = 1, @@ -107,9 +104,7 @@ class DocumentFilter extends Equatable { final queryParams = groupBy(params, (e) => e.key).map( (key, entries) => MapEntry( key, - entries.length == 1 - ? entries.first.value - : entries.map((e) => e.value).join(","), + entries.length == 1 ? entries.first.value : entries.map((e) => e.value).join(","), ), ); return queryParams; @@ -150,8 +145,7 @@ class DocumentFilter extends Equatable { modified: modified ?? this.modified, moreLike: moreLike != null ? moreLike.call() : this.moreLike, ); - if (query?.queryType != QueryType.extended && - newFilter.forceExtendedQuery) { + if (query?.queryType != QueryType.extended && newFilter.forceExtendedQuery) { //Prevents infinite recursion return newFilter.copyWith( query: newFilter.query.copyWith(queryType: QueryType.extended), @@ -207,8 +201,7 @@ class DocumentFilter extends Equatable { query, ]; - factory DocumentFilter.fromJson(Map json) => - _$DocumentFilterFromJson(json); + factory DocumentFilter.fromJson(Map json) => _$DocumentFilterFromJson(json); Map toJson() => _$DocumentFilterToJson(this); } diff --git a/packages/paperless_api/lib/src/models/filter_rule_model.dart b/packages/paperless_api/lib/src/models/filter_rule_model.dart index ba0f4df..b8feec3 100644 --- a/packages/paperless_api/lib/src/models/filter_rule_model.dart +++ b/packages/paperless_api/lib/src/models/filter_rule_model.dart @@ -3,12 +3,7 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/src/constants.dart'; import 'package:paperless_api/src/converters/local_date_time_json_converter.dart'; - -import 'query_parameters/tags_query/any_assigned_tags_query.dart'; -import 'query_parameters/tags_query/exclude_tag_id_query.dart'; -import 'query_parameters/tags_query/ids_tags_query.dart'; -import 'query_parameters/tags_query/include_tag_id_query.dart'; -import 'query_parameters/tags_query/only_not_assigned_tags_query.dart'; +import 'package:paperless_api/src/models/query_parameters/tags_query/tags_query.dart'; part 'filter_rule_model.g.dart'; @@ -77,21 +72,29 @@ class FilterRule with EquatableMixin { ); case hasAnyTag: return filter.copyWith( - tags: value == "true" - ? const AnyAssignedTagsQuery() - : const OnlyNotAssignedTagsQuery(), + tags: value == "true" ? const TagsQuery.anyAssigned() : const TagsQuery.notAssigned(), ); case includeTagsRule: assert(filter.tags is IdsTagsQuery); return filter.copyWith( - tags: (filter.tags as IdsTagsQuery) - .withIdQueriesAdded([IncludeTagIdQuery(int.parse(value!))]), + tags: filter.tags.maybeWhen( + ids: (include, exclude) => TagsQuery.ids( + include: [...include, int.parse(value!)], + exclude: exclude, + ), + orElse: () => filter.tags, + ), ); case excludeTagsRule: assert(filter.tags is IdsTagsQuery); return filter.copyWith( - tags: (filter.tags as IdsTagsQuery) - .withIdQueriesAdded([ExcludeTagIdQuery(int.parse(value!))]), + tags: filter.tags.maybeWhen( + ids: (include, exclude) => TagsQuery.ids( + include: include, + exclude: [...exclude, int.parse(value!)], + ), + orElse: () => filter.tags, + ), ); case createdBeforeRule: if (filter.created is AbsoluteDateRangeQuery) { @@ -101,8 +104,7 @@ class FilterRule with EquatableMixin { ); } else { return filter.copyWith( - created: AbsoluteDateRangeQuery( - before: _dateTimeConverter.fromJson(value!)), + created: AbsoluteDateRangeQuery(before: _dateTimeConverter.fromJson(value!)), ); } case createdAfterRule: @@ -113,8 +115,7 @@ class FilterRule with EquatableMixin { ); } else { return filter.copyWith( - created: AbsoluteDateRangeQuery( - after: _dateTimeConverter.fromJson(value!)), + created: AbsoluteDateRangeQuery(after: _dateTimeConverter.fromJson(value!)), ); } case addedBeforeRule: @@ -125,8 +126,7 @@ class FilterRule with EquatableMixin { ); } else { return filter.copyWith( - added: AbsoluteDateRangeQuery( - before: _dateTimeConverter.fromJson(value!)), + added: AbsoluteDateRangeQuery(before: _dateTimeConverter.fromJson(value!)), ); } case addedAfterRule: @@ -137,8 +137,7 @@ class FilterRule with EquatableMixin { ); } else { return filter.copyWith( - added: AbsoluteDateRangeQuery( - after: _dateTimeConverter.fromJson(value!)), + added: AbsoluteDateRangeQuery(after: _dateTimeConverter.fromJson(value!)), ); } case modifiedBeforeRule: @@ -149,8 +148,7 @@ class FilterRule with EquatableMixin { ); } else { return filter.copyWith( - modified: AbsoluteDateRangeQuery( - before: _dateTimeConverter.fromJson(value!)), + modified: AbsoluteDateRangeQuery(before: _dateTimeConverter.fromJson(value!)), ); } case modifiedAfterRule: @@ -161,8 +159,7 @@ class FilterRule with EquatableMixin { ); } else { return filter.copyWith( - added: AbsoluteDateRangeQuery( - after: _dateTimeConverter.fromJson(value!)), + added: AbsoluteDateRangeQuery(after: _dateTimeConverter.fromJson(value!)), ); } case titleAndContentRule: @@ -236,49 +233,46 @@ class FilterRule with EquatableMixin { /// static List fromFilter(final DocumentFilter filter) { List filterRules = []; - if (filter.correspondent.onlyNotAssigned) { - filterRules.add(FilterRule(correspondentRule, null)); + final corrRule = filter.correspondent.whenOrNull( + notAssigned: () => FilterRule(correspondentRule, null), + fromId: (id) => FilterRule(correspondentRule, id.toString()), + ); + if (corrRule != null) { + filterRules.add(corrRule); } - if (filter.correspondent.isSet) { - filterRules.add( - FilterRule(correspondentRule, filter.correspondent.id!.toString())); + + final docTypeRule = filter.documentType.whenOrNull( + notAssigned: () => FilterRule(documentTypeRule, null), + fromId: (id) => FilterRule(documentTypeRule, id.toString()), + ); + if (docTypeRule != null) { + filterRules.add(docTypeRule); } - if (filter.documentType.onlyNotAssigned) { - filterRules.add(FilterRule(documentTypeRule, null)); - } - if (filter.documentType.isSet) { - filterRules.add( - FilterRule(documentTypeRule, filter.documentType.id!.toString())); - } - if (filter.storagePath.onlyNotAssigned) { - filterRules.add(FilterRule(storagePathRule, null)); - } - if (filter.storagePath.isSet) { - filterRules - .add(FilterRule(storagePathRule, filter.storagePath.id!.toString())); - } - if (filter.tags is OnlyNotAssignedTagsQuery) { - filterRules.add(FilterRule(hasAnyTag, false.toString())); - } - if (filter.tags is AnyAssignedTagsQuery) { - filterRules.add(FilterRule(hasAnyTag, true.toString())); - } - if (filter.tags is IdsTagsQuery) { - filterRules.addAll((filter.tags as IdsTagsQuery) - .includedIds - .map((id) => FilterRule(includeTagsRule, id.toString()))); - filterRules.addAll((filter.tags as IdsTagsQuery) - .excludedIds - .map((id) => FilterRule(excludeTagsRule, id.toString()))); + + final sPathRule = filter.documentType.whenOrNull( + notAssigned: () => FilterRule(storagePathRule, null), + fromId: (id) => FilterRule(storagePathRule, id.toString()), + ); + if (sPathRule != null) { + filterRules.add(sPathRule); } + final tagRules = filter.tags.when( + notAssigned: () => [FilterRule(hasAnyTag, 'false')], + anyAssigned: (_) => [FilterRule(hasAnyTag, 'true')], + ids: (include, exclude) => [ + ...include.map((id) => FilterRule(includeTagsRule, id.toString())), + ...exclude.map((id) => FilterRule(excludeTagsRule, id.toString())), + ], + ); + filterRules.addAll(tagRules); + if (filter.query.queryText != null) { switch (filter.query.queryType) { case QueryType.title: filterRules.add(FilterRule(titleRule, filter.query.queryText!)); break; case QueryType.titleAndContent: - filterRules - .add(FilterRule(titleAndContentRule, filter.query.queryText!)); + filterRules.add(FilterRule(titleAndContentRule, filter.query.queryText!)); break; case QueryType.extended: filterRules.add(FilterRule(extendedRule, filter.query.queryText!)); @@ -304,8 +298,8 @@ class FilterRule with EquatableMixin { } } else if (created is RelativeDateRangeQuery) { filterRules.add( - FilterRule(extendedRule, - created.toQueryParameter(DateRangeQueryField.created).values.first), + FilterRule( + extendedRule, created.toQueryParameter(DateRangeQueryField.created).values.first), ); } @@ -324,8 +318,7 @@ class FilterRule with EquatableMixin { } } else if (added is RelativeDateRangeQuery) { filterRules.add( - FilterRule(extendedRule, - added.toQueryParameter(DateRangeQueryField.added).values.first), + FilterRule(extendedRule, added.toQueryParameter(DateRangeQueryField.added).values.first), ); } @@ -339,25 +332,19 @@ class FilterRule with EquatableMixin { } if (modified.before != null) { filterRules.add( - FilterRule( - modifiedBeforeRule, apiDateFormat.format(modified.before!)), + FilterRule(modifiedBeforeRule, apiDateFormat.format(modified.before!)), ); } } else if (modified is RelativeDateRangeQuery) { filterRules.add( FilterRule( - extendedRule, - modified - .toQueryParameter(DateRangeQueryField.modified) - .values - .first), + extendedRule, modified.toQueryParameter(DateRangeQueryField.modified).values.first), ); } //Join values of all extended filter rules if exist if (filterRules.isNotEmpty && - filterRules.where((e) => e.ruleType == FilterRule.extendedRule).length > - 1) { + filterRules.where((e) => e.ruleType == FilterRule.extendedRule).length > 1) { final mergedExtendedRule = filterRules .where((r) => r.ruleType == FilterRule.extendedRule) .map((e) => e.value) @@ -381,6 +368,5 @@ class FilterRule with EquatableMixin { Map toJson() => _$FilterRuleToJson(this); - factory FilterRule.fromJson(Map json) => - _$FilterRuleFromJson(json); + factory FilterRule.fromJson(Map json) => _$FilterRuleFromJson(json); } diff --git a/packages/paperless_api/lib/src/models/models.dart b/packages/paperless_api/lib/src/models/models.dart index 7ba46a8..c1dd7f9 100644 --- a/packages/paperless_api/lib/src/models/models.dart +++ b/packages/paperless_api/lib/src/models/models.dart @@ -8,8 +8,8 @@ export 'query_parameters/id_query_parameter.dart'; export 'query_parameters/query_type.dart'; export 'query_parameters/sort_field.dart'; export 'query_parameters/sort_order.dart'; -export 'query_parameters/tags_query/tags_queries.dart'; export 'query_parameters/date_range_queries/date_range_queries.dart'; +export 'query_parameters/tags_query/tags_query.dart'; export 'query_parameters/text_query.dart'; export 'bulk_edit_model.dart'; export 'document_filter.dart'; diff --git a/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/absolute_date_range_query.dart b/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/absolute_date_range_query.dart index 73a3665..777a9d2 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/absolute_date_range_query.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/absolute_date_range_query.dart @@ -1,4 +1,6 @@ +import 'package:hive/hive.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'package:paperless_api/config/hive/hive_type_ids.dart'; import 'package:paperless_api/src/constants.dart'; import 'package:paperless_api/src/converters/local_date_time_json_converter.dart'; @@ -8,11 +10,14 @@ import 'date_range_query_field.dart'; part 'absolute_date_range_query.g.dart'; @JsonSerializable() +@HiveType(typeId: PaperlessApiHiveTypeIds.absoluteDateRangeQuery) class AbsoluteDateRangeQuery extends DateRangeQuery { @LocalDateTimeJsonConverter() + @HiveField(0) final DateTime? after; @LocalDateTimeJsonConverter() + @HiveField(1) final DateTime? before; const AbsoluteDateRangeQuery({this.after, this.before}); @@ -47,8 +52,7 @@ class AbsoluteDateRangeQuery extends DateRangeQuery { ); } - factory AbsoluteDateRangeQuery.fromJson(json) => - _$AbsoluteDateRangeQueryFromJson(json); + factory AbsoluteDateRangeQuery.fromJson(json) => _$AbsoluteDateRangeQueryFromJson(json); @override Map toJson() => _$AbsoluteDateRangeQueryToJson(this); diff --git a/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/date_range_query.dart b/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/date_range_query.dart index 7a7c6f7..3410eca 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/date_range_query.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/date_range_query.dart @@ -4,6 +4,7 @@ import 'date_range_query_field.dart'; abstract class DateRangeQuery extends Equatable { const DateRangeQuery(); + Map toQueryParameter(DateRangeQueryField field); Map toJson(); diff --git a/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/date_range_unit.dart b/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/date_range_unit.dart index 3c35f14..8da38e3 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/date_range_unit.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/date_range_unit.dart @@ -1,6 +1,15 @@ +import 'package:hive/hive.dart'; +import 'package:paperless_api/config/hive/hive_type_ids.dart'; +part 'date_range_unit.g.dart'; + +@HiveType(typeId: PaperlessApiHiveTypeIds.dateRangeUnit) enum DateRangeUnit { + @HiveField(0) day, + @HiveField(1) week, + @HiveField(2) month, + @HiveField(3) year; } diff --git a/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/relative_date_range_query.dart b/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/relative_date_range_query.dart index ef5ea56..32b8410 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/relative_date_range_query.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/relative_date_range_query.dart @@ -1,5 +1,7 @@ +import 'package:hive/hive.dart'; import 'package:jiffy/jiffy.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'package:paperless_api/config/hive/hive_type_ids.dart'; import 'date_range_query.dart'; import 'date_range_query_field.dart'; @@ -7,8 +9,11 @@ import 'date_range_unit.dart'; part 'relative_date_range_query.g.dart'; @JsonSerializable() +@HiveType(typeId: PaperlessApiHiveTypeIds.relativeDateRangeQuery) class RelativeDateRangeQuery extends DateRangeQuery { + @HiveField(0) final int offset; + @HiveField(1) final DateRangeUnit unit; const RelativeDateRangeQuery([ diff --git a/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/unset_date_range_query.dart b/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/unset_date_range_query.dart index 1a0e0aa..cfbc407 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/unset_date_range_query.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/unset_date_range_query.dart @@ -1,7 +1,11 @@ +import 'package:hive/hive.dart'; +import 'package:paperless_api/config/hive/hive_type_ids.dart'; import 'package:paperless_api/src/models/query_parameters/date_range_queries/date_range_query_field.dart'; import 'date_range_query.dart'; +part 'unset_date_range_query.g.dart'; +@HiveType(typeId: PaperlessApiHiveTypeIds.unsetDateRangeQuery) class UnsetDateRangeQuery extends DateRangeQuery { const UnsetDateRangeQuery(); @override @@ -11,9 +15,7 @@ class UnsetDateRangeQuery extends DateRangeQuery { Map toQueryParameter(DateRangeQueryField field) => const {}; @override - Map toJson() { - return {}; - } + Map toJson() => const {}; @override bool matches(DateTime dt) => true; diff --git a/packages/paperless_api/lib/src/models/query_parameters/id_query_parameter.dart b/packages/paperless_api/lib/src/models/query_parameters/id_query_parameter.dart index 3b3dc63..699e99d 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/id_query_parameter.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/id_query_parameter.dart @@ -1,101 +1,113 @@ -import 'package:equatable/equatable.dart'; -import 'package:json_annotation/json_annotation.dart'; +import 'package:hive/hive.dart'; +import 'package:paperless_api/config/hive/hive_type_ids.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +part 'id_query_parameter.freezed.dart'; part 'id_query_parameter.g.dart'; -@JsonSerializable() -class IdQueryParameter extends Equatable { - final int? assignmentStatus; - final int? id; - - @Deprecated("Use named constructors, this is only meant for code generation") - const IdQueryParameter(this.assignmentStatus, this.id); - - const IdQueryParameter.notAssigned() - : assignmentStatus = 1, - id = null; - - const IdQueryParameter.anyAssigned() - : assignmentStatus = 0, - id = null; - - const IdQueryParameter.fromId(this.id) : assignmentStatus = null; - - const IdQueryParameter.unset() : this.fromId(null); - - bool get isUnset => id == null && assignmentStatus == null; - - bool get isSet => id != null && assignmentStatus == null; - - bool get onlyNotAssigned => assignmentStatus == 1; - - bool get onlyAssigned => assignmentStatus == 0; +@freezed +class IdQueryParameter with _$IdQueryParameter { + const IdQueryParameter._(); + @HiveType(typeId: PaperlessApiHiveTypeIds.unsetIdQueryParameter) + const factory IdQueryParameter.unset() = UnsetIdQueryParameter; + @HiveType(typeId: PaperlessApiHiveTypeIds.notAssignedIdQueryParameter) + const factory IdQueryParameter.notAssigned() = NotAssignedIdQueryParameter; + @HiveType(typeId: PaperlessApiHiveTypeIds.anyAssignedIdQueryParameter) + const factory IdQueryParameter.anyAssigned() = AnyAssignedIdQueryParameter; + @HiveType(typeId: PaperlessApiHiveTypeIds.setIdQueryParameter) + const factory IdQueryParameter.fromId(@HiveField(0) int? id) = SetIdQueryParameter; Map toQueryParameter(String field) { - final Map params = {}; - if (onlyNotAssigned || onlyAssigned) { - params.putIfAbsent( - '${field}__isnull', () => assignmentStatus!.toString()); - } - if (isSet) { - params.putIfAbsent("${field}__id", () => id!.toString()); - } - return params; + return when( + unset: () => {}, + notAssigned: () => { + '${field}__isnull': '1', + }, + anyAssigned: () => { + '${field}__isnull': '0', + }, + fromId: (id) { + if (id == null) { + return {}; + } + return {'${field}_id': '$id'}; + }, + ); } + bool isOnlyNotAssigned() => this is NotAssignedIdQueryParameter; + + bool isOnlyAssigned() => this is AnyAssignedIdQueryParameter; + + bool isSet() => this is SetIdQueryParameter; + + bool isUnset() => this is UnsetIdQueryParameter; + bool matches(int? id) { - return onlyAssigned && id != null || - onlyNotAssigned && id == null || - isSet && id == this.id || - isUnset; + return when( + unset: () => true, + notAssigned: () => id == null, + anyAssigned: () => id != null, + fromId: (id_) => id == id_, + ); } - @override - List get props => [assignmentStatus, id]; - - Map toJson() => _$IdQueryParameterToJson(this); - - factory IdQueryParameter.fromJson(Map json) => - _$IdQueryParameterFromJson(json); + factory IdQueryParameter.fromJson(Map json) => _$IdQueryParameterFromJson(json); } -// @freezed -// class IdQueryParameter with _$IdQueryParameter { -// const IdQueryParameter._(); -// const factory IdQueryParameter.unset() = _UnsetIdQueryParameter; -// const factory IdQueryParameter.notAssigned() = _NotAssignedIdQueryParameter; -// const factory IdQueryParameter.anyAssigned() = _AnyAssignedIdQueryParameter; -// const factory IdQueryParameter.id(int id) = _SetIdQueryParameter; + +// @JsonSerializable() +// @HiveType(typeId: PaperlessApiHiveTypeIds.idQueryParameter) +// class IdQueryParameter extends Equatable { +// @HiveField(0) +// final int? assignmentStatus; +// @HiveField(1) +// final int? id; + +// @Deprecated("Use named constructors, this is only meant for code generation") +// const IdQueryParameter(this.assignmentStatus, this.id); + +// const IdQueryParameter.notAssigned() +// : assignmentStatus = 1, +// id = null; + +// const IdQueryParameter.anyAssigned() +// : assignmentStatus = 0, +// id = null; + +// const IdQueryParameter.fromId(this.id) : assignmentStatus = null; + +// const IdQueryParameter.unset() : this.fromId(null); + +// bool get isUnset => id == null && assignmentStatus == null; + +// bool get isSet => id != null && assignmentStatus == null; + +// bool get onlyNotAssigned => assignmentStatus == 1; + +// bool get onlyAssigned => assignmentStatus == 0; // Map toQueryParameter(String field) { -// return when( -// unset: () => {}, -// notAssigned: () => { -// '${field}__isnull': '1', -// }, -// anyAssigned: () => { -// '${field}__isnull': '0', -// }, -// id: (id) => { -// '${field}_id': '$id', -// }, -// ); +// final Map params = {}; +// if (onlyNotAssigned || onlyAssigned) { +// params.putIfAbsent('${field}__isnull', () => assignmentStatus!.toString()); +// } +// if (isSet) { +// params.putIfAbsent("${field}__id", () => id!.toString()); +// } +// return params; // } -// bool get onlyNotAssigned => this is _NotAssignedIdQueryParameter; - -// bool get onlyAssigned => this is _AnyAssignedIdQueryParameter; - -// bool get isSet => this is _SetIdQueryParameter; - -// bool get isUnset => this is _UnsetIdQueryParameter; // bool matches(int? id) { -// return when( -// unset: () => true, -// notAssigned: () => id == null, -// anyAssigned: () => id != null, -// id: (id_) => id == id_, -// ); +// return onlyAssigned && id != null || +// onlyNotAssigned && id == null || +// isSet && id == this.id || +// isUnset; // } -// factory IdQueryParameter.fromJson(Map json) => -// _$IdQueryParameterFromJson(json); +// @override +// List get props => [assignmentStatus, id]; + +// Map toJson() => _$IdQueryParameterToJson(this); + +// factory IdQueryParameter.fromJson(Map json) => _$IdQueryParameterFromJson(json); // } + diff --git a/packages/paperless_api/lib/src/models/query_parameters/id_query_parameter.freezed.dart b/packages/paperless_api/lib/src/models/query_parameters/id_query_parameter.freezed.dart new file mode 100644 index 0000000..5acf14e --- /dev/null +++ b/packages/paperless_api/lib/src/models/query_parameters/id_query_parameter.freezed.dart @@ -0,0 +1,686 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'id_query_parameter.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +IdQueryParameter _$IdQueryParameterFromJson(Map json) { + switch (json['runtimeType']) { + case 'unset': + return UnsetIdQueryParameter.fromJson(json); + case 'notAssigned': + return NotAssignedIdQueryParameter.fromJson(json); + case 'anyAssigned': + return AnyAssignedIdQueryParameter.fromJson(json); + case 'fromId': + return SetIdQueryParameter.fromJson(json); + + default: + throw CheckedFromJsonException(json, 'runtimeType', 'IdQueryParameter', + 'Invalid union type "${json['runtimeType']}"!'); + } +} + +/// @nodoc +mixin _$IdQueryParameter { + @optionalTypeArgs + TResult when({ + required TResult Function() unset, + required TResult Function() notAssigned, + required TResult Function() anyAssigned, + required TResult Function(@HiveField(0) int? id) fromId, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? unset, + TResult? Function()? notAssigned, + TResult? Function()? anyAssigned, + TResult? Function(@HiveField(0) int? id)? fromId, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? unset, + TResult Function()? notAssigned, + TResult Function()? anyAssigned, + TResult Function(@HiveField(0) int? id)? fromId, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(UnsetIdQueryParameter value) unset, + required TResult Function(NotAssignedIdQueryParameter value) notAssigned, + required TResult Function(AnyAssignedIdQueryParameter value) anyAssigned, + required TResult Function(SetIdQueryParameter value) fromId, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(UnsetIdQueryParameter value)? unset, + TResult? Function(NotAssignedIdQueryParameter value)? notAssigned, + TResult? Function(AnyAssignedIdQueryParameter value)? anyAssigned, + TResult? Function(SetIdQueryParameter value)? fromId, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(UnsetIdQueryParameter value)? unset, + TResult Function(NotAssignedIdQueryParameter value)? notAssigned, + TResult Function(AnyAssignedIdQueryParameter value)? anyAssigned, + TResult Function(SetIdQueryParameter value)? fromId, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + Map toJson() => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $IdQueryParameterCopyWith<$Res> { + factory $IdQueryParameterCopyWith( + IdQueryParameter value, $Res Function(IdQueryParameter) then) = + _$IdQueryParameterCopyWithImpl<$Res, IdQueryParameter>; +} + +/// @nodoc +class _$IdQueryParameterCopyWithImpl<$Res, $Val extends IdQueryParameter> + implements $IdQueryParameterCopyWith<$Res> { + _$IdQueryParameterCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; +} + +/// @nodoc +abstract class _$$UnsetIdQueryParameterCopyWith<$Res> { + factory _$$UnsetIdQueryParameterCopyWith(_$UnsetIdQueryParameter value, + $Res Function(_$UnsetIdQueryParameter) then) = + __$$UnsetIdQueryParameterCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$UnsetIdQueryParameterCopyWithImpl<$Res> + extends _$IdQueryParameterCopyWithImpl<$Res, _$UnsetIdQueryParameter> + implements _$$UnsetIdQueryParameterCopyWith<$Res> { + __$$UnsetIdQueryParameterCopyWithImpl(_$UnsetIdQueryParameter _value, + $Res Function(_$UnsetIdQueryParameter) _then) + : super(_value, _then); +} + +/// @nodoc +@JsonSerializable() +@HiveType(typeId: PaperlessApiHiveTypeIds.unsetIdQueryParameter) +class _$UnsetIdQueryParameter extends UnsetIdQueryParameter { + const _$UnsetIdQueryParameter({final String? $type}) + : $type = $type ?? 'unset', + super._(); + + factory _$UnsetIdQueryParameter.fromJson(Map json) => + _$$UnsetIdQueryParameterFromJson(json); + + @JsonKey(name: 'runtimeType') + final String $type; + + @override + String toString() { + return 'IdQueryParameter.unset()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$UnsetIdQueryParameter); + } + + @JsonKey(ignore: true) + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() unset, + required TResult Function() notAssigned, + required TResult Function() anyAssigned, + required TResult Function(@HiveField(0) int? id) fromId, + }) { + return unset(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? unset, + TResult? Function()? notAssigned, + TResult? Function()? anyAssigned, + TResult? Function(@HiveField(0) int? id)? fromId, + }) { + return unset?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? unset, + TResult Function()? notAssigned, + TResult Function()? anyAssigned, + TResult Function(@HiveField(0) int? id)? fromId, + required TResult orElse(), + }) { + if (unset != null) { + return unset(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(UnsetIdQueryParameter value) unset, + required TResult Function(NotAssignedIdQueryParameter value) notAssigned, + required TResult Function(AnyAssignedIdQueryParameter value) anyAssigned, + required TResult Function(SetIdQueryParameter value) fromId, + }) { + return unset(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(UnsetIdQueryParameter value)? unset, + TResult? Function(NotAssignedIdQueryParameter value)? notAssigned, + TResult? Function(AnyAssignedIdQueryParameter value)? anyAssigned, + TResult? Function(SetIdQueryParameter value)? fromId, + }) { + return unset?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(UnsetIdQueryParameter value)? unset, + TResult Function(NotAssignedIdQueryParameter value)? notAssigned, + TResult Function(AnyAssignedIdQueryParameter value)? anyAssigned, + TResult Function(SetIdQueryParameter value)? fromId, + required TResult orElse(), + }) { + if (unset != null) { + return unset(this); + } + return orElse(); + } + + @override + Map toJson() { + return _$$UnsetIdQueryParameterToJson( + this, + ); + } +} + +abstract class UnsetIdQueryParameter extends IdQueryParameter { + const factory UnsetIdQueryParameter() = _$UnsetIdQueryParameter; + const UnsetIdQueryParameter._() : super._(); + + factory UnsetIdQueryParameter.fromJson(Map json) = + _$UnsetIdQueryParameter.fromJson; +} + +/// @nodoc +abstract class _$$NotAssignedIdQueryParameterCopyWith<$Res> { + factory _$$NotAssignedIdQueryParameterCopyWith( + _$NotAssignedIdQueryParameter value, + $Res Function(_$NotAssignedIdQueryParameter) then) = + __$$NotAssignedIdQueryParameterCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$NotAssignedIdQueryParameterCopyWithImpl<$Res> + extends _$IdQueryParameterCopyWithImpl<$Res, _$NotAssignedIdQueryParameter> + implements _$$NotAssignedIdQueryParameterCopyWith<$Res> { + __$$NotAssignedIdQueryParameterCopyWithImpl( + _$NotAssignedIdQueryParameter _value, + $Res Function(_$NotAssignedIdQueryParameter) _then) + : super(_value, _then); +} + +/// @nodoc +@JsonSerializable() +@HiveType(typeId: PaperlessApiHiveTypeIds.notAssignedIdQueryParameter) +class _$NotAssignedIdQueryParameter extends NotAssignedIdQueryParameter { + const _$NotAssignedIdQueryParameter({final String? $type}) + : $type = $type ?? 'notAssigned', + super._(); + + factory _$NotAssignedIdQueryParameter.fromJson(Map json) => + _$$NotAssignedIdQueryParameterFromJson(json); + + @JsonKey(name: 'runtimeType') + final String $type; + + @override + String toString() { + return 'IdQueryParameter.notAssigned()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$NotAssignedIdQueryParameter); + } + + @JsonKey(ignore: true) + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() unset, + required TResult Function() notAssigned, + required TResult Function() anyAssigned, + required TResult Function(@HiveField(0) int? id) fromId, + }) { + return notAssigned(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? unset, + TResult? Function()? notAssigned, + TResult? Function()? anyAssigned, + TResult? Function(@HiveField(0) int? id)? fromId, + }) { + return notAssigned?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? unset, + TResult Function()? notAssigned, + TResult Function()? anyAssigned, + TResult Function(@HiveField(0) int? id)? fromId, + required TResult orElse(), + }) { + if (notAssigned != null) { + return notAssigned(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(UnsetIdQueryParameter value) unset, + required TResult Function(NotAssignedIdQueryParameter value) notAssigned, + required TResult Function(AnyAssignedIdQueryParameter value) anyAssigned, + required TResult Function(SetIdQueryParameter value) fromId, + }) { + return notAssigned(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(UnsetIdQueryParameter value)? unset, + TResult? Function(NotAssignedIdQueryParameter value)? notAssigned, + TResult? Function(AnyAssignedIdQueryParameter value)? anyAssigned, + TResult? Function(SetIdQueryParameter value)? fromId, + }) { + return notAssigned?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(UnsetIdQueryParameter value)? unset, + TResult Function(NotAssignedIdQueryParameter value)? notAssigned, + TResult Function(AnyAssignedIdQueryParameter value)? anyAssigned, + TResult Function(SetIdQueryParameter value)? fromId, + required TResult orElse(), + }) { + if (notAssigned != null) { + return notAssigned(this); + } + return orElse(); + } + + @override + Map toJson() { + return _$$NotAssignedIdQueryParameterToJson( + this, + ); + } +} + +abstract class NotAssignedIdQueryParameter extends IdQueryParameter { + const factory NotAssignedIdQueryParameter() = _$NotAssignedIdQueryParameter; + const NotAssignedIdQueryParameter._() : super._(); + + factory NotAssignedIdQueryParameter.fromJson(Map json) = + _$NotAssignedIdQueryParameter.fromJson; +} + +/// @nodoc +abstract class _$$AnyAssignedIdQueryParameterCopyWith<$Res> { + factory _$$AnyAssignedIdQueryParameterCopyWith( + _$AnyAssignedIdQueryParameter value, + $Res Function(_$AnyAssignedIdQueryParameter) then) = + __$$AnyAssignedIdQueryParameterCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$AnyAssignedIdQueryParameterCopyWithImpl<$Res> + extends _$IdQueryParameterCopyWithImpl<$Res, _$AnyAssignedIdQueryParameter> + implements _$$AnyAssignedIdQueryParameterCopyWith<$Res> { + __$$AnyAssignedIdQueryParameterCopyWithImpl( + _$AnyAssignedIdQueryParameter _value, + $Res Function(_$AnyAssignedIdQueryParameter) _then) + : super(_value, _then); +} + +/// @nodoc +@JsonSerializable() +@HiveType(typeId: PaperlessApiHiveTypeIds.anyAssignedIdQueryParameter) +class _$AnyAssignedIdQueryParameter extends AnyAssignedIdQueryParameter { + const _$AnyAssignedIdQueryParameter({final String? $type}) + : $type = $type ?? 'anyAssigned', + super._(); + + factory _$AnyAssignedIdQueryParameter.fromJson(Map json) => + _$$AnyAssignedIdQueryParameterFromJson(json); + + @JsonKey(name: 'runtimeType') + final String $type; + + @override + String toString() { + return 'IdQueryParameter.anyAssigned()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$AnyAssignedIdQueryParameter); + } + + @JsonKey(ignore: true) + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() unset, + required TResult Function() notAssigned, + required TResult Function() anyAssigned, + required TResult Function(@HiveField(0) int? id) fromId, + }) { + return anyAssigned(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? unset, + TResult? Function()? notAssigned, + TResult? Function()? anyAssigned, + TResult? Function(@HiveField(0) int? id)? fromId, + }) { + return anyAssigned?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? unset, + TResult Function()? notAssigned, + TResult Function()? anyAssigned, + TResult Function(@HiveField(0) int? id)? fromId, + required TResult orElse(), + }) { + if (anyAssigned != null) { + return anyAssigned(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(UnsetIdQueryParameter value) unset, + required TResult Function(NotAssignedIdQueryParameter value) notAssigned, + required TResult Function(AnyAssignedIdQueryParameter value) anyAssigned, + required TResult Function(SetIdQueryParameter value) fromId, + }) { + return anyAssigned(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(UnsetIdQueryParameter value)? unset, + TResult? Function(NotAssignedIdQueryParameter value)? notAssigned, + TResult? Function(AnyAssignedIdQueryParameter value)? anyAssigned, + TResult? Function(SetIdQueryParameter value)? fromId, + }) { + return anyAssigned?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(UnsetIdQueryParameter value)? unset, + TResult Function(NotAssignedIdQueryParameter value)? notAssigned, + TResult Function(AnyAssignedIdQueryParameter value)? anyAssigned, + TResult Function(SetIdQueryParameter value)? fromId, + required TResult orElse(), + }) { + if (anyAssigned != null) { + return anyAssigned(this); + } + return orElse(); + } + + @override + Map toJson() { + return _$$AnyAssignedIdQueryParameterToJson( + this, + ); + } +} + +abstract class AnyAssignedIdQueryParameter extends IdQueryParameter { + const factory AnyAssignedIdQueryParameter() = _$AnyAssignedIdQueryParameter; + const AnyAssignedIdQueryParameter._() : super._(); + + factory AnyAssignedIdQueryParameter.fromJson(Map json) = + _$AnyAssignedIdQueryParameter.fromJson; +} + +/// @nodoc +abstract class _$$SetIdQueryParameterCopyWith<$Res> { + factory _$$SetIdQueryParameterCopyWith(_$SetIdQueryParameter value, + $Res Function(_$SetIdQueryParameter) then) = + __$$SetIdQueryParameterCopyWithImpl<$Res>; + @useResult + $Res call({@HiveField(0) int? id}); +} + +/// @nodoc +class __$$SetIdQueryParameterCopyWithImpl<$Res> + extends _$IdQueryParameterCopyWithImpl<$Res, _$SetIdQueryParameter> + implements _$$SetIdQueryParameterCopyWith<$Res> { + __$$SetIdQueryParameterCopyWithImpl( + _$SetIdQueryParameter _value, $Res Function(_$SetIdQueryParameter) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + }) { + return _then(_$SetIdQueryParameter( + freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int?, + )); + } +} + +/// @nodoc +@JsonSerializable() +@HiveType(typeId: PaperlessApiHiveTypeIds.setIdQueryParameter) +class _$SetIdQueryParameter extends SetIdQueryParameter { + const _$SetIdQueryParameter(@HiveField(0) this.id, {final String? $type}) + : $type = $type ?? 'fromId', + super._(); + + factory _$SetIdQueryParameter.fromJson(Map json) => + _$$SetIdQueryParameterFromJson(json); + + @override + @HiveField(0) + final int? id; + + @JsonKey(name: 'runtimeType') + final String $type; + + @override + String toString() { + return 'IdQueryParameter.fromId(id: $id)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SetIdQueryParameter && + (identical(other.id, id) || other.id == id)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, id); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$SetIdQueryParameterCopyWith<_$SetIdQueryParameter> get copyWith => + __$$SetIdQueryParameterCopyWithImpl<_$SetIdQueryParameter>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() unset, + required TResult Function() notAssigned, + required TResult Function() anyAssigned, + required TResult Function(@HiveField(0) int? id) fromId, + }) { + return fromId(id); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? unset, + TResult? Function()? notAssigned, + TResult? Function()? anyAssigned, + TResult? Function(@HiveField(0) int? id)? fromId, + }) { + return fromId?.call(id); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? unset, + TResult Function()? notAssigned, + TResult Function()? anyAssigned, + TResult Function(@HiveField(0) int? id)? fromId, + required TResult orElse(), + }) { + if (fromId != null) { + return fromId(id); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(UnsetIdQueryParameter value) unset, + required TResult Function(NotAssignedIdQueryParameter value) notAssigned, + required TResult Function(AnyAssignedIdQueryParameter value) anyAssigned, + required TResult Function(SetIdQueryParameter value) fromId, + }) { + return fromId(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(UnsetIdQueryParameter value)? unset, + TResult? Function(NotAssignedIdQueryParameter value)? notAssigned, + TResult? Function(AnyAssignedIdQueryParameter value)? anyAssigned, + TResult? Function(SetIdQueryParameter value)? fromId, + }) { + return fromId?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(UnsetIdQueryParameter value)? unset, + TResult Function(NotAssignedIdQueryParameter value)? notAssigned, + TResult Function(AnyAssignedIdQueryParameter value)? anyAssigned, + TResult Function(SetIdQueryParameter value)? fromId, + required TResult orElse(), + }) { + if (fromId != null) { + return fromId(this); + } + return orElse(); + } + + @override + Map toJson() { + return _$$SetIdQueryParameterToJson( + this, + ); + } +} + +abstract class SetIdQueryParameter extends IdQueryParameter { + const factory SetIdQueryParameter(@HiveField(0) final int? id) = + _$SetIdQueryParameter; + const SetIdQueryParameter._() : super._(); + + factory SetIdQueryParameter.fromJson(Map json) = + _$SetIdQueryParameter.fromJson; + + @HiveField(0) + int? get id; + @JsonKey(ignore: true) + _$$SetIdQueryParameterCopyWith<_$SetIdQueryParameter> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/packages/paperless_api/lib/src/models/query_parameters/private/tags_query_type.dart b/packages/paperless_api/lib/src/models/query_parameters/private/tags_query_type.dart deleted file mode 100644 index c62a6a2..0000000 --- a/packages/paperless_api/lib/src/models/query_parameters/private/tags_query_type.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:json_annotation/json_annotation.dart'; - -@JsonEnum() -enum TagsQueryType { - notAssigned, - anyAssigned, - ids, - id, - include, - exclude; -} diff --git a/packages/paperless_api/lib/src/models/query_parameters/query_type.dart b/packages/paperless_api/lib/src/models/query_parameters/query_type.dart index 4216a89..02062fb 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/query_type.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/query_type.dart @@ -1,7 +1,17 @@ +import 'package:hive/hive.dart'; +import 'package:paperless_api/config/hive/hive_type_ids.dart'; + +part 'query_type.g.dart'; + +@HiveType(typeId: PaperlessApiHiveTypeIds.queryType) enum QueryType { + @HiveField(0) title('title__icontains'), + @HiveField(1) titleAndContent('title_content'), + @HiveField(2) extended('query'), + @HiveField(3) asn('asn'); final String queryParam; diff --git a/packages/paperless_api/lib/src/models/query_parameters/sort_field.dart b/packages/paperless_api/lib/src/models/query_parameters/sort_field.dart index 761b6d0..e6ce525 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/sort_field.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/sort_field.dart @@ -1,14 +1,27 @@ +import 'package:hive/hive.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'package:paperless_api/config/hive/hive_type_ids.dart'; + +part 'sort_field.g.dart'; @JsonEnum(valueField: 'queryString') +@HiveType(typeId: PaperlessApiHiveTypeIds.sortField) enum SortField { + @HiveField(0) archiveSerialNumber("archive_serial_number"), + @HiveField(1) correspondentName("correspondent__name"), + @HiveField(2) title("title"), + @HiveField(3) documentType("document_type__name"), + @HiveField(4) created("created"), + @HiveField(5) added("added"), + @HiveField(6) modified("modified"), + @HiveField(7) score("score"); final String queryString; diff --git a/packages/paperless_api/lib/src/models/query_parameters/sort_order.dart b/packages/paperless_api/lib/src/models/query_parameters/sort_order.dart index d11d0d2..e0eab4c 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/sort_order.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/sort_order.dart @@ -1,8 +1,15 @@ +import 'package:hive/hive.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'package:paperless_api/config/hive/hive_type_ids.dart'; + +part 'sort_order.g.dart'; @JsonEnum() +@HiveType(typeId: PaperlessApiHiveTypeIds.sortOrder) enum SortOrder { + @HiveField(0) ascending(""), + @HiveField(1) descending("-"); final String queryString; diff --git a/packages/paperless_api/lib/src/models/query_parameters/tags_query/any_assigned_tags_query.dart b/packages/paperless_api/lib/src/models/query_parameters/tags_query/any_assigned_tags_query.dart deleted file mode 100644 index 3bdff23..0000000 --- a/packages/paperless_api/lib/src/models/query_parameters/tags_query/any_assigned_tags_query.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:json_annotation/json_annotation.dart'; - -import 'tags_query.dart'; -part 'any_assigned_tags_query.g.dart'; - -@JsonSerializable(explicitToJson: true) -class AnyAssignedTagsQuery extends TagsQuery { - final Iterable tagIds; - - const AnyAssignedTagsQuery({ - this.tagIds = const [], - }); - - @override - Map toQueryParameter() { - if (tagIds.isEmpty) { - return {'is_tagged': '1'}; - } - return {'tags__id__in': tagIds.join(',')}; - } - - @override - List get props => [tagIds]; - - AnyAssignedTagsQuery withRemoved(Iterable ids) { - return AnyAssignedTagsQuery( - tagIds: tagIds.toSet().difference(ids.toSet()).toList(), - ); - } - - AnyAssignedTagsQuery withAdded(Iterable ids) { - return AnyAssignedTagsQuery(tagIds: [...tagIds, ...ids]); - } - - @override - Map toJson() => _$AnyAssignedTagsQueryToJson(this); - - factory AnyAssignedTagsQuery.fromJson(Map json) => - _$AnyAssignedTagsQueryFromJson(json); - - @override - bool matches(Iterable ids) { - return ids.isNotEmpty; - } -} diff --git a/packages/paperless_api/lib/src/models/query_parameters/tags_query/exclude_tag_id_query.dart b/packages/paperless_api/lib/src/models/query_parameters/tags_query/exclude_tag_id_query.dart deleted file mode 100644 index 69fdbdb..0000000 --- a/packages/paperless_api/lib/src/models/query_parameters/tags_query/exclude_tag_id_query.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:paperless_api/src/models/query_parameters/tags_query/tag_id_query.dart'; - -import 'include_tag_id_query.dart'; - -class ExcludeTagIdQuery extends TagIdQuery { - const ExcludeTagIdQuery(super.id); - - @override - String get methodName => 'exclude'; - - @override - TagIdQuery toggle() { - return IncludeTagIdQuery(id); - } -} diff --git a/packages/paperless_api/lib/src/models/query_parameters/tags_query/ids_tags_query.dart b/packages/paperless_api/lib/src/models/query_parameters/tags_query/ids_tags_query.dart deleted file mode 100644 index f8802f0..0000000 --- a/packages/paperless_api/lib/src/models/query_parameters/tags_query/ids_tags_query.dart +++ /dev/null @@ -1,94 +0,0 @@ -import 'package:json_annotation/json_annotation.dart'; -import 'package:collection/collection.dart'; -import 'exclude_tag_id_query.dart'; -import 'include_tag_id_query.dart'; -import 'tag_id_query.dart'; -import 'tags_query.dart'; - -class IdsTagsQuery extends TagsQuery { - final Iterable _idQueries; - - const IdsTagsQuery([this._idQueries = const []]); - - IdsTagsQuery.included(Iterable ids) - : _idQueries = ids.map((id) => IncludeTagIdQuery(id)); - - IdsTagsQuery.fromIds(Iterable ids) : this.included(ids); - - IdsTagsQuery.excluded(Iterable ids) - : _idQueries = ids.map((id) => ExcludeTagIdQuery(id)); - - IdsTagsQuery withIdQueriesAdded(Iterable idQueries) { - final intersection = idQueries - .map((idQ) => idQ.id) - .toSet() - .intersection(_idQueries.map((idQ) => idQ.id).toSet()); - final res = IdsTagsQuery( - [...withIdsRemoved(intersection).queries, ...idQueries], - ); - return res; - } - - IdsTagsQuery withIdsRemoved(Iterable ids) { - return IdsTagsQuery( - _idQueries.where((idQuery) => !ids.contains(idQuery.id)), - ); - } - - Iterable get queries => _idQueries; - - Iterable get includedIds { - return _idQueries.whereType().map((e) => e.id); - } - - Iterable get excludedIds { - return _idQueries.whereType().map((e) => e.id); - } - - /// - /// Returns a new instance with the type of the given [id] toggled. - /// E.g. if the provided [id] is currently registered as a [IncludeTagIdQuery], - /// then the new isntance will contain a [ExcludeTagIdQuery] with given id. - /// - IdsTagsQuery withIdQueryToggled(int id) { - return IdsTagsQuery( - _idQueries.map((idQ) => idQ.id == id ? idQ.toggle() : idQ), - ); - } - - Iterable get ids => [...includedIds, ...excludedIds]; - - @override - Map toQueryParameter() { - final Map params = {}; - if (includedIds.isNotEmpty) { - params.putIfAbsent('tags__id__all', () => includedIds.join(',')); - } - if (excludedIds.isNotEmpty) { - params.putIfAbsent('tags__id__none', () => excludedIds.join(',')); - } - return params; - } - - @override - List get props => [_idQueries]; - - @override - Map toJson() { - return { - 'queries': _idQueries.map((e) => e.toJson()).toList(), - }; - } - - factory IdsTagsQuery.fromJson(Map json) { - return IdsTagsQuery( - (json['queries'] as List).map((e) => TagIdQuery.fromJson(e)), - ); - } - - @override - bool matches(Iterable ids) { - return includedIds.toSet().difference(ids.toSet()).isEmpty && - excludedIds.toSet().intersection(ids.toSet()).isEmpty; - } -} diff --git a/packages/paperless_api/lib/src/models/query_parameters/tags_query/include_tag_id_query.dart b/packages/paperless_api/lib/src/models/query_parameters/tags_query/include_tag_id_query.dart deleted file mode 100644 index f0b6686..0000000 --- a/packages/paperless_api/lib/src/models/query_parameters/tags_query/include_tag_id_query.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:paperless_api/src/models/query_parameters/tags_query/tag_id_query.dart'; - -import 'exclude_tag_id_query.dart'; - -class IncludeTagIdQuery extends TagIdQuery { - const IncludeTagIdQuery(super.id); - - @override - String get methodName => 'include'; - - @override - TagIdQuery toggle() { - return ExcludeTagIdQuery(id); - } -} diff --git a/packages/paperless_api/lib/src/models/query_parameters/tags_query/only_not_assigned_tags_query.dart b/packages/paperless_api/lib/src/models/query_parameters/tags_query/only_not_assigned_tags_query.dart deleted file mode 100644 index 6d24678..0000000 --- a/packages/paperless_api/lib/src/models/query_parameters/tags_query/only_not_assigned_tags_query.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'tags_query.dart'; - -class OnlyNotAssignedTagsQuery extends TagsQuery { - const OnlyNotAssignedTagsQuery(); - @override - Map toQueryParameter() { - return {'is_tagged': '0'}; - } - - @override - List get props => []; - - @override - Map toJson() { - return {}; - } - - @override - bool matches(Iterable ids) { - return ids.isEmpty; - } -} diff --git a/packages/paperless_api/lib/src/models/query_parameters/tags_query/tag_id_query.dart b/packages/paperless_api/lib/src/models/query_parameters/tags_query/tag_id_query.dart deleted file mode 100644 index ded52e8..0000000 --- a/packages/paperless_api/lib/src/models/query_parameters/tags_query/tag_id_query.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:paperless_api/paperless_api.dart'; - -abstract class TagIdQuery extends Equatable { - final int id; - - const TagIdQuery(this.id); - - String get methodName; - - @override - List get props => [id, methodName]; - - TagIdQuery toggle(); - - Map toJson() { - return { - 'type': methodName, - 'id': id, - }; - } - - factory TagIdQuery.fromJson(Map json) { - final type = json['type'] as String; - var id = json['id']; - switch (type) { - case 'include': - return IncludeTagIdQuery(id); - case 'exclude': - return ExcludeTagIdQuery(id); - default: - throw Exception('Error parsing TagIdQuery: Unknown type $type'); - } - } -} diff --git a/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_queries.dart b/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_queries.dart deleted file mode 100644 index 9529320..0000000 --- a/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_queries.dart +++ /dev/null @@ -1,7 +0,0 @@ -export 'any_assigned_tags_query.dart'; -export 'ids_tags_query.dart'; -export 'tags_query.dart'; -export 'exclude_tag_id_query.dart'; -export 'include_tag_id_query.dart'; -export 'only_not_assigned_tags_query.dart'; -export 'tag_id_query.dart'; diff --git a/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart b/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart index 984d846..a87553b 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart @@ -1,9 +1,64 @@ import 'package:equatable/equatable.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:hive/hive.dart'; +import 'package:paperless_api/config/hive/hive_type_ids.dart'; +part 'tags_query.freezed.dart'; +part 'tags_query.g.dart'; -abstract class TagsQuery extends Equatable { - const TagsQuery(); - Map toQueryParameter(); - Map toJson(); +@freezed +class TagsQuery with _$TagsQuery { + const TagsQuery._(); + @HiveType(typeId: PaperlessApiHiveTypeIds.notAssignedTagsQuery) + const factory TagsQuery.notAssigned() = NotAssignedTagsQuery; - bool matches(Iterable ids); + @HiveType(typeId: PaperlessApiHiveTypeIds.anyAssignedTagsQuery) + const factory TagsQuery.anyAssigned({ + @Default([]) Iterable tagIds, + }) = AnyAssignedTagsQuery; + + @HiveType(typeId: PaperlessApiHiveTypeIds.idsTagsQuery) + const factory TagsQuery.ids({ + @Default([]) Iterable include, + @Default([]) Iterable exclude, + }) = IdsTagsQuery; + + Map toQueryParameter() { + return when( + anyAssigned: (tagIds) { + if (tagIds.isEmpty) { + return {'is_tagged': '1'}; + } + return {'tags__id__in': tagIds.join(',')}; + }, + ids: (include, exclude) { + final Map params = {}; + if (include.isNotEmpty) { + params.putIfAbsent('tags__id__all', () => include.join(',')); + } + if (exclude.isNotEmpty) { + params.putIfAbsent('tags__id__none', () => exclude.join(',')); + } + return params; + }, + notAssigned: () { + return {'is_tagged': '0'}; + }, + ); + } + + Map toJson() { + return {}; + } + + bool matches(Iterable ids) { + return when( + anyAssigned: (_) => ids.isNotEmpty, + ids: (include, exclude) => + include.toSet().difference(ids.toSet()).isEmpty && + exclude.toSet().intersection(ids.toSet()).isEmpty, + notAssigned: () => ids.isEmpty, + ); + } + + factory TagsQuery.fromJson(Map json) => _$TagsQueryFromJson(json); } diff --git a/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.freezed.dart b/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.freezed.dart new file mode 100644 index 0000000..d3d3ecf --- /dev/null +++ b/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.freezed.dart @@ -0,0 +1,566 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'tags_query.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +TagsQuery _$TagsQueryFromJson(Map json) { + switch (json['runtimeType']) { + case 'notAssigned': + return NotAssignedTagsQuery.fromJson(json); + case 'anyAssigned': + return AnyAssignedTagsQuery.fromJson(json); + case 'ids': + return IdsTagsQuery.fromJson(json); + + default: + throw CheckedFromJsonException(json, 'runtimeType', 'TagsQuery', + 'Invalid union type "${json['runtimeType']}"!'); + } +} + +/// @nodoc +mixin _$TagsQuery { + @optionalTypeArgs + TResult when({ + required TResult Function() notAssigned, + required TResult Function(Iterable tagIds) anyAssigned, + required TResult Function(Iterable include, Iterable exclude) ids, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? notAssigned, + TResult? Function(Iterable tagIds)? anyAssigned, + TResult? Function(Iterable include, Iterable exclude)? ids, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? notAssigned, + TResult Function(Iterable tagIds)? anyAssigned, + TResult Function(Iterable include, Iterable exclude)? ids, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(NotAssignedTagsQuery value) notAssigned, + required TResult Function(AnyAssignedTagsQuery value) anyAssigned, + required TResult Function(IdsTagsQuery value) ids, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(NotAssignedTagsQuery value)? notAssigned, + TResult? Function(AnyAssignedTagsQuery value)? anyAssigned, + TResult? Function(IdsTagsQuery value)? ids, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(NotAssignedTagsQuery value)? notAssigned, + TResult Function(AnyAssignedTagsQuery value)? anyAssigned, + TResult Function(IdsTagsQuery value)? ids, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + Map toJson() => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $TagsQueryCopyWith<$Res> { + factory $TagsQueryCopyWith(TagsQuery value, $Res Function(TagsQuery) then) = + _$TagsQueryCopyWithImpl<$Res, TagsQuery>; +} + +/// @nodoc +class _$TagsQueryCopyWithImpl<$Res, $Val extends TagsQuery> + implements $TagsQueryCopyWith<$Res> { + _$TagsQueryCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; +} + +/// @nodoc +abstract class _$$NotAssignedTagsQueryCopyWith<$Res> { + factory _$$NotAssignedTagsQueryCopyWith(_$NotAssignedTagsQuery value, + $Res Function(_$NotAssignedTagsQuery) then) = + __$$NotAssignedTagsQueryCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$NotAssignedTagsQueryCopyWithImpl<$Res> + extends _$TagsQueryCopyWithImpl<$Res, _$NotAssignedTagsQuery> + implements _$$NotAssignedTagsQueryCopyWith<$Res> { + __$$NotAssignedTagsQueryCopyWithImpl(_$NotAssignedTagsQuery _value, + $Res Function(_$NotAssignedTagsQuery) _then) + : super(_value, _then); +} + +/// @nodoc +@JsonSerializable() +@HiveType(typeId: PaperlessApiHiveTypeIds.notAssignedTagsQuery) +class _$NotAssignedTagsQuery extends NotAssignedTagsQuery { + const _$NotAssignedTagsQuery({final String? $type}) + : $type = $type ?? 'notAssigned', + super._(); + + factory _$NotAssignedTagsQuery.fromJson(Map json) => + _$$NotAssignedTagsQueryFromJson(json); + + @JsonKey(name: 'runtimeType') + final String $type; + + @override + String toString() { + return 'TagsQuery.notAssigned()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$NotAssignedTagsQuery); + } + + @JsonKey(ignore: true) + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() notAssigned, + required TResult Function(Iterable tagIds) anyAssigned, + required TResult Function(Iterable include, Iterable exclude) ids, + }) { + return notAssigned(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? notAssigned, + TResult? Function(Iterable tagIds)? anyAssigned, + TResult? Function(Iterable include, Iterable exclude)? ids, + }) { + return notAssigned?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? notAssigned, + TResult Function(Iterable tagIds)? anyAssigned, + TResult Function(Iterable include, Iterable exclude)? ids, + required TResult orElse(), + }) { + if (notAssigned != null) { + return notAssigned(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(NotAssignedTagsQuery value) notAssigned, + required TResult Function(AnyAssignedTagsQuery value) anyAssigned, + required TResult Function(IdsTagsQuery value) ids, + }) { + return notAssigned(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(NotAssignedTagsQuery value)? notAssigned, + TResult? Function(AnyAssignedTagsQuery value)? anyAssigned, + TResult? Function(IdsTagsQuery value)? ids, + }) { + return notAssigned?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(NotAssignedTagsQuery value)? notAssigned, + TResult Function(AnyAssignedTagsQuery value)? anyAssigned, + TResult Function(IdsTagsQuery value)? ids, + required TResult orElse(), + }) { + if (notAssigned != null) { + return notAssigned(this); + } + return orElse(); + } + + @override + Map toJson() { + return _$$NotAssignedTagsQueryToJson( + this, + ); + } +} + +abstract class NotAssignedTagsQuery extends TagsQuery { + const factory NotAssignedTagsQuery() = _$NotAssignedTagsQuery; + const NotAssignedTagsQuery._() : super._(); + + factory NotAssignedTagsQuery.fromJson(Map json) = + _$NotAssignedTagsQuery.fromJson; +} + +/// @nodoc +abstract class _$$AnyAssignedTagsQueryCopyWith<$Res> { + factory _$$AnyAssignedTagsQueryCopyWith(_$AnyAssignedTagsQuery value, + $Res Function(_$AnyAssignedTagsQuery) then) = + __$$AnyAssignedTagsQueryCopyWithImpl<$Res>; + @useResult + $Res call({Iterable tagIds}); +} + +/// @nodoc +class __$$AnyAssignedTagsQueryCopyWithImpl<$Res> + extends _$TagsQueryCopyWithImpl<$Res, _$AnyAssignedTagsQuery> + implements _$$AnyAssignedTagsQueryCopyWith<$Res> { + __$$AnyAssignedTagsQueryCopyWithImpl(_$AnyAssignedTagsQuery _value, + $Res Function(_$AnyAssignedTagsQuery) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? tagIds = null, + }) { + return _then(_$AnyAssignedTagsQuery( + tagIds: null == tagIds + ? _value.tagIds + : tagIds // ignore: cast_nullable_to_non_nullable + as Iterable, + )); + } +} + +/// @nodoc +@JsonSerializable() +@HiveType(typeId: PaperlessApiHiveTypeIds.anyAssignedTagsQuery) +class _$AnyAssignedTagsQuery extends AnyAssignedTagsQuery { + const _$AnyAssignedTagsQuery({this.tagIds = const [], final String? $type}) + : $type = $type ?? 'anyAssigned', + super._(); + + factory _$AnyAssignedTagsQuery.fromJson(Map json) => + _$$AnyAssignedTagsQueryFromJson(json); + + @override + @JsonKey() + final Iterable tagIds; + + @JsonKey(name: 'runtimeType') + final String $type; + + @override + String toString() { + return 'TagsQuery.anyAssigned(tagIds: $tagIds)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$AnyAssignedTagsQuery && + const DeepCollectionEquality().equals(other.tagIds, tagIds)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(tagIds)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$AnyAssignedTagsQueryCopyWith<_$AnyAssignedTagsQuery> get copyWith => + __$$AnyAssignedTagsQueryCopyWithImpl<_$AnyAssignedTagsQuery>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() notAssigned, + required TResult Function(Iterable tagIds) anyAssigned, + required TResult Function(Iterable include, Iterable exclude) ids, + }) { + return anyAssigned(tagIds); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? notAssigned, + TResult? Function(Iterable tagIds)? anyAssigned, + TResult? Function(Iterable include, Iterable exclude)? ids, + }) { + return anyAssigned?.call(tagIds); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? notAssigned, + TResult Function(Iterable tagIds)? anyAssigned, + TResult Function(Iterable include, Iterable exclude)? ids, + required TResult orElse(), + }) { + if (anyAssigned != null) { + return anyAssigned(tagIds); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(NotAssignedTagsQuery value) notAssigned, + required TResult Function(AnyAssignedTagsQuery value) anyAssigned, + required TResult Function(IdsTagsQuery value) ids, + }) { + return anyAssigned(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(NotAssignedTagsQuery value)? notAssigned, + TResult? Function(AnyAssignedTagsQuery value)? anyAssigned, + TResult? Function(IdsTagsQuery value)? ids, + }) { + return anyAssigned?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(NotAssignedTagsQuery value)? notAssigned, + TResult Function(AnyAssignedTagsQuery value)? anyAssigned, + TResult Function(IdsTagsQuery value)? ids, + required TResult orElse(), + }) { + if (anyAssigned != null) { + return anyAssigned(this); + } + return orElse(); + } + + @override + Map toJson() { + return _$$AnyAssignedTagsQueryToJson( + this, + ); + } +} + +abstract class AnyAssignedTagsQuery extends TagsQuery { + const factory AnyAssignedTagsQuery({final Iterable tagIds}) = + _$AnyAssignedTagsQuery; + const AnyAssignedTagsQuery._() : super._(); + + factory AnyAssignedTagsQuery.fromJson(Map json) = + _$AnyAssignedTagsQuery.fromJson; + + Iterable get tagIds; + @JsonKey(ignore: true) + _$$AnyAssignedTagsQueryCopyWith<_$AnyAssignedTagsQuery> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$IdsTagsQueryCopyWith<$Res> { + factory _$$IdsTagsQueryCopyWith( + _$IdsTagsQuery value, $Res Function(_$IdsTagsQuery) then) = + __$$IdsTagsQueryCopyWithImpl<$Res>; + @useResult + $Res call({Iterable include, Iterable exclude}); +} + +/// @nodoc +class __$$IdsTagsQueryCopyWithImpl<$Res> + extends _$TagsQueryCopyWithImpl<$Res, _$IdsTagsQuery> + implements _$$IdsTagsQueryCopyWith<$Res> { + __$$IdsTagsQueryCopyWithImpl( + _$IdsTagsQuery _value, $Res Function(_$IdsTagsQuery) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? include = null, + Object? exclude = null, + }) { + return _then(_$IdsTagsQuery( + include: null == include + ? _value.include + : include // ignore: cast_nullable_to_non_nullable + as Iterable, + exclude: null == exclude + ? _value.exclude + : exclude // ignore: cast_nullable_to_non_nullable + as Iterable, + )); + } +} + +/// @nodoc +@JsonSerializable() +@HiveType(typeId: PaperlessApiHiveTypeIds.idsTagsQuery) +class _$IdsTagsQuery extends IdsTagsQuery { + const _$IdsTagsQuery( + {this.include = const [], this.exclude = const [], final String? $type}) + : $type = $type ?? 'ids', + super._(); + + factory _$IdsTagsQuery.fromJson(Map json) => + _$$IdsTagsQueryFromJson(json); + + @override + @JsonKey() + final Iterable include; + @override + @JsonKey() + final Iterable exclude; + + @JsonKey(name: 'runtimeType') + final String $type; + + @override + String toString() { + return 'TagsQuery.ids(include: $include, exclude: $exclude)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$IdsTagsQuery && + const DeepCollectionEquality().equals(other.include, include) && + const DeepCollectionEquality().equals(other.exclude, exclude)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(include), + const DeepCollectionEquality().hash(exclude)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$IdsTagsQueryCopyWith<_$IdsTagsQuery> get copyWith => + __$$IdsTagsQueryCopyWithImpl<_$IdsTagsQuery>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() notAssigned, + required TResult Function(Iterable tagIds) anyAssigned, + required TResult Function(Iterable include, Iterable exclude) ids, + }) { + return ids(include, exclude); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? notAssigned, + TResult? Function(Iterable tagIds)? anyAssigned, + TResult? Function(Iterable include, Iterable exclude)? ids, + }) { + return ids?.call(include, exclude); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? notAssigned, + TResult Function(Iterable tagIds)? anyAssigned, + TResult Function(Iterable include, Iterable exclude)? ids, + required TResult orElse(), + }) { + if (ids != null) { + return ids(include, exclude); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(NotAssignedTagsQuery value) notAssigned, + required TResult Function(AnyAssignedTagsQuery value) anyAssigned, + required TResult Function(IdsTagsQuery value) ids, + }) { + return ids(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(NotAssignedTagsQuery value)? notAssigned, + TResult? Function(AnyAssignedTagsQuery value)? anyAssigned, + TResult? Function(IdsTagsQuery value)? ids, + }) { + return ids?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(NotAssignedTagsQuery value)? notAssigned, + TResult Function(AnyAssignedTagsQuery value)? anyAssigned, + TResult Function(IdsTagsQuery value)? ids, + required TResult orElse(), + }) { + if (ids != null) { + return ids(this); + } + return orElse(); + } + + @override + Map toJson() { + return _$$IdsTagsQueryToJson( + this, + ); + } +} + +abstract class IdsTagsQuery extends TagsQuery { + const factory IdsTagsQuery( + {final Iterable include, + final Iterable exclude}) = _$IdsTagsQuery; + const IdsTagsQuery._() : super._(); + + factory IdsTagsQuery.fromJson(Map json) = + _$IdsTagsQuery.fromJson; + + Iterable get include; + Iterable get exclude; + @JsonKey(ignore: true) + _$$IdsTagsQueryCopyWith<_$IdsTagsQuery> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/packages/paperless_api/lib/src/models/query_parameters/text_query.dart b/packages/paperless_api/lib/src/models/query_parameters/text_query.dart index fdebfb8..d024790 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/text_query.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/text_query.dart @@ -1,13 +1,18 @@ import 'package:equatable/equatable.dart'; +import 'package:hive/hive.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'package:paperless_api/config/hive/hive_type_ids.dart'; import 'query_type.dart'; part 'text_query.g.dart'; +@HiveType(typeId: PaperlessApiHiveTypeIds.textQuery) @JsonSerializable() class TextQuery extends Equatable { + @HiveField(0) final QueryType queryType; + @HiveField(1) final String? queryText; const TextQuery({ @@ -17,8 +22,7 @@ class TextQuery extends Equatable { const TextQuery.title(this.queryText) : queryType = QueryType.title; - const TextQuery.titleAndContent(this.queryText) - : queryType = QueryType.titleAndContent; + const TextQuery.titleAndContent(this.queryText) : queryType = QueryType.titleAndContent; const TextQuery.extended(this.queryText) : queryType = QueryType.extended; @@ -68,8 +72,7 @@ class TextQuery extends Equatable { case QueryType.title: return title.contains(queryText!); case QueryType.titleAndContent: - return title.contains(queryText!) || - (content?.contains(queryText!) ?? false); + return title.contains(queryText!) || (content?.contains(queryText!) ?? false); case QueryType.extended: //TODO: Implement. Might be too complex... return true; @@ -80,8 +83,7 @@ class TextQuery extends Equatable { Map toJson() => _$TextQueryToJson(this); - factory TextQuery.fromJson(Map json) => - _$TextQueryFromJson(json); + factory TextQuery.fromJson(Map json) => _$TextQueryFromJson(json); @override List get props => [queryType, queryText]; diff --git a/packages/paperless_api/test/saved_view_test.dart b/packages/paperless_api/test/saved_view_test.dart index 37e842b..e87d979 100644 --- a/packages/paperless_api/test/saved_view_test.dart +++ b/packages/paperless_api/test/saved_view_test.dart @@ -1,6 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:paperless_api/paperless_api.dart'; -import 'package:paperless_api/src/models/query_parameters/tags_query/include_tag_id_query.dart'; import 'package:paperless_api/src/models/query_parameters/text_query.dart'; void main() { @@ -70,13 +69,9 @@ void main() { correspondent: const IdQueryParameter.fromId(42), documentType: const IdQueryParameter.fromId(69), storagePath: const IdQueryParameter.fromId(14), - tags: const IdsTagsQuery( - [ - IncludeTagIdQuery(1), - IncludeTagIdQuery(2), - ExcludeTagIdQuery(3), - ExcludeTagIdQuery(4), - ], + tags: const TagsQuery.ids( + include: [1, 2], + exclude: [3, 4], ), created: AbsoluteDateRangeQuery( before: DateTime.parse("2022-10-27"), @@ -140,7 +135,7 @@ void main() { correspondent: const IdQueryParameter.notAssigned(), documentType: const IdQueryParameter.notAssigned(), storagePath: const IdQueryParameter.notAssigned(), - tags: const OnlyNotAssignedTagsQuery(), + tags: const TagsQuery.notAssigned(), ); expect( actual, @@ -157,13 +152,10 @@ void main() { correspondent: const IdQueryParameter.fromId(1), documentType: const IdQueryParameter.fromId(2), storagePath: const IdQueryParameter.fromId(3), - tags: const IdsTagsQuery([ - IncludeTagIdQuery(4), - IncludeTagIdQuery(5), - ExcludeTagIdQuery(6), - ExcludeTagIdQuery(7), - ExcludeTagIdQuery(8), - ]), + tags: const TagsQuery.ids( + include: [4, 5], + exclude: [6, 7, 8], + ), sortField: SortField.added, sortOrder: SortOrder.ascending, created: AbsoluteDateRangeQuery( @@ -241,11 +233,11 @@ void main() { test('Values are correctly parsed if not assigned.', () { expect( SavedView.fromDocumentFilter( - DocumentFilter( - correspondent: const IdQueryParameter.notAssigned(), - documentType: const IdQueryParameter.notAssigned(), - storagePath: const IdQueryParameter.notAssigned(), - tags: const OnlyNotAssignedTagsQuery(), + const DocumentFilter( + correspondent: IdQueryParameter.notAssigned(), + documentType: IdQueryParameter.notAssigned(), + storagePath: IdQueryParameter.notAssigned(), + tags: TagsQuery.notAssigned(), sortField: SortField.created, sortOrder: SortOrder.ascending, ),