diff --git a/assets/translations/app_en.arb b/assets/translations/app_en.arb index bab40d2..51a6554 100644 --- a/assets/translations/app_en.arb +++ b/assets/translations/app_en.arb @@ -183,7 +183,21 @@ "example": "Uber" } } - } + }, + "otp_code_incomplete": "Code is incomplete. Please enter the full code.", + "passwords_do_not_match": "Passwords do not match.", + "reset_password": "Reset password", + "new_password": "New password", + "enter_otp_code": "Enter the 5-digit code sent to you at {phoneNumber}", + "@enter_otp_code":{ + "placeholders": { + "phoneNumber": { + "type": "String" + } + } + }, + "resend_otp_after": "You can resend OTP after" + diff --git a/assets/translations/app_ru.arb b/assets/translations/app_ru.arb index e7a7fc7..e48d97e 100644 --- a/assets/translations/app_ru.arb +++ b/assets/translations/app_ru.arb @@ -178,7 +178,20 @@ "example": "Uber" } } - } + }, + "otp_code_incomplete": "Код неполный. Пожалуйста, введите полный код.", + "passwords_do_not_match": "Пароли не совпадают.", + "reset_password": "Восстановить пароль", + "new_password": "Новый пароль", + "enter_otp_code": "Введите 5-значный код, отправленный на номер {phoneNumber}", + "@enter_otp_code":{ + "placeholders": { + "phoneNumber": { + "type": "String" + } + } + }, + "resend_otp_after": "Вы можете повторно отправить код через" diff --git a/assets/translations/app_uz.arb b/assets/translations/app_uz.arb index 9b25a58..ae22aaf 100644 --- a/assets/translations/app_uz.arb +++ b/assets/translations/app_uz.arb @@ -178,7 +178,20 @@ "example": "Uber" } } - } + }, + "otp_code_incomplete": "Kod yetarli emas. Iltimos, to‘liq kodni kiriting.", + "passwords_do_not_match": "Parollar mos kelmadi.", + "reset_password": "Parolni qayta tiklash", + "new_password": "Yangi parol", + "enter_otp_code": "{phoneNumber} raqamiga yuborilgan 5 xonali kodni kiriting", + "@enter_otp_code":{ + "placeholders": { + "phoneNumber": { + "type": "String" + } + } + }, + "resend_otp_after": "OTP kodni qayta yuborish mumkin bo‘ladi" diff --git a/lib/core/di/injection_container.config.dart b/lib/core/di/injection_container.config.dart index 16e7e64..70cbf67 100644 --- a/lib/core/di/injection_container.config.dart +++ b/lib/core/di/injection_container.config.dart @@ -30,6 +30,10 @@ import '../../feature/auth/domain/usecases/verify_phone_register_usecase.dart' as _i664; import '../../feature/auth/presentation/blocs/login_bloc/login_bloc.dart' as _i1065; +import '../../feature/auth/presentation/blocs/register_bloc/register_bloc.dart' + as _i1050; +import '../../feature/auth/presentation/blocs/reset_password_bloc/reset_password_bloc.dart' + as _i97; import '../../feature/auth/presentation/blocs/verify_otp_bloc/verify_otp_bloc.dart' as _i323; import '../../feature/auth/presentation/blocs/verify_phone_bloc/verify_phone_bloc.dart' @@ -119,6 +123,12 @@ extension GetItInjectableX on _i174.GetIt { gh<_i664.VerifyPhoneRegisterUseCase>(), ), ); + gh.factory<_i1050.RegisterBloc>( + () => _i1050.RegisterBloc(gh<_i607.RegisterUseCase>()), + ); + gh.factory<_i97.ResetPasswordBloc>( + () => _i97.ResetPasswordBloc(gh<_i694.ResetPasswordUseCase>()), + ); gh.factory<_i1065.LoginBloc>( () => _i1065.LoginBloc( gh<_i241.LoginUseCase>(), diff --git a/lib/core/helpers/validator_helpers.dart b/lib/core/helpers/validator_helpers.dart index 0809196..f8a47b9 100644 --- a/lib/core/helpers/validator_helpers.dart +++ b/lib/core/helpers/validator_helpers.dart @@ -19,10 +19,31 @@ abstract class Validators { return null; } + static String? validateRepeatPassword( + String repeatPassword, + String password, + ) { + if (repeatPassword.isEmpty) { + return navigatorKey.currentContext?.loc.field_cannot_be_empty; + } else if (repeatPassword != password) { + return navigatorKey.currentContext?.loc.passwords_do_not_match; + } + return null; + } + static String? validateFields(String value) { if (value.isEmpty) { return navigatorKey.currentContext?.loc.field_cannot_be_empty; } return null; } + + static String? validateOtpFields(String value) { + if (value.isEmpty) { + return navigatorKey.currentContext?.loc.field_cannot_be_empty; + } else if (value.length < 5) { + return navigatorKey.currentContext?.loc.otp_code_incomplete; + } + return null; + } } diff --git a/lib/core/l10n/app_localizations.dart b/lib/core/l10n/app_localizations.dart index efbc7ee..44f3850 100644 --- a/lib/core/l10n/app_localizations.dart +++ b/lib/core/l10n/app_localizations.dart @@ -867,6 +867,42 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'By proceeding, you consent to get calls or SMS messages, including by automated means, from {appName} and its affiliates to the number provided.'** String consent_message(String appName); + + /// No description provided for @otp_code_incomplete. + /// + /// In en, this message translates to: + /// **'Code is incomplete. Please enter the full code.'** + String get otp_code_incomplete; + + /// No description provided for @passwords_do_not_match. + /// + /// In en, this message translates to: + /// **'Passwords do not match.'** + String get passwords_do_not_match; + + /// No description provided for @reset_password. + /// + /// In en, this message translates to: + /// **'Reset password'** + String get reset_password; + + /// No description provided for @new_password. + /// + /// In en, this message translates to: + /// **'New password'** + String get new_password; + + /// No description provided for @enter_otp_code. + /// + /// In en, this message translates to: + /// **'Enter the 5-digit code sent to you at {phoneNumber}'** + String enter_otp_code(String phoneNumber); + + /// No description provided for @resend_otp_after. + /// + /// In en, this message translates to: + /// **'You can resend OTP after'** + String get resend_otp_after; } class _AppLocalizationsDelegate diff --git a/lib/core/l10n/app_localizations_en.dart b/lib/core/l10n/app_localizations_en.dart index f479403..44c5a4e 100644 --- a/lib/core/l10n/app_localizations_en.dart +++ b/lib/core/l10n/app_localizations_en.dart @@ -406,4 +406,25 @@ class AppLocalizationsEn extends AppLocalizations { String consent_message(String appName) { return 'By proceeding, you consent to get calls or SMS messages, including by automated means, from $appName and its affiliates to the number provided.'; } + + @override + String get otp_code_incomplete => + 'Code is incomplete. Please enter the full code.'; + + @override + String get passwords_do_not_match => 'Passwords do not match.'; + + @override + String get reset_password => 'Reset password'; + + @override + String get new_password => 'New password'; + + @override + String enter_otp_code(String phoneNumber) { + return 'Enter the 5-digit code sent to you at $phoneNumber'; + } + + @override + String get resend_otp_after => 'You can resend OTP after'; } diff --git a/lib/core/l10n/app_localizations_ru.dart b/lib/core/l10n/app_localizations_ru.dart index b343dcb..154a850 100644 --- a/lib/core/l10n/app_localizations_ru.dart +++ b/lib/core/l10n/app_localizations_ru.dart @@ -406,4 +406,25 @@ class AppLocalizationsRu extends AppLocalizations { String consent_message(String appName) { return 'Продолжая, вы соглашаетесь получать звонки или SMS-сообщения, включая автоматические, от $appName и его партнёров на указанный номер.'; } + + @override + String get otp_code_incomplete => + 'Код неполный. Пожалуйста, введите полный код.'; + + @override + String get passwords_do_not_match => 'Пароли не совпадают.'; + + @override + String get reset_password => 'Восстановить пароль'; + + @override + String get new_password => 'Новый пароль'; + + @override + String enter_otp_code(String phoneNumber) { + return 'Введите 5-значный код, отправленный на номер $phoneNumber'; + } + + @override + String get resend_otp_after => 'Вы можете повторно отправить код через'; } diff --git a/lib/core/l10n/app_localizations_uz.dart b/lib/core/l10n/app_localizations_uz.dart index 17444df..cbae3f0 100644 --- a/lib/core/l10n/app_localizations_uz.dart +++ b/lib/core/l10n/app_localizations_uz.dart @@ -407,4 +407,25 @@ class AppLocalizationsUz extends AppLocalizations { String consent_message(String appName) { return 'Davom etish orqali siz $appName va uning hamkorlaridan siz kiritgan raqamga, shu jumladan avtomatik vositalar orqali, qo‘ng‘iroqlar yoki SMS xabarlar olishingizga rozilik bildirasiz.'; } + + @override + String get otp_code_incomplete => + 'Kod yetarli emas. Iltimos, to‘liq kodni kiriting.'; + + @override + String get passwords_do_not_match => 'Parollar mos kelmadi.'; + + @override + String get reset_password => 'Parolni qayta tiklash'; + + @override + String get new_password => 'Yangi parol'; + + @override + String enter_otp_code(String phoneNumber) { + return '$phoneNumber raqamiga yuborilgan 5 xonali kodni kiriting'; + } + + @override + String get resend_otp_after => 'OTP kodni qayta yuborish mumkin bo‘ladi'; } diff --git a/lib/feature/auth/domain/usecases/verify_otp_code_login_usecase.dart b/lib/feature/auth/domain/usecases/verify_otp_code_login_usecase.dart index 181428d..6cb2544 100644 --- a/lib/feature/auth/domain/usecases/verify_otp_code_login_usecase.dart +++ b/lib/feature/auth/domain/usecases/verify_otp_code_login_usecase.dart @@ -19,7 +19,7 @@ class VerifyOtpCodeForgotPasswordUseCase } class VerifyOtpCodeParams { - final String otpCode; + final dynamic otpCode; final String phoneNumber; VerifyOtpCodeParams({required this.otpCode, required this.phoneNumber}); diff --git a/lib/feature/auth/presentation/blocs/register_bloc/register_bloc.dart b/lib/feature/auth/presentation/blocs/register_bloc/register_bloc.dart new file mode 100644 index 0000000..b7e6f61 --- /dev/null +++ b/lib/feature/auth/presentation/blocs/register_bloc/register_bloc.dart @@ -0,0 +1,35 @@ +import 'package:food_delivery_client/feature/auth/domain/usecases/register_usecase.dart'; +import 'package:food_delivery_client/feature/common/presentation/widgets/w_toastification.dart'; +import 'package:food_delivery_client/food_delivery_client.dart'; + +part 'register_event.dart'; + +part 'register_state.dart'; + +part 'register_bloc.freezed.dart'; + +@injectable +class RegisterBloc extends Bloc { + final RegisterUseCase _registerUseCase; + + RegisterBloc(this._registerUseCase) : super(const RegisterState()) { + on<_Loaded>(_onLoaded); + } + + Future _onLoaded(_Loaded event, Emitter emit) async { + emit(state.copyWith(status: RequestStatus.loading)); + + final response = await _registerUseCase.call(event.params); + + response.fold( + (l) { + showErrorToast(l.errorMessage); + emit(state.copyWith(status: RequestStatus.error)); + }, + (r) { + showSuccessToast(r.message); + emit(state.copyWith(status: RequestStatus.loaded)); + }, + ); + } +} diff --git a/lib/feature/auth/presentation/blocs/register_bloc/register_bloc.freezed.dart b/lib/feature/auth/presentation/blocs/register_bloc/register_bloc.freezed.dart new file mode 100644 index 0000000..b45bbba --- /dev/null +++ b/lib/feature/auth/presentation/blocs/register_bloc/register_bloc.freezed.dart @@ -0,0 +1,535 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// 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 'register_bloc.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; +/// @nodoc +mixin _$RegisterEvent { + + + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is RegisterEvent); +} + + +@override +int get hashCode => runtimeType.hashCode; + +@override +String toString() { + return 'RegisterEvent()'; +} + + +} + +/// @nodoc +class $RegisterEventCopyWith<$Res> { +$RegisterEventCopyWith(RegisterEvent _, $Res Function(RegisterEvent) __); +} + + +/// Adds pattern-matching-related methods to [RegisterEvent]. +extension RegisterEventPatterns on RegisterEvent { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap({TResult Function( _Started value)? started,TResult Function( _Loaded value)? loaded,required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _Started() when started != null: +return started(_that);case _Loaded() when loaded != null: +return loaded(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map({required TResult Function( _Started value) started,required TResult Function( _Loaded value) loaded,}){ +final _that = this; +switch (_that) { +case _Started(): +return started(_that);case _Loaded(): +return loaded(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull({TResult? Function( _Started value)? started,TResult? Function( _Loaded value)? loaded,}){ +final _that = this; +switch (_that) { +case _Started() when started != null: +return started(_that);case _Loaded() when loaded != null: +return loaded(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen({TResult Function()? started,TResult Function( RegisterParams params)? loaded,required TResult orElse(),}) {final _that = this; +switch (_that) { +case _Started() when started != null: +return started();case _Loaded() when loaded != null: +return loaded(_that.params);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when({required TResult Function() started,required TResult Function( RegisterParams params) loaded,}) {final _that = this; +switch (_that) { +case _Started(): +return started();case _Loaded(): +return loaded(_that.params);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull({TResult? Function()? started,TResult? Function( RegisterParams params)? loaded,}) {final _that = this; +switch (_that) { +case _Started() when started != null: +return started();case _Loaded() when loaded != null: +return loaded(_that.params);case _: + return null; + +} +} + +} + +/// @nodoc + + +class _Started implements RegisterEvent { + const _Started(); + + + + + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _Started); +} + + +@override +int get hashCode => runtimeType.hashCode; + +@override +String toString() { + return 'RegisterEvent.started()'; +} + + +} + + + + +/// @nodoc + + +class _Loaded implements RegisterEvent { + const _Loaded(this.params); + + + final RegisterParams params; + +/// Create a copy of RegisterEvent +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$LoadedCopyWith<_Loaded> get copyWith => __$LoadedCopyWithImpl<_Loaded>(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _Loaded&&(identical(other.params, params) || other.params == params)); +} + + +@override +int get hashCode => Object.hash(runtimeType,params); + +@override +String toString() { + return 'RegisterEvent.loaded(params: $params)'; +} + + +} + +/// @nodoc +abstract mixin class _$LoadedCopyWith<$Res> implements $RegisterEventCopyWith<$Res> { + factory _$LoadedCopyWith(_Loaded value, $Res Function(_Loaded) _then) = __$LoadedCopyWithImpl; +@useResult +$Res call({ + RegisterParams params +}); + + + + +} +/// @nodoc +class __$LoadedCopyWithImpl<$Res> + implements _$LoadedCopyWith<$Res> { + __$LoadedCopyWithImpl(this._self, this._then); + + final _Loaded _self; + final $Res Function(_Loaded) _then; + +/// Create a copy of RegisterEvent +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') $Res call({Object? params = null,}) { + return _then(_Loaded( +null == params ? _self.params : params // ignore: cast_nullable_to_non_nullable +as RegisterParams, + )); +} + + +} + +/// @nodoc +mixin _$RegisterState { + + RequestStatus get status; +/// Create a copy of RegisterState +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$RegisterStateCopyWith get copyWith => _$RegisterStateCopyWithImpl(this as RegisterState, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is RegisterState&&(identical(other.status, status) || other.status == status)); +} + + +@override +int get hashCode => Object.hash(runtimeType,status); + +@override +String toString() { + return 'RegisterState(status: $status)'; +} + + +} + +/// @nodoc +abstract mixin class $RegisterStateCopyWith<$Res> { + factory $RegisterStateCopyWith(RegisterState value, $Res Function(RegisterState) _then) = _$RegisterStateCopyWithImpl; +@useResult +$Res call({ + RequestStatus status +}); + + + + +} +/// @nodoc +class _$RegisterStateCopyWithImpl<$Res> + implements $RegisterStateCopyWith<$Res> { + _$RegisterStateCopyWithImpl(this._self, this._then); + + final RegisterState _self; + final $Res Function(RegisterState) _then; + +/// Create a copy of RegisterState +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? status = null,}) { + return _then(_self.copyWith( +status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable +as RequestStatus, + )); +} + +} + + +/// Adds pattern-matching-related methods to [RegisterState]. +extension RegisterStatePatterns on RegisterState { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _RegisterState value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _RegisterState() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _RegisterState value) $default,){ +final _that = this; +switch (_that) { +case _RegisterState(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _RegisterState value)? $default,){ +final _that = this; +switch (_that) { +case _RegisterState() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( RequestStatus status)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _RegisterState() when $default != null: +return $default(_that.status);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( RequestStatus status) $default,) {final _that = this; +switch (_that) { +case _RegisterState(): +return $default(_that.status);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( RequestStatus status)? $default,) {final _that = this; +switch (_that) { +case _RegisterState() when $default != null: +return $default(_that.status);case _: + return null; + +} +} + +} + +/// @nodoc + + +class _RegisterState implements RegisterState { + const _RegisterState({this.status = RequestStatus.initial}); + + +@override@JsonKey() final RequestStatus status; + +/// Create a copy of RegisterState +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$RegisterStateCopyWith<_RegisterState> get copyWith => __$RegisterStateCopyWithImpl<_RegisterState>(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _RegisterState&&(identical(other.status, status) || other.status == status)); +} + + +@override +int get hashCode => Object.hash(runtimeType,status); + +@override +String toString() { + return 'RegisterState(status: $status)'; +} + + +} + +/// @nodoc +abstract mixin class _$RegisterStateCopyWith<$Res> implements $RegisterStateCopyWith<$Res> { + factory _$RegisterStateCopyWith(_RegisterState value, $Res Function(_RegisterState) _then) = __$RegisterStateCopyWithImpl; +@override @useResult +$Res call({ + RequestStatus status +}); + + + + +} +/// @nodoc +class __$RegisterStateCopyWithImpl<$Res> + implements _$RegisterStateCopyWith<$Res> { + __$RegisterStateCopyWithImpl(this._self, this._then); + + final _RegisterState _self; + final $Res Function(_RegisterState) _then; + +/// Create a copy of RegisterState +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? status = null,}) { + return _then(_RegisterState( +status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable +as RequestStatus, + )); +} + + +} + +// dart format on diff --git a/lib/feature/auth/presentation/blocs/register_bloc/register_event.dart b/lib/feature/auth/presentation/blocs/register_bloc/register_event.dart new file mode 100644 index 0000000..ec74eef --- /dev/null +++ b/lib/feature/auth/presentation/blocs/register_bloc/register_event.dart @@ -0,0 +1,8 @@ +part of 'register_bloc.dart'; + +@freezed +class RegisterEvent with _$RegisterEvent { + const factory RegisterEvent.started() = _Started; + const factory RegisterEvent.loaded(RegisterParams params) = _Loaded; + +} diff --git a/lib/feature/auth/presentation/blocs/register_bloc/register_state.dart b/lib/feature/auth/presentation/blocs/register_bloc/register_state.dart new file mode 100644 index 0000000..ff80d35 --- /dev/null +++ b/lib/feature/auth/presentation/blocs/register_bloc/register_state.dart @@ -0,0 +1,8 @@ +part of 'register_bloc.dart'; + +@freezed +abstract class RegisterState with _$RegisterState { + const factory RegisterState({ + @Default(RequestStatus.initial) RequestStatus status, + }) = _RegisterState; +} diff --git a/lib/feature/auth/presentation/blocs/reset_password_bloc/reset_password_bloc.dart b/lib/feature/auth/presentation/blocs/reset_password_bloc/reset_password_bloc.dart new file mode 100644 index 0000000..0c54bfc --- /dev/null +++ b/lib/feature/auth/presentation/blocs/reset_password_bloc/reset_password_bloc.dart @@ -0,0 +1,36 @@ +import 'package:food_delivery_client/feature/auth/domain/usecases/reset_password_usecase.dart'; +import 'package:food_delivery_client/feature/common/presentation/widgets/w_toastification.dart'; +import 'package:food_delivery_client/food_delivery_client.dart'; + +part 'reset_password_event.dart'; + +part 'reset_password_state.dart'; + +part 'reset_password_bloc.freezed.dart'; + +@injectable +class ResetPasswordBloc extends Bloc { + final ResetPasswordUseCase _passwordUseCase; + + ResetPasswordBloc(this._passwordUseCase) : super(const ResetPasswordState()) { + on<_Loaded>(_onLoaded); + } + + Future _onLoaded( + _Loaded event, + Emitter emit, + ) async { + emit(state.copyWith(status: RequestStatus.loading)); + final response = await _passwordUseCase.call(event.params); + response.fold( + (l) { + showErrorToast(l.errorMessage); + emit(state.copyWith(status: RequestStatus.error)); + }, + (r) { + showSuccessToast(r.message); + emit(state.copyWith(status: RequestStatus.loaded)); + }, + ); + } +} diff --git a/lib/feature/auth/presentation/blocs/reset_password_bloc/reset_password_bloc.freezed.dart b/lib/feature/auth/presentation/blocs/reset_password_bloc/reset_password_bloc.freezed.dart new file mode 100644 index 0000000..fb7109f --- /dev/null +++ b/lib/feature/auth/presentation/blocs/reset_password_bloc/reset_password_bloc.freezed.dart @@ -0,0 +1,535 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// 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 'reset_password_bloc.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; +/// @nodoc +mixin _$ResetPasswordEvent { + + + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ResetPasswordEvent); +} + + +@override +int get hashCode => runtimeType.hashCode; + +@override +String toString() { + return 'ResetPasswordEvent()'; +} + + +} + +/// @nodoc +class $ResetPasswordEventCopyWith<$Res> { +$ResetPasswordEventCopyWith(ResetPasswordEvent _, $Res Function(ResetPasswordEvent) __); +} + + +/// Adds pattern-matching-related methods to [ResetPasswordEvent]. +extension ResetPasswordEventPatterns on ResetPasswordEvent { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap({TResult Function( _Started value)? started,TResult Function( _Loaded value)? loaded,required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _Started() when started != null: +return started(_that);case _Loaded() when loaded != null: +return loaded(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map({required TResult Function( _Started value) started,required TResult Function( _Loaded value) loaded,}){ +final _that = this; +switch (_that) { +case _Started(): +return started(_that);case _Loaded(): +return loaded(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull({TResult? Function( _Started value)? started,TResult? Function( _Loaded value)? loaded,}){ +final _that = this; +switch (_that) { +case _Started() when started != null: +return started(_that);case _Loaded() when loaded != null: +return loaded(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen({TResult Function()? started,TResult Function( ResetPasswordParams params)? loaded,required TResult orElse(),}) {final _that = this; +switch (_that) { +case _Started() when started != null: +return started();case _Loaded() when loaded != null: +return loaded(_that.params);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when({required TResult Function() started,required TResult Function( ResetPasswordParams params) loaded,}) {final _that = this; +switch (_that) { +case _Started(): +return started();case _Loaded(): +return loaded(_that.params);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull({TResult? Function()? started,TResult? Function( ResetPasswordParams params)? loaded,}) {final _that = this; +switch (_that) { +case _Started() when started != null: +return started();case _Loaded() when loaded != null: +return loaded(_that.params);case _: + return null; + +} +} + +} + +/// @nodoc + + +class _Started implements ResetPasswordEvent { + const _Started(); + + + + + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _Started); +} + + +@override +int get hashCode => runtimeType.hashCode; + +@override +String toString() { + return 'ResetPasswordEvent.started()'; +} + + +} + + + + +/// @nodoc + + +class _Loaded implements ResetPasswordEvent { + const _Loaded(this.params); + + + final ResetPasswordParams params; + +/// Create a copy of ResetPasswordEvent +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$LoadedCopyWith<_Loaded> get copyWith => __$LoadedCopyWithImpl<_Loaded>(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _Loaded&&(identical(other.params, params) || other.params == params)); +} + + +@override +int get hashCode => Object.hash(runtimeType,params); + +@override +String toString() { + return 'ResetPasswordEvent.loaded(params: $params)'; +} + + +} + +/// @nodoc +abstract mixin class _$LoadedCopyWith<$Res> implements $ResetPasswordEventCopyWith<$Res> { + factory _$LoadedCopyWith(_Loaded value, $Res Function(_Loaded) _then) = __$LoadedCopyWithImpl; +@useResult +$Res call({ + ResetPasswordParams params +}); + + + + +} +/// @nodoc +class __$LoadedCopyWithImpl<$Res> + implements _$LoadedCopyWith<$Res> { + __$LoadedCopyWithImpl(this._self, this._then); + + final _Loaded _self; + final $Res Function(_Loaded) _then; + +/// Create a copy of ResetPasswordEvent +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') $Res call({Object? params = null,}) { + return _then(_Loaded( +null == params ? _self.params : params // ignore: cast_nullable_to_non_nullable +as ResetPasswordParams, + )); +} + + +} + +/// @nodoc +mixin _$ResetPasswordState { + + RequestStatus get status; +/// Create a copy of ResetPasswordState +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$ResetPasswordStateCopyWith get copyWith => _$ResetPasswordStateCopyWithImpl(this as ResetPasswordState, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ResetPasswordState&&(identical(other.status, status) || other.status == status)); +} + + +@override +int get hashCode => Object.hash(runtimeType,status); + +@override +String toString() { + return 'ResetPasswordState(status: $status)'; +} + + +} + +/// @nodoc +abstract mixin class $ResetPasswordStateCopyWith<$Res> { + factory $ResetPasswordStateCopyWith(ResetPasswordState value, $Res Function(ResetPasswordState) _then) = _$ResetPasswordStateCopyWithImpl; +@useResult +$Res call({ + RequestStatus status +}); + + + + +} +/// @nodoc +class _$ResetPasswordStateCopyWithImpl<$Res> + implements $ResetPasswordStateCopyWith<$Res> { + _$ResetPasswordStateCopyWithImpl(this._self, this._then); + + final ResetPasswordState _self; + final $Res Function(ResetPasswordState) _then; + +/// Create a copy of ResetPasswordState +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? status = null,}) { + return _then(_self.copyWith( +status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable +as RequestStatus, + )); +} + +} + + +/// Adds pattern-matching-related methods to [ResetPasswordState]. +extension ResetPasswordStatePatterns on ResetPasswordState { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _ResetPasswordState value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _ResetPasswordState() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _ResetPasswordState value) $default,){ +final _that = this; +switch (_that) { +case _ResetPasswordState(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _ResetPasswordState value)? $default,){ +final _that = this; +switch (_that) { +case _ResetPasswordState() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( RequestStatus status)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _ResetPasswordState() when $default != null: +return $default(_that.status);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( RequestStatus status) $default,) {final _that = this; +switch (_that) { +case _ResetPasswordState(): +return $default(_that.status);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( RequestStatus status)? $default,) {final _that = this; +switch (_that) { +case _ResetPasswordState() when $default != null: +return $default(_that.status);case _: + return null; + +} +} + +} + +/// @nodoc + + +class _ResetPasswordState implements ResetPasswordState { + const _ResetPasswordState({this.status = RequestStatus.initial}); + + +@override@JsonKey() final RequestStatus status; + +/// Create a copy of ResetPasswordState +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$ResetPasswordStateCopyWith<_ResetPasswordState> get copyWith => __$ResetPasswordStateCopyWithImpl<_ResetPasswordState>(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _ResetPasswordState&&(identical(other.status, status) || other.status == status)); +} + + +@override +int get hashCode => Object.hash(runtimeType,status); + +@override +String toString() { + return 'ResetPasswordState(status: $status)'; +} + + +} + +/// @nodoc +abstract mixin class _$ResetPasswordStateCopyWith<$Res> implements $ResetPasswordStateCopyWith<$Res> { + factory _$ResetPasswordStateCopyWith(_ResetPasswordState value, $Res Function(_ResetPasswordState) _then) = __$ResetPasswordStateCopyWithImpl; +@override @useResult +$Res call({ + RequestStatus status +}); + + + + +} +/// @nodoc +class __$ResetPasswordStateCopyWithImpl<$Res> + implements _$ResetPasswordStateCopyWith<$Res> { + __$ResetPasswordStateCopyWithImpl(this._self, this._then); + + final _ResetPasswordState _self; + final $Res Function(_ResetPasswordState) _then; + +/// Create a copy of ResetPasswordState +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? status = null,}) { + return _then(_ResetPasswordState( +status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable +as RequestStatus, + )); +} + + +} + +// dart format on diff --git a/lib/feature/auth/presentation/blocs/reset_password_bloc/reset_password_event.dart b/lib/feature/auth/presentation/blocs/reset_password_bloc/reset_password_event.dart new file mode 100644 index 0000000..6daed9b --- /dev/null +++ b/lib/feature/auth/presentation/blocs/reset_password_bloc/reset_password_event.dart @@ -0,0 +1,8 @@ +part of 'reset_password_bloc.dart'; + +@freezed +class ResetPasswordEvent with _$ResetPasswordEvent { + const factory ResetPasswordEvent.started() = _Started; + + const factory ResetPasswordEvent.loaded(ResetPasswordParams params) = _Loaded; +} diff --git a/lib/feature/auth/presentation/blocs/reset_password_bloc/reset_password_state.dart b/lib/feature/auth/presentation/blocs/reset_password_bloc/reset_password_state.dart new file mode 100644 index 0000000..b3fddb5 --- /dev/null +++ b/lib/feature/auth/presentation/blocs/reset_password_bloc/reset_password_state.dart @@ -0,0 +1,8 @@ +part of 'reset_password_bloc.dart'; + +@freezed +abstract class ResetPasswordState with _$ResetPasswordState { + const factory ResetPasswordState({ + @Default(RequestStatus.initial) RequestStatus status, + }) = _ResetPasswordState; +} diff --git a/lib/feature/auth/presentation/blocs/verify_otp_bloc/verify_otp_bloc.dart b/lib/feature/auth/presentation/blocs/verify_otp_bloc/verify_otp_bloc.dart index 672efef..50cd0a5 100644 --- a/lib/feature/auth/presentation/blocs/verify_otp_bloc/verify_otp_bloc.dart +++ b/lib/feature/auth/presentation/blocs/verify_otp_bloc/verify_otp_bloc.dart @@ -67,6 +67,7 @@ class VerifyOtpBloc extends Bloc { emit(state.copyWith(status: RequestStatus.error)); }, (r) { + showSuccessToast(r.message); emit(state.copyWith(status: RequestStatus.loaded)); add(VerifyOtpEvent.cancelTimer()); }, @@ -86,6 +87,7 @@ class VerifyOtpBloc extends Bloc { emit(state.copyWith(status: RequestStatus.error)); }, (r) { + showSuccessToast(r.message); emit(state.copyWith(status: RequestStatus.loaded)); add(VerifyOtpEvent.cancelTimer()); }, diff --git a/lib/feature/auth/presentation/pages/login_page/widgets/login_body.dart b/lib/feature/auth/presentation/pages/login_page/widgets/login_body.dart index 3442605..5e4c90d 100644 --- a/lib/feature/auth/presentation/pages/login_page/widgets/login_body.dart +++ b/lib/feature/auth/presentation/pages/login_page/widgets/login_body.dart @@ -2,6 +2,7 @@ import 'package:food_delivery_client/core/helpers/formatters.dart'; import 'package:food_delivery_client/core/helpers/validator_helpers.dart'; import 'package:food_delivery_client/feature/auth/presentation/widgets/w_auth_background.dart'; import '../../../../../../food_delivery_client.dart'; +import '../../../../domain/usecases/login_usecase.dart'; import '../../../blocs/login_bloc/login_bloc.dart'; class WLoginBody extends StatefulWidget { @@ -128,16 +129,15 @@ class _WLoginBodyState extends State { ).paddingOnly(left: 10), onPressed: () { if (_formKey.currentState?.validate() ?? false) { - context.go(Routes.main); - // context.read().add( - // LoginEvent.login( - // LoginParams( - // phoneNumber: - // "+998${_phoneController.text.trim().replaceAll(" ", "")}", - // password: _passwordController.text.trim(), - // ), - // ), - // ); + context.read().add( + LoginEvent.login( + LoginParams( + phoneNumber: + "+998${_phoneController.text.trim().replaceAll(" ", "")}", + password: _passwordController.text.trim(), + ), + ), + ); } }, borderRadius: 15, diff --git a/lib/feature/auth/presentation/pages/register_page/register_page.dart b/lib/feature/auth/presentation/pages/register_page/register_page.dart index 6e57e8a..109808c 100644 --- a/lib/feature/auth/presentation/pages/register_page/register_page.dart +++ b/lib/feature/auth/presentation/pages/register_page/register_page.dart @@ -1,6 +1,7 @@ import 'package:food_delivery_client/feature/auth/presentation/pages/register_page/widgets/w_register_body.dart'; import '../../../../../food_delivery_client.dart'; +import '../../blocs/register_bloc/register_bloc.dart'; class RegisterPage extends StatelessWidget { const RegisterPage({super.key, required this.phoneNumber}); @@ -9,6 +10,16 @@ class RegisterPage extends StatelessWidget { @override Widget build(BuildContext context) { - return WLayout(top: false, child: Scaffold(body: WRegisterBody())); + return BlocProvider( + create: (context) => sl(), + child: BlocBuilder( + builder: (context, state) { + return WLayout( + top: false, + child: Scaffold(body: WRegisterBody(phoneNumber: phoneNumber)), + ); + }, + ), + ); } } diff --git a/lib/feature/auth/presentation/pages/register_page/widgets/w_register_body.dart b/lib/feature/auth/presentation/pages/register_page/widgets/w_register_body.dart index 8003d5d..7563ca5 100644 --- a/lib/feature/auth/presentation/pages/register_page/widgets/w_register_body.dart +++ b/lib/feature/auth/presentation/pages/register_page/widgets/w_register_body.dart @@ -1,9 +1,13 @@ import 'package:food_delivery_client/core/helpers/validator_helpers.dart'; +import 'package:food_delivery_client/feature/auth/domain/usecases/register_usecase.dart'; import 'package:food_delivery_client/feature/auth/presentation/widgets/w_auth_background.dart'; import '../../../../../../food_delivery_client.dart'; +import '../../../blocs/register_bloc/register_bloc.dart'; class WRegisterBody extends StatefulWidget { - const WRegisterBody({super.key}); + const WRegisterBody({super.key, required this.phoneNumber}); + + final String phoneNumber; @override State createState() => _WRegisterBodyState(); @@ -40,62 +44,69 @@ class _WRegisterBodyState extends State { @override Widget build(BuildContext context) { - return Form( - key: _formKey, - child: WAuthBackground( - child: SizedBox( - width: context.w, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - 20.verticalSpace, - Align( - alignment: AlignmentGeometry.center, - child: Text( - context.loc.sign_up, - style: AppTextStyles.size24Bold, - ), - ), - 20.verticalSpace, - Text( - context.loc.first_name, - style: AppTextStyles.size14Regular.copyWith( - color: AppColors.c6A6E7F, - ), - ), - 5.verticalSpace, - AppTextFormField( - hintText: context.loc.enter_first_name, - controller: _firstNameController, - borderRadius: AppUtils.kBorderRadius8, - keyBoardType: TextInputType.name, - validator: (value) { - return Validators.validateFields( - _firstNameController.text.trim(), - ); - }, - ), - 10.verticalSpace, - Text( - 'Last name', - style: AppTextStyles.size14Regular.copyWith( - color: AppColors.c6A6E7F, - ), - ), - 5.verticalSpace, - AppTextFormField( - hintText: "Enter LastName", - controller: _lastNameController, - borderRadius: AppUtils.kBorderRadius8, - keyBoardType: TextInputType.text, - validator: (value) { - return Validators.validateFields( - _lastNameController.text.trim(), - ); - }, - ), - 10.verticalSpace, - /* Text( + return BlocConsumer( + listener: (context, state) { + if (state.status.isLoaded()) { + context.go(Routes.login); + } + }, + builder: (context, state) { + return Form( + key: _formKey, + child: WAuthBackground( + child: SizedBox( + width: context.w, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 20.verticalSpace, + Align( + alignment: AlignmentGeometry.center, + child: Text( + context.loc.sign_up, + style: AppTextStyles.size24Bold, + ), + ), + 20.verticalSpace, + Text( + context.loc.first_name, + style: AppTextStyles.size14Regular.copyWith( + color: AppColors.c6A6E7F, + ), + ), + 5.verticalSpace, + AppTextFormField( + hintText: context.loc.enter_first_name, + controller: _firstNameController, + borderRadius: AppUtils.kBorderRadius8, + keyBoardType: TextInputType.name, + validator: (value) { + return Validators.validateFields( + _firstNameController.text.trim(), + ); + }, + ), + 10.verticalSpace, + Text( + 'Last name', + style: AppTextStyles.size14Regular.copyWith( + color: AppColors.c6A6E7F, + ), + ), + 5.verticalSpace, + AppTextFormField( + hintText: "Enter LastName", + controller: _lastNameController, + borderRadius: AppUtils.kBorderRadius8, + keyBoardType: TextInputType.text, + validator: (value) { + return Validators.validateFields( + _lastNameController.text.trim(), + ); + }, + ), + 10.verticalSpace, + /* Text( context.loc.phone_number, style: AppTextStyles.size14Regular.copyWith( color: AppColors.c6A6E7F, @@ -121,88 +132,99 @@ class _WRegisterBodyState extends State { ), */ - 10.verticalSpace, - Text( - context.loc.enter_password, - style: AppTextStyles.size14Regular.copyWith( - color: AppColors.c6A6E7F, - ), - ), - 5.verticalSpace, - AppTextFormField( - obscureText: true, - hintText: context.loc.password, - controller: _passwordController, - borderRadius: AppUtils.kBorderRadius8, - keyBoardType: TextInputType.visiblePassword, - validator: (value) { - return Validators.validatePassword( - _passwordController.text.trim(), - ); - }, - ), - 10.verticalSpace, - - Text( - context.loc.repeat_password, - style: AppTextStyles.size14Regular.copyWith( - color: AppColors.c6A6E7F, - ), - ), - 5.verticalSpace, - AppTextFormField( - obscureText: true, - hintText: context.loc.enter_repeat_password, - controller: _repeatPasswordController, - borderRadius: AppUtils.kBorderRadius8, - keyBoardType: TextInputType.visiblePassword, - validator: (value) { - return Validators.validatePassword( - _repeatPasswordController.text.trim(), - ); - }, - ), - 20.verticalSpace, - AppButton( - name: context.loc.continue_str, - borderRadius: 15, - - backgroundColor: AppColors.c34A853, - trailing: SvgPicture.asset( - AppIcons.icArrowRightLight, - ).paddingOnly(left: 8), - onPressed: () { - if (_formKey.currentState?.validate() ?? false) { - - - } - }, - ), - 20.verticalSpace, - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ + 10.verticalSpace, Text( - context.loc.already_has_account, - style: AppTextStyles.size14Medium, - ), - TextButton( - onPressed: () { - context.pushReplacement(Routes.login); - }, - child: Text( - context.loc.login, - style: AppTextStyles.size15Bold.copyWith( - color: AppColors.c34A853, - ), + context.loc.enter_password, + style: AppTextStyles.size14Regular.copyWith( + color: AppColors.c6A6E7F, ), ), + 5.verticalSpace, + AppTextFormField( + obscureText: true, + hintText: context.loc.password, + controller: _passwordController, + borderRadius: AppUtils.kBorderRadius8, + keyBoardType: TextInputType.visiblePassword, + validator: (value) { + return Validators.validatePassword( + _passwordController.text.trim(), + ); + }, + ), + 10.verticalSpace, + + Text( + context.loc.repeat_password, + style: AppTextStyles.size14Regular.copyWith( + color: AppColors.c6A6E7F, + ), + ), + 5.verticalSpace, + AppTextFormField( + obscureText: true, + hintText: context.loc.enter_repeat_password, + controller: _repeatPasswordController, + borderRadius: AppUtils.kBorderRadius8, + keyBoardType: TextInputType.visiblePassword, + validator: (value) { + return Validators.validateRepeatPassword( + _repeatPasswordController.text.trim(), + _passwordController.text.trim(), + ); + }, + ), + 20.verticalSpace, + AppButton( + isLoading: state.status.isLoading(), + name: context.loc.continue_str, + borderRadius: 15, + backgroundColor: AppColors.c34A853, + trailing: SvgPicture.asset( + AppIcons.icArrowRightLight, + ).paddingOnly(left: 8), + onPressed: () { + if (_formKey.currentState?.validate() ?? false) { + context.read().add( + RegisterEvent.loaded( + RegisterParams( + phoneNumber: widget.phoneNumber, + password: _passwordController.text.trim(), + firstName: _firstNameController.text.trim(), + lastName: _lastNameController.text.trim(), + ), + ), + ); + } + }, + ), + 20.verticalSpace, + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + context.loc.already_has_account, + style: AppTextStyles.size14Medium, + ), + TextButton( + onPressed: () { + context.pushReplacement(Routes.login); + }, + child: Text( + context.loc.login, + style: AppTextStyles.size15Bold.copyWith( + color: AppColors.c34A853, + ), + ), + ), + ], + ), ], - ), - ], - ).paddingSymmetric(horizontal: 16), - ), - ), + ).paddingSymmetric(horizontal: 16), + ), + ), + ); + }, ); } } diff --git a/lib/feature/auth/presentation/pages/reset_password_page/reset_password_page.dart b/lib/feature/auth/presentation/pages/reset_password_page/reset_password_page.dart index 12ed4b4..6e139f4 100644 --- a/lib/feature/auth/presentation/pages/reset_password_page/reset_password_page.dart +++ b/lib/feature/auth/presentation/pages/reset_password_page/reset_password_page.dart @@ -1,21 +1,137 @@ +import 'package:food_delivery_client/core/helpers/validator_helpers.dart'; +import 'package:food_delivery_client/feature/auth/domain/usecases/reset_password_usecase.dart'; +import 'package:food_delivery_client/feature/auth/presentation/blocs/reset_password_bloc/reset_password_bloc.dart'; import 'package:food_delivery_client/feature/auth/presentation/widgets/w_auth_background.dart'; import '../../../../../food_delivery_client.dart'; -class ResetPasswordPage extends StatelessWidget { +class ResetPasswordPage extends StatefulWidget { const ResetPasswordPage({super.key, required this.phoneNumber}); - final String phoneNumber; + + final String phoneNumber; + + @override + State createState() => _ResetPasswordPageState(); +} + +class _ResetPasswordPageState extends State { + late TextEditingController _passwordController; + late TextEditingController _repeatPasswordController; + final _formKey = GlobalKey(); + + @override + void initState() { + _passwordController = TextEditingController(); + _repeatPasswordController = TextEditingController(); + super.initState(); + } + + @override + void dispose() { + _passwordController.dispose(); + _repeatPasswordController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { - return WLayout( - top: false, - child: Scaffold( - body: WAuthBackground( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [], - ), - ), + return BlocProvider( + create: (context) => sl(), + child: BlocConsumer( + listener: (context, state) { + if (state.status.isLoaded()) { + context.go(Routes.login); + } + }, + builder: (context, state) { + return Form( + key: _formKey, + child: WLayout( + top: false, + child: Scaffold( + body: WAuthBackground( + child: SizedBox( + width: context.w, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 25.verticalSpace, + Text( + context.loc.reset_password, + style: AppTextStyles.size20Medium, + ), + 20.verticalSpace, + Text( + context.loc.new_password, + style: AppTextStyles.size14Regular.copyWith( + color: AppColors.c6A6E7F, + ), + ), + 5.verticalSpace, + AppTextFormField( + obscureText: true, + controller: _passwordController, + borderRadius: AppUtils.kBorderRadius8, + hintText: context.loc.enter_password, + keyBoardType: TextInputType.visiblePassword, + validator: (value) { + return Validators.validatePassword( + _passwordController.text.trim(), + ); + }, + ), + 20.verticalSpace, + Text( + context.loc.password, + style: AppTextStyles.size14Regular.copyWith( + color: AppColors.c6A6E7F, + ), + ), + 5.verticalSpace, + AppTextFormField( + obscureText: true, + controller: _repeatPasswordController, + borderRadius: AppUtils.kBorderRadius8, + hintText: context.loc.enter_password, + keyBoardType: TextInputType.visiblePassword, + validator: (value) { + return Validators.validateRepeatPassword( + _repeatPasswordController.text.trim(), + _passwordController.text.trim(), + ); + }, + ), + 25.verticalSpace, + AppButton( + name: context.loc.continue_str, + trailing: SvgPicture.asset( + AppIcons.icArrowRightLight, + ).paddingOnly(left: 8), + isLoading: state.status.isLoading(), + onPressed: () { + if (_formKey.currentState?.validate() ?? false) { + context.read().add( + ResetPasswordEvent.loaded( + ResetPasswordParams( + phoneNumber: widget.phoneNumber, + newPassword: _passwordController.text + .trim(), + ), + ), + ); + } + }, + backgroundColor: AppColors.c34A853, + borderRadius: 15, + ), + ], + ).paddingSymmetric(horizontal: 16), + ), + ), + ), + ), + ); + }, ), ); } diff --git a/lib/feature/auth/presentation/pages/verify_otp_code_page/verify_otp_code_page.dart b/lib/feature/auth/presentation/pages/verify_otp_code_page/verify_otp_code_page.dart index 8379572..30c5d87 100644 --- a/lib/feature/auth/presentation/pages/verify_otp_code_page/verify_otp_code_page.dart +++ b/lib/feature/auth/presentation/pages/verify_otp_code_page/verify_otp_code_page.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:food_delivery_client/core/helpers/time_formatters.dart'; import 'package:food_delivery_client/feature/auth/domain/usecases/verify_otp_code_login_usecase.dart'; import 'package:food_delivery_client/feature/auth/presentation/blocs/verify_otp_bloc/verify_otp_bloc.dart'; @@ -68,7 +69,7 @@ class _VerifyOtpCodePageState extends State { children: [ 40.verticalSpace, Text( - "Enter the 5-digit code sent to you at ${widget.params.phoneNumber}", + context.loc.enter_otp_code(widget.params.phoneNumber), style: AppTextStyles.size20Medium, ), 20.verticalSpace, @@ -98,36 +99,47 @@ class _VerifyOtpCodePageState extends State { }, ), 10.verticalSpace, - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text("You can resend otp after"), + SizedBox( + height: 40, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + context.loc.resend_otp_after, + style: AppTextStyles.size14Regular, + ), - if (state.time != 0) - Text(TimeFormatters.formatMinutesToTime(state.time)), + if (state.time != 0) + Text( + TimeFormatters.formatMinutesToTime(state.time), + ), - if (state.time == 0) - state.resendStatus.isLoading() - ? CircularProgressIndicator.adaptive() - : TextButton( - onPressed: () { - if (widget.params.isRegister) { - context.read().add( - VerifyOtpEvent.resendRegister( - widget.params.phoneNumber, - ), - ); - } else { - context.read().add( - VerifyOtpEvent.resendForgot( - widget.params.phoneNumber, - ), - ); - } - }, - child: Text("Resend"), - ), - ], + if (state.time == 0) + state.resendStatus.isLoading() + ? CircularProgressIndicator.adaptive() + : IconButton( + onPressed: () { + if (widget.params.isRegister) { + context.read().add( + VerifyOtpEvent.resendRegister( + widget.params.phoneNumber, + ), + ); + } else { + context.read().add( + VerifyOtpEvent.resendForgot( + widget.params.phoneNumber, + ), + ); + } + }, + icon: Icon( + CupertinoIcons.restart, + color: AppColors.c000000, + ), + ), + ], + ), ), 20.verticalSpace, @@ -153,9 +165,8 @@ class _VerifyOtpCodePageState extends State { VerifyOtpEvent.verifyOtpReset( VerifyOtpCodeParams( phoneNumber: widget.params.phoneNumber, - otpCode: _controller.text.trim().substring( - 0, - 4, + otpCode: int.tryParse( + _controller.text.trim().substring(0, 4), ), ), ), @@ -163,6 +174,7 @@ class _VerifyOtpCodePageState extends State { } } }, + isLoading: state.status.isLoading(), backgroundColor: AppColors.c34A853, borderRadius: 15, trailing: SvgPicture.asset( diff --git a/lib/feature/auth/presentation/pages/verify_phone_number_page/verify_phone_page.dart b/lib/feature/auth/presentation/pages/verify_phone_number_page/verify_phone_page.dart index 76b528a..141576d 100644 --- a/lib/feature/auth/presentation/pages/verify_phone_number_page/verify_phone_page.dart +++ b/lib/feature/auth/presentation/pages/verify_phone_number_page/verify_phone_page.dart @@ -115,6 +115,7 @@ class _VerifyPhoneNumberPageState extends State { } } }, + isLoading: state.status.isLoading(), name: context.loc.continue_str, borderRadius: 15, backgroundColor: AppColors.c34A853, diff --git a/lib/feature/auth/presentation/widgets/w_auth_background.dart b/lib/feature/auth/presentation/widgets/w_auth_background.dart index c34e1d4..652cf0c 100644 --- a/lib/feature/auth/presentation/widgets/w_auth_background.dart +++ b/lib/feature/auth/presentation/widgets/w_auth_background.dart @@ -14,8 +14,8 @@ class WAuthBackground extends StatelessWidget { child: Image.asset(AppImages.imgBurger2, fit: BoxFit.cover), ), SingleChildScrollView( - // keyboardDismissBehavior: - // ScrollViewKeyboardDismissBehavior.onDrag, + keyboardDismissBehavior: + ScrollViewKeyboardDismissBehavior.onDrag, child: Column( children: [ SizedBox(height: context.h * .2),