diff --git a/lib/core/routing/other_routes.dart b/lib/core/routing/other_routes.dart index 038474ca..c09f8983 100644 --- a/lib/core/routing/other_routes.dart +++ b/lib/core/routing/other_routes.dart @@ -159,8 +159,11 @@ class OtherRoutes { path: CreateDietScreen.path, name: CreateDietScreen.name, builder: (context, state) { - final diet = state.extra as Diet?; - return CreateDietScreen(diet: diet); + final data = state.extra as Map; + final diet = data['diet'] as Diet?; + final isEditing = data['isEditing'] as bool?; + return CreateDietScreen(diet: diet, isEditing: isEditing); + }, ), GoRoute( diff --git a/lib/core/widgets/info_row.dart b/lib/core/widgets/info_row.dart index 115dedc0..6138195a 100644 --- a/lib/core/widgets/info_row.dart +++ b/lib/core/widgets/info_row.dart @@ -12,15 +12,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; class InfoRow extends StatelessWidget { - const InfoRow({ - required this.type, - super.key, - this.diet, - this.itemsBackgroundColor, - this.disableDescriptionOfTypes = false, - this.plan, - this.recipe, - }); + const InfoRow({required this.type, super.key, this.diet, this.itemsBackgroundColor, this.disableDescriptionOfTypes = false, this.plan, this.recipe}); final ProductType type; final Color? itemsBackgroundColor; final Diet? diet; @@ -50,44 +42,13 @@ class InfoRow extends StatelessWidget { InfoBouble( backgroundColor: itemsBackgroundColor, children: [ - SvgPicture.asset( - 'assets/icons/time2.svg', - width: 13.3, - height: 13.3, - ), + SvgPicture.asset('assets/icons/time2.svg', width: 13.3, height: 13.3), const SizedBox(width: 4), - Text( - switch (type) { - ProductType.diet => '${diet?.duration} ${getDayForm(diet?.duration ?? 7, s)}', - ProductType.plan => '${plan?.duration} ${getDayForm(plan?.duration ?? 7, s)}', - ProductType.recipe => timeTitle ?? '', - }, - style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), - ), - ], - ), - const SizedBox(width: 12), - InfoBouble( - backgroundColor: itemsBackgroundColor, - children: [ - SvgPicture.asset( - switch (type) { - ProductType.diet => 'assets/icons/people.svg', - ProductType.plan => 'assets/icons/people.svg', - ProductType.recipe => 'assets/icons/calories.svg', - }, - width: 13.3, - height: 13.3, - ), - const SizedBox(width: 4), - Text( - switch (type) { - ProductType.diet => '80 человек прошли', - ProductType.plan => '80 человек прошли', - ProductType.recipe => '${recipe?.calories.toStringAsFixed(0) ?? '0'} ккал', - }, - style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), - ), + Text(switch (type) { + ProductType.diet => '${diet?.duration} ${getDayForm(diet?.duration ?? 7, s)}', + ProductType.plan => '${plan?.duration} ${getDayForm(plan?.duration ?? 7, s)}', + ProductType.recipe => timeTitle ?? '', + }, style: TextStyles.textXSSemibold.copyWith(color: appTheme.black)), ], ), const SizedBox(width: 12), @@ -95,11 +56,17 @@ class InfoRow extends StatelessWidget { InfoBouble( backgroundColor: itemsBackgroundColor, children: [ - SvgPicture.asset( - 'assets/icons/steps.svg', - width: 13.3, - height: 13.3, - ), + SvgPicture.asset('assets/icons/calories.svg', width: 13.3, height: 13.3), + const SizedBox(width: 4), + Text('${recipe?.calories.toStringAsFixed(0) ?? '0'} ккал', style: TextStyles.textXSSemibold.copyWith(color: appTheme.black)), + ], + ), + const SizedBox(width: 12), + if (type == ProductType.recipe) + InfoBouble( + backgroundColor: itemsBackgroundColor, + children: [ + SvgPicture.asset('assets/icons/steps.svg', width: 13.3, height: 13.3), const SizedBox(width: 4), Text( '${recipe?.steps?.length ?? 0} ${getStepForm(recipe?.steps?.length ?? 0)}', @@ -110,13 +77,7 @@ class InfoRow extends StatelessWidget { if (plan?.type.hasAtLeastOneType() == true || diet?.type.hasAtLeastOneType() == true) InfoBouble( backgroundColor: itemsBackgroundColor, - children: [ - TypeBadges( - type: diet?.type, - planType: plan?.type, - disableDescriptionOfTypes: disableDescriptionOfTypes, - ), - ], + children: [TypeBadges(type: diet?.type, planType: plan?.type, disableDescriptionOfTypes: disableDescriptionOfTypes)], ), ], ), diff --git a/lib/features/create_diet/presentation/create_diet_screen.dart b/lib/features/create_diet/presentation/create_diet_screen.dart index 95775e86..0abb37a9 100644 --- a/lib/features/create_diet/presentation/create_diet_screen.dart +++ b/lib/features/create_diet/presentation/create_diet_screen.dart @@ -29,10 +29,10 @@ class CreateDietScreen extends StatelessWidget { static const name = 'createDietScreen'; static const path = '/create-diet-screen'; - const CreateDietScreen({super.key, this.diet}); + const CreateDietScreen({super.key, this.diet, this.isEditing}); final Diet? diet; - + final bool? isEditing; @override Widget build(BuildContext context) { return BlocProvider( @@ -237,13 +237,23 @@ class _CreateDietScreenContentState extends State { else CustomOutlinedButton( onPressed: () async { - final result = await context.read().createDiet(); - if (result == 'error') { - if (!context.mounted) return; - showErrorDialog(context, title: '${s.could_not_publish_diet} ${state.dietData.name}'); + if (widget.diet != null) { + final result = await context.read().updateDiet( + widget.diet!.id, + ); + if (result == 'error') { + if (!context.mounted) return; + showErrorDialog(context, title: '${s.could_not_update_diet} ${state.dietData.name}'); + } + } else { + final result = await context.read().createDiet(); + if (result == 'error') { + if (!context.mounted) return; + showErrorDialog(context, title: '${s.could_not_publish_diet} ${state.dietData.name}'); + } } }, - text: s.publish, + text: widget.diet != null ? s.save : s.publish, height: 52, buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), buttonStyle: ButtonStyle( diff --git a/lib/features/create_diet/presentation/cubit/create_diet_cubit.dart b/lib/features/create_diet/presentation/cubit/create_diet_cubit.dart index 7d50d11c..9cc64123 100644 --- a/lib/features/create_diet/presentation/cubit/create_diet_cubit.dart +++ b/lib/features/create_diet/presentation/cubit/create_diet_cubit.dart @@ -32,7 +32,7 @@ class CreateDietCubit extends Cubit { emit(state.copyWith(isLoading: true, isDietBeingEdited: true)); dietId = diet.id; final dailyScheduleWithNutrition = await createDietRepository.getDietPortionsNutrition(diet.id); - final countOfDays = dailyScheduleWithNutrition.length; + final countOfDays = diet.duration; final List>> dailySchedule = List.generate(countOfDays, (index) => [[], [], [], [], []]); for (final dayEntry in dailyScheduleWithNutrition.entries) { @@ -199,7 +199,10 @@ class CreateDietCubit extends Cubit { if (!_isFromValid()) return null; final authorProfile = await profileRepository.getMyProfile(); final imageBytes = (await state.dietData.image?.readAsBytes()) ?? []; - final imageInBase64 = base64.encode(imageBytes); + String image = base64.encode(imageBytes); + if (image.isEmpty && state.dietData.imageUrl != null) { + image = state.dietData.imageUrl ?? ''; + } final carbs = state.dietData.dailySchedule?.map((element) { return element.fold(0, (prev, element) { return prev + @@ -259,7 +262,7 @@ class CreateDietCubit extends Cubit { id: 0, name: state.dietData.name ?? '', slug: '', - image: imageInBase64, + image: image.isNotEmpty ? image : null, duration: state.dietData.duration ?? 0, state: state.dietData.state ?? DietState.active, type: DietType( @@ -340,7 +343,7 @@ class CreateDietCubit extends Cubit { } Future updateDiet(int dietId) async { - if (!_isFromValid()) return 'error'; + // if (!_isFromValid()) return 'error'; try { File? image; if (state.dietData.image != null) { diff --git a/lib/features/create_diet/presentation/diet_preview_screen.dart b/lib/features/create_diet/presentation/diet_preview_screen.dart index f57548f0..2d059896 100644 --- a/lib/features/create_diet/presentation/diet_preview_screen.dart +++ b/lib/features/create_diet/presentation/diet_preview_screen.dart @@ -31,11 +31,13 @@ class DietPreviewScreen extends StatefulWidget { class _DietPreviewScreenState extends State { final ScrollController scrollController = ScrollController(); - late final Uint8List imageData; + Uint8List? imageData; @override void initState() { super.initState(); - imageData = base64.decode(widget.diet.image); + if (widget.diet.image != null) { + imageData = base64.decode(widget.diet.image!); + } } @override @@ -58,7 +60,7 @@ class _DietPreviewScreenState extends State { ), body: SingleChildScrollView( controller: scrollController, - padding: const EdgeInsets.only(bottom: 160), + padding: const EdgeInsets.only(bottom: 40), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -67,49 +69,47 @@ class _DietPreviewScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Stack( - children: [ - Image.memory(imageData, height: 300, width: MediaQuery.sizeOf(context).width, fit: BoxFit.fill), - // CachedNetworkImage( - // imageUrl: baseUrl + '/storage/' + widget.diet.image, - // ), - Positioned( - bottom: 16, - left: 0, - right: 0, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - InfoBouble( - backgroundColor: appTheme.whiteA700, - children: [ - SvgPicture.asset('assets/icons/eye.svg', width: 18, height: 12), - const SizedBox(width: 8), - Text(widget.diet.totalViews.toString(), style: TextStyles.textXSSemibold.copyWith(color: appTheme.black)), - ], - ), - const SizedBox(width: 12), - GestureDetector( - onTap: () {}, - child: InfoBouble( + if (imageData != null) + Stack( + children: [ + Image.memory(imageData!, height: 300, width: MediaQuery.sizeOf(context).width, fit: BoxFit.fill), + Positioned( + bottom: 16, + left: 0, + right: 0, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + InfoBouble( backgroundColor: appTheme.whiteA700, children: [ - SvgPicture.asset( - 'assets/icons/heart.svg', - width: 14, - height: 12, - colorFilter: widget.diet.wasLikedByMe ? ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn) : null, - ), + SvgPicture.asset('assets/icons/eye.svg', width: 18, height: 12), const SizedBox(width: 8), - Text(widget.diet.totalLikes.toString(), style: TextStyles.textXSSemibold.copyWith(color: appTheme.black)), + Text(widget.diet.totalViews.toString(), style: TextStyles.textXSSemibold.copyWith(color: appTheme.black)), ], ), - ), - ], + const SizedBox(width: 12), + GestureDetector( + onTap: () {}, + child: InfoBouble( + backgroundColor: appTheme.whiteA700, + children: [ + SvgPicture.asset( + 'assets/icons/heart.svg', + width: 14, + height: 12, + colorFilter: widget.diet.wasLikedByMe ? ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn) : null, + ), + const SizedBox(width: 8), + Text(widget.diet.totalLikes.toString(), style: TextStyles.textXSSemibold.copyWith(color: appTheme.black)), + ], + ), + ), + ], + ), ), - ), - ], - ), + ], + ), const SizedBox(height: 16), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), @@ -148,14 +148,6 @@ class _DietPreviewScreenState extends State { }, itemCount: widget.diet.dailySchedule.length, ), - const MainBackgroundBouble( - padding: EdgeInsets.symmetric(vertical: 16), - child: Column( - children: [ - SizedBox(height: 16), - ], - ), - ), ], ), ), diff --git a/lib/features/feed/models/diets_pagination_model.dart b/lib/features/feed/models/diets_pagination_model.dart index 5da0bf7e..857a529b 100644 --- a/lib/features/feed/models/diets_pagination_model.dart +++ b/lib/features/feed/models/diets_pagination_model.dart @@ -22,12 +22,12 @@ abstract class Diet with _$Diet { @JsonKey(name: "id") required int id, @JsonKey(name: "slug") required String slug, @JsonKey(name: "name") required String name, - @JsonKey(name: "image") required String image, + @JsonKey(name: "image") required String? image, @JsonKey(name: "duration") required int duration, @JsonKey(name: "state") required DietState state, - @JsonKey(name: "type") required DietType type, - @JsonKey(name: "description_md") required String details, - @JsonKey(name: "daily_schedule") required Map>> dailySchedule, + @JsonKey(name: "type", fromJson: parseDietTypeFromJson) required DietType type, + @JsonKey(name: "description_md", defaultValue: '') required String details, + @JsonKey(name: "daily_schedule", defaultValue: {}) required Map>> dailySchedule, @JsonKey(name: "author") required Author author, @JsonKey(name: "total_likes") required int totalLikes, @JsonKey(name: "total_views") required int totalViews, @@ -40,12 +40,20 @@ abstract class Diet with _$Diet { factory Diet.fromJson(Map json) => _$DietFromJson(json); } +DietType parseDietTypeFromJson(Map? json) { + if (json == null) { + return const DietType(carb: false, light: false, meat: false, veg: false); + } + + return DietType.fromJson(json); +} + enum DietState { - @JsonKey(name: 'active') + @JsonValue('active') active, - @JsonKey(name: 'drafted') + @JsonValue('drafted') draft, - @JsonKey(name: 'personal') + @JsonValue('personal') personal; @override @@ -63,9 +71,7 @@ enum DietState { @freezed abstract class CreatedDiet with _$CreatedDiet { - const factory CreatedDiet({ - @JsonKey(name: "id") required int id, - }) = _CreatedDiet; + const factory CreatedDiet({@JsonKey(name: "id") required int id}) = _CreatedDiet; factory CreatedDiet.fromJson(Map json) => _$CreatedDietFromJson(json); } @@ -135,10 +141,10 @@ enum Portion { } String getValue() => switch (this) { - Portion.grams => 'gr', - Portion.milliliters => 'milliliters', - Portion.pcs => 'pcs', - }; + Portion.grams => 'gr', + Portion.milliliters => 'milliliters', + Portion.pcs => 'pcs', + }; } @freezed diff --git a/lib/features/my_diets/presentation/my_diets_screen.dart b/lib/features/my_diets/presentation/my_diets_screen.dart index 59c9dd09..01ed19fa 100644 --- a/lib/features/my_diets/presentation/my_diets_screen.dart +++ b/lib/features/my_diets/presentation/my_diets_screen.dart @@ -62,14 +62,12 @@ class MyDietsScreenContent extends StatelessWidget { controller: switch (state.selectedStateIndex) { 1 => draftDietsScrollController, 2 => personalDietsScrollController, - _ => activeDietsScrollController + _ => activeDietsScrollController, }, headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { return [ SliverAppBar( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(bottom: Radius.circular(12)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(bottom: Radius.circular(12))), backgroundColor: appTheme.gray100, foregroundColor: appTheme.gray100, automaticallyImplyLeading: false, @@ -79,20 +77,14 @@ class MyDietsScreenContent extends StatelessWidget { snap: true, scrolledUnderElevation: 0, bottom: PreferredSize( - preferredSize: Size( - MediaQuery.sizeOf(context).width, - 64, - ), + preferredSize: Size(MediaQuery.sizeOf(context).width, 64), child: Padding( padding: const EdgeInsets.only(bottom: 0), child: Column( mainAxisSize: MainAxisSize.min, children: [ const SizedBox(height: 8), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 4), - child: MyDietsTab(), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 4), child: MyDietsTab()), const SizedBox(height: 8), Row( children: [ @@ -105,23 +97,17 @@ class MyDietsScreenContent extends StatelessWidget { searchController: switch (state.selectedStateIndex) { 1 => draftDietsSearchController, 2 => personalDietsSearchController, - _ => activeDietsSearchController + _ => activeDietsSearchController, }, onChanged: (query) { - EasyDebounce.debounce( - 'searchDiets', - const Duration(milliseconds: 500), - () { - context.read().loadCurrentDiets(query: query == '' ? null : query); - }, - ); + EasyDebounce.debounce('searchDiets', const Duration(milliseconds: 500), () { + context.read().loadCurrentDiets(query: query == '' ? null : query); + }); }, ), ), const SizedBox(width: 12), - AddItemButton( - onTap: () => GoRouter.of(context).pushNamed(CreateDietScreen.name), - ), + AddItemButton(onTap: () => GoRouter.of(context).pushNamed(CreateDietScreen.name)), const SizedBox(width: 4), ], ), @@ -138,14 +124,12 @@ class MyDietsScreenContent extends StatelessWidget { child: BlocBuilder( builder: (BuildContext context, MyDietsState state) { if (state.isLoading) { - return Scaffold( - backgroundColor: Colors.grey.shade200, - body: const Loader(), - ); + return Scaffold(backgroundColor: Colors.grey.shade200, body: const Loader()); } else if (state.error != null) { return DataErrorWidget(onReload: () => context.read().loadAllDiets()); } else { - final gotDietLoadingError = state.activeDietsError != null && state.selectedStateIndex == 0 || + final gotDietLoadingError = + state.activeDietsError != null && state.selectedStateIndex == 0 || state.draftDietsError != null && state.selectedStateIndex == 1 || state.personalDietsError != null && state.selectedStateIndex == 2; @@ -170,10 +154,7 @@ class MyDietsScreenContent extends StatelessWidget { shrinkWrap: true, physics: const BouncingScrollPhysics(), itemBuilder: (context, index) { - return MyDietWidget( - diet: diets[index], - key: ValueKey(diets[index].id), - ); + return MyDietWidget(diet: diets[index], key: ValueKey(diets[index].id)); }, separatorBuilder: (context, index) { return const SizedBox(height: 20); @@ -183,11 +164,7 @@ class MyDietsScreenContent extends StatelessWidget { Positioned( right: 0, bottom: 74, - child: FilterBadge( - filterBottomSheet: DietFilterBottomSheet( - myDietsCubit: context.read(), - ), - ), + child: FilterBadge(filterBottomSheet: DietFilterBottomSheet(myDietsCubit: context.read())), ), ], ); diff --git a/lib/features/my_diets/presentation/widgets/my_diet_widget.dart b/lib/features/my_diets/presentation/widgets/my_diet_widget.dart index f2f5892e..166ff523 100644 --- a/lib/features/my_diets/presentation/widgets/my_diet_widget.dart +++ b/lib/features/my_diets/presentation/widgets/my_diet_widget.dart @@ -1,6 +1,5 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:eatvio_app/core/app_config.dart'; -import 'package:eatvio_app/core/app_router.dart'; import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; @@ -12,6 +11,7 @@ import 'package:eatvio_app/core/widgets/info_row.dart'; import 'package:eatvio_app/core/widgets/show_error_dialog.dart'; import 'package:eatvio_app/core/widgets/success_published_diet_bottom_sheet.dart'; import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart'; +import 'package:eatvio_app/features/create_diet/presentation/diet_preview_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'; @@ -36,6 +36,65 @@ class MyDietWidget extends StatefulWidget { } class _MyDietWidgetState extends State { + void _editDiet(Diet diet) { + GoRouter.of(context).pushNamed(CreateDietScreen.name, extra: {'diet': diet, 'isEditing': true}); + } + + void _suggestDiet(Diet diet) { + GoRouter.of(context).pushNamed(PersonalDietSuggestionScreen.name, extra: diet.id); + } + + Future _shareDiet(int dietId) async { + await SharePlus.instance.share(ShareParams(uri: Uri.parse('${sl().baseUrl}/ru/diet/$dietId'))); + } + + Future _publishDiet(Diet diet, BuildContext context) async { + final result = await context.read().publishDiet(widget.diet.id); + if (result) { + if (!context.mounted) return; + showBottomSheetWithBlur( + context: context, + child: SuccessPublishedDietOrPlanBottomSheet(title: widget.diet.name), + ); + await context.read().loadAllDiets(); + } else { + showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время'); + } + } + + void _unpublishDiet(Diet diet, BuildContext context) async { + final result = await context.read().unpublishDiet(widget.diet.id); + if (result) { + if (!context.mounted) return; + showBottomSheetWithBlur( + context: context, + height: 250, + child: DietDraftedBottomSheet(title: widget.diet.name), + ); + await context.read().loadAllDiets(); + } else { + showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время'); + } + } + + void _createBasedOnChosen(Diet diet, BuildContext context) async { + await showErrorDialog(context, title: 'Предупреждение', description: 'Вы переходите к созданию новой диеты на основе скопированной'); + if (!context.mounted) return; + GoRouter.of(context).pushNamed(CreateDietScreen.name, extra: {'diet': diet, 'isEditing': false}); + } + + void _deleteDiet(Diet diet, BuildContext context) async { + final result = await context.read().deleteDiet(widget.diet.id); + if (result) { + if (!context.mounted) return; + showBottomSheetWithBlur( + context: context, + child: DietDeletedBottomSheet(title: widget.diet.name), + ); + await context.read().loadAllDiets(); + } + } + @override Widget build(BuildContext context) { final s = S.of(context); @@ -45,28 +104,34 @@ class _MyDietWidgetState extends State { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Stack( - children: [ - InkWell( - onTap: () => GoRouter.of(context).pushNamed('aboutDietScreen', extra: {'slug': widget.diet.slug, 'navigateToComments': false}), - child: ClipRRect( - borderRadius: const BorderRadius.vertical(top: Radius.circular(12)), - child: CachedNetworkImage( - imageUrl: '${sl().baseUrl}/storage/${widget.diet.image}', - fit: BoxFit.fitWidth, - width: MediaQuery.of(context).size.width, + if (widget.diet.image != null && (widget.diet.image?.isNotEmpty ?? false)) + Stack( + children: [ + InkWell( + onTap: () { + if (widget.diet.state == DietState.draft) { + return; + } + GoRouter.of(context).pushNamed('aboutDietScreen', extra: {'slug': widget.diet.slug, 'navigateToComments': false}); + }, + child: ClipRRect( + borderRadius: const BorderRadius.vertical(top: Radius.circular(12)), + child: CachedNetworkImage( + imageUrl: '${sl().baseUrl}/storage/${widget.diet.image}', + fit: BoxFit.fitWidth, + width: MediaQuery.of(context).size.width, + ), ), ), - ), - Positioned( - bottom: 16, - child: SizedBox( - width: MediaQuery.sizeOf(context).width, - child: InfoRow(diet: widget.diet, itemsBackgroundColor: appTheme.whiteA700, disableDescriptionOfTypes: true, type: ProductType.diet), + Positioned( + bottom: 16, + child: SizedBox( + width: MediaQuery.sizeOf(context).width, + child: InfoRow(diet: widget.diet, itemsBackgroundColor: appTheme.whiteA700, disableDescriptionOfTypes: true, type: ProductType.diet), + ), ), - ), - ], - ), + ], + ), const SizedBox(height: 12), Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -81,19 +146,20 @@ class _MyDietWidgetState extends State { padding: const EdgeInsets.symmetric(horizontal: 16), child: Builder( builder: (context) { - String details = widget.diet.details.length > 200 - ? '${widget.diet.details.substring(0, 200)}...' - : widget.diet.details; + String details = widget.diet.details.length > 200 ? '${widget.diet.details.substring(0, 200)}...' : widget.diet.details; - return MarkdownHtmlWidget( - markdownText: details, - fontSize: TextStyles.textSMedium.fontSize ?? 14, - textColor: appTheme.black, - ); + return MarkdownHtmlWidget(markdownText: details, fontSize: TextStyles.textSMedium.fontSize ?? 14, textColor: appTheme.black); }, ), ), ], + if (widget.diet.image == null || (widget.diet.image?.isEmpty ?? true)) ...[ + const SizedBox(height: 12), + SizedBox( + width: MediaQuery.sizeOf(context).width, + child: InfoRow(diet: widget.diet, itemsBackgroundColor: appTheme.dividerColor, disableDescriptionOfTypes: true, type: ProductType.diet), + ), + ], ], ), const SizedBox(height: 20), @@ -109,21 +175,11 @@ class _MyDietWidgetState extends State { child: CustomOutlinedButton( onPressed: () async { if (state.selectedStateIndex == 1) { - final result = await context.read().publishDiet(widget.diet.id); - if (result) { - if (!context.mounted) return; - showBottomSheetWithBlur( - context: context, - child: SuccessPublishedDietOrPlanBottomSheet(title: widget.diet.name), - ); - await context.read().loadAllDiets(); - } else { - showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время'); - } + _publishDiet(widget.diet, context); } else if (state.selectedStateIndex == 2) { - GoRouter.of(context).pushNamed(PersonalDietSuggestionScreen.name, extra: widget.diet.id); + _suggestDiet(widget.diet); } else { - GoRouter.of(context).pushNamed(CreateDietScreen.name, extra: widget.diet); + _editDiet(widget.diet); } }, text: switch (state.selectedStateIndex) { @@ -144,13 +200,7 @@ class _MyDietWidgetState extends State { const SizedBox(width: 6), CustomOutlinedButton( onPressed: () async { - final box = context.findRenderObject() as RenderBox?; - await SharePlus.instance.share( - ShareParams( - uri: Uri.parse('${sl().baseUrl}/ru/diet/${widget.diet.id}'), - sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size, - ), - ); + await _shareDiet(widget.diet.id); }, height: 52, width: 52, @@ -169,8 +219,53 @@ class _MyDietWidgetState extends State { text: '', ), ], + if (state.selectedStateIndex == 2) ...[ + const SizedBox(width: 6), + CustomOutlinedButton( + onPressed: () async { + _createBasedOnChosen(widget.diet, context); + }, + height: 52, + width: 52, + leftIcon: SvgPicture.asset( + 'assets/icons/copy2.svg', + width: 20, + height: 20, + colorFilter: ColorFilter.mode(appTheme.whiteA700, BlendMode.srcIn), + ), + buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.red), + buttonStyle: ButtonStyle( + shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.circular(20))), + backgroundColor: WidgetStatePropertyAll(appTheme.blueA400), + side: const WidgetStatePropertyAll(BorderSide.none), + ), + text: '', + ), + ], ], ), + if (state.selectedStateIndex == 1) ...[ + const SizedBox(height: 8), + Row( + children: [ + Expanded( + child: CustomOutlinedButton( + onPressed: () async { + GoRouter.of(context).pushNamed(DietPreviewScreen.name, extra: widget.diet); + }, + text: 'Превью', + height: 52, + buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.black), + buttonStyle: ButtonStyle( + shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.circular(20))), + backgroundColor: WidgetStatePropertyAll(appTheme.lightGray0), + side: const WidgetStatePropertyAll(BorderSide.none), + ), + ), + ), + ], + ), + ], const SizedBox(height: 8), Row( children: [ @@ -178,21 +273,9 @@ class _MyDietWidgetState extends State { child: CustomOutlinedButton( onPressed: () async { if (state.selectedStateIndex == 0) { - final result = await context.read().unpublishDiet(widget.diet.id); - if (result) { - if (!context.mounted) return; - showBottomSheetWithBlur( - context: context, - height: 250, - child: DietDraftedBottomSheet(title: widget.diet.name), - ); - await context.read().loadAllDiets(); - } else { - showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время'); - } + _unpublishDiet(widget.diet, context); } else { - await showErrorDialog(context, title: 'Предупреждение', description: 'Вы можете создать новую диету на основе существующей.'); - AppRouter.navigatorKey.currentContext?.pushNamed(CreateDietScreen.name, extra: widget.diet); + _editDiet(widget.diet); } }, text: switch (state.selectedStateIndex) { @@ -211,19 +294,23 @@ class _MyDietWidgetState extends State { const SizedBox(width: 6), CustomOutlinedButton( onPressed: () async { - final result = await context.read().deleteDiet(widget.diet.id); - if (result) { - if (!context.mounted) return; - showBottomSheetWithBlur( - context: context, - child: DietDeletedBottomSheet(title: widget.diet.name), - ); - await context.read().loadAllDiets(); + if (state.selectedStateIndex == 1) { + _createBasedOnChosen(widget.diet, context); + return; } + _deleteDiet(widget.diet, context); }, height: 52, width: 52, - leftIcon: SvgPicture.asset('assets/icons/trash_can.svg', width: 20, height: 20), + leftIcon: switch (state.selectedStateIndex) { + 1 => SvgPicture.asset( + 'assets/icons/copy2.svg', + width: 20, + height: 20, + colorFilter: ColorFilter.mode(appTheme.gray, BlendMode.srcIn), + ), + _ => SvgPicture.asset('assets/icons/trash_can.svg', width: 20, height: 20), + }, buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.red), buttonStyle: ButtonStyle( shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.circular(20))), @@ -245,4 +332,3 @@ class _MyDietWidgetState extends State { ); } } -