diff --git a/lib/features/authentication/presentation/pages/reset_password_page.dart b/lib/features/authentication/presentation/pages/reset_password_page.dart index 4b312a1..b838ae1 100644 --- a/lib/features/authentication/presentation/pages/reset_password_page.dart +++ b/lib/features/authentication/presentation/pages/reset_password_page.dart @@ -20,6 +20,8 @@ class _ResetPasswordPageState extends ConsumerState { String? _errorMessage; String? _successMessage; bool _emailSent = false; + bool _canResendAfter = false; + DateTime? _lastAttemptTime; @override void dispose() { @@ -27,9 +29,26 @@ class _ResetPasswordPageState extends ConsumerState { super.dispose(); } + /// Handles email input changes and clears errors + void _handleEmailChanged(String value) { + setState(() { + _errorMessage = null; + _successMessage = null; + }); + } + Future _handlePasswordReset(String email) async { if (!_formKey.currentState!.validate()) return; + // Check for empty email + final trimmedEmail = email.trim(); + if (trimmedEmail.isEmpty) { + setState(() { + _errorMessage = 'Please enter your email address'; + }); + return; + } + setState(() { _isLoading = true; _errorMessage = null; @@ -38,7 +57,7 @@ class _ResetPasswordPageState extends ConsumerState { try { final authProvider = ref.read(authProvider.notifier); - await authProvider.resetPassword(email.trim()); + await authProvider.resetPassword(trimmedEmail); if (mounted) { setState(() { @@ -46,31 +65,51 @@ class _ResetPasswordPageState extends ConsumerState { _emailSent = true; _successMessage = 'Password reset email sent! Check your inbox for further instructions.'; }); + + // Announce success for accessibility + _announceMessageForAccessibility(_successMessage!); } } catch (e) { if (mounted) { setState(() { _isLoading = false; - _errorMessage = _mapErrorToMessage(e); + _errorMessage = _mapErrorToUserFriendlyMessage(e); }); + + // Announce error for accessibility + _announceErrorForAccessibility(_errorMessage!); } } } - String _mapErrorToMessage(Object error) { + String _mapErrorToUserFriendlyMessage(Object error) { if (error is UserNotFoundException) { - return 'No account found with this email address.'; + return 'No account found with this email address. Please check the email or sign up for a new account.'; } else if (error is TooManyRequestsException) { - return 'Too many reset attempts. Please try again later.'; + return 'Too many reset attempts. Please wait 15 minutes before trying again, or contact support.'; } else if (error is NetworkException) { - return 'Network error. Please check your connection and try again.'; + return 'Network connection failed. Please check your internet connection and try again.'; + } else if (error is AuthDisabledException) { + return 'Password reset is currently disabled. Please try again later or contact support.'; + } else if (error is InvalidTokenException) { + return 'Reset request failed. Please start the password reset process again.'; } else if (error is AuthException) { return error.message; } else { - return 'An unexpected error occurred. Please try again.'; + return 'An unexpected error occurred. Please try again or contact support if the problem persists.'; } } + /// Announces success message for screen readers + void _announceMessageForAccessibility(String message) { + SemanticsService.announce(message, TextDirection.ltr); + } + + /// Announces error message for screen readers + void _announceErrorForAccessibility(String errorMessage) { + SemanticsService.announce(errorMessage, TextDirection.ltr); + } + void _navigateToLogin() { Navigator.of(context).pushReplacementNamed('/login'); } @@ -108,6 +147,7 @@ class _ResetPasswordPageState extends ConsumerState { emailController: _emailController, isLoading: _isLoading, errorMessage: _errorMessage, + onEmailChanged: _handleEmailChanged, onSubmit: _handlePasswordReset, ), ] else ...[ @@ -223,19 +263,60 @@ class _ResetPasswordPageState extends ConsumerState { const SizedBox(height: 4), Text( '1. Open your email inbox\n' - '2. Look for the password reset email\n' - '3. Click the reset link in the email\n' - '4. Create a new password', + '2. Look for password reset email (check spam folder)\n' + '3. Click reset link in the email\n' + '4. Create a new password\n' + '5. Return to sign in with your new password', style: Theme.of(context).textTheme.bodySmall, ), + const SizedBox(height: 8), + Text( + 'Didn\'t receive the email? Check your spam folder or try again in a few minutes.', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + fontStyle: FontStyle.italic, + ), + ), ], ), ), + const SizedBox(height: 16), + _buildResendSection(), ], ), ); } + Widget _buildResendSection() { + return Column( + children: [ + Text( + "Didn't receive the email?", + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colorScheme.onPrimaryContainer, + ), + ), + const SizedBox(height: 8), + TextButton( + onPressed: _canResendAfter ? _handleResendEmail : null, + style: TextButton.styleFrom( + foregroundColor: _canResendAfter + ? Theme.of(context).colorScheme.onPrimaryContainer + : Theme.of(context).colorScheme.onPrimaryContainer.withOpacity(0.5), + ), + child: Text( + _canResendAfter ? 'Resend Email' : 'Resend Email (available soon)', + ), + ), + ], + ); + } + + void _handleResendEmail() { + if (_canResendAfter && _emailController.text.isNotEmpty) { + _handlePasswordReset(_emailController.text); + } + } + Widget _buildBackToLoginButton() { return OutlinedButton( onPressed: _navigateToLogin, diff --git a/lib/features/authentication/presentation/widgets/password_reset_form.dart b/lib/features/authentication/presentation/widgets/password_reset_form.dart index d851ce2..596b42c 100644 --- a/lib/features/authentication/presentation/widgets/password_reset_form.dart +++ b/lib/features/authentication/presentation/widgets/password_reset_form.dart @@ -9,6 +9,7 @@ class PasswordResetForm extends StatefulWidget { final bool isLoading; final String? errorMessage; final ValueChanged? onSubmit; + final ValueChanged? onEmailChanged; final String emailLabel; final String submitButtonText; final bool autofocusEmail; @@ -22,6 +23,7 @@ class PasswordResetForm extends StatefulWidget { this.isLoading = false, this.errorMessage, this.onSubmit, + this.onEmailChanged, this.emailLabel = 'Email', this.submitButtonText = 'Send Reset Email', this.autofocusEmail = true, @@ -150,6 +152,10 @@ class _PasswordResetFormState extends State { _emailError = null; }); } + // Call external callback if provided + if (widget.onEmailChanged != null) { + widget.onEmailChanged!(value); + } }, inputFormatters: [ FilteringTextInputFormatter.deny(RegExp(r'\s')), // Prevent spaces