feat: Externalize settings into LocalUserAppState, fix bugs

This commit is contained in:
Anton Stubenbord
2023-04-26 01:29:14 +02:00
parent 8c2a6928b4
commit 37c9559888
41 changed files with 340 additions and 316 deletions

View File

@@ -1,19 +0,0 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/bloc/paperless_server_information_state.dart';
class PaperlessServerInformationCubit
extends Cubit<PaperlessServerInformationState> {
final PaperlessServerStatsApi _api;
PaperlessServerInformationCubit(this._api)
: super(PaperlessServerInformationState());
Future<void> updateInformtion() async {
final information = await _api.getServerInformation();
emit(PaperlessServerInformationState(
isLoaded: true,
information: information,
));
}
}

View File

@@ -0,0 +1,17 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/bloc/server_information_state.dart';
class ServerInformationCubit extends Cubit<ServerInformationState> {
final PaperlessServerStatsApi _api;
ServerInformationCubit(this._api) : super(ServerInformationState());
Future<void> updateInformation() async {
final information = await _api.getServerInformation();
emit(ServerInformationState(
isLoaded: true,
information: information,
));
}
}

View File

@@ -1,10 +1,10 @@
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
class PaperlessServerInformationState { class ServerInformationState {
final bool isLoaded; final bool isLoaded;
final PaperlessServerInformationModel? information; final PaperlessServerInformationModel? information;
PaperlessServerInformationState({ ServerInformationState({
this.isLoaded = false, this.isLoaded = false,
this.information, this.information,
}); });

View File

@@ -2,35 +2,37 @@ import 'package:hive_flutter/adapters.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/config/hive/custom_adapters/theme_mode_adapter.dart'; import 'package:paperless_mobile/core/config/hive/custom_adapters/theme_mode_adapter.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/core/database/tables/user_app_state.dart'; import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart';
import 'package:paperless_mobile/core/database/tables/user_credentials.dart'; import 'package:paperless_mobile/core/database/tables/user_credentials.dart';
import 'package:paperless_mobile/features/login/model/authentication_information.dart'; import 'package:paperless_mobile/features/login/model/authentication_information.dart';
import 'package:paperless_mobile/features/login/model/client_certificate.dart'; import 'package:paperless_mobile/features/login/model/client_certificate.dart';
import 'package:paperless_mobile/core/database/tables/user_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/features/settings/model/color_scheme_option.dart'; import 'package:paperless_mobile/features/settings/model/color_scheme_option.dart';
import 'package:paperless_mobile/core/database/tables/user_settings.dart'; import 'package:paperless_mobile/core/database/tables/local_user_settings.dart';
import 'package:paperless_mobile/features/settings/model/view_type.dart';
class HiveBoxes { class HiveBoxes {
HiveBoxes._(); HiveBoxes._();
static const globalSettings = 'globalSettings'; static const globalSettings = 'globalSettings';
static const authentication = 'authentication'; static const authentication = 'authentication';
static const userCredentials = 'userCredentials'; static const localUserCredentials = 'localUserCredentials';
static const userAccount = 'userAccount'; static const localUserAccount = 'localUserAccount';
static const userAppState = 'userAppState'; static const localUserAppState = 'localUserAppState';
static const userSettings = 'userSettings'; static const localUserSettings = 'localUserSettings';
} }
class HiveTypeIds { class HiveTypeIds {
HiveTypeIds._(); HiveTypeIds._();
static const globalSettings = 0; static const globalSettings = 0;
static const userSettings = 1; static const localUserSettings = 1;
static const themeMode = 2; static const themeMode = 2;
static const colorSchemeOption = 3; static const colorSchemeOption = 3;
static const authentication = 4; static const authentication = 4;
static const clientCertificate = 5; static const clientCertificate = 5;
static const userCredentials = 6; static const localUserCredentials = 6;
static const userAccount = 7; static const localUserAccount = 7;
static const userAppState = 8; static const localUserAppState = 8;
static const viewType = 9;
} }
void registerHiveAdapters() { void registerHiveAdapters() {
@@ -40,10 +42,11 @@ void registerHiveAdapters() {
Hive.registerAdapter(GlobalSettingsAdapter()); Hive.registerAdapter(GlobalSettingsAdapter());
Hive.registerAdapter(AuthenticationInformationAdapter()); Hive.registerAdapter(AuthenticationInformationAdapter());
Hive.registerAdapter(ClientCertificateAdapter()); Hive.registerAdapter(ClientCertificateAdapter());
Hive.registerAdapter(UserSettingsAdapter()); Hive.registerAdapter(LocalUserSettingsAdapter());
Hive.registerAdapter(UserCredentialsAdapter()); Hive.registerAdapter(UserCredentialsAdapter());
Hive.registerAdapter(UserAccountAdapter()); Hive.registerAdapter(LocalUserAccountAdapter());
Hive.registerAdapter(UserAppStateAdapter()); Hive.registerAdapter(LocalUserAppStateAdapter());
Hive.registerAdapter(ViewTypeAdapter());
} }
extension HiveSingleValueBox<T> on Box<T> { extension HiveSingleValueBox<T> on Box<T> {

View File

@@ -1,11 +1,11 @@
import 'package:hive_flutter/adapters.dart'; import 'package:hive_flutter/adapters.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/user_settings.dart'; import 'package:paperless_mobile/core/database/tables/local_user_settings.dart';
part 'user_account.g.dart'; part 'local_user_account.g.dart';
@HiveType(typeId: HiveTypeIds.userAccount) @HiveType(typeId: HiveTypeIds.localUserAccount)
class UserAccount extends HiveObject { class LocalUserAccount extends HiveObject {
@HiveField(0) @HiveField(0)
final String serverUrl; final String serverUrl;
@@ -19,9 +19,9 @@ class UserAccount extends HiveObject {
final String id; final String id;
@HiveField(4) @HiveField(4)
UserSettings settings; LocalUserSettings settings;
UserAccount({ LocalUserAccount({
required this.id, required this.id,
required this.serverUrl, required this.serverUrl,
required this.username, required this.username,

View File

@@ -0,0 +1,40 @@
import 'package:hive_flutter/adapters.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/features/settings/model/view_type.dart';
part 'local_user_app_state.g.dart';
///
/// Object used for the persistence of app state, e.g. set filters,
/// search history and implicit settings.
///
@HiveType(typeId: HiveTypeIds.localUserAppState)
class LocalUserAppState extends HiveObject {
@HiveField(0)
final String userId;
@HiveField(1)
DocumentFilter currentDocumentFilter;
@HiveField(2)
List<String> documentSearchHistory;
@HiveField(3)
ViewType documentsPageViewType;
@HiveField(4)
ViewType savedViewsViewType;
@HiveField(5)
ViewType documentSearchViewType;
LocalUserAppState({
required this.userId,
this.currentDocumentFilter = const DocumentFilter(),
this.documentSearchHistory = const [],
this.documentsPageViewType = ViewType.list,
this.documentSearchViewType = ViewType.list,
this.savedViewsViewType = ViewType.list,
});
}

View File

@@ -1,15 +1,14 @@
import 'package:hive/hive.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/core/config/hive/hive_config.dart';
part 'user_settings.g.dart'; part 'local_user_settings.g.dart';
@HiveType(typeId: HiveTypeIds.userSettings) @HiveType(typeId: HiveTypeIds.localUserSettings)
class UserSettings with HiveObjectMixin { class LocalUserSettings with HiveObjectMixin {
@HiveField(0) @HiveField(0)
bool isBiometricAuthenticationEnabled; bool isBiometricAuthenticationEnabled;
UserSettings({ LocalUserSettings({
this.isBiometricAuthenticationEnabled = false, this.isBiometricAuthenticationEnabled = false,
}); });
} }

View File

@@ -1,22 +0,0 @@
import 'package:hive_flutter/adapters.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
part 'user_app_state.g.dart';
@HiveType(typeId: HiveTypeIds.userAppState)
class UserAppState extends HiveObject {
@HiveField(0)
final String userId;
@HiveField(1)
DocumentFilter currentDocumentFilter;
@HiveField(2)
List<String> documentSearchHistory;
UserAppState({
required this.userId,
this.currentDocumentFilter = const DocumentFilter(),
this.documentSearchHistory = const [],
});
}

View File

@@ -4,7 +4,7 @@ import 'package:paperless_mobile/features/login/model/client_certificate.dart';
part 'user_credentials.g.dart'; part 'user_credentials.g.dart';
@HiveType(typeId: HiveTypeIds.userCredentials) @HiveType(typeId: HiveTypeIds.localUserCredentials)
class UserCredentials extends HiveObject { class UserCredentials extends HiveObject {
@HiveField(0) @HiveField(0)
final String token; final String token;

View File

@@ -1,4 +1,3 @@
import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
@@ -8,14 +7,18 @@ import 'package:paperless_mobile/core/interceptor/retry_on_connection_change_int
import 'package:paperless_mobile/features/login/model/client_certificate.dart'; import 'package:paperless_mobile/features/login/model/client_certificate.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart'; import 'package:pretty_dio_logger/pretty_dio_logger.dart';
/// Manages the security context, authentication and base request URL for
/// an underlying [Dio] client which is injected into all services
/// requiring authenticated access to the Paperless HTTP API.
class SessionManager { class SessionManager {
final Dio client; final Dio _client;
final List<Interceptor> interceptors; PaperlessServerInformationModel _serverInformation;
PaperlessServerInformationModel serverInformation;
SessionManager([this.interceptors = const []]) Dio get client => _client;
: client = _initDio(interceptors),
serverInformation = PaperlessServerInformationModel(); SessionManager([List<Interceptor> interceptors = const []])
: _client = _initDio(interceptors),
_serverInformation = PaperlessServerInformationModel();
static Dio _initDio(List<Interceptor> interceptors) { static Dio _initDio(List<Interceptor> interceptors) {
//en- and decoded by utf8 by default //en- and decoded by utf8 by default
@@ -63,8 +66,7 @@ class SessionManager {
); );
final adapter = IOHttpClientAdapter() final adapter = IOHttpClientAdapter()
..onHttpClientCreate = (client) => HttpClient(context: context) ..onHttpClientCreate = (client) => HttpClient(context: context)
..badCertificateCallback = ..badCertificateCallback = (X509Certificate cert, String host, int port) => true;
(X509Certificate cert, String host, int port) => true;
client.httpClientAdapter = adapter; client.httpClientAdapter = adapter;
} }
@@ -80,7 +82,7 @@ class SessionManager {
} }
if (serverInformation != null) { if (serverInformation != null) {
this.serverInformation = serverInformation; _serverInformation = serverInformation;
} }
} }
@@ -88,6 +90,6 @@ class SessionManager {
client.httpClientAdapter = IOHttpClientAdapter(); client.httpClientAdapter = IOHttpClientAdapter();
client.options.baseUrl = ''; client.options.baseUrl = '';
client.options.headers.remove(HttpHeaders.authorizationHeader); client.options.headers.remove(HttpHeaders.authorizationHeader);
serverInformation = PaperlessServerInformationModel(); _serverInformation = PaperlessServerInformationModel();
} }
} }

View File

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:paperless_mobile/constants.dart'; import 'package:paperless_mobile/constants.dart';
import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart'; import 'package:paperless_mobile/core/bloc/server_information_cubit.dart';
import 'package:paperless_mobile/core/widgets/paperless_logo.dart'; import 'package:paperless_mobile/core/widgets/paperless_logo.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/settings/view/settings_page.dart'; import 'package:paperless_mobile/features/settings/view/settings_page.dart';
@@ -69,7 +69,7 @@ class AppDrawer extends StatelessWidget {
onTap: () => Navigator.of(context).push( onTap: () => Navigator.of(context).push(
MaterialPageRoute( MaterialPageRoute(
builder: (_) => BlocProvider.value( builder: (_) => BlocProvider.value(
value: context.read<PaperlessServerInformationCubit>(), value: context.read<ServerInformationCubit>(),
child: const SettingsPage(), child: const SettingsPage(),
), ),
), ),

View File

@@ -55,7 +55,9 @@ class _BulkEditLabelBottomSheetState<T extends Label> extends State<BulkEditLabe
FormBuilder( FormBuilder(
key: _formKey, key: _formKey,
child: LabelFormField<T>( child: LabelFormField<T>(
initialValue: IdQueryParameter.fromId(widget.initialValue), initialValue: widget.initialValue != null
? IdQueryParameter.fromId(widget.initialValue!)
: const IdQueryParameter.unset(),
name: "labelFormField", name: "labelFormField",
options: widget.availableOptionsSelector(state), options: widget.availableOptionsSelector(state),
labelText: widget.formFieldLabel, labelText: widget.formFieldLabel,

View File

@@ -111,9 +111,9 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
addLabelText: S.of(context)!.addCorrespondent, addLabelText: S.of(context)!.addCorrespondent,
labelText: S.of(context)!.correspondent, labelText: S.of(context)!.correspondent,
options: context.watch<DocumentEditCubit>().state.correspondents, options: context.watch<DocumentEditCubit>().state.correspondents,
initialValue: IdQueryParameter.fromId( initialValue: state.document.correspondent != null
state.document.correspondent, ? IdQueryParameter.fromId(state.document.correspondent!)
), : const IdQueryParameter.unset(),
name: fkCorrespondent, name: fkCorrespondent,
prefixIcon: const Icon(Icons.person_outlined), prefixIcon: const Icon(Icons.person_outlined),
), ),
@@ -145,7 +145,9 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
), ),
addLabelText: S.of(context)!.addDocumentType, addLabelText: S.of(context)!.addDocumentType,
labelText: S.of(context)!.documentType, labelText: S.of(context)!.documentType,
initialValue: IdQueryParameter.fromId(state.document.documentType), initialValue: state.document.documentType != null
? IdQueryParameter.fromId(state.document.documentType!)
: const IdQueryParameter.unset(),
options: state.documentTypes, options: state.documentTypes,
name: _DocumentEditPageState.fkDocumentType, name: _DocumentEditPageState.fkDocumentType,
prefixIcon: const Icon(Icons.description_outlined), prefixIcon: const Icon(Icons.description_outlined),
@@ -176,7 +178,9 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
addLabelText: S.of(context)!.addStoragePath, addLabelText: S.of(context)!.addStoragePath,
labelText: S.of(context)!.storagePath, labelText: S.of(context)!.storagePath,
options: state.storagePaths, options: state.storagePaths,
initialValue: IdQueryParameter.fromId(state.document.storagePath), initialValue: state.document.storagePath != null
? IdQueryParameter.fromId(state.document.storagePath!)
: const IdQueryParameter.unset(),
name: fkStoragePath, name: fkStoragePath,
prefixIcon: const Icon(Icons.folder_outlined), prefixIcon: const Icon(Icons.folder_outlined),
), ),

View File

@@ -2,7 +2,7 @@ import 'package:collection/collection.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/database/tables/user_app_state.dart'; import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart';
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart'; import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart';
@@ -20,7 +20,7 @@ class DocumentSearchCubit extends Cubit<DocumentSearchState> with DocumentPaging
@override @override
final DocumentChangedNotifier notifier; final DocumentChangedNotifier notifier;
final UserAppState _userAppState; final LocalUserAppState _userAppState;
DocumentSearchCubit( DocumentSearchCubit(
this.api, this.api,
this.notifier, this.notifier,

View File

@@ -6,7 +6,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/core/database/tables/user_app_state.dart'; import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/document_search/cubit/document_search_cubit.dart'; import 'package:paperless_mobile/features/document_search/cubit/document_search_cubit.dart';
import 'package:paperless_mobile/features/document_search/view/remove_history_entry_dialog.dart'; import 'package:paperless_mobile/features/document_search/view/remove_history_entry_dialog.dart';
@@ -27,7 +27,7 @@ Future<void> showDocumentSearchPage(BuildContext context) {
context.read(), context.read(),
context.read(), context.read(),
context.read(), context.read(),
Hive.box<UserAppState>(HiveBoxes.userAppState).get(currentUser)!, Hive.box<LocalUserAppState>(HiveBoxes.localUserAppState).get(currentUser)!,
), ),
child: const DocumentSearchPage(), child: const DocumentSearchPage(),
), ),

View File

@@ -1,9 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive_flutter/adapters.dart'; import 'package:hive_flutter/adapters.dart';
import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart'; import 'package:paperless_mobile/core/bloc/server_information_cubit.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/user_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/delegate/customizable_sliver_persistent_header_delegate.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/document_search/view/document_search_page.dart';
@@ -43,7 +43,8 @@ class SliverSearchBar extends StatelessWidget {
icon: GlobalSettingsBuilder( icon: GlobalSettingsBuilder(
builder: (context, settings) { builder: (context, settings) {
return ValueListenableBuilder( return ValueListenableBuilder(
valueListenable: Hive.box<UserAccount>(HiveBoxes.userAccount).listenable(), valueListenable:
Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount).listenable(),
builder: (context, box, _) { builder: (context, box, _) {
final account = box.get(settings.currentLoggedInUser!)!; final account = box.get(settings.currentLoggedInUser!)!;
return UserAvatar(userId: settings.currentLoggedInUser!, account: account); return UserAvatar(userId: settings.currentLoggedInUser!, account: account);
@@ -55,7 +56,7 @@ class SliverSearchBar extends StatelessWidget {
showDialog( showDialog(
context: context, context: context,
builder: (_) => BlocProvider.value( builder: (_) => BlocProvider.value(
value: context.read<PaperlessServerInformationCubit>(), value: context.read<ServerInformationCubit>(),
child: const ManageAccountsPage(), child: const ManageAccountsPage(),
), ),
); );

View File

@@ -4,10 +4,9 @@ import 'package:flutter/foundation.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/database/tables/user_app_state.dart'; import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart';
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/core/database/tables/user_account.dart';
import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart'; import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart';
import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart'; import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart';
import 'package:paperless_mobile/features/settings/model/view_type.dart'; import 'package:paperless_mobile/features/settings/model/view_type.dart';
@@ -24,14 +23,17 @@ class DocumentsCubit extends Cubit<DocumentsState> with DocumentPagingBlocMixin
@override @override
final DocumentChangedNotifier notifier; final DocumentChangedNotifier notifier;
final UserAppState _userState; final LocalUserAppState _userState;
DocumentsCubit( DocumentsCubit(
this.api, this.api,
this.notifier, this.notifier,
this._labelRepository, this._labelRepository,
this._userState, this._userState,
) : super(DocumentsState(filter: _userState.currentDocumentFilter)) { ) : super(DocumentsState(
filter: _userState.currentDocumentFilter,
viewType: _userState.documentsPageViewType,
)) {
notifier.addListener( notifier.addListener(
this, this,
onUpdated: (document) { onUpdated: (document) {
@@ -103,16 +105,6 @@ class DocumentsCubit extends Cubit<DocumentsState> with DocumentPagingBlocMixin
return res; return res;
} }
@override
DocumentsState? fromJson(Map<String, dynamic> json) {
return DocumentsState.fromJson(json);
}
@override
Map<String, dynamic>? toJson(DocumentsState state) {
return state.toJson();
}
@override @override
Future<void> close() { Future<void> close() {
notifier.removeListener(this); notifier.removeListener(this);
@@ -122,6 +114,8 @@ class DocumentsCubit extends Cubit<DocumentsState> with DocumentPagingBlocMixin
void setViewType(ViewType viewType) { void setViewType(ViewType viewType) {
emit(state.copyWith(viewType: viewType)); emit(state.copyWith(viewType: viewType));
_userState.documentsPageViewType = viewType;
_userState.save();
} }
@override @override

View File

@@ -482,7 +482,7 @@ class _DocumentsPageState extends State<DocumentsPage> with SingleTickerProvider
try { try {
final correspondent = cubit.state.filter.correspondent; final correspondent = cubit.state.filter.correspondent;
if (correspondent is SetIdQueryParameter) { if (correspondent is SetIdQueryParameter) {
if (correspondent.id == correspondentId) { if (correspondentId == null || correspondent.id == correspondentId) {
cubit.updateCurrentFilter( cubit.updateCurrentFilter(
(filter) => filter.copyWith(correspondent: const IdQueryParameter.unset()), (filter) => filter.copyWith(correspondent: const IdQueryParameter.unset()),
); );
@@ -502,7 +502,7 @@ class _DocumentsPageState extends State<DocumentsPage> with SingleTickerProvider
try { try {
final documentType = cubit.state.filter.documentType; final documentType = cubit.state.filter.documentType;
if (documentType is SetIdQueryParameter) { if (documentType is SetIdQueryParameter) {
if (documentType.id == documentTypeId) { if (documentTypeId == null || documentType.id == documentTypeId) {
cubit.updateCurrentFilter( cubit.updateCurrentFilter(
(filter) => filter.copyWith(documentType: const IdQueryParameter.unset()), (filter) => filter.copyWith(documentType: const IdQueryParameter.unset()),
); );
@@ -522,7 +522,7 @@ class _DocumentsPageState extends State<DocumentsPage> with SingleTickerProvider
try { try {
final path = cubit.state.filter.documentType; final path = cubit.state.filter.documentType;
if (path is SetIdQueryParameter) { if (path is SetIdQueryParameter) {
if (path.id == pathId) { if (pathId == null || path.id == pathId) {
cubit.updateCurrentFilter( cubit.updateCurrentFilter(
(filter) => filter.copyWith(storagePath: const IdQueryParameter.unset()), (filter) => filter.copyWith(storagePath: const IdQueryParameter.unset()),
); );

View File

@@ -9,10 +9,10 @@ import 'package:fluttertoast/fluttertoast.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/bloc/connectivity_cubit.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/bloc/server_information_cubit.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/core/database/tables/user_app_state.dart'; import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart';
import 'package:paperless_mobile/core/global/constants.dart'; import 'package:paperless_mobile/core/global/constants.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/core/repository/saved_view_repository.dart'; import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
@@ -30,7 +30,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/cubit/label_cubit.dart';
import 'package:paperless_mobile/features/labels/view/pages/labels_page.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/cubit/authentication_cubit.dart';
import 'package:paperless_mobile/core/database/tables/user_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/features/notifications/services/local_notification_service.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/saved_view/cubit/saved_view_cubit.dart';
import 'package:paperless_mobile/features/sharing/share_intent_queue.dart'; import 'package:paperless_mobile/features/sharing/share_intent_queue.dart';
@@ -62,15 +62,6 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
_listenForReceivedFiles();
});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
_initializeData(context); _initializeData(context);
final userId = final userId =
Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser; Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser;
@@ -78,7 +69,7 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
context.read(), context.read(),
context.read(), context.read(),
context.read(), context.read(),
Hive.box<UserAppState>(HiveBoxes.userAppState).get(userId)!, Hive.box<LocalUserAppState>(HiveBoxes.localUserAppState).get(userId)!,
)..reload(); )..reload();
_savedViewCubit = SavedViewCubit( _savedViewCubit = SavedViewCubit(
context.read(), context.read(),
@@ -91,6 +82,14 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
context.read(), context.read(),
); );
_listenToInboxChanges(); _listenToInboxChanges();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
_listenForReceivedFiles();
});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
} }
void _listenToInboxChanges() { void _listenToInboxChanges() {
@@ -352,7 +351,7 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
Future.wait([ Future.wait([
context.read<LabelRepository>().initialize(), context.read<LabelRepository>().initialize(),
context.read<SavedViewRepository>().findAll(), context.read<SavedViewRepository>().findAll(),
context.read<PaperlessServerInformationCubit>().updateInformtion(), context.read<ServerInformationCubit>().updateInformation(),
]).onError<PaperlessServerException>((error, stackTrace) { ]).onError<PaperlessServerException>((error, stackTrace) {
showErrorMessage(context, error, stackTrace); showErrorMessage(context, error, stackTrace);
throw error; throw error;

View File

@@ -0,0 +1,29 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_mobile/core/bloc/server_information_cubit.dart';
import 'package:paperless_mobile/features/home/view/home_page.dart';
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
class HomeRoute extends StatelessWidget {
const HomeRoute({super.key});
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => TaskStatusCubit(
context.read(),
),
),
BlocProvider<ServerInformationCubit>(
create: (context) => ServerInformationCubit(
context.read(),
)..updateInformation(),
),
],
child: HomePage(),
);
}
}

View File

@@ -145,7 +145,7 @@ class _LabelsPageState extends State<LabelsPage> with SingleTickerProviderStateM
LabelTabView<Correspondent>( LabelTabView<Correspondent>(
labels: context.watch<LabelCubit>().state.correspondents, labels: context.watch<LabelCubit>().state.correspondents,
filterBuilder: (label) => DocumentFilter( filterBuilder: (label) => DocumentFilter(
correspondent: IdQueryParameter.fromId(label.id), correspondent: IdQueryParameter.fromId(label.id!),
pageSize: label.documentCount ?? 0, pageSize: label.documentCount ?? 0,
), ),
onEdit: _openEditCorrespondentPage, onEdit: _openEditCorrespondentPage,
@@ -166,7 +166,7 @@ class _LabelsPageState extends State<LabelsPage> with SingleTickerProviderStateM
LabelTabView<DocumentType>( LabelTabView<DocumentType>(
labels: context.watch<LabelCubit>().state.documentTypes, labels: context.watch<LabelCubit>().state.documentTypes,
filterBuilder: (label) => DocumentFilter( filterBuilder: (label) => DocumentFilter(
documentType: IdQueryParameter.fromId(label.id), documentType: IdQueryParameter.fromId(label.id!),
pageSize: label.documentCount ?? 0, pageSize: label.documentCount ?? 0,
), ),
onEdit: _openEditDocumentTypePage, onEdit: _openEditDocumentTypePage,
@@ -218,7 +218,7 @@ class _LabelsPageState extends State<LabelsPage> with SingleTickerProviderStateM
labels: context.watch<LabelCubit>().state.storagePaths, labels: context.watch<LabelCubit>().state.storagePaths,
onEdit: _openEditStoragePathPage, onEdit: _openEditStoragePathPage,
filterBuilder: (label) => DocumentFilter( filterBuilder: (label) => DocumentFilter(
storagePath: IdQueryParameter.fromId(label.id), storagePath: IdQueryParameter.fromId(label.id!),
pageSize: label.documentCount ?? 0, pageSize: label.documentCount ?? 0,
), ),
contentBuilder: (path) => Text(path.path), contentBuilder: (path) => Text(path.path),

View File

@@ -80,11 +80,12 @@ class _FullscreenLabelFormState<T extends Label> extends State<FullscreenLabelFo
final index = AutocompleteHighlightedOption.of(context); final index = AutocompleteHighlightedOption.of(context);
final value = index.isNegative ? null : options.elementAt(index); final value = index.isNegative ? null : options.elementAt(index);
widget.onSubmit( widget.onSubmit(
returnValue: IdQueryParameter.fromId( returnValue: value?.maybeWhen(
value?.whenOrNull( fromId: (id) => IdQueryParameter.fromId(id),
fromId: (id) => id, orElse: () => const IdQueryParameter.unset(),
), ) ??
)); const IdQueryParameter.unset(),
);
}, },
autofocus: true, autofocus: true,
style: theme.textTheme.bodyLarge?.apply( style: theme.textTheme.bodyLarge?.apply(
@@ -167,7 +168,7 @@ class _FullscreenLabelFormState<T extends Label> extends State<FullscreenLabelFo
if (widget.initialValue == null) { if (widget.initialValue == null) {
// If nothing is selected yet, show all options first. // If nothing is selected yet, show all options first.
for (final option in widget.options.values) { for (final option in widget.options.values) {
yield IdQueryParameter.fromId(option.id); yield IdQueryParameter.fromId(option.id!);
} }
if (widget.showNotAssignedOption) { if (widget.showNotAssignedOption) {
yield const IdQueryParameter.notAssigned(); yield const IdQueryParameter.notAssigned();
@@ -189,7 +190,7 @@ class _FullscreenLabelFormState<T extends Label> extends State<FullscreenLabelFo
if (initialValue is SetIdQueryParameter && option.id == initialValue.id) { if (initialValue is SetIdQueryParameter && option.id == initialValue.id) {
continue; continue;
} }
yield IdQueryParameter.fromId(option.id); yield IdQueryParameter.fromId(option.id!);
} }
} }
} else { } else {
@@ -198,7 +199,7 @@ class _FullscreenLabelFormState<T extends Label> extends State<FullscreenLabelFo
widget.options.values.where((e) => e.name.trim().toLowerCase().contains(normalizedQuery)); widget.options.values.where((e) => e.name.trim().toLowerCase().contains(normalizedQuery));
if (matches.isNotEmpty) { if (matches.isNotEmpty) {
for (final match in matches) { for (final match in matches) {
yield IdQueryParameter.fromId(match.id); yield IdQueryParameter.fromId(match.id!);
} }
if (widget.showNotAssignedOption) { if (widget.showNotAssignedOption) {
yield const IdQueryParameter.notAssigned(); yield const IdQueryParameter.notAssigned();
@@ -225,7 +226,7 @@ class _FullscreenLabelFormState<T extends Label> extends State<FullscreenLabelFo
unset: () => S.of(context)!.startTyping, unset: () => S.of(context)!.startTyping,
notAssigned: () => S.of(context)!.notAssigned, notAssigned: () => S.of(context)!.notAssigned,
anyAssigned: () => S.of(context)!.anyAssigned, anyAssigned: () => S.of(context)!.anyAssigned,
fromId: (id) => widget.options[id]!.name, fromId: (id) => widget.options[id]?.name ?? S.of(context)!.startTyping,
); );
} }

View File

@@ -49,7 +49,7 @@ class LabelFormField<T extends Label> extends StatelessWidget {
unset: () => '', unset: () => '',
notAssigned: () => S.of(context)!.notAssigned, notAssigned: () => S.of(context)!.notAssigned,
anyAssigned: () => S.of(context)!.anyAssigned, anyAssigned: () => S.of(context)!.anyAssigned,
fromId: (id) => options[id]!.name, fromId: (id) => options[id]?.name,
) ?? ) ??
''; '';
} }
@@ -142,7 +142,7 @@ class LabelFormField<T extends Label> extends StatelessWidget {
child: ActionChip( child: ActionChip(
label: Text(suggestion.name), label: Text(suggestion.name),
onPressed: () => field.didChange( onPressed: () => field.didChange(
IdQueryParameter.fromId(suggestion.id), IdQueryParameter.fromId(suggestion.id!),
), ),
), ),
); );

View File

@@ -5,7 +5,7 @@ import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.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/cubit/linked_documents_cubit.dart';
import 'package:paperless_mobile/features/linked_documents/view/linked_documents_page.dart'; import 'package:paperless_mobile/features/linked_documents/view/linked_documents_page.dart';
import 'package:paperless_mobile/core/database/tables/user_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/helpers/format_helpers.dart'; import 'package:paperless_mobile/helpers/format_helpers.dart';

View File

@@ -7,25 +7,25 @@ import 'package:hive_flutter/adapters.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/user_app_state.dart'; import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart';
import 'package:paperless_mobile/core/interceptor/dio_http_error_interceptor.dart'; import 'package:paperless_mobile/core/interceptor/dio_http_error_interceptor.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/core/repository/saved_view_repository.dart'; import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
import 'package:paperless_mobile/core/security/session_manager.dart'; import 'package:paperless_mobile/core/security/session_manager.dart';
import 'package:paperless_mobile/features/login/model/client_certificate.dart'; import 'package:paperless_mobile/features/login/model/client_certificate.dart';
import 'package:paperless_mobile/features/login/model/login_form_credentials.dart'; import 'package:paperless_mobile/features/login/model/login_form_credentials.dart';
import 'package:paperless_mobile/core/database/tables/user_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/database/tables/user_credentials.dart'; import 'package:paperless_mobile/core/database/tables/user_credentials.dart';
import 'package:paperless_mobile/features/login/services/authentication_service.dart'; import 'package:paperless_mobile/features/login/services/authentication_service.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/core/database/tables/user_settings.dart'; import 'package:paperless_mobile/core/database/tables/local_user_settings.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
part 'authentication_state.dart'; part 'authentication_state.dart';
class AuthenticationCubit extends Cubit<AuthenticationState> { class AuthenticationCubit extends Cubit<AuthenticationState> {
final LocalAuthenticationService _localAuthService; final LocalAuthenticationService _localAuthService;
final PaperlessAuthenticationApi _authApi; final PaperlessAuthenticationApi _authApi;
final SessionManager _dioWrapper; final SessionManager _sessionManager;
final LabelRepository _labelRepository; final LabelRepository _labelRepository;
final SavedViewRepository _savedViewRepository; final SavedViewRepository _savedViewRepository;
final PaperlessServerStatsApi _serverStatsApi; final PaperlessServerStatsApi _serverStatsApi;
@@ -33,7 +33,7 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
AuthenticationCubit( AuthenticationCubit(
this._localAuthService, this._localAuthService,
this._authApi, this._authApi,
this._dioWrapper, this._sessionManager,
this._labelRepository, this._labelRepository,
this._savedViewRepository, this._savedViewRepository,
this._serverStatsApi, this._serverStatsApi,
@@ -46,7 +46,7 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
}) async { }) async {
assert(credentials.username != null && credentials.password != null); assert(credentials.username != null && credentials.password != null);
_dioWrapper.updateSettings( _sessionManager.updateSettings(
baseUrl: serverUrl, baseUrl: serverUrl,
clientCertificate: clientCertificate, clientCertificate: clientCertificate,
); );
@@ -54,13 +54,14 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
username: credentials.username!, username: credentials.username!,
password: credentials.password!, password: credentials.password!,
); );
_dioWrapper.updateSettings( _sessionManager.updateSettings(
baseUrl: serverUrl, baseUrl: serverUrl,
clientCertificate: clientCertificate, clientCertificate: clientCertificate,
authToken: token, authToken: token,
); );
final userAccountBox = Hive.box<UserAccount>(HiveBoxes.userAccount);
final userStateBox = Hive.box<UserAppState>(HiveBoxes.userAppState); final userAccountBox = Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount);
final userStateBox = Hive.box<LocalUserAppState>(HiveBoxes.localUserAppState);
final userId = "${credentials.username}@$serverUrl"; final userId = "${credentials.username}@$serverUrl";
@@ -72,9 +73,9 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
// Create user account // Create user account
await userAccountBox.put( await userAccountBox.put(
userId, userId,
UserAccount( LocalUserAccount(
id: userId, id: userId,
settings: UserSettings(), settings: LocalUserSettings(),
serverUrl: serverUrl, serverUrl: serverUrl,
username: credentials.username!, username: credentials.username!,
fullName: fullName, fullName: fullName,
@@ -84,7 +85,7 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
// Create user state // Create user state
await userStateBox.put( await userStateBox.put(
userId, userId,
UserAppState(userId: userId), LocalUserAppState(userId: userId),
); );
// Save credentials in encrypted box // Save credentials in encrypted box
@@ -119,7 +120,7 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
if (globalSettings.currentLoggedInUser == userId) { if (globalSettings.currentLoggedInUser == userId) {
return; return;
} }
final userAccountBox = Hive.box<UserAccount>(HiveBoxes.userAccount); final userAccountBox = Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount);
if (!userAccountBox.containsKey(userId)) { if (!userAccountBox.containsKey(userId)) {
debugPrint("User $userId not yet registered."); debugPrint("User $userId not yet registered.");
@@ -148,7 +149,7 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
await _resetExternalState(); await _resetExternalState();
_dioWrapper.updateSettings( _sessionManager.updateSettings(
authToken: credentials!.token, authToken: credentials!.token,
clientCertificate: credentials.clientCertificate, clientCertificate: credentials.clientCertificate,
serverInformation: PaperlessServerInformationModel(), serverInformation: PaperlessServerInformationModel(),
@@ -178,8 +179,8 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
assert(credentials.password != null && credentials.username != null); assert(credentials.password != null && credentials.username != null);
final userId = "${credentials.username}@$serverUrl"; final userId = "${credentials.username}@$serverUrl";
final userAccountsBox = Hive.box<UserAccount>(HiveBoxes.userAccount); final userAccountsBox = Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount);
final userStateBox = Hive.box<UserAppState>(HiveBoxes.userAppState); final userStateBox = Hive.box<LocalUserAppState>(HiveBoxes.localUserAppState);
if (userAccountsBox.containsKey(userId)) { if (userAccountsBox.containsKey(userId)) {
throw Exception("User already exists"); throw Exception("User already exists");
@@ -204,11 +205,11 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
await userAccountsBox.put( await userAccountsBox.put(
userId, userId,
UserAccount( LocalUserAccount(
id: userId, id: userId,
serverUrl: serverUrl, serverUrl: serverUrl,
username: credentials.username!, username: credentials.username!,
settings: UserSettings( settings: LocalUserSettings(
isBiometricAuthenticationEnabled: enableBiometricAuthentication, isBiometricAuthenticationEnabled: enableBiometricAuthentication,
), ),
fullName: fullName, fullName: fullName,
@@ -217,7 +218,7 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
await userStateBox.put( await userStateBox.put(
userId, userId,
UserAppState( LocalUserAppState(
userId: userId, userId: userId,
), ),
); );
@@ -236,15 +237,15 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
Future<void> removeAccount(String userId) async { Future<void> removeAccount(String userId) async {
final globalSettings = Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!; final globalSettings = Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!;
final userAccountBox = Hive.box<UserAccount>(HiveBoxes.userAccount); final userAccountBox = Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount);
final userCredentialsBox = await _getUserCredentialsBox(); final userCredentialsBox = await _getUserCredentialsBox();
final userAppStateBox = Hive.box<UserAppState>(HiveBoxes.userAppState); final userAppStateBox = Hive.box<LocalUserAppState>(HiveBoxes.localUserAppState);
final currentUser = globalSettings.currentLoggedInUser; final currentUser = globalSettings.currentLoggedInUser;
await userAccountBox.delete(userId); await userAccountBox.delete(userId);
await userAppStateBox.delete(userId); await userAppStateBox.delete(userId);
await userCredentialsBox.delete(userId); await userCredentialsBox.delete(userId);
await userAccountBox.close(); await userCredentialsBox.close();
if (currentUser == userId) { if (currentUser == userId) {
return logout(); return logout();
@@ -262,7 +263,7 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
return; return;
} }
final userAccount = Hive.box<UserAccount>(HiveBoxes.userAccount).get(userId)!; final userAccount = Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount).get(userId)!;
if (userAccount.settings.isBiometricAuthenticationEnabled) { if (userAccount.settings.isBiometricAuthenticationEnabled) {
final localAuthSuccess = final localAuthSuccess =
@@ -280,7 +281,7 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
if (authentication == null) { if (authentication == null) {
throw Exception("User should be authenticated but no authentication information was found."); throw Exception("User should be authenticated but no authentication information was found.");
} }
_dioWrapper.updateSettings( _sessionManager.updateSettings(
clientCertificate: authentication.clientCertificate, clientCertificate: authentication.clientCertificate,
authToken: authentication.token, authToken: authentication.token,
baseUrl: userAccount.serverUrl, baseUrl: userAccount.serverUrl,
@@ -321,13 +322,13 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
Future<Box<UserCredentials>> _getUserCredentialsBox() async { Future<Box<UserCredentials>> _getUserCredentialsBox() async {
final keyBytes = await _getEncryptedBoxKey(); final keyBytes = await _getEncryptedBoxKey();
return Hive.openBox<UserCredentials>( return Hive.openBox<UserCredentials>(
HiveBoxes.userCredentials, HiveBoxes.localUserCredentials,
encryptionCipher: HiveAesCipher(keyBytes), encryptionCipher: HiveAesCipher(keyBytes),
); );
} }
Future<void> _resetExternalState() { Future<void> _resetExternalState() {
_dioWrapper.resetSettings(); _sessionManager.resetSettings();
return Future.wait([ return Future.wait([
HydratedBloc.storage.clear(), HydratedBloc.storage.clear(),
_labelRepository.clear(), _labelRepository.clear(),

View File

@@ -1,10 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/type/types.dart';
import 'package:paperless_mobile/features/app_intro/application_intro_slideshow.dart';
import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart';
import 'package:paperless_mobile/features/login/model/client_certificate.dart'; import 'package:paperless_mobile/features/login/model/client_certificate.dart';
import 'package:paperless_mobile/features/login/model/client_certificate_form_model.dart'; import 'package:paperless_mobile/features/login/model/client_certificate_form_model.dart';
import 'package:paperless_mobile/features/login/model/login_form_credentials.dart'; import 'package:paperless_mobile/features/login/model/login_form_credentials.dart';
@@ -12,8 +7,6 @@ import 'package:paperless_mobile/features/login/view/widgets/form_fields/client_
import 'package:paperless_mobile/features/login/view/widgets/form_fields/server_address_form_field.dart'; import 'package:paperless_mobile/features/login/view/widgets/form_fields/server_address_form_field.dart';
import 'package:paperless_mobile/features/login/view/widgets/form_fields/user_credentials_form_field.dart'; import 'package:paperless_mobile/features/login/view/widgets/form_fields/user_credentials_form_field.dart';
import 'package:paperless_mobile/features/login/view/widgets/login_pages/server_connection_page.dart'; import 'package:paperless_mobile/features/login/view/widgets/login_pages/server_connection_page.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'widgets/login_pages/server_login_page.dart'; import 'widgets/login_pages/server_login_page.dart';
import 'widgets/never_scrollable_scroll_behavior.dart'; import 'widgets/never_scrollable_scroll_behavior.dart';

View File

@@ -1,6 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive/hive.dart';
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart'; import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart';
import 'package:paperless_mobile/core/widgets/hint_card.dart'; import 'package:paperless_mobile/core/widgets/hint_card.dart';
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart'; import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
import 'package:paperless_mobile/features/saved_view_details/cubit/saved_view_details_cubit.dart'; import 'package:paperless_mobile/features/saved_view_details/cubit/saved_view_details_cubit.dart';
@@ -17,11 +21,9 @@ class SavedViewList extends StatelessWidget {
return BlocBuilder<SavedViewCubit, SavedViewState>( return BlocBuilder<SavedViewCubit, SavedViewState>(
builder: (context, state) { builder: (context, state) {
return state.when( return state.when(
initial: (correspondents, documentTypes, tags, storagePaths) => initial: (correspondents, documentTypes, tags, storagePaths) => Container(),
Container(), loading: (correspondents, documentTypes, tags, storagePaths) => Center(
loading: (correspondents, documentTypes, tags, storagePaths) => child: Text("Saved views loading..."), //TODO: INTL
Center(
child: Text("Saved views loading..."),
), ),
loaded: ( loaded: (
savedViews, savedViews,
@@ -33,9 +35,7 @@ class SavedViewList extends StatelessWidget {
if (savedViews.isEmpty) { if (savedViews.isEmpty) {
return SliverToBoxAdapter( return SliverToBoxAdapter(
child: HintCard( child: HintCard(
hintText: S hintText: S.of(context)!.createViewsToQuicklyFilterYourDocuments,
.of(context)!
.createViewsToQuicklyFilterYourDocuments,
), ),
); );
} }
@@ -59,13 +59,17 @@ class SavedViewList extends StatelessWidget {
ctxt.read(), ctxt.read(),
ctxt.read(), ctxt.read(),
context.read(), context.read(),
Hive.box<LocalUserAppState>(HiveBoxes.localUserAppState).get(
Hive.box<GlobalSettings>(HiveBoxes.globalSettings)
.getValue()!
.currentLoggedInUser!,
)!,
savedView: view, savedView: view,
), ),
), ),
], ],
child: SavedViewDetailsPage( child: SavedViewDetailsPage(
onDelete: onDelete: context.read<SavedViewCubit>().remove,
context.read<SavedViewCubit>().remove,
), ),
), ),
), ),

View File

@@ -1,17 +1,16 @@
import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart';
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.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/paged_document_view/cubit/document_paging_bloc_mixin.dart';
import 'package:paperless_mobile/features/settings/model/view_type.dart'; import 'package:paperless_mobile/features/settings/model/view_type.dart';
part 'saved_view_details_cubit.g.dart';
part 'saved_view_details_state.dart'; part 'saved_view_details_state.dart';
class SavedViewDetailsCubit extends HydratedCubit<SavedViewDetailsState> class SavedViewDetailsCubit extends Cubit<SavedViewDetailsState> with DocumentPagingBlocMixin {
with DocumentPagingBlocMixin {
@override @override
final PaperlessDocumentsApi api; final PaperlessDocumentsApi api;
@@ -22,10 +21,13 @@ class SavedViewDetailsCubit extends HydratedCubit<SavedViewDetailsState>
final SavedView savedView; final SavedView savedView;
final LocalUserAppState _userState;
SavedViewDetailsCubit( SavedViewDetailsCubit(
this.api, this.api,
this.notifier, this.notifier,
this._labelRepository, { this._labelRepository,
this._userState, {
required this.savedView, required this.savedView,
}) : super( }) : super(
SavedViewDetailsState( SavedViewDetailsState(
@@ -33,6 +35,7 @@ class SavedViewDetailsCubit extends HydratedCubit<SavedViewDetailsState>
documentTypes: _labelRepository.state.documentTypes, documentTypes: _labelRepository.state.documentTypes,
tags: _labelRepository.state.tags, tags: _labelRepository.state.tags,
storagePaths: _labelRepository.state.storagePaths, storagePaths: _labelRepository.state.storagePaths,
viewType: _userState.savedViewsViewType,
), ),
) { ) {
notifier.addListener( notifier.addListener(
@@ -58,16 +61,9 @@ class SavedViewDetailsCubit extends HydratedCubit<SavedViewDetailsState>
void setViewType(ViewType viewType) { void setViewType(ViewType viewType) {
emit(state.copyWith(viewType: viewType)); emit(state.copyWith(viewType: viewType));
} _userState
..savedViewsViewType = viewType
@override ..save();
SavedViewDetailsState? fromJson(Map<String, dynamic> json) {
return SavedViewDetailsState.fromJson(json);
}
@override
Map<String, dynamic>? toJson(SavedViewDetailsState state) {
return state.toJson();
} }
@override @override

View File

@@ -1,8 +1,6 @@
part of 'saved_view_details_cubit.dart'; part of 'saved_view_details_cubit.dart';
@JsonSerializable(ignoreUnannotated: true)
class SavedViewDetailsState extends DocumentPagingState { class SavedViewDetailsState extends DocumentPagingState {
@JsonKey()
final ViewType viewType; final ViewType viewType;
final Map<int, Correspondent> correspondents; final Map<int, Correspondent> correspondents;
@@ -70,9 +68,4 @@ class SavedViewDetailsState extends DocumentPagingState {
storagePaths: storagePaths ?? this.storagePaths, storagePaths: storagePaths ?? this.storagePaths,
); );
} }
factory SavedViewDetailsState.fromJson(Map<String, dynamic> json) =>
_$SavedViewDetailsStateFromJson(json);
Map<String, dynamic> toJson() => _$SavedViewDetailsStateToJson(this);
} }

View File

@@ -1,6 +1,15 @@
import 'package:hive/hive.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
part 'view_type.g.dart';
@HiveType(typeId: HiveTypeIds.viewType)
enum ViewType { enum ViewType {
@HiveField(0)
grid, grid,
@HiveField(1)
list, list,
@HiveField(2)
detailed; detailed;
ViewType toggle() { ViewType toggle() {

View File

@@ -9,7 +9,7 @@ import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart'; import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart';
import 'package:paperless_mobile/features/login/model/login_form_credentials.dart'; import 'package:paperless_mobile/features/login/model/login_form_credentials.dart';
import 'package:paperless_mobile/core/database/tables/user_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/features/login/view/login_page.dart'; import 'package:paperless_mobile/features/login/view/login_page.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/features/settings/view/dialogs/switch_account_dialog.dart'; import 'package:paperless_mobile/features/settings/view/dialogs/switch_account_dialog.dart';
@@ -26,7 +26,7 @@ class ManageAccountsPage extends StatelessWidget {
return GlobalSettingsBuilder( return GlobalSettingsBuilder(
builder: (context, globalSettings) { builder: (context, globalSettings) {
return ValueListenableBuilder( return ValueListenableBuilder(
valueListenable: Hive.box<UserAccount>(HiveBoxes.userAccount).listenable(), valueListenable: Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount).listenable(),
builder: (context, box, _) { builder: (context, box, _) {
final userIds = box.keys.toList().cast<String>(); final userIds = box.keys.toList().cast<String>();
final otherAccounts = userIds final otherAccounts = userIds
@@ -68,7 +68,7 @@ class ManageAccountsPage extends StatelessWidget {
title: Text(S.of(context)!.addAccount), title: Text(S.of(context)!.addAccount),
leading: const Icon(Icons.person_add), leading: const Icon(Icons.person_add),
onTap: () { onTap: () {
_onAddAccount(context); _onAddAccount(context, globalSettings.currentLoggedInUser!);
}, },
), ),
], ],
@@ -82,7 +82,7 @@ class ManageAccountsPage extends StatelessWidget {
Widget _buildAccountTile( Widget _buildAccountTile(
BuildContext context, BuildContext context,
String userId, String userId,
UserAccount account, LocalUserAccount account,
GlobalSettings settings, GlobalSettings settings,
) { ) {
final isLoggedIn = userId == settings.currentLoggedInUser; final isLoggedIn = userId == settings.currentLoggedInUser;
@@ -147,16 +147,7 @@ class ManageAccountsPage extends StatelessWidget {
onSelected: (value) async { onSelected: (value) async {
if (value == 0) { if (value == 0) {
// Switch // Switch
final navigator = Navigator.of(context); _onSwitchAccount(context, settings.currentLoggedInUser!, userId);
if (settings.currentLoggedInUser == userId) return;
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const SwitchingAccountsPage(),
),
);
await context.read<AuthenticationCubit>().switchAccount(userId);
navigator.popUntil((route) => route.isFirst);
} else if (value == 1) { } else if (value == 1) {
// Remove // Remove
final shouldPop = userId == settings.currentLoggedInUser; final shouldPop = userId == settings.currentLoggedInUser;
@@ -177,7 +168,7 @@ class ManageAccountsPage extends StatelessWidget {
return child; return child;
} }
Future<void> _onAddAccount(BuildContext context) async { Future<void> _onAddAccount(BuildContext context, String currentUser) async {
final userId = await Navigator.push( final userId = await Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
@@ -207,9 +198,21 @@ class ManageAccountsPage extends StatelessWidget {
) ?? ) ??
false; false;
if (shoudSwitch) { if (shoudSwitch) {
await context.read<AuthenticationCubit>().switchAccount(userId); _onSwitchAccount(context, currentUser, userId);
Navigator.pop(context);
} }
} }
} }
_onSwitchAccount(BuildContext context, String currentUser, String newUser) async {
final navigator = Navigator.of(context);
if (currentUser == newUser) return;
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const SwitchingAccountsPage(),
),
);
await context.read<AuthenticationCubit>().switchAccount(newUser);
navigator.popUntil((route) => route.isFirst);
}
} }

View File

@@ -1,9 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart'; import 'package:paperless_mobile/core/bloc/server_information_cubit.dart';
import 'package:paperless_mobile/core/bloc/paperless_server_information_state.dart'; import 'package:paperless_mobile/core/bloc/server_information_state.dart';
import 'package:paperless_mobile/features/settings/view/pages/application_settings_page.dart'; import 'package:paperless_mobile/features/settings/view/pages/application_settings_page.dart';
import 'package:paperless_mobile/features/settings/view/pages/security_settings_page.dart'; import 'package:paperless_mobile/features/settings/view/pages/security_settings_page.dart';
import 'package:paperless_mobile/features/settings/view/widgets/user_settings_builder.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
class SettingsPage extends StatelessWidget { class SettingsPage extends StatelessWidget {
@@ -15,23 +16,27 @@ class SettingsPage extends StatelessWidget {
appBar: AppBar( appBar: AppBar(
title: Text(S.of(context)!.settings), title: Text(S.of(context)!.settings),
), ),
bottomNavigationBar: bottomNavigationBar: UserAccountBuilder(
BlocBuilder<PaperlessServerInformationCubit, PaperlessServerInformationState>( builder: (context, user) {
builder: (context, state) { assert(user != null);
final info = state.information!; final host = user!.serverUrl.replaceFirst(RegExp(r"https?://"), "");
return ListTile( return ListTile(
title: Text( title: Text(
S.of(context)!.loggedInAs(info.username ?? 'unknown') + "@${info.host}", S.of(context)!.loggedInAs(user.username) + "@$host",
style: Theme.of(context).textTheme.labelSmall, style: Theme.of(context).textTheme.labelSmall,
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
subtitle: Text( subtitle: BlocBuilder<ServerInformationCubit, ServerInformationState>(
S.of(context)!.paperlessServerVersion + builder: (context, state) {
' ' + return Text(
info.version.toString() + S.of(context)!.paperlessServerVersion +
' (API v${info.apiVersion})', ' ' +
style: Theme.of(context).textTheme.labelSmall, state.information!.version.toString() +
textAlign: TextAlign.center, ' (API v${state.information!.apiVersion})',
style: Theme.of(context).textTheme.labelSmall,
textAlign: TextAlign.center,
);
},
), ),
); );
}, },

View File

@@ -1,9 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:paperless_mobile/core/database/tables/user_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
class UserAvatar extends StatelessWidget { class UserAvatar extends StatelessWidget {
final String userId; final String userId;
final UserAccount account; final LocalUserAccount account;
const UserAvatar({ const UserAvatar({
super.key, super.key,
required this.userId, required this.userId,

View File

@@ -1,14 +1,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hive_flutter/adapters.dart'; import 'package:hive_flutter/adapters.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/user_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/core/database/tables/user_settings.dart'; import 'package:paperless_mobile/core/database/tables/local_user_settings.dart';
class UserAccountBuilder extends StatelessWidget { class UserAccountBuilder extends StatelessWidget {
final Widget Function( final Widget Function(
BuildContext context, BuildContext context,
UserAccount? settings, LocalUserAccount? settings,
) builder; ) builder;
const UserAccountBuilder({ const UserAccountBuilder({
@@ -18,8 +18,8 @@ class UserAccountBuilder extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ValueListenableBuilder<Box<UserAccount>>( return ValueListenableBuilder<Box<LocalUserAccount>>(
valueListenable: Hive.box<UserAccount>(HiveBoxes.userAccount).listenable(), valueListenable: Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount).listenable(),
builder: (context, accountBox, _) { builder: (context, accountBox, _) {
final currentUser = final currentUser =
Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser; Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser;

View File

@@ -23,7 +23,7 @@ class SimilarDocumentsCubit extends Cubit<SimilarDocumentsState> with DocumentPa
this.notifier, this.notifier,
this._labelRepository, { this._labelRepository, {
required this.documentId, required this.documentId,
}) : super(SimilarDocumentsState(filter: DocumentFilter())) { }) : super(const SimilarDocumentsState(filter: DocumentFilter())) {
notifier.addListener( notifier.addListener(
this, this,
onDeleted: remove, onDeleted: remove,

View File

@@ -18,9 +18,9 @@ import 'package:package_info_plus/package_info_plus.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/constants.dart'; import 'package:paperless_mobile/constants.dart';
import 'package:paperless_mobile/core/bloc/connectivity_cubit.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/bloc/server_information_cubit.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/user_app_state.dart'; import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart';
import 'package:paperless_mobile/core/interceptor/dio_http_error_interceptor.dart'; import 'package:paperless_mobile/core/interceptor/dio_http_error_interceptor.dart';
import 'package:paperless_mobile/core/interceptor/language_header.interceptor.dart'; import 'package:paperless_mobile/core/interceptor/language_header.interceptor.dart';
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
@@ -32,11 +32,12 @@ import 'package:paperless_mobile/core/service/dio_file_service.dart';
import 'package:paperless_mobile/core/type/types.dart'; import 'package:paperless_mobile/core/type/types.dart';
import 'package:paperless_mobile/features/app_intro/application_intro_slideshow.dart'; import 'package:paperless_mobile/features/app_intro/application_intro_slideshow.dart';
import 'package:paperless_mobile/features/home/view/home_page.dart'; import 'package:paperless_mobile/features/home/view/home_page.dart';
import 'package:paperless_mobile/features/home/view/home_route.dart';
import 'package:paperless_mobile/features/home/view/widget/verify_identity_page.dart'; import 'package:paperless_mobile/features/home/view/widget/verify_identity_page.dart';
import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart'; import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart';
import 'package:paperless_mobile/features/login/model/client_certificate.dart'; import 'package:paperless_mobile/features/login/model/client_certificate.dart';
import 'package:paperless_mobile/features/login/model/login_form_credentials.dart'; import 'package:paperless_mobile/features/login/model/login_form_credentials.dart';
import 'package:paperless_mobile/core/database/tables/user_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/features/login/services/authentication_service.dart'; import 'package:paperless_mobile/features/login/services/authentication_service.dart';
import 'package:paperless_mobile/features/login/view/login_page.dart'; import 'package:paperless_mobile/features/login/view/login_page.dart';
import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart'; import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart';
@@ -66,8 +67,8 @@ Future<void> _initHive() async {
// await getApplicationDocumentsDirectory().then((value) => value.delete(recursive: true)); // await getApplicationDocumentsDirectory().then((value) => value.delete(recursive: true));
registerHiveAdapters(); registerHiveAdapters();
await Hive.openBox<UserAccount>(HiveBoxes.userAccount); await Hive.openBox<LocalUserAccount>(HiveBoxes.localUserAccount);
await Hive.openBox<UserAppState>(HiveBoxes.userAppState); await Hive.openBox<LocalUserAppState>(HiveBoxes.localUserAppState);
final globalSettingsBox = await Hive.openBox<GlobalSettings>(HiveBoxes.globalSettings); final globalSettingsBox = await Hive.openBox<GlobalSettings>(HiveBoxes.globalSettings);
if (!globalSettingsBox.hasValue) { if (!globalSettingsBox.hasValue) {
@@ -302,26 +303,8 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
return BlocBuilder<AuthenticationCubit, AuthenticationState>( return BlocBuilder<AuthenticationCubit, AuthenticationState>(
builder: (context, authentication) { builder: (context, authentication) {
if (authentication.isAuthenticated) { if (authentication.isAuthenticated) {
return MultiBlocProvider( return HomeRoute(
// This key will cause the subtree to be remounted, which will again
// call the provider's create callback! Without this key, the blocs
// would not be recreated on account switch!
key: ValueKey(authentication.userId), key: ValueKey(authentication.userId),
providers: [
BlocProvider(
create: (context) => TaskStatusCubit(
context.read<PaperlessTasksApi>(),
),
),
BlocProvider<PaperlessServerInformationCubit>(
create: (context) => PaperlessServerInformationCubit(
context.read<PaperlessServerStatsApi>(),
),
),
],
child: HomePage(
key: ValueKey(authentication.userId),
),
); );
} else if (authentication.showBiometricAuthenticationScreen) { } else if (authentication.showBiometricAuthenticationScreen) {
return const VerifyIdentityPage(); return const VerifyIdentityPage();

View File

@@ -14,7 +14,7 @@ class IdQueryParameter with _$IdQueryParameter {
@HiveType(typeId: PaperlessApiHiveTypeIds.anyAssignedIdQueryParameter) @HiveType(typeId: PaperlessApiHiveTypeIds.anyAssignedIdQueryParameter)
const factory IdQueryParameter.anyAssigned() = AnyAssignedIdQueryParameter; const factory IdQueryParameter.anyAssigned() = AnyAssignedIdQueryParameter;
@HiveType(typeId: PaperlessApiHiveTypeIds.setIdQueryParameter) @HiveType(typeId: PaperlessApiHiveTypeIds.setIdQueryParameter)
const factory IdQueryParameter.fromId(@HiveField(0) int? id) = SetIdQueryParameter; const factory IdQueryParameter.fromId(@HiveField(0) int id) = SetIdQueryParameter;
Map<String, String> toQueryParameter(String field) { Map<String, String> toQueryParameter(String field) {
return when( return when(

View File

@@ -38,7 +38,7 @@ mixin _$IdQueryParameter {
required TResult Function() unset, required TResult Function() unset,
required TResult Function() notAssigned, required TResult Function() notAssigned,
required TResult Function() anyAssigned, required TResult Function() anyAssigned,
required TResult Function(@HiveField(0) int? id) fromId, required TResult Function(@HiveField(0) int id) fromId,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@optionalTypeArgs @optionalTypeArgs
@@ -46,7 +46,7 @@ mixin _$IdQueryParameter {
TResult? Function()? unset, TResult? Function()? unset,
TResult? Function()? notAssigned, TResult? Function()? notAssigned,
TResult? Function()? anyAssigned, TResult? Function()? anyAssigned,
TResult? Function(@HiveField(0) int? id)? fromId, TResult? Function(@HiveField(0) int id)? fromId,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@optionalTypeArgs @optionalTypeArgs
@@ -54,7 +54,7 @@ mixin _$IdQueryParameter {
TResult Function()? unset, TResult Function()? unset,
TResult Function()? notAssigned, TResult Function()? notAssigned,
TResult Function()? anyAssigned, TResult Function()? anyAssigned,
TResult Function(@HiveField(0) int? id)? fromId, TResult Function(@HiveField(0) int id)? fromId,
required TResult orElse(), required TResult orElse(),
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@@ -155,7 +155,7 @@ class _$UnsetIdQueryParameter extends UnsetIdQueryParameter {
required TResult Function() unset, required TResult Function() unset,
required TResult Function() notAssigned, required TResult Function() notAssigned,
required TResult Function() anyAssigned, required TResult Function() anyAssigned,
required TResult Function(@HiveField(0) int? id) fromId, required TResult Function(@HiveField(0) int id) fromId,
}) { }) {
return unset(); return unset();
} }
@@ -166,7 +166,7 @@ class _$UnsetIdQueryParameter extends UnsetIdQueryParameter {
TResult? Function()? unset, TResult? Function()? unset,
TResult? Function()? notAssigned, TResult? Function()? notAssigned,
TResult? Function()? anyAssigned, TResult? Function()? anyAssigned,
TResult? Function(@HiveField(0) int? id)? fromId, TResult? Function(@HiveField(0) int id)? fromId,
}) { }) {
return unset?.call(); return unset?.call();
} }
@@ -177,7 +177,7 @@ class _$UnsetIdQueryParameter extends UnsetIdQueryParameter {
TResult Function()? unset, TResult Function()? unset,
TResult Function()? notAssigned, TResult Function()? notAssigned,
TResult Function()? anyAssigned, TResult Function()? anyAssigned,
TResult Function(@HiveField(0) int? id)? fromId, TResult Function(@HiveField(0) int id)? fromId,
required TResult orElse(), required TResult orElse(),
}) { }) {
if (unset != null) { if (unset != null) {
@@ -293,7 +293,7 @@ class _$NotAssignedIdQueryParameter extends NotAssignedIdQueryParameter {
required TResult Function() unset, required TResult Function() unset,
required TResult Function() notAssigned, required TResult Function() notAssigned,
required TResult Function() anyAssigned, required TResult Function() anyAssigned,
required TResult Function(@HiveField(0) int? id) fromId, required TResult Function(@HiveField(0) int id) fromId,
}) { }) {
return notAssigned(); return notAssigned();
} }
@@ -304,7 +304,7 @@ class _$NotAssignedIdQueryParameter extends NotAssignedIdQueryParameter {
TResult? Function()? unset, TResult? Function()? unset,
TResult? Function()? notAssigned, TResult? Function()? notAssigned,
TResult? Function()? anyAssigned, TResult? Function()? anyAssigned,
TResult? Function(@HiveField(0) int? id)? fromId, TResult? Function(@HiveField(0) int id)? fromId,
}) { }) {
return notAssigned?.call(); return notAssigned?.call();
} }
@@ -315,7 +315,7 @@ class _$NotAssignedIdQueryParameter extends NotAssignedIdQueryParameter {
TResult Function()? unset, TResult Function()? unset,
TResult Function()? notAssigned, TResult Function()? notAssigned,
TResult Function()? anyAssigned, TResult Function()? anyAssigned,
TResult Function(@HiveField(0) int? id)? fromId, TResult Function(@HiveField(0) int id)? fromId,
required TResult orElse(), required TResult orElse(),
}) { }) {
if (notAssigned != null) { if (notAssigned != null) {
@@ -431,7 +431,7 @@ class _$AnyAssignedIdQueryParameter extends AnyAssignedIdQueryParameter {
required TResult Function() unset, required TResult Function() unset,
required TResult Function() notAssigned, required TResult Function() notAssigned,
required TResult Function() anyAssigned, required TResult Function() anyAssigned,
required TResult Function(@HiveField(0) int? id) fromId, required TResult Function(@HiveField(0) int id) fromId,
}) { }) {
return anyAssigned(); return anyAssigned();
} }
@@ -442,7 +442,7 @@ class _$AnyAssignedIdQueryParameter extends AnyAssignedIdQueryParameter {
TResult? Function()? unset, TResult? Function()? unset,
TResult? Function()? notAssigned, TResult? Function()? notAssigned,
TResult? Function()? anyAssigned, TResult? Function()? anyAssigned,
TResult? Function(@HiveField(0) int? id)? fromId, TResult? Function(@HiveField(0) int id)? fromId,
}) { }) {
return anyAssigned?.call(); return anyAssigned?.call();
} }
@@ -453,7 +453,7 @@ class _$AnyAssignedIdQueryParameter extends AnyAssignedIdQueryParameter {
TResult Function()? unset, TResult Function()? unset,
TResult Function()? notAssigned, TResult Function()? notAssigned,
TResult Function()? anyAssigned, TResult Function()? anyAssigned,
TResult Function(@HiveField(0) int? id)? fromId, TResult Function(@HiveField(0) int id)? fromId,
required TResult orElse(), required TResult orElse(),
}) { }) {
if (anyAssigned != null) { if (anyAssigned != null) {
@@ -521,7 +521,7 @@ abstract class _$$SetIdQueryParameterCopyWith<$Res> {
$Res Function(_$SetIdQueryParameter) then) = $Res Function(_$SetIdQueryParameter) then) =
__$$SetIdQueryParameterCopyWithImpl<$Res>; __$$SetIdQueryParameterCopyWithImpl<$Res>;
@useResult @useResult
$Res call({@HiveField(0) int? id}); $Res call({@HiveField(0) int id});
} }
/// @nodoc /// @nodoc
@@ -535,13 +535,13 @@ class __$$SetIdQueryParameterCopyWithImpl<$Res>
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
Object? id = freezed, Object? id = null,
}) { }) {
return _then(_$SetIdQueryParameter( return _then(_$SetIdQueryParameter(
freezed == id null == id
? _value.id ? _value.id
: id // ignore: cast_nullable_to_non_nullable : id // ignore: cast_nullable_to_non_nullable
as int?, as int,
)); ));
} }
} }
@@ -559,7 +559,7 @@ class _$SetIdQueryParameter extends SetIdQueryParameter {
@override @override
@HiveField(0) @HiveField(0)
final int? id; final int id;
@JsonKey(name: 'runtimeType') @JsonKey(name: 'runtimeType')
final String $type; final String $type;
@@ -594,7 +594,7 @@ class _$SetIdQueryParameter extends SetIdQueryParameter {
required TResult Function() unset, required TResult Function() unset,
required TResult Function() notAssigned, required TResult Function() notAssigned,
required TResult Function() anyAssigned, required TResult Function() anyAssigned,
required TResult Function(@HiveField(0) int? id) fromId, required TResult Function(@HiveField(0) int id) fromId,
}) { }) {
return fromId(id); return fromId(id);
} }
@@ -605,7 +605,7 @@ class _$SetIdQueryParameter extends SetIdQueryParameter {
TResult? Function()? unset, TResult? Function()? unset,
TResult? Function()? notAssigned, TResult? Function()? notAssigned,
TResult? Function()? anyAssigned, TResult? Function()? anyAssigned,
TResult? Function(@HiveField(0) int? id)? fromId, TResult? Function(@HiveField(0) int id)? fromId,
}) { }) {
return fromId?.call(id); return fromId?.call(id);
} }
@@ -616,7 +616,7 @@ class _$SetIdQueryParameter extends SetIdQueryParameter {
TResult Function()? unset, TResult Function()? unset,
TResult Function()? notAssigned, TResult Function()? notAssigned,
TResult Function()? anyAssigned, TResult Function()? anyAssigned,
TResult Function(@HiveField(0) int? id)? fromId, TResult Function(@HiveField(0) int id)? fromId,
required TResult orElse(), required TResult orElse(),
}) { }) {
if (fromId != null) { if (fromId != null) {
@@ -671,7 +671,7 @@ class _$SetIdQueryParameter extends SetIdQueryParameter {
} }
abstract class SetIdQueryParameter extends IdQueryParameter { abstract class SetIdQueryParameter extends IdQueryParameter {
const factory SetIdQueryParameter(@HiveField(0) final int? id) = const factory SetIdQueryParameter(@HiveField(0) final int id) =
_$SetIdQueryParameter; _$SetIdQueryParameter;
const SetIdQueryParameter._() : super._(); const SetIdQueryParameter._() : super._();
@@ -679,7 +679,7 @@ abstract class SetIdQueryParameter extends IdQueryParameter {
_$SetIdQueryParameter.fromJson; _$SetIdQueryParameter.fromJson;
@HiveField(0) @HiveField(0)
int? get id; int get id;
@JsonKey(ignore: true) @JsonKey(ignore: true)
_$$SetIdQueryParameterCopyWith<_$SetIdQueryParameter> get copyWith => _$$SetIdQueryParameterCopyWith<_$SetIdQueryParameter> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;

View File

@@ -7,6 +7,7 @@ import 'query_type.dart';
part 'text_query.g.dart'; part 'text_query.g.dart';
//TODO: Realize with freezed...
@HiveType(typeId: PaperlessApiHiveTypeIds.textQuery) @HiveType(typeId: PaperlessApiHiveTypeIds.textQuery)
@JsonSerializable() @JsonSerializable()
class TextQuery extends Equatable { class TextQuery extends Equatable {

View File

@@ -78,6 +78,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
throw const PaperlessServerException(ErrorCode.documentUpdateFailed); throw const PaperlessServerException(ErrorCode.documentUpdateFailed);
} }
} on DioError catch (err) { } on DioError catch (err) {
//TODO: Handle 403 permission errors for 1.14.0
throw err.error!; throw err.error!;
} }
} }
@@ -86,8 +87,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
Future<PagedSearchResult<DocumentModel>> findAll( Future<PagedSearchResult<DocumentModel>> findAll(
DocumentFilter filter, DocumentFilter filter,
) async { ) async {
final filterParams = filter.toQueryParameters() final filterParams = filter.toQueryParameters()..addAll({'truncate_content': "true"});
..addAll({'truncate_content': "true"});
try { try {
final response = await client.get( final response = await client.get(
"/api/documents/", "/api/documents/",
@@ -137,9 +137,8 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
try { try {
final response = await client.get( final response = await client.get(
getPreviewUrl(documentId), getPreviewUrl(documentId),
options: Options( options:
responseType: ResponseType Options(responseType: ResponseType.bytes), //TODO: Check if bytes or stream is required
.bytes), //TODO: Check if bytes or stream is required
); );
if (response.statusCode == 200) { if (response.statusCode == 200) {
return response.data; return response.data;
@@ -219,8 +218,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
final response = await client.download( final response = await client.download(
"/api/documents/${document.id}/download/", "/api/documents/${document.id}/download/",
localFilePath, localFilePath,
onReceiveProgress: (count, total) => onReceiveProgress: (count, total) => onProgressChanged?.call(count / total),
onProgressChanged?.call(count / total),
queryParameters: {'original': original}, queryParameters: {'original': original},
); );
return response.data; return response.data;
@@ -232,8 +230,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
@override @override
Future<DocumentMetaData> getMetaData(DocumentModel document) async { Future<DocumentMetaData> getMetaData(DocumentModel document) async {
try { try {
final response = final response = await client.get("/api/documents/${document.id}/metadata/");
await client.get("/api/documents/${document.id}/metadata/");
return compute( return compute(
DocumentMetaData.fromJson, DocumentMetaData.fromJson,
response.data as Map<String, dynamic>, response.data as Map<String, dynamic>,
@@ -265,11 +262,9 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
@override @override
Future<FieldSuggestions> findSuggestions(DocumentModel document) async { Future<FieldSuggestions> findSuggestions(DocumentModel document) async {
try { try {
final response = final response = await client.get("/api/documents/${document.id}/suggestions/");
await client.get("/api/documents/${document.id}/suggestions/");
if (response.statusCode == 200) { if (response.statusCode == 200) {
return FieldSuggestions.fromJson(response.data) return FieldSuggestions.fromJson(response.data).forDocumentId(document.id);
.forDocumentId(document.id);
} }
throw const PaperlessServerException(ErrorCode.suggestionsQueryError); throw const PaperlessServerException(ErrorCode.suggestionsQueryError);
} on DioError catch (err) { } on DioError catch (err) {

View File

@@ -1,7 +1,4 @@
import 'dart:convert';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:http/http.dart';
import 'package:paperless_api/src/models/paperless_server_exception.dart'; import 'package:paperless_api/src/models/paperless_server_exception.dart';
import 'package:paperless_api/src/models/paperless_server_information_model.dart'; import 'package:paperless_api/src/models/paperless_server_information_model.dart';
import 'package:paperless_api/src/models/paperless_server_statistics_model.dart'; import 'package:paperless_api/src/models/paperless_server_statistics_model.dart';
@@ -21,20 +18,14 @@ class PaperlessServerStatsApiImpl implements PaperlessServerStatsApi {
@override @override
Future<PaperlessServerInformationModel> getServerInformation() async { Future<PaperlessServerInformationModel> getServerInformation() async {
final response = await client.get("/api/ui_settings/"); final response = await client.get("/api/");
final version = final version =
response.headers[PaperlessServerInformationModel.versionHeader]?.first ?? 'unknown'; response.headers[PaperlessServerInformationModel.versionHeader]?.first ?? 'unknown';
final apiVersion = int.tryParse( final apiVersion = int.tryParse(
response.headers[PaperlessServerInformationModel.apiVersionHeader]?.first ?? '1'); response.headers[PaperlessServerInformationModel.apiVersionHeader]?.first ?? '1');
final String username = response.data['username'];
final String host = response.headers[PaperlessServerInformationModel.hostHeader]?.first ??
response.headers[PaperlessServerInformationModel.hostHeader]?.first ??
('${response.requestOptions.uri.host}:${response.requestOptions.uri.port}');
return PaperlessServerInformationModel( return PaperlessServerInformationModel(
username: username,
version: version, version: version,
apiVersion: apiVersion, apiVersion: apiVersion,
host: host,
); );
} }