Merge pull request 'Issues-236, 332, 256' (#698) from mso7nueh/eatvio_app:main into main

Reviewed-on: eatvio/eatvio_app#698
This commit is contained in:
mso7nueh
2025-09-12 10:48:22 +00:00
5 changed files with 100 additions and 135 deletions

View File

@@ -137,7 +137,7 @@ Future<void> init() async {
sl.registerFactory<DietsCubit>(() => DietsCubit(feedRepository: sl(), dietsRepository: sl()));
sl.registerFactory<CommentsCubit>(() => CommentsCubit(feedRepository: sl(), commentsRepository: sl()));
sl.registerFactory<PlansCubit>(() => PlansCubit(feedRepository: sl(), planRepository: sl()));
sl.registerFactory<SettingsCubit>(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl(), consultantRepository: sl()));
sl.registerFactory<SettingsCubit>(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl(), consultantRepository: sl(), consultationsRepository: sl()));
sl.registerFactory<RegisterConsultantCubit>(() => RegisterConsultantCubit(consultantRepository: sl()));
sl.registerFactory<DiaryCubit>(
() => DiaryCubit(

View File

@@ -33,21 +33,12 @@ class _UserPurchasesScreen extends State<UserPurchasesScreen> {
return Scaffold(
backgroundColor: const Color(0xFFF0F2F5),
appBar: AppBar(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadiusDirectional.vertical(
bottom: Radius.circular(12),
),
),
title: Text(
'Финансы',
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
shape: const RoundedRectangleBorder(borderRadius: BorderRadiusDirectional.vertical(bottom: Radius.circular(12))),
title: Text('Финансы', style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black)),
centerTitle: true,
),
body: state.isLoading
? const Center(
child: Loader(),
)
? const Center(child: Loader())
: SingleChildScrollView(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Column(
@@ -67,10 +58,7 @@ class _UserPurchasesScreen extends State<UserPurchasesScreen> {
.map(
(value) => DropdownMenuItem(
value: value,
child: Text(
value.name,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
child: Text(value.name, style: TextStyles.textMMedium.copyWith(color: appTheme.black)),
),
)
.toList(),
@@ -105,60 +93,41 @@ class _UserPurchasesScreen extends State<UserPurchasesScreen> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Builder(builder: (context) {
final idLen = state.purchasesModels[index].bankId.length;
return Text(
'${state.purchasesModels[index].bankId.substring(0, 4)}...${state.purchasesModels[index].bankId.substring(idLen - 4, idLen)} ',
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
);
}),
Builder(
builder: (context) {
final idLen = state.purchasesModels[index].bankId.length;
return Text(
'${state.purchasesModels[index].bankId.substring(0, 4)}...${state.purchasesModels[index].bankId.substring(idLen - 4, idLen)} ',
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
);
},
),
Text(
state.purchasesModels[index].startDate.format('yyyy.MM.dd'),
style: const TextStyle(
color: Color(0xFF6D6F71),
height: 1,
fontWeight: FontWeight.w500,
),
state.purchasesModels[index].startDate.format('dd.MM.yyyy'),
style: const TextStyle(color: Color(0xFF6D6F71), height: 1, fontWeight: FontWeight.w500),
),
],
),
const SizedBox(height: 8),
Text(
state.purchasesModels[index].title,
style: TextStyle(
color: appTheme.blueA400,
height: 1,
fontSize: 18,
fontWeight: FontWeight.w600,
),
style: TextStyle(color: appTheme.blueA400, height: 1, fontSize: 18, fontWeight: FontWeight.w600),
),
const SizedBox(height: 8),
Text(
'Продавец: ${state.purchasesModels[index].consultantName}',
style: const TextStyle(
color: Color(0xFF323F4B),
height: 1,
fontSize: 14,
fontWeight: FontWeight.w500,
),
style: const TextStyle(color: Color(0xFF323F4B), height: 1, fontSize: 14, fontWeight: FontWeight.w500),
),
const SizedBox(height: 8),
Text(
'${state.purchasesModels[index].duration} ${getDayForm(state.purchasesModels[index].duration, s)}',
style: const TextStyle(
color: Color(0xFF323F4B),
height: 1,
fontSize: 14,
fontWeight: FontWeight.w500,
),
style: const TextStyle(color: Color(0xFF323F4B), height: 1, fontSize: 14, fontWeight: FontWeight.w500),
),
const SizedBox(height: 8),
Container(
height: 1,
width: double.infinity,
decoration: const BoxDecoration(
color: Color(0xFFDEDEDF),
),
decoration: const BoxDecoration(color: Color(0xFFDEDEDF)),
),
const SizedBox(height: 8),
Row(
@@ -166,16 +135,9 @@ class _UserPurchasesScreen extends State<UserPurchasesScreen> {
children: [
Text(
'${state.purchasesModels[index].paymentPrice} ₽',
style: const TextStyle(
color: Color(0xFF323F4B),
height: 1,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
StatusWidget(
status: purchaseStatus,
style: const TextStyle(color: Color(0xFF323F4B), height: 1, fontSize: 18, fontWeight: FontWeight.w600),
),
StatusWidget(status: purchaseStatus),
],
),
],
@@ -189,10 +151,7 @@ class _UserPurchasesScreen extends State<UserPurchasesScreen> {
else
const Padding(
padding: EdgeInsets.symmetric(vertical: 40.0),
child: Text(
'У вас нет таких консультаций',
style: TextStyles.textMMedium,
),
child: Text('У вас нет таких консультаций', style: TextStyles.textMMedium),
),
],
),
@@ -200,7 +159,8 @@ class _UserPurchasesScreen extends State<UserPurchasesScreen> {
),
),
],
)),
),
),
);
},
),

View File

@@ -1,5 +1,7 @@
import 'package:eatvio_app/features/consultant/become_consultant/models/consultation_request_model.dart';
import 'package:eatvio_app/features/consultant/become_consultant/repositories/consultant_repository.dart';
import 'package:eatvio_app/features/consultations/repositories/consultations_repository.dart';
import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
import 'package:eatvio_app/features/profile/models/profile_model.dart';
import 'package:eatvio_app/features/profile/repositories/profile_repository.dart';
import 'package:eatvio_app/features/settings/models/profile_settings_model.dart';
@@ -11,16 +13,20 @@ class SettingsCubit extends Cubit<SettingsState> {
final ProfileRepository profileRepository;
final SettingsRepository settingsRepository;
final ConsultantRepository consultantRepository;
final ConsultationsRepository consultationsRepository;
SettingsCubit({
required this.profileRepository,
required this.settingsRepository,
required this.profileRepository,
required this.settingsRepository,
required this.consultantRepository,
required this.consultationsRepository,
}) : super(const SettingsState.loading());
Future<void> init() async {
final profile = await profileRepository.getMyProfile();
late final ConsultationRequestModel? consultantInfo;
late final bool hasActiveConsultation;
try {
if (profile.phoneConfirmationState.contains('confirmed')) {
consultantInfo = await consultantRepository.getMyRequestStatus(profile.slug);
@@ -30,6 +36,13 @@ class SettingsCubit extends Cubit<SettingsState> {
} catch (e) {
consultantInfo = null;
}
try {
hasActiveConsultation = await _checkActiveConsultation();
} catch (e) {
hasActiveConsultation = false;
}
try {
emit(
SettingsState.loaded(
@@ -37,6 +50,7 @@ class SettingsCubit extends Cubit<SettingsState> {
profileDetails: await profileRepository.getMyProfileDetails(),
profile: profile,
consultationRequestModel: consultantInfo,
hasActiveConsultation: hasActiveConsultation,
),
);
} catch (e) {
@@ -44,13 +58,9 @@ class SettingsCubit extends Cubit<SettingsState> {
}
}
Future<void> updateProfileDetails({
required ProfileDetailsModel profileDetailsModel,
}) async {
Future<void> updateProfileDetails({required ProfileDetailsModel profileDetailsModel}) async {
try {
await settingsRepository.updateProfileDetails(
profileDetailsModel: profileDetailsModel,
);
await settingsRepository.updateProfileDetails(profileDetailsModel: profileDetailsModel);
} catch (e) {
rethrow;
} finally {
@@ -58,13 +68,9 @@ class SettingsCubit extends Cubit<SettingsState> {
}
}
Future<void> updateProfileSettings({
required ProfileSettingsModel profileSettingsModel,
}) async {
Future<void> updateProfileSettings({required ProfileSettingsModel profileSettingsModel}) async {
try {
await settingsRepository.updateProfileSettings(
profileSettingsModel: profileSettingsModel,
);
await settingsRepository.updateProfileSettings(profileSettingsModel: profileSettingsModel);
} catch (e) {
rethrow;
} finally {
@@ -80,4 +86,17 @@ class SettingsCubit extends Cubit<SettingsState> {
return false;
}
}
Future<bool> _checkActiveConsultation() async {
try {
final userPurchases = await consultationsRepository.getUserPurchases(null, null);
final activeStates = ChosenPlanStateEnum.getActiveStates();
return userPurchases.any((purchase) =>
activeStates.contains(purchase.chosenPlanState)
);
} catch (e) {
return false;
}
}
}

View File

@@ -16,6 +16,7 @@ abstract class SettingsState with _$SettingsState {
required ProfileDetailsModel profileDetails,
required ProfileModel profile,
ConsultationRequestModel? consultationRequestModel,
required bool hasActiveConsultation,
}) = SettingsStateLoaded;
const factory SettingsState.error({

View File

@@ -32,6 +32,24 @@ class _SettingsScreenState extends State<SettingsScreen> {
late bool bedTimeSwitchState;
late bool moodSwitchState;
void _showConsultationWarning() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Предупреждение'),
content: const Text('Во время консультации этот параметр нельзя отключить'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Понятно'),
),
],
);
},
);
}
@override
Widget build(BuildContext context) {
return BlocProvider<SettingsCubit>(
@@ -58,8 +76,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
children: [
_additionalInfo(state),
const SizedBox(height: 12),
// _notifications(state),
// const SizedBox(height: 12),
_notifications(state),
const SizedBox(height: 12),
_becomeConsultantButton(state),
_deleteAccountButton(state, settingsCubit),
],
@@ -147,76 +165,35 @@ class _SettingsScreenState extends State<SettingsScreen> {
);
}
// ignore: unused_element
Column _notifications(SettingsStateLoaded state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Text('Уведомления', style: TextStyle(fontSize: 16, height: 1, color: Colors.grey)),
child: Text('При использовании дневника', style: TextStyle(fontSize: 16, height: 1, color: Colors.grey)),
),
_tile(
padding: const EdgeInsets.symmetric(horizontal: 16),
onTap: () {
setState(() {
updatesNotificationsSwitchState = !updatesNotificationsSwitchState;
});
settingsCubit.updateProfileSettings(profileSettingsModel: state.profileSettings.copyWith(newUpdatesNotifications: updatesNotificationsSwitchState));
},
title: 'Уведомление о новых обновлениях',
trailing: CupertinoSwitch(
value: updatesNotificationsSwitchState,
activeTrackColor: appTheme.blueA400,
onChanged: (newValue) {
setState(() {
updatesNotificationsSwitchState = newValue;
});
settingsCubit.updateProfileSettings(
profileSettingsModel: state.profileSettings.copyWith(newUpdatesNotifications: updatesNotificationsSwitchState),
);
},
),
),
_divider(context),
_tile(
padding: const EdgeInsets.symmetric(horizontal: 16),
onTap: () {
setState(() {
fillDiaryNotificationsSwitchState = !fillDiaryNotificationsSwitchState;
});
settingsCubit.updateProfileSettings(
profileSettingsModel: state.profileSettings.copyWith(diaryReminderNotifications: fillDiaryNotificationsSwitchState),
);
},
title: 'Напоминание о заполнении дневника',
trailing: CupertinoSwitch(
value: fillDiaryNotificationsSwitchState,
activeTrackColor: appTheme.blueA400,
onChanged: (newValue) {
setState(() {
fillDiaryNotificationsSwitchState = newValue;
});
settingsCubit.updateProfileSettings(
profileSettingsModel: state.profileSettings.copyWith(diaryReminderNotifications: fillDiaryNotificationsSwitchState),
);
},
),
),
_divider(context),
_tile(
padding: const EdgeInsets.symmetric(horizontal: 16),
onTap: () {
if (state.hasActiveConsultation) {
_showConsultationWarning();
return;
}
setState(() {
moodSwitchState = !moodSwitchState;
});
settingsCubit.updateProfileSettings(profileSettingsModel: state.profileSettings.copyWith(isMoodFillable: moodSwitchState));
},
title: 'Указывать “настроение” в заполнение дневника',
title: 'Указывать "настроение" в заполнение дневника',
trailing: CupertinoSwitch(
value: moodSwitchState,
value: state.hasActiveConsultation ? true : moodSwitchState,
activeTrackColor: appTheme.blueA400,
onChanged: (newValue) {
if (state.hasActiveConsultation) {
_showConsultationWarning();
return;
}
setState(() {
moodSwitchState = newValue;
});
@@ -228,16 +205,24 @@ class _SettingsScreenState extends State<SettingsScreen> {
_tile(
padding: const EdgeInsets.symmetric(horizontal: 16),
onTap: () {
if (state.hasActiveConsultation) {
_showConsultationWarning();
return;
}
setState(() {
bedTimeSwitchState = !bedTimeSwitchState;
});
settingsCubit.updateProfileSettings(profileSettingsModel: state.profileSettings.copyWith(isBedtimeFillable: bedTimeSwitchState));
},
title: 'Указывать “отход ко сну” в заполнение дневника',
title: 'Указывать "отход ко сну" в заполнение дневника',
trailing: CupertinoSwitch(
value: bedTimeSwitchState,
value: state.hasActiveConsultation ? true : bedTimeSwitchState,
activeTrackColor: appTheme.blueA400,
onChanged: (newValue) {
if (state.hasActiveConsultation) {
_showConsultationWarning();
return;
}
setState(() {
bedTimeSwitchState = newValue;
});