forked from eatvio/eatvio_app
Merge pull request 'main' (#461) from main into test-flight
Reviewed-on: eatvio/eatvio_app#461
This commit is contained in:
@@ -388,7 +388,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 15;
|
||||
CURRENT_PROJECT_VERSION = 16;
|
||||
DEVELOPMENT_TEAM = VL68C36HYS;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
@@ -538,7 +538,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 15;
|
||||
CURRENT_PROJECT_VERSION = 16;
|
||||
DEVELOPMENT_TEAM = VL68C36HYS;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
@@ -576,7 +576,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 15;
|
||||
CURRENT_PROJECT_VERSION = 16;
|
||||
DEVELOPMENT_TEAM = VL68C36HYS;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>15</string>
|
||||
<string>16</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
|
||||
@@ -43,7 +43,7 @@ import 'package:eatvio_app/features/profile/presentation/pages/subs_screen.dart'
|
||||
import 'package:eatvio_app/features/recipe/presentation/about_recipe_screen.dart';
|
||||
import 'package:eatvio_app/features/create_recipe/presentation/pages/recipe_create_screen.dart';
|
||||
import 'package:eatvio_app/features/registration/presentation/pages/registration_screen.dart';
|
||||
import 'package:eatvio_app/features/registration/presentation/pages/webview_registration_screen.dart';
|
||||
import 'package:eatvio_app/features/registration/presentation/pages/verify_screen.dart';
|
||||
import 'package:eatvio_app/features/settings/entities/settings_entity.dart';
|
||||
import 'package:eatvio_app/features/settings/presentation/pages/change_email_screen.dart';
|
||||
import 'package:eatvio_app/features/settings/presentation/pages/change_password_screen.dart';
|
||||
@@ -76,7 +76,7 @@ class AppRouter {
|
||||
name: 'authScreen',
|
||||
builder: (context, state) => const AuthScreen(),
|
||||
routes: [
|
||||
GoRoute(path: 'webView-registration-screen', name: 'webViewRegistrationScreen', builder: (context, state) => const WebViewRegistrationScreen()),
|
||||
// GoRoute(path: 'webView-registration-screen', name: 'webViewRegistrationScreen', builder: (context, state) => const WebViewRegistrationScreen()),
|
||||
GoRoute(path: 'registration-screen', name: 'registrationScreen', builder: (context, state) => const RegistrationScreen()),
|
||||
],
|
||||
),
|
||||
@@ -103,7 +103,15 @@ class AppRouter {
|
||||
return const NoTransitionPage(child: ProfileScreen());
|
||||
},
|
||||
),
|
||||
GoRoute(path: '/feed-screen', name: 'feedScreen', pageBuilder: (context, state) => const NoTransitionPage(child: FeedScreen())),
|
||||
GoRoute(
|
||||
path: '/feed-screen',
|
||||
name: 'feedScreen',
|
||||
pageBuilder: (context, state) => const NoTransitionPage(child: FeedScreen()),
|
||||
redirect: (context, state) async {
|
||||
await sl<WsManager>().initialize();
|
||||
return null;
|
||||
},
|
||||
),
|
||||
GoRoute(path: '/diary-screen', name: 'diaryScreen', pageBuilder: (context, state) => const NoTransitionPage(child: DiaryScreen())),
|
||||
GoRoute(
|
||||
path: ConsultationsScreen.path,
|
||||
@@ -342,6 +350,11 @@ class AppRouter {
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/verify-screen',
|
||||
name: 'verifyScreen',
|
||||
builder: (context, state) => VerifyScreen(passKey: state.extra as String),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// String baseUrl = 'https://eatvio.ru';
|
||||
String baseUrl = 'https://dev2.eatvio.com';
|
||||
String baseUrl = 'https://eatvio.ru';
|
||||
// String baseUrl = 'https://dev2.eatvio.com';
|
||||
|
||||
const List<int> durationValues = [7, 14, 21];
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'selection_popup_model.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class SelectionPopupModel with _$SelectionPopupModel {
|
||||
abstract class SelectionPopupModel with _$SelectionPopupModel {
|
||||
const factory SelectionPopupModel({
|
||||
int? id,
|
||||
required String title,
|
||||
|
||||
29
lib/core/dio/phone_verify_error_interceptor.dart
Normal file
29
lib/core/dio/phone_verify_error_interceptor.dart
Normal file
@@ -0,0 +1,29 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/core/app_router.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/features/profile/repositories/profile_repository.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class PhoneVerifyErrorInterceptor extends Interceptor {
|
||||
@override
|
||||
void onError(DioException err, ErrorInterceptorHandler handler) async {
|
||||
if (err.response?.statusCode == 405) {
|
||||
final data = err.response?.data;
|
||||
|
||||
if (data is Map<String, dynamic> && data['type'] == 'unverificated_phone') {
|
||||
final String phone = await sl<ProfileRepository>().getMyPhone();
|
||||
final String passKey = await sl<ProfileRepository>().getPassKey(phone);
|
||||
AppRouter.navigatorKey.currentContext?.pushNamed('verifyScreen', extra: passKey);
|
||||
return handler.reject(
|
||||
DioException(
|
||||
requestOptions: err.requestOptions,
|
||||
response: err.response,
|
||||
error: 'Phone number not verified',
|
||||
type: DioExceptionType.badResponse,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
return handler.next(err);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/dio/phone_verify_error_interceptor.dart';
|
||||
import 'package:eatvio_app/core/interceptors/token_interceptor.dart';
|
||||
import 'package:eatvio_app/core/websockets/ws_manager.dart';
|
||||
import 'package:eatvio_app/core/websockets/ws_service.dart';
|
||||
@@ -64,6 +65,8 @@ import 'package:eatvio_app/features/my_finance/services/cards_service.dart';
|
||||
import 'package:eatvio_app/features/my_plans/presentation/cubit/my_plans_cubit.dart';
|
||||
import 'package:eatvio_app/features/my_plans/repositories/my_diets_repository.dart';
|
||||
import 'package:eatvio_app/features/my_plans/services/my_plans_service.dart';
|
||||
import 'package:eatvio_app/features/notifications/repository/notifications_repository.dart';
|
||||
import 'package:eatvio_app/features/notifications/service/notifications_service.dart';
|
||||
import 'package:eatvio_app/features/order_info/cubit/order_info_cubit.dart';
|
||||
import 'package:eatvio_app/features/plans/repositories/plans_repository.dart';
|
||||
import 'package:eatvio_app/features/plans/services/plans_service.dart';
|
||||
@@ -97,7 +100,7 @@ Future<void> init() async {
|
||||
// Cubit singletons
|
||||
sl.registerLazySingleton<WsManager>(() => WsManager(wsService: sl()));
|
||||
sl.registerLazySingleton<NewMessagesCubit>(() => NewMessagesCubit());
|
||||
sl.registerLazySingleton<NotificationsCubit>(() => NotificationsCubit(wsManager: sl()));
|
||||
sl.registerLazySingleton<NotificationsCubit>(() => NotificationsCubit(wsManager: sl(), notificationsRepository: sl()));
|
||||
sl.registerLazySingleton<ProfileAvatarCubit>(() => ProfileAvatarCubit(profileRepository: sl()));
|
||||
sl.registerLazySingleton<ChatsCubit>(() => ChatsCubit(chatRepository: sl(), wsManager: sl(), profileRepository: sl()));
|
||||
|
||||
@@ -122,7 +125,7 @@ Future<void> init() async {
|
||||
sl.registerFactory<CreatePlanCubit>(() => CreatePlanCubit(createPlanRepository: sl()));
|
||||
sl.registerFactory<FeedProfileCubit>(() => FeedProfileCubit(profileRepository: sl()));
|
||||
sl.registerFactory<AboutPlanCubit>(() => AboutPlanCubit(planRepository: sl()));
|
||||
sl.registerFactory<CreateRecipeCubit>(() => CreateRecipeCubit(recipeRepository: sl()));
|
||||
sl.registerFactory<CreateRecipeCubit>(() => CreateRecipeCubit(recipeRepository: sl(), glossaryRepository: sl()));
|
||||
sl.registerFactory<MyDietsCubit>(() => MyDietsCubit(myDietsRepository: sl(), createDietRepository: sl()));
|
||||
sl.registerFactory<MyPlansCubit>(() => MyPlansCubit(myPlansRepository: sl()));
|
||||
sl.registerFactory<UserDetailsCubit>(() => UserDetailsCubit(profileRepository: sl()));
|
||||
@@ -161,6 +164,7 @@ Future<void> init() async {
|
||||
sl.registerLazySingleton<ConsultationsRepository>(() => ConsultationsRepository(consultationService: sl()));
|
||||
sl.registerLazySingleton<CancelPlanRepository>(() => CancelPlanRepository(cancelService: sl()));
|
||||
sl.registerLazySingleton<CreatePlanRepository>(() => CreatePlanRepository(createPlanService: sl()));
|
||||
sl.registerLazySingleton<NotificationsRepository>(() => NotificationsRepository(notificationsService: sl()));
|
||||
|
||||
// Services
|
||||
sl.registerLazySingleton<ChatService>(() => ChatService(sl()));
|
||||
@@ -188,10 +192,24 @@ Future<void> init() async {
|
||||
sl.registerLazySingleton<ConsultationService>(() => ConsultationService(sl()));
|
||||
sl.registerLazySingleton<CancelPlanService>(() => CancelPlanService(sl()));
|
||||
sl.registerLazySingleton<CreatePlanService>(() => CreatePlanService(sl()));
|
||||
sl.registerLazySingleton<NotificationsService>(() => NotificationsService(sl()));
|
||||
|
||||
// External
|
||||
sl.registerLazySingleton(
|
||||
() => Dio(BaseOptions(baseUrl: baseUrl, headers: {'accept': 'application/json', 'content-type': 'application/json'}))..interceptors.add(TokenInterceptor()),
|
||||
() => Dio(
|
||||
BaseOptions(
|
||||
baseUrl: baseUrl,
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
),
|
||||
)..interceptors.addAll(
|
||||
[
|
||||
TokenInterceptor(),
|
||||
PhoneVerifyErrorInterceptor(),
|
||||
],
|
||||
),
|
||||
);
|
||||
sl.registerLazySingleton(() async => const FlutterSecureStorage());
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ part 'pagination_model.freezed.dart';
|
||||
part 'pagination_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class Links with _$Links {
|
||||
abstract class Links with _$Links {
|
||||
const factory Links({
|
||||
@JsonKey(name: "first") required String first,
|
||||
@JsonKey(name: "last") required String last,
|
||||
@@ -16,7 +16,7 @@ class Links with _$Links {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Meta with _$Meta {
|
||||
abstract class Meta with _$Meta {
|
||||
const factory Meta({
|
||||
@JsonKey(name: "current_page") required int currentPage,
|
||||
@JsonKey(name: "from") required dynamic from,
|
||||
@@ -34,7 +34,7 @@ class Meta with _$Meta {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Link with _$Link {
|
||||
abstract class Link with _$Link {
|
||||
const factory Link({
|
||||
@JsonKey(name: "url") String? url,
|
||||
@JsonKey(name: "label") String? label,
|
||||
|
||||
@@ -18,8 +18,7 @@ class WsManager {
|
||||
PrivateChannel? _privateDirectChatChannel;
|
||||
|
||||
Stream<ChannelReadEvent>? get newMessagesStream => _privateMessagesChannel?.bind('App\\Events\\ChatMessageUpdated');
|
||||
|
||||
// TODO: change name of event to a right one
|
||||
|
||||
Stream<ChannelReadEvent>? get newNotificationsStream => _privateNotificationChannel?.bind('Illuminate\\Notifications\\Events\\BroadcastNotificationCreated');
|
||||
|
||||
Stream<ChannelReadEvent>? get directChatStream => _privateDirectChatChannel?.bind('App\\Events\\ChatMessageUpdated');
|
||||
|
||||
@@ -6,7 +6,9 @@ import 'package:eatvio_app/core/utils/toasts.dart';
|
||||
import 'package:eatvio_app/core/widgets/show_error_dialog.dart';
|
||||
import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_cubit.dart';
|
||||
import 'package:eatvio_app/features/complaining/presentation/complaining_bottom_sheet.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart';
|
||||
import 'package:eatvio_app/features/create_recipe/presentation/pages/recipe_create_screen.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart';
|
||||
import 'package:eatvio_app/features/feed/models/recipes_model.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
|
||||
@@ -28,6 +30,7 @@ class CustomPopup extends StatelessWidget {
|
||||
final int? id;
|
||||
final FeedBlogPostModel? feedBlogPostModel;
|
||||
final Recipe? recipe;
|
||||
final Diet? diet;
|
||||
|
||||
const CustomPopup({
|
||||
super.key,
|
||||
@@ -38,6 +41,7 @@ class CustomPopup extends StatelessWidget {
|
||||
this.slug,
|
||||
this.feedBlogPostModel,
|
||||
this.recipe,
|
||||
this.diet,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -87,18 +91,25 @@ class CustomPopup extends StatelessWidget {
|
||||
});
|
||||
addItem('assets/icons/share2.svg', 'Поделиться', () {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
Share.shareUri(
|
||||
Uri.parse(
|
||||
'$baseUrl/ru/$path',
|
||||
SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse(
|
||||
'$baseUrl/ru/$path',
|
||||
),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
);
|
||||
});
|
||||
addItem('assets/icons/edit_paint.svg', 'Редактировать', () {
|
||||
addItem('assets/icons/edit_paint.svg', 'Редактировать', () async {
|
||||
if (complainedContentType == ComplainedContent.posts) {
|
||||
GoRouter.of(context).pushNamed('blogPostCreateScreen', extra: feedBlogPostModel);
|
||||
} else if (complainedContentType == ComplainedContent.recipe) {
|
||||
GoRouter.of(context).pushNamed(RecipeCreateScreen.name, extra: recipe);
|
||||
} else if (complainedContentType == ComplainedContent.diet) {
|
||||
await showErrorDialog(context,
|
||||
title: 'Предупреждение', description: 'Диета будет перенесена в архив. Вы создаете новую диету на основе существующей.');
|
||||
await sl<MyDietsCubit>().unpublishDiet(id!);
|
||||
GoRouter.of(context).pushNamed(CreateDietScreen.name, extra: diet);
|
||||
} else {
|
||||
showInfoToast('В разработке');
|
||||
return;
|
||||
@@ -161,11 +172,13 @@ class CustomPopup extends StatelessWidget {
|
||||
});
|
||||
addItem('assets/icons/share2.svg', 'Поделиться', () {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
Share.shareUri(
|
||||
Uri.parse(
|
||||
'$baseUrl/ru/$path',
|
||||
SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse(
|
||||
'$baseUrl/ru/$path',
|
||||
),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
);
|
||||
});
|
||||
addItem('assets/icons/complain.svg', 'Пожаловаться', () {
|
||||
|
||||
@@ -5,8 +5,8 @@ import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
void showErrorDialog(BuildContext context, {String? title, String? description}) {
|
||||
showDialog<void>(
|
||||
Future<void> showErrorDialog(BuildContext context, {String? title, String? description}) async {
|
||||
return await showDialog<void>(
|
||||
context: context,
|
||||
barrierColor: Colors.transparent,
|
||||
builder: (BuildContext context) {
|
||||
|
||||
@@ -5,7 +5,7 @@ part 'credentials_model.freezed.dart';
|
||||
part 'credentials_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class CredentialsModel with _$CredentialsModel {
|
||||
abstract class CredentialsModel with _$CredentialsModel {
|
||||
const factory CredentialsModel({
|
||||
required String email,
|
||||
required String password,
|
||||
|
||||
@@ -5,7 +5,7 @@ part 'token_model.freezed.dart';
|
||||
part 'token_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class TokenModel with _$TokenModel {
|
||||
abstract class TokenModel with _$TokenModel {
|
||||
const factory TokenModel({
|
||||
required String token,
|
||||
}) = _TokenModel;
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'auth_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class AuthState with _$AuthState {
|
||||
abstract class AuthState with _$AuthState {
|
||||
const factory AuthState() = _AuthState;
|
||||
|
||||
const factory AuthState.loading() = AuthStateLoading;
|
||||
|
||||
@@ -119,7 +119,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
children: [
|
||||
const Text('Нет аккаунта? '),
|
||||
GestureDetector(
|
||||
onTap: () => GoRouter.of(context).goNamed('webViewRegistrationScreen'),
|
||||
onTap: () => GoRouter.of(context).goNamed('registrationScreen'),
|
||||
child: Text(
|
||||
'Зарегистрироваться',
|
||||
style: TextStyle(
|
||||
|
||||
@@ -242,9 +242,11 @@ class _BlogPostScreenState extends State<BlogPostScreen> {
|
||||
icon: Icons.reply_rounded,
|
||||
onTap: () {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
Share.shareUri(
|
||||
Uri.parse('$baseUrl/ru/blog_post/${widget.blogPost.id}'),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse('$baseUrl/ru/blog_post/${widget.blogPost.id}'),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -5,7 +5,7 @@ part '../../../presentation/feed/blog_post_create_screen/models/blog_post_create
|
||||
part '../../../presentation/feed/blog_post_create_screen/models/blog_post_create_credentials_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class BlogPostCreateCredentialsModel with _$BlogPostCreateCredentialsModel {
|
||||
abstract class BlogPostCreateCredentialsModel with _$BlogPostCreateCredentialsModel {
|
||||
const factory BlogPostCreateCredentialsModel({
|
||||
required String title,
|
||||
required String description,
|
||||
|
||||
@@ -5,7 +5,7 @@ part 'calendar_model.freezed.dart';
|
||||
part 'calendar_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class CalendarModel with _$CalendarModel {
|
||||
abstract class CalendarModel with _$CalendarModel {
|
||||
const factory CalendarModel({
|
||||
required String date,
|
||||
@JsonKey(name: 'is_current_day_included_in_the_chosen_diet_progression') required bool isCurrentDayIncludedInTheChosenDietProgression,
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'cancel_plan_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class CancelPlanState with _$CancelPlanState {
|
||||
abstract class CancelPlanState with _$CancelPlanState {
|
||||
|
||||
factory CancelPlanState() = _CancelPlanState;
|
||||
}
|
||||
@@ -4,7 +4,7 @@ part 'cancelation_model.freezed.dart';
|
||||
part 'cancelation_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class CancelationModel with _$CancelationModel {
|
||||
abstract class CancelationModel with _$CancelationModel {
|
||||
factory CancelationModel({
|
||||
required String description,
|
||||
required int price,
|
||||
|
||||
@@ -5,7 +5,7 @@ part 'chat_message_model.freezed.dart';
|
||||
part 'chat_message_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class ChatMessageModel with _$ChatMessageModel {
|
||||
abstract class ChatMessageModel with _$ChatMessageModel {
|
||||
const factory ChatMessageModel({
|
||||
required int id,
|
||||
required String data,
|
||||
|
||||
@@ -6,7 +6,7 @@ part 'chat_messages_model.freezed.dart';
|
||||
part 'chat_messages_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class ChatMessagesModel with _$ChatMessagesModel {
|
||||
abstract class ChatMessagesModel with _$ChatMessagesModel {
|
||||
const factory ChatMessagesModel({
|
||||
@JsonKey(name: 'data') required List<ChatMessageModel> messagesList,
|
||||
required ChatMetaModel meta,
|
||||
|
||||
@@ -4,7 +4,7 @@ part 'chat_meta_model.freezed.dart';
|
||||
part 'chat_meta_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class ChatMetaModel with _$ChatMetaModel {
|
||||
abstract class ChatMetaModel with _$ChatMetaModel {
|
||||
const factory ChatMetaModel({
|
||||
@JsonKey(name: "last_page") required int lastPage,
|
||||
}) = _ChatMetaModel;
|
||||
|
||||
@@ -7,7 +7,7 @@ part 'chat_room_model.freezed.dart';
|
||||
part 'chat_room_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class ChatRoomModel with _$ChatRoomModel {
|
||||
abstract class ChatRoomModel with _$ChatRoomModel {
|
||||
const factory ChatRoomModel({
|
||||
required String id,
|
||||
@JsonKey(name: 'chat_room_setting') required ChatRoomSettingsModel chatRoomSettings,
|
||||
@@ -20,7 +20,7 @@ class ChatRoomModel with _$ChatRoomModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Users with _$Users {
|
||||
abstract class Users with _$Users {
|
||||
const factory Users({
|
||||
|
||||
@JsonKey(name: 'from_chat_user') required ChatUserModel sender,
|
||||
|
||||
@@ -6,7 +6,7 @@ part 'chat_room_pagination_model.freezed.dart';
|
||||
part 'chat_room_pagination_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class ChatRoomPaginationModel with _$ChatRoomPaginationModel {
|
||||
abstract class ChatRoomPaginationModel with _$ChatRoomPaginationModel {
|
||||
const factory ChatRoomPaginationModel({
|
||||
@JsonKey(name: "chatRooms") required ChatRooms chatRooms,
|
||||
@JsonKey(name: "number_of_unread_chat_rooms") required int unreadChatsCount,
|
||||
@@ -16,7 +16,7 @@ class ChatRoomPaginationModel with _$ChatRoomPaginationModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ChatRooms with _$ChatRooms {
|
||||
abstract class ChatRooms with _$ChatRooms {
|
||||
const factory ChatRooms({
|
||||
@JsonKey(name: "data") required List<ChatRoom> data,
|
||||
@JsonKey(name: "last_page") required int lastPage,
|
||||
@@ -27,7 +27,7 @@ class ChatRooms with _$ChatRooms {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ChatRoom with _$ChatRoom {
|
||||
abstract class ChatRoom with _$ChatRoom {
|
||||
const factory ChatRoom({
|
||||
@JsonKey(name: "id") required String id,
|
||||
@JsonKey(name: "room_name") required String roomName,
|
||||
@@ -48,7 +48,7 @@ ChatMessageModel? parseLastMessage(dynamic json) {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Users with _$Users {
|
||||
abstract class Users with _$Users {
|
||||
const factory Users({
|
||||
@JsonKey(name: "from_chat_user") required ChatUser fromChatUser,
|
||||
@JsonKey(name: "to_chat_user") required ChatUser toChatUser,
|
||||
@@ -58,7 +58,7 @@ class Users with _$Users {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ChatUser with _$ChatUser {
|
||||
abstract class ChatUser with _$ChatUser {
|
||||
const factory ChatUser({
|
||||
@JsonKey(name: "id") required int id,
|
||||
@JsonKey(name: "name") required String name,
|
||||
|
||||
@@ -4,7 +4,7 @@ part 'chat_room_settings_model.freezed.dart';
|
||||
part 'chat_room_settings_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class ChatRoomSettingsModel with _$ChatRoomSettingsModel {
|
||||
abstract class ChatRoomSettingsModel with _$ChatRoomSettingsModel {
|
||||
const factory ChatRoomSettingsModel({
|
||||
required int id,
|
||||
@JsonKey(name: 'room_name') required String roomName,
|
||||
|
||||
@@ -4,7 +4,7 @@ part 'chat_user_model.freezed.dart';
|
||||
part 'chat_user_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class ChatUserModel with _$ChatUserModel {
|
||||
abstract class ChatUserModel with _$ChatUserModel {
|
||||
const factory ChatUserModel({
|
||||
required int id,
|
||||
required String name,
|
||||
|
||||
@@ -4,7 +4,7 @@ part 'payment_model.freezed.dart';
|
||||
part 'payment_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class PaymentModel with _$PaymentModel {
|
||||
abstract class PaymentModel with _$PaymentModel {
|
||||
|
||||
factory PaymentModel({
|
||||
@JsonKey(name: 'checkout_url') required String paymentUrl,
|
||||
|
||||
@@ -4,7 +4,7 @@ part 'plan_complain_model.freezed.dart';
|
||||
part 'plan_complain_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class PlanComplainModel with _$PlanComplainModel {
|
||||
abstract class PlanComplainModel with _$PlanComplainModel {
|
||||
|
||||
factory PlanComplainModel({
|
||||
@JsonKey(name: 'description') required String description,
|
||||
|
||||
@@ -4,7 +4,7 @@ part 'websocket_info_model.freezed.dart';
|
||||
part 'websocket_info_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class WebsocketInfoModel with _$WebsocketInfoModel {
|
||||
abstract class WebsocketInfoModel with _$WebsocketInfoModel {
|
||||
const factory WebsocketInfoModel({
|
||||
required String key,
|
||||
required String host,
|
||||
|
||||
@@ -5,7 +5,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'chats_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class ChatsState with _$ChatsState {
|
||||
abstract class ChatsState with _$ChatsState {
|
||||
|
||||
factory ChatsState({
|
||||
@Default(BaseState.initial) BaseState state,
|
||||
|
||||
@@ -4,7 +4,7 @@ part 'complain_model.freezed.dart';
|
||||
part 'complain_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class ComplainModel with _$ComplainModel {
|
||||
abstract class ComplainModel with _$ComplainModel {
|
||||
|
||||
const factory ComplainModel({
|
||||
@Default(null) String? description,
|
||||
|
||||
@@ -6,7 +6,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'complaining_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class ComplainingState with _$ComplainingState {
|
||||
abstract class ComplainingState with _$ComplainingState {
|
||||
|
||||
factory ComplainingState({
|
||||
@Default(false) bool isLoading,
|
||||
|
||||
@@ -5,7 +5,7 @@ part 'consultant_profile_model.freezed.dart';
|
||||
part 'consultant_profile_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class ConsultantProfileModel with _$ConsultantProfileModel {
|
||||
abstract class ConsultantProfileModel with _$ConsultantProfileModel {
|
||||
const factory ConsultantProfileModel({
|
||||
@JsonKey(name: 'has_medical_education', includeIfNull: false) bool? hasMedicalEducation,
|
||||
@JsonKey(name: 'social_links', includeIfNull: false) SocialLinksModel? socialLinks,
|
||||
@@ -17,7 +17,7 @@ class ConsultantProfileModel with _$ConsultantProfileModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SocialLinksModel with _$SocialLinksModel {
|
||||
abstract class SocialLinksModel with _$SocialLinksModel {
|
||||
const factory SocialLinksModel({
|
||||
@JsonKey(name: 'vk_link', includeIfNull: false) String? vkLink,
|
||||
@JsonKey(name: 'tg_link', includeIfNull: false) String? tgLink,
|
||||
@@ -30,7 +30,7 @@ class SocialLinksModel with _$SocialLinksModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class CertificatesModel with _$CertificatesModel {
|
||||
abstract class CertificatesModel with _$CertificatesModel {
|
||||
const factory CertificatesModel({
|
||||
required String title,
|
||||
required String date,
|
||||
|
||||
@@ -6,7 +6,7 @@ part 'consultation_request_model.freezed.dart';
|
||||
part 'consultation_request_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class ConsultationRequestModel with _$ConsultationRequestModel {
|
||||
abstract class ConsultationRequestModel with _$ConsultationRequestModel {
|
||||
const factory ConsultationRequestModel({
|
||||
@JsonKey(name: 'rating_place') required int ratingPlace,
|
||||
@JsonKey(name: 'education_resource') required EducationModel educationModel,
|
||||
@@ -17,7 +17,7 @@ class ConsultationRequestModel with _$ConsultationRequestModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class CertPhotosModel with _$CertPhotosModel {
|
||||
abstract class CertPhotosModel with _$CertPhotosModel {
|
||||
const factory CertPhotosModel({
|
||||
required String url,
|
||||
}) = _CertPhotosModel;
|
||||
|
||||
@@ -4,7 +4,7 @@ part 'rating_model.freezed.dart';
|
||||
part 'rating_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class RatingModel with _$RatingModel {
|
||||
abstract class RatingModel with _$RatingModel {
|
||||
|
||||
factory RatingModel({
|
||||
required DateTime date,
|
||||
|
||||
@@ -6,7 +6,7 @@ part 'consultation_model.freezed.dart';
|
||||
part 'consultation_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class ConsultationModel with _$ConsultationModel {
|
||||
abstract class ConsultationModel with _$ConsultationModel {
|
||||
factory ConsultationModel({
|
||||
required int id,
|
||||
@JsonKey(name: 'last_state') required ChosenPlanStateEnum chosenPlanState,
|
||||
@@ -26,7 +26,7 @@ class ConsultationModel with _$ConsultationModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class DiaryModel with _$DiaryModel {
|
||||
abstract class DiaryModel with _$DiaryModel {
|
||||
factory DiaryModel({
|
||||
required int id,
|
||||
required DiaryStateEnum state,
|
||||
@@ -58,7 +58,7 @@ enum DiaryStateEnum {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ConsultationPaginationModel with _$ConsultationPaginationModel {
|
||||
abstract class ConsultationPaginationModel with _$ConsultationPaginationModel {
|
||||
factory ConsultationPaginationModel({required List<ConsultationModel> data}) = _ConsultationPaginationModel;
|
||||
|
||||
factory ConsultationPaginationModel.fromJson(Map<String, dynamic> json) => _$ConsultationPaginationModelFromJson(json);
|
||||
|
||||
@@ -5,7 +5,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'consultations_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class ConsultationsState with _$ConsultationsState {
|
||||
abstract class ConsultationsState with _$ConsultationsState {
|
||||
|
||||
factory ConsultationsState({
|
||||
@Default(BaseState.initial) BaseState state,
|
||||
|
||||
@@ -5,7 +5,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'create_diet_model.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class CreateDietModel with _$CreateDietModel {
|
||||
abstract class CreateDietModel with _$CreateDietModel {
|
||||
const CreateDietModel._();
|
||||
|
||||
const factory CreateDietModel({
|
||||
|
||||
@@ -27,8 +27,11 @@ 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, this.diet});
|
||||
|
||||
final Diet? diet;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
@@ -40,6 +43,7 @@ class CreateDietScreen extends StatelessWidget {
|
||||
|
||||
class CreateDietScreenContent extends StatefulWidget {
|
||||
const CreateDietScreenContent({super.key, this.diet});
|
||||
|
||||
final Diet? diet;
|
||||
|
||||
@override
|
||||
@@ -95,7 +99,7 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
widget.diet != null ? 'Редактирование диеты' : s.create_diet,
|
||||
s.create_diet,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
@@ -151,7 +155,7 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
|
||||
const SizedBox(height: 12),
|
||||
const DietDurationDropDownWidget(),
|
||||
const SizedBox(height: 12),
|
||||
const StatusDropDownWidget(),
|
||||
StatusDropDownWidget(dietState: widget.diet?.state),
|
||||
const SizedBox(height: 16),
|
||||
const DietTypeChoiceWidget(),
|
||||
const SizedBox(height: 16),
|
||||
@@ -247,23 +251,13 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
|
||||
else
|
||||
CustomOutlinedButton(
|
||||
onPressed: () async {
|
||||
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}');
|
||||
}
|
||||
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: widget.diet != null ? s.save : s.publish,
|
||||
text: s.publish,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
|
||||
@@ -12,6 +12,8 @@ import 'package:eatvio_app/features/glossary/repositories/glossary_repository.da
|
||||
import 'package:eatvio_app/features/profile/repositories/profile_repository.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
final CreateDietRepository createDietRepository;
|
||||
@@ -319,18 +321,38 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
return diet;
|
||||
}
|
||||
|
||||
Future<File> downloadImageAsFile(String imageUrl) async {
|
||||
final response = await http.get(Uri.parse(imageUrl));
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
final filePath = '${tempDir.path}/${imageUrl.split('/').last}';
|
||||
final file = File(filePath);
|
||||
return await file.writeAsBytes(response.bodyBytes);
|
||||
} else {
|
||||
throw Exception('Не удалось загрузить изображение');
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> createDiet() async {
|
||||
emit(state.copyWith(isDietPublishing: true));
|
||||
if (!_isFromValid()) return 'validation';
|
||||
try {
|
||||
final image = await ImageCompressUtility.compressFile(File(state.dietData.image!.path));
|
||||
File? file;
|
||||
if (state.dietData.imageUrl != null) {
|
||||
file = await downloadImageAsFile(state.dietData.imageUrl!);
|
||||
}
|
||||
File? image;
|
||||
if (state.dietData.image?.path != null) {
|
||||
image = await ImageCompressUtility.compressFile(File(state.dietData.image!.path));
|
||||
}
|
||||
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,
|
||||
image: image ?? file!,
|
||||
meatType: state.dietData.type['meat'] == true ? '1' : '0',
|
||||
carbType: state.dietData.type['carb'] == true ? '1' : '0',
|
||||
vegType: state.dietData.type['veg'] == true ? '1' : '0',
|
||||
|
||||
@@ -6,7 +6,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'create_diet_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class CreateDietState with _$CreateDietState {
|
||||
abstract class CreateDietState with _$CreateDietState {
|
||||
const factory CreateDietState({
|
||||
required bool isLoading,
|
||||
required bool isDietPublishing,
|
||||
|
||||
@@ -127,11 +127,12 @@ class _DietImagePickerWidgetState extends State<DietImagePickerWidget> {
|
||||
: Stack(
|
||||
children: [
|
||||
DottedBorder(
|
||||
stackFit: StackFit.expand,
|
||||
dashPattern: const [14, 14],
|
||||
color: state.dietImageError != null ? appTheme.red : appTheme.dottedBorder,
|
||||
borderType: BorderType.RRect,
|
||||
radius: const Radius.circular(12),
|
||||
options: RoundedRectDottedBorderOptions(
|
||||
stackFit: StackFit.expand,
|
||||
dashPattern: const [14, 14],
|
||||
color: state.dietImageError != null ? appTheme.red : appTheme.dottedBorder,
|
||||
radius: const Radius.circular(12),
|
||||
),
|
||||
child: const SizedBox.expand(),
|
||||
),
|
||||
Center(
|
||||
|
||||
@@ -11,7 +11,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
class StatusDropDownWidget extends StatefulWidget {
|
||||
const StatusDropDownWidget({super.key});
|
||||
final DietState? dietState;
|
||||
|
||||
const StatusDropDownWidget({super.key, this.dietState});
|
||||
|
||||
@override
|
||||
State<StatusDropDownWidget> createState() => _StatusDropDownWidgetState();
|
||||
@@ -19,6 +21,7 @@ class StatusDropDownWidget extends StatefulWidget {
|
||||
|
||||
class _StatusDropDownWidgetState extends State<StatusDropDownWidget> {
|
||||
final List<String> dietStateTitles = [];
|
||||
String? currentState;
|
||||
// late final List<DietState> dietStates = DietState.values;
|
||||
|
||||
@override
|
||||
@@ -39,6 +42,13 @@ class _StatusDropDownWidgetState extends State<StatusDropDownWidget> {
|
||||
// }
|
||||
// }
|
||||
}
|
||||
if (widget.dietState != null) {
|
||||
if (widget.dietState == DietState.active) {
|
||||
currentState = dietStateTitles[0];
|
||||
} else if (widget.dietState == DietState.personal) {
|
||||
currentState = dietStateTitles[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -77,7 +87,7 @@ class _StatusDropDownWidgetState extends State<StatusDropDownWidget> {
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
value: state.dietState == DietState.active ? dietStateTitles[0] : state.dietState == DietState.personal ? dietStateTitles[1] : null,
|
||||
value: currentState,
|
||||
),
|
||||
if (state.dietStateError != null) ...[
|
||||
const SizedBox(
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart';
|
||||
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
|
||||
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
@@ -9,7 +5,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'create_plan_model.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class CreatePlanModel with _$CreatePlanModel {
|
||||
abstract class CreatePlanModel with _$CreatePlanModel {
|
||||
const CreatePlanModel._();
|
||||
|
||||
const factory CreatePlanModel({
|
||||
@@ -20,20 +16,5 @@ class CreatePlanModel with _$CreatePlanModel {
|
||||
required List<bool> type,
|
||||
XFile? image,
|
||||
required List<PacketOptions> options,
|
||||
}) = _CreatePlanModel;
|
||||
|
||||
Future<Map<String, dynamic>> toCreationJson() async => {
|
||||
'name': name,
|
||||
'duration': duration,
|
||||
'state': 'active',
|
||||
'description_md': details,
|
||||
'type[slim]': type[0] ? '1' : '0',
|
||||
'type[fit]': type[1] ? '1' : '0',
|
||||
'type[wellness]': type[2] ? '1' : '0',
|
||||
'type[behavior]': type[3] ? '1' : '0',
|
||||
'image': image != null ? await MultipartFile.fromFile((await ImageCompressUtility.compressFile(File(image!.path))).path, filename: image!.name) : null,
|
||||
};
|
||||
|
||||
Map<String, dynamic> toOptionCreationJson() => {'packet1_options': options[0]};
|
||||
Map<String, dynamic> toOptionsCreationJson() => {'packet1_options': options[0], 'packet2_options': options[1], 'packet3_options': options[2]};
|
||||
}) = _CreatePlanModel;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ part 'plan_slug_model.freezed.dart';
|
||||
part 'plan_slug_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class PlanSlugModel with _$PlanSlugModel {
|
||||
abstract class PlanSlugModel with _$PlanSlugModel {
|
||||
factory PlanSlugModel({@JsonKey(name: 'slug') required String slug}) = _PlanSlugModel;
|
||||
|
||||
factory PlanSlugModel.fromJson(Map<String, dynamic> json) => _$PlanSlugModelFromJson(json);
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
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_plan/presentation/cubit/create_plan_state.dart';
|
||||
import 'package:eatvio_app/features/create_plan/repositories/create_plan_repository.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -386,17 +391,63 @@ class CreatePlanCubit extends Cubit<CreatePlanState> {
|
||||
}
|
||||
|
||||
Future<void> publishPlan() async {
|
||||
if (validateAllFields()) {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
final planModel = await state.planData.toCreationJson();
|
||||
final plan = await createPlanRepository.createPlan(model: planModel);
|
||||
if (state.planData.options[1].short.isNotEmpty) {
|
||||
final options = state.planData.toOptionsCreationJson();
|
||||
await createPlanRepository.createPacketOptions(slug: plan.slug, model: options);
|
||||
} else {
|
||||
final option = state.planData.toOptionCreationJson();
|
||||
await createPlanRepository.createPacketOption(slug: plan.slug, model: option);
|
||||
try {
|
||||
if (validateAllFields()) {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
final planMap = <String, dynamic>{};
|
||||
|
||||
planMap.addAll({
|
||||
'name': state.planData.name,
|
||||
'duration': state.planData.duration,
|
||||
'state': 'active',
|
||||
'description_md': state.planData.details,
|
||||
'type[slim]': state.planData.type[0] ? '1' : '0',
|
||||
'type[fit]': state.planData.type[1] ? '1' : '0',
|
||||
'type[wellness]': state.planData.type[2] ? '1' : '0',
|
||||
'type[behavior]': state.planData.type[3] ? '1' : '0',
|
||||
'image': state.planData.image != null
|
||||
? await MultipartFile.fromFile((await ImageCompressUtility.compressFile(File(state.planData.image!.path))).path,
|
||||
filename: state.planData.image!.name)
|
||||
: null
|
||||
});
|
||||
if (state.planData.options[1].short.isNotEmpty) {
|
||||
planMap.addAll({
|
||||
'packets_count': 3,
|
||||
'packet1_options': {
|
||||
'short': state.planData.options[0].short,
|
||||
'price': int.parse((state.planData.options[0].price as double).toStringAsFixed(0)),
|
||||
'description_md': state.planData.options[0].description,
|
||||
'items': Map.fromIterable(state.planData.options[0].items),
|
||||
},
|
||||
'packet2_options': {
|
||||
'short': state.planData.options[1].short,
|
||||
'price': int.parse((state.planData.options[1].price as double).toStringAsFixed(0)),
|
||||
'description_md': state.planData.options[1].description,
|
||||
'items': Map.fromIterable(state.planData.options[1].items),
|
||||
},
|
||||
'packet3_options': {
|
||||
'short': state.planData.options[2].short,
|
||||
'price': int.parse((state.planData.options[2].price as double).toStringAsFixed(0)),
|
||||
'description_md': state.planData.options[2].description,
|
||||
'items': Map.fromIterable(state.planData.options[2].items),
|
||||
},
|
||||
});
|
||||
} else {
|
||||
planMap.addAll({
|
||||
'packets_count': 1,
|
||||
'packet1_options': {
|
||||
'short': state.planData.options[0].short,
|
||||
'price': int.parse((state.planData.options[0].price as double).toStringAsFixed(0)),
|
||||
'description_md': state.planData.options[0].description,
|
||||
'items': Map.fromIterable(state.planData.options[0].items),
|
||||
},
|
||||
});
|
||||
}
|
||||
await createPlanRepository.createPlan(model: planMap);
|
||||
emit(state.copyWith(isLoading: false, planCreated: true));
|
||||
}
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
emit(state.copyWith(isLoading: false, planCreated: true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'create_plan_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class CreatePlanState with _$CreatePlanState {
|
||||
abstract class CreatePlanState with _$CreatePlanState {
|
||||
const factory CreatePlanState({
|
||||
required bool isLoading,
|
||||
required String? errorMessage,
|
||||
@@ -29,7 +29,7 @@ class CreatePlanState with _$CreatePlanState {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class PlanValidationState with _$PlanValidationState {
|
||||
abstract class PlanValidationState with _$PlanValidationState {
|
||||
factory PlanValidationState({
|
||||
required bool titleHasError,
|
||||
required bool durationHasError,
|
||||
@@ -54,7 +54,7 @@ class PlanValidationState with _$PlanValidationState {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class OptionValidationState with _$OptionValidationState {
|
||||
abstract class OptionValidationState with _$OptionValidationState {
|
||||
factory OptionValidationState({
|
||||
required bool titleHasError,
|
||||
required bool priceHasError,
|
||||
|
||||
@@ -116,11 +116,12 @@ class _PlanImagePickerWidgetState extends State<PlanImagePickerWidget> {
|
||||
: Stack(
|
||||
children: [
|
||||
DottedBorder(
|
||||
stackFit: StackFit.expand,
|
||||
dashPattern: const [14, 14],
|
||||
color: state.validationState.imageHasError ? appTheme.red : appTheme.dottedBorder,
|
||||
borderType: BorderType.RRect,
|
||||
radius: const Radius.circular(12),
|
||||
options: RoundedRectDottedBorderOptions(
|
||||
stackFit: StackFit.expand,
|
||||
dashPattern: const [14, 14],
|
||||
color: state.validationState.imageHasError ? appTheme.red : appTheme.dottedBorder,
|
||||
radius: const Radius.circular(12),
|
||||
),
|
||||
child: const SizedBox.expand(),
|
||||
),
|
||||
Center(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/features/create_plan/models/plan_slug_model.dart';
|
||||
import 'package:eatvio_app/features/create_plan/services/create_plan_service.dart';
|
||||
|
||||
class CreatePlanRepository {
|
||||
@@ -7,24 +6,10 @@ class CreatePlanRepository {
|
||||
|
||||
CreatePlanRepository({required this.createPlanService});
|
||||
|
||||
Future<PlanSlugModel> createPlan({
|
||||
Future<void> createPlan({
|
||||
required Map<String, dynamic> model,
|
||||
}) async {
|
||||
final formData = FormData.fromMap(model);
|
||||
return await createPlanService.createPlan(formData: formData);
|
||||
}
|
||||
|
||||
Future<void> createPacketOption({
|
||||
required String slug,
|
||||
required Map<String, dynamic> model,
|
||||
}) async {
|
||||
await createPlanService.createPacketOption(slug: slug, model: model);
|
||||
}
|
||||
|
||||
Future<void> createPacketOptions({
|
||||
required String slug,
|
||||
required Map<String, dynamic> model,
|
||||
}) async {
|
||||
await createPlanService.createPacketOptions(slug: slug, model: model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/features/create_plan/models/plan_slug_model.dart';
|
||||
import 'package:retrofit/retrofit.dart';
|
||||
|
||||
part 'create_plan_service.g.dart';
|
||||
@@ -8,21 +7,10 @@ part 'create_plan_service.g.dart';
|
||||
abstract class CreatePlanService {
|
||||
factory CreatePlanService(Dio dio, {String baseUrl}) = _CreatePlanService;
|
||||
|
||||
@POST('/api/plans/create')
|
||||
@POST('/api/plans/create/published')
|
||||
@MultiPart()
|
||||
Future<PlanSlugModel> createPlan({
|
||||
Future<void> createPlan({
|
||||
@Body() required FormData formData,
|
||||
});
|
||||
|
||||
@POST('/api/plans/create/{slug}/packet_option')
|
||||
Future<void> createPacketOption({
|
||||
@Path() required String slug,
|
||||
@Body() required Map<String, dynamic> model,
|
||||
});
|
||||
|
||||
@POST('/api/plans/create/{slug}/packet_options')
|
||||
Future<void> createPacketOptions({
|
||||
@Path() required String slug,
|
||||
@Body() required Map<String, dynamic> model,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ enum TypeOfRecipe {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class RecipeCreateEntity with _$RecipeCreateEntity {
|
||||
abstract class RecipeCreateEntity with _$RecipeCreateEntity {
|
||||
const RecipeCreateEntity._();
|
||||
|
||||
const factory RecipeCreateEntity({
|
||||
@@ -138,7 +138,7 @@ class RecipeCreateEntity with _$RecipeCreateEntity {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class StepEntity with _$StepEntity {
|
||||
abstract class StepEntity with _$StepEntity {
|
||||
const StepEntity._();
|
||||
|
||||
const factory StepEntity({
|
||||
|
||||
@@ -5,11 +5,12 @@ part 'ingredient.freezed.dart';
|
||||
part 'ingredient.g.dart';
|
||||
|
||||
@freezed
|
||||
class Ingredient with _$Ingredient {
|
||||
abstract class Ingredient with _$Ingredient {
|
||||
const factory Ingredient({
|
||||
required String name,
|
||||
required String unit,
|
||||
required String portion,
|
||||
@JsonKey(name: 'product_nutritional_id') required int productId,
|
||||
}) = _Ingredient;
|
||||
|
||||
factory Ingredient.fromJson(Map<String, Object?> json) => _$IngredientFromJson(json);
|
||||
|
||||
@@ -5,7 +5,7 @@ part 'product.freezed.dart';
|
||||
part 'product.g.dart';
|
||||
|
||||
@freezed
|
||||
class Product with _$Product {
|
||||
abstract class Product with _$Product {
|
||||
const factory Product({
|
||||
required int id,
|
||||
required String name,
|
||||
|
||||
@@ -17,7 +17,7 @@ enum RecipeType {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class RecipeCreateModel with _$RecipeCreateModel {
|
||||
abstract class RecipeCreateModel with _$RecipeCreateModel {
|
||||
const factory RecipeCreateModel({
|
||||
required String title,
|
||||
required String state,
|
||||
|
||||
@@ -7,7 +7,7 @@ part 'recipe_model.freezed.dart';
|
||||
part 'recipe_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class RecipeModel with _$RecipeModel {
|
||||
abstract class RecipeModel with _$RecipeModel {
|
||||
const factory RecipeModel({
|
||||
List<Ingredient?>? ingredients,
|
||||
List<Step?>? steps,
|
||||
|
||||
@@ -6,7 +6,7 @@ part 'recipe_tag_model.freezed.dart';
|
||||
part 'recipe_tag_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class RecipeTagModel with _$RecipeTagModel {
|
||||
abstract class RecipeTagModel with _$RecipeTagModel {
|
||||
const factory RecipeTagModel({
|
||||
@JsonKey(name: "data") required List<RecipeTag> data,
|
||||
@JsonKey(name: "links") required Links links,
|
||||
@@ -17,7 +17,7 @@ class RecipeTagModel with _$RecipeTagModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class RecipeTag with _$RecipeTag {
|
||||
abstract class RecipeTag with _$RecipeTag {
|
||||
const factory RecipeTag({
|
||||
@JsonKey(name: "id") required int id,
|
||||
@JsonKey(name: "name") required String name,
|
||||
|
||||
@@ -5,7 +5,7 @@ part 'step.freezed.dart';
|
||||
part 'step.g.dart';
|
||||
|
||||
@freezed
|
||||
class Step with _$Step {
|
||||
abstract class Step with _$Step {
|
||||
const factory Step({
|
||||
String? image,
|
||||
String? description,
|
||||
|
||||
@@ -7,15 +7,18 @@ import 'package:eatvio_app/features/create_recipe/models/ingredient.dart' as i;
|
||||
import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_state.dart';
|
||||
import 'package:eatvio_app/features/create_recipe/reporitories/recipe_repository.dart';
|
||||
import 'package:eatvio_app/features/feed/models/recipes_model.dart';
|
||||
import 'package:eatvio_app/features/glossary/models/glossary_model.dart';
|
||||
import 'package:eatvio_app/features/glossary/repositories/glossary_repository.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class CreateRecipeCubit extends Cubit<CreateRecipeState> {
|
||||
final RecipeRepository recipeRepository;
|
||||
|
||||
final GlossaryRepository glossaryRepository;
|
||||
CreateRecipeCubit({
|
||||
required this.recipeRepository,
|
||||
required this.glossaryRepository,
|
||||
}) : super(
|
||||
const CreateRecipeState(
|
||||
loading: true,
|
||||
@@ -63,6 +66,12 @@ class CreateRecipeCubit extends Cubit<CreateRecipeState> {
|
||||
emit(state.copyWith.entity(ingredients: ingredients));
|
||||
}
|
||||
|
||||
void updateIngredient({required int index, required i.Ingredient ingredient}) {
|
||||
final ingredients = [...state.entity.ingredients];
|
||||
ingredients[index] = ingredient;
|
||||
emit(state.copyWith.entity(ingredients: ingredients));
|
||||
}
|
||||
|
||||
replaceIngredientPosition(int oldIndex, int newIndex) {
|
||||
List<i.Ingredient> ingredients = [...state.entity.ingredients];
|
||||
final item = ingredients.removeAt(oldIndex);
|
||||
@@ -201,7 +210,14 @@ class CreateRecipeCubit extends Cubit<CreateRecipeState> {
|
||||
description: recipe.description,
|
||||
image: file,
|
||||
videoUrl: recipe.videoUrl,
|
||||
ingredients: recipe.ingredients!.map((e) => i.Ingredient(name: e.name, unit: e.unit!, portion: e.portion)).toList(),
|
||||
ingredients: recipe.ingredients!
|
||||
.map((e) => i.Ingredient(
|
||||
name: e.name,
|
||||
unit: e.unit!,
|
||||
portion: e.portion,
|
||||
productId: e.productId,
|
||||
))
|
||||
.toList(),
|
||||
steps: steps,
|
||||
tags: recipe.tags.map((e) => (e.name)).toList(),
|
||||
),
|
||||
@@ -306,4 +322,8 @@ class CreateRecipeCubit extends Cubit<CreateRecipeState> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<GlossaryModel>> getGlossaryListByQuery(String query) async {
|
||||
return await glossaryRepository.getGlossaryListByQuery(query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'create_recipe_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class CreateRecipeState with _$CreateRecipeState {
|
||||
abstract class CreateRecipeState with _$CreateRecipeState {
|
||||
const factory CreateRecipeState({
|
||||
required RecipeCreateEntity entity,
|
||||
required bool loading,
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
import 'package:easy_debounce/easy_debounce.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_divider.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
||||
import 'package:eatvio_app/features/create_recipe/models/ingredient.dart';
|
||||
import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/widgets/glossary_moderation_request_modal_widget.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/glossary/models/glossary_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AddPortionWidget extends StatefulWidget {
|
||||
const AddPortionWidget({
|
||||
super.key,
|
||||
required this.createRecipeCubit,
|
||||
this.portionIndex,
|
||||
this.productNameControllerText,
|
||||
this.quantityControllerText,
|
||||
});
|
||||
final CreateRecipeCubit createRecipeCubit;
|
||||
final String? productNameControllerText;
|
||||
final String? quantityControllerText;
|
||||
final int? portionIndex;
|
||||
@override
|
||||
State<AddPortionWidget> createState() => _AddPortionWidgetState();
|
||||
}
|
||||
|
||||
class _AddPortionWidgetState extends State<AddPortionWidget> {
|
||||
GlossaryModel? selectedGlossary;
|
||||
final productNameController = TextEditingController();
|
||||
final quantityController = TextEditingController();
|
||||
List<GlossaryModel> glossariesList = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
productNameController.text = widget.productNameControllerText ?? '';
|
||||
quantityController.text = widget.quantityControllerText ?? '';
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16) +
|
||||
EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).viewInsets.bottom,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
'Добавление блюда',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF323F4B),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.8 - 32,
|
||||
child: CustomTextFormField(
|
||||
controller: productNameController,
|
||||
autofocus: true,
|
||||
hintText: 'Введите название продукта/блюда',
|
||||
onChanged: (query) {
|
||||
EasyDebounce.debounce(
|
||||
'glossary-search',
|
||||
const Duration(milliseconds: 250),
|
||||
() async {
|
||||
selectedGlossary = null;
|
||||
if (query.isEmpty) {
|
||||
glossariesList = [];
|
||||
selectedGlossary = null;
|
||||
setState(() {});
|
||||
return;
|
||||
}
|
||||
glossariesList = await widget.createRecipeCubit.getGlossaryListByQuery(query);
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.2 - 4,
|
||||
child: CustomTextFormField(
|
||||
controller: quantityController,
|
||||
keyboardType: TextInputType.number,
|
||||
hintText: selectedGlossary == null
|
||||
? '100'
|
||||
: selectedGlossary!.grInPortion == 0
|
||||
? '100'
|
||||
: '1',
|
||||
suffixText: selectedGlossary == null
|
||||
? 'гр'
|
||||
: selectedGlossary?.grInPortion == 0
|
||||
? 'гр'
|
||||
: 'шт',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
glossariesList.isEmpty ? const SizedBox(height: 16) : const SizedBox.shrink(),
|
||||
Container(
|
||||
constraints: const BoxConstraints(
|
||||
maxHeight: 256,
|
||||
),
|
||||
child: ListView.separated(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, i) {
|
||||
return ListTile(
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
selectedGlossary = glossariesList[i];
|
||||
productNameController.text = selectedGlossary!.name;
|
||||
});
|
||||
glossariesList = [];
|
||||
setState(() {});
|
||||
FocusManager.instance.primaryFocus?.nextFocus();
|
||||
},
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
title: Text(
|
||||
glossariesList[i].name,
|
||||
style: const TextStyle(
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
subtitle: glossariesList[i].grInPortion == 0
|
||||
? null
|
||||
: Text(
|
||||
'${glossariesList[i].grInPortion} г',
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
),
|
||||
trailing: Icon(
|
||||
Icons.add_rounded,
|
||||
color: Colors.grey.shade400,
|
||||
),
|
||||
contentPadding: const EdgeInsets.only(left: 12),
|
||||
);
|
||||
},
|
||||
itemCount: glossariesList.length,
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return const CustomDivider();
|
||||
},
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
if (widget.portionIndex != null)
|
||||
InkWell(
|
||||
onTap: () {
|
||||
widget.createRecipeCubit.removeIngredient(widget.portionIndex!);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Ink(
|
||||
width: 52,
|
||||
height: 52,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: const Color(0xFFF6F6F6),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.delete_outline_rounded,
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.portionIndex != null) const SizedBox(width: 8),
|
||||
InkWell(
|
||||
onTap: () => addPortion(context),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Ink(
|
||||
width: MediaQuery.of(context).size.width - (widget.portionIndex == null ? 32 : 92),
|
||||
height: 52,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: appTheme.blueA400,
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
widget.portionIndex == null ? 'Добавить' : 'Сохранить',
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> addPortion(BuildContext context) async {
|
||||
final portionIndex = widget.portionIndex;
|
||||
var measurementUnits = selectedGlossary?.grInPortion == null
|
||||
? Portion.pcs
|
||||
: selectedGlossary?.grInPortion == 0
|
||||
? Portion.grams
|
||||
: Portion.pcs;
|
||||
final glossary = selectedGlossary;
|
||||
if (glossary != null) {
|
||||
final [calories, carbohydrates, proteins, fats] = _getCPFC(glossary, measurementUnits);
|
||||
final portion = DailySchedule(
|
||||
dailyNutrition: DailyNutrition(energy: calories, proteins: proteins, fats: fats, carbohydrates: carbohydrates),
|
||||
quantity: int.parse(quantityController.text),
|
||||
name: glossary.name,
|
||||
productNutritionalValueId: glossary.id.toString(),
|
||||
portion: measurementUnits,
|
||||
);
|
||||
if (portionIndex == null) {
|
||||
widget.createRecipeCubit.addIngredient(
|
||||
Ingredient(
|
||||
name: portion.name,
|
||||
unit: portion.portion.getValue(),
|
||||
portion: portion.quantity.toString(),
|
||||
productId: glossary.id,
|
||||
),
|
||||
);
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
} else {}
|
||||
} else {
|
||||
Navigator.of(context).pop();
|
||||
final newGlossary = await showModalBottomSheet<GlossaryModel>(
|
||||
isScrollControlled: true,
|
||||
useRootNavigator: true,
|
||||
context: context,
|
||||
builder: (context) => Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).viewInsets.bottom,
|
||||
),
|
||||
child: GlossaryModerationRequestModalWidget(
|
||||
productName: productNameController.text,
|
||||
diaryCubit: sl<DiaryCubit>(),
|
||||
),
|
||||
),
|
||||
);
|
||||
if (newGlossary != null) {
|
||||
measurementUnits = Portion.grams;
|
||||
final [calories, carbohydrates, proteins, fats] = _getCPFC(newGlossary, measurementUnits);
|
||||
final portion = DailySchedule(
|
||||
dailyNutrition: DailyNutrition(energy: calories, proteins: proteins, fats: fats, carbohydrates: carbohydrates),
|
||||
quantity: int.parse(quantityController.text),
|
||||
name: newGlossary.name,
|
||||
productNutritionalValueId: newGlossary.id.toString(),
|
||||
portion: measurementUnits,
|
||||
);
|
||||
if (portionIndex == null) {
|
||||
widget.createRecipeCubit.addIngredient(
|
||||
Ingredient(
|
||||
name: portion.name,
|
||||
unit: portion.portion.getValue(),
|
||||
portion: portion.quantity.toString(),
|
||||
productId: newGlossary.id,
|
||||
),
|
||||
);
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
} else {
|
||||
widget.createRecipeCubit.updateIngredient(
|
||||
index: portionIndex,
|
||||
ingredient: Ingredient(
|
||||
name: portion.name,
|
||||
unit: portion.portion.getValue(),
|
||||
portion: portion.quantity.toString(),
|
||||
productId: newGlossary.id,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<double> _getCPFC(GlossaryModel glossary, Portion measurementUnits) {
|
||||
final double calories;
|
||||
final double carbohydrates;
|
||||
final double proteins;
|
||||
final double fats;
|
||||
if (measurementUnits == Portion.grams) {
|
||||
calories = (glossary.energyOn100Gr * int.parse(quantityController.text) / 100);
|
||||
carbohydrates = (glossary.carbohydratesOn100Gr * int.parse(quantityController.text) / 100);
|
||||
proteins = (glossary.proteinsOn100Gr * int.parse(quantityController.text) / 100);
|
||||
fats = (glossary.fatsOn100Gr * int.parse(quantityController.text) / 100);
|
||||
} else {
|
||||
calories = (glossary.energyOnPortion * int.parse(quantityController.text));
|
||||
carbohydrates = (glossary.carbohydratesOnPortion * int.parse(quantityController.text));
|
||||
proteins = (glossary.proteinsOnPortion * int.parse(quantityController.text));
|
||||
fats = (glossary.fatsOnPortion * int.parse(quantityController.text));
|
||||
}
|
||||
return [calories, carbohydrates, proteins, fats];
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
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_recipe/models/ingredient.dart';
|
||||
import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart';
|
||||
import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_state.dart';
|
||||
import 'package:eatvio_app/features/create_recipe/presentation/widgets/add_portion_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
@@ -87,116 +86,8 @@ class _IngredientsWidgetState extends State<IngredientsWidget> {
|
||||
nameController.clear();
|
||||
gramsController.clear();
|
||||
pcsController.clear();
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
context: context,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
|
||||
),
|
||||
isScrollControlled: true,
|
||||
builder: (context) {
|
||||
return StatefulBuilder(
|
||||
builder: (BuildContext context, void Function(void Function()) setStateInner) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16) +
|
||||
EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).viewInsets.bottom,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
'Добавление ингредиента',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF323F4B),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width - 32,
|
||||
child: CustomTextFormField(
|
||||
autofocus: true,
|
||||
controller: nameController,
|
||||
hintText: 'Введите название продукта',
|
||||
onChanged: (query) {},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 2 - 20,
|
||||
child: CustomTextFormField(
|
||||
enabled: pcsController.text.isEmpty ? true : false,
|
||||
keyboardType: TextInputType.number,
|
||||
controller: gramsController,
|
||||
hintText: pcsController.text.isEmpty ? '100' : null,
|
||||
suffixText: 'гр',
|
||||
onChanged: (value) {
|
||||
setStateInner(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 2 - 20,
|
||||
child: CustomTextFormField(
|
||||
enabled: gramsController.text.isEmpty ? true : false,
|
||||
keyboardType: TextInputType.number,
|
||||
controller: pcsController,
|
||||
onChanged: (value) {
|
||||
setStateInner(() {});
|
||||
},
|
||||
hintText: gramsController.text.isEmpty ? '1' : null,
|
||||
suffixText: 'шт',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
if (nameController.text.isNotEmpty && (pcsController.text.isNotEmpty || gramsController.text.isNotEmpty)) {
|
||||
createRecipeCubit.addIngredient(
|
||||
Ingredient(
|
||||
name: nameController.text,
|
||||
unit: gramsController.text.isEmpty ? 'pcs' : 'gr',
|
||||
portion: gramsController.text.isEmpty ? pcsController.text : gramsController.text,
|
||||
),
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Ink(
|
||||
width: MediaQuery.of(context).size.width - 32,
|
||||
height: 52,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: appTheme.blueA400,
|
||||
),
|
||||
child: const Center(
|
||||
child: Text(
|
||||
'Добавить',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
_modalBottomSheet(
|
||||
createRecipeCubit: createRecipeCubit,
|
||||
);
|
||||
},
|
||||
shape: RoundedRectangleBorder(
|
||||
@@ -220,4 +111,28 @@ class _IngredientsWidgetState extends State<IngredientsWidget> {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
_modalBottomSheet({
|
||||
required CreateRecipeCubit createRecipeCubit,
|
||||
int? portionIndex,
|
||||
String? productNameControllerText,
|
||||
String? quantityControllerText,
|
||||
}) {
|
||||
return showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
context: context,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
|
||||
),
|
||||
isScrollControlled: true,
|
||||
builder: (context) {
|
||||
return AddPortionWidget(
|
||||
createRecipeCubit: createRecipeCubit,
|
||||
portionIndex: portionIndex,
|
||||
productNameControllerText: productNameControllerText,
|
||||
quantityControllerText: quantityControllerText,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart';
|
||||
import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
@@ -14,79 +15,73 @@ class TagsWidget extends StatefulWidget {
|
||||
class _TagsWidgetState extends State<TagsWidget> {
|
||||
late final CreateRecipeCubit createRecipeCubit;
|
||||
final tagController = TextEditingController();
|
||||
late final List<String> tags;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
createRecipeCubit = context.read<CreateRecipeCubit>();
|
||||
tags = createRecipeCubit.state.entity.tags;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('Теги'),
|
||||
const SizedBox(height: 4),
|
||||
CustomTextFormField(
|
||||
hintText: 'Например: без майонеза',
|
||||
controller: tagController,
|
||||
suffix: IconButton(
|
||||
onPressed: () {
|
||||
if (tagController.text.isNotEmpty) {
|
||||
setState(() {
|
||||
tags.add(tagController.text);
|
||||
tagController.clear();
|
||||
});
|
||||
}
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.done,
|
||||
color: appTheme.blueA400,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
return BlocBuilder<CreateRecipeCubit, CreateRecipeState>(
|
||||
builder: (BuildContext context, CreateRecipeState state) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
for (int i = 0; i < tags.length; i++) ...[
|
||||
InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
tags.remove(tags[i]);
|
||||
});
|
||||
const Text('Теги'),
|
||||
const SizedBox(height: 4),
|
||||
CustomTextFormField(
|
||||
hintText: 'Например: без майонеза',
|
||||
controller: tagController,
|
||||
suffix: IconButton(
|
||||
onPressed: () {
|
||||
if (tagController.text.isNotEmpty) {
|
||||
context.read<CreateRecipeCubit>().addTag(tagController.text);
|
||||
setState(() {
|
||||
tagController.clear();
|
||||
});
|
||||
}
|
||||
},
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Ink(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
color: Colors.grey.shade200,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
tags[i],
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Icon(
|
||||
Icons.delete_outline_rounded,
|
||||
color: Colors.grey.shade600,
|
||||
size: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
icon: Icon(
|
||||
Icons.done,
|
||||
color: appTheme.blueA400,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
for (int i = 0; i < state.entity.tags.length; i++) ...[
|
||||
InkWell(
|
||||
onTap: () {
|
||||
context.read<CreateRecipeCubit>().removeTag(state.entity.tags[i]);
|
||||
},
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Ink(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
color: Colors.grey.shade200,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
state.entity.tags[i],
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Icon(
|
||||
Icons.delete_outline_rounded,
|
||||
color: Colors.grey.shade600,
|
||||
size: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ part 'consultant_comment_model.freezed.dart';
|
||||
part 'consultant_comment_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class ConsultantCommentModel with _$ConsultantCommentModel {
|
||||
abstract class ConsultantCommentModel with _$ConsultantCommentModel {
|
||||
factory ConsultantCommentModel({
|
||||
String? comment,
|
||||
@Default({}) @JsonKey(name: 'meals') Map<String, MealCommentModel> mealsComments,
|
||||
@@ -15,7 +15,7 @@ class ConsultantCommentModel with _$ConsultantCommentModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class PortionCommentModel with _$PortionCommentModel {
|
||||
abstract class PortionCommentModel with _$PortionCommentModel {
|
||||
factory PortionCommentModel({
|
||||
required String comment,
|
||||
required int id,
|
||||
@@ -25,7 +25,7 @@ class PortionCommentModel with _$PortionCommentModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class MealCommentModel with _$MealCommentModel {
|
||||
abstract class MealCommentModel with _$MealCommentModel {
|
||||
|
||||
factory MealCommentModel({
|
||||
required String comment,
|
||||
|
||||
@@ -4,7 +4,7 @@ part 'consultant_review_model.freezed.dart';
|
||||
part 'consultant_review_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class ConsultantReviewModel with _$ConsultantReviewModel {
|
||||
abstract class ConsultantReviewModel with _$ConsultantReviewModel {
|
||||
|
||||
factory ConsultantReviewModel({
|
||||
required String planName,
|
||||
@@ -18,7 +18,7 @@ class ConsultantReviewModel with _$ConsultantReviewModel {
|
||||
|
||||
|
||||
@freezed
|
||||
class ConsultantComments with _$ConsultantComments {
|
||||
abstract class ConsultantComments with _$ConsultantComments {
|
||||
|
||||
factory ConsultantComments({
|
||||
required String? dayComment,
|
||||
@@ -32,7 +32,7 @@ class ConsultantComments with _$ConsultantComments {
|
||||
|
||||
|
||||
@freezed
|
||||
class MealComment with _$MealComment {
|
||||
abstract class MealComment with _$MealComment {
|
||||
|
||||
factory MealComment({
|
||||
required String comment,
|
||||
@@ -43,7 +43,7 @@ class MealComment with _$MealComment {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class PortionComment with _$PortionComment {
|
||||
abstract class PortionComment with _$PortionComment {
|
||||
|
||||
factory PortionComment({
|
||||
@JsonKey(name: 'id') required int portionId,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,9 +6,9 @@ part of 'consultant_review_model.dart';
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$ConsultantReviewModelImpl _$$ConsultantReviewModelImplFromJson(
|
||||
_ConsultantReviewModel _$ConsultantReviewModelFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$ConsultantReviewModelImpl(
|
||||
_ConsultantReviewModel(
|
||||
planName: json['planName'] as String,
|
||||
planDuration: (json['planDuration'] as num).toInt(),
|
||||
currentPlanDay: (json['currentPlanDay'] as num).toInt(),
|
||||
@@ -16,8 +16,8 @@ _$ConsultantReviewModelImpl _$$ConsultantReviewModelImplFromJson(
|
||||
json['consultantComments'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ConsultantReviewModelImplToJson(
|
||||
_$ConsultantReviewModelImpl instance) =>
|
||||
Map<String, dynamic> _$ConsultantReviewModelToJson(
|
||||
_ConsultantReviewModel instance) =>
|
||||
<String, dynamic>{
|
||||
'planName': instance.planName,
|
||||
'planDuration': instance.planDuration,
|
||||
@@ -25,9 +25,8 @@ Map<String, dynamic> _$$ConsultantReviewModelImplToJson(
|
||||
'consultantComments': instance.consultantComments,
|
||||
};
|
||||
|
||||
_$ConsultantCommentsImpl _$$ConsultantCommentsImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$ConsultantCommentsImpl(
|
||||
_ConsultantComments _$ConsultantCommentsFromJson(Map<String, dynamic> json) =>
|
||||
_ConsultantComments(
|
||||
dayComment: json['dayComment'] as String?,
|
||||
mealsComments: (json['mealsComments'] as List<dynamic>)
|
||||
.map((e) => MealComment.fromJson(e as Map<String, dynamic>))
|
||||
@@ -37,34 +36,31 @@ _$ConsultantCommentsImpl _$$ConsultantCommentsImplFromJson(
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ConsultantCommentsImplToJson(
|
||||
_$ConsultantCommentsImpl instance) =>
|
||||
Map<String, dynamic> _$ConsultantCommentsToJson(_ConsultantComments instance) =>
|
||||
<String, dynamic>{
|
||||
'dayComment': instance.dayComment,
|
||||
'mealsComments': instance.mealsComments,
|
||||
'portionsComments': instance.portionsComments,
|
||||
};
|
||||
|
||||
_$MealCommentImpl _$$MealCommentImplFromJson(Map<String, dynamic> json) =>
|
||||
_$MealCommentImpl(
|
||||
_MealComment _$MealCommentFromJson(Map<String, dynamic> json) => _MealComment(
|
||||
comment: json['comment'] as String,
|
||||
typeNo: (json['typeNo'] as num).toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$MealCommentImplToJson(_$MealCommentImpl instance) =>
|
||||
Map<String, dynamic> _$MealCommentToJson(_MealComment instance) =>
|
||||
<String, dynamic>{
|
||||
'comment': instance.comment,
|
||||
'typeNo': instance.typeNo,
|
||||
};
|
||||
|
||||
_$PortionCommentImpl _$$PortionCommentImplFromJson(Map<String, dynamic> json) =>
|
||||
_$PortionCommentImpl(
|
||||
_PortionComment _$PortionCommentFromJson(Map<String, dynamic> json) =>
|
||||
_PortionComment(
|
||||
portionId: (json['id'] as num).toInt(),
|
||||
comment: json['comment'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$PortionCommentImplToJson(
|
||||
_$PortionCommentImpl instance) =>
|
||||
Map<String, dynamic> _$PortionCommentToJson(_PortionComment instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.portionId,
|
||||
'comment': instance.comment,
|
||||
|
||||
@@ -21,7 +21,7 @@ enum PortionState {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class DiaryDayWrapperModel with _$DiaryDayWrapperModel {
|
||||
abstract class DiaryDayWrapperModel with _$DiaryDayWrapperModel {
|
||||
const factory DiaryDayWrapperModel({
|
||||
@JsonKey(
|
||||
name: "diary",
|
||||
@@ -33,7 +33,7 @@ class DiaryDayWrapperModel with _$DiaryDayWrapperModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class DiaryDayModel with _$DiaryDayModel {
|
||||
abstract class DiaryDayModel with _$DiaryDayModel {
|
||||
const factory DiaryDayModel({
|
||||
@JsonKey(name: "id") required int id,
|
||||
@JsonKey(name: "date") required String date,
|
||||
@@ -50,7 +50,7 @@ class DiaryDayModel with _$DiaryDayModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class MealModel with _$MealModel {
|
||||
abstract class MealModel with _$MealModel {
|
||||
const factory MealModel({
|
||||
@JsonKey(name: "id") required int id,
|
||||
@JsonKey(name: "time") required String time,
|
||||
@@ -65,7 +65,7 @@ class MealModel with _$MealModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class DiaryPhotoModel with _$DiaryPhotoModel {
|
||||
abstract class DiaryPhotoModel with _$DiaryPhotoModel {
|
||||
const factory DiaryPhotoModel({
|
||||
@JsonKey(name: "id") required int id,
|
||||
@JsonKey(name: "original_name") required String originalName,
|
||||
@@ -77,7 +77,7 @@ class DiaryPhotoModel with _$DiaryPhotoModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class PortionModel with _$PortionModel {
|
||||
abstract class PortionModel with _$PortionModel {
|
||||
const factory PortionModel({
|
||||
@JsonKey(name: "id") required int id,
|
||||
@JsonKey(name: "product_nutritional_value_id") int? productNutritionalValueId,
|
||||
@@ -92,7 +92,7 @@ class PortionModel with _$PortionModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class TypeOfDiaryModel with _$TypeOfDiaryModel {
|
||||
abstract class TypeOfDiaryModel with _$TypeOfDiaryModel {
|
||||
const factory TypeOfDiaryModel({
|
||||
@JsonKey(name: "has_chosen_diet") required bool hasChosenDiet,
|
||||
@JsonKey(name: "has_chosen_plan") required bool hasChosenPlan,
|
||||
@@ -103,7 +103,7 @@ class TypeOfDiaryModel with _$TypeOfDiaryModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProductInMeal with _$ProductInMeal {
|
||||
abstract class ProductInMeal with _$ProductInMeal {
|
||||
const factory ProductInMeal({
|
||||
@JsonKey(name: 'product_name') required String productName,
|
||||
@JsonKey(name: 'portion') required String portion,
|
||||
|
||||
@@ -5,7 +5,7 @@ part 'diary_index_model.freezed.dart';
|
||||
part 'diary_index_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class DiaryIndexModel with _$DiaryIndexModel {
|
||||
abstract class DiaryIndexModel with _$DiaryIndexModel {
|
||||
const factory DiaryIndexModel({
|
||||
required int id,
|
||||
}) = _DiaryIndexModel;
|
||||
|
||||
@@ -5,7 +5,7 @@ part 'finish_diary_day_model.freezed.dart';
|
||||
part 'finish_diary_day_model.g.dart';
|
||||
|
||||
@Freezed()
|
||||
class FinishDiaryDayModel with _$FinishDiaryDayModel {
|
||||
abstract class FinishDiaryDayModel with _$FinishDiaryDayModel {
|
||||
const factory FinishDiaryDayModel({required String wakeUp, required String retire, @JsonKey(includeIfNull: false) String? comment}) = _FinishDiaryDayModel;
|
||||
|
||||
factory FinishDiaryDayModel.fromJson(Map<String, dynamic> json) => _$FinishDiaryDayModelFromJson(json);
|
||||
|
||||
@@ -5,7 +5,7 @@ part 'nutrition_model.freezed.dart';
|
||||
part 'nutrition_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class NutritionDataModel with _$NutritionDataModel {
|
||||
abstract class NutritionDataModel with _$NutritionDataModel {
|
||||
const factory NutritionDataModel({
|
||||
required NutritionModel dailyNutritionIntake,
|
||||
required NutritionModel dailyNutritionRecommendation,
|
||||
@@ -15,7 +15,7 @@ class NutritionDataModel with _$NutritionDataModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class NutritionModel with _$NutritionModel {
|
||||
abstract class NutritionModel with _$NutritionModel {
|
||||
const factory NutritionModel({
|
||||
required double energy,
|
||||
required double proteins,
|
||||
|
||||
@@ -4,7 +4,7 @@ part 'product_moderation_model.freezed.dart';
|
||||
part 'product_moderation_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class ProductModerationModel with _$ProductModerationModel {
|
||||
abstract class ProductModerationModel with _$ProductModerationModel {
|
||||
const factory ProductModerationModel({
|
||||
required String name,
|
||||
@JsonKey(name: 'energy_on_100gr')
|
||||
|
||||
@@ -5,7 +5,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'consultant_comments_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class ConsultantCommentsState with _$ConsultantCommentsState {
|
||||
abstract class ConsultantCommentsState with _$ConsultantCommentsState {
|
||||
|
||||
factory ConsultantCommentsState({
|
||||
required ConsultantCommentModel commentsModel,
|
||||
|
||||
@@ -6,7 +6,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'diary_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class DiaryState with _$DiaryState {
|
||||
abstract class DiaryState with _$DiaryState {
|
||||
const factory DiaryState() = _DiaryState;
|
||||
|
||||
const factory DiaryState.loading() = DiaryStateLoading;
|
||||
|
||||
@@ -79,12 +79,13 @@ class _ProductWidgetState extends State<ProductWidget> {
|
||||
}
|
||||
if (widget.portion.portionState == PortionState.eatenDeleted) {
|
||||
return DottedBorder(
|
||||
dashPattern: const [6, 6],
|
||||
padding: EdgeInsets.zero,
|
||||
borderPadding: EdgeInsets.zero,
|
||||
borderType: BorderType.RRect,
|
||||
radius: const Radius.circular(20),
|
||||
color: const Color(0xFF6D6F71),
|
||||
options: const RoundedRectDottedBorderOptions(
|
||||
dashPattern: [6, 6],
|
||||
padding: EdgeInsets.zero,
|
||||
borderPadding: EdgeInsets.zero,
|
||||
radius: Radius.circular(20),
|
||||
color: Color(0xFF6D6F71),
|
||||
),
|
||||
child: ListTile(
|
||||
onTap: () => diaryCubit.restore(
|
||||
diaryId: state.diary.id,
|
||||
|
||||
@@ -4,7 +4,7 @@ part 'diet_likes_model.freezed.dart';
|
||||
part 'diet_likes_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class DietLikesModel with _$DietLikesModel {
|
||||
abstract class DietLikesModel with _$DietLikesModel {
|
||||
const factory DietLikesModel({
|
||||
@JsonKey(name: 'total_likers') required int totalLikes,
|
||||
@JsonKey(name: 'is_liked') required bool isLiked,
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'about_diet_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class AboutDietState with _$AboutDietState {
|
||||
abstract class AboutDietState with _$AboutDietState {
|
||||
const factory AboutDietState() = _AboutDietState;
|
||||
|
||||
const factory AboutDietState.initial() = DietStateInitial;
|
||||
|
||||
@@ -321,6 +321,7 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
|
||||
path: 'diet/${widget.diet.id}',
|
||||
complainedContentType: ComplainedContent.diet,
|
||||
id: widget.diet.id,
|
||||
diet: widget.diet,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
],
|
||||
@@ -501,6 +502,7 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
|
||||
path: 'diet/${widget.diet.id}',
|
||||
complainedContentType: ComplainedContent.diet,
|
||||
id: widget.diet.id,
|
||||
diet: widget.diet,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
],
|
||||
@@ -527,6 +529,7 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
|
||||
path: 'diet/${widget.diet.id}',
|
||||
complainedContentType: ComplainedContent.diet,
|
||||
id: widget.diet.id,
|
||||
diet: widget.diet,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
],
|
||||
|
||||
@@ -60,11 +60,11 @@ class MoreOptions extends StatelessWidget {
|
||||
? null
|
||||
: () {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
Share.shareUri(
|
||||
Uri.parse(
|
||||
'$baseUrl/ru/$path',
|
||||
SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse('$baseUrl/ru/$path',),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
);
|
||||
},
|
||||
child: Row(
|
||||
|
||||
@@ -5,7 +5,7 @@ part 'comment_model.freezed.dart';
|
||||
part 'comment_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class CommentModel with _$CommentModel {
|
||||
abstract class CommentModel with _$CommentModel {
|
||||
const factory CommentModel({
|
||||
@JsonKey(name: "data") required List<Comment> data,
|
||||
@JsonKey(name: "links") required Links links,
|
||||
@@ -16,7 +16,7 @@ class CommentModel with _$CommentModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Comment with _$Comment {
|
||||
abstract class Comment with _$Comment {
|
||||
const factory Comment({
|
||||
@JsonKey(name: "id") required int id,
|
||||
@JsonKey(name: "author_name") required String authorName,
|
||||
|
||||
@@ -6,7 +6,7 @@ part 'diets_pagination_model.freezed.dart';
|
||||
part 'diets_pagination_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class DietsPagination with _$DietsPagination {
|
||||
abstract class DietsPagination with _$DietsPagination {
|
||||
const factory DietsPagination({
|
||||
@JsonKey(name: "data") required List<Diet> data,
|
||||
@JsonKey(name: "links") required Links links,
|
||||
@@ -17,7 +17,7 @@ class DietsPagination with _$DietsPagination {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Diet with _$Diet {
|
||||
abstract class Diet with _$Diet {
|
||||
const factory Diet({
|
||||
@JsonKey(name: "id") required int id,
|
||||
@JsonKey(name: "name") required String name,
|
||||
@@ -42,7 +42,7 @@ class Diet with _$Diet {
|
||||
enum DietState {
|
||||
@JsonKey(name: 'active')
|
||||
active,
|
||||
@JsonKey(name: 'draft')
|
||||
@JsonKey(name: 'drafted')
|
||||
draft,
|
||||
@JsonKey(name: 'personal')
|
||||
personal;
|
||||
@@ -53,7 +53,7 @@ enum DietState {
|
||||
case DietState.active:
|
||||
return 'active';
|
||||
case DietState.draft:
|
||||
return 'draft';
|
||||
return 'drafted';
|
||||
case DietState.personal:
|
||||
return 'personal';
|
||||
}
|
||||
@@ -61,7 +61,7 @@ enum DietState {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class CreatedDiet with _$CreatedDiet {
|
||||
abstract class CreatedDiet with _$CreatedDiet {
|
||||
const factory CreatedDiet({
|
||||
@JsonKey(name: "id") required int id,
|
||||
}) = _CreatedDiet;
|
||||
@@ -78,7 +78,7 @@ int parseDuration(dynamic duration) {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Author with _$Author {
|
||||
abstract class Author with _$Author {
|
||||
const factory Author({
|
||||
@JsonKey(name: "author_slug") required String authorSlug,
|
||||
@JsonKey(name: "author_name") required String authorName,
|
||||
@@ -92,7 +92,7 @@ class Author with _$Author {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class DailySchedule with _$DailySchedule {
|
||||
abstract class DailySchedule with _$DailySchedule {
|
||||
const factory DailySchedule({
|
||||
@JsonKey(name: "name") required String name,
|
||||
@JsonKey(name: "portion") required Portion portion,
|
||||
@@ -132,10 +132,16 @@ enum Portion {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String getValue() => switch (this) {
|
||||
Portion.grams => 'gr',
|
||||
Portion.milliliters => 'milliliters',
|
||||
Portion.pcs => 'pcs',
|
||||
};
|
||||
}
|
||||
|
||||
@freezed
|
||||
class DietType with _$DietType {
|
||||
abstract class DietType with _$DietType {
|
||||
const DietType._();
|
||||
const factory DietType({
|
||||
@JsonKey(name: "light") bool? light,
|
||||
@@ -164,7 +170,7 @@ class EnumValues<T> {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class DailyNutrition with _$DailyNutrition {
|
||||
abstract class DailyNutrition with _$DailyNutrition {
|
||||
const factory DailyNutrition({
|
||||
@JsonKey(name: 'energy') required double energy,
|
||||
@JsonKey(name: 'proteins') required double proteins,
|
||||
|
||||
@@ -5,7 +5,7 @@ part 'feed_blog_post_model.freezed.dart';
|
||||
part 'feed_blog_post_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class FeedBlogPostModel with _$FeedBlogPostModel {
|
||||
abstract class FeedBlogPostModel with _$FeedBlogPostModel {
|
||||
const factory FeedBlogPostModel({
|
||||
required int id,
|
||||
required String state,
|
||||
|
||||
@@ -7,7 +7,7 @@ part 'feed_blog_post_settings_model.freezed.dart';
|
||||
part 'feed_blog_post_settings_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class FeedBlogPostSettingsModel with _$FeedBlogPostSettingsModel {
|
||||
abstract class FeedBlogPostSettingsModel with _$FeedBlogPostSettingsModel {
|
||||
const factory FeedBlogPostSettingsModel({
|
||||
@JsonKey(name: 'data') required List<FeedBlogPostModel> feedBlogPostList,
|
||||
required Meta meta,
|
||||
|
||||
@@ -6,7 +6,7 @@ part 'feed_consultant_model.freezed.dart';
|
||||
part 'feed_consultant_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class FeedConsultantPaginationModel with _$FeedConsultantPaginationModel {
|
||||
abstract class FeedConsultantPaginationModel with _$FeedConsultantPaginationModel {
|
||||
const factory FeedConsultantPaginationModel({
|
||||
required List<FeedConsultantModel> data,
|
||||
required Links links,
|
||||
@@ -17,7 +17,7 @@ class FeedConsultantPaginationModel with _$FeedConsultantPaginationModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class FeedConsultantModel with _$FeedConsultantModel {
|
||||
abstract class FeedConsultantModel with _$FeedConsultantModel {
|
||||
const factory FeedConsultantModel({
|
||||
required String slug,
|
||||
required String name,
|
||||
|
||||
@@ -8,7 +8,7 @@ part 'plan_pagination_model.freezed.dart';
|
||||
part 'plan_pagination_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class PlanPaginationModel with _$PlanPaginationModel {
|
||||
abstract class PlanPaginationModel with _$PlanPaginationModel {
|
||||
const factory PlanPaginationModel({
|
||||
@JsonKey(name: "data") required List<PlanModel> data,
|
||||
@JsonKey(name: "links") required Links links,
|
||||
@@ -19,7 +19,7 @@ class PlanPaginationModel with _$PlanPaginationModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class PlanModel with _$PlanModel {
|
||||
abstract class PlanModel with _$PlanModel {
|
||||
const factory PlanModel({
|
||||
@JsonKey(name: "id") required int id,
|
||||
@JsonKey(name: "slug") required String slug,
|
||||
@@ -61,9 +61,9 @@ PlanType planTypeFromJson(dynamic json) {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class PacketOptions with _$PacketOptions {
|
||||
abstract class PacketOptions with _$PacketOptions {
|
||||
const factory PacketOptions({
|
||||
@JsonKey(name: "items") required List<String> items,
|
||||
@JsonKey(name: "items", fromJson: _parseItems) required List<String> items,
|
||||
@JsonKey(name: "price") required dynamic price,
|
||||
@JsonKey(name: "short") required String short,
|
||||
@JsonKey(name: "description_md") required String description,
|
||||
@@ -71,11 +71,18 @@ class PacketOptions with _$PacketOptions {
|
||||
|
||||
factory PacketOptions.fromJson(Map<String, dynamic> json) => _$PacketOptionsFromJson(json);
|
||||
|
||||
factory PacketOptions.empty() => const PacketOptions(items: [], price: 1200, short: '', description: '');
|
||||
factory PacketOptions.empty() => PacketOptions(items: [], price: double.parse('1200'), short: '', description: '');
|
||||
}
|
||||
|
||||
List<String> _parseItems(json) {
|
||||
if (json is Map<String, dynamic>) {
|
||||
return json.values.map((e) => e as String).toList();
|
||||
}
|
||||
return (json as List<dynamic>).map((e) => e as String).toList();
|
||||
}
|
||||
|
||||
@freezed
|
||||
class PlanType with _$PlanType {
|
||||
abstract class PlanType with _$PlanType {
|
||||
const PlanType._();
|
||||
const factory PlanType({
|
||||
@JsonKey(name: "fit", fromJson: parseBool) required bool? fit,
|
||||
|
||||
@@ -5,7 +5,7 @@ part 'recipes_model.freezed.dart';
|
||||
part 'recipes_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class Recipe with _$Recipe {
|
||||
abstract class Recipe with _$Recipe {
|
||||
const factory Recipe({
|
||||
required int id,
|
||||
required String image,
|
||||
@@ -36,21 +36,31 @@ class Recipe with _$Recipe {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Tag with _$Tag {
|
||||
abstract class Tag with _$Tag {
|
||||
const factory Tag({required String name}) = _Tag;
|
||||
|
||||
factory Tag.fromJson(Map<String, Object?> json) => _$TagFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Ingredient with _$Ingredient {
|
||||
const factory Ingredient({required String name, String? unit, required String portion}) = _Ingredient;
|
||||
abstract class Ingredient with _$Ingredient {
|
||||
const factory Ingredient({
|
||||
required String name,
|
||||
String? unit,
|
||||
required String portion,
|
||||
@JsonKey(name: 'product_nutritional_id', fromJson: _parseProductId) required int productId,
|
||||
}) = _Ingredient;
|
||||
|
||||
factory Ingredient.fromJson(Map<String, Object?> json) => _$IngredientFromJson(json);
|
||||
}
|
||||
|
||||
int _parseProductId(json) {
|
||||
if (json is String) {
|
||||
return int.parse(json);
|
||||
}
|
||||
return json as int;
|
||||
}
|
||||
@freezed
|
||||
class Step with _$Step {
|
||||
abstract class Step with _$Step {
|
||||
const factory Step({required String image, @JsonKey(name: 'description_md') required String description}) = _Step;
|
||||
|
||||
factory Step.fromJson(Map<String, Object?> json) => _$StepFromJson(json);
|
||||
|
||||
@@ -6,7 +6,7 @@ part 'recipes_pagination_model.freezed.dart';
|
||||
part 'recipes_pagination_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class RecipesPagination with _$RecipesPagination {
|
||||
abstract class RecipesPagination with _$RecipesPagination {
|
||||
const factory RecipesPagination({
|
||||
@JsonKey(name: 'data') required List<Recipe> recipesList,
|
||||
// @JsonKey(name: "current_page") required int currentPage,
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'diets_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class DietsState with _$DietsState {
|
||||
abstract class DietsState with _$DietsState {
|
||||
const factory DietsState({
|
||||
required DietsFilterState filterState,
|
||||
required List<Diet> dietsList,
|
||||
@@ -25,7 +25,7 @@ class DietsState with _$DietsState {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class DietsFilterState with _$DietsFilterState {
|
||||
abstract class DietsFilterState with _$DietsFilterState {
|
||||
const factory DietsFilterState({
|
||||
required int sortingIndex,
|
||||
required int durationIndex,
|
||||
|
||||
@@ -27,7 +27,7 @@ abstract class PlansState with _$PlansState {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class PlansFilterState with _$PlansFilterState {
|
||||
abstract class PlansFilterState with _$PlansFilterState {
|
||||
const factory PlansFilterState({
|
||||
required (int?, int?) priceFilter,
|
||||
required int durationIndex,
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'profile_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class ProfileState with _$ProfileState {
|
||||
abstract class ProfileState with _$ProfileState {
|
||||
|
||||
factory ProfileState({
|
||||
ProfileModel? profile,
|
||||
|
||||
@@ -243,11 +243,11 @@ class _BlogPostWidgetState extends State<BlogPostWidget> {
|
||||
),
|
||||
onTap: () {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
Share.shareUri(
|
||||
Uri.parse(
|
||||
'$baseUrl/ru/blog_post/${state.feedBlogPostList[widget.index].id}',
|
||||
SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse('$baseUrl/ru/blog_post/${state.feedBlogPostList[widget.index].id}',),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -93,6 +93,7 @@ class _DietsWidgetState extends State<DietsWidget> {
|
||||
path: 'diet/${widget.diet.id}',
|
||||
id: widget.diet.id,
|
||||
complainedContentType: ComplainedContent.diet,
|
||||
diet: widget.diet,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -185,11 +186,11 @@ class _DietsWidgetState extends State<DietsWidget> {
|
||||
svgIcon: SvgPicture.asset('assets/icons/repost.svg'),
|
||||
onTap: () async {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
final result = await Share.shareUri(
|
||||
Uri.parse(
|
||||
'$baseUrl/ru/diet/${widget.diet.id}',
|
||||
final result = await SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse('$baseUrl/ru/diet/${widget.diet.id}'),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
);
|
||||
switch (result.status) {
|
||||
case ShareResultStatus.success:
|
||||
|
||||
@@ -257,11 +257,11 @@ class _PlanWidgetState extends State<PlanWidget> {
|
||||
svgIcon: SvgPicture.asset('assets/icons/repost.svg'),
|
||||
onTap: () async {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
final result = await Share.shareUri(
|
||||
Uri.parse(
|
||||
'$baseUrl/ru/diet/${widget.plan.id}',
|
||||
final result = await SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse('$baseUrl/ru/diet/${widget.plan.id}'),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
);
|
||||
switch (result.status) {
|
||||
case ShareResultStatus.success:
|
||||
|
||||
@@ -226,11 +226,11 @@ class _RecipesWidgetState extends State<RecipesWidget> {
|
||||
),
|
||||
onTap: () {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
Share.shareUri(
|
||||
Uri.parse(
|
||||
'$baseUrl/ru/blog_post/${widget.recipe.id}',
|
||||
SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse('$baseUrl/ru/blog_post/${widget.recipe.id}'),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -4,7 +4,7 @@ part 'glossary_model.freezed.dart';
|
||||
part 'glossary_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class GlossaryPaginationModel with _$GlossaryPaginationModel {
|
||||
abstract class GlossaryPaginationModel with _$GlossaryPaginationModel {
|
||||
const factory GlossaryPaginationModel({
|
||||
@JsonKey(name: 'data') required List<GlossaryModel> glossariesList,
|
||||
}) = _GlossaryPaginationModel;
|
||||
@@ -13,7 +13,7 @@ class GlossaryPaginationModel with _$GlossaryPaginationModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class GlossaryModerationModel with _$GlossaryModerationModel {
|
||||
abstract class GlossaryModerationModel with _$GlossaryModerationModel {
|
||||
const factory GlossaryModerationModel({
|
||||
@JsonKey(name: 'product_nutritional_value') required GlossaryModel glossary,
|
||||
}) = _GlossaryModerationModel;
|
||||
@@ -22,7 +22,7 @@ class GlossaryModerationModel with _$GlossaryModerationModel {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class GlossaryModel with _$GlossaryModel {
|
||||
abstract class GlossaryModel with _$GlossaryModel {
|
||||
const factory GlossaryModel({
|
||||
required int id,
|
||||
required String name,
|
||||
|
||||
@@ -5,7 +5,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'my_diets_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class MyDietsState with _$MyDietsState {
|
||||
abstract class MyDietsState with _$MyDietsState {
|
||||
factory MyDietsState({
|
||||
@Default([]) List<Diet> activeDiets,
|
||||
@Default([]) List<Diet> draftDiets,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:eatvio_app/core/app_router.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
@@ -7,6 +8,7 @@ import 'package:eatvio_app/core/utils/toasts.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/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/feed/models/diets_pagination_model.dart';
|
||||
@@ -148,11 +150,11 @@ class _MyDietWidgetState extends State<MyDietWidget> {
|
||||
CustomOutlinedButton(
|
||||
onPressed: () async {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
await Share.shareUri(
|
||||
Uri.parse(
|
||||
'$baseUrl/ru/diet/${widget.diet.id}',
|
||||
await SharePlus.instance.share(
|
||||
ShareParams(
|
||||
uri: Uri.parse('$baseUrl/ru/diet/${widget.diet.id}'),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
),
|
||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||
);
|
||||
},
|
||||
height: 52,
|
||||
@@ -194,7 +196,8 @@ class _MyDietWidgetState extends State<MyDietWidget> {
|
||||
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
||||
}
|
||||
} else {
|
||||
GoRouter.of(context).pushNamed(CreateDietScreen.name, extra: widget.diet);
|
||||
await showErrorDialog(context, title: 'Предупреждение', description: 'Вы можете создать новую диету на основе существующей.');
|
||||
AppRouter.navigatorKey.currentContext?.pushNamed(CreateDietScreen.name, extra: widget.diet);
|
||||
}
|
||||
},
|
||||
text: switch (state.selectedStateIndex) {
|
||||
|
||||
@@ -4,7 +4,7 @@ part 'card_model.freezed.dart';
|
||||
part 'card_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class CardModel with _$CardModel {
|
||||
abstract class CardModel with _$CardModel {
|
||||
factory CardModel({
|
||||
required int id,
|
||||
@JsonKey(name: 'bank_pan') required String cardNumber,
|
||||
|
||||
@@ -4,7 +4,7 @@ part 'payment_url_model.freezed.dart';
|
||||
part 'payment_url_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class PaymentUrlModel with _$PaymentUrlModel {
|
||||
abstract class PaymentUrlModel with _$PaymentUrlModel {
|
||||
|
||||
factory PaymentUrlModel({
|
||||
required String paymentUrl,
|
||||
|
||||
@@ -5,7 +5,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'user_cards_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class UserCardsState with _$UserCardsState {
|
||||
abstract class UserCardsState with _$UserCardsState {
|
||||
|
||||
factory UserCardsState({
|
||||
@Default([]) List<CardModel> cards,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user