Refact/verification code input check (#6924)

* Refact, login verification code, input check

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Refact, settings, enable 2fa, dialog input

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Refact. Connect, 2fa code, input check

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact, 2Fa text field, input formatter, only digits

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact, error message

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
fufesou 2024-01-19 23:36:32 -08:00 committed by GitHub
parent d39129887b
commit 06b3894249
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 374 additions and 87 deletions

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:io';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -362,6 +363,7 @@ class DialogTextField extends StatelessWidget {
final Widget? suffixIcon;
final TextEditingController controller;
final FocusNode? focusNode;
final List<TextInputFormatter>? inputFormatters;
static const kUsernameTitle = 'Username';
static const kUsernameIcon = Icon(Icons.account_circle_outlined);
@ -377,6 +379,7 @@ class DialogTextField extends StatelessWidget {
this.prefixIcon,
this.suffixIcon,
this.hintText,
this.inputFormatters,
required this.title,
required this.controller})
: super(key: key);
@ -401,6 +404,7 @@ class DialogTextField extends StatelessWidget {
focusNode: focusNode,
autofocus: true,
obscureText: obscureText,
inputFormatters: inputFormatters,
),
),
],
@ -408,6 +412,241 @@ class DialogTextField extends StatelessWidget {
}
}
abstract class ValidationField extends StatelessWidget {
ValidationField({Key? key}) : super(key: key);
String? validate();
bool get isReady;
}
class Dialog2FaField extends ValidationField {
Dialog2FaField({
Key? key,
required this.controller,
this.autoFocus = true,
this.reRequestFocus = false,
this.title,
this.helperText,
this.hintText,
this.errorText,
this.readyCallback,
this.onChanged,
}) : super(key: key);
final TextEditingController controller;
final bool autoFocus;
final bool reRequestFocus;
final String? title;
final String? helperText;
final String? hintText;
final String? errorText;
final VoidCallback? readyCallback;
final VoidCallback? onChanged;
final errMsg = translate('2FA code must be 6 digits.');
@override
Widget build(BuildContext context) {
return DialogVerificationCodeField(
title: title ?? translate('2FA code'),
controller: controller,
errorText: errorText,
autoFocus: autoFocus,
reRequestFocus: reRequestFocus,
hintText: hintText,
readyCallback: readyCallback,
helperText: helperText ?? translate('2fa_tip'),
onChanged: _onChanged,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[0-9]')),
],
);
}
String get text => controller.text;
bool get isAllDigits => text.codeUnits.every((e) => e >= 48 && e <= 57);
@override
bool get isReady => text.length == 6 && isAllDigits;
@override
String? validate() => isReady ? null : errMsg;
_onChanged(StateSetter setState, SimpleWrapper<String?> errText) {
onChanged?.call();
if (text.length > 6) {
setState(() => errText.value = errMsg);
return;
}
if (!isAllDigits) {
setState(() => errText.value = errMsg);
return;
}
if (isReady) {
readyCallback?.call();
return;
}
if (errText.value != null) {
setState(() => errText.value = null);
}
}
}
class DialogEmailCodeField extends ValidationField {
DialogEmailCodeField({
Key? key,
required this.controller,
this.autoFocus = true,
this.reRequestFocus = false,
this.hintText,
this.errorText,
this.readyCallback,
this.onChanged,
}) : super(key: key);
final TextEditingController controller;
final bool autoFocus;
final bool reRequestFocus;
final String? hintText;
final String? errorText;
final VoidCallback? readyCallback;
final VoidCallback? onChanged;
final errMsg = translate('Email verification code must be 6 characters.');
@override
Widget build(BuildContext context) {
return DialogVerificationCodeField(
title: translate('Verification code'),
controller: controller,
errorText: errorText,
autoFocus: autoFocus,
reRequestFocus: reRequestFocus,
hintText: hintText,
readyCallback: readyCallback,
helperText: translate('verification_tip'),
onChanged: _onChanged,
);
}
String get text => controller.text;
@override
bool get isReady => text.length == 6;
@override
String? validate() => isReady ? null : errMsg;
_onChanged(StateSetter setState, SimpleWrapper<String?> errText) {
onChanged?.call();
if (text.length > 6) {
setState(() => errText.value = errMsg);
return;
}
if (isReady) {
readyCallback?.call();
return;
}
if (errText.value != null) {
setState(() => errText.value = null);
}
}
}
class DialogVerificationCodeField extends StatefulWidget {
DialogVerificationCodeField({
Key? key,
required this.controller,
required this.title,
this.autoFocus = true,
this.reRequestFocus = false,
this.helperText,
this.hintText,
this.errorText,
this.textLength,
this.readyCallback,
this.onChanged,
this.inputFormatters,
}) : super(key: key);
final TextEditingController controller;
final bool autoFocus;
final bool reRequestFocus;
final String title;
final String? helperText;
final String? hintText;
final String? errorText;
final int? textLength;
final VoidCallback? readyCallback;
final Function(StateSetter setState, SimpleWrapper<String?> errText)?
onChanged;
final List<TextInputFormatter>? inputFormatters;
@override
State<DialogVerificationCodeField> createState() =>
_DialogVerificationCodeField();
}
class _DialogVerificationCodeField extends State<DialogVerificationCodeField> {
final _focusNode = FocusNode();
Timer? _timer;
Timer? _timerReRequestFocus;
SimpleWrapper<String?> errorText = SimpleWrapper(null);
@override
void initState() {
super.initState();
if (widget.autoFocus) {
_timer =
Timer(Duration(milliseconds: 50), () => _focusNode.requestFocus());
if (widget.onChanged != null) {
widget.controller.addListener(() {
widget.onChanged!(setState, errorText);
});
}
}
// software secure keyboard will take the focus since flutter 3.13
// request focus again when android account password obtain focus
if (Platform.isAndroid && widget.reRequestFocus) {
_focusNode.addListener(() {
if (_focusNode.hasFocus) {
_timerReRequestFocus?.cancel();
_timerReRequestFocus = Timer(
Duration(milliseconds: 100), () => _focusNode.requestFocus());
}
});
}
}
@override
void dispose() {
_timer?.cancel();
_timerReRequestFocus?.cancel();
_focusNode.unfocus();
_focusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return DialogTextField(
title: widget.title,
controller: widget.controller,
errorText: widget.errorText ?? errorText.value,
focusNode: _focusNode,
helperText: widget.helperText,
inputFormatters: widget.inputFormatters,
);
}
}
class PasswordWidget extends StatefulWidget {
PasswordWidget({
Key? key,
@ -1512,9 +1751,31 @@ void change2fa({Function()? callback}) async {
var new2fa = (await bind.mainGenerate2Fa());
final secretRegex = RegExp(r'secret=([^&]+)');
final secret = secretRegex.firstMatch(new2fa)?.group(1);
var msg = ''.obs;
final RxString code = "".obs;
String? errorText;
final controller = TextEditingController();
gFFI.dialogManager.show((setState, close, context) {
onVerify() async {
if (await bind.mainVerify2Fa(code: controller.text.trim())) {
callback?.call();
close();
} else {
errorText = translate('wrong-2fa-code');
}
}
final codeField = Dialog2FaField(
controller: controller,
errorText: errorText,
onChanged: () => setState(() => errorText = null),
title: translate('Verification code'),
readyCallback: () {
onVerify();
setState(() {});
},
);
getOnSubmit() => codeField.isReady ? onVerify : null;
return CustomAlertDialog(
title: Text(translate("enable-2fa-title")),
content: Column(
@ -1535,36 +1796,12 @@ void change2fa({Function()? callback}) async {
)).marginOnly(bottom: 6),
SelectableText(secret ?? '', style: TextStyle(fontSize: 12))
.marginOnly(bottom: 12),
Row(children: [
Expanded(
child: Obx(() => TextField(
decoration: InputDecoration(
errorText:
msg.value.isEmpty ? null : translate(msg.value),
hintText: translate("Verification code")),
onChanged: (value) {
code.value = value;
msg.value = '';
},
autofocus: true)))
]),
Row(children: [Expanded(child: codeField)]),
],
),
actions: [
dialogButton("Cancel", onPressed: close, isOutline: true),
Obx(() => dialogButton(
"OK",
onPressed: code.value.trim().length == 6
? () async {
if (await bind.mainVerify2Fa(code: code.value.trim())) {
callback?.call();
close();
} else {
msg.value = translate('wrong-2fa-code');
}
}
: null,
)),
dialogButton("OK", onPressed: getOnSubmit()),
],
onCancel: close,
);
@ -1573,7 +1810,9 @@ void change2fa({Function()? callback}) async {
void enter2FaDialog(
SessionID sessionId, OverlayDialogManager dialogManager) async {
final RxString code = "".obs;
final controller = TextEditingController();
final RxBool submitReady = false.obs;
dialogManager.dismissAll();
dialogManager.show((setState, close, context) {
cancel() {
@ -1582,34 +1821,23 @@ void enter2FaDialog(
}
submit() {
if (code.value.trim().length != 6) {
return;
}
gFFI.send2FA(sessionId, code.value.trim());
gFFI.send2FA(sessionId, controller.text.trim());
close();
dialogManager.showLoading(translate('Logging in...'),
onCancel: closeConnection);
}
late Dialog2FaField codeField;
codeField = Dialog2FaField(
controller: controller,
title: translate('Verification code'),
onChanged: () => submitReady.value = codeField.isReady,
);
return CustomAlertDialog(
title: Text(translate("enter-2fa-title")),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(children: [
Expanded(
child: TextField(
decoration: InputDecoration(
hintText: translate("Verification code")),
onChanged: (value) {
code.value = value;
},
autofocus: true))
]),
],
),
onSubmit: submit,
onCancel: cancel,
title: Text(translate('enter-2fa-title')),
content: codeField,
actions: [
dialogButton(
'Cancel',
@ -1618,7 +1846,7 @@ void enter2FaDialog(
),
Obx(() => dialogButton(
'OK',
onPressed: code.value.trim().length == 6 ? submit : null,
onPressed: submitReady.isTrue ? submit : null,
)),
]);
});

View File

@ -615,23 +615,11 @@ Future<bool?> verificationCodeDialog(
var autoLogin = true;
var isInProgress = false;
String? errorText;
String preCode = '';
final code = TextEditingController();
final focusNode = FocusNode()..requestFocus();
Timer(Duration(milliseconds: 100), () => focusNode..requestFocus());
final res = await gFFI.dialogManager.show<bool>((setState, close, context) {
bool validate() {
return code.text.length >= 6;
}
void onVerify() async {
if (!validate()) {
setState(
() => errorText = translate('Too short, at least 6 characters.'));
return;
}
setState(() => isInProgress = true);
try {
@ -666,18 +654,21 @@ Future<bool?> verificationCodeDialog(
setState(() => isInProgress = false);
}
code.addListener(() {
if (errorText != null) {
setState(() => errorText = null);
}
if (preCode.length != 6 && code.text.length == 6) {
onVerify();
}
if (!isEmailVerification && preCode.length != 10 && code.text.length == 10) {
onVerify();
}
preCode = code.text;
});
final codeField = isEmailVerification
? DialogEmailCodeField(
controller: code,
errorText: errorText,
readyCallback: onVerify,
onChanged: () => errorText = null,
)
: Dialog2FaField(
controller: code,
errorText: errorText,
readyCallback: onVerify,
onChanged: () => errorText = null,
);
getOnSubmit() => codeField.isReady ? onVerify : null;
return CustomAlertDialog(
title: Text(translate("Verification code")),
@ -693,15 +684,7 @@ Future<bool?> verificationCodeDialog(
controller: TextEditingController(text: user?.email),
)),
isEmailVerification ? const SizedBox(height: 8) : const Offstage(),
DialogTextField(
title:
'${translate(isEmailVerification ? "Verification code" : "2FA code")}:',
controller: code,
errorText: errorText,
focusNode: focusNode,
helperText: translate(
isEmailVerification ? 'verification_tip' : '2fa_tip'),
),
codeField,
/*
CheckboxListTile(
contentPadding: const EdgeInsets.all(0),
@ -722,10 +705,10 @@ Future<bool?> verificationCodeDialog(
],
),
onCancel: close,
onSubmit: onVerify,
onSubmit: getOnSubmit(),
actions: [
dialogButton("Cancel", onPressed: close, isOutline: true),
dialogButton("Verify", onPressed: onVerify),
dialogButton("Verify", onPressed: getOnSubmit()),
]);
});

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", "Email 验证码必须是 6 个字符。"),
("2FA code must be 6 digits.", "2FA 码必须是6位数字。"),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", "Bitte richten Sie jetzt Ihren Authentifikator ein. Sie können eine Authentifizierungs-App wie Authy, Microsoft oder Google Authenticator auf Ihrem Telefon oder Desktop verwenden.\n\nScannen Sie den QR-Code mit Ihrer App und geben Sie den Code ein, den Ihre App anzeigt, um die Zwei-Faktor-Authentifizierung zu aktivieren."),
("wrong-2fa-code", "Der Code kann nicht verifiziert werden. Prüfen Sie, ob der Code und die lokalen Zeiteinstellungen korrekt sind."),
("enter-2fa-title", "Zwei-Faktor-Authentifizierung"),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}

View File

@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("enable-2fa-desc", ""),
("wrong-2fa-code", ""),
("enter-2fa-title", ""),
("Email verification code must be 6 characters.", ""),
("2FA code must be 6 digits.", ""),
].iter().cloned().collect();
}