feat(my_diets): draft diets fixes

This commit is contained in:
misha
2026-02-04 11:38:53 +03:00
parent 3dc36ab52d
commit 42ffde820d
8 changed files with 280 additions and 242 deletions

View File

@@ -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<String, dynamic>;
final diet = data['diet'] as Diet?;
final isEditing = data['isEditing'] as bool?;
return CreateDietScreen(diet: diet, isEditing: isEditing);
},
),
GoRoute(

View File

@@ -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)],
),
],
),

View File

@@ -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<CreateDietScreenContent> {
else
CustomOutlinedButton(
onPressed: () async {
final result = await context.read<CreateDietCubit>().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<CreateDietCubit>().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<CreateDietCubit>().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(

View File

@@ -32,7 +32,7 @@ class CreateDietCubit extends Cubit<CreateDietState> {
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<List<List<DailySchedule>>> dailySchedule = List.generate(countOfDays, (index) => [[], [], [], [], []]);
for (final dayEntry in dailyScheduleWithNutrition.entries) {
@@ -199,7 +199,10 @@ class CreateDietCubit extends Cubit<CreateDietState> {
if (!_isFromValid()) return null;
final authorProfile = await profileRepository.getMyProfile();
final imageBytes = (await state.dietData.image?.readAsBytes()) ?? <int>[];
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<double>(0, (prev, element) {
return prev +
@@ -259,7 +262,7 @@ class CreateDietCubit extends Cubit<CreateDietState> {
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<CreateDietState> {
}
Future<String?> updateDiet(int dietId) async {
if (!_isFromValid()) return 'error';
// if (!_isFromValid()) return 'error';
try {
File? image;
if (state.dietData.image != null) {

View File

@@ -31,11 +31,13 @@ class DietPreviewScreen extends StatefulWidget {
class _DietPreviewScreenState extends State<DietPreviewScreen> {
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<DietPreviewScreen> {
),
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<DietPreviewScreen> {
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<DietPreviewScreen> {
},
itemCount: widget.diet.dailySchedule.length,
),
const MainBackgroundBouble(
padding: EdgeInsets.symmetric(vertical: 16),
child: Column(
children: [
SizedBox(height: 16),
],
),
),
],
),
),

View File

@@ -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<int, Map<int, Map<int, DailySchedule>>> dailySchedule,
@JsonKey(name: "type", fromJson: parseDietTypeFromJson) required DietType type,
@JsonKey(name: "description_md", defaultValue: '') required String details,
@JsonKey(name: "daily_schedule", defaultValue: {}) 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,
@@ -40,12 +40,20 @@ abstract class Diet with _$Diet {
factory Diet.fromJson(Map<String, dynamic> json) => _$DietFromJson(json);
}
DietType parseDietTypeFromJson(Map<String, dynamic>? 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<String, dynamic> 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

View File

@@ -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<MyDietsCubit>().loadCurrentDiets(query: query == '' ? null : query);
},
);
EasyDebounce.debounce('searchDiets', const Duration(milliseconds: 500), () {
context.read<MyDietsCubit>().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<MyDietsCubit, MyDietsState>(
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<MyDietsCubit>().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<MyDietsCubit>(),
),
),
child: FilterBadge(filterBottomSheet: DietFilterBottomSheet(myDietsCubit: context.read<MyDietsCubit>())),
),
],
);

View File

@@ -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<MyDietWidget> {
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<void> _shareDiet(int dietId) async {
await SharePlus.instance.share(ShareParams(uri: Uri.parse('${sl<AppConfig>().baseUrl}/ru/diet/$dietId')));
}
Future<void> _publishDiet(Diet diet, BuildContext context) async {
final result = await context.read<MyDietsCubit>().publishDiet(widget.diet.id);
if (result) {
if (!context.mounted) return;
showBottomSheetWithBlur(
context: context,
child: SuccessPublishedDietOrPlanBottomSheet(title: widget.diet.name),
);
await context.read<MyDietsCubit>().loadAllDiets();
} else {
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
}
}
void _unpublishDiet(Diet diet, BuildContext context) async {
final result = await context.read<MyDietsCubit>().unpublishDiet(widget.diet.id);
if (result) {
if (!context.mounted) return;
showBottomSheetWithBlur(
context: context,
height: 250,
child: DietDraftedBottomSheet(title: widget.diet.name),
);
await context.read<MyDietsCubit>().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<MyDietsCubit>().deleteDiet(widget.diet.id);
if (result) {
if (!context.mounted) return;
showBottomSheetWithBlur(
context: context,
child: DietDeletedBottomSheet(title: widget.diet.name),
);
await context.read<MyDietsCubit>().loadAllDiets();
}
}
@override
Widget build(BuildContext context) {
final s = S.of(context);
@@ -45,28 +104,34 @@ class _MyDietWidgetState extends State<MyDietWidget> {
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<AppConfig>().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<AppConfig>().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<MyDietWidget> {
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<MyDietWidget> {
child: CustomOutlinedButton(
onPressed: () async {
if (state.selectedStateIndex == 1) {
final result = await context.read<MyDietsCubit>().publishDiet(widget.diet.id);
if (result) {
if (!context.mounted) return;
showBottomSheetWithBlur(
context: context,
child: SuccessPublishedDietOrPlanBottomSheet(title: widget.diet.name),
);
await context.read<MyDietsCubit>().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<MyDietWidget> {
const SizedBox(width: 6),
CustomOutlinedButton(
onPressed: () async {
final box = context.findRenderObject() as RenderBox?;
await SharePlus.instance.share(
ShareParams(
uri: Uri.parse('${sl<AppConfig>().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<MyDietWidget> {
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<MyDietWidget> {
child: CustomOutlinedButton(
onPressed: () async {
if (state.selectedStateIndex == 0) {
final result = await context.read<MyDietsCubit>().unpublishDiet(widget.diet.id);
if (result) {
if (!context.mounted) return;
showBottomSheetWithBlur(
context: context,
height: 250,
child: DietDraftedBottomSheet(title: widget.diet.name),
);
await context.read<MyDietsCubit>().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<MyDietWidget> {
const SizedBox(width: 6),
CustomOutlinedButton(
onPressed: () async {
final result = await context.read<MyDietsCubit>().deleteDiet(widget.diet.id);
if (result) {
if (!context.mounted) return;
showBottomSheetWithBlur(
context: context,
child: DietDeletedBottomSheet(title: widget.diet.name),
);
await context.read<MyDietsCubit>().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<MyDietWidget> {
);
}
}