forked from eatvio/eatvio_app
Merge branch 'main' of http://gitea.office.local:3000/eatvio/eatvio_app
This commit is contained in:
@@ -59,7 +59,6 @@ class AppRouter {
|
||||
path: '/auth-screen',
|
||||
name: 'authScreen',
|
||||
redirect: (context, state) async {
|
||||
// TokenStorage.removeTokenInStorage();
|
||||
if (await TokenStorage.haveTokenInStorage()) {
|
||||
return '/my-profile-screen';
|
||||
}
|
||||
@@ -292,7 +291,10 @@ class AppRouter {
|
||||
GoRoute(
|
||||
path: CreateDietScreen.path,
|
||||
name: CreateDietScreen.name,
|
||||
builder: (context, state) => CreateDietScreen(),
|
||||
builder: (context, state) {
|
||||
final diet = state.extra as Diet?;
|
||||
return CreateDietScreen(diet: diet);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: DietPreviewScreen.path,
|
||||
|
||||
@@ -4,42 +4,44 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
Future<void> showBottomSheetWithBlur({
|
||||
required BuildContext context,
|
||||
required Widget child,
|
||||
double height = 200,
|
||||
bool hasBlur = true,
|
||||
bool enableDrag = true,
|
||||
}) {
|
||||
return showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
barrierColor: appTheme.transparentBlack,
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
useSafeArea: true,
|
||||
enableDrag: enableDrag,
|
||||
builder: (context) => GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(
|
||||
sigmaX: hasBlur ? 24 : 0,
|
||||
sigmaY: hasBlur ? 24 : 0,
|
||||
tileMode: TileMode.mirror,
|
||||
),
|
||||
child: SizedBox(
|
||||
child: Container(
|
||||
height: height,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20),
|
||||
),
|
||||
color: appTheme.whiteA700,
|
||||
required BuildContext context,
|
||||
required Widget child,
|
||||
double height = 200,
|
||||
bool hasBlur = true,
|
||||
bool enableDrag = true,
|
||||
bool userRootNavigator = false,
|
||||
}) {
|
||||
return showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
barrierColor: appTheme.transparentBlack,
|
||||
backgroundColor: Colors.transparent,
|
||||
useRootNavigator: userRootNavigator,
|
||||
elevation: 0,
|
||||
useSafeArea: true,
|
||||
enableDrag: enableDrag,
|
||||
builder: (context) => GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(
|
||||
sigmaX: hasBlur ? 24 : 0,
|
||||
sigmaY: hasBlur ? 24 : 0,
|
||||
tileMode: TileMode.mirror,
|
||||
),
|
||||
child: SizedBox(
|
||||
child: Container(
|
||||
height: height,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20),
|
||||
),
|
||||
child: child,
|
||||
color: appTheme.whiteA700,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,12 +9,15 @@ class CreateDietModel with _$CreateDietModel {
|
||||
const CreateDietModel._();
|
||||
|
||||
const factory CreateDietModel({
|
||||
String? imageUrl,
|
||||
String? name,
|
||||
String? details,
|
||||
String? state,
|
||||
DietState? state,
|
||||
int? duration,
|
||||
required Map<String, bool> type,
|
||||
XFile? image,
|
||||
List<List<List<DailySchedule>>>? dailySchedule,
|
||||
}) = _CreateDietModel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,9 +16,10 @@ final _privateConstructorUsedError = UnsupportedError(
|
||||
|
||||
/// @nodoc
|
||||
mixin _$CreateDietModel {
|
||||
String? get imageUrl => throw _privateConstructorUsedError;
|
||||
String? get name => throw _privateConstructorUsedError;
|
||||
String? get details => throw _privateConstructorUsedError;
|
||||
String? get state => throw _privateConstructorUsedError;
|
||||
DietState? get state => throw _privateConstructorUsedError;
|
||||
int? get duration => throw _privateConstructorUsedError;
|
||||
Map<String, bool> get type => throw _privateConstructorUsedError;
|
||||
XFile? get image => throw _privateConstructorUsedError;
|
||||
@@ -39,9 +40,10 @@ abstract class $CreateDietModelCopyWith<$Res> {
|
||||
_$CreateDietModelCopyWithImpl<$Res, CreateDietModel>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{String? name,
|
||||
{String? imageUrl,
|
||||
String? name,
|
||||
String? details,
|
||||
String? state,
|
||||
DietState? state,
|
||||
int? duration,
|
||||
Map<String, bool> type,
|
||||
XFile? image,
|
||||
@@ -63,6 +65,7 @@ class _$CreateDietModelCopyWithImpl<$Res, $Val extends CreateDietModel>
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? imageUrl = freezed,
|
||||
Object? name = freezed,
|
||||
Object? details = freezed,
|
||||
Object? state = freezed,
|
||||
@@ -72,6 +75,10 @@ class _$CreateDietModelCopyWithImpl<$Res, $Val extends CreateDietModel>
|
||||
Object? dailySchedule = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
imageUrl: freezed == imageUrl
|
||||
? _value.imageUrl
|
||||
: imageUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
name: freezed == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
@@ -83,7 +90,7 @@ class _$CreateDietModelCopyWithImpl<$Res, $Val extends CreateDietModel>
|
||||
state: freezed == state
|
||||
? _value.state
|
||||
: state // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
as DietState?,
|
||||
duration: freezed == duration
|
||||
? _value.duration
|
||||
: duration // ignore: cast_nullable_to_non_nullable
|
||||
@@ -113,9 +120,10 @@ abstract class _$$CreateDietModelImplCopyWith<$Res>
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{String? name,
|
||||
{String? imageUrl,
|
||||
String? name,
|
||||
String? details,
|
||||
String? state,
|
||||
DietState? state,
|
||||
int? duration,
|
||||
Map<String, bool> type,
|
||||
XFile? image,
|
||||
@@ -135,6 +143,7 @@ class __$$CreateDietModelImplCopyWithImpl<$Res>
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? imageUrl = freezed,
|
||||
Object? name = freezed,
|
||||
Object? details = freezed,
|
||||
Object? state = freezed,
|
||||
@@ -144,6 +153,10 @@ class __$$CreateDietModelImplCopyWithImpl<$Res>
|
||||
Object? dailySchedule = freezed,
|
||||
}) {
|
||||
return _then(_$CreateDietModelImpl(
|
||||
imageUrl: freezed == imageUrl
|
||||
? _value.imageUrl
|
||||
: imageUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
name: freezed == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
@@ -155,7 +168,7 @@ class __$$CreateDietModelImplCopyWithImpl<$Res>
|
||||
state: freezed == state
|
||||
? _value.state
|
||||
: state // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
as DietState?,
|
||||
duration: freezed == duration
|
||||
? _value.duration
|
||||
: duration // ignore: cast_nullable_to_non_nullable
|
||||
@@ -180,7 +193,8 @@ class __$$CreateDietModelImplCopyWithImpl<$Res>
|
||||
|
||||
class _$CreateDietModelImpl extends _CreateDietModel {
|
||||
const _$CreateDietModelImpl(
|
||||
{this.name,
|
||||
{this.imageUrl,
|
||||
this.name,
|
||||
this.details,
|
||||
this.state,
|
||||
this.duration,
|
||||
@@ -191,12 +205,14 @@ class _$CreateDietModelImpl extends _CreateDietModel {
|
||||
_dailySchedule = dailySchedule,
|
||||
super._();
|
||||
|
||||
@override
|
||||
final String? imageUrl;
|
||||
@override
|
||||
final String? name;
|
||||
@override
|
||||
final String? details;
|
||||
@override
|
||||
final String? state;
|
||||
final DietState? state;
|
||||
@override
|
||||
final int? duration;
|
||||
final Map<String, bool> _type;
|
||||
@@ -221,7 +237,7 @@ class _$CreateDietModelImpl extends _CreateDietModel {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CreateDietModel(name: $name, details: $details, state: $state, duration: $duration, type: $type, image: $image, dailySchedule: $dailySchedule)';
|
||||
return 'CreateDietModel(imageUrl: $imageUrl, name: $name, details: $details, state: $state, duration: $duration, type: $type, image: $image, dailySchedule: $dailySchedule)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -229,6 +245,8 @@ class _$CreateDietModelImpl extends _CreateDietModel {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$CreateDietModelImpl &&
|
||||
(identical(other.imageUrl, imageUrl) ||
|
||||
other.imageUrl == imageUrl) &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
(identical(other.details, details) || other.details == details) &&
|
||||
(identical(other.state, state) || other.state == state) &&
|
||||
@@ -243,6 +261,7 @@ class _$CreateDietModelImpl extends _CreateDietModel {
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
imageUrl,
|
||||
name,
|
||||
details,
|
||||
state,
|
||||
@@ -263,9 +282,10 @@ class _$CreateDietModelImpl extends _CreateDietModel {
|
||||
|
||||
abstract class _CreateDietModel extends CreateDietModel {
|
||||
const factory _CreateDietModel(
|
||||
{final String? name,
|
||||
{final String? imageUrl,
|
||||
final String? name,
|
||||
final String? details,
|
||||
final String? state,
|
||||
final DietState? state,
|
||||
final int? duration,
|
||||
required final Map<String, bool> type,
|
||||
final XFile? image,
|
||||
@@ -273,12 +293,14 @@ abstract class _CreateDietModel extends CreateDietModel {
|
||||
_$CreateDietModelImpl;
|
||||
const _CreateDietModel._() : super._();
|
||||
|
||||
@override
|
||||
String? get imageUrl;
|
||||
@override
|
||||
String? get name;
|
||||
@override
|
||||
String? get details;
|
||||
@override
|
||||
String? get state;
|
||||
DietState? get state;
|
||||
@override
|
||||
int? get duration;
|
||||
@override
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart';
|
||||
import 'package:eatvio_app/core/widgets/bottom_sheet_with_blur.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
||||
import 'package:eatvio_app/core/widgets/loader.dart';
|
||||
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
|
||||
@@ -16,6 +17,7 @@ import 'package:eatvio_app/features/create_diet/presentation/widgets/diet_image_
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/status_drop_down_widget.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/diet_type_choice_widget.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -24,19 +26,37 @@ import 'package:go_router/go_router.dart';
|
||||
class CreateDietScreen extends StatelessWidget {
|
||||
static const name = 'createDietScreen';
|
||||
static const path = '/create-diet-screen';
|
||||
const CreateDietScreen({super.key});
|
||||
|
||||
const CreateDietScreen({super.key, this.diet});
|
||||
final Diet? diet;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => sl<CreateDietCubit>(),
|
||||
child: const CreateDietScreenContent(),
|
||||
create: (context) => sl<CreateDietCubit>()..setInitialData(diet),
|
||||
child: CreateDietScreenContent(diet: diet),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CreateDietScreenContent extends StatelessWidget {
|
||||
const CreateDietScreenContent({super.key});
|
||||
class CreateDietScreenContent extends StatefulWidget {
|
||||
CreateDietScreenContent({super.key, this.diet});
|
||||
final Diet? diet;
|
||||
|
||||
@override
|
||||
State<CreateDietScreenContent> createState() => _CreateDietScreenContentState();
|
||||
}
|
||||
|
||||
class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
|
||||
final nameController = TextEditingController();
|
||||
final descriptionController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.diet != null) {
|
||||
nameController.text = widget.diet?.name ?? '';
|
||||
descriptionController.text = widget.diet?.details ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -44,55 +64,12 @@ class CreateDietScreenContent extends StatelessWidget {
|
||||
return BlocBuilder<CreateDietCubit, CreateDietState>(
|
||||
builder: (context, state) {
|
||||
if (state.published) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
context.pop();
|
||||
showBottomSheetWithBlur(
|
||||
context: context,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
BottomSheetHandlerBar(),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
'${state.dietData.name ?? 'Диета'}',
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
Text(
|
||||
'опубликованна',
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: CustomOutlinedButton(
|
||||
onPressed: () {
|
||||
context.pop();
|
||||
},
|
||||
text: 'Хорошо',
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textSSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
Future.delayed(Duration(milliseconds: 500), () {
|
||||
context.pop('${state.dietData.name ?? 'Диета'}');
|
||||
});
|
||||
} else if (state.drafted) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
context.pop();
|
||||
Future.delayed(Duration(milliseconds: 500), () {
|
||||
context.pop('${state.dietData.name ?? 'Диета'}');
|
||||
showBottomSheetWithBlur(
|
||||
context: context,
|
||||
child: Column(
|
||||
@@ -116,7 +93,7 @@ class CreateDietScreenContent extends StatelessWidget {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: CustomOutlinedButton(
|
||||
onPressed: () {
|
||||
context.pop();
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
},
|
||||
text: 'Хорошо',
|
||||
height: 52,
|
||||
@@ -141,7 +118,7 @@ class CreateDietScreenContent extends StatelessWidget {
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
s.create_diet,
|
||||
widget.diet != null ? 'Редактирование диеты' : s.create_diet,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
@@ -160,6 +137,7 @@ class CreateDietScreenContent extends StatelessWidget {
|
||||
SizedBox(height: 16),
|
||||
CustomTextFormField(
|
||||
hintText: s.enter_diet_name,
|
||||
controller: nameController,
|
||||
hasError: state.dietNameError != null,
|
||||
onChanged: (value) {
|
||||
context.read<CreateDietCubit>().setName(value);
|
||||
@@ -176,6 +154,7 @@ class CreateDietScreenContent extends StatelessWidget {
|
||||
height: 132,
|
||||
child: CustomTextFormField(
|
||||
hasError: state.dietDescriptionError != null,
|
||||
controller: descriptionController,
|
||||
textAlignVertical: TextAlignVertical.top,
|
||||
maxLines: null,
|
||||
minLines: null,
|
||||
@@ -227,30 +206,19 @@ class CreateDietScreenContent extends StatelessWidget {
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
separatorBuilder: (context, index) => SizedBox(height: 8),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
SizedBox(height: 12),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
CustomOutlinedButton(
|
||||
onPressed: () async {
|
||||
await context.read<CreateDietCubit>().publishDiet(toDraft: true);
|
||||
},
|
||||
text: s.to_draft,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.dottedBorder),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
CustomOutlinedButton(
|
||||
onPressed: () async {
|
||||
final diet = await context.read<CreateDietCubit>().getDietForPreview();
|
||||
if (diet == null) return;
|
||||
GoRouter.of(context).pushNamed(DietPreviewScreen.name, extra: diet);
|
||||
},
|
||||
onPressed: state.isDietPublishing
|
||||
? () {}
|
||||
: () async {
|
||||
final diet = await context.read<CreateDietCubit>().getDietForPreview();
|
||||
if (diet == null) return;
|
||||
GoRouter.of(context).pushNamed(DietPreviewScreen.name, extra: diet);
|
||||
},
|
||||
text: s.preview,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray),
|
||||
@@ -260,18 +228,21 @@ class CreateDietScreenContent extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
CustomOutlinedButton(
|
||||
onPressed: () {
|
||||
context.read<CreateDietCubit>().publishDiet();
|
||||
},
|
||||
text: s.publish,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
if (state.isDietPublishing)
|
||||
Loader()
|
||||
else
|
||||
CustomOutlinedButton(
|
||||
onPressed: () {
|
||||
context.read<CreateDietCubit>().publishDiet();
|
||||
},
|
||||
text: widget.diet != null ? s.save : s.publish,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -282,5 +253,5 @@ class CreateDietScreenContent extends StatelessWidget {
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:developer';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
|
||||
@@ -16,10 +17,46 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
final CreateDietRepository createDietRepository;
|
||||
final ProfileRepository profileRepository;
|
||||
final GlossaryRepository glossaryRepository;
|
||||
static const _dietStateValues = ['active', 'personal', 'draft'];
|
||||
String? tempImagePath;
|
||||
static const _dietStateValues = DietState.values;
|
||||
|
||||
CreateDietCubit({required this.profileRepository, required this.createDietRepository, required this.glossaryRepository}) : super(CreateDietState.initial());
|
||||
|
||||
Future<void> setInitialData(Diet? diet) async {
|
||||
if (diet == null) return;
|
||||
emit(state.copyWith(isLoading: true, isDietBeingEdited: true));
|
||||
final countOfDays = diet.dailySchedule.length;
|
||||
final List<List<List<DailySchedule>>> dailySchedule = List.generate(countOfDays, (index) => [[], [], [], [], []]);
|
||||
|
||||
for (final dayEntry in diet.dailySchedule.entries) {
|
||||
for (final mealEntry in dayEntry.value.entries) {
|
||||
for (final product in mealEntry.value.values) {
|
||||
dailySchedule[dayEntry.key - 1][mealEntry.key - 1].add(product);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final dietStateIndex = _dietStateValues.indexOf(diet.state);
|
||||
final type = Map<String, bool>.fromIterable(dietTypeValues, value: (element) => false);
|
||||
type['meat'] = diet.type.meat ?? false;
|
||||
type['veg'] = diet.type.veg ?? false;
|
||||
type['carb'] = diet.type.carb ?? false;
|
||||
type['light'] = diet.type.light ?? false;
|
||||
emit(
|
||||
state.copyWith.dietData(
|
||||
dailySchedule: dailySchedule,
|
||||
details: diet.details,
|
||||
state: diet.state,
|
||||
name: diet.name,
|
||||
duration: diet.duration,
|
||||
type: type,
|
||||
imageUrl: baseUrl + '/storage/' + diet.image,
|
||||
),
|
||||
);
|
||||
emit(state.copyWith(dietStateIndex: dietStateIndex, durationIndex: durationValues.indexOf(diet.duration)));
|
||||
emit(state.copyWith(isLoading: true));
|
||||
}
|
||||
|
||||
void setDetails(String text) {
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(details: text), dietDescriptionError: null));
|
||||
}
|
||||
@@ -82,16 +119,16 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
return await glossaryRepository.getGlossaryListByQuery(query);
|
||||
}
|
||||
|
||||
void setImage(XFile? image) {
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(image: image), dietImageError: null));
|
||||
void setImage(XFile? image, {String? imageUrl}) {
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(image: image, imageUrl: imageUrl), dietImageError: null));
|
||||
}
|
||||
|
||||
void setDailySchedule(List<List<List<DailySchedule>>> dailySchedule) {
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(dailySchedule: dailySchedule), dietDurationError: null));
|
||||
}
|
||||
|
||||
void setDietState(int index) {
|
||||
emit(state.copyWith(dietStateIndex: index, dietData: state.dietData.copyWith(state: _dietStateValues[index]), dietStateError: null));
|
||||
void setDietState(DietState dietState, int index) {
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(state: dietState), dietStateIndex: index, dietStateError: null));
|
||||
}
|
||||
|
||||
void setDietType({required String key, required bool value}) {
|
||||
@@ -236,12 +273,26 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
);
|
||||
}
|
||||
}
|
||||
final dailySchedule = <int, Map<int, Map<int, DailySchedule>>>{};
|
||||
|
||||
for (final (dayIndex, day) in state.dietData.dailySchedule!.indexed) {
|
||||
final mappedDay = <int, Map<int, DailySchedule>>{};
|
||||
for (final (mealIndex, meal) in day.indexed) {
|
||||
final mappedMeal = <int, DailySchedule>{};
|
||||
for (final (productIndex, product) in meal.indexed) {
|
||||
mappedMeal[productIndex + 1] = product;
|
||||
}
|
||||
mappedDay[mealIndex + 1] = mappedMeal;
|
||||
}
|
||||
dailySchedule[dayIndex + 1] = mappedDay;
|
||||
}
|
||||
|
||||
final diet = Diet(
|
||||
id: 0,
|
||||
name: state.dietData.name ?? '',
|
||||
image: imageInBase64,
|
||||
duration: state.dietData.duration ?? 0,
|
||||
state: state.dietData.state ?? '',
|
||||
state: state.dietData.state ?? DietState.active,
|
||||
type: DietType(
|
||||
meat: state.dietData.type['meat'],
|
||||
veg: state.dietData.type['veg'],
|
||||
@@ -249,7 +300,7 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
light: state.dietData.type['light'],
|
||||
),
|
||||
details: state.dietData.details ?? '',
|
||||
dailySchedule: state.dietData.dailySchedule ?? [],
|
||||
dailySchedule: dailySchedule,
|
||||
author: Author(
|
||||
authorSlug: authorProfile.slug,
|
||||
authorName: authorProfile.name,
|
||||
@@ -267,32 +318,32 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
return diet;
|
||||
}
|
||||
|
||||
Future<String?> publishDiet({bool toDraft = false}) async {
|
||||
Future<String?> publishDiet() async {
|
||||
emit(state.copyWith(isDietPublishing: true));
|
||||
if (!_isFromValid()) return null;
|
||||
try {
|
||||
final image = await ImageCompressUtility.compressFile(File(state.dietData.image!.path));
|
||||
final dietState = toDraft ? _dietStateValues.last : state.dietData.state;
|
||||
final diet = await createDietRepository.createDiet(
|
||||
name: state.dietData.name,
|
||||
duration: state.dietData.duration,
|
||||
final dietState = state.dietData.state!;
|
||||
final dailySchedule = state.dietData.dailySchedule;
|
||||
await createDietRepository.createDiet(
|
||||
name: state.dietData.name ?? '',
|
||||
duration: state.dietData.duration ?? 7,
|
||||
state: dietState,
|
||||
image: image,
|
||||
meatType: state.dietData.type['meat'] == true ? '1' : '0',
|
||||
carbType: state.dietData.type['carb'] == true ? '1' : '0',
|
||||
vegType: state.dietData.type['veg'] == true ? '1' : '0',
|
||||
lightType: state.dietData.type['light'] == true ? '1' : '0',
|
||||
details: state.dietData.details,
|
||||
details: state.dietData.details ?? '',
|
||||
dailySchedule: dailySchedule ?? [],
|
||||
);
|
||||
final dailySchedule = state.dietData.dailySchedule;
|
||||
createDietRepository.createDietDailySchedule(
|
||||
id: diet.id,
|
||||
model: {'daily_schedule': dailySchedule},
|
||||
);
|
||||
emit(state.copyWith(published: !toDraft, drafted: toDraft));
|
||||
|
||||
emit(state.copyWith(published: dietState != DietState.draft, drafted: dietState == DietState.draft));
|
||||
return state.dietData.name;
|
||||
} catch (e, stackTrace) {
|
||||
} on DioException catch (e, stackTrace) {
|
||||
log(e.toString(), stackTrace: stackTrace);
|
||||
}
|
||||
emit(state.copyWith(isDietPublishing: false));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -304,7 +355,7 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
}
|
||||
if (state.dietData.name == null || state.dietData.name == '') {
|
||||
result = false;
|
||||
emit(state.copyWith(dietNameError: 'Поле не заполненно'));
|
||||
emit(state.copyWith(dietNameError: 'Поле не заполнено'));
|
||||
}
|
||||
if ((state.dietData.details?.length ?? 0) < 3) {
|
||||
result = false;
|
||||
@@ -312,7 +363,7 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
}
|
||||
if (state.dietData.details == null || state.dietData.details == '') {
|
||||
result = false;
|
||||
emit(state.copyWith(dietDescriptionError: 'Поле не заполненно'));
|
||||
emit(state.copyWith(dietDescriptionError: 'Поле не заполнено'));
|
||||
}
|
||||
if (state.dietData.duration == null) {
|
||||
result = false;
|
||||
@@ -336,15 +387,11 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
if (dailySchedule == null) {
|
||||
result = false;
|
||||
} else {
|
||||
final dailyScheduleErrors = state.dailyScheduleErrors.map((element) {
|
||||
return element.map((element) => element).toList();
|
||||
}).toList();
|
||||
final dailyScheduleErrors = [...state.dailyScheduleErrors];
|
||||
for (final (dayIndex, day) in dailySchedule.indexed) {
|
||||
for (final (mealIndex, meal) in day.indexed) {
|
||||
if (meal.isEmpty) {
|
||||
result = false;
|
||||
dailyScheduleErrors[dayIndex][mealIndex] = 'Добавьте продукты';
|
||||
}
|
||||
if (day.every((meal) => meal.isEmpty)) {
|
||||
result = false;
|
||||
dailyScheduleErrors[dayIndex] = 'Добавьте продукты';
|
||||
}
|
||||
}
|
||||
emit(state.copyWith(dailyScheduleErrors: dailyScheduleErrors));
|
||||
@@ -358,16 +405,13 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
if (dailySchedule == null) {
|
||||
result = false;
|
||||
} else {
|
||||
final dailyScheduleErrors = state.dailyScheduleErrors.map((element) {
|
||||
return element.map((element) => element).toList();
|
||||
}).toList();
|
||||
for (final (mealIndex, meal) in dailySchedule[dayIndex].indexed) {
|
||||
if (meal.isEmpty) {
|
||||
result = false;
|
||||
dailyScheduleErrors[dayIndex][mealIndex] = 'Добавьте продукты';
|
||||
} else {
|
||||
dailyScheduleErrors[dayIndex][mealIndex] = '';
|
||||
}
|
||||
final dailyScheduleErrors = [...state.dailyScheduleErrors];
|
||||
final day = dailySchedule[dayIndex];
|
||||
if (day.every((meal) => meal.isEmpty)) {
|
||||
result = false;
|
||||
dailyScheduleErrors[dayIndex] = 'Добавьте продукты';
|
||||
} else {
|
||||
dailyScheduleErrors[dayIndex] = '';
|
||||
}
|
||||
emit(state.copyWith(dailyScheduleErrors: dailyScheduleErrors));
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ part 'create_diet_state.freezed.dart';
|
||||
class CreateDietState with _$CreateDietState {
|
||||
const factory CreateDietState({
|
||||
required bool isLoading,
|
||||
required bool isDietPublishing,
|
||||
required String? errorMessage,
|
||||
required CreateDietModel dietData,
|
||||
List<List<DailySchedule>>? copiedDay,
|
||||
@@ -17,15 +18,17 @@ class CreateDietState with _$CreateDietState {
|
||||
String? dietDurationError,
|
||||
String? dietStateError,
|
||||
String? dietImageError,
|
||||
required List<List<String>> dailyScheduleErrors,
|
||||
required List<String> dailyScheduleErrors,
|
||||
int? durationIndex,
|
||||
int? dietStateIndex,
|
||||
@Default(false) bool published,
|
||||
@Default(false) bool drafted,
|
||||
@Default(false) bool isDietBeingEdited,
|
||||
}) = _CreateDietState;
|
||||
|
||||
factory CreateDietState.initial() => CreateDietState(
|
||||
dailyScheduleErrors: List.filled(21, ['', '', '', '', '']),
|
||||
dailyScheduleErrors: List.filled(21, ''),
|
||||
isDietPublishing: false,
|
||||
isLoading: false,
|
||||
errorMessage: null,
|
||||
dietData: CreateDietModel(
|
||||
|
||||
@@ -17,6 +17,7 @@ final _privateConstructorUsedError = UnsupportedError(
|
||||
/// @nodoc
|
||||
mixin _$CreateDietState {
|
||||
bool get isLoading => throw _privateConstructorUsedError;
|
||||
bool get isDietPublishing => throw _privateConstructorUsedError;
|
||||
String? get errorMessage => throw _privateConstructorUsedError;
|
||||
CreateDietModel get dietData => throw _privateConstructorUsedError;
|
||||
List<List<DailySchedule>>? get copiedDay =>
|
||||
@@ -26,12 +27,12 @@ mixin _$CreateDietState {
|
||||
String? get dietDurationError => throw _privateConstructorUsedError;
|
||||
String? get dietStateError => throw _privateConstructorUsedError;
|
||||
String? get dietImageError => throw _privateConstructorUsedError;
|
||||
List<List<String>> get dailyScheduleErrors =>
|
||||
throw _privateConstructorUsedError;
|
||||
List<String> get dailyScheduleErrors => throw _privateConstructorUsedError;
|
||||
int? get durationIndex => throw _privateConstructorUsedError;
|
||||
int? get dietStateIndex => throw _privateConstructorUsedError;
|
||||
bool get published => throw _privateConstructorUsedError;
|
||||
bool get drafted => throw _privateConstructorUsedError;
|
||||
bool get isDietBeingEdited => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of CreateDietState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -48,6 +49,7 @@ abstract class $CreateDietStateCopyWith<$Res> {
|
||||
@useResult
|
||||
$Res call(
|
||||
{bool isLoading,
|
||||
bool isDietPublishing,
|
||||
String? errorMessage,
|
||||
CreateDietModel dietData,
|
||||
List<List<DailySchedule>>? copiedDay,
|
||||
@@ -56,11 +58,12 @@ abstract class $CreateDietStateCopyWith<$Res> {
|
||||
String? dietDurationError,
|
||||
String? dietStateError,
|
||||
String? dietImageError,
|
||||
List<List<String>> dailyScheduleErrors,
|
||||
List<String> dailyScheduleErrors,
|
||||
int? durationIndex,
|
||||
int? dietStateIndex,
|
||||
bool published,
|
||||
bool drafted});
|
||||
bool drafted,
|
||||
bool isDietBeingEdited});
|
||||
|
||||
$CreateDietModelCopyWith<$Res> get dietData;
|
||||
}
|
||||
@@ -81,6 +84,7 @@ class _$CreateDietStateCopyWithImpl<$Res, $Val extends CreateDietState>
|
||||
@override
|
||||
$Res call({
|
||||
Object? isLoading = null,
|
||||
Object? isDietPublishing = null,
|
||||
Object? errorMessage = freezed,
|
||||
Object? dietData = null,
|
||||
Object? copiedDay = freezed,
|
||||
@@ -94,12 +98,17 @@ class _$CreateDietStateCopyWithImpl<$Res, $Val extends CreateDietState>
|
||||
Object? dietStateIndex = freezed,
|
||||
Object? published = null,
|
||||
Object? drafted = null,
|
||||
Object? isDietBeingEdited = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
isLoading: null == isLoading
|
||||
? _value.isLoading
|
||||
: isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
isDietPublishing: null == isDietPublishing
|
||||
? _value.isDietPublishing
|
||||
: isDietPublishing // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
errorMessage: freezed == errorMessage
|
||||
? _value.errorMessage
|
||||
: errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
@@ -135,7 +144,7 @@ class _$CreateDietStateCopyWithImpl<$Res, $Val extends CreateDietState>
|
||||
dailyScheduleErrors: null == dailyScheduleErrors
|
||||
? _value.dailyScheduleErrors
|
||||
: dailyScheduleErrors // ignore: cast_nullable_to_non_nullable
|
||||
as List<List<String>>,
|
||||
as List<String>,
|
||||
durationIndex: freezed == durationIndex
|
||||
? _value.durationIndex
|
||||
: durationIndex // ignore: cast_nullable_to_non_nullable
|
||||
@@ -152,6 +161,10 @@ class _$CreateDietStateCopyWithImpl<$Res, $Val extends CreateDietState>
|
||||
? _value.drafted
|
||||
: drafted // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
isDietBeingEdited: null == isDietBeingEdited
|
||||
? _value.isDietBeingEdited
|
||||
: isDietBeingEdited // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
@@ -176,6 +189,7 @@ abstract class _$$CreateDietStateImplCopyWith<$Res>
|
||||
@useResult
|
||||
$Res call(
|
||||
{bool isLoading,
|
||||
bool isDietPublishing,
|
||||
String? errorMessage,
|
||||
CreateDietModel dietData,
|
||||
List<List<DailySchedule>>? copiedDay,
|
||||
@@ -184,11 +198,12 @@ abstract class _$$CreateDietStateImplCopyWith<$Res>
|
||||
String? dietDurationError,
|
||||
String? dietStateError,
|
||||
String? dietImageError,
|
||||
List<List<String>> dailyScheduleErrors,
|
||||
List<String> dailyScheduleErrors,
|
||||
int? durationIndex,
|
||||
int? dietStateIndex,
|
||||
bool published,
|
||||
bool drafted});
|
||||
bool drafted,
|
||||
bool isDietBeingEdited});
|
||||
|
||||
@override
|
||||
$CreateDietModelCopyWith<$Res> get dietData;
|
||||
@@ -208,6 +223,7 @@ class __$$CreateDietStateImplCopyWithImpl<$Res>
|
||||
@override
|
||||
$Res call({
|
||||
Object? isLoading = null,
|
||||
Object? isDietPublishing = null,
|
||||
Object? errorMessage = freezed,
|
||||
Object? dietData = null,
|
||||
Object? copiedDay = freezed,
|
||||
@@ -221,12 +237,17 @@ class __$$CreateDietStateImplCopyWithImpl<$Res>
|
||||
Object? dietStateIndex = freezed,
|
||||
Object? published = null,
|
||||
Object? drafted = null,
|
||||
Object? isDietBeingEdited = null,
|
||||
}) {
|
||||
return _then(_$CreateDietStateImpl(
|
||||
isLoading: null == isLoading
|
||||
? _value.isLoading
|
||||
: isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
isDietPublishing: null == isDietPublishing
|
||||
? _value.isDietPublishing
|
||||
: isDietPublishing // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
errorMessage: freezed == errorMessage
|
||||
? _value.errorMessage
|
||||
: errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
@@ -262,7 +283,7 @@ class __$$CreateDietStateImplCopyWithImpl<$Res>
|
||||
dailyScheduleErrors: null == dailyScheduleErrors
|
||||
? _value._dailyScheduleErrors
|
||||
: dailyScheduleErrors // ignore: cast_nullable_to_non_nullable
|
||||
as List<List<String>>,
|
||||
as List<String>,
|
||||
durationIndex: freezed == durationIndex
|
||||
? _value.durationIndex
|
||||
: durationIndex // ignore: cast_nullable_to_non_nullable
|
||||
@@ -279,6 +300,10 @@ class __$$CreateDietStateImplCopyWithImpl<$Res>
|
||||
? _value.drafted
|
||||
: drafted // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
isDietBeingEdited: null == isDietBeingEdited
|
||||
? _value.isDietBeingEdited
|
||||
: isDietBeingEdited // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -288,6 +313,7 @@ class __$$CreateDietStateImplCopyWithImpl<$Res>
|
||||
class _$CreateDietStateImpl implements _CreateDietState {
|
||||
const _$CreateDietStateImpl(
|
||||
{required this.isLoading,
|
||||
required this.isDietPublishing,
|
||||
required this.errorMessage,
|
||||
required this.dietData,
|
||||
final List<List<DailySchedule>>? copiedDay,
|
||||
@@ -296,17 +322,20 @@ class _$CreateDietStateImpl implements _CreateDietState {
|
||||
this.dietDurationError,
|
||||
this.dietStateError,
|
||||
this.dietImageError,
|
||||
required final List<List<String>> dailyScheduleErrors,
|
||||
required final List<String> dailyScheduleErrors,
|
||||
this.durationIndex,
|
||||
this.dietStateIndex,
|
||||
this.published = false,
|
||||
this.drafted = false})
|
||||
this.drafted = false,
|
||||
this.isDietBeingEdited = false})
|
||||
: _copiedDay = copiedDay,
|
||||
_dailyScheduleErrors = dailyScheduleErrors;
|
||||
|
||||
@override
|
||||
final bool isLoading;
|
||||
@override
|
||||
final bool isDietPublishing;
|
||||
@override
|
||||
final String? errorMessage;
|
||||
@override
|
||||
final CreateDietModel dietData;
|
||||
@@ -330,9 +359,9 @@ class _$CreateDietStateImpl implements _CreateDietState {
|
||||
final String? dietStateError;
|
||||
@override
|
||||
final String? dietImageError;
|
||||
final List<List<String>> _dailyScheduleErrors;
|
||||
final List<String> _dailyScheduleErrors;
|
||||
@override
|
||||
List<List<String>> get dailyScheduleErrors {
|
||||
List<String> get dailyScheduleErrors {
|
||||
if (_dailyScheduleErrors is EqualUnmodifiableListView)
|
||||
return _dailyScheduleErrors;
|
||||
// ignore: implicit_dynamic_type
|
||||
@@ -349,10 +378,13 @@ class _$CreateDietStateImpl implements _CreateDietState {
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool drafted;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool isDietBeingEdited;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CreateDietState(isLoading: $isLoading, errorMessage: $errorMessage, dietData: $dietData, copiedDay: $copiedDay, dietNameError: $dietNameError, dietDescriptionError: $dietDescriptionError, dietDurationError: $dietDurationError, dietStateError: $dietStateError, dietImageError: $dietImageError, dailyScheduleErrors: $dailyScheduleErrors, durationIndex: $durationIndex, dietStateIndex: $dietStateIndex, published: $published, drafted: $drafted)';
|
||||
return 'CreateDietState(isLoading: $isLoading, isDietPublishing: $isDietPublishing, errorMessage: $errorMessage, dietData: $dietData, copiedDay: $copiedDay, dietNameError: $dietNameError, dietDescriptionError: $dietDescriptionError, dietDurationError: $dietDurationError, dietStateError: $dietStateError, dietImageError: $dietImageError, dailyScheduleErrors: $dailyScheduleErrors, durationIndex: $durationIndex, dietStateIndex: $dietStateIndex, published: $published, drafted: $drafted, isDietBeingEdited: $isDietBeingEdited)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -362,6 +394,8 @@ class _$CreateDietStateImpl implements _CreateDietState {
|
||||
other is _$CreateDietStateImpl &&
|
||||
(identical(other.isLoading, isLoading) ||
|
||||
other.isLoading == isLoading) &&
|
||||
(identical(other.isDietPublishing, isDietPublishing) ||
|
||||
other.isDietPublishing == isDietPublishing) &&
|
||||
(identical(other.errorMessage, errorMessage) ||
|
||||
other.errorMessage == errorMessage) &&
|
||||
(identical(other.dietData, dietData) ||
|
||||
@@ -386,13 +420,16 @@ class _$CreateDietStateImpl implements _CreateDietState {
|
||||
other.dietStateIndex == dietStateIndex) &&
|
||||
(identical(other.published, published) ||
|
||||
other.published == published) &&
|
||||
(identical(other.drafted, drafted) || other.drafted == drafted));
|
||||
(identical(other.drafted, drafted) || other.drafted == drafted) &&
|
||||
(identical(other.isDietBeingEdited, isDietBeingEdited) ||
|
||||
other.isDietBeingEdited == isDietBeingEdited));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
isLoading,
|
||||
isDietPublishing,
|
||||
errorMessage,
|
||||
dietData,
|
||||
const DeepCollectionEquality().hash(_copiedDay),
|
||||
@@ -405,7 +442,8 @@ class _$CreateDietStateImpl implements _CreateDietState {
|
||||
durationIndex,
|
||||
dietStateIndex,
|
||||
published,
|
||||
drafted);
|
||||
drafted,
|
||||
isDietBeingEdited);
|
||||
|
||||
/// Create a copy of CreateDietState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -420,6 +458,7 @@ class _$CreateDietStateImpl implements _CreateDietState {
|
||||
abstract class _CreateDietState implements CreateDietState {
|
||||
const factory _CreateDietState(
|
||||
{required final bool isLoading,
|
||||
required final bool isDietPublishing,
|
||||
required final String? errorMessage,
|
||||
required final CreateDietModel dietData,
|
||||
final List<List<DailySchedule>>? copiedDay,
|
||||
@@ -428,15 +467,18 @@ abstract class _CreateDietState implements CreateDietState {
|
||||
final String? dietDurationError,
|
||||
final String? dietStateError,
|
||||
final String? dietImageError,
|
||||
required final List<List<String>> dailyScheduleErrors,
|
||||
required final List<String> dailyScheduleErrors,
|
||||
final int? durationIndex,
|
||||
final int? dietStateIndex,
|
||||
final bool published,
|
||||
final bool drafted}) = _$CreateDietStateImpl;
|
||||
final bool drafted,
|
||||
final bool isDietBeingEdited}) = _$CreateDietStateImpl;
|
||||
|
||||
@override
|
||||
bool get isLoading;
|
||||
@override
|
||||
bool get isDietPublishing;
|
||||
@override
|
||||
String? get errorMessage;
|
||||
@override
|
||||
CreateDietModel get dietData;
|
||||
@@ -453,7 +495,7 @@ abstract class _CreateDietState implements CreateDietState {
|
||||
@override
|
||||
String? get dietImageError;
|
||||
@override
|
||||
List<List<String>> get dailyScheduleErrors;
|
||||
List<String> get dailyScheduleErrors;
|
||||
@override
|
||||
int? get durationIndex;
|
||||
@override
|
||||
@@ -462,6 +504,8 @@ abstract class _CreateDietState implements CreateDietState {
|
||||
bool get published;
|
||||
@override
|
||||
bool get drafted;
|
||||
@override
|
||||
bool get isDietBeingEdited;
|
||||
|
||||
/// Create a copy of CreateDietState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
||||
@@ -335,7 +335,7 @@ class _DietPreviewScreenState extends State<DietPreviewScreen> {
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
return _dayOfPlan(widget.diet.dailySchedule[index], index, s);
|
||||
return _dayOfPlan(widget.diet.dailySchedule[index] ?? {}, index, s);
|
||||
},
|
||||
itemCount: widget.diet.dailySchedule.length,
|
||||
),
|
||||
@@ -363,7 +363,7 @@ class _DietPreviewScreenState extends State<DietPreviewScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
_dayOfPlan(List<List<DailySchedule>> day, int dayNumber, S s) {
|
||||
_dayOfPlan(Map<int, Map<int, DailySchedule>> day, int dayNumber, S s) {
|
||||
_choiceChipWidget(int index) {
|
||||
return ChoiceChip(
|
||||
selectedColor: appTheme.blueA400,
|
||||
@@ -431,8 +431,8 @@ class _DietPreviewScreenState extends State<DietPreviewScreen> {
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _daily(day[widget.selectedIndexes[dayNumber]][index], s),
|
||||
itemCount: day[widget.selectedIndexes[dayNumber]].length,
|
||||
itemBuilder: (context, index) => _daily(day[widget.selectedIndexes[dayNumber] + 1]?[index + 1], s),
|
||||
itemCount: day[widget.selectedIndexes[dayNumber] + 1]?.length ?? 0,
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
],
|
||||
@@ -450,7 +450,8 @@ class _DietPreviewScreenState extends State<DietPreviewScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
_daily(DailySchedule daily, S s) {
|
||||
_daily(DailySchedule? daily, S s) {
|
||||
if (daily == null) return SizedBox.shrink();
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: Row(
|
||||
|
||||
@@ -199,8 +199,7 @@ class _AddMealWidgetState extends State<AddMealWidget> {
|
||||
final portion = DailySchedule(
|
||||
dailyNutrition: DailyNutrition(energy: calories, proteins: proteins, fats: fats, carbohydrates: carbohydrates),
|
||||
quantity: int.parse(quantityController.text),
|
||||
name: glossary.name,
|
||||
uniq: '',
|
||||
name: glossary.name,
|
||||
productNutritionalValueId: glossary.id.toString(),
|
||||
portion: measurementUnits,
|
||||
);
|
||||
@@ -240,7 +239,6 @@ class _AddMealWidgetState extends State<AddMealWidget> {
|
||||
dailyNutrition: DailyNutrition(energy: calories, proteins: proteins, fats: fats, carbohydrates: carbohydrates),
|
||||
quantity: int.parse(quantityController.text),
|
||||
name: newGlossary.name,
|
||||
uniq: '',
|
||||
productNutritionalValueId: newGlossary.id.toString(),
|
||||
portion: measurementUnits,
|
||||
);
|
||||
|
||||
@@ -35,7 +35,7 @@ class _DayWidgetState extends State<DayWidget> {
|
||||
return BlocBuilder<CreateDietCubit, CreateDietState>(
|
||||
builder: (context, state) {
|
||||
return MainBackgroundBouble(
|
||||
hasError: state.dailyScheduleErrors[widget.index].where((element) => element.isNotEmpty).isNotEmpty,
|
||||
hasError: state.dailyScheduleErrors[widget.index].isNotEmpty,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -216,16 +216,15 @@ class _DayWidgetState extends State<DayWidget> {
|
||||
),
|
||||
],
|
||||
),
|
||||
if (state.dailyScheduleErrors[widget.index][_selectedMealIndex].isNotEmpty) ...[
|
||||
if (state.dailyScheduleErrors[widget.index].isNotEmpty) ...[
|
||||
SizedBox(height: 12),
|
||||
CustomErrorWidget(error: state.dailyScheduleErrors[widget.index][_selectedMealIndex]),
|
||||
CustomErrorWidget(error: state.dailyScheduleErrors[widget.index]),
|
||||
],
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
BlocBuilder<CreateDietCubit, CreateDietState>(
|
||||
buildWhen: (previous, current) => previous.dietData.dailySchedule != current.dietData.dailySchedule,
|
||||
builder: (context, state) {
|
||||
if (state.dietData.dailySchedule != null) {
|
||||
final day = state.dietData.dailySchedule?[widget.index];
|
||||
|
||||
@@ -71,6 +71,8 @@ class _DietImagePickerWidgetState extends State<DietImagePickerWidget> {
|
||||
final s = S.of(context);
|
||||
return BlocBuilder<CreateDietCubit, CreateDietState>(
|
||||
builder: (context, state) {
|
||||
final hasImage = state.dietData.image != null || state.dietData.imageUrl != null;
|
||||
final hasFile = state.dietData.image != null;
|
||||
return Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
@@ -81,17 +83,26 @@ class _DietImagePickerWidgetState extends State<DietImagePickerWidget> {
|
||||
height: 240,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
image: state.dietData.image != null ? DecorationImage(fit: BoxFit.cover, image: FileImage(File(state.dietData.image?.path ?? ''))) : null,
|
||||
image: hasImage
|
||||
? DecorationImage(
|
||||
fit: BoxFit.cover,
|
||||
image: (hasFile
|
||||
? FileImage(
|
||||
File(state.dietData.image?.path ?? ''),
|
||||
)
|
||||
: NetworkImage(state.dietData.imageUrl ?? '')) as ImageProvider,
|
||||
)
|
||||
: null,
|
||||
color: appTheme.gray50,
|
||||
),
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
padding: state.dietData.image != null ? null : EdgeInsets.all(8),
|
||||
child: state.dietData.image != null
|
||||
padding: hasImage ? null : EdgeInsets.all(8),
|
||||
child: hasImage
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(top: 6, right: 6),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
context.read<CreateDietCubit>().setImage(null);
|
||||
context.read<CreateDietCubit>().setImage(null, imageUrl: null);
|
||||
},
|
||||
child: Align(
|
||||
alignment: Alignment.topRight,
|
||||
|
||||
@@ -18,8 +18,7 @@ class DietTypeChoiceWidget extends StatefulWidget {
|
||||
class _DietTypeChoiceWidgetState extends State<DietTypeChoiceWidget> {
|
||||
final List<(String, String)> choiceContent = [];
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
// const List<String> typeValues = ['veg', 'light', 'carb', 'meat'];
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
final s = S.of(context);
|
||||
if (choiceContent.isEmpty) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_c
|
||||
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -18,14 +19,23 @@ class StatusDropDownWidget extends StatefulWidget {
|
||||
|
||||
class _StatusDropDownWidgetState extends State<StatusDropDownWidget> {
|
||||
final List<String> dietStateTitles = [];
|
||||
late final List<DietState> dietStates = DietState.values;
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
final s = S.of(context);
|
||||
if (dietStateTitles.isEmpty) {
|
||||
dietStateTitles.add(s.active_diet_state);
|
||||
dietStateTitles.add(s.personal_diet_state);
|
||||
dietStates.forEach((elem) {
|
||||
switch (elem) {
|
||||
case DietState.active:
|
||||
dietStateTitles.add(s.active_diet_state);
|
||||
case DietState.draft:
|
||||
dietStateTitles.add(s.draft);
|
||||
case DietState.personal:
|
||||
dietStateTitles.add(s.personal_diet_state);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +55,8 @@ class _StatusDropDownWidgetState extends State<StatusDropDownWidget> {
|
||||
),
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
context.read<CreateDietCubit>().setDietState(dietStateTitles.indexOf(value));
|
||||
final index = dietStates.indexOf(value);
|
||||
context.read<CreateDietCubit>().setDietState(value, index);
|
||||
}
|
||||
},
|
||||
items: dietStateTitles
|
||||
@@ -55,11 +66,11 @@ class _StatusDropDownWidgetState extends State<StatusDropDownWidget> {
|
||||
dietStateTitles[index],
|
||||
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
value: dietStateTitles[index],
|
||||
value: dietStates[index],
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
value: state.dietStateIndex == null ? null : dietStateTitles[state.dietStateIndex ?? 0],
|
||||
value: state.dietStateIndex == null ? null : dietStates[state.dietStateIndex ?? 0],
|
||||
),
|
||||
if (state.dietStateError != null) ...[
|
||||
SizedBox(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/features/create_diet/services/create_diet_service.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
|
||||
@@ -8,40 +8,54 @@ class CreateDietRepository {
|
||||
CreateDietRepository({required this.createDietService});
|
||||
|
||||
Future<CreatedDiet> createDiet({
|
||||
String? name,
|
||||
int? duration,
|
||||
String? state,
|
||||
String? meatType,
|
||||
String? carbType,
|
||||
String? vegType,
|
||||
String? lightType,
|
||||
String? details,
|
||||
File? image,
|
||||
required String name,
|
||||
required int duration,
|
||||
required DietState state,
|
||||
required String meatType,
|
||||
required String carbType,
|
||||
required String vegType,
|
||||
required String lightType,
|
||||
required String details,
|
||||
required File image,
|
||||
required List<List<List<DailySchedule>>> dailySchedule,
|
||||
}) {
|
||||
return createDietService.createDiet(
|
||||
name: name,
|
||||
duration: duration,
|
||||
state: state,
|
||||
meatType: meatType,
|
||||
carbType: carbType,
|
||||
vegType: vegType,
|
||||
lightType: lightType,
|
||||
details: details,
|
||||
image: image,
|
||||
);
|
||||
}
|
||||
final dietMap = <String, dynamic>{};
|
||||
for (final (dayIndex, day) in dailySchedule.indexed) {
|
||||
for (final (mealIndex, meal) in day.indexed) {
|
||||
for (final (productIndex, product) in meal.indexed) {
|
||||
final jsonProduct = product.toJson();
|
||||
dietMap.addAll({
|
||||
'daily_schedule[${dayIndex + 1}][${mealIndex + 1}][${productIndex}][name]': jsonProduct['name'],
|
||||
'daily_schedule[${dayIndex + 1}][${mealIndex + 1}][${productIndex}][portion]': jsonProduct['portion'],
|
||||
'daily_schedule[${dayIndex + 1}][${mealIndex + 1}][${productIndex}][quantity]': jsonProduct['quantity'],
|
||||
'daily_schedule[${dayIndex + 1}][${mealIndex + 1}][${productIndex}][product_nutritional_value_id]': jsonProduct['product_nutritional_value_id'],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> createDietDailySchedule({
|
||||
required int id,
|
||||
required Map<String, dynamic> model,
|
||||
}) {
|
||||
try {
|
||||
return createDietService.createDietDailySchedule(
|
||||
id: id,
|
||||
model: model,
|
||||
);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
dietMap.addAll({
|
||||
'name': name,
|
||||
'details': details,
|
||||
'duration': duration,
|
||||
'type[meat]': meatType,
|
||||
'type[carb]': carbType,
|
||||
'type[veg]': vegType,
|
||||
'type[light]': lightType,
|
||||
'image': MultipartFile.fromFileSync(
|
||||
image.path,
|
||||
filename: image.path.split(Platform.pathSeparator).last,
|
||||
),
|
||||
});
|
||||
|
||||
final formData = FormData.fromMap(dietMap);
|
||||
switch (state) {
|
||||
case DietState.active:
|
||||
return createDietService.createPublishedDiet(formData: formData);
|
||||
case DietState.draft:
|
||||
return createDietService.createDraftedDiet(formData: formData);
|
||||
case DietState.personal:
|
||||
return createDietService.createPersonalDiet(formData: formData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
import 'package:retrofit/retrofit.dart';
|
||||
@@ -10,23 +8,21 @@ part 'create_diet_service.g.dart';
|
||||
abstract class CreateDietService {
|
||||
factory CreateDietService(Dio dio, {String baseUrl}) = _CreateDietService;
|
||||
|
||||
@POST('/api/diets/create')
|
||||
@POST('/api/diets/create/published')
|
||||
@MultiPart()
|
||||
Future<CreatedDiet> createDiet({
|
||||
@Part(name: 'name') String? name,
|
||||
@Part(name: 'duration') int? duration,
|
||||
@Part(name: 'state') String? state,
|
||||
@Part(name: 'type[meat]') String? meatType,
|
||||
@Part(name: 'type[carb]') String? carbType,
|
||||
@Part(name: 'type[veg]') String? vegType,
|
||||
@Part(name: 'type[light]') String? lightType,
|
||||
@Part(name: 'details') String? details,
|
||||
@Part(name: 'image') File? image,
|
||||
Future<CreatedDiet> createPublishedDiet({
|
||||
@Body() required FormData formData,
|
||||
});
|
||||
|
||||
@POST('/api/diets/create/{id}/daily_schedule')
|
||||
Future<void> createDietDailySchedule({
|
||||
@Path() required int id,
|
||||
@Body() required Map<String, dynamic> model,
|
||||
@POST('/api/diets/create/drafted')
|
||||
@MultiPart()
|
||||
Future<CreatedDiet> createDraftedDiet({
|
||||
@Body() required FormData formData,
|
||||
});
|
||||
|
||||
@POST('/api/diets/create/personalized')
|
||||
@MultiPart()
|
||||
Future<CreatedDiet> createPersonalDiet({
|
||||
@Body() required FormData formData,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -18,59 +18,11 @@ class _CreateDietService implements CreateDietService {
|
||||
final ParseErrorLogger? errorLogger;
|
||||
|
||||
@override
|
||||
Future<CreatedDiet> createDiet({
|
||||
String? name,
|
||||
int? duration,
|
||||
String? state,
|
||||
String? meatType,
|
||||
String? carbType,
|
||||
String? vegType,
|
||||
String? lightType,
|
||||
String? details,
|
||||
File? image,
|
||||
}) async {
|
||||
Future<CreatedDiet> createPublishedDiet({required FormData formData}) async {
|
||||
final _extra = <String, dynamic>{};
|
||||
final queryParameters = <String, dynamic>{};
|
||||
queryParameters.removeWhere((k, v) => v == null);
|
||||
final _headers = <String, dynamic>{};
|
||||
final _data = FormData();
|
||||
if (name != null) {
|
||||
_data.fields.add(MapEntry('name', name));
|
||||
}
|
||||
if (duration != null) {
|
||||
_data.fields.add(MapEntry('duration', duration.toString()));
|
||||
}
|
||||
if (state != null) {
|
||||
_data.fields.add(MapEntry('state', state));
|
||||
}
|
||||
if (meatType != null) {
|
||||
_data.fields.add(MapEntry('type[meat]', meatType));
|
||||
}
|
||||
if (carbType != null) {
|
||||
_data.fields.add(MapEntry('type[carb]', carbType));
|
||||
}
|
||||
if (vegType != null) {
|
||||
_data.fields.add(MapEntry('type[veg]', vegType));
|
||||
}
|
||||
if (lightType != null) {
|
||||
_data.fields.add(MapEntry('type[light]', lightType));
|
||||
}
|
||||
if (details != null) {
|
||||
_data.fields.add(MapEntry('details', details));
|
||||
}
|
||||
if (image != null) {
|
||||
if (image != null) {
|
||||
_data.files.add(
|
||||
MapEntry(
|
||||
'image',
|
||||
MultipartFile.fromFileSync(
|
||||
image.path,
|
||||
filename: image.path.split(Platform.pathSeparator).last,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
final _data = formData;
|
||||
final _options = _setStreamType<CreatedDiet>(
|
||||
Options(
|
||||
method: 'POST',
|
||||
@@ -80,7 +32,7 @@ class _CreateDietService implements CreateDietService {
|
||||
)
|
||||
.compose(
|
||||
_dio.options,
|
||||
'/api/diets/create',
|
||||
'/api/diets/create/published',
|
||||
queryParameters: queryParameters,
|
||||
data: _data,
|
||||
)
|
||||
@@ -98,26 +50,67 @@ class _CreateDietService implements CreateDietService {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> createDietDailySchedule({
|
||||
required int id,
|
||||
required Map<String, dynamic> model,
|
||||
}) async {
|
||||
Future<CreatedDiet> createDraftedDiet({required FormData formData}) async {
|
||||
final _extra = <String, dynamic>{};
|
||||
final queryParameters = <String, dynamic>{};
|
||||
final _headers = <String, dynamic>{};
|
||||
final _data = <String, dynamic>{};
|
||||
_data.addAll(model);
|
||||
final _options = _setStreamType<void>(
|
||||
Options(method: 'POST', headers: _headers, extra: _extra)
|
||||
final _data = formData;
|
||||
final _options = _setStreamType<CreatedDiet>(
|
||||
Options(
|
||||
method: 'POST',
|
||||
headers: _headers,
|
||||
extra: _extra,
|
||||
contentType: 'multipart/form-data',
|
||||
)
|
||||
.compose(
|
||||
_dio.options,
|
||||
'/api/diets/create/${id}/daily_schedule',
|
||||
'/api/diets/create/drafted',
|
||||
queryParameters: queryParameters,
|
||||
data: _data,
|
||||
)
|
||||
.copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
|
||||
);
|
||||
await _dio.fetch<void>(_options);
|
||||
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
|
||||
late CreatedDiet _value;
|
||||
try {
|
||||
_value = CreatedDiet.fromJson(_result.data!);
|
||||
} on Object catch (e, s) {
|
||||
errorLogger?.logError(e, s, _options);
|
||||
rethrow;
|
||||
}
|
||||
return _value;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<CreatedDiet> createPersonalDiet({required FormData formData}) async {
|
||||
final _extra = <String, dynamic>{};
|
||||
final queryParameters = <String, dynamic>{};
|
||||
final _headers = <String, dynamic>{};
|
||||
final _data = formData;
|
||||
final _options = _setStreamType<CreatedDiet>(
|
||||
Options(
|
||||
method: 'POST',
|
||||
headers: _headers,
|
||||
extra: _extra,
|
||||
contentType: 'multipart/form-data',
|
||||
)
|
||||
.compose(
|
||||
_dio.options,
|
||||
'/api/diets/create/personalized',
|
||||
queryParameters: queryParameters,
|
||||
data: _data,
|
||||
)
|
||||
.copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
|
||||
);
|
||||
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
|
||||
late CreatedDiet _value;
|
||||
try {
|
||||
_value = CreatedDiet.fromJson(_result.data!);
|
||||
} on Object catch (e, s) {
|
||||
errorLogger?.logError(e, s, _options);
|
||||
rethrow;
|
||||
}
|
||||
return _value;
|
||||
}
|
||||
|
||||
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
|
||||
|
||||
@@ -25,7 +25,25 @@ class DevToolsScreenContent extends StatefulWidget {
|
||||
|
||||
class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
|
||||
String baseUrlForTest = '';
|
||||
final List<TestCases> testCases = [Test850task(), GetChatRoomPreviews(), GetChatRoom(), SendMessage(), GetChatMessages(), GetChatRoomIdByUserSlug()];
|
||||
final List<TestCases> testCases = [
|
||||
Test850task(),
|
||||
GetChatRoomPreviews(),
|
||||
GetChatRoom(),
|
||||
SendMessage(),
|
||||
GetChatMessages(),
|
||||
GetChatRoomIdByUserSlug(),
|
||||
GetBlogPostList(),
|
||||
GetRecipes(),
|
||||
GetDiets(),
|
||||
GetPlans(),
|
||||
GetBlogPostComments(),
|
||||
SendBlogPostComment(),
|
||||
GetDietsComments(),
|
||||
SendDietsComment(),
|
||||
GetPlansComments(),
|
||||
SendPlansComment(),
|
||||
GetRecipesComments(),
|
||||
];
|
||||
final controller = TextEditingController();
|
||||
late final List<TestState> testsStates;
|
||||
|
||||
@@ -41,7 +59,6 @@ class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
@@ -74,76 +91,89 @@ class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
if (baseUrlForTest != baseUrl)
|
||||
CustomOutlinedButton(
|
||||
text: 'Set dev2 base url',
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
this.baseUrlForTest = baseUrl;
|
||||
});
|
||||
},
|
||||
buttonStyle: ButtonStyle(),
|
||||
),
|
||||
SizedBox(
|
||||
height: baseUrlForTest != baseUrl ? 40 : 10,
|
||||
),
|
||||
if (baseUrlForTest != 'https://eatvio.ru')
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: testCases.length,
|
||||
itemBuilder: (context, index) => Row(
|
||||
children: [
|
||||
Text('${index + 1}. ${testCases[index].name}'),
|
||||
SizedBox(
|
||||
width: 50,
|
||||
),
|
||||
Expanded(
|
||||
child: CustomOutlinedButton(
|
||||
text: 'run',
|
||||
height: 20,
|
||||
width: 60,
|
||||
onPressed: () async {
|
||||
if (testsStates[index] == TestState.loading) return;
|
||||
|
||||
setState(() {
|
||||
testsStates[index] = TestState.loading;
|
||||
});
|
||||
final result = await testCases[index].run(baseUrl: baseUrlForTest);
|
||||
if (result) {
|
||||
setState(() {
|
||||
testsStates[index] = TestState.success;
|
||||
});
|
||||
showSuccessToast('${testCases[index].name} passed');
|
||||
} else {
|
||||
setState(() {
|
||||
testsStates[index] = TestState.fail;
|
||||
});
|
||||
showErrorToast('${testCases[index].name} failed');
|
||||
}
|
||||
},
|
||||
buttonStyle: ButtonStyle(),
|
||||
itemBuilder: (context, index) => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text('${index + 1}. ${testCases[index].name}'),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 30,
|
||||
),
|
||||
switch (testsStates[index]) {
|
||||
TestState.success => Expanded(
|
||||
child: Container(
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CustomOutlinedButton(
|
||||
text: 'run',
|
||||
height: 20,
|
||||
color: Colors.green,
|
||||
width: 60,
|
||||
onPressed: () async {
|
||||
if (testsStates[index] == TestState.loading) return;
|
||||
|
||||
setState(() {
|
||||
testsStates[index] = TestState.loading;
|
||||
});
|
||||
final result = await testCases[index].run(baseUrl: baseUrlForTest);
|
||||
if (result) {
|
||||
setState(() {
|
||||
testsStates[index] = TestState.success;
|
||||
});
|
||||
showSuccessToast('${testCases[index].name} passed');
|
||||
} else {
|
||||
setState(() {
|
||||
testsStates[index] = TestState.fail;
|
||||
});
|
||||
showErrorToast('${testCases[index].name} failed');
|
||||
}
|
||||
},
|
||||
buttonStyle: ButtonStyle(),
|
||||
),
|
||||
),
|
||||
TestState.fail => Expanded(
|
||||
child: Container(
|
||||
height: 20,
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
TestState.notRun => Expanded(
|
||||
child: Container(
|
||||
height: 20,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
TestState.loading => Expanded(
|
||||
child: Container(
|
||||
height: 20,
|
||||
child: SizedBox(height: 20, width: 20, child: Center(child: CircularProgressIndicator())),
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
SizedBox(width: 5),
|
||||
switch (testsStates[index]) {
|
||||
TestState.success => Container(
|
||||
width: 20,
|
||||
height: 20,
|
||||
color: Colors.green,
|
||||
),
|
||||
TestState.fail => Container(
|
||||
width: 20,
|
||||
height: 20,
|
||||
color: Colors.red,
|
||||
),
|
||||
TestState.notRun => Container(
|
||||
width: 20,
|
||||
height: 20,
|
||||
color: Colors.grey,
|
||||
),
|
||||
TestState.loading => Container(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: SizedBox(height: 20, width: 20, child: Center(child: CircularProgressIndicator())),
|
||||
),
|
||||
},
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -8,6 +8,7 @@ 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';
|
||||
import 'package:eatvio_app/features/recipes/services/recipe_service.dart';
|
||||
|
||||
sealed class TestCases {
|
||||
String get name => throw UnimplementedError();
|
||||
@@ -115,6 +116,7 @@ class Test850task implements TestCases {
|
||||
}
|
||||
}
|
||||
|
||||
// ChatService
|
||||
class GetChatRoomPreviews implements TestCases {
|
||||
@override
|
||||
String get name => '/api/chat/index';
|
||||
@@ -140,7 +142,7 @@ class GetChatRoom implements TestCases {
|
||||
|
||||
@override
|
||||
Future<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
final chatService = ChatService(dio, baseUrl: baseUrl);
|
||||
|
||||
@@ -159,7 +161,7 @@ class SendMessage implements TestCases {
|
||||
|
||||
@override
|
||||
Future<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
final chatService = ChatService(dio, baseUrl: baseUrl);
|
||||
|
||||
@@ -178,7 +180,7 @@ class GetChatMessages implements TestCases {
|
||||
|
||||
@override
|
||||
Future<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
final chatService = ChatService(dio, baseUrl: baseUrl);
|
||||
|
||||
@@ -197,7 +199,7 @@ class GetChatRoomIdByUserSlug implements TestCases {
|
||||
|
||||
@override
|
||||
Future<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
final chatService = ChatService(dio, baseUrl: baseUrl);
|
||||
final consultantService = FeedService(dio, baseUrl: baseUrl);
|
||||
@@ -217,3 +219,230 @@ class GetChatRoomIdByUserSlug implements TestCases {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FeedService
|
||||
class GetBlogPostList implements TestCases {
|
||||
@override
|
||||
String get name => '/api/blogPosts/index';
|
||||
|
||||
@override
|
||||
Future<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
final feedService = FeedService(dio, baseUrl: baseUrl);
|
||||
await feedService.getBlogPostList(page: 1);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GetRecipes implements TestCases {
|
||||
@override
|
||||
String get name => '/api/recipes/index';
|
||||
|
||||
@override
|
||||
Future<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
final feedService = FeedService(dio, baseUrl: baseUrl);
|
||||
final recipeService = RecipeService(dio, baseUrl: baseUrl);
|
||||
final tagsModel = await recipeService.getTags();
|
||||
final tags = tagsModel.data;
|
||||
final tagsIds = tags.map((e) => e.id).toList();
|
||||
await feedService.getRecipes(page: 1, tags: tagsIds, query: null);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GetDiets implements TestCases {
|
||||
@override
|
||||
String get name => '/api/diets/index';
|
||||
|
||||
@override
|
||||
Future<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
final feedService = FeedService(dio, baseUrl: baseUrl);
|
||||
await feedService.getDiets(page: 1, query: null);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GetPlans implements TestCases {
|
||||
@override
|
||||
String get name => '/api/plans/index';
|
||||
|
||||
@override
|
||||
Future<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
final feedService = FeedService(dio, baseUrl: baseUrl);
|
||||
await feedService.getPlans(page: 1, query: null);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GetBlogPostComments implements TestCases {
|
||||
@override
|
||||
String get name => '/api/blogPosts/{id}/comments/show';
|
||||
|
||||
@override
|
||||
Future<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
final feedService = FeedService(dio, baseUrl: baseUrl);
|
||||
final blogPostsModel = await feedService.getBlogPostList(page: 1, query: null);
|
||||
final blogPosts = blogPostsModel.feedBlogPostList;
|
||||
await feedService.getBlogPostComments(
|
||||
id: blogPosts.first.id.toString(),
|
||||
);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SendBlogPostComment implements TestCases {
|
||||
@override
|
||||
String get name => '/api/blogPosts/{id}/comment/create';
|
||||
|
||||
@override
|
||||
Future<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
final feedService = FeedService(dio, baseUrl: baseUrl);
|
||||
final blogPostsModel = await feedService.getBlogPostList(page: 1, query: null);
|
||||
final blogPosts = blogPostsModel.feedBlogPostList;
|
||||
await feedService.sendBlogPostComment(
|
||||
id: blogPosts.first.id.toString(),
|
||||
body: 'auto test message',
|
||||
);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GetDietsComments implements TestCases {
|
||||
@override
|
||||
String get name => '/api/diets/{id}/comments/show';
|
||||
|
||||
@override
|
||||
Future<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
final feedService = FeedService(dio, baseUrl: baseUrl);
|
||||
final dietsModel = await feedService.getDiets(page: 1, query: null);
|
||||
final diets = dietsModel.data;
|
||||
await feedService.getDietsComments(
|
||||
id: diets.first.id.toString(),
|
||||
);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SendDietsComment implements TestCases {
|
||||
@override
|
||||
String get name => '/api/diets/{id}/comment/create';
|
||||
|
||||
@override
|
||||
Future<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
final feedService = FeedService(dio, baseUrl: baseUrl);
|
||||
final dietsModel = await feedService.getDiets(page: 1, query: null);
|
||||
final diets = dietsModel.data;
|
||||
await feedService.sendDietsComment(
|
||||
id: diets.first.id.toString(),
|
||||
body: 'auto test message',
|
||||
);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GetPlansComments implements TestCases {
|
||||
@override
|
||||
String get name => '/api/plans/{slug}/comments/show';
|
||||
|
||||
@override
|
||||
Future<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
final feedService = FeedService(dio, baseUrl: baseUrl);
|
||||
final plansModel = await feedService.getPlans(page: 1, query: null);
|
||||
final plans = plansModel.data;
|
||||
await feedService.getPlansComments(
|
||||
slug: plans.first.slug,
|
||||
);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SendPlansComment implements TestCases {
|
||||
@override
|
||||
String get name => '/api/plans/{slug}/comment/create';
|
||||
|
||||
@override
|
||||
Future<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
final feedService = FeedService(dio, baseUrl: baseUrl);
|
||||
final plansModel = await feedService.getPlans(page: 1, query: null);
|
||||
final plans = plansModel.data;
|
||||
await feedService.sendPlansComment(
|
||||
slug: plans.first.slug,
|
||||
body: 'auto test message',
|
||||
);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GetRecipesComments implements TestCases {
|
||||
@override
|
||||
String get name => '/api/recipes/{id}/comments/show';
|
||||
|
||||
@override
|
||||
Future<bool> run({required String baseUrl}) async {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
final feedService = FeedService(dio, baseUrl: baseUrl);
|
||||
final recipeService = RecipeService(dio, baseUrl: baseUrl);
|
||||
final tagsModel = await recipeService.getTags();
|
||||
final tags = tagsModel.data;
|
||||
final tagsIds = tags.map((e) => e.id).toList();
|
||||
final recipesModel = await feedService.getRecipes(page: 1, tags: tagsIds, query: null);
|
||||
final recipes = recipesModel.recipesList;
|
||||
await feedService.getRecipesComments(
|
||||
id: recipes.first.id.toString(),
|
||||
);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,7 +454,7 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
return _dayOfPlan(widget.diet.dailySchedule[index], index, s);
|
||||
return _dayOfPlan(widget.diet.dailySchedule[index + 1] ?? {}, index, s);
|
||||
},
|
||||
itemCount: widget.diet.dailySchedule.length,
|
||||
),
|
||||
@@ -500,7 +500,7 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
_dayOfPlan(List<List<DailySchedule>> day, int dayNumber, S s) {
|
||||
_dayOfPlan(Map<int, Map<int, DailySchedule>> day, int dayNumber, S s) {
|
||||
_choiceChipWidget(int index) {
|
||||
return ChoiceChip(
|
||||
selectedColor: appTheme.blueA400,
|
||||
@@ -568,8 +568,8 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _daily(day[widget.selectedIndexes[dayNumber]][index], s),
|
||||
itemCount: day[widget.selectedIndexes[dayNumber]].length,
|
||||
itemBuilder: (context, index) => _daily(day[widget.selectedIndexes[dayNumber] + 1]?[index + 1], s),
|
||||
itemCount: day[widget.selectedIndexes[dayNumber] + 1]?.length ?? 0,
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
],
|
||||
@@ -587,7 +587,8 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
_daily(DailySchedule daily, S s) {
|
||||
_daily(DailySchedule? daily, S s) {
|
||||
if (daily == null) return const SizedBox();
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: Row(
|
||||
|
||||
@@ -23,10 +23,10 @@ class Diet with _$Diet {
|
||||
@JsonKey(name: "name") required String name,
|
||||
@JsonKey(name: "image") required String image,
|
||||
@JsonKey(name: "duration") required int duration,
|
||||
@JsonKey(name: "state") required String state,
|
||||
@JsonKey(name: "state") required DietState state,
|
||||
@JsonKey(name: "type") required DietType type,
|
||||
@JsonKey(name: "details") required String details,
|
||||
@JsonKey(name: "daily_schedule") required List<List<List<DailySchedule>>> dailySchedule,
|
||||
@JsonKey(name: "daily_schedule") required Map<int, Map<int, Map<int, DailySchedule>>> dailySchedule,
|
||||
@JsonKey(name: "author") required Author author,
|
||||
@JsonKey(name: "total_likes") required int totalLikes,
|
||||
@JsonKey(name: "total_views") required int totalViews,
|
||||
@@ -39,10 +39,30 @@ class Diet with _$Diet {
|
||||
factory Diet.fromJson(Map<String, dynamic> json) => _$DietFromJson(json);
|
||||
}
|
||||
|
||||
enum DietState {
|
||||
@JsonKey(name: 'active')
|
||||
active,
|
||||
@JsonKey(name: 'draft')
|
||||
draft,
|
||||
@JsonKey(name: 'personal')
|
||||
personal;
|
||||
|
||||
String toString() {
|
||||
switch (this) {
|
||||
case DietState.active:
|
||||
return 'active';
|
||||
case DietState.draft:
|
||||
return 'draft';
|
||||
case DietState.personal:
|
||||
return 'personal';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class CreatedDiet with _$CreatedDiet {
|
||||
const factory CreatedDiet({
|
||||
@JsonKey(name: "id") required int id,
|
||||
@JsonKey(name: "id") required int id,
|
||||
}) = _CreatedDiet;
|
||||
|
||||
factory CreatedDiet.fromJson(Map<String, dynamic> json) => _$CreatedDietFromJson(json);
|
||||
@@ -51,9 +71,9 @@ class CreatedDiet with _$CreatedDiet {
|
||||
int parseDuration(dynamic duration) {
|
||||
if (duration is String) {
|
||||
return int.parse(duration);
|
||||
}else {
|
||||
} else {
|
||||
return duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
@@ -74,9 +94,8 @@ class Author with _$Author {
|
||||
class DailySchedule with _$DailySchedule {
|
||||
const factory DailySchedule({
|
||||
@JsonKey(name: "name") required String name,
|
||||
@JsonKey(name: "uniq", includeToJson: false) required String uniq,
|
||||
@JsonKey(name: "portion") required Portion portion,
|
||||
@JsonKey(name: "quantity") required int quantity,
|
||||
@JsonKey(name: "quantity", fromJson: _parseQuantity) required int quantity,
|
||||
@JsonKey(includeToJson: false, includeFromJson: false) DailyNutrition? dailyNutrition,
|
||||
@JsonKey(name: 'product_nutritional_value_id', includeToJson: true, includeFromJson: false, includeIfNull: false) String? productNutritionalValueId,
|
||||
}) = _DailySchedule;
|
||||
@@ -84,8 +103,16 @@ class DailySchedule with _$DailySchedule {
|
||||
factory DailySchedule.fromJson(Map<String, dynamic> json) => _$DailyScheduleFromJson(json);
|
||||
}
|
||||
|
||||
int _parseQuantity(dynamic quantity) {
|
||||
if (quantity is String) {
|
||||
return int.parse(quantity);
|
||||
} else {
|
||||
return quantity;
|
||||
}
|
||||
}
|
||||
|
||||
enum Portion {
|
||||
@JsonValue("grams")
|
||||
@JsonValue("gr")
|
||||
GRAMS,
|
||||
@JsonValue("milliliters")
|
||||
MILLILITERS,
|
||||
|
||||
@@ -268,13 +268,13 @@ mixin _$Diet {
|
||||
@JsonKey(name: "duration")
|
||||
int get duration => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "state")
|
||||
String get state => throw _privateConstructorUsedError;
|
||||
DietState get state => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "type")
|
||||
DietType get type => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "details")
|
||||
String get details => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "daily_schedule")
|
||||
List<List<List<DailySchedule>>> get dailySchedule =>
|
||||
Map<int, Map<int, Map<int, DailySchedule>>> get dailySchedule =>
|
||||
throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "author")
|
||||
Author get author => throw _privateConstructorUsedError;
|
||||
@@ -310,11 +310,11 @@ abstract class $DietCopyWith<$Res> {
|
||||
@JsonKey(name: "name") String name,
|
||||
@JsonKey(name: "image") String image,
|
||||
@JsonKey(name: "duration") int duration,
|
||||
@JsonKey(name: "state") String state,
|
||||
@JsonKey(name: "state") DietState state,
|
||||
@JsonKey(name: "type") DietType type,
|
||||
@JsonKey(name: "details") String details,
|
||||
@JsonKey(name: "daily_schedule")
|
||||
List<List<List<DailySchedule>>> dailySchedule,
|
||||
Map<int, Map<int, Map<int, DailySchedule>>> dailySchedule,
|
||||
@JsonKey(name: "author") Author author,
|
||||
@JsonKey(name: "total_likes") int totalLikes,
|
||||
@JsonKey(name: "total_views") int totalViews,
|
||||
@@ -378,7 +378,7 @@ class _$DietCopyWithImpl<$Res, $Val extends Diet>
|
||||
state: null == state
|
||||
? _value.state
|
||||
: state // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as DietState,
|
||||
type: null == type
|
||||
? _value.type
|
||||
: type // ignore: cast_nullable_to_non_nullable
|
||||
@@ -390,7 +390,7 @@ class _$DietCopyWithImpl<$Res, $Val extends Diet>
|
||||
dailySchedule: null == dailySchedule
|
||||
? _value.dailySchedule
|
||||
: dailySchedule // ignore: cast_nullable_to_non_nullable
|
||||
as List<List<List<DailySchedule>>>,
|
||||
as Map<int, Map<int, Map<int, DailySchedule>>>,
|
||||
author: null == author
|
||||
? _value.author
|
||||
: author // ignore: cast_nullable_to_non_nullable
|
||||
@@ -455,11 +455,11 @@ abstract class _$$DietImplCopyWith<$Res> implements $DietCopyWith<$Res> {
|
||||
@JsonKey(name: "name") String name,
|
||||
@JsonKey(name: "image") String image,
|
||||
@JsonKey(name: "duration") int duration,
|
||||
@JsonKey(name: "state") String state,
|
||||
@JsonKey(name: "state") DietState state,
|
||||
@JsonKey(name: "type") DietType type,
|
||||
@JsonKey(name: "details") String details,
|
||||
@JsonKey(name: "daily_schedule")
|
||||
List<List<List<DailySchedule>>> dailySchedule,
|
||||
Map<int, Map<int, Map<int, DailySchedule>>> dailySchedule,
|
||||
@JsonKey(name: "author") Author author,
|
||||
@JsonKey(name: "total_likes") int totalLikes,
|
||||
@JsonKey(name: "total_views") int totalViews,
|
||||
@@ -522,7 +522,7 @@ class __$$DietImplCopyWithImpl<$Res>
|
||||
state: null == state
|
||||
? _value.state
|
||||
: state // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as DietState,
|
||||
type: null == type
|
||||
? _value.type
|
||||
: type // ignore: cast_nullable_to_non_nullable
|
||||
@@ -534,7 +534,7 @@ class __$$DietImplCopyWithImpl<$Res>
|
||||
dailySchedule: null == dailySchedule
|
||||
? _value._dailySchedule
|
||||
: dailySchedule // ignore: cast_nullable_to_non_nullable
|
||||
as List<List<List<DailySchedule>>>,
|
||||
as Map<int, Map<int, Map<int, DailySchedule>>>,
|
||||
author: null == author
|
||||
? _value.author
|
||||
: author // ignore: cast_nullable_to_non_nullable
|
||||
@@ -579,7 +579,7 @@ class _$DietImpl implements _Diet {
|
||||
@JsonKey(name: "type") required this.type,
|
||||
@JsonKey(name: "details") required this.details,
|
||||
@JsonKey(name: "daily_schedule")
|
||||
required final List<List<List<DailySchedule>>> dailySchedule,
|
||||
required final Map<int, Map<int, Map<int, DailySchedule>>> dailySchedule,
|
||||
@JsonKey(name: "author") required this.author,
|
||||
@JsonKey(name: "total_likes") required this.totalLikes,
|
||||
@JsonKey(name: "total_views") required this.totalViews,
|
||||
@@ -608,20 +608,20 @@ class _$DietImpl implements _Diet {
|
||||
final int duration;
|
||||
@override
|
||||
@JsonKey(name: "state")
|
||||
final String state;
|
||||
final DietState state;
|
||||
@override
|
||||
@JsonKey(name: "type")
|
||||
final DietType type;
|
||||
@override
|
||||
@JsonKey(name: "details")
|
||||
final String details;
|
||||
final List<List<List<DailySchedule>>> _dailySchedule;
|
||||
final Map<int, Map<int, Map<int, DailySchedule>>> _dailySchedule;
|
||||
@override
|
||||
@JsonKey(name: "daily_schedule")
|
||||
List<List<List<DailySchedule>>> get dailySchedule {
|
||||
if (_dailySchedule is EqualUnmodifiableListView) return _dailySchedule;
|
||||
Map<int, Map<int, Map<int, DailySchedule>>> get dailySchedule {
|
||||
if (_dailySchedule is EqualUnmodifiableMapView) return _dailySchedule;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_dailySchedule);
|
||||
return EqualUnmodifiableMapView(_dailySchedule);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -728,11 +728,11 @@ abstract class _Diet implements Diet {
|
||||
@JsonKey(name: "name") required final String name,
|
||||
@JsonKey(name: "image") required final String image,
|
||||
@JsonKey(name: "duration") required final int duration,
|
||||
@JsonKey(name: "state") required final String state,
|
||||
@JsonKey(name: "state") required final DietState state,
|
||||
@JsonKey(name: "type") required final DietType type,
|
||||
@JsonKey(name: "details") required final String details,
|
||||
@JsonKey(name: "daily_schedule")
|
||||
required final List<List<List<DailySchedule>>> dailySchedule,
|
||||
required final Map<int, Map<int, Map<int, DailySchedule>>> dailySchedule,
|
||||
@JsonKey(name: "author") required final Author author,
|
||||
@JsonKey(name: "total_likes") required final int totalLikes,
|
||||
@JsonKey(name: "total_views") required final int totalViews,
|
||||
@@ -758,7 +758,7 @@ abstract class _Diet implements Diet {
|
||||
int get duration;
|
||||
@override
|
||||
@JsonKey(name: "state")
|
||||
String get state;
|
||||
DietState get state;
|
||||
@override
|
||||
@JsonKey(name: "type")
|
||||
DietType get type;
|
||||
@@ -767,7 +767,7 @@ abstract class _Diet implements Diet {
|
||||
String get details;
|
||||
@override
|
||||
@JsonKey(name: "daily_schedule")
|
||||
List<List<List<DailySchedule>>> get dailySchedule;
|
||||
Map<int, Map<int, Map<int, DailySchedule>>> get dailySchedule;
|
||||
@override
|
||||
@JsonKey(name: "author")
|
||||
Author get author;
|
||||
@@ -1242,11 +1242,9 @@ DailySchedule _$DailyScheduleFromJson(Map<String, dynamic> json) {
|
||||
mixin _$DailySchedule {
|
||||
@JsonKey(name: "name")
|
||||
String get name => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "uniq", includeToJson: false)
|
||||
String get uniq => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "portion")
|
||||
Portion get portion => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "quantity")
|
||||
@JsonKey(name: "quantity", fromJson: _parseQuantity)
|
||||
int get quantity => throw _privateConstructorUsedError;
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
DailyNutrition? get dailyNutrition => throw _privateConstructorUsedError;
|
||||
@@ -1275,9 +1273,8 @@ abstract class $DailyScheduleCopyWith<$Res> {
|
||||
@useResult
|
||||
$Res call(
|
||||
{@JsonKey(name: "name") String name,
|
||||
@JsonKey(name: "uniq", includeToJson: false) String uniq,
|
||||
@JsonKey(name: "portion") Portion portion,
|
||||
@JsonKey(name: "quantity") int quantity,
|
||||
@JsonKey(name: "quantity", fromJson: _parseQuantity) int quantity,
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
DailyNutrition? dailyNutrition,
|
||||
@JsonKey(
|
||||
@@ -1306,7 +1303,6 @@ class _$DailyScheduleCopyWithImpl<$Res, $Val extends DailySchedule>
|
||||
@override
|
||||
$Res call({
|
||||
Object? name = null,
|
||||
Object? uniq = null,
|
||||
Object? portion = null,
|
||||
Object? quantity = null,
|
||||
Object? dailyNutrition = freezed,
|
||||
@@ -1317,10 +1313,6 @@ class _$DailyScheduleCopyWithImpl<$Res, $Val extends DailySchedule>
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
uniq: null == uniq
|
||||
? _value.uniq
|
||||
: uniq // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
portion: null == portion
|
||||
? _value.portion
|
||||
: portion // ignore: cast_nullable_to_non_nullable
|
||||
@@ -1365,9 +1357,8 @@ abstract class _$$DailyScheduleImplCopyWith<$Res>
|
||||
@useResult
|
||||
$Res call(
|
||||
{@JsonKey(name: "name") String name,
|
||||
@JsonKey(name: "uniq", includeToJson: false) String uniq,
|
||||
@JsonKey(name: "portion") Portion portion,
|
||||
@JsonKey(name: "quantity") int quantity,
|
||||
@JsonKey(name: "quantity", fromJson: _parseQuantity) int quantity,
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
DailyNutrition? dailyNutrition,
|
||||
@JsonKey(
|
||||
@@ -1395,7 +1386,6 @@ class __$$DailyScheduleImplCopyWithImpl<$Res>
|
||||
@override
|
||||
$Res call({
|
||||
Object? name = null,
|
||||
Object? uniq = null,
|
||||
Object? portion = null,
|
||||
Object? quantity = null,
|
||||
Object? dailyNutrition = freezed,
|
||||
@@ -1406,10 +1396,6 @@ class __$$DailyScheduleImplCopyWithImpl<$Res>
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
uniq: null == uniq
|
||||
? _value.uniq
|
||||
: uniq // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
portion: null == portion
|
||||
? _value.portion
|
||||
: portion // ignore: cast_nullable_to_non_nullable
|
||||
@@ -1435,9 +1421,9 @@ class __$$DailyScheduleImplCopyWithImpl<$Res>
|
||||
class _$DailyScheduleImpl implements _DailySchedule {
|
||||
const _$DailyScheduleImpl(
|
||||
{@JsonKey(name: "name") required this.name,
|
||||
@JsonKey(name: "uniq", includeToJson: false) required this.uniq,
|
||||
@JsonKey(name: "portion") required this.portion,
|
||||
@JsonKey(name: "quantity") required this.quantity,
|
||||
@JsonKey(name: "quantity", fromJson: _parseQuantity)
|
||||
required this.quantity,
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
this.dailyNutrition,
|
||||
@JsonKey(
|
||||
@@ -1454,13 +1440,10 @@ class _$DailyScheduleImpl implements _DailySchedule {
|
||||
@JsonKey(name: "name")
|
||||
final String name;
|
||||
@override
|
||||
@JsonKey(name: "uniq", includeToJson: false)
|
||||
final String uniq;
|
||||
@override
|
||||
@JsonKey(name: "portion")
|
||||
final Portion portion;
|
||||
@override
|
||||
@JsonKey(name: "quantity")
|
||||
@JsonKey(name: "quantity", fromJson: _parseQuantity)
|
||||
final int quantity;
|
||||
@override
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
@@ -1475,7 +1458,7 @@ class _$DailyScheduleImpl implements _DailySchedule {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DailySchedule(name: $name, uniq: $uniq, portion: $portion, quantity: $quantity, dailyNutrition: $dailyNutrition, productNutritionalValueId: $productNutritionalValueId)';
|
||||
return 'DailySchedule(name: $name, portion: $portion, quantity: $quantity, dailyNutrition: $dailyNutrition, productNutritionalValueId: $productNutritionalValueId)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1484,7 +1467,6 @@ class _$DailyScheduleImpl implements _DailySchedule {
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$DailyScheduleImpl &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
(identical(other.uniq, uniq) || other.uniq == uniq) &&
|
||||
(identical(other.portion, portion) || other.portion == portion) &&
|
||||
(identical(other.quantity, quantity) ||
|
||||
other.quantity == quantity) &&
|
||||
@@ -1497,7 +1479,7 @@ class _$DailyScheduleImpl implements _DailySchedule {
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, name, uniq, portion, quantity,
|
||||
int get hashCode => Object.hash(runtimeType, name, portion, quantity,
|
||||
dailyNutrition, productNutritionalValueId);
|
||||
|
||||
/// Create a copy of DailySchedule
|
||||
@@ -1519,9 +1501,9 @@ class _$DailyScheduleImpl implements _DailySchedule {
|
||||
abstract class _DailySchedule implements DailySchedule {
|
||||
const factory _DailySchedule(
|
||||
{@JsonKey(name: "name") required final String name,
|
||||
@JsonKey(name: "uniq", includeToJson: false) required final String uniq,
|
||||
@JsonKey(name: "portion") required final Portion portion,
|
||||
@JsonKey(name: "quantity") required final int quantity,
|
||||
@JsonKey(name: "quantity", fromJson: _parseQuantity)
|
||||
required final int quantity,
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
final DailyNutrition? dailyNutrition,
|
||||
@JsonKey(
|
||||
@@ -1538,13 +1520,10 @@ abstract class _DailySchedule implements DailySchedule {
|
||||
@JsonKey(name: "name")
|
||||
String get name;
|
||||
@override
|
||||
@JsonKey(name: "uniq", includeToJson: false)
|
||||
String get uniq;
|
||||
@override
|
||||
@JsonKey(name: "portion")
|
||||
Portion get portion;
|
||||
@override
|
||||
@JsonKey(name: "quantity")
|
||||
@JsonKey(name: "quantity", fromJson: _parseQuantity)
|
||||
int get quantity;
|
||||
@override
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
|
||||
@@ -29,16 +29,21 @@ _$DietImpl _$$DietImplFromJson(Map<String, dynamic> json) => _$DietImpl(
|
||||
name: json['name'] as String,
|
||||
image: json['image'] as String,
|
||||
duration: (json['duration'] as num).toInt(),
|
||||
state: json['state'] as String,
|
||||
state: $enumDecode(_$DietStateEnumMap, json['state']),
|
||||
type: DietType.fromJson(json['type'] as Map<String, dynamic>),
|
||||
details: json['details'] as String,
|
||||
dailySchedule: (json['daily_schedule'] as List<dynamic>)
|
||||
.map((e) => (e as List<dynamic>)
|
||||
.map((e) => (e as List<dynamic>)
|
||||
.map((e) => DailySchedule.fromJson(e as Map<String, dynamic>))
|
||||
.toList())
|
||||
.toList())
|
||||
.toList(),
|
||||
dailySchedule: (json['daily_schedule'] as Map<String, dynamic>).map(
|
||||
(k, e) => MapEntry(
|
||||
int.parse(k),
|
||||
(e as Map<String, dynamic>).map(
|
||||
(k, e) => MapEntry(
|
||||
int.parse(k),
|
||||
(e as Map<String, dynamic>).map(
|
||||
(k, e) => MapEntry(int.parse(k),
|
||||
DailySchedule.fromJson(e as Map<String, dynamic>)),
|
||||
)),
|
||||
)),
|
||||
),
|
||||
author: Author.fromJson(json['author'] as Map<String, dynamic>),
|
||||
totalLikes: (json['total_likes'] as num).toInt(),
|
||||
totalViews: (json['total_views'] as num).toInt(),
|
||||
@@ -56,10 +61,13 @@ Map<String, dynamic> _$$DietImplToJson(_$DietImpl instance) =>
|
||||
'name': instance.name,
|
||||
'image': instance.image,
|
||||
'duration': instance.duration,
|
||||
'state': instance.state,
|
||||
'state': _$DietStateEnumMap[instance.state]!,
|
||||
'type': instance.type,
|
||||
'details': instance.details,
|
||||
'daily_schedule': instance.dailySchedule,
|
||||
'daily_schedule': instance.dailySchedule.map((k, e) => MapEntry(
|
||||
k.toString(),
|
||||
e.map((k, e) => MapEntry(
|
||||
k.toString(), e.map((k, e) => MapEntry(k.toString(), e)))))),
|
||||
'author': instance.author,
|
||||
'total_likes': instance.totalLikes,
|
||||
'total_views': instance.totalViews,
|
||||
@@ -69,6 +77,12 @@ Map<String, dynamic> _$$DietImplToJson(_$DietImpl instance) =>
|
||||
'daily_nutrition': instance.dailyNutrition,
|
||||
};
|
||||
|
||||
const _$DietStateEnumMap = {
|
||||
DietState.active: 'active',
|
||||
DietState.draft: 'draft',
|
||||
DietState.personal: 'personal',
|
||||
};
|
||||
|
||||
_$CreatedDietImpl _$$CreatedDietImplFromJson(Map<String, dynamic> json) =>
|
||||
_$CreatedDietImpl(
|
||||
id: (json['id'] as num).toInt(),
|
||||
@@ -101,9 +115,8 @@ Map<String, dynamic> _$$AuthorImplToJson(_$AuthorImpl instance) =>
|
||||
_$DailyScheduleImpl _$$DailyScheduleImplFromJson(Map<String, dynamic> json) =>
|
||||
_$DailyScheduleImpl(
|
||||
name: json['name'] as String,
|
||||
uniq: json['uniq'] as String,
|
||||
portion: $enumDecode(_$PortionEnumMap, json['portion']),
|
||||
quantity: (json['quantity'] as num).toInt(),
|
||||
quantity: _parseQuantity(json['quantity']),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$DailyScheduleImplToJson(_$DailyScheduleImpl instance) =>
|
||||
@@ -116,7 +129,7 @@ Map<String, dynamic> _$$DailyScheduleImplToJson(_$DailyScheduleImpl instance) =>
|
||||
};
|
||||
|
||||
const _$PortionEnumMap = {
|
||||
Portion.GRAMS: 'grams',
|
||||
Portion.GRAMS: 'gr',
|
||||
Portion.MILLILITERS: 'milliliters',
|
||||
Portion.PCS: 'pcs',
|
||||
};
|
||||
|
||||
@@ -11,14 +11,14 @@ class Recipes with _$Recipes {
|
||||
required String image,
|
||||
required String state,
|
||||
@JsonKey(name: 'cooking_time') required int cookingTime,
|
||||
required List<Ingredient> ingredients,
|
||||
required List<Ingredient>? ingredients,
|
||||
required String description,
|
||||
required List<Step> steps,
|
||||
@JsonKey(name: 'video_url') required String videoUrl,
|
||||
required List<Step>? steps,
|
||||
@JsonKey(name: 'video_url') required String? videoUrl,
|
||||
@JsonKey(name: 'created_at') required DateTime createdAt,
|
||||
@JsonKey(name: 'updated_at') required DateTime updatedAt,
|
||||
required String title,
|
||||
required String type,
|
||||
required String? title,
|
||||
required String? type,
|
||||
required String slug,
|
||||
@JsonKey(name: 'total_comments') required int commentsCount,
|
||||
@JsonKey(name: 'was_liked_by_me') required bool wasLikedByMe,
|
||||
|
||||
@@ -25,17 +25,17 @@ mixin _$Recipes {
|
||||
String get state => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'cooking_time')
|
||||
int get cookingTime => throw _privateConstructorUsedError;
|
||||
List<Ingredient> get ingredients => throw _privateConstructorUsedError;
|
||||
List<Ingredient>? get ingredients => throw _privateConstructorUsedError;
|
||||
String get description => throw _privateConstructorUsedError;
|
||||
List<Step> get steps => throw _privateConstructorUsedError;
|
||||
List<Step>? get steps => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'video_url')
|
||||
String get videoUrl => throw _privateConstructorUsedError;
|
||||
String? get videoUrl => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'created_at')
|
||||
DateTime get createdAt => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'updated_at')
|
||||
DateTime get updatedAt => throw _privateConstructorUsedError;
|
||||
String get title => throw _privateConstructorUsedError;
|
||||
String get type => throw _privateConstructorUsedError;
|
||||
String? get title => throw _privateConstructorUsedError;
|
||||
String? get type => throw _privateConstructorUsedError;
|
||||
String get slug => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'total_comments')
|
||||
int get commentsCount => throw _privateConstructorUsedError;
|
||||
@@ -68,14 +68,14 @@ abstract class $RecipesCopyWith<$Res> {
|
||||
String image,
|
||||
String state,
|
||||
@JsonKey(name: 'cooking_time') int cookingTime,
|
||||
List<Ingredient> ingredients,
|
||||
List<Ingredient>? ingredients,
|
||||
String description,
|
||||
List<Step> steps,
|
||||
@JsonKey(name: 'video_url') String videoUrl,
|
||||
List<Step>? steps,
|
||||
@JsonKey(name: 'video_url') String? videoUrl,
|
||||
@JsonKey(name: 'created_at') DateTime createdAt,
|
||||
@JsonKey(name: 'updated_at') DateTime updatedAt,
|
||||
String title,
|
||||
String type,
|
||||
String? title,
|
||||
String? type,
|
||||
String slug,
|
||||
@JsonKey(name: 'total_comments') int commentsCount,
|
||||
@JsonKey(name: 'was_liked_by_me') bool wasLikedByMe,
|
||||
@@ -106,14 +106,14 @@ class _$RecipesCopyWithImpl<$Res, $Val extends Recipes>
|
||||
Object? image = null,
|
||||
Object? state = null,
|
||||
Object? cookingTime = null,
|
||||
Object? ingredients = null,
|
||||
Object? ingredients = freezed,
|
||||
Object? description = null,
|
||||
Object? steps = null,
|
||||
Object? videoUrl = null,
|
||||
Object? steps = freezed,
|
||||
Object? videoUrl = freezed,
|
||||
Object? createdAt = null,
|
||||
Object? updatedAt = null,
|
||||
Object? title = null,
|
||||
Object? type = null,
|
||||
Object? title = freezed,
|
||||
Object? type = freezed,
|
||||
Object? slug = null,
|
||||
Object? commentsCount = null,
|
||||
Object? wasLikedByMe = null,
|
||||
@@ -139,22 +139,22 @@ class _$RecipesCopyWithImpl<$Res, $Val extends Recipes>
|
||||
? _value.cookingTime
|
||||
: cookingTime // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
ingredients: null == ingredients
|
||||
ingredients: freezed == ingredients
|
||||
? _value.ingredients
|
||||
: ingredients // ignore: cast_nullable_to_non_nullable
|
||||
as List<Ingredient>,
|
||||
as List<Ingredient>?,
|
||||
description: null == description
|
||||
? _value.description
|
||||
: description // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
steps: null == steps
|
||||
steps: freezed == steps
|
||||
? _value.steps
|
||||
: steps // ignore: cast_nullable_to_non_nullable
|
||||
as List<Step>,
|
||||
videoUrl: null == videoUrl
|
||||
as List<Step>?,
|
||||
videoUrl: freezed == videoUrl
|
||||
? _value.videoUrl
|
||||
: videoUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as String?,
|
||||
createdAt: null == createdAt
|
||||
? _value.createdAt
|
||||
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||
@@ -163,14 +163,14 @@ class _$RecipesCopyWithImpl<$Res, $Val extends Recipes>
|
||||
? _value.updatedAt
|
||||
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,
|
||||
title: null == title
|
||||
title: freezed == title
|
||||
? _value.title
|
||||
: title // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
type: null == type
|
||||
as String?,
|
||||
type: freezed == type
|
||||
? _value.type
|
||||
: type // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as String?,
|
||||
slug: null == slug
|
||||
? _value.slug
|
||||
: slug // ignore: cast_nullable_to_non_nullable
|
||||
@@ -225,14 +225,14 @@ abstract class _$$RecipesImplCopyWith<$Res> implements $RecipesCopyWith<$Res> {
|
||||
String image,
|
||||
String state,
|
||||
@JsonKey(name: 'cooking_time') int cookingTime,
|
||||
List<Ingredient> ingredients,
|
||||
List<Ingredient>? ingredients,
|
||||
String description,
|
||||
List<Step> steps,
|
||||
@JsonKey(name: 'video_url') String videoUrl,
|
||||
List<Step>? steps,
|
||||
@JsonKey(name: 'video_url') String? videoUrl,
|
||||
@JsonKey(name: 'created_at') DateTime createdAt,
|
||||
@JsonKey(name: 'updated_at') DateTime updatedAt,
|
||||
String title,
|
||||
String type,
|
||||
String? title,
|
||||
String? type,
|
||||
String slug,
|
||||
@JsonKey(name: 'total_comments') int commentsCount,
|
||||
@JsonKey(name: 'was_liked_by_me') bool wasLikedByMe,
|
||||
@@ -262,14 +262,14 @@ class __$$RecipesImplCopyWithImpl<$Res>
|
||||
Object? image = null,
|
||||
Object? state = null,
|
||||
Object? cookingTime = null,
|
||||
Object? ingredients = null,
|
||||
Object? ingredients = freezed,
|
||||
Object? description = null,
|
||||
Object? steps = null,
|
||||
Object? videoUrl = null,
|
||||
Object? steps = freezed,
|
||||
Object? videoUrl = freezed,
|
||||
Object? createdAt = null,
|
||||
Object? updatedAt = null,
|
||||
Object? title = null,
|
||||
Object? type = null,
|
||||
Object? title = freezed,
|
||||
Object? type = freezed,
|
||||
Object? slug = null,
|
||||
Object? commentsCount = null,
|
||||
Object? wasLikedByMe = null,
|
||||
@@ -295,22 +295,22 @@ class __$$RecipesImplCopyWithImpl<$Res>
|
||||
? _value.cookingTime
|
||||
: cookingTime // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
ingredients: null == ingredients
|
||||
ingredients: freezed == ingredients
|
||||
? _value._ingredients
|
||||
: ingredients // ignore: cast_nullable_to_non_nullable
|
||||
as List<Ingredient>,
|
||||
as List<Ingredient>?,
|
||||
description: null == description
|
||||
? _value.description
|
||||
: description // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
steps: null == steps
|
||||
steps: freezed == steps
|
||||
? _value._steps
|
||||
: steps // ignore: cast_nullable_to_non_nullable
|
||||
as List<Step>,
|
||||
videoUrl: null == videoUrl
|
||||
as List<Step>?,
|
||||
videoUrl: freezed == videoUrl
|
||||
? _value.videoUrl
|
||||
: videoUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as String?,
|
||||
createdAt: null == createdAt
|
||||
? _value.createdAt
|
||||
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||
@@ -319,14 +319,14 @@ class __$$RecipesImplCopyWithImpl<$Res>
|
||||
? _value.updatedAt
|
||||
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,
|
||||
title: null == title
|
||||
title: freezed == title
|
||||
? _value.title
|
||||
: title // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
type: null == type
|
||||
as String?,
|
||||
type: freezed == type
|
||||
? _value.type
|
||||
: type // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as String?,
|
||||
slug: null == slug
|
||||
? _value.slug
|
||||
: slug // ignore: cast_nullable_to_non_nullable
|
||||
@@ -367,9 +367,9 @@ class _$RecipesImpl implements _Recipes {
|
||||
required this.image,
|
||||
required this.state,
|
||||
@JsonKey(name: 'cooking_time') required this.cookingTime,
|
||||
required final List<Ingredient> ingredients,
|
||||
required final List<Ingredient>? ingredients,
|
||||
required this.description,
|
||||
required final List<Step> steps,
|
||||
required final List<Step>? steps,
|
||||
@JsonKey(name: 'video_url') required this.videoUrl,
|
||||
@JsonKey(name: 'created_at') required this.createdAt,
|
||||
@JsonKey(name: 'updated_at') required this.updatedAt,
|
||||
@@ -397,27 +397,31 @@ class _$RecipesImpl implements _Recipes {
|
||||
@override
|
||||
@JsonKey(name: 'cooking_time')
|
||||
final int cookingTime;
|
||||
final List<Ingredient> _ingredients;
|
||||
final List<Ingredient>? _ingredients;
|
||||
@override
|
||||
List<Ingredient> get ingredients {
|
||||
List<Ingredient>? get ingredients {
|
||||
final value = _ingredients;
|
||||
if (value == null) return null;
|
||||
if (_ingredients is EqualUnmodifiableListView) return _ingredients;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_ingredients);
|
||||
return EqualUnmodifiableListView(value);
|
||||
}
|
||||
|
||||
@override
|
||||
final String description;
|
||||
final List<Step> _steps;
|
||||
final List<Step>? _steps;
|
||||
@override
|
||||
List<Step> get steps {
|
||||
List<Step>? get steps {
|
||||
final value = _steps;
|
||||
if (value == null) return null;
|
||||
if (_steps is EqualUnmodifiableListView) return _steps;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_steps);
|
||||
return EqualUnmodifiableListView(value);
|
||||
}
|
||||
|
||||
@override
|
||||
@JsonKey(name: 'video_url')
|
||||
final String videoUrl;
|
||||
final String? videoUrl;
|
||||
@override
|
||||
@JsonKey(name: 'created_at')
|
||||
final DateTime createdAt;
|
||||
@@ -425,9 +429,9 @@ class _$RecipesImpl implements _Recipes {
|
||||
@JsonKey(name: 'updated_at')
|
||||
final DateTime updatedAt;
|
||||
@override
|
||||
final String title;
|
||||
final String? title;
|
||||
@override
|
||||
final String type;
|
||||
final String? type;
|
||||
@override
|
||||
final String slug;
|
||||
@override
|
||||
@@ -537,14 +541,14 @@ abstract class _Recipes implements Recipes {
|
||||
required final String image,
|
||||
required final String state,
|
||||
@JsonKey(name: 'cooking_time') required final int cookingTime,
|
||||
required final List<Ingredient> ingredients,
|
||||
required final List<Ingredient>? ingredients,
|
||||
required final String description,
|
||||
required final List<Step> steps,
|
||||
@JsonKey(name: 'video_url') required final String videoUrl,
|
||||
required final List<Step>? steps,
|
||||
@JsonKey(name: 'video_url') required final String? videoUrl,
|
||||
@JsonKey(name: 'created_at') required final DateTime createdAt,
|
||||
@JsonKey(name: 'updated_at') required final DateTime updatedAt,
|
||||
required final String title,
|
||||
required final String type,
|
||||
required final String? title,
|
||||
required final String? type,
|
||||
required final String slug,
|
||||
@JsonKey(name: 'total_comments') required final int commentsCount,
|
||||
@JsonKey(name: 'was_liked_by_me') required final bool wasLikedByMe,
|
||||
@@ -565,14 +569,14 @@ abstract class _Recipes implements Recipes {
|
||||
@JsonKey(name: 'cooking_time')
|
||||
int get cookingTime;
|
||||
@override
|
||||
List<Ingredient> get ingredients;
|
||||
List<Ingredient>? get ingredients;
|
||||
@override
|
||||
String get description;
|
||||
@override
|
||||
List<Step> get steps;
|
||||
List<Step>? get steps;
|
||||
@override
|
||||
@JsonKey(name: 'video_url')
|
||||
String get videoUrl;
|
||||
String? get videoUrl;
|
||||
@override
|
||||
@JsonKey(name: 'created_at')
|
||||
DateTime get createdAt;
|
||||
@@ -580,9 +584,9 @@ abstract class _Recipes implements Recipes {
|
||||
@JsonKey(name: 'updated_at')
|
||||
DateTime get updatedAt;
|
||||
@override
|
||||
String get title;
|
||||
String? get title;
|
||||
@override
|
||||
String get type;
|
||||
String? get type;
|
||||
@override
|
||||
String get slug;
|
||||
@override
|
||||
|
||||
@@ -12,18 +12,18 @@ _$RecipesImpl _$$RecipesImplFromJson(Map<String, dynamic> json) =>
|
||||
image: json['image'] as String,
|
||||
state: json['state'] as String,
|
||||
cookingTime: (json['cooking_time'] as num).toInt(),
|
||||
ingredients: (json['ingredients'] as List<dynamic>)
|
||||
.map((e) => Ingredient.fromJson(e as Map<String, dynamic>))
|
||||
ingredients: (json['ingredients'] as List<dynamic>?)
|
||||
?.map((e) => Ingredient.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
description: json['description'] as String,
|
||||
steps: (json['steps'] as List<dynamic>)
|
||||
.map((e) => Step.fromJson(e as Map<String, dynamic>))
|
||||
steps: (json['steps'] as List<dynamic>?)
|
||||
?.map((e) => Step.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
videoUrl: json['video_url'] as String,
|
||||
videoUrl: json['video_url'] as String?,
|
||||
createdAt: DateTime.parse(json['created_at'] as String),
|
||||
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||
title: json['title'] as String,
|
||||
type: json['type'] as String,
|
||||
title: json['title'] as String?,
|
||||
type: json['type'] as String?,
|
||||
slug: json['slug'] as String,
|
||||
commentsCount: (json['total_comments'] as num).toInt(),
|
||||
wasLikedByMe: json['was_liked_by_me'] as bool,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/consultants_cubit/consultants_state.dart';
|
||||
import 'package:eatvio_app/features/feed/repositories/feed_repository.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -18,7 +20,8 @@ class ConsultantsCubit extends Cubit<ConsultantsState> {
|
||||
isLoading: false,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
} catch (e, st) {
|
||||
log(e.toString(), stackTrace: st);
|
||||
emit(ConsultantsState.error(errorMessage: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,9 +107,10 @@ class DietsCubit extends Cubit<DietsState> {
|
||||
errorMessage: null,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
} catch (e, st) {
|
||||
log(e.toString(), stackTrace: st);
|
||||
emit(
|
||||
state.copyWith(errorMessage: e.toString()),
|
||||
state.copyWith(isLoading: false),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -143,14 +144,13 @@ class DietsCubit extends Cubit<DietsState> {
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
emit(state.copyWith(errorMessage: e.toString()));
|
||||
emit(state.copyWith(isLoading: false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> likeDiet(int id, int index) async {
|
||||
final currentDiets = state.dietsList;
|
||||
// if (currentDiets.isEmpty) return;
|
||||
final currentDiets = state.dietsList;
|
||||
final dietsForModification = [...currentDiets];
|
||||
final currentDiet = currentDiets[index];
|
||||
if (currentDiet.wasLikedByMe) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:eatvio_app/features/feed/repositories/feed_repository.dart';
|
||||
import 'package:eatvio_app/features/recipes/models/recipe_tag_model.dart';
|
||||
import 'package:eatvio_app/features/recipes/reporitories/recipe_repository.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'dart:developer';
|
||||
|
||||
class RecipesCubit extends Cubit<RecipesState> {
|
||||
final FeedRepository feedRepository;
|
||||
@@ -38,7 +39,8 @@ class RecipesCubit extends Cubit<RecipesState> {
|
||||
),
|
||||
);
|
||||
lastPage = currentPage;
|
||||
} catch (e) {
|
||||
} catch (e, st) {
|
||||
log(e.toString(), stackTrace: st);
|
||||
emit(RecipesState.error(errorMessage: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:easy_debounce/easy_debounce.dart';
|
||||
import 'package:eatvio_app/core/widgets/add_item_button.dart';
|
||||
import 'package:eatvio_app/core/widgets/bottom_sheet_with_blur.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_search_bar.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart';
|
||||
import 'package:eatvio_app/features/create_plan/presentation/create_plan_screen.dart';
|
||||
@@ -14,6 +15,7 @@ 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';
|
||||
import 'package:eatvio_app/features/feed/presentation/widgets/success_published_diet_bottom_sheet.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/widgets/tags_modal_widget.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_cubit.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
@@ -25,7 +27,6 @@ import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../widgets/recipes.dart';
|
||||
|
||||
|
||||
class FeedScreen extends StatelessWidget {
|
||||
const FeedScreen({super.key});
|
||||
|
||||
@@ -260,10 +261,23 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
|
||||
onTap: () => GoRouter.of(context).pushNamed(RecipeCreateScreen.name),
|
||||
),
|
||||
2 => AddItemButton(
|
||||
onTap: state.profile!.roles.contains('consultant') ? () => GoRouter.of(context).pushNamed(CreateDietScreen.name) : null,
|
||||
onTap: state.profile?.roles.contains('consultant') ?? false
|
||||
? () async {
|
||||
final result = await GoRouter.of(context).pushNamed<String?>(CreateDietScreen.name);
|
||||
if (result != null) {
|
||||
showBottomSheetWithBlur(
|
||||
userRootNavigator: true,
|
||||
context: context,
|
||||
child: SuccessPublishedDietBottomSheet(
|
||||
title: result,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
: null,
|
||||
),
|
||||
3 => AddItemButton(
|
||||
onTap: state.profile!.roles.contains('consultant') ? () => GoRouter.of(context).pushNamed(CreatePlanScreen.name) : null,
|
||||
onTap: state.profile?.roles.contains('consultant') ?? false ? () => GoRouter.of(context).pushNamed(CreatePlanScreen.name) : null,
|
||||
),
|
||||
_ => AddItemButton()
|
||||
},
|
||||
|
||||
@@ -63,4 +63,4 @@ class Diets extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ class _RecipesWidgetState extends State<RecipesWidget> {
|
||||
children: [
|
||||
_iconButton('assets/icons/clock.svg', widget.recipes.cookingTime.toString() + ' минут'),
|
||||
SizedBox(width: 8),
|
||||
_iconButton('assets/icons/steps.svg', widget.recipes.steps.length.toString() + ' этапов'),
|
||||
_iconButton('assets/icons/steps.svg', '${widget.recipes.steps?.length.toString()}' + ' этапов'),
|
||||
SizedBox(width: 8),
|
||||
_iconButton('assets/icons/calories.svg', widget.recipes.calories.toStringAsFixed(0) + ' ккал'),
|
||||
],
|
||||
@@ -87,7 +87,7 @@ class _RecipesWidgetState extends State<RecipesWidget> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Text(
|
||||
widget.recipes.title,
|
||||
widget.recipes.title ?? '',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black87,
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
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/bottom_sheet_handler_bar.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class SuccessPublishedDietBottomSheet extends StatelessWidget {
|
||||
const SuccessPublishedDietBottomSheet({super.key, required this.title});
|
||||
final String title;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
BottomSheetHandlerBar(),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
Text(
|
||||
'опубликованна',
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: CustomOutlinedButton(
|
||||
onPressed: () {
|
||||
GoRouter.of(context).pop();
|
||||
},
|
||||
text: 'Хорошо',
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textSSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:easy_debounce/easy_debounce.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';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
class InternetConnectionCheckerScreen extends StatefulWidget {
|
||||
const InternetConnectionCheckerScreen({super.key, required this.child});
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
State<InternetConnectionCheckerScreen> createState() => _InternetConnectionCheckerScreenState();
|
||||
}
|
||||
|
||||
class _InternetConnectionCheckerScreenState extends State<InternetConnectionCheckerScreen> {
|
||||
bool _hasInternet = true;
|
||||
final Connectivity _connectivity = Connectivity();
|
||||
late StreamSubscription<List<ConnectivityResult>> _connectivitySubscription;
|
||||
late final AppLifecycleListener _listener;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_checkConnection();
|
||||
_connectivitySubscription = _connectivity.onConnectivityChanged.listen(_updateAccessStatus);
|
||||
_listener = AppLifecycleListener(
|
||||
onResume: () async {
|
||||
_connectivitySubscription.resume();
|
||||
await _checkConnection();
|
||||
},
|
||||
onHide: _connectivitySubscription.pause,
|
||||
onPause: _connectivitySubscription.pause,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _checkConnection() async {
|
||||
late List<ConnectivityResult> result;
|
||||
|
||||
try {
|
||||
result = await _connectivity.checkConnectivity();
|
||||
} on PlatformException catch (e) {
|
||||
log('Couldn\'t check connectivity status', error: e);
|
||||
return;
|
||||
}
|
||||
|
||||
return _updateAccessStatus(result);
|
||||
}
|
||||
|
||||
Future<void> _updateAccessStatus(List<ConnectivityResult> result) async {
|
||||
final hasInternet = await _checkInternetAccess(result);
|
||||
|
||||
setState(() {
|
||||
_hasInternet = hasInternet;
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> _checkInternetAccess(List<ConnectivityResult> result) async {
|
||||
if (result.contains(ConnectivityResult.none)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
final response = await Future.any([
|
||||
http.get(Uri.parse('https://google.com')),
|
||||
http.get(Uri.parse('https://yandex.ru')),
|
||||
http.get(Uri.parse('https://dev2.eatvio.com')),
|
||||
http.get(Uri.parse('https://eatvio.ru')),
|
||||
]).timeout(Duration(seconds: 5));
|
||||
|
||||
return response.statusCode >= 100 && response.statusCode < 600;
|
||||
} on SocketException catch (_) {
|
||||
return false;
|
||||
} on TimeoutException catch (_) {
|
||||
return false;
|
||||
} on HandshakeException catch (_) {
|
||||
return false;
|
||||
} on CertificateException catch (_) {
|
||||
return false;
|
||||
} on http.ClientException catch (_) {
|
||||
return false;
|
||||
} catch (e, st) {
|
||||
log(e.toString(), stackTrace: st);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_connectivitySubscription.cancel();
|
||||
_listener.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
widget.child,
|
||||
if (!_hasInternet)
|
||||
NoInternetScreen(
|
||||
onTryAgain: () async {
|
||||
EasyDebounce.debounce(
|
||||
'internet connection check',
|
||||
Duration(seconds: 2),
|
||||
() async {
|
||||
await _checkConnection();
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NoInternetScreen extends StatelessWidget {
|
||||
const NoInternetScreen({super.key, required this.onTryAgain});
|
||||
final Future<void> Function() onTryAgain;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Column(
|
||||
children: [
|
||||
Spacer(),
|
||||
SizedBox(height: 28),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 52),
|
||||
child: Text(
|
||||
'Отсутствует подключение к интернету',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyles.headingXLSemibold.copyWith(
|
||||
color: appTheme.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 40),
|
||||
child: Text(
|
||||
'Проверьте соединение с сетью и попробуйте ещё раз',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyles.textMMedium.copyWith(
|
||||
color: appTheme.gray,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: CustomOutlinedButton(
|
||||
onPressed: onTryAgain,
|
||||
height: 52,
|
||||
text: 'Попробовать ещё раз',
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
side: WidgetStatePropertyAll(BorderSide.none),
|
||||
),
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:easy_debounce/easy_debounce.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/my_diets/presentation/cubit/my_diets_state.dart';
|
||||
import 'package:eatvio_app/features/my_diets/repositories/my_diets_repository.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -28,6 +31,7 @@ class MyDietsCubit extends Cubit<MyDietsState> {
|
||||
'name',
|
||||
'name:desc',
|
||||
];
|
||||
|
||||
static const List<int?> _durationValues = [null, ...durationValues];
|
||||
static const List<String?> _types = [null, ...dietTypeValues];
|
||||
|
||||
@@ -39,7 +43,7 @@ class MyDietsCubit extends Cubit<MyDietsState> {
|
||||
EasyDebounce.debounce(
|
||||
'loadDiets',
|
||||
Duration(seconds: 1),
|
||||
() => loadDiets(
|
||||
() => loadCurrentDiets(
|
||||
query: _activeQuery,
|
||||
),
|
||||
);
|
||||
@@ -48,7 +52,7 @@ class MyDietsCubit extends Cubit<MyDietsState> {
|
||||
EasyDebounce.debounce(
|
||||
'loadDiets',
|
||||
Duration(seconds: 1),
|
||||
() => loadDiets(
|
||||
() => loadCurrentDiets(
|
||||
query: _draftQuery,
|
||||
),
|
||||
);
|
||||
@@ -57,7 +61,7 @@ class MyDietsCubit extends Cubit<MyDietsState> {
|
||||
EasyDebounce.debounce(
|
||||
'loadDiets',
|
||||
Duration(seconds: 1),
|
||||
() => loadDiets(
|
||||
() => loadCurrentDiets(
|
||||
query: _personalQuery,
|
||||
),
|
||||
);
|
||||
@@ -70,7 +74,7 @@ class MyDietsCubit extends Cubit<MyDietsState> {
|
||||
EasyDebounce.debounce(
|
||||
'loadDiets',
|
||||
Duration(seconds: 1),
|
||||
() => loadDiets(
|
||||
() => loadCurrentDiets(
|
||||
query: _activeQuery,
|
||||
),
|
||||
);
|
||||
@@ -79,7 +83,7 @@ class MyDietsCubit extends Cubit<MyDietsState> {
|
||||
EasyDebounce.debounce(
|
||||
'loadDiets',
|
||||
Duration(seconds: 1),
|
||||
() => loadDiets(
|
||||
() => loadCurrentDiets(
|
||||
query: _draftQuery,
|
||||
),
|
||||
);
|
||||
@@ -88,7 +92,7 @@ class MyDietsCubit extends Cubit<MyDietsState> {
|
||||
EasyDebounce.debounce(
|
||||
'loadDiets',
|
||||
Duration(seconds: 1),
|
||||
() => loadDiets(
|
||||
() => loadCurrentDiets(
|
||||
query: _personalQuery,
|
||||
),
|
||||
);
|
||||
@@ -101,7 +105,7 @@ class MyDietsCubit extends Cubit<MyDietsState> {
|
||||
EasyDebounce.debounce(
|
||||
'loadDiets',
|
||||
Duration(seconds: 1),
|
||||
() => loadDiets(
|
||||
() => loadCurrentDiets(
|
||||
query: _activeQuery,
|
||||
),
|
||||
);
|
||||
@@ -110,7 +114,7 @@ class MyDietsCubit extends Cubit<MyDietsState> {
|
||||
EasyDebounce.debounce(
|
||||
'loadDiets',
|
||||
Duration(seconds: 1),
|
||||
() => loadDiets(
|
||||
() => loadCurrentDiets(
|
||||
query: _draftQuery,
|
||||
),
|
||||
);
|
||||
@@ -119,7 +123,7 @@ class MyDietsCubit extends Cubit<MyDietsState> {
|
||||
EasyDebounce.debounce(
|
||||
'loadDiets',
|
||||
Duration(seconds: 1),
|
||||
() => loadDiets(
|
||||
() => loadCurrentDiets(
|
||||
query: _personalQuery,
|
||||
),
|
||||
);
|
||||
@@ -128,9 +132,34 @@ class MyDietsCubit extends Cubit<MyDietsState> {
|
||||
|
||||
void changeSelectedStateIndex(int index) {
|
||||
emit(state.copyWith(selectedStateIndex: index));
|
||||
if (state.activeDiets.isEmpty) {
|
||||
EasyDebounce.debounce(
|
||||
'loadActiveDiets',
|
||||
Duration(seconds: 1),
|
||||
() => loadCurrentDiets(
|
||||
query: _activeQuery,
|
||||
),
|
||||
);
|
||||
} else if (state.draftDiets.isEmpty) {
|
||||
EasyDebounce.debounce(
|
||||
'loadDraftDiets',
|
||||
Duration(seconds: 1),
|
||||
() => loadCurrentDiets(
|
||||
query: _draftQuery,
|
||||
),
|
||||
);
|
||||
} else if (state.personalDiets.isEmpty) {
|
||||
EasyDebounce.debounce(
|
||||
'loadPersonalDiets',
|
||||
Duration(seconds: 1),
|
||||
() => loadCurrentDiets(
|
||||
query: _personalQuery,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadDiets({
|
||||
Future<void> loadCurrentDiets({
|
||||
String? query,
|
||||
}) async {
|
||||
switch (state.selectedStateIndex) {
|
||||
@@ -160,11 +189,13 @@ class MyDietsCubit extends Cubit<MyDietsState> {
|
||||
final type = _types[state.activeFilterState.typeIndex];
|
||||
final duration = _durationValues[state.activeFilterState.durationIndex];
|
||||
final sorting = _sortingValues[state.activeFilterState.sortingIndex];
|
||||
final dietState = DietState.active;
|
||||
final dietsPagination = await myDietsRepository.getDiets(
|
||||
query: query,
|
||||
types: type,
|
||||
duration: duration,
|
||||
sorting: sorting,
|
||||
state: dietState.toString(),
|
||||
);
|
||||
lastActivePage = dietsPagination.meta.lastPage;
|
||||
emit(
|
||||
@@ -174,7 +205,8 @@ class MyDietsCubit extends Cubit<MyDietsState> {
|
||||
error: null,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
} catch (e, st) {
|
||||
log(e.toString(), stackTrace: st);
|
||||
emit(
|
||||
state.copyWith(error: e.toString()),
|
||||
);
|
||||
@@ -187,11 +219,13 @@ class MyDietsCubit extends Cubit<MyDietsState> {
|
||||
final type = _types[state.draftFilterState.typeIndex];
|
||||
final duration = _durationValues[state.draftFilterState.durationIndex];
|
||||
final sorting = _sortingValues[state.draftFilterState.sortingIndex];
|
||||
final dietState = DietState.draft;
|
||||
final dietsPagination = await myDietsRepository.getDiets(
|
||||
query: query,
|
||||
types: type,
|
||||
duration: duration,
|
||||
sorting: sorting,
|
||||
state: dietState.toString(),
|
||||
);
|
||||
lastActivePage = dietsPagination.meta.lastPage;
|
||||
emit(
|
||||
@@ -214,11 +248,13 @@ class MyDietsCubit extends Cubit<MyDietsState> {
|
||||
final type = _types[state.personalFilterState.typeIndex];
|
||||
final duration = _durationValues[state.personalFilterState.durationIndex];
|
||||
final sorting = _sortingValues[state.personalFilterState.sortingIndex];
|
||||
final dietState = DietState.personal;
|
||||
final dietsPagination = await myDietsRepository.getDiets(
|
||||
query: query,
|
||||
types: type,
|
||||
duration: duration,
|
||||
sorting: sorting,
|
||||
state: dietState.toString(),
|
||||
);
|
||||
lastActivePage = dietsPagination.meta.lastPage;
|
||||
emit(
|
||||
|
||||
@@ -33,7 +33,7 @@ class MyDietsScreenContent extends StatelessWidget {
|
||||
final activeDietsScrollController = ScrollController();
|
||||
final draftDietsScrollController = ScrollController();
|
||||
final personalDietsScrollController = ScrollController();
|
||||
|
||||
|
||||
final activeDietsSearchController = SearchController();
|
||||
final draftDietsSearchController = SearchController();
|
||||
final personalDietsSearchController = SearchController();
|
||||
@@ -111,7 +111,7 @@ class MyDietsScreenContent extends StatelessWidget {
|
||||
'searchDiets',
|
||||
Duration(milliseconds: 500),
|
||||
() {
|
||||
context.read<MyDietsCubit>().loadDiets(query: query == '' ? null : query);
|
||||
context.read<MyDietsCubit>().loadCurrentDiets(query: query == '' ? null : query);
|
||||
},
|
||||
);
|
||||
},
|
||||
@@ -133,7 +133,7 @@ class MyDietsScreenContent extends StatelessWidget {
|
||||
];
|
||||
},
|
||||
body: RefreshIndicator(
|
||||
onRefresh: () => context.read<MyDietsCubit>().loadDiets(),
|
||||
onRefresh: () => context.read<MyDietsCubit>().loadCurrentDiets(),
|
||||
child: BlocBuilder<MyDietsCubit, MyDietsState>(
|
||||
builder: (BuildContext context, MyDietsState state) {
|
||||
if (state.isLoading) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart';
|
||||
import 'package:eatvio_app/core/widgets/bottom_sheet_with_blur.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
|
||||
import 'package:eatvio_app/core/widgets/info_row.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/my_diets/presentation/cubit/my_diets_cubit.dart';
|
||||
import 'package:eatvio_app/features/my_diets/presentation/cubit/my_diets_state.dart';
|
||||
@@ -110,6 +111,13 @@ class _MyDietWidgetState extends State<MyDietWidget> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomOutlinedButton(
|
||||
onPressed: () {
|
||||
if (state.selectedStateIndex == 1) {
|
||||
} else if (state.selectedStateIndex == 2) {
|
||||
} else {
|
||||
GoRouter.of(context).pushNamed(CreateDietScreen.name, extra: widget.diet);
|
||||
}
|
||||
},
|
||||
text: switch (state.selectedStateIndex) {
|
||||
1 => s.publish,
|
||||
2 => s.toOffer,
|
||||
@@ -160,6 +168,12 @@ class _MyDietWidgetState extends State<MyDietWidget> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomOutlinedButton(
|
||||
onPressed: () {
|
||||
if (state.selectedStateIndex == 0) {
|
||||
} else {
|
||||
GoRouter.of(context).pushNamed(CreateDietScreen.name, extra: widget.diet);
|
||||
}
|
||||
},
|
||||
text: switch (state.selectedStateIndex) {
|
||||
0 => s.removeFromActive,
|
||||
_ => s.edit,
|
||||
@@ -178,51 +192,49 @@ class _MyDietWidgetState extends State<MyDietWidget> {
|
||||
onPressed: () async {
|
||||
final result = await context.read<MyDietsCubit>().deleteDiet(widget.diet.id);
|
||||
if (result)
|
||||
Future.delayed(Duration(seconds: 3), () {
|
||||
showBottomSheetWithBlur(
|
||||
context: context,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
BottomSheetHandlerBar(),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
'${widget.diet.name}',
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
Text(
|
||||
s.deleted,
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: CustomOutlinedButton(
|
||||
onPressed: () {
|
||||
context.pop();
|
||||
},
|
||||
text: 'Хорошо',
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textSSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
),
|
||||
showBottomSheetWithBlur(
|
||||
context: context,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
BottomSheetHandlerBar(),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
'${widget.diet.name}',
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
Text(
|
||||
s.deleted,
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: CustomOutlinedButton(
|
||||
onPressed: () {
|
||||
context.pop();
|
||||
},
|
||||
text: 'Хорошо',
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textSSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
height: 52,
|
||||
width: 52,
|
||||
|
||||
@@ -20,6 +20,7 @@ class MyDietsRepository {
|
||||
String? types,
|
||||
int? duration,
|
||||
String? sorting,
|
||||
String? state,
|
||||
}) async {
|
||||
try {
|
||||
return (await myDietsService.getDiets(
|
||||
@@ -28,6 +29,7 @@ class MyDietsRepository {
|
||||
types: types,
|
||||
duration: duration,
|
||||
sorting: sorting,
|
||||
state: state,
|
||||
));
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
|
||||
@@ -12,13 +12,14 @@ abstract class MyDietsService {
|
||||
@GET('/api/profile/my_diets')
|
||||
Future<DietsPagination> getDiets({
|
||||
@Query('page') required int page,
|
||||
@Query('filters[name][\$startsWith]') String? query,
|
||||
@Query('filters[name][\$startsWith]') String? query,
|
||||
@Query('filters[state][\$eq]') String? state,
|
||||
@Query('filters[type][\$contains]') String? types,
|
||||
@Query('filters[duration][\$eq]') int? duration,
|
||||
@Query('sort') String? sorting,
|
||||
});
|
||||
|
||||
@POST('/api/diets/delete/{dietId}')
|
||||
@DELETE('/api/diets/delete/{dietId}')
|
||||
Future<void> deleteDiet({
|
||||
@Path() required int dietId,
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ class _MyDietsService implements MyDietsService {
|
||||
Future<DietsPagination> getDiets({
|
||||
required int page,
|
||||
String? query,
|
||||
String? state,
|
||||
String? types,
|
||||
int? duration,
|
||||
String? sorting,
|
||||
@@ -29,6 +30,7 @@ class _MyDietsService implements MyDietsService {
|
||||
final queryParameters = <String, dynamic>{
|
||||
r'page': page,
|
||||
r'filters[name][$startsWith]': query,
|
||||
r'filters[state][$eq]': state,
|
||||
r'filters[type][$contains]': types,
|
||||
r'filters[duration][$eq]': duration,
|
||||
r'sort': sorting,
|
||||
@@ -64,7 +66,7 @@ class _MyDietsService implements MyDietsService {
|
||||
final _headers = <String, dynamic>{};
|
||||
const Map<String, dynamic>? _data = null;
|
||||
final _options = _setStreamType<void>(
|
||||
Options(method: 'POST', headers: _headers, extra: _extra)
|
||||
Options(method: 'DELETE', headers: _headers, extra: _extra)
|
||||
.compose(
|
||||
_dio.options,
|
||||
'/api/diets/delete/${dietId}',
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:eatvio_app/features/plans/repositories/plans_repository.dart';
|
||||
import 'package:eatvio_app/features/profile/presentation/bloc/plans_cubit/plans_state.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -14,7 +16,7 @@ class PlansCubit extends Cubit<PlansState> {
|
||||
try {
|
||||
emit(PlansState.loaded(plansList: await plansRepository.getPlansBySlug(slug)));
|
||||
} catch (e) {
|
||||
print(e);
|
||||
log(e.toString());
|
||||
emit(PlansState.error(errorMessage: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,7 +242,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||
}
|
||||
|
||||
_activePlans(ProfileStateLoaded state) {
|
||||
if (state.profile.roles.contains('consultant')) {
|
||||
if (!state.profile.roles.contains('consultant')) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return BlocBuilder<PlansCubit, PlansState>(
|
||||
@@ -372,7 +372,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||
}
|
||||
|
||||
_diplomasAndCertificates(ProfileStateLoaded state) {
|
||||
if (state.profile.roles.contains('consultant') || state.profile.diplomasAndCertificates == null || state.profile.diplomasAndCertificates!.isEmpty) {
|
||||
if (!state.profile.roles.contains('consultant') || state.profile.diplomasAndCertificates == null || state.profile.diplomasAndCertificates!.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Padding(
|
||||
@@ -456,7 +456,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||
}
|
||||
|
||||
_education(ProfileStateLoaded state) {
|
||||
if (state.profile.roles.contains('consultant') ||
|
||||
if (!state.profile.roles.contains('consultant') ||
|
||||
state.profile.educationResource?.completedCourses == null ||
|
||||
state.profile.educationResource?.completedCourses?.length == 0) {
|
||||
return const SizedBox.shrink();
|
||||
@@ -549,7 +549,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||
}
|
||||
|
||||
_socialLinks(ProfileStateLoaded state) {
|
||||
if (state.profile.roles.contains('consultant')) {
|
||||
if (!state.profile.roles.contains('consultant')) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Padding(
|
||||
|
||||
@@ -84,8 +84,10 @@ class RecipeCreateEntity with _$RecipeCreateEntity {
|
||||
TypeOfRecipe? typeOfRecipe,
|
||||
int? countOfPortions,
|
||||
double? energyInOnePortion,
|
||||
String? totalCookTime, // hh:mm
|
||||
String? activeCookTime, // hh:mm
|
||||
int? totalCookHours,
|
||||
int? totalCookMinutes,
|
||||
int? activeCookHours,
|
||||
int? activeCookMinutes,
|
||||
String? description,
|
||||
File? image,
|
||||
String? videoUrl,
|
||||
@@ -99,21 +101,28 @@ class RecipeCreateEntity with _$RecipeCreateEntity {
|
||||
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)),
|
||||
totalCookingTime: ((totalCookHours ?? 0) * 60) + (totalCookMinutes ?? 0),
|
||||
activeCookTime: ((activeCookHours ?? 0) * 60) + (activeCookMinutes ?? 0),
|
||||
description: description,
|
||||
image: image?.path,
|
||||
video_url: videoUrl,
|
||||
ingredients: ingredients,
|
||||
steps: steps.map((step) => Step(image: step.image?.path ?? '', description: step.description ?? '')).toList(),
|
||||
steps: steps.map((step) {
|
||||
if (step.hasProps()) return Step(image: step.image?.path ?? '', description: step.description ?? '');
|
||||
}).toList(),
|
||||
tags: tags,
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class StepEntity with _$StepEntity {
|
||||
const StepEntity._();
|
||||
const factory StepEntity({
|
||||
String? description,
|
||||
File? image,
|
||||
}) = _StepEntity;
|
||||
|
||||
bool hasProps() {
|
||||
return description != null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,10 @@ mixin _$RecipeCreateEntity {
|
||||
TypeOfRecipe? get typeOfRecipe => throw _privateConstructorUsedError;
|
||||
int? get countOfPortions => throw _privateConstructorUsedError;
|
||||
double? get energyInOnePortion => throw _privateConstructorUsedError;
|
||||
String? get totalCookTime => throw _privateConstructorUsedError; // hh:mm
|
||||
String? get activeCookTime => throw _privateConstructorUsedError; // hh:mm
|
||||
int? get totalCookHours => throw _privateConstructorUsedError;
|
||||
int? get totalCookMinutes => throw _privateConstructorUsedError;
|
||||
int? get activeCookHours => throw _privateConstructorUsedError;
|
||||
int? get activeCookMinutes => throw _privateConstructorUsedError;
|
||||
String? get description => throw _privateConstructorUsedError;
|
||||
File? get image => throw _privateConstructorUsedError;
|
||||
String? get videoUrl => throw _privateConstructorUsedError;
|
||||
@@ -47,8 +49,10 @@ abstract class $RecipeCreateEntityCopyWith<$Res> {
|
||||
TypeOfRecipe? typeOfRecipe,
|
||||
int? countOfPortions,
|
||||
double? energyInOnePortion,
|
||||
String? totalCookTime,
|
||||
String? activeCookTime,
|
||||
int? totalCookHours,
|
||||
int? totalCookMinutes,
|
||||
int? activeCookHours,
|
||||
int? activeCookMinutes,
|
||||
String? description,
|
||||
File? image,
|
||||
String? videoUrl,
|
||||
@@ -76,8 +80,10 @@ class _$RecipeCreateEntityCopyWithImpl<$Res, $Val extends RecipeCreateEntity>
|
||||
Object? typeOfRecipe = freezed,
|
||||
Object? countOfPortions = freezed,
|
||||
Object? energyInOnePortion = freezed,
|
||||
Object? totalCookTime = freezed,
|
||||
Object? activeCookTime = freezed,
|
||||
Object? totalCookHours = freezed,
|
||||
Object? totalCookMinutes = freezed,
|
||||
Object? activeCookHours = freezed,
|
||||
Object? activeCookMinutes = freezed,
|
||||
Object? description = freezed,
|
||||
Object? image = freezed,
|
||||
Object? videoUrl = freezed,
|
||||
@@ -102,14 +108,22 @@ class _$RecipeCreateEntityCopyWithImpl<$Res, $Val extends RecipeCreateEntity>
|
||||
? _value.energyInOnePortion
|
||||
: energyInOnePortion // ignore: cast_nullable_to_non_nullable
|
||||
as double?,
|
||||
totalCookTime: freezed == totalCookTime
|
||||
? _value.totalCookTime
|
||||
: totalCookTime // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
activeCookTime: freezed == activeCookTime
|
||||
? _value.activeCookTime
|
||||
: activeCookTime // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
totalCookHours: freezed == totalCookHours
|
||||
? _value.totalCookHours
|
||||
: totalCookHours // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
totalCookMinutes: freezed == totalCookMinutes
|
||||
? _value.totalCookMinutes
|
||||
: totalCookMinutes // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
activeCookHours: freezed == activeCookHours
|
||||
? _value.activeCookHours
|
||||
: activeCookHours // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
activeCookMinutes: freezed == activeCookMinutes
|
||||
? _value.activeCookMinutes
|
||||
: activeCookMinutes // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
description: freezed == description
|
||||
? _value.description
|
||||
: description // ignore: cast_nullable_to_non_nullable
|
||||
@@ -151,8 +165,10 @@ abstract class _$$RecipeCreateEntityImplCopyWith<$Res>
|
||||
TypeOfRecipe? typeOfRecipe,
|
||||
int? countOfPortions,
|
||||
double? energyInOnePortion,
|
||||
String? totalCookTime,
|
||||
String? activeCookTime,
|
||||
int? totalCookHours,
|
||||
int? totalCookMinutes,
|
||||
int? activeCookHours,
|
||||
int? activeCookMinutes,
|
||||
String? description,
|
||||
File? image,
|
||||
String? videoUrl,
|
||||
@@ -178,8 +194,10 @@ class __$$RecipeCreateEntityImplCopyWithImpl<$Res>
|
||||
Object? typeOfRecipe = freezed,
|
||||
Object? countOfPortions = freezed,
|
||||
Object? energyInOnePortion = freezed,
|
||||
Object? totalCookTime = freezed,
|
||||
Object? activeCookTime = freezed,
|
||||
Object? totalCookHours = freezed,
|
||||
Object? totalCookMinutes = freezed,
|
||||
Object? activeCookHours = freezed,
|
||||
Object? activeCookMinutes = freezed,
|
||||
Object? description = freezed,
|
||||
Object? image = freezed,
|
||||
Object? videoUrl = freezed,
|
||||
@@ -204,14 +222,22 @@ class __$$RecipeCreateEntityImplCopyWithImpl<$Res>
|
||||
? _value.energyInOnePortion
|
||||
: energyInOnePortion // ignore: cast_nullable_to_non_nullable
|
||||
as double?,
|
||||
totalCookTime: freezed == totalCookTime
|
||||
? _value.totalCookTime
|
||||
: totalCookTime // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
activeCookTime: freezed == activeCookTime
|
||||
? _value.activeCookTime
|
||||
: activeCookTime // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
totalCookHours: freezed == totalCookHours
|
||||
? _value.totalCookHours
|
||||
: totalCookHours // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
totalCookMinutes: freezed == totalCookMinutes
|
||||
? _value.totalCookMinutes
|
||||
: totalCookMinutes // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
activeCookHours: freezed == activeCookHours
|
||||
? _value.activeCookHours
|
||||
: activeCookHours // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
activeCookMinutes: freezed == activeCookMinutes
|
||||
? _value.activeCookMinutes
|
||||
: activeCookMinutes // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
description: freezed == description
|
||||
? _value.description
|
||||
: description // ignore: cast_nullable_to_non_nullable
|
||||
@@ -248,8 +274,10 @@ class _$RecipeCreateEntityImpl extends _RecipeCreateEntity {
|
||||
this.typeOfRecipe,
|
||||
this.countOfPortions,
|
||||
this.energyInOnePortion,
|
||||
this.totalCookTime,
|
||||
this.activeCookTime,
|
||||
this.totalCookHours,
|
||||
this.totalCookMinutes,
|
||||
this.activeCookHours,
|
||||
this.activeCookMinutes,
|
||||
this.description,
|
||||
this.image,
|
||||
this.videoUrl,
|
||||
@@ -270,11 +298,13 @@ class _$RecipeCreateEntityImpl extends _RecipeCreateEntity {
|
||||
@override
|
||||
final double? energyInOnePortion;
|
||||
@override
|
||||
final String? totalCookTime;
|
||||
// hh:mm
|
||||
final int? totalCookHours;
|
||||
@override
|
||||
final String? activeCookTime;
|
||||
// hh:mm
|
||||
final int? totalCookMinutes;
|
||||
@override
|
||||
final int? activeCookHours;
|
||||
@override
|
||||
final int? activeCookMinutes;
|
||||
@override
|
||||
final String? description;
|
||||
@override
|
||||
@@ -307,7 +337,7 @@ class _$RecipeCreateEntityImpl extends _RecipeCreateEntity {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'RecipeCreateEntity(title: $title, typeOfRecipe: $typeOfRecipe, countOfPortions: $countOfPortions, energyInOnePortion: $energyInOnePortion, totalCookTime: $totalCookTime, activeCookTime: $activeCookTime, description: $description, image: $image, videoUrl: $videoUrl, ingredients: $ingredients, steps: $steps, tags: $tags)';
|
||||
return 'RecipeCreateEntity(title: $title, typeOfRecipe: $typeOfRecipe, countOfPortions: $countOfPortions, energyInOnePortion: $energyInOnePortion, totalCookHours: $totalCookHours, totalCookMinutes: $totalCookMinutes, activeCookHours: $activeCookHours, activeCookMinutes: $activeCookMinutes, description: $description, image: $image, videoUrl: $videoUrl, ingredients: $ingredients, steps: $steps, tags: $tags)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -322,10 +352,14 @@ class _$RecipeCreateEntityImpl extends _RecipeCreateEntity {
|
||||
other.countOfPortions == countOfPortions) &&
|
||||
(identical(other.energyInOnePortion, energyInOnePortion) ||
|
||||
other.energyInOnePortion == energyInOnePortion) &&
|
||||
(identical(other.totalCookTime, totalCookTime) ||
|
||||
other.totalCookTime == totalCookTime) &&
|
||||
(identical(other.activeCookTime, activeCookTime) ||
|
||||
other.activeCookTime == activeCookTime) &&
|
||||
(identical(other.totalCookHours, totalCookHours) ||
|
||||
other.totalCookHours == totalCookHours) &&
|
||||
(identical(other.totalCookMinutes, totalCookMinutes) ||
|
||||
other.totalCookMinutes == totalCookMinutes) &&
|
||||
(identical(other.activeCookHours, activeCookHours) ||
|
||||
other.activeCookHours == activeCookHours) &&
|
||||
(identical(other.activeCookMinutes, activeCookMinutes) ||
|
||||
other.activeCookMinutes == activeCookMinutes) &&
|
||||
(identical(other.description, description) ||
|
||||
other.description == description) &&
|
||||
(identical(other.image, image) || other.image == image) &&
|
||||
@@ -344,8 +378,10 @@ class _$RecipeCreateEntityImpl extends _RecipeCreateEntity {
|
||||
typeOfRecipe,
|
||||
countOfPortions,
|
||||
energyInOnePortion,
|
||||
totalCookTime,
|
||||
activeCookTime,
|
||||
totalCookHours,
|
||||
totalCookMinutes,
|
||||
activeCookHours,
|
||||
activeCookMinutes,
|
||||
description,
|
||||
image,
|
||||
videoUrl,
|
||||
@@ -369,8 +405,10 @@ abstract class _RecipeCreateEntity extends RecipeCreateEntity {
|
||||
final TypeOfRecipe? typeOfRecipe,
|
||||
final int? countOfPortions,
|
||||
final double? energyInOnePortion,
|
||||
final String? totalCookTime,
|
||||
final String? activeCookTime,
|
||||
final int? totalCookHours,
|
||||
final int? totalCookMinutes,
|
||||
final int? activeCookHours,
|
||||
final int? activeCookMinutes,
|
||||
final String? description,
|
||||
final File? image,
|
||||
final String? videoUrl,
|
||||
@@ -388,9 +426,13 @@ abstract class _RecipeCreateEntity extends RecipeCreateEntity {
|
||||
@override
|
||||
double? get energyInOnePortion;
|
||||
@override
|
||||
String? get totalCookTime; // hh:mm
|
||||
int? get totalCookHours;
|
||||
@override
|
||||
String? get activeCookTime; // hh:mm
|
||||
int? get totalCookMinutes;
|
||||
@override
|
||||
int? get activeCookHours;
|
||||
@override
|
||||
int? get activeCookMinutes;
|
||||
@override
|
||||
String? get description;
|
||||
@override
|
||||
@@ -506,8 +548,8 @@ class __$$StepEntityImplCopyWithImpl<$Res>
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$StepEntityImpl implements _StepEntity {
|
||||
const _$StepEntityImpl({this.description, this.image});
|
||||
class _$StepEntityImpl extends _StepEntity {
|
||||
const _$StepEntityImpl({this.description, this.image}) : super._();
|
||||
|
||||
@override
|
||||
final String? description;
|
||||
@@ -541,9 +583,10 @@ class _$StepEntityImpl implements _StepEntity {
|
||||
__$$StepEntityImplCopyWithImpl<_$StepEntityImpl>(this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _StepEntity implements StepEntity {
|
||||
abstract class _StepEntity extends StepEntity {
|
||||
const factory _StepEntity({final String? description, final File? image}) =
|
||||
_$StepEntityImpl;
|
||||
const _StepEntity._() : super._();
|
||||
|
||||
@override
|
||||
String? get description;
|
||||
|
||||
@@ -12,5 +12,5 @@ class Ingredient with _$Ingredient {
|
||||
required String portion,
|
||||
}) = _Ingredient;
|
||||
|
||||
factory Ingredient.fromJson(Map<String, Object?> json) => _$IngredientFromJson(json);
|
||||
factory Ingredient.fromJson(Map<String, Object?> json) => _$IngredientFromJson(json);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:eatvio_app/features/recipes/entities/recipe_create_entity.dart';
|
||||
@@ -11,10 +12,10 @@ class CreateRecipeCubit extends Cubit<CreateRecipeState> {
|
||||
CreateRecipeCubit({required this.recipeRepository})
|
||||
: super(
|
||||
CreateRecipeState(
|
||||
RecipeCreateEntity(
|
||||
entity: RecipeCreateEntity(
|
||||
typeOfRecipe: TypeOfRecipe.first_meals,
|
||||
ingredients: [],
|
||||
steps: [],
|
||||
steps: [StepEntity()],
|
||||
tags: [],
|
||||
),
|
||||
),
|
||||
@@ -22,13 +23,11 @@ class CreateRecipeCubit extends Cubit<CreateRecipeState> {
|
||||
|
||||
addStep() {
|
||||
emit(
|
||||
CreateRecipeState(
|
||||
state.entity.copyWith(
|
||||
steps: [
|
||||
...state.entity.steps,
|
||||
StepEntity(),
|
||||
],
|
||||
),
|
||||
state.copyWith.entity(
|
||||
steps: [
|
||||
...state.entity.steps,
|
||||
StepEntity(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -36,21 +35,16 @@ class CreateRecipeCubit extends Cubit<CreateRecipeState> {
|
||||
removeStep(StepEntity step) {
|
||||
List<StepEntity> steps = [...state.entity.steps];
|
||||
steps.remove(step);
|
||||
|
||||
emit(
|
||||
CreateRecipeState(state.entity.copyWith(steps: steps)),
|
||||
);
|
||||
emit(state.copyWith.entity(steps: steps));
|
||||
}
|
||||
|
||||
addIngredient(Ingredient entity) {
|
||||
emit(
|
||||
CreateRecipeState(
|
||||
state.entity.copyWith(
|
||||
ingredients: [
|
||||
...state.entity.ingredients,
|
||||
entity,
|
||||
],
|
||||
),
|
||||
state.copyWith.entity(
|
||||
ingredients: [
|
||||
...state.entity.ingredients,
|
||||
entity,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -59,67 +53,35 @@ class CreateRecipeCubit extends Cubit<CreateRecipeState> {
|
||||
List<Ingredient> ingredients = [...state.entity.ingredients];
|
||||
ingredients.removeAt(index);
|
||||
|
||||
emit(
|
||||
CreateRecipeState(
|
||||
state.entity.copyWith(
|
||||
ingredients: ingredients,
|
||||
),
|
||||
),
|
||||
);
|
||||
emit(state.copyWith.entity(ingredients: ingredients));
|
||||
}
|
||||
|
||||
replaceIngredientPosition(int oldIndex, int newIndex) {
|
||||
List<Ingredient> ingredients = [...state.entity.ingredients];
|
||||
final item = ingredients.removeAt(oldIndex);
|
||||
ingredients.insert(newIndex, item);
|
||||
emit(
|
||||
CreateRecipeState(
|
||||
state.entity.copyWith(
|
||||
ingredients: ingredients,
|
||||
),
|
||||
),
|
||||
);
|
||||
emit(state.copyWith.entity(ingredients: ingredients));
|
||||
}
|
||||
|
||||
replaceTitle(String title) {
|
||||
emit(
|
||||
CreateRecipeState(
|
||||
state.entity.copyWith(
|
||||
title: title,
|
||||
),
|
||||
),
|
||||
);
|
||||
emit(state.copyWith.entity(title: title == '' ? null : title));
|
||||
}
|
||||
|
||||
replaceDescription(String description) {
|
||||
emit(
|
||||
CreateRecipeState(
|
||||
state.entity.copyWith(
|
||||
description: description,
|
||||
),
|
||||
),
|
||||
);
|
||||
emit(state.copyWith.entity(description: description == '' ? null : description));
|
||||
}
|
||||
|
||||
changeType(TypeOfRecipe typeOfRecipe) {
|
||||
emit(
|
||||
CreateRecipeState(
|
||||
state.entity.copyWith(
|
||||
typeOfRecipe: typeOfRecipe,
|
||||
),
|
||||
),
|
||||
);
|
||||
emit(state.copyWith.entity(typeOfRecipe: typeOfRecipe));
|
||||
}
|
||||
|
||||
addTag(String tag) {
|
||||
emit(
|
||||
CreateRecipeState(
|
||||
state.entity.copyWith(
|
||||
tags: [
|
||||
...state.entity.tags,
|
||||
tag,
|
||||
],
|
||||
),
|
||||
state.copyWith.entity(
|
||||
tags: [
|
||||
...state.entity.tags,
|
||||
tag,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -128,104 +90,153 @@ class CreateRecipeCubit extends Cubit<CreateRecipeState> {
|
||||
List<String> tags = [...state.entity.tags];
|
||||
tags.remove(tag);
|
||||
|
||||
emit(
|
||||
CreateRecipeState(
|
||||
state.entity.copyWith(
|
||||
tags: tags,
|
||||
),
|
||||
),
|
||||
);
|
||||
emit(state.copyWith.entity(tags: tags));
|
||||
}
|
||||
|
||||
replacePortionsCount(int count) {
|
||||
emit(
|
||||
CreateRecipeState(
|
||||
state.entity.copyWith(
|
||||
countOfPortions: count,
|
||||
),
|
||||
),
|
||||
);
|
||||
emit(state.copyWith.entity(countOfPortions: count));
|
||||
}
|
||||
|
||||
replaceEnegryInPortion(double energy) {
|
||||
emit(
|
||||
CreateRecipeState(
|
||||
state.entity.copyWith(
|
||||
energyInOnePortion: energy,
|
||||
),
|
||||
),
|
||||
);
|
||||
emit(state.copyWith.entity(energyInOnePortion: energy));
|
||||
}
|
||||
|
||||
replaceTotalTime(String time) {
|
||||
emit(
|
||||
CreateRecipeState(
|
||||
state.entity.copyWith(
|
||||
totalCookTime: time,
|
||||
),
|
||||
),
|
||||
);
|
||||
setTotalCookingMinutes(int minutes) {
|
||||
emit(state.copyWith.entity(totalCookMinutes: minutes));
|
||||
}
|
||||
|
||||
replaceActiveTime(String time) {
|
||||
emit(
|
||||
CreateRecipeState(
|
||||
state.entity.copyWith(
|
||||
activeCookTime: time,
|
||||
),
|
||||
),
|
||||
);
|
||||
setTotalCookingHours(int hours) {
|
||||
emit(state.copyWith.entity(totalCookHours: hours));
|
||||
}
|
||||
|
||||
setActiveCookingMinutes(int minutes) {
|
||||
emit(state.copyWith.entity(activeCookMinutes: minutes));
|
||||
}
|
||||
|
||||
setActiveCookingHours(int hours) {
|
||||
emit(state.copyWith.entity(activeCookHours: hours));
|
||||
}
|
||||
|
||||
replaceVideoUrl(String videoUrl) {
|
||||
emit(
|
||||
CreateRecipeState(
|
||||
state.entity.copyWith(
|
||||
videoUrl: videoUrl,
|
||||
),
|
||||
),
|
||||
);
|
||||
emit(state.copyWith.entity(videoUrl: videoUrl));
|
||||
}
|
||||
|
||||
replaceImage(File? image) {
|
||||
emit(
|
||||
CreateRecipeState(
|
||||
state.entity.copyWith(
|
||||
image: image,
|
||||
),
|
||||
),
|
||||
);
|
||||
emit(state.copyWith.entity(image: image));
|
||||
}
|
||||
|
||||
replaceDescriptionInStep(String description, int index) {
|
||||
List<StepEntity> steps = [...state.entity.steps];
|
||||
|
||||
steps[index] = steps[index].copyWith(description: description);
|
||||
emit(
|
||||
CreateRecipeState(
|
||||
state.entity.copyWith(
|
||||
steps: steps,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
emit(state.copyWith.entity(steps: steps));
|
||||
}
|
||||
|
||||
replaceImageInStep(File? image, int index) {
|
||||
List<StepEntity> steps = [...state.entity.steps];
|
||||
|
||||
steps[index] = steps[index].copyWith(image: image);
|
||||
emit(
|
||||
CreateRecipeState(
|
||||
state.entity.copyWith(
|
||||
steps: steps,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
emit(state.copyWith.entity(steps: steps));
|
||||
}
|
||||
|
||||
Future<void> publishRecipe() async {
|
||||
Future<bool> publishRecipe() async {
|
||||
try {
|
||||
await recipeRepository.publishRecipe(state.entity.toModel());
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
final model = state.entity.toModel();
|
||||
await recipeRepository.publishRecipe(model);
|
||||
return true;
|
||||
} catch (e, st) {
|
||||
log(e.toString(), stackTrace: st);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool validateAll() {
|
||||
final isTitleValid = validateTitle();
|
||||
final isDescValid = validateDescription();
|
||||
final isImageValid = validateImage();
|
||||
final areServingsValid = validateServings();
|
||||
final isCookTimeValid = validateTotalCookTime();
|
||||
final isActiveCookTimeValid = validateActiveCookTime();
|
||||
final isEnergyPerServingsValid = validateEnergyPerServing();
|
||||
|
||||
return isTitleValid && isDescValid && isImageValid && areServingsValid && isCookTimeValid && isActiveCookTimeValid && isEnergyPerServingsValid;
|
||||
}
|
||||
|
||||
bool validateTitle() {
|
||||
if (state.entity.title == null || (state.entity.title?.isEmpty ?? true)) {
|
||||
emit(state.copyWith(titleValidationError: 'Поле не заполнено'));
|
||||
return false;
|
||||
} else if (state.entity.title!.length < 3) {
|
||||
emit(state.copyWith(titleValidationError: 'Минимальная длина 3 символа '));
|
||||
return false;
|
||||
} else {
|
||||
emit(state.copyWith(titleValidationError: null));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool validateServings() {
|
||||
if (state.entity.countOfPortions == null) {
|
||||
emit(state.copyWith(servingsValidationError: 'Поле не заполнено'));
|
||||
return false;
|
||||
} else {
|
||||
emit(state.copyWith(servingsValidationError: null));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool validateEnergyPerServing() {
|
||||
if (state.entity.energyInOnePortion == null) {
|
||||
emit(state.copyWith(energyPerServingValidationError: 'Поле не заполнено'));
|
||||
return false;
|
||||
} else {
|
||||
emit(state.copyWith(energyPerServingValidationError: null));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool validateActiveCookTime() {
|
||||
if (state.entity.activeCookHours == null && state.entity.activeCookMinutes == null) {
|
||||
emit(state.copyWith(activeTimeValidationError: 'Поле не заполнено'));
|
||||
return false;
|
||||
} else {
|
||||
emit(state.copyWith(activeTimeValidationError: null));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool validateTotalCookTime() {
|
||||
if (state.entity.totalCookHours == null || state.entity.totalCookMinutes == null) {
|
||||
emit(state.copyWith(cookingTimeValidationError: 'Поле не заполнено'));
|
||||
return false;
|
||||
} else {
|
||||
emit(state.copyWith(cookingTimeValidationError: null));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool validateDescription() {
|
||||
if (state.entity.description == null || (state.entity.description?.isEmpty ?? true)) {
|
||||
emit(state.copyWith(descriptionValidationError: 'Поле не заполнено'));
|
||||
return false;
|
||||
} else if (state.entity.description!.length < 3) {
|
||||
emit(state.copyWith(descriptionValidationError: 'Минимальная длина 3 символа '));
|
||||
return false;
|
||||
} else {
|
||||
emit(state.copyWith(descriptionValidationError: null));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool validateImage() {
|
||||
if (state.entity.image == null) {
|
||||
emit(state.copyWith(imageValidationError: 'Поле не заполнено'));
|
||||
return false;
|
||||
} else {
|
||||
emit(state.copyWith(imageValidationError: null));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,16 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'create_recipe_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class CreateRecipeState with _$CreateRecipeState {
|
||||
const factory CreateRecipeState(
|
||||
RecipeCreateEntity entity,
|
||||
) = _CreateRecipeState;
|
||||
class CreateRecipeState with _$CreateRecipeState {
|
||||
const factory CreateRecipeState({
|
||||
required RecipeCreateEntity entity,
|
||||
String? titleValidationError,
|
||||
String? descriptionValidationError,
|
||||
String? cookingTimeValidationError,
|
||||
String? activeTimeValidationError,
|
||||
String? servingsValidationError,
|
||||
String? energyPerServingValidationError,
|
||||
String? imageValidationError,
|
||||
}) = _CreateRecipeState;
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,14 @@ final _privateConstructorUsedError = UnsupportedError(
|
||||
/// @nodoc
|
||||
mixin _$CreateRecipeState {
|
||||
RecipeCreateEntity get entity => throw _privateConstructorUsedError;
|
||||
String? get titleValidationError => throw _privateConstructorUsedError;
|
||||
String? get descriptionValidationError => throw _privateConstructorUsedError;
|
||||
String? get cookingTimeValidationError => throw _privateConstructorUsedError;
|
||||
String? get activeTimeValidationError => throw _privateConstructorUsedError;
|
||||
String? get servingsValidationError => throw _privateConstructorUsedError;
|
||||
String? get energyPerServingValidationError =>
|
||||
throw _privateConstructorUsedError;
|
||||
String? get imageValidationError => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of CreateRecipeState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -31,7 +39,15 @@ abstract class $CreateRecipeStateCopyWith<$Res> {
|
||||
CreateRecipeState value, $Res Function(CreateRecipeState) then) =
|
||||
_$CreateRecipeStateCopyWithImpl<$Res, CreateRecipeState>;
|
||||
@useResult
|
||||
$Res call({RecipeCreateEntity entity});
|
||||
$Res call(
|
||||
{RecipeCreateEntity entity,
|
||||
String? titleValidationError,
|
||||
String? descriptionValidationError,
|
||||
String? cookingTimeValidationError,
|
||||
String? activeTimeValidationError,
|
||||
String? servingsValidationError,
|
||||
String? energyPerServingValidationError,
|
||||
String? imageValidationError});
|
||||
|
||||
$RecipeCreateEntityCopyWith<$Res> get entity;
|
||||
}
|
||||
@@ -52,12 +68,48 @@ class _$CreateRecipeStateCopyWithImpl<$Res, $Val extends CreateRecipeState>
|
||||
@override
|
||||
$Res call({
|
||||
Object? entity = null,
|
||||
Object? titleValidationError = freezed,
|
||||
Object? descriptionValidationError = freezed,
|
||||
Object? cookingTimeValidationError = freezed,
|
||||
Object? activeTimeValidationError = freezed,
|
||||
Object? servingsValidationError = freezed,
|
||||
Object? energyPerServingValidationError = freezed,
|
||||
Object? imageValidationError = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
entity: null == entity
|
||||
? _value.entity
|
||||
: entity // ignore: cast_nullable_to_non_nullable
|
||||
as RecipeCreateEntity,
|
||||
titleValidationError: freezed == titleValidationError
|
||||
? _value.titleValidationError
|
||||
: titleValidationError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
descriptionValidationError: freezed == descriptionValidationError
|
||||
? _value.descriptionValidationError
|
||||
: descriptionValidationError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
cookingTimeValidationError: freezed == cookingTimeValidationError
|
||||
? _value.cookingTimeValidationError
|
||||
: cookingTimeValidationError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
activeTimeValidationError: freezed == activeTimeValidationError
|
||||
? _value.activeTimeValidationError
|
||||
: activeTimeValidationError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
servingsValidationError: freezed == servingsValidationError
|
||||
? _value.servingsValidationError
|
||||
: servingsValidationError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
energyPerServingValidationError: freezed ==
|
||||
energyPerServingValidationError
|
||||
? _value.energyPerServingValidationError
|
||||
: energyPerServingValidationError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
imageValidationError: freezed == imageValidationError
|
||||
? _value.imageValidationError
|
||||
: imageValidationError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
@@ -80,7 +132,15 @@ abstract class _$$CreateRecipeStateImplCopyWith<$Res>
|
||||
__$$CreateRecipeStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({RecipeCreateEntity entity});
|
||||
$Res call(
|
||||
{RecipeCreateEntity entity,
|
||||
String? titleValidationError,
|
||||
String? descriptionValidationError,
|
||||
String? cookingTimeValidationError,
|
||||
String? activeTimeValidationError,
|
||||
String? servingsValidationError,
|
||||
String? energyPerServingValidationError,
|
||||
String? imageValidationError});
|
||||
|
||||
@override
|
||||
$RecipeCreateEntityCopyWith<$Res> get entity;
|
||||
@@ -100,12 +160,48 @@ class __$$CreateRecipeStateImplCopyWithImpl<$Res>
|
||||
@override
|
||||
$Res call({
|
||||
Object? entity = null,
|
||||
Object? titleValidationError = freezed,
|
||||
Object? descriptionValidationError = freezed,
|
||||
Object? cookingTimeValidationError = freezed,
|
||||
Object? activeTimeValidationError = freezed,
|
||||
Object? servingsValidationError = freezed,
|
||||
Object? energyPerServingValidationError = freezed,
|
||||
Object? imageValidationError = freezed,
|
||||
}) {
|
||||
return _then(_$CreateRecipeStateImpl(
|
||||
null == entity
|
||||
entity: null == entity
|
||||
? _value.entity
|
||||
: entity // ignore: cast_nullable_to_non_nullable
|
||||
as RecipeCreateEntity,
|
||||
titleValidationError: freezed == titleValidationError
|
||||
? _value.titleValidationError
|
||||
: titleValidationError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
descriptionValidationError: freezed == descriptionValidationError
|
||||
? _value.descriptionValidationError
|
||||
: descriptionValidationError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
cookingTimeValidationError: freezed == cookingTimeValidationError
|
||||
? _value.cookingTimeValidationError
|
||||
: cookingTimeValidationError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
activeTimeValidationError: freezed == activeTimeValidationError
|
||||
? _value.activeTimeValidationError
|
||||
: activeTimeValidationError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
servingsValidationError: freezed == servingsValidationError
|
||||
? _value.servingsValidationError
|
||||
: servingsValidationError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
energyPerServingValidationError: freezed ==
|
||||
energyPerServingValidationError
|
||||
? _value.energyPerServingValidationError
|
||||
: energyPerServingValidationError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
imageValidationError: freezed == imageValidationError
|
||||
? _value.imageValidationError
|
||||
: imageValidationError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -113,14 +209,36 @@ class __$$CreateRecipeStateImplCopyWithImpl<$Res>
|
||||
/// @nodoc
|
||||
|
||||
class _$CreateRecipeStateImpl implements _CreateRecipeState {
|
||||
const _$CreateRecipeStateImpl(this.entity);
|
||||
const _$CreateRecipeStateImpl(
|
||||
{required this.entity,
|
||||
this.titleValidationError,
|
||||
this.descriptionValidationError,
|
||||
this.cookingTimeValidationError,
|
||||
this.activeTimeValidationError,
|
||||
this.servingsValidationError,
|
||||
this.energyPerServingValidationError,
|
||||
this.imageValidationError});
|
||||
|
||||
@override
|
||||
final RecipeCreateEntity entity;
|
||||
@override
|
||||
final String? titleValidationError;
|
||||
@override
|
||||
final String? descriptionValidationError;
|
||||
@override
|
||||
final String? cookingTimeValidationError;
|
||||
@override
|
||||
final String? activeTimeValidationError;
|
||||
@override
|
||||
final String? servingsValidationError;
|
||||
@override
|
||||
final String? energyPerServingValidationError;
|
||||
@override
|
||||
final String? imageValidationError;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CreateRecipeState(entity: $entity)';
|
||||
return 'CreateRecipeState(entity: $entity, titleValidationError: $titleValidationError, descriptionValidationError: $descriptionValidationError, cookingTimeValidationError: $cookingTimeValidationError, activeTimeValidationError: $activeTimeValidationError, servingsValidationError: $servingsValidationError, energyPerServingValidationError: $energyPerServingValidationError, imageValidationError: $imageValidationError)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -128,11 +246,42 @@ class _$CreateRecipeStateImpl implements _CreateRecipeState {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$CreateRecipeStateImpl &&
|
||||
(identical(other.entity, entity) || other.entity == entity));
|
||||
(identical(other.entity, entity) || other.entity == entity) &&
|
||||
(identical(other.titleValidationError, titleValidationError) ||
|
||||
other.titleValidationError == titleValidationError) &&
|
||||
(identical(other.descriptionValidationError,
|
||||
descriptionValidationError) ||
|
||||
other.descriptionValidationError ==
|
||||
descriptionValidationError) &&
|
||||
(identical(other.cookingTimeValidationError,
|
||||
cookingTimeValidationError) ||
|
||||
other.cookingTimeValidationError ==
|
||||
cookingTimeValidationError) &&
|
||||
(identical(other.activeTimeValidationError,
|
||||
activeTimeValidationError) ||
|
||||
other.activeTimeValidationError == activeTimeValidationError) &&
|
||||
(identical(
|
||||
other.servingsValidationError, servingsValidationError) ||
|
||||
other.servingsValidationError == servingsValidationError) &&
|
||||
(identical(other.energyPerServingValidationError,
|
||||
energyPerServingValidationError) ||
|
||||
other.energyPerServingValidationError ==
|
||||
energyPerServingValidationError) &&
|
||||
(identical(other.imageValidationError, imageValidationError) ||
|
||||
other.imageValidationError == imageValidationError));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, entity);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
entity,
|
||||
titleValidationError,
|
||||
descriptionValidationError,
|
||||
cookingTimeValidationError,
|
||||
activeTimeValidationError,
|
||||
servingsValidationError,
|
||||
energyPerServingValidationError,
|
||||
imageValidationError);
|
||||
|
||||
/// Create a copy of CreateRecipeState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -145,11 +294,32 @@ class _$CreateRecipeStateImpl implements _CreateRecipeState {
|
||||
}
|
||||
|
||||
abstract class _CreateRecipeState implements CreateRecipeState {
|
||||
const factory _CreateRecipeState(final RecipeCreateEntity entity) =
|
||||
_$CreateRecipeStateImpl;
|
||||
const factory _CreateRecipeState(
|
||||
{required final RecipeCreateEntity entity,
|
||||
final String? titleValidationError,
|
||||
final String? descriptionValidationError,
|
||||
final String? cookingTimeValidationError,
|
||||
final String? activeTimeValidationError,
|
||||
final String? servingsValidationError,
|
||||
final String? energyPerServingValidationError,
|
||||
final String? imageValidationError}) = _$CreateRecipeStateImpl;
|
||||
|
||||
@override
|
||||
RecipeCreateEntity get entity;
|
||||
@override
|
||||
String? get titleValidationError;
|
||||
@override
|
||||
String? get descriptionValidationError;
|
||||
@override
|
||||
String? get cookingTimeValidationError;
|
||||
@override
|
||||
String? get activeTimeValidationError;
|
||||
@override
|
||||
String? get servingsValidationError;
|
||||
@override
|
||||
String? get energyPerServingValidationError;
|
||||
@override
|
||||
String? get imageValidationError;
|
||||
|
||||
/// Create a copy of CreateRecipeState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
||||
@@ -212,7 +212,7 @@ class _AboutRecipesScreenState extends State<AboutRecipesScreen> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(
|
||||
widget.recipes.title,
|
||||
widget.recipes.title ?? '',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 24,
|
||||
@@ -241,7 +241,7 @@ class _AboutRecipesScreenState extends State<AboutRecipesScreen> {
|
||||
Icon(Icons.fast_forward_outlined),
|
||||
SizedBox(width: 4),
|
||||
Text(
|
||||
widget.recipes.steps.length.toString() + ' этапов',
|
||||
'${widget.recipes.steps?.length ?? 0}' + ' этапов',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 12,
|
||||
@@ -286,9 +286,14 @@ class _AboutRecipesScreenState extends State<AboutRecipesScreen> {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _ingredient(widget.recipes.ingredients[index]),
|
||||
itemBuilder: (context, index) {
|
||||
if (widget.recipes.ingredients != null) {
|
||||
return _ingredient(widget.recipes.ingredients![index]);
|
||||
}
|
||||
return SizedBox.shrink();
|
||||
},
|
||||
separatorBuilder: (context, index) => SizedBox(height: 4),
|
||||
itemCount: widget.recipes.ingredients.length,
|
||||
itemCount: widget.recipes.ingredients?.length ?? 0,
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
Padding(
|
||||
@@ -307,7 +312,10 @@ class _AboutRecipesScreenState extends State<AboutRecipesScreen> {
|
||||
ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _step(widget.recipes.steps[index], index + 1),
|
||||
itemBuilder: (context, index) {
|
||||
if (widget.recipes.steps == null) return SizedBox.shrink();
|
||||
return _step(widget.recipes.steps![index], index + 1);
|
||||
},
|
||||
separatorBuilder: (context, index) => Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Container(
|
||||
@@ -318,7 +326,7 @@ class _AboutRecipesScreenState extends State<AboutRecipesScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
itemCount: widget.recipes.steps.length,
|
||||
itemCount: widget.recipes.steps?.length ?? 0,
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Padding(
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/utils/toasts.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:eatvio_app/features/recipes/presentation/widgets/info_about_recipe_widget.dart';
|
||||
import 'package:eatvio_app/features/recipes/presentation/widgets/ingredients_widget.dart';
|
||||
import 'package:eatvio_app/features/recipes/presentation/widgets/steps_widget.dart';
|
||||
@@ -42,32 +44,44 @@ class _RecipeCreateScreenState extends State<RecipeCreateScreen> {
|
||||
InfoAboutRecipeWidget(),
|
||||
IngredientsWidget(),
|
||||
StepsWidget(),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
context.read<CreateRecipeCubit>().publishRecipe();
|
||||
},
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Ink(
|
||||
width: MediaQuery.of(context).size.width - 32,
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.blueA400,
|
||||
BlocBuilder<CreateRecipeCubit, CreateRecipeState>(
|
||||
builder: (context, state) {
|
||||
return Builder(
|
||||
builder: (context) {
|
||||
return InkWell(
|
||||
onTap: () async {
|
||||
if (context.read<CreateRecipeCubit>().validateAll()) {
|
||||
final isSuccess = await context.read<CreateRecipeCubit>().publishRecipe();
|
||||
if (isSuccess) {
|
||||
showSuccessToast('Рецепт успешно опубликован');
|
||||
Navigator.pop(context);
|
||||
} else {
|
||||
showErrorToast('Не удалось опубликовать рецепт');
|
||||
}
|
||||
}
|
||||
},
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -8,14 +8,13 @@ import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
|
||||
class ImageWidget extends StatefulWidget {
|
||||
const ImageWidget({super.key});
|
||||
|
||||
const ImageWidget({super.key, required this.image});
|
||||
final File? image;
|
||||
@override
|
||||
State<ImageWidget> createState() => _ImageWidgetState();
|
||||
}
|
||||
|
||||
class _ImageWidgetState extends State<ImageWidget> {
|
||||
XFile? image;
|
||||
late final CreateRecipeCubit createRecipeCubit;
|
||||
|
||||
@override
|
||||
@@ -30,7 +29,7 @@ class _ImageWidgetState extends State<ImageWidget> {
|
||||
onTap: () {
|
||||
_showImagePicker();
|
||||
},
|
||||
child: image == null
|
||||
child: widget.image == null
|
||||
? SvgPicture.asset(
|
||||
width: MediaQuery.of(context).size.width - 32,
|
||||
'assets/icons/upload_photo_of_portion.svg',
|
||||
@@ -40,7 +39,7 @@ class _ImageWidgetState extends State<ImageWidget> {
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Image.file(
|
||||
File(image!.path),
|
||||
File(widget.image!.path),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
@@ -48,9 +47,6 @@ class _ImageWidgetState extends State<ImageWidget> {
|
||||
top: 16,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
image = null;
|
||||
});
|
||||
createRecipeCubit.replaceImage(null);
|
||||
},
|
||||
child: Container(
|
||||
@@ -76,10 +72,11 @@ class _ImageWidgetState extends State<ImageWidget> {
|
||||
CupertinoActionSheetAction(
|
||||
onPressed: () async {
|
||||
final picker = ImagePicker();
|
||||
image = await picker.pickImage(source: ImageSource.camera);
|
||||
final image = await picker.pickImage(source: ImageSource.camera);
|
||||
setState(() {});
|
||||
if (image != null) {
|
||||
createRecipeCubit.replaceImage(File(image!.path));
|
||||
createRecipeCubit.replaceImage(File(image.path));
|
||||
createRecipeCubit.validateImage();
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
@@ -94,10 +91,11 @@ class _ImageWidgetState extends State<ImageWidget> {
|
||||
CupertinoActionSheetAction(
|
||||
onPressed: () async {
|
||||
final picker = ImagePicker();
|
||||
image = await picker.pickImage(source: ImageSource.gallery);
|
||||
final image = await picker.pickImage(source: ImageSource.gallery);
|
||||
setState(() {});
|
||||
if (image != null) {
|
||||
createRecipeCubit.replaceImage(File(image!.path));
|
||||
createRecipeCubit.replaceImage(File(image.path));
|
||||
createRecipeCubit.validateImage();
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import 'package:eatvio_app/core/data/selectionPopupModel/selection_popup_model.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_drop_down.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/features/recipes/entities/recipe_create_entity.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:eatvio_app/features/recipes/presentation/widgets/image_widget.dart';
|
||||
import 'package:eatvio_app/features/recipes/presentation/widgets/portion_widget.dart';
|
||||
import 'package:eatvio_app/features/recipes/presentation/widgets/tags_widget.dart';
|
||||
@@ -40,10 +43,29 @@ class _InfoAboutRecipeWidgetState extends State<InfoAboutRecipeWidget> {
|
||||
children: [
|
||||
Text('Название'),
|
||||
SizedBox(height: 4),
|
||||
CustomTextFormField(
|
||||
hintText: 'Например: тар-тар из говядины',
|
||||
onChanged: (value) {
|
||||
createRecipeCubit.replaceTitle(value);
|
||||
BlocBuilder<CreateRecipeCubit, CreateRecipeState>(
|
||||
buildWhen: (previous, current) => previous.titleValidationError != current.titleValidationError,
|
||||
builder: (context, state) {
|
||||
return Column(
|
||||
children: [
|
||||
CustomTextFormField(
|
||||
hintText: 'Например: тар-тар из говядины',
|
||||
hasError: state.titleValidationError != null,
|
||||
onChanged: (value) {
|
||||
createRecipeCubit.replaceTitle(value);
|
||||
if (state.titleValidationError != null) {
|
||||
createRecipeCubit.validateTitle();
|
||||
}
|
||||
},
|
||||
),
|
||||
if (state.titleValidationError != null) ...[
|
||||
SizedBox(height: 4),
|
||||
CustomErrorWidget(
|
||||
error: state.titleValidationError!,
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
@@ -70,18 +92,57 @@ class _InfoAboutRecipeWidgetState extends State<InfoAboutRecipeWidget> {
|
||||
const SizedBox(height: 12),
|
||||
Text('Описание'),
|
||||
SizedBox(height: 4),
|
||||
CustomTextFormField(
|
||||
hintText: 'Расскажите, почему вы выбрали именно этот рецепт? Опишите, каким будет готовое блюдо и в чём его достоинства.',
|
||||
minLines: 5,
|
||||
maxLines: 5,
|
||||
onChanged: (value) {
|
||||
createRecipeCubit.replaceDescription(value);
|
||||
BlocBuilder<CreateRecipeCubit, CreateRecipeState>(
|
||||
buildWhen: (previous, current) => previous.descriptionValidationError != current.descriptionValidationError,
|
||||
builder: (context, state) {
|
||||
return Column(
|
||||
children: [
|
||||
CustomTextFormField(
|
||||
hintText: 'Расскажите, почему вы выбрали именно этот рецепт? Опишите, каким будет готовое блюдо и в чём его достоинства.',
|
||||
minLines: 5,
|
||||
maxLines: 5,
|
||||
hasError: state.descriptionValidationError != null,
|
||||
onChanged: (value) {
|
||||
createRecipeCubit.replaceDescription(value);
|
||||
if (state.descriptionValidationError != null) {
|
||||
createRecipeCubit.validateDescription();
|
||||
}
|
||||
},
|
||||
),
|
||||
if (state.descriptionValidationError != null) ...[
|
||||
SizedBox(height: 4),
|
||||
CustomErrorWidget(
|
||||
error: state.descriptionValidationError!,
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text('Фото обложка'),
|
||||
SizedBox(height: 4),
|
||||
ImageWidget(),
|
||||
BlocBuilder<CreateRecipeCubit, CreateRecipeState>(
|
||||
buildWhen: (previous, current) => previous.imageValidationError != current.imageValidationError || previous.entity.image != current.entity.image,
|
||||
builder: (context, state) {
|
||||
return Column(
|
||||
children: [
|
||||
Container(
|
||||
child: ImageWidget(image: state.entity.image),
|
||||
decoration: state.imageValidationError != null
|
||||
? BoxDecoration(border: Border.all(width: 1, color: appTheme.red), borderRadius: BorderRadius.circular(8))
|
||||
: null,
|
||||
),
|
||||
if (state.imageValidationError != null) ...[
|
||||
SizedBox(height: 4),
|
||||
CustomErrorWidget(
|
||||
error: state.imageValidationError!,
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text('Ссылка на видео (если есть)'),
|
||||
SizedBox(height: 4),
|
||||
|
||||
@@ -69,7 +69,7 @@ class _IngredientsWidgetState extends State<IngredientsWidget> {
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
title: Text(state.entity.ingredients[index].name),
|
||||
subtitle: Text('${state.entity.ingredients[index].unit} ${state.entity.ingredients[index].portion == 'gr' ? 'гр' : 'шт'}'),
|
||||
subtitle: Text('${state.entity.ingredients[index].portion} ${state.entity.ingredients[index].unit == 'gr' ? 'гр' : 'шт'}'),
|
||||
trailing: IconButton(
|
||||
onPressed: () {
|
||||
createRecipeCubit.removeIngredient(index);
|
||||
@@ -163,8 +163,8 @@ class _IngredientsWidgetState extends State<IngredientsWidget> {
|
||||
createRecipeCubit.addIngredient(
|
||||
Ingredient(
|
||||
name: nameController.text,
|
||||
unit: gramsController.text.isEmpty ? pcsController.text : gramsController.text,
|
||||
portion: gramsController.text.isEmpty ? 'pcs' : 'gr',
|
||||
unit: gramsController.text.isEmpty ? 'pcs' : 'gr',
|
||||
portion: gramsController.text.isEmpty ? pcsController.text : gramsController.text,
|
||||
),
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.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';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
@@ -12,7 +14,6 @@ class PortionWidget extends StatefulWidget {
|
||||
|
||||
class _PortionWidgetState extends State<PortionWidget> {
|
||||
late final CreateRecipeCubit createRecipeCubit;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -21,49 +22,86 @@ class _PortionWidgetState extends State<PortionWidget> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
return BlocBuilder<CreateRecipeCubit, CreateRecipeState>(
|
||||
buildWhen: (previous, current) =>
|
||||
previous.servingsValidationError != current.servingsValidationError ||
|
||||
previous.energyPerServingValidationError != current.energyPerServingValidationError,
|
||||
builder: (context, state) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Кол-во порций'),
|
||||
SizedBox(height: 4),
|
||||
CustomTextFormField(
|
||||
hintText: '1',
|
||||
onChanged: (value) {
|
||||
createRecipeCubit.replacePortionsCount(int.parse(value));
|
||||
},
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Кол-во порций'),
|
||||
SizedBox(height: 4),
|
||||
Column(
|
||||
children: [
|
||||
CustomTextFormField(
|
||||
hintText: '1',
|
||||
hasError: state.servingsValidationError != null,
|
||||
textInputType: TextInputType.number,
|
||||
onChanged: (value) {
|
||||
createRecipeCubit.replacePortionsCount(int.parse(value));
|
||||
if (state.servingsValidationError != null) {
|
||||
createRecipeCubit.validateServings();
|
||||
}
|
||||
},
|
||||
),
|
||||
if (state.servingsValidationError != null) ...[
|
||||
SizedBox(height: 4),
|
||||
CustomErrorWidget(
|
||||
error: state.servingsValidationError!,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Ккал в одной порции'),
|
||||
SizedBox(height: 4),
|
||||
CustomTextFormField(
|
||||
hintText: '100',
|
||||
suffixText: 'Ккал',
|
||||
onChanged: (value) {
|
||||
createRecipeCubit.replaceEnegryInPortion(double.parse(value));
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Ккал в одной порции'),
|
||||
SizedBox(height: 4),
|
||||
Column(
|
||||
children: [
|
||||
CustomTextFormField(
|
||||
hintText: '100',
|
||||
hasError: state.energyPerServingValidationError != null,
|
||||
suffixText: 'Ккал',
|
||||
textInputType: TextInputType.numberWithOptions(decimal: true),
|
||||
onChanged: (value) {
|
||||
createRecipeCubit.replaceEnegryInPortion(double.parse(value));
|
||||
if (state.energyPerServingValidationError != null) {
|
||||
createRecipeCubit.validateEnergyPerServing();
|
||||
}
|
||||
},
|
||||
),
|
||||
if (state.energyPerServingValidationError != null) ...[
|
||||
SizedBox(height: 4),
|
||||
CustomErrorWidget(
|
||||
error: state.energyPerServingValidationError!,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
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/create_diet/presentation/widgets/error_widget.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';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
@@ -22,130 +24,190 @@ class _TimeOfCookWidgetState extends State<TimeOfCookWidget> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text('Общее время приготовления'),
|
||||
const SizedBox(width: 8),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
content: Text(
|
||||
'Общее время приготовления - это период времени от начала приготовления блюда до подачи его на стол.',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF323F4B),
|
||||
height: 1.2,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Icon(
|
||||
Icons.help_outline_rounded,
|
||||
color: appTheme.blueA400,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Row(
|
||||
return BlocBuilder<CreateRecipeCubit, CreateRecipeState>(
|
||||
buildWhen: (previous, current) =>
|
||||
previous.activeTimeValidationError != current.activeTimeValidationError || previous.cookingTimeValidationError != current.cookingTimeValidationError,
|
||||
builder: (context, state) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomTextFormField(
|
||||
hintText: 'Часов',
|
||||
onChanged: (value) {
|
||||
if (createRecipeCubit.state.entity.totalCookTime == null) {
|
||||
createRecipeCubit.replaceTotalTime('${value.padLeft(2, '0')}:00');
|
||||
} else {
|
||||
createRecipeCubit.replaceTotalTime('${value.padLeft(2, '0')}:${createRecipeCubit.state.entity.totalCookTime!.substring(3, 5)}');
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: CustomTextFormField(
|
||||
hintText: 'Минут',
|
||||
onChanged: (value) {
|
||||
if (createRecipeCubit.state.entity.totalCookTime == null) {
|
||||
createRecipeCubit.replaceTotalTime('00:${value.padLeft(2, '0')}');
|
||||
} else {
|
||||
createRecipeCubit.replaceTotalTime('${createRecipeCubit.state.entity.totalCookTime!.substring(0, 2)}:${value.padLeft(2, '0')}');
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Text('Активное время приготовления'),
|
||||
const SizedBox(width: 8),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
content: Text(
|
||||
'Активное время приготовления - это период времени, требующий активного участия в приготовлении блюда. Оно включает мытье и очистку продуктов, нарезку, шинковку, взбивание, перемешивание, размешивание, перекладывание и т.п.',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF323F4B),
|
||||
height: 1.2,
|
||||
Row(
|
||||
children: [
|
||||
Text('Общее время приготовления'),
|
||||
const SizedBox(width: 8),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
content: Text(
|
||||
'Общее время приготовления - это период времени от начала приготовления блюда до подачи его на стол.',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF323F4B),
|
||||
height: 1.2,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Icon(
|
||||
Icons.help_outline_rounded,
|
||||
color: appTheme.blueA400,
|
||||
size: 20,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Icon(
|
||||
Icons.help_outline_rounded,
|
||||
color: appTheme.blueA400,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
CustomTextFormField(
|
||||
hintText: 'Часов',
|
||||
hasError: state.cookingTimeValidationError != null,
|
||||
textInputType: TextInputType.number,
|
||||
onChanged: (value) {
|
||||
final hours = int.tryParse(value);
|
||||
if (hours == null) return;
|
||||
createRecipeCubit.setTotalCookingHours(hours);
|
||||
if (state.cookingTimeValidationError != null) {
|
||||
createRecipeCubit.validateTotalCookTime();
|
||||
}
|
||||
},
|
||||
),
|
||||
if (state.cookingTimeValidationError != null) ...[
|
||||
SizedBox(height: 4),
|
||||
CustomErrorWidget(
|
||||
error: state.cookingTimeValidationError!,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
CustomTextFormField(
|
||||
hintText: 'Минут',
|
||||
hasError: state.cookingTimeValidationError != null,
|
||||
textInputType: TextInputType.number,
|
||||
onChanged: (value) {
|
||||
final minutes = int.tryParse(value);
|
||||
if (minutes == null) return;
|
||||
createRecipeCubit.setTotalCookingMinutes(minutes);
|
||||
if (state.cookingTimeValidationError != null) {
|
||||
createRecipeCubit.validateTotalCookTime();
|
||||
}
|
||||
},
|
||||
),
|
||||
if (state.cookingTimeValidationError != null) ...[
|
||||
SizedBox(height: 4),
|
||||
CustomErrorWidget(
|
||||
error: state.cookingTimeValidationError!,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Text('Активное время приготовления'),
|
||||
const SizedBox(width: 8),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
content: Text(
|
||||
'Активное время приготовления - это период времени, требующий активного участия в приготовлении блюда. Оно включает мытье и очистку продуктов, нарезку, шинковку, взбивание, перемешивание, размешивание, перекладывание и т.п.',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF323F4B),
|
||||
height: 1.2,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Icon(
|
||||
Icons.help_outline_rounded,
|
||||
color: appTheme.blueA400,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
CustomTextFormField(
|
||||
hintText: 'Часов',
|
||||
hasError: state.activeTimeValidationError != null,
|
||||
textInputType: TextInputType.number,
|
||||
onChanged: (value) {
|
||||
final hours = int.tryParse(value);
|
||||
if (hours == null) return;
|
||||
createRecipeCubit.setActiveCookingHours(hours);
|
||||
|
||||
if (state.activeTimeValidationError != null) {
|
||||
createRecipeCubit.validateActiveCookTime();
|
||||
}
|
||||
},
|
||||
),
|
||||
if (state.activeTimeValidationError != null) ...[
|
||||
SizedBox(height: 4),
|
||||
CustomErrorWidget(
|
||||
error: state.activeTimeValidationError!,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
CustomTextFormField(
|
||||
hintText: 'Минут',
|
||||
hasError: state.activeTimeValidationError != null,
|
||||
textInputType: TextInputType.number,
|
||||
onChanged: (value) {
|
||||
final minutes = int.tryParse(value);
|
||||
if (minutes == null) return;
|
||||
createRecipeCubit.setActiveCookingMinutes(minutes);
|
||||
|
||||
if (state.activeTimeValidationError != null) {
|
||||
createRecipeCubit.validateActiveCookTime();
|
||||
}
|
||||
},
|
||||
),
|
||||
if (state.activeTimeValidationError != null) ...[
|
||||
SizedBox(height: 4),
|
||||
CustomErrorWidget(
|
||||
error: state.activeTimeValidationError!,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomTextFormField(
|
||||
hintText: 'Часов',
|
||||
onChanged: (value) {
|
||||
if (createRecipeCubit.state.entity.activeCookTime == null) {
|
||||
createRecipeCubit.replaceActiveTime('${value.padLeft(2, '0')}:00');
|
||||
} else {
|
||||
createRecipeCubit.replaceActiveTime('${value.padLeft(2, '0')}:${createRecipeCubit.state.entity.activeCookTime!.substring(3, 5)}');
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: CustomTextFormField(
|
||||
hintText: 'Минут',
|
||||
onChanged: (value) {
|
||||
if (createRecipeCubit.state.entity.activeCookTime == null) {
|
||||
createRecipeCubit.replaceActiveTime('00:${value.padLeft(2, '0')}');
|
||||
} else {
|
||||
createRecipeCubit.replaceActiveTime('${createRecipeCubit.state.entity.activeCookTime!.substring(0, 2)}:${value.padLeft(2, '0')}');
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart';
|
||||
import 'package:eatvio_app/features/recipes/models/recipe_model.dart';
|
||||
import 'package:eatvio_app/features/recipes/models/recipe_tag_model.dart';
|
||||
import 'package:eatvio_app/features/recipes/models/step.dart';
|
||||
import 'package:eatvio_app/features/recipes/services/recipe_service.dart';
|
||||
import 'package:eatvio_app/features/recipes/models/product.dart';
|
||||
|
||||
@@ -19,58 +22,56 @@ class RecipeRepository {
|
||||
}
|
||||
}
|
||||
|
||||
Future<RecipeModel?> publishRecipe(RecipeModel recipe) async {
|
||||
Future<void> publishRecipe(RecipeModel recipe) async {
|
||||
try {
|
||||
if (recipe.image == null || (recipe.image?.isEmpty ?? true)) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
final file = File(recipe.image ?? '');
|
||||
final image = await ImageCompressUtility.compressFile(File(recipe.image ?? ''));
|
||||
final multiPartFile = await MultipartFile.fromFile(image.path);
|
||||
|
||||
final ingredientsMap = recipe.ingredients?.map((ingredient) {
|
||||
return {
|
||||
'name': ingredient!.name,
|
||||
'unit': ingredient.unit,
|
||||
'portion': ingredient.portion,
|
||||
};
|
||||
if (ingredient != null) {
|
||||
return ingredient.toJson();
|
||||
}
|
||||
}).toList() ??
|
||||
[];
|
||||
|
||||
final stepsMap = recipe.steps?.map((step) {
|
||||
return {
|
||||
'description': step?.description ?? "",
|
||||
};
|
||||
}).toList() ??
|
||||
[];
|
||||
|
||||
// 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!,
|
||||
);
|
||||
final stepsMap = [];
|
||||
for (final Step? step in recipe.steps ?? []) {
|
||||
if (step != null) {
|
||||
if ((step.image == null || (step.image?.isEmpty ?? true)) && step.description != null) {
|
||||
stepsMap.add({
|
||||
'description': step.description ?? "",
|
||||
});
|
||||
} else if (step.image != null && (step.image?.isNotEmpty ?? false)) {
|
||||
final image = await ImageCompressUtility.compressFile(File(step.image ?? ''));
|
||||
final multipartFile = await MultipartFile.fromFile(image.path);
|
||||
stepsMap.add({
|
||||
'description': step.description ?? "",
|
||||
'image': multipartFile,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
final formMap = {
|
||||
'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': multiPartFile,
|
||||
'servings': recipe.servings,
|
||||
'energy_per_serving': recipe.energyPerServing,
|
||||
};
|
||||
formMap.removeWhere((k, v) => v == null || (v is String && v.isEmpty));
|
||||
final formData = FormData.fromMap(formMap);
|
||||
await recipeService.publishRecipe(formData: formData);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/features/recipes/models/recipe_model.dart';
|
||||
import 'package:eatvio_app/features/recipes/models/recipe_tag_model.dart';
|
||||
@@ -19,24 +16,10 @@ abstract class RecipeService {
|
||||
@GET('/api/diary/product_nutritional_values/filter')
|
||||
Future<Map<String, List<Product>>> getInputProduct(@Query('name') String name);
|
||||
|
||||
|
||||
@POST('/api/recipes/create')
|
||||
@MultiPart()
|
||||
Future<RecipeModel> publishRecipe({
|
||||
@Part() required String type,
|
||||
@Part() required String state,
|
||||
@Part() String? title,
|
||||
@Part() required String description,
|
||||
@Part() String? video_url,
|
||||
@Part() List<Map<String, Object>>? ingredients,
|
||||
@Part() List<Map<String, Object>>? steps,
|
||||
@Part() List<String>? tags,
|
||||
@Part() int? cooking_time,
|
||||
@Part() int? active_time,
|
||||
@Part() required int servings,
|
||||
@Part() required int energy_per_serving,
|
||||
@Part() File? image,
|
||||
|
||||
Future<void> publishRecipe({
|
||||
@Body() required FormData formData,
|
||||
});
|
||||
|
||||
@POST('/api/recipes/update/{id}')
|
||||
@@ -44,4 +27,4 @@ abstract class RecipeService {
|
||||
|
||||
@GET('/api/recipes/tags')
|
||||
Future<RecipeTagModel> getTags();
|
||||
}
|
||||
}
|
||||
@@ -79,68 +79,12 @@ class _RecipeService implements RecipeService {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<RecipeModel> publishRecipe({
|
||||
required String type,
|
||||
required String state,
|
||||
String? title,
|
||||
required String description,
|
||||
String? video_url,
|
||||
List<Map<String, Object>>? ingredients,
|
||||
List<Map<String, Object>>? steps,
|
||||
List<String>? tags,
|
||||
int? cooking_time,
|
||||
int? active_time,
|
||||
required int servings,
|
||||
required int energy_per_serving,
|
||||
File? image,
|
||||
}) async {
|
||||
Future<void> publishRecipe({required FormData formData}) async {
|
||||
final _extra = <String, dynamic>{};
|
||||
final queryParameters = <String, dynamic>{};
|
||||
queryParameters.removeWhere((k, v) => v == null);
|
||||
final _headers = <String, dynamic>{};
|
||||
final _data = FormData();
|
||||
_data.fields.add(MapEntry('type', type));
|
||||
_data.fields.add(MapEntry('state', state));
|
||||
if (title != null) {
|
||||
_data.fields.add(MapEntry('title', title));
|
||||
}
|
||||
_data.fields.add(MapEntry('description', description));
|
||||
if (video_url != null) {
|
||||
_data.fields.add(MapEntry('video_url', video_url));
|
||||
}
|
||||
ingredients?.forEach((i) {
|
||||
_data.fields.add(MapEntry('ingredients', jsonEncode(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()));
|
||||
}
|
||||
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>(
|
||||
final _data = formData;
|
||||
final _options = _setStreamType<void>(
|
||||
Options(
|
||||
method: 'POST',
|
||||
headers: _headers,
|
||||
@@ -155,15 +99,7 @@ class _RecipeService implements RecipeService {
|
||||
)
|
||||
.copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
|
||||
);
|
||||
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
|
||||
late RecipeModel _value;
|
||||
try {
|
||||
_value = RecipeModel.fromJson(_result.data!);
|
||||
} on Object catch (e, s) {
|
||||
errorLogger?.logError(e, s, _options);
|
||||
rethrow;
|
||||
}
|
||||
return _value;
|
||||
await _dio.fetch<void>(_options);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -56,7 +56,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
const SizedBox(height: 12),
|
||||
_aboutEatvio(state),
|
||||
const SizedBox(height: 12),
|
||||
if (state.profile.roles.contains('consultant'))
|
||||
if (!state.profile.roles.contains('consultant'))
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: _becomeConsultantButton(state),
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/new_
|
||||
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/notifications_cubit/notifications_cubit.dart';
|
||||
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
|
||||
import 'package:eatvio_app/features/internet_connection_checker/internet_connection_checker_screen.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -44,6 +45,7 @@ class MyApp extends StatelessWidget {
|
||||
child: MaterialApp.router(
|
||||
theme: ThemeHelper().themeData(),
|
||||
routerConfig: AppRouter.appRouter,
|
||||
builder: (context, child) => InternetConnectionCheckerScreen(child: child!),
|
||||
debugShowCheckedModeBanner: false,
|
||||
localizationsDelegates: const [
|
||||
S.delegate,
|
||||
|
||||
@@ -60,6 +60,7 @@ dependencies:
|
||||
flutter_native_splash: ^2.4.4
|
||||
webview_flutter: ^4.10.0
|
||||
sentry_flutter: ^8.13.0
|
||||
connectivity_plus: ^6.1.2
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.4.14
|
||||
|
||||
Reference in New Issue
Block a user