mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-08 08:07:56 -06:00
feat: Add more user related state to hive
This commit is contained in:
@@ -70,7 +70,7 @@ android {
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
signingConfig signingConfigs.release
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<T> on Box<T> {
|
||||
|
||||
@@ -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<DocumentSearchState>
|
||||
@@ -20,7 +21,6 @@ class DocumentSearchCubit extends HydratedCubit<DocumentSearchState>
|
||||
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<DocumentSearchState>
|
||||
Map<String, dynamic>? toJson(DocumentSearchState state) {
|
||||
return state.toJson();
|
||||
}
|
||||
|
||||
@override
|
||||
// TODO: implement account
|
||||
UserAccount get account => throw UnimplementedError();
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<UserAccount>(HiveBoxes.userAccount).listenable(),
|
||||
valueListenable:
|
||||
Hive.box<UserAccount>(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);
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@@ -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<DocumentsState>
|
||||
@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) {
|
||||
|
||||
@@ -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';
|
||||
@@ -230,7 +233,8 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
||||
}
|
||||
return icon;
|
||||
},
|
||||
)),
|
||||
),
|
||||
),
|
||||
];
|
||||
final routes = <Widget>[
|
||||
MultiBlocProvider(
|
||||
@@ -241,6 +245,7 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
||||
context.read(),
|
||||
context.read(),
|
||||
context.read(),
|
||||
Hive.box<UserAccount>(HiveBoxes.userAccount).get(userId)!,
|
||||
)..reload(),
|
||||
),
|
||||
BlocProvider(
|
||||
@@ -275,7 +280,8 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
||||
listeners: [
|
||||
BlocListener<ConnectivityCubit, ConnectivityState>(
|
||||
//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<HomePage> with WidgetsBindingObserver {
|
||||
listener: (context, state) {
|
||||
if (state.task != null) {
|
||||
// Handle local notifications on task change (only when app is running for now).
|
||||
context.read<LocalNotificationService>().notifyTaskChanged(state.task!);
|
||||
context
|
||||
.read<LocalNotificationService>()
|
||||
.notifyTaskChanged(state.task!);
|
||||
}
|
||||
},
|
||||
),
|
||||
@@ -297,7 +305,9 @@ class _HomePageState extends State<HomePage> 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<HomePage> 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],
|
||||
);
|
||||
|
||||
@@ -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<AuthenticationCubit>().restoreSessionState(),
|
||||
onPressed: () => context
|
||||
.read<AuthenticationCubit>()
|
||||
.restoreSessionState(),
|
||||
child: Text(S.of(context)!.verifyIdentity),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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<InboxState> with DocumentPagingBlocMixin {
|
||||
class InboxCubit extends HydratedCubit<InboxState>
|
||||
with DocumentPagingBlocMixin {
|
||||
final LabelRepository _labelRepository;
|
||||
|
||||
final PaperlessDocumentsApi _documentsApi;
|
||||
@@ -31,12 +32,17 @@ class InboxCubit extends HydratedCubit<InboxState> 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<InboxState> with DocumentPagingBlocMixin
|
||||
/// from the inbox.
|
||||
///
|
||||
Future<Iterable<int>> 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<InboxState> with DocumentPagingBlocMixin
|
||||
Future<void> 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);
|
||||
}
|
||||
|
||||
@@ -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<T extends Label> extends StatelessWidget {
|
||||
@@ -42,6 +46,10 @@ class LabelItem<T extends Label> extends StatelessWidget {
|
||||
onPressed: (label.documentCount ?? 0) == 0
|
||||
? null
|
||||
: () {
|
||||
final currentUser =
|
||||
Hive.box<GlobalSettings>(HiveBoxes.globalSettings)
|
||||
.getValue()!
|
||||
.currentLoggedInUser!;
|
||||
final filter = filterBuilder(label);
|
||||
Navigator.push(
|
||||
context,
|
||||
@@ -52,6 +60,8 @@ class LabelItem<T extends Label> extends StatelessWidget {
|
||||
context.read(),
|
||||
context.read(),
|
||||
context.read(),
|
||||
Hive.box<UserAccount>(HiveBoxes.userAccount)
|
||||
.get(currentUser)!,
|
||||
),
|
||||
child: const LinkedDocumentsPage(),
|
||||
),
|
||||
|
||||
@@ -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<LinkedDocumentsState>
|
||||
|
||||
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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -62,17 +62,17 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
|
||||
final userId = "${credentials.username}@$serverUrl";
|
||||
|
||||
// If it is first time login, create settings for this user.
|
||||
final userSettingsBox = Hive.box<UserSettings>(HiveBoxes.userSettings);
|
||||
final userAccountBox = Hive.box<UserAccount>(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<AuthenticationState> {
|
||||
}
|
||||
|
||||
// Mark logged in user as currently active user.
|
||||
final globalSettings = GlobalSettings.boxedValue;
|
||||
final globalSettings =
|
||||
Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!;
|
||||
globalSettings.currentLoggedInUser = userId;
|
||||
globalSettings.save();
|
||||
|
||||
@@ -108,26 +109,25 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
|
||||
|
||||
/// Switches to another account if it exists.
|
||||
Future<void> switchAccount(String userId) async {
|
||||
final globalSettings = GlobalSettings.boxedValue;
|
||||
final globalSettings =
|
||||
Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!;
|
||||
if (globalSettings.currentLoggedInUser == userId) {
|
||||
return;
|
||||
}
|
||||
final userAccountBox = Hive.box<UserAccount>(HiveBoxes.userAccount);
|
||||
final userSettingsBox = Hive.box<UserSettings>(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<AuthenticationState> {
|
||||
final userId = "${credentials.username}@$serverUrl";
|
||||
|
||||
final userAccountsBox = Hive.box<UserAccount>(HiveBoxes.userAccount);
|
||||
final userSettingsBox = Hive.box<UserSettings>(HiveBoxes.userSettings);
|
||||
|
||||
if (userAccountsBox.containsKey(userId)) {
|
||||
throw Exception("User already exists");
|
||||
@@ -192,18 +191,19 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
|
||||
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<AuthenticationState> {
|
||||
}
|
||||
|
||||
Future<void> removeAccount(String userId) async {
|
||||
final globalSettings = GlobalSettings.boxedValue;
|
||||
final globalSettings =
|
||||
Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!;
|
||||
final currentUser = globalSettings.currentLoggedInUser;
|
||||
final userAccountBox = Hive.box<UserAccount>(HiveBoxes.userAccount);
|
||||
final userCredentialsBox = await _getUserCredentialsBox();
|
||||
final userSettingsBox = Hive.box<UserSettings>(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<AuthenticationState> {
|
||||
/// Performs a conditional hydration based on the local authentication success.
|
||||
///
|
||||
Future<void> restoreSessionState() async {
|
||||
final globalSettings = GlobalSettings.boxedValue;
|
||||
final globalSettings =
|
||||
Hive.box<GlobalSettings>(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<UserSettings>(HiveBoxes.userSettings).get(userId)!;
|
||||
final userAccount = Hive.box<UserAccount>(HiveBoxes.userAccount).get(userId)!;
|
||||
final userAccount =
|
||||
Hive.box<UserAccount>(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<AuthenticationState> {
|
||||
),
|
||||
);
|
||||
} 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<void> logout() async {
|
||||
await _resetExternalState();
|
||||
final globalSettings = GlobalSettings.boxedValue;
|
||||
final globalSettings =
|
||||
Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!;
|
||||
globalSettings
|
||||
..currentLoggedInUser = null
|
||||
..save();
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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<State extends DocumentPagingState>
|
||||
on BlocBase<State> {
|
||||
PaperlessDocumentsApi get api;
|
||||
DocumentChangedNotifier get notifier;
|
||||
UserAccount get account;
|
||||
|
||||
Future<void> loadMore() async {
|
||||
if (state.isLastPageLoaded) {
|
||||
@@ -28,6 +30,8 @@ mixin DocumentPagingBlocMixin<State extends DocumentPagingState>
|
||||
value: [...state.value, result],
|
||||
));
|
||||
} finally {
|
||||
account.settings.currentDocumentFilter = newFilter;
|
||||
account.save();
|
||||
emit(state.copyWithPaged(isLoading: false));
|
||||
}
|
||||
}
|
||||
@@ -36,7 +40,7 @@ mixin DocumentPagingBlocMixin<State extends DocumentPagingState>
|
||||
/// Updates document filter and automatically reloads documents. Always resets page to 1.
|
||||
/// Use [loadMore] to load more data.
|
||||
Future<void> updateFilter({
|
||||
final DocumentFilter filter = DocumentFilter.initial,
|
||||
final DocumentFilter filter = const DocumentFilter(),
|
||||
}) async {
|
||||
try {
|
||||
emit(state.copyWithPaged(isLoading: true));
|
||||
@@ -48,6 +52,8 @@ mixin DocumentPagingBlocMixin<State extends DocumentPagingState>
|
||||
hasLoaded: true,
|
||||
));
|
||||
} finally {
|
||||
account.settings.currentDocumentFilter = filter;
|
||||
account.save();
|
||||
emit(state.copyWithPaged(isLoading: false));
|
||||
}
|
||||
}
|
||||
@@ -65,13 +71,15 @@ mixin DocumentPagingBlocMixin<State extends DocumentPagingState>
|
||||
sortField: state.filter.sortField,
|
||||
sortOrder: state.filter.sortOrder,
|
||||
);
|
||||
account.settings.currentDocumentFilter = filter;
|
||||
account.save();
|
||||
return updateFilter(filter: filter);
|
||||
}
|
||||
|
||||
Future<void> reload() async {
|
||||
emit(state.copyWithPaged(isLoading: true));
|
||||
try {
|
||||
final filter = state.filter.copyWith(page: 1);
|
||||
try {
|
||||
final result = await api.findAll(filter);
|
||||
if (!isClosed) {
|
||||
emit(state.copyWithPaged(
|
||||
@@ -82,6 +90,8 @@ mixin DocumentPagingBlocMixin<State extends DocumentPagingState>
|
||||
));
|
||||
}
|
||||
} finally {
|
||||
account.settings.currentDocumentFilter = filter;
|
||||
account.save();
|
||||
if (!isClosed) {
|
||||
emit(state.copyWithPaged(isLoading: false));
|
||||
}
|
||||
@@ -106,7 +116,6 @@ mixin DocumentPagingBlocMixin<State extends DocumentPagingState>
|
||||
try {
|
||||
await api.delete(document);
|
||||
notifier.notifyDeleted(document);
|
||||
// remove(document); // Removing deleted now works with the change notifier.
|
||||
} finally {
|
||||
emit(state.copyWithPaged(isLoading: false));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:paperless_api/paperless_api.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,
|
||||
|
||||
@@ -29,7 +29,4 @@ class GlobalSettings with HiveObjectMixin {
|
||||
this.showOnboarding = true,
|
||||
this.currentLoggedInUser,
|
||||
});
|
||||
|
||||
static GlobalSettings get boxedValue =>
|
||||
Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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,68 +16,58 @@ 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(
|
||||
return GlobalSettingsBuilder(
|
||||
builder: (context, globalSettings) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: Hive.box<UserAccount>(HiveBoxes.userAccount).listenable(),
|
||||
valueListenable:
|
||||
Hive.box<UserAccount>(HiveBoxes.userAccount).listenable(),
|
||||
builder: (context, box, _) {
|
||||
final userIds = box.keys.toList().cast<String>();
|
||||
final otherAccounts = userIds
|
||||
.whereNot((element) => element == globalSettings.currentLoggedInUser)
|
||||
.whereNot(
|
||||
(element) => element == globalSettings.currentLoggedInUser)
|
||||
.toList();
|
||||
return CustomScrollView(
|
||||
slivers: [
|
||||
SliverToBoxAdapter(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
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: [
|
||||
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(),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (otherAccounts.isNotEmpty)
|
||||
SliverToBoxAdapter(
|
||||
child: Text(
|
||||
"Other accounts", //TODO: INTL
|
||||
style: Theme.of(context).textTheme.labelLarge,
|
||||
).padded(16),
|
||||
),
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) => _buildAccountTile(
|
||||
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,
|
||||
),
|
||||
childCount: otherAccounts.length,
|
||||
],
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Column(
|
||||
children: [
|
||||
const Divider(),
|
||||
ListTile(
|
||||
title: const Text("Add account"),
|
||||
@@ -84,21 +76,11 @@ class ManageAccountsPage extends StatelessWidget {
|
||||
_onAddAccount(context);
|
||||
},
|
||||
),
|
||||
// FilledButton.tonalIcon(
|
||||
// icon: Icon(Icons.person_add),
|
||||
// label: Text("Add account"),
|
||||
// onPressed: () {},
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -108,14 +90,22 @@ class ManageAccountsPage extends StatelessWidget {
|
||||
UserAccount account,
|
||||
GlobalSettings settings,
|
||||
) {
|
||||
final isLoggedIn = userId == settings.currentLoggedInUser;
|
||||
final theme = Theme.of(context);
|
||||
return ListTile(
|
||||
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),
|
||||
Text(
|
||||
account.serverUrl.replaceFirst(RegExp(r'https://?'), ''),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
isThreeLine: true,
|
||||
@@ -127,19 +117,31 @@ class ManageAccountsPage extends StatelessWidget {
|
||||
icon: const Icon(Icons.more_vert),
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
if (settings.currentLoggedInUser != userId)
|
||||
if (!isLoggedIn)
|
||||
const PopupMenuItem(
|
||||
child: ListTile(
|
||||
title: Text("Switch"), //TODO: INTL
|
||||
leading: Icon(Icons.switch_account_outlined),
|
||||
leading: Icon(Icons.switch_account_rounded),
|
||||
),
|
||||
value: 0,
|
||||
),
|
||||
if (!isLoggedIn)
|
||||
const PopupMenuItem(
|
||||
child: ListTile(
|
||||
title: Text("Remove"), // TODO: INTL
|
||||
leading: Icon(
|
||||
Icons.remove_circle_outline,
|
||||
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,
|
||||
),
|
||||
),
|
||||
@@ -170,7 +172,14 @@ class ManageAccountsPage extends StatelessWidget {
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
if (isLoggedIn) {
|
||||
return Card(
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
Future<void> _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<AuthenticationCubit>().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) {
|
||||
|
||||
@@ -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..."),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -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<LocalAuthenticationService>()
|
||||
.authenticateLocalUser(localizedReason);
|
||||
if (isAuthenticated) {
|
||||
settings.isBiometricAuthenticationEnabled = val;
|
||||
settings.save();
|
||||
account.settings.isBiometricAuthenticationEnabled = val;
|
||||
account.save();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -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<Box<UserSettings>>(
|
||||
valueListenable: Hive.box<UserSettings>(HiveBoxes.userSettings).listenable(),
|
||||
builder: (context, value, _) {
|
||||
final currentUser =
|
||||
Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser;
|
||||
return ValueListenableBuilder<Box<UserAccount>>(
|
||||
valueListenable:
|
||||
Hive.box<UserAccount>(HiveBoxes.userAccount).listenable(),
|
||||
builder: (context, accountBox, _) {
|
||||
final currentUser = Hive.box<GlobalSettings>(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);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ class SimilarDocumentsCubit extends Cubit<SimilarDocumentsState>
|
||||
this.notifier,
|
||||
this._labelRepository, {
|
||||
required this.documentId,
|
||||
}) : super(const SimilarDocumentsState()) {
|
||||
}) : super(SimilarDocumentsState(filter: DocumentFilter())) {
|
||||
notifier.addListener(
|
||||
this,
|
||||
onDeleted: remove,
|
||||
|
||||
@@ -7,7 +7,7 @@ class SimilarDocumentsState extends DocumentPagingState {
|
||||
final Map<int, StoragePath> storagePaths;
|
||||
|
||||
const SimilarDocumentsState({
|
||||
super.filter,
|
||||
required super.filter,
|
||||
super.hasLoaded,
|
||||
super.isLoading,
|
||||
super.value,
|
||||
|
||||
@@ -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<void> _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<UserAccount>(HiveBoxes.userAccount);
|
||||
await Hive.openBox<UserSettings>(HiveBoxes.userSettings);
|
||||
final globalSettingsBox = await Hive.openBox<GlobalSettings>(HiveBoxes.globalSettings);
|
||||
final globalSettingsBox =
|
||||
await Hive.openBox<GlobalSettings>(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<ConnectivityStatusService>.value(
|
||||
value: connectivityStatusService,
|
||||
),
|
||||
Provider<LocalNotificationService>.value(value: localNotificationService),
|
||||
Provider<LocalNotificationService>.value(
|
||||
value: localNotificationService),
|
||||
Provider.value(value: DocumentChangedNotifier()),
|
||||
],
|
||||
child: MultiRepositoryProvider(
|
||||
@@ -206,7 +211,8 @@ class PaperlessMobileEntrypoint extends StatefulWidget {
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<PaperlessMobileEntrypoint> createState() => _PaperlessMobileEntrypointState();
|
||||
State<PaperlessMobileEntrypoint> createState() =>
|
||||
_PaperlessMobileEntrypointState();
|
||||
}
|
||||
|
||||
class _PaperlessMobileEntrypointState extends State<PaperlessMobileEntrypoint> {
|
||||
@@ -241,7 +247,8 @@ class _PaperlessMobileEntrypointState extends State<PaperlessMobileEntrypoint> {
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
],
|
||||
routes: {
|
||||
DocumentDetailsRoute.routeName: (context) => const DocumentDetailsRoute(),
|
||||
DocumentDetailsRoute.routeName: (context) =>
|
||||
const DocumentDetailsRoute(),
|
||||
},
|
||||
home: const AuthenticationWrapper(),
|
||||
);
|
||||
@@ -276,9 +283,11 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
|
||||
}
|
||||
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<void> _setOptimalDisplayMode() async {
|
||||
@@ -290,7 +299,8 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
|
||||
.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<AuthenticationWrapper> {
|
||||
) async {
|
||||
try {
|
||||
await context.read<AuthenticationCubit>().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<GlobalSettings>(HiveBoxes.globalSettings).getValue()!;
|
||||
if (globalSettings.showOnboarding) {
|
||||
Navigator.push(
|
||||
context,
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
class PaperlessApiHiveTypeIds {
|
||||
PaperlessApiHiveTypeIds._();
|
||||
static const int documentFilter = 1000;
|
||||
}
|
||||
@@ -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({
|
||||
|
||||
@@ -152,10 +152,10 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
|
||||
|
||||
@override
|
||||
Future<int> 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,
|
||||
);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user