pre merge commit

This commit is contained in:
Username1548
2025-03-10 22:25:30 +07:00
parent afac964e2e
commit f590c49def
18 changed files with 604 additions and 32 deletions

View File

@@ -0,0 +1,20 @@
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'chosen_plan_model.freezed.dart';
part 'chosen_plan_model.g.dart';
@freezed
class ChosenPlanModel with _$ChosenPlanModel {
factory ChosenPlanModel({
@JsonKey() required int id,
@JsonKey(name: 'option_type') required String optionType,
@JsonKey(name: 'start_date') required DateTime startDate,
@JsonKey(name: 'chat_id') required String chatId,
@JsonKey(name: 'plan') required PlanModel plan,
}) = _ChosenPlanModel;
factory ChosenPlanModel.fromJson(Map<String, dynamic> json) => _$ChosenPlanModelFromJson(json);
}

View File

@@ -0,0 +1,283 @@
// 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 'chosen_plan_model.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#adding-getters-and-methods-to-our-models');
ChosenPlanModel _$ChosenPlanModelFromJson(Map<String, dynamic> json) {
return _ChosenPlanModel.fromJson(json);
}
/// @nodoc
mixin _$ChosenPlanModel {
@JsonKey()
int get id => throw _privateConstructorUsedError;
@JsonKey(name: 'option_type')
String get optionType => throw _privateConstructorUsedError;
@JsonKey(name: 'start_date')
DateTime get startDate => throw _privateConstructorUsedError;
@JsonKey(name: 'chat_id')
String get chatId => throw _privateConstructorUsedError;
@JsonKey(name: 'plan')
PlanModel get plan => throw _privateConstructorUsedError;
/// Serializes this ChosenPlanModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of ChosenPlanModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ChosenPlanModelCopyWith<ChosenPlanModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ChosenPlanModelCopyWith<$Res> {
factory $ChosenPlanModelCopyWith(
ChosenPlanModel value, $Res Function(ChosenPlanModel) then) =
_$ChosenPlanModelCopyWithImpl<$Res, ChosenPlanModel>;
@useResult
$Res call(
{@JsonKey() int id,
@JsonKey(name: 'option_type') String optionType,
@JsonKey(name: 'start_date') DateTime startDate,
@JsonKey(name: 'chat_id') String chatId,
@JsonKey(name: 'plan') PlanModel plan});
$PlanModelCopyWith<$Res> get plan;
}
/// @nodoc
class _$ChosenPlanModelCopyWithImpl<$Res, $Val extends ChosenPlanModel>
implements $ChosenPlanModelCopyWith<$Res> {
_$ChosenPlanModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of ChosenPlanModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? optionType = null,
Object? startDate = null,
Object? chatId = null,
Object? plan = null,
}) {
return _then(_value.copyWith(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
optionType: null == optionType
? _value.optionType
: optionType // ignore: cast_nullable_to_non_nullable
as String,
startDate: null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
chatId: null == chatId
? _value.chatId
: chatId // ignore: cast_nullable_to_non_nullable
as String,
plan: null == plan
? _value.plan
: plan // ignore: cast_nullable_to_non_nullable
as PlanModel,
) as $Val);
}
/// Create a copy of ChosenPlanModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$PlanModelCopyWith<$Res> get plan {
return $PlanModelCopyWith<$Res>(_value.plan, (value) {
return _then(_value.copyWith(plan: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$ChosenPlanModelImplCopyWith<$Res>
implements $ChosenPlanModelCopyWith<$Res> {
factory _$$ChosenPlanModelImplCopyWith(_$ChosenPlanModelImpl value,
$Res Function(_$ChosenPlanModelImpl) then) =
__$$ChosenPlanModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{@JsonKey() int id,
@JsonKey(name: 'option_type') String optionType,
@JsonKey(name: 'start_date') DateTime startDate,
@JsonKey(name: 'chat_id') String chatId,
@JsonKey(name: 'plan') PlanModel plan});
@override
$PlanModelCopyWith<$Res> get plan;
}
/// @nodoc
class __$$ChosenPlanModelImplCopyWithImpl<$Res>
extends _$ChosenPlanModelCopyWithImpl<$Res, _$ChosenPlanModelImpl>
implements _$$ChosenPlanModelImplCopyWith<$Res> {
__$$ChosenPlanModelImplCopyWithImpl(
_$ChosenPlanModelImpl _value, $Res Function(_$ChosenPlanModelImpl) _then)
: super(_value, _then);
/// Create a copy of ChosenPlanModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? optionType = null,
Object? startDate = null,
Object? chatId = null,
Object? plan = null,
}) {
return _then(_$ChosenPlanModelImpl(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
optionType: null == optionType
? _value.optionType
: optionType // ignore: cast_nullable_to_non_nullable
as String,
startDate: null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
chatId: null == chatId
? _value.chatId
: chatId // ignore: cast_nullable_to_non_nullable
as String,
plan: null == plan
? _value.plan
: plan // ignore: cast_nullable_to_non_nullable
as PlanModel,
));
}
}
/// @nodoc
@JsonSerializable()
class _$ChosenPlanModelImpl implements _ChosenPlanModel {
_$ChosenPlanModelImpl(
{@JsonKey() required this.id,
@JsonKey(name: 'option_type') required this.optionType,
@JsonKey(name: 'start_date') required this.startDate,
@JsonKey(name: 'chat_id') required this.chatId,
@JsonKey(name: 'plan') required this.plan});
factory _$ChosenPlanModelImpl.fromJson(Map<String, dynamic> json) =>
_$$ChosenPlanModelImplFromJson(json);
@override
@JsonKey()
final int id;
@override
@JsonKey(name: 'option_type')
final String optionType;
@override
@JsonKey(name: 'start_date')
final DateTime startDate;
@override
@JsonKey(name: 'chat_id')
final String chatId;
@override
@JsonKey(name: 'plan')
final PlanModel plan;
@override
String toString() {
return 'ChosenPlanModel(id: $id, optionType: $optionType, startDate: $startDate, chatId: $chatId, plan: $plan)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ChosenPlanModelImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.optionType, optionType) ||
other.optionType == optionType) &&
(identical(other.startDate, startDate) ||
other.startDate == startDate) &&
(identical(other.chatId, chatId) || other.chatId == chatId) &&
(identical(other.plan, plan) || other.plan == plan));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(runtimeType, id, optionType, startDate, chatId, plan);
/// Create a copy of ChosenPlanModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ChosenPlanModelImplCopyWith<_$ChosenPlanModelImpl> get copyWith =>
__$$ChosenPlanModelImplCopyWithImpl<_$ChosenPlanModelImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$ChosenPlanModelImplToJson(
this,
);
}
}
abstract class _ChosenPlanModel implements ChosenPlanModel {
factory _ChosenPlanModel(
{@JsonKey() required final int id,
@JsonKey(name: 'option_type') required final String optionType,
@JsonKey(name: 'start_date') required final DateTime startDate,
@JsonKey(name: 'chat_id') required final String chatId,
@JsonKey(name: 'plan') required final PlanModel plan}) =
_$ChosenPlanModelImpl;
factory _ChosenPlanModel.fromJson(Map<String, dynamic> json) =
_$ChosenPlanModelImpl.fromJson;
@override
@JsonKey()
int get id;
@override
@JsonKey(name: 'option_type')
String get optionType;
@override
@JsonKey(name: 'start_date')
DateTime get startDate;
@override
@JsonKey(name: 'chat_id')
String get chatId;
@override
@JsonKey(name: 'plan')
PlanModel get plan;
/// Create a copy of ChosenPlanModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ChosenPlanModelImplCopyWith<_$ChosenPlanModelImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,27 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'chosen_plan_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$ChosenPlanModelImpl _$$ChosenPlanModelImplFromJson(
Map<String, dynamic> json) =>
_$ChosenPlanModelImpl(
id: (json['id'] as num).toInt(),
optionType: json['option_type'] as String,
startDate: DateTime.parse(json['start_date'] as String),
chatId: json['chat_id'] as String,
plan: PlanModel.fromJson(json['plan'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$ChosenPlanModelImplToJson(
_$ChosenPlanModelImpl instance) =>
<String, dynamic>{
'id': instance.id,
'option_type': instance.optionType,
'start_date': instance.startDate.toIso8601String(),
'chat_id': instance.chatId,
'plan': instance.plan,
};

View File

@@ -1,8 +1,10 @@
import 'dart:developer';
import 'package:dio/dio.dart';
import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/core/utils/toasts.dart';
import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
import 'package:eatvio_app/features/plans/presentation/cubit/about_plan_state.dart';
import 'package:eatvio_app/features/plans/repositories/plan_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -125,4 +127,27 @@ class AboutPlanCubit extends Cubit<AboutPlanState> {
},
);
}
Future<ChosenPlanModel?> chosePlan() async {
try {
final slug = state.planModel?.slug;
final optionIndex = state.chosenOptionIndex;
if (slug != null) {
final optionName = _getOptionNameFromIndex(optionIndex);
final body = {'options_type': optionName};
return await planRepository.chosePlan(slug, body);
}
} on DioException catch (e, st) {
log(e.toString(), stackTrace: st);
} catch (e, st) {
log(e.toString(), stackTrace: st);
}
return null;
}
String _getOptionNameFromIndex(int index) => switch (index) {
1 => 'options2',
2 => 'options3',
_ => 'options1',
};
}

View File

@@ -10,6 +10,7 @@ import 'package:eatvio_app/core/widgets/bookmark_button.dart';
import 'package:eatvio_app/core/widgets/data_error_widget.dart';
import 'package:eatvio_app/core/widgets/info_row.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/core/widgets/show_error_dialog.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart';
@@ -30,6 +31,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_portal/flutter_portal.dart';
import 'package:flutter_svg/svg.dart';
import 'package:go_router/go_router.dart';
import '../widgets/option_widget.dart';
@@ -217,17 +219,21 @@ class _AboutPlanScreenState extends State<AboutPlanScreen> {
borderRadius: BorderRadius.circular(16),
),
backgroundColor: appTheme.greenA700,
onPressed: () {
/// TODO: добавить обработку покупки плана
// showModalBottomSheet(
// context: context,
// builder: (context) => StartDietBottomSheet(
// aboutDietCubit: context.read<AboutDietCubit>(),
// ),
// );
onPressed: () async {
final chosenPlan = await context.read<AboutPlanCubit>().chosePlan();
if (chosenPlan == null) {
showErrorDialog(context, title: 'Не удалось заказать план');
return;
}
GoRouter.of(context).pushReplacementNamed(
'chatScreen',
extra: {
'chatId': chosenPlan.chatId,
},
);
},
label: Text(
s.startDiet,
s.to_order,
style: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
),
),

View File

@@ -1,9 +1,9 @@
import 'dart:async';
import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
import 'package:eatvio_app/features/plans/services/plan_service.dart';
class PlanRepository {
final PlanService planService;
@@ -48,9 +48,15 @@ class PlanRepository {
}
}
Future<ChosenPlanModel> chosePlan(String slug, Map<String, dynamic> option ) async {
try {
return await planService.chosePlan(slug: slug, option: option);
} catch (e) {
rethrow;
}
}
void emitLikesStateToFeed(LikesState state) => _planLikeStateStreamController.add(state);
void emitFavoriteStateToFeed(FavoriteState state) => _planFavoriteStateStreamController.add(state);
void emitCommentsStateToFeed(CommentsState state) => _planCommentsStateStreamController.add(state);
}

View File

@@ -1,20 +1,18 @@
import 'package:dio/dio.dart';
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
import 'package:retrofit/retrofit.dart';
part 'plan_service.g.dart';
@RestApi()
abstract class PlanService {
factory PlanService(Dio dio, {String baseUrl}) = _PlanService;
@GET('/api/plans/show/{slug}')
Future<PlanModel> getPlan(@Path('slug') String slug);
@POST('/api/plans/{slug}/like')
@POST('/api/plans/{slug}/like')
Future<void> likePlan({
@Path() required String slug,
});
@@ -28,4 +26,10 @@ abstract class PlanService {
Future<void> removeFromFavorite({
@Path() required String slug,
});
}
@POST('/api/plans/{slug}/choose')
Future<ChosenPlanModel> chosePlan({
@Path() required String slug,
@Body() required Map<String, dynamic> option,
});
}

View File

@@ -101,6 +101,37 @@ class _PlanService implements PlanService {
await _dio.fetch<void>(_options);
}
@override
Future<ChosenPlanModel> chosePlan({
required String slug,
required Map<String, dynamic> option,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
final _data = <String, dynamic>{};
_data.addAll(option);
final _options = _setStreamType<ChosenPlanModel>(
Options(method: 'POST', headers: _headers, extra: _extra)
.compose(
_dio.options,
'/api/plans/${slug}/choose',
queryParameters: queryParameters,
data: _data,
)
.copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
);
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late ChosenPlanModel _value;
try {
_value = ChosenPlanModel.fromJson(_result.data!);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
return _value;
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||