plans filters ready

This commit is contained in:
Username1548
2024-12-16 20:31:23 +07:00
parent 067b10ed7f
commit 27241aebac
45 changed files with 1806 additions and 657 deletions

View File

@@ -25,7 +25,7 @@ class DietsCubit extends Cubit<DietsState> {
'name:desc',
];
static const List<int?> _durationValues = [null, ...durationValues];
static const List<String?> _types = [null, ...typeValues];
static const List<String?> _types = [null, ...dietTypeValues];
late final StreamSubscription likesStateSubscription;
late final StreamSubscription favoriteStateSubscription;
late final StreamSubscription commentsStateSubscription;
@@ -54,17 +54,35 @@ class DietsCubit extends Cubit<DietsState> {
void changeTypeFilter(int index) {
emit(state.copyWith(filterState: state.filterState.copyWith(typeIndex: index), errorMessage: null));
loadDiets(query: _query);
EasyDebounce.debounce(
'loadDiets',
Duration(seconds: 1),
() => loadDiets(
query: _query,
),
);
}
void changeSortingType(int index) {
emit(state.copyWith(filterState: state.filterState.copyWith(sortingIndex: index), errorMessage: null));
loadDiets(query: _query);
EasyDebounce.debounce(
'loadDiets',
Duration(seconds: 1),
() => loadDiets(
query: _query,
),
);
}
void changeDurationFilter(int index) {
emit(state.copyWith(filterState: state.filterState.copyWith(durationIndex: index), errorMessage: null));
loadDiets(query: _query);
EasyDebounce.debounce(
'loadDiets',
Duration(seconds: 1),
() => loadDiets(
query: _query,
),
);
}
loadDiets({
@@ -82,9 +100,8 @@ class DietsCubit extends Cubit<DietsState> {
sorting: sorting,
);
lastPage = dietsPagination.meta.lastPage;
emit(
state.copyWith(dietsList: dietsPagination.data, isLoading: isLoading, errorMessage: null),
state.copyWith(dietsList: dietsPagination.data, isLoading: isLoading, errorMessage: null,),
);
} catch (e) {
emit(
@@ -181,7 +198,7 @@ class DietsCubit extends Cubit<DietsState> {
);
}
void _changeDietLikeState(DietLikesState likesState) {
void _changeDietLikeState(LikesState likesState) {
final currentDiets = state.dietsList;
final dietsForModification = [...currentDiets];
final currentDiet = currentDiets[likesState.index];
@@ -201,7 +218,7 @@ class DietsCubit extends Cubit<DietsState> {
);
}
void _changeDietFavoriteState(DietFavoriteState favoriteState) {
void _changeDietFavoriteState(FavoriteState favoriteState) {
final currentDiets = state.dietsList;
final dietsForModification = [...currentDiets];
final currentDiet = currentDiets[favoriteState.index];
@@ -220,7 +237,7 @@ class DietsCubit extends Cubit<DietsState> {
);
}
void _changeDietCommentsState(DietCommentsState commentsState) {
void _changeDietCommentsState(CommentsState commentsState) {
final currentDiets = state.dietsList;
final dietsForModification = [...currentDiets];
final currentDiet = currentDiets[commentsState.index];
@@ -261,7 +278,7 @@ class DietsCubit extends Cubit<DietsState> {
Duration(milliseconds: 200),
() async {
try {
await dietsRepository.addToFavorite(id);
await dietsRepository.addToFavorite(id);
} catch (e) {
log(e.toString());
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
@@ -301,7 +318,7 @@ class DietsCubit extends Cubit<DietsState> {
Duration(milliseconds: 200),
() async {
try {
await dietsRepository.removeFromFavorite(id);
await dietsRepository.removeFromFavorite(id);
} catch (e) {
log(e.toString());
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');

View File

@@ -1,41 +1,176 @@
import 'dart:async';
import 'dart:developer';
import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/utils/toasts.dart';
import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_state.dart';
import 'package:eatvio_app/features/feed/repositories/feed_repository.dart';
import 'package:eatvio_app/features/plans/repositories/plan_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class PlansCubit extends Cubit<PlansState> {
final FeedRepository feedRepository;
final PlanRepository planRepository;
int currentPage = 1;
late int lastPage;
bool isLoading = false;
String? _query;
static const List<int?> _durationValues = [null, ...durationValues];
static const List<String?> _types = [null, ...planTypeValues];
late final StreamSubscription likesStateSubscription;
late final StreamSubscription favoriteStateSubscription;
late final StreamSubscription commentsStateSubscription;
PlansCubit({
required this.feedRepository,
}) : super(PlansState.initial());
required this.planRepository,
}) : super(PlansState.initial()) {
likesStateSubscription = planRepository.planLikeState.listen((likesState) {
_changePlanLikeState(likesState);
});
favoriteStateSubscription = planRepository.planFavoriteState.listen((favoriteState) {
_changePlanFavoriteState(favoriteState);
});
commentsStateSubscription = planRepository.planCommentsState.listen((commentsState) {
_changePlanCommentsState(commentsState);
});
}
void changeDurationFilter(int index) {
emit(state.copyWith(filterState: state.filterState.copyWith(durationIndex: index), errorMessage: null));
EasyDebounce.debounce(
'loadPlans',
Duration(seconds: 1),
() => loadPlans(
query: _query,
),
);
}
void changeTypeFilter(int index) {
emit(state.copyWith(filterState: state.filterState.copyWith(typeIndex: index), errorMessage: null));
EasyDebounce.debounce(
'loadPlans',
Duration(seconds: 1),
() => loadPlans(
query: _query,
),
);
}
void changePriceFilter(int minPrice, int maxPrice, {bool disableDebounce = false}) {
emit(state.copyWith(filterState: state.filterState.copyWith(priceFilter: (minPrice, maxPrice)), errorMessage: null));
EasyDebounce.debounce(
'loadPlans',
Duration(seconds: disableDebounce ? 0 : 1),
() => loadPlans(
query: _query,
),
);
}
void _changePlanLikeState(LikesState likesState) {
final currentPlans = state.plans;
final plansForModification = [...currentPlans];
final currentPlan = currentPlans[likesState.index];
plansForModification[likesState.index] = currentPlan.copyWith(
totalLikes: likesState.totalLikes,
wasLikedByMe: likesState.wasLikedByMe,
);
emit(
state.copyWith(
plans: [
...plansForModification,
],
isLoading: isLoading,
errorMessage: null,
),
);
}
void _changePlanFavoriteState(FavoriteState favoriteState) {
final currentPlans = state.plans;
final plansForModification = [...currentPlans];
final currentPlan = currentPlans[favoriteState.index];
plansForModification[favoriteState.index] = currentPlan.copyWith(
isFavorite: favoriteState.isFavorite,
);
emit(
state.copyWith(
plans: [
...plansForModification,
],
isLoading: isLoading,
errorMessage: null,
),
);
}
void _changePlanCommentsState(CommentsState commentsState) {
final currentPlans = state.plans;
final plansForModification = [...currentPlans];
final currentPlan = currentPlans[commentsState.index];
plansForModification[commentsState.index] = currentPlan.copyWith(
totalComments: commentsState.totalComments,
);
emit(
state.copyWith(
plans: [
...plansForModification,
],
isLoading: isLoading,
errorMessage: null,
),
);
}
loadPlans({
String? query,
}) async {
try {
_query = query;
final type = _types[state.filterState.typeIndex];
final duration = _durationValues[state.filterState.durationIndex];
final minPrice = state.filterState.priceFilter.$1;
final maxPrice = state.filterState.priceFilter.$2;
final plansPagination = await feedRepository.getPlans(
query: query,
duration: duration,
maxPrice: maxPrice,
minPrice: minPrice,
types: type,
);
lastPage = plansPagination.meta.lastPage;
final minPlanPrice = plansPagination.meta.totalMinPrice ?? 0;
final maxPlanPrice = plansPagination.meta.totalMaxPrice ?? 0;
if (state.filterState.priceFilter.$1 == null || state.filterState.priceFilter.$2 == null) {
emit(
state.copyWith(
filterState: state.filterState.copyWith(priceFilter: (minPlanPrice, maxPlanPrice)),
),
);
}
emit(
state.copyWith(
plans: plansPagination.data,
isLoading: isLoading,
errorMessage: null,
isLoading: false,
minPlanPrice: minPlanPrice,
maxPlanPrice: maxPlanPrice,
),
);
} catch (e, stackTrace) {
log(e.toString());
log(stackTrace.toString());
emit(state.copyWith(errorMessage: e.toString()));
emit(state.copyWith(isLoading: isLoading, errorMessage: e.toString()));
}
}
@@ -69,4 +204,84 @@ class PlansCubit extends Cubit<PlansState> {
}
}
}
Future<void> addPlanToFavorite(String slug, int index) async {
final currentPlans = state.plans;
final plansForModification = [...currentPlans];
final currentPlan = currentPlans[index];
plansForModification[index] = currentPlan.copyWith(
isFavorite: true,
);
emit(
state.copyWith(
plans: [
...plansForModification,
],
isLoading: isLoading,
errorMessage: null,
),
);
EasyDebounce.debounce(
'add_or_remove_plan_from_favorite',
Duration(milliseconds: 200),
() async {
try {
await planRepository.addToFavorite(slug);
} catch (e) {
log(e.toString());
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
emit(
state.copyWith(
plans: [
...currentPlans,
],
isLoading: isLoading,
errorMessage: null,
),
);
}
},
);
}
Future<void> removePlanFromFavorite(String slug, int index) async {
final currentPlans = state.plans;
final plansForModification = [...currentPlans];
final currentPlan = currentPlans[index];
plansForModification[index] = currentPlan.copyWith(
isFavorite: false,
);
emit(
state.copyWith(
plans: [
...plansForModification,
],
isLoading: isLoading,
errorMessage: null,
),
);
EasyDebounce.debounce(
'add_or_remove_plan_from_favorite',
Duration(milliseconds: 200),
() async {
try {
await planRepository.addToFavorite(slug);
} catch (e) {
log(e.toString());
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
emit(
state.copyWith(
plans: [
...currentPlans,
],
isLoading: isLoading,
errorMessage: null,
),
);
}
},
);
}
}

View File

@@ -9,11 +9,28 @@ abstract class PlansState with _$PlansState {
required bool isLoading,
required List<PlanModel> plans,
required String? errorMessage,
required PlansFilterState filterState,
@Default(0) int minPlanPrice,
@Default(0) int maxPlanPrice,
}) = _PlansState;
factory PlansState.initial() => const _PlansState(
factory PlansState.initial() => const PlansState(
isLoading: true,
plans: [],
errorMessage: null,
);
filterState: PlansFilterState(
priceFilter: (null, null),
durationIndex: 0,
typeIndex: 0,
),
);
}
@freezed
class PlansFilterState with _$PlansFilterState {
const factory PlansFilterState({
required (int?, int?) priceFilter,
required int durationIndex,
required int typeIndex,
}) = _PlansFilterState;
}

View File

@@ -19,6 +19,9 @@ mixin _$PlansState {
bool get isLoading => throw _privateConstructorUsedError;
List<PlanModel> get plans => throw _privateConstructorUsedError;
String? get errorMessage => throw _privateConstructorUsedError;
PlansFilterState get filterState => throw _privateConstructorUsedError;
int get minPlanPrice => throw _privateConstructorUsedError;
int get maxPlanPrice => throw _privateConstructorUsedError;
/// Create a copy of PlansState
/// with the given fields replaced by the non-null parameter values.
@@ -33,7 +36,15 @@ abstract class $PlansStateCopyWith<$Res> {
PlansState value, $Res Function(PlansState) then) =
_$PlansStateCopyWithImpl<$Res, PlansState>;
@useResult
$Res call({bool isLoading, List<PlanModel> plans, String? errorMessage});
$Res call(
{bool isLoading,
List<PlanModel> plans,
String? errorMessage,
PlansFilterState filterState,
int minPlanPrice,
int maxPlanPrice});
$PlansFilterStateCopyWith<$Res> get filterState;
}
/// @nodoc
@@ -54,6 +65,9 @@ class _$PlansStateCopyWithImpl<$Res, $Val extends PlansState>
Object? isLoading = null,
Object? plans = null,
Object? errorMessage = freezed,
Object? filterState = null,
Object? minPlanPrice = null,
Object? maxPlanPrice = null,
}) {
return _then(_value.copyWith(
isLoading: null == isLoading
@@ -68,8 +82,30 @@ class _$PlansStateCopyWithImpl<$Res, $Val extends PlansState>
? _value.errorMessage
: errorMessage // ignore: cast_nullable_to_non_nullable
as String?,
filterState: null == filterState
? _value.filterState
: filterState // ignore: cast_nullable_to_non_nullable
as PlansFilterState,
minPlanPrice: null == minPlanPrice
? _value.minPlanPrice
: minPlanPrice // ignore: cast_nullable_to_non_nullable
as int,
maxPlanPrice: null == maxPlanPrice
? _value.maxPlanPrice
: maxPlanPrice // ignore: cast_nullable_to_non_nullable
as int,
) as $Val);
}
/// Create a copy of PlansState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$PlansFilterStateCopyWith<$Res> get filterState {
return $PlansFilterStateCopyWith<$Res>(_value.filterState, (value) {
return _then(_value.copyWith(filterState: value) as $Val);
});
}
}
/// @nodoc
@@ -80,7 +116,16 @@ abstract class _$$PlansStateImplCopyWith<$Res>
__$$PlansStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({bool isLoading, List<PlanModel> plans, String? errorMessage});
$Res call(
{bool isLoading,
List<PlanModel> plans,
String? errorMessage,
PlansFilterState filterState,
int minPlanPrice,
int maxPlanPrice});
@override
$PlansFilterStateCopyWith<$Res> get filterState;
}
/// @nodoc
@@ -99,6 +144,9 @@ class __$$PlansStateImplCopyWithImpl<$Res>
Object? isLoading = null,
Object? plans = null,
Object? errorMessage = freezed,
Object? filterState = null,
Object? minPlanPrice = null,
Object? maxPlanPrice = null,
}) {
return _then(_$PlansStateImpl(
isLoading: null == isLoading
@@ -113,6 +161,18 @@ class __$$PlansStateImplCopyWithImpl<$Res>
? _value.errorMessage
: errorMessage // ignore: cast_nullable_to_non_nullable
as String?,
filterState: null == filterState
? _value.filterState
: filterState // ignore: cast_nullable_to_non_nullable
as PlansFilterState,
minPlanPrice: null == minPlanPrice
? _value.minPlanPrice
: minPlanPrice // ignore: cast_nullable_to_non_nullable
as int,
maxPlanPrice: null == maxPlanPrice
? _value.maxPlanPrice
: maxPlanPrice // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
@@ -123,7 +183,10 @@ class _$PlansStateImpl implements _PlansState {
const _$PlansStateImpl(
{required this.isLoading,
required final List<PlanModel> plans,
required this.errorMessage})
required this.errorMessage,
required this.filterState,
this.minPlanPrice = 0,
this.maxPlanPrice = 0})
: _plans = plans;
@override
@@ -138,10 +201,18 @@ class _$PlansStateImpl implements _PlansState {
@override
final String? errorMessage;
@override
final PlansFilterState filterState;
@override
@JsonKey()
final int minPlanPrice;
@override
@JsonKey()
final int maxPlanPrice;
@override
String toString() {
return 'PlansState(isLoading: $isLoading, plans: $plans, errorMessage: $errorMessage)';
return 'PlansState(isLoading: $isLoading, plans: $plans, errorMessage: $errorMessage, filterState: $filterState, minPlanPrice: $minPlanPrice, maxPlanPrice: $maxPlanPrice)';
}
@override
@@ -153,12 +224,24 @@ class _$PlansStateImpl implements _PlansState {
other.isLoading == isLoading) &&
const DeepCollectionEquality().equals(other._plans, _plans) &&
(identical(other.errorMessage, errorMessage) ||
other.errorMessage == errorMessage));
other.errorMessage == errorMessage) &&
(identical(other.filterState, filterState) ||
other.filterState == filterState) &&
(identical(other.minPlanPrice, minPlanPrice) ||
other.minPlanPrice == minPlanPrice) &&
(identical(other.maxPlanPrice, maxPlanPrice) ||
other.maxPlanPrice == maxPlanPrice));
}
@override
int get hashCode => Object.hash(runtimeType, isLoading,
const DeepCollectionEquality().hash(_plans), errorMessage);
int get hashCode => Object.hash(
runtimeType,
isLoading,
const DeepCollectionEquality().hash(_plans),
errorMessage,
filterState,
minPlanPrice,
maxPlanPrice);
/// Create a copy of PlansState
/// with the given fields replaced by the non-null parameter values.
@@ -173,7 +256,10 @@ abstract class _PlansState implements PlansState {
const factory _PlansState(
{required final bool isLoading,
required final List<PlanModel> plans,
required final String? errorMessage}) = _$PlansStateImpl;
required final String? errorMessage,
required final PlansFilterState filterState,
final int minPlanPrice,
final int maxPlanPrice}) = _$PlansStateImpl;
@override
bool get isLoading;
@@ -181,6 +267,12 @@ abstract class _PlansState implements PlansState {
List<PlanModel> get plans;
@override
String? get errorMessage;
@override
PlansFilterState get filterState;
@override
int get minPlanPrice;
@override
int get maxPlanPrice;
/// Create a copy of PlansState
/// with the given fields replaced by the non-null parameter values.
@@ -189,3 +281,174 @@ abstract class _PlansState implements PlansState {
_$$PlansStateImplCopyWith<_$PlansStateImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$PlansFilterState {
(int?, int?) get priceFilter => throw _privateConstructorUsedError;
int get durationIndex => throw _privateConstructorUsedError;
int get typeIndex => throw _privateConstructorUsedError;
/// Create a copy of PlansFilterState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$PlansFilterStateCopyWith<PlansFilterState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $PlansFilterStateCopyWith<$Res> {
factory $PlansFilterStateCopyWith(
PlansFilterState value, $Res Function(PlansFilterState) then) =
_$PlansFilterStateCopyWithImpl<$Res, PlansFilterState>;
@useResult
$Res call({(int?, int?) priceFilter, int durationIndex, int typeIndex});
}
/// @nodoc
class _$PlansFilterStateCopyWithImpl<$Res, $Val extends PlansFilterState>
implements $PlansFilterStateCopyWith<$Res> {
_$PlansFilterStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of PlansFilterState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? priceFilter = null,
Object? durationIndex = null,
Object? typeIndex = null,
}) {
return _then(_value.copyWith(
priceFilter: null == priceFilter
? _value.priceFilter
: priceFilter // ignore: cast_nullable_to_non_nullable
as (int?, int?),
durationIndex: null == durationIndex
? _value.durationIndex
: durationIndex // ignore: cast_nullable_to_non_nullable
as int,
typeIndex: null == typeIndex
? _value.typeIndex
: typeIndex // ignore: cast_nullable_to_non_nullable
as int,
) as $Val);
}
}
/// @nodoc
abstract class _$$PlansFilterStateImplCopyWith<$Res>
implements $PlansFilterStateCopyWith<$Res> {
factory _$$PlansFilterStateImplCopyWith(_$PlansFilterStateImpl value,
$Res Function(_$PlansFilterStateImpl) then) =
__$$PlansFilterStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({(int?, int?) priceFilter, int durationIndex, int typeIndex});
}
/// @nodoc
class __$$PlansFilterStateImplCopyWithImpl<$Res>
extends _$PlansFilterStateCopyWithImpl<$Res, _$PlansFilterStateImpl>
implements _$$PlansFilterStateImplCopyWith<$Res> {
__$$PlansFilterStateImplCopyWithImpl(_$PlansFilterStateImpl _value,
$Res Function(_$PlansFilterStateImpl) _then)
: super(_value, _then);
/// Create a copy of PlansFilterState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? priceFilter = null,
Object? durationIndex = null,
Object? typeIndex = null,
}) {
return _then(_$PlansFilterStateImpl(
priceFilter: null == priceFilter
? _value.priceFilter
: priceFilter // ignore: cast_nullable_to_non_nullable
as (int?, int?),
durationIndex: null == durationIndex
? _value.durationIndex
: durationIndex // ignore: cast_nullable_to_non_nullable
as int,
typeIndex: null == typeIndex
? _value.typeIndex
: typeIndex // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// @nodoc
class _$PlansFilterStateImpl implements _PlansFilterState {
const _$PlansFilterStateImpl(
{required this.priceFilter,
required this.durationIndex,
required this.typeIndex});
@override
final (int?, int?) priceFilter;
@override
final int durationIndex;
@override
final int typeIndex;
@override
String toString() {
return 'PlansFilterState(priceFilter: $priceFilter, durationIndex: $durationIndex, typeIndex: $typeIndex)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$PlansFilterStateImpl &&
(identical(other.priceFilter, priceFilter) ||
other.priceFilter == priceFilter) &&
(identical(other.durationIndex, durationIndex) ||
other.durationIndex == durationIndex) &&
(identical(other.typeIndex, typeIndex) ||
other.typeIndex == typeIndex));
}
@override
int get hashCode =>
Object.hash(runtimeType, priceFilter, durationIndex, typeIndex);
/// Create a copy of PlansFilterState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$PlansFilterStateImplCopyWith<_$PlansFilterStateImpl> get copyWith =>
__$$PlansFilterStateImplCopyWithImpl<_$PlansFilterStateImpl>(
this, _$identity);
}
abstract class _PlansFilterState implements PlansFilterState {
const factory _PlansFilterState(
{required final (int?, int?) priceFilter,
required final int durationIndex,
required final int typeIndex}) = _$PlansFilterStateImpl;
@override
(int?, int?) get priceFilter;
@override
int get durationIndex;
@override
int get typeIndex;
/// Create a copy of PlansFilterState
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$PlansFilterStateImplCopyWith<_$PlansFilterStateImpl> get copyWith =>
throw _privateConstructorUsedError;
}