2021-02-18 17:20:50 +00:00
import ' package:comunic/helpers/server_config_helper.dart ' ;
import ' package:comunic/models/server_config.dart ' ;
import ' package:comunic/utils/intl_utils.dart ' ;
import ' package:flutter/material.dart ' ;
/// New password input widget
///
/// @author Pierre Hubert
2021-02-18 17:58:47 +00:00
class UserInfoForPassword {
2022-03-10 18:39:57 +00:00
final String ? firstName ;
final String ? lastName ;
final String ? email ;
2021-02-18 17:58:47 +00:00
const UserInfoForPassword ( {
2022-03-10 18:39:57 +00:00
required this . firstName ,
required this . lastName ,
required this . email ,
2021-02-18 17:58:47 +00:00
} ) ;
}
2021-02-18 17:20:50 +00:00
class NewPasswordInputWidget extends StatefulWidget {
2022-03-10 18:39:57 +00:00
final Widget ? icon ;
final VoidCallback ? onEdited ;
final VoidCallback ? onSubmitted ;
final TextInputAction ? textInputAction ;
2021-02-18 17:20:50 +00:00
final String label ;
2021-02-18 17:58:47 +00:00
final UserInfoForPassword user ;
2021-02-18 17:20:50 +00:00
const NewPasswordInputWidget ( {
2022-03-10 18:39:57 +00:00
Key ? key ,
2021-02-18 17:20:50 +00:00
this . icon ,
this . onEdited ,
2021-02-18 17:58:47 +00:00
this . onSubmitted ,
this . textInputAction ,
2022-03-10 18:39:57 +00:00
required this . label ,
required this . user ,
2021-02-18 17:58:47 +00:00
} ) : assert ( label ! = null ) ,
assert ( user ! = null ) ,
super ( key: key ) ;
2021-02-18 17:20:50 +00:00
@ override
NewPasswordInputWidgetState createState ( ) = > NewPasswordInputWidgetState ( ) ;
}
class NewPasswordInputWidgetState extends State < NewPasswordInputWidget > {
final TextEditingController _controller = TextEditingController ( ) ;
String get value = > _controller . text ;
2021-02-19 16:38:12 +00:00
bool get valid = > value . isNotEmpty & & ( _errorMessage ? ? " " ) . isEmpty ;
2021-02-18 17:20:50 +00:00
2022-03-10 18:39:57 +00:00
PasswordPolicy get _policy = > ServerConfigurationHelper . config ! . passwordPolicy ;
2021-02-18 17:20:50 +00:00
@ override
void didUpdateWidget ( covariant NewPasswordInputWidget oldWidget ) {
super . didUpdateWidget ( oldWidget ) ;
setState ( ( ) { } ) ;
}
@ override
Widget build ( BuildContext context ) = > TextField (
controller: _controller ,
obscureText: true ,
onChanged: ( s ) = > _onChanged ( ) ,
2021-02-18 17:58:47 +00:00
onSubmitted:
2022-03-10 18:39:57 +00:00
widget . onSubmitted = = null ? null : ( s ) = > widget . onSubmitted ! ( ) ,
2021-02-18 17:58:47 +00:00
textInputAction: widget . textInputAction ,
2021-02-18 17:20:50 +00:00
decoration: InputDecoration (
errorText: _errorMessage ,
errorMaxLines: 3 ,
icon: widget . icon ,
labelText: widget . label ,
) ,
) ;
void _onChanged ( ) {
setState ( ( ) { } ) ;
2022-03-10 18:39:57 +00:00
if ( widget . onEdited ! = null ) widget . onEdited ! ( ) ;
2021-02-18 17:20:50 +00:00
}
/// Generate an error message associated with current password
2022-03-10 18:39:57 +00:00
String ? get _errorMessage {
2021-02-19 16:38:12 +00:00
if ( value . isEmpty ) return null ;
2021-02-18 17:20:50 +00:00
// Mandatory checks
if ( ! _policy . allowMailInPassword & &
2021-02-18 17:58:47 +00:00
( widget . user . email ? ? " " ) . isNotEmpty & &
2022-03-10 18:39:57 +00:00
( widget . user . email ! . toLowerCase ( ) . contains ( value . toLowerCase ( ) ) | |
value . toLowerCase ( ) . contains ( widget . user . email ! . toLowerCase ( ) ) ) ) {
2021-02-18 17:20:50 +00:00
return tr ( " Your password must not contains part of your email address! " ) ;
}
if ( ! _policy . allowNameInPassword & &
2021-02-18 17:58:47 +00:00
( widget . user . firstName ? ? " " ) . isNotEmpty & &
2022-03-10 18:39:57 +00:00
value . toLowerCase ( ) . contains ( widget . user . firstName ! . toLowerCase ( ) ) ) {
2021-02-18 17:20:50 +00:00
return tr ( " Your password must not contains your first name! " ) ;
}
if ( ! _policy . allowNameInPassword & &
2021-02-18 17:58:47 +00:00
( widget . user . lastName ? ? " " ) . isNotEmpty & &
2022-03-10 18:39:57 +00:00
value . toLowerCase ( ) . contains ( widget . user . lastName ! . toLowerCase ( ) ) ) {
2021-02-18 17:20:50 +00:00
return tr ( " Your password must not contains your last name! " ) ;
}
if ( _policy . minPasswordLength > value . length ) {
return tr (
" Your password must be composed of at least %num% characters! " ,
args: {
" num " : _policy . minPasswordLength . toString ( ) ,
} ,
) ;
}
// Characteristics check
var count = 0 ;
if ( _hasCharacteristic ( RegExp ( r'[A-Z]' ) , _policy . minNumberUpperCaseLetters ) )
count + + ;
if ( _hasCharacteristic ( RegExp ( r'[a-z]' ) , _policy . minNumberLowerCaseLetters ) )
count + + ;
if ( _hasCharacteristic ( RegExp ( r'[0-9]' ) , _policy . minNumberDigits ) ) count + + ;
if ( _hasCharacteristic (
RegExp ( r'[^A-Za-z0-9]' ) , _policy . minNumberSpecialCharacters ) ) count + + ;
if ( count > = _policy . minCategoriesPresence ) return null ;
return tr (
" Your password must contains characters of at least %num% of the following categories : %upper% upper case letter, %lower% lowercase letter, %digit% digit, %special% special character. " ,
args: {
" num " : ( _policy . minCategoriesPresence = = 4
? tr ( " ALL " )
: _policy . minCategoriesPresence . toString ( ) ) ,
" upper " : _policy . minNumberUpperCaseLetters . toString ( ) ,
" lower " : _policy . minNumberLowerCaseLetters . toString ( ) ,
" digit " : _policy . minNumberDigits . toString ( ) ,
" special " : _policy . minNumberSpecialCharacters . toString ( ) ,
} ,
) ;
}
bool _hasCharacteristic ( RegExp exp , int requiredCount ) {
if ( requiredCount < 1 ) return true ;
return exp . allMatches ( value ) . length > = requiredCount ;
}
}