mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-09 18:07:50 -06:00
Implemented inbox (still WIP)
This commit is contained in:
27
lib/features/labels/bloc/label_bloc_provider.dart
Normal file
27
lib/features/labels/bloc/label_bloc_provider.dart
Normal file
@@ -0,0 +1,27 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/saved_view_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.dart';
|
||||
|
||||
class LabelBlocProvider extends StatelessWidget {
|
||||
final Widget child;
|
||||
const LabelBlocProvider({super.key, required this.child});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider.value(value: getIt<DocumentTypeCubit>()),
|
||||
BlocProvider.value(value: getIt<CorrespondentCubit>()),
|
||||
BlocProvider.value(value: getIt<TagCubit>()),
|
||||
BlocProvider.value(value: getIt<StoragePathCubit>()),
|
||||
BlocProvider.value(value: getIt<SavedViewCubit>()),
|
||||
],
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
75
lib/features/labels/bloc/label_cubit.dart
Normal file
75
lib/features/labels/bloc/label_cubit.dart
Normal file
@@ -0,0 +1,75 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/repository/label_repository.dart';
|
||||
|
||||
abstract class LabelCubit<T extends Label> extends Cubit<LabelState<T>> {
|
||||
final LabelRepository labelRepository;
|
||||
|
||||
LabelCubit(this.labelRepository) : super(LabelState.initial());
|
||||
|
||||
@protected
|
||||
void loadFrom(Iterable<T> items) {
|
||||
emit(
|
||||
LabelState(
|
||||
isLoaded: true,
|
||||
labels: Map.fromIterable(items, key: (e) => (e as T).id!),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<T> add(T item) async {
|
||||
assert(item.id == null);
|
||||
final addedItem = await save(item);
|
||||
final newValues = {...state.labels};
|
||||
newValues.putIfAbsent(addedItem.id!, () => addedItem);
|
||||
emit(
|
||||
LabelState(
|
||||
isLoaded: true,
|
||||
labels: newValues,
|
||||
),
|
||||
);
|
||||
return addedItem;
|
||||
}
|
||||
|
||||
Future<T> replace(T item) async {
|
||||
assert(item.id != null);
|
||||
final updatedItem = await update(item);
|
||||
final updatedValues = {...state.labels};
|
||||
updatedValues[item.id!] = updatedItem;
|
||||
emit(
|
||||
LabelState(
|
||||
isLoaded: state.isLoaded,
|
||||
labels: updatedValues,
|
||||
),
|
||||
);
|
||||
return updatedItem;
|
||||
}
|
||||
|
||||
Future<void> remove(T item) async {
|
||||
assert(item.id != null);
|
||||
if (state.labels.containsKey(item.id)) {
|
||||
final deletedId = await delete(item);
|
||||
final updatedValues = {...state.labels}..remove(deletedId);
|
||||
emit(
|
||||
LabelState(isLoaded: true, labels: updatedValues),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
emit(LabelState(isLoaded: false, labels: {}));
|
||||
}
|
||||
|
||||
Future<void> initialize();
|
||||
|
||||
@protected
|
||||
Future<T> save(T item);
|
||||
|
||||
@protected
|
||||
Future<T> update(T item);
|
||||
|
||||
@protected
|
||||
Future<int> delete(T item);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:paperless_mobile/core/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/model/correspondent.model.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/correspondent_query.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/model/correspondent.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label_state.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
|
||||
class CorrespondentWidget extends StatelessWidget {
|
||||
@@ -16,7 +17,7 @@ class CorrespondentWidget extends StatelessWidget {
|
||||
|
||||
const CorrespondentWidget({
|
||||
Key? key,
|
||||
required this.correspondentId,
|
||||
this.correspondentId,
|
||||
this.afterSelected,
|
||||
this.textColor,
|
||||
this.isClickable = true,
|
||||
@@ -26,12 +27,12 @@ class CorrespondentWidget extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return AbsorbPointer(
|
||||
absorbing: !isClickable,
|
||||
child: BlocBuilder<CorrespondentCubit, Map<int, Correspondent>>(
|
||||
child: BlocBuilder<CorrespondentCubit, LabelState<Correspondent>>(
|
||||
builder: (context, state) {
|
||||
return GestureDetector(
|
||||
onTap: () => _addCorrespondentToFilter(context),
|
||||
child: Text(
|
||||
(state[correspondentId]?.name) ?? "-",
|
||||
(state.getLabel(correspondentId)?.name) ?? "-",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:paperless_mobile/core/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/model/document_type.model.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/document_type_query.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/model/document_type.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label_state.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
|
||||
class DocumentTypeWidget extends StatelessWidget {
|
||||
@@ -24,10 +25,10 @@ class DocumentTypeWidget extends StatelessWidget {
|
||||
absorbing: !isClickable,
|
||||
child: GestureDetector(
|
||||
onTap: () => _addDocumentTypeToFilter(context),
|
||||
child: BlocBuilder<DocumentTypeCubit, Map<int, DocumentType>>(
|
||||
child: BlocBuilder<DocumentTypeCubit, LabelState<DocumentType>>(
|
||||
builder: (context, state) {
|
||||
return Text(
|
||||
state[documentTypeId]?.toString() ?? "-",
|
||||
state.labels[documentTypeId]?.toString() ?? "-",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyText2!
|
||||
|
||||
19
lib/features/labels/model/label_state.dart
Normal file
19
lib/features/labels/model/label_state.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import 'package:paperless_mobile/features/labels/model/label.model.dart';
|
||||
|
||||
class LabelState<T extends Label> {
|
||||
LabelState.initial() : this(isLoaded: false, labels: {});
|
||||
final bool isLoaded;
|
||||
final Map<int, T> labels;
|
||||
|
||||
LabelState({
|
||||
required this.isLoaded,
|
||||
required this.labels,
|
||||
});
|
||||
|
||||
T? getLabel(int? key) {
|
||||
if (isLoaded) {
|
||||
return labels[key];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:paperless_mobile/core/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/storage_path/model/storage_path.model.dart';
|
||||
|
||||
@singleton
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/storage_path_query.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/storage_path/model/storage_path.model.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
@@ -15,7 +16,7 @@ class StoragePathWidget extends StatelessWidget {
|
||||
|
||||
const StoragePathWidget({
|
||||
Key? key,
|
||||
required this.pathId,
|
||||
this.pathId,
|
||||
this.afterSelected,
|
||||
this.textColor,
|
||||
this.isClickable = true,
|
||||
@@ -25,12 +26,12 @@ class StoragePathWidget extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return AbsorbPointer(
|
||||
absorbing: !isClickable,
|
||||
child: BlocBuilder<StoragePathCubit, Map<int, StoragePath>>(
|
||||
child: BlocBuilder<StoragePathCubit, LabelState<StoragePath>>(
|
||||
builder: (context, state) {
|
||||
return GestureDetector(
|
||||
onTap: () => _addStoragePathToFilter(context),
|
||||
child: Text(
|
||||
(state[pathId]?.name) ?? "-",
|
||||
state.getLabel(pathId)?.name ?? "-",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:paperless_mobile/core/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/model/tag.model.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:paperless_mobile/core/bloc/paperless_statistics_cubit.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document_filter.dart';
|
||||
@@ -21,7 +22,11 @@ class EditTagPage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return EditLabelPage<Tag>(
|
||||
label: tag,
|
||||
onSubmit: BlocProvider.of<TagCubit>(context).replace,
|
||||
onSubmit: (tag) async {
|
||||
await BlocProvider.of<TagCubit>(context).replace(tag);
|
||||
//If inbox property was added/removed from tag, the number of documetns in inbox may increase/decrease.
|
||||
BlocProvider.of<PaperlessStatisticsCubit>(context).updateStatistics();
|
||||
},
|
||||
onDelete: (tag) => _onDelete(tag, context),
|
||||
fromJson: Tag.fromJson,
|
||||
additionalFields: [
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/tags_query.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/model/tag.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/view/pages/add_tag_page.dart';
|
||||
@@ -45,7 +46,7 @@ class _TagFormFieldState extends State<TagFormField> {
|
||||
_textEditingController = TextEditingController()
|
||||
..addListener(() {
|
||||
setState(() {
|
||||
_showCreationSuffixIcon = state.values
|
||||
_showCreationSuffixIcon = state.labels.values
|
||||
.where(
|
||||
(item) => item.name.toLowerCase().startsWith(
|
||||
_textEditingController.text.toLowerCase(),
|
||||
@@ -61,7 +62,7 @@ class _TagFormFieldState extends State<TagFormField> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<TagCubit, Map<int, Tag>>(
|
||||
return BlocBuilder<TagCubit, LabelState<Tag>>(
|
||||
builder: (context, tagState) {
|
||||
return FormBuilderField<TagsQuery>(
|
||||
builder: (field) {
|
||||
@@ -81,7 +82,7 @@ class _TagFormFieldState extends State<TagFormField> {
|
||||
controller: _textEditingController,
|
||||
),
|
||||
suggestionsCallback: (query) {
|
||||
final suggestions = tagState.values
|
||||
final suggestions = tagState.labels.values
|
||||
.where((element) => element.name
|
||||
.toLowerCase()
|
||||
.startsWith(query.toLowerCase()))
|
||||
@@ -113,7 +114,7 @@ class _TagFormFieldState extends State<TagFormField> {
|
||||
title: Text(S.of(context).labelAnyAssignedText),
|
||||
);
|
||||
}
|
||||
final tag = tagState[data]!;
|
||||
final tag = tagState.getLabel(data)!;
|
||||
return ListTile(
|
||||
leading: Icon(
|
||||
Icons.circle,
|
||||
@@ -159,7 +160,7 @@ class _TagFormFieldState extends State<TagFormField> {
|
||||
(query) => _buildTag(
|
||||
field,
|
||||
query,
|
||||
tagState[query.id]!,
|
||||
tagState.getLabel(query.id)!,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/model/tag.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tag_widget.dart';
|
||||
@@ -27,13 +28,13 @@ class TagsWidget extends StatefulWidget {
|
||||
class _TagsWidgetState extends State<TagsWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<TagCubit, Map<int, Tag>>(
|
||||
return BlocBuilder<TagCubit, LabelState<Tag>>(
|
||||
builder: (context, state) {
|
||||
final children = widget.tagIds
|
||||
.where((id) => state.containsKey(id))
|
||||
.where((id) => state.labels.containsKey(id))
|
||||
.map(
|
||||
(id) => TagWidget(
|
||||
tag: state[id]!,
|
||||
tag: state.getLabel(id)!,
|
||||
afterTagTapped: widget.afterTagTapped,
|
||||
isClickable: widget.isClickable,
|
||||
),
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:paperless_mobile/core/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/bloc/label_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/core/bloc/paperless_statistics_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document_filter.dart';
|
||||
@@ -227,6 +228,7 @@ class _LabelsPageState extends State<LabelsPage>
|
||||
providers: [
|
||||
BlocProvider.value(value: getIt<DocumentsCubit>()),
|
||||
BlocProvider.value(value: BlocProvider.of<TagCubit>(context)),
|
||||
BlocProvider.value(value: getIt<PaperlessStatisticsCubit>()),
|
||||
],
|
||||
child: EditTagPage(tag: tag),
|
||||
),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/bloc/label_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||
import 'package:paperless_mobile/core/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/core/widgets/offline_widget.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document_filter.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/view/widgets/label_item.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
|
||||
@@ -45,10 +46,10 @@ class LabelTabView<T extends Label> extends StatelessWidget {
|
||||
}
|
||||
return RefreshIndicator(
|
||||
onRefresh: cubit.initialize,
|
||||
child: BlocBuilder<Cubit<Map<int, T>>, Map<int, T>>(
|
||||
child: BlocBuilder<Cubit<LabelState<T>>, LabelState<T>>(
|
||||
bloc: cubit,
|
||||
builder: (context, state) {
|
||||
final labels = state.values.toList()..sort();
|
||||
final labels = state.labels.values.toList()..sort();
|
||||
if (labels.isEmpty) {
|
||||
return Center(
|
||||
child: Column(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/bloc/label_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document.model.dart';
|
||||
|
||||
Reference in New Issue
Block a user