Resetting filter doesn't reset sorting, some bugfixes and UI updates

This commit is contained in:
Anton Stubenbord
2022-12-14 17:57:01 +01:00
parent a3c3810d35
commit 4bf4ff1cbd
23 changed files with 327 additions and 253 deletions

View File

@@ -49,7 +49,6 @@ void main() async {
await getIt<ConnectivityCubit>().initialize(); await getIt<ConnectivityCubit>().initialize();
await getIt<ApplicationSettingsCubit>().initialize(); await getIt<ApplicationSettingsCubit>().initialize();
await getIt<AuthenticationCubit>().initialize();
}); });
// Mocked classes // Mocked classes
@@ -97,7 +96,6 @@ void main() async {
await getIt<ConnectivityCubit>().initialize(); await getIt<ConnectivityCubit>().initialize();
await getIt<ApplicationSettingsCubit>().initialize(); await getIt<ApplicationSettingsCubit>().initialize();
await getIt<AuthenticationCubit>().initialize();
}); });
// Mocked classes // Mocked classes
@@ -149,7 +147,6 @@ void main() async {
)); ));
await getIt<ConnectivityCubit>().initialize(); await getIt<ConnectivityCubit>().initialize();
await getIt<ApplicationSettingsCubit>().initialize(); await getIt<ApplicationSettingsCubit>().initialize();
await getIt<AuthenticationCubit>().initialize();
}); });
await t.binding.waitUntilFirstFrameRasterized; await t.binding.waitUntilFirstFrameRasterized;
@@ -199,7 +196,6 @@ void main() async {
await getIt<ConnectivityCubit>().initialize(); await getIt<ConnectivityCubit>().initialize();
await getIt<ApplicationSettingsCubit>().initialize(); await getIt<ApplicationSettingsCubit>().initialize();
await getIt<AuthenticationCubit>().initialize();
}); });
await t.binding.waitUntilFirstFrameRasterized; await t.binding.waitUntilFirstFrameRasterized;

View File

@@ -2,6 +2,7 @@ import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart'; import 'package:integration_test/integration_test.dart';
import 'package:paperless_mobile/di_initializer.dart'; import 'package:paperless_mobile/di_initializer.dart';
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
import 'package:paperless_mobile/generated/l10n.dart'; import 'package:paperless_mobile/generated/l10n.dart';
import 'package:paperless_mobile/main.dart'; import 'package:paperless_mobile/main.dart';
@@ -35,5 +36,5 @@ Future<void> initAndLaunchTestApp(
Future<void> Function() initializationCallback, Future<void> Function() initializationCallback,
) async { ) async {
await initializationCallback(); await initializationCallback();
runApp(const PaperlessMobileEntrypoint()); //runApp(const PaperlessMobileEntrypoint(authenticationCubit: ),));
} }

View File

@@ -1,5 +1,4 @@
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_api/src/models/saved_view_model.dart';
import 'package:paperless_mobile/core/repository/saved_view_repository.dart'; import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
@@ -8,11 +7,10 @@ class SavedViewRepositoryImpl implements SavedViewRepository {
SavedViewRepositoryImpl(this._api); SavedViewRepositoryImpl(this._api);
final BehaviorSubject<Map<int, SavedView>> _subject = final BehaviorSubject<Map<int, SavedView>?> _subject = BehaviorSubject();
BehaviorSubject.seeded({});
@override @override
Stream<Map<int, SavedView>> get savedViews => Stream<Map<int, SavedView>?> get savedViews =>
_subject.stream.asBroadcastStream(); _subject.stream.asBroadcastStream();
@override @override
@@ -23,7 +21,7 @@ class SavedViewRepositoryImpl implements SavedViewRepository {
@override @override
Future<SavedView> create(SavedView view) async { Future<SavedView> create(SavedView view) async {
final created = await _api.save(view); final created = await _api.save(view);
final updatedState = {..._subject.value} final updatedState = {..._subject.valueOrNull ?? {}}
..putIfAbsent(created.id!, () => created); ..putIfAbsent(created.id!, () => created);
_subject.add(updatedState); _subject.add(updatedState);
return created; return created;
@@ -32,7 +30,7 @@ class SavedViewRepositoryImpl implements SavedViewRepository {
@override @override
Future<int> delete(SavedView view) async { Future<int> delete(SavedView view) async {
await _api.delete(view); await _api.delete(view);
final updatedState = {..._subject.value}..remove(view.id); final updatedState = {..._subject.valueOrNull ?? {}}..remove(view.id);
_subject.add(updatedState); _subject.add(updatedState);
return view.id!; return view.id!;
} }
@@ -40,7 +38,7 @@ class SavedViewRepositoryImpl implements SavedViewRepository {
@override @override
Future<SavedView?> find(int id) async { Future<SavedView?> find(int id) async {
final found = await _api.find(id); final found = await _api.find(id);
final updatedState = {..._subject.value} final updatedState = {..._subject.valueOrNull ?? {}}
..update(id, (_) => found, ifAbsent: () => found); ..update(id, (_) => found, ifAbsent: () => found);
_subject.add(updatedState); _subject.add(updatedState);
return found; return found;
@@ -50,7 +48,7 @@ class SavedViewRepositoryImpl implements SavedViewRepository {
Future<Iterable<SavedView>> findAll([Iterable<int>? ids]) async { Future<Iterable<SavedView>> findAll([Iterable<int>? ids]) async {
final found = await _api.findAll(ids); final found = await _api.findAll(ids);
final updatedState = { final updatedState = {
..._subject.value, ..._subject.valueOrNull ?? {},
...{for (final view in found) view.id!: view}, ...{for (final view in found) view.id!: view},
}; };
_subject.add(updatedState); _subject.add(updatedState);

View File

@@ -1,7 +1,7 @@
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
abstract class SavedViewRepository { abstract class SavedViewRepository {
Stream<Map<int, SavedView>> get savedViews; Stream<Map<int, SavedView>?> get savedViews;
Future<SavedView> create(SavedView view); Future<SavedView> create(SavedView view);
Future<SavedView?> find(int id); Future<SavedView?> find(int id);

View File

@@ -2,7 +2,6 @@ import 'dart:io';
import 'package:paperless_mobile/di_initializer.config.dart'; import 'package:paperless_mobile/di_initializer.config.dart';
import 'package:paperless_mobile/di_modules.dart'; import 'package:paperless_mobile/di_modules.dart';
import 'package:paperless_mobile/di_paperless_api.dart';
import 'package:paperless_mobile/features/login/model/client_certificate.dart'; import 'package:paperless_mobile/features/login/model/client_certificate.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import 'package:injectable/injectable.dart'; import 'package:injectable/injectable.dart';
@@ -21,7 +20,7 @@ void configureDependencies(String environment) =>
/// ///
/// Registers new security context, which will be used by the HttpClient, see [RegisterModule]. /// Registers new security context, which will be used by the HttpClient, see [RegisterModule].
/// ///
void registerSecurityContext(ClientCertificate? cert) { Future<void> registerSecurityContext(ClientCertificate? cert) async {
var context = SecurityContext(); var context = SecurityContext();
if (cert != null) { if (cert != null) {
context = context context = context
@@ -29,6 +28,6 @@ void registerSecurityContext(ClientCertificate? cert) {
..useCertificateChainBytes(cert.bytes, password: cert.passphrase) ..useCertificateChainBytes(cert.bytes, password: cert.passphrase)
..setTrustedCertificatesBytes(cert.bytes, password: cert.passphrase); ..setTrustedCertificatesBytes(cert.bytes, password: cert.passphrase);
} }
getIt.unregister<SecurityContext>(); await getIt.unregister<SecurityContext>();
getIt.registerSingleton<SecurityContext>(context); getIt.registerSingleton<SecurityContext>(context);
} }

View File

@@ -107,11 +107,11 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
color: Colors color: Colors
.black, //TODO: check if there is a way to dynamically determine color... .black, //TODO: check if there is a way to dynamically determine color...
), ),
onPressed: () => Navigator.pop( onPressed: () => Navigator.of(context).pop(
context, BlocProvider.of<DocumentDetailsCubit>(context)
BlocProvider.of<DocumentDetailsCubit>(context) .state
.state .document,
.document), ),
), ),
floating: true, floating: true,
pinned: true, pinned: true,
@@ -237,13 +237,13 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
return ListView( return ListView(
children: [ children: [
_DetailsItem.text(DateFormat().format(document.modified), _DetailsItem.text(DateFormat().format(document.modified),
label: S.of(context).documentModifiedPropertyLabel, label: S.of(context).documentModifiedPropertyLabel,
context: context), context: context)
_separator(), .paddedOnly(bottom: 16),
_DetailsItem.text(DateFormat().format(document.added), _DetailsItem.text(DateFormat().format(document.added),
label: S.of(context).documentAddedPropertyLabel, label: S.of(context).documentAddedPropertyLabel,
context: context), context: context)
_separator(), .paddedSymmetrically(vertical: 16),
_DetailsItem( _DetailsItem(
label: S.of(context).documentArchiveSerialNumberPropertyLongLabel, label: S.of(context).documentArchiveSerialNumberPropertyLongLabel,
content: document.archiveSerialNumber != null content: document.archiveSerialNumber != null
@@ -255,30 +255,26 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
onPressed: onPressed:
widget.allowEdit ? () => _assignAsn(document) : null, widget.allowEdit ? () => _assignAsn(document) : null,
), ),
), ).paddedSymmetrically(vertical: 16),
_separator(),
_DetailsItem.text( _DetailsItem.text(
meta.mediaFilename, meta.mediaFilename,
context: context, context: context,
label: S.of(context).documentMetaDataMediaFilenamePropertyLabel, label: S.of(context).documentMetaDataMediaFilenamePropertyLabel,
), ).paddedSymmetrically(vertical: 16),
_separator(),
_DetailsItem.text( _DetailsItem.text(
meta.originalChecksum, meta.originalChecksum,
context: context, context: context,
label: S.of(context).documentMetaDataChecksumLabel, label: S.of(context).documentMetaDataChecksumLabel,
), ).paddedSymmetrically(vertical: 16),
_separator(),
_DetailsItem.text(formatBytes(meta.originalSize, 2), _DetailsItem.text(formatBytes(meta.originalSize, 2),
label: S.of(context).documentMetaDataOriginalFileSizeLabel, label: S.of(context).documentMetaDataOriginalFileSizeLabel,
context: context), context: context)
_separator(), .paddedSymmetrically(vertical: 16),
_DetailsItem.text( _DetailsItem.text(
meta.originalMimeType, meta.originalMimeType,
label: S.of(context).documentMetaDataOriginalMimeTypeLabel, label: S.of(context).documentMetaDataOriginalMimeTypeLabel,
context: context, context: context,
), ).paddedSymmetrically(vertical: 16),
_separator(),
], ],
); );
}, },
@@ -295,16 +291,13 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
Widget _buildDocumentContentView(DocumentModel document, String? match) { Widget _buildDocumentContentView(DocumentModel document, String? match) {
return SingleChildScrollView( return SingleChildScrollView(
child: _DetailsItem( child: HighlightedText(
content: HighlightedText( text: document.content ?? "",
text: document.content ?? "", highlights: match == null ? [] : match.split(" "),
highlights: match == null ? [] : match.split(" "), style: Theme.of(context).textTheme.bodyText2,
style: Theme.of(context).textTheme.bodyText2, caseSensitive: false,
caseSensitive: false,
),
label: S.of(context).documentDetailsPageTabContentLabel,
), ),
); ).paddedOnly(top: 8);
} }
Widget _buildDocumentOverview(DocumentModel document, String? match) { Widget _buildDocumentOverview(DocumentModel document, String? match) {
@@ -314,60 +307,61 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
content: HighlightedText( content: HighlightedText(
text: document.title, text: document.title,
highlights: match?.split(" ") ?? <String>[], highlights: match?.split(" ") ?? <String>[],
style: Theme.of(context).textTheme.bodyLarge,
), ),
label: S.of(context).documentTitlePropertyLabel, label: S.of(context).documentTitlePropertyLabel,
), ).paddedOnly(bottom: 16),
_separator(),
_DetailsItem.text( _DetailsItem.text(
DateFormat.yMMMd().format(document.created), DateFormat.yMMMd().format(document.created),
context: context, context: context,
label: S.of(context).documentCreatedPropertyLabel, label: S.of(context).documentCreatedPropertyLabel,
), ).paddedSymmetrically(vertical: 16),
_separator(), Visibility(
_DetailsItem( visible: document.documentType != null,
content: DocumentTypeWidget( child: _DetailsItem(
isClickable: widget.isLabelClickable, content: DocumentTypeWidget(
documentTypeId: document.documentType, textStyle: Theme.of(context).textTheme.bodyLarge,
afterSelected: () {
Navigator.pop(context);
},
),
label: S.of(context).documentDocumentTypePropertyLabel,
),
_separator(),
_DetailsItem(
label: S.of(context).documentCorrespondentPropertyLabel,
content: CorrespondentWidget(
isClickable: widget.isLabelClickable,
correspondentId: document.correspondent,
afterSelected: () {
Navigator.pop(context);
},
),
),
_separator(),
_DetailsItem(
label: S.of(context).documentStoragePathPropertyLabel,
content: StoragePathWidget(
isClickable: widget.isLabelClickable,
pathId: document.storagePath,
afterSelected: () {
Navigator.pop(context);
},
),
),
_separator(),
_DetailsItem(
label: S.of(context).documentTagsPropertyLabel,
content: Padding(
padding: const EdgeInsets.only(top: 8.0),
child: TagsWidget(
isClickable: widget.isLabelClickable, isClickable: widget.isLabelClickable,
tagIds: document.tags, documentTypeId: document.documentType,
isSelectedPredicate: (_) => false,
onTagSelected: (int tagId) {},
), ),
), label: S.of(context).documentDocumentTypePropertyLabel,
).paddedSymmetrically(vertical: 16),
),
Visibility(
visible: document.correspondent != null,
child: _DetailsItem(
label: S.of(context).documentCorrespondentPropertyLabel,
content: CorrespondentWidget(
textStyle: Theme.of(context).textTheme.bodyLarge,
isClickable: widget.isLabelClickable,
correspondentId: document.correspondent,
),
).paddedSymmetrically(vertical: 16),
),
Visibility(
visible: document.storagePath != null,
child: _DetailsItem(
label: S.of(context).documentStoragePathPropertyLabel,
content: StoragePathWidget(
isClickable: widget.isLabelClickable,
pathId: document.storagePath,
),
).paddedSymmetrically(vertical: 16),
),
Visibility(
visible: document.tags.isNotEmpty,
child: _DetailsItem(
label: S.of(context).documentTagsPropertyLabel,
content: Padding(
padding: const EdgeInsets.only(top: 8.0),
child: TagsWidget(
isClickable: widget.isLabelClickable,
tagIds: document.tags,
isSelectedPredicate: (_) => false,
onTagSelected: (int tagId) {},
),
),
).paddedSymmetrically(vertical: 16),
), ),
// _separator(), // _separator(),
// FutureBuilder<List<SimilarDocumentModel>>( // FutureBuilder<List<SimilarDocumentModel>>(
@@ -396,10 +390,6 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
); );
} }
Widget _separator() {
return const SizedBox(height: 32.0);
}
/// ///
/// Downloads file to temporary directory, from which it can then be shared. /// Downloads file to temporary directory, from which it can then be shared.
/// ///
@@ -477,10 +467,7 @@ class _DetailsItem extends StatelessWidget {
children: [ children: [
Text( Text(
label, label,
style: Theme.of(context) style: Theme.of(context).textTheme.caption,
.textTheme
.headline5
?.copyWith(fontWeight: FontWeight.bold),
), ),
content, content,
], ],
@@ -492,7 +479,7 @@ class _DetailsItem extends StatelessWidget {
String text, { String text, {
required this.label, required this.label,
required BuildContext context, required BuildContext context,
}) : content = Text(text, style: Theme.of(context).textTheme.bodyText2); }) : content = Text(text, style: Theme.of(context).textTheme.bodyLarge);
} }
class ColoredTabBar extends Container implements PreferredSizeWidget { class ColoredTabBar extends Container implements PreferredSizeWidget {

View File

@@ -88,6 +88,13 @@ class DocumentsCubit extends Cubit<DocumentsState> {
emit(DocumentsState(filter: filter, value: [result], isLoaded: true)); emit(DocumentsState(filter: filter, value: [result], isLoaded: true));
} }
Future<void> resetFilter() async {
final filter = DocumentFilter.initial.copyWith(
sortField: state.filter.sortField,
sortOrder: state.filter.sortOrder,
);
}
/// ///
/// Convenience method which allows to directly use [DocumentFilter.copyWith] on the current filter. /// Convenience method which allows to directly use [DocumentFilter.copyWith] on the current filter.
/// ///

View File

@@ -151,22 +151,28 @@ class _DocumentsPageState extends State<DocumentsPage> {
switch (settings.preferredViewType) { switch (settings.preferredViewType) {
case ViewType.list: case ViewType.list:
child = DocumentListView( child = DocumentListView(
onTap: _openDetails,
state: state, state: state,
onTap: _openDetails,
onSelected: _onSelected, onSelected: _onSelected,
pagingController: _pagingController, pagingController: _pagingController,
hasInternetConnection: isConnected, hasInternetConnection: isConnected,
onTagSelected: _addTagToFilter, onTagSelected: _addTagToFilter,
onCorrespondentSelected: _addCorrespondentToFilter,
onDocumentTypeSelected: _addDocumentTypeToFilter,
onStoragePathSelected: _addStoragePathToFilter,
); );
break; break;
case ViewType.grid: case ViewType.grid:
child = DocumentGridView( child = DocumentGridView(
onTap: _openDetails,
state: state, state: state,
onTap: _openDetails,
onSelected: _onSelected, onSelected: _onSelected,
pagingController: _pagingController, pagingController: _pagingController,
hasInternetConnection: isConnected, hasInternetConnection: isConnected,
onTagSelected: _addTagToFilter, onTagSelected: _addTagToFilter,
onCorrespondentSelected: _addCorrespondentToFilter,
onDocumentTypeSelected: _addDocumentTypeToFilter,
onStoragePathSelected: _addStoragePathToFilter,
); );
break; break;
} }
@@ -176,7 +182,7 @@ class _DocumentsPageState extends State<DocumentsPage> {
child: DocumentsEmptyState( child: DocumentsEmptyState(
state: state, state: state,
onReset: () { onReset: () {
_documentsCubit.updateFilter(); _documentsCubit.resetFilter();
_savedViewCubit.resetSelection(); _savedViewCubit.resetSelection();
}, },
), ),
@@ -195,7 +201,7 @@ class _DocumentsPageState extends State<DocumentsPage> {
listener: (context, state) { listener: (context, state) {
try { try {
if (state.selectedSavedViewId == null) { if (state.selectedSavedViewId == null) {
_documentsCubit.updateFilter(); _documentsCubit.resetFilter();
} else { } else {
final newFilter = state final newFilter = state
.value[state.selectedSavedViewId] .value[state.selectedSavedViewId]
@@ -280,6 +286,63 @@ class _DocumentsPageState extends State<DocumentsPage> {
} }
} }
void _addCorrespondentToFilter(int? correspondentId) {
final cubit = BlocProvider.of<DocumentsCubit>(context);
try {
if (cubit.state.filter.correspondent.id == correspondentId) {
cubit.updateCurrentFilter(
(filter) =>
filter.copyWith(correspondent: const CorrespondentQuery.unset()),
);
} else {
cubit.updateCurrentFilter(
(filter) => filter.copyWith(
correspondent: CorrespondentQuery.fromId(correspondentId)),
);
}
} on PaperlessServerException catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace);
}
}
void _addDocumentTypeToFilter(int? documentTypeId) {
final cubit = BlocProvider.of<DocumentsCubit>(context);
try {
if (cubit.state.filter.documentType.id == documentTypeId) {
cubit.updateCurrentFilter(
(filter) =>
filter.copyWith(documentType: const DocumentTypeQuery.unset()),
);
} else {
cubit.updateCurrentFilter(
(filter) => filter.copyWith(
documentType: DocumentTypeQuery.fromId(documentTypeId)),
);
}
} on PaperlessServerException catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace);
}
}
void _addStoragePathToFilter(int? pathId) {
final cubit = BlocProvider.of<DocumentsCubit>(context);
try {
if (cubit.state.filter.correspondent.id == pathId) {
cubit.updateCurrentFilter(
(filter) =>
filter.copyWith(storagePath: const StoragePathQuery.unset()),
);
} else {
cubit.updateCurrentFilter(
(filter) =>
filter.copyWith(storagePath: StoragePathQuery.fromId(pathId)),
);
}
} on PaperlessServerException catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace);
}
}
Future<void> _loadNewPage(int pageKey) async { Future<void> _loadNewPage(int pageKey) async {
final pageCount = _documentsCubit.state final pageCount = _documentsCubit.state
.inferPageCount(pageSize: _documentsCubit.state.filter.pageSize); .inferPageCount(pageSize: _documentsCubit.state.filter.pageSize);
@@ -299,9 +362,10 @@ class _DocumentsPageState extends State<DocumentsPage> {
Future<void> _onRefresh() async { Future<void> _onRefresh() async {
try { try {
await _documentsCubit.updateCurrentFilter( _documentsCubit.updateCurrentFilter(
(filter) => filter.copyWith(page: 1), (filter) => filter.copyWith(page: 1),
); );
_savedViewCubit.reload();
} on PaperlessServerException catch (error, stackTrace) { } on PaperlessServerException catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace); showErrorMessage(context, error, stackTrace);
} }

View File

@@ -12,6 +12,9 @@ class DocumentGridView extends StatelessWidget {
final DocumentsState state; final DocumentsState state;
final bool hasInternetConnection; final bool hasInternetConnection;
final void Function(int tagId) onTagSelected; final void Function(int tagId) onTagSelected;
final void Function(int correspondentId) onCorrespondentSelected;
final void Function(int correspondentId) onDocumentTypeSelected;
final void Function(int? id)? onStoragePathSelected;
const DocumentGridView({ const DocumentGridView({
super.key, super.key,
@@ -21,6 +24,9 @@ class DocumentGridView extends StatelessWidget {
required this.onSelected, required this.onSelected,
required this.hasInternetConnection, required this.hasInternetConnection,
required this.onTagSelected, required this.onTagSelected,
required this.onCorrespondentSelected,
required this.onDocumentTypeSelected,
this.onStoragePathSelected,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/repository/provider/label_repositories_provider.dart'; import 'package:paperless_mobile/core/repository/provider/label_repositories_provider.dart';
import 'package:paperless_mobile/core/widgets/documents_list_loading_widget.dart'; import 'package:paperless_mobile/core/widgets/documents_list_loading_widget.dart';
@@ -16,7 +15,10 @@ class DocumentListView extends StatelessWidget {
final DocumentsState state; final DocumentsState state;
final bool hasInternetConnection; final bool hasInternetConnection;
final bool isLabelClickable; final bool isLabelClickable;
final void Function(int tagId) onTagSelected; final void Function(int id)? onTagSelected;
final void Function(int? id)? onCorrespondentSelected;
final void Function(int? id)? onDocumentTypeSelected;
final void Function(int? id)? onStoragePathSelected;
const DocumentListView({ const DocumentListView({
super.key, super.key,
@@ -26,7 +28,10 @@ class DocumentListView extends StatelessWidget {
required this.onSelected, required this.onSelected,
required this.hasInternetConnection, required this.hasInternetConnection,
this.isLabelClickable = true, this.isLabelClickable = true,
required this.onTagSelected, this.onTagSelected,
this.onCorrespondentSelected,
this.onDocumentTypeSelected,
this.onStoragePathSelected,
}); });
@override @override
@@ -52,6 +57,9 @@ class DocumentListView extends StatelessWidget {
: false; : false;
}, },
onTagSelected: onTagSelected, onTagSelected: onTagSelected,
onCorrespondentSelected: onCorrespondentSelected,
onDocumentTypeSelected: onDocumentTypeSelected,
onStoragePathSelected: onStoragePathSelected,
), ),
); );
}, },

View File

@@ -14,7 +14,10 @@ class DocumentListItem extends StatelessWidget {
final bool isLabelClickable; final bool isLabelClickable;
final bool Function(int tagId) isTagSelectedPredicate; final bool Function(int tagId) isTagSelectedPredicate;
final void Function(int tagId) onTagSelected; final void Function(int tagId)? onTagSelected;
final void Function(int? correspondentId)? onCorrespondentSelected;
final void Function(int? documentTypeId)? onDocumentTypeSelected;
final void Function(int? id)? onStoragePathSelected;
const DocumentListItem({ const DocumentListItem({
Key? key, Key? key,
@@ -25,7 +28,10 @@ class DocumentListItem extends StatelessWidget {
required this.isAtLeastOneSelected, required this.isAtLeastOneSelected,
this.isLabelClickable = true, this.isLabelClickable = true,
required this.isTagSelectedPredicate, required this.isTagSelectedPredicate,
required this.onTagSelected, this.onTagSelected,
this.onCorrespondentSelected,
this.onDocumentTypeSelected,
this.onStoragePathSelected,
}) : super(key: key); }) : super(key: key);
@override @override
@@ -48,6 +54,7 @@ class DocumentListItem extends StatelessWidget {
child: CorrespondentWidget( child: CorrespondentWidget(
isClickable: isLabelClickable, isClickable: isLabelClickable,
correspondentId: document.correspondent, correspondentId: document.correspondent,
onSelected: onCorrespondentSelected,
), ),
), ),
], ],
@@ -68,7 +75,7 @@ class DocumentListItem extends StatelessWidget {
tagIds: document.tags, tagIds: document.tags,
isMultiLine: false, isMultiLine: false,
isSelectedPredicate: isTagSelectedPredicate, isSelectedPredicate: isTagSelectedPredicate,
onTagSelected: onTagSelected, onTagSelected: (id) => onTagSelected?.call(id),
), ),
), ),
), ),

View File

@@ -123,7 +123,12 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
void _resetFilter() async { void _resetFilter() async {
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
Navigator.pop(context, DocumentFilter.initial); Navigator.pop(
context,
DocumentFilter.initial.copyWith(
sortField: widget.initialFilter.sortField,
sortOrder: widget.initialFilter.sortOrder,
));
} }
Widget _buildDocumentTypeFormField() { Widget _buildDocumentTypeFormField() {

View File

@@ -100,7 +100,7 @@ class _LabelFormState<T extends Label> extends State<LabelForm<T>> {
), ),
FormBuilderCheckbox( FormBuilderCheckbox(
name: Label.isInsensitiveKey, name: Label.isInsensitiveKey,
initialValue: widget.initialValue?.isInsensitive, initialValue: widget.initialValue?.isInsensitive ?? true,
title: Text(S.of(context).labelIsInsensivitePropertyLabel), title: Text(S.of(context).labelIsInsensivitePropertyLabel),
), ),
...widget.additionalFields, ...widget.additionalFields,

View File

@@ -5,7 +5,6 @@ import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart'; import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/core/repository/saved_view_repository.dart'; import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
import 'package:paperless_mobile/core/widgets/offline_banner.dart';
import 'package:paperless_mobile/di_initializer.dart'; import 'package:paperless_mobile/di_initializer.dart';
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart'; import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
import 'package:paperless_mobile/features/documents/view/pages/documents_page.dart'; import 'package:paperless_mobile/features/documents/view/pages/documents_page.dart';

View File

@@ -9,16 +9,18 @@ import 'package:paperless_mobile/util.dart';
class CorrespondentWidget extends StatelessWidget { class CorrespondentWidget extends StatelessWidget {
final int? correspondentId; final int? correspondentId;
final void Function()? afterSelected; final void Function(int? id)? onSelected;
final Color? textColor; final Color? textColor;
final bool isClickable; final bool isClickable;
final TextStyle? textStyle;
const CorrespondentWidget({ const CorrespondentWidget({
Key? key, Key? key,
this.correspondentId, required this.correspondentId,
this.afterSelected,
this.textColor, this.textColor,
this.isClickable = true, this.isClickable = true,
this.textStyle,
this.onSelected,
}) : super(key: key); }) : super(key: key);
@override @override
@@ -30,14 +32,15 @@ class CorrespondentWidget extends StatelessWidget {
BlocBuilder<LabelCubit<Correspondent>, LabelState<Correspondent>>( BlocBuilder<LabelCubit<Correspondent>, LabelState<Correspondent>>(
builder: (context, state) { builder: (context, state) {
return GestureDetector( return GestureDetector(
onTap: () => _addCorrespondentToFilter(context), onTap: () => onSelected?.call(correspondentId!),
child: Text( child: Text(
(state.getLabel(correspondentId)?.name) ?? "-", (state.getLabel(correspondentId)?.name) ?? "-",
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodyText2?.copyWith( style: (textStyle ?? Theme.of(context).textTheme.bodyText2)
color: textColor ?? Theme.of(context).colorScheme.primary, ?.copyWith(
), color: textColor ?? Theme.of(context).colorScheme.primary,
),
), ),
); );
}, },
@@ -45,24 +48,4 @@ class CorrespondentWidget extends StatelessWidget {
), ),
); );
} }
void _addCorrespondentToFilter(BuildContext context) {
final cubit = BlocProvider.of<DocumentsCubit>(context);
try {
if (cubit.state.filter.correspondent.id == correspondentId) {
cubit.updateCurrentFilter(
(filter) =>
filter.copyWith(correspondent: const CorrespondentQuery.unset()),
);
} else {
cubit.updateCurrentFilter(
(filter) => filter.copyWith(
correspondent: CorrespondentQuery.fromId(correspondentId)),
);
}
afterSelected?.call();
} on PaperlessServerException catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace);
}
}
} }

View File

@@ -2,20 +2,20 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart'; import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
import 'package:paperless_mobile/features/labels/bloc/label_state.dart'; import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
import 'package:paperless_mobile/util.dart';
class DocumentTypeWidget extends StatelessWidget { class DocumentTypeWidget extends StatelessWidget {
final int? documentTypeId; final int? documentTypeId;
final void Function()? afterSelected;
final bool isClickable; final bool isClickable;
final TextStyle? textStyle;
final void Function(int? id)? onSelected;
const DocumentTypeWidget({ const DocumentTypeWidget({
Key? key, Key? key,
required this.documentTypeId, required this.documentTypeId,
this.afterSelected,
this.isClickable = true, this.isClickable = true,
this.textStyle,
this.onSelected,
}) : super(key: key); }) : super(key: key);
@override @override
@@ -27,16 +27,14 @@ class DocumentTypeWidget extends StatelessWidget {
child: AbsorbPointer( child: AbsorbPointer(
absorbing: !isClickable, absorbing: !isClickable,
child: GestureDetector( child: GestureDetector(
onTap: () => _addDocumentTypeToFilter(context), onTap: () => onSelected?.call(documentTypeId),
child: child:
BlocBuilder<LabelCubit<DocumentType>, LabelState<DocumentType>>( BlocBuilder<LabelCubit<DocumentType>, LabelState<DocumentType>>(
builder: (context, state) { builder: (context, state) {
return Text( return Text(
state.labels[documentTypeId]?.toString() ?? "-", state.labels[documentTypeId]?.toString() ?? "-",
style: Theme.of(context) style: (textStyle ?? Theme.of(context).textTheme.bodyText2)
.textTheme ?.copyWith(color: Theme.of(context).colorScheme.tertiary),
.bodyText2!
.copyWith(color: Theme.of(context).colorScheme.tertiary),
); );
}, },
), ),
@@ -44,24 +42,4 @@ class DocumentTypeWidget extends StatelessWidget {
), ),
); );
} }
void _addDocumentTypeToFilter(BuildContext context) {
final cubit = BlocProvider.of<DocumentsCubit>(context);
try {
if (cubit.state.filter.documentType.id == documentTypeId) {
cubit.updateCurrentFilter(
(filter) =>
filter.copyWith(documentType: const DocumentTypeQuery.unset()),
);
} else {
cubit.updateCurrentFilter(
(filter) => filter.copyWith(
documentType: DocumentTypeQuery.fromId(documentTypeId)),
);
}
afterSelected?.call();
} on PaperlessServerException catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace);
}
}
} }

View File

@@ -2,23 +2,21 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart'; import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
import 'package:paperless_mobile/features/labels/bloc/label_state.dart'; import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
import 'package:paperless_mobile/util.dart';
class StoragePathWidget extends StatelessWidget { class StoragePathWidget extends StatelessWidget {
final int? pathId; final int? pathId;
final void Function()? afterSelected;
final Color? textColor; final Color? textColor;
final bool isClickable; final bool isClickable;
final void Function(int? id)? onSelected;
const StoragePathWidget({ const StoragePathWidget({
Key? key, Key? key,
this.pathId, this.pathId,
this.afterSelected,
this.textColor, this.textColor,
this.isClickable = true, this.isClickable = true,
this.onSelected,
}) : super(key: key); }) : super(key: key);
@override @override
@@ -32,7 +30,7 @@ class StoragePathWidget extends StatelessWidget {
child: BlocBuilder<LabelCubit<StoragePath>, LabelState<StoragePath>>( child: BlocBuilder<LabelCubit<StoragePath>, LabelState<StoragePath>>(
builder: (context, state) { builder: (context, state) {
return GestureDetector( return GestureDetector(
onTap: () => _addStoragePathToFilter(context), onTap: () => onSelected?.call(pathId),
child: Text( child: Text(
state.getLabel(pathId)?.name ?? "-", state.getLabel(pathId)?.name ?? "-",
maxLines: 1, maxLines: 1,
@@ -47,24 +45,4 @@ class StoragePathWidget extends StatelessWidget {
), ),
); );
} }
void _addStoragePathToFilter(BuildContext context) {
final cubit = BlocProvider.of<DocumentsCubit>(context);
try {
if (cubit.state.filter.correspondent.id == pathId) {
cubit.updateCurrentFilter(
(filter) =>
filter.copyWith(storagePath: const StoragePathQuery.unset()),
);
} else {
cubit.updateCurrentFilter(
(filter) =>
filter.copyWith(storagePath: StoragePathQuery.fromId(pathId)),
);
}
afterSelected?.call();
} on PaperlessServerException catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace);
}
}
} }

View File

@@ -1,7 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:injectable/injectable.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/store/local_vault.dart'; import 'package:paperless_mobile/core/store/local_vault.dart';
import 'package:paperless_mobile/di_initializer.dart'; import 'package:paperless_mobile/di_initializer.dart';
@@ -11,12 +10,9 @@ import 'package:paperless_mobile/features/login/model/user_credentials.model.dar
import 'package:paperless_mobile/features/login/services/authentication_service.dart'; import 'package:paperless_mobile/features/login/services/authentication_service.dart';
import 'package:paperless_mobile/features/settings/model/application_settings_state.dart'; import 'package:paperless_mobile/features/settings/model/application_settings_state.dart';
@prod
@test
@singleton
class AuthenticationCubit extends Cubit<AuthenticationState> { class AuthenticationCubit extends Cubit<AuthenticationState> {
final LocalAuthenticationService _localAuthService; final LocalAuthenticationService _localAuthService;
final PaperlessAuthenticationApi _authApi; PaperlessAuthenticationApi _authApi;
final LocalVault _localVault; final LocalVault _localVault;
AuthenticationCubit( AuthenticationCubit(
@@ -25,10 +21,6 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
this._authApi, this._authApi,
) : super(AuthenticationState.initial); ) : super(AuthenticationState.initial);
Future<void> initialize() {
return restoreSessionState();
}
Future<void> login({ Future<void> login({
required UserCredentials credentials, required UserCredentials credentials,
required String serverUrl, required String serverUrl,
@@ -37,6 +29,8 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
assert(credentials.username != null && credentials.password != null); assert(credentials.username != null && credentials.password != null);
try { try {
registerSecurityContext(clientCertificate); registerSecurityContext(clientCertificate);
//TODO: Workaround for new architecture, listen for security context changes in timeout_client, possibly persisted in hive.
_authApi = getIt<PaperlessAuthenticationApi>();
// Store information required to make requests // Store information required to make requests
final currentAuth = AuthenticationInformation( final currentAuth = AuthenticationInformation(
serverUrl: serverUrl, serverUrl: serverUrl,
@@ -82,13 +76,16 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
} }
if (storedAuth == null || !storedAuth.isValid) { if (storedAuth == null || !storedAuth.isValid) {
return emit( return emit(
AuthenticationState(isAuthenticated: false, wasLoginStored: false)); AuthenticationState(isAuthenticated: false, wasLoginStored: false),
);
} else { } else {
if (appSettings.isLocalAuthenticationEnabled) { if (appSettings.isLocalAuthenticationEnabled) {
final localAuthSuccess = await _localAuthService final localAuthSuccess = await _localAuthService
.authenticateLocalUser("Authenticate to log back in"); .authenticateLocalUser("Authenticate to log back in");
if (localAuthSuccess) { if (localAuthSuccess) {
registerSecurityContext(storedAuth.clientCertificate); await registerSecurityContext(storedAuth.clientCertificate);
//TODO: Workaround for new architecture, listen for security context changes in timeout_client, possibly persisted in hive.
_authApi = getIt<PaperlessAuthenticationApi>();
return emit( return emit(
AuthenticationState( AuthenticationState(
isAuthenticated: true, isAuthenticated: true,
@@ -105,11 +102,13 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
)); ));
} }
} else { } else {
return emit(AuthenticationState( await registerSecurityContext(storedAuth.clientCertificate);
final authState = AuthenticationState(
isAuthenticated: true, isAuthenticated: true,
authentication: storedAuth, authentication: storedAuth,
wasLoginStored: true, wasLoginStored: true,
)); );
return emit(authState);
} }
} }
} }

View File

@@ -1,7 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:injectable/injectable.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/repository/saved_view_repository.dart'; import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_state.dart'; import 'package:paperless_mobile/features/saved_view/cubit/saved_view_state.dart';
@@ -12,22 +11,26 @@ class SavedViewCubit extends Cubit<SavedViewState> {
SavedViewCubit(this._repository) : super(SavedViewState(value: {})) { SavedViewCubit(this._repository) : super(SavedViewState(value: {})) {
_subscription = _repository.savedViews.listen( _subscription = _repository.savedViews.listen(
(savedViews) => emit(state.copyWith(value: savedViews)), (savedViews) {
if (savedViews == null) {
emit(state.copyWith(isLoaded: false));
} else {
emit(state.copyWith(value: savedViews, isLoaded: true));
}
},
); );
} }
void selectView(SavedView? view) { void selectView(SavedView? view) {
emit(SavedViewState(value: state.value, selectedSavedViewId: view?.id)); emit(state.copyWith(
selectedSavedViewId: view?.id,
overwriteSelectedSavedViewId: true,
));
} }
Future<SavedView> add(SavedView view) async { Future<SavedView> add(SavedView view) async {
final savedView = await _repository.create(view); final savedView = await _repository.create(view);
emit( emit(state.copyWith(value: {...state.value, savedView.id!: savedView}));
SavedViewState(
value: {...state.value, savedView.id!: savedView},
selectedSavedViewId: state.selectedSavedViewId,
),
);
return savedView; return savedView;
} }
@@ -42,11 +45,16 @@ class SavedViewCubit extends Cubit<SavedViewState> {
Future<void> initialize() async { Future<void> initialize() async {
final views = await _repository.findAll(); final views = await _repository.findAll();
final values = {for (var element in views) element.id!: element}; final values = {for (var element in views) element.id!: element};
emit(SavedViewState(value: values)); emit(SavedViewState(value: values, isLoaded: true));
} }
Future<void> reload() => initialize();
void resetSelection() { void resetSelection() {
emit(SavedViewState(value: state.value)); emit(SavedViewState(
value: state.value,
isLoaded: true,
));
} }
@override @override

View File

@@ -2,11 +2,13 @@ import 'package:equatable/equatable.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
class SavedViewState with EquatableMixin { class SavedViewState with EquatableMixin {
final bool isLoaded;
final Map<int, SavedView> value; final Map<int, SavedView> value;
final int? selectedSavedViewId; final int? selectedSavedViewId;
SavedViewState({ SavedViewState({
required this.value, required this.value,
this.isLoaded = false,
this.selectedSavedViewId, this.selectedSavedViewId,
}); });
@@ -20,9 +22,11 @@ class SavedViewState with EquatableMixin {
Map<int, SavedView>? value, Map<int, SavedView>? value,
int? selectedSavedViewId, int? selectedSavedViewId,
bool overwriteSelectedSavedViewId = false, bool overwriteSelectedSavedViewId = false,
bool? isLoaded,
}) { }) {
return SavedViewState( return SavedViewState(
value: value ?? this.value, value: value ?? this.value,
isLoaded: isLoaded ?? this.isLoaded,
selectedSavedViewId: overwriteSelectedSavedViewId selectedSavedViewId: overwriteSelectedSavedViewId
? selectedSavedViewId ? selectedSavedViewId
: this.selectedSavedViewId, : this.selectedSavedViewId,

View File

@@ -1,3 +1,5 @@
import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
@@ -8,6 +10,7 @@ import 'package:paperless_mobile/features/saved_view/cubit/saved_view_state.dart
import 'package:paperless_mobile/features/saved_view/view/add_saved_view_page.dart'; import 'package:paperless_mobile/features/saved_view/view/add_saved_view_page.dart';
import 'package:paperless_mobile/generated/l10n.dart'; import 'package:paperless_mobile/generated/l10n.dart';
import 'package:paperless_mobile/util.dart'; import 'package:paperless_mobile/util.dart';
import 'package:shimmer/shimmer.dart';
class SavedViewSelectionWidget extends StatelessWidget { class SavedViewSelectionWidget extends StatelessWidget {
final DocumentFilter currentFilter; final DocumentFilter currentFilter;
@@ -29,6 +32,9 @@ class SavedViewSelectionWidget extends StatelessWidget {
children: [ children: [
BlocBuilder<SavedViewCubit, SavedViewState>( BlocBuilder<SavedViewCubit, SavedViewState>(
builder: (context, state) { builder: (context, state) {
if (!state.isLoaded) {
return _buildLoadingWidget(context);
}
if (state.value.isEmpty) { if (state.value.isEmpty) {
return Text(S.of(context).savedViewsEmptyStateText); return Text(S.of(context).savedViewsEmptyStateText);
} }
@@ -58,32 +64,61 @@ class SavedViewSelectionWidget extends StatelessWidget {
); );
}, },
), ),
Row( BlocBuilder<SavedViewCubit, SavedViewState>(
mainAxisAlignment: MainAxisAlignment.spaceBetween, builder: (context, state) {
children: [ return Row(
Text( mainAxisAlignment: MainAxisAlignment.spaceBetween,
S.of(context).savedViewsLabel, children: [
style: Theme.of(context).textTheme.titleSmall, Text(
), S.of(context).savedViewsLabel,
BlocBuilder<DocumentsCubit, DocumentsState>( style: Theme.of(context).textTheme.titleSmall,
buildWhen: (previous, current) => ),
previous.filter != current.filter, BlocBuilder<DocumentsCubit, DocumentsState>(
builder: (context, docState) { buildWhen: (previous, current) =>
return TextButton.icon( previous.filter != current.filter,
icon: const Icon(Icons.add), builder: (context, docState) {
onPressed: enabled return TextButton.icon(
? () => _onCreatePressed(context, docState.filter) icon: const Icon(Icons.add),
: null, onPressed: (enabled && state.isLoaded)
label: Text(S.of(context).savedViewCreateNewLabel), ? () => _onCreatePressed(context, docState.filter)
); : null,
}, label: Text(S.of(context).savedViewCreateNewLabel),
), );
], },
),
],
);
},
), ),
], ],
); );
} }
Widget _buildLoadingWidget(BuildContext context) {
final r = Random(123456789);
return SizedBox(
height: height,
width: double.infinity,
child: Shimmer.fromColors(
baseColor: Theme.of(context).brightness == Brightness.light
? Colors.grey[300]!
: Colors.grey[900]!,
highlightColor: Theme.of(context).brightness == Brightness.light
? Colors.grey[100]!
: Colors.grey[600]!,
child: ListView.separated(
scrollDirection: Axis.horizontal,
physics: const NeverScrollableScrollPhysics(),
itemCount: 10,
itemBuilder: (context, index) => FilterChip(
label: SizedBox(width: r.nextInt((index * 20) + 50).toDouble()),
onSelected: null),
separatorBuilder: (context, index) => SizedBox(width: 8.0),
),
),
);
}
void _onCreatePressed(BuildContext context, DocumentFilter filter) async { void _onCreatePressed(BuildContext context, DocumentFilter filter) async {
final newView = await Navigator.of(context).push<SavedView?>( final newView = await Navigator.of(context).push<SavedView?>(
MaterialPageRoute( MaterialPageRoute(

View File

@@ -1,3 +1,4 @@
import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -34,6 +35,7 @@ import 'package:paperless_mobile/features/document_upload/cubit/document_upload_
import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart'; import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart';
import 'package:paperless_mobile/features/home/view/home_page.dart'; import 'package:paperless_mobile/features/home/view/home_page.dart';
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart'; import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
import 'package:paperless_mobile/features/login/services/authentication_service.dart';
import 'package:paperless_mobile/features/login/view/login_page.dart'; import 'package:paperless_mobile/features/login/view/login_page.dart';
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart'; import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
import 'package:paperless_mobile/features/settings/model/application_settings_state.dart'; import 'package:paperless_mobile/features/settings/model/application_settings_state.dart';
@@ -57,7 +59,13 @@ void main() async {
// Load application settings and stored authentication data // Load application settings and stored authentication data
await getIt<ConnectivityCubit>().initialize(); await getIt<ConnectivityCubit>().initialize();
await getIt<ApplicationSettingsCubit>().initialize(); await getIt<ApplicationSettingsCubit>().initialize();
await getIt<AuthenticationCubit>().initialize();
final authCubit = AuthenticationCubit(
getIt<LocalVault>(),
getIt<LocalAuthenticationService>(),
getIt<PaperlessAuthenticationApi>(),
);
await authCubit.restoreSessionState();
// Create repositories // Create repositories
final LabelRepository<Tag> tagRepository = final LabelRepository<Tag> tagRepository =
@@ -80,13 +88,17 @@ void main() async {
RepositoryProvider.value(value: storagePathRepository), RepositoryProvider.value(value: storagePathRepository),
RepositoryProvider.value(value: savedViewRepository), RepositoryProvider.value(value: savedViewRepository),
], ],
child: const PaperlessMobileEntrypoint(), child: PaperlessMobileEntrypoint(authenticationCubit: authCubit),
), ),
); );
} }
class PaperlessMobileEntrypoint extends StatefulWidget { class PaperlessMobileEntrypoint extends StatefulWidget {
const PaperlessMobileEntrypoint({Key? key}) : super(key: key); final AuthenticationCubit authenticationCubit;
const PaperlessMobileEntrypoint({
Key? key,
required this.authenticationCubit,
}) : super(key: key);
@override @override
State<PaperlessMobileEntrypoint> createState() => State<PaperlessMobileEntrypoint> createState() =>
@@ -165,8 +177,8 @@ class _PaperlessMobileEntrypointState extends State<PaperlessMobileEntrypoint> {
GlobalWidgetsLocalizations.delegate, GlobalWidgetsLocalizations.delegate,
FormBuilderLocalizations.delegate, FormBuilderLocalizations.delegate,
], ],
home: BlocProvider<AuthenticationCubit>.value( home: BlocProvider.value(
value: getIt<AuthenticationCubit>(), value: widget.authenticationCubit,
child: const AuthenticationWrapper(), child: const AuthenticationWrapper(),
), ),
); );
@@ -291,10 +303,10 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
if (authentication.isAuthenticated) { if (authentication.isAuthenticated) {
return const HomePage(); return const HomePage();
} else { } else {
// if (authentication.wasLoginStored && if (authentication.wasLoginStored &&
// !(authentication.wasLocalAuthenticationSuccessful ?? false)) { !(authentication.wasLocalAuthenticationSuccessful ?? false)) {
// return const BiometricAuthenticationPage(); return const BiometricAuthenticationPage();
// } }
return const LoginPage(); return const LoginPage();
} }
}, },

View File

@@ -261,7 +261,7 @@ class PaperlessLabelApiImpl implements PaperlessLabelsApi {
@override @override
Future<StoragePath?> getStoragePath(int id) { Future<StoragePath?> getStoragePath(int id) {
return getSingleResult( return getSingleResult(
"/api/storage_paths/?page=1&page_size=100000", "/api/storage_paths/$id/",
StoragePath.fromJson, StoragePath.fromJson,
ErrorCode.storagePathLoadFailed, ErrorCode.storagePathLoadFailed,
client: client, client: client,