diff --git a/ios/Podfile b/ios/Podfile
index 27c089c7..53a8d390 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -40,6 +40,17 @@ post_install do |installer|
target.build_configurations.each do |config|
config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.0'
+ config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
+ '$(inherited)',
+ ## dart: PermissionGroup.camera
+ 'PERMISSION_CAMERA=1',
+ ## dart: PermissionGroup.photos
+ 'PERMISSION_PHOTOS=1',
+ 'PERMISSION_NOTIFICATIONS=1',
+ ## dart: PermissionGroup.mediaLibrary
+ 'PERMISSION_MEDIA_LIBRARY=1',
+ ]
+
end
end
end
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index b7e87af6..fa92cb88 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -388,7 +388,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
CODE_SIGN_STYLE = Manual;
- CURRENT_PROJECT_VERSION = 26;
+ CURRENT_PROJECT_VERSION = 27;
DEVELOPMENT_TEAM = VL68C36HYS;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@@ -538,7 +538,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
CODE_SIGN_STYLE = Manual;
- CURRENT_PROJECT_VERSION = 26;
+ CURRENT_PROJECT_VERSION = 27;
DEVELOPMENT_TEAM = VL68C36HYS;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@@ -576,7 +576,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
CODE_SIGN_STYLE = Manual;
- CURRENT_PROJECT_VERSION = 26;
+ CURRENT_PROJECT_VERSION = 27;
DEVELOPMENT_TEAM = VL68C36HYS;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 793b8e28..35ec3cec 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -21,7 +21,7 @@
CFBundleSignature
????
CFBundleVersion
- 26
+ 27
ITSAppUsesNonExemptEncryption
LSRequiresIPhoneOS
diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart
index 0bd17c58..0e0c2213 100644
--- a/lib/core/app_router.dart
+++ b/lib/core/app_router.dart
@@ -20,6 +20,8 @@ import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.da
import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/become_consultant_screen.dart';
import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/stepper_become_consultant_screen.dart';
import 'package:eatvio_app/features/consultations/presentation/pages/consultations_screen.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/consultant_finances_screen.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/user_purchases_screen.dart';
import 'package:eatvio_app/features/order_info/presentation/info_by_order_screen.dart';
import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart';
import 'package:eatvio_app/features/create_diet/presentation/diet_preview_screen.dart';
@@ -202,10 +204,20 @@ class AppRouter {
},
),
GoRoute(
- path: '/consultant-finance-screen',
- name: 'consultantFinanceScreen',
+ path: ConsultationsArchiveScreen.path,
+ name: ConsultationsArchiveScreen.name,
builder: (context, state) => const ConsultationsArchiveScreen(),
),
+ GoRoute(
+ path: ConsultantFinancesScreen.path,
+ name: ConsultantFinancesScreen.name,
+ builder: (context, state) => const ConsultantFinancesScreen(),
+ ),
+ GoRoute(
+ path: UserPurchasesScreen.path,
+ name: UserPurchasesScreen.name,
+ builder: (context, state) => const UserPurchasesScreen(),
+ ),
GoRoute(
path: '/user-finance-screen',
name: 'userFinanceScreen',
diff --git a/lib/core/locator_service.dart b/lib/core/locator_service.dart
index ad23cce1..accb729d 100644
--- a/lib/core/locator_service.dart
+++ b/lib/core/locator_service.dart
@@ -31,6 +31,8 @@ import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit
import 'package:eatvio_app/features/blog_post_create/repositories/blog_post_repository.dart';
import 'package:eatvio_app/features/blog_post_create/services/blog_post_service.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/new_messages_cubit/new_messages_cubit.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_cubit.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_cubit.dart';
import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart';
import 'package:eatvio_app/features/calendar/presentation/bloc/calendar_cubit.dart';
@@ -123,8 +125,14 @@ Future init() async {
sl.registerFactory(() => PlansCubit(feedRepository: sl(), planRepository: sl()));
sl.registerFactory(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl(), consultantRepository: sl()));
sl.registerFactory(() => RegisterConsultantCubit(consultantRepository: sl()));
- sl.registerFactory(
- () => DiaryCubit(chatRepository: sl(), diaryRepository: sl(), glossaryRepository: sl(), profileRepository: sl(), dietRepository: sl()));
+ sl.registerFactory(() => DiaryCubit(
+ chatRepository: sl(),
+ diaryRepository: sl(),
+ glossaryRepository: sl(),
+ profileRepository: sl(),
+ dietRepository: sl(),
+ settingsRepository: sl(),
+ ));
sl.registerFactory(() => AboutDietCubit(dietsRepository: sl(), profileRepository: sl()));
sl.registerFactory(() => BlogPostCreateCubit(blogPostRepository: sl()));
sl.registerFactory(() => SubCubit(profileRepository: sl()));
@@ -148,6 +156,8 @@ Future init() async {
sl.registerFactory(() => VerifyEmailCubit(profileRepository: sl()));
sl.registerFactory(() => MyRecipesCubit(myRecipesRepository: sl(), aboutRecipeRepository: sl()));
sl.registerFactory(() => ConsultationsArchiveCubit(consultationsRepository: sl()));
+ sl.registerFactory(() => ConsultantFinancesCubit(consultationsRepository: sl()));
+ sl.registerFactory(() => UserPurchasesCubit(consultationsRepository: sl()));
// Repository
sl.registerLazySingleton(() => ChatRepository(chatService: sl()));
diff --git a/lib/core/permission_handler/permission_handler.dart b/lib/core/permission_handler/permission_handler.dart
new file mode 100644
index 00000000..a311e84d
--- /dev/null
+++ b/lib/core/permission_handler/permission_handler.dart
@@ -0,0 +1,23 @@
+import 'package:eatvio_app/core/widgets/show_error_dialog.dart';
+import 'package:eatvio_app/generated/l10n.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:permission_handler/permission_handler.dart';
+
+class PermissionHandler {
+ static Future checkPermission({
+ required Permission permission,
+ required String title,
+ required String description,
+ required Future Function() onGranted,
+ required BuildContext context,
+ }) async {
+ final permissionState = await permission.request();
+ if (permissionState.isGranted) {
+ await onGranted();
+ return;
+ }
+ if (context.mounted) {
+ showErrorDialog(context, title: title, description: description, buttonTitle: S.of(context).allowAccess, needToOpenSettings: true);
+ }
+ }
+}
diff --git a/lib/core/utils/date_time_utils.dart b/lib/core/utils/date_time_utils.dart
index b6c1c86e..f10ae9ec 100644
--- a/lib/core/utils/date_time_utils.dart
+++ b/lib/core/utils/date_time_utils.dart
@@ -8,31 +8,39 @@ extension DateTimeExtension on DateTime {
String pattern = dateTimeFormatPattern,
String? locale,
]) {
+ final localDate = isUtc ? toLocal() : this;
if (locale != null && locale.isNotEmpty) {
initializeDateFormatting(locale);
}
- return DateFormat(pattern, locale).format(this);
+ return DateFormat(pattern, locale).format(localDate);
}
- bool isYesterday(DateTime date) {
- final DateTime localDate = date.isUtc ? date.toLocal() : date;
- final yesterday = DateTime.now().subtract(const Duration(days: 1));
- return yesterday.day == localDate.day && yesterday.month == localDate.month && yesterday.year == localDate.year;
- }
-
- bool isToday(DateTime date) {
- final DateTime localDate = date.isUtc ? date.toLocal() : date;
+ bool isYesterday() {
+ final localDate = isUtc ? toLocal() : this;
final now = DateTime.now();
- return now.day == localDate.day && now.month == localDate.month && now.year == localDate.year;
+ final yesterday = DateTime(now.year, now.month, now.day).subtract(const Duration(days: 1));
+ return localDate.year == yesterday.year &&
+ localDate.month == yesterday.month &&
+ localDate.day == yesterday.day;
+ }
+
+ bool isToday() {
+ final localDate = isUtc ? toLocal() : this;
+ final now = DateTime.now();
+ return now.year == localDate.year &&
+ now.month == localDate.month &&
+ now.day == localDate.day;
}
}
String calculateDifference(DateTime dateTime) {
- if (dateTime.isToday(dateTime)) {
+ final localDateTime = dateTime.isUtc ? dateTime.toLocal() : dateTime;
+
+ if (localDateTime.isToday()) {
return 'Сегодня';
- } else if (dateTime.isYesterday(dateTime)) {
+ } else if (localDateTime.isYesterday()) {
return 'Вчера';
} else {
- return dateTime.format('d MMMM', 'ru');
+ return localDateTime.format('d MMMM', 'ru');
}
}
diff --git a/lib/core/widgets/show_error_dialog.dart b/lib/core/widgets/show_error_dialog.dart
index 9685ad14..8ae4666e 100644
--- a/lib/core/widgets/show_error_dialog.dart
+++ b/lib/core/widgets/show_error_dialog.dart
@@ -4,8 +4,9 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
+import 'package:permission_handler/permission_handler.dart';
-Future showErrorDialog(BuildContext context, {String? title, String? description}) async {
+Future showErrorDialog(BuildContext context, {String? title, String? description, String? buttonTitle, bool? needToOpenSettings}) async {
return await showDialog(
context: context,
barrierColor: Colors.transparent,
@@ -43,8 +44,13 @@ Future showErrorDialog(BuildContext context, {String? title, String? descr
Align(
alignment: Alignment.centerRight,
child: TextButton(
- onPressed: () => AppRouter.navigatorKey.currentContext?.pop(),
- child: Text(s.ok, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
+ onPressed: () {
+ AppRouter.navigatorKey.currentContext?.pop();
+ if (needToOpenSettings ?? false) {
+ openAppSettings();
+ }
+ },
+ child: Text(buttonTitle ?? s.ok, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
),
),
const SizedBox(height: 12),
diff --git a/lib/features/blog_post_create/presentation/pages/blog_post_create_screen.dart b/lib/features/blog_post_create/presentation/pages/blog_post_create_screen.dart
index f7b57ddd..d1682338 100644
--- a/lib/features/blog_post_create/presentation/pages/blog_post_create_screen.dart
+++ b/lib/features/blog_post_create/presentation/pages/blog_post_create_screen.dart
@@ -1,6 +1,8 @@
import 'dart:async';
import 'dart:io';
+import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/const.dart';
+import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
import 'package:eatvio_app/core/theme/app_decoration.dart';
import 'package:eatvio_app/core/widgets/show_error_dialog.dart';
import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_cubit.dart';
@@ -17,6 +19,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart';
+import 'package:permission_handler/permission_handler.dart';
class BlogPostCreateScreen extends StatefulWidget {
final FeedBlogPostModel? feedBlogPostModel;
@@ -80,47 +83,65 @@ class _BlogPostCreateScreenState extends State {
_showImagePicker() {
showCupertinoModalPopup(
context: context,
- builder: (context) => CupertinoActionSheet(
- actions: [
- CupertinoActionSheetAction(
- onPressed: () async {
- final picker = ImagePicker();
- image = await picker.pickImage(source: ImageSource.camera);
- setState(() {});
- if (!context.mounted) return;
- Navigator.of(context).pop();
- },
- child: const Row(
- children: [
- Icon(Icons.photo_camera),
- SizedBox(width: 8),
- Text('Фото'),
- ],
+ builder: (context) {
+ final s = S.of(context);
+ return CupertinoActionSheet(
+ actions: [
+ CupertinoActionSheetAction(
+ onPressed: () async {
+ PermissionHandler.checkPermission(
+ permission: Permission.camera,
+ title: s.allowAccessToCamera,
+ description: s.allowAccessToCameraDescription,
+ onGranted: () async {
+ final picker = ImagePicker();
+ image = await picker.pickImage(source: ImageSource.camera);
+ setState(() {});
+ AppRouter.navigatorKey.currentContext?.pop();
+ },
+ context: context,
+ );
+ },
+ child: const Row(
+ children: [
+ Icon(Icons.photo_camera),
+ SizedBox(width: 8),
+ Text('Фото'),
+ ],
+ ),
),
- ),
- CupertinoActionSheetAction(
- onPressed: () async {
- final picker = ImagePicker();
- image = await picker.pickImage(source: ImageSource.gallery);
- setState(() {});
- if (!context.mounted) return;
- Navigator.of(context).pop();
- },
- child: const Row(
- children: [
- Icon(Icons.photo),
- SizedBox(width: 8),
- Text('Галерея'),
- ],
+ CupertinoActionSheetAction(
+ onPressed: () async {
+ PermissionHandler.checkPermission(
+ permission: Permission.photos,
+ title: s.allowAcessToGalery,
+ description: s.allowAcessToGaleryDescription,
+ onGranted: () async {
+ final picker = ImagePicker();
+ image = await picker.pickImage(source: ImageSource.gallery);
+ setState(() {});
+ if (!context.mounted) return;
+ AppRouter.navigatorKey.currentContext?.pop();
+ },
+ context: context,
+ );
+ },
+ child: const Row(
+ children: [
+ Icon(Icons.photo),
+ SizedBox(width: 8),
+ Text('Галерея'),
+ ],
+ ),
),
+ ],
+ cancelButton: CupertinoActionSheetAction(
+ onPressed: () => Navigator.of(context).pop(),
+ isDestructiveAction: true,
+ child: const Text('Отмена'),
),
- ],
- cancelButton: CupertinoActionSheetAction(
- onPressed: () => Navigator.of(context).pop(),
- isDestructiveAction: true,
- child: const Text('Отмена'),
- ),
- ),
+ );
+ },
);
}
diff --git a/lib/features/chat/presentation/widgets/chat_bubble_widget.dart b/lib/features/chat/presentation/widgets/chat_bubble_widget.dart
index b80b12ac..74e6f6a6 100644
--- a/lib/features/chat/presentation/widgets/chat_bubble_widget.dart
+++ b/lib/features/chat/presentation/widgets/chat_bubble_widget.dart
@@ -39,7 +39,7 @@ class ChatBubbleWidget extends StatelessWidget {
Padding(
padding: const EdgeInsets.only(right: 4, bottom: 4),
child: Text(
- sendTime.format('hh:mm'),
+ sendTime.format('HH:mm', 'ru'),
style: const TextStyle(
color: Color(0xFF7B8794),
height: 1,
@@ -144,7 +144,7 @@ class ChatBubbleWidget extends StatelessWidget {
Padding(
padding: const EdgeInsets.only(left: 4, bottom: 4),
child: Text(
- sendTime.format('hh:mm'),
+ sendTime.format('HH:mm', 'ru'),
style: const TextStyle(
color: Color(0xFF7B8794),
height: 1,
diff --git a/lib/features/consultations/presentation/pages/consultations_screen.dart b/lib/features/consultations/presentation/pages/consultations_screen.dart
index d6acb306..6f0021d9 100644
--- a/lib/features/consultations/presentation/pages/consultations_screen.dart
+++ b/lib/features/consultations/presentation/pages/consultations_screen.dart
@@ -6,6 +6,7 @@ import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_cubit.dart';
import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_state.dart';
import 'package:eatvio_app/features/consultations/presentation/widgets/consultation_card_widget.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/consultations_archive_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
@@ -67,7 +68,7 @@ class ConsultationsContent extends StatelessWidget {
InkWell(
borderRadius: BorderRadius.circular(20),
onTap: () {
- GoRouter.of(context).pushNamed('consultantFinanceScreen');
+ GoRouter.of(context).pushNamed(ConsultationsArchiveScreen.name);
},
child: Ink(
width: MediaQuery.of(context).size.width - 32,
diff --git a/lib/features/consultations/presentation/widgets/consultation_card_widget.dart b/lib/features/consultations/presentation/widgets/consultation_card_widget.dart
index c9b9f8b2..11322228 100644
--- a/lib/features/consultations/presentation/widgets/consultation_card_widget.dart
+++ b/lib/features/consultations/presentation/widgets/consultation_card_widget.dart
@@ -185,6 +185,8 @@ class _ConsultationCardWidgetState extends State {
children: List.generate(widget.consultationsModel.duration, (index) {
final day = givenDaysWithDayNumber.firstWhereOrNull((e) => e.$1 == index);
final hasActivity = day != null && day.$2.state != DiaryStateEnum.init;
+ final canConsultantOpenThisDay = day != null &&
+ (day.$2.state != DiaryStateEnum.init && day.$2.state != DiaryStateEnum.started && day.$2.state != DiaryStateEnum.lastDayStarted);
return Padding(
padding: const EdgeInsets.only(right: 12),
child: Column(
@@ -192,7 +194,7 @@ class _ConsultationCardWidgetState extends State {
children: [
InkWell(
onTap: () {
- if (!hasActivity) {
+ if (!hasActivity || !canConsultantOpenThisDay) {
showErrorDialog(context,
title: s.you_can_not_open_this_day, description: '${widget.consultationsModel.userName} ${s.has_not_filled_it_yet}');
return;
diff --git a/lib/features/consultations/repositories/consultations_repository.dart b/lib/features/consultations/repositories/consultations_repository.dart
index 3ebcef87..15490dc9 100644
--- a/lib/features/consultations/repositories/consultations_repository.dart
+++ b/lib/features/consultations/repositories/consultations_repository.dart
@@ -1,7 +1,9 @@
import 'package:eatvio_app/core/enums/filters_comparison_enum.dart';
import 'package:eatvio_app/features/consultations/models/consultation_model.dart';
import 'package:eatvio_app/features/consultations/services/consultation_service.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/models/consultant_finance_model.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/models/statistic_model.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/models/user_purchases_model.dart';
class ConsultationsRepository {
final ConsultationService consultationService;
@@ -42,4 +44,20 @@ class ConsultationsRepository {
rethrow;
}
}
+
+ Future> getConsultantFinances(String? date, FiltersComparisonEnum? dateComparison) async {
+ try {
+ return await consultationService.getConsultantFinances(date, dateComparison);
+ } catch (e) {
+ rethrow;
+ }
+ }
+
+ Future> getUserPurchases(String? date, FiltersComparisonEnum? dateComparison) async {
+ try {
+ return await consultationService.getUserPurchases(date, dateComparison);
+ } catch (e) {
+ rethrow;
+ }
+ }
}
diff --git a/lib/features/consultations/services/consultation_service.dart b/lib/features/consultations/services/consultation_service.dart
index fa14b69c..f0a89220 100644
--- a/lib/features/consultations/services/consultation_service.dart
+++ b/lib/features/consultations/services/consultation_service.dart
@@ -1,7 +1,9 @@
import 'package:dio/dio.dart';
import 'package:eatvio_app/core/enums/filters_comparison_enum.dart';
import 'package:eatvio_app/features/consultations/models/consultation_model.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/models/consultant_finance_model.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/models/statistic_model.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/models/user_purchases_model.dart';
class ConsultationService {
final Dio dio;
@@ -66,4 +68,42 @@ class ConsultationService {
rethrow;
}
}
+
+ Future> getConsultantFinances(
+ String? date,
+ FiltersComparisonEnum? dateComparison,
+ ) async {
+ try {
+ final response = await dio.get(
+ '/api/finances/my_finances',
+ queryParameters: {
+ if (dateComparison != null && date != null) 'filters[end_date]': date,
+ },
+ );
+ final data = response.data as Map;
+ final consultations = data['data'] as List;
+ return consultations.map((consultation) => ConsultantFinanceModel.fromJson(consultation as Map)).toList();
+ } catch (e) {
+ rethrow;
+ }
+ }
+
+ Future> getUserPurchases(
+ String? date,
+ FiltersComparisonEnum? dateComparison,
+ ) async {
+ try {
+ final response = await dio.get(
+ '/api/finances/my_purchases',
+ queryParameters: {
+ if (dateComparison != null && date != null) 'filters[start_date]': date,
+ },
+ );
+ final data = response.data as Map;
+ final purchases = data['data'] as List;
+ return purchases.map((purchase) => UserPurchasesModel.fromJson(purchase as Map)).toList();
+ } catch (e) {
+ rethrow;
+ }
+ }
}
diff --git a/lib/features/create_diet/presentation/widgets/diet_image_picker_widget.dart b/lib/features/create_diet/presentation/widgets/diet_image_picker_widget.dart
index c6250bb4..192df5d3 100644
--- a/lib/features/create_diet/presentation/widgets/diet_image_picker_widget.dart
+++ b/lib/features/create_diet/presentation/widgets/diet_image_picker_widget.dart
@@ -1,6 +1,8 @@
import 'dart:io';
import 'package:dotted_border/dotted_border.dart';
+import 'package:eatvio_app/core/app_router.dart';
+import 'package:eatvio_app/core/permission_handler/permission_handler.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/create_diet/presentation/cubit/create_diet_cubit.dart';
@@ -11,7 +13,9 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
+import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart';
+import 'package:permission_handler/permission_handler.dart';
class DietImagePickerWidget extends StatefulWidget {
const DietImagePickerWidget({super.key});
@@ -28,11 +32,18 @@ class _DietImagePickerWidgetState extends State {
actions: [
CupertinoActionSheetAction(
onPressed: () async {
- final picker = ImagePicker();
- final image = await picker.pickImage(source: ImageSource.camera);
- cubit.setImage(image);
- if (!context.mounted) return;
- Navigator.of(context).pop();
+ PermissionHandler.checkPermission(
+ permission: Permission.camera,
+ title: s.allowAccessToCamera,
+ description: s.allowAccessToCameraDescription,
+ onGranted: () async {
+ final picker = ImagePicker();
+ final image = await picker.pickImage(source: ImageSource.camera);
+ cubit.setImage(image);
+ AppRouter.navigatorKey.currentContext?.pop();
+ },
+ context: context,
+ );
},
child: Row(
children: [
@@ -44,11 +55,18 @@ class _DietImagePickerWidgetState extends State {
),
CupertinoActionSheetAction(
onPressed: () async {
- final picker = ImagePicker();
- final image = await picker.pickImage(source: ImageSource.gallery);
- cubit.setImage(image);
- if (!context.mounted) return;
- Navigator.of(context).pop();
+ PermissionHandler.checkPermission(
+ permission: Permission.photos,
+ title: s.allowAcessToGalery,
+ description: s.allowAcessToGaleryDescription,
+ onGranted: () async {
+ final picker = ImagePicker();
+ final image = await picker.pickImage(source: ImageSource.gallery);
+ cubit.setImage(image);
+ AppRouter.navigatorKey.currentContext?.pop();
+ },
+ context: context,
+ );
},
child: Row(
children: [
diff --git a/lib/features/create_plan/presentation/widgets/plan_image_picker_widget.dart b/lib/features/create_plan/presentation/widgets/plan_image_picker_widget.dart
index d5041035..67af4a61 100644
--- a/lib/features/create_plan/presentation/widgets/plan_image_picker_widget.dart
+++ b/lib/features/create_plan/presentation/widgets/plan_image_picker_widget.dart
@@ -1,6 +1,8 @@
import 'dart:io';
import 'package:dotted_border/dotted_border.dart';
+import 'package:eatvio_app/core/app_router.dart';
+import 'package:eatvio_app/core/permission_handler/permission_handler.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/create_diet/presentation/widgets/error_widget.dart';
@@ -11,7 +13,9 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
+import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart';
+import 'package:permission_handler/permission_handler.dart';
class PlanImagePickerWidget extends StatefulWidget {
const PlanImagePickerWidget({super.key});
@@ -28,11 +32,18 @@ class _PlanImagePickerWidgetState extends State {
actions: [
CupertinoActionSheetAction(
onPressed: () async {
- final picker = ImagePicker();
- final image = await picker.pickImage(source: ImageSource.camera);
- cubit.setImage(image);
- if (!context.mounted) return;
- Navigator.of(context).pop();
+ PermissionHandler.checkPermission(
+ permission: Permission.camera,
+ title: s.allowAccessToCamera,
+ description: s.allowAccessToCameraDescription,
+ onGranted: () async {
+ final picker = ImagePicker();
+ final image = await picker.pickImage(source: ImageSource.camera);
+ cubit.setImage(image);
+ AppRouter.navigatorKey.currentContext?.pop();
+ },
+ context: context,
+ );
},
child: Row(
children: [
@@ -44,11 +55,18 @@ class _PlanImagePickerWidgetState extends State {
),
CupertinoActionSheetAction(
onPressed: () async {
- final picker = ImagePicker();
- final image = await picker.pickImage(source: ImageSource.gallery);
- cubit.setImage(image);
- if (!context.mounted) return;
- Navigator.of(context).pop();
+ PermissionHandler.checkPermission(
+ permission: Permission.photos,
+ title: s.allowAcessToGalery,
+ description: s.allowAcessToGaleryDescription,
+ onGranted: () async {
+ final picker = ImagePicker();
+ final image = await picker.pickImage(source: ImageSource.gallery);
+ cubit.setImage(image);
+ AppRouter.navigatorKey.currentContext?.pop();
+ },
+ context: context,
+ );
},
child: Row(
children: [
diff --git a/lib/features/create_recipe/presentation/widgets/image_widget.dart b/lib/features/create_recipe/presentation/widgets/image_widget.dart
index 534499e4..eb444b8e 100644
--- a/lib/features/create_recipe/presentation/widgets/image_widget.dart
+++ b/lib/features/create_recipe/presentation/widgets/image_widget.dart
@@ -1,11 +1,16 @@
import 'dart:io';
+import 'package:eatvio_app/core/app_router.dart';
+import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart';
+import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
+import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart';
+import 'package:permission_handler/permission_handler.dart';
class ImageWidget extends StatefulWidget {
const ImageWidget({super.key, required this.image});
@@ -67,55 +72,72 @@ class _ImageWidgetState extends State {
_showImagePicker() {
showCupertinoModalPopup(
context: context,
- builder: (context) => CupertinoActionSheet(
- actions: [
- CupertinoActionSheetAction(
- onPressed: () async {
- final picker = ImagePicker();
- final image = await picker.pickImage(source: ImageSource.camera);
- setState(() {});
- if (image != null) {
- createRecipeCubit.replaceImage(File(image.path));
- createRecipeCubit.validateImage();
- }
- if (!context.mounted) return;
- Navigator.of(context).pop();
- },
- child: const Row(
- children: [
- Icon(Icons.photo_camera),
- SizedBox(width: 8),
- Text('Фото'),
- ],
+ builder: (context) {
+ final s = S.of(context);
+ return CupertinoActionSheet(
+ actions: [
+ CupertinoActionSheetAction(
+ onPressed: () async {
+ PermissionHandler.checkPermission(
+ permission: Permission.camera,
+ title: s.allowAccessToCamera,
+ description: s.allowAccessToCameraDescription,
+ onGranted: () async {
+ final picker = ImagePicker();
+ final image = await picker.pickImage(source: ImageSource.camera);
+ setState(() {});
+ if (image != null) {
+ createRecipeCubit.replaceImage(File(image.path));
+ createRecipeCubit.validateImage();
+ }
+ AppRouter.navigatorKey.currentContext?.pop();
+ },
+ context: context,
+ );
+ },
+ child: const Row(
+ children: [
+ Icon(Icons.photo_camera),
+ SizedBox(width: 8),
+ Text('Фото'),
+ ],
+ ),
),
- ),
- CupertinoActionSheetAction(
- onPressed: () async {
- final picker = ImagePicker();
- final image = await picker.pickImage(source: ImageSource.gallery);
- setState(() {});
- if (image != null) {
- createRecipeCubit.replaceImage(File(image.path));
- createRecipeCubit.validateImage();
- }
- if (!context.mounted) return;
- Navigator.of(context).pop();
- },
- child: const Row(
- children: [
- Icon(Icons.photo),
- SizedBox(width: 8),
- Text('Галерея'),
- ],
+ CupertinoActionSheetAction(
+ onPressed: () async {
+ PermissionHandler.checkPermission(
+ permission: Permission.photos,
+ title: s.allowAcessToGalery,
+ description: s.allowAcessToGaleryDescription,
+ onGranted: () async {
+ final picker = ImagePicker();
+ final image = await picker.pickImage(source: ImageSource.gallery);
+ setState(() {});
+ if (image != null) {
+ createRecipeCubit.replaceImage(File(image.path));
+ createRecipeCubit.validateImage();
+ }
+ AppRouter.navigatorKey.currentContext?.pop();
+ },
+ context: context,
+ );
+ },
+ child: const Row(
+ children: [
+ Icon(Icons.photo),
+ SizedBox(width: 8),
+ Text('Галерея'),
+ ],
+ ),
),
+ ],
+ cancelButton: CupertinoActionSheetAction(
+ onPressed: () => Navigator.of(context).pop(),
+ isDestructiveAction: true,
+ child: const Text('Отмена'),
),
- ],
- cancelButton: CupertinoActionSheetAction(
- onPressed: () => Navigator.of(context).pop(),
- isDestructiveAction: true,
- child: const Text('Отмена'),
- ),
- ),
+ );
+ },
);
}
}
diff --git a/lib/features/create_recipe/presentation/widgets/step_image_widget.dart b/lib/features/create_recipe/presentation/widgets/step_image_widget.dart
index 1189f0f8..d00d08c7 100644
--- a/lib/features/create_recipe/presentation/widgets/step_image_widget.dart
+++ b/lib/features/create_recipe/presentation/widgets/step_image_widget.dart
@@ -1,10 +1,15 @@
import 'dart:io';
+import 'package:eatvio_app/core/app_router.dart';
+import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart';
+import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
+import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart';
+import 'package:permission_handler/permission_handler.dart';
class StepImageWidget extends StatefulWidget {
final CreateRecipeCubit createRecipeCubit;
@@ -12,7 +17,8 @@ class StepImageWidget extends StatefulWidget {
const StepImageWidget({
super.key,
- required this.index, required this.createRecipeCubit,
+ required this.index,
+ required this.createRecipeCubit,
});
@override
@@ -79,53 +85,70 @@ class _StepImageWidgetState extends State {
_showImagePicker() {
showCupertinoModalPopup(
context: context,
- builder: (context) => CupertinoActionSheet(
- actions: [
- CupertinoActionSheetAction(
- onPressed: () async {
- final picker = ImagePicker();
- image = await picker.pickImage(source: ImageSource.camera);
- setState(() {});
- if (image != null) {
- widget.createRecipeCubit.replaceImageInStep(File(image!.path), widget.index);
- }
- if (!context.mounted) return;
- Navigator.of(context).pop();
- },
- child: const Row(
- children: [
- Icon(Icons.photo_camera),
- SizedBox(width: 8),
- Text('Фото'),
- ],
+ builder: (context) {
+ final s = S.of(context);
+ return CupertinoActionSheet(
+ actions: [
+ CupertinoActionSheetAction(
+ onPressed: () async {
+ PermissionHandler.checkPermission(
+ permission: Permission.camera,
+ title: s.allowAccessToCamera,
+ description: s.allowAccessToCameraDescription,
+ onGranted: () async {
+ final picker = ImagePicker();
+ image = await picker.pickImage(source: ImageSource.camera);
+ setState(() {});
+ if (image != null) {
+ widget.createRecipeCubit.replaceImageInStep(File(image!.path), widget.index);
+ }
+ AppRouter.navigatorKey.currentContext?.pop();
+ },
+ context: context,
+ );
+ },
+ child: const Row(
+ children: [
+ Icon(Icons.photo_camera),
+ SizedBox(width: 8),
+ Text('Фото'),
+ ],
+ ),
),
- ),
- CupertinoActionSheetAction(
- onPressed: () async {
- final picker = ImagePicker();
- image = await picker.pickImage(source: ImageSource.gallery);
- setState(() {});
- if (image != null) {
- widget.createRecipeCubit.replaceImageInStep(File(image!.path), widget.index);
- }
- if (!context.mounted) return;
- Navigator.of(context).pop();
- },
- child: const Row(
- children: [
- Icon(Icons.photo),
- SizedBox(width: 8),
- Text('Галерея'),
- ],
+ CupertinoActionSheetAction(
+ onPressed: () async {
+ PermissionHandler.checkPermission(
+ permission: Permission.photos,
+ title: s.allowAcessToGalery,
+ description: s.allowAcessToGaleryDescription,
+ onGranted: () async {
+ final picker = ImagePicker();
+ image = await picker.pickImage(source: ImageSource.gallery);
+ setState(() {});
+ if (image != null) {
+ widget.createRecipeCubit.replaceImageInStep(File(image!.path), widget.index);
+ }
+ AppRouter.navigatorKey.currentContext?.pop();
+ },
+ context: context,
+ );
+ },
+ child: const Row(
+ children: [
+ Icon(Icons.photo),
+ SizedBox(width: 8),
+ Text('Галерея'),
+ ],
+ ),
),
+ ],
+ cancelButton: CupertinoActionSheetAction(
+ onPressed: () => Navigator.of(context).pop(),
+ isDestructiveAction: true,
+ child: const Text('Отмена'),
),
- ],
- cancelButton: CupertinoActionSheetAction(
- onPressed: () => Navigator.of(context).pop(),
- isDestructiveAction: true,
- child: const Text('Отмена'),
- ),
- ),
+ );
+ },
);
}
}
diff --git a/lib/features/diary/presentation/bloc/diary_cubit.dart b/lib/features/diary/presentation/bloc/diary_cubit.dart
index afe81398..7216ed1c 100644
--- a/lib/features/diary/presentation/bloc/diary_cubit.dart
+++ b/lib/features/diary/presentation/bloc/diary_cubit.dart
@@ -14,6 +14,7 @@ import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
import 'package:eatvio_app/features/glossary/models/glossary_model.dart';
import 'package:eatvio_app/features/glossary/repositories/glossary_repository.dart';
import 'package:eatvio_app/features/profile/repositories/profile_repository.dart';
+import 'package:eatvio_app/features/settings/repositories/service_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class DiaryCubit extends Cubit {
@@ -25,14 +26,16 @@ class DiaryCubit extends Cubit {
final ProfileRepository profileRepository;
final DietRepository dietRepository;
final ChatRepository chatRepository;
+ final SettingsRepository settingsRepository;
int? diaryId;
- DiaryCubit(
- {required this.chatRepository,
- required this.diaryRepository,
- required this.glossaryRepository,
- required this.profileRepository,
- required this.dietRepository})
- : super(const DiaryState.loading()) {
+ DiaryCubit({
+ required this.chatRepository,
+ required this.diaryRepository,
+ required this.glossaryRepository,
+ required this.profileRepository,
+ required this.dietRepository,
+ required this.settingsRepository,
+ }) : super(const DiaryState.loading()) {
today = DateTime.now();
selectedDay = today;
diaryRepository.forceFetchStream.listen((_) {
@@ -160,11 +163,16 @@ class DiaryCubit extends Cubit {
}
try {
final DiaryDayModel diaryDay;
+ bool isMoodEnabled = false;
+ bool isWakeUpAndRetireTimeEnabled = false;
if (diaryId != null) {
diaryDay = await diaryRepository.getDiaryDay(diaryId);
} else {
final stringDate = selectedDay.format('yyyy-MM-dd');
diaryDay = await diaryRepository.getDiaryByDate(stringDate);
+ final profileSettings = await settingsRepository.getProfileSettings();
+ isMoodEnabled = profileSettings.isMoodFillable;
+ isWakeUpAndRetireTimeEnabled = profileSettings.isBedtimeFillable;
}
ConsultantReviewModel? consultantReview;
int? planDuration;
@@ -197,6 +205,8 @@ class DiaryCubit extends Cubit {
chosenPlanCurrentDay: currentPlanDay,
chosenPlanDuration: planDuration,
chosenPlanName: planName,
+ isMoodEnabled: isMoodEnabled,
+ isWakeUpAndRetireEnabled: isWakeUpAndRetireTimeEnabled
),
);
} catch (e, st) {
diff --git a/lib/features/diary/presentation/bloc/diary_state.dart b/lib/features/diary/presentation/bloc/diary_state.dart
index 828831d3..d5e8e39d 100644
--- a/lib/features/diary/presentation/bloc/diary_state.dart
+++ b/lib/features/diary/presentation/bloc/diary_state.dart
@@ -27,6 +27,8 @@ abstract class DiaryState with _$DiaryState {
@Default(false) bool isConsultantView,
@Default(false) bool isDiaryFinished,
@Default(false) bool isDiaryCommented,
+ @Default(false) bool isWakeUpAndRetireEnabled,
+ @Default(false) bool isMoodEnabled,
}) = DiaryStateLoaded;
const factory DiaryState.loadedTomorrowWithDiet({
diff --git a/lib/features/diary/presentation/pages/diary_screen.dart b/lib/features/diary/presentation/pages/diary_screen.dart
index 2f80c22e..fb1987ef 100644
--- a/lib/features/diary/presentation/pages/diary_screen.dart
+++ b/lib/features/diary/presentation/pages/diary_screen.dart
@@ -80,13 +80,17 @@ class DiaryScreenContent extends StatelessWidget {
return Scaffold(
backgroundColor: Colors.grey.shade200,
- appBar: AppBar(
- title: Text(
- state is DiaryStateLoaded ? DateTime.parse(state.diary.date).toLocal().format('d MMMM') : '',
- style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
- ),
- centerTitle: true,
- ),
+ appBar: diaryId == null
+ ? AppBar(
+ toolbarHeight: 0,
+ )
+ : AppBar(
+ title: Text(
+ state is DiaryStateLoaded ? DateTime.parse(state.diary.date).toLocal().format('d MMMM') : '',
+ style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
+ ),
+ centerTitle: true,
+ ),
body: SingleChildScrollView(
padding: const EdgeInsets.only(bottom: 16, top: 8),
physics: const BouncingScrollPhysics(),
@@ -101,13 +105,13 @@ class DiaryScreenContent extends StatelessWidget {
],
if (state is DiaryStateLoaded)
WakeupAndRetireWidget(
- showRetire: state.diary.typeOfDiary.hasChosenPlan,
+ isEnabled: state.diary.typeOfDiary.hasChosenPlan || state.isConsultantView || state.isWakeUpAndRetireEnabled,
disable: state.isConsultantView || state.isDiaryFinished || state.isDiaryCommented,
),
const SizedBox(height: 12),
if (state is DiaryStateLoaded)
MealWidget(
- showMood: state.diary.typeOfDiary.hasChosenPlan,
+ showMood: state.diary.typeOfDiary.hasChosenPlan || state.isConsultantView || state.isMoodEnabled,
showAddButton: !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented,
),
if (state is DiaryStateLoadedTomorrowWithDiet)
diff --git a/lib/features/diary/presentation/widgets/calories_chart_widget.dart b/lib/features/diary/presentation/widgets/calories_chart_widget.dart
index 6cdc6e86..817508af 100644
--- a/lib/features/diary/presentation/widgets/calories_chart_widget.dart
+++ b/lib/features/diary/presentation/widgets/calories_chart_widget.dart
@@ -30,7 +30,7 @@ class CaloriesChartWidget extends StatelessWidget {
context: context,
builder: (context) => const AlertDialog(
content: Text(
- 'График отображает сколько калорий вам нужно ежедневно для похуденияи поддержания веса, согласно формуле Харриса-Бенедикта.\nИзменить коэффициент физической нагрузки можно в настройках.',
+ 'График отображает сколько калорий Вам нужно ежедневно для поддержания веса, согласно формуле Харриса-Бенедикта. Изменить коэффициент физической нагрузки можно в настройках',
style: TextStyle(
fontWeight: FontWeight.w500,
color: Color(0xFF323F4B),
diff --git a/lib/features/diary/presentation/widgets/meal_widget.dart b/lib/features/diary/presentation/widgets/meal_widget.dart
index b5bd196a..ce71ef66 100644
--- a/lib/features/diary/presentation/widgets/meal_widget.dart
+++ b/lib/features/diary/presentation/widgets/meal_widget.dart
@@ -59,7 +59,6 @@ class _MealWidgetState extends State {
),
const SizedBox(height: 12),
const TimeOfMealPickerWidget(),
-
const ChosenDietWidget(),
const ChosenPlanWidget(),
const ProductsListWidget(),
diff --git a/lib/features/diary/presentation/widgets/photo_gallery_widget.dart b/lib/features/diary/presentation/widgets/photo_gallery_widget.dart
index 80c7e58a..7a89d466 100644
--- a/lib/features/diary/presentation/widgets/photo_gallery_widget.dart
+++ b/lib/features/diary/presentation/widgets/photo_gallery_widget.dart
@@ -1,16 +1,21 @@
import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart';
+import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/const.dart';
+import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/diary/models/diary_day_model.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
+import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
+import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart';
+import 'package:permission_handler/permission_handler.dart';
class PhotoGalleryWidget extends StatefulWidget {
const PhotoGalleryWidget({super.key});
@@ -32,55 +37,75 @@ class _PhotoGalleryWidgetState extends State {
showImagePicker(DiaryDayModel diaryDay) {
showCupertinoModalPopup(
context: context,
- builder: (context) => CupertinoActionSheet(
- actions: [
- CupertinoActionSheetAction(
- onPressed: () async {
- Navigator.of(context).pop();
- final picker = ImagePicker();
- XFile? image = await picker.pickImage(source: ImageSource.camera);
- if (image != null) {
- diaryCubit.uploadPhotos(
- files: [File(image.path)],
- diaryId: diaryDay.id,
+ builder: (context) {
+ final s = S.of(context);
+ return CupertinoActionSheet(
+ actions: [
+ CupertinoActionSheetAction(
+ onPressed: () async {
+ PermissionHandler.checkPermission(
+ permission: Permission.camera,
+ title: s.allowAccessToCamera,
+ description: s.allowAccessToCameraDescription,
+ onGranted: () async {
+ AppRouter.navigatorKey.currentContext?.pop();
+
+ final picker = ImagePicker();
+ XFile? image = await picker.pickImage(source: ImageSource.camera);
+ if (image != null) {
+ diaryCubit.uploadPhotos(
+ files: [File(image.path)],
+ diaryId: diaryDay.id,
+ );
+ }
+ },
+ context: context,
);
- }
- },
- child: const Row(
- children: [
- Icon(Icons.photo_camera),
- SizedBox(width: 8),
- Text('Фото'),
- ],
+ },
+ child: const Row(
+ children: [
+ Icon(Icons.photo_camera),
+ SizedBox(width: 8),
+ Text('Фото'),
+ ],
+ ),
),
- ),
- CupertinoActionSheetAction(
- onPressed: () async {
- Navigator.of(context).pop();
- final picker = ImagePicker();
- List? images = await picker.pickMultiImage();
- if (images.isNotEmpty) {
- diaryCubit.uploadPhotos(
- files: images.map((f) => File(f.path)).toList(),
- diaryId: diaryDay.id,
+ CupertinoActionSheetAction(
+ onPressed: () async {
+ PermissionHandler.checkPermission(
+ permission: Permission.photos,
+ title: s.allowAcessToGalery,
+ description: s.allowAcessToGaleryDescription,
+ onGranted: () async {
+ AppRouter.navigatorKey.currentContext?.pop();
+ final picker = ImagePicker();
+ List? images = await picker.pickMultiImage();
+ if (images.isNotEmpty) {
+ diaryCubit.uploadPhotos(
+ files: images.map((f) => File(f.path)).toList(),
+ diaryId: diaryDay.id,
+ );
+ }
+ },
+ context: context,
);
- }
- },
- child: const Row(
- children: [
- Icon(Icons.photo),
- SizedBox(width: 8),
- Text('Галерея'),
- ],
+ },
+ child: const Row(
+ children: [
+ Icon(Icons.photo),
+ SizedBox(width: 8),
+ Text('Галерея'),
+ ],
+ ),
),
+ ],
+ cancelButton: CupertinoActionSheetAction(
+ onPressed: () => Navigator.of(context).pop(),
+ isDestructiveAction: true,
+ child: const Text('Отмена'),
),
- ],
- cancelButton: CupertinoActionSheetAction(
- onPressed: () => Navigator.of(context).pop(),
- isDestructiveAction: true,
- child: const Text('Отмена'),
- ),
- ),
+ );
+ },
);
}
@@ -92,7 +117,9 @@ class _PhotoGalleryWidgetState extends State {
int mealIndex = state.selectedMeal - 1;
if (state.diary.meals[mealIndex].photos.isEmpty && !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented) {
return GestureDetector(
- onTap: () => showImagePicker(state.diary),
+ onTap: () async {
+ showImagePicker(state.diary);
+ },
child: SvgPicture.asset(
width: MediaQuery.of(context).size.width - 32,
'assets/no_one_image_was_added.svg',
diff --git a/lib/features/diary/presentation/widgets/wakeup_and_retire_widget.dart b/lib/features/diary/presentation/widgets/wakeup_and_retire_widget.dart
index 8c9f6028..1dbe9925 100644
--- a/lib/features/diary/presentation/widgets/wakeup_and_retire_widget.dart
+++ b/lib/features/diary/presentation/widgets/wakeup_and_retire_widget.dart
@@ -8,8 +8,8 @@ import 'package:flutter_svg/flutter_svg.dart';
import '../../../../core/theme/theme_helper.dart';
class WakeupAndRetireWidget extends StatefulWidget {
- const WakeupAndRetireWidget({super.key, required this.showRetire, this.disable = false});
- final bool showRetire;
+ const WakeupAndRetireWidget({super.key, required this.isEnabled, this.disable = false});
+ final bool isEnabled;
final bool disable;
@override
State createState() => _WakeupAndRetireWidgetState();
@@ -94,6 +94,9 @@ class _WakeupAndRetireWidgetState extends State {
Widget build(BuildContext context) {
return BlocBuilder(
builder: (context, state) {
+ if (!widget.isEnabled) {
+ return const SizedBox.shrink();
+ }
if (state is DiaryStateLoaded) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
@@ -157,67 +160,65 @@ class _WakeupAndRetireWidgetState extends State {
),
),
),
- if (widget.showRetire) ...[
- const SizedBox(width: 8),
- Expanded(
- child: Material(
- borderRadius: BorderRadius.circular(12),
- color: Colors.white,
- child: Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 8,
- vertical: 12,
- ),
- child: Column(
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- SvgPicture.asset('assets/icons/retire.svg'),
- const SizedBox(width: 8),
- const Text(
- 'Уход ко сну',
- style: TextStyle(
- fontSize: 16,
+ const SizedBox(width: 8),
+ Expanded(
+ child: Material(
+ borderRadius: BorderRadius.circular(12),
+ color: Colors.white,
+ child: Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 8,
+ vertical: 12,
+ ),
+ child: Column(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ SvgPicture.asset('assets/icons/retire.svg'),
+ const SizedBox(width: 8),
+ const Text(
+ 'Уход ко сну',
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ height: 1,
+ ),
+ ),
+ ],
+ ),
+ const SizedBox(height: 8),
+ InkWell(
+ onTap: widget.disable ? null : () => showTimePicker(state.diary.id, false),
+ borderRadius: BorderRadius.circular(100),
+ child: Ink(
+ height: 32,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(100),
+ color: const Color(0xFFF1F2F6),
+ ),
+ child: Container(
+ alignment: Alignment.center,
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: Text(
+ state.diary.retire.toString().substring(
+ 0,
+ state.diary.retire.toString().length - 3,
+ ),
+ style: const TextStyle(
+ color: Color(0xFF6D6F71),
+ fontSize: 17,
fontWeight: FontWeight.w500,
- height: 1,
- ),
- ),
- ],
- ),
- const SizedBox(height: 8),
- InkWell(
- onTap: widget.disable ? null : () => showTimePicker(state.diary.id, false),
- borderRadius: BorderRadius.circular(100),
- child: Ink(
- height: 32,
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(100),
- color: const Color(0xFFF1F2F6),
- ),
- child: Container(
- alignment: Alignment.center,
- padding: const EdgeInsets.symmetric(horizontal: 16),
- child: Text(
- state.diary.retire.toString().substring(
- 0,
- state.diary.retire.toString().length - 3,
- ),
- style: const TextStyle(
- color: Color(0xFF6D6F71),
- fontSize: 17,
- fontWeight: FontWeight.w500,
- ),
),
),
),
),
- ],
- ),
+ ),
+ ],
),
),
),
- ]
+ ),
],
),
);
diff --git a/lib/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_cubit.dart b/lib/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_cubit.dart
index 1324da92..69d25052 100644
--- a/lib/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_cubit.dart
+++ b/lib/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_cubit.dart
@@ -1,5 +1,6 @@
import 'dart:developer';
+import 'package:eatvio_app/core/enums/filters_comparison_enum.dart';
import 'package:eatvio_app/features/consultations/repositories/consultations_repository.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_state.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
@@ -13,19 +14,22 @@ class ConsultantFinancesCubit extends Cubit {
ConsultantFinancesState(),
);
- Future getConsultations() async {
+ Future getConsultantFinances() async {
try {
emit(state.copyWith(isLoading: true));
+ final consultantFinances = await consultationsRepository.getConsultantFinances(
+ state.periodFilterStatus?.value,
+ state.periodFilterStatus == PeriodFilterStatus.all ? null : FiltersComparisonEnum.greaterThanOrEqual,
+ );
+ emit(state.copyWith(financesModels: consultantFinances, isLoading: false));
} catch (e) {
- emit(state.copyWith(isLoading: false));
+ emit(state.copyWith(financesModels: [], isLoading: false));
log(e.toString());
}
}
- Future setConsultationsPeriod(PeriodFilterStatus period) async {
+ Future setFinancesPeriod(PeriodFilterStatus period) async {
emit(state.copyWith(periodFilterStatus: period));
- await getConsultations();
+ await getConsultantFinances();
}
}
-
-
diff --git a/lib/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_state.dart b/lib/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_state.dart
index 9692d4a6..fff61e45 100644
--- a/lib/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_state.dart
+++ b/lib/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_state.dart
@@ -1,3 +1,4 @@
+import 'package:eatvio_app/features/finances_and_consultations_archive/models/consultant_finance_model.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -6,7 +7,7 @@ part 'consultant_finances_state.freezed.dart';
@freezed
abstract class ConsultantFinancesState with _$ConsultantFinancesState {
factory ConsultantFinancesState({
- @Default([]) List financesModels,
+ @Default([]) List financesModels,
@Default(false) isLoading,
@Default(null) String? error,
@Default(null) PeriodFilterStatus? periodFilterStatus,
diff --git a/lib/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_cubit.dart b/lib/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_cubit.dart
new file mode 100644
index 00000000..55fdbc68
--- /dev/null
+++ b/lib/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_cubit.dart
@@ -0,0 +1,36 @@
+import 'dart:developer';
+
+import 'package:eatvio_app/core/enums/filters_comparison_enum.dart';
+import 'package:eatvio_app/features/consultations/repositories/consultations_repository.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_state.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+class UserPurchasesCubit extends Cubit {
+ final ConsultationsRepository consultationsRepository;
+ UserPurchasesCubit({
+ required this.consultationsRepository,
+ }) : super(
+ UserPurchasesState(),
+ );
+
+ Future getUserPurchases() async {
+ try {
+ emit(state.copyWith(isLoading: true));
+ final purchasesModels = await consultationsRepository.getUserPurchases(
+ state.periodFilterStatus?.value,
+ state.periodFilterStatus == PeriodFilterStatus.all ? null : FiltersComparisonEnum.greaterThanOrEqual,
+ );
+
+ emit(state.copyWith(purchasesModels: purchasesModels, isLoading: false));
+ } catch (e) {
+ emit(state.copyWith(purchasesModels: [], isLoading: false));
+ log(e.toString());
+ }
+ }
+
+ Future setPurchasesPeriod(PeriodFilterStatus period) async {
+ emit(state.copyWith(periodFilterStatus: period));
+ await getUserPurchases();
+ }
+}
diff --git a/lib/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_state.dart b/lib/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_state.dart
new file mode 100644
index 00000000..7ae22c03
--- /dev/null
+++ b/lib/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_state.dart
@@ -0,0 +1,15 @@
+import 'package:eatvio_app/features/finances_and_consultations_archive/models/user_purchases_model.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'user_purchases_state.freezed.dart';
+
+@freezed
+abstract class UserPurchasesState with _$UserPurchasesState {
+ factory UserPurchasesState({
+ @Default([]) List purchasesModels,
+ @Default(false) isLoading,
+ @Default(null) String? error,
+ @Default(null) PeriodFilterStatus? periodFilterStatus,
+ }) = _UserPurchasesState;
+}
diff --git a/lib/features/finances_and_consultations_archive/models/consultant_finance_model.dart b/lib/features/finances_and_consultations_archive/models/consultant_finance_model.dart
new file mode 100644
index 00000000..9e8cc751
--- /dev/null
+++ b/lib/features/finances_and_consultations_archive/models/consultant_finance_model.dart
@@ -0,0 +1,33 @@
+import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'consultant_finance_model.freezed.dart';
+part 'consultant_finance_model.g.dart';
+
+@freezed
+abstract class ConsultantFinanceModel with _$ConsultantFinanceModel {
+ factory ConsultantFinanceModel({
+ @JsonKey(name: 'start_date', fromJson: parseDate) required DateTime startDate,
+ @JsonKey(name: 'bank_id') required String bankId,
+ @JsonKey(name: 'title') required String title,
+ @JsonKey(name: 'clientName') required String clientName,
+ @JsonKey(name: 'duration') required int duration,
+ @JsonKey(name: 'payment_price', fromJson: _parsePayment) required String paymentPrice,
+ required PaymentStatus? paymentStatus,
+ }) = _ConsultantFinanceModel;
+
+ factory ConsultantFinanceModel.fromJson(Map json) => _$ConsultantFinanceModelFromJson(json);
+}
+
+String _parsePayment(json) {
+ return ((json as int) / 100).toStringAsFixed(2);
+}
+
+enum PaymentStatus {
+ @JsonValue('COMPLETED')
+ completed,
+ @JsonValue('REJECTED')
+ rejected,
+ @JsonValue('CHECKED')
+ checked
+}
diff --git a/lib/features/finances_and_consultations_archive/models/user_purchases_model.dart b/lib/features/finances_and_consultations_archive/models/user_purchases_model.dart
new file mode 100644
index 00000000..10c89545
--- /dev/null
+++ b/lib/features/finances_and_consultations_archive/models/user_purchases_model.dart
@@ -0,0 +1,21 @@
+import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart';
+import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'user_purchases_model.freezed.dart';
+part 'user_purchases_model.g.dart';
+
+@freezed
+abstract class UserPurchasesModel with _$UserPurchasesModel {
+ factory UserPurchasesModel({
+ @JsonKey(name: 'start_date', fromJson: parseDate) required DateTime startDate,
+ @JsonKey(name: 'bank_id') required String bankId,
+ @JsonKey(name: 'title') required String title,
+ @JsonKey(name: 'consultantName') required String consultantName,
+ @JsonKey(name: 'duration') required int duration,
+ @JsonKey(name: 'price') required int paymentPrice,
+ @JsonKey(name: 'chosen_plan_last_state') required ChosenPlanStateEnum chosenPlanState,
+ }) = _UserPurchasesModel;
+
+ factory UserPurchasesModel.fromJson(Map json) => _$UserPurchasesModelFromJson(json);
+}
diff --git a/lib/features/finances_and_consultations_archive/presentation/consultant_finances_screen.dart b/lib/features/finances_and_consultations_archive/presentation/consultant_finances_screen.dart
index bb225221..7e731474 100644
--- a/lib/features/finances_and_consultations_archive/presentation/consultant_finances_screen.dart
+++ b/lib/features/finances_and_consultations_archive/presentation/consultant_finances_screen.dart
@@ -1,281 +1,225 @@
-// 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/core/utils/date_time_utils.dart';
-// import 'package:eatvio_app/core/utils/localization_utils.dart';
-// import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart';
-// import 'package:eatvio_app/core/widgets/loader.dart';
-// import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/archive/consultations_archive_cubit.dart';
-// import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/archive/consultations_archive_state.dart';
-// import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_cubit.dart';
-// import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_state.dart';
-// import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
-// import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart';
-// import 'package:eatvio_app/generated/l10n.dart';
-// import 'package:flutter/material.dart';
-// import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:eatvio_app/core/locator_service.dart';
+import 'package:eatvio_app/core/theme/custom_text_style.dart';
+import 'package:eatvio_app/core/theme/theme_helper.dart';
+import 'package:eatvio_app/core/utils/date_time_utils.dart';
+import 'package:eatvio_app/core/utils/localization_utils.dart';
+import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart';
+import 'package:eatvio_app/core/widgets/loader.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_cubit.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_state.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/models/consultant_finance_model.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart';
+import 'package:eatvio_app/features/my_finance/presentation/pages/card_settings.dart';
+import 'package:eatvio_app/generated/l10n.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:go_router/go_router.dart';
-// class ConsultantFinancesScreen extends StatefulWidget {
-// const ConsultantFinancesScreen({super.key});
+class ConsultantFinancesScreen extends StatefulWidget {
+ const ConsultantFinancesScreen({super.key});
+ static const name = 'consultantFinancesScreen';
+ static const path = '/consultant-finances-screen';
+ @override
+ State createState() => _ConsultantFinancesScreenState();
+}
-// @override
-// State createState() => _ConsultantFinancesScreenState();
-// }
-
-// class _ConsultantFinancesScreenState extends State {
-// @override
-// Widget build(BuildContext context) {
-// final s = S.of(context);
-// return BlocProvider(
-// create: (context) => sl(),
-// child: BlocBuilder(
-// builder: (context, state) {
-// return Scaffold(
-// backgroundColor: const Color(0xFFF0F2F5),
-// appBar: AppBar(
-// shape: const RoundedRectangleBorder(
-// borderRadius: BorderRadiusDirectional.vertical(
-// bottom: Radius.circular(12),
-// ),
-// ),
-// title: const Text('Архив консультаций'),
-// centerTitle: true,
-// ),
-// body: state.isLoading
-// ? const Center(
-// child: Loader(),
-// )
-// : SingleChildScrollView(
-// padding: const EdgeInsets.symmetric(vertical: 8),
-// child: Column(
-// children: [
-// Material(
-// color: Colors.white,
-// borderRadius: BorderRadius.circular(12),
-// child: Column(
-// crossAxisAlignment: CrossAxisAlignment.start,
-// children: [
-// const SizedBox(height: 16),
-// Padding(
-// padding: const EdgeInsets.symmetric(horizontal: 16),
-// child: Text(
-// 'Всего консультаций: ${state.statisticModel.total}',
-// style: const TextStyle(
-// fontSize: 16,
-// fontWeight: FontWeight.w500,
-// color: Color(0xFF6D6F71),
-// height: 1,
-// ),
-// ),
-// ),
-// const SizedBox(height: 12),
-// Row(
-// children: [
-// const SizedBox(width: 16),
-// Material(
-// color: const Color(0xFF0BB450).withAlpha(11),
-// borderRadius: BorderRadius.circular(8),
-// child: Padding(
-// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
-// child: Text(
-// 'Завершено: ${state.statisticModel.finished}',
-// style: const TextStyle(
-// fontSize: 14,
-// fontWeight: FontWeight.w400,
-// color: Color(0xFF0BB450),
-// height: 1,
-// ),
-// ),
-// ),
-// ),
-// const SizedBox(width: 8),
-// Material(
-// color: const Color(0xFFD01717).withAlpha(11),
-// borderRadius: BorderRadius.circular(8),
-// child: Padding(
-// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
-// child: Text(
-// 'Отменено: ${state.statisticModel.canceled}',
-// style: const TextStyle(
-// fontSize: 14,
-// fontWeight: FontWeight.w400,
-// color: Color(0xFFD01717),
-// height: 1,
-// ),
-// ),
-// ),
-// ),
-// ],
-// ),
-// const SizedBox(height: 16),
-// ],
-// ),
-// ),
-// const SizedBox(height: 8),
-// Material(
-// color: Colors.white,
-// borderRadius: BorderRadius.circular(12),
-// child: Padding(
-// padding: const EdgeInsets.only(left: 16, right: 16, top: 16),
-// child: SingleChildScrollView(
-// physics: const NeverScrollableScrollPhysics(),
-// child: Column(
-// children: [
-// CustomDropDownButton(
-// items: PeriodFilterStatus.values
-// .map(
-// (value) => DropdownMenuItem(
-// value: value,
-// child: Text(
-// value.name,
-// style: TextStyles.textMMedium.copyWith(color: appTheme.black),
-// ),
-// ),
-// )
-// .toList(),
-// onChanged: (value) {
-// if (value == null) return;
-// context.read().setConsultationsPeriod(value);
-// },
-// value: state.periodFilterStatus ?? PeriodFilterStatus.values.first,
-// ),
-// const SizedBox(height: 12),
-// CustomDropDownButton(
-// items: ConsultationFilterStatus.values
-// .map(
-// (value) => DropdownMenuItem(
-// value: value,
-// child: Text(
-// value.name,
-// style: TextStyles.textMMedium.copyWith(color: appTheme.black),
-// ),
-// ),
-// )
-// .toList(),
-// onChanged: (value) {
-// if (value == null) return;
-// context.read().setConsultationsStatus(value);
-// },
-// value: state.consultationFilterStatus,
-// ),
-// const SizedBox(height: 12),
-// if (state.consultations.isNotEmpty)
-// ListView.separated(
-// shrinkWrap: true,
-// physics: const NeverScrollableScrollPhysics(),
-// itemBuilder: (context, index) {
-// return Material(
-// color: const Color(0xFFF6F6F6),
-// borderRadius: BorderRadius.circular(20),
-// child: Padding(
-// padding: const EdgeInsets.all(12),
-// child: Column(
-// crossAxisAlignment: CrossAxisAlignment.start,
-// children: [
-// Row(
-// mainAxisAlignment: MainAxisAlignment.spaceBetween,
-// children: [
-// Text(
-// state.consultations[index].id.toString(),
-// style: const TextStyle(
-// color: Color(0xFF323F4B),
-// height: 1,
-// fontWeight: FontWeight.w500,
-// ),
-// ),
-// Text(
-// state.consultations[index].startDate.format('yyyy.MM.dd'),
-// style: const TextStyle(
-// color: Color(0xFF6D6F71),
-// height: 1,
-// fontWeight: FontWeight.w500,
-// ),
-// ),
-// ],
-// ),
-// const SizedBox(height: 8),
-// Text(
-// state.consultations[index].planTitle,
-// style: TextStyle(
-// color: appTheme.blueA400,
-// height: 1,
-// fontSize: 18,
-// fontWeight: FontWeight.w600,
-// ),
-// ),
-// const SizedBox(height: 8),
-// Text(
-// 'Покупатель: ${state.consultations[index].userName}',
-// style: const TextStyle(
-// color: Color(0xFF323F4B),
-// height: 1,
-// fontSize: 14,
-// fontWeight: FontWeight.w500,
-// ),
-// ),
-// const SizedBox(height: 8),
-// Text(
-// '${state.consultations[index].duration} ${getDayForm(state.consultations[index].duration, s)}',
-// 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),
-// ),
-// ),
-// const SizedBox(height: 8),
-// Row(
-// mainAxisAlignment: MainAxisAlignment.spaceBetween,
-// children: [
-// Text(
-// '${state.consultations[index].consultantPayment.toStringAsFixed(2)} ₽',
-// style: const TextStyle(
-// color: Color(0xFF323F4B),
-// height: 1,
-// fontSize: 18,
-// fontWeight: FontWeight.w600,
-// ),
-// ),
-// StatusWidget(
-// status: state.consultations[index].chosenPlanState,
-// ),
-// ],
-// ),
-// ],
-// ),
-// ),
-// );
-// },
-// separatorBuilder: (context, index) => const SizedBox(height: 12),
-// itemCount: state.financesModels.length,
-// )
-// else
-// const Padding(
-// padding: EdgeInsets.symmetric(vertical: 40.0),
-// child: Text(
-// 'У вас нет таких консультаций',
-// style: TextStyles.textMMedium,
-// ),
-// ),
-// ],
-// ),
-// ),
-// ),
-// ),
-// ],
-// ),
-// ),
-// );
-// },
-// ),
-// );
-// }
-// }
+class _ConsultantFinancesScreenState extends State {
+ @override
+ Widget build(BuildContext context) {
+ final s = S.of(context);
+ return BlocProvider(
+ create: (context) => sl()..getConsultantFinances(),
+ child: BlocBuilder(
+ builder: (context, state) {
+ 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),
+ ),
+ centerTitle: true,
+ ),
+ body: state.isLoading
+ ? const Center(
+ child: Loader(),
+ )
+ : SingleChildScrollView(
+ padding: const EdgeInsets.symmetric(vertical: 8),
+ child: Column(
+ children: [
+ InkWell(
+ onTap: () {
+ GoRouter.of(context).pushNamed(CardSettingsScreen.name);
+ },
+ borderRadius: BorderRadius.circular(20),
+ child: Ink(
+ decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), color: appTheme.blueA400),
+ width: MediaQuery.of(context).size.width - 32,
+ child: const Center(
+ child: Padding(
+ padding: EdgeInsets.all(16),
+ child: Text('Управление картами', style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600)),
+ ),
+ ),
+ ),
+ ),
+ const SizedBox(height: 8),
+ Material(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(12),
+ child: Padding(
+ padding: const EdgeInsets.only(left: 16, right: 16, top: 16),
+ child: SingleChildScrollView(
+ physics: const NeverScrollableScrollPhysics(),
+ child: Column(
+ children: [
+ CustomDropDownButton(
+ items: PeriodFilterStatus.values
+ .map(
+ (value) => DropdownMenuItem(
+ value: value,
+ child: Text(
+ value.name,
+ style: TextStyles.textMMedium.copyWith(color: appTheme.black),
+ ),
+ ),
+ )
+ .toList(),
+ onChanged: (value) {
+ if (value == null) return;
+ context.read().setFinancesPeriod(value);
+ },
+ value: state.periodFilterStatus ?? PeriodFilterStatus.values.first,
+ ),
+ const SizedBox(height: 12),
+ if (state.financesModels.isNotEmpty)
+ ListView.separated(
+ shrinkWrap: true,
+ physics: const NeverScrollableScrollPhysics(),
+ itemBuilder: (context, index) {
+ final Status modelStatus = switch (state.financesModels[index].paymentStatus) {
+ null => Status.inProgress,
+ PaymentStatus.completed => Status.success,
+ PaymentStatus.rejected => Status.canceled,
+ PaymentStatus.checked => Status.success,
+ };
+ return Material(
+ color: const Color(0xFFF6F6F6),
+ borderRadius: BorderRadius.circular(20),
+ child: Padding(
+ padding: const EdgeInsets.all(12),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Builder(builder: (context) {
+ final idLen = state.financesModels[index].bankId.length;
+ return Text(
+ '${state.financesModels[index].bankId.substring(0, 4)}...${state.financesModels[index].bankId.substring(idLen - 4, idLen)} ',
+ style: TextStyles.textSMedium.copyWith(color: appTheme.black),
+ );
+ }),
+ Text(
+ state.financesModels[index].startDate.format('yyyy.MM.dd'),
+ style: const TextStyle(
+ color: Color(0xFF6D6F71),
+ height: 1,
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ ],
+ ),
+ const SizedBox(height: 8),
+ Text(
+ state.financesModels[index].title,
+ style: TextStyle(
+ color: appTheme.blueA400,
+ height: 1,
+ fontSize: 18,
+ fontWeight: FontWeight.w600,
+ ),
+ ),
+ const SizedBox(height: 8),
+ Text(
+ 'Покупатель: ${state.financesModels[index].clientName}',
+ style: const TextStyle(
+ color: Color(0xFF323F4B),
+ height: 1,
+ fontSize: 14,
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ const SizedBox(height: 8),
+ Text(
+ '${state.financesModels[index].duration} ${getDayForm(state.financesModels[index].duration, s)}',
+ 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),
+ ),
+ ),
+ const SizedBox(height: 8),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ '${state.financesModels[index].paymentPrice} ₽',
+ style: const TextStyle(
+ color: Color(0xFF323F4B),
+ height: 1,
+ fontSize: 18,
+ fontWeight: FontWeight.w600,
+ ),
+ ),
+ StatusWidget(
+ status: modelStatus,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ );
+ },
+ separatorBuilder: (context, index) => const SizedBox(height: 12),
+ itemCount: state.financesModels.length,
+ )
+ else
+ const Padding(
+ padding: EdgeInsets.symmetric(vertical: 40.0),
+ child: Text(
+ 'У вас нет таких консультаций',
+ style: TextStyles.textMMedium,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ],
+ )),
+ );
+ },
+ ),
+ );
+ }
+}
diff --git a/lib/features/finances_and_consultations_archive/presentation/consultations_archive_screen.dart b/lib/features/finances_and_consultations_archive/presentation/consultations_archive_screen.dart
index fa934bcf..ec2d4900 100644
--- a/lib/features/finances_and_consultations_archive/presentation/consultations_archive_screen.dart
+++ b/lib/features/finances_and_consultations_archive/presentation/consultations_archive_screen.dart
@@ -9,13 +9,15 @@ import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/arc
import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/archive/consultations_archive_state.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart';
+import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class ConsultationsArchiveScreen extends StatefulWidget {
const ConsultationsArchiveScreen({super.key});
-
+ static const name = 'consultationsArchiveScreen';
+ static const path = '/consultations-archive-screen';
@override
State createState() => _ConsultationsArchiveScreenState();
}
@@ -164,6 +166,15 @@ class _ConsultationsArchiveScreenState extends State
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
+ final Status consultationStatus;
+ if (state.consultations[index].chosenPlanState == ChosenPlanStateEnum.stateCanceledByTheInitiator) {
+ consultationStatus = Status.canceled;
+ } else if (state.consultations[index].chosenPlanState == ChosenPlanStateEnum.stateChargeWasCollected) {
+ consultationStatus = Status.success;
+ } else {
+ consultationStatus = Status.inProgress;
+ }
+
return Material(
color: const Color(0xFFF6F6F6),
borderRadius: BorderRadius.circular(20),
@@ -245,7 +256,7 @@ class _ConsultationsArchiveScreenState extends State
),
),
StatusWidget(
- status: state.consultations[index].chosenPlanState,
+ status: consultationStatus,
),
],
),
diff --git a/lib/features/finances_and_consultations_archive/presentation/user_purchases_screen.dart b/lib/features/finances_and_consultations_archive/presentation/user_purchases_screen.dart
new file mode 100644
index 00000000..6f137211
--- /dev/null
+++ b/lib/features/finances_and_consultations_archive/presentation/user_purchases_screen.dart
@@ -0,0 +1,209 @@
+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/core/utils/date_time_utils.dart';
+import 'package:eatvio_app/core/utils/localization_utils.dart';
+import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart';
+import 'package:eatvio_app/core/widgets/loader.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_cubit.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_state.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart';
+import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
+import 'package:eatvio_app/generated/l10n.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+class UserPurchasesScreen extends StatefulWidget {
+ const UserPurchasesScreen({super.key});
+ static const name = 'userPurchasesScreen';
+ static const path = '/user-purchases-screen';
+ @override
+ State createState() => _UserPurchasesScreen();
+}
+
+class _UserPurchasesScreen extends State {
+ @override
+ Widget build(BuildContext context) {
+ final s = S.of(context);
+ return BlocProvider(
+ create: (context) => sl()..getUserPurchases(),
+ child: BlocBuilder(
+ builder: (context, state) {
+ 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),
+ ),
+ centerTitle: true,
+ ),
+ body: state.isLoading
+ ? const Center(
+ child: Loader(),
+ )
+ : SingleChildScrollView(
+ padding: const EdgeInsets.symmetric(vertical: 8),
+ child: Column(
+ children: [
+ const SizedBox(height: 8),
+ Material(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(12),
+ child: Padding(
+ padding: const EdgeInsets.only(left: 16, right: 16, top: 16),
+ child: SingleChildScrollView(
+ physics: const NeverScrollableScrollPhysics(),
+ child: Column(
+ children: [
+ CustomDropDownButton(
+ items: PeriodFilterStatus.values
+ .map(
+ (value) => DropdownMenuItem(
+ value: value,
+ child: Text(
+ value.name,
+ style: TextStyles.textMMedium.copyWith(color: appTheme.black),
+ ),
+ ),
+ )
+ .toList(),
+ onChanged: (value) {
+ if (value == null) return;
+ context.read().setPurchasesPeriod(value);
+ },
+ value: state.periodFilterStatus ?? PeriodFilterStatus.values.first,
+ ),
+ const SizedBox(height: 12),
+ if (state.purchasesModels.isNotEmpty)
+ ListView.separated(
+ shrinkWrap: true,
+ physics: const NeverScrollableScrollPhysics(),
+ itemBuilder: (context, index) {
+ final Status purchaseStatus;
+ if (state.purchasesModels[index].chosenPlanState == ChosenPlanStateEnum.stateCanceledByTheInitiator) {
+ purchaseStatus = Status.canceled;
+ } else if (state.purchasesModels[index].chosenPlanState == ChosenPlanStateEnum.stateChargeWasCollected) {
+ purchaseStatus = Status.success;
+ } else {
+ purchaseStatus = Status.inProgress;
+ }
+ return Material(
+ color: const Color(0xFFF6F6F6),
+ borderRadius: BorderRadius.circular(20),
+ child: Padding(
+ padding: const EdgeInsets.all(12),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ 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),
+ );
+ }),
+ Text(
+ state.purchasesModels[index].startDate.format('yyyy.MM.dd'),
+ 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,
+ ),
+ ),
+ const SizedBox(height: 8),
+ Text(
+ 'Продавец: ${state.purchasesModels[index].consultantName}',
+ 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,
+ ),
+ ),
+ const SizedBox(height: 8),
+ Container(
+ height: 1,
+ width: double.infinity,
+ decoration: const BoxDecoration(
+ color: Color(0xFFDEDEDF),
+ ),
+ ),
+ const SizedBox(height: 8),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ '${state.purchasesModels[index].paymentPrice} ₽',
+ style: const TextStyle(
+ color: Color(0xFF323F4B),
+ height: 1,
+ fontSize: 18,
+ fontWeight: FontWeight.w600,
+ ),
+ ),
+ StatusWidget(
+ status: purchaseStatus,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ );
+ },
+ separatorBuilder: (context, index) => const SizedBox(height: 12),
+ itemCount: state.purchasesModels.length,
+ )
+ else
+ const Padding(
+ padding: EdgeInsets.symmetric(vertical: 40.0),
+ child: Text(
+ 'У вас нет таких консультаций',
+ style: TextStyles.textMMedium,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ],
+ )),
+ );
+ },
+ ),
+ );
+ }
+}
diff --git a/lib/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart b/lib/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart
index d53c254a..fab1fc21 100644
--- a/lib/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart
+++ b/lib/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart
@@ -1,15 +1,15 @@
import 'package:eatvio_app/core/theme/theme_helper.dart';
-import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
import 'package:flutter/material.dart';
class StatusWidget extends StatelessWidget {
const StatusWidget({super.key, required this.status});
- final ChosenPlanStateEnum status;
+ final Status status;
@override
Widget build(BuildContext context) {
- if (status == ChosenPlanStateEnum.stateCanceledByTheInitiator) {
- return Material(
+ return switch (status){
+
+ Status.canceled => Material(
color: appTheme.lightRed,
borderRadius: BorderRadius.circular(8),
child: Padding(
@@ -24,9 +24,9 @@ class StatusWidget extends StatelessWidget {
),
),
),
- );
- } else if (status == ChosenPlanStateEnum.stateChargeWasCollected) {
- return Material(
+ ),
+
+ Status.success => Material(
color: const Color(0xFF0BB450).withAlpha(11),
borderRadius: BorderRadius.circular(8),
child: const Padding(
@@ -41,9 +41,9 @@ class StatusWidget extends StatelessWidget {
),
),
),
- );
- }
- return Material(
+ ),
+
+ Status.inProgress => Material(
color: appTheme.transparentBlue,
borderRadius: BorderRadius.circular(8),
child: Padding(
@@ -58,6 +58,13 @@ class StatusWidget extends StatelessWidget {
),
),
),
- );
+ ),
+ };
}
}
+
+enum Status {
+ canceled,
+ success,
+ inProgress
+}
\ No newline at end of file
diff --git a/lib/features/menu/presentation/pages/menu_screen.dart b/lib/features/menu/presentation/pages/menu_screen.dart
index cb2ff7ff..f657c6a7 100644
--- a/lib/features/menu/presentation/pages/menu_screen.dart
+++ b/lib/features/menu/presentation/pages/menu_screen.dart
@@ -4,6 +4,8 @@ import 'package:eatvio_app/core/token_storage.dart';
import 'package:eatvio_app/core/utils/image_constant.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/consultant_finances_screen.dart';
+import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/user_purchases_screen.dart';
import 'package:eatvio_app/features/my_diets/presentation/my_diets_screen.dart';
import 'package:eatvio_app/features/my_plans/presentation/my_plans_screen.dart';
import 'package:eatvio_app/features/profile/models/profile_model.dart';
@@ -40,7 +42,11 @@ class MenuScreen extends StatelessWidget {
leading: 'assets/icons/finance.svg',
title: 'Мои финансы',
onTap: () {
- GoRouter.of(context).pushNamed('userFinanceScreen');
+ if (profileModel.roles.contains('consultant')) {
+ GoRouter.of(context).push(ConsultantFinancesScreen.path);
+ return;
+ }
+ GoRouter.of(context).push(UserPurchasesScreen.path);
},
),
_divider(context),
diff --git a/lib/features/profile/presentation/pages/profile_screen.dart b/lib/features/profile/presentation/pages/profile_screen.dart
index 363c2152..e02d1a34 100644
--- a/lib/features/profile/presentation/pages/profile_screen.dart
+++ b/lib/features/profile/presentation/pages/profile_screen.dart
@@ -2,8 +2,10 @@ import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:dismissible_page/dismissible_page.dart';
+import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/enums/complained_content.dart';
+import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
import 'package:eatvio_app/core/utils/date_time_utils.dart';
import 'package:eatvio_app/core/widgets/data_error_widget.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
@@ -26,6 +28,7 @@ import 'package:eatvio_app/features/profile/presentation/widgets/profile_blog_po
import 'package:eatvio_app/features/profile/presentation/widgets/weight_chart_widget.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
+import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -34,6 +37,7 @@ import 'package:go_router/go_router.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart';
+import 'package:permission_handler/permission_handler.dart';
import 'package:share_plus/share_plus.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -600,9 +604,7 @@ class _ProfileScreenState extends State {
_educationBlock(String title, String date) {
final parsedDate = DateTime.tryParse(date);
- final formattedDate = parsedDate != null
- ? DateFormat('d MMMM y', 'ru_RU').format(parsedDate)
- : date;
+ final formattedDate = parsedDate != null ? DateFormat('d MMMM y', 'ru_RU').format(parsedDate) : date;
return SizedBox(
width: double.infinity,
@@ -640,7 +642,6 @@ class _ProfileScreenState extends State {
);
}
-
_socialLinks(ProfileStateLoaded state) {
if (!state.profile.roles.contains('consultant')) {
return const SizedBox.shrink();
@@ -993,51 +994,70 @@ class _ProfileScreenState extends State {
if (value == 'replace') {
showCupertinoModalPopup(
context: context,
- builder: (context) => CupertinoActionSheet(
- actions: [
- CupertinoActionSheetAction(
- onPressed: () async {
- Navigator.of(context).pop();
- final picker = ImagePicker();
- XFile? image = await picker.pickImage(source: ImageSource.camera);
- if (image != null) {
- await profileCubit.uploadPhoto(File(image.path));
- await sl().getMyProfile();
- }
- },
- child: const Row(
- children: [
- Icon(Icons.photo_camera),
- SizedBox(width: 8),
- Text('Фото'),
- ],
+ builder: (context) {
+ final s = S.of(context);
+ return CupertinoActionSheet(
+ actions: [
+ CupertinoActionSheetAction(
+ onPressed: () async {
+ PermissionHandler.checkPermission(
+ permission: Permission.camera,
+ title: s.allowAccessToCamera,
+ description: s.allowAccessToCameraDescription,
+ onGranted: () async {
+ AppRouter.navigatorKey.currentContext?.pop();
+ final picker = ImagePicker();
+ XFile? image = await picker.pickImage(source: ImageSource.camera);
+ if (image != null) {
+ await profileCubit.uploadPhoto(File(image.path));
+ await sl().getMyProfile();
+ }
+ },
+ context: context,
+ );
+ },
+ child: const Row(
+ children: [
+ Icon(Icons.photo_camera),
+ SizedBox(width: 8),
+ Text('Фото'),
+ ],
+ ),
),
- ),
- CupertinoActionSheetAction(
- onPressed: () async {
- Navigator.of(context).pop();
- final picker = ImagePicker();
- XFile? image = await picker.pickImage(source: ImageSource.gallery);
- if (image != null) {
- await profileCubit.uploadPhoto(File(image.path));
- await sl().getMyProfile();
- }
- },
- child: const Row(
- children: [
- Icon(Icons.photo),
- SizedBox(width: 8),
- Text('Галерея'),
- ],
+ CupertinoActionSheetAction(
+ onPressed: () async {
+ PermissionHandler.checkPermission(
+ permission: Permission.photos,
+ title: s.allowAcessToGalery,
+ description: s.allowAcessToGaleryDescription,
+ onGranted: () async {
+ AppRouter.navigatorKey.currentContext?.pop();
+ final picker = ImagePicker();
+ XFile? image = await picker.pickImage(source: ImageSource.gallery);
+ if (image != null) {
+ await profileCubit.uploadPhoto(File(image.path));
+ await sl().getMyProfile();
+ }
+ },
+ context: context,
+ );
+ },
+ child: const Row(
+ children: [
+ Icon(Icons.photo),
+ SizedBox(width: 8),
+ Text('Галерея'),
+ ],
+ ),
),
+ ],
+ cancelButton: CupertinoActionSheetAction(
+ onPressed: () => Navigator.of(context).pop(),
+ isDestructiveAction: true,
+ child: const Text('Отмена'),
),
- ],
- cancelButton: CupertinoActionSheetAction(
- onPressed: () => Navigator.of(context).pop(),
- isDestructiveAction: true,
- child: const Text('Отмена'),
- ),
- ),
+ );
+ },
);
}
if (value == 'delete') {
diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart
index 611eb301..e318c962 100644
--- a/lib/generated/intl/messages_en.dart
+++ b/lib/generated/intl/messages_en.dart
@@ -73,6 +73,19 @@ class MessageLookup extends MessageLookupByLibrary {
"allergies_presence": MessageLookupByLibrary.simpleMessage(
"Наличие аллергии",
),
+ "allowAccess": MessageLookupByLibrary.simpleMessage("Разрешить доступ"),
+ "allowAccessToCamera": MessageLookupByLibrary.simpleMessage(
+ "Разрешите доступ к камере",
+ ),
+ "allowAccessToCameraDescription": MessageLookupByLibrary.simpleMessage(
+ "Предоставление доступа позволит вам делать фотографии и добавлять к записям",
+ ),
+ "allowAcessToGalery": MessageLookupByLibrary.simpleMessage(
+ "Разрешите доступ к фото",
+ ),
+ "allowAcessToGaleryDescription": MessageLookupByLibrary.simpleMessage(
+ "Предоставление доступа позволит вам добавлять фото к записям",
+ ),
"another_form_of_comment": MessageLookupByLibrary.simpleMessage(
"комментария",
),
diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart
index 34d632d2..9f338062 100644
--- a/lib/generated/l10n.dart
+++ b/lib/generated/l10n.dart
@@ -28,9 +28,10 @@ class S {
static const AppLocalizationDelegate delegate = AppLocalizationDelegate();
static Future load(Locale locale) {
- final name = (locale.countryCode?.isEmpty ?? false)
- ? locale.languageCode
- : locale.toString();
+ final name =
+ (locale.countryCode?.isEmpty ?? false)
+ ? locale.languageCode
+ : locale.toString();
final localeName = Intl.canonicalizedLocale(name);
return initializeMessages(localeName).then((_) {
Intl.defaultLocale = localeName;
@@ -3343,6 +3344,56 @@ class S {
args: [],
);
}
+
+ /// `Разрешите доступ к фото`
+ String get allowAcessToGalery {
+ return Intl.message(
+ 'Разрешите доступ к фото',
+ name: 'allowAcessToGalery',
+ desc: '',
+ args: [],
+ );
+ }
+
+ /// `Предоставление доступа позволит вам добавлять фото к записям`
+ String get allowAcessToGaleryDescription {
+ return Intl.message(
+ 'Предоставление доступа позволит вам добавлять фото к записям',
+ name: 'allowAcessToGaleryDescription',
+ desc: '',
+ args: [],
+ );
+ }
+
+ /// `Разрешите доступ к камере`
+ String get allowAccessToCamera {
+ return Intl.message(
+ 'Разрешите доступ к камере',
+ name: 'allowAccessToCamera',
+ desc: '',
+ args: [],
+ );
+ }
+
+ /// `Предоставление доступа позволит вам делать фотографии и добавлять к записям`
+ String get allowAccessToCameraDescription {
+ return Intl.message(
+ 'Предоставление доступа позволит вам делать фотографии и добавлять к записям',
+ name: 'allowAccessToCameraDescription',
+ desc: '',
+ args: [],
+ );
+ }
+
+ /// `Разрешить доступ`
+ String get allowAccess {
+ return Intl.message(
+ 'Разрешить доступ',
+ name: 'allowAccess',
+ desc: '',
+ args: [],
+ );
+ }
}
class AppLocalizationDelegate extends LocalizationsDelegate {
diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb
index 1145f9d1..96ddc4be 100644
--- a/lib/l10n/intl_en.arb
+++ b/lib/l10n/intl_en.arb
@@ -397,6 +397,11 @@
"planStarted": "План «{planName}» начался",
"userCanceldThePlanSubscription": "Пользователь {name} отменил подписку на план «{planName}»",
"userSubscribedToPlan": "Пользователь {name} подписался на план «{planName}»",
- "markAsRead": "Отметить всё прочитанным"
+ "markAsRead": "Отметить всё прочитанным",
+ "allowAcessToGalery": "Разрешите доступ к фото",
+ "allowAcessToGaleryDescription": "Предоставление доступа позволит вам добавлять фото к записям",
+ "allowAccessToCamera": "Разрешите доступ к камере",
+ "allowAccessToCameraDescription": "Предоставление доступа позволит вам делать фотографии и добавлять к записям",
+ "allowAccess": "Разрешить доступ"
}
diff --git a/lib/main.dart b/lib/main.dart
index d7d37793..b4eaba75 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -21,7 +21,7 @@ import 'package:sentry_flutter/sentry_flutter.dart';
Future main() async {
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
- FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
+ FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await di.init();
await SentryFlutter.init((options) {
diff --git a/pubspec.yaml b/pubspec.yaml
index c9e7430b..9a8b0e16 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -70,6 +70,7 @@ dependencies:
version: ^3.0.2
package_info_plus: ^8.3.0
device_info_plus: ^11.4.0
+ permission_handler: ^12.0.0+1
dev_dependencies:
build_runner: ^2.4.15