diff --git a/lib/features/document_details/cubit/document_details_cubit.dart b/lib/features/document_details/cubit/document_details_cubit.dart index e3e2b65..51c3d52 100644 --- a/lib/features/document_details/cubit/document_details_cubit.dart +++ b/lib/features/document_details/cubit/document_details_cubit.dart @@ -54,12 +54,16 @@ class DocumentDetailsCubit extends Cubit { Future loadSuggestions() async { final suggestions = await _api.findSuggestions(state.document); - emit(state.copyWith(suggestions: suggestions)); + if (!isClosed) { + emit(state.copyWith(suggestions: suggestions)); + } } Future loadMetaData() async { final metaData = await _api.getMetaData(state.document); - emit(state.copyWith(metaData: metaData)); + if (!isClosed) { + emit(state.copyWith(metaData: metaData)); + } } Future loadFullContent() async { @@ -85,8 +89,8 @@ class DocumentDetailsCubit extends Cubit { _notifier.notifyUpdated(updatedDocument); } else { final int autoAsn = await _api.findNextAsn(); - final updatedDocument = await _api - .update(document.copyWith(archiveSerialNumber: () => autoAsn)); + final updatedDocument = + await _api.update(document.copyWith(archiveSerialNumber: () => autoAsn)); _notifier.notifyUpdated(updatedDocument); } } @@ -97,8 +101,7 @@ class DocumentDetailsCubit extends Cubit { if (state.metaData == null) { await loadMetaData(); } - final desc = FileDescription.fromPath( - state.metaData!.mediaFilename.replaceAll("/", " ")); + final desc = FileDescription.fromPath(state.metaData!.mediaFilename.replaceAll("/", " ")); final fileName = "${desc.filename}.pdf"; final file = File("${cacheDir.path}/$fileName"); @@ -132,8 +135,7 @@ class DocumentDetailsCubit extends Cubit { await FileService.downloadsDirectory, ); final desc = FileDescription.fromPath( - state.metaData!.mediaFilename - .replaceAll("/", " "), // Flatten directory structure + state.metaData!.mediaFilename.replaceAll("/", " "), // Flatten directory structure ); if (!File(filePath).existsSync()) { File(filePath).createSync(); @@ -198,8 +200,7 @@ class DocumentDetailsCubit extends Cubit { String _buildDownloadFilePath(bool original, Directory dir) { final description = FileDescription.fromPath( - state.metaData!.mediaFilename - .replaceAll("/", " "), // Flatten directory structure + state.metaData!.mediaFilename.replaceAll("/", " "), // Flatten directory structure ); final extension = original ? description.extension : 'pdf'; return "${dir.path}/${description.filename}.$extension"; diff --git a/lib/features/document_details/view/pages/document_details_page.dart b/lib/features/document_details/view/pages/document_details_page.dart index a8af541..5596b29 100644 --- a/lib/features/document_details/view/pages/document_details_page.dart +++ b/lib/features/document_details/view/pages/document_details_page.dart @@ -60,36 +60,28 @@ class _DocumentDetailsPageState extends State { Widget build(BuildContext context) { return WillPopScope( onWillPop: () async { - Navigator.of(context) - .pop(context.read().state.document); + Navigator.of(context).pop(context.read().state.document); return false; }, child: DefaultTabController( length: 4, child: BlocListener( - listenWhen: (previous, current) => - !previous.isConnected && current.isConnected, + listenWhen: (previous, current) => !previous.isConnected && current.isConnected, listener: (context, state) { _loadMetaData(); setState(() {}); }, child: Scaffold( extendBodyBehindAppBar: false, - floatingActionButtonLocation: - FloatingActionButtonLocation.endDocked, + floatingActionButtonLocation: FloatingActionButtonLocation.endDocked, floatingActionButton: widget.allowEdit ? _buildEditButton() : null, bottomNavigationBar: _buildBottomAppBar(), body: NestedScrollView( headerSliverBuilder: (context, innerBoxIsScrolled) => [ SliverOverlapAbsorber( - handle: - NestedScrollView.sliverOverlapAbsorberHandleFor(context), + handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), sliver: SliverAppBar( - title: Text(context - .watch() - .state - .document - .title), + title: Text(context.watch().state.document.title), leading: const BackButton(), pinned: true, forceElevated: innerBoxIsScrolled, @@ -99,8 +91,7 @@ class _DocumentDetailsPageState extends State { background: Stack( alignment: Alignment.topCenter, children: [ - BlocBuilder( + BlocBuilder( builder: (context, state) => Positioned.fill( child: DocumentPreview( document: state.document, @@ -137,9 +128,7 @@ class _DocumentDetailsPageState extends State { child: Text( S.of(context)!.overview, style: TextStyle( - color: Theme.of(context) - .colorScheme - .onPrimaryContainer, + color: Theme.of(context).colorScheme.onPrimaryContainer, ), ), ), @@ -147,9 +136,7 @@ class _DocumentDetailsPageState extends State { child: Text( S.of(context)!.content, style: TextStyle( - color: Theme.of(context) - .colorScheme - .onPrimaryContainer, + color: Theme.of(context).colorScheme.onPrimaryContainer, ), ), ), @@ -157,9 +144,7 @@ class _DocumentDetailsPageState extends State { child: Text( S.of(context)!.metaData, style: TextStyle( - color: Theme.of(context) - .colorScheme - .onPrimaryContainer, + color: Theme.of(context).colorScheme.onPrimaryContainer, ), ), ), @@ -167,9 +152,7 @@ class _DocumentDetailsPageState extends State { child: Text( S.of(context)!.similarDocuments, style: TextStyle( - color: Theme.of(context) - .colorScheme - .onPrimaryContainer, + color: Theme.of(context).colorScheme.onPrimaryContainer, ), ), ), @@ -198,8 +181,7 @@ class _DocumentDetailsPageState extends State { CustomScrollView( slivers: [ SliverOverlapInjector( - handle: NestedScrollView - .sliverOverlapAbsorberHandleFor(context), + handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), ), DocumentOverviewWidget( document: state.document, @@ -215,8 +197,7 @@ class _DocumentDetailsPageState extends State { CustomScrollView( slivers: [ SliverOverlapInjector( - handle: NestedScrollView - .sliverOverlapAbsorberHandleFor(context), + handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), ), DocumentContentWidget( isFullContentLoaded: state.isFullContentLoaded, @@ -229,8 +210,7 @@ class _DocumentDetailsPageState extends State { CustomScrollView( slivers: [ SliverOverlapInjector( - handle: NestedScrollView - .sliverOverlapAbsorberHandleFor(context), + handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), ), DocumentMetaDataWidget( document: state.document, @@ -242,8 +222,7 @@ class _DocumentDetailsPageState extends State { controller: _pagingScrollController, slivers: [ SliverOverlapInjector( - handle: NestedScrollView - .sliverOverlapAbsorberHandleFor(context), + handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), ), SimilarDocumentsView( pagingScrollController: _pagingScrollController, @@ -301,9 +280,8 @@ class _DocumentDetailsPageState extends State { IconButton( tooltip: S.of(context)!.deleteDocumentTooltip, icon: const Icon(Icons.delete), - onPressed: widget.allowEdit && isConnected - ? () => _onDelete(state.document) - : null, + onPressed: + widget.allowEdit && isConnected ? () => _onDelete(state.document) : null, ).paddedSymmetrically(horizontal: 4), DocumentDownloadButton( document: state.document, @@ -313,8 +291,7 @@ class _DocumentDetailsPageState extends State { IconButton( tooltip: S.of(context)!.previewTooltip, icon: const Icon(Icons.visibility), - onPressed: - isConnected ? () => _onOpen(state.document) : null, + onPressed: isConnected ? () => _onOpen(state.document) : null, ).paddedOnly(right: 4.0), IconButton( tooltip: S.of(context)!.openInSystemViewer, @@ -352,8 +329,7 @@ class _DocumentDetailsPageState extends State { ), ], child: BlocListener( - listenWhen: (previous, current) => - previous.document != current.document, + listenWhen: (previous, current) => previous.document != current.document, listener: (context, state) { cubit.replace(state.document); }, @@ -373,8 +349,7 @@ class _DocumentDetailsPageState extends State { } void _onOpenFileInSystemViewer() async { - final status = - await context.read().openDocumentInSystemViewer(); + final status = await context.read().openDocumentInSystemViewer(); if (status == ResultType.done) return; if (status == ResultType.noAppToOpen) { showGenericError(context, S.of(context)!.noAppToDisplayPDFFilesFound); @@ -383,16 +358,14 @@ class _DocumentDetailsPageState extends State { showGenericError(context, translateError(context, ErrorCode.unknown)); } if (status == ResultType.permissionDenied) { - showGenericError( - context, S.of(context)!.couldNotOpenFilePermissionDenied); + showGenericError(context, S.of(context)!.couldNotOpenFilePermissionDenied); } } void _onDelete(DocumentModel document) async { final delete = await showDialog( context: context, - builder: (context) => - DeleteDocumentConfirmationDialog(document: document), + builder: (context) => DeleteDocumentConfirmationDialog(document: document), ) ?? false; if (delete) { @@ -412,8 +385,7 @@ class _DocumentDetailsPageState extends State { Navigator.of(context).push( MaterialPageRoute( builder: (context) => DocumentView( - documentBytes: - context.read().getPreview(document.id), + documentBytes: context.read().getPreview(document.id), ), ), ); diff --git a/lib/features/document_edit/view/document_edit_page.dart b/lib/features/document_edit/view/document_edit_page.dart index 03b3208..99362f8 100644 --- a/lib/features/document_edit/view/document_edit_page.dart +++ b/lib/features/document_edit/view/document_edit_page.dart @@ -190,7 +190,7 @@ class _DocumentEditPageState extends State { allowCreation: true, allowExclude: false, initialValue: TagsQuery.ids( - include: state.document.tags, + include: state.document.tags.toList(), ), ).padded(), if (_filteredSuggestions?.tags diff --git a/lib/features/document_search/cubit/document_search_cubit.dart b/lib/features/document_search/cubit/document_search_cubit.dart index 355e453..4cd5717 100644 --- a/lib/features/document_search/cubit/document_search_cubit.dart +++ b/lib/features/document_search/cubit/document_search_cubit.dart @@ -1,27 +1,32 @@ import 'package:collection/collection.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; +import 'package:json_annotation/json_annotation.dart'; import 'package:paperless_api/paperless_api.dart'; +import 'package:paperless_mobile/core/database/tables/user_app_state.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart'; -import 'package:paperless_mobile/core/database/tables/user_account.dart'; import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart'; -import 'package:json_annotation/json_annotation.dart'; import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart'; -import 'package:paperless_mobile/core/database/tables/user_settings.dart'; import 'package:paperless_mobile/features/settings/model/view_type.dart'; -part 'document_search_state.dart'; part 'document_search_cubit.g.dart'; +part 'document_search_state.dart'; -class DocumentSearchCubit extends HydratedCubit with DocumentPagingBlocMixin { +class DocumentSearchCubit extends Cubit with DocumentPagingBlocMixin { @override final PaperlessDocumentsApi api; final LabelRepository _labelRepository; @override final DocumentChangedNotifier notifier; - DocumentSearchCubit(this.api, this.notifier, this._labelRepository) - : super(const DocumentSearchState()) { + + final UserAppState _userAppState; + DocumentSearchCubit( + this.api, + this.notifier, + this._labelRepository, + this._userAppState, + ) : super(DocumentSearchState(searchHistory: _userAppState.documentSearchHistory)) { _labelRepository.addListener( this, onChanged: (labels) { @@ -61,6 +66,9 @@ class DocumentSearchCubit extends HydratedCubit with Docume ], ), ); + _userAppState + ..documentSearchHistory = state.searchHistory + ..save(); } void updateViewType(ViewType viewType) { @@ -73,6 +81,9 @@ class DocumentSearchCubit extends HydratedCubit with Docume searchHistory: state.searchHistory.whereNot((element) => element == entry).toList(), ), ); + _userAppState + ..documentSearchHistory = state.searchHistory + ..save(); } Future suggest(String query) async { @@ -92,11 +103,13 @@ class DocumentSearchCubit extends HydratedCubit with Docume } void reset() { - emit(state.copyWith( - view: SearchView.suggestions, - suggestions: [], - isLoading: false, - )); + emit( + state.copyWith( + view: SearchView.suggestions, + suggestions: [], + isLoading: false, + ), + ); } @override @@ -106,16 +119,6 @@ class DocumentSearchCubit extends HydratedCubit with Docume return super.close(); } - @override - DocumentSearchState? fromJson(Map json) { - return DocumentSearchState.fromJson(json); - } - - @override - Map? toJson(DocumentSearchState state) { - return state.toJson(); - } - @override Future onFilterUpdated(DocumentFilter filter) async {} } diff --git a/lib/features/document_search/view/document_search_page.dart b/lib/features/document_search/view/document_search_page.dart index 292548c..02570f4 100644 --- a/lib/features/document_search/view/document_search_page.dart +++ b/lib/features/document_search/view/document_search_page.dart @@ -3,6 +3,10 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:hive/hive.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/user_app_state.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/view/remove_history_entry_dialog.dart'; @@ -14,6 +18,8 @@ import 'package:paperless_mobile/routes/document_details_route.dart'; import 'dart:math' as math; Future showDocumentSearchPage(BuildContext context) { + final currentUser = + Hive.box(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser; return Navigator.of(context).push( MaterialPageRoute( builder: (context) => BlocProvider( @@ -21,6 +27,7 @@ Future showDocumentSearchPage(BuildContext context) { context.read(), context.read(), context.read(), + Hive.box(HiveBoxes.userAppState).get(currentUser)!, ), child: const DocumentSearchPage(), ), @@ -111,9 +118,8 @@ class _DocumentSearchPageState extends State { } Widget _buildSuggestionsView(DocumentSearchState state) { - final suggestions = state.suggestions - .whereNot((element) => state.searchHistory.contains(element)) - .toList(); + final suggestions = + state.suggestions.whereNot((element) => state.searchHistory.contains(element)).toList(); final historyMatches = state.searchHistory .where( (element) => element.startsWith(query), @@ -195,8 +201,7 @@ class _DocumentSearchPageState extends State { builder: (context, state) { return ViewTypeSelectionWidget( viewType: state.viewType, - onChanged: (type) => - context.read().updateViewType(type), + onChanged: (type) => context.read().updateViewType(type), ); }, ) diff --git a/lib/features/document_search/view/sliver_search_bar.dart b/lib/features/document_search/view/sliver_search_bar.dart index cd7d3cd..1882999 100644 --- a/lib/features/document_search/view/sliver_search_bar.dart +++ b/lib/features/document_search/view/sliver_search_bar.dart @@ -2,13 +2,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hive_flutter/adapters.dart'; import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart'; -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/database/tables/user_account.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/features/document_search/view/document_search_page.dart'; -import 'package:paperless_mobile/core/database/tables/user_account.dart'; -import 'package:paperless_mobile/features/settings/view/dialogs/account_settings_dialog.dart'; import 'package:paperless_mobile/features/settings/view/manage_accounts_page.dart'; import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; import 'package:paperless_mobile/features/settings/view/widgets/user_avatar.dart'; diff --git a/lib/features/documents/cubit/documents_cubit.dart b/lib/features/documents/cubit/documents_cubit.dart index 2d9b558..5326f12 100644 --- a/lib/features/documents/cubit/documents_cubit.dart +++ b/lib/features/documents/cubit/documents_cubit.dart @@ -15,7 +15,7 @@ import 'package:paperless_mobile/features/settings/model/view_type.dart'; part 'documents_cubit.g.dart'; part 'documents_state.dart'; -class DocumentsCubit extends HydratedCubit with DocumentPagingBlocMixin { +class DocumentsCubit extends Cubit with DocumentPagingBlocMixin { @override final PaperlessDocumentsApi api; diff --git a/lib/features/documents/view/pages/documents_page.dart b/lib/features/documents/view/pages/documents_page.dart index f463465..dd0bf80 100644 --- a/lib/features/documents/view/pages/documents_page.dart +++ b/lib/features/documents/view/pages/documents_page.dart @@ -461,8 +461,8 @@ class _DocumentsPageState extends State with SingleTickerProvider context.read().updateCurrentFilter( (filter) => filter.copyWith( tags: tagsQuery.copyWith( - include: tagsQuery.include.whereNot((id) => id == tagId), - exclude: tagsQuery.exclude.whereNot((id) => id == tagId)), + include: tagsQuery.include.whereNot((id) => id == tagId).toList(), + exclude: tagsQuery.exclude.whereNot((id) => id == tagId).toList()), ), ); } else { diff --git a/lib/features/home/view/home_page.dart b/lib/features/home/view/home_page.dart index 77b00ca..5eee767 100644 --- a/lib/features/home/view/home_page.dart +++ b/lib/features/home/view/home_page.dart @@ -11,6 +11,7 @@ import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart'; import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart'; +import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/database/tables/user_app_state.dart'; import 'package:paperless_mobile/core/global/constants.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart'; @@ -40,6 +41,8 @@ import 'package:paperless_mobile/helpers/message_helpers.dart'; import 'package:receive_sharing_intent/receive_sharing_intent.dart'; import 'package:responsive_builder/responsive_builder.dart'; +/// Wrapper around all functionality for a logged in user. +/// Performs initialization logic. class HomePage extends StatefulWidget { const HomePage({Key? key}) : super(key: key); @@ -50,14 +53,37 @@ class HomePage extends StatefulWidget { class _HomePageState extends State with WidgetsBindingObserver { int _currentIndex = 0; final DocumentScannerCubit _scannerCubit = DocumentScannerCubit(); + late final DocumentsCubit _documentsCubit; late final InboxCubit _inboxCubit; + late final SavedViewCubit _savedViewCubit; late Timer _inboxTimer; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); + + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + _listenForReceivedFiles(); + }); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); _initializeData(context); + final userId = + Hive.box(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser; + _documentsCubit = DocumentsCubit( + context.read(), + context.read(), + context.read(), + Hive.box(HiveBoxes.userAppState).get(userId)!, + )..reload(); + _savedViewCubit = SavedViewCubit( + context.read(), + context.read(), + )..reload(); _inboxCubit = InboxCubit( context.read(), context.read(), @@ -65,14 +91,9 @@ class _HomePageState extends State with WidgetsBindingObserver { context.read(), ); _listenToInboxChanges(); - - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - _listenForReceivedFiles(); - }); } void _listenToInboxChanges() { - _inboxCubit.refreshItemsInInboxCount(); _inboxTimer = Timer.periodic(const Duration(seconds: 10), (timer) { if (!mounted) { timer.cancel(); @@ -108,6 +129,8 @@ class _HomePageState extends State with WidgetsBindingObserver { WidgetsBinding.instance.removeObserver(this); _inboxTimer.cancel(); _inboxCubit.close(); + _documentsCubit.close(); + _savedViewCubit.close(); super.dispose(); } @@ -190,7 +213,8 @@ class _HomePageState extends State with WidgetsBindingObserver { @override Widget build(BuildContext context) { - final userId = context.watch().state.userId; + final userId = + Hive.box(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser!; final destinations = [ RouteDescription( icon: const Icon(Icons.description_outlined), @@ -239,22 +263,10 @@ class _HomePageState extends State with WidgetsBindingObserver { ]; final routes = [ MultiBlocProvider( - key: ValueKey(userId), + // key: ValueKey(userId), providers: [ - BlocProvider( - create: (context) => DocumentsCubit( - context.read(), - context.read(), - context.read(), - Hive.box(HiveBoxes.userAppState).get(userId)!, - )..reload(), - ), - BlocProvider( - create: (context) => SavedViewCubit( - context.read(), - context.read(), - )..reload(), - ), + BlocProvider.value(value: _documentsCubit), + BlocProvider.value(value: _savedViewCubit), ], child: const DocumentsPage(), ), @@ -263,7 +275,7 @@ class _HomePageState extends State with WidgetsBindingObserver { child: const ScannerPage(), ), MultiBlocProvider( - key: ValueKey(userId), + // key: ValueKey(userId), providers: [ BlocProvider( create: (context) => LabelCubit(context.read()), diff --git a/lib/features/inbox/cubit/inbox_cubit.dart b/lib/features/inbox/cubit/inbox_cubit.dart index aadad67..3ed0188 100644 --- a/lib/features/inbox/cubit/inbox_cubit.dart +++ b/lib/features/inbox/cubit/inbox_cubit.dart @@ -97,7 +97,7 @@ class InboxCubit extends HydratedCubit with DocumentPagingBlocMixin updateFilter( filter: DocumentFilter( sortField: SortField.added, - tags: TagsQuery.ids(include: inboxTags), + tags: TagsQuery.ids(include: inboxTags.toList()), ), ); } @@ -127,7 +127,7 @@ class InboxCubit extends HydratedCubit with DocumentPagingBlocMixin updateFilter( filter: DocumentFilter( sortField: SortField.added, - tags: TagsQuery.ids(include: inboxTags), + tags: TagsQuery.ids(include: inboxTags.toList()), ), ); } diff --git a/lib/features/labels/tags/view/widgets/tags_form_field.dart b/lib/features/labels/tags/view/widgets/tags_form_field.dart index 31cd29e..e4a2f62 100644 --- a/lib/features/labels/tags/view/widgets/tags_form_field.dart +++ b/lib/features/labels/tags/view/widgets/tags_form_field.dart @@ -121,15 +121,15 @@ class TagsFormField extends StatelessWidget { final tag = options[id]!; return QueryTagChip( onDeleted: () => field.didChange(formValue.copyWith( - include: formValue.include.whereNot((element) => element == id), - exclude: formValue.exclude.whereNot((element) => element == id), + include: formValue.include.whereNot((element) => element == id).toList(), + exclude: formValue.exclude.whereNot((element) => element == id).toList(), )), onSelected: allowExclude ? () { if (formValue.include.contains(id)) { field.didChange( formValue.copyWith( - include: formValue.include.whereNot((element) => element == id), + include: formValue.include.whereNot((element) => element == id).toList(), exclude: [...formValue.exclude, id], ), ); @@ -137,7 +137,7 @@ class TagsFormField extends StatelessWidget { field.didChange( formValue.copyWith( include: [...formValue.include, id], - exclude: formValue.exclude.whereNot((element) => element == id), + exclude: formValue.exclude.whereNot((element) => element == id).toList(), ), ); } @@ -171,7 +171,7 @@ class TagsFormField extends StatelessWidget { return QueryTagChip( onDeleted: () { final updatedQuery = query.copyWith( - tagIds: query.tagIds.whereNot((element) => element == e), + tagIds: query.tagIds.whereNot((element) => element == e).toList(), ); if (updatedQuery.tagIds.isEmpty) { field.didChange(const TagsQuery.ids()); diff --git a/lib/features/labels/view/widgets/label_item.dart b/lib/features/labels/view/widgets/label_item.dart index 860aa89..333a617 100644 --- a/lib/features/labels/view/widgets/label_item.dart +++ b/lib/features/labels/view/widgets/label_item.dart @@ -59,7 +59,6 @@ class LabelItem extends StatelessWidget { context.read(), context.read(), context.read(), - Hive.box(HiveBoxes.userAccount).get(currentUser)!, ), child: const LinkedDocumentsPage(), ), diff --git a/lib/features/linked_documents/cubit/linked_documents_cubit.dart b/lib/features/linked_documents/cubit/linked_documents_cubit.dart index 50116f1..39e3c0d 100644 --- a/lib/features/linked_documents/cubit/linked_documents_cubit.dart +++ b/lib/features/linked_documents/cubit/linked_documents_cubit.dart @@ -3,7 +3,6 @@ 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/core/database/tables/user_account.dart'; import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart'; import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart'; import 'package:paperless_mobile/features/settings/model/view_type.dart'; @@ -21,15 +20,11 @@ class LinkedDocumentsCubit extends HydratedCubit final LabelRepository _labelRepository; - @override - // TODO: implement account - final UserAccount account; LinkedDocumentsCubit( DocumentFilter filter, this.api, this.notifier, this._labelRepository, - this.account, ) : super(LinkedDocumentsState(filter: filter)) { updateFilter(filter: filter); _labelRepository.addListener( diff --git a/lib/features/login/cubit/authentication_cubit.dart b/lib/features/login/cubit/authentication_cubit.dart index caa4fba..e32e43b 100644 --- a/lib/features/login/cubit/authentication_cubit.dart +++ b/lib/features/login/cubit/authentication_cubit.dart @@ -143,8 +143,9 @@ class AuthenticationCubit extends Cubit { debugPrint("Invalid authentication for $userId"); return; } - final credentials = credentialsBox.get(userId); + await credentialsBox.close(); + await _resetExternalState(); _dioWrapper.updateSettings( @@ -154,9 +155,10 @@ class AuthenticationCubit extends Cubit { baseUrl: account.serverUrl, ); + await _reloadRepositories(); globalSettings.currentLoggedInUser = userId; await globalSettings.save(); - await _reloadRepositories(); + emit( AuthenticationState( isAuthenticated: true, diff --git a/lib/features/login/view/widgets/login_pages/server_connection_page.dart b/lib/features/login/view/widgets/login_pages/server_connection_page.dart index d289eab..550ed90 100644 --- a/lib/features/login/view/widgets/login_pages/server_connection_page.dart +++ b/lib/features/login/view/widgets/login_pages/server_connection_page.dart @@ -65,7 +65,7 @@ class _ServerConnectionPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ TextButton( - child: Text("Test connection"), //TODO: INTL + child: Text(S.of(context)!.testConnection), onPressed: _updateReachability, ), FilledButton( diff --git a/lib/features/settings/view/dialogs/account_settings_dialog.dart b/lib/features/settings/view/dialogs/account_settings_dialog.dart deleted file mode 100644 index 8cfdb1d..0000000 --- a/lib/features/settings/view/dialogs/account_settings_dialog.dart +++ /dev/null @@ -1,114 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:hive_flutter/adapters.dart'; -import 'package:hydrated_bloc/hydrated_bloc.dart'; -import 'package:paperless_api/paperless_api.dart'; -import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart'; -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/widgets/hint_card.dart'; -import 'package:paperless_mobile/extensions/flutter_extensions.dart'; -import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart'; -import 'package:paperless_mobile/core/database/tables/user_account.dart'; -import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; -import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; -import 'package:paperless_mobile/helpers/message_helpers.dart'; - -class AccountSettingsDialog extends StatelessWidget { - const AccountSettingsDialog({super.key}); - - @override - Widget build(BuildContext context) { - return GlobalSettingsBuilder(builder: (context, globalSettings) { - return AlertDialog( - insetPadding: EdgeInsets.symmetric(horizontal: 24, vertical: 32), - scrollable: true, - contentPadding: EdgeInsets.zero, - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(S.of(context)!.account), - const CloseButton(), - ], - ), - content: BlocBuilder( - builder: (context, state) { - return Column( - children: [ - ValueListenableBuilder( - valueListenable: Hive.box(HiveBoxes.userAccount).listenable(), - builder: (context, box, _) { - // final currentUser = globalSettings.currentLoggedInUser; - final currentUser = null; - final accountIds = - box.keys.whereNot((element) => element == currentUser).toList(); - final accounts = accountIds.map((id) => box.get(id)!).toList(); - return ExpansionTile( - leading: CircleAvatar( - child: Text(state.information?.userInitials ?? ''), - ), - title: Text(state.information?.username ?? ''), - subtitle: Text(state.information?.host ?? ''), - children: - accounts.map((account) => _buildAccountTile(account, true)).toList(), - ); - }, - ), - ListTile( - dense: true, - leading: const Icon(Icons.person_add_rounded), - title: Text(S.of(context)!.addAnotherAccount), - onTap: () {}, - ), - const Divider(), - FilledButton( - style: ButtonStyle( - backgroundColor: MaterialStatePropertyAll( - Theme.of(context).colorScheme.error, - ), - ), - child: Text( - S.of(context)!.disconnect, - style: TextStyle( - color: Theme.of(context).colorScheme.onError, - ), - ), - onPressed: () async { - await _onLogout(context); - Navigator.of(context).maybePop(); - }, - ).padded(16), - ], - ); - }, - ), - ); - }); - } - - Future _onLogout(BuildContext context) async { - try { - await context.read().logout(); - await HydratedBloc.storage.clear(); - } on PaperlessServerException catch (error, stackTrace) { - showErrorMessage(context, error, stackTrace); - } - } - - Widget _buildAccountTile(UserAccount account, bool isActive) { - return ListTile( - selected: isActive, - title: Text(account.username), - subtitle: Text(account.serverUrl), - leading: CircleAvatar( - child: Text((account.fullName ?? account.username) - .split(" ") - .take(2) - .map((e) => e.substring(0, 1)) - .map((e) => e.toUpperCase()) - .join(" ")), - ), - ); - } -} diff --git a/lib/features/settings/view/dialogs/switch_account_dialog.dart b/lib/features/settings/view/dialogs/switch_account_dialog.dart index 7cc3ab6..b3e7add 100644 --- a/lib/features/settings/view/dialogs/switch_account_dialog.dart +++ b/lib/features/settings/view/dialogs/switch_account_dialog.dart @@ -4,25 +4,19 @@ import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_confirm_button import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; class SwitchAccountDialog extends StatelessWidget { - final String username; - final String serverUrl; - const SwitchAccountDialog({ - super.key, - required this.username, - required this.serverUrl, - }); + const SwitchAccountDialog({super.key}); @override Widget build(BuildContext context) { return AlertDialog( - title: Text("Switch account"), - content: Text("Do you want to switch to $serverUrl and log in as $username?"), + title: Text(S.of(context)!.switchAccountTitle), + content: Text(S.of(context)!.switchToNewAccount), actions: [ + const DialogCancelButton(), DialogConfirmButton( - style: DialogConfirmButtonStyle.danger, - label: S.of(context)!.continueLabel, //TODO: INTL change labels + style: DialogConfirmButtonStyle.normal, + label: S.of(context)!.switchAccount, ), - DialogCancelButton(), ], ); } diff --git a/lib/features/settings/view/manage_accounts_page.dart b/lib/features/settings/view/manage_accounts_page.dart index 2f5df23..282de66 100644 --- a/lib/features/settings/view/manage_accounts_page.dart +++ b/lib/features/settings/view/manage_accounts_page.dart @@ -42,7 +42,7 @@ class ManageAccountsPage extends StatelessWidget { alignment: Alignment.centerLeft, child: CloseButton(), ), - Center(child: Text("Accounts")), + Center(child: Text(S.of(context)!.accounts)), ], ), //TODO: INTL shape: RoundedRectangleBorder( @@ -65,7 +65,7 @@ class ManageAccountsPage extends StatelessWidget { ), const Divider(), ListTile( - title: const Text("Add account"), + title: Text(S.of(context)!.addAccount), leading: const Icon(Icons.person_add), onTap: () { _onAddAccount(context); @@ -113,17 +113,17 @@ class ManageAccountsPage extends StatelessWidget { itemBuilder: (context) { return [ if (!isLoggedIn) - const PopupMenuItem( + PopupMenuItem( child: ListTile( - title: Text("Switch"), //TODO: INTL + title: Text(S.of(context)!.switchAccount), leading: Icon(Icons.switch_account_rounded), ), value: 0, ), if (!isLoggedIn) - const PopupMenuItem( + PopupMenuItem( child: ListTile( - title: Text("Remove"), // TODO: INTL + title: Text(S.of(context)!.remove), leading: Icon( Icons.person_remove, color: Colors.red, @@ -132,9 +132,9 @@ class ManageAccountsPage extends StatelessWidget { value: 1, ) else - const PopupMenuItem( + PopupMenuItem( child: ListTile( - title: Text("Logout"), // TODO: INTL + title: Text(S.of(context)!.logout), leading: Icon( Icons.person_remove, color: Colors.red, @@ -177,12 +177,12 @@ class ManageAccountsPage extends StatelessWidget { return child; } - Future _onAddAccount(BuildContext context) { - return Navigator.push( + Future _onAddAccount(BuildContext context) async { + final userId = await Navigator.push( context, MaterialPageRoute( builder: (context) => LoginPage( - titleString: "Add account", //TODO: INTL + titleString: S.of(context)!.addAccount, onSubmit: (context, username, password, serverUrl, clientCertificate) async { final userId = await context.read().addAccount( credentials: LoginFormCredentials( @@ -194,19 +194,22 @@ class ManageAccountsPage extends StatelessWidget { //TODO: Ask user whether to enable biometric authentication enableBiometricAuthentication: false, ); - final shoudSwitch = await showDialog( - context: context, - builder: (context) => - SwitchAccountDialog(username: username, serverUrl: serverUrl), - ) ?? - false; - if (shoudSwitch) { - context.read().switchAccount(userId); - } + Navigator.of(context).pop(userId); }, - submitText: "Add account", //TODO: INTL + submitText: S.of(context)!.addAccount, ), ), ); + if (userId != null) { + final shoudSwitch = await showDialog( + context: context, + builder: (context) => const SwitchAccountDialog(), + ) ?? + false; + if (shoudSwitch) { + await context.read().switchAccount(userId); + Navigator.pop(context); + } + } } } diff --git a/lib/features/settings/view/pages/application_settings_page.dart b/lib/features/settings/view/pages/application_settings_page.dart index 7399e9c..1cc2220 100644 --- a/lib/features/settings/view/pages/application_settings_page.dart +++ b/lib/features/settings/view/pages/application_settings_page.dart @@ -15,12 +15,8 @@ class ApplicationSettingsPage extends StatelessWidget { actions: [ Padding( padding: const EdgeInsets.all(16.0), - child: Tooltip( - triggerMode: TooltipTriggerMode.tap, - message: "These settings apply to all accounts", //TODO: INTL - child: Icon(Icons.info_outline), - ), - ), + child: const Icon(Icons.public), + ) ], ), body: ListView( diff --git a/lib/features/settings/view/pages/security_settings_page.dart b/lib/features/settings/view/pages/security_settings_page.dart index c812e54..da28480 100644 --- a/lib/features/settings/view/pages/security_settings_page.dart +++ b/lib/features/settings/view/pages/security_settings_page.dart @@ -13,12 +13,8 @@ class SecuritySettingsPage extends StatelessWidget { actions: [ Padding( padding: const EdgeInsets.all(16.0), - child: Tooltip( - triggerMode: TooltipTriggerMode.tap, - message: "These settings apply to the current user only", //TODO: INTL - child: Icon(Icons.info_outline), - ), - ), + child: const Icon(Icons.person_outline), + ) ], ), body: ListView( diff --git a/lib/features/settings/view/pages/switching_accounts_page.dart b/lib/features/settings/view/pages/switching_accounts_page.dart index 76f7801..8bfd21d 100644 --- a/lib/features/settings/view/pages/switching_accounts_page.dart +++ b/lib/features/settings/view/pages/switching_accounts_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; class SwitchingAccountsPage extends StatelessWidget { const SwitchingAccountsPage({super.key}); @@ -6,17 +7,19 @@ class SwitchingAccountsPage extends StatelessWidget { @override Widget build(BuildContext context) { return WillPopScope( - onWillPop: () async { - return false; - }, + onWillPop: () async => false, child: Material( child: Center( child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - CircularProgressIndicator(), - Text("Switching accounts. Please wait..."), + const CircularProgressIndicator(), + const SizedBox(height: 16), + Text( + S.of(context)!.switchingAccountsPleaseWait, + style: Theme.of(context).textTheme.labelLarge, + ), ], ), ), diff --git a/lib/features/settings/view/widgets/biometric_authentication_setting.dart b/lib/features/settings/view/widgets/biometric_authentication_setting.dart index 139e49d..f600b0f 100644 --- a/lib/features/settings/view/widgets/biometric_authentication_setting.dart +++ b/lib/features/settings/view/widgets/biometric_authentication_setting.dart @@ -1,10 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:hive_flutter/adapters.dart'; -import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/features/login/services/authentication_service.dart'; -import 'package:paperless_mobile/core/database/tables/global_settings.dart'; -import 'package:paperless_mobile/core/database/tables/user_settings.dart'; import 'package:paperless_mobile/features/settings/view/widgets/user_settings_builder.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:provider/provider.dart'; diff --git a/lib/l10n/intl_cs.arb b/lib/l10n/intl_cs.arb index e0aaae0..a3233cc 100644 --- a/lib/l10n/intl_cs.arb +++ b/lib/l10n/intl_cs.arb @@ -729,5 +729,37 @@ "allTags": "All", "@allTags": { "description": "Label shown when a document has to be assigned to all selected tags" + }, + "switchingAccountsPleaseWait": "Switching accounts. Please wait...", + "@switchingAccountsPleaseWait": { + "description": "Message shown while switching accounts is in progress." + }, + "testConnection": "Test connection", + "@testConnection": { + "description": "Button label shown on login page. Allows user to test whether the server is reachable or not." + }, + "accounts": "Accounts", + "@accounts": { + "description": "Title of the account management dialog" + }, + "addAccount": "Add account", + "@addAccount": { + "description": "Label of add account action" + }, + "switchAccount": "Switch", + "@switchAccount": { + "description": "Label for switch account action" + }, + "logout": "Logout", + "@logout": { + "description": "Generic Logout label" + }, + "switchAccountTitle": "Switch account", + "@switchAccountTitle": { + "description": "Title of the dialog shown after adding an account, asking the user whether to switch to the newly added account or not." + }, + "switchToNewAccount": "Do you want to switch to the new account? You can switch back at any time.", + "@switchToNewAccount": { + "description": "Content of the dialog shown after adding an account, asking the user whether to switch to the newly added account or not." } } \ No newline at end of file diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index b9c3f43..5cfa63a 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -729,5 +729,37 @@ "allTags": "Alle", "@allTags": { "description": "Label shown when a document has to be assigned to all selected tags" + }, + "switchingAccountsPleaseWait": "Kontowechsel, bitte warten...", + "@switchingAccountsPleaseWait": { + "description": "Message shown while switching accounts is in progress." + }, + "testConnection": "Verbindung testen", + "@testConnection": { + "description": "Button label shown on login page. Allows user to test whether the server is reachable or not." + }, + "accounts": "Accounts", + "@accounts": { + "description": "Title of the account management dialog" + }, + "addAccount": "Account hinzufügen", + "@addAccount": { + "description": "Label of add account action" + }, + "switchAccount": "Wechseln", + "@switchAccount": { + "description": "Label for switch account action" + }, + "logout": "Ausloggen", + "@logout": { + "description": "Generic Logout label" + }, + "switchAccountTitle": "Account wechseln", + "@switchAccountTitle": { + "description": "Title of the dialog shown after adding an account, asking the user whether to switch to the newly added account or not." + }, + "switchToNewAccount": "Möchtest du zum neuen Account wechseln? Du kannst jederzeit zurückwechseln.", + "@switchToNewAccount": { + "description": "Content of the dialog shown after adding an account, asking the user whether to switch to the newly added account or not." } } \ No newline at end of file diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 2ccb0b5..110c5d0 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -729,5 +729,37 @@ "allTags": "All", "@allTags": { "description": "Label shown when a document has to be assigned to all selected tags" + }, + "switchingAccountsPleaseWait": "Switching accounts. Please wait...", + "@switchingAccountsPleaseWait": { + "description": "Message shown while switching accounts is in progress." + }, + "testConnection": "Test connection", + "@testConnection": { + "description": "Button label shown on login page. Allows user to test whether the server is reachable or not." + }, + "accounts": "Accounts", + "@accounts": { + "description": "Title of the account management dialog" + }, + "addAccount": "Add account", + "@addAccount": { + "description": "Label of add account action" + }, + "switchAccount": "Switch", + "@switchAccount": { + "description": "Label for switch account action" + }, + "logout": "Logout", + "@logout": { + "description": "Generic Logout label" + }, + "switchAccountTitle": "Switch account", + "@switchAccountTitle": { + "description": "Title of the dialog shown after adding an account, asking the user whether to switch to the newly added account or not." + }, + "switchToNewAccount": "Do you want to switch to the new account? You can switch back at any time.", + "@switchToNewAccount": { + "description": "Content of the dialog shown after adding an account, asking the user whether to switch to the newly added account or not." } } \ No newline at end of file diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index d7ee77a..745b1ab 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -729,5 +729,37 @@ "allTags": "All", "@allTags": { "description": "Label shown when a document has to be assigned to all selected tags" + }, + "switchingAccountsPleaseWait": "Switching accounts. Please wait...", + "@switchingAccountsPleaseWait": { + "description": "Message shown while switching accounts is in progress." + }, + "testConnection": "Test connection", + "@testConnection": { + "description": "Button label shown on login page. Allows user to test whether the server is reachable or not." + }, + "accounts": "Accounts", + "@accounts": { + "description": "Title of the account management dialog" + }, + "addAccount": "Add account", + "@addAccount": { + "description": "Label of add account action" + }, + "switchAccount": "Switch", + "@switchAccount": { + "description": "Label for switch account action" + }, + "logout": "Logout", + "@logout": { + "description": "Generic Logout label" + }, + "switchAccountTitle": "Switch account", + "@switchAccountTitle": { + "description": "Title of the dialog shown after adding an account, asking the user whether to switch to the newly added account or not." + }, + "switchToNewAccount": "Do you want to switch to the new account? You can switch back at any time.", + "@switchToNewAccount": { + "description": "Content of the dialog shown after adding an account, asking the user whether to switch to the newly added account or not." } } \ No newline at end of file diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index f36f991..d715ccb 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -729,5 +729,37 @@ "allTags": "All", "@allTags": { "description": "Label shown when a document has to be assigned to all selected tags" + }, + "switchingAccountsPleaseWait": "Switching accounts. Please wait...", + "@switchingAccountsPleaseWait": { + "description": "Message shown while switching accounts is in progress." + }, + "testConnection": "Test connection", + "@testConnection": { + "description": "Button label shown on login page. Allows user to test whether the server is reachable or not." + }, + "accounts": "Accounts", + "@accounts": { + "description": "Title of the account management dialog" + }, + "addAccount": "Add account", + "@addAccount": { + "description": "Label of add account action" + }, + "switchAccount": "Switch", + "@switchAccount": { + "description": "Label for switch account action" + }, + "logout": "Logout", + "@logout": { + "description": "Generic Logout label" + }, + "switchAccountTitle": "Switch account", + "@switchAccountTitle": { + "description": "Title of the dialog shown after adding an account, asking the user whether to switch to the newly added account or not." + }, + "switchToNewAccount": "Do you want to switch to the new account? You can switch back at any time.", + "@switchToNewAccount": { + "description": "Content of the dialog shown after adding an account, asking the user whether to switch to the newly added account or not." } } \ No newline at end of file diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index 0cbc425..771e24c 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -729,5 +729,37 @@ "allTags": "All", "@allTags": { "description": "Label shown when a document has to be assigned to all selected tags" + }, + "switchingAccountsPleaseWait": "Switching accounts. Please wait...", + "@switchingAccountsPleaseWait": { + "description": "Message shown while switching accounts is in progress." + }, + "testConnection": "Test connection", + "@testConnection": { + "description": "Button label shown on login page. Allows user to test whether the server is reachable or not." + }, + "accounts": "Accounts", + "@accounts": { + "description": "Title of the account management dialog" + }, + "addAccount": "Add account", + "@addAccount": { + "description": "Label of add account action" + }, + "switchAccount": "Switch", + "@switchAccount": { + "description": "Label for switch account action" + }, + "logout": "Logout", + "@logout": { + "description": "Generic Logout label" + }, + "switchAccountTitle": "Switch account", + "@switchAccountTitle": { + "description": "Title of the dialog shown after adding an account, asking the user whether to switch to the newly added account or not." + }, + "switchToNewAccount": "Do you want to switch to the new account? You can switch back at any time.", + "@switchToNewAccount": { + "description": "Content of the dialog shown after adding an account, asking the user whether to switch to the newly added account or not." } } \ No newline at end of file diff --git a/lib/l10n/intl_tr.arb b/lib/l10n/intl_tr.arb index 871936b..e144a80 100644 --- a/lib/l10n/intl_tr.arb +++ b/lib/l10n/intl_tr.arb @@ -729,5 +729,37 @@ "allTags": "All", "@allTags": { "description": "Label shown when a document has to be assigned to all selected tags" + }, + "switchingAccountsPleaseWait": "Switching accounts. Please wait...", + "@switchingAccountsPleaseWait": { + "description": "Message shown while switching accounts is in progress." + }, + "testConnection": "Test connection", + "@testConnection": { + "description": "Button label shown on login page. Allows user to test whether the server is reachable or not." + }, + "accounts": "Accounts", + "@accounts": { + "description": "Title of the account management dialog" + }, + "addAccount": "Add account", + "@addAccount": { + "description": "Label of add account action" + }, + "switchAccount": "Switch", + "@switchAccount": { + "description": "Label for switch account action" + }, + "logout": "Logout", + "@logout": { + "description": "Generic Logout label" + }, + "switchAccountTitle": "Switch account", + "@switchAccountTitle": { + "description": "Title of the dialog shown after adding an account, asking the user whether to switch to the newly added account or not." + }, + "switchToNewAccount": "Do you want to switch to the new account? You can switch back at any time.", + "@switchToNewAccount": { + "description": "Content of the dialog shown after adding an account, asking the user whether to switch to the newly added account or not." } } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index f7eef58..f7029ee 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -319,7 +319,9 @@ class _AuthenticationWrapperState extends State { ), ), ], - child: const HomePage(), + child: HomePage( + key: ValueKey(authentication.userId), + ), ); } else if (authentication.showBiometricAuthenticationScreen) { return const VerifyIdentityPage(); diff --git a/packages/paperless_api/lib/src/models/document_filter.dart b/packages/paperless_api/lib/src/models/document_filter.dart index 6f6012f..be89909 100644 --- a/packages/paperless_api/lib/src/models/document_filter.dart +++ b/packages/paperless_api/lib/src/models/document_filter.dart @@ -2,9 +2,7 @@ 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/models/query_parameters/tags_query/tags_query.dart'; part 'document_filter.g.dart'; @@ -23,30 +21,43 @@ class DocumentFilter extends Equatable { @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; + @HiveField(13) final int? moreLike; diff --git a/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/unset_date_range_query.dart b/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/unset_date_range_query.dart index cfbc407..94e494d 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/unset_date_range_query.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/unset_date_range_query.dart @@ -3,9 +3,7 @@ import 'package:paperless_api/config/hive/hive_type_ids.dart'; import 'package:paperless_api/src/models/query_parameters/date_range_queries/date_range_query_field.dart'; import 'date_range_query.dart'; -part 'unset_date_range_query.g.dart'; -@HiveType(typeId: PaperlessApiHiveTypeIds.unsetDateRangeQuery) class UnsetDateRangeQuery extends DateRangeQuery { const UnsetDateRangeQuery(); @override @@ -20,3 +18,29 @@ class UnsetDateRangeQuery extends DateRangeQuery { @override bool matches(DateTime dt) => true; } + +class UnsetDateRangeQueryAdapter extends TypeAdapter { + @override + final int typeId = 113; + + @override + UnsetDateRangeQuery read(BinaryReader reader) { + reader.readByte(); + return const UnsetDateRangeQuery(); + } + + @override + void write(BinaryWriter writer, UnsetDateRangeQuery obj) { + writer.writeByte(0); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is UnsetDateRangeQueryAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} diff --git a/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart b/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart index a87553b..6d66da0 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart @@ -13,13 +13,13 @@ class TagsQuery with _$TagsQuery { @HiveType(typeId: PaperlessApiHiveTypeIds.anyAssignedTagsQuery) const factory TagsQuery.anyAssigned({ - @Default([]) Iterable tagIds, + @Default([]) List tagIds, }) = AnyAssignedTagsQuery; @HiveType(typeId: PaperlessApiHiveTypeIds.idsTagsQuery) const factory TagsQuery.ids({ - @Default([]) Iterable include, - @Default([]) Iterable exclude, + @Default([]) List include, + @Default([]) List exclude, }) = IdsTagsQuery; Map toQueryParameter() { @@ -46,10 +46,6 @@ class TagsQuery with _$TagsQuery { ); } - Map toJson() { - return {}; - } - bool matches(Iterable ids) { return when( anyAssigned: (_) => ids.isNotEmpty, diff --git a/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.freezed.dart b/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.freezed.dart index d3d3ecf..6102564 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.freezed.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.freezed.dart @@ -34,22 +34,22 @@ mixin _$TagsQuery { @optionalTypeArgs TResult when({ required TResult Function() notAssigned, - required TResult Function(Iterable tagIds) anyAssigned, - required TResult Function(Iterable include, Iterable exclude) ids, + required TResult Function(List tagIds) anyAssigned, + required TResult Function(List include, List exclude) ids, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult? whenOrNull({ TResult? Function()? notAssigned, - TResult? Function(Iterable tagIds)? anyAssigned, - TResult? Function(Iterable include, Iterable exclude)? ids, + TResult? Function(List tagIds)? anyAssigned, + TResult? Function(List include, List exclude)? ids, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult maybeWhen({ TResult Function()? notAssigned, - TResult Function(Iterable tagIds)? anyAssigned, - TResult Function(Iterable include, Iterable exclude)? ids, + TResult Function(List tagIds)? anyAssigned, + TResult Function(List include, List exclude)? ids, required TResult orElse(), }) => throw _privateConstructorUsedError; @@ -144,8 +144,8 @@ class _$NotAssignedTagsQuery extends NotAssignedTagsQuery { @optionalTypeArgs TResult when({ required TResult Function() notAssigned, - required TResult Function(Iterable tagIds) anyAssigned, - required TResult Function(Iterable include, Iterable exclude) ids, + required TResult Function(List tagIds) anyAssigned, + required TResult Function(List include, List exclude) ids, }) { return notAssigned(); } @@ -154,8 +154,8 @@ class _$NotAssignedTagsQuery extends NotAssignedTagsQuery { @optionalTypeArgs TResult? whenOrNull({ TResult? Function()? notAssigned, - TResult? Function(Iterable tagIds)? anyAssigned, - TResult? Function(Iterable include, Iterable exclude)? ids, + TResult? Function(List tagIds)? anyAssigned, + TResult? Function(List include, List exclude)? ids, }) { return notAssigned?.call(); } @@ -164,8 +164,8 @@ class _$NotAssignedTagsQuery extends NotAssignedTagsQuery { @optionalTypeArgs TResult maybeWhen({ TResult Function()? notAssigned, - TResult Function(Iterable tagIds)? anyAssigned, - TResult Function(Iterable include, Iterable exclude)? ids, + TResult Function(List tagIds)? anyAssigned, + TResult Function(List include, List exclude)? ids, required TResult orElse(), }) { if (notAssigned != null) { @@ -230,7 +230,7 @@ abstract class _$$AnyAssignedTagsQueryCopyWith<$Res> { $Res Function(_$AnyAssignedTagsQuery) then) = __$$AnyAssignedTagsQueryCopyWithImpl<$Res>; @useResult - $Res call({Iterable tagIds}); + $Res call({List tagIds}); } /// @nodoc @@ -248,9 +248,9 @@ class __$$AnyAssignedTagsQueryCopyWithImpl<$Res> }) { return _then(_$AnyAssignedTagsQuery( tagIds: null == tagIds - ? _value.tagIds + ? _value._tagIds : tagIds // ignore: cast_nullable_to_non_nullable - as Iterable, + as List, )); } } @@ -259,16 +259,23 @@ class __$$AnyAssignedTagsQueryCopyWithImpl<$Res> @JsonSerializable() @HiveType(typeId: PaperlessApiHiveTypeIds.anyAssignedTagsQuery) class _$AnyAssignedTagsQuery extends AnyAssignedTagsQuery { - const _$AnyAssignedTagsQuery({this.tagIds = const [], final String? $type}) - : $type = $type ?? 'anyAssigned', + const _$AnyAssignedTagsQuery( + {final List tagIds = const [], final String? $type}) + : _tagIds = tagIds, + $type = $type ?? 'anyAssigned', super._(); factory _$AnyAssignedTagsQuery.fromJson(Map json) => _$$AnyAssignedTagsQueryFromJson(json); + final List _tagIds; @override @JsonKey() - final Iterable tagIds; + List get tagIds { + if (_tagIds is EqualUnmodifiableListView) return _tagIds; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_tagIds); + } @JsonKey(name: 'runtimeType') final String $type; @@ -283,13 +290,13 @@ class _$AnyAssignedTagsQuery extends AnyAssignedTagsQuery { return identical(this, other) || (other.runtimeType == runtimeType && other is _$AnyAssignedTagsQuery && - const DeepCollectionEquality().equals(other.tagIds, tagIds)); + const DeepCollectionEquality().equals(other._tagIds, _tagIds)); } @JsonKey(ignore: true) @override int get hashCode => - Object.hash(runtimeType, const DeepCollectionEquality().hash(tagIds)); + Object.hash(runtimeType, const DeepCollectionEquality().hash(_tagIds)); @JsonKey(ignore: true) @override @@ -302,8 +309,8 @@ class _$AnyAssignedTagsQuery extends AnyAssignedTagsQuery { @optionalTypeArgs TResult when({ required TResult Function() notAssigned, - required TResult Function(Iterable tagIds) anyAssigned, - required TResult Function(Iterable include, Iterable exclude) ids, + required TResult Function(List tagIds) anyAssigned, + required TResult Function(List include, List exclude) ids, }) { return anyAssigned(tagIds); } @@ -312,8 +319,8 @@ class _$AnyAssignedTagsQuery extends AnyAssignedTagsQuery { @optionalTypeArgs TResult? whenOrNull({ TResult? Function()? notAssigned, - TResult? Function(Iterable tagIds)? anyAssigned, - TResult? Function(Iterable include, Iterable exclude)? ids, + TResult? Function(List tagIds)? anyAssigned, + TResult? Function(List include, List exclude)? ids, }) { return anyAssigned?.call(tagIds); } @@ -322,8 +329,8 @@ class _$AnyAssignedTagsQuery extends AnyAssignedTagsQuery { @optionalTypeArgs TResult maybeWhen({ TResult Function()? notAssigned, - TResult Function(Iterable tagIds)? anyAssigned, - TResult Function(Iterable include, Iterable exclude)? ids, + TResult Function(List tagIds)? anyAssigned, + TResult Function(List include, List exclude)? ids, required TResult orElse(), }) { if (anyAssigned != null) { @@ -375,14 +382,14 @@ class _$AnyAssignedTagsQuery extends AnyAssignedTagsQuery { } abstract class AnyAssignedTagsQuery extends TagsQuery { - const factory AnyAssignedTagsQuery({final Iterable tagIds}) = + const factory AnyAssignedTagsQuery({final List tagIds}) = _$AnyAssignedTagsQuery; const AnyAssignedTagsQuery._() : super._(); factory AnyAssignedTagsQuery.fromJson(Map json) = _$AnyAssignedTagsQuery.fromJson; - Iterable get tagIds; + List get tagIds; @JsonKey(ignore: true) _$$AnyAssignedTagsQueryCopyWith<_$AnyAssignedTagsQuery> get copyWith => throw _privateConstructorUsedError; @@ -394,7 +401,7 @@ abstract class _$$IdsTagsQueryCopyWith<$Res> { _$IdsTagsQuery value, $Res Function(_$IdsTagsQuery) then) = __$$IdsTagsQueryCopyWithImpl<$Res>; @useResult - $Res call({Iterable include, Iterable exclude}); + $Res call({List include, List exclude}); } /// @nodoc @@ -413,13 +420,13 @@ class __$$IdsTagsQueryCopyWithImpl<$Res> }) { return _then(_$IdsTagsQuery( include: null == include - ? _value.include + ? _value._include : include // ignore: cast_nullable_to_non_nullable - as Iterable, + as List, exclude: null == exclude - ? _value.exclude + ? _value._exclude : exclude // ignore: cast_nullable_to_non_nullable - as Iterable, + as List, )); } } @@ -429,19 +436,34 @@ class __$$IdsTagsQueryCopyWithImpl<$Res> @HiveType(typeId: PaperlessApiHiveTypeIds.idsTagsQuery) class _$IdsTagsQuery extends IdsTagsQuery { const _$IdsTagsQuery( - {this.include = const [], this.exclude = const [], final String? $type}) - : $type = $type ?? 'ids', + {final List include = const [], + final List exclude = const [], + final String? $type}) + : _include = include, + _exclude = exclude, + $type = $type ?? 'ids', super._(); factory _$IdsTagsQuery.fromJson(Map json) => _$$IdsTagsQueryFromJson(json); + final List _include; @override @JsonKey() - final Iterable include; + List get include { + if (_include is EqualUnmodifiableListView) return _include; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_include); + } + + final List _exclude; @override @JsonKey() - final Iterable exclude; + List get exclude { + if (_exclude is EqualUnmodifiableListView) return _exclude; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_exclude); + } @JsonKey(name: 'runtimeType') final String $type; @@ -456,16 +478,16 @@ class _$IdsTagsQuery extends IdsTagsQuery { return identical(this, other) || (other.runtimeType == runtimeType && other is _$IdsTagsQuery && - const DeepCollectionEquality().equals(other.include, include) && - const DeepCollectionEquality().equals(other.exclude, exclude)); + const DeepCollectionEquality().equals(other._include, _include) && + const DeepCollectionEquality().equals(other._exclude, _exclude)); } @JsonKey(ignore: true) @override int get hashCode => Object.hash( runtimeType, - const DeepCollectionEquality().hash(include), - const DeepCollectionEquality().hash(exclude)); + const DeepCollectionEquality().hash(_include), + const DeepCollectionEquality().hash(_exclude)); @JsonKey(ignore: true) @override @@ -477,8 +499,8 @@ class _$IdsTagsQuery extends IdsTagsQuery { @optionalTypeArgs TResult when({ required TResult Function() notAssigned, - required TResult Function(Iterable tagIds) anyAssigned, - required TResult Function(Iterable include, Iterable exclude) ids, + required TResult Function(List tagIds) anyAssigned, + required TResult Function(List include, List exclude) ids, }) { return ids(include, exclude); } @@ -487,8 +509,8 @@ class _$IdsTagsQuery extends IdsTagsQuery { @optionalTypeArgs TResult? whenOrNull({ TResult? Function()? notAssigned, - TResult? Function(Iterable tagIds)? anyAssigned, - TResult? Function(Iterable include, Iterable exclude)? ids, + TResult? Function(List tagIds)? anyAssigned, + TResult? Function(List include, List exclude)? ids, }) { return ids?.call(include, exclude); } @@ -497,8 +519,8 @@ class _$IdsTagsQuery extends IdsTagsQuery { @optionalTypeArgs TResult maybeWhen({ TResult Function()? notAssigned, - TResult Function(Iterable tagIds)? anyAssigned, - TResult Function(Iterable include, Iterable exclude)? ids, + TResult Function(List tagIds)? anyAssigned, + TResult Function(List include, List exclude)? ids, required TResult orElse(), }) { if (ids != null) { @@ -551,15 +573,14 @@ class _$IdsTagsQuery extends IdsTagsQuery { abstract class IdsTagsQuery extends TagsQuery { const factory IdsTagsQuery( - {final Iterable include, - final Iterable exclude}) = _$IdsTagsQuery; + {final List include, final List exclude}) = _$IdsTagsQuery; const IdsTagsQuery._() : super._(); factory IdsTagsQuery.fromJson(Map json) = _$IdsTagsQuery.fromJson; - Iterable get include; - Iterable get exclude; + List get include; + List get exclude; @JsonKey(ignore: true) _$$IdsTagsQueryCopyWith<_$IdsTagsQuery> get copyWith => throw _privateConstructorUsedError;