mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-11-22 16:32:01 +03:00
Merge PR #204
This commit is contained in:
commit
3290a5c65e
@ -29,7 +29,7 @@
|
||||
"oath_digits": "digits",
|
||||
"oath_success_delete_account": "Account deleted",
|
||||
"oath_delete": "Delete",
|
||||
"oath_warning_this_will_delete_from_key": "Warning! This action will delete the account from your YubiKey.",
|
||||
"oath_warning_this_will_delete_account_from_key": "Warning! This action will delete the account from your YubiKey.",
|
||||
"oath_warning_disable_this_cred": "You will no longer be able to generate OTPs for this account. Make sure to first disable this credential from the website to avoid being locked out of your account.",
|
||||
"oath_account": "Account",
|
||||
"oath_password_set": "Password set",
|
||||
@ -44,6 +44,35 @@
|
||||
"oath_enter_new_password": "Enter your new password. A password may contain letters, numbers and special characters.",
|
||||
"oath_new_password": "New password",
|
||||
"oath_confirm_password": "Confirm password",
|
||||
"oath_authenticator": "Authenticator",
|
||||
"oath_reset_oath": "Reset OATH",
|
||||
"oath_no_accounts": "No accounts",
|
||||
"oath_search_accounts": "Search accounts",
|
||||
"oath_set_password": "Set password",
|
||||
"oath_failed_remember_pw": "Failed to remember password",
|
||||
"oath_enter_oath_pw": "Enter the OATH password for your YubiKey",
|
||||
"oath_password": "Password",
|
||||
"oath_keystore_unavailable": "OS Keystore unavailable",
|
||||
"oath_remember_password": "Remember password",
|
||||
"oath_unlock": "Unlock",
|
||||
"oath_warning_will_change_account_displayed": "This will change how the account is displayed in the list.",
|
||||
"oath_account_must_have_name": "Your account must have a name",
|
||||
"oath_name_exists": "This name already exists for the Issuer",
|
||||
"oath_account_renamed": "Account renamed",
|
||||
"oath_rename": "Rename {label}?",
|
||||
"@oath_rename" : {
|
||||
"placeholders": {
|
||||
"label": {}
|
||||
}
|
||||
},
|
||||
"oath_factory_reset": "Factory reset",
|
||||
"oath_oath_application_reset": "OATH application reset",
|
||||
"oath_reset": "Reset",
|
||||
"oath_warning_will_delete_accounts": "Warning! This will irrevocably delete all OATH TOTP/HOTP accounts from your YubiKey.",
|
||||
"oath_warning_disable_these_creds": "Your OATH credentials, as well as any password set, will be removed from this YubiKey. Make sure to first disable these from their respective web sites to avoid being locked out of your accounts.",
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -50,7 +50,7 @@ class DeleteAccountDialog extends ConsumerWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(AppLocalizations.of(context)!
|
||||
.oath_warning_this_will_delete_from_key),
|
||||
.oath_warning_this_will_delete_account_from_key),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.oath_warning_disable_this_cred,
|
||||
style: Theme.of(context).textTheme.bodyText1,
|
||||
|
@ -2,6 +2,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../app/message.dart';
|
||||
@ -28,12 +29,12 @@ class OathScreen extends ConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return ref.watch(oathStateProvider(devicePath)).when(
|
||||
loading: () => AppPage(
|
||||
title: const Text('Authenticator'),
|
||||
title: Text(AppLocalizations.of(context)!.oath_authenticator),
|
||||
centered: true,
|
||||
child: const AppLoadingScreen(),
|
||||
),
|
||||
error: (error, _) => AppFailurePage(
|
||||
title: const Text('Authenticator'),
|
||||
title: Text(AppLocalizations.of(context)!.oath_authenticator),
|
||||
cause: error,
|
||||
),
|
||||
data: (oathState) => oathState.locked
|
||||
@ -51,10 +52,10 @@ class _LockedView extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) => AppPage(
|
||||
title: const Text('Authenticator'),
|
||||
title: Text(AppLocalizations.of(context)!.oath_authenticator),
|
||||
keyActions: [
|
||||
buildMenuItem(
|
||||
title: const Text('Manage password'),
|
||||
title: Text(AppLocalizations.of(context)!.oath_manage_password),
|
||||
leading: const Icon(Icons.password),
|
||||
action: () {
|
||||
showBlurDialog(
|
||||
@ -65,7 +66,7 @@ class _LockedView extends ConsumerWidget {
|
||||
},
|
||||
),
|
||||
buildMenuItem(
|
||||
title: const Text('Reset OATH'),
|
||||
title: Text(AppLocalizations.of(context)!.oath_reset_oath),
|
||||
leading: const Icon(Icons.delete),
|
||||
action: () {
|
||||
showBlurDialog(
|
||||
@ -119,9 +120,9 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
|
||||
final credentials = ref.watch(credentialsProvider);
|
||||
if (credentials?.isEmpty == true) {
|
||||
return MessagePage(
|
||||
title: const Text('Authenticator'),
|
||||
title: Text(AppLocalizations.of(context)!.oath_authenticator),
|
||||
graphic: noAccounts,
|
||||
header: 'No accounts',
|
||||
header: AppLocalizations.of(context)!.oath_no_accounts,
|
||||
keyActions: _buildActions(
|
||||
context,
|
||||
credentials: null,
|
||||
@ -153,11 +154,11 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
|
||||
controller: searchController,
|
||||
focusNode: searchFocus,
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Search accounts',
|
||||
decoration: InputDecoration(
|
||||
hintText: AppLocalizations.of(context)!.oath_search_accounts,
|
||||
isDense: true,
|
||||
prefixIcon: Icon(Icons.search_outlined),
|
||||
prefixIconConstraints: BoxConstraints(
|
||||
prefixIcon: const Icon(Icons.search_outlined),
|
||||
prefixIconConstraints: const BoxConstraints(
|
||||
minHeight: 30,
|
||||
minWidth: 30,
|
||||
),
|
||||
@ -190,7 +191,7 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
|
||||
final capacity = widget.oathState.version.isAtLeast(4) ? 32 : null;
|
||||
return [
|
||||
buildMenuItem(
|
||||
title: const Text('Add account'),
|
||||
title: Text(AppLocalizations.of(context)!.oath_add_account),
|
||||
leading: const Icon(Icons.person_add_alt_1),
|
||||
trailing: capacity != null ? '$used/$capacity' : null,
|
||||
action: capacity == null || capacity > used
|
||||
@ -208,8 +209,9 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
|
||||
: null,
|
||||
),
|
||||
buildMenuItem(
|
||||
title: Text(
|
||||
widget.oathState.hasKey ? 'Manage password' : 'Set password'),
|
||||
title: Text(widget.oathState.hasKey
|
||||
? AppLocalizations.of(context)!.oath_manage_password
|
||||
: AppLocalizations.of(context)!.oath_set_password),
|
||||
leading: const Icon(Icons.password),
|
||||
action: () {
|
||||
showBlurDialog(
|
||||
@ -219,7 +221,7 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
|
||||
);
|
||||
}),
|
||||
buildMenuItem(
|
||||
title: const Text('Reset OATH'),
|
||||
title: Text(AppLocalizations.of(context)!.oath_reset_oath),
|
||||
leading: const Icon(Icons.delete),
|
||||
action: () {
|
||||
showBlurDialog(
|
||||
@ -260,7 +262,8 @@ class _UnlockFormState extends ConsumerState<_UnlockForm> {
|
||||
_passwordController.clear();
|
||||
});
|
||||
} else if (_remember && !result.second) {
|
||||
showMessage(context, 'Failed to remember password');
|
||||
showMessage(
|
||||
context, AppLocalizations.of(context)!.oath_failed_remember_pw);
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,8 +277,8 @@ class _UnlockFormState extends ConsumerState<_UnlockForm> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Enter the OATH password for your YubiKey',
|
||||
Text(
|
||||
AppLocalizations.of(context)!.oath_enter_oath_pw,
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
TextField(
|
||||
@ -284,8 +287,10 @@ class _UnlockFormState extends ConsumerState<_UnlockForm> {
|
||||
obscureText: _isObscure,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: 'Password',
|
||||
errorText: _passwordIsWrong ? 'Wrong password' : null,
|
||||
labelText: AppLocalizations.of(context)!.oath_password,
|
||||
errorText: _passwordIsWrong
|
||||
? AppLocalizations.of(context)!.oath_wrong_password
|
||||
: null,
|
||||
helperText: '', // Prevents resizing when errorText shown
|
||||
prefixIcon: const Icon(Icons.password_outlined),
|
||||
suffixIcon: IconButton(
|
||||
@ -310,14 +315,16 @@ class _UnlockFormState extends ConsumerState<_UnlockForm> {
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
keystoreFailed
|
||||
? const ListTile(
|
||||
leading: Icon(Icons.warning_amber_rounded),
|
||||
title: Text('OS Keystore unavailable'),
|
||||
? ListTile(
|
||||
leading: const Icon(Icons.warning_amber_rounded),
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!.oath_keystore_unavailable),
|
||||
dense: true,
|
||||
minLeadingWidth: 0,
|
||||
)
|
||||
: CheckboxListTile(
|
||||
title: const Text('Remember password'),
|
||||
title:
|
||||
Text(AppLocalizations.of(context)!.oath_remember_password),
|
||||
dense: true,
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
value: _remember,
|
||||
@ -332,7 +339,7 @@ class _UnlockFormState extends ConsumerState<_UnlockForm> {
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: ElevatedButton.icon(
|
||||
label: const Text('Unlock'),
|
||||
label: Text(AppLocalizations.of(context)!.oath_unlock),
|
||||
icon: const Icon(Icons.lock_open),
|
||||
onPressed: _passwordController.text.isNotEmpty ? _submit : null,
|
||||
),
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
@ -54,7 +55,7 @@ class _RenameAccountDialogState extends ConsumerState<RenameAccountDialog> {
|
||||
|
||||
if (!mounted) return;
|
||||
Navigator.of(context).pop(renamed);
|
||||
showMessage(context, 'Account renamed');
|
||||
showMessage(context, AppLocalizations.of(context)!.oath_account_renamed);
|
||||
} on CancellationException catch (_) {
|
||||
// ignored
|
||||
} catch (e) {
|
||||
@ -68,7 +69,7 @@ class _RenameAccountDialogState extends ConsumerState<RenameAccountDialog> {
|
||||
}
|
||||
showMessage(
|
||||
context,
|
||||
'Failed adding account: $errorMessage',
|
||||
'${AppLocalizations.of(context)!.oath_fail_add_account}: $errorMessage',
|
||||
duration: const Duration(seconds: 4),
|
||||
);
|
||||
}
|
||||
@ -111,30 +112,30 @@ class _RenameAccountDialogState extends ConsumerState<RenameAccountDialog> {
|
||||
final isValid = isUnique && isValidFormat;
|
||||
|
||||
return ResponsiveDialog(
|
||||
title: const Text('Rename account'),
|
||||
title: Text(AppLocalizations.of(context)!.oath_rename_account),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: didChange && isValid ? _submit : null,
|
||||
child: const Text('Save'),
|
||||
child: Text(AppLocalizations.of(context)!.oath_save),
|
||||
),
|
||||
],
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Rename $label?'),
|
||||
const Text(
|
||||
'This will change how the account is displayed in the list.'),
|
||||
Text(AppLocalizations.of(context)!.oath_rename(label)),
|
||||
Text(AppLocalizations.of(context)!
|
||||
.oath_warning_will_change_account_displayed),
|
||||
TextFormField(
|
||||
initialValue: _issuer,
|
||||
enabled: issuerRemaining > 0,
|
||||
maxLength: issuerRemaining > 0 ? issuerRemaining : null,
|
||||
buildCounter: buildByteCounterFor(_issuer),
|
||||
inputFormatters: [limitBytesLength(issuerRemaining)],
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: 'Issuer (optional)',
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: AppLocalizations.of(context)!.oath_issuer_optional,
|
||||
helperText: '', // Prevents dialog resizing when disabled
|
||||
prefixIcon: Icon(Icons.business_outlined),
|
||||
prefixIcon: const Icon(Icons.business_outlined),
|
||||
),
|
||||
textInputAction: TextInputAction.next,
|
||||
onChanged: (value) {
|
||||
@ -150,12 +151,12 @@ class _RenameAccountDialogState extends ConsumerState<RenameAccountDialog> {
|
||||
buildCounter: buildByteCounterFor(_account),
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: 'Account name',
|
||||
labelText: AppLocalizations.of(context)!.oath_account_name,
|
||||
helperText: '', // Prevents dialog resizing when disabled
|
||||
errorText: !isValidFormat
|
||||
? 'Your account must have a name'
|
||||
? AppLocalizations.of(context)!.oath_account_must_have_name
|
||||
: !isUnique
|
||||
? 'This name already exists for the Issuer'
|
||||
? AppLocalizations.of(context)!.oath_name_exists
|
||||
: null,
|
||||
prefixIcon: const Icon(Icons.people_alt_outlined),
|
||||
),
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../app/message.dart';
|
||||
@ -14,25 +15,25 @@ class ResetDialog extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return ResponsiveDialog(
|
||||
title: const Text('Factory reset'),
|
||||
title: Text(AppLocalizations.of(context)!.oath_factory_reset),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
await ref.read(oathStateProvider(devicePath).notifier).reset();
|
||||
await ref.read(withContextProvider)((context) async {
|
||||
Navigator.of(context).pop();
|
||||
showMessage(context, 'OATH application reset');
|
||||
showMessage(context,
|
||||
AppLocalizations.of(context)!.oath_oath_application_reset);
|
||||
});
|
||||
},
|
||||
child: const Text('Reset'),
|
||||
child: Text(AppLocalizations.of(context)!.oath_reset),
|
||||
),
|
||||
],
|
||||
child: Column(
|
||||
children: [
|
||||
const Text(
|
||||
'Warning! This will irrevocably delete all OATH TOTP/HOTP accounts from your YubiKey.'),
|
||||
Text(AppLocalizations.of(context)!.oath_warning_will_delete_accounts),
|
||||
Text(
|
||||
'Your OATH credentials, as well as any password set, will be removed from this YubiKey. Make sure to first disable these from their respective web sites to avoid being locked out of your accounts.',
|
||||
AppLocalizations.of(context)!.oath_warning_disable_these_creds,
|
||||
style: Theme.of(context).textTheme.bodyText1,
|
||||
),
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user