feat(crud): change responsibility of blocs (closes #45, closes #44)

This commit is contained in:
2023-04-13 23:29:27 +02:00
parent 4acab9a662
commit 216a6c2aae
34 changed files with 1146 additions and 498 deletions
@@ -15,3 +15,4 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'enums/where_query_type.dart';
export 'mixins/operation.dart';
@@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
/// Defines different query types for WhereQuery.
enum WhereQueryType {
isEqualTo,
isNotEqualTo,
@@ -0,0 +1,30 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
/// Defines every write operation in CRUD.
mixin CreateOperation<Model extends ObjectModel, Out> on AsyncUseCase<Model, Out> {}
/// Defines every read operation in CRUD.
mixin ReadOperation<Model extends ObjectModel, In, Out> on AsyncUseCase<In, Out> {}
/// Defines every update operation in CRUD.
mixin UpdateOperation<Model extends ObjectModel, In> on AsyncUseCase<In, void> {}
/// Defines every delete operation in CRUD.
mixin DeleteOperation<Model extends ObjectModel, In> on AsyncUseCase<In, void> {}
@@ -17,13 +17,17 @@
import 'dart:async';
import 'package:wyatt_crud_bloc/src/core/enums/where_query_type.dart';
import 'package:wyatt_crud_bloc/src/core/extensions/num_extension.dart';
import 'package:wyatt_crud_bloc/src/domain/data_sources/crud_data_source.dart';
import 'package:wyatt_crud_bloc/src/domain/data_sources/data_sources.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
/// {@template crud_in_memory_data_source_impl}
/// A [CrudDataSource] that stores data in memory.
/// {@endtemplate}
class CrudInMemoryDataSourceImpl<Model extends ObjectModel>
extends CrudDataSource<Model> {
/// {@macro crud_in_memory_data_source_impl}
CrudInMemoryDataSourceImpl({required this.toMap, Map<String, Model>? data})
: _data = data ?? {};
final Map<String, Model> _data;
@@ -20,15 +20,23 @@ import 'package:wyatt_crud_bloc/src/domain/data_sources/crud_data_source.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
/// {@template crud_firestore_data_source_impl}
/// A concrete implementation of [CrudDataSource] that uses
/// [FirebaseFirestore] as the data source.
/// {@endtemplate}
class CrudFirestoreDataSourceImpl<Model extends ObjectModel, Entity>
extends CrudDataSource<Model> {
/// {@macro crud_firestore_data_source_impl}
CrudFirestoreDataSourceImpl(
String collection, {
/// The function that converts a [DocumentSnapshot] to a [Model].
required Model Function(
DocumentSnapshot<Map<String, dynamic>>,
SnapshotOptions?,
)
fromFirestore,
/// The function that converts a [Model] to a [Map<String, Object?>].
required Map<String, Object?> Function(Model, SetOptions?) toFirestore,
FirebaseFirestore? firestore,
}) : _firestore = firestore ?? FirebaseFirestore.instance,
@@ -21,9 +21,13 @@ import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
/// {@template crud_repository_impl}
/// A repository that implements the [CrudRepository] interface.
/// {@endtemplate}
class CrudRepositoryImpl<Model extends ObjectModel>
extends CrudRepository<Model> {
CrudRepositoryImpl({
/// {@macro crud_repository_impl}
const CrudRepositoryImpl({
required CrudDataSource<Model> crudDataSource,
}) : _crudDataSource = crudDataSource;
final CrudDataSource<Model> _crudDataSource;
@@ -99,6 +103,6 @@ class CrudRepositoryImpl<Model extends ObjectModel>
if (lst.isNotNull) {
return Ok<List<Model?>, AppException>(lst);
}
return Err<List<Model?>, AppException>(ServerException());
return Err<List<Model?>, AppException>(const ServerException());
});
}
@@ -17,27 +17,42 @@
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
/// {@template crud_data_source}
/// A [BaseDataSource] that provides SCRUD operations.
/// {@endtemplate}
abstract class CrudDataSource<Model> extends BaseDataSource {
/// {@macro crud_data_source}
const CrudDataSource();
/// Creates a new [Model] object.
Future<void> create(Model object, {String? id});
/// Gets a [Model] object by its [id].
Future<Model?> get(String id);
/// Gets all [Model] objects.
Future<List<Model?>> getAll();
/// Updates a [Model] object by its [id].
Future<void> update(
String id, {
Model? object,
Map<String, dynamic>? raw,
});
/// Updates all [Model] objects.
Future<void> updateAll(Map<String, Object?>? data);
/// Deletes a [Model] object by its [id].
Future<void> delete(String id);
/// Deletes all [Model] objects.
Future<void> deleteAll();
/// Queries [Model] objects by [conditions].
Future<List<Model?>> query(List<QueryInterface> conditions);
/// Streams [Model] objects by [conditions].
Stream<List<Model?>> stream({
String? id,
List<QueryInterface>? conditions,
@@ -16,6 +16,13 @@
import 'package:wyatt_architecture/wyatt_architecture.dart';
/// {@template object_model}
/// An abstract class that represents an object model.
/// {@endtemplate}
abstract class ObjectModel extends Entity {
/// {@macro object_model}
const ObjectModel();
/// The id of the object model.
String? get id;
}
@@ -17,27 +17,59 @@
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/enums/where_query_type.dart';
// ignore: one_member_abstracts
abstract class QueryParser<Q> {
Q parser(QueryInterface condition, Q query);
// // ignore: one_member_abstracts
// abstract class QueryParser<Q> {
// Q parser(QueryInterface condition, Q query);
// }
typedef QueryParser<Q> = Q Function(QueryInterface condition, Q query);
/// {@template query}
/// An abstract class that represents a query.
/// {@endtemplate}
abstract class QueryInterface extends Entity {
/// {@macro query}
const QueryInterface();
}
abstract class QueryInterface extends Entity {}
/// {@template where_query}
/// Represents a where query.
/// {@endtemplate}
class WhereQuery<Value> extends QueryInterface {
WhereQuery(this.type, this.field, this.value);
/// {@macro where_query}
const WhereQuery(this.type, this.field, this.value);
/// The type of the where query.
final WhereQueryType type;
/// The field of the where query.
final String field;
/// The value of the where query.
final Value value;
}
/// {@template limit_query}
/// Represents a limit query.
/// {@endtemplate}
class LimitQuery extends QueryInterface {
LimitQuery(this.limit);
/// {@macro limit_query}
const LimitQuery(this.limit);
/// The limit of the limit query.
final int limit;
}
/// {@template offset_query}
/// Represents an offset query.
/// {@endtemplate}
class OrderByQuery extends QueryInterface {
OrderByQuery(this.field, {this.ascending = true});
/// {@macro offset_query}
const OrderByQuery(this.field, {this.ascending = true});
/// The field of the order by query.
final String field;
/// The ascending of the order by query.
final bool ascending;
}
@@ -18,20 +18,43 @@ import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
/// {@template crud_repository}
/// An abstract class that represents a SCRUD repository.
/// {@endtemplate}
abstract class CrudRepository<Model extends ObjectModel>
extends BaseRepository {
/// {@macro crud_repository}
const CrudRepository();
/// Creates a new object.
FutureOrResult<void> create(Model object, {String? id});
/// Gets an object by its [id].
FutureOrResult<Model?> get(String id);
/// Gets all objects.
FutureOrResult<List<Model?>> getAll();
/// Updates an object by its [id].
FutureOrResult<void> update(
String id, {
Model? object,
Map<String, dynamic>? raw,
});
/// Updates all objects.
FutureOrResult<void> updateAll(Map<String, Object?> raw);
/// Deletes an object by its [id].
FutureOrResult<void> delete(String id);
/// Deletes all objects.
FutureOrResult<void> deleteAll();
/// Queries objects by [conditions].
FutureOrResult<List<Model?>> query(List<QueryInterface> conditions);
/// Streams objects by [conditions].
StreamResult<List<Model?>> stream({
String? id,
List<QueryInterface>? conditions,
@@ -1,4 +1,3 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
@@ -18,13 +17,19 @@
import 'dart:async';
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
class Create<Model extends ObjectModel> extends AsyncUseCase<Model, void> {
final CrudRepository<Model> _crudRepository;
/// {@template create}
/// A use case that creates an object model.
/// {@endtemplate}
class Create<Model extends ObjectModel> extends AsyncUseCase<Model, void>
with CreateOperation<Model, void> {
/// {@macro create}
const Create(this._crudRepository);
Create(this._crudRepository);
final CrudRepository<Model> _crudRepository;
@override
FutureOr<void> onStart(Model? params) {
@@ -17,17 +17,24 @@
import 'dart:async';
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
class Delete<Model extends ObjectModel> extends AsyncUseCase<String, void> {
Delete(this._crudRepository);
/// {@template delete}
/// A use case that deletes an object model.
/// {@endtemplate}
class Delete<Model extends ObjectModel> extends AsyncUseCase<String, void>
with DeleteOperation<Model, String> {
/// {@macro delete}
const Delete(this._crudRepository);
final CrudRepository<Model> _crudRepository;
@override
FutureOr<void> onStart(String? params) {
if (params == null) {
throw ClientException('Id cannot be null.');
throw const ClientException('Id cannot be null.');
}
}
@@ -15,11 +15,18 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
class DeleteAll<Model extends ObjectModel> extends AsyncUseCase<void, void> {
DeleteAll(this._crudRepository);
/// {@template delete_all}
/// A use case that deletes all the object models.
/// {@endtemplate}
class DeleteAll<Model extends ObjectModel> extends AsyncUseCase<void, void>
with DeleteOperation<Model, void> {
/// {@macro delete_all}
const DeleteAll(this._crudRepository);
final CrudRepository<Model> _crudRepository;
@override
@@ -17,17 +17,24 @@
import 'dart:async';
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
class Get<Model extends ObjectModel> extends AsyncUseCase<String, Model?> {
/// {@template get}
/// A use case that gets an object model.
/// {@endtemplate}
class Get<Model extends ObjectModel> extends AsyncUseCase<String, Model?>
with ReadOperation<Model, String, Model?> {
/// {@macro get}
Get(this._crudRepository);
final CrudRepository<Model> _crudRepository;
@override
FutureOr<void> onStart(String? params) {
if (params == null) {
throw ClientException('Id cannot be null.');
throw const ClientException('Id cannot be null.');
}
}
@@ -15,12 +15,18 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
class GetAll<Model extends ObjectModel>
extends AsyncUseCase<void, List<Model?>> {
GetAll(this._crudRepository);
/// {@template get_all}
/// A use case that gets all the object models.
/// {@endtemplate}
class GetAll<Model extends ObjectModel> extends AsyncUseCase<void, List<Model?>>
with ReadOperation<Model, void, List<Model?>> {
/// {@macro get_all}
const GetAll(this._crudRepository);
final CrudRepository<Model> _crudRepository;
@override
@@ -1,4 +1,3 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
@@ -17,12 +16,19 @@
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
/// {@template stream_parameters}
/// Represents the parameters for a query stream
/// {@endtemplate}
class StreamParameters {
final String? id;
final List<QueryInterface>? conditions;
StreamParameters({
/// {@macro stream_parameters}
const StreamParameters({
this.id,
this.conditions,
});
/// The id of the object model.
final String? id;
/// The conditions of the query.
final List<QueryInterface>? conditions;
}
@@ -1,4 +1,3 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
@@ -15,14 +14,23 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
/// {@template update_parameters}
/// Represents the parameters for an update use case
/// {@endtemplate}
class UpdateParameters<Model> {
final String id;
final Model? object;
final Map<String, dynamic>? raw;
UpdateParameters({
/// {@macro update_parameters}
const UpdateParameters({
required this.id,
this.object,
this.raw,
});
/// The id of the object model.
final String id;
/// The object model.
final Model? object;
/// The raw data.
final Map<String, dynamic>? raw;
}
@@ -17,19 +17,26 @@
import 'dart:async';
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
/// {@template query}
/// A use case that queries the object models.
/// {@endtemplate}
class Query<Model extends ObjectModel>
extends AsyncUseCase<List<QueryInterface>, List<Model?>> {
Query(this._crudRepository);
extends AsyncUseCase<List<QueryInterface>, List<Model?>>
with ReadOperation<Model, List<QueryInterface>, List<Model?>> {
/// {@macro query}
const Query(this._crudRepository);
final CrudRepository<Model> _crudRepository;
@override
FutureOr<void> onStart(List<QueryInterface>? params) {
if (params == null) {
throw ClientException('List of conditions cannot be null.');
throw const ClientException('List of conditions cannot be null.');
}
}
@@ -1,44 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// class Stream<Model extends ObjectModel> extends UseCase<StreamParameters,
//List<Model?>> {
// final CrudRepository<Model> _crudRepository;
// Stream(this._crudRepository);
// @override
// StreamResult<List<Model?>> call(StreamParameters params) =>
// _crudRepository.stream(id: params.id, conditions: params.conditions);
// }
// class StreamQuery<Model extends ObjectModel>
// extends StreamUseCase<StreamParameters, List<Model?>> {
// final CrudRepository<Model> _crudRepository;
// StreamQuery(this._crudRepository);
// @override
// FutureOr<void> onStart(StreamParameters? params) {
// if(params == null){
// throw ClientException('Stream parameters cannot be null.');
// }
// }
// @override
// FutureOrResult<Stream<List<Model?>>> call(StreamParameters? params) =>
// _crudRepository.stream();
// }
@@ -17,19 +17,26 @@
import 'dart:async';
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/params/update_parameters.dart';
/// {@template update}
/// A use case that updates an object model.
/// {@endtemplate}
class Update<Model extends ObjectModel>
extends AsyncUseCase<UpdateParameters<Model>, void> {
Update(this._crudRepository);
extends AsyncUseCase<UpdateParameters<Model>, void>
with UpdateOperation<Model, UpdateParameters<Model>> {
/// {@macro update}
const Update(this._crudRepository);
final CrudRepository<Model> _crudRepository;
@override
FutureOr<void> onStart(UpdateParameters<Model>? params) {
if (params == null) {
throw ClientException('Update parameters cannot be null.');
throw const ClientException('Update parameters cannot be null.');
}
}
@@ -17,18 +17,25 @@
import 'dart:async';
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
/// {@template update_all}
/// A use case that updates all the object models.
/// {@endtemplate}
class UpdateAll<Model extends ObjectModel>
extends AsyncUseCase<Map<String, Object?>, void> {
UpdateAll(this._crudRepository);
extends AsyncUseCase<Map<String, Object?>, void>
with UpdateOperation<Model, Map<String, Object?>> {
/// {@macro update_all}
const UpdateAll(this._crudRepository);
final CrudRepository<Model> _crudRepository;
@override
FutureOr<void> onStart(Map<String, Object?>? params) {
if (params == null) {
throw ClientException('Data cannot be null.');
throw const ClientException('Data cannot be null.');
}
}
@@ -21,6 +21,5 @@ export 'get.dart';
export 'get_all.dart';
export 'params/params.dart';
export 'query.dart';
export 'stream_query.dart';
export 'update.dart';
export 'update_all.dart';
@@ -0,0 +1,305 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:wyatt_crud_bloc/src/core/enums/where_query_type.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/create.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/delete.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/delete_all.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/get.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/get_all.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/params/update_parameters.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/query.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/update.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/update_all.dart';
import 'package:wyatt_crud_bloc/src/features/crud/blocs/crud_base_cubit/crud_base_cubit.dart';
/// {@template crud_cubit_advanced}
/// Cubit that handles CRUD operations with more granularity.
/// {@endtemplate}
abstract class CrudAdvancedCubit<Model extends ObjectModel>
extends CrudBaseCubit {
/// {@macro crud_cubit}
CrudAdvancedCubit() : super();
Create<Model>? get crudCreate;
DeleteAll<Model>? get crudDeleteAll;
Delete<Model>? get crudDelete;
GetAll<Model>? get crudGetAll;
Get<Model>? get crudGet;
Query<Model>? get crudQuery;
UpdateAll<Model>? get crudUpdateAll;
Update<Model>? get crudUpdate;
FutureOr<void> create(Model model) async {
final crud = crudCreate;
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(model);
emit(
result.fold(
(_) {
if (stateCopy is CrudLoaded<Model?>) {
if (stateCopy.data == null) {
return CrudLoaded<Model?>(model);
}
if (stateCopy.data!.id == model.id) {
return CrudLoaded<Model?>(model);
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
if (stateCopy.data.isEmpty) {
return CrudListLoaded<Model?>([model]);
}
final List<Model?> lst = stateCopy.data.toList()..add(model);
return CrudListLoaded<Model?>(lst);
}
return const CrudOkReturn();
},
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> delete(String id) async {
final crud = crudDelete;
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(id);
emit(
result.fold(
(_) {
if (stateCopy is CrudLoaded<Model?>) {
if (stateCopy.data?.id == id) {
return CrudLoaded<Model?>(null);
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
return CrudListLoaded<Model?>(
stateCopy.data.where((element) => element?.id != id).toList(),
);
}
return const CrudOkReturn();
},
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> deleteAll() async {
final crud = crudDeleteAll;
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(null);
emit(
result.fold(
(_) {
if (stateCopy is CrudLoaded<Model?>) {
return CrudLoaded<Model?>(null);
}
if (stateCopy is CrudListLoaded<Model?>) {
return CrudListLoaded<Model?>(const []);
}
return const CrudOkReturn();
},
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> get(String id) async {
final crud = crudGet;
if (crud == null) {
return;
}
emit(const CrudLoading());
final result = await crud.call(id);
emit(
result.fold(
CrudLoaded<Model?>.new,
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> getAll() async {
final crud = crudGetAll;
if (crud == null) {
return;
}
emit(const CrudLoading());
final result = await crud.call(null);
emit(
result.fold(
CrudListLoaded<Model?>.new,
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> query(List<QueryInterface> conditions) async {
final crud = crudQuery;
if (crud == null) {
return;
}
emit(const CrudLoading());
final result = await crud.call(conditions);
emit(
result.fold(
CrudListLoaded<Model?>.new,
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> update(UpdateParameters<Model> param) async {
final crud = crudUpdate;
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(param);
emit(
await result.foldAsync(
(_) async {
if (stateCopy is CrudLoaded<Model?>) {
if (stateCopy.data?.id == param.id) {
// Same object, need to update actual stateCopy
final crudGet = this.crudGet;
if (crudGet == null) {
// No read operation, can't update stateCopy.
return stateCopy;
}
final newVersion = await crudGet.call(param.id);
if (newVersion.isOk) {
return CrudLoaded<Model?>(newVersion.ok);
}
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
final bool listContains =
stateCopy.data.any((element) => element?.id == param.id);
if (listContains) {
// Loaded objects contains the modified object.
final crudGet = this.crudGet;
if (crudGet == null) {
// No read operation, can't update stateCopy.
return stateCopy;
}
final newVersion = await crudGet.call(param.id);
if (newVersion.isOk) {
final newList = stateCopy.data
.where(
(element) => element?.id != param.id,
)
.toList();
return CrudListLoaded<Model?>(newList + [newVersion.ok]);
}
}
return stateCopy;
}
return const CrudOkReturn();
},
(error) async => CrudError(error.toString()),
),
);
}
FutureOr<void> updateAll(Map<String, Object?> param) async {
final crud = crudUpdateAll;
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(param);
emit(
await result.foldAsync(
(_) async {
if (stateCopy is CrudLoaded<Model?>) {
// Same object, need to update actual stateCopy
final crudGet = this.crudGet;
if (crudGet == null) {
// No read operation, can't update stateCopy.
return stateCopy;
}
final actualId = stateCopy.data?.id;
final newVersion = await crudGet.call(actualId ?? '');
if (newVersion.isOk) {
return CrudLoaded<Model?>(newVersion.ok);
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
final crudQuery = this.crudQuery;
if (crudQuery == null) {
// No read operation, can't update stateCopy.
return stateCopy;
}
// Load all id to retrieve exactly same object
// (not all because previous stateCopy can be a query result)
final List<String?> ids = stateCopy.data
.map(
(e) => e?.id,
)
.toList();
final result = await crudQuery.call([
WhereQuery(
WhereQueryType.whereIn,
'id',
ids,
)
]);
if (result.isOk) {
return CrudListLoaded<Model?>(result.ok ?? []);
}
return stateCopy;
}
return const CrudOkReturn();
},
(error) async => CrudError(error.toString()),
),
);
}
}
@@ -14,38 +14,15 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
extension NumExtension on num? {
bool operator <(num? other) {
if (this == null || other == null) {
return false;
}
return this < other;
}
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
bool operator >(num? other) {
if (this == null || other == null) {
return false;
}
return this > other;
}
part 'crud_state.dart';
bool operator <=(num? other) {
if (this == null && other == null) {
return true;
}
if (this == null || other == null) {
return false;
}
return this <= other;
}
bool operator >=(num? other) {
if (this == null && other == null) {
return true;
}
if (this == null || other == null) {
return false;
}
return this >= other;
}
/// {@template crud_base_cubit}
/// A base [Cubit] that handles SCRUD operations.
/// {@endtemplate}
abstract class CrudBaseCubit extends Cubit<CrudState> {
/// {@macro crud_base_cubit}
CrudBaseCubit() : super(const CrudInitial());
}
@@ -1,4 +1,4 @@
// Copyright (C) 2022 WYATT GROUP
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'crud_cubit.dart';
part of 'crud_base_cubit.dart';
abstract class CrudState extends Equatable {
const CrudState();
@@ -23,18 +23,17 @@ abstract class CrudState extends Equatable {
List<Object?> get props => [];
}
class CrudInitial extends CrudState {}
class CrudLoading extends CrudState {}
abstract class CrudSuccess extends CrudState {
const CrudSuccess();
/// Initial state of the CrudBaseCubit.
class CrudInitial extends CrudState {
const CrudInitial();
}
class CrudOkReturn extends CrudState {
const CrudOkReturn();
/// Loading state of the CrudBaseCubit.
class CrudLoading extends CrudState {
const CrudLoading();
}
/// Error state of the CrudBaseCubit.
class CrudError extends CrudState {
const CrudError(this.message);
final String? message;
@@ -43,6 +42,24 @@ class CrudError extends CrudState {
List<Object?> get props => [message];
}
/// Success state of the CrudBaseCubit.
/// This state is used to indicate that the operation was successful.
/// Can be one or list of objects.
abstract class CrudSuccess extends CrudState {
const CrudSuccess();
}
/// Success state of the CrudBaseCubit.
/// This state is used to indicate that the operation was successful.
/// Contains no objects.
/// Used for create, update, delete operations.
class CrudOkReturn extends CrudSuccess {
const CrudOkReturn();
}
/// Loaded state of the CrudBaseCubit.
/// This state is used to indicate that the operation was successful.
/// Contains one object.
class CrudLoaded<T> extends CrudSuccess {
const CrudLoaded(this.data);
final T? data;
@@ -51,6 +68,9 @@ class CrudLoaded<T> extends CrudSuccess {
List<Object?> get props => [data];
}
/// Loaded state of the CrudBaseCubit.
/// This state is used to indicate that the operation was successful.
/// Contains list of objects.
class CrudListLoaded<T> extends CrudSuccess {
const CrudListLoaded(this.data);
final List<T?> data;
@@ -0,0 +1,369 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:wyatt_crud_bloc/src/core/enums/where_query_type.dart';
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/create.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/delete.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/delete_all.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/get.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/get_all.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/params/update_parameters.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/query.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/update.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/update_all.dart';
import 'package:wyatt_crud_bloc/src/features/crud/blocs/crud_base_cubit/crud_base_cubit.dart';
/// {@template crud_cubit}
/// Cubit that handles CRUD operations.
/// {@endtemplate}
abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
/// {@macro crud_cubit}
CrudCubit() : super();
/// Create operation.
/// Can be create.
CreateOperation<Model, dynamic>? get createOperation;
/// Read operation.
/// Can be get, getAll, query.
ReadOperation<Model, dynamic, dynamic>? get readOperation;
/// Update operation.
/// Can be update, updateAll.
UpdateOperation<Model, dynamic>? get updateOperation;
/// Delete operation.
/// Can be delete or deleteAll.
DeleteOperation<Model, dynamic>? get deleteOperation;
Expected? _checkOperation<Expected>(dynamic operation) {
if (operation == null) {
return null;
}
if (operation is! Expected) {
return null;
}
return operation;
}
FutureOr<void> create(Model model) async {
if (_checkOperation<Create<Model>>(createOperation) != null) {
return _create(model);
}
}
FutureOr<void> read({String? id, List<QueryInterface>? conditions}) async {
if (_checkOperation<Get<Model>>(readOperation) != null && id != null) {
return _get(id);
}
if (_checkOperation<GetAll<Model>>(readOperation) != null) {
return _getAll();
}
if (_checkOperation<Query<Model>>(readOperation) != null &&
conditions != null) {
return _query(conditions);
}
if (_checkOperation<Query<Model>>(readOperation) != null &&
conditions == null) {
return _getAll();
}
}
FutureOr<void> update(
UpdateParameters<Model>? single,
Map<String, dynamic>? all,
) async {
if (_checkOperation<Update<Model>>(updateOperation) != null &&
single != null) {
return _update(single);
}
if (_checkOperation<UpdateAll<Model>>(updateOperation) != null &&
all != null) {
return _updateAll(all);
}
}
FutureOr<void> delete({String? id}) async {
if (_checkOperation<Delete<Model>>(deleteOperation) != null && id != null) {
return _delete(id);
}
if (_checkOperation<DeleteAll<Model>>(deleteOperation) != null) {
return _deleteAll();
}
}
FutureOr<void> _create(Model model) async {
final crud = _checkOperation<Create<Model>>(createOperation);
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(model);
emit(
result.fold(
(_) {
if (stateCopy is CrudLoaded<Model?>) {
if (stateCopy.data == null) {
return CrudLoaded<Model?>(model);
}
if (stateCopy.data!.id == model.id) {
return CrudLoaded<Model?>(model);
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
if (stateCopy.data.isEmpty) {
return CrudListLoaded<Model?>([model]);
}
final List<Model?> lst = stateCopy.data.toList()..add(model);
return CrudListLoaded<Model?>(lst);
}
return const CrudOkReturn();
},
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> _delete(String id) async {
final crud = _checkOperation<Delete<Model>>(deleteOperation);
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(id);
emit(
result.fold(
(_) {
if (stateCopy is CrudLoaded<Model?>) {
if (stateCopy.data?.id == id) {
return CrudLoaded<Model?>(null);
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
return CrudListLoaded<Model?>(
stateCopy.data.where((element) => element?.id != id).toList(),
);
}
return const CrudOkReturn();
},
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> _deleteAll() async {
final crud = _checkOperation<DeleteAll<Model>>(deleteOperation);
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(null);
emit(
result.fold(
(_) {
if (stateCopy is CrudLoaded<Model?>) {
return CrudLoaded<Model?>(null);
}
if (stateCopy is CrudListLoaded<Model?>) {
return CrudListLoaded<Model?>(const []);
}
return const CrudOkReturn();
},
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> _get(String id) async {
final crud = _checkOperation<Get<Model>>(readOperation);
if (crud == null) {
return;
}
emit(const CrudLoading());
final result = await crud.call(id);
emit(
result.fold(
CrudLoaded<Model?>.new,
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> _getAll() async {
final crud = _checkOperation<GetAll<Model>>(readOperation);
if (crud == null) {
return;
}
emit(const CrudLoading());
final result = await crud.call(null);
emit(
result.fold(
CrudListLoaded<Model?>.new,
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> _query(List<QueryInterface> conditions) async {
final crud = _checkOperation<Query<Model>>(readOperation);
if (crud == null) {
return;
}
emit(const CrudLoading());
final result = await crud.call(conditions);
emit(
result.fold(
CrudListLoaded<Model?>.new,
(error) => CrudError(error.toString()),
),
);
}
FutureOr<void> _update(UpdateParameters<Model> param) async {
final crud = _checkOperation<Update<Model>>(updateOperation);
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(param);
emit(
await result.foldAsync(
(_) async {
if (stateCopy is CrudLoaded<Model?>) {
if (stateCopy.data?.id == param.id) {
// Same object, need to update actual stateCopy
final crudGet = _checkOperation<Get<Model>>(readOperation);
if (crudGet == null) {
// No read operation, can't update stateCopy.
return stateCopy;
}
final newVersion = await crudGet.call(param.id);
if (newVersion.isOk) {
return CrudLoaded<Model?>(newVersion.ok);
}
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
final bool listContains =
stateCopy.data.any((element) => element?.id == param.id);
if (listContains) {
// Loaded objects contains the modified object.
final crudGet = _checkOperation<Get<Model>>(readOperation);
if (crudGet == null) {
// No read operation, can't update stateCopy.
return stateCopy;
}
final newVersion = await crudGet.call(param.id);
if (newVersion.isOk) {
final newList = stateCopy.data
.where(
(element) => element?.id != param.id,
)
.toList();
return CrudListLoaded<Model?>(newList + [newVersion.ok]);
}
}
return stateCopy;
}
return const CrudOkReturn();
},
(error) async => CrudError(error.toString()),
),
);
}
FutureOr<void> _updateAll(Map<String, Object?> param) async {
final crud = _checkOperation<UpdateAll<Model>>(updateOperation);
if (crud == null) {
return;
}
final stateCopy = state;
emit(const CrudLoading());
final result = await crud.call(param);
emit(
await result.foldAsync(
(_) async {
if (stateCopy is CrudLoaded<Model?>) {
// Same object, need to update actual stateCopy
final crudGet = _checkOperation<Get<Model>>(readOperation);
if (crudGet == null) {
// No read operation, can't update stateCopy.
return stateCopy;
}
final actualId = stateCopy.data?.id;
final newVersion = await crudGet.call(actualId ?? '');
if (newVersion.isOk) {
return CrudLoaded<Model?>(newVersion.ok);
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
final crudQuery = _checkOperation<Query<Model>>(readOperation);
if (crudQuery == null) {
// No read operation, can't update stateCopy.
return stateCopy;
}
// Load all id to retrieve exactly same object
// (not all because previous stateCopy can be a query result)
final List<String?> ids = stateCopy.data
.map(
(e) => e?.id,
)
.toList();
final result = await crudQuery.call([
WhereQuery(
WhereQueryType.whereIn,
'id',
ids,
)
]);
if (result.isOk) {
return CrudListLoaded<Model?>(result.ok ?? []);
}
return stateCopy;
}
return const CrudOkReturn();
},
(error) async => CrudError(error.toString()),
),
);
}
}
@@ -15,15 +15,19 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
import 'package:wyatt_crud_bloc/src/features/crud/cubit/crud_cubit.dart';
import 'package:wyatt_crud_bloc/src/features/crud/blocs/crud_base_cubit/crud_base_cubit.dart';
/// {@template crud_builder}
/// A widget that builds itself based on the latest snapshot of interaction
/// with a [CrudBaseCubit].
///
/// * I = Initial State
/// * L = Loading State
/// * S = Success State
/// * E = Error State
/// {@endtemplate}
class CrudBuilder<I, L, S, E> extends StatelessWidget {
/// `<I, L, S, E>`
///
/// - I: the Initial State
/// - L: the Loading State
/// - S: the Success State
/// - E: the Error State
/// {@macro crud_builder}
const CrudBuilder({
required this.state,
required this.builder,
@@ -34,11 +38,11 @@ class CrudBuilder<I, L, S, E> extends StatelessWidget {
super.key,
});
/// `<CrudInitial, CrudLoading, S extends CrudSuccess, CrudError>`
/// {@macro crud_builder}
///
/// - S: the Success State
///
/// For CrudStates only.
/// This factory constructor is used to create a [CrudBuilder] with
/// [CrudState]s. `S` is the Success State, and it must be a subtype of
/// [CrudSuccess]. It the only type that you have to specify.
static CrudBuilder<CrudInitial, CrudLoading, CrudSuccess, CrudError>
typed<S extends CrudSuccess>({
required CrudState state,
@@ -14,5 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'blocs/crud_advanced_cubit.dart';
export 'blocs/crud_base_cubit/crud_base_cubit.dart';
export 'blocs/crud_cubit.dart';
export 'builder/builder.dart';
export 'cubit/crud_cubit.dart';
@@ -1,266 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_crud_bloc/src/core/enums/where_query_type.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/create.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/delete.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/delete_all.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/get.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/get_all.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/params/update_parameters.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/query.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/update.dart';
import 'package:wyatt_crud_bloc/src/domain/usecases/update_all.dart';
part 'crud_state.dart';
abstract class CrudCubit<Model extends ObjectModel> extends Cubit<CrudState> {
CrudCubit() : super(CrudInitial());
Create<Model>? get crudCreate;
DeleteAll<Model>? get crudDeleteAll;
Delete<Model>? get crudDelete;
GetAll<Model>? get crudGetAll;
Get<Model>? get crudGet;
Query<Model>? get crudQuery;
UpdateAll<Model>? get crudUpdateAll;
Update<Model>? get crudUpdate;
FutureOr<void> create(Model model) async {
if (crudCreate != null) {
final stateCopy = state;
emit(CrudLoading());
final result = await crudCreate!.call(model);
emit(
result.fold(
(_) {
if (stateCopy is CrudLoaded<Model?>) {
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
if (stateCopy.data.isEmpty) {
return CrudListLoaded<Model?>([model]);
}
final List<Model?> lst = stateCopy.data.toList()..add(model);
return CrudListLoaded<Model?>(lst);
}
return const CrudOkReturn();
},
(error) => CrudError(error.toString()),
),
);
}
}
FutureOr<void> delete(String id) async {
if (crudDelete != null) {
final stateCopy = state;
emit(CrudLoading());
final result = await crudDelete!.call(id);
emit(
result.fold(
(_) {
if (stateCopy is CrudLoaded<Model?>) {
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
return CrudListLoaded<Model?>(
stateCopy.data.where((element) => element?.id != id).toList(),
);
}
return const CrudOkReturn();
},
(error) => CrudError(error.toString()),
),
);
}
}
FutureOr<void> deleteAll() async {
if (crudDeleteAll != null) {
final stateCopy = state;
emit(CrudLoading());
final result = await crudDeleteAll!.call(null);
emit(
result.fold(
(_) {
if (stateCopy is CrudLoaded<Model?>) {
return CrudLoaded<Model?>(null);
}
if (stateCopy is CrudListLoaded<Model?>) {
return CrudListLoaded<Model?>(const []);
}
return const CrudOkReturn();
},
(error) => CrudError(error.toString()),
),
);
}
}
FutureOr<void> get(String id) async {
if (crudGet != null) {
emit(CrudLoading());
final result = await crudGet!.call(id);
emit(
result.fold(
CrudLoaded<Model?>.new,
(error) => CrudError(error.toString()),
),
);
}
}
FutureOr<void> getAll() async {
if (crudGetAll != null) {
emit(CrudLoading());
final result = await crudGetAll!.call(null);
emit(
result.fold(
CrudListLoaded<Model?>.new,
(error) => CrudError(error.toString()),
),
);
}
}
FutureOr<void> query(List<QueryInterface> conditions) async {
if (crudQuery != null) {
emit(CrudLoading());
final result = await crudQuery!.call(conditions);
emit(
result.fold(
CrudListLoaded<Model?>.new,
(error) => CrudError(error.toString()),
),
);
}
}
FutureOr<void> update(UpdateParameters<Model> param) async {
if (crudUpdate != null) {
final stateCopy = state;
emit(CrudLoading());
final result = await crudUpdate!.call(param);
emit(
await result.foldAsync(
(_) async {
if (stateCopy is CrudLoaded<Model?>) {
if (stateCopy.data?.id == param.id) {
// Same object, need to update actual stateCopy
if (crudGet == null) {
throw ClientException(
'Need to init Get usecase to use update.',
);
}
final newVersion = await crudGet!.call(param.id);
if (newVersion.isOk) {
return CrudLoaded<Model?>(newVersion.ok);
}
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
final bool listContains =
stateCopy.data.any((element) => element?.id == param.id);
if (listContains) {
// Loaded objects contains the modified object.
if (crudGet == null) {
throw ClientException(
'Need to init Get usecase to use update.',
);
}
final newVersion = await crudGet!.call(param.id);
if (newVersion.isOk) {
final newList = stateCopy.data
.where(
(element) => element?.id != param.id,
)
.toList();
return CrudListLoaded<Model?>(newList + [newVersion.ok]);
}
}
return stateCopy;
}
return const CrudOkReturn();
},
(error) async => CrudError(error.toString()),
),
);
}
}
FutureOr<void> updateAll(Map<String, Object?> param) async {
if (crudUpdateAll != null) {
final stateCopy = state;
emit(CrudLoading());
final result = await crudUpdateAll!.call(param);
emit(
await result.foldAsync(
(_) async {
if (stateCopy is CrudLoaded<Model?>) {
// Same object, need to update actual stateCopy
if (crudGet == null) {
throw ClientException(
'Need to init Get usecase to use updateAll.',
);
}
final actualId = stateCopy.data?.id;
final newVersion = await crudGet!.call(actualId ?? '');
if (newVersion.isOk) {
return CrudLoaded<Model?>(newVersion.ok);
}
return stateCopy;
}
if (stateCopy is CrudListLoaded<Model?>) {
if (crudQuery == null) {
throw ClientException(
'Need to init Query usecase to use updateAll.',
);
}
// Load all id to retrieve exactly same object
// (not all because previous stateCopy can be a query result)
final List<String?> ids = stateCopy.data
.map(
(e) => e?.id,
)
.toList();
final result = await crudQuery!.call([
WhereQuery(
WhereQueryType.whereIn,
'id',
ids,
)
]);
if (result.isOk) {
return CrudListLoaded<Model?>(result.ok ?? []);
}
return stateCopy;
}
return const CrudOkReturn();
},
(error) async => CrudError(error.toString()),
),
);
}
}
}