1
0
forked from baron/baron-sso

링크로그인 동작

This commit is contained in:
Lectom C Han
2026-01-29 09:40:01 +09:00
parent 742964cf71
commit 8faa08e377
2 changed files with 100 additions and 15 deletions

View File

@@ -42,6 +42,10 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
final TextEditingController _shortCodePrefixController = TextEditingController();
final TextEditingController _shortCodeDigitsController = TextEditingController();
String? _linkPendingRef;
String? _lastLinkLoginId;
bool _lastLinkIsEmail = true;
int _linkResendSeconds = 0;
Timer? _linkResendTimer;
@override
void initState() {
@@ -111,10 +115,30 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
void _resetLinkLoginState() {
_linkPendingRef = null;
_lastLinkLoginId = null;
_lastLinkIsEmail = true;
_linkResendTimer?.cancel();
_linkResendTimer = null;
_linkResendSeconds = 0;
_shortCodePrefixController.clear();
_shortCodeDigitsController.clear();
}
void _startLinkResendTimer(int seconds) {
_linkResendSeconds = seconds;
_linkResendTimer?.cancel();
_linkResendTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (!mounted) return;
setState(() {
if (_linkResendSeconds > 0) {
_linkResendSeconds--;
} else {
timer.cancel();
}
});
});
}
// Helper to decode JWT and get loginId
String _getLoginIdFromJwt(String jwt) {
try {
@@ -406,6 +430,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
_passwordController.dispose();
_shortCodePrefixController.dispose();
_shortCodeDigitsController.dispose();
_linkResendTimer?.cancel();
super.dispose();
}
@@ -496,11 +521,14 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
final mode = (initResponse['mode'] ?? '').toString();
final provider = (initResponse['provider'] ?? '').toString();
final interval = initResponse['interval'];
final resendAfter = initResponse['resendAfter'];
debugPrint("[Auth] Link Sent. PendingRef: $pendingRef, Mode: $mode, Provider: $provider");
if (mounted) {
setState(() {
_linkPendingRef = pendingRef?.toString();
_lastLinkLoginId = loginId;
_lastLinkIsEmail = isEmail;
});
Navigator.of(context).pop(); // Close Loading
@@ -512,15 +540,16 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
final initialInterval = (interval is int && interval > 0)
? Duration(seconds: interval)
: const Duration(seconds: 2);
if (resendAfter is int && resendAfter > 0) {
_startLinkResendTimer(resendAfter);
}
_pollForSession(pendingRef, initialInterval: initialInterval);
}
} catch (e) {
debugPrint("[Auth] Initialization failed: $e");
if (mounted && Navigator.canPop(context)) Navigator.of(context).pop();
if (mounted) {
setState(() {
_linkPendingRef = null;
});
setState(_resetLinkLoginState);
}
if (e.toString().contains("User not registered")) {
_showUnregisteredDialog();
@@ -537,6 +566,9 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
debugPrint("[Auth] Starting poll for ref: $pendingRef");
while (attempts < maxAttempts && mounted) {
if (_linkPendingRef != pendingRef) {
return;
}
await Future.delayed(pollInterval);
attempts++;
@@ -883,6 +915,24 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
),
child: const Text("코드로 로그인"),
),
const SizedBox(height: 12),
TextButton(
onPressed: _linkResendSeconds > 0
? null
: () {
final loginId = _lastLinkLoginId ?? _linkIdController.text.trim();
if (loginId.isEmpty) {
_showError("이메일 또는 휴대폰 번호를 입력해 주세요.");
return;
}
_startEnchantedFlow(loginId, isEmail: _lastLinkIsEmail || loginId.contains('@'));
},
child: Text(
_linkResendSeconds > 0
? "재발송 (${_formatTime(_linkResendSeconds)})"
: "재발송",
),
),
],
],
),