From c909c75d4705d251af81dbaa2069d0edfac2c74b Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Fri, 22 Nov 2024 15:27:51 +0300 Subject: [PATCH] feat(profile): subscribers and subscriptions --- .gitea/workflows/test.yml | 2 +- android/app/build.gradle | 12 +- android/build.gradle | 7 +- .../reports/problems/problems-report.html | 663 ++++++++++++++++++ lib/core/app_router.dart | 29 +- lib/core/locator_service.dart | 2 + lib/core/widgets/loader.dart | 19 + .../presentation/pages/blog_post_screen.dart | 13 +- .../presentation/pages/calendar_screen.dart | 12 +- .../chat/presentation/pages/chat_screen.dart | 22 +- .../pages/chat_select_screen.dart | 22 +- .../models/consultant_profile_model.g.dart | 48 +- .../models/product_moderation_model.dart | 8 +- .../product_moderation_model.freezed.dart | 64 +- .../models/product_moderation_model.g.dart | 8 +- .../diary/presentation/bloc/diary_cubit.dart | 30 +- .../diary/presentation/bloc/diary_state.dart | 2 +- .../bloc/diary_state.freezed.dart | 62 +- .../presentation/pages/diary_screen.dart | 161 ++--- ...ssary_moderation_request_modal_widget.dart | 8 +- .../widgets/satiety_rating_widget.dart | 50 +- .../diary/repositories/diary_repository.dart | 20 +- .../diary/services/diary_service.dart | 7 + .../diary/services/diary_service.g.dart | 29 + .../presentation/pages/about_diet_screen.dart | 23 +- .../feed/presentation/pages/feed_screen.dart | 53 +- lib/features/profile/models/sub_model.dart | 38 + .../profile/models/sub_model.freezed.dart | 646 +++++++++++++++++ lib/features/profile/models/sub_model.g.dart | 57 ++ .../bloc/sub_cubit/sub_cubit.dart | 29 + .../bloc/sub_cubit/sub_state.dart | 19 + .../bloc/sub_cubit/sub_state.freezed.dart | 638 +++++++++++++++++ .../presentation/pages/profile_screen.dart | 87 ++- .../presentation/pages/subs_screen.dart | 69 ++ .../widgets/sub_action_button_widget.dart | 63 ++ .../presentation/widgets/sub_widget.dart | 65 ++ .../repositories/profile_repository.dart | 17 + .../profile/services/profile_service.dart | 11 + .../profile/services/profile_service.g.dart | 66 ++ .../pages/about_recipes_screen.dart | 10 +- .../pages/recipe_create_screen.dart | 12 +- .../pages/recipe_edit_screen.dart | 12 +- .../pages/registration_screen.dart | 12 +- .../presentation/pages/settings_screen.dart | 12 +- pubspec.yaml | 2 +- 45 files changed, 2790 insertions(+), 451 deletions(-) create mode 100644 android/build/reports/problems/problems-report.html create mode 100644 lib/core/widgets/loader.dart create mode 100644 lib/features/profile/models/sub_model.dart create mode 100644 lib/features/profile/models/sub_model.freezed.dart create mode 100644 lib/features/profile/models/sub_model.g.dart create mode 100644 lib/features/profile/presentation/bloc/sub_cubit/sub_cubit.dart create mode 100644 lib/features/profile/presentation/bloc/sub_cubit/sub_state.dart create mode 100644 lib/features/profile/presentation/bloc/sub_cubit/sub_state.freezed.dart create mode 100644 lib/features/profile/presentation/pages/subs_screen.dart create mode 100644 lib/features/profile/presentation/widgets/sub_action_button_widget.dart create mode 100644 lib/features/profile/presentation/widgets/sub_widget.dart diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 24078471..2e33a319 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -100,7 +100,7 @@ jobs: run: flutter test - name: Build apk - run: flutter build apk + run: flutter build apk --release - name: Rename and move apk run: | diff --git a/android/app/build.gradle b/android/app/build.gradle index fe0bd484..3d80d62a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -6,22 +6,22 @@ plugins { android { namespace = "com.eatvioapp.app" - compileSdk = 35 - ndkVersion = "28.0.12433566" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion compileOptions { - sourceCompatibility JavaVersion.VERSION_21 - targetCompatibility JavaVersion.VERSION_21 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 } kotlinOptions { - jvmTarget = 21 + jvmTarget = JavaVersion.VERSION_21 } defaultConfig { applicationId = "com.eatvioapp.app" minSdk = flutter.minSdkVersion - targetSdk = 35 + targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode versionName = flutter.versionName } diff --git a/android/build.gradle b/android/build.gradle index 002c6222..d2ffbffa 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,19 +1,16 @@ allprojects { - tasks.withType(JavaCompile) { - options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" - } repositories { google() mavenCentral() } } -rootProject.buildDir = '../build' +rootProject.buildDir = "../build" subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" } subprojects { - project.evaluationDependsOn(':app') + project.evaluationDependsOn(":app") } tasks.register("clean", Delete) { diff --git a/android/build/reports/problems/problems-report.html b/android/build/reports/problems/problems-report.html new file mode 100644 index 00000000..f2f60fc9 --- /dev/null +++ b/android/build/reports/problems/problems-report.html @@ -0,0 +1,663 @@ + + + + + + + + + + + + + Gradle Configuration Cache + + + +
+ +
+ Loading... +
+ + + + + + diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index 7f5a024d..77635f79 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -23,6 +23,7 @@ import 'package:eatvio_app/features/notifications/presentation/pages/notificatio import 'package:eatvio_app/features/plans/models/plan_pagination_model.dart'; import 'package:eatvio_app/features/plans/presentation/pages/about_plan_screen.dart'; import 'package:eatvio_app/features/profile/presentation/pages/profile_screen.dart'; +import 'package:eatvio_app/features/profile/presentation/pages/subs_screen.dart'; import 'package:eatvio_app/features/recipes/presentation/pages/about_recipes_screen.dart'; import 'package:eatvio_app/features/recipes/presentation/pages/recipe_create_screen.dart'; import 'package:eatvio_app/features/recipes/presentation/pages/recipe_edit_screen.dart'; @@ -77,32 +78,32 @@ class AppRouter { await context.read().runInitialization(); return null; }, - builder: (context, state) => ProfileScreen(), + pageBuilder: (context, state) => NoTransitionPage(child: ProfileScreen()), ), GoRoute( path: '/feed-screen', name: 'feedScreen', - builder: (context, state) => FeedScreen(), + pageBuilder: (context, state) => NoTransitionPage(child: FeedScreen()), ), GoRoute( path: '/diary-screen', name: 'diaryScreen', - builder: (context, state) => DiaryScreen(), + pageBuilder: (context, state) => NoTransitionPage(child: DiaryScreen()), ), GoRoute( path: '/chat-select-screen', name: 'chatSelectScreen', - builder: (context, state) => ChatSelectScreen(), + pageBuilder: (context, state) => NoTransitionPage(child: ChatSelectScreen()), ), GoRoute( path: '/notifications-screen', name: 'notificationsScreen', - builder: (context, state) => NotificationsScreen(), + pageBuilder: (context, state) => NoTransitionPage(child: NotificationsScreen()), ), GoRoute( path: '/menu-screen', name: 'menuScreen', - builder: (context, state) => MenuScreen(), + pageBuilder: (context, state) => NoTransitionPage(child: MenuScreen()), ), ], ), @@ -139,16 +140,24 @@ class AppRouter { author: state.extra as Author, ), ), + GoRoute( + path: '/subs-screen/:slug', + name: 'subsScreen', + builder: (context, state) => SubsScreen( + slug: state.pathParameters['slug']!, + subType: state.extra as SubType, + ), + ), GoRoute( path: '/about-diet-screen', name: 'aboutDietScreen', builder: (context, state) { final data = state.extra as Map; return AboutDietScreen( - diet: data['diet'] as Diet, - index: data['index'] as int, - navigateToComments: data['navigateToComments'] as bool, - ); + diet: data['diet'] as Diet, + index: data['index'] as int, + navigateToComments: data['navigateToComments'] as bool, + ); }, ), GoRoute( diff --git a/lib/core/locator_service.dart b/lib/core/locator_service.dart index 44378eb1..96b60fd3 100644 --- a/lib/core/locator_service.dart +++ b/lib/core/locator_service.dart @@ -35,6 +35,7 @@ import 'package:eatvio_app/features/plans/presentation/bloc/plans_cubit/plans_cu import 'package:eatvio_app/features/plans/repositories/plans_repository.dart'; import 'package:eatvio_app/features/plans/services/plans_service.dart'; import 'package:eatvio_app/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart'; +import 'package:eatvio_app/features/profile/presentation/bloc/sub_cubit/sub_cubit.dart'; import 'package:eatvio_app/features/profile/repositories/profile_repository.dart'; import 'package:eatvio_app/features/profile/services/profile_service.dart'; import 'package:eatvio_app/features/recipes/presentation/bloc/recipe_create_cubit.dart'; @@ -74,6 +75,7 @@ Future init() async { sl.registerFactory(() => DiaryCubit(diaryRepository: sl(), glossaryRepository: sl())); sl.registerFactory(() => AboutDietCubit(dietsRepository: sl())); sl.registerFactory(() => BlogPostCreateCubit(blogPostRepository: sl())); + sl.registerFactory(() => SubCubit(profileRepository: sl())); // Repository sl.registerLazySingleton(() => ChatRepository(chatService: sl())); diff --git a/lib/core/widgets/loader.dart b/lib/core/widgets/loader.dart new file mode 100644 index 00000000..be4e7b56 --- /dev/null +++ b/lib/core/widgets/loader.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; +import 'package:rive/rive.dart'; + +class Loader extends StatelessWidget { + const Loader({super.key}); + + @override + Widget build(BuildContext context) { + return Center( + child: SizedBox( + height: 32, + width: 32, + child: RiveAnimation.asset( + 'assets/rive_animations/loading.riv', + ), + ), + ); + } +} diff --git a/lib/features/blog_post/presentation/pages/blog_post_screen.dart b/lib/features/blog_post/presentation/pages/blog_post_screen.dart index a1dd837f..1d73d7cd 100644 --- a/lib/features/blog_post/presentation/pages/blog_post_screen.dart +++ b/lib/features/blog_post/presentation/pages/blog_post_screen.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/utils/date_time_utils.dart'; +import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_state.dart'; @@ -13,7 +14,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:go_router/go_router.dart'; -import 'package:rive/rive.dart'; import 'package:share_plus/share_plus.dart'; class BlogPostScreen extends StatefulWidget { @@ -319,16 +319,7 @@ class _BlogPostScreenState extends State { ), SizedBox(height: 16), if (state is CommentsStateLoading) - Padding( - padding: const EdgeInsets.all(32), - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ), + const Loader(), if (state is CommentsStateLoaded) CommentsWidget( id: widget.blogPost.id.toString(), diff --git a/lib/features/calendar/presentation/pages/calendar_screen.dart b/lib/features/calendar/presentation/pages/calendar_screen.dart index 495c08df..0457c9bc 100644 --- a/lib/features/calendar/presentation/pages/calendar_screen.dart +++ b/lib/features/calendar/presentation/pages/calendar_screen.dart @@ -1,4 +1,5 @@ import 'package:eatvio_app/core/theme/app_decoration.dart'; +import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/features/calendar/presentation/bloc/calendar_cubit.dart'; import 'package:eatvio_app/features/calendar/presentation/bloc/calendar_state.dart'; import 'package:eatvio_app/core/locator_service.dart'; @@ -9,7 +10,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; -import 'package:rive/rive.dart'; class CalendarScreen extends StatelessWidget { CalendarScreen({Key? key}) : super(key: key); @@ -23,15 +23,7 @@ class CalendarScreen extends StatelessWidget { return _buildCalendar(context, state); } else if (state is CalendarStateLoading) { return Scaffold( - body: Center( - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ), + body: const Loader(), ); } else if (state is CalendarStateError) { return Scaffold( diff --git a/lib/features/chat/presentation/pages/chat_screen.dart b/lib/features/chat/presentation/pages/chat_screen.dart index a0d598a3..d43411cb 100644 --- a/lib/features/chat/presentation/pages/chat_screen.dart +++ b/lib/features/chat/presentation/pages/chat_screen.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart'; import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.dart'; import 'package:eatvio_app/features/chat/presentation/widgets/chat_bubble_widget.dart'; @@ -10,7 +11,6 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:intl/intl.dart'; -import 'package:rive/rive.dart'; class ChatScreen extends StatefulWidget { final String chatId; @@ -65,15 +65,7 @@ class _ChatScreenState extends State { ); } else { return Scaffold( - body: Center( - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ), + body: const Loader(), ); } }, @@ -175,15 +167,7 @@ class _ChatScreenState extends State { } else { return Padding( padding: const EdgeInsets.all(8), - child: Center( - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ), + child: const Loader(), ); } }, diff --git a/lib/features/chat/presentation/pages/chat_select_screen.dart b/lib/features/chat/presentation/pages/chat_select_screen.dart index 0da5dfda..ce286468 100644 --- a/lib/features/chat/presentation/pages/chat_select_screen.dart +++ b/lib/features/chat/presentation/pages/chat_select_screen.dart @@ -1,4 +1,5 @@ import 'package:easy_debounce/easy_debounce.dart'; +import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/features/chat/presentation/bloc/chat_select_cubit/chat_select_cubit.dart'; import 'package:eatvio_app/features/chat/presentation/bloc/chat_select_cubit/chat_select_state.dart'; import 'package:eatvio_app/features/chat/presentation/widgets/chat_entry_widget.dart'; @@ -6,7 +7,6 @@ import 'package:eatvio_app/features/chat/presentation/widgets/chat_search_bar_wi import 'package:eatvio_app/core/locator_service.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:rive/rive.dart'; class ChatSelectScreen extends StatefulWidget { const ChatSelectScreen({Key? key}) : super(key: key); @@ -78,15 +78,7 @@ class _ChatSelectScreenState extends State { stateBuilder(state) { if (state is ChatSelectStateLoading) { return Scaffold( - body: Center( - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ), + body: const Loader(), ); } else if (state is ChatSelectStateLoaded) { return _buildChatList(context, state); @@ -97,15 +89,7 @@ class _ChatSelectScreenState extends State { ); } else { return Scaffold( - body: Center( - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ), + body: const Loader(), ); } } diff --git a/lib/features/consultant/become_consultant/models/consultant_profile_model.g.dart b/lib/features/consultant/become_consultant/models/consultant_profile_model.g.dart index e611c201..e66c2cee 100644 --- a/lib/features/consultant/become_consultant/models/consultant_profile_model.g.dart +++ b/lib/features/consultant/become_consultant/models/consultant_profile_model.g.dart @@ -23,21 +23,14 @@ _$ConsultantProfileModelImpl _$$ConsultantProfileModelImplFromJson( ); Map _$$ConsultantProfileModelImplToJson( - _$ConsultantProfileModelImpl instance) { - final val = {}; - - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('has_medical_education', instance.hasMedicalEducation); - writeNotNull('social_links', instance.socialLinks); - writeNotNull('cert_photos', instance.photos); - writeNotNull('completed_courses', instance.certificates); - return val; -} + _$ConsultantProfileModelImpl instance) => + { + if (instance.hasMedicalEducation case final value?) + 'has_medical_education': value, + if (instance.socialLinks case final value?) 'social_links': value, + if (instance.photos case final value?) 'cert_photos': value, + if (instance.certificates case final value?) 'completed_courses': value, + }; _$SocialLinksModelImpl _$$SocialLinksModelImplFromJson( Map json) => @@ -50,22 +43,15 @@ _$SocialLinksModelImpl _$$SocialLinksModelImplFromJson( ); Map _$$SocialLinksModelImplToJson( - _$SocialLinksModelImpl instance) { - final val = {}; - - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('vk_link', instance.vkLink); - writeNotNull('tg_link', instance.tgLink); - writeNotNull('fb_link', instance.fbLink); - writeNotNull('insta_link', instance.instaLink); - writeNotNull('personal_site_link', instance.personalSiteLink); - return val; -} + _$SocialLinksModelImpl instance) => + { + if (instance.vkLink case final value?) 'vk_link': value, + if (instance.tgLink case final value?) 'tg_link': value, + if (instance.fbLink case final value?) 'fb_link': value, + if (instance.instaLink case final value?) 'insta_link': value, + if (instance.personalSiteLink case final value?) + 'personal_site_link': value, + }; _$CertificatesModelImpl _$$CertificatesModelImplFromJson( Map json) => diff --git a/lib/features/diary/models/product_moderation_model.dart b/lib/features/diary/models/product_moderation_model.dart index c28ffe64..c3baf561 100644 --- a/lib/features/diary/models/product_moderation_model.dart +++ b/lib/features/diary/models/product_moderation_model.dart @@ -8,13 +8,13 @@ class ProductModerationModel with _$ProductModerationModel { const factory ProductModerationModel({ required String name, @JsonKey(name: 'energy_on_100gr') - required int energy, + required double energy, @JsonKey(name: 'proteins_on_100gr') - required int proteins, + required double proteins, @JsonKey(name: 'fats_on_100gr') - required int fats, + required double fats, @JsonKey(name: 'carbohydrates_on_100gr') - required int carbohydrates, + required double carbohydrates, }) = _ProductModerationModel; factory ProductModerationModel.fromJson(Map json) => _$ProductModerationModelFromJson(json); diff --git a/lib/features/diary/models/product_moderation_model.freezed.dart b/lib/features/diary/models/product_moderation_model.freezed.dart index 9566e207..ec1db929 100644 --- a/lib/features/diary/models/product_moderation_model.freezed.dart +++ b/lib/features/diary/models/product_moderation_model.freezed.dart @@ -23,13 +23,13 @@ ProductModerationModel _$ProductModerationModelFromJson( mixin _$ProductModerationModel { String get name => throw _privateConstructorUsedError; @JsonKey(name: 'energy_on_100gr') - int get energy => throw _privateConstructorUsedError; + double get energy => throw _privateConstructorUsedError; @JsonKey(name: 'proteins_on_100gr') - int get proteins => throw _privateConstructorUsedError; + double get proteins => throw _privateConstructorUsedError; @JsonKey(name: 'fats_on_100gr') - int get fats => throw _privateConstructorUsedError; + double get fats => throw _privateConstructorUsedError; @JsonKey(name: 'carbohydrates_on_100gr') - int get carbohydrates => throw _privateConstructorUsedError; + double get carbohydrates => throw _privateConstructorUsedError; /// Serializes this ProductModerationModel to a JSON map. Map toJson() => throw _privateConstructorUsedError; @@ -49,10 +49,10 @@ abstract class $ProductModerationModelCopyWith<$Res> { @useResult $Res call( {String name, - @JsonKey(name: 'energy_on_100gr') int energy, - @JsonKey(name: 'proteins_on_100gr') int proteins, - @JsonKey(name: 'fats_on_100gr') int fats, - @JsonKey(name: 'carbohydrates_on_100gr') int carbohydrates}); + @JsonKey(name: 'energy_on_100gr') double energy, + @JsonKey(name: 'proteins_on_100gr') double proteins, + @JsonKey(name: 'fats_on_100gr') double fats, + @JsonKey(name: 'carbohydrates_on_100gr') double carbohydrates}); } /// @nodoc @@ -85,19 +85,19 @@ class _$ProductModerationModelCopyWithImpl<$Res, energy: null == energy ? _value.energy : energy // ignore: cast_nullable_to_non_nullable - as int, + as double, proteins: null == proteins ? _value.proteins : proteins // ignore: cast_nullable_to_non_nullable - as int, + as double, fats: null == fats ? _value.fats : fats // ignore: cast_nullable_to_non_nullable - as int, + as double, carbohydrates: null == carbohydrates ? _value.carbohydrates : carbohydrates // ignore: cast_nullable_to_non_nullable - as int, + as double, ) as $Val); } } @@ -113,10 +113,10 @@ abstract class _$$ProductModerationModelImplCopyWith<$Res> @useResult $Res call( {String name, - @JsonKey(name: 'energy_on_100gr') int energy, - @JsonKey(name: 'proteins_on_100gr') int proteins, - @JsonKey(name: 'fats_on_100gr') int fats, - @JsonKey(name: 'carbohydrates_on_100gr') int carbohydrates}); + @JsonKey(name: 'energy_on_100gr') double energy, + @JsonKey(name: 'proteins_on_100gr') double proteins, + @JsonKey(name: 'fats_on_100gr') double fats, + @JsonKey(name: 'carbohydrates_on_100gr') double carbohydrates}); } /// @nodoc @@ -148,19 +148,19 @@ class __$$ProductModerationModelImplCopyWithImpl<$Res> energy: null == energy ? _value.energy : energy // ignore: cast_nullable_to_non_nullable - as int, + as double, proteins: null == proteins ? _value.proteins : proteins // ignore: cast_nullable_to_non_nullable - as int, + as double, fats: null == fats ? _value.fats : fats // ignore: cast_nullable_to_non_nullable - as int, + as double, carbohydrates: null == carbohydrates ? _value.carbohydrates : carbohydrates // ignore: cast_nullable_to_non_nullable - as int, + as double, )); } } @@ -182,16 +182,16 @@ class _$ProductModerationModelImpl implements _ProductModerationModel { final String name; @override @JsonKey(name: 'energy_on_100gr') - final int energy; + final double energy; @override @JsonKey(name: 'proteins_on_100gr') - final int proteins; + final double proteins; @override @JsonKey(name: 'fats_on_100gr') - final int fats; + final double fats; @override @JsonKey(name: 'carbohydrates_on_100gr') - final int carbohydrates; + final double carbohydrates; @override String toString() { @@ -237,11 +237,11 @@ class _$ProductModerationModelImpl implements _ProductModerationModel { abstract class _ProductModerationModel implements ProductModerationModel { const factory _ProductModerationModel( {required final String name, - @JsonKey(name: 'energy_on_100gr') required final int energy, - @JsonKey(name: 'proteins_on_100gr') required final int proteins, - @JsonKey(name: 'fats_on_100gr') required final int fats, + @JsonKey(name: 'energy_on_100gr') required final double energy, + @JsonKey(name: 'proteins_on_100gr') required final double proteins, + @JsonKey(name: 'fats_on_100gr') required final double fats, @JsonKey(name: 'carbohydrates_on_100gr') - required final int carbohydrates}) = _$ProductModerationModelImpl; + required final double carbohydrates}) = _$ProductModerationModelImpl; factory _ProductModerationModel.fromJson(Map json) = _$ProductModerationModelImpl.fromJson; @@ -250,16 +250,16 @@ abstract class _ProductModerationModel implements ProductModerationModel { String get name; @override @JsonKey(name: 'energy_on_100gr') - int get energy; + double get energy; @override @JsonKey(name: 'proteins_on_100gr') - int get proteins; + double get proteins; @override @JsonKey(name: 'fats_on_100gr') - int get fats; + double get fats; @override @JsonKey(name: 'carbohydrates_on_100gr') - int get carbohydrates; + double get carbohydrates; /// Create a copy of ProductModerationModel /// with the given fields replaced by the non-null parameter values. diff --git a/lib/features/diary/models/product_moderation_model.g.dart b/lib/features/diary/models/product_moderation_model.g.dart index 2199a5f6..cb27b8a5 100644 --- a/lib/features/diary/models/product_moderation_model.g.dart +++ b/lib/features/diary/models/product_moderation_model.g.dart @@ -10,10 +10,10 @@ _$ProductModerationModelImpl _$$ProductModerationModelImplFromJson( Map json) => _$ProductModerationModelImpl( name: json['name'] as String, - energy: (json['energy_on_100gr'] as num).toInt(), - proteins: (json['proteins_on_100gr'] as num).toInt(), - fats: (json['fats_on_100gr'] as num).toInt(), - carbohydrates: (json['carbohydrates_on_100gr'] as num).toInt(), + energy: (json['energy_on_100gr'] as num).toDouble(), + proteins: (json['proteins_on_100gr'] as num).toDouble(), + fats: (json['fats_on_100gr'] as num).toDouble(), + carbohydrates: (json['carbohydrates_on_100gr'] as num).toDouble(), ); Map _$$ProductModerationModelImplToJson( diff --git a/lib/features/diary/presentation/bloc/diary_cubit.dart b/lib/features/diary/presentation/bloc/diary_cubit.dart index 976d16e6..59158554 100644 --- a/lib/features/diary/presentation/bloc/diary_cubit.dart +++ b/lib/features/diary/presentation/bloc/diary_cubit.dart @@ -12,7 +12,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; class DiaryCubit extends Cubit { final DiaryRepository diaryRepository; final GlossaryRepository glossaryRepository; - double currentSelectedSatietyRating = 3; DiaryCubit({ required this.diaryRepository, @@ -22,11 +21,16 @@ class DiaryCubit extends Cubit { getDiaryDay(String date, [bool needLoading = true]) async { if (needLoading) emit(const DiaryState.loading()); try { + final diaryDay = await diaryRepository.getDiaryByDate(date); + final List satiety = []; + for (int i = 0; i < diaryDay.meals.length; i++) { + satiety.add(diaryDay.meals[i].satiety); + } emit( DiaryState.loaded( - diary: await diaryRepository.getDiaryByDate(date), + diary: diaryDay, nutrition: await getNutrition((await diaryRepository.getDiaryByDate(date)).id), - currentSelectedSatietyRating: currentSelectedSatietyRating, + currentSelectedSatietyRating: satiety, ), ); } catch (e) { @@ -136,17 +140,23 @@ class DiaryCubit extends Cubit { await getDiaryDay(date, false); } - updateCurrentSelectedSatietyRating(double value) { - currentSelectedSatietyRating = value; - emit((state as DiaryStateLoaded).copyWith(currentSelectedSatietyRating: currentSelectedSatietyRating)); + updateCurrentSelectedSatietyRating({ + required int diaryId, + required int meal, + required int value, + }) { + List satiety = [...(state as DiaryStateLoaded).currentSelectedSatietyRating]; + satiety[meal] = value; + diaryRepository.fillSatiety(diaryId: diaryId, meal: meal, satiety: value.toInt() - 1); + emit((state as DiaryStateLoaded).copyWith(currentSelectedSatietyRating: satiety)); } sendProductToModeration({ required String name, - required int energy, - required int proteins, - required int fats, - required int carbohydrates, + required double energy, + required double proteins, + required double fats, + required double carbohydrates, }) async { return await diaryRepository.sendProductToModeration( name: name, diff --git a/lib/features/diary/presentation/bloc/diary_state.dart b/lib/features/diary/presentation/bloc/diary_state.dart index 001c9d16..47eeee2d 100644 --- a/lib/features/diary/presentation/bloc/diary_state.dart +++ b/lib/features/diary/presentation/bloc/diary_state.dart @@ -17,6 +17,6 @@ class DiaryState with _$DiaryState { const factory DiaryState.loaded({ required DiaryDayModel diary, required NutritionDataModel nutrition, - required double currentSelectedSatietyRating, + required List currentSelectedSatietyRating, }) = DiaryStateLoaded; } diff --git a/lib/features/diary/presentation/bloc/diary_state.freezed.dart b/lib/features/diary/presentation/bloc/diary_state.freezed.dart index 58fa1471..6e958949 100644 --- a/lib/features/diary/presentation/bloc/diary_state.freezed.dart +++ b/lib/features/diary/presentation/bloc/diary_state.freezed.dart @@ -22,7 +22,7 @@ mixin _$DiaryState { required TResult Function() loading, required TResult Function(String errorMessage) error, required TResult Function(DiaryDayModel diary, NutritionDataModel nutrition, - double currentSelectedSatietyRating) + List currentSelectedSatietyRating) loaded, }) => throw _privateConstructorUsedError; @@ -32,7 +32,7 @@ mixin _$DiaryState { TResult? Function()? loading, TResult? Function(String errorMessage)? error, TResult? Function(DiaryDayModel diary, NutritionDataModel nutrition, - double currentSelectedSatietyRating)? + List currentSelectedSatietyRating)? loaded, }) => throw _privateConstructorUsedError; @@ -42,7 +42,7 @@ mixin _$DiaryState { TResult Function()? loading, TResult Function(String errorMessage)? error, TResult Function(DiaryDayModel diary, NutritionDataModel nutrition, - double currentSelectedSatietyRating)? + List currentSelectedSatietyRating)? loaded, required TResult orElse(), }) => @@ -140,7 +140,7 @@ class _$DiaryStateImpl implements _DiaryState { required TResult Function() loading, required TResult Function(String errorMessage) error, required TResult Function(DiaryDayModel diary, NutritionDataModel nutrition, - double currentSelectedSatietyRating) + List currentSelectedSatietyRating) loaded, }) { return $default(); @@ -153,7 +153,7 @@ class _$DiaryStateImpl implements _DiaryState { TResult? Function()? loading, TResult? Function(String errorMessage)? error, TResult? Function(DiaryDayModel diary, NutritionDataModel nutrition, - double currentSelectedSatietyRating)? + List currentSelectedSatietyRating)? loaded, }) { return $default?.call(); @@ -166,7 +166,7 @@ class _$DiaryStateImpl implements _DiaryState { TResult Function()? loading, TResult Function(String errorMessage)? error, TResult Function(DiaryDayModel diary, NutritionDataModel nutrition, - double currentSelectedSatietyRating)? + List currentSelectedSatietyRating)? loaded, required TResult orElse(), }) { @@ -263,7 +263,7 @@ class _$DiaryStateLoadingImpl implements DiaryStateLoading { required TResult Function() loading, required TResult Function(String errorMessage) error, required TResult Function(DiaryDayModel diary, NutritionDataModel nutrition, - double currentSelectedSatietyRating) + List currentSelectedSatietyRating) loaded, }) { return loading(); @@ -276,7 +276,7 @@ class _$DiaryStateLoadingImpl implements DiaryStateLoading { TResult? Function()? loading, TResult? Function(String errorMessage)? error, TResult? Function(DiaryDayModel diary, NutritionDataModel nutrition, - double currentSelectedSatietyRating)? + List currentSelectedSatietyRating)? loaded, }) { return loading?.call(); @@ -289,7 +289,7 @@ class _$DiaryStateLoadingImpl implements DiaryStateLoading { TResult Function()? loading, TResult Function(String errorMessage)? error, TResult Function(DiaryDayModel diary, NutritionDataModel nutrition, - double currentSelectedSatietyRating)? + List currentSelectedSatietyRating)? loaded, required TResult orElse(), }) { @@ -415,7 +415,7 @@ class _$DiaryStateErrorImpl implements DiaryStateError { required TResult Function() loading, required TResult Function(String errorMessage) error, required TResult Function(DiaryDayModel diary, NutritionDataModel nutrition, - double currentSelectedSatietyRating) + List currentSelectedSatietyRating) loaded, }) { return error(errorMessage); @@ -428,7 +428,7 @@ class _$DiaryStateErrorImpl implements DiaryStateError { TResult? Function()? loading, TResult? Function(String errorMessage)? error, TResult? Function(DiaryDayModel diary, NutritionDataModel nutrition, - double currentSelectedSatietyRating)? + List currentSelectedSatietyRating)? loaded, }) { return error?.call(errorMessage); @@ -441,7 +441,7 @@ class _$DiaryStateErrorImpl implements DiaryStateError { TResult Function()? loading, TResult Function(String errorMessage)? error, TResult Function(DiaryDayModel diary, NutritionDataModel nutrition, - double currentSelectedSatietyRating)? + List currentSelectedSatietyRating)? loaded, required TResult orElse(), }) { @@ -511,7 +511,7 @@ abstract class _$$DiaryStateLoadedImplCopyWith<$Res> { $Res call( {DiaryDayModel diary, NutritionDataModel nutrition, - double currentSelectedSatietyRating}); + List currentSelectedSatietyRating}); $DiaryDayModelCopyWith<$Res> get diary; $NutritionDataModelCopyWith<$Res> get nutrition; @@ -544,9 +544,9 @@ class __$$DiaryStateLoadedImplCopyWithImpl<$Res> : nutrition // ignore: cast_nullable_to_non_nullable as NutritionDataModel, currentSelectedSatietyRating: null == currentSelectedSatietyRating - ? _value.currentSelectedSatietyRating + ? _value._currentSelectedSatietyRating : currentSelectedSatietyRating // ignore: cast_nullable_to_non_nullable - as double, + as List, )); } @@ -577,14 +577,21 @@ class _$DiaryStateLoadedImpl implements DiaryStateLoaded { const _$DiaryStateLoadedImpl( {required this.diary, required this.nutrition, - required this.currentSelectedSatietyRating}); + required final List currentSelectedSatietyRating}) + : _currentSelectedSatietyRating = currentSelectedSatietyRating; @override final DiaryDayModel diary; @override final NutritionDataModel nutrition; + final List _currentSelectedSatietyRating; @override - final double currentSelectedSatietyRating; + List get currentSelectedSatietyRating { + if (_currentSelectedSatietyRating is EqualUnmodifiableListView) + return _currentSelectedSatietyRating; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_currentSelectedSatietyRating); + } @override String toString() { @@ -599,15 +606,14 @@ class _$DiaryStateLoadedImpl implements DiaryStateLoaded { (identical(other.diary, diary) || other.diary == diary) && (identical(other.nutrition, nutrition) || other.nutrition == nutrition) && - (identical(other.currentSelectedSatietyRating, - currentSelectedSatietyRating) || - other.currentSelectedSatietyRating == - currentSelectedSatietyRating)); + const DeepCollectionEquality().equals( + other._currentSelectedSatietyRating, + _currentSelectedSatietyRating)); } @override - int get hashCode => - Object.hash(runtimeType, diary, nutrition, currentSelectedSatietyRating); + int get hashCode => Object.hash(runtimeType, diary, nutrition, + const DeepCollectionEquality().hash(_currentSelectedSatietyRating)); /// Create a copy of DiaryState /// with the given fields replaced by the non-null parameter values. @@ -625,7 +631,7 @@ class _$DiaryStateLoadedImpl implements DiaryStateLoaded { required TResult Function() loading, required TResult Function(String errorMessage) error, required TResult Function(DiaryDayModel diary, NutritionDataModel nutrition, - double currentSelectedSatietyRating) + List currentSelectedSatietyRating) loaded, }) { return loaded(diary, nutrition, currentSelectedSatietyRating); @@ -638,7 +644,7 @@ class _$DiaryStateLoadedImpl implements DiaryStateLoaded { TResult? Function()? loading, TResult? Function(String errorMessage)? error, TResult? Function(DiaryDayModel diary, NutritionDataModel nutrition, - double currentSelectedSatietyRating)? + List currentSelectedSatietyRating)? loaded, }) { return loaded?.call(diary, nutrition, currentSelectedSatietyRating); @@ -651,7 +657,7 @@ class _$DiaryStateLoadedImpl implements DiaryStateLoaded { TResult Function()? loading, TResult Function(String errorMessage)? error, TResult Function(DiaryDayModel diary, NutritionDataModel nutrition, - double currentSelectedSatietyRating)? + List currentSelectedSatietyRating)? loaded, required TResult orElse(), }) { @@ -703,12 +709,12 @@ abstract class DiaryStateLoaded implements DiaryState { const factory DiaryStateLoaded( {required final DiaryDayModel diary, required final NutritionDataModel nutrition, - required final double currentSelectedSatietyRating}) = + required final List currentSelectedSatietyRating}) = _$DiaryStateLoadedImpl; DiaryDayModel get diary; NutritionDataModel get nutrition; - double get currentSelectedSatietyRating; + List get currentSelectedSatietyRating; /// Create a copy of DiaryState /// with the given fields replaced by the non-null parameter values. diff --git a/lib/features/diary/presentation/pages/diary_screen.dart b/lib/features/diary/presentation/pages/diary_screen.dart index 9f3dc735..9c405a7e 100644 --- a/lib/features/diary/presentation/pages/diary_screen.dart +++ b/lib/features/diary/presentation/pages/diary_screen.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/features/diary/models/diary_day_model.dart'; import 'package:eatvio_app/features/diary/models/nutrition_model.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart'; @@ -18,7 +19,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:image_picker/image_picker.dart'; -import 'package:rive/rive.dart'; import 'package:syncfusion_flutter_gauges/gauges.dart'; import 'package:table_calendar/table_calendar.dart'; @@ -42,7 +42,6 @@ class _DiaryScreenState extends State { int currentSelectedPhotoIndex = 0; String time = "${DateTime.now().hour.toString().padLeft(2, '0')}:${DateTime.now().minute.toString().padLeft(2, '0')}"; DateTime _time = DateTime.now(); - bool enableQuantity = false; @override void initState() { @@ -124,7 +123,7 @@ class _DiaryScreenState extends State { child: Column( children: [ _calendar(state), - if (state is DiaryStateLoading) _loader(), + if (state is DiaryStateLoading) const Loader(), if (state is DiaryStateLoaded) _meals( state.diary, @@ -249,7 +248,7 @@ class _DiaryScreenState extends State { _meals( DiaryDayModel diaryDay, - double currentSelectedSatietyRating, + List satietyRating, ) { return Material( borderRadius: BorderRadius.circular(20), @@ -322,7 +321,9 @@ class _DiaryScreenState extends State { _selectedDay!.compareTo(today!) == 1 ? const SizedBox.shrink() : SatietyRatingWidget( - currentSelectedSatietyRating: currentSelectedSatietyRating, + currentSelectedSatietyRating: satietyRating, + diaryId: diaryDay.id, + meal: currentSelectedMealNumber, ), ], ), @@ -767,18 +768,6 @@ class _DiaryScreenState extends State { ); } - _loader() { - return Center( - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ); - } - _calendar(DiaryState state) { return Column( mainAxisSize: MainAxisSize.min, @@ -883,76 +872,74 @@ class _DiaryScreenState extends State { ), ), const SizedBox(height: 16), - TypeAheadField( - controller: productNameController, - itemBuilder: (context, glossary) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - glossary.name, - style: TextStyle( - height: 1, - fontSize: 16, + Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: MediaQuery.of(context).size.width * 0.8 - 32, + child: TypeAheadField( + controller: productNameController, + itemBuilder: (context, glossary) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + glossary.name, + style: TextStyle( + height: 1, + fontSize: 16, + ), + ), + ); + }, + itemSeparatorBuilder: (context, index) => _divider(context), + onSelected: (newSelectedGlossary) async { + setState(() { + selectedGlossary = newSelectedGlossary; + productNameController.text = selectedGlossary!.name; + }); + }, + suggestionsCallback: (query) async { + if (query.length < 1) return []; + final suggestionsList = await diaryCubit.getGlossaryListByQuery(query); + return suggestionsList; + }, + builder: (context, controller, focusNode) { + return CustomTextFormField( + controller: controller, + autofocus: true, + focusNode: focusNode, + hintText: 'Введите название продукта/блюда', + ); + }, + emptyBuilder: (context) { + return const SizedBox.shrink(); + }, + errorBuilder: (context, object) => Padding( + padding: const EdgeInsets.all(8.0), + child: Text('Нет подходящих вариантов'), ), ), - ); - }, - itemSeparatorBuilder: (context, index) => _divider(context), - onSelected: (newSelectedGlossary) async { - setState(() { - selectedGlossary = newSelectedGlossary; - productNameController.text = selectedGlossary!.name; - }); - }, - suggestionsCallback: (query) async { - if (query.length < 1) return []; - final suggestionsList = await diaryCubit.getGlossaryListByQuery(query); - if (suggestionsList.length == 1) { - setState(() { - selectedGlossary = suggestionsList.first; - }); - } - if (suggestionsList.length == 0) { - setState(() { - enableQuantity = true; - }); - } - return suggestionsList; - }, - builder: (context, controller, focusNode) { - return CustomTextFormField( - controller: controller, - autofocus: true, - focusNode: focusNode, - hintText: 'Введите название продукта/блюда', - ); - }, - emptyBuilder: (context) { - return const SizedBox.shrink(); - }, - errorBuilder: (context, object) => Padding( - padding: const EdgeInsets.all(8.0), - child: Text('Нет подходящих вариантов'), - ), - ), - if (selectedGlossary != null || enableQuantity) - Padding( - padding: const EdgeInsets.only(top: 8), - child: CustomTextFormField( - controller: quantityController, - keyboardType: TextInputType.number, - hintText: selectedGlossary == null - ? '100' - : selectedGlossary!.grInPortion == 0 - ? '100' - : '1', - suffixText: selectedGlossary == null - ? 'гр' - : selectedGlossary?.grInPortion == 0 - ? 'гр' - : 'шт', ), - ), + const SizedBox(width: 4), + SizedBox( + width: MediaQuery.of(context).size.width * 0.2 - 4, + child: CustomTextFormField( + controller: quantityController, + keyboardType: TextInputType.number, + hintText: selectedGlossary == null + ? '100' + : selectedGlossary!.grInPortion == 0 + ? '100' + : '1', + suffixText: selectedGlossary == null + ? 'гр' + : selectedGlossary?.grInPortion == 0 + ? 'гр' + : 'шт', + ), + ), + ], + ), const SizedBox(height: 16), Row( children: [ @@ -1013,7 +1000,7 @@ class _DiaryScreenState extends State { ); } Navigator.of(context).pop(); - if (enableQuantity) { + if (selectedGlossary == null) { showModalBottomSheet( isScrollControlled: true, useRootNavigator: true, @@ -1029,9 +1016,6 @@ class _DiaryScreenState extends State { ), ); } - setState(() { - enableQuantity = false; - }); }, borderRadius: BorderRadius.circular(20), child: Ink( @@ -1070,7 +1054,6 @@ class _DiaryScreenState extends State { onTap: () { setState(() { selectedGlossary = null; - enableQuantity = false; productNameController.clear(); quantityController.clear(); }); diff --git a/lib/features/diary/presentation/widgets/glossary_moderation_request_modal_widget.dart b/lib/features/diary/presentation/widgets/glossary_moderation_request_modal_widget.dart index 1fb44138..3461d687 100644 --- a/lib/features/diary/presentation/widgets/glossary_moderation_request_modal_widget.dart +++ b/lib/features/diary/presentation/widgets/glossary_moderation_request_modal_widget.dart @@ -264,10 +264,10 @@ class _GlossaryModerationRequestModalWidgetState extends State currentSelectedSatietyRating; + final int diaryId; + final int meal; const SatietyRatingWidget({ super.key, required this.currentSelectedSatietyRating, + required this.diaryId, + required this.meal, }); @override @@ -39,7 +43,7 @@ class _SatietyRatingWidgetState extends State { width: 32, ), RatingBar.builder( - initialRating: 3, + initialRating: widget.currentSelectedSatietyRating[widget.meal - 1].toDouble() + 1, itemCount: 6, itemSize: MediaQuery.of(context).size.width / 6 - (124 / 6), updateOnDrag: true, @@ -49,17 +53,17 @@ class _SatietyRatingWidgetState extends State { return Container( width: MediaQuery.of(context).size.width / 6, height: 24, - margin: widget.currentSelectedSatietyRating == 1 ? EdgeInsets.symmetric(horizontal: 2) : null, + margin: widget.currentSelectedSatietyRating[widget.meal - 1].toDouble() + 1 == 1 ? EdgeInsets.symmetric(horizontal: 2) : null, decoration: BoxDecoration( color: Color(0xFFC82222), - border: widget.currentSelectedSatietyRating == 1 + border: widget.currentSelectedSatietyRating[widget.meal - 1].toDouble() + 1 == 1 ? Border.all( width: 2, color: Color(0xFFAA3333), strokeAlign: BorderSide.strokeAlignOutside, ) : null, - borderRadius: widget.currentSelectedSatietyRating == 1 + borderRadius: widget.currentSelectedSatietyRating[widget.meal - 1].toDouble() + 1 == 1 ? BorderRadius.horizontal( left: Radius.circular(8), right: Radius.circular(2), @@ -73,85 +77,85 @@ class _SatietyRatingWidgetState extends State { return Container( width: MediaQuery.of(context).size.width / 6, height: 24, - margin: widget.currentSelectedSatietyRating == 2 ? EdgeInsets.symmetric(horizontal: 2) : null, + margin: widget.currentSelectedSatietyRating[widget.meal - 1].toDouble() + 1 == 2 ? EdgeInsets.symmetric(horizontal: 2) : null, decoration: BoxDecoration( color: Color(0xFFFF3E3E), - border: widget.currentSelectedSatietyRating == 2 + border: widget.currentSelectedSatietyRating[widget.meal - 1].toDouble() + 1 == 2 ? Border.all( width: 2, color: Color(0xFF550000), strokeAlign: BorderSide.strokeAlignOutside, ) : null, - borderRadius: widget.currentSelectedSatietyRating == 2 ? BorderRadius.circular(2) : null, + borderRadius: widget.currentSelectedSatietyRating[widget.meal - 1].toDouble() + 1 == 2 ? BorderRadius.circular(2) : null, ), ); case 2: return Container( width: MediaQuery.of(context).size.width / 6, height: 24, - margin: widget.currentSelectedSatietyRating == 3 ? EdgeInsets.symmetric(horizontal: 2) : null, + margin: widget.currentSelectedSatietyRating[widget.meal - 1].toDouble() + 1 == 3 ? EdgeInsets.symmetric(horizontal: 2) : null, decoration: BoxDecoration( color: Color(0xFFF8C926), - border: widget.currentSelectedSatietyRating == 3 + border: widget.currentSelectedSatietyRating[widget.meal - 1].toDouble() + 1 == 3 ? Border.all( width: 2, color: Color(0xFF8C5D00), strokeAlign: BorderSide.strokeAlignOutside, ) : null, - borderRadius: widget.currentSelectedSatietyRating == 3 ? BorderRadius.circular(2) : null, + borderRadius: widget.currentSelectedSatietyRating[widget.meal - 1].toDouble() + 1 == 3 ? BorderRadius.circular(2) : null, ), ); case 3: return Container( width: MediaQuery.of(context).size.width / 6, height: 24, - margin: widget.currentSelectedSatietyRating == 4 ? EdgeInsets.symmetric(horizontal: 2) : null, + margin: widget.currentSelectedSatietyRating[widget.meal - 1].toDouble() + 1 == 4 ? EdgeInsets.symmetric(horizontal: 2) : null, decoration: BoxDecoration( color: Color(0xFFFFF3BF), - border: widget.currentSelectedSatietyRating == 4 + border: widget.currentSelectedSatietyRating[widget.meal - 1].toDouble() + 1 == 4 ? Border.all( width: 2, color: Color(0xFFB5911B), strokeAlign: BorderSide.strokeAlignOutside, ) : null, - borderRadius: widget.currentSelectedSatietyRating == 4 ? BorderRadius.circular(2) : null, + borderRadius: widget.currentSelectedSatietyRating[widget.meal - 1].toDouble() + 1 == 4 ? BorderRadius.circular(2) : null, ), ); case 4: return Container( width: MediaQuery.of(context).size.width / 6, height: 24, - margin: widget.currentSelectedSatietyRating == 5 ? EdgeInsets.symmetric(horizontal: 2) : null, + margin: widget.currentSelectedSatietyRating[widget.meal - 1].toDouble() + 1 == 5 ? EdgeInsets.symmetric(horizontal: 2) : null, decoration: BoxDecoration( color: Color(0xFF62E833), - border: widget.currentSelectedSatietyRating == 5 + border: widget.currentSelectedSatietyRating[widget.meal - 1].toDouble() + 1 == 5 ? Border.all( width: 2, color: Color(0xFF276918), strokeAlign: BorderSide.strokeAlignOutside, ) : null, - borderRadius: widget.currentSelectedSatietyRating == 5 ? BorderRadius.circular(2) : null, + borderRadius: widget.currentSelectedSatietyRating[widget.meal - 1].toDouble() + 1 == 5 ? BorderRadius.circular(2) : null, ), ); default: return Container( width: MediaQuery.of(context).size.width / 6, height: 24, - margin: widget.currentSelectedSatietyRating == 6 ? EdgeInsets.symmetric(horizontal: 2) : null, + margin: widget.currentSelectedSatietyRating[widget.meal - 1].toDouble() + 1 == 6 ? EdgeInsets.symmetric(horizontal: 2) : null, decoration: BoxDecoration( color: Color(0xFF4DCE20), - border: widget.currentSelectedSatietyRating == 6 + border: widget.currentSelectedSatietyRating[widget.meal - 1].toDouble() + 1 == 6 ? Border.all( width: 2, color: Color(0xFF184D12), strokeAlign: BorderSide.strokeAlignOutside, ) : null, - borderRadius: widget.currentSelectedSatietyRating == 6 + borderRadius: widget.currentSelectedSatietyRating[widget.meal - 1].toDouble() + 1 == 6 ? BorderRadius.horizontal( left: Radius.circular(2), right: Radius.circular(8), @@ -163,7 +167,11 @@ class _SatietyRatingWidgetState extends State { ); } }, - onRatingUpdate: (value) => context.read().updateCurrentSelectedSatietyRating(value), + onRatingUpdate: (value) => context.read().updateCurrentSelectedSatietyRating( + diaryId: widget.diaryId, + meal: widget.meal, + value: value.toInt(), + ), ), SvgPicture.asset( 'assets/icons/satiety_emoji_right.svg', diff --git a/lib/features/diary/repositories/diary_repository.dart b/lib/features/diary/repositories/diary_repository.dart index 1ba36a60..7cb7e888 100644 --- a/lib/features/diary/repositories/diary_repository.dart +++ b/lib/features/diary/repositories/diary_repository.dart @@ -124,10 +124,10 @@ class DiaryRepository { Future sendProductToModeration({ required String name, - required int energy, - required int proteins, - required int fats, - required int carbohydrates, + required double energy, + required double proteins, + required double fats, + required double carbohydrates, }) async { return await diaryService.sendProductToModeration( data: ProductModerationModel( @@ -139,4 +139,16 @@ class DiaryRepository { ).toJson(), ); } + + Future fillSatiety({ + required int diaryId, + required int meal, + required int satiety, + }) async { + try { + return await diaryService.fillSatiety(diaryId: diaryId, meal: meal, satiety: satiety); + } catch (e) { + rethrow; + } + } } diff --git a/lib/features/diary/services/diary_service.dart b/lib/features/diary/services/diary_service.dart index 4ff8b2e1..f78c646f 100644 --- a/lib/features/diary/services/diary_service.dart +++ b/lib/features/diary/services/diary_service.dart @@ -71,6 +71,13 @@ abstract class DiaryService { // @Body() required FinishDiaryDayModel finishDiaryDayModel, }); + @POST('/api/diary/{diary_id}/meal/{meal}/satiety/fill') + Future fillSatiety({ + @Path('diary_id') required int diaryId, + @Path('meal') required int meal, + @Field() required int satiety, + }); + @POST('/api/diary/product_nutritional_values/sendToModeration') Future sendProductToModeration({ @Body() required Map data, diff --git a/lib/features/diary/services/diary_service.g.dart b/lib/features/diary/services/diary_service.g.dart index b9327c5f..8c88dd36 100644 --- a/lib/features/diary/services/diary_service.g.dart +++ b/lib/features/diary/services/diary_service.g.dart @@ -320,6 +320,35 @@ class _DiaryService implements DiaryService { await _dio.fetch(_options); } + @override + Future fillSatiety({ + required int diaryId, + required int meal, + required int satiety, + }) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + final _data = {'satiety': satiety}; + final _options = _setStreamType(Options( + method: 'POST', + headers: _headers, + extra: _extra, + ) + .compose( + _dio.options, + '/api/diary/${diaryId}/meal/${meal}/satiety/fill', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + ))); + await _dio.fetch(_options); + } + @override Future sendProductToModeration( {required Map data}) async { diff --git a/lib/features/diets/presentation/pages/about_diet_screen.dart b/lib/features/diets/presentation/pages/about_diet_screen.dart index a229618d..0b645aea 100644 --- a/lib/features/diets/presentation/pages/about_diet_screen.dart +++ b/lib/features/diets/presentation/pages/about_diet_screen.dart @@ -7,6 +7,7 @@ import 'package:eatvio_app/core/utils/string_extentions.dart'; import 'package:eatvio_app/core/widgets/bookmark_button.dart'; import 'package:eatvio_app/core/widgets/custom_divider.dart'; import 'package:eatvio_app/core/widgets/diet_info_row.dart'; +import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/features/diets/presentation/bloc/about_diet_cubit.dart'; import 'package:eatvio_app/features/diets/presentation/bloc/about_diet_state.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart'; @@ -28,7 +29,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:rive/rive.dart' hide LinearGradient; class AboutDietScreen extends StatefulWidget { final Diet diet; @@ -441,16 +441,7 @@ class _AboutDietScreenState extends State { ), SizedBox(height: 16), if (state is CommentsStateLoading) - Padding( - padding: const EdgeInsets.all(32), - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ), + const Loader(), if (state is CommentsStateLoaded) CommentsWidget( id: widget.diet.id.toString(), @@ -467,15 +458,7 @@ class _AboutDietScreenState extends State { ); } return Scaffold( - body: Center( - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ), + body: const Loader(), ); }, ); diff --git a/lib/features/feed/presentation/pages/feed_screen.dart b/lib/features/feed/presentation/pages/feed_screen.dart index a02b931b..83ab049a 100644 --- a/lib/features/feed/presentation/pages/feed_screen.dart +++ b/lib/features/feed/presentation/pages/feed_screen.dart @@ -1,4 +1,5 @@ import 'package:easy_debounce/easy_debounce.dart'; +import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_state.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart'; @@ -20,7 +21,6 @@ import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; -import 'package:rive/rive.dart'; final bucket = PageStorageBucket(); @@ -179,16 +179,7 @@ class _FeedScreenState extends State { physics: BouncingScrollPhysics(), itemBuilder: (context, index) { if (index == state.feedBlogPostList.length) { - return Container( - alignment: Alignment.center, - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ); + return const Loader(); } return BlogPostWidget(feedBlogPostModel: state.feedBlogPostList[index]); }, @@ -210,15 +201,7 @@ class _FeedScreenState extends State { } else { return Scaffold( backgroundColor: Colors.grey.shade200, - body: Center( - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ), + body: const Loader(), ); } }, @@ -260,15 +243,7 @@ class _FeedScreenState extends State { } else { return Scaffold( backgroundColor: Colors.grey.shade200, - body: Center( - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ), + body: const Loader(), ); } }, @@ -321,15 +296,7 @@ class _FeedScreenState extends State { } else { return Scaffold( backgroundColor: Colors.grey.shade200, - body: Center( - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ), + body: const Loader(), ); } }, @@ -371,15 +338,7 @@ class _FeedScreenState extends State { } else { return Scaffold( backgroundColor: Colors.grey.shade200, - body: Center( - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ), + body: const Loader(), ); } }, diff --git a/lib/features/profile/models/sub_model.dart b/lib/features/profile/models/sub_model.dart new file mode 100644 index 00000000..adce4bfb --- /dev/null +++ b/lib/features/profile/models/sub_model.dart @@ -0,0 +1,38 @@ +import 'package:eatvio_app/core/models/pagination_model.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'sub_model.freezed.dart'; + +part 'sub_model.g.dart'; + +@freezed +class SubPaginationModel with _$SubPaginationModel { + const factory SubPaginationModel({ + required List data, + required Links links, + required Meta meta, + }) = _SubPaginationModel; + + factory SubPaginationModel.fromJson(Map json) => _$SubPaginationModelFromJson(json); +} + +@freezed +class SubModel with _$SubModel { + const factory SubModel({ + required int id, + required String slug, + required String name, + required String city, + required DateTime birthdate, + @JsonKey(name: 'is_subscribe') + required bool isSubscribe, + required String email, + @JsonKey(name: 'profile_photo_url') required String profilePhotoUrl, + required String role, + @JsonKey(name: 'is_public_profile') required bool isPublicProfile, + @JsonKey(name: 'created_at') required DateTime createdAt, + @JsonKey(name: 'updated_at') required DateTime updatedAt, + }) = _SubModel; + + factory SubModel.fromJson(Map json) => _$SubModelFromJson(json); +} diff --git a/lib/features/profile/models/sub_model.freezed.dart b/lib/features/profile/models/sub_model.freezed.dart new file mode 100644 index 00000000..b8938a71 --- /dev/null +++ b/lib/features/profile/models/sub_model.freezed.dart @@ -0,0 +1,646 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'sub_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +SubPaginationModel _$SubPaginationModelFromJson(Map json) { + return _SubPaginationModel.fromJson(json); +} + +/// @nodoc +mixin _$SubPaginationModel { + List get data => throw _privateConstructorUsedError; + Links get links => throw _privateConstructorUsedError; + Meta get meta => throw _privateConstructorUsedError; + + /// Serializes this SubPaginationModel to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of SubPaginationModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $SubPaginationModelCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SubPaginationModelCopyWith<$Res> { + factory $SubPaginationModelCopyWith( + SubPaginationModel value, $Res Function(SubPaginationModel) then) = + _$SubPaginationModelCopyWithImpl<$Res, SubPaginationModel>; + @useResult + $Res call({List data, Links links, Meta meta}); + + $LinksCopyWith<$Res> get links; + $MetaCopyWith<$Res> get meta; +} + +/// @nodoc +class _$SubPaginationModelCopyWithImpl<$Res, $Val extends SubPaginationModel> + implements $SubPaginationModelCopyWith<$Res> { + _$SubPaginationModelCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of SubPaginationModel + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? data = null, + Object? links = null, + Object? meta = null, + }) { + return _then(_value.copyWith( + data: null == data + ? _value.data + : data // ignore: cast_nullable_to_non_nullable + as List, + links: null == links + ? _value.links + : links // ignore: cast_nullable_to_non_nullable + as Links, + meta: null == meta + ? _value.meta + : meta // ignore: cast_nullable_to_non_nullable + as Meta, + ) as $Val); + } + + /// Create a copy of SubPaginationModel + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $LinksCopyWith<$Res> get links { + return $LinksCopyWith<$Res>(_value.links, (value) { + return _then(_value.copyWith(links: value) as $Val); + }); + } + + /// Create a copy of SubPaginationModel + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $MetaCopyWith<$Res> get meta { + return $MetaCopyWith<$Res>(_value.meta, (value) { + return _then(_value.copyWith(meta: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$SubPaginationModelImplCopyWith<$Res> + implements $SubPaginationModelCopyWith<$Res> { + factory _$$SubPaginationModelImplCopyWith(_$SubPaginationModelImpl value, + $Res Function(_$SubPaginationModelImpl) then) = + __$$SubPaginationModelImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({List data, Links links, Meta meta}); + + @override + $LinksCopyWith<$Res> get links; + @override + $MetaCopyWith<$Res> get meta; +} + +/// @nodoc +class __$$SubPaginationModelImplCopyWithImpl<$Res> + extends _$SubPaginationModelCopyWithImpl<$Res, _$SubPaginationModelImpl> + implements _$$SubPaginationModelImplCopyWith<$Res> { + __$$SubPaginationModelImplCopyWithImpl(_$SubPaginationModelImpl _value, + $Res Function(_$SubPaginationModelImpl) _then) + : super(_value, _then); + + /// Create a copy of SubPaginationModel + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? data = null, + Object? links = null, + Object? meta = null, + }) { + return _then(_$SubPaginationModelImpl( + data: null == data + ? _value._data + : data // ignore: cast_nullable_to_non_nullable + as List, + links: null == links + ? _value.links + : links // ignore: cast_nullable_to_non_nullable + as Links, + meta: null == meta + ? _value.meta + : meta // ignore: cast_nullable_to_non_nullable + as Meta, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$SubPaginationModelImpl implements _SubPaginationModel { + const _$SubPaginationModelImpl( + {required final List data, + required this.links, + required this.meta}) + : _data = data; + + factory _$SubPaginationModelImpl.fromJson(Map json) => + _$$SubPaginationModelImplFromJson(json); + + final List _data; + @override + List get data { + if (_data is EqualUnmodifiableListView) return _data; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_data); + } + + @override + final Links links; + @override + final Meta meta; + + @override + String toString() { + return 'SubPaginationModel(data: $data, links: $links, meta: $meta)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SubPaginationModelImpl && + const DeepCollectionEquality().equals(other._data, _data) && + (identical(other.links, links) || other.links == links) && + (identical(other.meta, meta) || other.meta == meta)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, const DeepCollectionEquality().hash(_data), links, meta); + + /// Create a copy of SubPaginationModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$SubPaginationModelImplCopyWith<_$SubPaginationModelImpl> get copyWith => + __$$SubPaginationModelImplCopyWithImpl<_$SubPaginationModelImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$SubPaginationModelImplToJson( + this, + ); + } +} + +abstract class _SubPaginationModel implements SubPaginationModel { + const factory _SubPaginationModel( + {required final List data, + required final Links links, + required final Meta meta}) = _$SubPaginationModelImpl; + + factory _SubPaginationModel.fromJson(Map json) = + _$SubPaginationModelImpl.fromJson; + + @override + List get data; + @override + Links get links; + @override + Meta get meta; + + /// Create a copy of SubPaginationModel + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$SubPaginationModelImplCopyWith<_$SubPaginationModelImpl> get copyWith => + throw _privateConstructorUsedError; +} + +SubModel _$SubModelFromJson(Map json) { + return _SubModel.fromJson(json); +} + +/// @nodoc +mixin _$SubModel { + int get id => throw _privateConstructorUsedError; + String get slug => throw _privateConstructorUsedError; + String get name => throw _privateConstructorUsedError; + String get city => throw _privateConstructorUsedError; + DateTime get birthdate => throw _privateConstructorUsedError; + @JsonKey(name: 'is_subscribe') + bool get isSubscribe => throw _privateConstructorUsedError; + String get email => throw _privateConstructorUsedError; + @JsonKey(name: 'profile_photo_url') + String get profilePhotoUrl => throw _privateConstructorUsedError; + String get role => throw _privateConstructorUsedError; + @JsonKey(name: 'is_public_profile') + bool get isPublicProfile => throw _privateConstructorUsedError; + @JsonKey(name: 'created_at') + DateTime get createdAt => throw _privateConstructorUsedError; + @JsonKey(name: 'updated_at') + DateTime get updatedAt => throw _privateConstructorUsedError; + + /// Serializes this SubModel to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of SubModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $SubModelCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SubModelCopyWith<$Res> { + factory $SubModelCopyWith(SubModel value, $Res Function(SubModel) then) = + _$SubModelCopyWithImpl<$Res, SubModel>; + @useResult + $Res call( + {int id, + String slug, + String name, + String city, + DateTime birthdate, + @JsonKey(name: 'is_subscribe') bool isSubscribe, + String email, + @JsonKey(name: 'profile_photo_url') String profilePhotoUrl, + String role, + @JsonKey(name: 'is_public_profile') bool isPublicProfile, + @JsonKey(name: 'created_at') DateTime createdAt, + @JsonKey(name: 'updated_at') DateTime updatedAt}); +} + +/// @nodoc +class _$SubModelCopyWithImpl<$Res, $Val extends SubModel> + implements $SubModelCopyWith<$Res> { + _$SubModelCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of SubModel + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? slug = null, + Object? name = null, + Object? city = null, + Object? birthdate = null, + Object? isSubscribe = null, + Object? email = null, + Object? profilePhotoUrl = null, + Object? role = null, + Object? isPublicProfile = null, + Object? createdAt = null, + Object? updatedAt = null, + }) { + return _then(_value.copyWith( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + slug: null == slug + ? _value.slug + : slug // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + city: null == city + ? _value.city + : city // ignore: cast_nullable_to_non_nullable + as String, + birthdate: null == birthdate + ? _value.birthdate + : birthdate // ignore: cast_nullable_to_non_nullable + as DateTime, + isSubscribe: null == isSubscribe + ? _value.isSubscribe + : isSubscribe // ignore: cast_nullable_to_non_nullable + as bool, + email: null == email + ? _value.email + : email // ignore: cast_nullable_to_non_nullable + as String, + profilePhotoUrl: null == profilePhotoUrl + ? _value.profilePhotoUrl + : profilePhotoUrl // ignore: cast_nullable_to_non_nullable + as String, + role: null == role + ? _value.role + : role // ignore: cast_nullable_to_non_nullable + as String, + isPublicProfile: null == isPublicProfile + ? _value.isPublicProfile + : isPublicProfile // ignore: cast_nullable_to_non_nullable + as bool, + createdAt: null == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as DateTime, + updatedAt: null == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as DateTime, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$SubModelImplCopyWith<$Res> + implements $SubModelCopyWith<$Res> { + factory _$$SubModelImplCopyWith( + _$SubModelImpl value, $Res Function(_$SubModelImpl) then) = + __$$SubModelImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {int id, + String slug, + String name, + String city, + DateTime birthdate, + @JsonKey(name: 'is_subscribe') bool isSubscribe, + String email, + @JsonKey(name: 'profile_photo_url') String profilePhotoUrl, + String role, + @JsonKey(name: 'is_public_profile') bool isPublicProfile, + @JsonKey(name: 'created_at') DateTime createdAt, + @JsonKey(name: 'updated_at') DateTime updatedAt}); +} + +/// @nodoc +class __$$SubModelImplCopyWithImpl<$Res> + extends _$SubModelCopyWithImpl<$Res, _$SubModelImpl> + implements _$$SubModelImplCopyWith<$Res> { + __$$SubModelImplCopyWithImpl( + _$SubModelImpl _value, $Res Function(_$SubModelImpl) _then) + : super(_value, _then); + + /// Create a copy of SubModel + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? slug = null, + Object? name = null, + Object? city = null, + Object? birthdate = null, + Object? isSubscribe = null, + Object? email = null, + Object? profilePhotoUrl = null, + Object? role = null, + Object? isPublicProfile = null, + Object? createdAt = null, + Object? updatedAt = null, + }) { + return _then(_$SubModelImpl( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + slug: null == slug + ? _value.slug + : slug // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + city: null == city + ? _value.city + : city // ignore: cast_nullable_to_non_nullable + as String, + birthdate: null == birthdate + ? _value.birthdate + : birthdate // ignore: cast_nullable_to_non_nullable + as DateTime, + isSubscribe: null == isSubscribe + ? _value.isSubscribe + : isSubscribe // ignore: cast_nullable_to_non_nullable + as bool, + email: null == email + ? _value.email + : email // ignore: cast_nullable_to_non_nullable + as String, + profilePhotoUrl: null == profilePhotoUrl + ? _value.profilePhotoUrl + : profilePhotoUrl // ignore: cast_nullable_to_non_nullable + as String, + role: null == role + ? _value.role + : role // ignore: cast_nullable_to_non_nullable + as String, + isPublicProfile: null == isPublicProfile + ? _value.isPublicProfile + : isPublicProfile // ignore: cast_nullable_to_non_nullable + as bool, + createdAt: null == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as DateTime, + updatedAt: null == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as DateTime, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$SubModelImpl implements _SubModel { + const _$SubModelImpl( + {required this.id, + required this.slug, + required this.name, + required this.city, + required this.birthdate, + @JsonKey(name: 'is_subscribe') required this.isSubscribe, + required this.email, + @JsonKey(name: 'profile_photo_url') required this.profilePhotoUrl, + required this.role, + @JsonKey(name: 'is_public_profile') required this.isPublicProfile, + @JsonKey(name: 'created_at') required this.createdAt, + @JsonKey(name: 'updated_at') required this.updatedAt}); + + factory _$SubModelImpl.fromJson(Map json) => + _$$SubModelImplFromJson(json); + + @override + final int id; + @override + final String slug; + @override + final String name; + @override + final String city; + @override + final DateTime birthdate; + @override + @JsonKey(name: 'is_subscribe') + final bool isSubscribe; + @override + final String email; + @override + @JsonKey(name: 'profile_photo_url') + final String profilePhotoUrl; + @override + final String role; + @override + @JsonKey(name: 'is_public_profile') + final bool isPublicProfile; + @override + @JsonKey(name: 'created_at') + final DateTime createdAt; + @override + @JsonKey(name: 'updated_at') + final DateTime updatedAt; + + @override + String toString() { + return 'SubModel(id: $id, slug: $slug, name: $name, city: $city, birthdate: $birthdate, isSubscribe: $isSubscribe, email: $email, profilePhotoUrl: $profilePhotoUrl, role: $role, isPublicProfile: $isPublicProfile, createdAt: $createdAt, updatedAt: $updatedAt)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SubModelImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.slug, slug) || other.slug == slug) && + (identical(other.name, name) || other.name == name) && + (identical(other.city, city) || other.city == city) && + (identical(other.birthdate, birthdate) || + other.birthdate == birthdate) && + (identical(other.isSubscribe, isSubscribe) || + other.isSubscribe == isSubscribe) && + (identical(other.email, email) || other.email == email) && + (identical(other.profilePhotoUrl, profilePhotoUrl) || + other.profilePhotoUrl == profilePhotoUrl) && + (identical(other.role, role) || other.role == role) && + (identical(other.isPublicProfile, isPublicProfile) || + other.isPublicProfile == isPublicProfile) && + (identical(other.createdAt, createdAt) || + other.createdAt == createdAt) && + (identical(other.updatedAt, updatedAt) || + other.updatedAt == updatedAt)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + id, + slug, + name, + city, + birthdate, + isSubscribe, + email, + profilePhotoUrl, + role, + isPublicProfile, + createdAt, + updatedAt); + + /// Create a copy of SubModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$SubModelImplCopyWith<_$SubModelImpl> get copyWith => + __$$SubModelImplCopyWithImpl<_$SubModelImpl>(this, _$identity); + + @override + Map toJson() { + return _$$SubModelImplToJson( + this, + ); + } +} + +abstract class _SubModel implements SubModel { + const factory _SubModel( + {required final int id, + required final String slug, + required final String name, + required final String city, + required final DateTime birthdate, + @JsonKey(name: 'is_subscribe') required final bool isSubscribe, + required final String email, + @JsonKey(name: 'profile_photo_url') required final String profilePhotoUrl, + required final String role, + @JsonKey(name: 'is_public_profile') required final bool isPublicProfile, + @JsonKey(name: 'created_at') required final DateTime createdAt, + @JsonKey(name: 'updated_at') + required final DateTime updatedAt}) = _$SubModelImpl; + + factory _SubModel.fromJson(Map json) = + _$SubModelImpl.fromJson; + + @override + int get id; + @override + String get slug; + @override + String get name; + @override + String get city; + @override + DateTime get birthdate; + @override + @JsonKey(name: 'is_subscribe') + bool get isSubscribe; + @override + String get email; + @override + @JsonKey(name: 'profile_photo_url') + String get profilePhotoUrl; + @override + String get role; + @override + @JsonKey(name: 'is_public_profile') + bool get isPublicProfile; + @override + @JsonKey(name: 'created_at') + DateTime get createdAt; + @override + @JsonKey(name: 'updated_at') + DateTime get updatedAt; + + /// Create a copy of SubModel + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$SubModelImplCopyWith<_$SubModelImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/features/profile/models/sub_model.g.dart b/lib/features/profile/models/sub_model.g.dart new file mode 100644 index 00000000..a6536132 --- /dev/null +++ b/lib/features/profile/models/sub_model.g.dart @@ -0,0 +1,57 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'sub_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$SubPaginationModelImpl _$$SubPaginationModelImplFromJson( + Map json) => + _$SubPaginationModelImpl( + data: (json['data'] as List) + .map((e) => SubModel.fromJson(e as Map)) + .toList(), + links: Links.fromJson(json['links'] as Map), + meta: Meta.fromJson(json['meta'] as Map), + ); + +Map _$$SubPaginationModelImplToJson( + _$SubPaginationModelImpl instance) => + { + 'data': instance.data, + 'links': instance.links, + 'meta': instance.meta, + }; + +_$SubModelImpl _$$SubModelImplFromJson(Map json) => + _$SubModelImpl( + id: (json['id'] as num).toInt(), + slug: json['slug'] as String, + name: json['name'] as String, + city: json['city'] as String, + birthdate: DateTime.parse(json['birthdate'] as String), + isSubscribe: json['is_subscribe'] as bool, + email: json['email'] as String, + profilePhotoUrl: json['profile_photo_url'] as String, + role: json['role'] as String, + isPublicProfile: json['is_public_profile'] as bool, + createdAt: DateTime.parse(json['created_at'] as String), + updatedAt: DateTime.parse(json['updated_at'] as String), + ); + +Map _$$SubModelImplToJson(_$SubModelImpl instance) => + { + 'id': instance.id, + 'slug': instance.slug, + 'name': instance.name, + 'city': instance.city, + 'birthdate': instance.birthdate.toIso8601String(), + 'is_subscribe': instance.isSubscribe, + 'email': instance.email, + 'profile_photo_url': instance.profilePhotoUrl, + 'role': instance.role, + 'is_public_profile': instance.isPublicProfile, + 'created_at': instance.createdAt.toIso8601String(), + 'updated_at': instance.updatedAt.toIso8601String(), + }; diff --git a/lib/features/profile/presentation/bloc/sub_cubit/sub_cubit.dart b/lib/features/profile/presentation/bloc/sub_cubit/sub_cubit.dart new file mode 100644 index 00000000..98e93bcb --- /dev/null +++ b/lib/features/profile/presentation/bloc/sub_cubit/sub_cubit.dart @@ -0,0 +1,29 @@ +import 'package:eatvio_app/features/profile/presentation/bloc/sub_cubit/sub_state.dart'; +import 'package:eatvio_app/features/profile/repositories/profile_repository.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class SubCubit extends Cubit { + final ProfileRepository profileRepository; + + SubCubit({ + required this.profileRepository, + }) : super(const SubState()); + + getSubscriptions(String slug) async { + emit(const SubState.loading()); + try { + emit(SubState.loaded(subList: await profileRepository.getSubscriptionsBySlug(slug))); + } catch (e) { + emit(SubState.error(errorMessage: e.toString())); + } + } + + getSubscribers(String slug) async { + emit(const SubState.loading()); + try { + emit(SubState.loaded(subList: await profileRepository.getSubscribersBySlug(slug))); + } catch (e) { + emit(SubState.error(errorMessage: e.toString())); + } + } +} diff --git a/lib/features/profile/presentation/bloc/sub_cubit/sub_state.dart b/lib/features/profile/presentation/bloc/sub_cubit/sub_state.dart new file mode 100644 index 00000000..8548e593 --- /dev/null +++ b/lib/features/profile/presentation/bloc/sub_cubit/sub_state.dart @@ -0,0 +1,19 @@ +import 'package:eatvio_app/features/profile/models/sub_model.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'sub_state.freezed.dart'; + +@freezed +abstract class SubState with _$SubState { + const factory SubState() = _SubState; + + const factory SubState.loading() = SubStateLoading; + + const factory SubState.loaded({ + required List subList, + }) = SubStateLoaded; + + const factory SubState.error({ + required String errorMessage, + }) = SubStateError; +} diff --git a/lib/features/profile/presentation/bloc/sub_cubit/sub_state.freezed.dart b/lib/features/profile/presentation/bloc/sub_cubit/sub_state.freezed.dart new file mode 100644 index 00000000..f811921a --- /dev/null +++ b/lib/features/profile/presentation/bloc/sub_cubit/sub_state.freezed.dart @@ -0,0 +1,638 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'sub_state.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +/// @nodoc +mixin _$SubState { + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() loading, + required TResult Function(List subList) loaded, + required TResult Function(String errorMessage) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? loading, + TResult? Function(List subList)? loaded, + TResult? Function(String errorMessage)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? loading, + TResult Function(List subList)? loaded, + TResult Function(String errorMessage)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map( + TResult Function(_SubState value) $default, { + required TResult Function(SubStateLoading value) loading, + required TResult Function(SubStateLoaded value) loaded, + required TResult Function(SubStateError value) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_SubState value)? $default, { + TResult? Function(SubStateLoading value)? loading, + TResult? Function(SubStateLoaded value)? loaded, + TResult? Function(SubStateError value)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap( + TResult Function(_SubState value)? $default, { + TResult Function(SubStateLoading value)? loading, + TResult Function(SubStateLoaded value)? loaded, + TResult Function(SubStateError value)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SubStateCopyWith<$Res> { + factory $SubStateCopyWith(SubState value, $Res Function(SubState) then) = + _$SubStateCopyWithImpl<$Res, SubState>; +} + +/// @nodoc +class _$SubStateCopyWithImpl<$Res, $Val extends SubState> + implements $SubStateCopyWith<$Res> { + _$SubStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of SubState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc +abstract class _$$SubStateImplCopyWith<$Res> { + factory _$$SubStateImplCopyWith( + _$SubStateImpl value, $Res Function(_$SubStateImpl) then) = + __$$SubStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$SubStateImplCopyWithImpl<$Res> + extends _$SubStateCopyWithImpl<$Res, _$SubStateImpl> + implements _$$SubStateImplCopyWith<$Res> { + __$$SubStateImplCopyWithImpl( + _$SubStateImpl _value, $Res Function(_$SubStateImpl) _then) + : super(_value, _then); + + /// Create a copy of SubState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$SubStateImpl implements _SubState { + const _$SubStateImpl(); + + @override + String toString() { + return 'SubState()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$SubStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() loading, + required TResult Function(List subList) loaded, + required TResult Function(String errorMessage) error, + }) { + return $default(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? loading, + TResult? Function(List subList)? loaded, + TResult? Function(String errorMessage)? error, + }) { + return $default?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? loading, + TResult Function(List subList)? loaded, + TResult Function(String errorMessage)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_SubState value) $default, { + required TResult Function(SubStateLoading value) loading, + required TResult Function(SubStateLoaded value) loaded, + required TResult Function(SubStateError value) error, + }) { + return $default(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_SubState value)? $default, { + TResult? Function(SubStateLoading value)? loading, + TResult? Function(SubStateLoaded value)? loaded, + TResult? Function(SubStateError value)? error, + }) { + return $default?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_SubState value)? $default, { + TResult Function(SubStateLoading value)? loading, + TResult Function(SubStateLoaded value)? loaded, + TResult Function(SubStateError value)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(this); + } + return orElse(); + } +} + +abstract class _SubState implements SubState { + const factory _SubState() = _$SubStateImpl; +} + +/// @nodoc +abstract class _$$SubStateLoadingImplCopyWith<$Res> { + factory _$$SubStateLoadingImplCopyWith(_$SubStateLoadingImpl value, + $Res Function(_$SubStateLoadingImpl) then) = + __$$SubStateLoadingImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$SubStateLoadingImplCopyWithImpl<$Res> + extends _$SubStateCopyWithImpl<$Res, _$SubStateLoadingImpl> + implements _$$SubStateLoadingImplCopyWith<$Res> { + __$$SubStateLoadingImplCopyWithImpl( + _$SubStateLoadingImpl _value, $Res Function(_$SubStateLoadingImpl) _then) + : super(_value, _then); + + /// Create a copy of SubState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$SubStateLoadingImpl implements SubStateLoading { + const _$SubStateLoadingImpl(); + + @override + String toString() { + return 'SubState.loading()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$SubStateLoadingImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() loading, + required TResult Function(List subList) loaded, + required TResult Function(String errorMessage) error, + }) { + return loading(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? loading, + TResult? Function(List subList)? loaded, + TResult? Function(String errorMessage)? error, + }) { + return loading?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? loading, + TResult Function(List subList)? loaded, + TResult Function(String errorMessage)? error, + required TResult orElse(), + }) { + if (loading != null) { + return loading(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_SubState value) $default, { + required TResult Function(SubStateLoading value) loading, + required TResult Function(SubStateLoaded value) loaded, + required TResult Function(SubStateError value) error, + }) { + return loading(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_SubState value)? $default, { + TResult? Function(SubStateLoading value)? loading, + TResult? Function(SubStateLoaded value)? loaded, + TResult? Function(SubStateError value)? error, + }) { + return loading?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_SubState value)? $default, { + TResult Function(SubStateLoading value)? loading, + TResult Function(SubStateLoaded value)? loaded, + TResult Function(SubStateError value)? error, + required TResult orElse(), + }) { + if (loading != null) { + return loading(this); + } + return orElse(); + } +} + +abstract class SubStateLoading implements SubState { + const factory SubStateLoading() = _$SubStateLoadingImpl; +} + +/// @nodoc +abstract class _$$SubStateLoadedImplCopyWith<$Res> { + factory _$$SubStateLoadedImplCopyWith(_$SubStateLoadedImpl value, + $Res Function(_$SubStateLoadedImpl) then) = + __$$SubStateLoadedImplCopyWithImpl<$Res>; + @useResult + $Res call({List subList}); +} + +/// @nodoc +class __$$SubStateLoadedImplCopyWithImpl<$Res> + extends _$SubStateCopyWithImpl<$Res, _$SubStateLoadedImpl> + implements _$$SubStateLoadedImplCopyWith<$Res> { + __$$SubStateLoadedImplCopyWithImpl( + _$SubStateLoadedImpl _value, $Res Function(_$SubStateLoadedImpl) _then) + : super(_value, _then); + + /// Create a copy of SubState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? subList = null, + }) { + return _then(_$SubStateLoadedImpl( + subList: null == subList + ? _value._subList + : subList // ignore: cast_nullable_to_non_nullable + as List, + )); + } +} + +/// @nodoc + +class _$SubStateLoadedImpl implements SubStateLoaded { + const _$SubStateLoadedImpl({required final List subList}) + : _subList = subList; + + final List _subList; + @override + List get subList { + if (_subList is EqualUnmodifiableListView) return _subList; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_subList); + } + + @override + String toString() { + return 'SubState.loaded(subList: $subList)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SubStateLoadedImpl && + const DeepCollectionEquality().equals(other._subList, _subList)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(_subList)); + + /// Create a copy of SubState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$SubStateLoadedImplCopyWith<_$SubStateLoadedImpl> get copyWith => + __$$SubStateLoadedImplCopyWithImpl<_$SubStateLoadedImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() loading, + required TResult Function(List subList) loaded, + required TResult Function(String errorMessage) error, + }) { + return loaded(subList); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? loading, + TResult? Function(List subList)? loaded, + TResult? Function(String errorMessage)? error, + }) { + return loaded?.call(subList); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? loading, + TResult Function(List subList)? loaded, + TResult Function(String errorMessage)? error, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded(subList); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_SubState value) $default, { + required TResult Function(SubStateLoading value) loading, + required TResult Function(SubStateLoaded value) loaded, + required TResult Function(SubStateError value) error, + }) { + return loaded(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_SubState value)? $default, { + TResult? Function(SubStateLoading value)? loading, + TResult? Function(SubStateLoaded value)? loaded, + TResult? Function(SubStateError value)? error, + }) { + return loaded?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_SubState value)? $default, { + TResult Function(SubStateLoading value)? loading, + TResult Function(SubStateLoaded value)? loaded, + TResult Function(SubStateError value)? error, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded(this); + } + return orElse(); + } +} + +abstract class SubStateLoaded implements SubState { + const factory SubStateLoaded({required final List subList}) = + _$SubStateLoadedImpl; + + List get subList; + + /// Create a copy of SubState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$SubStateLoadedImplCopyWith<_$SubStateLoadedImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$SubStateErrorImplCopyWith<$Res> { + factory _$$SubStateErrorImplCopyWith( + _$SubStateErrorImpl value, $Res Function(_$SubStateErrorImpl) then) = + __$$SubStateErrorImplCopyWithImpl<$Res>; + @useResult + $Res call({String errorMessage}); +} + +/// @nodoc +class __$$SubStateErrorImplCopyWithImpl<$Res> + extends _$SubStateCopyWithImpl<$Res, _$SubStateErrorImpl> + implements _$$SubStateErrorImplCopyWith<$Res> { + __$$SubStateErrorImplCopyWithImpl( + _$SubStateErrorImpl _value, $Res Function(_$SubStateErrorImpl) _then) + : super(_value, _then); + + /// Create a copy of SubState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? errorMessage = null, + }) { + return _then(_$SubStateErrorImpl( + errorMessage: null == errorMessage + ? _value.errorMessage + : errorMessage // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$SubStateErrorImpl implements SubStateError { + const _$SubStateErrorImpl({required this.errorMessage}); + + @override + final String errorMessage; + + @override + String toString() { + return 'SubState.error(errorMessage: $errorMessage)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SubStateErrorImpl && + (identical(other.errorMessage, errorMessage) || + other.errorMessage == errorMessage)); + } + + @override + int get hashCode => Object.hash(runtimeType, errorMessage); + + /// Create a copy of SubState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$SubStateErrorImplCopyWith<_$SubStateErrorImpl> get copyWith => + __$$SubStateErrorImplCopyWithImpl<_$SubStateErrorImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() loading, + required TResult Function(List subList) loaded, + required TResult Function(String errorMessage) error, + }) { + return error(errorMessage); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult? Function()? $default, { + TResult? Function()? loading, + TResult? Function(List subList)? loaded, + TResult? Function(String errorMessage)? error, + }) { + return error?.call(errorMessage); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? loading, + TResult Function(List subList)? loaded, + TResult Function(String errorMessage)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(errorMessage); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(_SubState value) $default, { + required TResult Function(SubStateLoading value) loading, + required TResult Function(SubStateLoaded value) loaded, + required TResult Function(SubStateError value) error, + }) { + return error(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult? Function(_SubState value)? $default, { + TResult? Function(SubStateLoading value)? loading, + TResult? Function(SubStateLoaded value)? loaded, + TResult? Function(SubStateError value)? error, + }) { + return error?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(_SubState value)? $default, { + TResult Function(SubStateLoading value)? loading, + TResult Function(SubStateLoaded value)? loaded, + TResult Function(SubStateError value)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(this); + } + return orElse(); + } +} + +abstract class SubStateError implements SubState { + const factory SubStateError({required final String errorMessage}) = + _$SubStateErrorImpl; + + String get errorMessage; + + /// Create a copy of SubState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$SubStateErrorImplCopyWith<_$SubStateErrorImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/features/profile/presentation/pages/profile_screen.dart b/lib/features/profile/presentation/pages/profile_screen.dart index d84e7611..f8c2a192 100644 --- a/lib/features/profile/presentation/pages/profile_screen.dart +++ b/lib/features/profile/presentation/pages/profile_screen.dart @@ -1,11 +1,13 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/features/feed/presentation/widgets/blog_post_widget.dart'; import 'package:eatvio_app/features/profile/models/circumference_model.dart'; import 'package:eatvio_app/features/profile/models/weight_model.dart'; import 'package:eatvio_app/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart'; import 'package:eatvio_app/features/profile/presentation/bloc/profile_cubit/profile_state.dart'; +import 'package:eatvio_app/features/profile/presentation/pages/subs_screen.dart'; import 'package:eatvio_app/features/profile/presentation/widgets/circumference_chart_widget.dart'; import 'package:eatvio_app/features/profile/presentation/widgets/imt_chart_widget.dart'; import 'package:eatvio_app/features/profile/presentation/widgets/weight_chart_widget.dart'; @@ -14,7 +16,6 @@ import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; -import 'package:rive/rive.dart'; import 'package:flutter_svg/flutter_svg.dart'; class ProfileScreen extends StatefulWidget { @@ -56,7 +57,7 @@ class _ProfileScreenState extends State { circumferencePoints = state.circumferencePoints!; weightPoints = state.weightPoints!; } - return _profileScreen(state); + return widget.author == null ? _myProfileScreen(state) : _profileScreen(state); } else if (state is ProfileStateError) { return Scaffold( appBar: AppBar(), @@ -70,15 +71,7 @@ class _ProfileScreenState extends State { } else { return Scaffold( appBar: AppBar(), - body: Center( - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ), + body: const Loader(), ); } }, @@ -88,7 +81,7 @@ class _ProfileScreenState extends State { _profileScreen(ProfileStateLoaded state) { return Scaffold( backgroundColor: Colors.grey.shade200, - appBar: widget.author == null ? AppBar(toolbarHeight: 0) : AppBar(), + appBar: AppBar(), body: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -105,26 +98,56 @@ class _ProfileScreenState extends State { children: [ _header(state), if (state.profileDetails.additionalInfo.isNotEmpty) _additionalInfo(state), - if (widget.author != null) const SizedBox(height: 8), - if (widget.author != null) _messageAndSubscribe(), - if (widget.author != null) const SizedBox(height: 16), + const SizedBox(height: 8), + _messageAndSubscribe(), + const SizedBox(height: 16), ], ), ), _subscriptionsAndSubscribers(state), - if(state.profile.badges.isNotEmpty) _badges(state), - if (widget.author == null) const SizedBox(height: 16), + if (state.profile.badges.isNotEmpty) _badges(state), _socialLinks(state), - if (widget.author == null) const SizedBox(height: 16), _education(state), - if (widget.author == null) const SizedBox(height: 16), _diplomasAndCertificates(state), - if (widget.author == null) const SizedBox(height: 16), - if (widget.author == null) _charts(), - if (widget.author == null) const SizedBox(height: 16), _activePlans(state), - if (widget.author == null) const SizedBox(height: 16), - if (widget.author == null) _newBlogPostButton(), + _blogPosts(state), + ], + ), + ), + ); + } + + _myProfileScreen(ProfileStateLoaded state) { + return Scaffold( + backgroundColor: Colors.grey.shade200, + appBar: AppBar(toolbarHeight: 0), + body: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(16), + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _header(state), + if (state.profileDetails.additionalInfo.isNotEmpty) _additionalInfo(state), + ], + ), + ), + _subscriptionsAndSubscribers(state), + if (state.profile.badges.isNotEmpty) Padding(padding: EdgeInsets.only(bottom: 16), child: _badges(state)), + if (state.profile.role == 'consultant') Padding(padding: EdgeInsets.only(bottom: 16), child: _socialLinks(state)), + if (state.profile.role == 'consultant') Padding(padding: EdgeInsets.only(bottom: 16), child: _education(state)), + if (state.profile.role == 'consultant') Padding(padding: EdgeInsets.only(bottom: 16), child: _diplomasAndCertificates(state)), + Padding(padding: EdgeInsets.only(bottom: 16), child: _charts()), + if (state.profile.role == 'consultant') Padding(padding: EdgeInsets.only(bottom: 16), child: _activePlans(state)), + _newBlogPostButton(), _blogPosts(state), ], ), @@ -511,7 +534,13 @@ class _ProfileScreenState extends State { child: Row( children: [ InkWell( - onTap: () {}, + onTap: () => GoRouter.of(context).pushNamed( + 'subsScreen', + extra: SubType.Subscriptions, + pathParameters: { + 'slug': state.profile.slug, + }, + ), borderRadius: BorderRadius.circular(12), child: Ink( padding: const EdgeInsets.all(8), @@ -555,7 +584,13 @@ class _ProfileScreenState extends State { ), SizedBox(width: 4), InkWell( - onTap: () {}, + onTap: () => GoRouter.of(context).pushNamed( + 'subsScreen', + extra: SubType.Subscribers, + pathParameters: { + 'slug': state.profile.slug, + }, + ), borderRadius: BorderRadius.circular(12), child: Ink( padding: const EdgeInsets.all(8), diff --git a/lib/features/profile/presentation/pages/subs_screen.dart b/lib/features/profile/presentation/pages/subs_screen.dart new file mode 100644 index 00000000..2b923dc0 --- /dev/null +++ b/lib/features/profile/presentation/pages/subs_screen.dart @@ -0,0 +1,69 @@ +import 'package:eatvio_app/core/locator_service.dart'; +import 'package:eatvio_app/core/widgets/loader.dart'; +import 'package:eatvio_app/features/profile/presentation/bloc/sub_cubit/sub_cubit.dart'; +import 'package:eatvio_app/features/profile/presentation/bloc/sub_cubit/sub_state.dart'; +import 'package:eatvio_app/features/profile/presentation/widgets/sub_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +enum SubType { Subscribers, Subscriptions } + +class SubsScreen extends StatefulWidget { + final SubType subType; + final String slug; + + const SubsScreen({ + super.key, + required this.slug, + required this.subType, + }); + + @override + State createState() => _SubsScreenState(); +} + +class _SubsScreenState extends State { + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (BuildContext context) => widget.subType == SubType.Subscriptions + ? (sl() + ..getSubscriptions( + widget.slug, + )) + : (sl() + ..getSubscribers( + widget.slug, + )), + child: BlocBuilder( + builder: (BuildContext context, SubState state) { + return Scaffold( + appBar: AppBar( + title: Text( + widget.subType == SubType.Subscriptions ? 'Подписки' : 'Подписчики', + ), + ), + body: _buildStates(state), + ); + }, + ), + ); + } + + _buildStates(SubState state) { + if (state is SubStateLoading) { + return Loader(); + } + if (state is SubStateError) { + return Text(state.errorMessage); + } + if (state is SubStateLoaded) { + return ListView.builder( + itemBuilder: (context, index) => SubWidget( + subModel: state.subList[index], + ), + itemCount: state.subList.length, + ); + } + } +} diff --git a/lib/features/profile/presentation/widgets/sub_action_button_widget.dart b/lib/features/profile/presentation/widgets/sub_action_button_widget.dart new file mode 100644 index 00000000..4337775f --- /dev/null +++ b/lib/features/profile/presentation/widgets/sub_action_button_widget.dart @@ -0,0 +1,63 @@ +import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:flutter/material.dart'; + +enum SubActionType { SendMessage, Subscribe } + +class SubActionButtonWidget extends StatefulWidget { + final SubActionType subActionType; + + const SubActionButtonWidget({ + super.key, + required this.subActionType, + }); + + @override + State createState() => _SubActionButtonWidgetState(); +} + +class _SubActionButtonWidgetState extends State { + @override + Widget build(BuildContext context) { + if (widget.subActionType == SubActionType.Subscribe) { + return InkWell( + onTap: () {}, + borderRadius: BorderRadius.circular(8), + child: Ink( + padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + color: appTheme.blueA400, + ), + child: Text( + 'Подписаться', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + ), + ); + } else { + return InkWell( + onTap: () {}, + borderRadius: BorderRadius.circular(8), + child: Ink( + padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + color: Color(0xFFF1F2F6), + ), + child: Text( + 'Сообщение', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + color: Color(0xFF6D6F71), + ), + ), + ), + ); + } + } +} diff --git a/lib/features/profile/presentation/widgets/sub_widget.dart b/lib/features/profile/presentation/widgets/sub_widget.dart new file mode 100644 index 00000000..fc4e9f0d --- /dev/null +++ b/lib/features/profile/presentation/widgets/sub_widget.dart @@ -0,0 +1,65 @@ +import 'package:eatvio_app/features/profile/models/sub_model.dart'; +import 'package:eatvio_app/features/profile/presentation/widgets/sub_action_button_widget.dart'; +import 'package:flutter/material.dart'; + +class SubWidget extends StatefulWidget { + final SubModel subModel; + + const SubWidget({ + super.key, + required this.subModel, + }); + + @override + State createState() => _SubWidgetState(); +} + +class _SubWidgetState extends State { + @override + Widget build(BuildContext context) { + return InkWell( + onTap: () {}, + child: Ink( + padding: EdgeInsets.symmetric(vertical: 8, horizontal: 16), + child: Row( + children: [ + CircleAvatar( + backgroundImage: NetworkImage(widget.subModel.profilePhotoUrl), + radius: 28, + ), + const SizedBox(width: 12), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + widget.subModel.name, + style: TextStyle( + color: Color(0xFF323F4B), + fontSize: 16, + fontWeight: FontWeight.w600, + height: 1, + ), + ), + const SizedBox(height: 4), + Text( + '${DateTime.now().year - widget.subModel.birthdate.year - (DateTime.now().isBefore(DateTime(DateTime.now().year, widget.subModel.birthdate.month, widget.subModel.birthdate.day)) ? 1 : 0)} лет, ${widget.subModel.city}', + style: TextStyle( + color: Color(0xFF6D6F71), + fontSize: 12, + fontWeight: FontWeight.w500, + height: 1, + ), + ), + ], + ), + const Spacer(), + SubActionButtonWidget( + subActionType: widget.subModel.isSubscribe ? SubActionType.SendMessage : SubActionType.Subscribe, + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/profile/repositories/profile_repository.dart b/lib/features/profile/repositories/profile_repository.dart index d423fbfa..911fc981 100644 --- a/lib/features/profile/repositories/profile_repository.dart +++ b/lib/features/profile/repositories/profile_repository.dart @@ -1,6 +1,7 @@ import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart'; import 'package:eatvio_app/features/profile/models/circumference_model.dart'; import 'package:eatvio_app/features/profile/models/profile_model.dart'; +import 'package:eatvio_app/features/profile/models/sub_model.dart'; import 'package:eatvio_app/features/profile/models/weight_model.dart'; import 'package:eatvio_app/features/profile/services/profile_service.dart'; @@ -112,4 +113,20 @@ class ProfileRepository { rethrow; } } + + Future> getSubscribersBySlug(String slug) async { + try { + return (await profileService.getSubscribers(slug: slug)).data; + } catch (e) { + rethrow; + } + } + + Future> getSubscriptionsBySlug(String slug) async { + try { + return (await profileService.getSubscriptions(slug: slug)).data; + } catch (e) { + rethrow; + } + } } diff --git a/lib/features/profile/services/profile_service.dart b/lib/features/profile/services/profile_service.dart index f092cc4f..11b7abaf 100644 --- a/lib/features/profile/services/profile_service.dart +++ b/lib/features/profile/services/profile_service.dart @@ -2,6 +2,7 @@ import 'package:dio/dio.dart'; import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart'; import 'package:eatvio_app/features/profile/models/circumference_model.dart'; import 'package:eatvio_app/features/profile/models/profile_model.dart'; +import 'package:eatvio_app/features/profile/models/sub_model.dart'; import 'package:eatvio_app/features/profile/models/weight_model.dart'; import 'package:retrofit/retrofit.dart'; part 'profile_service.g.dart'; @@ -59,4 +60,14 @@ abstract class ProfileService { @Path() required String period, @Path() required String slug, }); + + @GET('/api/profile/{slug}/subscribers') + Future getSubscribers({ + @Path() required String slug, + }); + + @GET('/api/profile/{slug}/subscriptions') + Future getSubscriptions({ + @Path() required String slug, + }); } diff --git a/lib/features/profile/services/profile_service.g.dart b/lib/features/profile/services/profile_service.g.dart index 274c84b8..5121bf4c 100644 --- a/lib/features/profile/services/profile_service.g.dart +++ b/lib/features/profile/services/profile_service.g.dart @@ -376,6 +376,72 @@ class _ProfileService implements ProfileService { return _value; } + @override + Future getSubscribers({required String slug}) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType(Options( + method: 'GET', + headers: _headers, + extra: _extra, + ) + .compose( + _dio.options, + '/api/profile/${slug}/subscribers', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + ))); + final _result = await _dio.fetch>(_options); + late SubPaginationModel _value; + try { + _value = SubPaginationModel.fromJson(_result.data!); + } on Object catch (e, s) { + errorLogger?.logError(e, s, _options); + rethrow; + } + return _value; + } + + @override + Future getSubscriptions({required String slug}) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType(Options( + method: 'GET', + headers: _headers, + extra: _extra, + ) + .compose( + _dio.options, + '/api/profile/${slug}/subscriptions', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + ))); + final _result = await _dio.fetch>(_options); + late SubPaginationModel _value; + try { + _value = SubPaginationModel.fromJson(_result.data!); + } on Object catch (e, s) { + errorLogger?.logError(e, s, _options); + rethrow; + } + return _value; + } + RequestOptions _setStreamType(RequestOptions requestOptions) { if (T != dynamic && !(requestOptions.responseType == ResponseType.bytes || diff --git a/lib/features/recipes/presentation/pages/about_recipes_screen.dart b/lib/features/recipes/presentation/pages/about_recipes_screen.dart index 4fd97a0f..20162eca 100644 --- a/lib/features/recipes/presentation/pages/about_recipes_screen.dart +++ b/lib/features/recipes/presentation/pages/about_recipes_screen.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/features/feed/models/recipes_model.dart' as step; import 'package:eatvio_app/features/feed/models/recipes_model.dart'; @@ -13,7 +14,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:go_router/go_router.dart'; -import 'package:rive/rive.dart'; class AboutRecipesScreen extends StatefulWidget { final Recipes recipes; @@ -348,13 +348,7 @@ class _AboutRecipesScreenState extends State { if (state is CommentsStateLoading) Padding( padding: const EdgeInsets.all(32), - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), + child: const Loader(), ), if (state is CommentsStateLoaded) CommentsWidget( diff --git a/lib/features/recipes/presentation/pages/recipe_create_screen.dart b/lib/features/recipes/presentation/pages/recipe_create_screen.dart index 6c7b51fc..08a0dd94 100644 --- a/lib/features/recipes/presentation/pages/recipe_create_screen.dart +++ b/lib/features/recipes/presentation/pages/recipe_create_screen.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:eatvio_app/core/theme/app_decoration.dart'; import 'package:eatvio_app/core/widgets/custom_drop_down.dart'; +import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/features/recipes/models/product.dart'; import 'package:eatvio_app/features/recipes/models/ingredient.dart'; import 'package:eatvio_app/features/recipes/models/recipe_model.dart'; @@ -15,7 +16,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:image_picker/image_picker.dart'; -import 'package:rive/rive.dart' as rive; class RecipeCreateScreen extends StatelessWidget { RecipeCreateScreen({Key? key}) : super(key: key); @@ -29,15 +29,7 @@ class RecipeCreateScreen extends StatelessWidget { return _buildRecipeCreate(context, state); } else if (state is RecipeCreateStateLoading) { return Scaffold( - body: Center( - child: SizedBox( - height: 32, - width: 32, - child: rive.RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ), + body: const Loader(), ); } else if (state is RecipeCreateStateError) { return Scaffold( diff --git a/lib/features/recipes/presentation/pages/recipe_edit_screen.dart b/lib/features/recipes/presentation/pages/recipe_edit_screen.dart index 0f0c88f8..644b581d 100644 --- a/lib/features/recipes/presentation/pages/recipe_edit_screen.dart +++ b/lib/features/recipes/presentation/pages/recipe_edit_screen.dart @@ -1,6 +1,7 @@ import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/theme/app_decoration.dart'; import 'package:eatvio_app/core/widgets/custom_drop_down.dart'; +import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/features/recipes/models/product.dart'; import 'package:eatvio_app/features/recipes/models/ingredient.dart'; import 'package:eatvio_app/features/recipes/models/recipe_model.dart'; @@ -14,7 +15,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:image_picker/image_picker.dart'; -import 'package:rive/rive.dart' as rive; // ignore: must_be_immutable class RecipeEditScreen extends StatelessWidget { @@ -35,15 +35,7 @@ class RecipeEditScreen extends StatelessWidget { return _buildRecipeCreate(context, state); } else if (state is RecipeCreateStateLoading) { return Scaffold( - body: Center( - child: SizedBox( - height: 32, - width: 32, - child: rive.RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ), + body: Loader(), ); } else if (state is RecipeCreateStateError) { return Scaffold( diff --git a/lib/features/registration/presentation/pages/registration_screen.dart b/lib/features/registration/presentation/pages/registration_screen.dart index c36bd18e..9c44163e 100644 --- a/lib/features/registration/presentation/pages/registration_screen.dart +++ b/lib/features/registration/presentation/pages/registration_screen.dart @@ -1,4 +1,5 @@ import 'package:eatvio_app/core/utils/image_constant.dart'; +import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/features/registration/presentation/bloc/registration_cubit/registration_cubit.dart'; import 'package:eatvio_app/features/registration/presentation/bloc/registration_cubit/registration_state.dart'; import 'package:eatvio_app/features/registration/presentation/pages/additional_information_screen.dart'; @@ -23,7 +24,6 @@ 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:rive/rive.dart' as rive; import 'package:validators/validators.dart'; class RegistrationScreen extends StatefulWidget { @@ -322,15 +322,7 @@ class _RegistrationScreenState extends State { context: context, builder: (context) => PopScope( canPop: false, - child: Center( - child: SizedBox( - height: 32, - width: 32, - child: rive.RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ), + child: const Loader(), ), ); await registrationCubit.tryRegistration(); diff --git a/lib/features/settings/presentation/pages/settings_screen.dart b/lib/features/settings/presentation/pages/settings_screen.dart index 0cf68c10..cccb16b7 100644 --- a/lib/features/settings/presentation/pages/settings_screen.dart +++ b/lib/features/settings/presentation/pages/settings_screen.dart @@ -1,4 +1,5 @@ import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/features/settings/entities/settings_entity.dart'; import 'package:eatvio_app/features/settings/presentation/bloc/settings_cubit/settings_cubit.dart'; import 'package:eatvio_app/features/settings/presentation/bloc/settings_cubit/settings_state.dart'; @@ -8,7 +9,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; -import 'package:rive/rive.dart'; import 'package:url_launcher/url_launcher.dart'; class SettingsScreen extends StatefulWidget { @@ -76,15 +76,7 @@ class _SettingsScreenState extends State { ); } return Scaffold( - body: Center( - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ), + body: const Loader(), ); }, ), diff --git a/pubspec.yaml b/pubspec.yaml index 105c362a..5b8ed7b4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: eatvio_app description: A new Flutter project. -version: 1.0.2+17 +version: 1.0.4+19 publish_to: "none" environment: