diff --git a/assets/svgs/change_language_icon.svg b/assets/svgs/change_language_icon.svg new file mode 100644 index 0000000..99b4d15 --- /dev/null +++ b/assets/svgs/change_language_icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/svgs/change_password_icon.svg b/assets/svgs/change_password_icon.svg new file mode 100644 index 0000000..0e8ff3f --- /dev/null +++ b/assets/svgs/change_password_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svgs/help_icon.svg b/assets/svgs/help_icon.svg new file mode 100644 index 0000000..d0ca6bf --- /dev/null +++ b/assets/svgs/help_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svgs/lock_settings_icon.svg b/assets/svgs/lock_settings_icon.svg new file mode 100644 index 0000000..8fdb55f --- /dev/null +++ b/assets/svgs/lock_settings_icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/svgs/logout_icon.svg b/assets/svgs/logout_icon.svg new file mode 100644 index 0000000..0b323cc --- /dev/null +++ b/assets/svgs/logout_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svgs/security_warning_icon.svg b/assets/svgs/security_warning_icon.svg new file mode 100644 index 0000000..6f27036 --- /dev/null +++ b/assets/svgs/security_warning_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/translations/en.json b/assets/translations/en.json index 4d38970..01e57c9 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -96,7 +96,7 @@ "Logout": "Logout", "PrivacyPolicy": "Privacy Policy", "EditProfile": "Edit Profile", - "AreYouSureToCloseTheApplication": "Are you sure to close the \napplication?", + "AreYouSureToCloseTheApplication": "Are You Sure To Close The \nApplication?", "Yes": "Yes", "No": "No", "TapToEdit": "tap to edit", diff --git a/lib/core/assets/app_icons.dart b/lib/core/assets/app_icons.dart index 4a4d76c..5cd48d9 100644 --- a/lib/core/assets/app_icons.dart +++ b/lib/core/assets/app_icons.dart @@ -1,5 +1,6 @@ class AppIcons { AppIcons._(); + static const String fitnessOne = 'assets/svgs/fitness_1.svg'; static const String fitnessTwo = 'assets/svgs/fitness_2.svg'; static const String fitnessThree = 'assets/svgs/fitness_3.svg'; @@ -38,4 +39,13 @@ class AppIcons { static const String profileIcon = 'assets/svgs/profile_icon.svg'; static const String chatIcon = 'assets/svgs/chat_icon.svg'; static const String workoutIcon = 'assets/svgs/workout_icon.svg'; + static const String changePasswordIcon = + 'assets/svgs/change_password_icon.svg'; + static const String logoutIcon = 'assets/svgs/logout_icon.svg'; + static const String helpIcon = 'assets/svgs/help_icon.svg'; + static const String lockSettingsIcon = 'assets/svgs/lock_settings_icon.svg'; + static const String securityWarningIcon = + 'assets/svgs/security_warning_icon.svg'; + static const String changeLanguageIcon = + 'assets/svgs/change_language_icon.svg'; } diff --git a/lib/core/utils/di/di.config.dart b/lib/core/utils/di/di.config.dart index 44435b9..95ae64e 100644 --- a/lib/core/utils/di/di.config.dart +++ b/lib/core/utils/di/di.config.dart @@ -37,8 +37,11 @@ import '../../../data/home/data_source/remote/home_remote_data_source_impl.dart' import '../../../data/home/repo_impl/home_repo_impl.dart' as _i779; import '../../../domain/auth/repo/auth_repo.dart' as _i1047; import '../../../domain/auth/use_case/forget_password_use_case.dart' as _i728; +import '../../../domain/auth/use_case/get_profile_data_use_case.dart' as _i336; +import '../../../domain/auth/use_case/logout_use_case.dart' as _i1046; import '../../../domain/auth/use_case/otp_verification_use_case.dart' as _i777; import '../../../domain/auth/use_case/reset_password_use_case.dart' as _i55; +import '../../../domain/auth/use_case/select_language_use_case.dart' as _i753; import '../../../domain/home/repo/home_repo.dart' as _i81; import '../../../domain/home/use_case/get_all_muscles_use_case.dart' as _i840; import '../../../domain/home/use_case/get_daily_recommendation_exercise_use_case.dart' @@ -59,6 +62,8 @@ import '../../../features/onBoarding/presentation/view_model/cubit/on_boarding_c as _i485; import '../../../features/otp_verification/presentation/view_model/cubit/otp_verification_cubit.dart' as _i662; +import '../../../features/profile/presentation/view_model/profile_cubit.dart' + as _i782; import '../../../features/reset_password/presentation/view_model/cubit/reset_password_cubit.dart' as _i893; import '../../functions/inital_route_function.dart' as _i420; @@ -97,15 +102,18 @@ extension GetItInjectableX on _i174.GetIt { gh.singleton<_i649.BlocObserverService>( () => _i649.BlocObserverService(gh<_i974.Logger>()), ); - gh.factory<_i1063.AuthLocalDataSource>( - () => _i757.AuthLocalDataSourceImpl(gh<_i558.FlutterSecureStorage>()), - ); gh.factory<_i420.RouteInitializer>( () => _i420.RouteInitializer( flutterSecureStorage: gh<_i558.FlutterSecureStorage>(), sharedPreferences: gh<_i460.SharedPreferences>(), ), ); + gh.factory<_i1063.AuthLocalDataSource>( + () => _i757.AuthLocalDataSourceImpl( + gh<_i460.SharedPreferences>(), + gh<_i558.FlutterSecureStorage>(), + ), + ); gh.factory<_i368.HomeLocalDataSource>( () => _i410.HomeLocalDataSourceImpl(), ); @@ -127,10 +135,28 @@ extension GetItInjectableX on _i174.GetIt { gh.factory<_i1047.AuthRepo>( () => _i15.AuthRepoImpl( gh<_i28.ApiManager>(), - gh<_i774.AuthRemoteDataSource>(), gh<_i1063.AuthLocalDataSource>(), + gh<_i774.AuthRemoteDataSource>(), ), ); + gh.factory<_i728.ForgetPasswordUseCase>( + () => _i728.ForgetPasswordUseCase(gh<_i1047.AuthRepo>()), + ); + gh.factory<_i336.GetProfileDataUseCase>( + () => _i336.GetProfileDataUseCase(gh<_i1047.AuthRepo>()), + ); + gh.factory<_i1046.LogoutUseCase>( + () => _i1046.LogoutUseCase(gh<_i1047.AuthRepo>()), + ); + gh.factory<_i777.OtpVerificationUseCase>( + () => _i777.OtpVerificationUseCase(gh<_i1047.AuthRepo>()), + ); + gh.factory<_i55.ResetPasswordUseCase>( + () => _i55.ResetPasswordUseCase(gh<_i1047.AuthRepo>()), + ); + gh.factory<_i753.SelectLanguageUseCase>( + () => _i753.SelectLanguageUseCase(gh<_i1047.AuthRepo>()), + ); gh.factory<_i81.HomeRepo>( () => _i779.HomeRepoImpl( gh<_i958.HomeRemoteDataSource>(), @@ -138,14 +164,30 @@ extension GetItInjectableX on _i174.GetIt { gh<_i28.ApiManager>(), ), ); - gh.factory<_i728.ForgetPasswordUseCase>( - () => _i728.ForgetPasswordUseCase(gh<_i1047.AuthRepo>()), + gh.factory<_i782.ProfileCubit>( + () => _i782.ProfileCubit( + gh<_i336.GetProfileDataUseCase>(), + gh<_i1046.LogoutUseCase>(), + gh<_i753.SelectLanguageUseCase>(), + ), ); - gh.factory<_i777.OtpVerificationUseCase>( - () => _i777.OtpVerificationUseCase(gh<_i1047.AuthRepo>()), + gh.factory<_i662.OtpVerificationCubit>( + () => _i662.OtpVerificationCubit( + gh<_i777.OtpVerificationUseCase>(), + gh<_i728.ForgetPasswordUseCase>(), + ), ); - gh.factory<_i55.ResetPasswordUseCase>( - () => _i55.ResetPasswordUseCase(gh<_i1047.AuthRepo>()), + gh.factory<_i893.ResetPasswordCubit>( + () => _i893.ResetPasswordCubit( + gh<_i55.ResetPasswordUseCase>(), + gh<_i468.Validator>(), + ), + ); + gh.factory<_i70.ForgetPasswordCubit>( + () => _i70.ForgetPasswordCubit( + gh<_i728.ForgetPasswordUseCase>(), + gh<_i468.Validator>(), + ), ); gh.factory<_i840.GetAllMusclesUseCase>( () => _i840.GetAllMusclesUseCase(gh<_i81.HomeRepo>()), @@ -171,24 +213,6 @@ extension GetItInjectableX on _i174.GetIt { gh<_i840.GetAllMusclesUseCase>(), ), ); - gh.factory<_i662.OtpVerificationCubit>( - () => _i662.OtpVerificationCubit( - gh<_i777.OtpVerificationUseCase>(), - gh<_i728.ForgetPasswordUseCase>(), - ), - ); - gh.factory<_i893.ResetPasswordCubit>( - () => _i893.ResetPasswordCubit( - gh<_i55.ResetPasswordUseCase>(), - gh<_i468.Validator>(), - ), - ); - gh.factory<_i70.ForgetPasswordCubit>( - () => _i70.ForgetPasswordCubit( - gh<_i728.ForgetPasswordUseCase>(), - gh<_i468.Validator>(), - ), - ); return this; } } diff --git a/lib/core/utils/dialogs/app_dialogs.dart b/lib/core/utils/dialogs/app_dialogs.dart index 2513b0d..2d0808d 100644 --- a/lib/core/utils/dialogs/app_dialogs.dart +++ b/lib/core/utils/dialogs/app_dialogs.dart @@ -191,4 +191,77 @@ class AppDialogs { ), ); } + + // Show a confirmation dialog + static Future showConfirmationDialog( + BuildContext context, { + required String message, + String? description, + String? firstButtonText, + String? secondButtonText, + VoidCallback? firstButtonAction, + VoidCallback? secondButtonAction, + }) async { + await showDialog( + context: context, + builder: (context) => AlertDialog( + backgroundColor: AppColors.darkgrey, + titlePadding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 0), + actionsPadding: const EdgeInsets.all(16.0), + titleTextStyle: Theme.of(context).textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.w600, + fontSize: 20, + ), + content: description != null + ? Text(description, textAlign: TextAlign.center, maxLines: 2) + : null, + contentPadding: const EdgeInsets.only( + left: 55, + right: 55, + top: 20, + bottom: 35, + ), + contentTextStyle: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: AppColors.white[AppColors.colorCode40], + fontWeight: FontWeight.w500, + ), + actions: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (firstButtonText != null) + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.darkgrey, + side: const BorderSide(color: AppColors.orange), + ), + onPressed: firstButtonAction ?? () => Navigator.pop(context), + child: Text( + firstButtonText, + style: Theme.of(context).textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.w800, + fontSize: 14, + ), + ), + ), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.orange, + ), + onPressed: secondButtonAction ?? () => Navigator.pop(context), + child: Text( + secondButtonText ?? LocaleKeys.Ok.tr(), + style: Theme.of(context).textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.w800, + fontSize: 14, + ), + ), + ), + ], + ), + ], + title: Text(message, textAlign: TextAlign.center), + ), + ); + } } diff --git a/lib/core/utils/l10n/locale_keys.g.dart b/lib/core/utils/l10n/locale_keys.g.dart index 50df9ae..bd81035 100644 --- a/lib/core/utils/l10n/locale_keys.g.dart +++ b/lib/core/utils/l10n/locale_keys.g.dart @@ -2,7 +2,7 @@ // ignore_for_file: constant_identifier_names -abstract class LocaleKeys { +abstract class LocaleKeys { static const InvalidCredentials = 'InvalidCredentials'; static const Receive_timeout = 'Receive_timeout'; static const Timeout_occurred = 'Timeout_occurred'; @@ -23,10 +23,8 @@ abstract class LocaleKeys { static const Service_unavailable = 'Service_unavailable'; static const DataParsingException = 'DataParsingException'; static const OtpCodeIsInvalidOrExpired = 'OtpCodeIsInvalidOrExpired'; - static const ThePriceOfExcellenceIsDiscipline = - 'ThePriceOfExcellenceIsDiscipline'; - static const LoremIpsumDolorSitAmetConsecteturEuUrnaUtGravidaQuisIdPretiumPurusMaurIsMassa = - 'LoremIpsumDolorSitAmetConsecteturEuUrnaUtGravidaQuisIdPretiumPurusMaurIsMassa'; + static const ThePriceOfExcellenceIsDiscipline = 'ThePriceOfExcellenceIsDiscipline'; + static const LoremIpsumDolorSitAmetConsecteturEuUrnaUtGravidaQuisIdPretiumPurusMaurIsMassa = 'LoremIpsumDolorSitAmetConsecteturEuUrnaUtGravidaQuisIdPretiumPurusMaurIsMassa'; static const FitnessHasNeverBeenSoMuchFun = 'FitnessHasNeverBeenSoMuchFun'; static const NOMOREEXCUSESDoItNow = 'NOMOREEXCUSESDoItNow'; static const Back = 'Back'; @@ -45,17 +43,14 @@ abstract class LocaleKeys { static const FirstName = 'FirstName'; static const LastName = 'LastName'; static const AlreadyHaveAnAccount = 'AlreadyHaveAnAccount'; - static const TellUsAboutYourselfWeNeedToKnowYourGender = - 'TellUsAboutYourselfWeNeedToKnowYourGender'; + static const TellUsAboutYourselfWeNeedToKnowYourGender = 'TellUsAboutYourselfWeNeedToKnowYourGender'; static const Male = 'Male'; static const Female = 'Female'; static const Year = 'Year'; - static const HowOldAreYouThisHelpsUsCreateYourPersonalizedPlan = - 'HowOldAreYouThisHelpsUsCreateYourPersonalizedPlan'; + static const HowOldAreYouThisHelpsUsCreateYourPersonalizedPlan = 'HowOldAreYouThisHelpsUsCreateYourPersonalizedPlan'; static const Done = 'Done'; static const WhatIsYourWeight = 'WhatIsYourWeight'; - static const ThisHelpsUsCreateYourPersonalizedPlan = - 'ThisHelpsUsCreateYourPersonalizedPlan'; + static const ThisHelpsUsCreateYourPersonalizedPlan = 'ThisHelpsUsCreateYourPersonalizedPlan'; static const WhatIsYourHight = 'WhatIsYourHight'; static const whatIsYourGoal = 'whatIsYourGoal'; static const GainWeight = 'GainWeight'; @@ -68,8 +63,7 @@ abstract class LocaleKeys { static const Intermediate = 'Intermediate'; static const Advanced = 'Advanced'; static const TrueBeast = 'TrueBeast'; - static const YourRegularPhysicalActivityLevel = - 'YourRegularPhysicalActivityLevel'; + static const YourRegularPhysicalActivityLevel = 'YourRegularPhysicalActivityLevel'; static const EnterYourEmail = 'EnterYourEmail'; static const EnterYourPassword = 'EnterYourPassword'; static const SendOTP = 'SendOTP'; @@ -104,8 +98,7 @@ abstract class LocaleKeys { static const Logout = 'Logout'; static const PrivacyPolicy = 'PrivacyPolicy'; static const EditProfile = 'EditProfile'; - static const AreYouSureToCloseTheApplication = - 'AreYouSureToCloseTheApplication'; + static const AreYouSureToCloseTheApplication = 'AreYouSureToCloseTheApplication'; static const Yes = 'Yes'; static const No = 'No'; static const TapToEdit = 'TapToEdit'; @@ -122,8 +115,7 @@ abstract class LocaleKeys { static const PasswordCannotBeEmpty = 'PasswordCannotBeEmpty'; static const InvalidPassword = 'InvalidPassword'; static const InvalidEmailFormat = 'InvalidEmailFormat'; - static const PasswordMustBeAtLeast8Characters = - 'PasswordMustBeAtLeast8Characters'; + static const PasswordMustBeAtLeast8Characters = 'PasswordMustBeAtLeast8Characters'; static const NameCannotBeEmpty = 'NameCannotBeEmpty'; static const InvalidName = 'InvalidName'; static const EnterAValidEmail = 'EnterAValidEmail'; @@ -138,9 +130,9 @@ abstract class LocaleKeys { static const ResetPasswordSuccessfully = 'ResetPasswordSuccessfully'; static const ConfirmPassword = 'ConfirmPassword'; static const OtpVerificationSuccessfully = 'OtpVerificationSuccessfully'; - static const SomethingWentWrongPleaseTryAgainLater = - 'SomethingWentWrongPleaseTryAgainLater'; + static const SomethingWentWrongPleaseTryAgainLater = 'SomethingWentWrongPleaseTryAgainLater'; static const Home = 'Home'; static const FitnessAI = 'FitnessAI'; static const DoIt = 'DoIt'; + } diff --git a/lib/core/utils/routes/app_routes.dart b/lib/core/utils/routes/app_routes.dart index 21a772d..b24723a 100644 --- a/lib/core/utils/routes/app_routes.dart +++ b/lib/core/utils/routes/app_routes.dart @@ -9,7 +9,7 @@ import '../../../features/home/presentation/view/home_screen.dart'; import '../../../features/chat_bot/presentation/view/chat_bot_screen.dart'; import '../../../features/main_layout/presentation/view/main_layout_screen.dart'; -import '../../../features/profile/presentation/view/profile_screen.dart'; +import '../../../features/profile/presentation/view/screens/profile_screen.dart'; import '../../../features/workouts/presentation/view/workouts_screen.dart'; class AppRoutes { @@ -23,17 +23,17 @@ class AppRoutes { static const String profileRoute = '/profile'; static const String editProfileRoute = '/edit-profile'; static const String onBoardingRoute = '/on-boarding'; + static const String workoutsRoute = '/workouts'; static const String otpVerificationRoute = '/otp-verification'; static const String resetPasswordRoute = '/reset-password'; - static const String workoutRoute = '/workout'; static const String chatBotRoute = 'chat-bot'; static Map routes = { onBoardingRoute: (context) => const OnBoardingScreen(), homeRoute: (context) => const HomeScreen(), mainLayoutRoute: (context) => const MainLayoutScreen(), - profileRoute: (context) => const ProfileScreen(), - workoutRoute: (context) => const WorkoutsScreen(), + profileRoute: (context) => ProfileScreen(), + workoutsRoute: (context) => const WorkoutsScreen(), chatBotRoute: (context) => const ChatBotScreen(), forgetPasswordRoute: (context) => const ForgetPasswordScreen(), otpVerificationRoute: (context) { diff --git a/lib/core/utils/shared_widgets/blured_container.dart b/lib/core/utils/shared_widgets/blured_container.dart index 0a6acc7..aa4868d 100644 --- a/lib/core/utils/shared_widgets/blured_container.dart +++ b/lib/core/utils/shared_widgets/blured_container.dart @@ -1,12 +1,14 @@ import 'dart:ui'; -import 'package:fitness_app/core/assets/app_colors.dart'; import 'package:flutter/material.dart'; +import '../../assets/app_colors.dart'; + class BluredContainer extends StatelessWidget { - const BluredContainer({super.key, required this.child}); + const BluredContainer({super.key, required this.child, this.padding}); final Widget child; + final EdgeInsets? padding; @override Widget build(BuildContext context) { @@ -15,8 +17,10 @@ class BluredContainer extends StatelessWidget { child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15), child: AnimatedContainer( - padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 16), - color: AppColors.containerBackGround, + padding: + padding ?? + const EdgeInsets.symmetric(vertical: 24, horizontal: 16), + color: AppColors.darkgrey.withAlpha(150), duration: const Duration(milliseconds: 3000), child: child, ), diff --git a/lib/data/auth/api/auth_retrofit_client.dart b/lib/data/auth/api/auth_retrofit_client.dart index 941f07f..1698207 100644 --- a/lib/data/auth/api/auth_retrofit_client.dart +++ b/lib/data/auth/api/auth_retrofit_client.dart @@ -1,4 +1,6 @@ import 'package:dio/dio.dart'; +import 'package:fitness_app/data/auth/models/logout_response_dto.dart'; +import 'package:fitness_app/data/auth/models/user_data_response_dto.dart'; import 'package:injectable/injectable.dart'; import 'package:retrofit/retrofit.dart'; @@ -18,6 +20,12 @@ abstract class AuthRetrofitClient { @factoryMethod factory AuthRetrofitClient(Dio dio) = _AuthRetrofitClient; + @GET(ApiConstants.logoutRoute) + Future logout(); + + @GET(ApiConstants.profileDataRoute) + Future getProfileData(); + @POST(ApiConstants.forgetPasswordRoute) Future forgetPassword( @Body() ForgetPasswordRequestDto request, diff --git a/lib/data/auth/api/auth_retrofit_client.g.dart b/lib/data/auth/api/auth_retrofit_client.g.dart index 92a048e..a56799e 100644 --- a/lib/data/auth/api/auth_retrofit_client.g.dart +++ b/lib/data/auth/api/auth_retrofit_client.g.dart @@ -19,6 +19,60 @@ class _AuthRetrofitClient implements AuthRetrofitClient { final ParseErrorLogger? errorLogger; + @override + Future logout() async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType( + Options(method: 'GET', headers: _headers, extra: _extra) + .compose( + _dio.options, + 'auth/logout', + queryParameters: queryParameters, + data: _data, + ) + .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), + ); + final _result = await _dio.fetch>(_options); + late LogoutResponseDto _value; + try { + _value = LogoutResponseDto.fromJson(_result.data!); + } on Object catch (e, s) { + errorLogger?.logError(e, s, _options); + rethrow; + } + return _value; + } + + @override + Future getProfileData() async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType( + Options(method: 'GET', headers: _headers, extra: _extra) + .compose( + _dio.options, + 'auth/profile-data', + queryParameters: queryParameters, + data: _data, + ) + .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), + ); + final _result = await _dio.fetch>(_options); + late UserDataResponseDto _value; + try { + _value = UserDataResponseDto.fromJson(_result.data!); + } on Object catch (e, s) { + errorLogger?.logError(e, s, _options); + rethrow; + } + return _value; + } + @override Future forgetPassword( ForgetPasswordRequestDto request, diff --git a/lib/data/auth/data_source/contract/auth_local_data_source.dart b/lib/data/auth/data_source/contract/auth_local_data_source.dart index e96c6cf..0f5c4f2 100644 --- a/lib/data/auth/data_source/contract/auth_local_data_source.dart +++ b/lib/data/auth/data_source/contract/auth_local_data_source.dart @@ -1,4 +1,8 @@ abstract class AuthLocalDataSource { + Future clearAll(); + + Future selectLanguage(String languageCode); + Future saveToken(String key, String value); Future getToken(String key); diff --git a/lib/data/auth/data_source/contract/auth_remote_data_source.dart b/lib/data/auth/data_source/contract/auth_remote_data_source.dart index 7dd179e..9e4ebf0 100644 --- a/lib/data/auth/data_source/contract/auth_remote_data_source.dart +++ b/lib/data/auth/data_source/contract/auth_remote_data_source.dart @@ -1,3 +1,5 @@ +import 'package:fitness_app/data/auth/models/logout_response_dto.dart'; +import 'package:fitness_app/data/auth/models/user_data_response_dto.dart'; import '../../models/forget_password/request/forget_password_request_dto.dart'; import '../../models/forget_password/response/forget_password_response_dto.dart'; import '../../models/otp_verification/request/otp_verification_request_dto.dart'; @@ -6,6 +8,10 @@ import '../../models/reset_password/request/reset_password_request_dto.dart'; import '../../models/reset_password/response/reset_password_response_dto.dart'; abstract class AuthRemoteDataSource { + Future getProfileData(); + + Future logout(); + Future forgetPassword( ForgetPasswordRequestDto request, ); diff --git a/lib/data/auth/data_source/local/auth_local_data_source_impl.dart b/lib/data/auth/data_source/local/auth_local_data_source_impl.dart index 8e701e8..b2fa7ac 100644 --- a/lib/data/auth/data_source/local/auth_local_data_source_impl.dart +++ b/lib/data/auth/data_source/local/auth_local_data_source_impl.dart @@ -1,13 +1,29 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:injectable/injectable.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import '../../../../core/utils/constants.dart'; import '../contract/auth_local_data_source.dart'; @Injectable(as: AuthLocalDataSource) class AuthLocalDataSourceImpl implements AuthLocalDataSource { + final SharedPreferences _sharedPreferences; final FlutterSecureStorage _flutterSecureStorage; - AuthLocalDataSourceImpl(this._flutterSecureStorage); + AuthLocalDataSourceImpl(this._sharedPreferences, this._flutterSecureStorage); + + @override + Future clearAll() async { + return await _flutterSecureStorage.deleteAll(); + } + + @override + Future selectLanguage(String languageCode) async { + return await _sharedPreferences.setString( + Constants.selectedLanguageCode, + languageCode, + ); + } @override Future deleteToken(String key) async { diff --git a/lib/data/auth/data_source/remote/auth_remote_data_source_impl.dart b/lib/data/auth/data_source/remote/auth_remote_data_source_impl.dart index 78dcfdc..da31bce 100644 --- a/lib/data/auth/data_source/remote/auth_remote_data_source_impl.dart +++ b/lib/data/auth/data_source/remote/auth_remote_data_source_impl.dart @@ -1,6 +1,8 @@ +import 'package:fitness_app/data/auth/api/auth_retrofit_client.dart'; +import 'package:fitness_app/data/auth/models/logout_response_dto.dart'; +import 'package:fitness_app/data/auth/models/user_data_response_dto.dart'; import 'package:injectable/injectable.dart'; -import '../../api/auth_retrofit_client.dart'; import '../../models/forget_password/request/forget_password_request_dto.dart'; import '../../models/forget_password/response/forget_password_response_dto.dart'; import '../../models/otp_verification/request/otp_verification_request_dto.dart'; @@ -12,8 +14,19 @@ import '../contract/auth_remote_data_source.dart'; @Injectable(as: AuthRemoteDataSource) class AuthRemoteDataSourceImpl implements AuthRemoteDataSource { final AuthRetrofitClient _authRetrofitClient; + AuthRemoteDataSourceImpl(this._authRetrofitClient); + @override + Future getProfileData() async { + return await _authRetrofitClient.getProfileData(); + } + + @override + Future logout() async { + return await _authRetrofitClient.logout(); + } + @override Future forgetPassword( ForgetPasswordRequestDto request, diff --git a/lib/data/auth/models/logout_response_dto.dart b/lib/data/auth/models/logout_response_dto.dart new file mode 100644 index 0000000..122e5d1 --- /dev/null +++ b/lib/data/auth/models/logout_response_dto.dart @@ -0,0 +1,19 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'logout_response_dto.g.dart'; + +@JsonSerializable() +class LogoutResponseDto { + @JsonKey(name: "message") + final String? message; + + LogoutResponseDto({this.message}); + + factory LogoutResponseDto.fromJson(Map json) { + return _$LogoutResponseDtoFromJson(json); + } + + Map toJson() { + return _$LogoutResponseDtoToJson(this); + } +} diff --git a/lib/data/auth/models/logout_response_dto.g.dart b/lib/data/auth/models/logout_response_dto.g.dart new file mode 100644 index 0000000..df9754d --- /dev/null +++ b/lib/data/auth/models/logout_response_dto.g.dart @@ -0,0 +1,13 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'logout_response_dto.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +LogoutResponseDto _$LogoutResponseDtoFromJson(Map json) => + LogoutResponseDto(message: json['message'] as String?); + +Map _$LogoutResponseDtoToJson(LogoutResponseDto instance) => + {'message': instance.message}; diff --git a/lib/data/auth/models/user_data_response_dto.dart b/lib/data/auth/models/user_data_response_dto.dart new file mode 100644 index 0000000..3d1e17c --- /dev/null +++ b/lib/data/auth/models/user_data_response_dto.dart @@ -0,0 +1,22 @@ +import 'package:fitness_app/data/auth/models/user_dto.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'user_data_response_dto.g.dart'; + +@JsonSerializable() +class UserDataResponseDto { + @JsonKey(name: "message") + final String? message; + @JsonKey(name: "user") + final UserDto? user; + + UserDataResponseDto({this.message, this.user}); + + factory UserDataResponseDto.fromJson(Map json) { + return _$UserDataResponseDtoFromJson(json); + } + + Map toJson() { + return _$UserDataResponseDtoToJson(this); + } +} diff --git a/lib/data/auth/models/user_data_response_dto.g.dart b/lib/data/auth/models/user_data_response_dto.g.dart new file mode 100644 index 0000000..ded4a1d --- /dev/null +++ b/lib/data/auth/models/user_data_response_dto.g.dart @@ -0,0 +1,19 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'user_data_response_dto.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +UserDataResponseDto _$UserDataResponseDtoFromJson(Map json) => + UserDataResponseDto( + message: json['message'] as String?, + user: json['user'] == null + ? null + : UserDto.fromJson(json['user'] as Map), + ); + +Map _$UserDataResponseDtoToJson( + UserDataResponseDto instance, +) => {'message': instance.message, 'user': instance.user}; diff --git a/lib/data/auth/models/user_dto.dart b/lib/data/auth/models/user_dto.dart new file mode 100644 index 0000000..91280d2 --- /dev/null +++ b/lib/data/auth/models/user_dto.dart @@ -0,0 +1,70 @@ +import 'package:fitness_app/domain/auth/entity/user_entity.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'user_dto.g.dart'; + +@JsonSerializable() +class UserDto { + @JsonKey(name: "_id") + final String? id; + @JsonKey(name: "firstName") + final String? firstName; + @JsonKey(name: "lastName") + final String? lastName; + @JsonKey(name: "email") + final String? email; + @JsonKey(name: "gender") + final String? gender; + @JsonKey(name: "age") + final int? age; + @JsonKey(name: "weight") + final int? weight; + @JsonKey(name: "height") + final int? height; + @JsonKey(name: "activityLevel") + final String? activityLevel; + @JsonKey(name: "goal") + final String? goal; + @JsonKey(name: "photo") + final String? photo; + @JsonKey(name: "createdAt") + final String? createdAt; + + UserDto({ + this.id, + this.firstName, + this.lastName, + this.email, + this.gender, + this.age, + this.weight, + this.height, + this.activityLevel, + this.goal, + this.photo, + this.createdAt, + }); + + factory UserDto.fromJson(Map json) { + return _$UserDtoFromJson(json); + } + + Map toJson() { + return _$UserDtoToJson(this); + } + + UserEntity toEntity() { + return UserEntity( + firstName: firstName, + lastName: lastName, + email: email, + gender: gender, + age: age, + weight: weight, + height: height, + activityLevel: activityLevel, + goal: goal, + photo: photo, + ); + } +} diff --git a/lib/data/auth/models/user_dto.g.dart b/lib/data/auth/models/user_dto.g.dart new file mode 100644 index 0000000..49c97ec --- /dev/null +++ b/lib/data/auth/models/user_dto.g.dart @@ -0,0 +1,37 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'user_dto.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +UserDto _$UserDtoFromJson(Map json) => UserDto( + id: json['_id'] as String?, + firstName: json['firstName'] as String?, + lastName: json['lastName'] as String?, + email: json['email'] as String?, + gender: json['gender'] as String?, + age: (json['age'] as num?)?.toInt(), + weight: (json['weight'] as num?)?.toInt(), + height: (json['height'] as num?)?.toInt(), + activityLevel: json['activityLevel'] as String?, + goal: json['goal'] as String?, + photo: json['photo'] as String?, + createdAt: json['createdAt'] as String?, +); + +Map _$UserDtoToJson(UserDto instance) => { + '_id': instance.id, + 'firstName': instance.firstName, + 'lastName': instance.lastName, + 'email': instance.email, + 'gender': instance.gender, + 'age': instance.age, + 'weight': instance.weight, + 'height': instance.height, + 'activityLevel': instance.activityLevel, + 'goal': instance.goal, + 'photo': instance.photo, + 'createdAt': instance.createdAt, +}; diff --git a/lib/data/auth/repo_impl/auth_repo_impl.dart b/lib/data/auth/repo_impl/auth_repo_impl.dart index 8ec3d88..37f8b9e 100644 --- a/lib/data/auth/repo_impl/auth_repo_impl.dart +++ b/lib/data/auth/repo_impl/auth_repo_impl.dart @@ -1,8 +1,11 @@ +import 'package:fitness_app/core/utils/datasource_excution/api_manager.dart'; +import 'package:fitness_app/core/utils/datasource_excution/api_result.dart'; +import 'package:fitness_app/data/auth/data_source/contract/auth_local_data_source.dart'; +import 'package:fitness_app/data/auth/data_source/contract/auth_remote_data_source.dart'; +import 'package:fitness_app/domain/auth/entity/user_entity.dart'; import 'package:injectable/injectable.dart'; import '../../../core/utils/constants.dart'; -import '../../../core/utils/datasource_excution/api_manager.dart'; -import '../../../core/utils/datasource_excution/api_result.dart'; import '../../../domain/auth/entity/forget_password/forget_password_request_entity.dart'; import '../../../domain/auth/entity/forget_password/forget_password_response_entity.dart'; import '../../../domain/auth/entity/otp_verification/request/otp_verification_request_entity.dart'; @@ -10,8 +13,6 @@ import '../../../domain/auth/entity/otp_verification/response/otp_verification_r import '../../../domain/auth/entity/reset_password/request/reset_password_request_entity.dart'; import '../../../domain/auth/entity/reset_password/response/reset_password_response_entity.dart'; import '../../../domain/auth/repo/auth_repo.dart'; -import '../data_source/contract/auth_local_data_source.dart'; -import '../data_source/contract/auth_remote_data_source.dart'; import '../models/forget_password/request/forget_password_request_dto.dart'; import '../models/otp_verification/request/otp_verification_request_dto.dart'; import '../models/reset_password/request/reset_password_request_dto.dart'; @@ -19,15 +20,42 @@ import '../models/reset_password/request/reset_password_request_dto.dart'; @Injectable(as: AuthRepo) class AuthRepoImpl implements AuthRepo { final ApiManager _apiManager; - final AuthRemoteDataSource _authRemoteDataSource; final AuthLocalDataSource _authLocalDataSource; + final AuthRemoteDataSource _authRemoteDataSource; AuthRepoImpl( this._apiManager, - this._authRemoteDataSource, this._authLocalDataSource, + this._authRemoteDataSource, ); + @override + Future> getProfileData() async { + final result = await _apiManager.execute(() async { + final response = await _authRemoteDataSource.getProfileData(); + return response.user!.toEntity(); + }); + return result; + } + + @override + Future> logout() async { + final result = await _apiManager.execute(() async { + await _authRemoteDataSource.logout(); + await _authLocalDataSource.clearAll(); + }); + return result; + } + + @override + Future> selectLanguage(String languageCode) async { + final result = await _apiManager.execute(() async { + final response = await _authLocalDataSource.selectLanguage(languageCode); + return response; + }); + return result; + } + @override Future> forgetPassword( ForgetPasswordRequestEntity request, diff --git a/lib/domain/auth/entity/user_entity.dart b/lib/domain/auth/entity/user_entity.dart new file mode 100644 index 0000000..f59b9f6 --- /dev/null +++ b/lib/domain/auth/entity/user_entity.dart @@ -0,0 +1,25 @@ +class UserEntity { + final String? firstName; + final String? lastName; + final String? email; + final String? gender; + final int? age; + final int? weight; + final int? height; + final String? activityLevel; + final String? goal; + final String? photo; + + UserEntity({ + this.firstName, + this.lastName, + this.email, + this.gender, + this.age, + this.weight, + this.height, + this.activityLevel, + this.goal, + this.photo, + }); +} diff --git a/lib/domain/auth/repo/auth_repo.dart b/lib/domain/auth/repo/auth_repo.dart index 53085e3..955df4f 100644 --- a/lib/domain/auth/repo/auth_repo.dart +++ b/lib/domain/auth/repo/auth_repo.dart @@ -1,4 +1,5 @@ -import '../../../core/utils/datasource_excution/api_result.dart'; +import 'package:fitness_app/core/utils/datasource_excution/api_result.dart'; +import 'package:fitness_app/domain/auth/entity/user_entity.dart'; import '../entity/forget_password/forget_password_request_entity.dart'; import '../entity/forget_password/forget_password_response_entity.dart'; import '../entity/otp_verification/request/otp_verification_request_entity.dart'; @@ -7,6 +8,12 @@ import '../entity/reset_password/request/reset_password_request_entity.dart'; import '../entity/reset_password/response/reset_password_response_entity.dart'; abstract class AuthRepo { + Future> getProfileData(); + + Future> logout(); + + Future> selectLanguage(String languageCode); + Future> forgetPassword( ForgetPasswordRequestEntity request, ); diff --git a/lib/domain/auth/use_case/get_profile_data_use_case.dart b/lib/domain/auth/use_case/get_profile_data_use_case.dart new file mode 100644 index 0000000..a1c998b --- /dev/null +++ b/lib/domain/auth/use_case/get_profile_data_use_case.dart @@ -0,0 +1,16 @@ +import 'package:fitness_app/domain/auth/repo/auth_repo.dart'; +import 'package:injectable/injectable.dart'; + +import '../../../core/utils/datasource_excution/api_result.dart'; +import '../entity/user_entity.dart'; + +@injectable +class GetProfileDataUseCase { + final AuthRepo _authRepo; + + GetProfileDataUseCase(this._authRepo); + + Future> call() async { + return await _authRepo.getProfileData(); + } +} diff --git a/lib/domain/auth/use_case/logout_use_case.dart b/lib/domain/auth/use_case/logout_use_case.dart new file mode 100644 index 0000000..d6c768e --- /dev/null +++ b/lib/domain/auth/use_case/logout_use_case.dart @@ -0,0 +1,14 @@ +import 'package:fitness_app/core/utils/datasource_excution/api_result.dart'; +import 'package:fitness_app/domain/auth/repo/auth_repo.dart'; +import 'package:injectable/injectable.dart'; + +@injectable +class LogoutUseCase { + final AuthRepo _authRepo; + + LogoutUseCase(this._authRepo); + + Future> call() async { + return await _authRepo.logout(); + } +} diff --git a/lib/domain/auth/use_case/select_language_use_case.dart b/lib/domain/auth/use_case/select_language_use_case.dart new file mode 100644 index 0000000..0de1d16 --- /dev/null +++ b/lib/domain/auth/use_case/select_language_use_case.dart @@ -0,0 +1,14 @@ +import 'package:fitness_app/core/utils/datasource_excution/api_result.dart'; +import 'package:fitness_app/domain/auth/repo/auth_repo.dart'; +import 'package:injectable/injectable.dart'; + +@injectable +class SelectLanguageUseCase { + final AuthRepo _authRepo; + + SelectLanguageUseCase(this._authRepo); + + Future> call(String languageCode) async { + return await _authRepo.selectLanguage(languageCode); + } +} diff --git a/lib/features/main_layout/presentation/view_model/cubit/main_layout_cubit.dart b/lib/features/main_layout/presentation/view_model/cubit/main_layout_cubit.dart index 54a219d..d5dd2de 100644 --- a/lib/features/main_layout/presentation/view_model/cubit/main_layout_cubit.dart +++ b/lib/features/main_layout/presentation/view_model/cubit/main_layout_cubit.dart @@ -4,7 +4,7 @@ import 'package:injectable/injectable.dart'; import '../../../../chat_bot/presentation/view/chat_bot_screen.dart'; import '../../../../home/presentation/view/home_screen.dart'; -import '../../../../profile/presentation/view/profile_screen.dart'; +import '../../../../profile/presentation/view/screens/profile_screen.dart'; import '../../../../workouts/presentation/view/workouts_screen.dart'; part 'main_layout_state.dart'; @@ -24,7 +24,7 @@ class MainLayoutCubit extends Cubit { MainLayoutTabs.home: () => const HomeScreen(), MainLayoutTabs.fitnessAI: () => const ChatBotScreen(), MainLayoutTabs.workouts: () => const WorkoutsScreen(), - MainLayoutTabs.profile: () => const ProfileScreen(), + MainLayoutTabs.profile: () => ProfileScreen(), }; } diff --git a/lib/features/profile/presentation/view/profile_screen.dart b/lib/features/profile/presentation/view/profile_screen.dart deleted file mode 100644 index 6ce8330..0000000 --- a/lib/features/profile/presentation/view/profile_screen.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:flutter/material.dart'; - -class ProfileScreen extends StatelessWidget { - const ProfileScreen({super.key}); - - @override - Widget build(BuildContext context) { - return const Scaffold( - body: Center(child: Text('Welcome to the Profile Screen!')), - ); - } -} diff --git a/lib/features/profile/presentation/view/screens/profile_screen.dart b/lib/features/profile/presentation/view/screens/profile_screen.dart new file mode 100644 index 0000000..a6e07a0 --- /dev/null +++ b/lib/features/profile/presentation/view/screens/profile_screen.dart @@ -0,0 +1,82 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:fitness_app/core/assets/app_images.dart'; +import 'package:fitness_app/core/utils/di/di.dart'; +import 'package:fitness_app/core/utils/dialogs/app_dialogs.dart'; +import 'package:fitness_app/core/utils/l10n/locale_keys.g.dart'; +import 'package:fitness_app/features/profile/presentation/view/widgets/profile_body.dart'; +import 'package:fitness_app/features/profile/presentation/view_model/profile_cubit.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../../../core/base/base_state.dart'; +import '../../../../../core/utils/routes/app_routes.dart'; +import '../../view_model/profile_state.dart'; + +class ProfileScreen extends StatelessWidget { + final ProfileCubit viewModel = getIt(); + + ProfileScreen({super.key}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => viewModel..doIntent(GetProfileAction()), + child: BlocListener( + listener: (context, state) { + final logoutState = state.logoutState; + if (state.profileState is BaseErrorState) { + AppDialogs.showFailureDialog( + context, + message: (state.profileState as BaseErrorState).errorMessage, + ); + Navigator.pushNamedAndRemoveUntil( + context, + AppRoutes.loginRoute, + (route) => false, + ); + } + if (logoutState is BaseSuccessState) { + AppDialogs.hideLoading(context); + Navigator.pushNamedAndRemoveUntil( + context, + AppRoutes.loginRoute, + (route) => false, + ); + } else if (logoutState is BaseErrorState) { + AppDialogs.showFailureDialog( + context, + message: logoutState.errorMessage, + ); + } + }, + child: Scaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + centerTitle: true, + title: Text( + LocaleKeys.Profile.tr(), + style: Theme.of(context).textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.w600, + fontSize: 24, + ), + ), + ), + extendBodyBehindAppBar: true, + body: Container( + width: double.infinity, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage(AppImages.backgroundThree), + fit: BoxFit.cover, + ), + ), + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), + child: ProfileBody(), + ), + ), + ), + ), + ); + } +} diff --git a/lib/features/profile/presentation/view/widgets/profile_body.dart b/lib/features/profile/presentation/view/widgets/profile_body.dart new file mode 100644 index 0000000..25e7ade --- /dev/null +++ b/lib/features/profile/presentation/view/widgets/profile_body.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import '../../../../../core/utils/shared_widgets/blured_container.dart'; +import 'profile_menu_list_widget.dart'; +import 'profile_header_widget.dart'; + +class ProfileBody extends StatelessWidget { + const ProfileBody({super.key}); + + @override + Widget build(BuildContext context) { + return const Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ProfileHeaderWidget(), + SizedBox(height: 40), + BluredContainer( + padding: EdgeInsets.symmetric(vertical: 8), + child: ProfileMenuList(), + ), + ], + ); + } +} diff --git a/lib/features/profile/presentation/view/widgets/profile_header_widget.dart b/lib/features/profile/presentation/view/widgets/profile_header_widget.dart new file mode 100644 index 0000000..d767141 --- /dev/null +++ b/lib/features/profile/presentation/view/widgets/profile_header_widget.dart @@ -0,0 +1,55 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:fitness_app/core/utils/l10n/locale_keys.g.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:skeletonizer/skeletonizer.dart'; + +import '../../../../../core/base/base_state.dart'; +import '../../../../../domain/auth/entity/user_entity.dart'; +import '../../view_model/profile_cubit.dart'; +import '../../view_model/profile_state.dart'; + +class ProfileHeaderWidget extends StatelessWidget { + const ProfileHeaderWidget({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + final profileState = state.profileState; + + final isLoading = profileState is BaseLoadingState; + final user = (profileState is BaseSuccessState) + ? profileState.data + : null; + + return Skeletonizer( + enabled: isLoading, + child: Column( + children: [ + CircleAvatar( + radius: 50, + backgroundImage: user?.photo != null + ? NetworkImage(user!.photo!) + : null, + child: user?.photo == null + ? const Icon(Icons.person, size: 40) + : null, + ), + const SizedBox(height: 8), + Text( + user != null + ? '${user.firstName ?? ''} ${user.lastName ?? ''}' + : LocaleKeys.Loading.tr(), + style: Theme.of(context).textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.w600, + fontSize: 20, + ), + ), + ], + ), + ); + }, + ); + } +} diff --git a/lib/features/profile/presentation/view/widgets/profile_menu_item_widget.dart b/lib/features/profile/presentation/view/widgets/profile_menu_item_widget.dart new file mode 100644 index 0000000..5815463 --- /dev/null +++ b/lib/features/profile/presentation/view/widgets/profile_menu_item_widget.dart @@ -0,0 +1,49 @@ +import 'package:fitness_app/core/assets/app_colors.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class ProfileMenuItemWidget extends StatelessWidget { + final String icon; + final String label; + final Widget? trailing; + final VoidCallback? onTap; + final Color? labelColor; + + const ProfileMenuItemWidget({ + super.key, + required this.icon, + required this.label, + this.trailing, + this.onTap, + this.labelColor, + }); + + @override + Widget build(BuildContext context) { + return ListTile( + leading: SvgPicture.asset( + icon, + width: 24, + height: 24, + colorFilter: const ColorFilter.mode(AppColors.orange, BlendMode.srcIn), + ), + title: Text( + label, + style: Theme.of(context).textTheme.labelLarge!.copyWith( + fontWeight: FontWeight.w600, + fontStyle: FontStyle.normal, + fontSize: 14, + color: labelColor ?? AppColors.white, + ), + ), + trailing: + trailing ?? + const Icon( + Icons.arrow_forward_ios, + color: AppColors.orange, + size: 16, + ), + onTap: onTap, + ); + } +} diff --git a/lib/features/profile/presentation/view/widgets/profile_menu_list_widget.dart b/lib/features/profile/presentation/view/widgets/profile_menu_list_widget.dart new file mode 100644 index 0000000..6a72db9 --- /dev/null +++ b/lib/features/profile/presentation/view/widgets/profile_menu_list_widget.dart @@ -0,0 +1,105 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:fitness_app/core/utils/dialogs/app_dialogs.dart'; +import 'package:fitness_app/features/profile/presentation/view_model/profile_cubit.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:skeletonizer/skeletonizer.dart'; + +import '../../../../../core/assets/app_colors.dart'; +import '../../../../../core/assets/app_icons.dart'; +import '../../../../../core/base/base_state.dart'; +import '../../../../../core/utils/constants.dart'; +import '../../../../../core/utils/l10n/locale_keys.g.dart'; +import '../../../../../core/utils/routes/app_routes.dart'; +import '../../view_model/profile_state.dart'; +import 'profile_menu_item_widget.dart'; + +class ProfileMenuList extends StatelessWidget { + const ProfileMenuList({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + final viewModel = context.read(); + final isLoading = state.profileState is BaseLoadingState; + + return Skeletonizer( + enabled: isLoading, + child: Column( + children: [ + ProfileMenuItemWidget( + icon: AppIcons.profileIcon, + label: LocaleKeys.EditProfile.tr(), + onTap: () => + Navigator.pushNamed(context, AppRoutes.editProfileRoute), + ), + ProfileMenuItemWidget( + icon: AppIcons.changePasswordIcon, + label: LocaleKeys.ChangePassword.tr(), + onTap: () {}, + ), + ProfileMenuItemWidget( + icon: AppIcons.changeLanguageIcon, + label: + '${LocaleKeys.SelectLanguage.tr()} (${context.locale.languageCode == Constants.en ? LocaleKeys.English.tr() : LocaleKeys.Arabic.tr()})', + trailing: Transform.scale( + scale: 0.8, + child: Switch( + padding: EdgeInsets.zero, + activeColor: AppColors.white, + activeTrackColor: AppColors.orange, + inactiveThumbColor: AppColors.white, + inactiveTrackColor: AppColors.darkgrey, + value: context.locale.languageCode == Constants.en, + onChanged: (value) { + viewModel.doIntent( + SelectLanguageAction( + value ? Constants.en : Constants.ar, + ), + ); + context.setLocale( + Locale(value ? Constants.en : Constants.ar), + ); + }, + ), + ), + ), + ProfileMenuItemWidget( + icon: AppIcons.lockSettingsIcon, + label: LocaleKeys.Security.tr(), + onTap: () {}, + ), + ProfileMenuItemWidget( + icon: AppIcons.securityWarningIcon, + label: LocaleKeys.PrivacyPolicy.tr(), + onTap: () {}, + ), + ProfileMenuItemWidget( + icon: AppIcons.helpIcon, + label: LocaleKeys.Help.tr(), + onTap: () {}, + ), + ProfileMenuItemWidget( + icon: AppIcons.logoutIcon, + label: LocaleKeys.Logout.tr(), + labelColor: AppColors.orange, + onTap: () { + AppDialogs.showConfirmationDialog( + context, + message: LocaleKeys.AreYouSureToCloseTheApplication.tr(), + firstButtonText: LocaleKeys.No.tr(), + secondButtonText: LocaleKeys.Yes.tr(), + secondButtonAction: () { + viewModel.doIntent(LogoutAction()); + }, + ); + }, + ), + ], + ), + ); + }, + ); + } +} diff --git a/lib/features/profile/presentation/view_model/profile_cubit.dart b/lib/features/profile/presentation/view_model/profile_cubit.dart new file mode 100644 index 0000000..b0744ad --- /dev/null +++ b/lib/features/profile/presentation/view_model/profile_cubit.dart @@ -0,0 +1,77 @@ +import 'package:fitness_app/core/base/base_state.dart'; +import 'package:fitness_app/core/utils/datasource_excution/api_result.dart'; +import 'package:fitness_app/domain/auth/entity/user_entity.dart'; +import 'package:fitness_app/features/profile/presentation/view_model/profile_state.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:injectable/injectable.dart'; + +import '../../../../domain/auth/use_case/get_profile_data_use_case.dart'; +import '../../../../domain/auth/use_case/logout_use_case.dart'; +import '../../../../domain/auth/use_case/select_language_use_case.dart'; + +@injectable +class ProfileCubit extends Cubit { + final GetProfileDataUseCase _getProfileDataUseCase; + final LogoutUseCase _logoutUseCase; + final SelectLanguageUseCase _selectLanguageUseCase; + + ProfileCubit( + this._getProfileDataUseCase, + this._logoutUseCase, + this._selectLanguageUseCase, + ) : super( + (ProfileState( + profileState: BaseInitialState(), + logoutState: BaseInitialState(), + )), + ); + + void doIntent(ProfileAction action) { + switch (action) { + case GetProfileAction(): + _getProfile(); + case LogoutAction(): + _logout(); + case SelectLanguageAction(): + _selectLanguage(action.languageCode); + } + } + + Future _getProfile() async { + emit(state.copyWith(profileState: BaseLoadingState())); + final result = await _getProfileDataUseCase.call(); + switch (result) { + case SuccessResult(): + emit(state.copyWith(profileState: BaseSuccessState(data: result.data))); + case FailureResult(): + emit( + state.copyWith( + profileState: BaseErrorState( + errorMessage: result.exception.toString(), + ), + ), + ); + } + } + + Future _logout() async { + emit(state.copyWith(logoutState: BaseLoadingState())); + final result = await _logoutUseCase.call(); + switch (result) { + case SuccessResult(): + emit(state.copyWith(logoutState: BaseSuccessState())); + case FailureResult(): + emit( + state.copyWith( + logoutState: BaseErrorState( + errorMessage: result.exception.toString(), + ), + ), + ); + } + } + + Future _selectLanguage(String languageCode) async { + await _selectLanguageUseCase.call(languageCode); + } +} diff --git a/lib/features/profile/presentation/view_model/profile_state.dart b/lib/features/profile/presentation/view_model/profile_state.dart new file mode 100644 index 0000000..5520a43 --- /dev/null +++ b/lib/features/profile/presentation/view_model/profile_state.dart @@ -0,0 +1,31 @@ +import 'package:equatable/equatable.dart'; +import 'package:fitness_app/core/base/base_state.dart'; + +final class ProfileState extends Equatable { + final BaseState profileState; + final BaseState logoutState; + + const ProfileState({required this.profileState, required this.logoutState}); + + ProfileState copyWith({BaseState? profileState, BaseState? logoutState}) { + return ProfileState( + profileState: profileState ?? this.profileState, + logoutState: logoutState ?? this.logoutState, + ); + } + + @override + List get props => [profileState, logoutState]; +} + +sealed class ProfileAction {} + +final class GetProfileAction extends ProfileAction {} + +final class LogoutAction extends ProfileAction {} + +final class SelectLanguageAction extends ProfileAction { + final String languageCode; + + SelectLanguageAction(this.languageCode); +} diff --git a/lib/main.dart b/lib/main.dart index be40d35..56266ba 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -67,7 +67,7 @@ class _MyAppState extends State { title: Constants.appName, routes: AppRoutes.routes, theme: AppTheme.appTheme, - initialRoute: AppRoutes.onBoardingRoute, + initialRoute: AppRoutes.profileRoute, ); }, ); diff --git a/test/data/auth/data_source/auth_remote_data_source_impl_test.mocks.dart b/test/data/auth/data_source/auth_remote_data_source_impl_test.mocks.dart deleted file mode 100644 index 21c15ca..0000000 --- a/test/data/auth/data_source/auth_remote_data_source_impl_test.mocks.dart +++ /dev/null @@ -1,108 +0,0 @@ -// Mocks generated by Mockito 5.4.5 from annotations -// in fitness_app/test/data/auth/data_source/auth_remote_data_source_impl_test.dart. -// Do not manually edit this file. - -// ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i6; - -import 'package:fitness_app/data/auth/api/auth_retrofit_client.dart' as _i5; -import 'package:fitness_app/data/auth/models/forget_password/request/forget_password_request_dto.dart' - as _i7; -import 'package:fitness_app/data/auth/models/forget_password/response/forget_password_response_dto.dart' - as _i2; -import 'package:fitness_app/data/auth/models/otp_verification/request/otp_verification_request_dto.dart' - as _i8; -import 'package:fitness_app/data/auth/models/otp_verification/response/otp_verification_response_dto.dart' - as _i3; -import 'package:fitness_app/data/auth/models/reset_password/request/reset_password_request_dto.dart' - as _i9; -import 'package:fitness_app/data/auth/models/reset_password/response/reset_password_response_dto.dart' - as _i4; -import 'package:mockito/mockito.dart' as _i1; - -// ignore_for_file: type=lint -// ignore_for_file: avoid_redundant_argument_values -// ignore_for_file: avoid_setters_without_getters -// ignore_for_file: comment_references -// ignore_for_file: deprecated_member_use -// ignore_for_file: deprecated_member_use_from_same_package -// ignore_for_file: implementation_imports -// ignore_for_file: invalid_use_of_visible_for_testing_member -// ignore_for_file: must_be_immutable -// ignore_for_file: prefer_const_constructors -// ignore_for_file: unnecessary_parenthesis -// ignore_for_file: camel_case_types -// ignore_for_file: subtype_of_sealed_class - -class _FakeForgetPasswordResponseDto_0 extends _i1.SmartFake - implements _i2.ForgetPasswordResponseDto { - _FakeForgetPasswordResponseDto_0(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); -} - -class _FakeOtpVerificationResponseDto_1 extends _i1.SmartFake - implements _i3.OtpVerificationResponseDto { - _FakeOtpVerificationResponseDto_1(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); -} - -class _FakeResetPasswordResponseDto_2 extends _i1.SmartFake - implements _i4.ResetPasswordResponseDto { - _FakeResetPasswordResponseDto_2(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); -} - -/// A class which mocks [AuthRetrofitClient]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockAuthRetrofitClient extends _i1.Mock - implements _i5.AuthRetrofitClient { - MockAuthRetrofitClient() { - _i1.throwOnMissingStub(this); - } - - @override - _i6.Future<_i2.ForgetPasswordResponseDto> forgetPassword( - _i7.ForgetPasswordRequestDto? request, - ) => - (super.noSuchMethod( - Invocation.method(#forgetPassword, [request]), - returnValue: _i6.Future<_i2.ForgetPasswordResponseDto>.value( - _FakeForgetPasswordResponseDto_0( - this, - Invocation.method(#forgetPassword, [request]), - ), - ), - ) - as _i6.Future<_i2.ForgetPasswordResponseDto>); - - @override - _i6.Future<_i3.OtpVerificationResponseDto> verifyOtp( - _i8.OtpVerificationRequestDto? request, - ) => - (super.noSuchMethod( - Invocation.method(#verifyOtp, [request]), - returnValue: _i6.Future<_i3.OtpVerificationResponseDto>.value( - _FakeOtpVerificationResponseDto_1( - this, - Invocation.method(#verifyOtp, [request]), - ), - ), - ) - as _i6.Future<_i3.OtpVerificationResponseDto>); - - @override - _i6.Future<_i4.ResetPasswordResponseDto> resetPassword( - _i9.ResetPasswordRequestDto? request, - ) => - (super.noSuchMethod( - Invocation.method(#resetPassword, [request]), - returnValue: _i6.Future<_i4.ResetPasswordResponseDto>.value( - _FakeResetPasswordResponseDto_2( - this, - Invocation.method(#resetPassword, [request]), - ), - ), - ) - as _i6.Future<_i4.ResetPasswordResponseDto>); -} diff --git a/test/data/auth/data_source/auth_remote_data_source_impl_test.dart b/test/data/auth/data_source/remote/auth_remote_data_source_impl_test.dart similarity index 79% rename from test/data/auth/data_source/auth_remote_data_source_impl_test.dart rename to test/data/auth/data_source/remote/auth_remote_data_source_impl_test.dart index a9bcc04..4d02ca7 100644 --- a/test/data/auth/data_source/auth_remote_data_source_impl_test.dart +++ b/test/data/auth/data_source/remote/auth_remote_data_source_impl_test.dart @@ -7,17 +7,19 @@ import 'package:fitness_app/data/auth/models/otp_verification/request/otp_verifi import 'package:fitness_app/data/auth/models/otp_verification/response/otp_verification_response_dto.dart'; import 'package:fitness_app/data/auth/models/reset_password/request/reset_password_request_dto.dart'; import 'package:fitness_app/data/auth/models/reset_password/response/reset_password_response_dto.dart'; +import 'package:fitness_app/data/auth/models/user_data_response_dto.dart'; +import 'package:fitness_app/data/auth/models/user_dto.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; -import '../../../test_constants.dart'; +import '../../../../test_constants.dart'; import 'auth_remote_data_source_impl_test.mocks.dart'; @GenerateMocks([AuthRetrofitClient]) void main() { TestWidgetsFlutterBinding.ensureInitialized(); - late AuthRemoteDataSourceImpl authDataSource; + late AuthRemoteDataSourceImpl authRemoteDataSourceImpl; late MockAuthRetrofitClient mockAuthRetrofitClient; // Test constants @@ -101,7 +103,59 @@ void main() { setUp(() { mockAuthRetrofitClient = MockAuthRetrofitClient(); - authDataSource = AuthRemoteDataSourceImpl(mockAuthRetrofitClient); + authRemoteDataSourceImpl = AuthRemoteDataSourceImpl(mockAuthRetrofitClient); + }); + + group('Auth Remote Data Source Test', () { + group('getProfileData', () { + test( + 'should return UserDataResponseDto when API call is successful', + () async { + // Arrange + final user = UserDto( + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + gender: 'male', + age: 30, + weight: 70, + height: 175, + activityLevel: 'level1', + goal: 'Gain More Flexible', + photo: 'url', + ); + final expectedResponse = UserDataResponseDto( + message: 'Success', + user: user, + ); + when( + mockAuthRetrofitClient.getProfileData(), + ).thenAnswer((_) async => expectedResponse); + + // Act + final result = await authRemoteDataSourceImpl.getProfileData(); + + // Assert + verify(mockAuthRetrofitClient.getProfileData()).called(1); + expect(result, isA()); + }, + ); + + test('should throw exception when API fails', () { + // Arrange + final exception = Exception('API error'); + when( + mockAuthRetrofitClient.getProfileData(), + ).thenAnswer((_) async => throw exception); + + // Act + final result = authRemoteDataSourceImpl.getProfileData(); + + // Assert + verify(mockAuthRetrofitClient.getProfileData()).called(1); + expect(result, throwsA(isA())); + }); + }); }); group('forgetPassword', () { @@ -114,7 +168,7 @@ void main() { ).thenAnswer((_) async => forgetPasswordSuccessResponse); // Act - final result = await authDataSource.forgetPassword( + final result = await authRemoteDataSourceImpl.forgetPassword( forgetPasswordRequest, ); @@ -136,7 +190,7 @@ void main() { // Act & Assert expect( - () => authDataSource.forgetPassword(forgetPasswordRequest), + () => authRemoteDataSourceImpl.forgetPassword(forgetPasswordRequest), throwsA(isA()), ); verify( @@ -155,7 +209,7 @@ void main() { // Act & Assert expect( - () => authDataSource.forgetPassword(forgetPasswordRequest), + () => authRemoteDataSourceImpl.forgetPassword(forgetPasswordRequest), throwsA(predicate((e) => e.toString().contains(unknownErrorMessage))), ); verify( @@ -174,7 +228,7 @@ void main() { // Act & Assert expect( - () => authDataSource.forgetPassword(forgetPasswordRequest), + () => authRemoteDataSourceImpl.forgetPassword(forgetPasswordRequest), throwsA( isA().having( (e) => e.type, @@ -200,7 +254,9 @@ void main() { ).thenAnswer((_) async => otpVerificationSuccessResponse); // Act - final result = await authDataSource.verifyOtp(otpVerificationRequest); + final result = await authRemoteDataSourceImpl.verifyOtp( + otpVerificationRequest, + ); // Assert expect(result, equals(otpVerificationSuccessResponse)); @@ -220,7 +276,7 @@ void main() { // Act & Assert expect( - () => authDataSource.verifyOtp(otpVerificationRequest), + () => authRemoteDataSourceImpl.verifyOtp(otpVerificationRequest), throwsA(isA()), ); verify( @@ -239,7 +295,7 @@ void main() { // Act & Assert expect( - () => authDataSource.verifyOtp(otpVerificationRequest), + () => authRemoteDataSourceImpl.verifyOtp(otpVerificationRequest), throwsA(predicate((e) => e.toString().contains(unknownErrorMessage))), ); verify( @@ -258,7 +314,7 @@ void main() { // Act & Assert expect( - () => authDataSource.verifyOtp(otpVerificationRequest), + () => authRemoteDataSourceImpl.verifyOtp(otpVerificationRequest), throwsA( isA().having( (e) => e.type, @@ -284,7 +340,9 @@ void main() { ).thenAnswer((_) async => resetPasswordSuccessResponse); // Act - final result = await authDataSource.resetPassword(resetPasswordRequest); + final result = await authRemoteDataSourceImpl.resetPassword( + resetPasswordRequest, + ); // Assert expect(result, equals(resetPasswordSuccessResponse)); @@ -304,7 +362,7 @@ void main() { // Act & Assert expect( - () => authDataSource.resetPassword(resetPasswordRequest), + () => authRemoteDataSourceImpl.resetPassword(resetPasswordRequest), throwsA(isA()), ); verify( @@ -323,7 +381,7 @@ void main() { // Act & Assert expect( - () => authDataSource.resetPassword(resetPasswordRequest), + () => authRemoteDataSourceImpl.resetPassword(resetPasswordRequest), throwsA(predicate((e) => e.toString().contains(unknownErrorMessage))), ); verify( @@ -342,7 +400,7 @@ void main() { // Act & Assert expect( - () => authDataSource.resetPassword(resetPasswordRequest), + () => authRemoteDataSourceImpl.resetPassword(resetPasswordRequest), throwsA( isA().having( (e) => e.type, diff --git a/test/data/auth/data_source/remote/auth_remote_data_source_impl_test.mocks.dart b/test/data/auth/data_source/remote/auth_remote_data_source_impl_test.mocks.dart new file mode 100644 index 0000000..b41ca2d --- /dev/null +++ b/test/data/auth/data_source/remote/auth_remote_data_source_impl_test.mocks.dart @@ -0,0 +1,146 @@ +// Mocks generated by Mockito 5.4.5 from annotations +// in fitness_app/test/data/auth/data_source/remote/auth_remote_data_source_impl_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i8; + +import 'package:fitness_app/data/auth/api/auth_retrofit_client.dart' as _i7; +import 'package:fitness_app/data/auth/models/forget_password/request/forget_password_request_dto.dart' + as _i9; +import 'package:fitness_app/data/auth/models/forget_password/response/forget_password_response_dto.dart' + as _i4; +import 'package:fitness_app/data/auth/models/logout_response_dto.dart' as _i2; +import 'package:fitness_app/data/auth/models/otp_verification/request/otp_verification_request_dto.dart' + as _i10; +import 'package:fitness_app/data/auth/models/otp_verification/response/otp_verification_response_dto.dart' + as _i5; +import 'package:fitness_app/data/auth/models/reset_password/request/reset_password_request_dto.dart' + as _i11; +import 'package:fitness_app/data/auth/models/reset_password/response/reset_password_response_dto.dart' + as _i6; +import 'package:fitness_app/data/auth/models/user_data_response_dto.dart' + as _i3; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeLogoutResponseDto_0 extends _i1.SmartFake + implements _i2.LogoutResponseDto { + _FakeLogoutResponseDto_0(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +class _FakeUserDataResponseDto_1 extends _i1.SmartFake + implements _i3.UserDataResponseDto { + _FakeUserDataResponseDto_1(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +class _FakeForgetPasswordResponseDto_2 extends _i1.SmartFake + implements _i4.ForgetPasswordResponseDto { + _FakeForgetPasswordResponseDto_2(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +class _FakeOtpVerificationResponseDto_3 extends _i1.SmartFake + implements _i5.OtpVerificationResponseDto { + _FakeOtpVerificationResponseDto_3(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +class _FakeResetPasswordResponseDto_4 extends _i1.SmartFake + implements _i6.ResetPasswordResponseDto { + _FakeResetPasswordResponseDto_4(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +/// A class which mocks [AuthRetrofitClient]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockAuthRetrofitClient extends _i1.Mock + implements _i7.AuthRetrofitClient { + MockAuthRetrofitClient() { + _i1.throwOnMissingStub(this); + } + + @override + _i8.Future<_i2.LogoutResponseDto> logout() => + (super.noSuchMethod( + Invocation.method(#logout, []), + returnValue: _i8.Future<_i2.LogoutResponseDto>.value( + _FakeLogoutResponseDto_0(this, Invocation.method(#logout, [])), + ), + ) + as _i8.Future<_i2.LogoutResponseDto>); + + @override + _i8.Future<_i3.UserDataResponseDto> getProfileData() => + (super.noSuchMethod( + Invocation.method(#getProfileData, []), + returnValue: _i8.Future<_i3.UserDataResponseDto>.value( + _FakeUserDataResponseDto_1( + this, + Invocation.method(#getProfileData, []), + ), + ), + ) + as _i8.Future<_i3.UserDataResponseDto>); + + @override + _i8.Future<_i4.ForgetPasswordResponseDto> forgetPassword( + _i9.ForgetPasswordRequestDto? request, + ) => + (super.noSuchMethod( + Invocation.method(#forgetPassword, [request]), + returnValue: _i8.Future<_i4.ForgetPasswordResponseDto>.value( + _FakeForgetPasswordResponseDto_2( + this, + Invocation.method(#forgetPassword, [request]), + ), + ), + ) + as _i8.Future<_i4.ForgetPasswordResponseDto>); + + @override + _i8.Future<_i5.OtpVerificationResponseDto> verifyOtp( + _i10.OtpVerificationRequestDto? request, + ) => + (super.noSuchMethod( + Invocation.method(#verifyOtp, [request]), + returnValue: _i8.Future<_i5.OtpVerificationResponseDto>.value( + _FakeOtpVerificationResponseDto_3( + this, + Invocation.method(#verifyOtp, [request]), + ), + ), + ) + as _i8.Future<_i5.OtpVerificationResponseDto>); + + @override + _i8.Future<_i6.ResetPasswordResponseDto> resetPassword( + _i11.ResetPasswordRequestDto? request, + ) => + (super.noSuchMethod( + Invocation.method(#resetPassword, [request]), + returnValue: _i8.Future<_i6.ResetPasswordResponseDto>.value( + _FakeResetPasswordResponseDto_4( + this, + Invocation.method(#resetPassword, [request]), + ), + ), + ) + as _i8.Future<_i6.ResetPasswordResponseDto>); +} diff --git a/test/data/auth/repo_impl/auth_repo_impl_test.dart b/test/data/auth/repo_impl/auth_repo_impl_test.dart index 8a5f5f7..61cb607 100644 --- a/test/data/auth/repo_impl/auth_repo_impl_test.dart +++ b/test/data/auth/repo_impl/auth_repo_impl_test.dart @@ -1,12 +1,15 @@ import 'package:fitness_app/core/utils/constants.dart'; import 'package:fitness_app/core/utils/datasource_excution/api_manager.dart'; import 'package:fitness_app/core/utils/datasource_excution/api_result.dart'; -import 'package:fitness_app/data/auth/data_source/local/auth_local_data_source_impl.dart'; -import 'package:fitness_app/data/auth/data_source/remote/auth_remote_data_source_impl.dart'; +import 'package:fitness_app/data/auth/data_source/contract/auth_local_data_source.dart'; +import 'package:fitness_app/data/auth/data_source/contract/auth_remote_data_source.dart'; +import 'package:fitness_app/data/auth/models/user_dto.dart'; import 'package:fitness_app/data/auth/models/forget_password/response/forget_password_response_dto.dart'; import 'package:fitness_app/data/auth/models/otp_verification/response/otp_verification_response_dto.dart'; import 'package:fitness_app/data/auth/models/reset_password/response/reset_password_response_dto.dart'; import 'package:fitness_app/data/auth/repo_impl/auth_repo_impl.dart'; +import 'package:fitness_app/data/auth/models/user_data_response_dto.dart'; +import 'package:fitness_app/domain/auth/entity/user_entity.dart'; import 'package:fitness_app/domain/auth/entity/forget_password/forget_password_request_entity.dart'; import 'package:fitness_app/domain/auth/entity/forget_password/forget_password_response_entity.dart'; import 'package:fitness_app/domain/auth/entity/otp_verification/request/otp_verification_request_entity.dart'; @@ -20,13 +23,13 @@ import 'package:mockito/mockito.dart'; import '../../../test_constants.dart'; import 'auth_repo_impl_test.mocks.dart'; -@GenerateMocks([AuthRemoteDataSourceImpl, AuthLocalDataSourceImpl, ApiManager]) +@GenerateMocks([ApiManager, AuthLocalDataSource, AuthRemoteDataSource]) void main() { TestWidgetsFlutterBinding.ensureInitialized(); late AuthRepoImpl authRepoImpl; - late MockAuthRemoteDataSourceImpl mockAuthRemoteDataSource; - late MockAuthLocalDataSourceImpl mockAuthLocalDataSource; late MockApiManager mockApiManager; + late MockAuthLocalDataSource mockAuthLocalDataSource; + late MockAuthRemoteDataSource mockAuthRemoteDataSource; const testEmail = 'ahmed@example.com'; const successMessage = 'Success'; @@ -50,13 +53,13 @@ void main() { late ResetPasswordResponseDto resetPasswordSuccessResponseDto; setUp(() { - mockAuthRemoteDataSource = MockAuthRemoteDataSourceImpl(); mockApiManager = MockApiManager(); - mockAuthLocalDataSource = MockAuthLocalDataSourceImpl(); + mockAuthLocalDataSource = MockAuthLocalDataSource(); + mockAuthRemoteDataSource = MockAuthRemoteDataSource(); authRepoImpl = AuthRepoImpl( mockApiManager, - mockAuthRemoteDataSource, mockAuthLocalDataSource, + mockAuthRemoteDataSource, ); }); @@ -82,6 +85,76 @@ void main() { ); }); + group('AuthRepoImpl Tests', () { + group('getProfileData', () { + test('returns SuccessResult on success', () async { + // Prepare DTO and expected entity + final userDto = UserDto( + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + gender: 'male', + age: 30, + weight: 70, + height: 175, + activityLevel: 'level1', + goal: 'Gain More Flexible', + photo: 'url', + ); + final userResponseDto = UserDataResponseDto( + message: 'OK', + user: userDto, + ); + + final expectedEntity = userDto.toEntity(); + + // Register dummy for generics + provideDummy>( + SuccessResult(expectedEntity), + ); + + // Stub remote data source + when( + mockAuthRemoteDataSource.getProfileData(), + ).thenAnswer((_) async => userResponseDto); + // Stub API manager + when( + mockApiManager.execute(any), + ).thenAnswer((_) async => SuccessResult(expectedEntity)); + + // Act + final result = await authRepoImpl.getProfileData(); + + // Assert + verify(mockApiManager.execute(any)).called(1); + expect(result, isA>()); + }); + + test('returns FailureResult on failure', () async { + final error = Exception('Network error'); + // Register dummy for generics + provideDummy>(FailureResult(error)); + + // Stub remote data source + final userDto = UserDataResponseDto(message: 'OK', user: null); + when( + mockAuthRemoteDataSource.getProfileData(), + ).thenAnswer((_) async => userDto); + // Stub API manager to return failure + when( + mockApiManager.execute(any), + ).thenAnswer((_) async => FailureResult(error)); + + // Act + final result = await authRepoImpl.getProfileData(); + + // Assert + verify(mockApiManager.execute(any)).called(1); + expect(result, isA>()); + }); + }); + }); + group('forgetPassword', () { test( 'should return SuccessResult when forgetPassword is successful', diff --git a/test/data/auth/repo_impl/auth_repo_impl_test.mocks.dart b/test/data/auth/repo_impl/auth_repo_impl_test.mocks.dart index af185d0..b3cbc59 100644 --- a/test/data/auth/repo_impl/auth_repo_impl_test.mocks.dart +++ b/test/data/auth/repo_impl/auth_repo_impl_test.mocks.dart @@ -3,30 +3,33 @@ // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i6; +import 'dart:async' as _i8; import 'package:fitness_app/core/utils/datasource_excution/api_manager.dart' - as _i11; + as _i7; import 'package:fitness_app/core/utils/datasource_excution/api_result.dart' + as _i9; +import 'package:fitness_app/data/auth/data_source/contract/auth_local_data_source.dart' + as _i11; +import 'package:fitness_app/data/auth/data_source/contract/auth_remote_data_source.dart' as _i12; -import 'package:fitness_app/data/auth/data_source/local/auth_local_data_source_impl.dart' - as _i10; -import 'package:fitness_app/data/auth/data_source/remote/auth_remote_data_source_impl.dart' - as _i5; import 'package:fitness_app/data/auth/models/forget_password/request/forget_password_request_dto.dart' - as _i7; + as _i13; import 'package:fitness_app/data/auth/models/forget_password/response/forget_password_response_dto.dart' - as _i2; + as _i4; +import 'package:fitness_app/data/auth/models/logout_response_dto.dart' as _i3; import 'package:fitness_app/data/auth/models/otp_verification/request/otp_verification_request_dto.dart' - as _i8; + as _i14; import 'package:fitness_app/data/auth/models/otp_verification/response/otp_verification_response_dto.dart' - as _i3; + as _i5; import 'package:fitness_app/data/auth/models/reset_password/request/reset_password_request_dto.dart' - as _i9; + as _i15; import 'package:fitness_app/data/auth/models/reset_password/response/reset_password_response_dto.dart' - as _i4; + as _i6; +import 'package:fitness_app/data/auth/models/user_data_response_dto.dart' + as _i2; import 'package:mockito/mockito.dart' as _i1; -import 'package:mockito/src/dummies.dart' as _i13; +import 'package:mockito/src/dummies.dart' as _i10; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -42,133 +45,185 @@ import 'package:mockito/src/dummies.dart' as _i13; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeForgetPasswordResponseDto_0 extends _i1.SmartFake - implements _i2.ForgetPasswordResponseDto { - _FakeForgetPasswordResponseDto_0(Object parent, Invocation parentInvocation) +class _FakeUserDataResponseDto_0 extends _i1.SmartFake + implements _i2.UserDataResponseDto { + _FakeUserDataResponseDto_0(Object parent, Invocation parentInvocation) : super(parent, parentInvocation); } -class _FakeOtpVerificationResponseDto_1 extends _i1.SmartFake - implements _i3.OtpVerificationResponseDto { - _FakeOtpVerificationResponseDto_1(Object parent, Invocation parentInvocation) +class _FakeLogoutResponseDto_1 extends _i1.SmartFake + implements _i3.LogoutResponseDto { + _FakeLogoutResponseDto_1(Object parent, Invocation parentInvocation) : super(parent, parentInvocation); } -class _FakeResetPasswordResponseDto_2 extends _i1.SmartFake - implements _i4.ResetPasswordResponseDto { - _FakeResetPasswordResponseDto_2(Object parent, Invocation parentInvocation) +class _FakeForgetPasswordResponseDto_2 extends _i1.SmartFake + implements _i4.ForgetPasswordResponseDto { + _FakeForgetPasswordResponseDto_2(Object parent, Invocation parentInvocation) : super(parent, parentInvocation); } -/// A class which mocks [AuthRemoteDataSourceImpl]. +class _FakeOtpVerificationResponseDto_3 extends _i1.SmartFake + implements _i5.OtpVerificationResponseDto { + _FakeOtpVerificationResponseDto_3(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +class _FakeResetPasswordResponseDto_4 extends _i1.SmartFake + implements _i6.ResetPasswordResponseDto { + _FakeResetPasswordResponseDto_4(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +/// A class which mocks [ApiManager]. /// /// See the documentation for Mockito's code generation for more information. -class MockAuthRemoteDataSourceImpl extends _i1.Mock - implements _i5.AuthRemoteDataSourceImpl { - MockAuthRemoteDataSourceImpl() { +class MockApiManager extends _i1.Mock implements _i7.ApiManager { + MockApiManager() { _i1.throwOnMissingStub(this); } @override - _i6.Future<_i2.ForgetPasswordResponseDto> forgetPassword( - _i7.ForgetPasswordRequestDto? request, - ) => + _i8.Future<_i9.Result> execute(_i8.Future Function()? apiCall) => (super.noSuchMethod( - Invocation.method(#forgetPassword, [request]), - returnValue: _i6.Future<_i2.ForgetPasswordResponseDto>.value( - _FakeForgetPasswordResponseDto_0( + Invocation.method(#execute, [apiCall]), + returnValue: _i8.Future<_i9.Result>.value( + _i10.dummyValue<_i9.Result>( this, - Invocation.method(#forgetPassword, [request]), + Invocation.method(#execute, [apiCall]), ), ), ) - as _i6.Future<_i2.ForgetPasswordResponseDto>); + as _i8.Future<_i9.Result>); +} + +/// A class which mocks [AuthLocalDataSource]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockAuthLocalDataSource extends _i1.Mock + implements _i11.AuthLocalDataSource { + MockAuthLocalDataSource() { + _i1.throwOnMissingStub(this); + } @override - _i6.Future<_i3.OtpVerificationResponseDto> verifyOtp( - _i8.OtpVerificationRequestDto? request, - ) => + _i8.Future clearAll() => (super.noSuchMethod( - Invocation.method(#verifyOtp, [request]), - returnValue: _i6.Future<_i3.OtpVerificationResponseDto>.value( - _FakeOtpVerificationResponseDto_1( - this, - Invocation.method(#verifyOtp, [request]), - ), - ), + Invocation.method(#clearAll, []), + returnValue: _i8.Future.value(), + returnValueForMissingStub: _i8.Future.value(), ) - as _i6.Future<_i3.OtpVerificationResponseDto>); + as _i8.Future); @override - _i6.Future<_i4.ResetPasswordResponseDto> resetPassword( - _i9.ResetPasswordRequestDto? request, - ) => + _i8.Future selectLanguage(String? languageCode) => (super.noSuchMethod( - Invocation.method(#resetPassword, [request]), - returnValue: _i6.Future<_i4.ResetPasswordResponseDto>.value( - _FakeResetPasswordResponseDto_2( - this, - Invocation.method(#resetPassword, [request]), - ), - ), + Invocation.method(#selectLanguage, [languageCode]), + returnValue: _i8.Future.value(false), ) - as _i6.Future<_i4.ResetPasswordResponseDto>); -} - -/// A class which mocks [AuthLocalDataSourceImpl]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockAuthLocalDataSourceImpl extends _i1.Mock - implements _i10.AuthLocalDataSourceImpl { - MockAuthLocalDataSourceImpl() { - _i1.throwOnMissingStub(this); - } + as _i8.Future); @override - _i6.Future deleteToken(String? key) => + _i8.Future saveToken(String? key, String? value) => (super.noSuchMethod( - Invocation.method(#deleteToken, [key]), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), + Invocation.method(#saveToken, [key, value]), + returnValue: _i8.Future.value(), + returnValueForMissingStub: _i8.Future.value(), ) - as _i6.Future); + as _i8.Future); @override - _i6.Future getToken(String? key) => + _i8.Future getToken(String? key) => (super.noSuchMethod( Invocation.method(#getToken, [key]), - returnValue: _i6.Future.value(), + returnValue: _i8.Future.value(), ) - as _i6.Future); + as _i8.Future); @override - _i6.Future saveToken(String? key, String? value) => + _i8.Future deleteToken(String? key) => (super.noSuchMethod( - Invocation.method(#saveToken, [key, value]), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), + Invocation.method(#deleteToken, [key]), + returnValue: _i8.Future.value(), + returnValueForMissingStub: _i8.Future.value(), ) - as _i6.Future); + as _i8.Future); } -/// A class which mocks [ApiManager]. +/// A class which mocks [AuthRemoteDataSource]. /// /// See the documentation for Mockito's code generation for more information. -class MockApiManager extends _i1.Mock implements _i11.ApiManager { - MockApiManager() { +class MockAuthRemoteDataSource extends _i1.Mock + implements _i12.AuthRemoteDataSource { + MockAuthRemoteDataSource() { _i1.throwOnMissingStub(this); } @override - _i6.Future<_i12.Result> execute(_i6.Future Function()? apiCall) => + _i8.Future<_i2.UserDataResponseDto> getProfileData() => (super.noSuchMethod( - Invocation.method(#execute, [apiCall]), - returnValue: _i6.Future<_i12.Result>.value( - _i13.dummyValue<_i12.Result>( + Invocation.method(#getProfileData, []), + returnValue: _i8.Future<_i2.UserDataResponseDto>.value( + _FakeUserDataResponseDto_0( this, - Invocation.method(#execute, [apiCall]), + Invocation.method(#getProfileData, []), + ), + ), + ) + as _i8.Future<_i2.UserDataResponseDto>); + + @override + _i8.Future<_i3.LogoutResponseDto> logout() => + (super.noSuchMethod( + Invocation.method(#logout, []), + returnValue: _i8.Future<_i3.LogoutResponseDto>.value( + _FakeLogoutResponseDto_1(this, Invocation.method(#logout, [])), + ), + ) + as _i8.Future<_i3.LogoutResponseDto>); + + @override + _i8.Future<_i4.ForgetPasswordResponseDto> forgetPassword( + _i13.ForgetPasswordRequestDto? request, + ) => + (super.noSuchMethod( + Invocation.method(#forgetPassword, [request]), + returnValue: _i8.Future<_i4.ForgetPasswordResponseDto>.value( + _FakeForgetPasswordResponseDto_2( + this, + Invocation.method(#forgetPassword, [request]), + ), + ), + ) + as _i8.Future<_i4.ForgetPasswordResponseDto>); + + @override + _i8.Future<_i5.OtpVerificationResponseDto> verifyOtp( + _i14.OtpVerificationRequestDto? request, + ) => + (super.noSuchMethod( + Invocation.method(#verifyOtp, [request]), + returnValue: _i8.Future<_i5.OtpVerificationResponseDto>.value( + _FakeOtpVerificationResponseDto_3( + this, + Invocation.method(#verifyOtp, [request]), + ), + ), + ) + as _i8.Future<_i5.OtpVerificationResponseDto>); + + @override + _i8.Future<_i6.ResetPasswordResponseDto> resetPassword( + _i15.ResetPasswordRequestDto? request, + ) => + (super.noSuchMethod( + Invocation.method(#resetPassword, [request]), + returnValue: _i8.Future<_i6.ResetPasswordResponseDto>.value( + _FakeResetPasswordResponseDto_4( + this, + Invocation.method(#resetPassword, [request]), ), ), ) - as _i6.Future<_i12.Result>); + as _i8.Future<_i6.ResetPasswordResponseDto>); } diff --git a/test/domain/auth/use_case/get_profile_data_use_case_test.dart b/test/domain/auth/use_case/get_profile_data_use_case_test.dart new file mode 100644 index 0000000..dd82cd6 --- /dev/null +++ b/test/domain/auth/use_case/get_profile_data_use_case_test.dart @@ -0,0 +1,68 @@ +import 'package:fitness_app/domain/auth/repo/auth_repo.dart'; +import 'package:fitness_app/domain/auth/use_case/get_profile_data_use_case.dart'; +import 'package:fitness_app/core/utils/datasource_excution/api_result.dart'; +import 'package:fitness_app/domain/auth/entity/user_entity.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'get_profile_data_use_case_test.mocks.dart'; + +@GenerateMocks([AuthRepo]) +void main() { + late MockAuthRepo mockAuthRepo; + late GetProfileDataUseCase getProfileDataUseCase; + + setUp(() { + mockAuthRepo = MockAuthRepo(); + getProfileDataUseCase = GetProfileDataUseCase(mockAuthRepo); + }); + + group('GetProfileDataUseCase', () { + test( + 'returns SuccessResult when repository succeeds', + () async { + // Arrange + final user = UserEntity( + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + gender: 'male', + age: 30, + weight: 70, + height: 175, + activityLevel: 'level1', + goal: 'Gain More Flexible', + photo: 'url', + ); + provideDummy>(SuccessResult(user)); + when( + mockAuthRepo.getProfileData(), + ).thenAnswer((_) async => SuccessResult(user)); + + // Act + final result = await getProfileDataUseCase.call(); + + // Assert + verify(mockAuthRepo.getProfileData()).called(1); + expect(result, isA>()); + }, + ); + + test('returns FailureResult when repository fails', () async { + // Arrange + final exception = Exception('Repo error'); + provideDummy>(FailureResult(exception)); + when( + mockAuthRepo.getProfileData(), + ).thenAnswer((_) async => FailureResult(exception)); + + // Act + final result = await getProfileDataUseCase.call(); + + // Assert + verify(mockAuthRepo.getProfileData()).called(1); + expect(result, isA>()); + }); + }); +} diff --git a/test/domain/auth/use_case/get_profile_data_use_case_test.mocks.dart b/test/domain/auth/use_case/get_profile_data_use_case_test.mocks.dart new file mode 100644 index 0000000..48b7a21 --- /dev/null +++ b/test/domain/auth/use_case/get_profile_data_use_case_test.mocks.dart @@ -0,0 +1,135 @@ +// Mocks generated by Mockito 5.4.5 from annotations +// in fitness_app/test/domain/auth/use_case/get_profile_data_use_case_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i3; + +import 'package:fitness_app/core/utils/datasource_excution/api_result.dart' + as _i4; +import 'package:fitness_app/domain/auth/entity/forget_password/forget_password_request_entity.dart' + as _i8; +import 'package:fitness_app/domain/auth/entity/forget_password/forget_password_response_entity.dart' + as _i7; +import 'package:fitness_app/domain/auth/entity/otp_verification/request/otp_verification_request_entity.dart' + as _i10; +import 'package:fitness_app/domain/auth/entity/otp_verification/response/otp_verification_response_entity.dart' + as _i9; +import 'package:fitness_app/domain/auth/entity/reset_password/request/reset_password_request_entity.dart' + as _i12; +import 'package:fitness_app/domain/auth/entity/reset_password/response/reset_password_response_entity.dart' + as _i11; +import 'package:fitness_app/domain/auth/entity/user_entity.dart' as _i5; +import 'package:fitness_app/domain/auth/repo/auth_repo.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i6; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +/// A class which mocks [AuthRepo]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockAuthRepo extends _i1.Mock implements _i2.AuthRepo { + MockAuthRepo() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future<_i4.Result<_i5.UserEntity>> getProfileData() => + (super.noSuchMethod( + Invocation.method(#getProfileData, []), + returnValue: _i3.Future<_i4.Result<_i5.UserEntity>>.value( + _i6.dummyValue<_i4.Result<_i5.UserEntity>>( + this, + Invocation.method(#getProfileData, []), + ), + ), + ) + as _i3.Future<_i4.Result<_i5.UserEntity>>); + + @override + _i3.Future<_i4.Result> logout() => + (super.noSuchMethod( + Invocation.method(#logout, []), + returnValue: _i3.Future<_i4.Result>.value( + _i6.dummyValue<_i4.Result>( + this, + Invocation.method(#logout, []), + ), + ), + ) + as _i3.Future<_i4.Result>); + + @override + _i3.Future<_i4.Result> selectLanguage(String? languageCode) => + (super.noSuchMethod( + Invocation.method(#selectLanguage, [languageCode]), + returnValue: _i3.Future<_i4.Result>.value( + _i6.dummyValue<_i4.Result>( + this, + Invocation.method(#selectLanguage, [languageCode]), + ), + ), + ) + as _i3.Future<_i4.Result>); + + @override + _i3.Future<_i4.Result<_i7.ForgetPasswordResponseEntity>> forgetPassword( + _i8.ForgetPasswordRequestEntity? request, + ) => + (super.noSuchMethod( + Invocation.method(#forgetPassword, [request]), + returnValue: + _i3.Future<_i4.Result<_i7.ForgetPasswordResponseEntity>>.value( + _i6.dummyValue<_i4.Result<_i7.ForgetPasswordResponseEntity>>( + this, + Invocation.method(#forgetPassword, [request]), + ), + ), + ) + as _i3.Future<_i4.Result<_i7.ForgetPasswordResponseEntity>>); + + @override + _i3.Future<_i4.Result<_i9.OtpVerificationResponseEntity>> verifyOtp( + _i10.OtpVerificationRequestEntity? request, + ) => + (super.noSuchMethod( + Invocation.method(#verifyOtp, [request]), + returnValue: + _i3.Future<_i4.Result<_i9.OtpVerificationResponseEntity>>.value( + _i6.dummyValue<_i4.Result<_i9.OtpVerificationResponseEntity>>( + this, + Invocation.method(#verifyOtp, [request]), + ), + ), + ) + as _i3.Future<_i4.Result<_i9.OtpVerificationResponseEntity>>); + + @override + _i3.Future<_i4.Result<_i11.ResetPasswordResponseEntity>> resetPassword( + _i12.ResetPasswordRequestEntity? request, + ) => + (super.noSuchMethod( + Invocation.method(#resetPassword, [request]), + returnValue: + _i3.Future<_i4.Result<_i11.ResetPasswordResponseEntity>>.value( + _i6.dummyValue<_i4.Result<_i11.ResetPasswordResponseEntity>>( + this, + Invocation.method(#resetPassword, [request]), + ), + ), + ) + as _i3.Future<_i4.Result<_i11.ResetPasswordResponseEntity>>); +} diff --git a/test/features/main_layout/presentation/view_model/main_layout_cubit_test.dart b/test/features/main_layout/presentation/view_model/main_layout_cubit_test.dart index c20c8f0..a78b318 100644 --- a/test/features/main_layout/presentation/view_model/main_layout_cubit_test.dart +++ b/test/features/main_layout/presentation/view_model/main_layout_cubit_test.dart @@ -2,7 +2,7 @@ import 'package:bloc_test/bloc_test.dart'; import 'package:fitness_app/features/chat_bot/presentation/view/chat_bot_screen.dart'; import 'package:fitness_app/features/home/presentation/view/home_screen.dart'; import 'package:fitness_app/features/main_layout/presentation/view_model/cubit/main_layout_cubit.dart'; -import 'package:fitness_app/features/profile/presentation/view/profile_screen.dart'; +import 'package:fitness_app/features/profile/presentation/view/screens/profile_screen.dart'; import 'package:fitness_app/features/workouts/presentation/view/workouts_screen.dart'; import 'package:flutter_test/flutter_test.dart';