2 Commits

Author SHA1 Message Date
0c6229331f Merge pull request 'Password field checks for match with old password from db and check so that new password feild matches with the confirm password field' (#143) from SEND013 into main
Some checks failed
ci/woodpecker/push/billing_fees Pipeline failed
ci/woodpecker/push/frontend Pipeline is pending
ci/woodpecker/push/chain_gateway Pipeline is pending
ci/woodpecker/push/db Pipeline is pending
ci/woodpecker/push/fx_ingestor Pipeline is pending
ci/woodpecker/push/fx_oracle Pipeline is pending
ci/woodpecker/push/ledger Pipeline is pending
ci/woodpecker/push/mntx_gateway Pipeline is pending
ci/woodpecker/push/nats Pipeline is pending
ci/woodpecker/push/notification Pipeline is pending
ci/woodpecker/push/payments_orchestrator Pipeline is pending
ci/woodpecker/push/bff Pipeline failed
Reviewed-on: #143
2025-12-24 16:07:58 +00:00
Arseni
43020f3eb6 Password field checks for match with old password from db and check so that new password feild matches with the confirm password field 2025-12-24 16:18:52 +03:00
7 changed files with 93 additions and 10 deletions

View File

@@ -10,6 +10,8 @@ class ConfirmPasswordField extends StatelessWidget {
required this.newPasswordController, required this.newPasswordController,
required this.missingPasswordError, required this.missingPasswordError,
required this.passwordsDoNotMatchError, required this.passwordsDoNotMatchError,
required this.obscureText,
required this.onToggleVisibility,
}); });
final TextEditingController controller; final TextEditingController controller;
@@ -19,6 +21,8 @@ class ConfirmPasswordField extends StatelessWidget {
final TextEditingController newPasswordController; final TextEditingController newPasswordController;
final String missingPasswordError; final String missingPasswordError;
final String passwordsDoNotMatchError; final String passwordsDoNotMatchError;
final bool obscureText;
final VoidCallback onToggleVisibility;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -26,11 +30,18 @@ class ConfirmPasswordField extends StatelessWidget {
width: fieldWidth, width: fieldWidth,
child: TextFormField( child: TextFormField(
controller: controller, controller: controller,
obscureText: true, obscureText: obscureText,
enabled: isEnabled, enabled: isEnabled,
autovalidateMode: AutovalidateMode.onUserInteraction,
decoration: InputDecoration( decoration: InputDecoration(
labelText: confirmPasswordLabel, labelText: confirmPasswordLabel,
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
suffixIcon: IconButton(
onPressed: onToggleVisibility,
icon: Icon(
obscureText ? Icons.visibility_off : Icons.visibility,
),
),
), ),
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) return missingPasswordError; if (value == null || value.isEmpty) return missingPasswordError;

View File

@@ -7,6 +7,8 @@ class PasswordField extends StatelessWidget {
required this.labelText, required this.labelText,
required this.fieldWidth, required this.fieldWidth,
required this.isEnabled, required this.isEnabled,
required this.obscureText,
required this.onToggleVisibility,
required this.validator, required this.validator,
}); });
@@ -14,6 +16,8 @@ class PasswordField extends StatelessWidget {
final String labelText; final String labelText;
final double fieldWidth; final double fieldWidth;
final bool isEnabled; final bool isEnabled;
final bool obscureText;
final VoidCallback onToggleVisibility;
final String? Function(String?) validator; final String? Function(String?) validator;
@override @override
@@ -22,11 +26,18 @@ class PasswordField extends StatelessWidget {
width: fieldWidth, width: fieldWidth,
child: TextFormField( child: TextFormField(
controller: controller, controller: controller,
obscureText: true, obscureText: obscureText,
enabled: isEnabled, enabled: isEnabled,
autovalidateMode: AutovalidateMode.onUserInteraction,
decoration: InputDecoration( decoration: InputDecoration(
labelText: labelText, labelText: labelText,
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
suffixIcon: IconButton(
onPressed: onToggleVisibility,
icon: Icon(
obscureText ? Icons.visibility_off : Icons.visibility,
),
),
), ),
validator: validator, validator: validator,
), ),

View File

@@ -18,6 +18,12 @@ class PasswordFields extends StatelessWidget {
required this.fieldWidth, required this.fieldWidth,
required this.gapSmall, required this.gapSmall,
required this.isEnabled, required this.isEnabled,
required this.showOldPassword,
required this.showNewPassword,
required this.showConfirmPassword,
required this.onToggleOldPassword,
required this.onToggleNewPassword,
required this.onToggleConfirmPassword,
}); });
final TextEditingController oldPasswordController; final TextEditingController oldPasswordController;
@@ -31,6 +37,12 @@ class PasswordFields extends StatelessWidget {
final double fieldWidth; final double fieldWidth;
final double gapSmall; final double gapSmall;
final bool isEnabled; final bool isEnabled;
final bool showOldPassword;
final bool showNewPassword;
final bool showConfirmPassword;
final VoidCallback onToggleOldPassword;
final VoidCallback onToggleNewPassword;
final VoidCallback onToggleConfirmPassword;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -41,6 +53,8 @@ class PasswordFields extends StatelessWidget {
labelText: oldPasswordLabel, labelText: oldPasswordLabel,
fieldWidth: fieldWidth, fieldWidth: fieldWidth,
isEnabled: isEnabled, isEnabled: isEnabled,
obscureText: !showOldPassword,
onToggleVisibility: onToggleOldPassword,
validator: (value) => validator: (value) =>
(value == null || value.isEmpty) ? missingPasswordError : null, (value == null || value.isEmpty) ? missingPasswordError : null,
), ),
@@ -50,6 +64,8 @@ class PasswordFields extends StatelessWidget {
labelText: newPasswordLabel, labelText: newPasswordLabel,
fieldWidth: fieldWidth, fieldWidth: fieldWidth,
isEnabled: isEnabled, isEnabled: isEnabled,
obscureText: !showNewPassword,
onToggleVisibility: onToggleNewPassword,
validator: (value) => validator: (value) =>
(value == null || value.isEmpty) ? missingPasswordError : null, (value == null || value.isEmpty) ? missingPasswordError : null,
), ),
@@ -62,6 +78,8 @@ class PasswordFields extends StatelessWidget {
newPasswordController: newPasswordController, newPasswordController: newPasswordController,
missingPasswordError: missingPasswordError, missingPasswordError: missingPasswordError,
passwordsDoNotMatchError: passwordsDoNotMatchError, passwordsDoNotMatchError: passwordsDoNotMatchError,
obscureText: !showConfirmPassword,
onToggleVisibility: onToggleConfirmPassword,
), ),
], ],
); );

View File

@@ -0,0 +1 @@
enum PasswordFieldType { old, newPassword, confirmPassword }

View File

@@ -0,0 +1 @@
enum VisibilityState { hidden, visible }

View File

@@ -4,6 +4,7 @@ import 'package:pshared/provider/account.dart';
import 'package:pshared/widgets/password/fields.dart'; import 'package:pshared/widgets/password/fields.dart';
import 'package:pshared/utils/snackbar.dart'; import 'package:pshared/utils/snackbar.dart';
import 'package:pweb/models/password_field_type.dart';
import 'package:pweb/providers/password_form.dart'; import 'package:pweb/providers/password_form.dart';
import 'package:pweb/pages/settings/profile/account/password/form/error_text.dart'; import 'package:pweb/pages/settings/profile/account/password/form/error_text.dart';
import 'package:pweb/pages/settings/profile/account/password/form/submit_button.dart'; import 'package:pweb/pages/settings/profile/account/password/form/submit_button.dart';
@@ -65,6 +66,18 @@ class PasswordForm extends StatelessWidget {
fieldWidth: _fieldWidth, fieldWidth: _fieldWidth,
gapSmall: _gapSmall, gapSmall: _gapSmall,
isEnabled: !isFormBusy, isEnabled: !isFormBusy,
showOldPassword:
formProvider.isPasswordVisible(PasswordFieldType.old),
showNewPassword:
formProvider.isPasswordVisible(PasswordFieldType.newPassword),
showConfirmPassword: formProvider
.isPasswordVisible(PasswordFieldType.confirmPassword),
onToggleOldPassword: () =>
formProvider.togglePasswordVisibility(PasswordFieldType.old),
onToggleNewPassword: () => formProvider
.togglePasswordVisibility(PasswordFieldType.newPassword),
onToggleConfirmPassword: () => formProvider
.togglePasswordVisibility(PasswordFieldType.confirmPassword),
), ),
const SizedBox(height: _gapMedium), const SizedBox(height: _gapMedium),
PasswordSubmitButton( PasswordSubmitButton(
@@ -72,10 +85,11 @@ class PasswordForm extends StatelessWidget {
label: savePassword, label: savePassword,
onSubmit: () async { onSubmit: () async {
try { try {
await formProvider.submit( final success = await formProvider.submit(
accountProvider: accountProvider, accountProvider: accountProvider,
errorText: errorText, errorText: errorText,
); );
if (!success) return;
if (!context.mounted) return; if (!context.mounted) return;
notifyUser(context, successText); notifyUser(context, successText);
} catch (e) { } catch (e) {

View File

@@ -3,6 +3,9 @@ import 'package:flutter/material.dart';
import 'package:pshared/provider/account.dart'; import 'package:pshared/provider/account.dart';
import 'package:pweb/models/edit_state.dart'; import 'package:pweb/models/edit_state.dart';
import 'package:pshared/api/responses/error/server.dart';
import 'package:pweb/models/password_field_type.dart';
import 'package:pweb/models/visibility.dart';
class PasswordFormProvider extends ChangeNotifier { class PasswordFormProvider extends ChangeNotifier {
@@ -11,6 +14,11 @@ class PasswordFormProvider extends ChangeNotifier {
final newPasswordController = TextEditingController(); final newPasswordController = TextEditingController();
final confirmPasswordController = TextEditingController(); final confirmPasswordController = TextEditingController();
final Map<PasswordFieldType, VisibilityState> _visibility = {
PasswordFieldType.old: VisibilityState.hidden,
PasswordFieldType.newPassword: VisibilityState.hidden,
PasswordFieldType.confirmPassword: VisibilityState.hidden,
};
EditState _state = EditState.view; EditState _state = EditState.view;
String _errorText = ''; String _errorText = '';
bool _disposed = false; bool _disposed = false;
@@ -19,6 +27,8 @@ class PasswordFormProvider extends ChangeNotifier {
bool get isSaving => _state == EditState.saving; bool get isSaving => _state == EditState.saving;
String get errorText => _errorText; String get errorText => _errorText;
EditState get state => _state; EditState get state => _state;
bool isPasswordVisible(PasswordFieldType type) =>
_visibility[type] == VisibilityState.visible;
void toggleExpanded() { void toggleExpanded() {
if (_state == EditState.saving) return; if (_state == EditState.saving) return;
@@ -26,12 +36,21 @@ class PasswordFormProvider extends ChangeNotifier {
_setError(''); _setError('');
} }
Future<void> submit({ void togglePasswordVisibility(PasswordFieldType type) {
final current = _visibility[type];
if (current == null) return;
_visibility[type] = current == VisibilityState.hidden
? VisibilityState.visible
: VisibilityState.hidden;
notifyListeners();
}
Future<bool> submit({
required AccountProvider accountProvider, required AccountProvider accountProvider,
required String errorText, required String errorText,
}) async { }) async {
final currentForm = formKey.currentState; final currentForm = formKey.currentState;
if (currentForm == null || !currentForm.validate()) return; if (currentForm == null || !currentForm.validate()) return false;
_setState(EditState.saving); _setState(EditState.saving);
_setError(''); _setError('');
@@ -45,14 +64,22 @@ class PasswordFormProvider extends ChangeNotifier {
oldPasswordController.clear(); oldPasswordController.clear();
newPasswordController.clear(); newPasswordController.clear();
confirmPasswordController.clear(); confirmPasswordController.clear();
_setState(EditState.view);
return true;
} catch (e) { } catch (e) {
_setError(errorText); _setError(_errorMessageForException(e, errorText));
rethrow;
} finally {
_setState(EditState.edit); _setState(EditState.edit);
rethrow;
} }
} }
String _errorMessageForException(Object exception, String fallback) {
if (exception is ErrorResponse && exception.details.isNotEmpty) {
return exception.details;
}
return fallback;
}
void _setState(EditState value) { void _setState(EditState value) {
if (_state == value || _disposed) return; if (_state == value || _disposed) return;
_state = value; _state = value;