Merge pull request 'chats have been updated to work with consultatons' (#255) from misha/eatvio_app:main into main

Reviewed-on: eatvio/eatvio_app#255
This commit is contained in:
mso7nueh
2025-03-14 13:04:42 +00:00
57 changed files with 3775 additions and 1565 deletions

View File

@@ -0,0 +1,4 @@
<svg width="68" height="69" viewBox="0 0 68 69" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M68 34.6406C68 53.2716 52.7777 68.375 34 68.375C15.2223 68.375 0 53.2716 0 34.6406C0 16.0096 15.2223 0.90625 34 0.90625C52.7777 0.90625 68 16.0096 68 34.6406Z" fill="white"/>
<path d="M31.6767 54.8812V26.6568H36.2667V54.8812H31.6767ZM31.11 20.2473V15.1309H36.7767V20.2473H31.11Z" fill="#2F80ED"/>
</svg>

After

Width:  |  Height:  |  Size: 409 B

View File

@@ -7,7 +7,6 @@ import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/prof
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_state.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart';
import 'package:eatvio_app/features/calendar/presentation/pages/calendar_screen.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart';
import 'package:eatvio_app/features/chat/presentation/pages/chat_screen.dart';
import 'package:eatvio_app/features/chat/presentation/pages/chats_screen.dart';
import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.dart';
@@ -242,12 +241,10 @@ class AppRouter {
path: '/chat-screen',
name: 'chatScreen',
builder: (context, state) {
final data = state.extra as Map<String, dynamic>;
final chatsCubit = data['chatsCubit'] as ChatsCubit;
final data = state.extra as Map<String, dynamic>;
final chatId = data['chatId'] as String;
return ChatScreen(
chatId: chatId,
chatsCubit: chatsCubit,
chatId: chatId,
);
},
),

View File

@@ -1,5 +1,5 @@
String baseUrl = 'https://eatvio.ru';
// String baseUrl = 'https://dev2.eatvio.com';
// String baseUrl = 'https://eatvio.ru';
String baseUrl = 'https://dev2.eatvio.com';
const List<int> durationValues = [7, 14, 21];
@@ -9,3 +9,4 @@ const List<String> planTypeValues = ['fit', 'slim', 'wellness', 'behavior'];
const newMessagesChannelName = 'private-new-chat-messages';
const newNotificationsChannelName = 'private-message-events';
const directChatChannelName = 'private-direct-chat';
const thresholdForPagination = 150;

View File

@@ -87,6 +87,7 @@ Future<void> init() async {
sl.registerLazySingleton<NotificationsCubit>(() => NotificationsCubit(wsManager: sl()));
sl.registerLazySingleton<FeedCubit>(() => FeedCubit(feedRepository: sl()));
sl.registerLazySingleton<ProfileAvatarCubit>(() => ProfileAvatarCubit(profileRepository: sl()));
sl.registerLazySingleton<ChatsCubit>(() => ChatsCubit(chatRepository: sl(), wsManager: sl(), profileRepository: sl()));
// Cubit factories
sl.registerFactory<RegistrationCubit>(() => RegistrationCubit(registrationRepository: sl()));
@@ -96,8 +97,7 @@ Future<void> init() async {
sl.registerFactory<ProfileCubit>(() => ProfileCubit(profileRepository: sl(), chatRepository: sl()));
sl.registerFactory<profilePlans.PlansCubit>(() => profilePlans.PlansCubit(plansRepository: sl()));
sl.registerFactory<DietsCubit>(() => DietsCubit(feedRepository: sl(), dietsRepository: sl()));
sl.registerFactory<CommentsCubit>(() => CommentsCubit(feedRepository: sl()));
sl.registerFactory<ChatsCubit>(() => ChatsCubit(chatRepository: sl(), wsManager: sl(), profileRepository: sl()));
sl.registerFactory<CommentsCubit>(() => CommentsCubit(feedRepository: sl()));
sl.registerFactory<PlansCubit>(() => PlansCubit(feedRepository: sl(), planRepository: sl()));
sl.registerFactory<SettingsCubit>(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl()));
sl.registerFactory<RegisterConsultantCubit>(() => RegisterConsultantCubit(consultantRepository: sl()));

View File

@@ -184,6 +184,7 @@ class PrimaryColors {
Color get dottedBorder => Color(0xFFE6E6E6);
Color get red => Color(0xFFCD4141);
Color get strongRed => Color(0xFFD01717);
Color get transparentRed => Color(0xFFCD4141).withValues(alpha: 0.75);

View File

@@ -0,0 +1,8 @@
import 'package:eatvio_app/core/const.dart';
import 'package:flutter/material.dart';
void loadNewContent(ScrollNotification notification, Future<void> Function() load) {
if (notification.metrics.pixels >= notification.metrics.maxScrollExtent - thresholdForPagination && !notification.metrics.outOfRange) {
load();
}
}

View File

@@ -0,0 +1,47 @@
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class ItemWidget extends StatelessWidget {
const ItemWidget({super.key, required this.text, this.reversed = false, this.iconSize = 20});
final String text;
final bool reversed;
final double iconSize;
@override
Widget build(BuildContext context) {
if (reversed) {
return Row(
children: [
SvgPicture.asset(
'assets/icons/ok.svg',
width: iconSize,
height: iconSize,
),
SizedBox(width: 8),
Expanded(
child: Text(
text,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
),
],
);
}
return Row(
children: [
Expanded(
child: Text(
text,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
),
SvgPicture.asset(
'assets/icons/ok.svg',
width: iconSize,
height: iconSize,
),
],
);
}
}

View File

@@ -1,10 +1,8 @@
import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/token_storage.dart';
import 'package:eatvio_app/core/utils/toasts.dart';
import 'package:eatvio_app/features/auth/presentation/bloc/auth_state.dart';
import 'package:eatvio_app/features/auth/repositories/auth_repository.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
@@ -15,7 +13,7 @@ class AuthCubit extends Cubit<AuthState> {
required this.authRepository,
}) : super(AuthState());
getTokenByCredentials(String email, String password) async {
Future<bool> getTokenByCredentials(String email, String password) async {
emit(AuthState.loading());
try {
final token = (await authRepository.getTokenByCredentials(
@@ -23,7 +21,6 @@ class AuthCubit extends Cubit<AuthState> {
password,
))
.token;
print(token);
emit(
AuthState.loaded(
token: token,
@@ -31,12 +28,13 @@ class AuthCubit extends Cubit<AuthState> {
);
showSuccessToast('Успешный вход');
await TokenStorage.saveTokenInStorage(token);
await sl<ProfileAvatarCubit>().getMyProfile();
AppRouter.navigatorKey.currentContext?.go('/my-profile-screen');
return true;
} catch (e) {
print(e);
showErrorToast('Неверный адрес электронной почты или пароль');
emit(AuthState.error(errorMessage: '$e'));
return false;
}
}
}

View File

@@ -6,8 +6,11 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/utils/image_constant.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/features/auth/presentation/bloc/auth_cubit.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
@@ -116,30 +119,30 @@ class _AuthScreenState extends State<AuthScreen> {
],
),
Spacer(),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Нажимая "Войти", Вы соглашаетесь с ',
Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
text: 'Нажимая "Войти", Вы соглашаетесь \n',
style: TextStyle(
fontSize: 12,
color: Colors.black,
),
),
GestureDetector(
onTap: () async {
await launchUrl(Uri.parse(baseUrl + '/oferta_bs.pdf'));
},
child: Text(
'Публичной офертой',
style: TextStyle(
fontSize: 12,
color: appTheme.blueA400,
decorationColor: appTheme.blueA400,
decoration: TextDecoration.underline,
children: [
TextSpan(text: 'c '),
TextSpan(
text: ' Публичной офертой',
style: TextStyle(
fontSize: 12,
color: appTheme.blueA400,
decorationColor: appTheme.blueA400,
decoration: TextDecoration.underline,
),
recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse(baseUrl + '/oferta_bs.pdf')),
),
),
],
),
],
),
),
const SizedBox(height: 16),
Container(
@@ -156,10 +159,16 @@ class _AuthScreenState extends State<AuthScreen> {
backgroundColor: state is AuthStateLoading ? Colors.black.withAlpha(50) : appTheme.blueA400,
onPressed: state is AuthStateLoading
? null
: () => authCubit.getTokenByCredentials(
: () async {
final isAuthenticated = await authCubit.getTokenByCredentials(
emailController.text,
passwordController.text,
),
);
if (isAuthenticated) {
context.read<ProfileAvatarCubit>().getMyProfile();
context.read<ChatsCubit>().getChatRoomsWithLoading();
}
},
label: state is AuthStateLoading
? CupertinoActivityIndicator()
: Text(

View File

@@ -1,5 +1,6 @@
import 'package:eatvio_app/features/chat/models/chat_room_settings_model.dart';
import 'package:eatvio_app/features/chat/models/chat_user_model.dart';
import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'chat_room_model.freezed.dart';
@@ -10,8 +11,23 @@ class ChatRoomModel with _$ChatRoomModel {
const factory ChatRoomModel({
required String id,
@JsonKey(name: 'chat_room_setting') required ChatRoomSettingsModel chatRoomSettings,
required Map<String, ChatUserModel> users,
required Users users,
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan) required ChosenPlanModel? chosenPlan,
}) = _ChatRoomModel;
factory ChatRoomModel.fromJson(Map<String, Object?> json) => _$ChatRoomModelFromJson(json);
}
@freezed
class Users with _$Users {
const factory Users({
@JsonKey(name: 'from_chat_user') required ChatUserModel sender,
@JsonKey(name: 'to_chat_user') required ChatUserModel receiver,
}) = _Users;
factory Users.fromJson(Map<String, Object?> json) => _$UsersFromJson(json);
}

View File

@@ -24,7 +24,9 @@ mixin _$ChatRoomModel {
@JsonKey(name: 'chat_room_setting')
ChatRoomSettingsModel get chatRoomSettings =>
throw _privateConstructorUsedError;
Map<String, ChatUserModel> get users => throw _privateConstructorUsedError;
Users get users => throw _privateConstructorUsedError;
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
ChosenPlanModel? get chosenPlan => throw _privateConstructorUsedError;
/// Serializes this ChatRoomModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@@ -46,9 +48,13 @@ abstract class $ChatRoomModelCopyWith<$Res> {
{String id,
@JsonKey(name: 'chat_room_setting')
ChatRoomSettingsModel chatRoomSettings,
Map<String, ChatUserModel> users});
Users users,
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
ChosenPlanModel? chosenPlan});
$ChatRoomSettingsModelCopyWith<$Res> get chatRoomSettings;
$UsersCopyWith<$Res> get users;
$ChosenPlanModelCopyWith<$Res>? get chosenPlan;
}
/// @nodoc
@@ -69,6 +75,7 @@ class _$ChatRoomModelCopyWithImpl<$Res, $Val extends ChatRoomModel>
Object? id = null,
Object? chatRoomSettings = null,
Object? users = null,
Object? chosenPlan = freezed,
}) {
return _then(_value.copyWith(
id: null == id
@@ -82,7 +89,11 @@ class _$ChatRoomModelCopyWithImpl<$Res, $Val extends ChatRoomModel>
users: null == users
? _value.users
: users // ignore: cast_nullable_to_non_nullable
as Map<String, ChatUserModel>,
as Users,
chosenPlan: freezed == chosenPlan
? _value.chosenPlan
: chosenPlan // ignore: cast_nullable_to_non_nullable
as ChosenPlanModel?,
) as $Val);
}
@@ -96,6 +107,30 @@ class _$ChatRoomModelCopyWithImpl<$Res, $Val extends ChatRoomModel>
return _then(_value.copyWith(chatRoomSettings: value) as $Val);
});
}
/// Create a copy of ChatRoomModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$UsersCopyWith<$Res> get users {
return $UsersCopyWith<$Res>(_value.users, (value) {
return _then(_value.copyWith(users: value) as $Val);
});
}
/// Create a copy of ChatRoomModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ChosenPlanModelCopyWith<$Res>? get chosenPlan {
if (_value.chosenPlan == null) {
return null;
}
return $ChosenPlanModelCopyWith<$Res>(_value.chosenPlan!, (value) {
return _then(_value.copyWith(chosenPlan: value) as $Val);
});
}
}
/// @nodoc
@@ -110,10 +145,16 @@ abstract class _$$ChatRoomModelImplCopyWith<$Res>
{String id,
@JsonKey(name: 'chat_room_setting')
ChatRoomSettingsModel chatRoomSettings,
Map<String, ChatUserModel> users});
Users users,
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
ChosenPlanModel? chosenPlan});
@override
$ChatRoomSettingsModelCopyWith<$Res> get chatRoomSettings;
@override
$UsersCopyWith<$Res> get users;
@override
$ChosenPlanModelCopyWith<$Res>? get chosenPlan;
}
/// @nodoc
@@ -132,6 +173,7 @@ class __$$ChatRoomModelImplCopyWithImpl<$Res>
Object? id = null,
Object? chatRoomSettings = null,
Object? users = null,
Object? chosenPlan = freezed,
}) {
return _then(_$ChatRoomModelImpl(
id: null == id
@@ -143,9 +185,13 @@ class __$$ChatRoomModelImplCopyWithImpl<$Res>
: chatRoomSettings // ignore: cast_nullable_to_non_nullable
as ChatRoomSettingsModel,
users: null == users
? _value._users
? _value.users
: users // ignore: cast_nullable_to_non_nullable
as Map<String, ChatUserModel>,
as Users,
chosenPlan: freezed == chosenPlan
? _value.chosenPlan
: chosenPlan // ignore: cast_nullable_to_non_nullable
as ChosenPlanModel?,
));
}
}
@@ -156,8 +202,9 @@ class _$ChatRoomModelImpl implements _ChatRoomModel {
const _$ChatRoomModelImpl(
{required this.id,
@JsonKey(name: 'chat_room_setting') required this.chatRoomSettings,
required final Map<String, ChatUserModel> users})
: _users = users;
required this.users,
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
required this.chosenPlan});
factory _$ChatRoomModelImpl.fromJson(Map<String, dynamic> json) =>
_$$ChatRoomModelImplFromJson(json);
@@ -167,17 +214,15 @@ class _$ChatRoomModelImpl implements _ChatRoomModel {
@override
@JsonKey(name: 'chat_room_setting')
final ChatRoomSettingsModel chatRoomSettings;
final Map<String, ChatUserModel> _users;
@override
Map<String, ChatUserModel> get users {
if (_users is EqualUnmodifiableMapView) return _users;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_users);
}
final Users users;
@override
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
final ChosenPlanModel? chosenPlan;
@override
String toString() {
return 'ChatRoomModel(id: $id, chatRoomSettings: $chatRoomSettings, users: $users)';
return 'ChatRoomModel(id: $id, chatRoomSettings: $chatRoomSettings, users: $users, chosenPlan: $chosenPlan)';
}
@override
@@ -188,13 +233,15 @@ class _$ChatRoomModelImpl implements _ChatRoomModel {
(identical(other.id, id) || other.id == id) &&
(identical(other.chatRoomSettings, chatRoomSettings) ||
other.chatRoomSettings == chatRoomSettings) &&
const DeepCollectionEquality().equals(other._users, _users));
(identical(other.users, users) || other.users == users) &&
(identical(other.chosenPlan, chosenPlan) ||
other.chosenPlan == chosenPlan));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, id, chatRoomSettings,
const DeepCollectionEquality().hash(_users));
int get hashCode =>
Object.hash(runtimeType, id, chatRoomSettings, users, chosenPlan);
/// Create a copy of ChatRoomModel
/// with the given fields replaced by the non-null parameter values.
@@ -217,7 +264,9 @@ abstract class _ChatRoomModel implements ChatRoomModel {
{required final String id,
@JsonKey(name: 'chat_room_setting')
required final ChatRoomSettingsModel chatRoomSettings,
required final Map<String, ChatUserModel> users}) = _$ChatRoomModelImpl;
required final Users users,
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
required final ChosenPlanModel? chosenPlan}) = _$ChatRoomModelImpl;
factory _ChatRoomModel.fromJson(Map<String, dynamic> json) =
_$ChatRoomModelImpl.fromJson;
@@ -228,7 +277,10 @@ abstract class _ChatRoomModel implements ChatRoomModel {
@JsonKey(name: 'chat_room_setting')
ChatRoomSettingsModel get chatRoomSettings;
@override
Map<String, ChatUserModel> get users;
Users get users;
@override
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
ChosenPlanModel? get chosenPlan;
/// Create a copy of ChatRoomModel
/// with the given fields replaced by the non-null parameter values.
@@ -237,3 +289,208 @@ abstract class _ChatRoomModel implements ChatRoomModel {
_$$ChatRoomModelImplCopyWith<_$ChatRoomModelImpl> get copyWith =>
throw _privateConstructorUsedError;
}
Users _$UsersFromJson(Map<String, dynamic> json) {
return _Users.fromJson(json);
}
/// @nodoc
mixin _$Users {
@JsonKey(name: 'from_chat_user')
ChatUserModel get sender => throw _privateConstructorUsedError;
@JsonKey(name: 'to_chat_user')
ChatUserModel get receiver => throw _privateConstructorUsedError;
/// Serializes this Users to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of Users
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$UsersCopyWith<Users> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $UsersCopyWith<$Res> {
factory $UsersCopyWith(Users value, $Res Function(Users) then) =
_$UsersCopyWithImpl<$Res, Users>;
@useResult
$Res call(
{@JsonKey(name: 'from_chat_user') ChatUserModel sender,
@JsonKey(name: 'to_chat_user') ChatUserModel receiver});
$ChatUserModelCopyWith<$Res> get sender;
$ChatUserModelCopyWith<$Res> get receiver;
}
/// @nodoc
class _$UsersCopyWithImpl<$Res, $Val extends Users>
implements $UsersCopyWith<$Res> {
_$UsersCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of Users
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? sender = null,
Object? receiver = null,
}) {
return _then(_value.copyWith(
sender: null == sender
? _value.sender
: sender // ignore: cast_nullable_to_non_nullable
as ChatUserModel,
receiver: null == receiver
? _value.receiver
: receiver // ignore: cast_nullable_to_non_nullable
as ChatUserModel,
) as $Val);
}
/// Create a copy of Users
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ChatUserModelCopyWith<$Res> get sender {
return $ChatUserModelCopyWith<$Res>(_value.sender, (value) {
return _then(_value.copyWith(sender: value) as $Val);
});
}
/// Create a copy of Users
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ChatUserModelCopyWith<$Res> get receiver {
return $ChatUserModelCopyWith<$Res>(_value.receiver, (value) {
return _then(_value.copyWith(receiver: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$UsersImplCopyWith<$Res> implements $UsersCopyWith<$Res> {
factory _$$UsersImplCopyWith(
_$UsersImpl value, $Res Function(_$UsersImpl) then) =
__$$UsersImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{@JsonKey(name: 'from_chat_user') ChatUserModel sender,
@JsonKey(name: 'to_chat_user') ChatUserModel receiver});
@override
$ChatUserModelCopyWith<$Res> get sender;
@override
$ChatUserModelCopyWith<$Res> get receiver;
}
/// @nodoc
class __$$UsersImplCopyWithImpl<$Res>
extends _$UsersCopyWithImpl<$Res, _$UsersImpl>
implements _$$UsersImplCopyWith<$Res> {
__$$UsersImplCopyWithImpl(
_$UsersImpl _value, $Res Function(_$UsersImpl) _then)
: super(_value, _then);
/// Create a copy of Users
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? sender = null,
Object? receiver = null,
}) {
return _then(_$UsersImpl(
sender: null == sender
? _value.sender
: sender // ignore: cast_nullable_to_non_nullable
as ChatUserModel,
receiver: null == receiver
? _value.receiver
: receiver // ignore: cast_nullable_to_non_nullable
as ChatUserModel,
));
}
}
/// @nodoc
@JsonSerializable()
class _$UsersImpl implements _Users {
const _$UsersImpl(
{@JsonKey(name: 'from_chat_user') required this.sender,
@JsonKey(name: 'to_chat_user') required this.receiver});
factory _$UsersImpl.fromJson(Map<String, dynamic> json) =>
_$$UsersImplFromJson(json);
@override
@JsonKey(name: 'from_chat_user')
final ChatUserModel sender;
@override
@JsonKey(name: 'to_chat_user')
final ChatUserModel receiver;
@override
String toString() {
return 'Users(sender: $sender, receiver: $receiver)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$UsersImpl &&
(identical(other.sender, sender) || other.sender == sender) &&
(identical(other.receiver, receiver) ||
other.receiver == receiver));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, sender, receiver);
/// Create a copy of Users
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$UsersImplCopyWith<_$UsersImpl> get copyWith =>
__$$UsersImplCopyWithImpl<_$UsersImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$UsersImplToJson(
this,
);
}
}
abstract class _Users implements Users {
const factory _Users(
{@JsonKey(name: 'from_chat_user') required final ChatUserModel sender,
@JsonKey(name: 'to_chat_user')
required final ChatUserModel receiver}) = _$UsersImpl;
factory _Users.fromJson(Map<String, dynamic> json) = _$UsersImpl.fromJson;
@override
@JsonKey(name: 'from_chat_user')
ChatUserModel get sender;
@override
@JsonKey(name: 'to_chat_user')
ChatUserModel get receiver;
/// Create a copy of Users
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$UsersImplCopyWith<_$UsersImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -11,10 +11,8 @@ _$ChatRoomModelImpl _$$ChatRoomModelImplFromJson(Map<String, dynamic> json) =>
id: json['id'] as String,
chatRoomSettings: ChatRoomSettingsModel.fromJson(
json['chat_room_setting'] as Map<String, dynamic>),
users: (json['users'] as Map<String, dynamic>).map(
(k, e) =>
MapEntry(k, ChatUserModel.fromJson(e as Map<String, dynamic>)),
),
users: Users.fromJson(json['users'] as Map<String, dynamic>),
chosenPlan: parseChosenPlan(json['chosen_plan']),
);
Map<String, dynamic> _$$ChatRoomModelImplToJson(_$ChatRoomModelImpl instance) =>
@@ -22,4 +20,18 @@ Map<String, dynamic> _$$ChatRoomModelImplToJson(_$ChatRoomModelImpl instance) =>
'id': instance.id,
'chat_room_setting': instance.chatRoomSettings,
'users': instance.users,
'chosen_plan': instance.chosenPlan,
};
_$UsersImpl _$$UsersImplFromJson(Map<String, dynamic> json) => _$UsersImpl(
sender: ChatUserModel.fromJson(
json['from_chat_user'] as Map<String, dynamic>),
receiver:
ChatUserModel.fromJson(json['to_chat_user'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$UsersImplToJson(_$UsersImpl instance) =>
<String, dynamic>{
'from_chat_user': instance.sender,
'to_chat_user': instance.receiver,
};

View File

@@ -1,16 +1,29 @@
import 'package:eatvio_app/features/chat/models/chat_message_model.dart';
import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'chat_room_preview_model.freezed.dart';
part 'chat_room_preview_model.g.dart';
part 'chat_room_pagination_model.freezed.dart';
part 'chat_room_pagination_model.g.dart';
@freezed
class ChatRoomPreviewModel with _$ChatRoomPreviewModel {
const factory ChatRoomPreviewModel({
@JsonKey(name: "chatRooms") required List<ChatRoom> chatRooms,
class ChatRoomPaginationModel with _$ChatRoomPaginationModel {
const factory ChatRoomPaginationModel({
@JsonKey(name: "chatRooms") required ChatRooms chatRooms,
@JsonKey(name: "number_of_unread_chat_rooms") required int unreadChatsCount,
}) = _ChatRoomPreviewModel;
factory ChatRoomPreviewModel.fromJson(Map<String, dynamic> json) => _$ChatRoomPreviewModelFromJson(json);
factory ChatRoomPaginationModel.fromJson(Map<String, dynamic> json) => _$ChatRoomPaginationModelFromJson(json);
}
@freezed
class ChatRooms with _$ChatRooms {
const factory ChatRooms({
@JsonKey(name: "data") required List<ChatRoom> data,
@JsonKey(name: "last_page") required int lastPage,
@JsonKey(name: "current_page") required int currentPage,
}) = _ChatRooms;
factory ChatRooms.fromJson(Map<String, dynamic> json) => _$ChatRoomsFromJson(json);
}
@freezed
@@ -21,6 +34,7 @@ class ChatRoom with _$ChatRoom {
@JsonKey(name: "last_message", fromJson: parseLastMessage) required ChatMessageModel? lastMessage,
@JsonKey(name: "users") required Users users,
@JsonKey(name: "unread_messages_count") required int unreadMessagesCount,
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan) required ChosenPlanModel? chosenPlan,
}) = _ChatRoom;
factory ChatRoom.fromJson(Map<String, dynamic> json) => _$ChatRoomFromJson(json);

View File

@@ -3,7 +3,7 @@
// 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 'chat_room_preview_model.dart';
part of 'chat_room_pagination_model.dart';
// **************************************************************************
// FreezedGenerator
@@ -14,92 +14,125 @@ T _$identity<T>(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');
ChatRoomPreviewModel _$ChatRoomPreviewModelFromJson(Map<String, dynamic> json) {
ChatRoomPaginationModel _$ChatRoomPaginationModelFromJson(
Map<String, dynamic> json) {
return _ChatRoomPreviewModel.fromJson(json);
}
/// @nodoc
mixin _$ChatRoomPreviewModel {
mixin _$ChatRoomPaginationModel {
@JsonKey(name: "chatRooms")
List<ChatRoom> get chatRooms => throw _privateConstructorUsedError;
ChatRooms get chatRooms => throw _privateConstructorUsedError;
@JsonKey(name: "number_of_unread_chat_rooms")
int get unreadChatsCount => throw _privateConstructorUsedError;
/// Serializes this ChatRoomPreviewModel to a JSON map.
/// Serializes this ChatRoomPaginationModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of ChatRoomPreviewModel
/// Create a copy of ChatRoomPaginationModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ChatRoomPreviewModelCopyWith<ChatRoomPreviewModel> get copyWith =>
$ChatRoomPaginationModelCopyWith<ChatRoomPaginationModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ChatRoomPreviewModelCopyWith<$Res> {
factory $ChatRoomPreviewModelCopyWith(ChatRoomPreviewModel value,
$Res Function(ChatRoomPreviewModel) then) =
_$ChatRoomPreviewModelCopyWithImpl<$Res, ChatRoomPreviewModel>;
abstract class $ChatRoomPaginationModelCopyWith<$Res> {
factory $ChatRoomPaginationModelCopyWith(ChatRoomPaginationModel value,
$Res Function(ChatRoomPaginationModel) then) =
_$ChatRoomPaginationModelCopyWithImpl<$Res, ChatRoomPaginationModel>;
@useResult
$Res call({@JsonKey(name: "chatRooms") List<ChatRoom> chatRooms});
$Res call(
{@JsonKey(name: "chatRooms") ChatRooms chatRooms,
@JsonKey(name: "number_of_unread_chat_rooms") int unreadChatsCount});
$ChatRoomsCopyWith<$Res> get chatRooms;
}
/// @nodoc
class _$ChatRoomPreviewModelCopyWithImpl<$Res,
$Val extends ChatRoomPreviewModel>
implements $ChatRoomPreviewModelCopyWith<$Res> {
_$ChatRoomPreviewModelCopyWithImpl(this._value, this._then);
class _$ChatRoomPaginationModelCopyWithImpl<$Res,
$Val extends ChatRoomPaginationModel>
implements $ChatRoomPaginationModelCopyWith<$Res> {
_$ChatRoomPaginationModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of ChatRoomPreviewModel
/// Create a copy of ChatRoomPaginationModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? chatRooms = null,
Object? unreadChatsCount = null,
}) {
return _then(_value.copyWith(
chatRooms: null == chatRooms
? _value.chatRooms
: chatRooms // ignore: cast_nullable_to_non_nullable
as List<ChatRoom>,
as ChatRooms,
unreadChatsCount: null == unreadChatsCount
? _value.unreadChatsCount
: unreadChatsCount // ignore: cast_nullable_to_non_nullable
as int,
) as $Val);
}
/// Create a copy of ChatRoomPaginationModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ChatRoomsCopyWith<$Res> get chatRooms {
return $ChatRoomsCopyWith<$Res>(_value.chatRooms, (value) {
return _then(_value.copyWith(chatRooms: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$ChatRoomPreviewModelImplCopyWith<$Res>
implements $ChatRoomPreviewModelCopyWith<$Res> {
implements $ChatRoomPaginationModelCopyWith<$Res> {
factory _$$ChatRoomPreviewModelImplCopyWith(_$ChatRoomPreviewModelImpl value,
$Res Function(_$ChatRoomPreviewModelImpl) then) =
__$$ChatRoomPreviewModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({@JsonKey(name: "chatRooms") List<ChatRoom> chatRooms});
$Res call(
{@JsonKey(name: "chatRooms") ChatRooms chatRooms,
@JsonKey(name: "number_of_unread_chat_rooms") int unreadChatsCount});
@override
$ChatRoomsCopyWith<$Res> get chatRooms;
}
/// @nodoc
class __$$ChatRoomPreviewModelImplCopyWithImpl<$Res>
extends _$ChatRoomPreviewModelCopyWithImpl<$Res, _$ChatRoomPreviewModelImpl>
extends _$ChatRoomPaginationModelCopyWithImpl<$Res,
_$ChatRoomPreviewModelImpl>
implements _$$ChatRoomPreviewModelImplCopyWith<$Res> {
__$$ChatRoomPreviewModelImplCopyWithImpl(_$ChatRoomPreviewModelImpl _value,
$Res Function(_$ChatRoomPreviewModelImpl) _then)
: super(_value, _then);
/// Create a copy of ChatRoomPreviewModel
/// Create a copy of ChatRoomPaginationModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? chatRooms = null,
Object? unreadChatsCount = null,
}) {
return _then(_$ChatRoomPreviewModelImpl(
chatRooms: null == chatRooms
? _value._chatRooms
? _value.chatRooms
: chatRooms // ignore: cast_nullable_to_non_nullable
as List<ChatRoom>,
as ChatRooms,
unreadChatsCount: null == unreadChatsCount
? _value.unreadChatsCount
: unreadChatsCount // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
@@ -108,24 +141,23 @@ class __$$ChatRoomPreviewModelImplCopyWithImpl<$Res>
@JsonSerializable()
class _$ChatRoomPreviewModelImpl implements _ChatRoomPreviewModel {
const _$ChatRoomPreviewModelImpl(
{@JsonKey(name: "chatRooms") required final List<ChatRoom> chatRooms})
: _chatRooms = chatRooms;
{@JsonKey(name: "chatRooms") required this.chatRooms,
@JsonKey(name: "number_of_unread_chat_rooms")
required this.unreadChatsCount});
factory _$ChatRoomPreviewModelImpl.fromJson(Map<String, dynamic> json) =>
_$$ChatRoomPreviewModelImplFromJson(json);
final List<ChatRoom> _chatRooms;
@override
@JsonKey(name: "chatRooms")
List<ChatRoom> get chatRooms {
if (_chatRooms is EqualUnmodifiableListView) return _chatRooms;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_chatRooms);
}
final ChatRooms chatRooms;
@override
@JsonKey(name: "number_of_unread_chat_rooms")
final int unreadChatsCount;
@override
String toString() {
return 'ChatRoomPreviewModel(chatRooms: $chatRooms)';
return 'ChatRoomPaginationModel(chatRooms: $chatRooms, unreadChatsCount: $unreadChatsCount)';
}
@override
@@ -133,16 +165,17 @@ class _$ChatRoomPreviewModelImpl implements _ChatRoomPreviewModel {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ChatRoomPreviewModelImpl &&
const DeepCollectionEquality()
.equals(other._chatRooms, _chatRooms));
(identical(other.chatRooms, chatRooms) ||
other.chatRooms == chatRooms) &&
(identical(other.unreadChatsCount, unreadChatsCount) ||
other.unreadChatsCount == unreadChatsCount));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(runtimeType, const DeepCollectionEquality().hash(_chatRooms));
int get hashCode => Object.hash(runtimeType, chatRooms, unreadChatsCount);
/// Create a copy of ChatRoomPreviewModel
/// Create a copy of ChatRoomPaginationModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@@ -160,19 +193,23 @@ class _$ChatRoomPreviewModelImpl implements _ChatRoomPreviewModel {
}
}
abstract class _ChatRoomPreviewModel implements ChatRoomPreviewModel {
abstract class _ChatRoomPreviewModel implements ChatRoomPaginationModel {
const factory _ChatRoomPreviewModel(
{@JsonKey(name: "chatRooms")
required final List<ChatRoom> chatRooms}) = _$ChatRoomPreviewModelImpl;
{@JsonKey(name: "chatRooms") required final ChatRooms chatRooms,
@JsonKey(name: "number_of_unread_chat_rooms")
required final int unreadChatsCount}) = _$ChatRoomPreviewModelImpl;
factory _ChatRoomPreviewModel.fromJson(Map<String, dynamic> json) =
_$ChatRoomPreviewModelImpl.fromJson;
@override
@JsonKey(name: "chatRooms")
List<ChatRoom> get chatRooms;
ChatRooms get chatRooms;
@override
@JsonKey(name: "number_of_unread_chat_rooms")
int get unreadChatsCount;
/// Create a copy of ChatRoomPreviewModel
/// Create a copy of ChatRoomPaginationModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -180,6 +217,218 @@ abstract class _ChatRoomPreviewModel implements ChatRoomPreviewModel {
get copyWith => throw _privateConstructorUsedError;
}
ChatRooms _$ChatRoomsFromJson(Map<String, dynamic> json) {
return _ChatRooms.fromJson(json);
}
/// @nodoc
mixin _$ChatRooms {
@JsonKey(name: "data")
List<ChatRoom> get data => throw _privateConstructorUsedError;
@JsonKey(name: "last_page")
int get lastPage => throw _privateConstructorUsedError;
@JsonKey(name: "current_page")
int get currentPage => throw _privateConstructorUsedError;
/// Serializes this ChatRooms to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of ChatRooms
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ChatRoomsCopyWith<ChatRooms> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ChatRoomsCopyWith<$Res> {
factory $ChatRoomsCopyWith(ChatRooms value, $Res Function(ChatRooms) then) =
_$ChatRoomsCopyWithImpl<$Res, ChatRooms>;
@useResult
$Res call(
{@JsonKey(name: "data") List<ChatRoom> data,
@JsonKey(name: "last_page") int lastPage,
@JsonKey(name: "current_page") int currentPage});
}
/// @nodoc
class _$ChatRoomsCopyWithImpl<$Res, $Val extends ChatRooms>
implements $ChatRoomsCopyWith<$Res> {
_$ChatRoomsCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of ChatRooms
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? data = null,
Object? lastPage = null,
Object? currentPage = null,
}) {
return _then(_value.copyWith(
data: null == data
? _value.data
: data // ignore: cast_nullable_to_non_nullable
as List<ChatRoom>,
lastPage: null == lastPage
? _value.lastPage
: lastPage // ignore: cast_nullable_to_non_nullable
as int,
currentPage: null == currentPage
? _value.currentPage
: currentPage // ignore: cast_nullable_to_non_nullable
as int,
) as $Val);
}
}
/// @nodoc
abstract class _$$ChatRoomsImplCopyWith<$Res>
implements $ChatRoomsCopyWith<$Res> {
factory _$$ChatRoomsImplCopyWith(
_$ChatRoomsImpl value, $Res Function(_$ChatRoomsImpl) then) =
__$$ChatRoomsImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{@JsonKey(name: "data") List<ChatRoom> data,
@JsonKey(name: "last_page") int lastPage,
@JsonKey(name: "current_page") int currentPage});
}
/// @nodoc
class __$$ChatRoomsImplCopyWithImpl<$Res>
extends _$ChatRoomsCopyWithImpl<$Res, _$ChatRoomsImpl>
implements _$$ChatRoomsImplCopyWith<$Res> {
__$$ChatRoomsImplCopyWithImpl(
_$ChatRoomsImpl _value, $Res Function(_$ChatRoomsImpl) _then)
: super(_value, _then);
/// Create a copy of ChatRooms
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? data = null,
Object? lastPage = null,
Object? currentPage = null,
}) {
return _then(_$ChatRoomsImpl(
data: null == data
? _value._data
: data // ignore: cast_nullable_to_non_nullable
as List<ChatRoom>,
lastPage: null == lastPage
? _value.lastPage
: lastPage // ignore: cast_nullable_to_non_nullable
as int,
currentPage: null == currentPage
? _value.currentPage
: currentPage // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// @nodoc
@JsonSerializable()
class _$ChatRoomsImpl implements _ChatRooms {
const _$ChatRoomsImpl(
{@JsonKey(name: "data") required final List<ChatRoom> data,
@JsonKey(name: "last_page") required this.lastPage,
@JsonKey(name: "current_page") required this.currentPage})
: _data = data;
factory _$ChatRoomsImpl.fromJson(Map<String, dynamic> json) =>
_$$ChatRoomsImplFromJson(json);
final List<ChatRoom> _data;
@override
@JsonKey(name: "data")
List<ChatRoom> get data {
if (_data is EqualUnmodifiableListView) return _data;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_data);
}
@override
@JsonKey(name: "last_page")
final int lastPage;
@override
@JsonKey(name: "current_page")
final int currentPage;
@override
String toString() {
return 'ChatRooms(data: $data, lastPage: $lastPage, currentPage: $currentPage)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ChatRoomsImpl &&
const DeepCollectionEquality().equals(other._data, _data) &&
(identical(other.lastPage, lastPage) ||
other.lastPage == lastPage) &&
(identical(other.currentPage, currentPage) ||
other.currentPage == currentPage));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,
const DeepCollectionEquality().hash(_data), lastPage, currentPage);
/// Create a copy of ChatRooms
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ChatRoomsImplCopyWith<_$ChatRoomsImpl> get copyWith =>
__$$ChatRoomsImplCopyWithImpl<_$ChatRoomsImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$ChatRoomsImplToJson(
this,
);
}
}
abstract class _ChatRooms implements ChatRooms {
const factory _ChatRooms(
{@JsonKey(name: "data") required final List<ChatRoom> data,
@JsonKey(name: "last_page") required final int lastPage,
@JsonKey(name: "current_page") required final int currentPage}) =
_$ChatRoomsImpl;
factory _ChatRooms.fromJson(Map<String, dynamic> json) =
_$ChatRoomsImpl.fromJson;
@override
@JsonKey(name: "data")
List<ChatRoom> get data;
@override
@JsonKey(name: "last_page")
int get lastPage;
@override
@JsonKey(name: "current_page")
int get currentPage;
/// Create a copy of ChatRooms
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ChatRoomsImplCopyWith<_$ChatRoomsImpl> get copyWith =>
throw _privateConstructorUsedError;
}
ChatRoom _$ChatRoomFromJson(Map<String, dynamic> json) {
return _ChatRoom.fromJson(json);
}
@@ -196,6 +445,8 @@ mixin _$ChatRoom {
Users get users => throw _privateConstructorUsedError;
@JsonKey(name: "unread_messages_count")
int get unreadMessagesCount => throw _privateConstructorUsedError;
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
ChosenPlanModel? get chosenPlan => throw _privateConstructorUsedError;
/// Serializes this ChatRoom to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@@ -218,10 +469,13 @@ abstract class $ChatRoomCopyWith<$Res> {
@JsonKey(name: "last_message", fromJson: parseLastMessage)
ChatMessageModel? lastMessage,
@JsonKey(name: "users") Users users,
@JsonKey(name: "unread_messages_count") int unreadMessagesCount});
@JsonKey(name: "unread_messages_count") int unreadMessagesCount,
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
ChosenPlanModel? chosenPlan});
$ChatMessageModelCopyWith<$Res>? get lastMessage;
$UsersCopyWith<$Res> get users;
$ChosenPlanModelCopyWith<$Res>? get chosenPlan;
}
/// @nodoc
@@ -244,6 +498,7 @@ class _$ChatRoomCopyWithImpl<$Res, $Val extends ChatRoom>
Object? lastMessage = freezed,
Object? users = null,
Object? unreadMessagesCount = null,
Object? chosenPlan = freezed,
}) {
return _then(_value.copyWith(
id: null == id
@@ -266,6 +521,10 @@ class _$ChatRoomCopyWithImpl<$Res, $Val extends ChatRoom>
? _value.unreadMessagesCount
: unreadMessagesCount // ignore: cast_nullable_to_non_nullable
as int,
chosenPlan: freezed == chosenPlan
? _value.chosenPlan
: chosenPlan // ignore: cast_nullable_to_non_nullable
as ChosenPlanModel?,
) as $Val);
}
@@ -292,6 +551,20 @@ class _$ChatRoomCopyWithImpl<$Res, $Val extends ChatRoom>
return _then(_value.copyWith(users: value) as $Val);
});
}
/// Create a copy of ChatRoom
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ChosenPlanModelCopyWith<$Res>? get chosenPlan {
if (_value.chosenPlan == null) {
return null;
}
return $ChosenPlanModelCopyWith<$Res>(_value.chosenPlan!, (value) {
return _then(_value.copyWith(chosenPlan: value) as $Val);
});
}
}
/// @nodoc
@@ -308,12 +581,16 @@ abstract class _$$ChatRoomImplCopyWith<$Res>
@JsonKey(name: "last_message", fromJson: parseLastMessage)
ChatMessageModel? lastMessage,
@JsonKey(name: "users") Users users,
@JsonKey(name: "unread_messages_count") int unreadMessagesCount});
@JsonKey(name: "unread_messages_count") int unreadMessagesCount,
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
ChosenPlanModel? chosenPlan});
@override
$ChatMessageModelCopyWith<$Res>? get lastMessage;
@override
$UsersCopyWith<$Res> get users;
@override
$ChosenPlanModelCopyWith<$Res>? get chosenPlan;
}
/// @nodoc
@@ -334,6 +611,7 @@ class __$$ChatRoomImplCopyWithImpl<$Res>
Object? lastMessage = freezed,
Object? users = null,
Object? unreadMessagesCount = null,
Object? chosenPlan = freezed,
}) {
return _then(_$ChatRoomImpl(
id: null == id
@@ -356,6 +634,10 @@ class __$$ChatRoomImplCopyWithImpl<$Res>
? _value.unreadMessagesCount
: unreadMessagesCount // ignore: cast_nullable_to_non_nullable
as int,
chosenPlan: freezed == chosenPlan
? _value.chosenPlan
: chosenPlan // ignore: cast_nullable_to_non_nullable
as ChosenPlanModel?,
));
}
}
@@ -369,8 +651,9 @@ class _$ChatRoomImpl implements _ChatRoom {
@JsonKey(name: "last_message", fromJson: parseLastMessage)
required this.lastMessage,
@JsonKey(name: "users") required this.users,
@JsonKey(name: "unread_messages_count")
required this.unreadMessagesCount});
@JsonKey(name: "unread_messages_count") required this.unreadMessagesCount,
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
required this.chosenPlan});
factory _$ChatRoomImpl.fromJson(Map<String, dynamic> json) =>
_$$ChatRoomImplFromJson(json);
@@ -390,10 +673,13 @@ class _$ChatRoomImpl implements _ChatRoom {
@override
@JsonKey(name: "unread_messages_count")
final int unreadMessagesCount;
@override
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
final ChosenPlanModel? chosenPlan;
@override
String toString() {
return 'ChatRoom(id: $id, roomName: $roomName, lastMessage: $lastMessage, users: $users, unreadMessagesCount: $unreadMessagesCount)';
return 'ChatRoom(id: $id, roomName: $roomName, lastMessage: $lastMessage, users: $users, unreadMessagesCount: $unreadMessagesCount, chosenPlan: $chosenPlan)';
}
@override
@@ -408,13 +694,15 @@ class _$ChatRoomImpl implements _ChatRoom {
other.lastMessage == lastMessage) &&
(identical(other.users, users) || other.users == users) &&
(identical(other.unreadMessagesCount, unreadMessagesCount) ||
other.unreadMessagesCount == unreadMessagesCount));
other.unreadMessagesCount == unreadMessagesCount) &&
(identical(other.chosenPlan, chosenPlan) ||
other.chosenPlan == chosenPlan));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType, id, roomName, lastMessage, users, unreadMessagesCount);
int get hashCode => Object.hash(runtimeType, id, roomName, lastMessage, users,
unreadMessagesCount, chosenPlan);
/// Create a copy of ChatRoom
/// with the given fields replaced by the non-null parameter values.
@@ -440,7 +728,9 @@ abstract class _ChatRoom implements ChatRoom {
required final ChatMessageModel? lastMessage,
@JsonKey(name: "users") required final Users users,
@JsonKey(name: "unread_messages_count")
required final int unreadMessagesCount}) = _$ChatRoomImpl;
required final int unreadMessagesCount,
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
required final ChosenPlanModel? chosenPlan}) = _$ChatRoomImpl;
factory _ChatRoom.fromJson(Map<String, dynamic> json) =
_$ChatRoomImpl.fromJson;
@@ -460,6 +750,9 @@ abstract class _ChatRoom implements ChatRoom {
@override
@JsonKey(name: "unread_messages_count")
int get unreadMessagesCount;
@override
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
ChosenPlanModel? get chosenPlan;
/// Create a copy of ChatRoom
/// with the given fields replaced by the non-null parameter values.

View File

@@ -1,6 +1,6 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'chat_room_preview_model.dart';
part of 'chat_room_pagination_model.dart';
// **************************************************************************
// JsonSerializableGenerator
@@ -9,15 +9,31 @@ part of 'chat_room_preview_model.dart';
_$ChatRoomPreviewModelImpl _$$ChatRoomPreviewModelImplFromJson(
Map<String, dynamic> json) =>
_$ChatRoomPreviewModelImpl(
chatRooms: (json['chatRooms'] as List<dynamic>)
.map((e) => ChatRoom.fromJson(e as Map<String, dynamic>))
.toList(),
chatRooms: ChatRooms.fromJson(json['chatRooms'] as Map<String, dynamic>),
unreadChatsCount: (json['number_of_unread_chat_rooms'] as num).toInt(),
);
Map<String, dynamic> _$$ChatRoomPreviewModelImplToJson(
_$ChatRoomPreviewModelImpl instance) =>
<String, dynamic>{
'chatRooms': instance.chatRooms,
'number_of_unread_chat_rooms': instance.unreadChatsCount,
};
_$ChatRoomsImpl _$$ChatRoomsImplFromJson(Map<String, dynamic> json) =>
_$ChatRoomsImpl(
data: (json['data'] as List<dynamic>)
.map((e) => ChatRoom.fromJson(e as Map<String, dynamic>))
.toList(),
lastPage: (json['last_page'] as num).toInt(),
currentPage: (json['current_page'] as num).toInt(),
);
Map<String, dynamic> _$$ChatRoomsImplToJson(_$ChatRoomsImpl instance) =>
<String, dynamic>{
'data': instance.data,
'last_page': instance.lastPage,
'current_page': instance.currentPage,
};
_$ChatRoomImpl _$$ChatRoomImplFromJson(Map<String, dynamic> json) =>
@@ -27,6 +43,7 @@ _$ChatRoomImpl _$$ChatRoomImplFromJson(Map<String, dynamic> json) =>
lastMessage: parseLastMessage(json['last_message']),
users: Users.fromJson(json['users'] as Map<String, dynamic>),
unreadMessagesCount: (json['unread_messages_count'] as num).toInt(),
chosenPlan: parseChosenPlan(json['chosen_plan']),
);
Map<String, dynamic> _$$ChatRoomImplToJson(_$ChatRoomImpl instance) =>
@@ -36,6 +53,7 @@ Map<String, dynamic> _$$ChatRoomImplToJson(_$ChatRoomImpl instance) =>
'last_message': instance.lastMessage,
'users': instance.users,
'unread_messages_count': instance.unreadMessagesCount,
'chosen_plan': instance.chosenPlan,
};
_$UsersImpl _$$UsersImplFromJson(Map<String, dynamic> json) => _$UsersImpl(

View File

@@ -86,6 +86,18 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
}
}
Future<void> cancelChosenPlan({required String chatId, required int planId}) async {
try {
await chatRepository.cancelChosenPlan(id: planId);
final chatRoom = await chatRepository.getChatRoom(chatId);
emit(
(state as ChatRoomStateLoaded).copyWith(chatRoom: chatRoom),
);
} catch (e) {
emit(ChatRoomState.error(errorMessage: 'Ошибка получеия данных'));
}
}
Future<void> _subscribeToChatChannel({
required String chatId,
}) async {
@@ -93,7 +105,7 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
if (client == null) return;
await wsManager.initPrivateDirectChatChannel(chatId);
wsManager.subscribeDirectChatChannel();
wsManager.directChatStream?.listen((event) {
messagesSubscription = wsManager.directChatStream?.listen((event) {
AudioPlayer().play((AssetSource('audio/recive_message.wav')));
final message = ChatMessageModel.fromJson(jsonDecode(event.data)['message']);
List<ChatMessageModel> messagesList = [
@@ -111,7 +123,7 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
);
});
wsManager.clientTypingStream?.listen((event) {
typingSubscription = wsManager.clientTypingStream?.listen((event) {
emit(
ChatRoomState.loaded(
chatRoom: (state as ChatRoomStateLoaded).chatRoom,
@@ -180,6 +192,8 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
@override
Future<void> close() async {
wsManager.unsubscribeDirectChatChannel();
messagesSubscription?.cancel();
typingSubscription?.cancel();
super.close();
}
}

View File

@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'package:eatvio_app/core/enums/base_state.dart';
import 'package:eatvio_app/core/websockets/ws_manager.dart';
@@ -30,21 +31,24 @@ class ChatsCubit extends Cubit<ChatsState> {
});
}
String? _query;
int page = 1;
late int lastPage;
Future<void> getChatRoomsWithLoading({String? query}) async {
page = 1;
_query = query;
emit(state.copyWith(state: BaseState.loading));
try {
final chatRooms = await chatRepository.getChatRoomPreviews(query: query);
final unreadChats = chatRooms.where(
(element) => element.unreadMessagesCount > 0,
);
final chatRoomsPaginationModel = await chatRepository.getChatRooms(query: query);
final unreadChats = chatRoomsPaginationModel.unreadChatsCount;
lastPage = chatRoomsPaginationModel.chatRooms.lastPage + 1;
emit(
state.copyWith(
chatRooms: chatRooms,
chatsWithUnreadMessagesCount: unreadChats.length,
chatRooms: chatRoomsPaginationModel.chatRooms.data,
chatsWithUnreadMessagesCount: unreadChats,
state: BaseState.success,
),
);
page++;
} catch (e) {
emit(
state.copyWith(errorMessage: e.toString(), state: BaseState.error),
@@ -53,26 +57,55 @@ class ChatsCubit extends Cubit<ChatsState> {
}
Future<void> getChatRooms({String? query}) async {
page = 1;
_query = query;
try {
final chatRooms = await chatRepository.getChatRoomPreviews(query: query);
final unreadChats = chatRooms.where(
(element) => element.unreadMessagesCount > 0,
);
final chatRoomsPaginationModel = await chatRepository.getChatRooms(query: query);
final unreadChats = chatRoomsPaginationModel.unreadChatsCount;
lastPage = chatRoomsPaginationModel.chatRooms.lastPage + 1;
emit(
state.copyWith(
chatRooms: chatRooms,
chatsWithUnreadMessagesCount: unreadChats.length,
chatRooms: chatRoomsPaginationModel.chatRooms.data,
chatsWithUnreadMessagesCount: unreadChats,
state: BaseState.success,
),
);
} catch (e) {
page++;
} catch (e, st) {
log(e.toString(), stackTrace: st);
emit(
state.copyWith(errorMessage: e.toString(), state: BaseState.error),
);
}
}
Future<void> loadMoreChatRooms() async {
try {
if (state.isLoadingMoreChats || lastPage == page) return;
emit(state.copyWith(isLoadingMoreChats: true));
final chatRoomsPaginationModel = await chatRepository.getChatRooms(query: _query, page: page);
final unreadChats = chatRoomsPaginationModel.unreadChatsCount;
emit(
state.copyWith(
chatRooms: [...state.chatRooms, ...chatRoomsPaginationModel.chatRooms.data],
chatsWithUnreadMessagesCount: unreadChats,
state: BaseState.success,
isLoadingMoreChats: false,
),
);
page++;
} catch (e, st) {
log(e.toString(), stackTrace: st);
emit(
state.copyWith(
errorMessage: e.toString(),
state: BaseState.error,
isLoadingMoreChats: false,
),
);
}
}
void reduceUnreadMessagesCount({required String chatId, required int count}) {
bool isChatRead = false;
final updatedChatRooms = state.chatRooms.map((room) {

View File

@@ -1,5 +1,5 @@
import 'package:eatvio_app/core/enums/base_state.dart';
import 'package:eatvio_app/features/chat/models/chat_room_preview_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_pagination_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'chats_state.freezed.dart';
@@ -11,6 +11,7 @@ class ChatsState with _$ChatsState {
@Default(BaseState.initial) BaseState state,
@Default(0) int chatsWithUnreadMessagesCount,
@Default([]) List<ChatRoom> chatRooms,
@Default(false) bool isLoadingMoreChats,
@Default(null) String? errorMessage,
}) = _ChatsState;

View File

@@ -19,6 +19,7 @@ mixin _$ChatsState {
BaseState get state => throw _privateConstructorUsedError;
int get chatsWithUnreadMessagesCount => throw _privateConstructorUsedError;
List<ChatRoom> get chatRooms => throw _privateConstructorUsedError;
bool get isLoadingMoreChats => throw _privateConstructorUsedError;
String? get errorMessage => throw _privateConstructorUsedError;
/// Create a copy of ChatsState
@@ -38,6 +39,7 @@ abstract class $ChatsStateCopyWith<$Res> {
{BaseState state,
int chatsWithUnreadMessagesCount,
List<ChatRoom> chatRooms,
bool isLoadingMoreChats,
String? errorMessage});
}
@@ -59,6 +61,7 @@ class _$ChatsStateCopyWithImpl<$Res, $Val extends ChatsState>
Object? state = null,
Object? chatsWithUnreadMessagesCount = null,
Object? chatRooms = null,
Object? isLoadingMoreChats = null,
Object? errorMessage = freezed,
}) {
return _then(_value.copyWith(
@@ -74,6 +77,10 @@ class _$ChatsStateCopyWithImpl<$Res, $Val extends ChatsState>
? _value.chatRooms
: chatRooms // ignore: cast_nullable_to_non_nullable
as List<ChatRoom>,
isLoadingMoreChats: null == isLoadingMoreChats
? _value.isLoadingMoreChats
: isLoadingMoreChats // ignore: cast_nullable_to_non_nullable
as bool,
errorMessage: freezed == errorMessage
? _value.errorMessage
: errorMessage // ignore: cast_nullable_to_non_nullable
@@ -94,6 +101,7 @@ abstract class _$$ChatsStateImplCopyWith<$Res>
{BaseState state,
int chatsWithUnreadMessagesCount,
List<ChatRoom> chatRooms,
bool isLoadingMoreChats,
String? errorMessage});
}
@@ -113,6 +121,7 @@ class __$$ChatsStateImplCopyWithImpl<$Res>
Object? state = null,
Object? chatsWithUnreadMessagesCount = null,
Object? chatRooms = null,
Object? isLoadingMoreChats = null,
Object? errorMessage = freezed,
}) {
return _then(_$ChatsStateImpl(
@@ -128,6 +137,10 @@ class __$$ChatsStateImplCopyWithImpl<$Res>
? _value._chatRooms
: chatRooms // ignore: cast_nullable_to_non_nullable
as List<ChatRoom>,
isLoadingMoreChats: null == isLoadingMoreChats
? _value.isLoadingMoreChats
: isLoadingMoreChats // ignore: cast_nullable_to_non_nullable
as bool,
errorMessage: freezed == errorMessage
? _value.errorMessage
: errorMessage // ignore: cast_nullable_to_non_nullable
@@ -143,6 +156,7 @@ class _$ChatsStateImpl implements _ChatsState {
{this.state = BaseState.initial,
this.chatsWithUnreadMessagesCount = 0,
final List<ChatRoom> chatRooms = const [],
this.isLoadingMoreChats = false,
this.errorMessage = null})
: _chatRooms = chatRooms;
@@ -161,13 +175,16 @@ class _$ChatsStateImpl implements _ChatsState {
return EqualUnmodifiableListView(_chatRooms);
}
@override
@JsonKey()
final bool isLoadingMoreChats;
@override
@JsonKey()
final String? errorMessage;
@override
String toString() {
return 'ChatsState(state: $state, chatsWithUnreadMessagesCount: $chatsWithUnreadMessagesCount, chatRooms: $chatRooms, errorMessage: $errorMessage)';
return 'ChatsState(state: $state, chatsWithUnreadMessagesCount: $chatsWithUnreadMessagesCount, chatRooms: $chatRooms, isLoadingMoreChats: $isLoadingMoreChats, errorMessage: $errorMessage)';
}
@override
@@ -182,6 +199,8 @@ class _$ChatsStateImpl implements _ChatsState {
chatsWithUnreadMessagesCount) &&
const DeepCollectionEquality()
.equals(other._chatRooms, _chatRooms) &&
(identical(other.isLoadingMoreChats, isLoadingMoreChats) ||
other.isLoadingMoreChats == isLoadingMoreChats) &&
(identical(other.errorMessage, errorMessage) ||
other.errorMessage == errorMessage));
}
@@ -192,6 +211,7 @@ class _$ChatsStateImpl implements _ChatsState {
state,
chatsWithUnreadMessagesCount,
const DeepCollectionEquality().hash(_chatRooms),
isLoadingMoreChats,
errorMessage);
/// Create a copy of ChatsState
@@ -208,6 +228,7 @@ abstract class _ChatsState implements ChatsState {
{final BaseState state,
final int chatsWithUnreadMessagesCount,
final List<ChatRoom> chatRooms,
final bool isLoadingMoreChats,
final String? errorMessage}) = _$ChatsStateImpl;
@override
@@ -217,6 +238,8 @@ abstract class _ChatsState implements ChatsState {
@override
List<ChatRoom> get chatRooms;
@override
bool get isLoadingMoreChats;
@override
String? get errorMessage;
/// Create a copy of ChatsState

View File

@@ -11,18 +11,20 @@ import 'package:eatvio_app/features/chat/presentation/widgets/chat_bubble_widget
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/chat/presentation/widgets/order_card.dart';
import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:intl/intl.dart';
import 'package:visibility_detector/visibility_detector.dart';
class ChatScreen extends StatefulWidget {
final String chatId;
final ChatsCubit chatsCubit;
const ChatScreen({
required this.chatId,
required this.chatsCubit,
super.key,
});
@@ -36,6 +38,7 @@ class _ChatScreenState extends State<ChatScreen> {
final ChatRoomCubit chatRoomCubit = sl<ChatRoomCubit>();
final List<({int id, int index})> unreadMessagesIds = [];
final List<({int id, int index})> unreadMessagesIdsCopy = [];
final showableChatBadgeStates = ChosenPlanStateEnum.getShowableChatBadgeStates();
@override
void initState() {
@@ -53,6 +56,7 @@ class _ChatScreenState extends State<ChatScreen> {
@override
void dispose() {
scrollController.dispose();
chatRoomCubit.close();
super.dispose();
}
@@ -80,6 +84,9 @@ class _ChatScreenState extends State<ChatScreen> {
}
_buildChat(BuildContext context, ChatRoomStateLoaded state) {
final chosenPlan = state.chatRoom.chosenPlan;
final amIConsultant = chosenPlan?.consultantId == state.chatRoom.users.sender.id;
final showAboutOrderButton = showableChatBadgeStates.contains(chosenPlan?.chosenPlanState);
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
@@ -88,12 +95,12 @@ class _ChatScreenState extends State<ChatScreen> {
centerTitle: false,
title: Row(
children: [
if (state.chatRoom.users['to_chat_user']!.profilePhotoUrl != null)
if (state.chatRoom.users.receiver.profilePhotoUrl != null)
CircleAvatar(
radius: 20,
foregroundImage: NetworkImage(state.chatRoom.users['to_chat_user']!.profilePhotoUrl!),
foregroundImage: NetworkImage(state.chatRoom.users.receiver.profilePhotoUrl!),
),
if (state.chatRoom.users['to_chat_user']!.profilePhotoUrl == null)
if (state.chatRoom.users.receiver.profilePhotoUrl == null)
Container(
width: 40,
height: 40,
@@ -103,7 +110,7 @@ class _ChatScreenState extends State<ChatScreen> {
shape: BoxShape.circle,
),
child: Text(
state.chatRoom.users['to_chat_user']!.name.split(' ').first[0] + state.chatRoom.users['to_chat_user']!.name.split(' ')[1][0],
state.chatRoom.users.receiver.name.split(' ').first[0] + state.chatRoom.users.receiver.name.split(' ')[1][0],
style: TextStyle(
color: Color(0xFF7F9CF5),
fontWeight: FontWeight.w400,
@@ -116,7 +123,7 @@ class _ChatScreenState extends State<ChatScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
state.chatRoom.users['to_chat_user']!.name,
state.chatRoom.users.receiver.name,
style: CustomTextStyles.titleSmallBold.copyWith(
color: PrimaryColors().black,
),
@@ -138,28 +145,38 @@ class _ChatScreenState extends State<ChatScreen> {
),
),
body: SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
child: Stack(
children: [
Expanded(
child: Container(
color: appTheme.whiteA700,
width: double.maxFinite,
child: CustomScrollView(
physics: BouncingScrollPhysics(),
controller: scrollController,
reverse: true,
slivers: [
SliverPadding(
padding: EdgeInsets.symmetric(vertical: 16),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index < state.chatMessages.length) {
Column(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: Container(
color: appTheme.whiteA700,
width: double.maxFinite,
child: SingleChildScrollView(
physics: BouncingScrollPhysics(),
controller: scrollController,
reverse: true,
child: Column(
children: [
SizedBox(height: 16),
ListView.builder(
physics: NeverScrollableScrollPhysics(),
reverse: true,
shrinkWrap: true,
itemBuilder: (context, index) {
if (index == state.chatMessages.length) {
return Padding(
padding: const EdgeInsets.all(8),
child: const Loader(),
);
}
if ((index + 1 != state.chatMessages.length &&
state.chatMessages[index].updatedAt.day != state.chatMessages[index + 1].updatedAt.day) ||
index + 1 == state.chatMessages.length) {
final isCurrentUserAuthor = state.chatRoom.users['to_chat_user']!.id != state.chatMessages[index].authorId;
final isCurrentUserAuthor = state.chatRoom.users.receiver.id != state.chatMessages[index].authorId;
return Column(
children: [
_messageDate(state.chatMessages[index].updatedAt),
@@ -174,7 +191,9 @@ class _ChatScreenState extends State<ChatScreen> {
() async {
final result = await context.read<ChatRoomCubit>().markMessagesAsRead(state.chatRoom.id, unreadMessagesIdsCopy);
if (!result) return;
widget.chatsCubit.reduceUnreadMessagesCount(chatId: state.chatRoom.id, count: unreadMessagesIdsCopy.length);
context
.read<ChatsCubit>()
.reduceUnreadMessagesCount(chatId: state.chatRoom.id, count: unreadMessagesIdsCopy.length);
unreadMessagesIdsCopy.clear();
},
);
@@ -191,7 +210,7 @@ class _ChatScreenState extends State<ChatScreen> {
],
);
}
final isCurrentUserAuthor = state.chatRoom.users['to_chat_user']!.id != state.chatMessages[index].authorId;
final isCurrentUserAuthor = state.chatRoom.users.receiver.id != state.chatMessages[index].authorId;
return VisibilityDetector(
key: Key(state.chatMessages[index].id.toString()),
onVisibilityChanged: (VisibilityInfo info) {
@@ -203,7 +222,7 @@ class _ChatScreenState extends State<ChatScreen> {
() async {
final result = await context.read<ChatRoomCubit>().markMessagesAsRead(state.chatRoom.id, unreadMessagesIds);
if (!result) return;
widget.chatsCubit.reduceUnreadMessagesCount(chatId: state.chatRoom.id, count: unreadMessagesIds.length);
context.read<ChatsCubit>().reduceUnreadMessagesCount(chatId: state.chatRoom.id, count: unreadMessagesIds.length);
unreadMessagesIds.clear();
},
);
@@ -217,157 +236,208 @@ class _ChatScreenState extends State<ChatScreen> {
mediaData: state.chatMessages[index].mediaData ?? [],
),
);
} else {
return Padding(
padding: const EdgeInsets.all(8),
child: const Loader(),
);
}
},
childCount: state.chatMessages.length + (state.isLoading ? 1 : 0),
),
},
itemCount: state.chatMessages.length + (state.isLoading ? 1 : 0),
),
SizedBox(height: 16),
if (chosenPlan != null)
OrderCard(
plan: chosenPlan.plan,
isUserView: !amIConsultant,
optionType: chosenPlan.optionType,
onCancel: () async {
await context.read<ChatRoomCubit>().cancelChosenPlan(chatId: state.chatRoom.id, planId: chosenPlan.id);
},
),
],
),
),
],
),
),
Container(
padding: EdgeInsets.symmetric(vertical: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
SizedBox(width: 8),
InkWell(
onTap: () async {
FilePickerResult? result = await FilePicker.platform.pickFiles(
allowMultiple: true,
type: FileType.custom,
allowedExtensions: [
'pdf',
'doc',
'docx',
'json',
'ppt',
'pptx',
'rtf',
'txt',
'xls',
'xlsx',
'csv',
'jpeg',
'jpg',
'png',
'gif',
'svg',
'xml',
'webp',
],
);
if (result != null) {
List<File> files = result.paths.map((path) => File(path!)).toList();
await chatRoomCubit.sendMessage(
chatId: widget.chatId,
files: files,
);
} else {
// User canceled the picker
}
},
customBorder: CircleBorder(),
child: Ink(
height: 40,
width: 40,
child: Transform.rotate(
angle: pi / 4,
child: Icon(
Icons.attach_file_rounded,
size: 30,
color: Color(0xFF858E99),
),
),
),
),
SizedBox(width: 8),
Expanded(
child: SizedBox(
height: 40,
child: TextFormField(
expands: true,
controller: textEditingController,
maxLines: null,
minLines: null,
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: 10),
hintText: 'Сообщение',
hintStyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
height: 1,
color: Color(0xFFAEAEB2),
),
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(100),
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(100),
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(100),
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
),
),
),
),
),
),
SizedBox(width: 8),
InkWell(
customBorder: CircleBorder(),
onTap: () {
if (textEditingController.text.isNotEmpty) {
chatRoomCubit.sendMessage(
chatId: widget.chatId,
data: textEditingController.text.trim(),
);
setState(() {
textEditingController.clear();
});
}
},
child: Ink(
decoration: BoxDecoration(
color: appTheme.blueA400,
shape: BoxShape.circle,
),
height: 40,
width: 40,
child: Icon(
Icons.arrow_upward_rounded,
size: 24,
color: Colors.white,
),
),
),
SizedBox(width: 8),
],
),
),
],
),
if (showAboutOrderButton)
Positioned(
top: 8,
right: 15,
left: 16,
child: GestureDetector(
onTap: () {},
child: Container(
height: 44,
decoration: BoxDecoration(
color: appTheme.gray50,
borderRadius: BorderRadius.circular(20),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox.shrink(),
Text(
S.current.more_about_order,
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
),
Container(
margin: EdgeInsets.only(right: 4),
width: 36,
height: 36,
decoration: BoxDecoration(
color: appTheme.whiteA700,
shape: BoxShape.circle,
),
child: Center(
child: SvgPicture.asset(
'assets/icons/right_arrow.svg',
height: 14,
width: 8,
colorFilter: ColorFilter.mode(
appTheme.blueA400,
BlendMode.srcIn,
),
),
),
),
],
),
),
),
),
),
Container(
padding: EdgeInsets.symmetric(vertical: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
SizedBox(width: 8),
InkWell(
onTap: () async {
FilePickerResult? result = await FilePicker.platform.pickFiles(
allowMultiple: true,
type: FileType.custom,
allowedExtensions: [
'pdf',
'doc',
'docx',
'json',
'ppt',
'pptx',
'rtf',
'txt',
'xls',
'xlsx',
'csv',
'jpeg',
'jpg',
'png',
'gif',
'svg',
'xml',
'webp',
],
);
if (result != null) {
List<File> files = result.paths.map((path) => File(path!)).toList();
await chatRoomCubit.sendMessage(
chatId: widget.chatId,
files: files,
);
} else {
// User canceled the picker
}
},
customBorder: CircleBorder(),
child: Ink(
height: 40,
width: 40,
child: Transform.rotate(
angle: pi / 4,
child: Icon(
Icons.attach_file_rounded,
size: 30,
color: Color(0xFF858E99),
),
),
),
),
SizedBox(width: 8),
Expanded(
child: SizedBox(
height: 40,
child: TextFormField(
expands: true,
controller: textEditingController,
maxLines: null,
minLines: null,
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: 10),
hintText: 'Сообщение',
hintStyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
height: 1,
color: Color(0xFFAEAEB2),
),
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(100),
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(100),
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(100),
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
),
),
),
),
),
),
SizedBox(width: 8),
InkWell(
customBorder: CircleBorder(),
onTap: () {
if (textEditingController.text.isNotEmpty) {
chatRoomCubit.sendMessage(
chatId: widget.chatId,
data: textEditingController.text.trim(),
);
setState(() {
textEditingController.clear();
});
}
},
child: Ink(
decoration: BoxDecoration(
color: appTheme.blueA400,
shape: BoxShape.circle,
),
height: 40,
width: 40,
child: Icon(
Icons.arrow_upward_rounded,
size: 24,
color: Colors.white,
),
),
),
SizedBox(width: 8),
],
),
),
],
),
),

View File

@@ -1,5 +1,6 @@
import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/core/enums/base_state.dart';
import 'package:eatvio_app/core/utils/pagination_utils.dart';
import 'package:eatvio_app/core/widgets/data_error_widget.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart';
@@ -16,7 +17,7 @@ class ChatsScreen extends StatefulWidget {
State<ChatsScreen> createState() => _ChatsScreenState();
}
class _ChatsScreenState extends State<ChatsScreen> {
class _ChatsScreenState extends State<ChatsScreen> {
final searchController = TextEditingController();
Widget build(BuildContext context) {
@@ -65,45 +66,40 @@ class _ChatsScreenState extends State<ChatsScreen> {
),
];
},
body: stateBuilder(state),
body: switch (state.state) {
BaseState.error => DataErrorWidget(onReload: () => context.read<ChatsCubit>().getChatRooms()),
BaseState.success => _buildChatList(context, state),
_ => const Loader(),
},
),
);
},
);
}
stateBuilder(ChatsState state) {
if (state.state == BaseState.loading) {
return Scaffold(
body: const Loader(),
);
} else if (state.state == BaseState.success) {
return _buildChatList(context, state);
} else if (state.state == BaseState.error) {
return Scaffold(
body: DataErrorWidget(onReload: () => context.read<ChatsCubit>().getChatRooms()),
);
} else {
return Scaffold(
body: const Loader(),
);
}
}
_buildChatList(BuildContext context, ChatsState state) {
return ListView.separated(
padding: EdgeInsets.all(16),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
separatorBuilder: (context, index) {
return SizedBox(
height: 8,
);
},
itemCount: state.chatRooms.length,
itemBuilder: (context, index) {
return ChatEntryWidget(chatRoom: state.chatRooms[index]);
return NotificationListener<ScrollNotification>(
onNotification: (notification) {
loadNewContent(notification, context.read<ChatsCubit>().loadMoreChatRooms);
return false;
},
child: ListView.separated(
padding: EdgeInsets.all(16),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
separatorBuilder: (context, index) {
return SizedBox(
height: 8,
);
},
itemCount: state.isLoadingMoreChats ? state.chatRooms.length + 1 : state.chatRooms.length,
itemBuilder: (context, index) {
if (index == state.chatRooms.length) {
return Center(child: Loader());
}
return ChatEntryWidget(chatRoom: state.chatRooms[index]);
},
),
);
}
}

View File

@@ -1,10 +1,8 @@
import 'package:eatvio_app/core/utils/date_time_utils.dart';
import 'package:eatvio_app/features/chat/models/chat_room_preview_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_pagination_model.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
class ChatEntryWidget extends StatefulWidget {
@@ -28,7 +26,6 @@ class _ChatEntryWidgetState extends State<ChatEntryWidget> {
'chatScreen',
extra: {
'chatId': widget.chatRoom.id.toString(),
'chatsCubit': context.read<ChatsCubit>(),
},
),
child: Ink(

View File

@@ -0,0 +1,77 @@
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
class NotificationBanner extends StatelessWidget {
const NotificationBanner({super.key, required this.bannerType});
final BannerType bannerType;
@override
Widget build(BuildContext context) {
final size = MediaQuery.sizeOf(context);
final s = S.of(context);
return Container(
decoration: BoxDecoration(
color: appTheme.gray50,
borderRadius: BorderRadius.circular(20),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: 12, right: 8, left: 12),
child: SvgPicture.asset('assets/icons/reversed_exclamation_mark.svg', height: 68, width: 68),
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: switch (bannerType) {
BannerType.date => [
SizedBox(
width: size.width * 0.6,
child: Text(s.can_not_choose_start_date, style: TextStyles.textMMedium.copyWith(color: appTheme.black)),
),
SizedBox(height: 12),
GestureDetector(
onTap: () {},
child: Text(s.add_card, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
),
SizedBox(height: 16),
],
BannerType.card => [
SizedBox(
width: size.width * 0.6,
child: Text(s.can_not_choose_start_date, style: TextStyles.textMMedium.copyWith(color: appTheme.black)),
),
SizedBox(height: 12),
GestureDetector(
onTap: () {},
child: Text(s.add_card, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
),
SizedBox(height: 16),
],
BannerType.payment => [
SizedBox(
width: size.width * 0.6,
child: Text(s.can_not_choose_start_date, style: TextStyles.textMMedium.copyWith(color: appTheme.black)),
),
SizedBox(height: 12),
GestureDetector(
onTap: () {},
child: Text(s.add_card, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
),
SizedBox(height: 16),
],
},
),
),
],
),
);
}
}
enum BannerType { date, card, payment }

View File

@@ -0,0 +1,138 @@
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/utils/localization_utils.dart';
import 'package:eatvio_app/core/utils/string_extentions.dart';
import 'package:eatvio_app/core/widgets/item_widget.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
class OrderCard extends StatefulWidget {
final PlanModel plan;
final String optionType;
final bool isUserView;
final Future<void> Function() onCancel;
const OrderCard({super.key, required this.plan, required this.isUserView, required this.optionType, required this.onCancel});
@override
State<OrderCard> createState() => _OrderCardState();
}
class _OrderCardState extends State<OrderCard> {
bool isLoading = false;
@override
Widget build(BuildContext context) {
final optionIndex = int.parse(widget.optionType.characters.last);
final option = switch (optionIndex) {
1 => widget.plan.packet1Options,
2 => widget.plan.packet2Options,
3 => widget.plan.packet3Options,
_ => null,
};
final s = S.of(context);
return Container(
margin: EdgeInsets.symmetric(horizontal: 16),
padding: EdgeInsets.only(top: 8, bottom: 12, left: 8, right: 8),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(12), color: appTheme.gray60004),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 200,
width: double.infinity,
child: Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.network(
baseUrl + '/storage/' + widget.plan.image,
width: double.infinity,
fit: BoxFit.cover,
),
),
Positioned(
left: 0,
bottom: 44,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: appTheme.blueA400,
borderRadius: BorderRadius.only(
topRight: Radius.circular(12),
bottomRight: Radius.circular(12),
),
),
child: Text(
'${widget.plan.duration} ${getDayForm(widget.plan.duration, s).capitalize()}',
style: TextStyles.textSMedium.copyWith(color: appTheme.whiteA700),
),
),
),
Positioned(
left: 0,
bottom: 12,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: appTheme.greenA700,
borderRadius: BorderRadius.only(
topRight: Radius.circular(12),
bottomRight: Radius.circular(12),
),
),
child: Text(
'${s.from} ${widget.plan.minPriceOfOptions.toStringAsFixed(0)}₽',
style: TextStyles.textSMedium.copyWith(color: appTheme.whiteA700),
),
),
),
],
),
),
SizedBox(height: 12),
Text(
'${widget.plan.name}',
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
SizedBox(height: 12),
Text(
'${s.option}: ${option?.short ?? ''}',
style: TextStyles.textMSemibold.copyWith(color: appTheme.black),
),
SizedBox(height: 8),
if (option != null)
...option.items
.map(
(element) => Padding(
padding: const EdgeInsets.only(bottom: 4),
child: ItemWidget(
reversed: true,
iconSize: 12,
text: element,
),
),
)
.toList(),
if (widget.isUserView) ...[
SizedBox(height: 8),
isLoading
? Loader()
: GestureDetector(
onTap: () async {
setState(() => isLoading = true);
await widget.onCancel();
setState(() => isLoading = false);
},
child: Text(
'${s.cancel_order}',
style: TextStyles.textSMedium.copyWith(color: appTheme.strongRed),
),
),
],
],
),
);
}
}

View File

@@ -5,7 +5,7 @@ import 'package:dio/dio.dart';
import 'package:eatvio_app/features/chat/models/chat_message_model.dart';
import 'package:eatvio_app/features/chat/models/chat_messages_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_preview_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_pagination_model.dart';
import 'package:eatvio_app/features/chat/services/chat_service.dart';
import 'package:http_parser/http_parser.dart';
@@ -62,9 +62,9 @@ class ChatRepository {
}
}
Future<List<ChatRoom>> getChatRoomPreviews({String? query}) async {
Future<ChatRoomPaginationModel> getChatRooms({String? query, int page = 1}) async {
try {
return (await chatService.getChatRoomPreviews(query: query)).chatRooms;
return (await chatService.getChatRooms(query: query, page: page));
} catch (e) {
rethrow;
}
@@ -93,4 +93,14 @@ class ChatRepository {
rethrow;
}
}
Future<void> cancelChosenPlan({
required int id,
}) async {
try {
await chatService.cancelChosenPlan(id: id);
} catch (e) {
rethrow;
}
}
}

View File

@@ -1,7 +1,7 @@
import 'package:dio/dio.dart';
import 'package:eatvio_app/features/chat/models/chat_messages_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_preview_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_pagination_model.dart';
import 'package:retrofit/retrofit.dart';
part 'chat_service.g.dart';
@@ -11,7 +11,8 @@ abstract class ChatService {
factory ChatService(Dio dio, {String baseUrl}) = _ChatService;
@GET('/api/chat/index')
Future<ChatRoomPreviewModel> getChatRoomPreviews({
Future<ChatRoomPaginationModel> getChatRooms({
@Query('page') required int page,
@Query('find') String? query,
});
@@ -40,5 +41,13 @@ abstract class ChatService {
});
@POST('/api/chat/{id}/messages/markAsRead')
Future<void> markMessagesAsRead({@Path() required String id, @Body() required Map<String, List<int>> body});
Future<void> markMessagesAsRead({
@Path() required String id,
@Body() required Map<String, List<int>> body,
});
@POST('/api/chosen_plans/{id}/cancel')
Future<void> cancelChosenPlan({
@Path() required int id,
});
}

View File

@@ -18,13 +18,16 @@ class _ChatService implements ChatService {
final ParseErrorLogger? errorLogger;
@override
Future<ChatRoomPreviewModel> getChatRoomPreviews({String? query}) async {
Future<ChatRoomPaginationModel> getChatRooms({
required int page,
String? query,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{r'find': query};
final queryParameters = <String, dynamic>{r'page': page, r'find': query};
queryParameters.removeWhere((k, v) => v == null);
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options = _setStreamType<ChatRoomPreviewModel>(
final _options = _setStreamType<ChatRoomPaginationModel>(
Options(method: 'GET', headers: _headers, extra: _extra)
.compose(
_dio.options,
@@ -35,9 +38,9 @@ class _ChatService implements ChatService {
.copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
);
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late ChatRoomPreviewModel _value;
late ChatRoomPaginationModel _value;
try {
_value = ChatRoomPreviewModel.fromJson(_result.data!);
_value = ChatRoomPaginationModel.fromJson(_result.data!);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
@@ -190,6 +193,25 @@ class _ChatService implements ChatService {
await _dio.fetch<void>(_options);
}
@override
Future<void> cancelChosenPlan({required int id}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options = _setStreamType<void>(
Options(method: 'POST', headers: _headers, extra: _extra)
.compose(
_dio.options,
'/api/chosen_plans/${id}/cancel',
queryParameters: queryParameters,
data: _data,
)
.copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
);
await _dio.fetch<void>(_options);
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||

View File

@@ -140,10 +140,10 @@ class GetChatRoomPreviews implements TestCases {
Future<bool> run({required String baseUrl}) async {
try {
// get all of needed services
final dio = sl<Dio>();
final chatService = ChatService(dio, baseUrl: baseUrl);
// final dio = sl<Dio>();
// final chatService = ChatService(dio, baseUrl: baseUrl);
await chatService.getChatRoomPreviews();
// await chatService.getChatRooms();
return true;
} catch (e) {
return false;
@@ -158,11 +158,11 @@ class GetChatRoom implements TestCases {
@override
Future<bool> run({required String baseUrl}) async {
try {
final dio = sl<Dio>();
final chatService = ChatService(dio, baseUrl: baseUrl);
// final dio = sl<Dio>();
// final chatService = ChatService(dio, baseUrl: baseUrl);
final chatRoomsModels = await chatService.getChatRoomPreviews();
await chatService.getChatRoom(id: chatRoomsModels.chatRooms.firstOrNull?.id ?? '');
// final chatRoomsModels = await chatService.getChatRoomPreviews();
// await chatService.getChatRoom(id: chatRoomsModels.chatRooms.firstOrNull?.id ?? '');
return true;
} catch (e) {
return false;
@@ -177,11 +177,11 @@ class SendMessage implements TestCases {
@override
Future<bool> run({required String baseUrl}) async {
try {
final dio = sl<Dio>();
final chatService = ChatService(dio, baseUrl: baseUrl);
// final dio = sl<Dio>();
// final chatService = ChatService(dio, baseUrl: baseUrl);
final chatRoomsModels = await chatService.getChatRoomPreviews();
await chatService.sendMessage(id: chatRoomsModels.chatRooms.firstOrNull?.id ?? '', data: 'test message');
// final chatRoomsModels = await chatService.getChatRoomPreviews();
// await chatService.sendMessage(id: chatRoomsModels.chatRooms.firstOrNull?.id ?? '', data: 'test message');
return true;
} catch (e) {
return false;
@@ -196,11 +196,11 @@ class GetChatMessages implements TestCases {
@override
Future<bool> run({required String baseUrl}) async {
try {
final dio = sl<Dio>();
final chatService = ChatService(dio, baseUrl: baseUrl);
// final dio = sl<Dio>();
// final chatService = ChatService(dio, baseUrl: baseUrl);
final chatRoomsModels = await chatService.getChatRoomPreviews();
await chatService.getChatMessages(id: chatRoomsModels.chatRooms.firstOrNull?.id ?? '', page: 1);
// final chatRoomsModels = await chatService.getChatRoomPreviews();
// await chatService.getChatMessages(id: chatRoomsModels.chatRooms.firstOrNull?.id ?? '', page: 1);
return true;
} catch (e) {
return false;

View File

@@ -115,7 +115,7 @@ class DietsCubit extends Cubit<DietsState> {
}
}
loadNextPage() async {
Future<void> loadNextPage() async {
if (currentPage < lastPage && !isLoading) {
isLoading = true;

View File

@@ -47,7 +47,7 @@ class FeedCubit extends Cubit<FeedState> {
}
}
loadNextPage() async {
Future<void> loadNextPage() async {
if (currentPage < lastPage && !isLoading) {
isLoading = true;

View File

@@ -174,7 +174,7 @@ class PlansCubit extends Cubit<PlansState> {
}
}
loadNextPage() async {
Future<void> loadNextPage() async {
if (currentPage < lastPage && !isLoading) {
isLoading = true;

View File

@@ -67,7 +67,7 @@ class RecipesCubit extends Cubit<RecipesState> {
}
}
loadNextPage() async {
Future<void> loadNextPage() async {
if (currentPage < lastPage && !isLoading) {
isLoading = true;

View File

@@ -77,38 +77,12 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
final consultantsScrollController = ScrollController();
@override
void initState() {
blogPostsScrollController.addListener(_onScrollBlogPosts);
recipesScrollController.addListener(_onScrollRecipes);
dietsScrollController.addListener(_onScrollDiets);
plansScrollController.addListener(_onScrollPlans);
void initState() {
consultantsScrollController.addListener(_onScrollConsultants);
super.initState();
}
}
_onScrollBlogPosts() {
if (blogPostsScrollController.offset >= blogPostsScrollController.position.maxScrollExtent && !blogPostsScrollController.position.outOfRange) {
context.read<FeedCubit>().loadNextPage();
}
}
_onScrollRecipes() {
if (recipesScrollController.offset >= recipesScrollController.position.maxScrollExtent && !recipesScrollController.position.outOfRange) {
context.read<RecipesCubit>().loadNextPage();
}
}
_onScrollDiets() {
if (dietsScrollController.offset >= dietsScrollController.position.maxScrollExtent && !dietsScrollController.position.outOfRange) {
context.read<DietsCubit>().loadNextPage();
}
}
_onScrollPlans() {
if (plansScrollController.offset >= plansScrollController.position.maxScrollExtent && !plansScrollController.position.outOfRange) {
context.read<PlansCubit>().loadNextPage();
}
}
_onScrollConsultants() {
if (consultantsScrollController.offset >= consultantsScrollController.position.maxScrollExtent && !consultantsScrollController.position.outOfRange) {}

View File

@@ -1,3 +1,4 @@
import 'package:eatvio_app/core/utils/pagination_utils.dart';
import 'package:eatvio_app/core/widgets/data_error_widget.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
@@ -16,20 +17,26 @@ class BlogPosts extends StatelessWidget {
child: BlocBuilder<FeedCubit, FeedState>(
builder: (BuildContext context, FeedState state) {
if (state is FeedStateLoaded) {
return ListView.separated(
padding: EdgeInsets.symmetric(vertical: 8),
shrinkWrap: true,
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
if (index == state.feedBlogPostList.length) {
return const Loader();
}
return BlogPostWidget(index: index);
return NotificationListener<ScrollNotification>(
onNotification: (notification) {
loadNewContent(notification, context.read<FeedCubit>().loadNextPage);
return false;
},
separatorBuilder: (context, index) {
return SizedBox(height: 8);
},
itemCount: state.isLoading ? state.feedBlogPostList.length + 1 : state.feedBlogPostList.length,
child: ListView.separated(
padding: EdgeInsets.symmetric(vertical: 8),
shrinkWrap: true,
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
if (index == state.feedBlogPostList.length) {
return const Loader();
}
return BlogPostWidget(index: index);
},
separatorBuilder: (context, index) {
return SizedBox(height: 8);
},
itemCount: state.isLoading ? state.feedBlogPostList.length + 1 : state.feedBlogPostList.length,
),
);
} else if (state is FeedStateError) {
return DataErrorWidget(

View File

@@ -1,3 +1,4 @@
import 'package:eatvio_app/core/utils/pagination_utils.dart';
import 'package:eatvio_app/core/widgets/data_error_widget.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
@@ -28,17 +29,23 @@ class Diets extends StatelessWidget {
return Stack(
children: [
SizedBox.expand(),
ListView.separated(
padding: EdgeInsets.symmetric(vertical: 8),
shrinkWrap: true,
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
return DietsWidget(diet: state.dietsList[index], index: index);
NotificationListener<ScrollNotification>(
onNotification: (notification) {
loadNewContent(notification, context.read<DietsCubit>().loadNextPage);
return false;
},
separatorBuilder: (context, index) {
return SizedBox(height: 8);
},
itemCount: state.dietsList.length,
child: ListView.separated(
padding: EdgeInsets.symmetric(vertical: 8),
shrinkWrap: true,
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
return DietsWidget(diet: state.dietsList[index], index: index);
},
separatorBuilder: (context, index) {
return SizedBox(height: 8);
},
itemCount: state.dietsList.length,
),
),
Positioned(
right: 0,

View File

@@ -1,3 +1,4 @@
import 'package:eatvio_app/core/utils/pagination_utils.dart';
import 'package:eatvio_app/core/widgets/data_error_widget.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_cubit.dart';
@@ -28,20 +29,26 @@ class Plans extends StatelessWidget {
return Stack(
children: [
SizedBox.expand(),
ListView.separated(
padding: EdgeInsets.symmetric(vertical: 8),
shrinkWrap: true,
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
return PlanWidget(
plan: state.plans[index],
index: index,
);
NotificationListener<ScrollNotification>(
onNotification: (notification) {
loadNewContent(notification, context.read<PlansCubit>().loadNextPage);
return false;
},
separatorBuilder: (context, index) {
return SizedBox(height: 8);
},
itemCount: state.plans.length,
child: ListView.separated(
padding: EdgeInsets.symmetric(vertical: 8),
shrinkWrap: true,
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
return PlanWidget(
plan: state.plans[index],
index: index,
);
},
separatorBuilder: (context, index) {
return SizedBox(height: 8);
},
itemCount: state.plans.length,
),
),
Positioned(
right: 0,

View File

@@ -1,3 +1,4 @@
import 'package:eatvio_app/core/utils/pagination_utils.dart';
import 'package:eatvio_app/core/widgets/data_error_widget.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart';
@@ -29,20 +30,26 @@ class Recipes extends StatelessWidget {
);
}
return ListView.separated(
padding: EdgeInsets.symmetric(vertical: 8),
shrinkWrap: true,
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
return RecipesWidget(
recipe: state.recipesList[index],
index: index,
);
return NotificationListener<ScrollNotification>(
onNotification: (notification) {
loadNewContent(notification, context.read<RecipesCubit>().loadNextPage);
return false;
},
separatorBuilder: (context, index) {
return SizedBox(height: 8);
},
itemCount: state.recipesList.length,
child: ListView.separated(
padding: EdgeInsets.symmetric(vertical: 8),
shrinkWrap: true,
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
return RecipesWidget(
recipe: state.recipesList[index],
index: index,
);
},
separatorBuilder: (context, index) {
return SizedBox(height: 8);
},
itemCount: state.recipesList.length,
),
);
},
),

View File

@@ -155,7 +155,7 @@ class MenuScreen extends StatelessWidget {
);
} catch (e) {
} finally {
await TokenStorage.removeTokenInStorage();
await TokenStorage.removeTokenInStorage();
GoRouter.of(context).go('/auth-screen', extra: true);
}
},

View File

@@ -0,0 +1,44 @@
import 'package:freezed_annotation/freezed_annotation.dart';
enum ChosenPlanStateEnum {
@JsonValue('initied')
stateInited,
@JsonValue('start_date_has_been_set')
stateStartDateHasBeenSet,
@JsonValue('in_coordination')
stateInCoordination,
@JsonValue('started')
stateStarted,
@JsonValue('in_progress')
stateInProgress,
@JsonValue('pending_payment')
statePendingPayment,
@JsonValue('confirmed_by_consultant')
stateConfirmedByConsultant,
@JsonValue('paid')
statePaid,
@JsonValue('canceled_by_the_initiator')
stateCanceledByTheInitiator,
@JsonValue('last_day_started')
stateLastDayStarted,
@JsonValue('last_day_finished')
stateLastDayFinished,
@JsonValue('complained')
stateComplained,
@JsonValue('done')
stateDone,
@JsonValue('charge_was_collected')
stateChargeWasCollected;
static List<ChosenPlanStateEnum> getShowableChatBadgeStates() {
return [
ChosenPlanStateEnum.stateInCoordination,
ChosenPlanStateEnum.stateStartDateHasBeenSet,
ChosenPlanStateEnum.statePendingPayment,
ChosenPlanStateEnum.statePaid,
ChosenPlanStateEnum.stateLastDayStarted,
ChosenPlanStateEnum.stateLastDayFinished,
ChosenPlanStateEnum.stateDone,
];
}
}

View File

@@ -0,0 +1,29 @@
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'chosen_plan_model.freezed.dart';
part 'chosen_plan_model.g.dart';
@freezed
class ChosenPlanModel with _$ChosenPlanModel {
factory ChosenPlanModel({
@JsonKey() required int id,
@JsonKey(name: 'option_type') required String optionType,
@JsonKey(name: 'start_date') required DateTime startDate,
@JsonKey(name: 'last_state') required ChosenPlanStateEnum chosenPlanState,
@JsonKey(name: 'chat_id') required String chatId,
@JsonKey(name: 'consultant_id') required int consultantId,
@JsonKey(name: 'plan') required PlanModel plan,
}) = _ChosenPlanModel;
factory ChosenPlanModel.fromJson(Map<String, dynamic> json) => _$ChosenPlanModelFromJson(json);
}
ChosenPlanModel? parseChosenPlan(json) {
if (json is Map<String, dynamic>) {
return ChosenPlanModel.fromJson(json);
}
return null;
}

View File

@@ -0,0 +1,332 @@
// 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 'chosen_plan_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(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');
ChosenPlanModel _$ChosenPlanModelFromJson(Map<String, dynamic> json) {
return _ChosenPlanModel.fromJson(json);
}
/// @nodoc
mixin _$ChosenPlanModel {
@JsonKey()
int get id => throw _privateConstructorUsedError;
@JsonKey(name: 'option_type')
String get optionType => throw _privateConstructorUsedError;
@JsonKey(name: 'start_date')
DateTime get startDate => throw _privateConstructorUsedError;
@JsonKey(name: 'last_state')
ChosenPlanStateEnum get chosenPlanState => throw _privateConstructorUsedError;
@JsonKey(name: 'chat_id')
String get chatId => throw _privateConstructorUsedError;
@JsonKey(name: 'consultant_id')
int get consultantId => throw _privateConstructorUsedError;
@JsonKey(name: 'plan')
PlanModel get plan => throw _privateConstructorUsedError;
/// Serializes this ChosenPlanModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of ChosenPlanModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ChosenPlanModelCopyWith<ChosenPlanModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ChosenPlanModelCopyWith<$Res> {
factory $ChosenPlanModelCopyWith(
ChosenPlanModel value, $Res Function(ChosenPlanModel) then) =
_$ChosenPlanModelCopyWithImpl<$Res, ChosenPlanModel>;
@useResult
$Res call(
{@JsonKey() int id,
@JsonKey(name: 'option_type') String optionType,
@JsonKey(name: 'start_date') DateTime startDate,
@JsonKey(name: 'last_state') ChosenPlanStateEnum chosenPlanState,
@JsonKey(name: 'chat_id') String chatId,
@JsonKey(name: 'consultant_id') int consultantId,
@JsonKey(name: 'plan') PlanModel plan});
$PlanModelCopyWith<$Res> get plan;
}
/// @nodoc
class _$ChosenPlanModelCopyWithImpl<$Res, $Val extends ChosenPlanModel>
implements $ChosenPlanModelCopyWith<$Res> {
_$ChosenPlanModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of ChosenPlanModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? optionType = null,
Object? startDate = null,
Object? chosenPlanState = null,
Object? chatId = null,
Object? consultantId = null,
Object? plan = null,
}) {
return _then(_value.copyWith(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
optionType: null == optionType
? _value.optionType
: optionType // ignore: cast_nullable_to_non_nullable
as String,
startDate: null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
chosenPlanState: null == chosenPlanState
? _value.chosenPlanState
: chosenPlanState // ignore: cast_nullable_to_non_nullable
as ChosenPlanStateEnum,
chatId: null == chatId
? _value.chatId
: chatId // ignore: cast_nullable_to_non_nullable
as String,
consultantId: null == consultantId
? _value.consultantId
: consultantId // ignore: cast_nullable_to_non_nullable
as int,
plan: null == plan
? _value.plan
: plan // ignore: cast_nullable_to_non_nullable
as PlanModel,
) as $Val);
}
/// Create a copy of ChosenPlanModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$PlanModelCopyWith<$Res> get plan {
return $PlanModelCopyWith<$Res>(_value.plan, (value) {
return _then(_value.copyWith(plan: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$ChosenPlanModelImplCopyWith<$Res>
implements $ChosenPlanModelCopyWith<$Res> {
factory _$$ChosenPlanModelImplCopyWith(_$ChosenPlanModelImpl value,
$Res Function(_$ChosenPlanModelImpl) then) =
__$$ChosenPlanModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{@JsonKey() int id,
@JsonKey(name: 'option_type') String optionType,
@JsonKey(name: 'start_date') DateTime startDate,
@JsonKey(name: 'last_state') ChosenPlanStateEnum chosenPlanState,
@JsonKey(name: 'chat_id') String chatId,
@JsonKey(name: 'consultant_id') int consultantId,
@JsonKey(name: 'plan') PlanModel plan});
@override
$PlanModelCopyWith<$Res> get plan;
}
/// @nodoc
class __$$ChosenPlanModelImplCopyWithImpl<$Res>
extends _$ChosenPlanModelCopyWithImpl<$Res, _$ChosenPlanModelImpl>
implements _$$ChosenPlanModelImplCopyWith<$Res> {
__$$ChosenPlanModelImplCopyWithImpl(
_$ChosenPlanModelImpl _value, $Res Function(_$ChosenPlanModelImpl) _then)
: super(_value, _then);
/// Create a copy of ChosenPlanModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? optionType = null,
Object? startDate = null,
Object? chosenPlanState = null,
Object? chatId = null,
Object? consultantId = null,
Object? plan = null,
}) {
return _then(_$ChosenPlanModelImpl(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
optionType: null == optionType
? _value.optionType
: optionType // ignore: cast_nullable_to_non_nullable
as String,
startDate: null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
chosenPlanState: null == chosenPlanState
? _value.chosenPlanState
: chosenPlanState // ignore: cast_nullable_to_non_nullable
as ChosenPlanStateEnum,
chatId: null == chatId
? _value.chatId
: chatId // ignore: cast_nullable_to_non_nullable
as String,
consultantId: null == consultantId
? _value.consultantId
: consultantId // ignore: cast_nullable_to_non_nullable
as int,
plan: null == plan
? _value.plan
: plan // ignore: cast_nullable_to_non_nullable
as PlanModel,
));
}
}
/// @nodoc
@JsonSerializable()
class _$ChosenPlanModelImpl implements _ChosenPlanModel {
_$ChosenPlanModelImpl(
{@JsonKey() required this.id,
@JsonKey(name: 'option_type') required this.optionType,
@JsonKey(name: 'start_date') required this.startDate,
@JsonKey(name: 'last_state') required this.chosenPlanState,
@JsonKey(name: 'chat_id') required this.chatId,
@JsonKey(name: 'consultant_id') required this.consultantId,
@JsonKey(name: 'plan') required this.plan});
factory _$ChosenPlanModelImpl.fromJson(Map<String, dynamic> json) =>
_$$ChosenPlanModelImplFromJson(json);
@override
@JsonKey()
final int id;
@override
@JsonKey(name: 'option_type')
final String optionType;
@override
@JsonKey(name: 'start_date')
final DateTime startDate;
@override
@JsonKey(name: 'last_state')
final ChosenPlanStateEnum chosenPlanState;
@override
@JsonKey(name: 'chat_id')
final String chatId;
@override
@JsonKey(name: 'consultant_id')
final int consultantId;
@override
@JsonKey(name: 'plan')
final PlanModel plan;
@override
String toString() {
return 'ChosenPlanModel(id: $id, optionType: $optionType, startDate: $startDate, chosenPlanState: $chosenPlanState, chatId: $chatId, consultantId: $consultantId, plan: $plan)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ChosenPlanModelImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.optionType, optionType) ||
other.optionType == optionType) &&
(identical(other.startDate, startDate) ||
other.startDate == startDate) &&
(identical(other.chosenPlanState, chosenPlanState) ||
other.chosenPlanState == chosenPlanState) &&
(identical(other.chatId, chatId) || other.chatId == chatId) &&
(identical(other.consultantId, consultantId) ||
other.consultantId == consultantId) &&
(identical(other.plan, plan) || other.plan == plan));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, id, optionType, startDate,
chosenPlanState, chatId, consultantId, plan);
/// Create a copy of ChosenPlanModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ChosenPlanModelImplCopyWith<_$ChosenPlanModelImpl> get copyWith =>
__$$ChosenPlanModelImplCopyWithImpl<_$ChosenPlanModelImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$ChosenPlanModelImplToJson(
this,
);
}
}
abstract class _ChosenPlanModel implements ChosenPlanModel {
factory _ChosenPlanModel(
{@JsonKey() required final int id,
@JsonKey(name: 'option_type') required final String optionType,
@JsonKey(name: 'start_date') required final DateTime startDate,
@JsonKey(name: 'last_state')
required final ChosenPlanStateEnum chosenPlanState,
@JsonKey(name: 'chat_id') required final String chatId,
@JsonKey(name: 'consultant_id') required final int consultantId,
@JsonKey(name: 'plan') required final PlanModel plan}) =
_$ChosenPlanModelImpl;
factory _ChosenPlanModel.fromJson(Map<String, dynamic> json) =
_$ChosenPlanModelImpl.fromJson;
@override
@JsonKey()
int get id;
@override
@JsonKey(name: 'option_type')
String get optionType;
@override
@JsonKey(name: 'start_date')
DateTime get startDate;
@override
@JsonKey(name: 'last_state')
ChosenPlanStateEnum get chosenPlanState;
@override
@JsonKey(name: 'chat_id')
String get chatId;
@override
@JsonKey(name: 'consultant_id')
int get consultantId;
@override
@JsonKey(name: 'plan')
PlanModel get plan;
/// Create a copy of ChosenPlanModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ChosenPlanModelImplCopyWith<_$ChosenPlanModelImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,49 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'chosen_plan_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$ChosenPlanModelImpl _$$ChosenPlanModelImplFromJson(
Map<String, dynamic> json) =>
_$ChosenPlanModelImpl(
id: (json['id'] as num).toInt(),
optionType: json['option_type'] as String,
startDate: DateTime.parse(json['start_date'] as String),
chosenPlanState:
$enumDecode(_$ChosenPlanStateEnumEnumMap, json['last_state']),
chatId: json['chat_id'] as String,
consultantId: (json['consultant_id'] as num).toInt(),
plan: PlanModel.fromJson(json['plan'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$ChosenPlanModelImplToJson(
_$ChosenPlanModelImpl instance) =>
<String, dynamic>{
'id': instance.id,
'option_type': instance.optionType,
'start_date': instance.startDate.toIso8601String(),
'last_state': _$ChosenPlanStateEnumEnumMap[instance.chosenPlanState]!,
'chat_id': instance.chatId,
'consultant_id': instance.consultantId,
'plan': instance.plan,
};
const _$ChosenPlanStateEnumEnumMap = {
ChosenPlanStateEnum.stateInited: 'initied',
ChosenPlanStateEnum.stateStartDateHasBeenSet: 'start_date_has_been_set',
ChosenPlanStateEnum.stateInCoordination: 'in_coordination',
ChosenPlanStateEnum.stateStarted: 'started',
ChosenPlanStateEnum.stateInProgress: 'in_progress',
ChosenPlanStateEnum.statePendingPayment: 'pending_payment',
ChosenPlanStateEnum.stateConfirmedByConsultant: 'confirmed_by_consultant',
ChosenPlanStateEnum.statePaid: 'paid',
ChosenPlanStateEnum.stateCanceledByTheInitiator: 'canceled_by_the_initiator',
ChosenPlanStateEnum.stateLastDayStarted: 'last_day_started',
ChosenPlanStateEnum.stateLastDayFinished: 'last_day_finished',
ChosenPlanStateEnum.stateComplained: 'complained',
ChosenPlanStateEnum.stateDone: 'done',
ChosenPlanStateEnum.stateChargeWasCollected: 'charge_was_collected',
};

View File

@@ -1,8 +1,10 @@
import 'dart:developer';
import 'package:dio/dio.dart';
import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/core/utils/toasts.dart';
import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
import 'package:eatvio_app/features/plans/presentation/cubit/about_plan_state.dart';
import 'package:eatvio_app/features/plans/repositories/plan_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -125,4 +127,27 @@ class AboutPlanCubit extends Cubit<AboutPlanState> {
},
);
}
Future<ChosenPlanModel?> chosePlan() async {
try {
final slug = state.planModel?.slug;
final optionIndex = state.chosenOptionIndex;
if (slug != null) {
final optionName = _getOptionNameFromIndex(optionIndex);
final body = {'options_type': optionName};
return await planRepository.chosePlan(slug, body);
}
} on DioException catch (e, st) {
log(e.toString(), stackTrace: st);
} catch (e, st) {
log(e.toString(), stackTrace: st);
}
return null;
}
String _getOptionNameFromIndex(int index) => switch (index) {
1 => 'options2',
2 => 'options3',
_ => 'options1',
};
}

View File

@@ -10,6 +10,7 @@ import 'package:eatvio_app/core/widgets/bookmark_button.dart';
import 'package:eatvio_app/core/widgets/data_error_widget.dart';
import 'package:eatvio_app/core/widgets/info_row.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/core/widgets/show_error_dialog.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart';
@@ -30,6 +31,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_portal/flutter_portal.dart';
import 'package:flutter_svg/svg.dart';
import 'package:go_router/go_router.dart';
import '../widgets/option_widget.dart';
@@ -217,17 +219,21 @@ class _AboutPlanScreenState extends State<AboutPlanScreen> {
borderRadius: BorderRadius.circular(16),
),
backgroundColor: appTheme.greenA700,
onPressed: () {
/// TODO: добавить обработку покупки плана
// showModalBottomSheet(
// context: context,
// builder: (context) => StartDietBottomSheet(
// aboutDietCubit: context.read<AboutDietCubit>(),
// ),
// );
onPressed: () async {
final chosenPlan = await context.read<AboutPlanCubit>().chosePlan();
if (chosenPlan == null) {
showErrorDialog(context, title: 'Не удалось заказать план');
return;
}
GoRouter.of(context).pushReplacementNamed(
'chatScreen',
extra: {
'chatId': chosenPlan.chatId,
},
);
},
label: Text(
s.startDiet,
s.to_order,
style: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
),
),

View File

@@ -1,27 +0,0 @@
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class ItemWidget extends StatelessWidget {
const ItemWidget({super.key, required this.text});
final String text;
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: Text(
text,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
),
SvgPicture.asset(
'assets/icons/ok.svg',
width: 20,
height: 20,
),
],
);
}
}

View File

@@ -3,7 +3,7 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_divider.dart';
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
import 'package:eatvio_app/features/plans/presentation/widgets/item_widget.dart';
import 'package:eatvio_app/core/widgets/item_widget.dart';
import 'package:eatvio_app/features/plans/presentation/widgets/option_duration_widget.dart';
import 'package:flutter/material.dart';

View File

@@ -1,9 +1,9 @@
import 'dart:async';
import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
import 'package:eatvio_app/features/plans/services/plan_service.dart';
class PlanRepository {
final PlanService planService;
@@ -48,9 +48,15 @@ class PlanRepository {
}
}
Future<ChosenPlanModel> chosePlan(String slug, Map<String, dynamic> option ) async {
try {
return await planService.chosePlan(slug: slug, option: option);
} catch (e) {
rethrow;
}
}
void emitLikesStateToFeed(LikesState state) => _planLikeStateStreamController.add(state);
void emitFavoriteStateToFeed(FavoriteState state) => _planFavoriteStateStreamController.add(state);
void emitCommentsStateToFeed(CommentsState state) => _planCommentsStateStreamController.add(state);
}

View File

@@ -1,20 +1,18 @@
import 'package:dio/dio.dart';
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
import 'package:retrofit/retrofit.dart';
part 'plan_service.g.dart';
@RestApi()
abstract class PlanService {
factory PlanService(Dio dio, {String baseUrl}) = _PlanService;
@GET('/api/plans/show/{slug}')
Future<PlanModel> getPlan(@Path('slug') String slug);
@POST('/api/plans/{slug}/like')
@POST('/api/plans/{slug}/like')
Future<void> likePlan({
@Path() required String slug,
});
@@ -28,4 +26,10 @@ abstract class PlanService {
Future<void> removeFromFavorite({
@Path() required String slug,
});
}
@POST('/api/plans/{slug}/choose')
Future<ChosenPlanModel> chosePlan({
@Path() required String slug,
@Body() required Map<String, dynamic> option,
});
}

View File

@@ -101,6 +101,37 @@ class _PlanService implements PlanService {
await _dio.fetch<void>(_options);
}
@override
Future<ChosenPlanModel> chosePlan({
required String slug,
required Map<String, dynamic> option,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
final _data = <String, dynamic>{};
_data.addAll(option);
final _options = _setStreamType<ChosenPlanModel>(
Options(method: 'POST', headers: _headers, extra: _extra)
.compose(
_dio.options,
'/api/plans/${slug}/choose',
queryParameters: queryParameters,
data: _data,
)
.copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
);
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late ChosenPlanModel _value;
try {
_value = ChosenPlanModel.fromJson(_result.data!);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
return _value;
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||

View File

@@ -101,8 +101,9 @@ class ProfileCubit extends Cubit<ProfileState> {
}) async {
try {
return (await chatRepository.getChatRoomIdByUserSlug(slug: slug)).id;
} catch (e) {
rethrow;
} catch (e, st) {
log(e.toString(), stackTrace: st);
return '';
}
}

View File

@@ -1079,7 +1079,9 @@ class _ProfileScreenState extends State<ProfileScreen> {
onTap: () async {
GoRouter.of(context).pushNamed(
'chatScreen',
extra: await profileCubit.getChatRoomIdBySlug(slug: widget.author!.authorSlug),
extra: {
'chatId': await profileCubit.getChatRoomIdBySlug(slug: widget.author!.authorSlug),
},
);
},
child: Container(

View File

@@ -62,8 +62,10 @@ class _SubActionButtonWidgetState extends State<SubActionButtonWidget> {
return InkWell(
onTap: () async {
GoRouter.of(context).pushNamed(
'chatScreen',
extra: await sl<ProfileCubit>().getChatRoomIdBySlug(slug: widget.slug),
'chatScreen',
extra: {
'chatId': await sl<ProfileCubit>().getChatRoomIdBySlug(slug: widget.slug),
},
);
},
borderRadius: BorderRadius.circular(8),

View File

@@ -39,10 +39,8 @@ MessageLookupByLibrary? _findExact(String localeName) {
/// User programs should call this before using [localeName] for messages.
Future<bool> initializeMessages(String localeName) {
var availableLocale = Intl.verifiedLocale(
localeName,
(locale) => _deferredLibraries[locale] != null,
onFailure: (_) => null,
);
localeName, (locale) => _deferredLibraries[locale] != null,
onFailure: (_) => null);
if (availableLocale == null) {
return new SynchronousFuture(false);
}
@@ -62,11 +60,8 @@ bool _messagesExistFor(String locale) {
}
MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) {
var actualLocale = Intl.verifiedLocale(
locale,
_messagesExistFor,
onFailure: (_) => null,
);
var actualLocale =
Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null);
if (actualLocale == null) return null;
return _findExact(actualLocale);
}

File diff suppressed because it is too large Load Diff

View File

@@ -22,353 +22,301 @@ class MessageLookup extends MessageLookupByLibrary {
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
"Moscow": MessageLookupByLibrary.simpleMessage("Москва"),
"Russia": MessageLookupByLibrary.simpleMessage("Россия"),
"about_diet": MessageLookupByLibrary.simpleMessage("Об этой диете"),
"about_plan": MessageLookupByLibrary.simpleMessage("Об этом плане"),
"active": MessageLookupByLibrary.simpleMessage("Активен"),
"active_diet_state": MessageLookupByLibrary.simpleMessage("Активная"),
"add": MessageLookupByLibrary.simpleMessage("Добавить"),
"add_cover": MessageLookupByLibrary.simpleMessage("Добавьте обложку"),
"add_measure": MessageLookupByLibrary.simpleMessage("Добавить замер"),
"adding_plan": MessageLookupByLibrary.simpleMessage("Добавление плана"),
"additional_information": MessageLookupByLibrary.simpleMessage(
"Дополнительная информация",
),
"admin": MessageLookupByLibrary.simpleMessage("Админ"),
"all": MessageLookupByLibrary.simpleMessage("Все"),
"allergies_presence": 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("График ИМТ"),
"by_default": MessageLookupByLibrary.simpleMessage("По умолчянию"),
"calories": MessageLookupByLibrary.simpleMessage("Калории"),
"carb_type": MessageLookupByLibrary.simpleMessage("Мучное"),
"carb_type_desc": MessageLookupByLibrary.simpleMessage(
"Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.",
),
"carbohydrates": MessageLookupByLibrary.simpleMessage("Углеводы"),
"chat": MessageLookupByLibrary.simpleMessage("Чат"),
"chest": MessageLookupByLibrary.simpleMessage("Грудь"),
"chest_circumference": MessageLookupByLibrary.simpleMessage("Обхват груди"),
"choose_diet_duration": MessageLookupByLibrary.simpleMessage(
"Выберите срок диеты",
),
"choose_plan_duration": MessageLookupByLibrary.simpleMessage(
"Выберите срок плана",
),
"choose_start_diet_date": 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("Город проживания"),
"comfort": MessageLookupByLibrary.simpleMessage("Комфорт"),
"comment": MessageLookupByLibrary.simpleMessage("комментарий"),
"comments": MessageLookupByLibrary.simpleMessage("Комментарии"),
"comments_plural": MessageLookupByLibrary.simpleMessage("комментариев"),
"complain": MessageLookupByLibrary.simpleMessage("Пожаловаться"),
"continueButton": MessageLookupByLibrary.simpleMessage("Продолжить"),
"continue_": MessageLookupByLibrary.simpleMessage("Продолжить"),
"copied": MessageLookupByLibrary.simpleMessage("cкопирован"),
"copy_link": MessageLookupByLibrary.simpleMessage("Скопировать ссылку"),
"country": MessageLookupByLibrary.simpleMessage("Страна"),
"create_diet": MessageLookupByLibrary.simpleMessage("Создание диеты"),
"customer_price": MessageLookupByLibrary.simpleMessage(
"Цена для покупателя",
),
"date_of_birth": MessageLookupByLibrary.simpleMessage("Дата рождения"),
"day": MessageLookupByLibrary.simpleMessage("День"),
"days": MessageLookupByLibrary.simpleMessage("дней"),
"deleted": MessageLookupByLibrary.simpleMessage("Удалена"),
"diary": MessageLookupByLibrary.simpleMessage("Дневник"),
"diet": MessageLookupByLibrary.simpleMessage("Диета"),
"diet_author": MessageLookupByLibrary.simpleMessage("Автор диеты"),
"diet_plan": MessageLookupByLibrary.simpleMessage("План диеты"),
"diet_state": MessageLookupByLibrary.simpleMessage("Статус диеты"),
"diet_type": 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("Черновик"),
"drafted": MessageLookupByLibrary.simpleMessage("Черновики"),
"eating_behavior": MessageLookupByLibrary.simpleMessage(
"Пищевое поведение",
),
"eating_behavior_type": MessageLookupByLibrary.simpleMessage(
"Формирование пищевого поведения",
),
"eating_behavior_type_desc": MessageLookupByLibrary.simpleMessage(
"Формирование здорового пищевого поведения — это процесс, который включает в себя изменение привычек и отношения к еде. Он направлен на то, чтобы помочь людям научиться правильно питаться и поддерживать здоровый образ жизни.Основные принципы формирования здорового пищевого поведения: \n● Разнообразие продуктов; \n● Сбалансированный рацион; \n● Питьевой режим; \n● Ограничение вредных продуктов; \n● Регулярные приёмы пищи; \n● Умеренность;Осознанное питание; \n● Физическая активность;",
),
"economy": MessageLookupByLibrary.simpleMessage("Эконом"),
"edit": MessageLookupByLibrary.simpleMessage("Редактировать"),
"email": MessageLookupByLibrary.simpleMessage("Электронная почта"),
"email_empty": MessageLookupByLibrary.simpleMessage(
"Необходимо ввести адрес электронной почты",
),
"email_invalid": MessageLookupByLibrary.simpleMessage(
"Некорректный адрес электронной почты",
),
"enter_additional_information": MessageLookupByLibrary.simpleMessage(
"Введите любую дополнительную информацию на своё усмотрение:",
),
"enter_diet_description": MessageLookupByLibrary.simpleMessage(
"Введите описание диеты",
),
"enter_diet_name": MessageLookupByLibrary.simpleMessage(
"Выведите название диеты",
),
"enter_information": MessageLookupByLibrary.simpleMessage(
"Введите информацию...",
),
"exit": MessageLookupByLibrary.simpleMessage("Выйти"),
"expand": MessageLookupByLibrary.simpleMessage("Показать полностью"),
"fats": 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 символ",
),
"fit_type": MessageLookupByLibrary.simpleMessage("Поддержание формы"),
"fit_type_desc": MessageLookupByLibrary.simpleMessage(
"План питания для поддержания формы сбалансированный и разнообразный, чтобы обеспечить организм всеми необходимыми питательными веществами. Общие принципы: \n● Дробное питание; \n● Разнообразие продуктов; \n● Контроль порций; \n● Питьевой режим; \n● Ограничение вредных продуктов;",
),
"fitness": MessageLookupByLibrary.simpleMessage("Поддержка формы"),
"from": MessageLookupByLibrary.simpleMessage("от"),
"from_a_to_z": MessageLookupByLibrary.simpleMessage("Название: А-Я"),
"from_z_to_a": MessageLookupByLibrary.simpleMessage("Название: Я-А"),
"gallery": MessageLookupByLibrary.simpleMessage("Галерея"),
"gr": MessageLookupByLibrary.simpleMessage("гр"),
"grams": MessageLookupByLibrary.simpleMessage("гр"),
"height_measurements": MessageLookupByLibrary.simpleMessage("Замер роста"),
"hide": MessageLookupByLibrary.simpleMessage("Свернуть"),
"hip_circumference": MessageLookupByLibrary.simpleMessage("Обхват бедер"),
"hips": MessageLookupByLibrary.simpleMessage("Бедра"),
"in_progress": MessageLookupByLibrary.simpleMessage("В процессе"),
"inactive_diet_state": MessageLookupByLibrary.simpleMessage("Черновик"),
"infoAboutOrder": MessageLookupByLibrary.simpleMessage(
"Информация о заказе",
),
"initial_measurements": MessageLookupByLibrary.simpleMessage(
"Первичные замеры",
),
"label": 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"),
"light_type": MessageLookupByLibrary.simpleMessage("Легкая"),
"light_type_desc": MessageLookupByLibrary.simpleMessage(
"С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками.",
),
"link_was_copied": MessageLookupByLibrary.simpleMessage(
"Ссылка успешно скопирована",
),
"loading": MessageLookupByLibrary.simpleMessage("Загрузка..."),
"male": MessageLookupByLibrary.simpleMessage("Мужской"),
"meal": MessageLookupByLibrary.simpleMessage("Приём пищи"),
"meat": MessageLookupByLibrary.simpleMessage("Мясо"),
"meat_type": MessageLookupByLibrary.simpleMessage("С мясом"),
"meat_type_desc": MessageLookupByLibrary.simpleMessage(
"Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму.",
),
"milliliter": MessageLookupByLibrary.simpleMessage("мл"),
"minimal_customer_price": MessageLookupByLibrary.simpleMessage(
"Минимальная цена для покупателя: 1200₽",
),
"most_new": MessageLookupByLibrary.simpleMessage("Самые новые"),
"most_old": MessageLookupByLibrary.simpleMessage("Самые старые"),
"msg_network_err": MessageLookupByLibrary.simpleMessage("Сетевая ошибка!"),
"msg_something_went_wrong": MessageLookupByLibrary.simpleMessage(
"Что то пошло не так!",
),
"my_blog": MessageLookupByLibrary.simpleMessage("Мой блог"),
"my_diets": MessageLookupByLibrary.simpleMessage("Мои диеты"),
"my_plans": MessageLookupByLibrary.simpleMessage("Мои планы"),
"my_settings": MessageLookupByLibrary.simpleMessage("Мои настройки"),
"name_empty": MessageLookupByLibrary.simpleMessage(
"Имя пользователя обязательно для заполнения",
),
"name_short": MessageLookupByLibrary.simpleMessage(
"В имени пользователя должно быть минимум 6 символов",
),
"no": MessageLookupByLibrary.simpleMessage("Нет"),
"no_account": MessageLookupByLibrary.simpleMessage("У меня нет аккаунта"),
"one_option": MessageLookupByLibrary.simpleMessage("1 опция"),
"option": MessageLookupByLibrary.simpleMessage("Опция"),
"option_content": MessageLookupByLibrary.simpleMessage("Содержание опции"),
"option_content_hint_text": MessageLookupByLibrary.simpleMessage(
"На связи 7 дней в неделю",
),
"option_desc": MessageLookupByLibrary.simpleMessage("Описание опции"),
"option_desc_hint_text": MessageLookupByLibrary.simpleMessage(
"Этот план питания для похудения на 7 дней сбалансированный и разнообразный",
),
"option_duration": MessageLookupByLibrary.simpleMessage(
"Продолжительность опции",
),
"option_personal_diet_desc": MessageLookupByLibrary.simpleMessage(
"Опция подразумевает создание индивидуальной диеты",
),
"option_title": MessageLookupByLibrary.simpleMessage("Заголовок опции"),
"order": 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("шт"),
"personal": MessageLookupByLibrary.simpleMessage("Персональные"),
"personal_diet_state": MessageLookupByLibrary.simpleMessage("Персональная"),
"photo": MessageLookupByLibrary.simpleMessage("Фото"),
"place_of_living": MessageLookupByLibrary.simpleMessage("Место проживания"),
"plan_author": MessageLookupByLibrary.simpleMessage("Автор плана"),
"plan_desc": MessageLookupByLibrary.simpleMessage("Описание плана"),
"plan_desc_example": MessageLookupByLibrary.simpleMessage(
"Большое подробное, привлекательное описание плана",
),
"plan_duration": MessageLookupByLibrary.simpleMessage("Срок плана"),
"plan_label_desc": MessageLookupByLibrary.simpleMessage(
"План для поддержания формы",
),
"plans_complete": MessageLookupByLibrary.simpleMessage("Пройденные планы"),
"popular": MessageLookupByLibrary.simpleMessage("Популярные"),
"preferred_language": MessageLookupByLibrary.simpleMessage(
"Предпочитаемый язык общения",
),
"pressure_empty": MessageLookupByLibrary.simpleMessage(
"Все показатели давления обязательны к заполнению",
),
"pressure_is_not_a_number": MessageLookupByLibrary.simpleMessage(
"Все показатели давления должны быть числами",
),
"preview": MessageLookupByLibrary.simpleMessage("Превью"),
"price": MessageLookupByLibrary.simpleMessage("Цена"),
"price_explanation": MessageLookupByLibrary.simpleMessage(
"Разница в ценах, это комиссия Итвио",
),
"profile": MessageLookupByLibrary.simpleMessage("Профиль"),
"proteins": MessageLookupByLibrary.simpleMessage("Белки"),
"publish": MessageLookupByLibrary.simpleMessage("Опубликовать"),
"published": MessageLookupByLibrary.simpleMessage("Опубликованные"),
"removeFromActive": MessageLookupByLibrary.simpleMessage(
"Снять с публикации",
),
"save": MessageLookupByLibrary.simpleMessage("Сохранить"),
"services": MessageLookupByLibrary.simpleMessage("Оказание услуг"),
"settings": MessageLookupByLibrary.simpleMessage("Настройки"),
"share": MessageLookupByLibrary.simpleMessage("Поделиться"),
"skip": MessageLookupByLibrary.simpleMessage("Пропустить"),
"slim": MessageLookupByLibrary.simpleMessage("Похудение"),
"slim_type": MessageLookupByLibrary.simpleMessage("Похудение"),
"slim_type_desc": MessageLookupByLibrary.simpleMessage(
"План питания для похудения должен быть разработан с учётом индивидуальных особенностей организма, возраста, пола, уровня физической активности и состояния здоровья. Общие принципы: \n● Снижение калорийности; \n● Сбалансированный рацион; \n● Разнообразие продуктов; \n● Питьевой режим; \n● Ограничение вредных продуктов;",
),
"standard": MessageLookupByLibrary.simpleMessage("Стандарт"),
"startDiet": MessageLookupByLibrary.simpleMessage("Начать диету"),
"tell_your_schedule": MessageLookupByLibrary.simpleMessage(
"Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:",
),
"three_options": MessageLookupByLibrary.simpleMessage("3 опции"),
"toOffer": MessageLookupByLibrary.simpleMessage("Предложить"),
"to_draft": MessageLookupByLibrary.simpleMessage("В черновик"),
"unfavorite_foods": MessageLookupByLibrary.simpleMessage(
"Нелюбимые продукты",
),
"up_pressure": MessageLookupByLibrary.simpleMessage(
"Верхние показатели давления:",
),
"user_authorization": MessageLookupByLibrary.simpleMessage(
"Аутентификация",
),
"user_data": MessageLookupByLibrary.simpleMessage("Данные пользователя"),
"user_registration": MessageLookupByLibrary.simpleMessage(
"Регистрация пользователя",
),
"username": MessageLookupByLibrary.simpleMessage("Имя пользователя"),
"vegan": MessageLookupByLibrary.simpleMessage("Веган."),
"vegan_type": MessageLookupByLibrary.simpleMessage("Вегетарианская"),
"vegan_type_desc": MessageLookupByLibrary.simpleMessage(
"Диета основана на потреблении растительной пищи и отказе от продуктов животного происхождения. Такой тип питания может быть разнообразным и вкусным, а также полезным для здоровья.",
),
"waist": MessageLookupByLibrary.simpleMessage("Талия"),
"waist_circumference": MessageLookupByLibrary.simpleMessage("Обхват талии"),
"weight": MessageLookupByLibrary.simpleMessage("Вес"),
"weight_chart": MessageLookupByLibrary.simpleMessage("График веса"),
"weight_measurements": MessageLookupByLibrary.simpleMessage("Замер веса"),
"wellness": MessageLookupByLibrary.simpleMessage("Улучшение самочувствия"),
"wellness_type": MessageLookupByLibrary.simpleMessage(
"Улучшение самочувствия",
),
"wellness_type_desc": MessageLookupByLibrary.simpleMessage(
"Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.",
),
"what_is_your_name": MessageLookupByLibrary.simpleMessage("Как вас зовут?"),
"wrong_credentials": MessageLookupByLibrary.simpleMessage(
"Неверные данные пользователя",
),
"you_will_get": 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("Ваш вес:"),
};
"Moscow": MessageLookupByLibrary.simpleMessage("Москва"),
"Russia": MessageLookupByLibrary.simpleMessage("Россия"),
"about_diet": MessageLookupByLibrary.simpleMessage("Об этой диете"),
"about_plan": MessageLookupByLibrary.simpleMessage("Об этом плане"),
"active": MessageLookupByLibrary.simpleMessage("Активен"),
"active_diet_state": MessageLookupByLibrary.simpleMessage("Активная"),
"add": MessageLookupByLibrary.simpleMessage("Добавить"),
"add_cover": MessageLookupByLibrary.simpleMessage("Добавьте обложку"),
"add_measure": MessageLookupByLibrary.simpleMessage("Добавить замер"),
"adding_plan": MessageLookupByLibrary.simpleMessage("Добавление плана"),
"additional_information":
MessageLookupByLibrary.simpleMessage("Дополнительная информация"),
"admin": MessageLookupByLibrary.simpleMessage("Админ"),
"all": MessageLookupByLibrary.simpleMessage("Все"),
"allergies_presence":
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("График ИМТ"),
"by_default": MessageLookupByLibrary.simpleMessage("По умолчянию"),
"calories": MessageLookupByLibrary.simpleMessage("Калории"),
"carb_type": MessageLookupByLibrary.simpleMessage("Мучное"),
"carb_type_desc": MessageLookupByLibrary.simpleMessage(
"Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы."),
"carbohydrates": MessageLookupByLibrary.simpleMessage("Углеводы"),
"chat": MessageLookupByLibrary.simpleMessage("Чат"),
"chest": MessageLookupByLibrary.simpleMessage("Грудь"),
"chest_circumference":
MessageLookupByLibrary.simpleMessage("Обхват груди"),
"choose_diet_duration":
MessageLookupByLibrary.simpleMessage("Выберите срок диеты"),
"choose_plan_duration":
MessageLookupByLibrary.simpleMessage("Выберите срок плана"),
"choose_start_diet_date": 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("Город проживания"),
"comfort": MessageLookupByLibrary.simpleMessage("Комфорт"),
"comment": MessageLookupByLibrary.simpleMessage("комментарий"),
"comments": MessageLookupByLibrary.simpleMessage("Комментарии"),
"comments_plural": MessageLookupByLibrary.simpleMessage("комментариев"),
"complain": MessageLookupByLibrary.simpleMessage("Пожаловаться"),
"continueButton": MessageLookupByLibrary.simpleMessage("Продолжить"),
"continue_": MessageLookupByLibrary.simpleMessage("Продолжить"),
"copied": MessageLookupByLibrary.simpleMessage("cкопирован"),
"copy_link": MessageLookupByLibrary.simpleMessage("Скопировать ссылку"),
"country": MessageLookupByLibrary.simpleMessage("Страна"),
"create_diet": MessageLookupByLibrary.simpleMessage("Создание диеты"),
"customer_price":
MessageLookupByLibrary.simpleMessage("Цена для покупателя"),
"date_of_birth": MessageLookupByLibrary.simpleMessage("Дата рождения"),
"day": MessageLookupByLibrary.simpleMessage("День"),
"days": MessageLookupByLibrary.simpleMessage("дней"),
"deleted": MessageLookupByLibrary.simpleMessage("Удалена"),
"diary": MessageLookupByLibrary.simpleMessage("Дневник"),
"diet": MessageLookupByLibrary.simpleMessage("Диета"),
"diet_author": MessageLookupByLibrary.simpleMessage("Автор диеты"),
"diet_plan": MessageLookupByLibrary.simpleMessage("План диеты"),
"diet_state": MessageLookupByLibrary.simpleMessage("Статус диеты"),
"diet_type": 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("Черновик"),
"drafted": MessageLookupByLibrary.simpleMessage("Черновики"),
"eating_behavior":
MessageLookupByLibrary.simpleMessage("Пищевое поведение"),
"eating_behavior_type": MessageLookupByLibrary.simpleMessage(
"Формирование пищевого поведения"),
"eating_behavior_type_desc": MessageLookupByLibrary.simpleMessage(
"Формирование здорового пищевого поведения — это процесс, который включает в себя изменение привычек и отношения к еде. Он направлен на то, чтобы помочь людям научиться правильно питаться и поддерживать здоровый образ жизни.Основные принципы формирования здорового пищевого поведения: \n● Разнообразие продуктов; \n● Сбалансированный рацион; \n● Питьевой режим; \n● Ограничение вредных продуктов; \n● Регулярные приёмы пищи; \n● Умеренность;Осознанное питание; \n● Физическая активность;"),
"economy": MessageLookupByLibrary.simpleMessage("Эконом"),
"edit": MessageLookupByLibrary.simpleMessage("Редактировать"),
"email": MessageLookupByLibrary.simpleMessage("Электронная почта"),
"email_empty": MessageLookupByLibrary.simpleMessage(
"Необходимо ввести адрес электронной почты"),
"email_invalid": MessageLookupByLibrary.simpleMessage(
"Некорректный адрес электронной почты"),
"enter_additional_information": MessageLookupByLibrary.simpleMessage(
"Введите любую дополнительную информацию на своё усмотрение:"),
"enter_diet_description":
MessageLookupByLibrary.simpleMessage("Введите описание диеты"),
"enter_diet_name":
MessageLookupByLibrary.simpleMessage("Выведите название диеты"),
"enter_information":
MessageLookupByLibrary.simpleMessage("Введите информацию..."),
"exit": MessageLookupByLibrary.simpleMessage("Выйти"),
"expand": MessageLookupByLibrary.simpleMessage("Показать полностью"),
"fats": 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 символ"),
"fit_type": MessageLookupByLibrary.simpleMessage("Поддержание формы"),
"fit_type_desc": MessageLookupByLibrary.simpleMessage(
"План питания для поддержания формы сбалансированный и разнообразный, чтобы обеспечить организм всеми необходимыми питательными веществами. Общие принципы: \n● Дробное питание; \n● Разнообразие продуктов; \n● Контроль порций; \n● Питьевой режим; \n● Ограничение вредных продуктов;"),
"fitness": MessageLookupByLibrary.simpleMessage("Поддержка формы"),
"from": MessageLookupByLibrary.simpleMessage("от"),
"from_a_to_z": MessageLookupByLibrary.simpleMessage("Название: А-Я"),
"from_z_to_a": MessageLookupByLibrary.simpleMessage("Название: Я-А"),
"gallery": MessageLookupByLibrary.simpleMessage("Галерея"),
"gr": MessageLookupByLibrary.simpleMessage("гр"),
"grams": MessageLookupByLibrary.simpleMessage("гр"),
"height_measurements":
MessageLookupByLibrary.simpleMessage("Замер роста"),
"hide": MessageLookupByLibrary.simpleMessage("Свернуть"),
"hip_circumference":
MessageLookupByLibrary.simpleMessage("Обхват бедер"),
"hips": MessageLookupByLibrary.simpleMessage("Бедра"),
"in_progress": MessageLookupByLibrary.simpleMessage("В процессе"),
"inactive_diet_state": MessageLookupByLibrary.simpleMessage("Черновик"),
"infoAboutOrder":
MessageLookupByLibrary.simpleMessage("Информация о заказе"),
"initial_measurements":
MessageLookupByLibrary.simpleMessage("Первичные замеры"),
"label": 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"),
"light_type": MessageLookupByLibrary.simpleMessage("Легкая"),
"light_type_desc": MessageLookupByLibrary.simpleMessage(
"С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками."),
"link_was_copied":
MessageLookupByLibrary.simpleMessage("Ссылка успешно скопирована"),
"loading": MessageLookupByLibrary.simpleMessage("Загрузка..."),
"male": MessageLookupByLibrary.simpleMessage("Мужской"),
"meal": MessageLookupByLibrary.simpleMessage("Приём пищи"),
"meat": MessageLookupByLibrary.simpleMessage("Мясо"),
"meat_type": MessageLookupByLibrary.simpleMessage("С мясом"),
"meat_type_desc": MessageLookupByLibrary.simpleMessage(
"Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму."),
"milliliter": MessageLookupByLibrary.simpleMessage("мл"),
"minimal_customer_price": MessageLookupByLibrary.simpleMessage(
"Минимальная цена для покупателя: 1200₽"),
"most_new": MessageLookupByLibrary.simpleMessage("Самые новые"),
"most_old": MessageLookupByLibrary.simpleMessage("Самые старые"),
"msg_network_err":
MessageLookupByLibrary.simpleMessage("Сетевая ошибка!"),
"msg_something_went_wrong":
MessageLookupByLibrary.simpleMessage("Что то пошло не так!"),
"my_blog": MessageLookupByLibrary.simpleMessage("Мой блог"),
"my_diets": MessageLookupByLibrary.simpleMessage("Мои диеты"),
"my_plans": MessageLookupByLibrary.simpleMessage("Мои планы"),
"my_settings": MessageLookupByLibrary.simpleMessage("Мои настройки"),
"name_empty": MessageLookupByLibrary.simpleMessage(
"Имя пользователя обязательно для заполнения"),
"name_short": MessageLookupByLibrary.simpleMessage(
"В имени пользователя должно быть минимум 6 символов"),
"no": MessageLookupByLibrary.simpleMessage("Нет"),
"no_account":
MessageLookupByLibrary.simpleMessage("У меня нет аккаунта"),
"one_option": MessageLookupByLibrary.simpleMessage("1 опция"),
"option": MessageLookupByLibrary.simpleMessage("Опция"),
"option_content":
MessageLookupByLibrary.simpleMessage("Содержание опции"),
"option_content_hint_text":
MessageLookupByLibrary.simpleMessage("На связи 7 дней в неделю"),
"option_desc": MessageLookupByLibrary.simpleMessage("Описание опции"),
"option_desc_hint_text": MessageLookupByLibrary.simpleMessage(
"Этот план питания для похудения на 7 дней сбалансированный и разнообразный"),
"option_duration":
MessageLookupByLibrary.simpleMessage("Продолжительность опции"),
"option_personal_diet_desc": MessageLookupByLibrary.simpleMessage(
"Опция подразумевает создание индивидуальной диеты"),
"option_title": MessageLookupByLibrary.simpleMessage("Заголовок опции"),
"order": 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("шт"),
"personal": MessageLookupByLibrary.simpleMessage("Персональные"),
"personal_diet_state":
MessageLookupByLibrary.simpleMessage("Персональная"),
"photo": MessageLookupByLibrary.simpleMessage("Фото"),
"place_of_living":
MessageLookupByLibrary.simpleMessage("Место проживания"),
"plan_author": MessageLookupByLibrary.simpleMessage("Автор плана"),
"plan_desc": MessageLookupByLibrary.simpleMessage("Описание плана"),
"plan_desc_example": MessageLookupByLibrary.simpleMessage(
"Большое подробное, привлекательное описание плана"),
"plan_duration": MessageLookupByLibrary.simpleMessage("Срок плана"),
"plan_label_desc":
MessageLookupByLibrary.simpleMessage("План для поддержания формы"),
"plans_complete":
MessageLookupByLibrary.simpleMessage("Пройденные планы"),
"popular": MessageLookupByLibrary.simpleMessage("Популярные"),
"preferred_language":
MessageLookupByLibrary.simpleMessage("Предпочитаемый язык общения"),
"pressure_empty": MessageLookupByLibrary.simpleMessage(
"Все показатели давления обязательны к заполнению"),
"pressure_is_not_a_number": MessageLookupByLibrary.simpleMessage(
"Все показатели давления должны быть числами"),
"preview": MessageLookupByLibrary.simpleMessage("Превью"),
"price": MessageLookupByLibrary.simpleMessage("Цена"),
"price_explanation": MessageLookupByLibrary.simpleMessage(
"Разница в ценах, это комиссия Итвио"),
"profile": MessageLookupByLibrary.simpleMessage("Профиль"),
"proteins": MessageLookupByLibrary.simpleMessage("Белки"),
"publish": MessageLookupByLibrary.simpleMessage("Опубликовать"),
"published": MessageLookupByLibrary.simpleMessage("Опубликованные"),
"removeFromActive":
MessageLookupByLibrary.simpleMessage("Снять с публикации"),
"save": MessageLookupByLibrary.simpleMessage("Сохранить"),
"services": MessageLookupByLibrary.simpleMessage("Оказание услуг"),
"settings": MessageLookupByLibrary.simpleMessage("Настройки"),
"share": MessageLookupByLibrary.simpleMessage("Поделиться"),
"skip": MessageLookupByLibrary.simpleMessage("Пропустить"),
"slim": MessageLookupByLibrary.simpleMessage("Похудение"),
"slim_type": MessageLookupByLibrary.simpleMessage("Похудение"),
"slim_type_desc": MessageLookupByLibrary.simpleMessage(
"План питания для похудения должен быть разработан с учётом индивидуальных особенностей организма, возраста, пола, уровня физической активности и состояния здоровья. Общие принципы: \n● Снижение калорийности; \n● Сбалансированный рацион; \n● Разнообразие продуктов; \n● Питьевой режим; \n● Ограничение вредных продуктов;"),
"standard": MessageLookupByLibrary.simpleMessage("Стандарт"),
"startDiet": MessageLookupByLibrary.simpleMessage("Начать диету"),
"tell_your_schedule": MessageLookupByLibrary.simpleMessage(
"Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:"),
"three_options": MessageLookupByLibrary.simpleMessage("3 опции"),
"toOffer": MessageLookupByLibrary.simpleMessage("Предложить"),
"to_draft": MessageLookupByLibrary.simpleMessage("В черновик"),
"unfavorite_foods":
MessageLookupByLibrary.simpleMessage("Нелюбимые продукты"),
"up_pressure": MessageLookupByLibrary.simpleMessage(
"Верхние показатели давления:"),
"user_authorization":
MessageLookupByLibrary.simpleMessage("Аутентификация"),
"user_data":
MessageLookupByLibrary.simpleMessage("Данные пользователя"),
"user_registration":
MessageLookupByLibrary.simpleMessage("Регистрация пользователя"),
"username": MessageLookupByLibrary.simpleMessage("Имя пользователя"),
"vegan": MessageLookupByLibrary.simpleMessage("Веган."),
"vegan_type": MessageLookupByLibrary.simpleMessage("Вегетарианская"),
"vegan_type_desc": MessageLookupByLibrary.simpleMessage(
"Диета основана на потреблении растительной пищи и отказе от продуктов животного происхождения. Такой тип питания может быть разнообразным и вкусным, а также полезным для здоровья."),
"waist": MessageLookupByLibrary.simpleMessage("Талия"),
"waist_circumference":
MessageLookupByLibrary.simpleMessage("Обхват талии"),
"weight": MessageLookupByLibrary.simpleMessage("Вес"),
"weight_chart": MessageLookupByLibrary.simpleMessage("График веса"),
"weight_measurements":
MessageLookupByLibrary.simpleMessage("Замер веса"),
"wellness":
MessageLookupByLibrary.simpleMessage("Улучшение самочувствия"),
"wellness_type":
MessageLookupByLibrary.simpleMessage("Улучшение самочувствия"),
"wellness_type_desc": MessageLookupByLibrary.simpleMessage(
"Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы."),
"what_is_your_name":
MessageLookupByLibrary.simpleMessage("Как вас зовут?"),
"wrong_credentials": MessageLookupByLibrary.simpleMessage(
"Неверные данные пользователя"),
"you_will_get": 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("Ваш вес:")
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -312,6 +312,18 @@
"could_not_publish_blog_post": "Не удалось опубликовать пост",
"could_not_update_blog_post": "Не удалось обновить пост",
"could_not_delete_blog_post": "Не удалось удалить пост",
"infoAboutOrder": "Информация о заказе"
"infoAboutOrder": "Информация о заказе",
"to_order": "Заказать",
"can_not_choose_start_date": "Дату старта плана невозможно выбрать, пока Вы не привяжите карту",
"add_card": "Привязать карту",
"choose_plan_start_date": "Выберите дату старта плана питания",
"choose_date": "Выбрать дату",
"date_has_ben_set": "Дата старта начала консультации установлена",
"tap_on_link_to)pay": "Перейдите по ссылке для оплаты консультации",
"pay": "Оплатить",
"more_about_order": "Подробнее о заказе",
"cancel_order": "Отменить заказ",
"refuse_order":"Отказаться от заказа"
}