Merge pull request 'new test cases' (#218) from misha/eatvio_app:main into main

Reviewed-on: eatvio/eatvio_app#218
This commit is contained in:
BLoC
2025-01-29 11:35:18 +00:00
38 changed files with 897 additions and 460 deletions

View File

@@ -154,7 +154,17 @@ class _ChatScreenState extends State<ChatScreen> {
if ((index + 1 != state.chatMessages.length &&
DateTime.parse(state.chatMessages[index].updatedAt).day != DateTime.parse(state.chatMessages[index + 1].updatedAt).day) ||
index + 1 == state.chatMessages.length) {
return _messageDate(state.chatMessages[index].updatedAt);
return Column(
children: [
_messageDate(state.chatMessages[index].updatedAt),
ChatBubbleWidget(
sendTime: state.chatMessages[index].updatedAt,
text: state.chatMessages[index].data.trim(),
isCurrentUser: state.chatRoom.users['to_chat_user']!.id != state.chatMessages[index].authorId,
mediaData: state.chatMessages[index].mediaData ?? [],
),
],
);
}
return ChatBubbleWidget(
sendTime: state.chatMessages[index].updatedAt,

View File

@@ -25,7 +25,7 @@ class DevToolsScreenContent extends StatefulWidget {
class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
String baseUrlForTest = '';
final List<TestCases> testCases = [Test850task()];
final List<TestCases> testCases = [Test850task(), GetChatRoomPreviews(), GetChatRoom(), SendMessage(), GetChatMessages(), GetChatRoomIdByUserSlug()];
final controller = TextEditingController();
late final List<TestState> testsStates;

View File

@@ -4,6 +4,7 @@ import 'dart:developer';
import 'package:dio/dio.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/utils/date_time_utils.dart';
import 'package:eatvio_app/features/chat/services/chat_service.dart';
import 'package:eatvio_app/features/diets/services/diet_service.dart';
import 'package:eatvio_app/features/feed/services/feed_service.dart';
import 'package:eatvio_app/features/profile/services/profile_service.dart';
@@ -113,3 +114,106 @@ class Test850task implements TestCases {
}
}
}
class GetChatRoomPreviews implements TestCases {
@override
String get name => '/api/chat/index';
@override
Future<bool> run({required String baseUrl}) async {
try {
// get all of needed services
final dio = sl<Dio>();
final chatService = ChatService(dio, baseUrl: baseUrl);
await chatService.getChatRoomPreviews();
return true;
} catch (e) {
return false;
}
}
}
class GetChatRoom implements TestCases {
@override
String get name => '/api/chat/show/{id}';
@override
Future<bool> run({required String baseUrl}) async {
try {
final dio = sl<Dio>();
final chatService = ChatService(dio, baseUrl: baseUrl);
final chatRoomsModels = await chatService.getChatRoomPreviews();
await chatService.getChatRoom(id: chatRoomsModels.chatRooms.firstOrNull?.id ?? '');
return true;
} catch (e) {
return false;
}
}
}
class SendMessage implements TestCases {
@override
String get name => '/api/chat/{id}/message/send';
@override
Future<bool> run({required String baseUrl}) async {
try {
final dio = sl<Dio>();
final chatService = ChatService(dio, baseUrl: baseUrl);
final chatRoomsModels = await chatService.getChatRoomPreviews();
await chatService.sendMessage(id: chatRoomsModels.chatRooms.firstOrNull?.id ?? '', data: 'test message');
return true;
} catch (e) {
return false;
}
}
}
class GetChatMessages implements TestCases {
@override
String get name => '/api/chat/show/{id}/messages?page={page}';
@override
Future<bool> run({required String baseUrl}) async {
try {
final dio = sl<Dio>();
final chatService = ChatService(dio, baseUrl: baseUrl);
final chatRoomsModels = await chatService.getChatRoomPreviews();
await chatService.getChatMessages(id: chatRoomsModels.chatRooms.firstOrNull?.id ?? '', page: 1);
return true;
} catch (e) {
return false;
}
}
}
class GetChatRoomIdByUserSlug implements TestCases {
@override
String get name => '/api/chat/get_by_user/{user}';
@override
Future<bool> run({required String baseUrl}) async {
try {
final dio = sl<Dio>();
final chatService = ChatService(dio, baseUrl: baseUrl);
final consultantService = FeedService(dio, baseUrl: baseUrl);
final profileService = ProfileService(dio, baseUrl: baseUrl);
final myProfile = await profileService.getMyProfile();
final consultantsModel = await consultantService.getConsultants();
for (var consultant in consultantsModel.data) {
if (consultant.slug != myProfile.slug) {
await chatService.getChatRoomIdByUserSlug(user: consultant.slug);
break;
}
}
return true;
} catch (e) {
return false;
}
}
}

View File

@@ -1,14 +1,21 @@
import 'dart:async';
import 'dart:developer';
import 'package:dio/dio.dart';
import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/core/utils/date_time_utils.dart';
import 'package:eatvio_app/core/utils/toasts.dart';
import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
import 'package:eatvio_app/features/diets/presentation/cubit/about_diet_state.dart';
import 'package:eatvio_app/features/profile/repositories/profile_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class AboutDietCubit extends Cubit<AboutDietState> {
AboutDietCubit({required this.dietsRepository}) : super(AboutDietState.initial());
AboutDietCubit({required this.dietsRepository, required this.profileRepository}) : super(AboutDietState.initial());
final DietRepository dietsRepository;
final ProfileRepository profileRepository;
bool isCurrentDietStarted = false;
bool hasStartedDiet = false;
String? userStartedDietName;
Future<void> getDiet(int id, {int? index}) async {
try {
@@ -16,17 +23,30 @@ class AboutDietCubit extends Cubit<AboutDietState> {
if (index != null) {
dietsRepository.emitCommentsStateToFeed(CommentsState(index: index, totalComments: diet.totalComments));
}
emit(AboutDietState.loaded(diet));
emit(AboutDietState.loaded(diet: diet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
} catch (e) {
log(e.toString());
}
}
Future<void> getDietWithLoading(int id) async {
Future<void> initDietPage(int id) async {
try {
emit(AboutDietState.loading());
final diet = await dietsRepository.getDiet(id);
emit(AboutDietState.loaded(diet));
final chosenDiets = await profileRepository.getChosenDiets();
final lastChosenDiet = chosenDiets.firstOrNull;
if (lastChosenDiet != null && ['inited', 'started'].contains(lastChosenDiet.state)) {
hasStartedDiet = true;
if (diet.id == lastChosenDiet.dietId) {
isCurrentDietStarted = true;
} else {
if (lastChosenDiet.dietId != null) {
final chosenDiet = await dietsRepository.getDiet(lastChosenDiet.dietId!);
userStartedDietName = chosenDiet.name;
}
}
}
emit(AboutDietState.loaded(diet: diet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
} catch (e) {
log(e.toString());
}
@@ -45,7 +65,7 @@ class AboutDietCubit extends Cubit<AboutDietState> {
if (dietIndexInFeed != null) {
dietsRepository.emitLikesStateToFeed(LikesState(wasLikedByMe: updatedDiet.wasLikedByMe, totalLikes: updatedDiet.totalLikes, index: dietIndexInFeed));
}
emit(AboutDietState.loaded(updatedDiet));
emit(AboutDietState.loaded(diet: updatedDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
} else {
final updatedDiet = currentDiet.copyWith(
totalLikes: currentDiet.totalLikes + 1,
@@ -54,7 +74,7 @@ class AboutDietCubit extends Cubit<AboutDietState> {
if (dietIndexInFeed != null) {
dietsRepository.emitLikesStateToFeed(LikesState(wasLikedByMe: updatedDiet.wasLikedByMe, totalLikes: updatedDiet.totalLikes, index: dietIndexInFeed));
}
emit(AboutDietState.loaded(updatedDiet));
emit(AboutDietState.loaded(diet: updatedDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
}
EasyDebounce.debounce(
@@ -69,7 +89,7 @@ class AboutDietCubit extends Cubit<AboutDietState> {
dietsRepository
.emitLikesStateToFeed(LikesState(wasLikedByMe: currentDiet.wasLikedByMe, totalLikes: currentDiet.totalLikes, index: dietIndexInFeed));
}
emit(AboutDietState.loaded(currentDiet));
emit(AboutDietState.loaded(diet: currentDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
}
},
);
@@ -86,7 +106,7 @@ class AboutDietCubit extends Cubit<AboutDietState> {
if (dietIndex != null) {
dietsRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: updatedDiet.isFavorite, index: dietIndex));
}
emit(AboutDietState.loaded(updatedDiet));
emit(AboutDietState.loaded(diet: updatedDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
EasyDebounce.debounce(
'add_or_remove_diet_to_favorites',
@@ -100,7 +120,7 @@ class AboutDietCubit extends Cubit<AboutDietState> {
if (dietIndex != null) {
dietsRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: currentDiet.isFavorite, index: dietIndex));
}
emit(AboutDietState.loaded(currentDiet));
emit(AboutDietState.loaded(diet: currentDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
}
},
);
@@ -117,7 +137,7 @@ class AboutDietCubit extends Cubit<AboutDietState> {
if (dietIndex != null) {
dietsRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: updatedDiet.isFavorite, index: dietIndex));
}
emit(AboutDietState.loaded(updatedDiet));
emit(AboutDietState.loaded(diet: updatedDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
EasyDebounce.debounce(
'add_or_remove_diet_to_favorites',
@@ -131,9 +151,50 @@ class AboutDietCubit extends Cubit<AboutDietState> {
if (dietIndex != null) {
dietsRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: currentDiet.isFavorite, index: dietIndex));
}
emit(AboutDietState.loaded(currentDiet));
emit(AboutDietState.loaded(diet: currentDiet, isStarted: isCurrentDietStarted, hasStartedDiet: hasStartedDiet));
}
},
);
}
Future<bool> startDiet(DateTime date) async {
try {
if (state is! DietStateLoaded) return false;
final id = (state as DietStateLoaded).diet.id;
final stringDate = date.format('yyyy-MM-dd');
await dietsRepository.startDiet(id, stringDate);
return true;
} catch (e, st) {
log(e.toString(), stackTrace: st);
return false;
}
}
Future<bool> cancelDiet() async {
try {
if (state is! DietStateLoaded) return false;
final id = (state as DietStateLoaded).diet.id;
await dietsRepository.cancelDiet(id);
return true;
} on DioException catch (e) {
log(e.toString());
return false;
} catch (e) {
log(e.toString());
return false;
}
}
void clearStartedDietsState() {
if (state is! DietStateLoaded) return;
final diet = (state as DietStateLoaded).diet;
emit(AboutDietState.loaded(diet: diet));
}
void emitCurrentStartedDietsState() {
if (state is! DietStateLoaded) return;
final diet = (state as DietStateLoaded).diet;
emit(AboutDietState.loaded(diet: diet, isStarted: true));
}
}

View File

@@ -9,6 +9,10 @@ class AboutDietState with _$AboutDietState {
const factory AboutDietState.initial() = DietStateInitial;
const factory AboutDietState.loading() = DietStateLoading;
const factory AboutDietState.loaded(Diet diet) = DietStateLoaded;
const factory AboutDietState.loaded({
required Diet diet,
@Default(false) bool isStarted,
@Default(false) bool hasStartedDiet,
}) = DietStateLoaded;
const factory AboutDietState.error({required String message}) = DietStateError;
}

View File

@@ -21,7 +21,8 @@ mixin _$AboutDietState {
TResult Function() $default, {
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(Diet diet) loaded,
required TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)
loaded,
required TResult Function(String message) error,
}) =>
throw _privateConstructorUsedError;
@@ -30,7 +31,7 @@ mixin _$AboutDietState {
TResult? Function()? $default, {
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(Diet diet)? loaded,
TResult? Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult? Function(String message)? error,
}) =>
throw _privateConstructorUsedError;
@@ -39,7 +40,7 @@ mixin _$AboutDietState {
TResult Function()? $default, {
TResult Function()? initial,
TResult Function()? loading,
TResult Function(Diet diet)? loaded,
TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) =>
@@ -139,7 +140,8 @@ class _$AboutDietStateImpl implements _AboutDietState {
TResult Function() $default, {
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(Diet diet) loaded,
required TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)
loaded,
required TResult Function(String message) error,
}) {
return $default();
@@ -151,7 +153,7 @@ class _$AboutDietStateImpl implements _AboutDietState {
TResult? Function()? $default, {
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(Diet diet)? loaded,
TResult? Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult? Function(String message)? error,
}) {
return $default?.call();
@@ -163,7 +165,7 @@ class _$AboutDietStateImpl implements _AboutDietState {
TResult Function()? $default, {
TResult Function()? initial,
TResult Function()? loading,
TResult Function(Diet diet)? loaded,
TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
@@ -262,7 +264,8 @@ class _$DietStateInitialImpl implements DietStateInitial {
TResult Function() $default, {
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(Diet diet) loaded,
required TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)
loaded,
required TResult Function(String message) error,
}) {
return initial();
@@ -274,7 +277,7 @@ class _$DietStateInitialImpl implements DietStateInitial {
TResult? Function()? $default, {
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(Diet diet)? loaded,
TResult? Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult? Function(String message)? error,
}) {
return initial?.call();
@@ -286,7 +289,7 @@ class _$DietStateInitialImpl implements DietStateInitial {
TResult Function()? $default, {
TResult Function()? initial,
TResult Function()? loading,
TResult Function(Diet diet)? loaded,
TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
@@ -385,7 +388,8 @@ class _$DietStateLoadingImpl implements DietStateLoading {
TResult Function() $default, {
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(Diet diet) loaded,
required TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)
loaded,
required TResult Function(String message) error,
}) {
return loading();
@@ -397,7 +401,7 @@ class _$DietStateLoadingImpl implements DietStateLoading {
TResult? Function()? $default, {
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(Diet diet)? loaded,
TResult? Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult? Function(String message)? error,
}) {
return loading?.call();
@@ -409,7 +413,7 @@ class _$DietStateLoadingImpl implements DietStateLoading {
TResult Function()? $default, {
TResult Function()? initial,
TResult Function()? loading,
TResult Function(Diet diet)? loaded,
TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
@@ -470,7 +474,7 @@ abstract class _$$DietStateLoadedImplCopyWith<$Res> {
$Res Function(_$DietStateLoadedImpl) then) =
__$$DietStateLoadedImplCopyWithImpl<$Res>;
@useResult
$Res call({Diet diet});
$Res call({Diet diet, bool isStarted, bool hasStartedDiet});
$DietCopyWith<$Res> get diet;
}
@@ -489,12 +493,22 @@ class __$$DietStateLoadedImplCopyWithImpl<$Res>
@override
$Res call({
Object? diet = null,
Object? isStarted = null,
Object? hasStartedDiet = null,
}) {
return _then(_$DietStateLoadedImpl(
null == diet
diet: null == diet
? _value.diet
: diet // ignore: cast_nullable_to_non_nullable
as Diet,
isStarted: null == isStarted
? _value.isStarted
: isStarted // ignore: cast_nullable_to_non_nullable
as bool,
hasStartedDiet: null == hasStartedDiet
? _value.hasStartedDiet
: hasStartedDiet // ignore: cast_nullable_to_non_nullable
as bool,
));
}
@@ -512,14 +526,23 @@ class __$$DietStateLoadedImplCopyWithImpl<$Res>
/// @nodoc
class _$DietStateLoadedImpl implements DietStateLoaded {
const _$DietStateLoadedImpl(this.diet);
const _$DietStateLoadedImpl(
{required this.diet,
this.isStarted = false,
this.hasStartedDiet = false});
@override
final Diet diet;
@override
@JsonKey()
final bool isStarted;
@override
@JsonKey()
final bool hasStartedDiet;
@override
String toString() {
return 'AboutDietState.loaded(diet: $diet)';
return 'AboutDietState.loaded(diet: $diet, isStarted: $isStarted, hasStartedDiet: $hasStartedDiet)';
}
@override
@@ -527,11 +550,15 @@ class _$DietStateLoadedImpl implements DietStateLoaded {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$DietStateLoadedImpl &&
(identical(other.diet, diet) || other.diet == diet));
(identical(other.diet, diet) || other.diet == diet) &&
(identical(other.isStarted, isStarted) ||
other.isStarted == isStarted) &&
(identical(other.hasStartedDiet, hasStartedDiet) ||
other.hasStartedDiet == hasStartedDiet));
}
@override
int get hashCode => Object.hash(runtimeType, diet);
int get hashCode => Object.hash(runtimeType, diet, isStarted, hasStartedDiet);
/// Create a copy of AboutDietState
/// with the given fields replaced by the non-null parameter values.
@@ -548,10 +575,11 @@ class _$DietStateLoadedImpl implements DietStateLoaded {
TResult Function() $default, {
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(Diet diet) loaded,
required TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)
loaded,
required TResult Function(String message) error,
}) {
return loaded(diet);
return loaded(diet, isStarted, hasStartedDiet);
}
@override
@@ -560,10 +588,10 @@ class _$DietStateLoadedImpl implements DietStateLoaded {
TResult? Function()? $default, {
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(Diet diet)? loaded,
TResult? Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult? Function(String message)? error,
}) {
return loaded?.call(diet);
return loaded?.call(diet, isStarted, hasStartedDiet);
}
@override
@@ -572,12 +600,12 @@ class _$DietStateLoadedImpl implements DietStateLoaded {
TResult Function()? $default, {
TResult Function()? initial,
TResult Function()? loading,
TResult Function(Diet diet)? loaded,
TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
if (loaded != null) {
return loaded(diet);
return loaded(diet, isStarted, hasStartedDiet);
}
return orElse();
}
@@ -624,9 +652,14 @@ class _$DietStateLoadedImpl implements DietStateLoaded {
}
abstract class DietStateLoaded implements AboutDietState {
const factory DietStateLoaded(final Diet diet) = _$DietStateLoadedImpl;
const factory DietStateLoaded(
{required final Diet diet,
final bool isStarted,
final bool hasStartedDiet}) = _$DietStateLoadedImpl;
Diet get diet;
bool get isStarted;
bool get hasStartedDiet;
/// Create a copy of AboutDietState
/// with the given fields replaced by the non-null parameter values.
@@ -707,7 +740,8 @@ class _$DietStateErrorImpl implements DietStateError {
TResult Function() $default, {
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(Diet diet) loaded,
required TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)
loaded,
required TResult Function(String message) error,
}) {
return error(message);
@@ -719,7 +753,7 @@ class _$DietStateErrorImpl implements DietStateError {
TResult? Function()? $default, {
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(Diet diet)? loaded,
TResult? Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult? Function(String message)? error,
}) {
return error?.call(message);
@@ -731,7 +765,7 @@ class _$DietStateErrorImpl implements DietStateError {
TResult Function()? $default, {
TResult Function()? initial,
TResult Function()? loading,
TResult Function(Diet diet)? loaded,
TResult Function(Diet diet, bool isStarted, bool hasStartedDiet)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {

View File

@@ -12,6 +12,7 @@ import 'package:eatvio_app/features/diets/presentation/cubit/about_diet_cubit.da
import 'package:eatvio_app/features/diets/presentation/cubit/about_diet_state.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart';
import 'package:eatvio_app/core/widgets/cpfc_container.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/cancel_diet_bottom_sheet.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart';
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
@@ -75,7 +76,7 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
Widget build(BuildContext context) {
final s = S.of(context);
return BlocProvider(
create: (context) => dietsCubit..getDietWithLoading(widget.diet.id),
create: (context) => dietsCubit..initDietPage(widget.diet.id),
child: BlocProvider<CommentsCubit>(
create: (BuildContext context) => commentsCubit
..getComments(
@@ -130,18 +131,37 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
backgroundColor: appTheme.greenA700,
backgroundColor: dietState.isStarted ? appTheme.transparentRed : appTheme.greenA700,
onPressed: () {
final cubit = context.read<AboutDietCubit>();
showModalBottomSheet(
context: context,
builder: (context) => StartDietBottomSheet(
aboutDietCubit: cubit,
),
);
if (dietState.isStarted) {
showModalBottomSheet(
context: context,
builder: (context) => CancelDietBottomSheet(
aboutDietCubit: cubit,
dietName: dietState.diet.name,
),
);
} else if (dietState.hasStartedDiet) {
showModalBottomSheet(
context: context,
builder: (context) => CancelDietBottomSheet(
aboutDietCubit: cubit,
title: 'Вы собираетесь начать диету ${dietState.diet.name}, завершить диету ${cubit.userStartedDietName}?',
),
);
} else {
showModalBottomSheet(
context: context,
builder: (context) => StartDietBottomSheet(
aboutDietCubit: cubit,
),
);
}
},
label: Text(
s.startDiet,
dietState.isStarted ? 'Отменить диету' : s.startDiet,
style: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
),
),

View File

@@ -5,8 +5,8 @@ import 'package:intl/intl.dart';
import 'package:table_calendar/table_calendar.dart';
class CalendarWidget extends StatefulWidget {
const CalendarWidget({super.key});
const CalendarWidget({super.key, required this.startDate});
final List<DateTime> startDate;
@override
State<CalendarWidget> createState() => _CalendarWidgetState();
}
@@ -56,6 +56,9 @@ class _CalendarWidgetState extends State<CalendarWidget> {
_selectedDay = selectedDay;
_focusedDay = focusedDay;
});
if (widget.startDate.isNotEmpty) {
widget.startDate[0] = selectedDay;
}
}
},
onFormatChanged: (format) {
@@ -69,6 +72,7 @@ class _CalendarWidgetState extends State<CalendarWidget> {
_focusedDay = focusedDay;
},
calendarStyle: CalendarStyle(
outsideDaysVisible: true,
weekendTextStyle: TextStyle(
color: appTheme.gray,
),
@@ -90,6 +94,17 @@ class _CalendarWidgetState extends State<CalendarWidget> {
color: appTheme.blueA400,
shape: BoxShape.circle,
),
outsideTextStyle: TextStyle(
color: appTheme.gray,
),
outsideDecoration: BoxDecoration(
color: appTheme.dividerColor,
shape: BoxShape.circle,
),
withinRangeDecoration: BoxDecoration(
color: appTheme.blueA400,
shape: BoxShape.circle,
),
todayTextStyle: TextStyle(
color: appTheme.gray,
),

View File

@@ -0,0 +1,79 @@
import 'package:eatvio_app/core/utils/toasts.dart';
import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart';
import 'package:eatvio_app/features/diets/presentation/cubit/about_diet_cubit.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/start_diet_bottom_sheet.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:flutter/material.dart';
class CancelDietBottomSheet extends StatelessWidget {
const CancelDietBottomSheet({super.key, required this.aboutDietCubit, this.title, this.dietName});
final AboutDietCubit aboutDietCubit;
final String? title;
final String? dietName;
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 4),
BottomSheetHandlerBar(),
SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25.0),
child: Text(
title ?? 'Вы действительно хотите отменить прохождение диеты ${dietName ?? ''}?',
textAlign: TextAlign.center,
style: TextStyles.headingXLSemibold.copyWith(
color: appTheme.black,
),
),
),
Padding(
padding: EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 28),
child: CustomOutlinedButton(
onPressed: () async {
if (title != null) {
final result = await aboutDietCubit.cancelDiet();
if (result) {
Navigator.pop(context);
showSuccessToast('предыдущая диета отменена');
showModalBottomSheet(
context: context,
builder: (context) => StartDietBottomSheet(
aboutDietCubit: aboutDietCubit,
),
);
} else {
showErrorToast('Не удалось отменить диету');
Navigator.pop(context);
}
} else {
final result = await aboutDietCubit.cancelDiet();
if (result) {
showSuccessToast('Диета отменена');
Navigator.pop(context);
aboutDietCubit.clearStartedDietsState();
} else {
showErrorToast('Не удалось отменить диету');
Navigator.pop(context);
}
}
},
height: 60,
text: 'Подтвердить',
buttonTextStyle: TextStyles.textLSemibold.copyWith(
color: appTheme.whiteA700,
),
buttonStyle: ButtonStyle(
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
side: WidgetStatePropertyAll(BorderSide.none),
),
),
),
],
);
}
}

View File

@@ -1,3 +1,4 @@
import 'package:eatvio_app/core/utils/toasts.dart';
import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart';
import 'package:eatvio_app/core/widgets/custom_divider.dart';
import 'package:eatvio_app/features/diets/presentation/cubit/about_diet_cubit.dart';
@@ -8,9 +9,16 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:flutter/material.dart';
class StartDietBottomSheet extends StatelessWidget {
class StartDietBottomSheet extends StatefulWidget {
const StartDietBottomSheet({super.key, required this.aboutDietCubit});
final AboutDietCubit aboutDietCubit;
@override
State<StartDietBottomSheet> createState() => _StartDietBottomSheetState();
}
class _StartDietBottomSheetState extends State<StartDietBottomSheet> {
final startDate = [DateTime.now()];
@override
Widget build(BuildContext context) {
final s = S.of(context);
@@ -42,11 +50,23 @@ class StartDietBottomSheet extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: CustomDivider(),
),
CalendarWidget(),
CalendarWidget(
startDate: startDate,
),
Padding(
padding: EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 28),
child: CustomOutlinedButton(
onPressed: () {},
onPressed: () async {
final result = await widget.aboutDietCubit.startDiet(startDate[0]);
if (result) {
showSuccessToast('Диета успешно начата');
Navigator.pop(context);
widget.aboutDietCubit.emitCurrentStartedDietsState();
} else {
showErrorToast('Не удалось начать диету');
Navigator.pop(context);
}
},
height: 60,
text: s.startDiet,
buttonTextStyle: TextStyles.textLSemibold.copyWith(

View File

@@ -49,6 +49,22 @@ class DietRepository {
}
}
Future<void> startDiet(int id, String date) async {
try {
return await dietsService.startDiet(id: id, body: {'date': date});
} catch (e) {
rethrow;
}
}
Future<void> cancelDiet(int id) async{
try {
return await dietsService.cancelDiet(id: id);
} catch (e) {
rethrow;
}
}
void emitLikesStateToFeed(LikesState state) => _dietLikeStateStreamController.add(state);
void emitFavoriteStateToFeed(FavoriteState state) => _dietFavoriteStateStreamController.add(state);
void emitCommentsStateToFeed(CommentsState state) => _dietCommentsStateStreamController.add(state);

View File

@@ -267,7 +267,7 @@ class PlansCubit extends Cubit<PlansState> {
Duration(milliseconds: 200),
() async {
try {
await planRepository.addToFavorite(slug);
await planRepository.removeFromFavorite(slug);
} catch (e) {
log(e.toString());
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');

View File

@@ -10,7 +10,7 @@ import 'package:eatvio_app/features/feed/presentation/bloc/profile_cubit/profile
import 'package:eatvio_app/features/feed/presentation/bloc/profile_cubit/profile_state.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/blog_posts.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/choice_chip_widget.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/chip_group.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/consultants.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/diets.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/plans.dart';
@@ -18,13 +18,13 @@ import 'package:eatvio_app/features/feed/presentation/widgets/tags_modal_widget.
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_cubit.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/recipes/presentation/pages/recipe_create_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import '../widgets/recipes.dart';
final bucket = PageStorageBucket();
class FeedScreen extends StatelessWidget {
const FeedScreen({super.key});
@@ -122,6 +122,7 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
recipesScrollController.dispose();
dietsScrollController.dispose();
plansScrollController.dispose();
consultantsScrollController.dispose();
super.dispose();
}
@@ -179,7 +180,18 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
floating: true,
snap: true,
scrolledUnderElevation: 0,
bottom: _chipGroup(),
bottom: PreferredSize(
preferredSize: Size(MediaQuery.of(context).size.width, 32),
child: ChipGroup(
selectedChipIndex: _selectedChipIndex,
chipsLabelList: chipsLabelList,
setSelectedChipIndex: (index) {
setState(() {
_selectedChipIndex = index;
});
},
),
),
title: Row(
children: [
CustomSearchBar(
@@ -245,7 +257,7 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
SizedBox(width: 12),
switch (_selectedChipIndex) {
1 => AddItemButton(
onTap: () => GoRouter.of(context).pushNamed('recipeCreateScreen'),
onTap: () => GoRouter.of(context).pushNamed(RecipeCreateScreen.name),
),
2 => AddItemButton(
onTap: state.profile?.role == 'consultant' ? () => GoRouter.of(context).pushNamed(CreateDietScreen.name) : null,
@@ -273,80 +285,4 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
),
);
}
_chipGroup() {
return PreferredSize(
preferredSize: Size(MediaQuery.of(context).size.width, 32),
child: Align(
alignment: Alignment.centerLeft,
child: SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 16),
scrollDirection: Axis.horizontal,
child: Row(
children: [
ChoiceChipWidget(
index: 0,
selectedChipIndex: _selectedChipIndex,
chipsLabelList: chipsLabelList,
onSelected: (bool selected) {
setState(() {
_selectedChipIndex = 0;
FocusScope.of(context).requestFocus(new FocusNode());
});
},
),
SizedBox(width: 8),
ChoiceChipWidget(
index: 1,
selectedChipIndex: _selectedChipIndex,
chipsLabelList: chipsLabelList,
onSelected: (bool selected) {
setState(() {
_selectedChipIndex = 1;
FocusScope.of(context).requestFocus(new FocusNode());
});
},
),
SizedBox(width: 8),
ChoiceChipWidget(
index: 2,
selectedChipIndex: _selectedChipIndex,
chipsLabelList: chipsLabelList,
onSelected: (bool selected) {
setState(() {
_selectedChipIndex = 2;
FocusScope.of(context).requestFocus(new FocusNode());
});
},
),
SizedBox(width: 8),
ChoiceChipWidget(
index: 3,
selectedChipIndex: _selectedChipIndex,
chipsLabelList: chipsLabelList,
onSelected: (bool selected) {
setState(() {
_selectedChipIndex = 3;
FocusScope.of(context).requestFocus(new FocusNode());
});
},
),
SizedBox(width: 8),
ChoiceChipWidget(
index: 4,
selectedChipIndex: _selectedChipIndex,
chipsLabelList: chipsLabelList,
onSelected: (bool selected) {
setState(() {
_selectedChipIndex = 4;
FocusScope.of(context).requestFocus(new FocusNode());
});
},
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,44 @@
import 'package:eatvio_app/features/feed/presentation/widgets/choice_chip_widget.dart';
import 'package:flutter/widgets.dart';
class ChipGroup extends StatelessWidget {
const ChipGroup({
super.key,
required this.selectedChipIndex,
required this.chipsLabelList,
required this.setSelectedChipIndex,
});
final int selectedChipIndex;
final List<String> chipsLabelList;
final void Function(int) setSelectedChipIndex;
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.centerLeft,
child: SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 16),
scrollDirection: Axis.horizontal,
child: Row(
children: [
...List.generate(
5,
(index) => Padding(
padding: EdgeInsets.only(right: 8.0),
child: ChoiceChipWidget(
index: index,
selectedChipIndex: selectedChipIndex,
chipsLabelList: chipsLabelList,
onSelected: (bool selected) {
setSelectedChipIndex(index);
FocusScope.of(context).requestFocus(new FocusNode());
},
),
),
),
],
),
),
);
}
}

View File

@@ -67,7 +67,7 @@ class MenuScreen extends StatelessWidget {
),
_divider(context),
_tile(
leading: ImageConstant.icnSupport,
leading: ImageConstant.icnDevTools,
title: 'Для разработчиков',
onTap: () {
GoRouter.of(context).pushNamed(DevToolsScreen.name);

View File

@@ -1,6 +1,7 @@
import 'dart:io';
import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart';
import 'package:eatvio_app/features/profile/models/chosen_diets_pagination_model.dart';
import 'package:eatvio_app/features/profile/models/circumference_model.dart';
import 'package:eatvio_app/features/profile/models/profile_model.dart';
import 'package:eatvio_app/features/profile/models/sub_model.dart';
@@ -171,4 +172,12 @@ class ProfileRepository {
rethrow;
}
}
Future<List<ChosenDietModel>> getChosenDiets() async {
try {
return (await profileService.getChosenDiets()).diets;
} catch (e) {
rethrow;
}
}
}

View File

@@ -1,5 +1,8 @@
import 'dart:io';
import 'package:eatvio_app/features/recipes/models/ingredient.dart';
import 'package:eatvio_app/features/recipes/models/recipe_model.dart';
import 'package:eatvio_app/features/recipes/models/step.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'recipe_create_entity.freezed.dart';
@@ -11,11 +14,71 @@ enum TypeOfRecipe {
snacks,
sweets,
drinks,
other,
other;
String toString() {
switch (this) {
case TypeOfRecipe.first_meals:
return 'Первые блюда';
case TypeOfRecipe.second_meals:
return 'Вторые блюда';
case TypeOfRecipe.salads:
return 'Салаты';
case TypeOfRecipe.snacks:
return 'Закуски';
case TypeOfRecipe.sweets:
return 'Сладости';
case TypeOfRecipe.drinks:
return 'Напитки';
case TypeOfRecipe.other:
return 'Другое';
}
}
String toApiType() {
switch (this) {
case TypeOfRecipe.first_meals:
return 'first_meals';
case TypeOfRecipe.second_meals:
return 'second_meals';
case TypeOfRecipe.salads:
return 'salads';
case TypeOfRecipe.snacks:
return 'snacks';
case TypeOfRecipe.sweets:
return 'sweets';
case TypeOfRecipe.drinks:
return 'drinks';
case TypeOfRecipe.other:
return 'other';
}
}
static TypeOfRecipe? fromString(String value) {
switch (value) {
case 'Первые блюда':
return TypeOfRecipe.first_meals;
case 'Вторые блюда':
return TypeOfRecipe.second_meals;
case 'Салаты':
return TypeOfRecipe.salads;
case 'Закуски':
return TypeOfRecipe.snacks;
case 'Сладости':
return TypeOfRecipe.sweets;
case 'Напитки':
return TypeOfRecipe.drinks;
case 'Другое':
return TypeOfRecipe.other;
default:
return null;
}
}
}
@freezed
class RecipeCreateEntity with _$RecipeCreateEntity {
const RecipeCreateEntity._();
const factory RecipeCreateEntity({
String? title,
TypeOfRecipe? typeOfRecipe,
@@ -26,19 +89,25 @@ class RecipeCreateEntity with _$RecipeCreateEntity {
String? description,
File? image,
String? videoUrl,
required List<IngredientEntity> ingredients,
required List<Ingredient> ingredients,
required List<StepEntity> steps,
required List<String> tags, // tags for search
}) = _RecipeCreateEntity;
}
@freezed
class IngredientEntity with _$IngredientEntity {
const factory IngredientEntity({
required String name,
required String unit,
required String portion,
}) = _IngredientEntity;
RecipeModel toModel() => RecipeModel(
title: title,
type: typeOfRecipe?.toApiType(),
servings: countOfPortions,
energyPerServing: energyInOnePortion?.toInt(),
totalCookingTime: totalCookTime?.split(':').fold(0, (value, element) => (int.tryParse(element) ?? 0)),
activeCookTime: activeCookTime?.split(':').fold(0, (value, element) => (int.tryParse(element) ?? 0)),
description: description,
image: image?.path,
video_url: videoUrl,
ingredients: ingredients,
steps: steps.map((step) => Step(image: step.image?.path ?? '', description: step.description ?? '')).toList(),
tags: tags,
);
}
@freezed

View File

@@ -25,7 +25,7 @@ mixin _$RecipeCreateEntity {
String? get description => throw _privateConstructorUsedError;
File? get image => throw _privateConstructorUsedError;
String? get videoUrl => throw _privateConstructorUsedError;
List<IngredientEntity> get ingredients => throw _privateConstructorUsedError;
List<Ingredient> get ingredients => throw _privateConstructorUsedError;
List<StepEntity> get steps => throw _privateConstructorUsedError;
List<String> get tags => throw _privateConstructorUsedError;
@@ -52,7 +52,7 @@ abstract class $RecipeCreateEntityCopyWith<$Res> {
String? description,
File? image,
String? videoUrl,
List<IngredientEntity> ingredients,
List<Ingredient> ingredients,
List<StepEntity> steps,
List<String> tags});
}
@@ -125,7 +125,7 @@ class _$RecipeCreateEntityCopyWithImpl<$Res, $Val extends RecipeCreateEntity>
ingredients: null == ingredients
? _value.ingredients
: ingredients // ignore: cast_nullable_to_non_nullable
as List<IngredientEntity>,
as List<Ingredient>,
steps: null == steps
? _value.steps
: steps // ignore: cast_nullable_to_non_nullable
@@ -156,7 +156,7 @@ abstract class _$$RecipeCreateEntityImplCopyWith<$Res>
String? description,
File? image,
String? videoUrl,
List<IngredientEntity> ingredients,
List<Ingredient> ingredients,
List<StepEntity> steps,
List<String> tags});
}
@@ -227,7 +227,7 @@ class __$$RecipeCreateEntityImplCopyWithImpl<$Res>
ingredients: null == ingredients
? _value._ingredients
: ingredients // ignore: cast_nullable_to_non_nullable
as List<IngredientEntity>,
as List<Ingredient>,
steps: null == steps
? _value._steps
: steps // ignore: cast_nullable_to_non_nullable
@@ -242,7 +242,7 @@ class __$$RecipeCreateEntityImplCopyWithImpl<$Res>
/// @nodoc
class _$RecipeCreateEntityImpl implements _RecipeCreateEntity {
class _$RecipeCreateEntityImpl extends _RecipeCreateEntity {
const _$RecipeCreateEntityImpl(
{this.title,
this.typeOfRecipe,
@@ -253,12 +253,13 @@ class _$RecipeCreateEntityImpl implements _RecipeCreateEntity {
this.description,
this.image,
this.videoUrl,
required final List<IngredientEntity> ingredients,
required final List<Ingredient> ingredients,
required final List<StepEntity> steps,
required final List<String> tags})
: _ingredients = ingredients,
_steps = steps,
_tags = tags;
_tags = tags,
super._();
@override
final String? title;
@@ -280,9 +281,9 @@ class _$RecipeCreateEntityImpl implements _RecipeCreateEntity {
final File? image;
@override
final String? videoUrl;
final List<IngredientEntity> _ingredients;
final List<Ingredient> _ingredients;
@override
List<IngredientEntity> get ingredients {
List<Ingredient> get ingredients {
if (_ingredients is EqualUnmodifiableListView) return _ingredients;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_ingredients);
@@ -362,7 +363,7 @@ class _$RecipeCreateEntityImpl implements _RecipeCreateEntity {
this, _$identity);
}
abstract class _RecipeCreateEntity implements RecipeCreateEntity {
abstract class _RecipeCreateEntity extends RecipeCreateEntity {
const factory _RecipeCreateEntity(
{final String? title,
final TypeOfRecipe? typeOfRecipe,
@@ -373,9 +374,10 @@ abstract class _RecipeCreateEntity implements RecipeCreateEntity {
final String? description,
final File? image,
final String? videoUrl,
required final List<IngredientEntity> ingredients,
required final List<Ingredient> ingredients,
required final List<StepEntity> steps,
required final List<String> tags}) = _$RecipeCreateEntityImpl;
const _RecipeCreateEntity._() : super._();
@override
String? get title;
@@ -396,7 +398,7 @@ abstract class _RecipeCreateEntity implements RecipeCreateEntity {
@override
String? get videoUrl;
@override
List<IngredientEntity> get ingredients;
List<Ingredient> get ingredients;
@override
List<StepEntity> get steps;
@override
@@ -410,171 +412,6 @@ abstract class _RecipeCreateEntity implements RecipeCreateEntity {
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$IngredientEntity {
String get name => throw _privateConstructorUsedError;
String get unit => throw _privateConstructorUsedError;
String get portion => throw _privateConstructorUsedError;
/// Create a copy of IngredientEntity
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$IngredientEntityCopyWith<IngredientEntity> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $IngredientEntityCopyWith<$Res> {
factory $IngredientEntityCopyWith(
IngredientEntity value, $Res Function(IngredientEntity) then) =
_$IngredientEntityCopyWithImpl<$Res, IngredientEntity>;
@useResult
$Res call({String name, String unit, String portion});
}
/// @nodoc
class _$IngredientEntityCopyWithImpl<$Res, $Val extends IngredientEntity>
implements $IngredientEntityCopyWith<$Res> {
_$IngredientEntityCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of IngredientEntity
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? name = null,
Object? unit = null,
Object? portion = null,
}) {
return _then(_value.copyWith(
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
unit: null == unit
? _value.unit
: unit // ignore: cast_nullable_to_non_nullable
as String,
portion: null == portion
? _value.portion
: portion // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$IngredientEntityImplCopyWith<$Res>
implements $IngredientEntityCopyWith<$Res> {
factory _$$IngredientEntityImplCopyWith(_$IngredientEntityImpl value,
$Res Function(_$IngredientEntityImpl) then) =
__$$IngredientEntityImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String name, String unit, String portion});
}
/// @nodoc
class __$$IngredientEntityImplCopyWithImpl<$Res>
extends _$IngredientEntityCopyWithImpl<$Res, _$IngredientEntityImpl>
implements _$$IngredientEntityImplCopyWith<$Res> {
__$$IngredientEntityImplCopyWithImpl(_$IngredientEntityImpl _value,
$Res Function(_$IngredientEntityImpl) _then)
: super(_value, _then);
/// Create a copy of IngredientEntity
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? name = null,
Object? unit = null,
Object? portion = null,
}) {
return _then(_$IngredientEntityImpl(
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
unit: null == unit
? _value.unit
: unit // ignore: cast_nullable_to_non_nullable
as String,
portion: null == portion
? _value.portion
: portion // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class _$IngredientEntityImpl implements _IngredientEntity {
const _$IngredientEntityImpl(
{required this.name, required this.unit, required this.portion});
@override
final String name;
@override
final String unit;
@override
final String portion;
@override
String toString() {
return 'IngredientEntity(name: $name, unit: $unit, portion: $portion)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$IngredientEntityImpl &&
(identical(other.name, name) || other.name == name) &&
(identical(other.unit, unit) || other.unit == unit) &&
(identical(other.portion, portion) || other.portion == portion));
}
@override
int get hashCode => Object.hash(runtimeType, name, unit, portion);
/// Create a copy of IngredientEntity
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$IngredientEntityImplCopyWith<_$IngredientEntityImpl> get copyWith =>
__$$IngredientEntityImplCopyWithImpl<_$IngredientEntityImpl>(
this, _$identity);
}
abstract class _IngredientEntity implements IngredientEntity {
const factory _IngredientEntity(
{required final String name,
required final String unit,
required final String portion}) = _$IngredientEntityImpl;
@override
String get name;
@override
String get unit;
@override
String get portion;
/// Create a copy of IngredientEntity
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$IngredientEntityImplCopyWith<_$IngredientEntityImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$StepEntity {
String? get description => throw _privateConstructorUsedError;

View File

@@ -13,4 +13,4 @@ class Ingredient with _$Ingredient {
}) = _Ingredient;
factory Ingredient.fromJson(Map<String, Object?> json) => _$IngredientFromJson(json);
}
}

View File

@@ -11,13 +11,17 @@ class RecipeModel with _$RecipeModel {
const factory RecipeModel({
List<Ingredient?>? ingredients,
List<Step?>? steps,
List<String>? tags,
String? type,
String? state,
String? title,
String? description,
String? video_url,
String? image,
int? cooking_time,
int? totalCookingTime,
int? activeCookTime,
int? servings,
int? energyPerServing,
}) = _RecipeModel;
factory RecipeModel.fromJson(Map<String, Object?> json) => _$RecipeModelFromJson(json);

View File

@@ -22,13 +22,17 @@ RecipeModel _$RecipeModelFromJson(Map<String, dynamic> json) {
mixin _$RecipeModel {
List<Ingredient?>? get ingredients => throw _privateConstructorUsedError;
List<Step?>? get steps => throw _privateConstructorUsedError;
List<String>? get tags => throw _privateConstructorUsedError;
String? get type => throw _privateConstructorUsedError;
String? get state => throw _privateConstructorUsedError;
String? get title => throw _privateConstructorUsedError;
String? get description => throw _privateConstructorUsedError;
String? get video_url => throw _privateConstructorUsedError;
String? get image => throw _privateConstructorUsedError;
int? get cooking_time => throw _privateConstructorUsedError;
int? get totalCookingTime => throw _privateConstructorUsedError;
int? get activeCookTime => throw _privateConstructorUsedError;
int? get servings => throw _privateConstructorUsedError;
int? get energyPerServing => throw _privateConstructorUsedError;
/// Serializes this RecipeModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@@ -49,13 +53,17 @@ abstract class $RecipeModelCopyWith<$Res> {
$Res call(
{List<Ingredient?>? ingredients,
List<Step?>? steps,
List<String>? tags,
String? type,
String? state,
String? title,
String? description,
String? video_url,
String? image,
int? cooking_time});
int? totalCookingTime,
int? activeCookTime,
int? servings,
int? energyPerServing});
}
/// @nodoc
@@ -75,13 +83,17 @@ class _$RecipeModelCopyWithImpl<$Res, $Val extends RecipeModel>
$Res call({
Object? ingredients = freezed,
Object? steps = freezed,
Object? tags = freezed,
Object? type = freezed,
Object? state = freezed,
Object? title = freezed,
Object? description = freezed,
Object? video_url = freezed,
Object? image = freezed,
Object? cooking_time = freezed,
Object? totalCookingTime = freezed,
Object? activeCookTime = freezed,
Object? servings = freezed,
Object? energyPerServing = freezed,
}) {
return _then(_value.copyWith(
ingredients: freezed == ingredients
@@ -92,6 +104,10 @@ class _$RecipeModelCopyWithImpl<$Res, $Val extends RecipeModel>
? _value.steps
: steps // ignore: cast_nullable_to_non_nullable
as List<Step?>?,
tags: freezed == tags
? _value.tags
: tags // ignore: cast_nullable_to_non_nullable
as List<String>?,
type: freezed == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
@@ -116,9 +132,21 @@ class _$RecipeModelCopyWithImpl<$Res, $Val extends RecipeModel>
? _value.image
: image // ignore: cast_nullable_to_non_nullable
as String?,
cooking_time: freezed == cooking_time
? _value.cooking_time
: cooking_time // ignore: cast_nullable_to_non_nullable
totalCookingTime: freezed == totalCookingTime
? _value.totalCookingTime
: totalCookingTime // ignore: cast_nullable_to_non_nullable
as int?,
activeCookTime: freezed == activeCookTime
? _value.activeCookTime
: activeCookTime // ignore: cast_nullable_to_non_nullable
as int?,
servings: freezed == servings
? _value.servings
: servings // ignore: cast_nullable_to_non_nullable
as int?,
energyPerServing: freezed == energyPerServing
? _value.energyPerServing
: energyPerServing // ignore: cast_nullable_to_non_nullable
as int?,
) as $Val);
}
@@ -135,13 +163,17 @@ abstract class _$$RecipeModelImplCopyWith<$Res>
$Res call(
{List<Ingredient?>? ingredients,
List<Step?>? steps,
List<String>? tags,
String? type,
String? state,
String? title,
String? description,
String? video_url,
String? image,
int? cooking_time});
int? totalCookingTime,
int? activeCookTime,
int? servings,
int? energyPerServing});
}
/// @nodoc
@@ -159,13 +191,17 @@ class __$$RecipeModelImplCopyWithImpl<$Res>
$Res call({
Object? ingredients = freezed,
Object? steps = freezed,
Object? tags = freezed,
Object? type = freezed,
Object? state = freezed,
Object? title = freezed,
Object? description = freezed,
Object? video_url = freezed,
Object? image = freezed,
Object? cooking_time = freezed,
Object? totalCookingTime = freezed,
Object? activeCookTime = freezed,
Object? servings = freezed,
Object? energyPerServing = freezed,
}) {
return _then(_$RecipeModelImpl(
ingredients: freezed == ingredients
@@ -176,6 +212,10 @@ class __$$RecipeModelImplCopyWithImpl<$Res>
? _value._steps
: steps // ignore: cast_nullable_to_non_nullable
as List<Step?>?,
tags: freezed == tags
? _value._tags
: tags // ignore: cast_nullable_to_non_nullable
as List<String>?,
type: freezed == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
@@ -200,9 +240,21 @@ class __$$RecipeModelImplCopyWithImpl<$Res>
? _value.image
: image // ignore: cast_nullable_to_non_nullable
as String?,
cooking_time: freezed == cooking_time
? _value.cooking_time
: cooking_time // ignore: cast_nullable_to_non_nullable
totalCookingTime: freezed == totalCookingTime
? _value.totalCookingTime
: totalCookingTime // ignore: cast_nullable_to_non_nullable
as int?,
activeCookTime: freezed == activeCookTime
? _value.activeCookTime
: activeCookTime // ignore: cast_nullable_to_non_nullable
as int?,
servings: freezed == servings
? _value.servings
: servings // ignore: cast_nullable_to_non_nullable
as int?,
energyPerServing: freezed == energyPerServing
? _value.energyPerServing
: energyPerServing // ignore: cast_nullable_to_non_nullable
as int?,
));
}
@@ -214,15 +266,20 @@ class _$RecipeModelImpl implements _RecipeModel {
const _$RecipeModelImpl(
{final List<Ingredient?>? ingredients,
final List<Step?>? steps,
final List<String>? tags,
this.type,
this.state,
this.title,
this.description,
this.video_url,
this.image,
this.cooking_time})
this.totalCookingTime,
this.activeCookTime,
this.servings,
this.energyPerServing})
: _ingredients = ingredients,
_steps = steps;
_steps = steps,
_tags = tags;
factory _$RecipeModelImpl.fromJson(Map<String, dynamic> json) =>
_$$RecipeModelImplFromJson(json);
@@ -247,6 +304,16 @@ class _$RecipeModelImpl implements _RecipeModel {
return EqualUnmodifiableListView(value);
}
final List<String>? _tags;
@override
List<String>? get tags {
final value = _tags;
if (value == null) return null;
if (_tags is EqualUnmodifiableListView) return _tags;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
@override
final String? type;
@override
@@ -260,11 +327,17 @@ class _$RecipeModelImpl implements _RecipeModel {
@override
final String? image;
@override
final int? cooking_time;
final int? totalCookingTime;
@override
final int? activeCookTime;
@override
final int? servings;
@override
final int? energyPerServing;
@override
String toString() {
return 'RecipeModel(ingredients: $ingredients, steps: $steps, type: $type, state: $state, title: $title, description: $description, video_url: $video_url, image: $image, cooking_time: $cooking_time)';
return 'RecipeModel(ingredients: $ingredients, steps: $steps, tags: $tags, type: $type, state: $state, title: $title, description: $description, video_url: $video_url, image: $image, totalCookingTime: $totalCookingTime, activeCookTime: $activeCookTime, servings: $servings, energyPerServing: $energyPerServing)';
}
@override
@@ -275,6 +348,7 @@ class _$RecipeModelImpl implements _RecipeModel {
const DeepCollectionEquality()
.equals(other._ingredients, _ingredients) &&
const DeepCollectionEquality().equals(other._steps, _steps) &&
const DeepCollectionEquality().equals(other._tags, _tags) &&
(identical(other.type, type) || other.type == type) &&
(identical(other.state, state) || other.state == state) &&
(identical(other.title, title) || other.title == title) &&
@@ -283,8 +357,14 @@ class _$RecipeModelImpl implements _RecipeModel {
(identical(other.video_url, video_url) ||
other.video_url == video_url) &&
(identical(other.image, image) || other.image == image) &&
(identical(other.cooking_time, cooking_time) ||
other.cooking_time == cooking_time));
(identical(other.totalCookingTime, totalCookingTime) ||
other.totalCookingTime == totalCookingTime) &&
(identical(other.activeCookTime, activeCookTime) ||
other.activeCookTime == activeCookTime) &&
(identical(other.servings, servings) ||
other.servings == servings) &&
(identical(other.energyPerServing, energyPerServing) ||
other.energyPerServing == energyPerServing));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -293,13 +373,17 @@ class _$RecipeModelImpl implements _RecipeModel {
runtimeType,
const DeepCollectionEquality().hash(_ingredients),
const DeepCollectionEquality().hash(_steps),
const DeepCollectionEquality().hash(_tags),
type,
state,
title,
description,
video_url,
image,
cooking_time);
totalCookingTime,
activeCookTime,
servings,
energyPerServing);
/// Create a copy of RecipeModel
/// with the given fields replaced by the non-null parameter values.
@@ -321,13 +405,17 @@ abstract class _RecipeModel implements RecipeModel {
const factory _RecipeModel(
{final List<Ingredient?>? ingredients,
final List<Step?>? steps,
final List<String>? tags,
final String? type,
final String? state,
final String? title,
final String? description,
final String? video_url,
final String? image,
final int? cooking_time}) = _$RecipeModelImpl;
final int? totalCookingTime,
final int? activeCookTime,
final int? servings,
final int? energyPerServing}) = _$RecipeModelImpl;
factory _RecipeModel.fromJson(Map<String, dynamic> json) =
_$RecipeModelImpl.fromJson;
@@ -337,6 +425,8 @@ abstract class _RecipeModel implements RecipeModel {
@override
List<Step?>? get steps;
@override
List<String>? get tags;
@override
String? get type;
@override
String? get state;
@@ -349,7 +439,13 @@ abstract class _RecipeModel implements RecipeModel {
@override
String? get image;
@override
int? get cooking_time;
int? get totalCookingTime;
@override
int? get activeCookTime;
@override
int? get servings;
@override
int? get energyPerServing;
/// Create a copy of RecipeModel
/// with the given fields replaced by the non-null parameter values.

View File

@@ -16,24 +16,32 @@ _$RecipeModelImpl _$$RecipeModelImplFromJson(Map<String, dynamic> json) =>
?.map((e) =>
e == null ? null : Step.fromJson(e as Map<String, dynamic>))
.toList(),
tags: (json['tags'] as List<dynamic>?)?.map((e) => e as String).toList(),
type: json['type'] as String?,
state: json['state'] as String?,
title: json['title'] as String?,
description: json['description'] as String?,
video_url: json['video_url'] as String?,
image: json['image'] as String?,
cooking_time: (json['cooking_time'] as num?)?.toInt(),
totalCookingTime: (json['totalCookingTime'] as num?)?.toInt(),
activeCookTime: (json['activeCookTime'] as num?)?.toInt(),
servings: (json['servings'] as num?)?.toInt(),
energyPerServing: (json['energyPerServing'] as num?)?.toInt(),
);
Map<String, dynamic> _$$RecipeModelImplToJson(_$RecipeModelImpl instance) =>
<String, dynamic>{
'ingredients': instance.ingredients,
'steps': instance.steps,
'tags': instance.tags,
'type': instance.type,
'state': instance.state,
'title': instance.title,
'description': instance.description,
'video_url': instance.video_url,
'image': instance.image,
'cooking_time': instance.cooking_time,
'totalCookingTime': instance.totalCookingTime,
'activeCookTime': instance.activeCookTime,
'servings': instance.servings,
'energyPerServing': instance.energyPerServing,
};

View File

@@ -1,14 +1,18 @@
import 'dart:io';
import 'package:eatvio_app/features/recipes/entities/recipe_create_entity.dart';
import 'package:eatvio_app/features/recipes/models/ingredient.dart';
import 'package:eatvio_app/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_state.dart';
import 'package:eatvio_app/features/recipes/reporitories/recipe_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class CreateRecipeCubit extends Cubit<CreateRecipeState> {
CreateRecipeCubit()
final RecipeRepository recipeRepository;
CreateRecipeCubit({required this.recipeRepository})
: super(
CreateRecipeState(
RecipeCreateEntity(
typeOfRecipe: TypeOfRecipe.first_meals,
ingredients: [],
steps: [],
tags: [],
@@ -38,7 +42,7 @@ class CreateRecipeCubit extends Cubit<CreateRecipeState> {
);
}
addIngredient(IngredientEntity entity) {
addIngredient(Ingredient entity) {
emit(
CreateRecipeState(
state.entity.copyWith(
@@ -52,7 +56,7 @@ class CreateRecipeCubit extends Cubit<CreateRecipeState> {
}
removeIngredient(int index) {
List<IngredientEntity> ingredients = [...state.entity.ingredients];
List<Ingredient> ingredients = [...state.entity.ingredients];
ingredients.removeAt(index);
emit(
@@ -65,8 +69,8 @@ class CreateRecipeCubit extends Cubit<CreateRecipeState> {
}
replaceIngredientPosition(int oldIndex, int newIndex) {
List<IngredientEntity> ingredients = [...state.entity.ingredients];
final IngredientEntity item = ingredients.removeAt(oldIndex);
List<Ingredient> ingredients = [...state.entity.ingredients];
final item = ingredients.removeAt(oldIndex);
ingredients.insert(newIndex, item);
emit(
CreateRecipeState(
@@ -216,4 +220,12 @@ class CreateRecipeCubit extends Cubit<CreateRecipeState> {
),
);
}
Future<void> publishRecipe() async {
try {
await recipeRepository.publishRecipe(state.entity.toModel());
} catch (e) {
rethrow;
}
}
}

View File

@@ -10,6 +10,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
class RecipeCreateScreen extends StatefulWidget {
const RecipeCreateScreen({super.key});
static const name = 'recipeCreateScreen';
static const path = '/recipe-create-screen';
@override
State<RecipeCreateScreen> createState() => _RecipeCreateScreenState();
}
@@ -39,28 +42,34 @@ class _RecipeCreateScreenState extends State<RecipeCreateScreen> {
InfoAboutRecipeWidget(),
IngredientsWidget(),
StepsWidget(),
InkWell(
onTap: () {},
borderRadius: BorderRadius.circular(20),
child: Ink(
width: MediaQuery.of(context).size.width - 32,
decoration: BoxDecoration(
color: appTheme.blueA400,
Builder(
builder: (context) {
return InkWell(
onTap: () {
context.read<CreateRecipeCubit>().publishRecipe();
},
borderRadius: BorderRadius.circular(20),
),
child: Container(
padding: const EdgeInsets.all(16),
alignment: Alignment.center,
child: Text(
'Опубликовать',
style: TextStyle(
fontSize: 16,
color: Colors.white,
fontWeight: FontWeight.w600,
child: Ink(
width: MediaQuery.of(context).size.width - 32,
decoration: BoxDecoration(
color: appTheme.blueA400,
borderRadius: BorderRadius.circular(20),
),
child: Container(
padding: const EdgeInsets.all(16),
alignment: Alignment.center,
child: Text(
'Опубликовать',
style: TextStyle(
fontSize: 16,
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
),
),
),
),
);
},
),
],
),

View File

@@ -18,14 +18,7 @@ class InfoAboutRecipeWidget extends StatefulWidget {
}
class _InfoAboutRecipeWidgetState extends State<InfoAboutRecipeWidget> {
final List<SelectionPopupModel> items = const [
SelectionPopupModel(title: 'Первые Блюда'),
SelectionPopupModel(title: 'Вторые Блюда'),
SelectionPopupModel(title: 'Салаты'),
SelectionPopupModel(title: 'Сладости'),
SelectionPopupModel(title: 'Напитки'),
SelectionPopupModel(title: 'Прочее'),
];
final List<SelectionPopupModel> items = TypeOfRecipe.values.map((e) => SelectionPopupModel(title: e.toString())).toList();
late SelectionPopupModel selectedType;
late final CreateRecipeCubit createRecipeCubit;
@@ -64,14 +57,7 @@ class _InfoAboutRecipeWidgetState extends State<InfoAboutRecipeWidget> {
selectedType = newSelectedType;
});
createRecipeCubit.changeType(
switch (selectedType.title) {
'Первые Блюда' => TypeOfRecipe.first_meals,
'Вторые Блюда' => TypeOfRecipe.second_meals,
'Салаты' => TypeOfRecipe.salads,
'Сладости' => TypeOfRecipe.sweets,
'Напитки' => TypeOfRecipe.drinks,
_ => TypeOfRecipe.other,
},
TypeOfRecipe.fromString(selectedType.title) ?? TypeOfRecipe.other,
);
},
),

View File

@@ -1,6 +1,6 @@
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/features/recipes/entities/recipe_create_entity.dart';
import 'package:eatvio_app/features/recipes/models/ingredient.dart';
import 'package:eatvio_app/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart';
import 'package:eatvio_app/features/recipes/presentation/bloc/create_recipe_cubit/create_recipe_state.dart';
import 'package:flutter/material.dart';
@@ -161,7 +161,7 @@ class _IngredientsWidgetState extends State<IngredientsWidget> {
onTap: () {
if (nameController.text.isNotEmpty && (pcsController.text.isNotEmpty || gramsController.text.isNotEmpty)) {
createRecipeCubit.addIngredient(
IngredientEntity(
Ingredient(
name: nameController.text,
unit: gramsController.text.isEmpty ? pcsController.text : gramsController.text,
portion: gramsController.text.isEmpty ? 'pcs' : 'gr',

View File

@@ -19,11 +19,14 @@ class RecipeRepository {
}
}
Future<RecipeModel> publishRecipe(RecipeModel recipe) async {
Future<RecipeModel?> publishRecipe(RecipeModel recipe) async {
try {
final File file = File(recipe.image ?? '');
if (recipe.image == null || (recipe.image?.isEmpty ?? true)) {
return null;
}
final file = File(recipe.image ?? '');
List<Map<String, Object>> ingredientsMap = recipe.ingredients?.map((ingredient) {
final ingredientsMap = recipe.ingredients?.map((ingredient) {
return {
'name': ingredient!.name,
'unit': ingredient.unit,
@@ -32,25 +35,42 @@ class RecipeRepository {
}).toList() ??
[];
List<Map<String, Object>> stepsMap = recipe.steps?.map((step) {
final stepsMap = recipe.steps?.map((step) {
return {
'image': File(step?.image ?? ""),
'description': step?.description ?? "",
};
}).toList() ??
[];
return await recipeService.publishRecipe(
recipe.type,
recipe.state,
recipe.title,
recipe.description,
recipe.video_url,
ingredientsMap,
stepsMap,
recipe.cooking_time,
file,
);
// recipe.steps?.map((step) {
// return File(step?.image ?? "");
// }).toList() ??
// [];
if (recipe.type != null &&
recipe.description != null &&
recipe.totalCookingTime != null &&
recipe.activeCookTime != null &&
recipe.servings != null &&
recipe.energyPerServing != null &&
ingredientsMap.isNotEmpty) {
return await recipeService.publishRecipe(
type: recipe.type!,
state: 'active',
title: recipe.title,
tags: recipe.tags,
description: recipe.description!,
video_url: recipe.video_url,
ingredients: ingredientsMap,
steps: stepsMap,
cooking_time: recipe.totalCookingTime!,
active_time: recipe.activeCookTime!,
image: file,
servings: recipe.servings!,
energy_per_serving: recipe.energyPerServing!,
);
}
return null;
} catch (e) {
rethrow;
}

View File

@@ -19,18 +19,25 @@ abstract class RecipeService {
@GET('/api/diary/product_nutritional_values/filter')
Future<Map<String, List<Product>>> getInputProduct(@Query('name') String name);
@POST('/api/recipes/create')
Future<RecipeModel> publishRecipe(
@Part() String? type,
@Part() String? state,
@MultiPart()
Future<RecipeModel> publishRecipe({
@Part() required String type,
@Part() required String state,
@Part() String? title,
@Part() String? description,
@Part() required String description,
@Part() String? video_url,
@Part() List<Map<String, Object>> ingredients,
@Part() List<Map<String, Object>> steps,
@Part() List<Map<String, Object>>? ingredients,
@Part() List<Map<String, Object>>? steps,
@Part() List<String>? tags,
@Part() int? cooking_time,
@Part() File image,
);
@Part() int? active_time,
@Part() required int servings,
@Part() required int energy_per_serving,
@Part() File? image,
});
@POST('/api/recipes/update/{id}')
Future<RecipeModel> editRecipe(@Part() Map<String, Object?> recipe, String id);

View File

@@ -92,75 +92,95 @@ class _RecipeService implements RecipeService {
}
@override
Future<RecipeModel> publishRecipe(
String? type,
String? state,
Future<RecipeModel> publishRecipe({
required String type,
required String state,
String? title,
String? description,
required String description,
String? video_url,
List<Map<String, Object>> ingredients,
List<Map<String, Object>> steps,
List<Map<String, Object>>? ingredients,
List<Map<String, Object>>? steps,
List<String>? tags,
int? cooking_time,
File image,
) async {
int? active_time,
required int servings,
required int energy_per_serving,
File? image,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
queryParameters.removeWhere((k, v) => v == null);
final _headers = <String, dynamic>{};
final _data = FormData();
if (type != null) {
_data.fields.add(MapEntry(
'type',
type,
));
}
if (state != null) {
_data.fields.add(MapEntry(
'state',
state,
));
}
_data.fields.add(MapEntry(
'type',
type,
));
_data.fields.add(MapEntry(
'state',
state,
));
if (title != null) {
_data.fields.add(MapEntry(
'title',
title,
));
}
if (description != null) {
_data.fields.add(MapEntry(
'description',
description,
));
}
_data.fields.add(MapEntry(
'description',
description,
));
if (video_url != null) {
_data.fields.add(MapEntry(
'video_url',
video_url,
));
}
ingredients.forEach((i) {
ingredients?.forEach((i) {
_data.fields.add(MapEntry('ingredients', jsonEncode(i)));
});
steps.forEach((i) {
steps?.forEach((i) {
_data.fields.add(MapEntry('steps', jsonEncode(i)));
});
tags?.forEach((i) {
_data.fields.add(MapEntry('tags', i));
});
if (cooking_time != null) {
_data.fields.add(MapEntry(
'cooking_time',
cooking_time.toString(),
));
}
_data.files.add(MapEntry(
'image',
MultipartFile.fromFileSync(
image.path,
filename: image.path.split(Platform.pathSeparator).last,
),
if (active_time != null) {
_data.fields.add(MapEntry(
'active_time',
active_time.toString(),
));
}
_data.fields.add(MapEntry(
'servings',
servings.toString(),
));
_data.fields.add(MapEntry(
'energy_per_serving',
energy_per_serving.toString(),
));
if (image != null) {
if (image != null) {
_data.files.add(MapEntry(
'image',
MultipartFile.fromFileSync(
image.path,
filename: image.path.split(Platform.pathSeparator).last,
),
));
}
}
final _options = _setStreamType<RecipeModel>(Options(
method: 'POST',
headers: _headers,
extra: _extra,
contentType: 'multipart/form-data',
)
.compose(
_dio.options,

View File

@@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/token_storage.dart';
@@ -57,7 +59,7 @@ class RegistrationCubit extends Cubit<RegistrationState> {
await TokenStorage.saveTokenInStorage(token);
AppRouter.navigatorKey.currentContext?.go('/my-profile-screen');
} catch (e) {
rethrow;
log(e.toString());
}
}

View File

@@ -12,12 +12,12 @@ abstract class SettingsService {
@GET('/api/profile/settings')
Future<ProfileSettingsModel> getProfileSettings();
@POST('/api/profile/settings/update')
@PUT('/api/profile/settings/update')
Future<ProfileSettingsModel> updateProfileSettings({
@Body() required Map<String, dynamic> data,
});
@POST('/api/profile/update')
@PUT('/api/profile/update')
Future<ProfileDetailsModel> updateProfileDetails({
@Body() required Map<String, dynamic> data,
});

View File

@@ -63,7 +63,7 @@ class _SettingsService implements SettingsService {
final _data = <String, dynamic>{};
_data.addAll(data);
final _options = _setStreamType<ProfileSettingsModel>(Options(
method: 'POST',
method: 'PUT',
headers: _headers,
extra: _extra,
)
@@ -98,7 +98,7 @@ class _SettingsService implements SettingsService {
final _data = <String, dynamic>{};
_data.addAll(data);
final _options = _setStreamType<ProfileDetailsModel>(Options(
method: 'POST',
method: 'PUT',
headers: _headers,
extra: _extra,
)