diff --git a/analysis_options.yaml b/analysis_options.yaml index 3d36fcec..e8fba18e 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,4 +1,11 @@ + +linter: + rules: + - require_trailing_commas + analyzer: + exclude: + - '**/*.g.dart' errors: invalid_annotation_target: ignore plugins: diff --git a/assets/icons/apple.svg b/assets/icons/apple.svg new file mode 100644 index 00000000..987a839b --- /dev/null +++ b/assets/icons/apple.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/arrow.svg b/assets/icons/arrow.svg new file mode 100644 index 00000000..6ea3765b --- /dev/null +++ b/assets/icons/arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/arrow_down.svg b/assets/icons/arrow_down.svg new file mode 100644 index 00000000..2101088f --- /dev/null +++ b/assets/icons/arrow_down.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/bread.svg b/assets/icons/bread.svg new file mode 100644 index 00000000..c5e28365 --- /dev/null +++ b/assets/icons/bread.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/broccoli.svg b/assets/icons/broccoli.svg new file mode 100644 index 00000000..e694e5a3 --- /dev/null +++ b/assets/icons/broccoli.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/close_button.svg b/assets/icons/close_button.svg new file mode 100644 index 00000000..df54d212 --- /dev/null +++ b/assets/icons/close_button.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/cocktail.svg b/assets/icons/cocktail.svg new file mode 100644 index 00000000..5e0b7e93 --- /dev/null +++ b/assets/icons/cocktail.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/assets/icons/comment2.svg b/assets/icons/comment2.svg new file mode 100644 index 00000000..de57faf3 --- /dev/null +++ b/assets/icons/comment2.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/complain.svg b/assets/icons/complain.svg new file mode 100644 index 00000000..c6ef0cf4 --- /dev/null +++ b/assets/icons/complain.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/copy2.svg b/assets/icons/copy2.svg new file mode 100644 index 00000000..5049a094 --- /dev/null +++ b/assets/icons/copy2.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/icons/eye.svg b/assets/icons/eye.svg new file mode 100644 index 00000000..9e56e334 --- /dev/null +++ b/assets/icons/eye.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/heart.svg b/assets/icons/heart.svg new file mode 100644 index 00000000..b0e7a93d --- /dev/null +++ b/assets/icons/heart.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/meat2.svg b/assets/icons/meat2.svg new file mode 100644 index 00000000..d8c25b41 --- /dev/null +++ b/assets/icons/meat2.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/more_button.svg b/assets/icons/more_button.svg new file mode 100644 index 00000000..d22c4b4a --- /dev/null +++ b/assets/icons/more_button.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/people.svg b/assets/icons/people.svg new file mode 100644 index 00000000..938109c1 --- /dev/null +++ b/assets/icons/people.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/share2.svg b/assets/icons/share2.svg new file mode 100644 index 00000000..a2eb2f77 --- /dev/null +++ b/assets/icons/share2.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/time2.svg b/assets/icons/time2.svg new file mode 100644 index 00000000..aefa127b --- /dev/null +++ b/assets/icons/time2.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/app_router.dart b/lib/app_router.dart index d88156f1..6b8edb3c 100644 --- a/lib/app_router.dart +++ b/lib/app_router.dart @@ -7,6 +7,7 @@ import 'package:eatvio_app/features/calendar/presentation/pages/calendar_screen. import 'package:eatvio_app/features/chat/presentation/pages/chat_screen.dart'; import 'package:eatvio_app/features/chat/presentation/pages/chat_select_screen.dart'; import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.dart'; +import 'package:eatvio_app/features/complaining/presentation/complaining_screen.dart'; import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/become_consultant_screen.dart'; import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/stepper_become_consultant_screen.dart'; import 'package:eatvio_app/features/diary/presentation/pages/diary_screen.dart'; @@ -274,6 +275,11 @@ class AppRouter { index: int.parse(state.pathParameters['index']!), ), ), + GoRoute( + path: '/complaining-screen', + name: 'complainingScreen', + builder: (context, state) => ComplainingScreen(), + ), ], ); } diff --git a/lib/core/initialization_cubit/initialization_cubit.dart b/lib/core/initialization_cubit/initialization_cubit.dart index da7588cd..93e02a47 100644 --- a/lib/core/initialization_cubit/initialization_cubit.dart +++ b/lib/core/initialization_cubit/initialization_cubit.dart @@ -89,11 +89,11 @@ class InitializationCubit extends Cubit { emit(InitializationState.success( client: client, - )); + ),); } catch (e) { emit(InitializationState.error( errorMessage: e.toString(), - )); + ),); rethrow; } } diff --git a/lib/core/utils/localization_utils.dart b/lib/core/utils/localization_utils.dart new file mode 100644 index 00000000..23d846a0 --- /dev/null +++ b/lib/core/utils/localization_utils.dart @@ -0,0 +1,43 @@ +import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; +import 'package:eatvio_app/generated/l10n.dart'; + +String getLocalizedMeasurementUnits(S s, Portion portion) { + switch (portion) { + case Portion.GRAMS: + return s.gr; + case Portion.MILLILITERS: + return s.milliliter; + case Portion.PCS: + return s.pcs; + } +} + +String getDayForm(int number, S s) { + if (number % 100 >= 11 && number % 100 <= 14) { + return s.days; + } + + int lastDigit = number % 10; + if (lastDigit == 1) { + return s.day.toLowerCase(); + } else if (lastDigit >= 2 && lastDigit <= 4) { + return s.another_form_of_day; + } else { + return s.days; + } +} + +String getCommentForm(int number, S s) { + if (number % 100 >= 11 && number % 100 <= 14) { + return s.comments_plural; + } + + int lastDigit = number % 10; + if (lastDigit == 1) { + return s.comment; + } else if (lastDigit >= 2 && lastDigit <= 4) { + return s.another_form_of_comment; + } else { + return s.comments_plural; + } +} diff --git a/lib/core/utils/string_extentions.dart b/lib/core/utils/string_extentions.dart new file mode 100644 index 00000000..4d0e5173 --- /dev/null +++ b/lib/core/utils/string_extentions.dart @@ -0,0 +1,9 @@ + + + +extension Capitalization on String { + + String capitalize() { + return "${this[0].toUpperCase()}${substring(1).toLowerCase()}"; + } +} \ No newline at end of file diff --git a/lib/core/utils/toasts.dart b/lib/core/utils/toasts.dart new file mode 100644 index 00000000..18195ccd --- /dev/null +++ b/lib/core/utils/toasts.dart @@ -0,0 +1,15 @@ +import 'package:eatvio_app/theme/theme_helper.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +const int _toastDuration = 2; + +void showInfoToast(String message) { + Fluttertoast.showToast( + msg: message, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.TOP, + timeInSecForIosWeb: _toastDuration, + backgroundColor: appTheme.blueA400, + textColor: appTheme.whiteA700, + fontSize: 16.0,); +} diff --git a/lib/core/widgets/base_button.dart b/lib/core/widgets/base_button.dart new file mode 100644 index 00000000..c632ff4d --- /dev/null +++ b/lib/core/widgets/base_button.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class BaseButton extends StatelessWidget { + const BaseButton({super.key}); + + @override + Widget build(BuildContext context) { + return FilledButton(onPressed: () {}, child: Text('qweqwe')); + } +} diff --git a/lib/core/widgets/custom_divider.dart b/lib/core/widgets/custom_divider.dart new file mode 100644 index 00000000..8262ee4a --- /dev/null +++ b/lib/core/widgets/custom_divider.dart @@ -0,0 +1,14 @@ +import 'package:eatvio_app/theme/theme_helper.dart'; +import 'package:flutter/material.dart'; + +class CustomDivider extends StatelessWidget { + const CustomDivider({super.key}); + + @override + Widget build(BuildContext context) { + return Divider( + color: appTheme.dividerColor, + height: 1, + ); + } +} diff --git a/lib/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart b/lib/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart index 629e3d26..2990a507 100644 --- a/lib/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart +++ b/lib/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart @@ -13,7 +13,7 @@ class ProfileAvatarCubit extends Cubit { try { emit(ProfileAvatarState.loaded( profile: await profileRepository.getMyProfile(), - )); + ),); } catch (e) { emit(ProfileAvatarState.error(errorMessage: e.toString())); } diff --git a/lib/features/calendar/presentation/pages/calendar_screen.dart b/lib/features/calendar/presentation/pages/calendar_screen.dart index cf9ca83e..329ad7ec 100644 --- a/lib/features/calendar/presentation/pages/calendar_screen.dart +++ b/lib/features/calendar/presentation/pages/calendar_screen.dart @@ -89,7 +89,7 @@ class CalendarScreen extends StatelessWidget { ), ), ), - ]); + ],); } _buildCalendarBody(BuildContext context, CalendarStateLoaded state) { @@ -225,7 +225,7 @@ class CalendarScreen extends StatelessWidget { style: CustomTextStyles.titleSmallBold, // Предполагается, что текст белый ), ), - ]), + ],), ], ), ), diff --git a/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart b/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart index ef4a3f3a..2ee24863 100644 --- a/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart +++ b/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart @@ -38,7 +38,7 @@ class ChatRoomCubit extends Cubit { chatMessages: (state as ChatRoomStateLoaded).chatMessages, isTyping: (state as ChatRoomStateLoaded).isTyping, isLoading: isLoading, - )); + ),); try { final chatMessages = await chatRepository.getChatMessages(chatId, currentPage + 1); @@ -49,7 +49,7 @@ class ChatRoomCubit extends Cubit { chatMessages: [...(state as ChatRoomStateLoaded).chatMessages, ...chatMessages.messagesList], isTyping: (state as ChatRoomStateLoaded).isTyping, isLoading: isLoading, - )); + ),); currentPage++; } catch (e) { @@ -72,7 +72,7 @@ class ChatRoomCubit extends Cubit { chatMessages: chatMessages.messagesList, isTyping: false, isLoading: isLoading, - )); + ),); } catch (e) { emit(ChatRoomState.error(errorMessage: 'Ошибка получеия данных')); } diff --git a/lib/features/chat/presentation/bloc/chat_select_cubit/chat_select_cubit.dart b/lib/features/chat/presentation/bloc/chat_select_cubit/chat_select_cubit.dart index 6c119f19..b190cb27 100644 --- a/lib/features/chat/presentation/bloc/chat_select_cubit/chat_select_cubit.dart +++ b/lib/features/chat/presentation/bloc/chat_select_cubit/chat_select_cubit.dart @@ -14,7 +14,7 @@ class ChatSelectCubit extends Cubit { try { emit(ChatSelectState.loaded( chatRooms: (await chatRepository.getChatRoomPreviews(query: query)), - )); + ),); } catch (e) { emit( ChatSelectState.error(errorMessage: e.toString()), diff --git a/lib/features/complaining/presentation/complaining_screen.dart b/lib/features/complaining/presentation/complaining_screen.dart new file mode 100644 index 00000000..2c3e0fd3 --- /dev/null +++ b/lib/features/complaining/presentation/complaining_screen.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class ComplainingScreen extends StatelessWidget { + const ComplainingScreen({super.key}); + + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} \ No newline at end of file diff --git a/lib/features/diets/presentation/bloc/about_diet_cubit.dart b/lib/features/diets/presentation/bloc/about_diet_cubit.dart new file mode 100644 index 00000000..830dd3a6 --- /dev/null +++ b/lib/features/diets/presentation/bloc/about_diet_cubit.dart @@ -0,0 +1,115 @@ +import 'dart:async'; +import 'dart:developer'; + +import 'package:easy_debounce/easy_debounce.dart'; +import 'package:eatvio_app/features/diets/presentation/bloc/about_diet_state.dart'; +import 'package:eatvio_app/features/diets/repositories/diets_repository.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class AboutDietCubit extends Cubit { + AboutDietCubit({required this.dietsRepository}) : super(AboutDietState.initial()); + final DietsRepository dietsRepository; + + Future getDiet(int id) async { + try { + final diet = await dietsRepository.getDiet(id); + emit(AboutDietState.loaded(diet)); + } catch (e) { + log(e.toString()); + } + } + + Future getDietWithLoading(int id) async{ + try { + emit(AboutDietState.loading()); + final diet = await dietsRepository.getDiet(id); + emit(AboutDietState.loaded(diet)); + } catch (e) { + log(e.toString()); + } + } + + Future likeDiet(int id) async { + if (state is! DietStateLoaded) return; + + final currentDiet = (state as DietStateLoaded).diet; + + if (currentDiet.wasLikedByMe) { + final updatedDiet = currentDiet.copyWith( + totalLikes: currentDiet.totalLikes - 1, + wasLikedByMe: false, + ); + emit(AboutDietState.loaded(updatedDiet)); + } else { + final updatedDiet = currentDiet.copyWith( + totalLikes: currentDiet.totalLikes + 1, + wasLikedByMe: true, + ); + emit(AboutDietState.loaded(updatedDiet)); + } + + EasyDebounce.debounce( + 'like_diet', + Duration(seconds: 1), + () async { + try { + await dietsRepository.likeDiet(id); + await getDiet(id); + } catch (e) { + log(e.toString()); + emit(AboutDietState.loaded(currentDiet)); + } + }, + ); + } + + Future addToFavorite(int id) async { + if (state is! DietStateLoaded) return; + + final currentDiet = (state as DietStateLoaded).diet; + + final updatedDiet = currentDiet.copyWith( + isFavorite: true, + ); + emit(AboutDietState.loaded(updatedDiet)); + + EasyDebounce.debounce( + 'add_diet_to_favorites', + Duration(seconds: 1), + () async { + try { + await dietsRepository.addToFavorite(id); + await getDiet(id); + } catch (e) { + log(e.toString()); + emit(AboutDietState.loaded(currentDiet)); + } + }, + ); + } + + Future removeFromFavorite(int id) async { + if (state is! DietStateLoaded) return; + + final currentDiet = (state as DietStateLoaded).diet; + + final updatedDiet = currentDiet.copyWith( + isFavorite: false, + ); + emit(AboutDietState.loaded(updatedDiet)); + + EasyDebounce.debounce( + 'remove_diet_from_favorites', + Duration(seconds: 1), + () async { + try { + await dietsRepository.removeFromFavorite(id); + await getDiet(id); + } catch (e) { + log(e.toString()); + emit(AboutDietState.loaded(currentDiet)); + } + }, + ); + } +} diff --git a/lib/features/diets/presentation/bloc/about_diet_state.dart b/lib/features/diets/presentation/bloc/about_diet_state.dart new file mode 100644 index 00000000..36a78a31 --- /dev/null +++ b/lib/features/diets/presentation/bloc/about_diet_state.dart @@ -0,0 +1,14 @@ +import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'about_diet_state.freezed.dart'; + +@freezed +class AboutDietState with _$AboutDietState { + const factory AboutDietState() = _AboutDietState; + + const factory AboutDietState.initial() = DietStateInitial; + const factory AboutDietState.loading() = DietStateLoading; + const factory AboutDietState.loaded(Diet diet) = DietStateLoaded; + const factory AboutDietState.error({required String message}) = DietStateError; +} diff --git a/lib/features/diets/presentation/bloc/about_diet_state.freezed.dart b/lib/features/diets/presentation/bloc/about_diet_state.freezed.dart new file mode 100644 index 00000000..ce2270dc --- /dev/null +++ b/lib/features/diets/presentation/bloc/about_diet_state.freezed.dart @@ -0,0 +1,796 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'about_diet_state.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +/// @nodoc +mixin _$AboutDietState { + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(Diet diet) loaded, + required TResult Function(String message) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(Diet diet)? loaded, + TResult? Function(String message)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initial, + TResult Function()? loading, + TResult Function(Diet diet)? loaded, + TResult Function(String message)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map( + TResult Function(_AboutDietState value) $default, { + required TResult Function(DietStateInitial value) initial, + required TResult Function(DietStateLoading value) loading, + required TResult Function(DietStateLoaded value) loaded, + required TResult Function(DietStateError value) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_AboutDietState value)? $default, { + TResult? Function(DietStateInitial value)? initial, + TResult? Function(DietStateLoading value)? loading, + TResult? Function(DietStateLoaded value)? loaded, + TResult? Function(DietStateError value)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap( + TResult Function(_AboutDietState value)? $default, { + TResult Function(DietStateInitial value)? initial, + TResult Function(DietStateLoading value)? loading, + TResult Function(DietStateLoaded value)? loaded, + TResult Function(DietStateError value)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $AboutDietStateCopyWith<$Res> { + factory $AboutDietStateCopyWith( + AboutDietState value, $Res Function(AboutDietState) then) = + _$AboutDietStateCopyWithImpl<$Res, AboutDietState>; +} + +/// @nodoc +class _$AboutDietStateCopyWithImpl<$Res, $Val extends AboutDietState> + implements $AboutDietStateCopyWith<$Res> { + _$AboutDietStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of AboutDietState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc +abstract class _$$AboutDietStateImplCopyWith<$Res> { + factory _$$AboutDietStateImplCopyWith(_$AboutDietStateImpl value, + $Res Function(_$AboutDietStateImpl) then) = + __$$AboutDietStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$AboutDietStateImplCopyWithImpl<$Res> + extends _$AboutDietStateCopyWithImpl<$Res, _$AboutDietStateImpl> + implements _$$AboutDietStateImplCopyWith<$Res> { + __$$AboutDietStateImplCopyWithImpl( + _$AboutDietStateImpl _value, $Res Function(_$AboutDietStateImpl) _then) + : super(_value, _then); + + /// Create a copy of AboutDietState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$AboutDietStateImpl implements _AboutDietState { + const _$AboutDietStateImpl(); + + @override + String toString() { + return 'AboutDietState()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$AboutDietStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(Diet diet) loaded, + required TResult Function(String message) error, + }) { + return $default(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(Diet diet)? loaded, + TResult? Function(String message)? error, + }) { + return $default?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initial, + TResult Function()? loading, + TResult Function(Diet diet)? loaded, + TResult Function(String message)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_AboutDietState value) $default, { + required TResult Function(DietStateInitial value) initial, + required TResult Function(DietStateLoading value) loading, + required TResult Function(DietStateLoaded value) loaded, + required TResult Function(DietStateError value) error, + }) { + return $default(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_AboutDietState value)? $default, { + TResult? Function(DietStateInitial value)? initial, + TResult? Function(DietStateLoading value)? loading, + TResult? Function(DietStateLoaded value)? loaded, + TResult? Function(DietStateError value)? error, + }) { + return $default?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_AboutDietState value)? $default, { + TResult Function(DietStateInitial value)? initial, + TResult Function(DietStateLoading value)? loading, + TResult Function(DietStateLoaded value)? loaded, + TResult Function(DietStateError value)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(this); + } + return orElse(); + } +} + +abstract class _AboutDietState implements AboutDietState { + const factory _AboutDietState() = _$AboutDietStateImpl; +} + +/// @nodoc +abstract class _$$DietStateInitialImplCopyWith<$Res> { + factory _$$DietStateInitialImplCopyWith(_$DietStateInitialImpl value, + $Res Function(_$DietStateInitialImpl) then) = + __$$DietStateInitialImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$DietStateInitialImplCopyWithImpl<$Res> + extends _$AboutDietStateCopyWithImpl<$Res, _$DietStateInitialImpl> + implements _$$DietStateInitialImplCopyWith<$Res> { + __$$DietStateInitialImplCopyWithImpl(_$DietStateInitialImpl _value, + $Res Function(_$DietStateInitialImpl) _then) + : super(_value, _then); + + /// Create a copy of AboutDietState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$DietStateInitialImpl implements DietStateInitial { + const _$DietStateInitialImpl(); + + @override + String toString() { + return 'AboutDietState.initial()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$DietStateInitialImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(Diet diet) loaded, + required TResult Function(String message) error, + }) { + return initial(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(Diet diet)? loaded, + TResult? Function(String message)? error, + }) { + return initial?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initial, + TResult Function()? loading, + TResult Function(Diet diet)? loaded, + TResult Function(String message)? error, + required TResult orElse(), + }) { + if (initial != null) { + return initial(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_AboutDietState value) $default, { + required TResult Function(DietStateInitial value) initial, + required TResult Function(DietStateLoading value) loading, + required TResult Function(DietStateLoaded value) loaded, + required TResult Function(DietStateError value) error, + }) { + return initial(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_AboutDietState value)? $default, { + TResult? Function(DietStateInitial value)? initial, + TResult? Function(DietStateLoading value)? loading, + TResult? Function(DietStateLoaded value)? loaded, + TResult? Function(DietStateError value)? error, + }) { + return initial?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_AboutDietState value)? $default, { + TResult Function(DietStateInitial value)? initial, + TResult Function(DietStateLoading value)? loading, + TResult Function(DietStateLoaded value)? loaded, + TResult Function(DietStateError value)? error, + required TResult orElse(), + }) { + if (initial != null) { + return initial(this); + } + return orElse(); + } +} + +abstract class DietStateInitial implements AboutDietState { + const factory DietStateInitial() = _$DietStateInitialImpl; +} + +/// @nodoc +abstract class _$$DietStateLoadingImplCopyWith<$Res> { + factory _$$DietStateLoadingImplCopyWith(_$DietStateLoadingImpl value, + $Res Function(_$DietStateLoadingImpl) then) = + __$$DietStateLoadingImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$DietStateLoadingImplCopyWithImpl<$Res> + extends _$AboutDietStateCopyWithImpl<$Res, _$DietStateLoadingImpl> + implements _$$DietStateLoadingImplCopyWith<$Res> { + __$$DietStateLoadingImplCopyWithImpl(_$DietStateLoadingImpl _value, + $Res Function(_$DietStateLoadingImpl) _then) + : super(_value, _then); + + /// Create a copy of AboutDietState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$DietStateLoadingImpl implements DietStateLoading { + const _$DietStateLoadingImpl(); + + @override + String toString() { + return 'AboutDietState.loading()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$DietStateLoadingImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(Diet diet) loaded, + required TResult Function(String message) error, + }) { + return loading(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(Diet diet)? loaded, + TResult? Function(String message)? error, + }) { + return loading?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initial, + TResult Function()? loading, + TResult Function(Diet diet)? loaded, + TResult Function(String message)? error, + required TResult orElse(), + }) { + if (loading != null) { + return loading(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_AboutDietState value) $default, { + required TResult Function(DietStateInitial value) initial, + required TResult Function(DietStateLoading value) loading, + required TResult Function(DietStateLoaded value) loaded, + required TResult Function(DietStateError value) error, + }) { + return loading(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_AboutDietState value)? $default, { + TResult? Function(DietStateInitial value)? initial, + TResult? Function(DietStateLoading value)? loading, + TResult? Function(DietStateLoaded value)? loaded, + TResult? Function(DietStateError value)? error, + }) { + return loading?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_AboutDietState value)? $default, { + TResult Function(DietStateInitial value)? initial, + TResult Function(DietStateLoading value)? loading, + TResult Function(DietStateLoaded value)? loaded, + TResult Function(DietStateError value)? error, + required TResult orElse(), + }) { + if (loading != null) { + return loading(this); + } + return orElse(); + } +} + +abstract class DietStateLoading implements AboutDietState { + const factory DietStateLoading() = _$DietStateLoadingImpl; +} + +/// @nodoc +abstract class _$$DietStateLoadedImplCopyWith<$Res> { + factory _$$DietStateLoadedImplCopyWith(_$DietStateLoadedImpl value, + $Res Function(_$DietStateLoadedImpl) then) = + __$$DietStateLoadedImplCopyWithImpl<$Res>; + @useResult + $Res call({Diet diet}); + + $DietCopyWith<$Res> get diet; +} + +/// @nodoc +class __$$DietStateLoadedImplCopyWithImpl<$Res> + extends _$AboutDietStateCopyWithImpl<$Res, _$DietStateLoadedImpl> + implements _$$DietStateLoadedImplCopyWith<$Res> { + __$$DietStateLoadedImplCopyWithImpl( + _$DietStateLoadedImpl _value, $Res Function(_$DietStateLoadedImpl) _then) + : super(_value, _then); + + /// Create a copy of AboutDietState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? diet = null, + }) { + return _then(_$DietStateLoadedImpl( + null == diet + ? _value.diet + : diet // ignore: cast_nullable_to_non_nullable + as Diet, + )); + } + + /// Create a copy of AboutDietState + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $DietCopyWith<$Res> get diet { + return $DietCopyWith<$Res>(_value.diet, (value) { + return _then(_value.copyWith(diet: value)); + }); + } +} + +/// @nodoc + +class _$DietStateLoadedImpl implements DietStateLoaded { + const _$DietStateLoadedImpl(this.diet); + + @override + final Diet diet; + + @override + String toString() { + return 'AboutDietState.loaded(diet: $diet)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$DietStateLoadedImpl && + (identical(other.diet, diet) || other.diet == diet)); + } + + @override + int get hashCode => Object.hash(runtimeType, diet); + + /// Create a copy of AboutDietState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$DietStateLoadedImplCopyWith<_$DietStateLoadedImpl> get copyWith => + __$$DietStateLoadedImplCopyWithImpl<_$DietStateLoadedImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(Diet diet) loaded, + required TResult Function(String message) error, + }) { + return loaded(diet); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(Diet diet)? loaded, + TResult? Function(String message)? error, + }) { + return loaded?.call(diet); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initial, + TResult Function()? loading, + TResult Function(Diet diet)? loaded, + TResult Function(String message)? error, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded(diet); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_AboutDietState value) $default, { + required TResult Function(DietStateInitial value) initial, + required TResult Function(DietStateLoading value) loading, + required TResult Function(DietStateLoaded value) loaded, + required TResult Function(DietStateError value) error, + }) { + return loaded(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_AboutDietState value)? $default, { + TResult? Function(DietStateInitial value)? initial, + TResult? Function(DietStateLoading value)? loading, + TResult? Function(DietStateLoaded value)? loaded, + TResult? Function(DietStateError value)? error, + }) { + return loaded?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_AboutDietState value)? $default, { + TResult Function(DietStateInitial value)? initial, + TResult Function(DietStateLoading value)? loading, + TResult Function(DietStateLoaded value)? loaded, + TResult Function(DietStateError value)? error, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded(this); + } + return orElse(); + } +} + +abstract class DietStateLoaded implements AboutDietState { + const factory DietStateLoaded(final Diet diet) = _$DietStateLoadedImpl; + + Diet get diet; + + /// Create a copy of AboutDietState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$DietStateLoadedImplCopyWith<_$DietStateLoadedImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$DietStateErrorImplCopyWith<$Res> { + factory _$$DietStateErrorImplCopyWith(_$DietStateErrorImpl value, + $Res Function(_$DietStateErrorImpl) then) = + __$$DietStateErrorImplCopyWithImpl<$Res>; + @useResult + $Res call({String message}); +} + +/// @nodoc +class __$$DietStateErrorImplCopyWithImpl<$Res> + extends _$AboutDietStateCopyWithImpl<$Res, _$DietStateErrorImpl> + implements _$$DietStateErrorImplCopyWith<$Res> { + __$$DietStateErrorImplCopyWithImpl( + _$DietStateErrorImpl _value, $Res Function(_$DietStateErrorImpl) _then) + : super(_value, _then); + + /// Create a copy of AboutDietState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? message = null, + }) { + return _then(_$DietStateErrorImpl( + message: null == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$DietStateErrorImpl implements DietStateError { + const _$DietStateErrorImpl({required this.message}); + + @override + final String message; + + @override + String toString() { + return 'AboutDietState.error(message: $message)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$DietStateErrorImpl && + (identical(other.message, message) || other.message == message)); + } + + @override + int get hashCode => Object.hash(runtimeType, message); + + /// Create a copy of AboutDietState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$DietStateErrorImplCopyWith<_$DietStateErrorImpl> get copyWith => + __$$DietStateErrorImplCopyWithImpl<_$DietStateErrorImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(Diet diet) loaded, + required TResult Function(String message) error, + }) { + return error(message); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(Diet diet)? loaded, + TResult? Function(String message)? error, + }) { + return error?.call(message); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initial, + TResult Function()? loading, + TResult Function(Diet diet)? loaded, + TResult Function(String message)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(message); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_AboutDietState value) $default, { + required TResult Function(DietStateInitial value) initial, + required TResult Function(DietStateLoading value) loading, + required TResult Function(DietStateLoaded value) loaded, + required TResult Function(DietStateError value) error, + }) { + return error(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_AboutDietState value)? $default, { + TResult? Function(DietStateInitial value)? initial, + TResult? Function(DietStateLoading value)? loading, + TResult? Function(DietStateLoaded value)? loaded, + TResult? Function(DietStateError value)? error, + }) { + return error?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_AboutDietState value)? $default, { + TResult Function(DietStateInitial value)? initial, + TResult Function(DietStateLoading value)? loading, + TResult Function(DietStateLoaded value)? loaded, + TResult Function(DietStateError value)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(this); + } + return orElse(); + } +} + +abstract class DietStateError implements AboutDietState { + const factory DietStateError({required final String message}) = + _$DietStateErrorImpl; + + String get message; + + /// Create a copy of AboutDietState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$DietStateErrorImplCopyWith<_$DietStateErrorImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/features/diets/presentation/pages/about_diet_screen.dart b/lib/features/diets/presentation/pages/about_diet_screen.dart index 7dd66a37..db0bd378 100644 --- a/lib/features/diets/presentation/pages/about_diet_screen.dart +++ b/lib/features/diets/presentation/pages/about_diet_screen.dart @@ -2,17 +2,31 @@ import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/core/utils/localization_utils.dart'; +import 'package:eatvio_app/core/utils/string_extentions.dart'; +import 'package:eatvio_app/core/widgets/custom_divider.dart'; +import 'package:eatvio_app/features/diets/presentation/bloc/about_diet_cubit.dart'; +import 'package:eatvio_app/features/diets/presentation/bloc/about_diet_state.dart'; +import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart'; +import 'package:eatvio_app/features/diets/presentation/widgets/cpfc_container.dart'; +import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart'; +import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart'; +import 'package:eatvio_app/features/diets/presentation/widgets/main_background_bouble.dart'; +import 'package:eatvio_app/features/diets/presentation/widgets/more_options.dart'; +import 'package:eatvio_app/features/diets/presentation/widgets/start_diet_bottom_sheet.dart'; +import 'package:eatvio_app/features/diets/presentation/widgets/type_badges.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_state.dart'; import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart'; +import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/locator_service.dart'; import 'package:eatvio_app/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_html/flutter_html.dart'; -import 'package:go_router/go_router.dart'; -import 'package:rive/rive.dart'; +import 'package:flutter_portal/flutter_portal.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:rive/rive.dart' hide LinearGradient; class AboutDietScreen extends StatefulWidget { final Diet diet; @@ -28,333 +42,490 @@ class AboutDietScreen extends StatefulWidget { class _AboutDietScreenState extends State { final CommentsCubit commentsCubit = sl(); + final AboutDietCubit dietsCubit = sl(); int? replyId; + final ScrollController scrollController = ScrollController(); final textEditingController = TextEditingController(); + bool isMoreOptionsVisible = false; + + @override + void initState() { + super.initState(); + scrollController.addListener(() { + if (isMoreOptionsVisible) { + setState(() { + isMoreOptionsVisible = false; + }); + } + }); + } @override Widget build(BuildContext context) { - return BlocProvider( - create: (BuildContext context) => commentsCubit - ..getComments( - widget.diet.id.toString(), - CommentSourceType.diet, - true, - ), - child: BlocConsumer( - listener: (BuildContext context, CommentsState state) { - if (state is CommentsStateLoaded) { - setState(() { - replyId = state.replyId; - }); - } - }, - builder: (BuildContext context, CommentsState state) { - return Scaffold( - resizeToAvoidBottomInset: true, - bottomSheet: Container( - padding: EdgeInsets.symmetric(vertical: 12) + (Platform.isIOS ? const EdgeInsets.only(bottom: 16) : EdgeInsets.zero), - decoration: BoxDecoration( - color: Colors.white, - border: Border( - top: BorderSide( - color: Colors.black.withOpacity(0.1), - ), - ), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - width: MediaQuery.of(context).size.width - 24, - child: FloatingActionButton.extended( - heroTag: 'nextButtonRegistration', - highlightElevation: 0, - elevation: 0, - focusElevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - backgroundColor: appTheme.greenA700, - onPressed: () {}, - label: Text( - 'Начать диету', - style: theme.textTheme.titleSmall, - ), - ), - ), - SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (replyId != null) - GestureDetector( - onTap: () { - context.read().setReplyId(null); - setState(() { - replyId = null; - textEditingController.clear(); - }); - FocusManager.instance.primaryFocus?.unfocus(); - }, - child: Container( - decoration: BoxDecoration( - color: appTheme.blueA400, - shape: BoxShape.circle, + final s = S.of(context); + return BlocProvider( + create: (context) => dietsCubit..getDietWithLoading(widget.diet.id), + child: BlocProvider( + create: (BuildContext context) => commentsCubit + ..getComments( + widget.diet.id.toString(), + CommentSourceType.diet, + true, + ), + child: BlocConsumer( + listener: (BuildContext context, CommentsState commentsState) { + if (commentsState is CommentsStateLoaded) { + setState(() { + replyId = commentsState.replyId; + }); + } + }, + builder: (BuildContext context, CommentsState state) { + return BlocBuilder( + builder: (context, dietState) { + return dietState is DietStateLoaded + ? Scaffold( + backgroundColor: appTheme.gray100, + resizeToAvoidBottomInset: true, + bottomSheet: Container( + padding: EdgeInsets.symmetric(vertical: 12) + (Platform.isIOS ? const EdgeInsets.only(bottom: 16) : EdgeInsets.zero), + decoration: BoxDecoration( + color: Colors.white, + border: Border( + top: BorderSide( + color: Colors.black.withOpacity(0.1), + ), ), + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Expanded( + child: SizedBox( + child: FloatingActionButton.extended( + heroTag: 'nextButtonRegistration', + highlightElevation: 0, + elevation: 0, + focusElevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + backgroundColor: appTheme.greenA700, + onPressed: () { + showModalBottomSheet(context: context, builder: (context) => StartDietBottomSheet()); + }, + label: Text( + s.startDiet, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: appTheme.whiteA700, + ), + ), + ), + ), + ), + SizedBox(width: 8), + GestureDetector( + onTap: () { + if (dietState.diet.isFavorite) { + dietsCubit.removeFromFavorite(widget.diet.id); + } else { + dietsCubit.addToFavorite(widget.diet.id); + } + }, + child: Container( + width: 52, + height: 52, + decoration: BoxDecoration(color: appTheme.gray50, shape: BoxShape.circle), + child: Icon( + dietState.diet.isFavorite ? Icons.bookmark : Icons.bookmark_border_rounded, + color: appTheme.blueGray800, + size: 24, + ), + ), + ), + ], + ), + SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (replyId != null) + GestureDetector( + onTap: () { + context.read().setReplyId(null); + setState(() { + replyId = null; + textEditingController.clear(); + }); + FocusManager.instance.primaryFocus?.unfocus(); + }, + child: Container( + decoration: BoxDecoration( + color: appTheme.blueA400, + shape: BoxShape.circle, + ), + height: 32, + width: 32, + child: Icon( + Icons.close_rounded, + size: 18, + color: Colors.white, + ), + ), + ), + if (replyId != null) SizedBox(width: 8), + Expanded( + child: SizedBox( + child: TextFormField( + onChanged: (_) { + setState(() {}); + }, + controller: textEditingController, + maxLines: 5, + minLines: 1, + keyboardType: TextInputType.multiline, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: Colors.black, + height: 1, + ), + decoration: InputDecoration( + isDense: true, + contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), + hintText: s.comment.capitalize(), + hintStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + height: 1, + color: Color(0xFFAEAEB2), + ), + filled: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide( + color: Color(0xFFD1D1D6), + width: 1, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide( + color: Color(0xFFD1D1D6), + width: 1, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide( + color: Color(0xFFD1D1D6), + width: 1, + ), + ), + ), + ), + ), + ), + SizedBox(width: 8), + GestureDetector( + onTap: () { + if (textEditingController.text.length > 2) { + commentsCubit.sendComment( + id: widget.diet.id.toString(), + commentText: textEditingController.text, + parentCommentId: replyId, + commentSourceType: CommentSourceType.diet, + ); + context.read().setReplyId(null); + setState(() { + textEditingController.clear(); + replyId = null; + }); + dietsCubit.getDiet(widget.diet.id); + FocusManager.instance.primaryFocus?.unfocus(); + } + }, + child: Container( + decoration: BoxDecoration( + color: textEditingController.text.length > 2 ? appTheme.blueA400 : appTheme.gray50, + shape: BoxShape.circle, + ), + height: 32, + width: 32, + child: Icon( + Icons.send_rounded, + size: 18, + color: textEditingController.text.length > 2 ? appTheme.whiteA700 : appTheme.blueGray800, + ), + ), + ), + ], + ), + ], + ), + ), + ), + appBar: AppBar( + centerTitle: true, + title: Text( + widget.diet.name, + maxLines: 2, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + softWrap: true, + style: TextStyle( + color: Colors.black, + fontSize: 24, + fontWeight: FontWeight.w500, + height: 1, + ), + ), + actions: [ + PortalTarget( + visible: isMoreOptionsVisible, + portalFollower: MoreOptions(id: widget.diet.id), + anchor: Aligned( + shiftToWithinBound: AxisFlag(x: true, y: true), + follower: Alignment.topRight, + target: Alignment.bottomRight, + ), + child: IconButton( + onPressed: () { + setState(() { + isMoreOptionsVisible = !isMoreOptionsVisible; + }); + }, + icon: SvgPicture.asset( + 'assets/icons/more_button.svg', + width: 20, + height: 20, + ),), + ), + ], + ), + body: SingleChildScrollView( + controller: scrollController, + padding: EdgeInsets.only(bottom: 160), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 8), + MainBackgroundBouble( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack(children: [ + CachedNetworkImage( + imageUrl: baseUrl + '/storage/' + widget.diet.image, + ), + Positioned( + bottom: 16, + left: 0, + right: 0, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + InfoBouble( + children: [ + SvgPicture.asset('assets/icons/eye.svg', width: 18, height: 12), + SizedBox(width: 8), + Text( + dietState.diet.totalViews.toString(), + style: TextStyle(color: appTheme.blueGray800, fontSize: 12, fontWeight: FontWeight.w500), + ), + ], + backgroundColor: appTheme.whiteA700, + ), + SizedBox(width: 12), + GestureDetector( + onTap: () { + dietsCubit.likeDiet(widget.diet.id); + }, + child: InfoBouble( + children: [ + SvgPicture.asset( + 'assets/icons/heart.svg', + width: 14, + height: 12, + colorFilter: dietState.diet.wasLikedByMe ? ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn) : null, + ), + SizedBox(width: 8), + Text( + dietState.diet.totalLikes.toString(), + style: TextStyle(color: appTheme.blueGray800, fontSize: 12, fontWeight: FontWeight.w500), + ), + ], + backgroundColor: appTheme.whiteA700, + ), + ), + SizedBox(width: 12), + InfoBouble( + children: [ + SvgPicture.asset('assets/icons/comment2.svg', width: 13, height: 12), + SizedBox(width: 8), + Text( + dietState.diet.totalComments.toString(), + style: TextStyle(color: appTheme.blueGray800, fontSize: 12, fontWeight: FontWeight.w500), + ), + ], + backgroundColor: appTheme.whiteA700, + ), + ], + ), + ), + ],), + SizedBox(height: 16), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Text( + s.about_diet, + style: TextStyle(color: appTheme.blueGray800, fontSize: 20, fontWeight: FontWeight.w600), + ), + ), + SizedBox(height: 12), + SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 16), + scrollDirection: Axis.horizontal, + child: Row( + children: [ + InfoBouble( + children: [ + SvgPicture.asset( + 'assets/icons/time2.svg', + width: 13.3, + height: 13.3, + ), + SizedBox(width: 4), + Text(widget.diet.duration.toString() + ' ' + getDayForm(widget.diet.duration, s), + style: TextStyle(color: appTheme.blueGray800, fontSize: 12, fontWeight: FontWeight.w500),), + ], + ), + SizedBox(width: 12), + InfoBouble( + children: [ + SvgPicture.asset( + 'assets/icons/people.svg', + width: 13.3, + height: 13.3, + ), + SizedBox(width: 4), + Text('80 человек прошли(статика, ещё нет бека)', + style: TextStyle(color: appTheme.blueGray800, fontSize: 12, fontWeight: FontWeight.w500),), + ], + ), + SizedBox(width: 12), + InfoBouble( + children: [ + TypeBadges(type: widget.diet.type), + ], + ), + ], + ), + ), + ExpandableText( + text: widget.diet.details, + ), + SizedBox(height: 20), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: AuthorCard( + author: widget.diet.author, + ), + ), + SizedBox(height: 16), + ], + ), + ), + SizedBox(height: 16), + MainBackgroundBouble( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Center( + child: Text( + s.diet_plan + '(${widget.diet.duration.toString()} ' + getDayForm(widget.diet.duration, s) + ')', + style: TextStyle( + color: appTheme.blueGray800, + fontSize: 20, + fontWeight: FontWeight.w600, + ), + ), + ), + ),), + SizedBox(height: 8), + ListView.builder( + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + return _dayOfPlan(widget.diet.dailySchedule[index], index, s); + }, + itemCount: widget.diet.dailySchedule.length, + ), + MainBackgroundBouble( + padding: EdgeInsets.symmetric(vertical: 16), + child: Column(children: [ + Container( + padding: EdgeInsets.symmetric(horizontal: 16), + alignment: Alignment.centerLeft, + child: Text( + dietState.diet.totalComments.toString() + ' ' + getCommentForm(widget.diet.totalComments, s), + style: TextStyle( + fontSize: 12, + color: appTheme.gray500, + fontWeight: FontWeight.w400, + ), + ), + ), + SizedBox(height: 16), + if (state is CommentsStateLoading) + Padding( + padding: const EdgeInsets.all(32), + child: SizedBox( + height: 32, + width: 32, + child: RiveAnimation.asset( + 'assets/rive_animations/loading.riv', + ), + ), + ), + if (state is CommentsStateLoaded) + CommentsWidget( + id: widget.diet.id.toString(), + commentSourceType: CommentSourceType.diet, + comment: state.comment, + replyId: replyId, + ), + ],), + ), + ], + ), + ), + ) + : Scaffold( + body: Center( + child: SizedBox( height: 32, width: 32, - child: Icon( - Icons.close_rounded, - size: 18, - color: Colors.white, + child: RiveAnimation.asset( + 'assets/rive_animations/loading.riv', ), ), ), - if (replyId != null) SizedBox(width: 8), - SizedBox( - width: MediaQuery.of(context).size.width - (replyId == null ? 64 : 104), - child: TextFormField( - onChanged: (_) { - setState(() {}); - }, - controller: textEditingController, - maxLines: 5, - minLines: 1, - keyboardType: TextInputType.multiline, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - color: Colors.black, - height: 1, - ), - decoration: InputDecoration( - isDense: true, - contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), - hintText: 'Комментарий', - hintStyle: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - height: 1, - color: Color(0xFFAEAEB2), - ), - filled: true, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - borderSide: BorderSide( - color: Color(0xFFD1D1D6), - width: 1, - ), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - borderSide: BorderSide( - color: Color(0xFFD1D1D6), - width: 1, - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - borderSide: BorderSide( - color: Color(0xFFD1D1D6), - width: 1, - ), - ), - ), - ), - ), - SizedBox(width: 8), - GestureDetector( - onTap: () { - if (textEditingController.text.length > 2) { - commentsCubit.sendComment( - id: widget.diet.id.toString(), - commentText: textEditingController.text, - parentCommentId: replyId, - commentSourceType: CommentSourceType.diet, - ); - context.read().setReplyId(null); - setState(() { - textEditingController.clear(); - replyId = null; - }); - FocusManager.instance.primaryFocus?.unfocus(); - } - }, - child: Container( - decoration: BoxDecoration( - color: textEditingController.text.length > 2 ? appTheme.blueA400 : Colors.grey, - shape: BoxShape.circle, - ), - height: 32, - width: 32, - child: Icon( - Icons.send_rounded, - size: 18, - color: Colors.white, - ), - ), - ), - ], - ), - ], - ), - ), - appBar: AppBar( - actions: [ - InkWell( - borderRadius: BorderRadius.circular(16), - onTap: () => GoRouter.of(context).pushNamed('profileScreen', extra: widget.diet.author), - child: Ink( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - CircleAvatar( - maxRadius: 16, - minRadius: 16, - backgroundImage: NetworkImage(widget.diet.author.authorProfileIcon), - ), - SizedBox(height: 4), - Text( - widget.diet.author.authorName, - style: TextStyle( - color: Colors.black, - fontSize: 14, - fontWeight: FontWeight.w400, - height: 1, - ), - ), - ], - ), - ), - ), - ], - ), - body: SingleChildScrollView( - padding: EdgeInsets.only(bottom: 160), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - widget.diet.name, - style: TextStyle( - color: Colors.black, - fontSize: 24, - fontWeight: FontWeight.w500, - height: 1, - ), - ), - ), - SizedBox(height: 12), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Row( - children: [ - Icon(Icons.timer_outlined), - SizedBox(width: 4), - Text( - widget.diet.duration.toString() + ' дней', - style: TextStyle( - color: Colors.black, - fontSize: 12, - fontWeight: FontWeight.w400, - height: 1, - ), - ), - ], - ), - ), - if (widget.diet.details.isNotEmpty) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Html( - data: widget.diet.details, - style: { - "*": Style( - color: Colors.black87, - ), - }, - ), - ), - SizedBox(height: 12), - CachedNetworkImage( - imageUrl: baseUrl + '/storage/' + widget.diet.image, - ), - SizedBox(height: 12), - ListView.separated( - padding: EdgeInsets.all(16), - physics: NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemBuilder: (context, index) { - return _dayOfPlan(widget.diet.dailySchedule[index], index); - }, - separatorBuilder: (context, index) => Padding( - padding: const EdgeInsets.symmetric(vertical: 16), - child: Container( - height: 0.4, - decoration: BoxDecoration( - color: Colors.grey, - borderRadius: BorderRadius.circular(16), - ), - ), - ), - itemCount: widget.diet.dailySchedule.length, - ), - SizedBox(height: 32), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Container( - height: 0.4, - decoration: BoxDecoration( - color: Colors.grey, - borderRadius: BorderRadius.circular(16), - ), - ), - ), - SizedBox(height: 8), - Container( - padding: EdgeInsets.symmetric(horizontal: 16), - alignment: Alignment.centerLeft, - child: Text( - widget.diet.totalComments.toString() + ' КОММЕНТАРИЕВ', - style: TextStyle( - fontSize: 12, - color: appTheme.gray500, - fontWeight: FontWeight.w400, - ), - ), - ), - SizedBox(height: 16), - if (state is CommentsStateLoading) - Padding( - padding: const EdgeInsets.all(32), - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ), - if (state is CommentsStateLoaded) - CommentsWidget( - id: widget.diet.id.toString(), - commentSourceType: CommentSourceType.diet, - comment: state.comment, - replyId: replyId, - ), - ], - ), - ), - ); - }, + ); + }, + ); + }, + ), ), ); } - _dayOfPlan(List> day, int dayNumber) { + _dayOfPlan(List> day, int dayNumber, S s) { _choiceChipWidget(int index) { return ChoiceChip( selectedColor: appTheme.blueA400, @@ -381,73 +552,91 @@ class _AboutDietScreenState extends State { ); } - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'День ' + (dayNumber + 1).toString(), - style: TextStyle( - color: Colors.black, - fontSize: 16, - fontWeight: FontWeight.w500, - height: 1, - ), + return Padding( + padding: const EdgeInsets.only(bottom: 8), + child: MainBackgroundBouble( + padding: EdgeInsets.only( + top: 16, ), - SizedBox(height: 4), - Text( - 'Приём пищи', - style: TextStyle( - color: Colors.black, - fontSize: 14, - fontWeight: FontWeight.w400, - height: 1, - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + child: Column( children: [ - _choiceChipWidget(0), - _choiceChipWidget(1), - _choiceChipWidget(2), - _choiceChipWidget(3), - _choiceChipWidget(4), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + s.day + ' ' + (dayNumber + 1).toString(), + style: TextStyle( + color: appTheme.blueGray800, + fontSize: 20, + fontWeight: FontWeight.w600, + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: CustomDivider(), + ), + Text( + s.meal, + style: TextStyle( + color: appTheme.blueGray800, + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _choiceChipWidget(0), + _choiceChipWidget(1), + _choiceChipWidget(2), + _choiceChipWidget(3), + _choiceChipWidget(4), + ], + ), + SizedBox(height: 8), + ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemBuilder: (context, index) => _daily(day[widget.selectedIndexes[dayNumber]][index], s), + itemCount: day[widget.selectedIndexes[dayNumber]].length, + ), + SizedBox(height: 8), + ], + ), + ), + CpfcContainer(calories: 0, protein: 0, fat: 0, carbohydrates: 0), ], ), - SizedBox(height: 8), - ListView.separated( - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemBuilder: (context, index) => _daily(day[widget.selectedIndexes[dayNumber]][index]), - separatorBuilder: (context, index) => SizedBox(height: 4), - itemCount: day[widget.selectedIndexes[dayNumber]].length, - ), - ], + ), ); } - _daily(DailySchedule daily) { - return Row( - children: [ - Text( - '• ' + daily.name, - style: TextStyle( - color: Colors.black, - fontSize: 14, - fontWeight: FontWeight.w400, - height: 1.2, + _daily(DailySchedule daily, S s) { + return Padding( + padding: const EdgeInsets.only(bottom: 12), + child: Row( + children: [ + Text( + daily.name, + style: TextStyle( + color: appTheme.blueGray800, + fontSize: 16, + fontWeight: FontWeight.w500, + ), ), - ), - Spacer(), - Text( - daily.quantity.toString() + ' ' + daily.portion.name, - style: TextStyle( - color: Colors.black, - fontSize: 14, - fontWeight: FontWeight.w400, - height: 1.2, + Spacer(), + Text( + daily.quantity.toString() + ' ' + getLocalizedMeasurementUnits(s, daily.portion), + style: TextStyle( + color: appTheme.blueGray800, + fontSize: 16, + fontWeight: FontWeight.w600, + ), ), - ), - ], + ], + ), ); } } diff --git a/lib/features/diets/presentation/widgets/author_card.dart b/lib/features/diets/presentation/widgets/author_card.dart new file mode 100644 index 00000000..d566dad5 --- /dev/null +++ b/lib/features/diets/presentation/widgets/author_card.dart @@ -0,0 +1,107 @@ +import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart'; +import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:eatvio_app/theme/theme_helper.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:go_router/go_router.dart'; + +class AuthorCard extends StatelessWidget { + const AuthorCard({super.key, required this.author}); + final Author author; + @override + Widget build(BuildContext context) { + final s = S.of(context); + return Container( + width: MediaQuery.sizeOf(context).width, + decoration: BoxDecoration(borderRadius: BorderRadius.circular(12), color: appTheme.cardBackground), + child: Stack( + children: [ + Padding( + padding: const EdgeInsets.only(left: 16, top: 16, bottom: 24), + child: Column( + children: [ + ListTile( + contentPadding: EdgeInsets.zero, + leading: Container( + width: 64, + height: 64, + decoration: BoxDecoration( + shape: BoxShape.circle, + image: DecorationImage( + image: NetworkImage(author.authorProfileIcon), + fit: BoxFit.cover, + ), + ), + ), + subtitle: Text( + author.authorName, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: appTheme.blueGray800, + ), + ), + title: Text( + s.diet_author, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: Color(0xFF6D6F71), + ), + ), + ), + Row( + children: [ + Text( + 'Конслуьтант', + style: TextStyle( + color: appTheme.blueGray800, + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + SizedBox(width: 10), + InfoBouble(radius: 12, backgroundColor: appTheme.whiteA700, children: [ + Text( + '128-й в рейтинге', + style: TextStyle( + color: appTheme.blueA400, + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + ],), + ], + ), + ], + ), + ), + Positioned( + right: 8, + top: 8, + child: GestureDetector( + onTap: () => GoRouter.of(context).pushNamed('profileScreen', extra: author), + child: Container( + width: 28, + height: 28, + decoration: BoxDecoration( + color: appTheme.whiteA700, + borderRadius: BorderRadius.circular(8), + ), + child: Center( + child: SvgPicture.asset( + 'assets/icons/arrow.svg', + width: 12, + height: 12, + fit: BoxFit.none, + ), + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/diets/presentation/widgets/calendar_widget.dart b/lib/features/diets/presentation/widgets/calendar_widget.dart new file mode 100644 index 00000000..e2353b28 --- /dev/null +++ b/lib/features/diets/presentation/widgets/calendar_widget.dart @@ -0,0 +1,90 @@ +import 'package:eatvio_app/theme/theme_helper.dart'; +import 'package:flutter/material.dart'; +import 'package:table_calendar/table_calendar.dart'; + +class CalendarWidget extends StatefulWidget { + const CalendarWidget({super.key}); + + @override + State createState() => _CalendarWidgetState(); +} + +class _CalendarWidgetState extends State { + CalendarFormat _calendarFormat = CalendarFormat.month; + DateTime _focusedDay = DateTime.now(); + DateTime? _selectedDay = DateTime.now(); + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.only(top: 5), + child: TableCalendar( + firstDay: DateTime.utc(2010, 10, 16), + lastDay: DateTime.utc(2030, 3, 14), + locale: 'ru_RU', + startingDayOfWeek: StartingDayOfWeek.monday, + focusedDay: _focusedDay, + rangeStartDay: _selectedDay, + rangeEndDay: _selectedDay?.add(const Duration(days: 6)), + calendarFormat: _calendarFormat, + headerStyle: HeaderStyle( + titleCentered: true, + formatButtonVisible: false, + leftChevronVisible: false, + rightChevronVisible: false, + ), + rowHeight: 40, + selectedDayPredicate: (day) { + return isSameDay(_selectedDay, day); + }, + onDaySelected: (selectedDay, focusedDay) { + if (!isSameDay(_selectedDay, selectedDay)) { + setState(() { + _selectedDay = selectedDay; + _focusedDay = focusedDay; + }); + } + }, + onFormatChanged: (format) { + if (_calendarFormat != format) { + setState(() { + _calendarFormat = format; + }); + } + }, + onPageChanged: (focusedDay) { + _focusedDay = focusedDay; + }, + calendarStyle: CalendarStyle( + weekendDecoration: BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + ), + defaultDecoration: BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + ), + selectedDecoration: BoxDecoration( + color: appTheme.blueA400, + shape: BoxShape.circle, + ), + todayTextStyle: TextStyle( + color: Colors.black, + ), + todayDecoration: BoxDecoration( + border: Border.all( + color: appTheme.blueA400, + width: 2, + ), + color: Colors.white, + shape: BoxShape.circle, + ), + ), + ), + ), + ], + ); + } +} diff --git a/lib/features/diets/presentation/widgets/cpfc_container.dart b/lib/features/diets/presentation/widgets/cpfc_container.dart new file mode 100644 index 00000000..e73cf57c --- /dev/null +++ b/lib/features/diets/presentation/widgets/cpfc_container.dart @@ -0,0 +1,46 @@ +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:eatvio_app/theme/theme_helper.dart'; +import 'package:flutter/material.dart'; + +class CpfcContainer extends StatelessWidget { + const CpfcContainer({super.key, required this.calories, required this.protein, required this.fat, required this.carbohydrates}); + final double calories; + final double protein; + final double fat; + final double carbohydrates; + + @override + Widget build(BuildContext context) { + final s = S.of(context); + return Container( + color: appTheme.cardBackground, + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Column( + children: [ + Text('$calories', style: TextStyle(color: appTheme.blueGray800, fontSize: 16, fontWeight: FontWeight.w600)), + Text(s.calories, style: TextStyle(color: appTheme.blueGray800, fontSize: 12, fontWeight: FontWeight.w500)), + ], + ), + Column( + children: [ + Text('$protein', style: TextStyle(color: appTheme.blueGray800, fontSize: 16, fontWeight: FontWeight.w600)), + Text(s.proteins, style: TextStyle(color: appTheme.blueGray800, fontSize: 12, fontWeight: FontWeight.w500)), + ], + ), + Column( + children: [ + Text('$fat', style: TextStyle(color: appTheme.blueGray800, fontSize: 16, fontWeight: FontWeight.w600)), + Text(s.fats, style: TextStyle(color: appTheme.blueGray800, fontSize: 12, fontWeight: FontWeight.w500)), + ], + ), + Column( + children: [ + Text('$carbohydrates', style: TextStyle(color: appTheme.blueGray800, fontSize: 16, fontWeight: FontWeight.w600)), + Text(s.carbohydrates, style: TextStyle(color: appTheme.blueGray800, fontSize: 12, fontWeight: FontWeight.w500)), + ], + ), + ],), + ); + } +} diff --git a/lib/features/diets/presentation/widgets/expandable_text.dart b/lib/features/diets/presentation/widgets/expandable_text.dart new file mode 100644 index 00000000..a035c287 --- /dev/null +++ b/lib/features/diets/presentation/widgets/expandable_text.dart @@ -0,0 +1,87 @@ +import 'dart:math' as math; + +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:eatvio_app/theme/theme_helper.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_html/flutter_html.dart'; +import 'package:flutter_svg/svg.dart'; + +class ExpandableText extends StatefulWidget { + final String text; + final int maxLines; + + ExpandableText({required this.text, this.maxLines = 3}); + + @override + _ExpandableTextState createState() => _ExpandableTextState(); +} + +class _ExpandableTextState extends State { + bool isExpanded = false; + + void toggleExpansion() { + setState(() { + isExpanded = !isExpanded; + }); + } + + @override + Widget build(BuildContext context) { + final s = S.of(context); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + children: [ + Html( + data: widget.text, + style: { + "*": Style( + color: Colors.black87, + maxLines: isExpanded ? null : widget.maxLines, + textOverflow: TextOverflow.clip, + ), + }, + ), + if (!isExpanded) + Positioned.fill( + child: Align( + alignment: Alignment.bottomCenter, + child: Container( + height: 35, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.white.withOpacity(0.0), + Colors.white.withOpacity(0.6), + Colors.white, + ], + ), + ), + ), + ), + ), + ], + ), + Align( + alignment: Alignment.bottomCenter, + child: GestureDetector( + onTap: toggleExpansion, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + isExpanded ? s.hide : s.expand, + style: TextStyle(color: appTheme.blueA400, fontSize: 16, fontWeight: FontWeight.w500), + ), + Transform.rotate(angle: isExpanded ? math.pi : 0, child: SvgPicture.asset('assets/icons/arrow_down.svg', width: 12, height: 12)), + ], + ), + ), + ), + ], + ); + } +} diff --git a/lib/features/diets/presentation/widgets/info_bouble.dart b/lib/features/diets/presentation/widgets/info_bouble.dart new file mode 100644 index 00000000..870859dd --- /dev/null +++ b/lib/features/diets/presentation/widgets/info_bouble.dart @@ -0,0 +1,20 @@ +import 'package:eatvio_app/theme/theme_helper.dart'; +import 'package:flutter/material.dart'; + +class InfoBouble extends StatelessWidget { + const InfoBouble({super.key, required this.children, this.backgroundColor, this.radius = 100}); + final List children; + final Color? backgroundColor; + final double radius; + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8.5), + decoration: BoxDecoration( + color: backgroundColor ?? appTheme.gray50, + borderRadius: BorderRadius.circular(radius), + ), + child: Row(mainAxisSize: MainAxisSize.min, children: children), + ); + } +} diff --git a/lib/features/diets/presentation/widgets/main_background_bouble.dart b/lib/features/diets/presentation/widgets/main_background_bouble.dart new file mode 100644 index 00000000..8b94131e --- /dev/null +++ b/lib/features/diets/presentation/widgets/main_background_bouble.dart @@ -0,0 +1,38 @@ +import 'package:eatvio_app/theme/theme_helper.dart'; +import 'package:flutter/material.dart'; + +class MainBackgroundBouble extends StatelessWidget { + const MainBackgroundBouble( + {super.key, required this.child, this.width, this.padding, this.enableShadow = false,}); + final double? width; + final Widget child; + final bool enableShadow; + final EdgeInsetsGeometry? padding; + @override + Widget build(BuildContext context) { + return DecoratedBox( + decoration: BoxDecoration( + boxShadow: enableShadow + ? [ + BoxShadow( + color: Color(0x00000000).withOpacity(0.2), + spreadRadius: 0, + blurRadius: 32, + offset: Offset(0, 0), + ), + ] + : null,), + child: ClipRRect( + borderRadius: BorderRadius.circular(12), + child: Container( + decoration: BoxDecoration( + color: appTheme.whiteA700, + ), + width: width ?? MediaQuery.sizeOf(context).width, + padding: padding, + child: child, + ), + ), + ); + } +} diff --git a/lib/features/diets/presentation/widgets/more_options.dart b/lib/features/diets/presentation/widgets/more_options.dart new file mode 100644 index 00000000..639fe8d2 --- /dev/null +++ b/lib/features/diets/presentation/widgets/more_options.dart @@ -0,0 +1,99 @@ +import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/core/utils/toasts.dart'; +import 'package:eatvio_app/core/widgets/custom_divider.dart'; +import 'package:eatvio_app/features/diets/presentation/widgets/main_background_bouble.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:eatvio_app/theme/theme_helper.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:go_router/go_router.dart'; +import 'package:share_plus/share_plus.dart'; + +class MoreOptions extends StatelessWidget { + const MoreOptions({super.key, required this.id}); + final int id; + @override + Widget build(BuildContext context) { + final s = S.of(context); + return MainBackgroundBouble( + enableShadow: true, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + GestureDetector( + onTap: () { + Clipboard.setData(ClipboardData(text: baseUrl + '/ru/diet/' + id.toString())); + showInfoToast(s.link_was_copied); + }, + child: Row( + children: [ + Text( + s.copy_link, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: appTheme.blueGray800, + ), + ), + Spacer(), + SvgPicture.asset('assets/icons/copy2.svg', width: 16, height: 20), + ], + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: CustomDivider(), + ), + GestureDetector( + onTap: () => Share.shareUri( + Uri.parse( + baseUrl + '/ru/diet/' + id.toString(), + ), + ), + child: Row( + children: [ + Text( + s.share, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: appTheme.blueGray800, + ), + ), + Spacer(), + SvgPicture.asset('assets/icons/share2.svg', width: 16, height: 20), + ], + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: CustomDivider(), + ), + GestureDetector( + onTap: () { + context.push('complainingScreen'); + }, + child: Row( + children: [ + Text( + s.complain, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: appTheme.blueGray800, + ), + ), + Spacer(), + SvgPicture.asset('assets/icons/complain.svg', width: 16, height: 20), + ], + ), + ), + ], + ), + ), + width: 272,); + } +} diff --git a/lib/features/diets/presentation/widgets/start_diet_bottom_sheet.dart b/lib/features/diets/presentation/widgets/start_diet_bottom_sheet.dart new file mode 100644 index 00000000..94e18e89 --- /dev/null +++ b/lib/features/diets/presentation/widgets/start_diet_bottom_sheet.dart @@ -0,0 +1,56 @@ +import 'package:eatvio_app/features/diets/presentation/widgets/calendar_widget.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:eatvio_app/theme/theme_helper.dart'; +import 'package:eatvio_app/widgets/custom_outlined_button.dart'; +import 'package:flutter/material.dart'; + +class StartDietBottomSheet extends StatelessWidget { + const StartDietBottomSheet({super.key}); + + @override + Widget build(BuildContext context) { + final s = S.of(context); + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(height: 5), + Container( + height: 4, + width: 40, + decoration: BoxDecoration(color: appTheme.lightGrey, borderRadius: BorderRadius.circular(20)), + ), + SizedBox(height: 22), + Text(s.startDiet, + style: TextStyle( + color: appTheme.blueGray800, + fontSize: 20, + fontWeight: FontWeight.w600, + ),), + SizedBox(height: 12), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 25.0), + child: Text(s.choose_start_diet_date, + textAlign: TextAlign.center, + style: TextStyle( + color: appTheme.blueGray800, + fontSize: 16, + fontWeight: FontWeight.w500, + ),), + ), + CalendarWidget(), + Padding( + padding: EdgeInsets.only(left: 16, right: 16, top: 20, bottom: 25), + child: CustomOutlinedButton( + height: 52, + text: s.continue_, + buttonStyle: ButtonStyle( + textStyle: WidgetStatePropertyAll(TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600)), + backgroundColor: WidgetStatePropertyAll(appTheme.blueA400), + side: WidgetStatePropertyAll(BorderSide.none), + ), + ), + ), + ], + ); + } +} diff --git a/lib/features/diets/presentation/widgets/type_badges.dart b/lib/features/diets/presentation/widgets/type_badges.dart new file mode 100644 index 00000000..b72c85d4 --- /dev/null +++ b/lib/features/diets/presentation/widgets/type_badges.dart @@ -0,0 +1,231 @@ +import 'package:eatvio_app/features/diets/presentation/widgets/main_background_bouble.dart'; +import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:eatvio_app/theme/theme_helper.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_portal/flutter_portal.dart'; +import 'package:flutter_svg/svg.dart'; + +class TypeBadges extends StatefulWidget { + const TypeBadges({super.key, required this.type}); + + final DietType type; + + @override + State createState() => _TypeBadgesState(); +} + +class _TypeBadgesState extends State { + final List overlaysVisibilityState = [false, false, false, false]; + + @override + Widget build(BuildContext context) { + final s = S.of(context); + final List<(String, Widget)> badgeSource = []; + if (widget.type.alko == true) { + badgeSource.add(( + 'assets/icons/cocktail.svg', + _InfoOverlayEntry( + onClose: closeOverlay, + assetPath: 'assets/icons/cocktail.svg', + title: s.alko_type, + description: s.alko_type_desc, + ) + ),); + } + if (widget.type.veg == true) { + badgeSource.add(( + 'assets/icons/broccoli.svg', + _InfoOverlayEntry( + onClose: closeOverlay, + assetPath: 'assets/icons/broccoli.svg', + title: s.vegan_type, + description: s.vegan_type_desc, + ) + ),); + } + if (widget.type.corb == true) { + badgeSource.add(( + 'assets/icons/bread.svg', + _InfoOverlayEntry( + onClose: closeOverlay, + assetPath: 'assets/icons/bread.svg', + title: s.corb_type, + description: s.corb_type_desc, + ) + ),); + } + if (widget.type.meat == true) { + badgeSource.add(( + 'assets/icons/meat2.svg', + _InfoOverlayEntry( + onClose: closeOverlay, + assetPath: 'assets/icons/meat2.svg', + title: s.meat_type, + description: s.meat_type_desc, + ) + ),); + } + return Row( + children: List.generate( + badgeSource.length, + (index) => _Barrier( + visible: overlaysVisibilityState[index], + onClose: closeOverlay, + child: PortalTarget( + visible: overlaysVisibilityState[index], + anchor: Aligned( + shiftToWithinBound: AxisFlag(x: true, y: true), + follower: Alignment.topRight, + target: Alignment.bottomRight, + ), + portalFollower: badgeSource[index].$2, + child: GestureDetector( + onTap: () { + for (int stateIndex = 0; stateIndex < overlaysVisibilityState.length; stateIndex++) { + if (index != stateIndex) { + setState(() { + overlaysVisibilityState[stateIndex] = false; + }); + } else { + setState(() { + overlaysVisibilityState[stateIndex] = true; + }); + } + } + }, + child: Padding( + padding: EdgeInsets.only(right: index != badgeSource.length - 1 ? 8.0 : 0), + child: SvgPicture.asset( + badgeSource[index].$1, + width: 16, + height: 16, + ), + ), + ), + ), + ), + ), + ); + } + + void closeOverlay() { + for (int stateIndex = 0; stateIndex < overlaysVisibilityState.length; stateIndex++) { + setState(() { + overlaysVisibilityState[stateIndex] = false; + }); + } + } +} + +class _InfoOverlayEntry extends StatelessWidget { + const _InfoOverlayEntry({ + required this.title, + required this.description, + required this.assetPath, + required this.onClose, + }); + + final String title; + final String description; + final assetPath; + final void Function() onClose; + + @override + Widget build(BuildContext context) { + return MainBackgroundBouble( + padding: EdgeInsets.all(8), + width: 343, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SvgPicture.asset( + assetPath, + width: 20, + height: 20, + ), + SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + title, + style: TextStyle( + decoration: TextDecoration.none, + color: appTheme.blueGray800, + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + SizedBox(height: 4), + Text( + description, + softWrap: true, + style: TextStyle( + decoration: TextDecoration.none, + color: appTheme.blueGray800, + fontSize: 12, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + GestureDetector( + onTap: onClose, + child: SvgPicture.asset( + 'assets/icons/close_button.svg', + width: 20, + height: 20, + ), + ), + ], + ), + ); + } +} + +class _Barrier extends StatelessWidget { + const _Barrier({ + Key? key, + required this.onClose, + required this.visible, + required this.child, + }) : super(key: key); + + final Widget child; + final VoidCallback onClose; + final bool visible; + + @override + Widget build(BuildContext context) { + return PortalTarget( + visible: visible, + closeDuration: kThemeAnimationDuration, + portalFollower: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: onClose, + child: TweenAnimationBuilder( + duration: kThemeAnimationDuration, + tween: ColorTween( + begin: Colors.transparent, + end: visible ? Colors.black54 : Colors.transparent, + ), + builder: (context, color, child) { + return ColoredBox(color: color); + }, + ), + ), + child: child, + ); + } +} + +class ColorTween extends Tween { + ColorTween({required Color begin, required Color end}) : super(begin: begin, end: end); + + @override + Color lerp(double t) => Color.lerp(begin, end, t)!; +} diff --git a/lib/features/diets/repositories/diets_repository.dart b/lib/features/diets/repositories/diets_repository.dart new file mode 100644 index 00000000..3e826995 --- /dev/null +++ b/lib/features/diets/repositories/diets_repository.dart @@ -0,0 +1,48 @@ +import 'package:eatvio_app/features/diets/services/diets_service.dart'; +import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; + +class DietsRepository { + DietsRepository({required this.dietsService}); + final DietsService dietsService; + + Future likeDiet(int id) async { + try { + await dietsService.likeDiet(id: id); + } catch (e) { + rethrow; + } + } + + Future unlikeDiet(int id) async { + try { + await dietsService.unlikeDiet(id: id); + } catch (e) { + rethrow; + } + } + + Future getDiet(int id) async { + try { + final diet = await dietsService.getDiet(id: id); + return diet; + } catch (e) { + rethrow; + } + } + + Future addToFavorite(int id) async { + try { + await dietsService.addToFavorite(id: id); + } catch (e) { + rethrow; + } + } + + Future removeFromFavorite(int id) async { + try { + await dietsService.removeFromFavorite(id: id); + } catch (e) { + rethrow; + } + } +} diff --git a/lib/features/diets/services/diets_service.dart b/lib/features/diets/services/diets_service.dart new file mode 100644 index 00000000..d5236e3d --- /dev/null +++ b/lib/features/diets/services/diets_service.dart @@ -0,0 +1,36 @@ +import 'package:dio/dio.dart'; +import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; +import 'package:retrofit/retrofit.dart'; + +part 'diets_service.g.dart'; + +@RestApi() +abstract class DietsService { + factory DietsService(Dio dio, {String baseUrl}) = _DietsService; + + @POST('/api/diets/{id}/like') + Future likeDiet({ + @Path() required int id, + }); + + @POST('/api/diets/{id}/add_to_favorite') + Future addToFavorite({ + @Path() required int id, + }); + + @POST('/api/diets/{id}/remove_from_favorite') + Future removeFromFavorite({ + @Path() required int id, + }); + + @POST('/api/diets/{id}/unliked') + Future unlikeDiet({ + @Path() required int id, + }); + + @GET('/api/diets/show/{id}') + Future getDiet({ + @Path() required int id, + }); + +} diff --git a/lib/features/diets/services/diets_service.g.dart b/lib/features/diets/services/diets_service.g.dart new file mode 100644 index 00000000..67718920 --- /dev/null +++ b/lib/features/diets/services/diets_service.g.dart @@ -0,0 +1,186 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'diets_service.dart'; + +// ************************************************************************** +// RetrofitGenerator +// ************************************************************************** + +// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers,unused_element,unnecessary_string_interpolations + +class _DietsService implements DietsService { + _DietsService( + this._dio, { + this.baseUrl, + this.errorLogger, + }); + + final Dio _dio; + + String? baseUrl; + + final ParseErrorLogger? errorLogger; + + @override + Future likeDiet({required int id}) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType(Options( + method: 'POST', + headers: _headers, + extra: _extra, + ) + .compose( + _dio.options, + '/api/diets/${id}/like', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + ))); + await _dio.fetch(_options); + } + + @override + Future addToFavorite({required int id}) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType(Options( + method: 'POST', + headers: _headers, + extra: _extra, + ) + .compose( + _dio.options, + '/api/diets/${id}/add_to_favorite', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + ))); + await _dio.fetch(_options); + } + + @override + Future removeFromFavorite({required int id}) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType(Options( + method: 'POST', + headers: _headers, + extra: _extra, + ) + .compose( + _dio.options, + '/api/diets/${id}/remove_from_favorite', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + ))); + await _dio.fetch(_options); + } + + @override + Future unlikeDiet({required int id}) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType(Options( + method: 'POST', + headers: _headers, + extra: _extra, + ) + .compose( + _dio.options, + '/api/diets/${id}/unliked', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + ))); + await _dio.fetch(_options); + } + + @override + Future getDiet({required int id}) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType(Options( + method: 'GET', + headers: _headers, + extra: _extra, + ) + .compose( + _dio.options, + '/api/diets/show/${id}', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + ))); + final _result = await _dio.fetch>(_options); + late Diet _value; + try { + _value = Diet.fromJson(_result.data!); + } on Object catch (e, s) { + errorLogger?.logError(e, s, _options); + rethrow; + } + return _value; + } + + RequestOptions _setStreamType(RequestOptions requestOptions) { + if (T != dynamic && + !(requestOptions.responseType == ResponseType.bytes || + requestOptions.responseType == ResponseType.stream)) { + if (T == String) { + requestOptions.responseType = ResponseType.plain; + } else { + requestOptions.responseType = ResponseType.json; + } + } + return requestOptions; + } + + String _combineBaseUrls( + String dioBaseUrl, + String? baseUrl, + ) { + if (baseUrl == null || baseUrl.trim().isEmpty) { + return dioBaseUrl; + } + + final url = Uri.parse(baseUrl); + + if (url.isAbsolute) { + return url.toString(); + } + + return Uri.parse(dioBaseUrl).resolveUri(url).toString(); + } +} diff --git a/lib/features/feed/models/diets_pagination_model.dart b/lib/features/feed/models/diets_pagination_model.dart index dd7f4491..99f43194 100644 --- a/lib/features/feed/models/diets_pagination_model.dart +++ b/lib/features/feed/models/diets_pagination_model.dart @@ -31,6 +31,8 @@ class Diet with _$Diet { @JsonKey(name: "total_likes") required int totalLikes, @JsonKey(name: "total_views") required int totalViews, @JsonKey(name: "total_comments") required int totalComments, + @JsonKey(name: "was_liked_by_me") required bool wasLikedByMe, + @JsonKey(name: "is_favorite") required bool isFavorite, }) = _Diet; factory Diet.fromJson(Map json) => _$DietFromJson(json); diff --git a/lib/features/feed/models/diets_pagination_model.freezed.dart b/lib/features/feed/models/diets_pagination_model.freezed.dart index 15b651f3..0bbb9b25 100644 --- a/lib/features/feed/models/diets_pagination_model.freezed.dart +++ b/lib/features/feed/models/diets_pagination_model.freezed.dart @@ -284,6 +284,10 @@ mixin _$Diet { int get totalViews => throw _privateConstructorUsedError; @JsonKey(name: "total_comments") int get totalComments => throw _privateConstructorUsedError; + @JsonKey(name: "was_liked_by_me") + bool get wasLikedByMe => throw _privateConstructorUsedError; + @JsonKey(name: "is_favorite") + bool get isFavorite => throw _privateConstructorUsedError; /// Serializes this Diet to a JSON map. Map toJson() => throw _privateConstructorUsedError; @@ -312,7 +316,9 @@ abstract class $DietCopyWith<$Res> { @JsonKey(name: "author") Author author, @JsonKey(name: "total_likes") int totalLikes, @JsonKey(name: "total_views") int totalViews, - @JsonKey(name: "total_comments") int totalComments}); + @JsonKey(name: "total_comments") int totalComments, + @JsonKey(name: "was_liked_by_me") bool wasLikedByMe, + @JsonKey(name: "is_favorite") bool isFavorite}); $DietTypeCopyWith<$Res> get type; $AuthorCopyWith<$Res> get author; @@ -345,6 +351,8 @@ class _$DietCopyWithImpl<$Res, $Val extends Diet> Object? totalLikes = null, Object? totalViews = null, Object? totalComments = null, + Object? wasLikedByMe = null, + Object? isFavorite = null, }) { return _then(_value.copyWith( id: null == id @@ -395,6 +403,14 @@ class _$DietCopyWithImpl<$Res, $Val extends Diet> ? _value.totalComments : totalComments // ignore: cast_nullable_to_non_nullable as int, + wasLikedByMe: null == wasLikedByMe + ? _value.wasLikedByMe + : wasLikedByMe // ignore: cast_nullable_to_non_nullable + as bool, + isFavorite: null == isFavorite + ? _value.isFavorite + : isFavorite // ignore: cast_nullable_to_non_nullable + as bool, ) as $Val); } @@ -439,7 +455,9 @@ abstract class _$$DietImplCopyWith<$Res> implements $DietCopyWith<$Res> { @JsonKey(name: "author") Author author, @JsonKey(name: "total_likes") int totalLikes, @JsonKey(name: "total_views") int totalViews, - @JsonKey(name: "total_comments") int totalComments}); + @JsonKey(name: "total_comments") int totalComments, + @JsonKey(name: "was_liked_by_me") bool wasLikedByMe, + @JsonKey(name: "is_favorite") bool isFavorite}); @override $DietTypeCopyWith<$Res> get type; @@ -471,6 +489,8 @@ class __$$DietImplCopyWithImpl<$Res> Object? totalLikes = null, Object? totalViews = null, Object? totalComments = null, + Object? wasLikedByMe = null, + Object? isFavorite = null, }) { return _then(_$DietImpl( id: null == id @@ -521,6 +541,14 @@ class __$$DietImplCopyWithImpl<$Res> ? _value.totalComments : totalComments // ignore: cast_nullable_to_non_nullable as int, + wasLikedByMe: null == wasLikedByMe + ? _value.wasLikedByMe + : wasLikedByMe // ignore: cast_nullable_to_non_nullable + as bool, + isFavorite: null == isFavorite + ? _value.isFavorite + : isFavorite // ignore: cast_nullable_to_non_nullable + as bool, )); } } @@ -541,7 +569,9 @@ class _$DietImpl implements _Diet { @JsonKey(name: "author") required this.author, @JsonKey(name: "total_likes") required this.totalLikes, @JsonKey(name: "total_views") required this.totalViews, - @JsonKey(name: "total_comments") required this.totalComments}) + @JsonKey(name: "total_comments") required this.totalComments, + @JsonKey(name: "was_liked_by_me") required this.wasLikedByMe, + @JsonKey(name: "is_favorite") required this.isFavorite}) : _dailySchedule = dailySchedule; factory _$DietImpl.fromJson(Map json) => @@ -589,10 +619,16 @@ class _$DietImpl implements _Diet { @override @JsonKey(name: "total_comments") final int totalComments; + @override + @JsonKey(name: "was_liked_by_me") + final bool wasLikedByMe; + @override + @JsonKey(name: "is_favorite") + final bool isFavorite; @override String toString() { - return 'Diet(id: $id, name: $name, image: $image, duration: $duration, state: $state, type: $type, details: $details, dailySchedule: $dailySchedule, author: $author, totalLikes: $totalLikes, totalViews: $totalViews, totalComments: $totalComments)'; + return 'Diet(id: $id, name: $name, image: $image, duration: $duration, state: $state, type: $type, details: $details, dailySchedule: $dailySchedule, author: $author, totalLikes: $totalLikes, totalViews: $totalViews, totalComments: $totalComments, wasLikedByMe: $wasLikedByMe, isFavorite: $isFavorite)'; } @override @@ -616,7 +652,11 @@ class _$DietImpl implements _Diet { (identical(other.totalViews, totalViews) || other.totalViews == totalViews) && (identical(other.totalComments, totalComments) || - other.totalComments == totalComments)); + other.totalComments == totalComments) && + (identical(other.wasLikedByMe, wasLikedByMe) || + other.wasLikedByMe == wasLikedByMe) && + (identical(other.isFavorite, isFavorite) || + other.isFavorite == isFavorite)); } @JsonKey(includeFromJson: false, includeToJson: false) @@ -634,7 +674,9 @@ class _$DietImpl implements _Diet { author, totalLikes, totalViews, - totalComments); + totalComments, + wasLikedByMe, + isFavorite); /// Create a copy of Diet /// with the given fields replaced by the non-null parameter values. @@ -666,7 +708,9 @@ abstract class _Diet implements Diet { @JsonKey(name: "author") required final Author author, @JsonKey(name: "total_likes") required final int totalLikes, @JsonKey(name: "total_views") required final int totalViews, - @JsonKey(name: "total_comments") required final int totalComments}) = + @JsonKey(name: "total_comments") required final int totalComments, + @JsonKey(name: "was_liked_by_me") required final bool wasLikedByMe, + @JsonKey(name: "is_favorite") required final bool isFavorite}) = _$DietImpl; factory _Diet.fromJson(Map json) = _$DietImpl.fromJson; @@ -707,6 +751,12 @@ abstract class _Diet implements Diet { @override @JsonKey(name: "total_comments") int get totalComments; + @override + @JsonKey(name: "was_liked_by_me") + bool get wasLikedByMe; + @override + @JsonKey(name: "is_favorite") + bool get isFavorite; /// Create a copy of Diet /// with the given fields replaced by the non-null parameter values. diff --git a/lib/features/feed/models/diets_pagination_model.g.dart b/lib/features/feed/models/diets_pagination_model.g.dart index 7fcab8cd..c79e3a26 100644 --- a/lib/features/feed/models/diets_pagination_model.g.dart +++ b/lib/features/feed/models/diets_pagination_model.g.dart @@ -43,6 +43,8 @@ _$DietImpl _$$DietImplFromJson(Map json) => _$DietImpl( totalLikes: (json['total_likes'] as num).toInt(), totalViews: (json['total_views'] as num).toInt(), totalComments: (json['total_comments'] as num).toInt(), + wasLikedByMe: json['was_liked_by_me'] as bool, + isFavorite: json['is_favorite'] as bool, ); Map _$$DietImplToJson(_$DietImpl instance) => @@ -59,6 +61,8 @@ Map _$$DietImplToJson(_$DietImpl instance) => 'total_likes': instance.totalLikes, 'total_views': instance.totalViews, 'total_comments': instance.totalComments, + 'was_liked_by_me': instance.wasLikedByMe, + 'is_favorite': instance.isFavorite, }; _$AuthorImpl _$$AuthorImplFromJson(Map json) => _$AuthorImpl( diff --git a/lib/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart b/lib/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart index dbfdaeb0..bf5be7e1 100644 --- a/lib/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart +++ b/lib/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart @@ -14,7 +14,7 @@ class CommentsCubit extends Cubit { emit((state as CommentsStateLoaded).copyWith(replyId: replyId)); } - getComments(String id, CommentSourceType commentSourceType, [bool isFirstFetch = false]) async { + getComments(String id, CommentSourceType commentSourceType, [bool isFirstFetch = false]) async { if (isFirstFetch) emit(CommentsState.loading()); try { switch (commentSourceType) { diff --git a/lib/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart b/lib/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart index fa44eafb..ab9e4396 100644 --- a/lib/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart +++ b/lib/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart @@ -23,11 +23,11 @@ class DietsCubit extends Cubit { emit(DietsState.loaded( dietsList: dietsPagination.data, isLoading: isLoading, - )); + ),); } catch (e) { emit(DietsState.error( errorMessage: e.toString(), - )); + ),); } } diff --git a/lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart b/lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart index 93bcf399..5147445a 100644 --- a/lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart +++ b/lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart @@ -34,7 +34,7 @@ class RecipesCubit extends Cubit { selectedTags: _selectedTags, selectedTagsCounter: _selectedTags.length, isLoading: isLoading, - )); + ),); } catch (e) { emit(RecipesState.error(errorMessage: e.toString())); } @@ -61,7 +61,7 @@ class RecipesCubit extends Cubit { ...recipesList, ], isLoading: isLoading, - )); + ),); } } catch (e) { emit(RecipesState.error(errorMessage: e.toString())); @@ -74,6 +74,6 @@ class RecipesCubit extends Cubit { emit((state as RecipesStateLoaded).copyWith( selectedTags: _selectedTags, selectedTagsCounter: _selectedTags.length, - )); + ),); } } diff --git a/lib/features/feed/presentation/widgets/comments_widget.dart b/lib/features/feed/presentation/widgets/comments_widget.dart index 695c5459..4421a4aa 100644 --- a/lib/features/feed/presentation/widgets/comments_widget.dart +++ b/lib/features/feed/presentation/widgets/comments_widget.dart @@ -1,6 +1,7 @@ import 'package:eatvio_app/core/utils/date_time_utils.dart'; import 'package:eatvio_app/features/feed/models/comment_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart'; +import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -28,15 +29,16 @@ class CommentsWidget extends StatefulWidget { class _CommentsWidgetState extends State { @override Widget build(BuildContext context) { + final s = S.of(context); return Column( children: [ for (int i = 0; i < widget.comment.data.length; i++) ...[ - _comment(widget.comment.data[i]), + _comment(s, widget.comment.data[i]), if (widget.comment.data[i].childrenComments.isNotEmpty) SizedBox(height: 8), for (int j = 0; j < widget.comment.data[i].childrenComments.length; j++) Padding( padding: EdgeInsets.only(left: 40), - child: _comment(widget.comment.data[i].childrenComments[j], 12, true), + child: _comment(s, widget.comment.data[i].childrenComments[j], 16, true), ), SizedBox(height: 8), ], @@ -44,7 +46,7 @@ class _CommentsWidgetState extends State { ); } - _comment(Comment comment, [double radius = 16, bool isChild = false]) { + _comment(S s, Comment comment, [double radius = 18, bool isChild = false]) { return Container( width: MediaQuery.of(context).size.width, decoration: BoxDecoration( @@ -69,16 +71,19 @@ class _CommentsWidgetState extends State { comment.authorName, style: TextStyle( height: 1, - color: Colors.grey.shade600, + color: appTheme.grey, + fontSize: 12, fontWeight: FontWeight.w500, ), ), - SizedBox(width: 4), + SizedBox(height: 4), Text( comment.body, style: TextStyle( height: 1, - color: Colors.black, + color: appTheme.blueGray800, + fontSize: 12, + fontWeight: FontWeight.w500, ), ), SizedBox(height: 4), @@ -87,10 +92,10 @@ class _CommentsWidgetState extends State { Text( comment.createdAt.format('dd MMMM', 'ru'), style: TextStyle( - color: Colors.grey.shade600, - fontSize: 12, - fontWeight: FontWeight.w400, height: 1, + color: appTheme.grey, + fontSize: 12, + fontWeight: FontWeight.w500, ), ), SizedBox(width: 16), @@ -100,11 +105,11 @@ class _CommentsWidgetState extends State { context.read().setReplyId(comment.id); }, child: Text( - 'Ответить', + s.answer, style: TextStyle( height: 1, + color: appTheme.grey, fontSize: 12, - color: Colors.grey.shade800, fontWeight: FontWeight.w500, ), ), diff --git a/lib/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart b/lib/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart index 409225e1..bdd4e0f4 100644 --- a/lib/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart +++ b/lib/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart @@ -19,7 +19,7 @@ class ProfileCubit extends Cubit { profile: await profileRepository.getProfileBySlug(slug), profileDetails: await profileRepository.getMyProfileDetails(), blogPosts: await profileRepository.getBlogPostsByAuthorSlug(slug: slug), - )); + ),); } catch (e) { emit(ProfileState.error(errorMessage: e.toString())); } @@ -37,7 +37,7 @@ class ProfileCubit extends Cubit { blogPosts: await profileRepository.getBlogPostsByAuthorSlug(slug: profile.slug), weightPoints: await profileRepository.getWeight(period: period), circumferencePoints: await profileRepository.getCircumferences(period: period), - )); + ),); } catch (e) { emit(ProfileState.error(errorMessage: e.toString())); } diff --git a/lib/features/recipes/presentation/bloc/recipe_create_cubit.dart b/lib/features/recipes/presentation/bloc/recipe_create_cubit.dart index aad1afb4..dbd10b07 100644 --- a/lib/features/recipes/presentation/bloc/recipe_create_cubit.dart +++ b/lib/features/recipes/presentation/bloc/recipe_create_cubit.dart @@ -88,7 +88,7 @@ class RecipeCreateCubit extends Cubit { statuses: statuses, selectedType: selectedType, selectedCookingTime: selectedCookingTime, - selectedStatus: selectedStatus)); + selectedStatus: selectedStatus,),); } catch (e) { emit(RecipeCreateState.error(errorMessage: e.toString())); } @@ -136,7 +136,7 @@ class RecipeCreateCubit extends Cubit { statuses: statuses, selectedType: types[0], selectedCookingTime: cookingTimes[0], - selectedStatus: statuses[0])); + selectedStatus: statuses[0],),); } catch (e) { emit(RecipeCreateState.error(errorMessage: e.toString())); } diff --git a/lib/features/recipes/presentation/pages/recipe_create_screen.dart b/lib/features/recipes/presentation/pages/recipe_create_screen.dart index 2398edc9..669caca3 100644 --- a/lib/features/recipes/presentation/pages/recipe_create_screen.dart +++ b/lib/features/recipes/presentation/pages/recipe_create_screen.dart @@ -238,7 +238,7 @@ class RecipeCreateScreen extends StatelessWidget { inactiveTrackColor: Colors.grey.shade300, ), Text('шт'), - ]), + ],), SizedBox(height: 8), Container( height: 48, @@ -340,7 +340,7 @@ class RecipeCreateScreen extends StatelessWidget { ), ), ], - )), + ),), ], ), ), @@ -381,7 +381,7 @@ class RecipeCreateScreen extends StatelessWidget { ], ), ) - : Image.file(File(state.stepImages[stepNumber]!.path), height: 150, fit: BoxFit.cover)), + : Image.file(File(state.stepImages[stepNumber]!.path), height: 150, fit: BoxFit.cover),), SizedBox(height: 16), CustomTextFormField( hintText: 'Введите описание шага', @@ -495,7 +495,7 @@ class RecipeCreateScreen extends StatelessWidget { steps.add(step.Step( description: state.stepControllers[i].text, image: state.stepImages[i]?.path, - )); + ),); } } diff --git a/lib/features/recipes/presentation/pages/recipe_edit_screen.dart b/lib/features/recipes/presentation/pages/recipe_edit_screen.dart index d1f1c7fc..174c0e99 100644 --- a/lib/features/recipes/presentation/pages/recipe_edit_screen.dart +++ b/lib/features/recipes/presentation/pages/recipe_edit_screen.dart @@ -247,7 +247,7 @@ class RecipeEditScreen extends StatelessWidget { inactiveTrackColor: Colors.grey.shade300, ), Text('шт'), - ]), + ],), SizedBox(height: 8), Container( height: 48, @@ -267,7 +267,7 @@ class RecipeEditScreen extends StatelessWidget { onPressed: () => context.read().addIngredient(new Ingredient( name: state.productNameController.text, portion: state.ingredientController.text, - unit: state.ingredientAsItems ? 'pcs' : 'gr'))), + unit: state.ingredientAsItems ? 'pcs' : 'gr',),),), ), SizedBox(height: 24), Text('Описание рецепта'), @@ -345,7 +345,7 @@ class RecipeEditScreen extends StatelessWidget { ), ), ], - )), + ),), ], ), ), @@ -390,7 +390,7 @@ class RecipeEditScreen extends StatelessWidget { baseUrl + '/storage/' + state.stepImages[stepNumber]!.path, height: 150, fit: BoxFit.cover, - )), + ),), SizedBox(height: 16), CustomTextFormField( hintText: 'Введите описание шага', @@ -504,7 +504,7 @@ class RecipeEditScreen extends StatelessWidget { steps.add(step.Step( description: state.stepControllers[i].text, image: state.stepImages[i]?.path, - )); + ),); } } diff --git a/lib/features/registration/presentation/pages/city_of_living_screen.dart b/lib/features/registration/presentation/pages/city_of_living_screen.dart index fffbd19e..bae25803 100644 --- a/lib/features/registration/presentation/pages/city_of_living_screen.dart +++ b/lib/features/registration/presentation/pages/city_of_living_screen.dart @@ -88,7 +88,7 @@ class _CityOfLivingScreenState extends State { textStyle: TextStyle( fontSize: 16, color: Colors.black, - )), + ),), itemBuilder: (BuildContext context, String item) { return Padding( padding: const EdgeInsets.all(8.0), diff --git a/lib/features/settings/presentation/bloc/settings_cubit/settings_cubit.dart b/lib/features/settings/presentation/bloc/settings_cubit/settings_cubit.dart index 811a4bbf..f0a388a4 100644 --- a/lib/features/settings/presentation/bloc/settings_cubit/settings_cubit.dart +++ b/lib/features/settings/presentation/bloc/settings_cubit/settings_cubit.dart @@ -20,7 +20,7 @@ class SettingsCubit extends Cubit { profileSettings: await settingsRepository.getProfileSettings(), profileDetails: await profileRepository.getMyProfileDetails(), profile: await profileRepository.getMyProfile(), - )); + ),); } catch (e) { emit(SettingsState.error(errorMessage: e.toString())); } diff --git a/lib/generated/intl/messages_all.dart b/lib/generated/intl/messages_all.dart index abae25a5..b6510a8a 100644 --- a/lib/generated/intl/messages_all.dart +++ b/lib/generated/intl/messages_all.dart @@ -17,20 +17,16 @@ import 'package:intl/message_lookup_by_library.dart'; import 'package:intl/src/intl_helpers.dart'; import 'messages_en.dart' as messages_en; -import 'messages_ru.dart' as messages_ru; typedef Future LibraryLoader(); Map _deferredLibraries = { 'en': () => new SynchronousFuture(null), - 'ru': () => new SynchronousFuture(null), }; MessageLookupByLibrary? _findExact(String localeName) { switch (localeName) { case 'en': return messages_en.messages; - case 'ru': - return messages_ru.messages; default: return null; } @@ -40,7 +36,7 @@ MessageLookupByLibrary? _findExact(String localeName) { Future initializeMessages(String localeName) { var availableLocale = Intl.verifiedLocale( localeName, (locale) => _deferredLibraries[locale] != null, - onFailure: (_) => null); + onFailure: (_) => null,); if (availableLocale == null) { return new SynchronousFuture(false); } diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 3d320130..42f0d564 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -22,157 +22,195 @@ class MessageLookup extends MessageLookupByLibrary { final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { - "Moscow": MessageLookupByLibrary.simpleMessage("Moscow"), - "Russia": MessageLookupByLibrary.simpleMessage("Russia"), - "active": MessageLookupByLibrary.simpleMessage("Active"), - "add_measure": MessageLookupByLibrary.simpleMessage("Add Measurement"), + "Moscow": MessageLookupByLibrary.simpleMessage("Москва"), + "Russia": MessageLookupByLibrary.simpleMessage("Россия"), + "about_diet": MessageLookupByLibrary.simpleMessage("Об этой диете"), + "active": MessageLookupByLibrary.simpleMessage("Активен"), + "add_measure": MessageLookupByLibrary.simpleMessage("Добавить замер"), "additional_information": - MessageLookupByLibrary.simpleMessage("Additional Information"), - "admin": MessageLookupByLibrary.simpleMessage("Admin"), + MessageLookupByLibrary.simpleMessage("Дополнительная информация"), + "admin": MessageLookupByLibrary.simpleMessage("Админ"), + "alko_type": MessageLookupByLibrary.simpleMessage("Алкоголь"), + "alko_type_desc": MessageLookupByLibrary.simpleMessage( + "С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками.",), "allergies_presence": - MessageLookupByLibrary.simpleMessage("Presence of Allergies"), - "archived": MessageLookupByLibrary.simpleMessage("Archived"), - "authentificate": MessageLookupByLibrary.simpleMessage("Log In"), - "average_pressure": - MessageLookupByLibrary.simpleMessage("Average Pressure Readings:"), - "bodymass_chart": MessageLookupByLibrary.simpleMessage("BMI Chart"), - "chat": MessageLookupByLibrary.simpleMessage("Chat"), - "chest": MessageLookupByLibrary.simpleMessage("Chest"), + MessageLookupByLibrary.simpleMessage("Наличие аллергии"), + "another_form_of_comment": + MessageLookupByLibrary.simpleMessage("комментария"), + "another_form_of_day": MessageLookupByLibrary.simpleMessage("дня"), + "answer": MessageLookupByLibrary.simpleMessage("Ответить"), + "archived": MessageLookupByLibrary.simpleMessage("В архиве"), + "authentificate": MessageLookupByLibrary.simpleMessage("Войти"), + "average_pressure": MessageLookupByLibrary.simpleMessage( + "Средние показатели давления:",), + "bodymass_chart": MessageLookupByLibrary.simpleMessage("График ИМТ"), + "calories": MessageLookupByLibrary.simpleMessage("Калории"), + "carbohydrates": MessageLookupByLibrary.simpleMessage("Углеводы"), + "chat": MessageLookupByLibrary.simpleMessage("Чат"), + "chest": MessageLookupByLibrary.simpleMessage("Грудь"), "chest_circumference": - MessageLookupByLibrary.simpleMessage("Chest Circumference"), + MessageLookupByLibrary.simpleMessage("Обхват груди"), + "choose_start_diet_date": MessageLookupByLibrary.simpleMessage( + "Выберите дату старты диеты. Она отобразиться у Вас в дневнике",), "circumference_empty": MessageLookupByLibrary.simpleMessage( - "All measurements are required"), + "Все замеры обязательны к заполнению",), "circumference_is_not_a_number": MessageLookupByLibrary.simpleMessage( - "All measurements must be numbers"), + "Все замеры должны быть числами",), "circumferences_chart": - MessageLookupByLibrary.simpleMessage("Circumference Chart"), - "city": MessageLookupByLibrary.simpleMessage("City"), + MessageLookupByLibrary.simpleMessage("График обхватов"), + "city": MessageLookupByLibrary.simpleMessage("Город"), "city_of_living": - MessageLookupByLibrary.simpleMessage("City of Residence"), - "continueButton": MessageLookupByLibrary.simpleMessage("Continue"), - "country": MessageLookupByLibrary.simpleMessage("Country"), - "date_of_birth": MessageLookupByLibrary.simpleMessage("Date of Birth"), - "diary": MessageLookupByLibrary.simpleMessage("Diary"), + MessageLookupByLibrary.simpleMessage("Город проживания"), + "comment": MessageLookupByLibrary.simpleMessage("комментарий"), + "comments": MessageLookupByLibrary.simpleMessage("Комментарии"), + "comments_plural": MessageLookupByLibrary.simpleMessage("комментариев"), + "complain": MessageLookupByLibrary.simpleMessage("Пожаловаться"), + "continueButton": MessageLookupByLibrary.simpleMessage("Продолжить"), + "continue_": MessageLookupByLibrary.simpleMessage("Продолжить"), + "copy_link": MessageLookupByLibrary.simpleMessage("Скопировать ссылку"), + "corb_type": MessageLookupByLibrary.simpleMessage("Мучное"), + "corb_type_desc": MessageLookupByLibrary.simpleMessage( + "Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.",), + "country": MessageLookupByLibrary.simpleMessage("Страна"), + "date_of_birth": MessageLookupByLibrary.simpleMessage("Дата рождения"), + "day": MessageLookupByLibrary.simpleMessage("День"), + "days": MessageLookupByLibrary.simpleMessage("дней"), + "diary": MessageLookupByLibrary.simpleMessage("Дневник"), + "diet_author": MessageLookupByLibrary.simpleMessage("Автор диеты"), + "diet_plan": MessageLookupByLibrary.simpleMessage("План диеты"), "do_you_have_hypertension_tendency": - MessageLookupByLibrary.simpleMessage( - "Do you have a tendency for hypertension?"), + MessageLookupByLibrary.simpleMessage("Есть ли у вас гипертония?"), "do_you_have_hypotension_tendency": - MessageLookupByLibrary.simpleMessage( - "Do you have a tendency for hypotension?"), + MessageLookupByLibrary.simpleMessage("Есть ли у вас гипотония?"), "do_you_take_pills": MessageLookupByLibrary.simpleMessage( - "Do you take any medications or supplements?"), + "Принимаете ли вы лекарства или бады?",), "down_pressure": - MessageLookupByLibrary.simpleMessage("Lower Pressure Readings:"), - "draft": MessageLookupByLibrary.simpleMessage("Draft"), - "email": MessageLookupByLibrary.simpleMessage("Email"), - "email_empty": - MessageLookupByLibrary.simpleMessage("Email address is required"), - "email_invalid": - MessageLookupByLibrary.simpleMessage("Invalid email address"), + MessageLookupByLibrary.simpleMessage("Нижние показатели давления:"), + "draft": MessageLookupByLibrary.simpleMessage("Черновик"), + "email": MessageLookupByLibrary.simpleMessage("Элетронная почта"), + "email_empty": MessageLookupByLibrary.simpleMessage( + "Необходимо ввести адрес электронной почты",), + "email_invalid": MessageLookupByLibrary.simpleMessage( + "Некорректный адрес электронной почты",), "enter_additional_information": MessageLookupByLibrary.simpleMessage( - "Enter any additional information at your discretion:"), + "Введите любую дополнительную информацию на своё усмотрение:",), "enter_information": - MessageLookupByLibrary.simpleMessage("Enter information..."), - "exit": MessageLookupByLibrary.simpleMessage("Log Out"), + MessageLookupByLibrary.simpleMessage("Введите информацию..."), + "exit": MessageLookupByLibrary.simpleMessage("Выйти"), + "expand": MessageLookupByLibrary.simpleMessage("Показать полностью"), + "fats": MessageLookupByLibrary.simpleMessage("Жиры"), "favorite_foods": - MessageLookupByLibrary.simpleMessage("Favorite Foods"), + MessageLookupByLibrary.simpleMessage("Любимые продукты"), "favorite_unfavorite_foods": MessageLookupByLibrary.simpleMessage( - "Your Favorite/Least Favorite Foods?"), - "female": MessageLookupByLibrary.simpleMessage("Female"), + "Ваши любимые/нелюбимые продукты питания?",), + "female": MessageLookupByLibrary.simpleMessage("Женский"), "fill_in_information": - MessageLookupByLibrary.simpleMessage("Fill in the information:"), - "finish": MessageLookupByLibrary.simpleMessage("Finish"), - "first_name": MessageLookupByLibrary.simpleMessage("First Name"), - "first_name_empty": - MessageLookupByLibrary.simpleMessage("First name is required"), + MessageLookupByLibrary.simpleMessage("Заполните информацию:"), + "finish": MessageLookupByLibrary.simpleMessage("Завершить"), + "first_name": MessageLookupByLibrary.simpleMessage("Имя"), + "first_name_empty": MessageLookupByLibrary.simpleMessage( + "Имя обязательно для заполнения",), "first_name_short": MessageLookupByLibrary.simpleMessage( - "First name must be at least 1 character long"), - "gr": MessageLookupByLibrary.simpleMessage("g"), - "grams": MessageLookupByLibrary.simpleMessage("g"), + "В имени должен быть минимум 1 символ",), + "gr": MessageLookupByLibrary.simpleMessage("гр"), + "grams": MessageLookupByLibrary.simpleMessage("гр"), "height_measurements": - MessageLookupByLibrary.simpleMessage("Height Measurement"), + MessageLookupByLibrary.simpleMessage("Замер роста"), + "hide": MessageLookupByLibrary.simpleMessage("Свернуть"), "hip_circumference": - MessageLookupByLibrary.simpleMessage("Hip Circumference"), - "hips": MessageLookupByLibrary.simpleMessage("Hips"), - "in_progress": MessageLookupByLibrary.simpleMessage("In Progress"), + MessageLookupByLibrary.simpleMessage("Обхват бедер"), + "hips": MessageLookupByLibrary.simpleMessage("Бедра"), + "in_progress": MessageLookupByLibrary.simpleMessage("В процессе"), "initial_measurements": - MessageLookupByLibrary.simpleMessage("Initial Measurements"), - "language": MessageLookupByLibrary.simpleMessage("Language"), - "last_name": MessageLookupByLibrary.simpleMessage("Last Name"), - "last_name_empty": - MessageLookupByLibrary.simpleMessage("Last name is required"), + MessageLookupByLibrary.simpleMessage("Первичные замеры"), + "language": MessageLookupByLibrary.simpleMessage("Язык"), + "last_name": MessageLookupByLibrary.simpleMessage("Фамилия"), + "last_name_empty": MessageLookupByLibrary.simpleMessage( + "Фамилия обязательна для заполнения",), "last_name_short": MessageLookupByLibrary.simpleMessage( - "Last name must be at least 6 characters long"), + "В фамилии должен быть минимум 6 символов",), "lbl_english": MessageLookupByLibrary.simpleMessage("English"), - "loading": MessageLookupByLibrary.simpleMessage("Loading..."), - "male": MessageLookupByLibrary.simpleMessage("Male"), + "link_was_copied": + MessageLookupByLibrary.simpleMessage("Ссылка успешно скопирована"), + "loading": MessageLookupByLibrary.simpleMessage("Загрузка..."), + "male": MessageLookupByLibrary.simpleMessage("Мужской"), + "meal": MessageLookupByLibrary.simpleMessage("Приём пищи"), + "meat_type": MessageLookupByLibrary.simpleMessage("С мясом"), + "meat_type_desc": MessageLookupByLibrary.simpleMessage( + "Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму.",), + "milliliter": MessageLookupByLibrary.simpleMessage("мл"), "msg_network_err": - MessageLookupByLibrary.simpleMessage("Network error!"), + MessageLookupByLibrary.simpleMessage("Сетевая ошибка!"), "msg_something_went_wrong": - MessageLookupByLibrary.simpleMessage("Something went wrong!"), - "my_blog": MessageLookupByLibrary.simpleMessage("My Blog"), - "my_settings": MessageLookupByLibrary.simpleMessage("My Settings"), - "name_empty": - MessageLookupByLibrary.simpleMessage("Username is required"), + MessageLookupByLibrary.simpleMessage("Что то пошло не так!"), + "my_blog": MessageLookupByLibrary.simpleMessage("Мой блог"), + "my_settings": MessageLookupByLibrary.simpleMessage("Мои настройки"), + "name_empty": MessageLookupByLibrary.simpleMessage( + "Имя пользователя обязательно для заполнения",), "name_short": MessageLookupByLibrary.simpleMessage( - "Username must be at least 6 characters long"), - "no": MessageLookupByLibrary.simpleMessage("No"), + "В имени пользователя должно быть минимум 6 символов",), + "no": MessageLookupByLibrary.simpleMessage("Нет"), "no_account": - MessageLookupByLibrary.simpleMessage("I do not have an account"), - "password": MessageLookupByLibrary.simpleMessage("Password"), + MessageLookupByLibrary.simpleMessage("У меня нет аккаунта"), + "password": MessageLookupByLibrary.simpleMessage("Пароль"), "password_confirmation": - MessageLookupByLibrary.simpleMessage("Confirm Password"), + MessageLookupByLibrary.simpleMessage("Подтвердите пароль"), "password_confirmation_mismatch": - MessageLookupByLibrary.simpleMessage("Passwords do not match"), - "password_empty": - MessageLookupByLibrary.simpleMessage("Password is required"), + MessageLookupByLibrary.simpleMessage("Пароли не совпадают"), + "password_empty": MessageLookupByLibrary.simpleMessage( + "Пароль обязателен для заполнения",), "password_short": MessageLookupByLibrary.simpleMessage( - "Password must be at least 6 characters long"), - "pcs": MessageLookupByLibrary.simpleMessage("pcs"), + "В пароле должно быть минимум 6 символов",), + "pcs": MessageLookupByLibrary.simpleMessage("шт"), "place_of_living": - MessageLookupByLibrary.simpleMessage("Place of Residence"), + MessageLookupByLibrary.simpleMessage("Место проживания"), "plans_complete": - MessageLookupByLibrary.simpleMessage("Completed Plans"), - "preferred_language": MessageLookupByLibrary.simpleMessage( - "Preferred Language of Communication"), + MessageLookupByLibrary.simpleMessage("Пройденные планы"), + "preferred_language": + MessageLookupByLibrary.simpleMessage("Предпочитаемый язык общения"), "pressure_empty": MessageLookupByLibrary.simpleMessage( - "All blood pressure readings are required"), + "Все показатели давления обязательны к заполнению",), "pressure_is_not_a_number": MessageLookupByLibrary.simpleMessage( - "All blood pressure readings must be numbers"), - "profile": MessageLookupByLibrary.simpleMessage("Profile"), - "save": MessageLookupByLibrary.simpleMessage("Save"), - "settings": MessageLookupByLibrary.simpleMessage("Settings"), - "skip": MessageLookupByLibrary.simpleMessage("Skip"), + "Все показатели давления должны быть числами",), + "profile": MessageLookupByLibrary.simpleMessage("Профиль"), + "proteins": MessageLookupByLibrary.simpleMessage("Белки"), + "save": MessageLookupByLibrary.simpleMessage("Сохранить"), + "settings": MessageLookupByLibrary.simpleMessage("Настройки"), + "share": MessageLookupByLibrary.simpleMessage("Поделиться"), + "skip": MessageLookupByLibrary.simpleMessage("Пропустить"), + "startDiet": MessageLookupByLibrary.simpleMessage("Начать диету"), "tell_your_schedule": MessageLookupByLibrary.simpleMessage( - "Describe your daily routine freely, such as when you wake up, eat, and go to bed:"), + "Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:",), "unfavorite_foods": - MessageLookupByLibrary.simpleMessage("Least Favorite Foods"), - "up_pressure": - MessageLookupByLibrary.simpleMessage("Upper Pressure Readings:"), + MessageLookupByLibrary.simpleMessage("Нелюбимые продукты"), + "up_pressure": MessageLookupByLibrary.simpleMessage( + "Верхние показатели давления:",), "user_authorization": - MessageLookupByLibrary.simpleMessage("Authentication"), + MessageLookupByLibrary.simpleMessage("Аутентификация"), "user_registration": - MessageLookupByLibrary.simpleMessage("User Registration"), - "username": MessageLookupByLibrary.simpleMessage("Username"), - "waist": MessageLookupByLibrary.simpleMessage("Waist"), + MessageLookupByLibrary.simpleMessage("Регистрация пользователя"), + "username": MessageLookupByLibrary.simpleMessage("Имя пользователя"), + "vegan_type": MessageLookupByLibrary.simpleMessage("Вегетарианская"), + "vegan_type_desc": MessageLookupByLibrary.simpleMessage( + "Диета основана на потреблении растительной пищи и отказе от продуктов животного происхождения. Такой тип питания может быть разнообразным и вкусным, а также полезным для здоровья.",), + "waist": MessageLookupByLibrary.simpleMessage("Талия"), "waist_circumference": - MessageLookupByLibrary.simpleMessage("Waist Circumference"), - "weight": MessageLookupByLibrary.simpleMessage("Weight"), - "weight_chart": MessageLookupByLibrary.simpleMessage("Weight Chart"), + MessageLookupByLibrary.simpleMessage("Обхват талии"), + "weight": MessageLookupByLibrary.simpleMessage("Вес"), + "weight_chart": MessageLookupByLibrary.simpleMessage("График веса"), "weight_measurements": - MessageLookupByLibrary.simpleMessage("Weight Measurement"), + MessageLookupByLibrary.simpleMessage("Замер веса"), "what_is_your_name": - MessageLookupByLibrary.simpleMessage("What is your name?"), - "wrong_credentials": - MessageLookupByLibrary.simpleMessage("Invalid user credentials"), + MessageLookupByLibrary.simpleMessage("Как вас зовут?"), + "wrong_credentials": MessageLookupByLibrary.simpleMessage( + "Неверные данные пользователя",), "your_blood_pressure": - MessageLookupByLibrary.simpleMessage("Your Blood Pressure?"), + MessageLookupByLibrary.simpleMessage("Ваше артериальное давление?"), "your_favorite_sports": - MessageLookupByLibrary.simpleMessage("Your Favorite Sports?"), - "your_gender": MessageLookupByLibrary.simpleMessage("Your Gender?"), - "your_height": MessageLookupByLibrary.simpleMessage("Your Height:"), - "your_schedule": - MessageLookupByLibrary.simpleMessage("Your Daily Routine?"), - "your_weight": MessageLookupByLibrary.simpleMessage("Your Weight:") + MessageLookupByLibrary.simpleMessage("Ваши спортивные увлечения?"), + "your_gender": MessageLookupByLibrary.simpleMessage("Ваш пол?"), + "your_height": MessageLookupByLibrary.simpleMessage("Ваш рост:"), + "your_schedule": MessageLookupByLibrary.simpleMessage("Ваш режим дня?"), + "your_weight": MessageLookupByLibrary.simpleMessage("Ваш вес:"), }; } diff --git a/lib/generated/intl/messages_ru.dart b/lib/generated/intl/messages_ru.dart deleted file mode 100644 index 9c4da5f8..00000000 --- a/lib/generated/intl/messages_ru.dart +++ /dev/null @@ -1,175 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a ru locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'ru'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "Moscow": MessageLookupByLibrary.simpleMessage("Москва"), - "Russia": MessageLookupByLibrary.simpleMessage("Россия"), - "active": MessageLookupByLibrary.simpleMessage("Активен"), - "add_measure": MessageLookupByLibrary.simpleMessage("Добавить замер"), - "additional_information": - MessageLookupByLibrary.simpleMessage("Дополнительная информация"), - "admin": MessageLookupByLibrary.simpleMessage("Админ"), - "allergies_presence": - MessageLookupByLibrary.simpleMessage("Наличие аллергии"), - "archived": MessageLookupByLibrary.simpleMessage("В архиве"), - "authentificate": MessageLookupByLibrary.simpleMessage("Войти"), - "average_pressure": MessageLookupByLibrary.simpleMessage( - "Средние показатели давления:"), - "bodymass_chart": MessageLookupByLibrary.simpleMessage("График ИМТ"), - "chat": MessageLookupByLibrary.simpleMessage("Чат"), - "chest": MessageLookupByLibrary.simpleMessage("Грудь"), - "chest_circumference": - MessageLookupByLibrary.simpleMessage("Обхват груди"), - "circumference_empty": MessageLookupByLibrary.simpleMessage( - "Все замеры обязательны к заполнению"), - "circumference_is_not_a_number": MessageLookupByLibrary.simpleMessage( - "Все замеры должны быть числами"), - "circumferences_chart": - MessageLookupByLibrary.simpleMessage("График обхватов"), - "city": MessageLookupByLibrary.simpleMessage("Город"), - "city_of_living": - MessageLookupByLibrary.simpleMessage("Город проживания"), - "continueButton": MessageLookupByLibrary.simpleMessage("Продолжить"), - "country": MessageLookupByLibrary.simpleMessage("Страна"), - "date_of_birth": MessageLookupByLibrary.simpleMessage("Дата рождения"), - "diary": MessageLookupByLibrary.simpleMessage("Дневник"), - "do_you_have_hypertension_tendency": - MessageLookupByLibrary.simpleMessage("Есть ли у вас гипертония?"), - "do_you_have_hypotension_tendency": - MessageLookupByLibrary.simpleMessage("Есть ли у вас гипотония?"), - "do_you_take_pills": MessageLookupByLibrary.simpleMessage( - "Принимаете ли вы лекарства или бады?"), - "down_pressure": - MessageLookupByLibrary.simpleMessage("Нижние показатели давления:"), - "draft": MessageLookupByLibrary.simpleMessage("Черновик"), - "email": MessageLookupByLibrary.simpleMessage("Элетронная почта"), - "email_empty": MessageLookupByLibrary.simpleMessage( - "Необходимо ввести адрес электронной почты"), - "email_invalid": MessageLookupByLibrary.simpleMessage( - "Некорректный адрес электронной почты"), - "enter_additional_information": MessageLookupByLibrary.simpleMessage( - "Введите любую дополнительную информацию на своё усмотрение:"), - "enter_information": - MessageLookupByLibrary.simpleMessage("Введите информацию..."), - "exit": MessageLookupByLibrary.simpleMessage("Выйти"), - "favorite_foods": - MessageLookupByLibrary.simpleMessage("Любимые продукты"), - "favorite_unfavorite_foods": MessageLookupByLibrary.simpleMessage( - "Ваши любимые/нелюбимые продукты питания?"), - "female": MessageLookupByLibrary.simpleMessage("Женский"), - "fill_in_information": - MessageLookupByLibrary.simpleMessage("Заполните информацию:"), - "finish": MessageLookupByLibrary.simpleMessage("Завершить"), - "first_name": MessageLookupByLibrary.simpleMessage("Имя"), - "first_name_empty": MessageLookupByLibrary.simpleMessage( - "Имя обязательно для заполнения"), - "first_name_short": MessageLookupByLibrary.simpleMessage( - "В имени должен быть минимум 1 символ"), - "gr": MessageLookupByLibrary.simpleMessage("гр"), - "grams": MessageLookupByLibrary.simpleMessage("гр"), - "height_measurements": - MessageLookupByLibrary.simpleMessage("Замер роста"), - "hip_circumference": - MessageLookupByLibrary.simpleMessage("Обхват бедер"), - "hips": MessageLookupByLibrary.simpleMessage("Бедра"), - "in_progress": MessageLookupByLibrary.simpleMessage("В процессе"), - "initial_measurements": - MessageLookupByLibrary.simpleMessage("Первичные замеры"), - "language": MessageLookupByLibrary.simpleMessage("Язык"), - "last_name": MessageLookupByLibrary.simpleMessage("Фамилия"), - "last_name_empty": MessageLookupByLibrary.simpleMessage( - "Фамилия обязательна для заполнения"), - "last_name_short": MessageLookupByLibrary.simpleMessage( - "В фамилии должен быть минимум 6 символов"), - "lbl_english": MessageLookupByLibrary.simpleMessage("English"), - "loading": MessageLookupByLibrary.simpleMessage("Загрузка..."), - "male": MessageLookupByLibrary.simpleMessage("Мужской"), - "msg_network_err": - MessageLookupByLibrary.simpleMessage("Сетевая ошибка!"), - "msg_something_went_wrong": - MessageLookupByLibrary.simpleMessage("Что то пошло не так!"), - "my_blog": MessageLookupByLibrary.simpleMessage("Мой блог"), - "my_settings": MessageLookupByLibrary.simpleMessage("Мои настройки"), - "name_empty": MessageLookupByLibrary.simpleMessage( - "Имя пользователя обязательно для заполнения"), - "name_short": MessageLookupByLibrary.simpleMessage( - "В имени пользователя должно быть минимум 6 символов"), - "no": MessageLookupByLibrary.simpleMessage("Нет"), - "no_account": - MessageLookupByLibrary.simpleMessage("У меня нет аккаунта"), - "password": MessageLookupByLibrary.simpleMessage("Пароль"), - "password_confirmation": - MessageLookupByLibrary.simpleMessage("Подтвердите пароль"), - "password_confirmation_mismatch": - MessageLookupByLibrary.simpleMessage("Пароли не совпадают"), - "password_empty": MessageLookupByLibrary.simpleMessage( - "Пароль обязателен для заполнения"), - "password_short": MessageLookupByLibrary.simpleMessage( - "В пароле должно быть минимум 6 символов"), - "pcs": MessageLookupByLibrary.simpleMessage("шт"), - "place_of_living": - MessageLookupByLibrary.simpleMessage("Место проживания"), - "plans_complete": - MessageLookupByLibrary.simpleMessage("Пройденные планы"), - "preferred_language": - MessageLookupByLibrary.simpleMessage("Предпочитаемый язык общения"), - "pressure_empty": MessageLookupByLibrary.simpleMessage( - "Все показатели давления обязательны к заполнению"), - "pressure_is_not_a_number": MessageLookupByLibrary.simpleMessage( - "Все показатели давления должны быть числами"), - "profile": MessageLookupByLibrary.simpleMessage("Профиль"), - "save": MessageLookupByLibrary.simpleMessage("Сохранить"), - "settings": MessageLookupByLibrary.simpleMessage("Настройки"), - "skip": MessageLookupByLibrary.simpleMessage("Пропустить"), - "tell_your_schedule": MessageLookupByLibrary.simpleMessage( - "Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:"), - "unfavorite_foods": - MessageLookupByLibrary.simpleMessage("Нелюбимые продукты"), - "up_pressure": MessageLookupByLibrary.simpleMessage( - "Верхние показатели давления:"), - "user_authorization": - MessageLookupByLibrary.simpleMessage("Аутентификация"), - "user_registration": - MessageLookupByLibrary.simpleMessage("Регистрация пользователя"), - "username": MessageLookupByLibrary.simpleMessage("Имя пользователя"), - "waist": MessageLookupByLibrary.simpleMessage("Талия"), - "waist_circumference": - MessageLookupByLibrary.simpleMessage("Обхват талии"), - "weight": MessageLookupByLibrary.simpleMessage("Вес"), - "weight_chart": MessageLookupByLibrary.simpleMessage("График веса"), - "weight_measurements": - MessageLookupByLibrary.simpleMessage("Замер веса"), - "what_is_your_name": - MessageLookupByLibrary.simpleMessage("Как вас зовут?"), - "wrong_credentials": MessageLookupByLibrary.simpleMessage( - "Неверные данные пользователя"), - "your_blood_pressure": - MessageLookupByLibrary.simpleMessage("Ваше артериальное давление?"), - "your_favorite_sports": - MessageLookupByLibrary.simpleMessage("Ваши спортивные увлечения?"), - "your_gender": MessageLookupByLibrary.simpleMessage("Ваш пол?"), - "your_height": MessageLookupByLibrary.simpleMessage("Ваш рост:"), - "your_schedule": MessageLookupByLibrary.simpleMessage("Ваш режим дня?"), - "your_weight": MessageLookupByLibrary.simpleMessage("Ваш вес:") - }; -} diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 1d48044a..2cc942c5 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -19,7 +19,7 @@ class S { static S get current { assert(_current != null, - 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.'); + 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.',); return _current!; } @@ -42,7 +42,7 @@ class S { static S of(BuildContext context) { final instance = S.maybeOf(context); assert(instance != null, - 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?'); + 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?',); return instance!; } @@ -50,430 +50,430 @@ class S { return Localizations.of(context, S); } - /// `User Registration` + /// `Регистрация пользователя` String get user_registration { return Intl.message( - 'User Registration', + 'Регистрация пользователя', name: 'user_registration', desc: '', args: [], ); } - /// `Email` + /// `Элетронная почта` String get email { return Intl.message( - 'Email', + 'Элетронная почта', name: 'email', desc: '', args: [], ); } - /// `Username` + /// `Имя пользователя` String get username { return Intl.message( - 'Username', + 'Имя пользователя', name: 'username', desc: '', args: [], ); } - /// `Password` + /// `Пароль` String get password { return Intl.message( - 'Password', + 'Пароль', name: 'password', desc: '', args: [], ); } - /// `Confirm Password` + /// `Подтвердите пароль` String get password_confirmation { return Intl.message( - 'Confirm Password', + 'Подтвердите пароль', name: 'password_confirmation', desc: '', args: [], ); } - /// `Invalid user credentials` + /// `Неверные данные пользователя` String get wrong_credentials { return Intl.message( - 'Invalid user credentials', + 'Неверные данные пользователя', name: 'wrong_credentials', desc: '', args: [], ); } - /// `First Name` + /// `Имя` String get first_name { return Intl.message( - 'First Name', + 'Имя', name: 'first_name', desc: '', args: [], ); } - /// `Last Name` + /// `Фамилия` String get last_name { return Intl.message( - 'Last Name', + 'Фамилия', name: 'last_name', desc: '', args: [], ); } - /// `What is your name?` + /// `Как вас зовут?` String get what_is_your_name { return Intl.message( - 'What is your name?', + 'Как вас зовут?', name: 'what_is_your_name', desc: '', args: [], ); } - /// `Date of Birth` + /// `Дата рождения` String get date_of_birth { return Intl.message( - 'Date of Birth', + 'Дата рождения', name: 'date_of_birth', desc: '', args: [], ); } - /// `Country` + /// `Страна` String get country { return Intl.message( - 'Country', + 'Страна', name: 'country', desc: '', args: [], ); } - /// `Russia` + /// `Россия` String get Russia { return Intl.message( - 'Russia', + 'Россия', name: 'Russia', desc: '', args: [], ); } - /// `City` + /// `Город` String get city { return Intl.message( - 'City', + 'Город', name: 'city', desc: '', args: [], ); } - /// `Moscow` + /// `Москва` String get Moscow { return Intl.message( - 'Moscow', + 'Москва', name: 'Moscow', desc: '', args: [], ); } - /// `Place of Residence` + /// `Место проживания` String get place_of_living { return Intl.message( - 'Place of Residence', + 'Место проживания', name: 'place_of_living', desc: '', args: [], ); } - /// `Hip Circumference` + /// `Обхват бедер` String get hip_circumference { return Intl.message( - 'Hip Circumference', + 'Обхват бедер', name: 'hip_circumference', desc: '', args: [], ); } - /// `Chest Circumference` + /// `Обхват груди` String get chest_circumference { return Intl.message( - 'Chest Circumference', + 'Обхват груди', name: 'chest_circumference', desc: '', args: [], ); } - /// `Waist Circumference` + /// `Обхват талии` String get waist_circumference { return Intl.message( - 'Waist Circumference', + 'Обхват талии', name: 'waist_circumference', desc: '', args: [], ); } - /// `Initial Measurements` + /// `Первичные замеры` String get initial_measurements { return Intl.message( - 'Initial Measurements', + 'Первичные замеры', name: 'initial_measurements', desc: '', args: [], ); } - /// `Male` + /// `Мужской` String get male { return Intl.message( - 'Male', + 'Мужской', name: 'male', desc: '', args: [], ); } - /// `Female` + /// `Женский` String get female { return Intl.message( - 'Female', + 'Женский', name: 'female', desc: '', args: [], ); } - /// `Weight Measurement` + /// `Замер веса` String get weight_measurements { return Intl.message( - 'Weight Measurement', + 'Замер веса', name: 'weight_measurements', desc: '', args: [], ); } - /// `Your Weight:` + /// `Ваш вес:` String get your_weight { return Intl.message( - 'Your Weight:', + 'Ваш вес:', name: 'your_weight', desc: '', args: [], ); } - /// `Height Measurement` + /// `Замер роста` String get height_measurements { return Intl.message( - 'Height Measurement', + 'Замер роста', name: 'height_measurements', desc: '', args: [], ); } - /// `Your Height:` + /// `Ваш рост:` String get your_height { return Intl.message( - 'Your Height:', + 'Ваш рост:', name: 'your_height', desc: '', args: [], ); } - /// `Presence of Allergies` + /// `Наличие аллергии` String get allergies_presence { return Intl.message( - 'Presence of Allergies', + 'Наличие аллергии', name: 'allergies_presence', desc: '', args: [], ); } - /// `Do you take any medications or supplements?` + /// `Принимаете ли вы лекарства или бады?` String get do_you_take_pills { return Intl.message( - 'Do you take any medications or supplements?', + 'Принимаете ли вы лекарства или бады?', name: 'do_you_take_pills', desc: '', args: [], ); } - /// `Your Gender?` + /// `Ваш пол?` String get your_gender { return Intl.message( - 'Your Gender?', + 'Ваш пол?', name: 'your_gender', desc: '', args: [], ); } - /// `No` + /// `Нет` String get no { return Intl.message( - 'No', + 'Нет', name: 'no', desc: '', args: [], ); } - /// `Do you have a tendency for hypertension?` + /// `Есть ли у вас гипертония?` String get do_you_have_hypertension_tendency { return Intl.message( - 'Do you have a tendency for hypertension?', + 'Есть ли у вас гипертония?', name: 'do_you_have_hypertension_tendency', desc: '', args: [], ); } - /// `Your Blood Pressure?` + /// `Ваше артериальное давление?` String get your_blood_pressure { return Intl.message( - 'Your Blood Pressure?', + 'Ваше артериальное давление?', name: 'your_blood_pressure', desc: '', args: [], ); } - /// `Average Pressure Readings:` + /// `Средние показатели давления:` String get average_pressure { return Intl.message( - 'Average Pressure Readings:', + 'Средние показатели давления:', name: 'average_pressure', desc: '', args: [], ); } - /// `Upper Pressure Readings:` + /// `Верхние показатели давления:` String get up_pressure { return Intl.message( - 'Upper Pressure Readings:', + 'Верхние показатели давления:', name: 'up_pressure', desc: '', args: [], ); } - /// `Lower Pressure Readings:` + /// `Нижние показатели давления:` String get down_pressure { return Intl.message( - 'Lower Pressure Readings:', + 'Нижние показатели давления:', name: 'down_pressure', desc: '', args: [], ); } - /// `Do you have a tendency for hypotension?` + /// `Есть ли у вас гипотония?` String get do_you_have_hypotension_tendency { return Intl.message( - 'Do you have a tendency for hypotension?', + 'Есть ли у вас гипотония?', name: 'do_you_have_hypotension_tendency', desc: '', args: [], ); } - /// `Your Favorite Sports?` + /// `Ваши спортивные увлечения?` String get your_favorite_sports { return Intl.message( - 'Your Favorite Sports?', + 'Ваши спортивные увлечения?', name: 'your_favorite_sports', desc: '', args: [], ); } - /// `Favorite Foods` + /// `Любимые продукты` String get favorite_foods { return Intl.message( - 'Favorite Foods', + 'Любимые продукты', name: 'favorite_foods', desc: '', args: [], ); } - /// `Your Favorite/Least Favorite Foods?` + /// `Ваши любимые/нелюбимые продукты питания?` String get favorite_unfavorite_foods { return Intl.message( - 'Your Favorite/Least Favorite Foods?', + 'Ваши любимые/нелюбимые продукты питания?', name: 'favorite_unfavorite_foods', desc: '', args: [], ); } - /// `Least Favorite Foods` + /// `Нелюбимые продукты` String get unfavorite_foods { return Intl.message( - 'Least Favorite Foods', + 'Нелюбимые продукты', name: 'unfavorite_foods', desc: '', args: [], ); } - /// `Your Daily Routine?` + /// `Ваш режим дня?` String get your_schedule { return Intl.message( - 'Your Daily Routine?', + 'Ваш режим дня?', name: 'your_schedule', desc: '', args: [], ); } - /// `Describe your daily routine freely, such as when you wake up, eat, and go to bed:` + /// `Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:` String get tell_your_schedule { return Intl.message( - 'Describe your daily routine freely, such as when you wake up, eat, and go to bed:', + 'Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:', name: 'tell_your_schedule', desc: '', args: [], ); } - /// `Additional Information` + /// `Дополнительная информация` String get additional_information { return Intl.message( - 'Additional Information', + 'Дополнительная информация', name: 'additional_information', desc: '', args: [], ); } - /// `Enter any additional information at your discretion:` + /// `Введите любую дополнительную информацию на своё усмотрение:` String get enter_additional_information { return Intl.message( - 'Enter any additional information at your discretion:', + 'Введите любую дополнительную информацию на своё усмотрение:', name: 'enter_additional_information', desc: '', args: [], @@ -490,545 +490,885 @@ class S { ); } - /// `Finish` + /// `Завершить` String get finish { return Intl.message( - 'Finish', + 'Завершить', name: 'finish', desc: '', args: [], ); } - /// `Preferred Language of Communication` + /// `Предпочитаемый язык общения` String get preferred_language { return Intl.message( - 'Preferred Language of Communication', + 'Предпочитаемый язык общения', name: 'preferred_language', desc: '', args: [], ); } - /// `Continue` + /// `Продолжить` String get continueButton { return Intl.message( - 'Continue', + 'Продолжить', name: 'continueButton', desc: '', args: [], ); } - /// `Skip` + /// `Пропустить` String get skip { return Intl.message( - 'Skip', + 'Пропустить', name: 'skip', desc: '', args: [], ); } - /// `Fill in the information:` + /// `Заполните информацию:` String get fill_in_information { return Intl.message( - 'Fill in the information:', + 'Заполните информацию:', name: 'fill_in_information', desc: '', args: [], ); } - /// `Enter information...` + /// `Введите информацию...` String get enter_information { return Intl.message( - 'Enter information...', + 'Введите информацию...', name: 'enter_information', desc: '', args: [], ); } - /// `Network error!` + /// `Сетевая ошибка!` String get msg_network_err { return Intl.message( - 'Network error!', + 'Сетевая ошибка!', name: 'msg_network_err', desc: '', args: [], ); } - /// `Something went wrong!` + /// `Что то пошло не так!` String get msg_something_went_wrong { return Intl.message( - 'Something went wrong!', + 'Что то пошло не так!', name: 'msg_something_went_wrong', desc: '', args: [], ); } - /// `Email address is required` + /// `Необходимо ввести адрес электронной почты` String get email_empty { return Intl.message( - 'Email address is required', + 'Необходимо ввести адрес электронной почты', name: 'email_empty', desc: '', args: [], ); } - /// `Invalid email address` + /// `Некорректный адрес электронной почты` String get email_invalid { return Intl.message( - 'Invalid email address', + 'Некорректный адрес электронной почты', name: 'email_invalid', desc: '', args: [], ); } - /// `Password is required` + /// `Пароль обязателен для заполнения` String get password_empty { return Intl.message( - 'Password is required', + 'Пароль обязателен для заполнения', name: 'password_empty', desc: '', args: [], ); } - /// `Password must be at least 6 characters long` + /// `В пароле должно быть минимум 6 символов` String get password_short { return Intl.message( - 'Password must be at least 6 characters long', + 'В пароле должно быть минимум 6 символов', name: 'password_short', desc: '', args: [], ); } - /// `Username is required` + /// `Имя пользователя обязательно для заполнения` String get name_empty { return Intl.message( - 'Username is required', + 'Имя пользователя обязательно для заполнения', name: 'name_empty', desc: '', args: [], ); } - /// `Username must be at least 6 characters long` + /// `В имени пользователя должно быть минимум 6 символов` String get name_short { return Intl.message( - 'Username must be at least 6 characters long', + 'В имени пользователя должно быть минимум 6 символов', name: 'name_short', desc: '', args: [], ); } - /// `Passwords do not match` + /// `Пароли не совпадают` String get password_confirmation_mismatch { return Intl.message( - 'Passwords do not match', + 'Пароли не совпадают', name: 'password_confirmation_mismatch', desc: '', args: [], ); } - /// `First name is required` + /// `Имя обязательно для заполнения` String get first_name_empty { return Intl.message( - 'First name is required', + 'Имя обязательно для заполнения', name: 'first_name_empty', desc: '', args: [], ); } - /// `First name must be at least 1 character long` + /// `В имени должен быть минимум 1 символ` String get first_name_short { return Intl.message( - 'First name must be at least 1 character long', + 'В имени должен быть минимум 1 символ', name: 'first_name_short', desc: '', args: [], ); } - /// `Last name is required` + /// `Фамилия обязательна для заполнения` String get last_name_empty { return Intl.message( - 'Last name is required', + 'Фамилия обязательна для заполнения', name: 'last_name_empty', desc: '', args: [], ); } - /// `Last name must be at least 6 characters long` + /// `В фамилии должен быть минимум 6 символов` String get last_name_short { return Intl.message( - 'Last name must be at least 6 characters long', + 'В фамилии должен быть минимум 6 символов', name: 'last_name_short', desc: '', args: [], ); } - /// `All measurements are required` + /// `Все замеры обязательны к заполнению` String get circumference_empty { return Intl.message( - 'All measurements are required', + 'Все замеры обязательны к заполнению', name: 'circumference_empty', desc: '', args: [], ); } - /// `All measurements must be numbers` + /// `Все замеры должны быть числами` String get circumference_is_not_a_number { return Intl.message( - 'All measurements must be numbers', + 'Все замеры должны быть числами', name: 'circumference_is_not_a_number', desc: '', args: [], ); } - /// `All blood pressure readings are required` + /// `Все показатели давления обязательны к заполнению` String get pressure_empty { return Intl.message( - 'All blood pressure readings are required', + 'Все показатели давления обязательны к заполнению', name: 'pressure_empty', desc: '', args: [], ); } - /// `All blood pressure readings must be numbers` + /// `Все показатели давления должны быть числами` String get pressure_is_not_a_number { return Intl.message( - 'All blood pressure readings must be numbers', + 'Все показатели давления должны быть числами', name: 'pressure_is_not_a_number', desc: '', args: [], ); } - /// `I do not have an account` + /// `У меня нет аккаунта` String get no_account { return Intl.message( - 'I do not have an account', + 'У меня нет аккаунта', name: 'no_account', desc: '', args: [], ); } - /// `Log In` + /// `Войти` String get authentificate { return Intl.message( - 'Log In', + 'Войти', name: 'authentificate', desc: '', args: [], ); } - /// `Authentication` + /// `Аутентификация` String get user_authorization { return Intl.message( - 'Authentication', + 'Аутентификация', name: 'user_authorization', desc: '', args: [], ); } - /// `Loading...` + /// `Загрузка...` String get loading { return Intl.message( - 'Loading...', + 'Загрузка...', name: 'loading', desc: '', args: [], ); } - /// `Weight Chart` + /// `График веса` String get weight_chart { return Intl.message( - 'Weight Chart', + 'График веса', name: 'weight_chart', desc: '', args: [], ); } - /// `Circumference Chart` + /// `График обхватов` String get circumferences_chart { return Intl.message( - 'Circumference Chart', + 'График обхватов', name: 'circumferences_chart', desc: '', args: [], ); } - /// `City of Residence` + /// `Город проживания` String get city_of_living { return Intl.message( - 'City of Residence', + 'Город проживания', name: 'city_of_living', desc: '', args: [], ); } - /// `BMI Chart` + /// `График ИМТ` String get bodymass_chart { return Intl.message( - 'BMI Chart', + 'График ИМТ', name: 'bodymass_chart', desc: '', args: [], ); } - /// `Chest` + /// `Грудь` String get chest { return Intl.message( - 'Chest', + 'Грудь', name: 'chest', desc: '', args: [], ); } - /// `Waist` + /// `Талия` String get waist { return Intl.message( - 'Waist', + 'Талия', name: 'waist', desc: '', args: [], ); } - /// `Hips` + /// `Бедра` String get hips { return Intl.message( - 'Hips', + 'Бедра', name: 'hips', desc: '', args: [], ); } - /// `Log Out` + /// `Выйти` String get exit { return Intl.message( - 'Log Out', + 'Выйти', name: 'exit', desc: '', args: [], ); } - /// `Active` + /// `Активен` String get active { return Intl.message( - 'Active', + 'Активен', name: 'active', desc: '', args: [], ); } - /// `Archived` + /// `В архиве` String get archived { return Intl.message( - 'Archived', + 'В архиве', name: 'archived', desc: '', args: [], ); } - /// `Draft` + /// `Черновик` String get draft { return Intl.message( - 'Draft', + 'Черновик', name: 'draft', desc: '', args: [], ); } - /// `My Blog` + /// `Мой блог` String get my_blog { return Intl.message( - 'My Blog', + 'Мой блог', name: 'my_blog', desc: '', args: [], ); } - /// `Language` + /// `Язык` String get language { return Intl.message( - 'Language', + 'Язык', name: 'language', desc: '', args: [], ); } - /// `In Progress` + /// `В процессе` String get in_progress { return Intl.message( - 'In Progress', + 'В процессе', name: 'in_progress', desc: '', args: [], ); } - /// `Add Measurement` + /// `Добавить замер` String get add_measure { return Intl.message( - 'Add Measurement', + 'Добавить замер', name: 'add_measure', desc: '', args: [], ); } - /// `Weight` + /// `Вес` String get weight { return Intl.message( - 'Weight', + 'Вес', name: 'weight', desc: '', args: [], ); } - /// `Completed Plans` + /// `Пройденные планы` String get plans_complete { return Intl.message( - 'Completed Plans', + 'Пройденные планы', name: 'plans_complete', desc: '', args: [], ); } - /// `My Settings` + /// `Мои настройки` String get my_settings { return Intl.message( - 'My Settings', + 'Мои настройки', name: 'my_settings', desc: '', args: [], ); } - /// `Settings` + /// `Настройки` String get settings { return Intl.message( - 'Settings', + 'Настройки', name: 'settings', desc: '', args: [], ); } - /// `Profile` + /// `Профиль` String get profile { return Intl.message( - 'Profile', + 'Профиль', name: 'profile', desc: '', args: [], ); } - /// `Chat` + /// `Чат` String get chat { return Intl.message( - 'Chat', + 'Чат', name: 'chat', desc: '', args: [], ); } - /// `Admin` + /// `Админ` String get admin { return Intl.message( - 'Admin', + 'Админ', name: 'admin', desc: '', args: [], ); } - /// `Diary` + /// `Дневник` String get diary { return Intl.message( - 'Diary', + 'Дневник', name: 'diary', desc: '', args: [], ); } - /// `Save` + /// `Сохранить` String get save { return Intl.message( - 'Save', + 'Сохранить', name: 'save', desc: '', args: [], ); } - /// `pcs` + /// `шт` String get pcs { return Intl.message( - 'pcs', + 'шт', name: 'pcs', desc: '', args: [], ); } - /// `g` + /// `гр` String get gr { return Intl.message( - 'g', + 'гр', name: 'gr', desc: '', args: [], ); } - /// `g` + /// `мл` + String get milliliter { + return Intl.message( + 'мл', + name: 'milliliter', + desc: '', + args: [], + ); + } + + /// `гр` String get grams { return Intl.message( - 'g', + 'гр', name: 'grams', desc: '', args: [], ); } + + /// `Начать диету` + String get startDiet { + return Intl.message( + 'Начать диету', + name: 'startDiet', + desc: '', + args: [], + ); + } + + /// `Комментарии` + String get comments { + return Intl.message( + 'Комментарии', + name: 'comments', + desc: '', + args: [], + ); + } + + /// `комментарий` + String get comment { + return Intl.message( + 'комментарий', + name: 'comment', + desc: '', + args: [], + ); + } + + /// `комментария` + String get another_form_of_comment { + return Intl.message( + 'комментария', + name: 'another_form_of_comment', + desc: '', + args: [], + ); + } + + /// `Об этой диете` + String get about_diet { + return Intl.message( + 'Об этой диете', + name: 'about_diet', + desc: '', + args: [], + ); + } + + /// `дней` + String get days { + return Intl.message( + 'дней', + name: 'days', + desc: '', + args: [], + ); + } + + /// `День` + String get day { + return Intl.message( + 'День', + name: 'day', + desc: '', + args: [], + ); + } + + /// `дня` + String get another_form_of_day { + return Intl.message( + 'дня', + name: 'another_form_of_day', + desc: '', + args: [], + ); + } + + /// `комментариев` + String get comments_plural { + return Intl.message( + 'комментариев', + name: 'comments_plural', + desc: '', + args: [], + ); + } + + /// `Приём пищи` + String get meal { + return Intl.message( + 'Приём пищи', + name: 'meal', + desc: '', + args: [], + ); + } + + /// `Автор диеты` + String get diet_author { + return Intl.message( + 'Автор диеты', + name: 'diet_author', + desc: '', + args: [], + ); + } + + /// `Свернуть` + String get hide { + return Intl.message( + 'Свернуть', + name: 'hide', + desc: '', + args: [], + ); + } + + /// `Показать полностью` + String get expand { + return Intl.message( + 'Показать полностью', + name: 'expand', + desc: '', + args: [], + ); + } + + /// `Скопировать ссылку` + String get copy_link { + return Intl.message( + 'Скопировать ссылку', + name: 'copy_link', + desc: '', + args: [], + ); + } + + /// `Поделиться` + String get share { + return Intl.message( + 'Поделиться', + name: 'share', + desc: '', + args: [], + ); + } + + /// `Пожаловаться` + String get complain { + return Intl.message( + 'Пожаловаться', + name: 'complain', + desc: '', + args: [], + ); + } + + /// `Вегетарианская` + String get vegan_type { + return Intl.message( + 'Вегетарианская', + name: 'vegan_type', + desc: '', + args: [], + ); + } + + /// `Алкоголь` + String get alko_type { + return Intl.message( + 'Алкоголь', + name: 'alko_type', + desc: '', + args: [], + ); + } + + /// `С мясом` + String get meat_type { + return Intl.message( + 'С мясом', + name: 'meat_type', + desc: '', + args: [], + ); + } + + /// `Мучное` + String get corb_type { + return Intl.message( + 'Мучное', + name: 'corb_type', + desc: '', + args: [], + ); + } + + /// `Диета основана на потреблении растительной пищи и отказе от продуктов животного происхождения. Такой тип питания может быть разнообразным и вкусным, а также полезным для здоровья.` + String get vegan_type_desc { + return Intl.message( + 'Диета основана на потреблении растительной пищи и отказе от продуктов животного происхождения. Такой тип питания может быть разнообразным и вкусным, а также полезным для здоровья.', + name: 'vegan_type_desc', + desc: '', + args: [], + ); + } + + /// `С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками.` + String get alko_type_desc { + return Intl.message( + 'С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками.', + name: 'alko_type_desc', + desc: '', + args: [], + ); + } + + /// `Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.` + String get corb_type_desc { + return Intl.message( + 'Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.', + name: 'corb_type_desc', + desc: '', + args: [], + ); + } + + /// `Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму.` + String get meat_type_desc { + return Intl.message( + 'Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму.', + name: 'meat_type_desc', + desc: '', + args: [], + ); + } + + /// `План диеты` + String get diet_plan { + return Intl.message( + 'План диеты', + name: 'diet_plan', + desc: '', + args: [], + ); + } + + /// `Калории` + String get calories { + return Intl.message( + 'Калории', + name: 'calories', + desc: '', + args: [], + ); + } + + /// `Белки` + String get proteins { + return Intl.message( + 'Белки', + name: 'proteins', + desc: '', + args: [], + ); + } + + /// `Жиры` + String get fats { + return Intl.message( + 'Жиры', + name: 'fats', + desc: '', + args: [], + ); + } + + /// `Углеводы` + String get carbohydrates { + return Intl.message( + 'Углеводы', + name: 'carbohydrates', + desc: '', + args: [], + ); + } + + /// `Ответить` + String get answer { + return Intl.message( + 'Ответить', + name: 'answer', + desc: '', + args: [], + ); + } + + /// `Ссылка успешно скопирована` + String get link_was_copied { + return Intl.message( + 'Ссылка успешно скопирована', + name: 'link_was_copied', + desc: '', + args: [], + ); + } + + /// `Выберите дату старты диеты. Она отобразиться у Вас в дневнике` + String get choose_start_diet_date { + return Intl.message( + 'Выберите дату старты диеты. Она отобразиться у Вас в дневнике', + name: 'choose_start_diet_date', + desc: '', + args: [], + ); + } + + /// `Продолжить` + String get continue_ { + return Intl.message( + 'Продолжить', + name: 'continue_', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { @@ -1037,7 +1377,6 @@ class AppLocalizationDelegate extends LocalizationsDelegate { List get supportedLocales { return const [ Locale.fromSubtags(languageCode: 'en'), - Locale.fromSubtags(languageCode: 'ru'), ]; } diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index a204a347..0d0d086a 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1,100 +1,134 @@ { - "user_registration": "User Registration", - "email": "Email", - "username": "Username", - "password": "Password", - "password_confirmation": "Confirm Password", - "wrong_credentials": "Invalid user credentials", - "first_name": "First Name", - "last_name": "Last Name", - "what_is_your_name": "What is your name?", - "date_of_birth": "Date of Birth", - "country": "Country", - "Russia": "Russia", - "city": "City", - "Moscow": "Moscow", - "place_of_living": "Place of Residence", - "hip_circumference": "Hip Circumference", - "chest_circumference": "Chest Circumference", - "waist_circumference": "Waist Circumference", - "initial_measurements": "Initial Measurements", - "male": "Male", - "female": "Female", - "weight_measurements": "Weight Measurement", - "your_weight": "Your Weight:", - "height_measurements": "Height Measurement", - "your_height": "Your Height:", - "allergies_presence": "Presence of Allergies", - "do_you_take_pills": "Do you take any medications or supplements?", - "your_gender": "Your Gender?", - "no": "No", - "do_you_have_hypertension_tendency": "Do you have a tendency for hypertension?", - "your_blood_pressure": "Your Blood Pressure?", - "average_pressure": "Average Pressure Readings:", - "up_pressure": "Upper Pressure Readings:", - "down_pressure": "Lower Pressure Readings:", - "do_you_have_hypotension_tendency": "Do you have a tendency for hypotension?", - "your_favorite_sports": "Your Favorite Sports?", - "favorite_foods": "Favorite Foods", - "favorite_unfavorite_foods": "Your Favorite/Least Favorite Foods?", - "unfavorite_foods": "Least Favorite Foods", - "your_schedule": "Your Daily Routine?", - "tell_your_schedule": "Describe your daily routine freely, such as when you wake up, eat, and go to bed:", - "additional_information": "Additional Information", - "enter_additional_information": "Enter any additional information at your discretion:", + "user_registration": "Регистрация пользователя", + "email": "Элетронная почта", + "username": "Имя пользователя", + "password": "Пароль", + "password_confirmation": "Подтвердите пароль", + "wrong_credentials": "Неверные данные пользователя", + "first_name": "Имя", + "last_name": "Фамилия", + "what_is_your_name": "Как вас зовут?", + "date_of_birth": "Дата рождения", + "country": "Страна", + "Russia": "Россия", + "city": "Город", + "Moscow": "Москва", + "place_of_living": "Место проживания", + "hip_circumference": "Обхват бедер", + "chest_circumference": "Обхват груди", + "waist_circumference": "Обхват талии", + "initial_measurements": "Первичные замеры", + "male": "Мужской", + "female": "Женский", + "weight_measurements": "Замер веса", + "your_weight": "Ваш вес:", + "height_measurements": "Замер роста", + "your_height": "Ваш рост:", + "allergies_presence": "Наличие аллергии", + "do_you_take_pills": "Принимаете ли вы лекарства или бады?", + "your_gender": "Ваш пол?", + "no": "Нет", + "do_you_have_hypertension_tendency": "Есть ли у вас гипертония?", + "your_blood_pressure": "Ваше артериальное давление?", + "average_pressure": "Средние показатели давления:", + "up_pressure": "Верхние показатели давления:", + "down_pressure": "Нижние показатели давления:", + "do_you_have_hypotension_tendency": "Есть ли у вас гипотония?", + "your_favorite_sports": "Ваши спортивные увлечения?", + "favorite_foods": "Любимые продукты", + "favorite_unfavorite_foods": "Ваши любимые/нелюбимые продукты питания?", + "unfavorite_foods": "Нелюбимые продукты", + "your_schedule": "Ваш режим дня?", + "tell_your_schedule": "Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:", + "additional_information": "Дополнительная информация", + "enter_additional_information": "Введите любую дополнительную информацию на своё усмотрение:", "lbl_english": "English", - "finish": "Finish", - "preferred_language": "Preferred Language of Communication", - "continueButton": "Continue", - "skip": "Skip", - "fill_in_information": "Fill in the information:", - "enter_information": "Enter information...", - "msg_network_err": "Network error!", - "msg_something_went_wrong": "Something went wrong!", - "email_empty": "Email address is required", - "email_invalid": "Invalid email address", - "password_empty": "Password is required", - "password_short": "Password must be at least 6 characters long", - "name_empty": "Username is required", - "name_short": "Username must be at least 6 characters long", - "password_confirmation_mismatch": "Passwords do not match", - "first_name_empty": "First name is required", - "first_name_short": "First name must be at least 1 character long", - "last_name_empty": "Last name is required", - "last_name_short": "Last name must be at least 6 characters long", - "circumference_empty": "All measurements are required", - "circumference_is_not_a_number": "All measurements must be numbers", - "pressure_empty": "All blood pressure readings are required", - "pressure_is_not_a_number": "All blood pressure readings must be numbers", - "no_account": "I do not have an account", - "authentificate": "Log In", - "user_authorization": "Authentication", - "loading": "Loading...", - "weight_chart": "Weight Chart", - "circumferences_chart": "Circumference Chart", - "city_of_living": "City of Residence", - "bodymass_chart": "BMI Chart", - "chest": "Chest", - "waist": "Waist", - "hips": "Hips", - "exit": "Log Out", - "active": "Active", - "archived": "Archived", - "draft": "Draft", - "my_blog": "My Blog", - "language": "Language", - "in_progress": "In Progress", - "add_measure": "Add Measurement", - "weight": "Weight", - "plans_complete": "Completed Plans", - "my_settings": "My Settings", - "settings": "Settings", - "profile": "Profile", - "chat": "Chat", - "admin": "Admin", - "diary": "Diary", - "save": "Save", - "pcs": "pcs", - "gr": "g", - "grams": "g" + "finish": "Завершить", + "preferred_language": "Предпочитаемый язык общения", + "continueButton": "Продолжить", + "skip": "Пропустить", + "fill_in_information": "Заполните информацию:", + "enter_information": "Введите информацию...", + "msg_network_err": "Сетевая ошибка!", + "msg_something_went_wrong": "Что то пошло не так!", + "email_empty": "Необходимо ввести адрес электронной почты", + "email_invalid": "Некорректный адрес электронной почты", + "password_empty": "Пароль обязателен для заполнения", + "password_short": "В пароле должно быть минимум 6 символов", + "name_empty": "Имя пользователя обязательно для заполнения", + "name_short": "В имени пользователя должно быть минимум 6 символов", + "password_confirmation_mismatch": "Пароли не совпадают", + "first_name_empty": "Имя обязательно для заполнения", + "first_name_short": "В имени должен быть минимум 1 символ", + "last_name_empty": "Фамилия обязательна для заполнения", + "last_name_short": "В фамилии должен быть минимум 6 символов", + "circumference_empty": "Все замеры обязательны к заполнению", + "circumference_is_not_a_number": "Все замеры должны быть числами", + "pressure_empty": "Все показатели давления обязательны к заполнению", + "pressure_is_not_a_number": "Все показатели давления должны быть числами", + "no_account": "У меня нет аккаунта", + "authentificate": "Войти", + "user_authorization": "Аутентификация", + "loading": "Загрузка...", + "weight_chart": "График веса", + "circumferences_chart": "График обхватов", + "city_of_living": "Город проживания", + "bodymass_chart": "График ИМТ", + "chest": "Грудь", + "waist": "Талия", + "hips": "Бедра", + "exit": "Выйти", + "active": "Активен", + "archived": "В архиве", + "draft": "Черновик", + "my_blog": "Мой блог", + "language": "Язык", + "in_progress": "В процессе", + "add_measure": "Добавить замер", + "weight": "Вес", + "plans_complete": "Пройденные планы", + "my_settings": "Мои настройки", + "settings": "Настройки", + "profile": "Профиль", + "chat": "Чат", + "admin": "Админ", + "diary": "Дневник", + "save": "Сохранить", + "pcs": "шт", + "gr": "гр", + "milliliter": "мл", + "grams": "гр", + "startDiet": "Начать диету", + "comments": "Комментарии", + "comment": "комментарий", + "another_form_of_comment": "комментария", + "about_diet": "Об этой диете", + "days": "дней", + "day": "День", + "another_form_of_day": "дня", + "comments_plural": "комментариев", + "meal": "Приём пищи", + "diet_author": "Автор диеты", + "hide": "Свернуть", + "expand": "Показать полностью", + "copy_link": "Скопировать ссылку", + "share": "Поделиться", + "complain": "Пожаловаться", + "vegan_type": "Вегетарианская", + "alko_type": "Алкоголь", + "meat_type": "С мясом", + "corb_type": "Мучное", + "vegan_type_desc": "Диета основана на потреблении растительной пищи и отказе от продуктов животного происхождения. Такой тип питания может быть разнообразным и вкусным, а также полезным для здоровья.", + "alko_type_desc": "С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками.", + "corb_type_desc": "Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.", + "meat_type_desc": "Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму.", + "diet_plan": "План диеты", + "calories": "Калории", + "proteins": "Белки", + "fats": "Жиры", + "carbohydrates": "Углеводы", + "answer": "Ответить", + "link_was_copied": "Ссылка успешно скопирована", + "choose_start_diet_date": "Выберите дату старты диеты. Она отобразиться у Вас в дневнике", + "continue_": "Продолжить" } diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb deleted file mode 100644 index 8d2d512c..00000000 --- a/lib/l10n/intl_ru.arb +++ /dev/null @@ -1,100 +0,0 @@ -{ - "user_registration": "Регистрация пользователя", - "email": "Элетронная почта", - "username": "Имя пользователя", - "password": "Пароль", - "password_confirmation": "Подтвердите пароль", - "wrong_credentials": "Неверные данные пользователя", - "first_name": "Имя", - "last_name": "Фамилия", - "what_is_your_name": "Как вас зовут?", - "date_of_birth": "Дата рождения", - "country": "Страна", - "Russia": "Россия", - "city": "Город", - "Moscow": "Москва", - "place_of_living": "Место проживания", - "hip_circumference": "Обхват бедер", - "chest_circumference": "Обхват груди", - "waist_circumference": "Обхват талии", - "initial_measurements": "Первичные замеры", - "male": "Мужской", - "female": "Женский", - "weight_measurements": "Замер веса", - "your_weight": "Ваш вес:", - "height_measurements": "Замер роста", - "your_height": "Ваш рост:", - "allergies_presence": "Наличие аллергии", - "do_you_take_pills": "Принимаете ли вы лекарства или бады?", - "your_gender": "Ваш пол?", - "no": "Нет", - "do_you_have_hypertension_tendency": "Есть ли у вас гипертония?", - "your_blood_pressure": "Ваше артериальное давление?", - "average_pressure": "Средние показатели давления:", - "up_pressure": "Верхние показатели давления:", - "down_pressure": "Нижние показатели давления:", - "do_you_have_hypotension_tendency": "Есть ли у вас гипотония?", - "your_favorite_sports": "Ваши спортивные увлечения?", - "favorite_foods": "Любимые продукты", - "favorite_unfavorite_foods": "Ваши любимые/нелюбимые продукты питания?", - "unfavorite_foods": "Нелюбимые продукты", - "your_schedule": "Ваш режим дня?", - "tell_your_schedule": "Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:", - "additional_information": "Дополнительная информация", - "enter_additional_information": "Введите любую дополнительную информацию на своё усмотрение:", - "lbl_english": "English", - "finish": "Завершить", - "preferred_language": "Предпочитаемый язык общения", - "continueButton": "Продолжить", - "skip": "Пропустить", - "fill_in_information": "Заполните информацию:", - "enter_information": "Введите информацию...", - "msg_network_err": "Сетевая ошибка!", - "msg_something_went_wrong": "Что то пошло не так!", - "email_empty": "Необходимо ввести адрес электронной почты", - "email_invalid": "Некорректный адрес электронной почты", - "password_empty": "Пароль обязателен для заполнения", - "password_short": "В пароле должно быть минимум 6 символов", - "name_empty": "Имя пользователя обязательно для заполнения", - "name_short": "В имени пользователя должно быть минимум 6 символов", - "password_confirmation_mismatch": "Пароли не совпадают", - "first_name_empty": "Имя обязательно для заполнения", - "first_name_short": "В имени должен быть минимум 1 символ", - "last_name_empty": "Фамилия обязательна для заполнения", - "last_name_short": "В фамилии должен быть минимум 6 символов", - "circumference_empty": "Все замеры обязательны к заполнению", - "circumference_is_not_a_number": "Все замеры должны быть числами", - "pressure_empty": "Все показатели давления обязательны к заполнению", - "pressure_is_not_a_number": "Все показатели давления должны быть числами", - "no_account": "У меня нет аккаунта", - "authentificate": "Войти", - "user_authorization": "Аутентификация", - "loading": "Загрузка...", - "weight_chart": "График веса", - "circumferences_chart": "График обхватов", - "city_of_living": "Город проживания", - "bodymass_chart": "График ИМТ", - "chest": "Грудь", - "waist": "Талия", - "hips": "Бедра", - "exit": "Выйти", - "active": "Активен", - "archived": "В архиве", - "draft": "Черновик", - "my_blog": "Мой блог", - "language": "Язык", - "in_progress": "В процессе", - "add_measure": "Добавить замер", - "weight": "Вес", - "plans_complete": "Пройденные планы", - "my_settings": "Мои настройки", - "settings": "Настройки", - "profile": "Профиль", - "chat": "Чат", - "admin": "Админ", - "diary": "Дневник", - "save": "Сохранить", - "pcs": "шт", - "gr": "гр", - "grams": "гр" -} \ No newline at end of file diff --git a/lib/locator_service.dart b/lib/locator_service.dart index 43d3082d..c459e3fd 100644 --- a/lib/locator_service.dart +++ b/lib/locator_service.dart @@ -17,6 +17,9 @@ import 'package:eatvio_app/features/consultant/become_consultant/services/consul import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart'; import 'package:eatvio_app/features/diary/repositories/diary_repository.dart'; import 'package:eatvio_app/features/diary/services/diary_service.dart'; +import 'package:eatvio_app/features/diets/presentation/bloc/about_diet_cubit.dart'; +import 'package:eatvio_app/features/diets/repositories/diets_repository.dart'; +import 'package:eatvio_app/features/diets/services/diets_service.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart'; @@ -66,6 +69,7 @@ Future init() async { sl.registerFactory(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl())); sl.registerFactory(() => RegisterConsultantCubit(consultantRepository: sl())); sl.registerFactory(() => DiaryCubit(diaryRepository: sl(), glossaryRepository: sl())); + sl.registerFactory(() => AboutDietCubit(dietsRepository: sl())); // Repository sl.registerLazySingleton(() => ChatRepository(chatService: sl())); @@ -79,6 +83,7 @@ Future init() async { sl.registerLazySingleton(() => ConsultantRepository(consultantService: sl())); sl.registerLazySingleton(() => DiaryRepository(diaryService: sl())); sl.registerLazySingleton(() => GlossaryRepository(glossaryService: sl())); + sl.registerLazySingleton(() => DietsRepository(dietsService: sl())); // Services sl.registerLazySingleton(() => ChatService(sl())); @@ -92,7 +97,9 @@ Future init() async { sl.registerLazySingleton(() => ConsultantService(sl())); sl.registerLazySingleton(() => DiaryService(sl())); sl.registerLazySingleton(() => GlossaryService(sl())); - + sl.registerLazySingleton(() => DietsService(sl())); + + // External sl.registerLazySingleton( () => Dio( diff --git a/lib/main.dart b/lib/main.dart index 816adbe7..23b031a9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,6 +9,7 @@ import 'package:eatvio_app/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_portal/flutter_portal.dart'; import 'package:keyboard_dismisser/keyboard_dismisser.dart'; void main() async { @@ -26,18 +27,20 @@ class MyApp extends StatelessWidget { BlocProvider(create: (context) => sl()), BlocProvider(create: (context) => sl()), ], - child: KeyboardDismisser( - child: MaterialApp.router( - theme: ThemeHelper().themeData(), - routerConfig: AppRouter.appRouter, - debugShowCheckedModeBanner: false, - localizationsDelegates: const [ - S.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: S.delegate.supportedLocales, + child: Portal( + child: KeyboardDismisser( + child: MaterialApp.router( + theme: ThemeHelper().themeData(), + routerConfig: AppRouter.appRouter, + debugShowCheckedModeBanner: false, + localizationsDelegates: const [ + S.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: S.delegate.supportedLocales, + ), ), ), ); diff --git a/lib/old/users/subscribers_screen/bloc/subscribers_bloc.dart b/lib/old/users/subscribers_screen/bloc/subscribers_bloc.dart index aec562b0..ef52c077 100644 --- a/lib/old/users/subscribers_screen/bloc/subscribers_bloc.dart +++ b/lib/old/users/subscribers_screen/bloc/subscribers_bloc.dart @@ -44,11 +44,11 @@ class SubscribersBloc extends Bloc { emit((currentState).copyWith( subscribers: data, myProfileModelObj: currentState.myProfileModelObj, - )); + ),); } else { emit(SubscribersLoadedState( subscribers: data, - )); + ),); } } else { // Один или несколько ключевых полей отсутствуют, обрабатываем ошибку diff --git a/lib/old/users/subscribers_screen/subscribers_screen.dart b/lib/old/users/subscribers_screen/subscribers_screen.dart index adef5d67..efdc730c 100644 --- a/lib/old/users/subscribers_screen/subscribers_screen.dart +++ b/lib/old/users/subscribers_screen/subscribers_screen.dart @@ -20,7 +20,7 @@ class SubscribersScreen extends StatelessWidget { static Widget builder(BuildContext context) { return BlocProvider( create: (context) => SubscribersBloc(SubscribersState(myProfileModelObj: SubscribersModel()))..add(SubscribersInitialEvent()), - child: SubscribersScreen()); + child: SubscribersScreen(),); } Widget build(BuildContext context) { @@ -93,7 +93,7 @@ class SubscribersScreen extends StatelessWidget { Padding( padding: const EdgeInsets.only(right: 20.0), child: GestureDetector(child: Icon(Icons.create_outlined)), - ) + ), ], ), ), @@ -149,7 +149,7 @@ class SubscribersScreen extends StatelessWidget { ), ), ], - ) + ), ], ), ), @@ -212,6 +212,6 @@ class SubscribersScreen extends StatelessWidget { ), subtitle: Text(subscriber?['description'] ?? ''), trailing: GestureDetector(child: Icon(Icons.edit)), - )); + ),); } } diff --git a/lib/theme/custom_text_style.dart b/lib/theme/custom_text_style.dart index 7cf7b69e..59f10924 100644 --- a/lib/theme/custom_text_style.dart +++ b/lib/theme/custom_text_style.dart @@ -165,6 +165,8 @@ class CustomTextStyles { static get titleSmallSegoeUIOnPrimary => theme.textTheme.titleSmall!.segoeUI.copyWith( color: theme.colorScheme.onPrimary, ); + + } extension on TextStyle { @@ -176,3 +178,5 @@ extension on TextStyle { return copyWith(); } } + + diff --git a/lib/theme/theme_helper.dart b/lib/theme/theme_helper.dart index ab5c1d2d..d0a8635e 100644 --- a/lib/theme/theme_helper.dart +++ b/lib/theme/theme_helper.dart @@ -164,6 +164,14 @@ class PrimaryColors { Color get whiteA700 => Color(0XFFFFFFFF); Color get backgroundAvatar => Color(0xFFEBF4FF); + + Color get cardBackground => Color(0xFFF7FAFF); + + Color get dividerColor => Color(0xFFF1F2F6); + + Color get grey => Color(0xFF6D6F71); + + Color get lightGrey => Color(0xFFCECECE); } PrimaryColors get appTheme => ThemeHelper().themeColor();