fix: Add labels to each cubit using repositories and state properties, remove label cubits

This commit is contained in:
Anton Stubenbord
2023-04-04 20:30:25 +02:00
parent 78fbd042a6
commit a2388b014b
95 changed files with 4790 additions and 1823 deletions

View File

@@ -1,68 +1,45 @@
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:bloc/bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/features/labels/cubit/label_cubit_mixin.dart';
part 'label_state.dart';
part 'label_cubit.freezed.dart';
class LabelCubit<T extends Label> extends Cubit<LabelState<T>> {
final LabelRepository<T> _repository;
class LabelCubit extends Cubit<LabelState> with LabelCubitMixin<LabelState> {
@override
final LabelRepository labelRepository;
late StreamSubscription _subscription;
LabelCubit(LabelRepository<T> repository)
: _repository = repository,
super(LabelState(
isLoaded: repository.isInitialized,
labels: repository.current?.values ?? {},
)) {
_subscription = _repository.values.listen(
(event) {
if (event == null) {
emit(LabelState());
}
emit(
LabelState(isLoaded: event!.hasLoaded, labels: event.values ?? {}));
LabelCubit(this.labelRepository) : super(const LabelState()) {
labelRepository.subscribe(
this,
onChanged: (labels) {
emit(state.copyWith(
correspondents: labels.correspondents,
documentTypes: labels.documentTypes,
storagePaths: labels.storagePaths,
tags: labels.tags,
));
},
);
}
///
/// Adds [item] to the current state. A new state is automatically pushed
/// due to the subscription to the repository, which updates the state on
/// operation.
///
Future<T> add(T item) async {
assert(item.id == null);
final addedItem = await _repository.create(item);
return addedItem;
}
Future<void> reload() {
return _repository.findAll();
}
Future<T> replace(T item) async {
assert(item.id != null);
final updatedItem = await _repository.update(item);
return updatedItem;
}
Future<void> remove(T item) async {
assert(item.id != null);
if (state.labels.containsKey(item.id)) {
await _repository.delete(item);
}
}
void reset() {
emit(LabelState(isLoaded: false, labels: {}));
@override
Future<void> close() {
labelRepository.unsubscribe(this);
return super.close();
}
@override
Future<void> close() {
_subscription.cancel();
return super.close();
}
Map<int, Correspondent> get correspondents => state.correspondents;
@override
Map<int, DocumentType> get documentTypes => state.documentTypes;
@override
Map<int, StoragePath> get storagePaths => state.storagePaths;
@override
Map<int, Tag> get tags => state.tags;
}

View File

@@ -0,0 +1,237 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'label_cubit.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
/// @nodoc
mixin _$LabelState {
Map<int, Correspondent> get correspondents =>
throw _privateConstructorUsedError;
Map<int, DocumentType> get documentTypes =>
throw _privateConstructorUsedError;
Map<int, Tag> get tags => throw _privateConstructorUsedError;
Map<int, StoragePath> get storagePaths => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$LabelStateCopyWith<LabelState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $LabelStateCopyWith<$Res> {
factory $LabelStateCopyWith(
LabelState value, $Res Function(LabelState) then) =
_$LabelStateCopyWithImpl<$Res, LabelState>;
@useResult
$Res call(
{Map<int, Correspondent> correspondents,
Map<int, DocumentType> documentTypes,
Map<int, Tag> tags,
Map<int, StoragePath> storagePaths});
}
/// @nodoc
class _$LabelStateCopyWithImpl<$Res, $Val extends LabelState>
implements $LabelStateCopyWith<$Res> {
_$LabelStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? correspondents = null,
Object? documentTypes = null,
Object? tags = null,
Object? storagePaths = null,
}) {
return _then(_value.copyWith(
correspondents: null == correspondents
? _value.correspondents
: correspondents // ignore: cast_nullable_to_non_nullable
as Map<int, Correspondent>,
documentTypes: null == documentTypes
? _value.documentTypes
: documentTypes // ignore: cast_nullable_to_non_nullable
as Map<int, DocumentType>,
tags: null == tags
? _value.tags
: tags // ignore: cast_nullable_to_non_nullable
as Map<int, Tag>,
storagePaths: null == storagePaths
? _value.storagePaths
: storagePaths // ignore: cast_nullable_to_non_nullable
as Map<int, StoragePath>,
) as $Val);
}
}
/// @nodoc
abstract class _$$_LabelStateCopyWith<$Res>
implements $LabelStateCopyWith<$Res> {
factory _$$_LabelStateCopyWith(
_$_LabelState value, $Res Function(_$_LabelState) then) =
__$$_LabelStateCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{Map<int, Correspondent> correspondents,
Map<int, DocumentType> documentTypes,
Map<int, Tag> tags,
Map<int, StoragePath> storagePaths});
}
/// @nodoc
class __$$_LabelStateCopyWithImpl<$Res>
extends _$LabelStateCopyWithImpl<$Res, _$_LabelState>
implements _$$_LabelStateCopyWith<$Res> {
__$$_LabelStateCopyWithImpl(
_$_LabelState _value, $Res Function(_$_LabelState) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? correspondents = null,
Object? documentTypes = null,
Object? tags = null,
Object? storagePaths = null,
}) {
return _then(_$_LabelState(
correspondents: null == correspondents
? _value._correspondents
: correspondents // ignore: cast_nullable_to_non_nullable
as Map<int, Correspondent>,
documentTypes: null == documentTypes
? _value._documentTypes
: documentTypes // ignore: cast_nullable_to_non_nullable
as Map<int, DocumentType>,
tags: null == tags
? _value._tags
: tags // ignore: cast_nullable_to_non_nullable
as Map<int, Tag>,
storagePaths: null == storagePaths
? _value._storagePaths
: storagePaths // ignore: cast_nullable_to_non_nullable
as Map<int, StoragePath>,
));
}
}
/// @nodoc
class _$_LabelState implements _LabelState {
const _$_LabelState(
{final Map<int, Correspondent> correspondents = const {},
final Map<int, DocumentType> documentTypes = const {},
final Map<int, Tag> tags = const {},
final Map<int, StoragePath> storagePaths = const {}})
: _correspondents = correspondents,
_documentTypes = documentTypes,
_tags = tags,
_storagePaths = storagePaths;
final Map<int, Correspondent> _correspondents;
@override
@JsonKey()
Map<int, Correspondent> get correspondents {
if (_correspondents is EqualUnmodifiableMapView) return _correspondents;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_correspondents);
}
final Map<int, DocumentType> _documentTypes;
@override
@JsonKey()
Map<int, DocumentType> get documentTypes {
if (_documentTypes is EqualUnmodifiableMapView) return _documentTypes;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_documentTypes);
}
final Map<int, Tag> _tags;
@override
@JsonKey()
Map<int, Tag> get tags {
if (_tags is EqualUnmodifiableMapView) return _tags;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_tags);
}
final Map<int, StoragePath> _storagePaths;
@override
@JsonKey()
Map<int, StoragePath> get storagePaths {
if (_storagePaths is EqualUnmodifiableMapView) return _storagePaths;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_storagePaths);
}
@override
String toString() {
return 'LabelState(correspondents: $correspondents, documentTypes: $documentTypes, tags: $tags, storagePaths: $storagePaths)';
}
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$_LabelState &&
const DeepCollectionEquality()
.equals(other._correspondents, _correspondents) &&
const DeepCollectionEquality()
.equals(other._documentTypes, _documentTypes) &&
const DeepCollectionEquality().equals(other._tags, _tags) &&
const DeepCollectionEquality()
.equals(other._storagePaths, _storagePaths));
}
@override
int get hashCode => Object.hash(
runtimeType,
const DeepCollectionEquality().hash(_correspondents),
const DeepCollectionEquality().hash(_documentTypes),
const DeepCollectionEquality().hash(_tags),
const DeepCollectionEquality().hash(_storagePaths));
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_LabelStateCopyWith<_$_LabelState> get copyWith =>
__$$_LabelStateCopyWithImpl<_$_LabelState>(this, _$identity);
}
abstract class _LabelState implements LabelState {
const factory _LabelState(
{final Map<int, Correspondent> correspondents,
final Map<int, DocumentType> documentTypes,
final Map<int, Tag> tags,
final Map<int, StoragePath> storagePaths}) = _$_LabelState;
@override
Map<int, Correspondent> get correspondents;
@override
Map<int, DocumentType> get documentTypes;
@override
Map<int, Tag> get tags;
@override
Map<int, StoragePath> get storagePaths;
@override
@JsonKey(ignore: true)
_$$_LabelStateCopyWith<_$_LabelState> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,104 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart';
mixin LabelCubitMixin<T> on BlocBase<T> {
LabelRepository get labelRepository;
Map<int, Correspondent> get correspondents;
Map<int, DocumentType> get documentTypes;
Map<int, Tag> get tags;
Map<int, StoragePath> get storagePaths;
Future<Correspondent> addCorrespondent(Correspondent item) async {
assert(item.id == null);
final addedItem = await labelRepository.createCorrespondent(item);
return addedItem;
}
Future<void> reloadCorrespondents() {
return labelRepository.findAllCorrespondents();
}
Future<Correspondent> replaceCorrespondent(Correspondent item) async {
assert(item.id != null);
final updatedItem = await labelRepository.updateCorrespondent(item);
return updatedItem;
}
Future<void> removeCorrespondent(Correspondent item) async {
assert(item.id != null);
if (correspondents.containsKey(item.id)) {
await labelRepository.deleteCorrespondent(item);
}
}
Future<DocumentType> addDocumentType(DocumentType item) async {
assert(item.id == null);
final addedItem = await labelRepository.createDocumentType(item);
return addedItem;
}
Future<void> reloadDocumentTypes() {
return labelRepository.findAllDocumentTypes();
}
Future<DocumentType> replaceDocumentType(DocumentType item) async {
assert(item.id != null);
final updatedItem = await labelRepository.updateDocumentType(item);
return updatedItem;
}
Future<void> removeDocumentType(DocumentType item) async {
assert(item.id != null);
if (documentTypes.containsKey(item.id)) {
await labelRepository.deleteDocumentType(item);
}
}
Future<StoragePath> addStoragePath(StoragePath item) async {
assert(item.id == null);
final addedItem = await labelRepository.createStoragePath(item);
return addedItem;
}
Future<void> reloadStoragePaths() {
return labelRepository.findAllStoragePaths();
}
Future<StoragePath> replaceStoragePath(StoragePath item) async {
assert(item.id != null);
final updatedItem = await labelRepository.updateStoragePath(item);
return updatedItem;
}
Future<void> removeStoragePath(StoragePath item) async {
assert(item.id != null);
if (storagePaths.containsKey(item.id)) {
await labelRepository.deleteStoragePath(item);
}
}
Future<Tag> addTag(Tag item) async {
assert(item.id == null);
final addedItem = await labelRepository.createTag(item);
return addedItem;
}
Future<void> reloadTags() {
return labelRepository.findAllTags();
}
Future<Tag> replaceTag(Tag item) async {
assert(item.id != null);
final updatedItem = await labelRepository.updateTag(item);
return updatedItem;
}
Future<void> removeTag(Tag item) async {
assert(item.id != null);
if (tags.containsKey(item.id)) {
await labelRepository.deleteTag(item);
}
}
}

View File

@@ -1,19 +1,11 @@
part of 'label_cubit.dart';
class LabelState<T extends Label> {
LabelState.initial() : this(isLoaded: false, labels: {});
final bool isLoaded;
final Map<int, T> labels;
LabelState({
this.isLoaded = false,
this.labels = const {},
});
T? getLabel(int? key) {
if (isLoaded) {
return labels[key];
}
return null;
}
@freezed
class LabelState with _$LabelState {
const factory LabelState({
@Default({}) Map<int, Correspondent> correspondents,
@Default({}) Map<int, DocumentType> documentTypes,
@Default({}) Map<int, Tag> tags,
@Default({}) Map<int, StoragePath> storagePaths,
}) = _LabelState;
}

View File

@@ -1,24 +0,0 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/core/repository/state/impl/correspondent_repository_state.dart';
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
class CorrespondentBlocProvider extends StatelessWidget {
final Widget child;
const CorrespondentBlocProvider({
super.key,
required this.child,
});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => LabelCubit<Correspondent>(
context.read<LabelRepository<Correspondent>>(),
),
child: child,
);
}
}

View File

@@ -1,21 +0,0 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/core/repository/state/impl/document_type_repository_state.dart';
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
class DocumentTypeBlocProvider extends StatelessWidget {
final Widget child;
const DocumentTypeBlocProvider({super.key, required this.child});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => LabelCubit<DocumentType>(
context.read<LabelRepository<DocumentType>>(),
),
child: child,
);
}
}

View File

@@ -1,43 +0,0 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/core/repository/state/impl/correspondent_repository_state.dart';
import 'package:paperless_mobile/core/repository/state/impl/document_type_repository_state.dart';
import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart';
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
class LabelsBlocProvider extends StatelessWidget {
final Widget child;
const LabelsBlocProvider({super.key, required this.child});
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<LabelCubit<StoragePath>>(
create: (context) => LabelCubit<StoragePath>(
context.read<LabelRepository<StoragePath>>(),
),
),
BlocProvider<LabelCubit<Correspondent>>(
create: (context) => LabelCubit<Correspondent>(
context.read<LabelRepository<Correspondent>>(),
),
),
BlocProvider<LabelCubit<DocumentType>>(
create: (context) => LabelCubit<DocumentType>(
context.read<LabelRepository<DocumentType>>(),
),
),
BlocProvider<LabelCubit<Tag>>(
create: (context) => LabelCubit<Tag>(
context.read<LabelRepository<Tag>>(),
),
),
],
child: child,
);
}
}

View File

@@ -1,21 +0,0 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart';
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
class StoragePathBlocProvider extends StatelessWidget {
final Widget child;
const StoragePathBlocProvider({super.key, required this.child});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => LabelCubit<StoragePath>(
context.read<LabelRepository<StoragePath>>(),
),
child: child,
);
}
}

View File

@@ -1,21 +0,0 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
class TagBlocProvider extends StatelessWidget {
final Widget child;
const TagBlocProvider({super.key, required this.child});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => LabelCubit<Tag>(
context.read<LabelRepository<Tag>>(),
),
child: child,
);
}
}