mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-12-24 10:42:47 +03:00
Merge PR #830.
This commit is contained in:
commit
c290ac52fb
@ -261,23 +261,28 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final period = int.tryParse(_periodController.text) ?? -1;
|
final period = int.tryParse(_periodController.text) ?? -1;
|
||||||
|
final issuerText = _issuerController.text.trim();
|
||||||
|
final nameText = _accountController.text.trim();
|
||||||
final remaining = getRemainingKeySpace(
|
final remaining = getRemainingKeySpace(
|
||||||
oathType: _oathType,
|
oathType: _oathType,
|
||||||
period: period,
|
period: period,
|
||||||
issuer: _issuerController.text.trim(),
|
issuer: issuerText,
|
||||||
name: _accountController.text.trim(),
|
name: nameText,
|
||||||
);
|
);
|
||||||
final issuerRemaining = remaining.first;
|
final issuerRemaining = remaining.first;
|
||||||
final nameRemaining = remaining.second;
|
final nameRemaining = remaining.second;
|
||||||
|
|
||||||
|
final issuerMaxLength = max(issuerRemaining, 1);
|
||||||
|
final nameMaxLength = max(nameRemaining, 1);
|
||||||
|
|
||||||
final secret = _secretController.text.replaceAll(' ', '');
|
final secret = _secretController.text.replaceAll(' ', '');
|
||||||
final secretLengthValid = secret.length * 5 % 8 < 5;
|
final secretLengthValid = secret.length * 5 % 8 < 5;
|
||||||
|
|
||||||
// is this credentials name/issuer pair different from all other?
|
// is this credentials name/issuer pair different from all other?
|
||||||
final isUnique = _credentials
|
final isUnique = _credentials
|
||||||
?.where((element) =>
|
?.where((element) =>
|
||||||
element.name == _accountController.text.trim() &&
|
element.name == nameText &&
|
||||||
(element.issuer ?? '') == _issuerController.text.trim())
|
(element.issuer ?? '') == issuerText)
|
||||||
.isEmpty ??
|
.isEmpty ??
|
||||||
true;
|
true;
|
||||||
final issuerNoColon = !_issuerController.text.contains(':');
|
final issuerNoColon = !_issuerController.text.contains(':');
|
||||||
@ -285,7 +290,7 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
|
|||||||
final isLocked = oathState?.locked ?? false;
|
final isLocked = oathState?.locked ?? false;
|
||||||
|
|
||||||
final isValid = !isLocked &&
|
final isValid = !isLocked &&
|
||||||
_accountController.text.trim().isNotEmpty &&
|
nameText.isNotEmpty &&
|
||||||
secret.isNotEmpty &&
|
secret.isNotEmpty &&
|
||||||
isUnique &&
|
isUnique &&
|
||||||
issuerNoColon &&
|
issuerNoColon &&
|
||||||
@ -311,11 +316,9 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
|
|||||||
|
|
||||||
void submit() async {
|
void submit() async {
|
||||||
if (secretLengthValid) {
|
if (secretLengthValid) {
|
||||||
final issuer = _issuerController.text.trim();
|
|
||||||
|
|
||||||
final cred = CredentialData(
|
final cred = CredentialData(
|
||||||
issuer: issuer.isEmpty ? null : issuer,
|
issuer: issuerText.isEmpty ? null : issuerText,
|
||||||
name: _accountController.text.trim(),
|
name: nameText,
|
||||||
secret: secret,
|
secret: secret,
|
||||||
oathType: _oathType,
|
oathType: _oathType,
|
||||||
hashAlgorithm: _hashAlgorithm,
|
hashAlgorithm: _hashAlgorithm,
|
||||||
@ -389,12 +392,11 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
|
|||||||
controller: _issuerController,
|
controller: _issuerController,
|
||||||
autofocus: widget.credentialData == null,
|
autofocus: widget.credentialData == null,
|
||||||
enabled: issuerRemaining > 0,
|
enabled: issuerRemaining > 0,
|
||||||
maxLength: max(issuerRemaining, 1),
|
maxLength: issuerMaxLength,
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
limitBytesLength(issuerRemaining),
|
limitBytesLength(issuerRemaining),
|
||||||
],
|
],
|
||||||
buildCounter:
|
buildCounter: buildByteCounterFor(issuerText),
|
||||||
buildByteCounterFor(_issuerController.text.trim()),
|
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
labelText:
|
labelText:
|
||||||
@ -402,10 +404,12 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
|
|||||||
helperText:
|
helperText:
|
||||||
'', // Prevents dialog resizing when disabled
|
'', // Prevents dialog resizing when disabled
|
||||||
prefixIcon: const Icon(Icons.business_outlined),
|
prefixIcon: const Icon(Icons.business_outlined),
|
||||||
errorText: issuerNoColon
|
errorText: (byteLength(issuerText) > issuerMaxLength)
|
||||||
? null
|
? '' // needs empty string to render as error
|
||||||
: AppLocalizations.of(context)!
|
: issuerNoColon
|
||||||
.oath_invalid_character_issuer,
|
? null
|
||||||
|
: AppLocalizations.of(context)!
|
||||||
|
.oath_invalid_character_issuer,
|
||||||
),
|
),
|
||||||
textInputAction: TextInputAction.next,
|
textInputAction: TextInputAction.next,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@ -420,9 +424,8 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
|
|||||||
TextField(
|
TextField(
|
||||||
key: keys.nameField,
|
key: keys.nameField,
|
||||||
controller: _accountController,
|
controller: _accountController,
|
||||||
maxLength: max(nameRemaining, 1),
|
maxLength: nameMaxLength,
|
||||||
buildCounter:
|
buildCounter: buildByteCounterFor(nameText),
|
||||||
buildByteCounterFor(_accountController.text.trim()),
|
|
||||||
inputFormatters: [limitBytesLength(nameRemaining)],
|
inputFormatters: [limitBytesLength(nameRemaining)],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
@ -431,9 +434,12 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
|
|||||||
AppLocalizations.of(context)!.oath_account_name,
|
AppLocalizations.of(context)!.oath_account_name,
|
||||||
helperText:
|
helperText:
|
||||||
'', // Prevents dialog resizing when disabled
|
'', // Prevents dialog resizing when disabled
|
||||||
errorText: isUnique
|
errorText: (byteLength(nameText) > nameMaxLength)
|
||||||
? null
|
? '' // needs empty string to render as error
|
||||||
: AppLocalizations.of(context)!.oath_duplicate_name,
|
: isUnique
|
||||||
|
? null
|
||||||
|
: AppLocalizations.of(context)!
|
||||||
|
.oath_duplicate_name,
|
||||||
),
|
),
|
||||||
textInputAction: TextInputAction.next,
|
textInputAction: TextInputAction.next,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
|
@ -28,10 +28,18 @@ int byteLength(String value) => utf8.encode(value).length;
|
|||||||
/// used rather than number of characters. [currentValue] should always match
|
/// used rather than number of characters. [currentValue] should always match
|
||||||
/// the input text value to measure.
|
/// the input text value to measure.
|
||||||
InputCounterWidgetBuilder buildByteCounterFor(String currentValue) =>
|
InputCounterWidgetBuilder buildByteCounterFor(String currentValue) =>
|
||||||
(context, {required currentLength, required isFocused, maxLength}) => Text(
|
(context, {required currentLength, required isFocused, maxLength}) {
|
||||||
maxLength != null ? '${byteLength(currentValue)}/$maxLength' : '',
|
final theme = Theme.of(context);
|
||||||
style: Theme.of(context).textTheme.caption,
|
final caption = theme.textTheme.caption;
|
||||||
);
|
final style = (byteLength(currentValue) <= (maxLength ?? 0))
|
||||||
|
? caption
|
||||||
|
: caption?.copyWith(color: theme.errorColor);
|
||||||
|
return Text(
|
||||||
|
maxLength != null ? '${byteLength(currentValue)}/$maxLength' : '',
|
||||||
|
style: style,
|
||||||
|
semanticsLabel: 'Character count',
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
/// Limits the input in length based on the byte length when encoded.
|
/// Limits the input in length based on the byte length when encoded.
|
||||||
/// This is generally used together with [buildByteCounterFor].
|
/// This is generally used together with [buildByteCounterFor].
|
||||||
|
Loading…
Reference in New Issue
Block a user