forked from baron/baron-sso
비밀번호 Caps Lock 활성화 안내 문구 표시
This commit is contained in:
@@ -159,6 +159,7 @@ resend_wait = "You can resend in {time}."
|
|||||||
short_code_help = "You can also sign in with the last 2 letters and 6 digits from the link you received."
|
short_code_help = "You can also sign in with the last 2 letters and 6 digits from the link you received."
|
||||||
|
|
||||||
[msg.userfront.login.password]
|
[msg.userfront.login.password]
|
||||||
|
caps_lock_on = "Caps Lock is on."
|
||||||
failed = "Sign-in failed: {error}"
|
failed = "Sign-in failed: {error}"
|
||||||
missing_credentials = "Enter both your email or phone number and your password."
|
missing_credentials = "Enter both your email or phone number and your password."
|
||||||
|
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ resend_wait = "재발송은 {time} 후 가능합니다."
|
|||||||
short_code_help = "링크로 받은 값의 뒤 문자 2개와 숫자 6자리를 입력하셔도 로그인 할 수 있습니다."
|
short_code_help = "링크로 받은 값의 뒤 문자 2개와 숫자 6자리를 입력하셔도 로그인 할 수 있습니다."
|
||||||
|
|
||||||
[msg.userfront.login.password]
|
[msg.userfront.login.password]
|
||||||
|
caps_lock_on = "Caps Lock이 켜져 있습니다."
|
||||||
failed = "로그인 실패: {error}"
|
failed = "로그인 실패: {error}"
|
||||||
missing_credentials = "이메일(또는 전화번호)와 비밀번호를 모두 입력해주세요."
|
missing_credentials = "이메일(또는 전화번호)와 비밀번호를 모두 입력해주세요."
|
||||||
|
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ resend_wait = ""
|
|||||||
short_code_help = ""
|
short_code_help = ""
|
||||||
|
|
||||||
[msg.userfront.login.password]
|
[msg.userfront.login.password]
|
||||||
|
caps_lock_on = ""
|
||||||
failed = ""
|
failed = ""
|
||||||
missing_credentials = ""
|
missing_credentials = ""
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
import 'package:qr_flutter/qr_flutter.dart';
|
||||||
@@ -43,8 +44,10 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
|
|||||||
final TextEditingController _passwordLoginIdController =
|
final TextEditingController _passwordLoginIdController =
|
||||||
TextEditingController();
|
TextEditingController();
|
||||||
final TextEditingController _passwordController = TextEditingController();
|
final TextEditingController _passwordController = TextEditingController();
|
||||||
|
final FocusNode _passwordFocusNode = FocusNode();
|
||||||
String? _redirectUrl;
|
String? _redirectUrl;
|
||||||
String? _loginChallenge;
|
String? _loginChallenge;
|
||||||
|
bool _isPasswordCapsLockOn = false;
|
||||||
|
|
||||||
// QR Login Variables
|
// QR Login Variables
|
||||||
String? _qrImageBase64;
|
String? _qrImageBase64;
|
||||||
@@ -93,6 +96,8 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
|
|||||||
_parseBoolParam(Uri.base.queryParameters['drySend']) &&
|
_parseBoolParam(Uri.base.queryParameters['drySend']) &&
|
||||||
!AuthProxyService.isProdEnv;
|
!AuthProxyService.isProdEnv;
|
||||||
_redirectUrl = widget.redirectUrl;
|
_redirectUrl = widget.redirectUrl;
|
||||||
|
_passwordFocusNode.addListener(_handlePasswordFocusChange);
|
||||||
|
HardwareKeyboard.instance.addHandler(_handleHardwareKeyEvent);
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
final uri = Uri.base;
|
final uri = Uri.base;
|
||||||
@@ -154,6 +159,40 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _handlePasswordFocusChange() {
|
||||||
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_passwordFocusNode.hasFocus) {
|
||||||
|
_syncPasswordCapsLockState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_isPasswordCapsLockOn) {
|
||||||
|
setState(() {
|
||||||
|
_isPasswordCapsLockOn = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _handleHardwareKeyEvent(KeyEvent event) {
|
||||||
|
if (_passwordFocusNode.hasFocus) {
|
||||||
|
_syncPasswordCapsLockState();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _syncPasswordCapsLockState() {
|
||||||
|
final isEnabled = HardwareKeyboard.instance.lockModesEnabled.contains(
|
||||||
|
KeyboardLockMode.capsLock,
|
||||||
|
);
|
||||||
|
if (!mounted || isEnabled == _isPasswordCapsLockOn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_isPasswordCapsLockOn = isEnabled;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _tryCookieSession({bool silent = true}) async {
|
Future<void> _tryCookieSession({bool silent = true}) async {
|
||||||
final loginChallenge = _loginChallenge;
|
final loginChallenge = _loginChallenge;
|
||||||
final token = AuthTokenStore.getToken();
|
final token = AuthTokenStore.getToken();
|
||||||
@@ -936,6 +975,10 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
|
|||||||
_linkIdController.dispose();
|
_linkIdController.dispose();
|
||||||
_passwordLoginIdController.dispose();
|
_passwordLoginIdController.dispose();
|
||||||
_passwordController.dispose();
|
_passwordController.dispose();
|
||||||
|
_passwordFocusNode
|
||||||
|
..removeListener(_handlePasswordFocusChange)
|
||||||
|
..dispose();
|
||||||
|
HardwareKeyboard.instance.removeHandler(_handleHardwareKeyEvent);
|
||||||
_shortCodePrefixController.dispose();
|
_shortCodePrefixController.dispose();
|
||||||
_shortCodeDigitsController.dispose();
|
_shortCodeDigitsController.dispose();
|
||||||
_linkResendTimer?.cancel();
|
_linkResendTimer?.cancel();
|
||||||
@@ -1299,6 +1342,24 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _capsLockWarningText(BuildContext context) {
|
||||||
|
const key = 'msg.userfront.login.password.caps_lock_on';
|
||||||
|
final languageCode = Localizations.localeOf(context).languageCode;
|
||||||
|
if (languageCode == 'ko') {
|
||||||
|
final translated = tr(key);
|
||||||
|
if (translated != key) {
|
||||||
|
return translated;
|
||||||
|
}
|
||||||
|
return 'Caps Lock이 켜져 있습니다.';
|
||||||
|
}
|
||||||
|
|
||||||
|
final translated = tr(key, fallback: 'Caps Lock is on.');
|
||||||
|
if (translated != key) {
|
||||||
|
return translated;
|
||||||
|
}
|
||||||
|
return 'Caps Lock is on.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (_verificationOnly && _verificationApproved) {
|
if (_verificationOnly && _verificationApproved) {
|
||||||
@@ -1410,6 +1471,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
|
|||||||
key: const ValueKey(
|
key: const ValueKey(
|
||||||
'password_login_password_input',
|
'password_login_password_input',
|
||||||
),
|
),
|
||||||
|
focusNode: _passwordFocusNode,
|
||||||
controller: _passwordController,
|
controller: _passwordController,
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
@@ -1423,6 +1485,29 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
|
|||||||
),
|
),
|
||||||
onSubmitted: (_) => _handlePasswordLogin(),
|
onSubmitted: (_) => _handlePasswordLogin(),
|
||||||
),
|
),
|
||||||
|
if (_isPasswordCapsLockOn) ...[
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.keyboard_capslock_rounded,
|
||||||
|
size: 18,
|
||||||
|
color: Colors.orange,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
_capsLockWarningText(context),
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.orange,
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
key: const ValueKey(
|
key: const ValueKey(
|
||||||
|
|||||||
Reference in New Issue
Block a user