Removed suggestions from inbox, added translations, added paging to inbox, visual updates, changed default matching algorithm to auto

This commit is contained in:
Anton Stubenbord
2023-01-20 00:34:18 +01:00
parent bfbf0a6f0e
commit f9dfddf704
56 changed files with 1748 additions and 766 deletions

View File

@@ -0,0 +1,163 @@
import 'package:collection/collection.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart';
import 'model/documents_paged_state.dart';
///
/// Mixin which can be used on cubits which handle documents. This implements all paging and filtering logic.
///
mixin DocumentsPagingMixin<State extends DocumentsPagedState>
on BlocBase<State> {
PaperlessDocumentsApi get api;
Future<void> loadMore() async {
if (state.isLastPageLoaded) {
return;
}
emit(state.copyWithPaged(isLoading: true));
final newFilter = state.filter.copyWith(page: state.filter.page + 1);
try {
final result = await api.findAll(newFilter);
emit(state.copyWithPaged(
hasLoaded: true,
filter: newFilter,
value: [...state.value, result],
));
} finally {
emit(state.copyWithPaged(isLoading: false));
}
}
///
/// Updates document filter and automatically reloads documents. Always resets page to 1.
/// Use [loadMore] to load more data.
Future<void> updateFilter({
final DocumentFilter filter = DocumentFilter.initial,
}) async {
try {
emit(state.copyWithPaged(isLoading: true));
final result = await api.findAll(filter.copyWith(page: 1));
emit(state.copyWithPaged(
filter: filter,
value: [result],
hasLoaded: true,
));
} finally {
emit(state.copyWithPaged(isLoading: false));
}
}
///
/// Convenience method which allows to directly use [DocumentFilter.copyWith] on the current filter.
///
Future<void> updateCurrentFilter(
final DocumentFilter Function(DocumentFilter) transformFn,
) async =>
updateFilter(filter: transformFn(state.filter));
Future<void> resetFilter() {
final filter = DocumentFilter.initial.copyWith(
sortField: state.filter.sortField,
sortOrder: state.filter.sortOrder,
);
return updateFilter(filter: filter);
}
Future<void> reload() async {
emit(state.copyWithPaged(isLoading: true));
try {
final filter = state.filter.copyWith(page: 1);
final result = await api.findAll(filter);
emit(state.copyWithPaged(
hasLoaded: true,
value: [result],
isLoading: false,
filter: filter,
));
} finally {
emit(state.copyWithPaged(isLoading: false));
}
}
///
/// Updates a document. If [shouldReload] is false, the updated document will
/// replace the currently loaded one, otherwise all documents will be reloaded.
///
Future<void> update(
DocumentModel document, {
bool shouldReload = true,
}) async {
final updatedDocument = await api.update(document);
if (shouldReload) {
await reload();
} else {
replace(updatedDocument);
}
}
///
/// Deletes a document and removes it from the currently loaded state.
///
Future<void> delete(DocumentModel document) async {
emit(state.copyWithPaged(isLoading: true));
try {
await api.delete(document);
await remove(document);
} finally {
emit(state.copyWithPaged(isLoading: false));
}
}
///
/// Removes [document] from the currently loaded state.
/// Does not delete it from the server!
///
Future<void> remove(DocumentModel document) async {
final index = state.value.indexWhere(
(page) => page.results.any((element) => element.id == document.id),
);
if (index != -1) {
final foundPage = state.value[index];
final replacementPage = foundPage.copyWith(
results: foundPage.results
..removeWhere((element) => element.id == document.id),
);
final newCount = foundPage.count - 1;
emit(
state.copyWithPaged(
value: state.value
.mapIndexed(
(currIndex, element) =>
(currIndex == index ? replacementPage : element)
.copyWith(count: newCount),
)
.toList(),
),
);
}
}
///
/// Replaces the document with the same id as [document] from the currently
/// loaded state.
///
Future<void> replace(DocumentModel document) async {
final index = state.value.indexWhere(
(page) => page.results.any((element) => element.id == document.id),
);
if (index != -1) {
final foundPage = state.value[index];
final replacementPage = foundPage.copyWith(
results: foundPage.results..replaceRange(index, index + 1, [document]),
);
emit(state.copyWithPaged(
value: state.value
.mapIndexed((currIndex, element) =>
currIndex == index ? replacementPage : element)
.toList(),
));
}
}
}

View File

@@ -0,0 +1,70 @@
import 'package:equatable/equatable.dart';
import 'package:paperless_api/paperless_api.dart';
abstract class DocumentsPagedState extends Equatable {
final bool hasLoaded;
final bool isLoading;
final List<PagedSearchResult<DocumentModel>> value;
final DocumentFilter filter;
const DocumentsPagedState({
this.value = const [],
this.hasLoaded = false,
this.isLoading = false,
this.filter = const DocumentFilter(),
});
List<DocumentModel> get documents {
return value.fold(
[],
(previousValue, element) => [
...previousValue,
...element.results,
],
);
}
int get currentPageNumber {
assert(value.isNotEmpty);
return value.last.pageKey;
}
int? get nextPageNumber {
return isLastPageLoaded ? null : currentPageNumber + 1;
}
int get count {
if (value.isEmpty) {
return 0;
}
return value.first.count;
}
bool get isLastPageLoaded {
if (!hasLoaded) {
return false;
}
if (value.isNotEmpty) {
return value.last.next == null;
}
return true;
}
int inferPageCount({required int pageSize}) {
if (!hasLoaded) {
return 100000;
}
if (value.isEmpty) {
return 0;
}
return value.first.inferPageCount(pageSize: pageSize);
}
// Return type has to be dynamic
dynamic copyWithPaged({
bool? hasLoaded,
bool? isLoading,
List<PagedSearchResult<DocumentModel>>? value,
DocumentFilter? filter,
});
}