Change password

This commit is contained in:
2026-02-23 09:46:41 +01:00
parent 471aa1067c
commit 8445af3f5a
21 changed files with 976 additions and 9 deletions

View File

@@ -1,3 +1,4 @@
import 'package:account/src/features/change_password/domain/models/entities/change_password_request_entity.dart';
import 'package:legacy_shared/src/data/models/entities/device_entity.dart';
import 'package:account/src/features/linked_devices/domain/entities/update_device_request_entity.dart';
import 'package:account/src/features/personal_data/domain/entities/update_user_request_entity.dart';
@@ -15,4 +16,6 @@ abstract class AccountRemoteDatasource {
Future<List<UserEntity>> getAppUsers({required String userId});
Future<void> deleteAppUser({required String userId});
Future<void> changePassword({required String userId, required ChangePasswordRequestEntity request});
}

View File

@@ -1,10 +1,12 @@
import 'dart:convert';
import 'package:account/src/core/data/datasource/account_remote_datasource.dart';
import 'package:account/src/core/data/models/change_password_request_model.dart';
import 'package:account/src/core/data/models/get_app_users_response_model.dart';
import 'package:account/src/core/data/models/get_linked_devices_response_model.dart';
import 'package:account/src/core/data/models/update_device_request_model.dart';
import 'package:account/src/core/data/models/update_user_request_model.dart';
import 'package:account/src/features/change_password/domain/models/entities/change_password_request_entity.dart';
import 'package:legacy_shared/src/data/models/entities/device_entity.dart';
import 'package:account/src/features/linked_devices/domain/entities/update_device_request_entity.dart';
import 'package:account/src/features/personal_data/domain/entities/update_user_request_entity.dart';
@@ -21,7 +23,7 @@ class AccountRemoteDatasourceImpl implements AccountRemoteDatasource {
@override
Future<List<DeviceEntity>> getLinkedDevices({required String userId}) async {
try {
/*final response = await _repository.get<Map<String, dynamic>>(
final response = await _repository.get<Map<String, dynamic>>(
'/$userId/devices',
);
final data = response.data!['items'];
@@ -29,8 +31,8 @@ class AccountRemoteDatasourceImpl implements AccountRemoteDatasource {
throw Exception('Empty response from /:userId/devices');
}
final model = GetLinkedDevicesResponseModel.fromJson(data);*/
final model = GetLinkedDevicesResponseModel(items: [
final model = GetLinkedDevicesResponseModel.fromJson(data);
/*final model = GetLinkedDevicesResponseModel(items: [
GetLinkedDevicesItemResponseModel(
identificator: '1111',
carrierName: 'Carlos',
@@ -79,7 +81,7 @@ class AccountRemoteDatasourceImpl implements AccountRemoteDatasource {
isDisconnect: false,
)
),
]);
]);*/
return model.toEntity();
} on DioException catch (error) {
throw _mapDioError(
@@ -191,6 +193,19 @@ class AccountRemoteDatasourceImpl implements AccountRemoteDatasource {
throw _mapDioError(error, defaultMessage: 'Error to delete device');
}
}
@override
Future<void> changePassword({required String userId, required ChangePasswordRequestEntity request}) async {
try {
final body = request.toModel().toJson();
await _repository.put<void>(
'/auth/change-password',
body: body,
);
} on DioException catch (error) {
throw _mapDioError(error, defaultMessage: 'Error to change password');
}
}
}
Exception _mapDioError(DioException error, {required String defaultMessage}) {

View File

@@ -0,0 +1,21 @@
import 'package:account/src/features/change_password/domain/models/entities/change_password_request_entity.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'change_password_request_model.freezed.dart';
part 'change_password_request_model.g.dart';
@freezed
abstract class ChangePasswordRequestModel with _$ChangePasswordRequestModel {
const factory ChangePasswordRequestModel({
required String password,
}) = _ChangePasswordRequestModel;
factory ChangePasswordRequestModel.fromJson(Map<String, dynamic> json) =>
_$ChangePasswordRequestModelFromJson(json);
}
extension ChangePasswordRequestModelMapper on ChangePasswordRequestEntity {
ChangePasswordRequestModel toModel() => ChangePasswordRequestModel(
password: password,
);
}

View File

@@ -0,0 +1,277 @@
// 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 'change_password_request_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$ChangePasswordRequestModel {
String get password;
/// Create a copy of ChangePasswordRequestModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ChangePasswordRequestModelCopyWith<ChangePasswordRequestModel> get copyWith => _$ChangePasswordRequestModelCopyWithImpl<ChangePasswordRequestModel>(this as ChangePasswordRequestModel, _$identity);
/// Serializes this ChangePasswordRequestModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ChangePasswordRequestModel&&(identical(other.password, password) || other.password == password));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,password);
@override
String toString() {
return 'ChangePasswordRequestModel(password: $password)';
}
}
/// @nodoc
abstract mixin class $ChangePasswordRequestModelCopyWith<$Res> {
factory $ChangePasswordRequestModelCopyWith(ChangePasswordRequestModel value, $Res Function(ChangePasswordRequestModel) _then) = _$ChangePasswordRequestModelCopyWithImpl;
@useResult
$Res call({
String password
});
}
/// @nodoc
class _$ChangePasswordRequestModelCopyWithImpl<$Res>
implements $ChangePasswordRequestModelCopyWith<$Res> {
_$ChangePasswordRequestModelCopyWithImpl(this._self, this._then);
final ChangePasswordRequestModel _self;
final $Res Function(ChangePasswordRequestModel) _then;
/// Create a copy of ChangePasswordRequestModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? password = null,}) {
return _then(_self.copyWith(
password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// Adds pattern-matching-related methods to [ChangePasswordRequestModel].
extension ChangePasswordRequestModelPatterns on ChangePasswordRequestModel {
/// 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 extends Object?>(TResult Function( _ChangePasswordRequestModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ChangePasswordRequestModel() 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 extends Object?>(TResult Function( _ChangePasswordRequestModel value) $default,){
final _that = this;
switch (_that) {
case _ChangePasswordRequestModel():
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 extends Object?>(TResult? Function( _ChangePasswordRequestModel value)? $default,){
final _that = this;
switch (_that) {
case _ChangePasswordRequestModel() 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 extends Object?>(TResult Function( String password)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ChangePasswordRequestModel() when $default != null:
return $default(_that.password);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 extends Object?>(TResult Function( String password) $default,) {final _that = this;
switch (_that) {
case _ChangePasswordRequestModel():
return $default(_that.password);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 extends Object?>(TResult? Function( String password)? $default,) {final _that = this;
switch (_that) {
case _ChangePasswordRequestModel() when $default != null:
return $default(_that.password);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _ChangePasswordRequestModel implements ChangePasswordRequestModel {
const _ChangePasswordRequestModel({required this.password});
factory _ChangePasswordRequestModel.fromJson(Map<String, dynamic> json) => _$ChangePasswordRequestModelFromJson(json);
@override final String password;
/// Create a copy of ChangePasswordRequestModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ChangePasswordRequestModelCopyWith<_ChangePasswordRequestModel> get copyWith => __$ChangePasswordRequestModelCopyWithImpl<_ChangePasswordRequestModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$ChangePasswordRequestModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ChangePasswordRequestModel&&(identical(other.password, password) || other.password == password));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,password);
@override
String toString() {
return 'ChangePasswordRequestModel(password: $password)';
}
}
/// @nodoc
abstract mixin class _$ChangePasswordRequestModelCopyWith<$Res> implements $ChangePasswordRequestModelCopyWith<$Res> {
factory _$ChangePasswordRequestModelCopyWith(_ChangePasswordRequestModel value, $Res Function(_ChangePasswordRequestModel) _then) = __$ChangePasswordRequestModelCopyWithImpl;
@override @useResult
$Res call({
String password
});
}
/// @nodoc
class __$ChangePasswordRequestModelCopyWithImpl<$Res>
implements _$ChangePasswordRequestModelCopyWith<$Res> {
__$ChangePasswordRequestModelCopyWithImpl(this._self, this._then);
final _ChangePasswordRequestModel _self;
final $Res Function(_ChangePasswordRequestModel) _then;
/// Create a copy of ChangePasswordRequestModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? password = null,}) {
return _then(_ChangePasswordRequestModel(
password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
// dart format on

View File

@@ -0,0 +1,15 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'change_password_request_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_ChangePasswordRequestModel _$ChangePasswordRequestModelFromJson(
Map<String, dynamic> json,
) => _ChangePasswordRequestModel(password: json['password'] as String);
Map<String, dynamic> _$ChangePasswordRequestModelToJson(
_ChangePasswordRequestModel instance,
) => <String, dynamic>{'password': instance.password};

View File

@@ -1,5 +1,6 @@
import 'package:account/src/core/data/datasource/account_remote_datasource.dart';
import 'package:account/src/core/domain/repositories/account_repository.dart';
import 'package:account/src/features/change_password/domain/models/entities/change_password_request_entity.dart';
import 'package:legacy_shared/src/data/models/entities/device_entity.dart';
import 'package:account/src/features/linked_devices/domain/entities/update_device_request_entity.dart';
import 'package:account/src/features/personal_data/domain/entities/update_user_request_entity.dart';
@@ -39,4 +40,9 @@ class AccountRepositoryImpl implements AccountRepository {
Future<void> deleteAppUser({required String userId}) {
return _remote.deleteAppUser(userId: userId);
}
@override
Future<void> changePassword({required String userId, required ChangePasswordRequestEntity request}) {
return _remote.changePassword(userId: userId, request: request);
}
}

View File

@@ -1,3 +1,4 @@
import 'package:account/src/features/change_password/domain/models/entities/change_password_request_entity.dart';
import 'package:legacy_shared/src/data/models/entities/device_entity.dart';
import 'package:account/src/features/linked_devices/domain/entities/update_device_request_entity.dart';
import 'package:account/src/features/personal_data/domain/entities/update_user_request_entity.dart';
@@ -8,11 +9,19 @@ abstract class AccountRepository {
Future<void> deleteDevice({required String userId, required String deviceId});
Future<void> updateDevice({required String userId, required String deviceId, required UpdateDeviceRequestEntity request});
Future<void> updateDevice({
required String userId,
required String deviceId,
required UpdateDeviceRequestEntity request
});
Future<void> updateUser({required String userId, required UpdateUserRequestEntity request});
Future<List<UserEntity>> getAppUsers({required String userId});
Future<void> deleteAppUser({required String userId});
Future<void> changePassword({required String userId,
required ChangePasswordRequestEntity request
});
}

View File

@@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:navigation/navigation.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:utils/utils.dart';
import 'package:flutter/services.dart';
import 'package:qr_flutter/qr_flutter.dart';
@@ -35,7 +36,7 @@ class AccountSettingsScreen extends ConsumerWidget {
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
onPressed: (){navigationContract.pushTo(AppRoutes.changePassword);},
icon: Icons.lock,
text: I18n.legacyChangePassword
),
@@ -59,7 +60,12 @@ class AccountSettingsScreen extends ConsumerWidget {
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
onPressed: () async {
final Uri url = Uri.parse('https://savefamilygps.com/pages/politica-de-privacidad-reloj-gps-infantil-localizador-savefamily');
if (!await launchUrl(url)) {
throw Exception('Could not launch $url');
}
},
icon: SFIcons.privacy,
text: I18n.legacyPrivacyPolicy
),
@@ -68,7 +74,11 @@ class AccountSettingsScreen extends ConsumerWidget {
onPressed: (){
showDialog(context: context, builder: (context)=>Dialog(
backgroundColor: Colors.transparent,
child: RegCodeDialog(regCode: selectedDevice?.id ?? '', deviceId: selectedDevice?.identificator ?? '', name: selectedDevice?.carrierName ?? ''),
child: RegCodeDialog(
regCode: selectedDevice?.id ?? '',
deviceId: selectedDevice?.identificator ?? '',
name: selectedDevice?.carrierName ?? ''
),
));
},
icon: Icons.qr_code,
@@ -77,7 +87,7 @@ class AccountSettingsScreen extends ConsumerWidget {
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: SFIcons.privacy,
icon: Icons.no_accounts,
text: I18n.legacyDeleteAccount
),
],

View File

@@ -0,0 +1,18 @@
import 'package:account/src/features/change_password/presentation/change_password_screen.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:go_router/go_router.dart';
import 'package:navigation/navigation.dart';
class ChangePasswordBuilder {
const ChangePasswordBuilder();
Page<void> buildPage(BuildContext context, GoRouterState state) {
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
return MaterialPage<void>(
key: state.pageKey,
child: ChangePasswordScreen(navigationContract: navigationContract),
);
}
}

View File

@@ -0,0 +1,8 @@
import 'package:account/src/features/change_password/domain/models/entities/change_password_request_entity.dart';
abstract class ChangePasswordUseCase {
Future<void> changePassword({
required String userId,
required ChangePasswordRequestEntity request,
});
}

View File

@@ -0,0 +1,14 @@
import 'package:account/src/core/domain/repositories/account_repository.dart';
import 'package:account/src/features/change_password/domain/models/entities/change_password_request_entity.dart';
import 'package:account/src/features/change_password/domain/change_password_use_case.dart';
class ChangePasswordUseCaseImpl implements ChangePasswordUseCase {
ChangePasswordUseCaseImpl(this._repository);
final AccountRepository _repository;
@override
Future<void> changePassword({required String userId, required ChangePasswordRequestEntity request}) {
return _repository.changePassword(userId: userId, request: request);
}
}

View File

@@ -0,0 +1,10 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'change_password_request_entity.freezed.dart';
@freezed
abstract class ChangePasswordRequestEntity with _$ChangePasswordRequestEntity {
const factory ChangePasswordRequestEntity({
required String password,
}) = _ChangePasswordRequestEntity;
}

View File

@@ -0,0 +1 @@
part of 'change_password_request_entity.dart';

View File

@@ -0,0 +1,75 @@
import 'package:account/src/features/change_password/presentation/state/change_password_view_model.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:navigation/navigation.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
class ChangePasswordScreen extends ConsumerWidget {
final NavigationContract navigationContract;
const ChangePasswordScreen({super.key, required this.navigationContract});
@override
Widget build(BuildContext context, WidgetRef ref) {
final vm = ref.read(changePasswordViewModelProvider.notifier);
final state = ref.watch(changePasswordViewModelProvider);
final theme = ref.watch(themePortProvider);
return PageLayout(
title: context.translate(I18n.legacyChangePassword),
body: Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 28, vertical: 10),
big: EdgeInsets.symmetric(horizontal: 27, vertical: 8)
),
child: SingleChildScrollView(child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CustomTextField(
controller: vm.currentPasswordController,
hint: '********',
label: context.translate(I18n.password),
),
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 22)),
CustomTextField(
controller: vm.newPasswordController,
hint: '********',
label: context.translate(I18n.newPassword),
),
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 22)),
CustomTextField(
controller: vm.repeatPasswordController,
hint: '********',
label: context.translate(I18n.repeatPassword),
),
if (state.errorMessage.isNotEmpty) ...[
const SizedBox(height: 8),
Text(
state.errorMessage,
textAlign: TextAlign.center,
style: const TextStyle(
color: Color.fromRGBO(239, 17, 17, 1),
fontSize: 12,
),
),
],
],
))
),
footer: PrimaryButton(
onPressed: () async {
final bool res = await vm.changePassword();
if (res){
Navigator.pop(context);
}
},
text: context.translate('OK'),
color: Color(0xFF588EA5)
),
);
}
}

View File

@@ -0,0 +1,9 @@
import 'package:account/src/core/providers/account_repository_provider.dart';
import 'package:account/src/features/change_password/domain/change_password_use_case.dart';
import 'package:account/src/features/change_password/domain/change_password_use_case_impl.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final changePasswordUseCaseProvider = Provider.autoDispose<ChangePasswordUseCase>((ref) {
final accountRepository = ref.read(accountRepositoryProvider);
return ChangePasswordUseCaseImpl(accountRepository);
});

View File

@@ -0,0 +1,143 @@
import 'package:account/src/features/change_password/domain/change_password_use_case.dart';
import 'package:account/src/features/change_password/domain/models/entities/change_password_request_entity.dart';
import 'package:account/src/features/change_password/presentation/providers/change_password_use_case_provider.dart';
import 'package:account/src/features/change_password/presentation/state/change_password_view_state.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_shared/legacy_shared.dart';
// import 'package:sf_localizations/sf_localizations.dart';
final changePasswordViewModelProvider =
NotifierProvider.autoDispose<ChangePasswordViewModel, ChangePasswordViewState>(
ChangePasswordViewModel.new,
);
class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
late final ChangePasswordUseCase _changePasswordUseCase;
late final TextEditingController currentPasswordController;
late final TextEditingController newPasswordController;
late final TextEditingController repeatPasswordController;
late final TextEditingController passwordController;
@override
ChangePasswordViewState build() {
_changePasswordUseCase = ref.read(changePasswordUseCaseProvider);
currentPasswordController = TextEditingController();
currentPasswordController.addListener(_onCurrentPasswordChanged);
newPasswordController = TextEditingController();
newPasswordController.addListener(_onNewPasswordChanged);
repeatPasswordController = TextEditingController();
repeatPasswordController.addListener(_onRepeatPasswordController);
ref.read(loggedUserProvider.future)
.then(setUser);
ref.onDispose(disposeControllers);
return const ChangePasswordViewState();
}
void setUser(UserEntity user) {
state = state.copyWith(loggedUser: user);
}
void _onCurrentPasswordChanged() {
final value = currentPasswordController.text;
if (value == state.currentPassword) return;
state = state.copyWith(
currentPassword: value,
);
}
void _onNewPasswordChanged() {
final value = newPasswordController.text;
if (value == state.newPassword) return;
state = state.copyWith(
newPassword: value,
);
}
void _onRepeatPasswordController() {
final value = repeatPasswordController.text;
if (value == state.repeatPassword) return;
state = state.copyWith(
repeatPassword: value,
);
}
bool _validateForm() {
if (state.currentPassword.trim().isEmpty){
state = state.copyWith(errorMessage: 'errorMessageCurrentPasswordIsEmpty');
return false;
}
if (state.newPassword.trim().isEmpty){
state = state.copyWith(errorMessage: 'errorMessageNewPasswordIsEmpty');
return false;
}
if (state.repeatPassword.trim().isEmpty){
state = state.copyWith(errorMessage: 'errorMessageRepeatPasswordIsEmpty');
return false;
}
if (state.newPassword.trim() != state.repeatPassword.trim()){
state = state.copyWith(errorMessage: 'errorMessagePasswordsDontMatch');
return false;
}
return true;
}
ChangePasswordRequestEntity _toRequest() {
return ChangePasswordRequestEntity(
password: state.newPassword.trim(),
);
}
Future<bool> changePassword() async {
if (state.isLoading) return false;
if (!_validateForm()) return false;
try {
final request = _toRequest();
await _changePasswordUseCase.changePassword(userId: state.loggedUser!.id, request: request);
ref.invalidate(loggedUserProvider);
return true;
} catch (e) {
if (!ref.mounted) return false;
_finishWithError(message: e.toString());
return false;
}
}
void _finishWithError({required String message}) {
state = state.copyWith(
isLoading: false,
errorMessage: message,
);
}
void disposeControllers() {
currentPasswordController.removeListener(_onCurrentPasswordChanged);
currentPasswordController.dispose();
newPasswordController.removeListener(_onNewPasswordChanged);
newPasswordController.dispose();
repeatPasswordController.removeListener(_onRepeatPasswordController);
repeatPasswordController.dispose();
}
}

View File

@@ -0,0 +1,16 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:legacy_shared/legacy_shared.dart';
part 'change_password_view_state.freezed.dart';
@freezed
abstract class ChangePasswordViewState with _$ChangePasswordViewState {
const factory ChangePasswordViewState({
UserEntity? loggedUser,
@Default(false) bool isLoading,
@Default('') String currentPassword,
@Default('') String newPassword,
@Default('') String repeatPassword,
@Default('') String errorMessage
}) = _ChangePasswordViewState;
}

View File

@@ -0,0 +1,310 @@
// 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 'change_password_view_state.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$ChangePasswordViewState {
UserEntity? get loggedUser; bool get isLoading; String get currentPassword; String get newPassword; String get repeatPassword; String get errorMessage;
/// Create a copy of ChangePasswordViewState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ChangePasswordViewStateCopyWith<ChangePasswordViewState> get copyWith => _$ChangePasswordViewStateCopyWithImpl<ChangePasswordViewState>(this as ChangePasswordViewState, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ChangePasswordViewState&&(identical(other.loggedUser, loggedUser) || other.loggedUser == loggedUser)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.currentPassword, currentPassword) || other.currentPassword == currentPassword)&&(identical(other.newPassword, newPassword) || other.newPassword == newPassword)&&(identical(other.repeatPassword, repeatPassword) || other.repeatPassword == repeatPassword)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,loggedUser,isLoading,currentPassword,newPassword,repeatPassword,errorMessage);
@override
String toString() {
return 'ChangePasswordViewState(loggedUser: $loggedUser, isLoading: $isLoading, currentPassword: $currentPassword, newPassword: $newPassword, repeatPassword: $repeatPassword, errorMessage: $errorMessage)';
}
}
/// @nodoc
abstract mixin class $ChangePasswordViewStateCopyWith<$Res> {
factory $ChangePasswordViewStateCopyWith(ChangePasswordViewState value, $Res Function(ChangePasswordViewState) _then) = _$ChangePasswordViewStateCopyWithImpl;
@useResult
$Res call({
UserEntity? loggedUser, bool isLoading, String currentPassword, String newPassword, String repeatPassword, String errorMessage
});
$UserEntityCopyWith<$Res>? get loggedUser;
}
/// @nodoc
class _$ChangePasswordViewStateCopyWithImpl<$Res>
implements $ChangePasswordViewStateCopyWith<$Res> {
_$ChangePasswordViewStateCopyWithImpl(this._self, this._then);
final ChangePasswordViewState _self;
final $Res Function(ChangePasswordViewState) _then;
/// Create a copy of ChangePasswordViewState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? loggedUser = freezed,Object? isLoading = null,Object? currentPassword = null,Object? newPassword = null,Object? repeatPassword = null,Object? errorMessage = null,}) {
return _then(_self.copyWith(
loggedUser: freezed == loggedUser ? _self.loggedUser : loggedUser // ignore: cast_nullable_to_non_nullable
as UserEntity?,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,currentPassword: null == currentPassword ? _self.currentPassword : currentPassword // ignore: cast_nullable_to_non_nullable
as String,newPassword: null == newPassword ? _self.newPassword : newPassword // ignore: cast_nullable_to_non_nullable
as String,repeatPassword: null == repeatPassword ? _self.repeatPassword : repeatPassword // ignore: cast_nullable_to_non_nullable
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));
}
/// Create a copy of ChangePasswordViewState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$UserEntityCopyWith<$Res>? get loggedUser {
if (_self.loggedUser == null) {
return null;
}
return $UserEntityCopyWith<$Res>(_self.loggedUser!, (value) {
return _then(_self.copyWith(loggedUser: value));
});
}
}
/// Adds pattern-matching-related methods to [ChangePasswordViewState].
extension ChangePasswordViewStatePatterns on ChangePasswordViewState {
/// 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 extends Object?>(TResult Function( _ChangePasswordViewState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ChangePasswordViewState() 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 extends Object?>(TResult Function( _ChangePasswordViewState value) $default,){
final _that = this;
switch (_that) {
case _ChangePasswordViewState():
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 extends Object?>(TResult? Function( _ChangePasswordViewState value)? $default,){
final _that = this;
switch (_that) {
case _ChangePasswordViewState() 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 extends Object?>(TResult Function( UserEntity? loggedUser, bool isLoading, String currentPassword, String newPassword, String repeatPassword, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ChangePasswordViewState() when $default != null:
return $default(_that.loggedUser,_that.isLoading,_that.currentPassword,_that.newPassword,_that.repeatPassword,_that.errorMessage);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 extends Object?>(TResult Function( UserEntity? loggedUser, bool isLoading, String currentPassword, String newPassword, String repeatPassword, String errorMessage) $default,) {final _that = this;
switch (_that) {
case _ChangePasswordViewState():
return $default(_that.loggedUser,_that.isLoading,_that.currentPassword,_that.newPassword,_that.repeatPassword,_that.errorMessage);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 extends Object?>(TResult? Function( UserEntity? loggedUser, bool isLoading, String currentPassword, String newPassword, String repeatPassword, String errorMessage)? $default,) {final _that = this;
switch (_that) {
case _ChangePasswordViewState() when $default != null:
return $default(_that.loggedUser,_that.isLoading,_that.currentPassword,_that.newPassword,_that.repeatPassword,_that.errorMessage);case _:
return null;
}
}
}
/// @nodoc
class _ChangePasswordViewState implements ChangePasswordViewState {
const _ChangePasswordViewState({this.loggedUser, this.isLoading = false, this.currentPassword = '', this.newPassword = '', this.repeatPassword = '', this.errorMessage = ''});
@override final UserEntity? loggedUser;
@override@JsonKey() final bool isLoading;
@override@JsonKey() final String currentPassword;
@override@JsonKey() final String newPassword;
@override@JsonKey() final String repeatPassword;
@override@JsonKey() final String errorMessage;
/// Create a copy of ChangePasswordViewState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ChangePasswordViewStateCopyWith<_ChangePasswordViewState> get copyWith => __$ChangePasswordViewStateCopyWithImpl<_ChangePasswordViewState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ChangePasswordViewState&&(identical(other.loggedUser, loggedUser) || other.loggedUser == loggedUser)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.currentPassword, currentPassword) || other.currentPassword == currentPassword)&&(identical(other.newPassword, newPassword) || other.newPassword == newPassword)&&(identical(other.repeatPassword, repeatPassword) || other.repeatPassword == repeatPassword)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,loggedUser,isLoading,currentPassword,newPassword,repeatPassword,errorMessage);
@override
String toString() {
return 'ChangePasswordViewState(loggedUser: $loggedUser, isLoading: $isLoading, currentPassword: $currentPassword, newPassword: $newPassword, repeatPassword: $repeatPassword, errorMessage: $errorMessage)';
}
}
/// @nodoc
abstract mixin class _$ChangePasswordViewStateCopyWith<$Res> implements $ChangePasswordViewStateCopyWith<$Res> {
factory _$ChangePasswordViewStateCopyWith(_ChangePasswordViewState value, $Res Function(_ChangePasswordViewState) _then) = __$ChangePasswordViewStateCopyWithImpl;
@override @useResult
$Res call({
UserEntity? loggedUser, bool isLoading, String currentPassword, String newPassword, String repeatPassword, String errorMessage
});
@override $UserEntityCopyWith<$Res>? get loggedUser;
}
/// @nodoc
class __$ChangePasswordViewStateCopyWithImpl<$Res>
implements _$ChangePasswordViewStateCopyWith<$Res> {
__$ChangePasswordViewStateCopyWithImpl(this._self, this._then);
final _ChangePasswordViewState _self;
final $Res Function(_ChangePasswordViewState) _then;
/// Create a copy of ChangePasswordViewState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? loggedUser = freezed,Object? isLoading = null,Object? currentPassword = null,Object? newPassword = null,Object? repeatPassword = null,Object? errorMessage = null,}) {
return _then(_ChangePasswordViewState(
loggedUser: freezed == loggedUser ? _self.loggedUser : loggedUser // ignore: cast_nullable_to_non_nullable
as UserEntity?,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,currentPassword: null == currentPassword ? _self.currentPassword : currentPassword // ignore: cast_nullable_to_non_nullable
as String,newPassword: null == newPassword ? _self.newPassword : newPassword // ignore: cast_nullable_to_non_nullable
as String,repeatPassword: null == repeatPassword ? _self.repeatPassword : repeatPassword // ignore: cast_nullable_to_non_nullable
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));
}
/// Create a copy of ChangePasswordViewState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$UserEntityCopyWith<$Res>? get loggedUser {
if (_self.loggedUser == null) {
return null;
}
return $UserEntityCopyWith<$Res>(_self.loggedUser!, (value) {
return _then(_self.copyWith(loggedUser: value));
});
}
}
// dart format on

View File

@@ -52,6 +52,7 @@ dependencies:
json_serializable: ^6.11.2
uuid: ^4.5.2
qr_flutter: ^4.1.0
url_launcher: ^6.3.2
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.