1
0
forked from baron/baron-sso

비밀번호 Caps Lock 활성화 안내 문구 표시

This commit is contained in:
2026-03-24 14:36:04 +09:00
parent 1951336307
commit 839fabd056
4 changed files with 88 additions and 0 deletions

View File

@@ -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."
[msg.userfront.login.password]
caps_lock_on = "Caps Lock is on."
failed = "Sign-in failed: {error}"
missing_credentials = "Enter both your email or phone number and your password."

View File

@@ -158,6 +158,7 @@ resend_wait = "재발송은 {time} 후 가능합니다."
short_code_help = "링크로 받은 값의 뒤 문자 2개와 숫자 6자리를 입력하셔도 로그인 할 수 있습니다."
[msg.userfront.login.password]
caps_lock_on = "Caps Lock이 켜져 있습니다."
failed = "로그인 실패: {error}"
missing_credentials = "이메일(또는 전화번호)와 비밀번호를 모두 입력해주세요."

View File

@@ -158,6 +158,7 @@ resend_wait = ""
short_code_help = ""
[msg.userfront.login.password]
caps_lock_on = ""
failed = ""
missing_credentials = ""

View File

@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:qr_flutter/qr_flutter.dart';
@@ -43,8 +44,10 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
final TextEditingController _passwordLoginIdController =
TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final FocusNode _passwordFocusNode = FocusNode();
String? _redirectUrl;
String? _loginChallenge;
bool _isPasswordCapsLockOn = false;
// QR Login Variables
String? _qrImageBase64;
@@ -93,6 +96,8 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
_parseBoolParam(Uri.base.queryParameters['drySend']) &&
!AuthProxyService.isProdEnv;
_redirectUrl = widget.redirectUrl;
_passwordFocusNode.addListener(_handlePasswordFocusChange);
HardwareKeyboard.instance.addHandler(_handleHardwareKeyEvent);
WidgetsBinding.instance.addPostFrameCallback((_) async {
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 {
final loginChallenge = _loginChallenge;
final token = AuthTokenStore.getToken();
@@ -936,6 +975,10 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
_linkIdController.dispose();
_passwordLoginIdController.dispose();
_passwordController.dispose();
_passwordFocusNode
..removeListener(_handlePasswordFocusChange)
..dispose();
HardwareKeyboard.instance.removeHandler(_handleHardwareKeyEvent);
_shortCodePrefixController.dispose();
_shortCodeDigitsController.dispose();
_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
Widget build(BuildContext context) {
if (_verificationOnly && _verificationApproved) {
@@ -1410,6 +1471,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
key: const ValueKey(
'password_login_password_input',
),
focusNode: _passwordFocusNode,
controller: _passwordController,
obscureText: true,
decoration: InputDecoration(
@@ -1423,6 +1485,29 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
),
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),
FilledButton(
key: const ValueKey(