diff --git a/android/app/build.gradle b/android/app/build.gradle index 20e446a..43710e2 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -70,7 +70,7 @@ android { } buildTypes { release { - signingConfig signingConfigs.release + signingConfig signingConfigs.debug } } diff --git a/lib/core/config/hive/custpm_adapters/theme_mode_adapter.dart b/lib/core/config/hive/custom_adapters/theme_mode_adapter.dart similarity index 100% rename from lib/core/config/hive/custpm_adapters/theme_mode_adapter.dart rename to lib/core/config/hive/custom_adapters/theme_mode_adapter.dart diff --git a/lib/core/config/hive/hive_config.dart b/lib/core/config/hive/hive_config.dart index afa52ff..3205853 100644 --- a/lib/core/config/hive/hive_config.dart +++ b/lib/core/config/hive/hive_config.dart @@ -1,5 +1,6 @@ import 'package:hive_flutter/adapters.dart'; -import 'package:paperless_mobile/core/config/hive/custpm_adapters/theme_mode_adapter.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/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'; @@ -38,6 +39,7 @@ void registerHiveAdapters() { Hive.registerAdapter(UserSettingsAdapter()); Hive.registerAdapter(UserCredentialsAdapter()); Hive.registerAdapter(UserAccountAdapter()); + Hive.registerAdapter(DocumentFilterAdapter()); } extension HiveSingleValueBox on Box { diff --git a/lib/features/document_search/cubit/document_search_cubit.dart b/lib/features/document_search/cubit/document_search_cubit.dart index a51dbea..8dfe803 100644 --- a/lib/features/document_search/cubit/document_search_cubit.dart +++ b/lib/features/document_search/cubit/document_search_cubit.dart @@ -3,13 +3,14 @@ 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/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/features/settings/model/view_type.dart'; part 'document_search_state.dart'; - part 'document_search_cubit.g.dart'; class DocumentSearchCubit extends HydratedCubit @@ -20,7 +21,6 @@ class DocumentSearchCubit extends HydratedCubit final LabelRepository _labelRepository; @override final DocumentChangedNotifier notifier; - DocumentSearchCubit(this.api, this.notifier, this._labelRepository) : super(const DocumentSearchState()) { _labelRepository.addListener( @@ -119,4 +119,8 @@ class DocumentSearchCubit extends HydratedCubit Map? toJson(DocumentSearchState state) { return state.toJson(); } + + @override + // TODO: implement account + UserAccount get account => throw UnimplementedError(); } diff --git a/lib/features/document_search/cubit/document_search_state.dart b/lib/features/document_search/cubit/document_search_state.dart index 2809af8..8855670 100644 --- a/lib/features/document_search/cubit/document_search_state.dart +++ b/lib/features/document_search/cubit/document_search_state.dart @@ -24,7 +24,7 @@ class DocumentSearchState extends DocumentPagingState { this.searchHistory = const [], this.suggestions = const [], this.viewType = ViewType.detailed, - super.filter, + super.filter = const DocumentFilter(), super.hasLoaded, super.isLoading, super.value, diff --git a/lib/features/document_search/view/sliver_search_bar.dart b/lib/features/document_search/view/sliver_search_bar.dart index 482232d..e4eb87e 100644 --- a/lib/features/document_search/view/sliver_search_bar.dart +++ b/lib/features/document_search/view/sliver_search_bar.dart @@ -5,7 +5,8 @@ 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/features/settings/view/dialogs/account_settings_dialog.dart'; @@ -45,10 +46,14 @@ 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/documents/cubit/documents_cubit.dart b/lib/features/documents/cubit/documents_cubit.dart index 4e6ad88..6baedda 100644 --- a/lib/features/documents/cubit/documents_cubit.dart +++ b/lib/features/documents/cubit/documents_cubit.dart @@ -6,6 +6,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/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'; @@ -23,8 +24,15 @@ class DocumentsCubit extends HydratedCubit @override final DocumentChangedNotifier notifier; - DocumentsCubit(this.api, this.notifier, this._labelRepository) - : super(const DocumentsState()) { + @override + final UserAccount account; + + DocumentsCubit( + this.api, + this.notifier, + this._labelRepository, + this.account, + ) : super(DocumentsState(filter: account.settings.currentDocumentFilter)) { notifier.addListener( this, onUpdated: (document) { diff --git a/lib/features/home/view/home_page.dart b/lib/features/home/view/home_page.dart index 6ae9ce6..5c577d0 100644 --- a/lib/features/home/view/home_page.dart +++ b/lib/features/home/view/home_page.dart @@ -6,9 +6,11 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hive/hive.dart'; 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/global/constants.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/saved_view_repository.dart'; @@ -26,6 +28,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/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'; @@ -213,24 +216,25 @@ class _HomePageState extends State with WidgetsBindingObserver { label: S.of(context)!.labels, ), RouteDescription( - icon: const Icon(Icons.inbox_outlined), - selectedIcon: Icon( - Icons.inbox, - color: Theme.of(context).colorScheme.primary, - ), - label: S.of(context)!.inbox, - badgeBuilder: (icon) => BlocBuilder( - bloc: _inboxCubit, - builder: (context, state) { - if (state.itemsInInboxCount > 0) { - return Badge.count( - count: state.itemsInInboxCount, - child: icon, - ); - } - return icon; - }, - )), + icon: const Icon(Icons.inbox_outlined), + selectedIcon: Icon( + Icons.inbox, + color: Theme.of(context).colorScheme.primary, + ), + label: S.of(context)!.inbox, + badgeBuilder: (icon) => BlocBuilder( + bloc: _inboxCubit, + builder: (context, state) { + if (state.itemsInInboxCount > 0) { + return Badge.count( + count: state.itemsInInboxCount, + child: icon, + ); + } + return icon; + }, + ), + ), ]; final routes = [ MultiBlocProvider( @@ -241,6 +245,7 @@ class _HomePageState extends State with WidgetsBindingObserver { context.read(), context.read(), context.read(), + Hive.box(HiveBoxes.userAccount).get(userId)!, )..reload(), ), BlocProvider( @@ -275,7 +280,8 @@ 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); }, @@ -284,7 +290,9 @@ 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!); } }, ), @@ -297,7 +305,9 @@ 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, ), @@ -315,7 +325,8 @@ 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 0eb1c3f..c9c193e 100644 --- a/lib/features/home/view/widget/verify_identity_page.dart +++ b/lib/features/home/view/widget/verify_identity_page.dart @@ -24,7 +24,7 @@ class VerifyIdentityPage extends StatelessWidget { backgroundColor: Theme.of(context).colorScheme.background, title: Text(S.of(context)!.verifyYourIdentity), ), - body: UserSettingsBuilder( + body: UserAccountBuilder( builder: (context, settings) { if (settings == null) { return const SizedBox.shrink(); @@ -32,7 +32,9 @@ 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, @@ -54,7 +56,9 @@ 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 e2b3493..3e15ca4 100644 --- a/lib/features/inbox/cubit/inbox_cubit.dart +++ b/lib/features/inbox/cubit/inbox_cubit.dart @@ -13,7 +13,8 @@ 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; @@ -31,12 +32,17 @@ class InboxCubit extends HydratedCubit with DocumentPagingBlocMixin this._statsApi, this._labelRepository, this.notifier, - ) : super(InboxState(labels: _labelRepository.state)) { + ) : super(InboxState( + labels: _labelRepository.state, + )) { notifier.addListener( 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 { @@ -135,7 +141,8 @@ class InboxCubit extends HydratedCubit with DocumentPagingBlocMixin /// 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( @@ -189,8 +196,8 @@ class InboxCubit extends HydratedCubit with DocumentPagingBlocMixin 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); } diff --git a/lib/features/labels/view/widgets/label_item.dart b/lib/features/labels/view/widgets/label_item.dart index ee2e993..47b6264 100644 --- a/lib/features/labels/view/widgets/label_item.dart +++ b/lib/features/labels/view/widgets/label_item.dart @@ -1,8 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:hive/hive.dart'; 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/helpers/format_helpers.dart'; class LabelItem extends StatelessWidget { @@ -42,6 +46,10 @@ class LabelItem extends StatelessWidget { onPressed: (label.documentCount ?? 0) == 0 ? null : () { + final currentUser = + Hive.box(HiveBoxes.globalSettings) + .getValue()! + .currentLoggedInUser!; final filter = filterBuilder(label); Navigator.push( context, @@ -52,6 +60,8 @@ class LabelItem extends StatelessWidget { context.read(), context.read(), context.read(), + 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 8bce26c..9c166e8 100644 --- a/lib/features/linked_documents/cubit/linked_documents_cubit.dart +++ b/lib/features/linked_documents/cubit/linked_documents_cubit.dart @@ -3,6 +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/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'; @@ -20,12 +21,16 @@ class LinkedDocumentsCubit extends HydratedCubit final LabelRepository _labelRepository; + @override + // TODO: implement account + final UserAccount account; LinkedDocumentsCubit( DocumentFilter filter, this.api, this.notifier, this._labelRepository, - ) : super(const LinkedDocumentsState()) { + this.account, + ) : super(LinkedDocumentsState(filter: filter)) { updateFilter(filter: filter); _labelRepository.addListener( this, diff --git a/lib/features/linked_documents/cubit/linked_documents_state.dart b/lib/features/linked_documents/cubit/linked_documents_state.dart index 9e02c0a..aa75e66 100644 --- a/lib/features/linked_documents/cubit/linked_documents_state.dart +++ b/lib/features/linked_documents/cubit/linked_documents_state.dart @@ -12,7 +12,7 @@ class LinkedDocumentsState extends DocumentPagingState { const LinkedDocumentsState({ this.viewType = ViewType.list, - super.filter, + super.filter = const DocumentFilter(), super.isLoading, super.hasLoaded, super.value, diff --git a/lib/features/login/cubit/authentication_cubit.dart b/lib/features/login/cubit/authentication_cubit.dart index e06a66c..0dc82e2 100644 --- a/lib/features/login/cubit/authentication_cubit.dart +++ b/lib/features/login/cubit/authentication_cubit.dart @@ -62,17 +62,17 @@ class AuthenticationCubit extends Cubit { final userId = "${credentials.username}@$serverUrl"; // If it is first time login, create settings for this user. - final userSettingsBox = Hive.box(HiveBoxes.userSettings); final userAccountBox = Hive.box(HiveBoxes.userAccount); - if (!userSettingsBox.containsKey(userId)) { - userSettingsBox.put(userId, UserSettings()); - } - final fullName = await _fetchFullName(); + 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, @@ -81,7 +81,8 @@ class AuthenticationCubit extends Cubit { } // Mark logged in user as currently active user. - final globalSettings = GlobalSettings.boxedValue; + final globalSettings = + Hive.box(HiveBoxes.globalSettings).getValue()!; globalSettings.currentLoggedInUser = userId; globalSettings.save(); @@ -108,26 +109,25 @@ class AuthenticationCubit extends Cubit { /// Switches to another account if it exists. Future switchAccount(String userId) async { - final globalSettings = GlobalSettings.boxedValue; + final globalSettings = + Hive.box(HiveBoxes.globalSettings).getValue()!; if (globalSettings.currentLoggedInUser == userId) { return; } final userAccountBox = Hive.box(HiveBoxes.userAccount); - final userSettingsBox = Hive.box(HiveBoxes.userSettings); - if (!userSettingsBox.containsKey(userId)) { + if (!userAccountBox.containsKey(userId)) { debugPrint("User $userId not yet registered."); return; } - final userSettings = userSettingsBox.get(userId)!; final account = userAccountBox.get(userId)!; - if (userSettings.isBiometricAuthenticationEnabled) { - final authenticated = - await _localAuthService.authenticateLocalUser("Authenticate to switch your account."); + if (account.settings.isBiometricAuthenticationEnabled) { + final authenticated = await _localAuthService + .authenticateLocalUser("Authenticate to switch your account."); if (!authenticated) { - debugPrint("User unable to authenticate."); + debugPrint("User not authenticated."); return; } } @@ -172,7 +172,6 @@ class AuthenticationCubit extends Cubit { final userId = "${credentials.username}@$serverUrl"; final userAccountsBox = Hive.box(HiveBoxes.userAccount); - final userSettingsBox = Hive.box(HiveBoxes.userSettings); if (userAccountsBox.containsKey(userId)) { throw Exception("User already exists"); @@ -192,18 +191,19 @@ class AuthenticationCubit extends Cubit { password: credentials.password!, ); sessionManager.resetSettings(); - await userSettingsBox.put( - userId, - UserSettings( - isBiometricAuthenticationEnabled: enableBiometricAuthentication, - ), - ); + final fullName = await _fetchFullName(); + await userAccountsBox.put( userId, UserAccount( + id: userId, serverUrl: serverUrl, username: credentials.username!, + settings: UserSettings( + isBiometricAuthenticationEnabled: enableBiometricAuthentication, + currentDocumentFilter: DocumentFilter(), + ), fullName: fullName, ), ); @@ -221,15 +221,14 @@ class AuthenticationCubit extends Cubit { } Future removeAccount(String userId) async { - final globalSettings = GlobalSettings.boxedValue; + final globalSettings = + Hive.box(HiveBoxes.globalSettings).getValue()!; final currentUser = globalSettings.currentLoggedInUser; final userAccountBox = Hive.box(HiveBoxes.userAccount); final userCredentialsBox = await _getUserCredentialsBox(); - final userSettingsBox = Hive.box(HiveBoxes.userSettings); await userAccountBox.delete(userId); await userCredentialsBox.delete(userId); - await userSettingsBox.delete(userId); if (currentUser == userId) { return logout(); @@ -240,27 +239,30 @@ class AuthenticationCubit extends Cubit { /// Performs a conditional hydration based on the local authentication success. /// Future restoreSessionState() async { - final globalSettings = GlobalSettings.boxedValue; + 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 userSettings = Hive.box(HiveBoxes.userSettings).get(userId)!; - final userAccount = Hive.box(HiveBoxes.userAccount).get(userId)!; + final userAccount = + Hive.box(HiveBoxes.userAccount).get(userId)!; - if (userSettings.isBiometricAuthenticationEnabled) { - final localAuthSuccess = - await _localAuthService.authenticateLocalUser("Authenticate to log back in"); //TODO: INTL + if (userAccount.settings.isBiometricAuthenticationEnabled) { + 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, @@ -276,13 +278,15 @@ class AuthenticationCubit extends Cubit { ), ); } else { - throw Exception("User should be authenticated but no authentication information was found."); + throw Exception( + "User should be authenticated but no authentication information was found."); } } Future logout() async { await _resetExternalState(); - final globalSettings = GlobalSettings.boxedValue; + final globalSettings = + Hive.box(HiveBoxes.globalSettings).getValue()!; globalSettings ..currentLoggedInUser = null ..save(); diff --git a/lib/features/login/model/user_account.dart b/lib/features/login/model/user_account.dart index 4894cf6..f253f5c 100644 --- a/lib/features/login/model/user_account.dart +++ b/lib/features/login/model/user_account.dart @@ -1,20 +1,31 @@ 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'; part 'user_account.g.dart'; @HiveType(typeId: HiveTypeIds.userAccount) -class UserAccount { +class UserAccount extends HiveObject { @HiveField(0) final String serverUrl; + @HiveField(1) final String username; + @HiveField(2) final String? fullName; + @HiveField(3) + final String id; + + @HiveField(4) + UserSettings settings; + UserAccount({ + required this.id, required this.serverUrl, required this.username, + required this.settings, this.fullName, }); } 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 c58c1cc..6d954d5 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,6 +2,7 @@ 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'; @@ -13,6 +14,7 @@ mixin DocumentPagingBlocMixin on BlocBase { PaperlessDocumentsApi get api; DocumentChangedNotifier get notifier; + UserAccount get account; Future loadMore() async { if (state.isLastPageLoaded) { @@ -28,6 +30,8 @@ mixin DocumentPagingBlocMixin value: [...state.value, result], )); } finally { + account.settings.currentDocumentFilter = newFilter; + account.save(); emit(state.copyWithPaged(isLoading: false)); } } @@ -36,7 +40,7 @@ mixin DocumentPagingBlocMixin /// Updates document filter and automatically reloads documents. Always resets page to 1. /// Use [loadMore] to load more data. Future updateFilter({ - final DocumentFilter filter = DocumentFilter.initial, + final DocumentFilter filter = const DocumentFilter(), }) async { try { emit(state.copyWithPaged(isLoading: true)); @@ -48,6 +52,8 @@ mixin DocumentPagingBlocMixin hasLoaded: true, )); } finally { + account.settings.currentDocumentFilter = filter; + account.save(); emit(state.copyWithPaged(isLoading: false)); } } @@ -65,13 +71,15 @@ mixin DocumentPagingBlocMixin sortField: state.filter.sortField, sortOrder: state.filter.sortOrder, ); + account.settings.currentDocumentFilter = filter; + account.save(); return updateFilter(filter: filter); } Future reload() async { emit(state.copyWithPaged(isLoading: true)); + final filter = state.filter.copyWith(page: 1); try { - final filter = state.filter.copyWith(page: 1); final result = await api.findAll(filter); if (!isClosed) { emit(state.copyWithPaged( @@ -82,6 +90,8 @@ mixin DocumentPagingBlocMixin )); } } finally { + account.settings.currentDocumentFilter = filter; + account.save(); if (!isClosed) { emit(state.copyWithPaged(isLoading: false)); } @@ -106,7 +116,6 @@ mixin DocumentPagingBlocMixin try { await api.delete(document); notifier.notifyDeleted(document); - // remove(document); // Removing deleted now works with the change notifier. } finally { emit(state.copyWithPaged(isLoading: false)); } diff --git a/lib/features/paged_document_view/cubit/paged_documents_state.dart b/lib/features/paged_document_view/cubit/paged_documents_state.dart index 298248e..d956cb3 100644 --- a/lib/features/paged_document_view/cubit/paged_documents_state.dart +++ b/lib/features/paged_document_view/cubit/paged_documents_state.dart @@ -1,4 +1,5 @@ import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; import 'package:paperless_api/paperless_api.dart'; /// diff --git a/lib/features/saved_view_details/cubit/saved_view_details_state.dart b/lib/features/saved_view_details/cubit/saved_view_details_state.dart index b61b7c3..00a160f 100644 --- a/lib/features/saved_view_details/cubit/saved_view_details_state.dart +++ b/lib/features/saved_view_details/cubit/saved_view_details_state.dart @@ -12,7 +12,7 @@ class SavedViewDetailsState extends DocumentPagingState { const SavedViewDetailsState({ this.viewType = ViewType.list, - super.filter, + super.filter = const DocumentFilter(), super.hasLoaded, super.isLoading, super.value, diff --git a/lib/features/settings/model/global_settings.dart b/lib/features/settings/model/global_settings.dart index ba0ee34..c0ea054 100644 --- a/lib/features/settings/model/global_settings.dart +++ b/lib/features/settings/model/global_settings.dart @@ -29,7 +29,4 @@ class GlobalSettings with HiveObjectMixin { this.showOnboarding = true, this.currentLoggedInUser, }); - - static GlobalSettings get boxedValue => - Hive.box(HiveBoxes.globalSettings).getValue()!; } diff --git a/lib/features/settings/model/user_settings.dart b/lib/features/settings/model/user_settings.dart index 3612eb0..cd4d965 100644 --- a/lib/features/settings/model/user_settings.dart +++ b/lib/features/settings/model/user_settings.dart @@ -1,4 +1,5 @@ import 'package:hive/hive.dart'; +import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart'; part 'user_settings.g.dart'; @@ -8,7 +9,11 @@ class UserSettings with HiveObjectMixin { @HiveField(0) bool isBiometricAuthenticationEnabled; + @HiveField(1) + DocumentFilter currentDocumentFilter; + UserSettings({ this.isBiometricAuthenticationEnabled = false, + required this.currentDocumentFilter, }); } diff --git a/lib/features/settings/view/manage_accounts_page.dart b/lib/features/settings/view/manage_accounts_page.dart index bd952d6..996b6f2 100644 --- a/lib/features/settings/view/manage_accounts_page.dart +++ b/lib/features/settings/view/manage_accounts_page.dart @@ -1,7 +1,9 @@ +import 'dart:ui'; + import 'package:collection/collection.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:hive_flutter/adapters.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; @@ -14,91 +16,71 @@ import 'package:paperless_mobile/features/settings/view/dialogs/switch_account_d import 'package:paperless_mobile/features/settings/view/pages/switching_accounts_page.dart'; import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; import 'package:paperless_mobile/features/settings/view/widgets/user_avatar.dart'; +import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; class ManageAccountsPage extends StatelessWidget { const ManageAccountsPage({super.key}); @override Widget build(BuildContext context) { - return Dialog.fullscreen( - child: Scaffold( - appBar: AppBar( - leading: const CloseButton(), - title: const Text("Accounts"), //TODO: INTL - ), - body: GlobalSettingsBuilder( - builder: (context, globalSettings) { - return ValueListenableBuilder( - valueListenable: Hive.box(HiveBoxes.userAccount).listenable(), - builder: (context, box, _) { - final userIds = box.keys.toList().cast(); - final otherAccounts = userIds - .whereNot((element) => element == globalSettings.currentLoggedInUser) - .toList(); - return CustomScrollView( - slivers: [ - SliverToBoxAdapter( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Your account", //TODO: INTL - style: Theme.of(context).textTheme.labelLarge, - ).padded(16), - _buildAccountTile( - context, - globalSettings.currentLoggedInUser!, - box.get(globalSettings.currentLoggedInUser!)!, - globalSettings, - ), - if (otherAccounts.isNotEmpty) const Divider(), - ], + return GlobalSettingsBuilder( + builder: (context, globalSettings) { + return ValueListenableBuilder( + valueListenable: + Hive.box(HiveBoxes.userAccount).listenable(), + builder: (context, box, _) { + final userIds = box.keys.toList().cast(); + final otherAccounts = userIds + .whereNot( + (element) => element == globalSettings.currentLoggedInUser) + .toList(); + return SimpleDialog( + insetPadding: EdgeInsets.all(24), + contentPadding: EdgeInsets.all(8), + title: Stack( + alignment: Alignment.center, + children: [ + Align( + alignment: Alignment.centerLeft, + child: CloseButton(), + ), + Center(child: Text("Accounts")), + ], + ), //TODO: INTL + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(24), + ), + children: [ + _buildAccountTile( + context, + globalSettings.currentLoggedInUser!, + box.get(globalSettings.currentLoggedInUser!)!, + globalSettings), + // if (otherAccounts.isNotEmpty) Text("Other accounts"), + Column( + children: [ + for (int index = 0; index < otherAccounts.length; index++) + _buildAccountTile( + context, + otherAccounts[index], + box.get(otherAccounts[index])!, + globalSettings, ), - ), - if (otherAccounts.isNotEmpty) - SliverToBoxAdapter( - child: Text( - "Other accounts", //TODO: INTL - style: Theme.of(context).textTheme.labelLarge, - ).padded(16), - ), - SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) => _buildAccountTile( - context, - otherAccounts[index], - box.get(otherAccounts[index])!, - globalSettings, - ), - childCount: otherAccounts.length, - ), - ), - SliverToBoxAdapter( - child: Column( - children: [ - const Divider(), - ListTile( - title: const Text("Add account"), - leading: const Icon(Icons.person_add), - onTap: () { - _onAddAccount(context); - }, - ), - // FilledButton.tonalIcon( - // icon: Icon(Icons.person_add), - // label: Text("Add account"), - // onPressed: () {}, - // ), - ], - ), - ), ], - ); - }, + ), + const Divider(), + ListTile( + title: const Text("Add account"), + leading: const Icon(Icons.person_add), + onTap: () { + _onAddAccount(context); + }, + ), + ], ); }, - ), - ), + ); + }, ); } @@ -108,69 +90,96 @@ class ManageAccountsPage extends StatelessWidget { UserAccount account, GlobalSettings settings, ) { + final isLoggedIn = userId == settings.currentLoggedInUser; final theme = Theme.of(context); - return ListTile( - title: Text(account.username), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (account.fullName != null) Text(account.fullName!), - Text(account.serverUrl), - ], - ), - isThreeLine: true, - leading: UserAvatar( - account: account, - userId: userId, - ), - trailing: PopupMenuButton( - icon: const Icon(Icons.more_vert), - itemBuilder: (context) { - return [ - if (settings.currentLoggedInUser != userId) - const PopupMenuItem( - child: ListTile( - title: Text("Switch"), //TODO: INTL - leading: Icon(Icons.switch_account_outlined), - ), - value: 0, + final child = SizedBox( + width: double.maxFinite, + child: ListTile( + title: Text(account.username), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (account.fullName != null) Text(account.fullName!), + Text( + account.serverUrl.replaceFirst(RegExp(r'https://?'), ''), + style: TextStyle( + color: Theme.of(context).colorScheme.primary, ), - const PopupMenuItem( - child: ListTile( - title: Text("Remove"), // TODO: INTL - leading: Icon( - Icons.remove_circle_outline, - color: Colors.red, - ), - ), - value: 1, ), - ]; - }, - onSelected: (value) async { - if (value == 0) { - // Switch - final navigator = Navigator.of(context); - if (settings.currentLoggedInUser == userId) return; - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) => const SwitchingAccountsPage(), - ), - ); - await context.read().switchAccount(userId); - navigator.popUntil((route) => route.isFirst); - } else if (value == 1) { - // Remove - final shouldPop = userId == settings.currentLoggedInUser; - await context.read().removeAccount(userId); - if (shouldPop) { - Navigator.pop(context); + ], + ), + isThreeLine: true, + leading: UserAvatar( + account: account, + userId: userId, + ), + trailing: PopupMenuButton( + icon: const Icon(Icons.more_vert), + itemBuilder: (context) { + return [ + if (!isLoggedIn) + const PopupMenuItem( + child: ListTile( + title: Text("Switch"), //TODO: INTL + leading: Icon(Icons.switch_account_rounded), + ), + value: 0, + ), + if (!isLoggedIn) + const PopupMenuItem( + child: ListTile( + title: Text("Remove"), // TODO: INTL + leading: Icon( + Icons.person_remove, + color: Colors.red, + ), + ), + value: 1, + ) + else + const PopupMenuItem( + child: ListTile( + title: Text("Logout"), // TODO: INTL + leading: Icon( + Icons.person_remove, + color: Colors.red, + ), + ), + value: 1, + ), + ]; + }, + onSelected: (value) async { + if (value == 0) { + // Switch + final navigator = Navigator.of(context); + if (settings.currentLoggedInUser == userId) return; + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => const SwitchingAccountsPage(), + ), + ); + await context.read().switchAccount(userId); + navigator.popUntil((route) => route.isFirst); + } else if (value == 1) { + // Remove + final shouldPop = userId == settings.currentLoggedInUser; + await context.read().removeAccount(userId); + if (shouldPop) { + Navigator.pop(context); + } } - } - }, + }, + ), ), ); + if (isLoggedIn) { + return Card( + child: child, + ); + } + return child; } Future _onAddAccount(BuildContext context) { @@ -179,7 +188,8 @@ 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, @@ -192,8 +202,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/pages/switching_accounts_page.dart b/lib/features/settings/view/pages/switching_accounts_page.dart index fd73578..76f7801 100644 --- a/lib/features/settings/view/pages/switching_accounts_page.dart +++ b/lib/features/settings/view/pages/switching_accounts_page.dart @@ -1,6 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; class SwitchingAccountsPage extends StatelessWidget { const SwitchingAccountsPage({super.key}); @@ -13,7 +11,14 @@ class SwitchingAccountsPage extends StatelessWidget { }, child: Material( child: Center( - child: Text("Switching accounts. Please wait..."), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CircularProgressIndicator(), + Text("Switching accounts. Please wait..."), + ], + ), ), ), ); diff --git a/lib/features/settings/view/widgets/biometric_authentication_setting.dart b/lib/features/settings/view/widgets/biometric_authentication_setting.dart index 1c36e06..329fd84 100644 --- a/lib/features/settings/view/widgets/biometric_authentication_setting.dart +++ b/lib/features/settings/view/widgets/biometric_authentication_setting.dart @@ -14,13 +14,13 @@ class BiometricAuthenticationSetting extends StatelessWidget { @override Widget build(BuildContext context) { - return UserSettingsBuilder( - builder: (context, settings) { - if (settings == null) { + return UserAccountBuilder( + builder: (context, account) { + if (account == null) { return const SizedBox.shrink(); } return SwitchListTile( - value: settings.isBiometricAuthenticationEnabled, + value: account.settings.isBiometricAuthenticationEnabled, title: Text(S.of(context)!.biometricAuthentication), subtitle: Text(S.of(context)!.authenticateOnAppStart), onChanged: (val) async { @@ -33,8 +33,8 @@ class BiometricAuthenticationSetting extends StatelessWidget { .read() .authenticateLocalUser(localizedReason); if (isAuthenticated) { - settings.isBiometricAuthenticationEnabled = val; - settings.save(); + account.settings.isBiometricAuthenticationEnabled = val; + account.save(); } }, ); diff --git a/lib/features/settings/view/widgets/user_settings_builder.dart b/lib/features/settings/view/widgets/user_settings_builder.dart index b1be602..2f7c287 100644 --- a/lib/features/settings/view/widgets/user_settings_builder.dart +++ b/lib/features/settings/view/widgets/user_settings_builder.dart @@ -1,30 +1,33 @@ 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'; -class UserSettingsBuilder extends StatelessWidget { +class UserAccountBuilder extends StatelessWidget { final Widget Function( BuildContext context, - UserSettings? settings, + UserAccount? settings, ) builder; - const UserSettingsBuilder({ + const UserAccountBuilder({ super.key, required this.builder, }); @override Widget build(BuildContext context) { - return ValueListenableBuilder>( - valueListenable: Hive.box(HiveBoxes.userSettings).listenable(), - builder: (context, value, _) { - final currentUser = - Hive.box(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser; + return ValueListenableBuilder>( + valueListenable: + Hive.box(HiveBoxes.userAccount).listenable(), + builder: (context, accountBox, _) { + final currentUser = Hive.box(HiveBoxes.globalSettings) + .getValue()! + .currentLoggedInUser; if (currentUser != null) { - final settings = value.get(currentUser); - return builder(context, settings); + final account = accountBox.get(currentUser); + return builder(context, account); } else { return builder(context, null); } diff --git a/lib/features/similar_documents/cubit/similar_documents_cubit.dart b/lib/features/similar_documents/cubit/similar_documents_cubit.dart index 0cbb0a4..3aea2c3 100644 --- a/lib/features/similar_documents/cubit/similar_documents_cubit.dart +++ b/lib/features/similar_documents/cubit/similar_documents_cubit.dart @@ -24,7 +24,7 @@ class SimilarDocumentsCubit extends Cubit this.notifier, this._labelRepository, { required this.documentId, - }) : super(const SimilarDocumentsState()) { + }) : super(SimilarDocumentsState(filter: DocumentFilter())) { notifier.addListener( this, onDeleted: remove, diff --git a/lib/features/similar_documents/cubit/similar_documents_state.dart b/lib/features/similar_documents/cubit/similar_documents_state.dart index 944c5d0..503dbb5 100644 --- a/lib/features/similar_documents/cubit/similar_documents_state.dart +++ b/lib/features/similar_documents/cubit/similar_documents_state.dart @@ -7,7 +7,7 @@ class SimilarDocumentsState extends DocumentPagingState { final Map storagePaths; const SimilarDocumentsState({ - super.filter, + required super.filter, super.hasLoaded, super.isLoading, super.value, diff --git a/lib/main.dart b/lib/main.dart index 8716f16..46fe95c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -54,7 +54,8 @@ 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,16 +64,18 @@ String get defaultPreferredLocaleSubtag { Future _initHive() async { await Hive.initFlutter(); //TODO: REMOVE! - // await getApplicationDocumentsDirectory().then((value) => value.delete(recursive: true)); + // await getApplicationDocumentsDirectory() + // .then((value) => value.delete(recursive: true)); registerHiveAdapters(); await Hive.openBox(HiveBoxes.userAccount); - await Hive.openBox(HiveBoxes.userSettings); - final globalSettingsBox = await Hive.openBox(HiveBoxes.globalSettings); + final globalSettingsBox = + await Hive.openBox(HiveBoxes.globalSettings); if (!globalSettingsBox.hasValue) { - await globalSettingsBox - .setValue(GlobalSettings(preferredLocaleSubtag: defaultPreferredLocaleSubtag)); + await globalSettingsBox.setValue( + GlobalSettings(preferredLocaleSubtag: defaultPreferredLocaleSubtag), + ); } } @@ -152,7 +155,8 @@ void main() async { //Update language header in interceptor on language change. globalSettingsBox.listenable().addListener(() { - languageHeaderInterceptor.preferredLocaleSubtag = globalSettings.preferredLocaleSubtag; + languageHeaderInterceptor.preferredLocaleSubtag = + globalSettings.preferredLocaleSubtag; }); runApp( @@ -176,7 +180,8 @@ void main() async { Provider.value( value: connectivityStatusService, ), - Provider.value(value: localNotificationService), + Provider.value( + value: localNotificationService), Provider.value(value: DocumentChangedNotifier()), ], child: MultiRepositoryProvider( @@ -206,7 +211,8 @@ class PaperlessMobileEntrypoint extends StatefulWidget { }) : super(key: key); @override - State createState() => _PaperlessMobileEntrypointState(); + State createState() => + _PaperlessMobileEntrypointState(); } class _PaperlessMobileEntrypointState extends State { @@ -241,7 +247,8 @@ class _PaperlessMobileEntrypointState extends State { GlobalWidgetsLocalizations.delegate, ], routes: { - DocumentDetailsRoute.routeName: (context) => const DocumentDetailsRoute(), + DocumentDetailsRoute.routeName: (context) => + const DocumentDetailsRoute(), }, home: const AuthenticationWrapper(), ); @@ -276,9 +283,11 @@ 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 { @@ -290,7 +299,8 @@ 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); @@ -341,12 +351,14 @@ 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 = GlobalSettings.boxedValue; + 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 new file mode 100644 index 0000000..546833b --- /dev/null +++ b/packages/paperless_api/lib/config/hive/hive_type_ids.dart @@ -0,0 +1,4 @@ +class PaperlessApiHiveTypeIds { + PaperlessApiHiveTypeIds._(); + static const int documentFilter = 1000; +} diff --git a/packages/paperless_api/lib/src/models/document_filter.dart b/packages/paperless_api/lib/src/models/document_filter.dart index 8ac6d99..807df70 100644 --- a/packages/paperless_api/lib/src/models/document_filter.dart +++ b/packages/paperless_api/lib/src/models/document_filter.dart @@ -1,6 +1,8 @@ import 'package:collection/collection.dart'; 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 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/src/converters/tags_query_json_converter.dart'; @@ -9,6 +11,7 @@ part 'document_filter.g.dart'; @TagsQueryJsonConverter() @DateRangeQueryJsonConverter() @JsonSerializable(explicitToJson: true) +@HiveType(typeId: PaperlessApiHiveTypeIds.documentFilter) class DocumentFilter extends Equatable { static const DocumentFilter initial = DocumentFilter(); @@ -19,21 +22,35 @@ class DocumentFilter extends Equatable { page: 1, ); + @HiveField(0) final int pageSize; + @HiveField(1) final int page; + @HiveField(2) final IdQueryParameter documentType; + @HiveField(3) final IdQueryParameter correspondent; + @HiveField(4) final IdQueryParameter storagePath; + @HiveField(5) final IdQueryParameter asnQuery; + @HiveField(6) final TagsQuery tags; + @HiveField(7) final SortField? sortField; + @HiveField(8) final SortOrder sortOrder; + @HiveField(9) final DateRangeQuery created; + @HiveField(10) final DateRangeQuery added; + @HiveField(11) final DateRangeQuery modified; + @HiveField(12) final TextQuery query; /// Query documents similar to the document with this id. + @HiveField(13) final int? moreLike; const DocumentFilter({ diff --git a/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart b/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart index e609e9a..b848362 100644 --- a/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart +++ b/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart @@ -152,10 +152,10 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi { @override Future findNextAsn() async { - const DocumentFilter asnQueryFilter = DocumentFilter( + final DocumentFilter asnQueryFilter = DocumentFilter( sortField: SortField.archiveSerialNumber, sortOrder: SortOrder.descending, - asnQuery: IdQueryParameter.anyAssigned(), + asnQuery: const IdQueryParameter.anyAssigned(), page: 1, pageSize: 1, ); diff --git a/packages/paperless_api/pubspec.yaml b/packages/paperless_api/pubspec.yaml index d0fc02d..d1ba2c6 100644 --- a/packages/paperless_api/pubspec.yaml +++ b/packages/paperless_api/pubspec.yaml @@ -21,6 +21,7 @@ dependencies: collection: ^1.17.0 jiffy: ^5.0.0 freezed_annotation: ^2.2.0 + hive: ^2.2.3 dev_dependencies: flutter_test: @@ -29,6 +30,7 @@ dev_dependencies: json_serializable: ^6.5.4 build_runner: ^2.3.2 freezed: ^2.3.2 + hive_generator: ^2.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/packages/paperless_api/test/saved_view_test.dart b/packages/paperless_api/test/saved_view_test.dart index 8bc10cd..37e842b 100644 --- a/packages/paperless_api/test/saved_view_test.dart +++ b/packages/paperless_api/test/saved_view_test.dart @@ -210,16 +210,16 @@ void main() { test('Values are correctly parsed if unset.', () { expect( SavedView.fromDocumentFilter( - const DocumentFilter( - correspondent: IdQueryParameter.unset(), - documentType: IdQueryParameter.unset(), - storagePath: IdQueryParameter.unset(), - tags: IdsTagsQuery(), + DocumentFilter( + correspondent: const IdQueryParameter.unset(), + documentType: const IdQueryParameter.unset(), + storagePath: const IdQueryParameter.unset(), + tags: const IdsTagsQuery(), sortField: SortField.created, sortOrder: SortOrder.descending, - added: UnsetDateRangeQuery(), - created: UnsetDateRangeQuery(), - query: TextQuery(), + added: const UnsetDateRangeQuery(), + created: const UnsetDateRangeQuery(), + query: const TextQuery(), ), name: "test_name", showInSidebar: false, @@ -241,11 +241,11 @@ void main() { test('Values are correctly parsed if not assigned.', () { expect( SavedView.fromDocumentFilter( - const DocumentFilter( - correspondent: IdQueryParameter.notAssigned(), - documentType: IdQueryParameter.notAssigned(), - storagePath: IdQueryParameter.notAssigned(), - tags: OnlyNotAssignedTagsQuery(), + DocumentFilter( + correspondent: const IdQueryParameter.notAssigned(), + documentType: const IdQueryParameter.notAssigned(), + storagePath: const IdQueryParameter.notAssigned(), + tags: const OnlyNotAssignedTagsQuery(), sortField: SortField.created, sortOrder: SortOrder.ascending, ),