feat: Fix hive errors, change provider hierarchy for blocs, translate strings

This commit is contained in:
Anton Stubenbord
2023-04-25 01:16:14 +02:00
parent 1f335119b3
commit 8c2a6928b4
34 changed files with 502 additions and 363 deletions

View File

@@ -54,12 +54,16 @@ class DocumentDetailsCubit extends Cubit<DocumentDetailsState> {
Future<void> loadSuggestions() async {
final suggestions = await _api.findSuggestions(state.document);
emit(state.copyWith(suggestions: suggestions));
if (!isClosed) {
emit(state.copyWith(suggestions: suggestions));
}
}
Future<void> loadMetaData() async {
final metaData = await _api.getMetaData(state.document);
emit(state.copyWith(metaData: metaData));
if (!isClosed) {
emit(state.copyWith(metaData: metaData));
}
}
Future<void> loadFullContent() async {
@@ -85,8 +89,8 @@ class DocumentDetailsCubit extends Cubit<DocumentDetailsState> {
_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<DocumentDetailsState> {
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<DocumentDetailsState> {
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<DocumentDetailsState> {
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";

View File

@@ -60,36 +60,28 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
Navigator.of(context)
.pop(context.read<DocumentDetailsCubit>().state.document);
Navigator.of(context).pop(context.read<DocumentDetailsCubit>().state.document);
return false;
},
child: DefaultTabController(
length: 4,
child: BlocListener<ConnectivityCubit, ConnectivityState>(
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<DocumentDetailsCubit>()
.state
.document
.title),
title: Text(context.watch<DocumentDetailsCubit>().state.document.title),
leading: const BackButton(),
pinned: true,
forceElevated: innerBoxIsScrolled,
@@ -99,8 +91,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
background: Stack(
alignment: Alignment.topCenter,
children: [
BlocBuilder<DocumentDetailsCubit,
DocumentDetailsState>(
BlocBuilder<DocumentDetailsCubit, DocumentDetailsState>(
builder: (context, state) => Positioned.fill(
child: DocumentPreview(
document: state.document,
@@ -137,9 +128,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
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<DocumentDetailsPage> {
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<DocumentDetailsPage> {
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<DocumentDetailsPage> {
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<DocumentDetailsPage> {
CustomScrollView(
slivers: [
SliverOverlapInjector(
handle: NestedScrollView
.sliverOverlapAbsorberHandleFor(context),
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
DocumentOverviewWidget(
document: state.document,
@@ -215,8 +197,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
CustomScrollView(
slivers: [
SliverOverlapInjector(
handle: NestedScrollView
.sliverOverlapAbsorberHandleFor(context),
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
DocumentContentWidget(
isFullContentLoaded: state.isFullContentLoaded,
@@ -229,8 +210,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
CustomScrollView(
slivers: [
SliverOverlapInjector(
handle: NestedScrollView
.sliverOverlapAbsorberHandleFor(context),
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
DocumentMetaDataWidget(
document: state.document,
@@ -242,8 +222,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
controller: _pagingScrollController,
slivers: [
SliverOverlapInjector(
handle: NestedScrollView
.sliverOverlapAbsorberHandleFor(context),
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
SimilarDocumentsView(
pagingScrollController: _pagingScrollController,
@@ -301,9 +280,8 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
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<DocumentDetailsPage> {
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<DocumentDetailsPage> {
),
],
child: BlocListener<DocumentEditCubit, DocumentEditState>(
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<DocumentDetailsPage> {
}
void _onOpenFileInSystemViewer() async {
final status =
await context.read<DocumentDetailsCubit>().openDocumentInSystemViewer();
final status = await context.read<DocumentDetailsCubit>().openDocumentInSystemViewer();
if (status == ResultType.done) return;
if (status == ResultType.noAppToOpen) {
showGenericError(context, S.of(context)!.noAppToDisplayPDFFilesFound);
@@ -383,16 +358,14 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
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<DocumentDetailsPage> {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => DocumentView(
documentBytes:
context.read<PaperlessDocumentsApi>().getPreview(document.id),
documentBytes: context.read<PaperlessDocumentsApi>().getPreview(document.id),
),
),
);

View File

@@ -190,7 +190,7 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
allowCreation: true,
allowExclude: false,
initialValue: TagsQuery.ids(
include: state.document.tags,
include: state.document.tags.toList(),
),
).padded(),
if (_filteredSuggestions?.tags

View File

@@ -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<DocumentSearchState> with DocumentPagingBlocMixin {
class DocumentSearchCubit extends Cubit<DocumentSearchState> 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<DocumentSearchState> with Docume
],
),
);
_userAppState
..documentSearchHistory = state.searchHistory
..save();
}
void updateViewType(ViewType viewType) {
@@ -73,6 +81,9 @@ class DocumentSearchCubit extends HydratedCubit<DocumentSearchState> with Docume
searchHistory: state.searchHistory.whereNot((element) => element == entry).toList(),
),
);
_userAppState
..documentSearchHistory = state.searchHistory
..save();
}
Future<void> suggest(String query) async {
@@ -92,11 +103,13 @@ class DocumentSearchCubit extends HydratedCubit<DocumentSearchState> 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<DocumentSearchState> with Docume
return super.close();
}
@override
DocumentSearchState? fromJson(Map<String, dynamic> json) {
return DocumentSearchState.fromJson(json);
}
@override
Map<String, dynamic>? toJson(DocumentSearchState state) {
return state.toJson();
}
@override
Future<void> onFilterUpdated(DocumentFilter filter) async {}
}

View File

@@ -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<void> showDocumentSearchPage(BuildContext context) {
final currentUser =
Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser;
return Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => BlocProvider(
@@ -21,6 +27,7 @@ Future<void> showDocumentSearchPage(BuildContext context) {
context.read(),
context.read(),
context.read(),
Hive.box<UserAppState>(HiveBoxes.userAppState).get(currentUser)!,
),
child: const DocumentSearchPage(),
),
@@ -111,9 +118,8 @@ class _DocumentSearchPageState extends State<DocumentSearchPage> {
}
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<DocumentSearchPage> {
builder: (context, state) {
return ViewTypeSelectionWidget(
viewType: state.viewType,
onChanged: (type) =>
context.read<DocumentSearchCubit>().updateViewType(type),
onChanged: (type) => context.read<DocumentSearchCubit>().updateViewType(type),
);
},
)

View File

@@ -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';

View File

@@ -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<DocumentsState> with DocumentPagingBlocMixin {
class DocumentsCubit extends Cubit<DocumentsState> with DocumentPagingBlocMixin {
@override
final PaperlessDocumentsApi api;

View File

@@ -461,8 +461,8 @@ class _DocumentsPageState extends State<DocumentsPage> with SingleTickerProvider
context.read<DocumentsCubit>().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 {

View File

@@ -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<HomePage> 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<GlobalSettings>(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser;
_documentsCubit = DocumentsCubit(
context.read(),
context.read(),
context.read(),
Hive.box<UserAppState>(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<HomePage> 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<HomePage> with WidgetsBindingObserver {
WidgetsBinding.instance.removeObserver(this);
_inboxTimer.cancel();
_inboxCubit.close();
_documentsCubit.close();
_savedViewCubit.close();
super.dispose();
}
@@ -190,7 +213,8 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
@override
Widget build(BuildContext context) {
final userId = context.watch<AuthenticationCubit>().state.userId;
final userId =
Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser!;
final destinations = [
RouteDescription(
icon: const Icon(Icons.description_outlined),
@@ -239,22 +263,10 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
];
final routes = <Widget>[
MultiBlocProvider(
key: ValueKey(userId),
// key: ValueKey(userId),
providers: [
BlocProvider(
create: (context) => DocumentsCubit(
context.read(),
context.read(),
context.read(),
Hive.box<UserAppState>(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<HomePage> with WidgetsBindingObserver {
child: const ScannerPage(),
),
MultiBlocProvider(
key: ValueKey(userId),
// key: ValueKey(userId),
providers: [
BlocProvider(
create: (context) => LabelCubit(context.read()),

View File

@@ -97,7 +97,7 @@ class InboxCubit extends HydratedCubit<InboxState> 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<InboxState> with DocumentPagingBlocMixin
updateFilter(
filter: DocumentFilter(
sortField: SortField.added,
tags: TagsQuery.ids(include: inboxTags),
tags: TagsQuery.ids(include: inboxTags.toList()),
),
);
}

View File

@@ -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());

View File

@@ -59,7 +59,6 @@ class LabelItem<T extends Label> extends StatelessWidget {
context.read(),
context.read(),
context.read(),
Hive.box<UserAccount>(HiveBoxes.userAccount).get(currentUser)!,
),
child: const LinkedDocumentsPage(),
),

View File

@@ -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<LinkedDocumentsState>
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(

View File

@@ -143,8 +143,9 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
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<AuthenticationState> {
baseUrl: account.serverUrl,
);
await _reloadRepositories();
globalSettings.currentLoggedInUser = userId;
await globalSettings.save();
await _reloadRepositories();
emit(
AuthenticationState(
isAuthenticated: true,

View File

@@ -65,7 +65,7 @@ class _ServerConnectionPageState extends State<ServerConnectionPage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
child: Text("Test connection"), //TODO: INTL
child: Text(S.of(context)!.testConnection),
onPressed: _updateReachability,
),
FilledButton(

View File

@@ -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<PaperlessServerInformationCubit, PaperlessServerInformationState>(
builder: (context, state) {
return Column(
children: [
ValueListenableBuilder(
valueListenable: Hive.box<UserAccount>(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<void> _onLogout(BuildContext context) async {
try {
await context.read<AuthenticationCubit>().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(" ")),
),
);
}
}

View File

@@ -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(),
],
);
}

View File

@@ -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<void> _onAddAccount(BuildContext context) {
return Navigator.push(
Future<void> _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<AuthenticationCubit>().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<AuthenticationCubit>().switchAccount(userId);
}
Navigator.of(context).pop<String?>(userId);
},
submitText: "Add account", //TODO: INTL
submitText: S.of(context)!.addAccount,
),
),
);
if (userId != null) {
final shoudSwitch = await showDialog<bool>(
context: context,
builder: (context) => const SwitchAccountDialog(),
) ??
false;
if (shoudSwitch) {
await context.read<AuthenticationCubit>().switchAccount(userId);
Navigator.pop(context);
}
}
}
}

View File

@@ -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(

View File

@@ -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(

View File

@@ -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,
),
],
),
),

View File

@@ -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';

View File

@@ -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."
}
}

View File

@@ -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."
}
}

View File

@@ -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."
}
}

View File

@@ -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."
}
}

View File

@@ -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."
}
}

View File

@@ -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."
}
}

View File

@@ -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."
}
}

View File

@@ -319,7 +319,9 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
),
),
],
child: const HomePage(),
child: HomePage(
key: ValueKey(authentication.userId),
),
);
} else if (authentication.showBiometricAuthenticationScreen) {
return const VerifyIdentityPage();

View File

@@ -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;

View File

@@ -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<UnsetDateRangeQuery> {
@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;
}

View File

@@ -13,13 +13,13 @@ class TagsQuery with _$TagsQuery {
@HiveType(typeId: PaperlessApiHiveTypeIds.anyAssignedTagsQuery)
const factory TagsQuery.anyAssigned({
@Default([]) Iterable<int> tagIds,
@Default([]) List<int> tagIds,
}) = AnyAssignedTagsQuery;
@HiveType(typeId: PaperlessApiHiveTypeIds.idsTagsQuery)
const factory TagsQuery.ids({
@Default([]) Iterable<int> include,
@Default([]) Iterable<int> exclude,
@Default([]) List<int> include,
@Default([]) List<int> exclude,
}) = IdsTagsQuery;
Map<String, String> toQueryParameter() {
@@ -46,10 +46,6 @@ class TagsQuery with _$TagsQuery {
);
}
Map<String, dynamic> toJson() {
return {};
}
bool matches(Iterable<int> ids) {
return when(
anyAssigned: (_) => ids.isNotEmpty,

View File

@@ -34,22 +34,22 @@ mixin _$TagsQuery {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() notAssigned,
required TResult Function(Iterable<int> tagIds) anyAssigned,
required TResult Function(Iterable<int> include, Iterable<int> exclude) ids,
required TResult Function(List<int> tagIds) anyAssigned,
required TResult Function(List<int> include, List<int> exclude) ids,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? notAssigned,
TResult? Function(Iterable<int> tagIds)? anyAssigned,
TResult? Function(Iterable<int> include, Iterable<int> exclude)? ids,
TResult? Function(List<int> tagIds)? anyAssigned,
TResult? Function(List<int> include, List<int> exclude)? ids,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? notAssigned,
TResult Function(Iterable<int> tagIds)? anyAssigned,
TResult Function(Iterable<int> include, Iterable<int> exclude)? ids,
TResult Function(List<int> tagIds)? anyAssigned,
TResult Function(List<int> include, List<int> exclude)? ids,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@@ -144,8 +144,8 @@ class _$NotAssignedTagsQuery extends NotAssignedTagsQuery {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() notAssigned,
required TResult Function(Iterable<int> tagIds) anyAssigned,
required TResult Function(Iterable<int> include, Iterable<int> exclude) ids,
required TResult Function(List<int> tagIds) anyAssigned,
required TResult Function(List<int> include, List<int> exclude) ids,
}) {
return notAssigned();
}
@@ -154,8 +154,8 @@ class _$NotAssignedTagsQuery extends NotAssignedTagsQuery {
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? notAssigned,
TResult? Function(Iterable<int> tagIds)? anyAssigned,
TResult? Function(Iterable<int> include, Iterable<int> exclude)? ids,
TResult? Function(List<int> tagIds)? anyAssigned,
TResult? Function(List<int> include, List<int> exclude)? ids,
}) {
return notAssigned?.call();
}
@@ -164,8 +164,8 @@ class _$NotAssignedTagsQuery extends NotAssignedTagsQuery {
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? notAssigned,
TResult Function(Iterable<int> tagIds)? anyAssigned,
TResult Function(Iterable<int> include, Iterable<int> exclude)? ids,
TResult Function(List<int> tagIds)? anyAssigned,
TResult Function(List<int> include, List<int> 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<int> tagIds});
$Res call({List<int> 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<int>,
as List<int>,
));
}
}
@@ -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<int> tagIds = const [], final String? $type})
: _tagIds = tagIds,
$type = $type ?? 'anyAssigned',
super._();
factory _$AnyAssignedTagsQuery.fromJson(Map<String, dynamic> json) =>
_$$AnyAssignedTagsQueryFromJson(json);
final List<int> _tagIds;
@override
@JsonKey()
final Iterable<int> tagIds;
List<int> 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<TResult extends Object?>({
required TResult Function() notAssigned,
required TResult Function(Iterable<int> tagIds) anyAssigned,
required TResult Function(Iterable<int> include, Iterable<int> exclude) ids,
required TResult Function(List<int> tagIds) anyAssigned,
required TResult Function(List<int> include, List<int> exclude) ids,
}) {
return anyAssigned(tagIds);
}
@@ -312,8 +319,8 @@ class _$AnyAssignedTagsQuery extends AnyAssignedTagsQuery {
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? notAssigned,
TResult? Function(Iterable<int> tagIds)? anyAssigned,
TResult? Function(Iterable<int> include, Iterable<int> exclude)? ids,
TResult? Function(List<int> tagIds)? anyAssigned,
TResult? Function(List<int> include, List<int> exclude)? ids,
}) {
return anyAssigned?.call(tagIds);
}
@@ -322,8 +329,8 @@ class _$AnyAssignedTagsQuery extends AnyAssignedTagsQuery {
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? notAssigned,
TResult Function(Iterable<int> tagIds)? anyAssigned,
TResult Function(Iterable<int> include, Iterable<int> exclude)? ids,
TResult Function(List<int> tagIds)? anyAssigned,
TResult Function(List<int> include, List<int> 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<int> tagIds}) =
const factory AnyAssignedTagsQuery({final List<int> tagIds}) =
_$AnyAssignedTagsQuery;
const AnyAssignedTagsQuery._() : super._();
factory AnyAssignedTagsQuery.fromJson(Map<String, dynamic> json) =
_$AnyAssignedTagsQuery.fromJson;
Iterable<int> get tagIds;
List<int> 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<int> include, Iterable<int> exclude});
$Res call({List<int> include, List<int> 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<int>,
as List<int>,
exclude: null == exclude
? _value.exclude
? _value._exclude
: exclude // ignore: cast_nullable_to_non_nullable
as Iterable<int>,
as List<int>,
));
}
}
@@ -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<int> include = const [],
final List<int> exclude = const [],
final String? $type})
: _include = include,
_exclude = exclude,
$type = $type ?? 'ids',
super._();
factory _$IdsTagsQuery.fromJson(Map<String, dynamic> json) =>
_$$IdsTagsQueryFromJson(json);
final List<int> _include;
@override
@JsonKey()
final Iterable<int> include;
List<int> get include {
if (_include is EqualUnmodifiableListView) return _include;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_include);
}
final List<int> _exclude;
@override
@JsonKey()
final Iterable<int> exclude;
List<int> 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<TResult extends Object?>({
required TResult Function() notAssigned,
required TResult Function(Iterable<int> tagIds) anyAssigned,
required TResult Function(Iterable<int> include, Iterable<int> exclude) ids,
required TResult Function(List<int> tagIds) anyAssigned,
required TResult Function(List<int> include, List<int> exclude) ids,
}) {
return ids(include, exclude);
}
@@ -487,8 +509,8 @@ class _$IdsTagsQuery extends IdsTagsQuery {
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? notAssigned,
TResult? Function(Iterable<int> tagIds)? anyAssigned,
TResult? Function(Iterable<int> include, Iterable<int> exclude)? ids,
TResult? Function(List<int> tagIds)? anyAssigned,
TResult? Function(List<int> include, List<int> exclude)? ids,
}) {
return ids?.call(include, exclude);
}
@@ -497,8 +519,8 @@ class _$IdsTagsQuery extends IdsTagsQuery {
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? notAssigned,
TResult Function(Iterable<int> tagIds)? anyAssigned,
TResult Function(Iterable<int> include, Iterable<int> exclude)? ids,
TResult Function(List<int> tagIds)? anyAssigned,
TResult Function(List<int> include, List<int> 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<int> include,
final Iterable<int> exclude}) = _$IdsTagsQuery;
{final List<int> include, final List<int> exclude}) = _$IdsTagsQuery;
const IdsTagsQuery._() : super._();
factory IdsTagsQuery.fromJson(Map<String, dynamic> json) =
_$IdsTagsQuery.fromJson;
Iterable<int> get include;
Iterable<int> get exclude;
List<int> get include;
List<int> get exclude;
@JsonKey(ignore: true)
_$$IdsTagsQueryCopyWith<_$IdsTagsQuery> get copyWith =>
throw _privateConstructorUsedError;