From 1954992203afaa7a0306143d0b1abf7738b6893d Mon Sep 17 00:00:00 2001 From: "soroush.asadi" Date: Sat, 13 Jun 2026 08:21:20 +0330 Subject: [PATCH] fix(auth): advance to OTP code step in production + clear profile on logout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AuthScreen gated the code-entry step on devCode != null, so with real SMS (no devCode) it got stuck after "send". Gate on a `sent` flag instead; add sending state, send-failure message, "code sent" hint, change-number, and raise the code input cap to 6 (codes are 5 digits). - signOut now resets the store to a fresh guest profile, and the SignalR service clears its cachedProfile — so the previous user's name/avatar no longer linger after logout. - i18n: auth.sending / sendFailed / codeSent / invalidPhone / changeNumber. Co-Authored-By: Claude Opus 4.8 --- src/components/screens/AuthScreen.tsx | 48 +++++++++++++++++++++------ src/lib/i18n.tsx | 10 ++++++ src/lib/online/signalr-service.ts | 1 + src/lib/session-store.ts | 4 ++- 4 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/components/screens/AuthScreen.tsx b/src/components/screens/AuthScreen.tsx index b618be5..5f73ab4 100644 --- a/src/components/screens/AuthScreen.tsx +++ b/src/components/screens/AuthScreen.tsx @@ -62,14 +62,27 @@ function PhoneForm({ onDone }: { onDone: () => void }) { const verifyOtp = useSessionStore((s) => s.verifyOtp); const [phone, setPhone] = useState(""); const [code, setCode] = useState(""); + const [sent, setSent] = useState(false); const [devCode, setDevCode] = useState(null); + const [busy, setBusy] = useState(false); const [error, setError] = useState(""); const send = async () => { - if (phone.trim().length < 4) return; - const res = await requestOtp(phone.trim()); - setDevCode(res.devCode ?? null); + if (phone.trim().length < 10) { + setError(t("auth.invalidPhone")); + return; + } + setBusy(true); setError(""); + try { + const res = await requestOtp(phone.trim()); + setDevCode(res.devCode ?? null); + setSent(true); // advance to the code step regardless of dev/prod + } catch { + setError(t("auth.sendFailed")); + } finally { + setBusy(false); + } }; const verify = async () => { @@ -95,15 +108,22 @@ function PhoneForm({ onDone }: { onDone: () => void }) { /> - {devCode == null ? ( - + {!sent ? ( + <> + + {error &&

{error}

} + ) : ( -
- {t("auth.devCode", { code: devCode })} -
+ {devCode ? ( +
+ {t("auth.devCode", { code: devCode })} +
+ ) : ( +

{t("auth.codeSent")}

+ )}
void }) { value={code} onChange={(e) => setCode(e.target.value)} placeholder={t("auth.codePlaceholder")} - maxLength={4} + maxLength={6} className="w-full rounded-xl bg-navy-900/70 gold-border px-4 py-3 text-cream text-center text-xl tracking-[0.5em] outline-none focus:ring-2 focus:ring-gold-500/40" />
@@ -120,6 +140,12 @@ function PhoneForm({ onDone }: { onDone: () => void }) { +
)} diff --git a/src/lib/i18n.tsx b/src/lib/i18n.tsx index a0a9264..d0fa6fc 100644 --- a/src/lib/i18n.tsx +++ b/src/lib/i18n.tsx @@ -282,6 +282,11 @@ const fa: Dict = { "auth.toggleSignup": "حساب ندارید؟ ثبت‌نام کنید", "auth.toggleSignin": "حساب دارید؟ وارد شوید", "auth.invalidCode": "کد نادرست است", + "auth.invalidPhone": "شماره موبایل را درست وارد کنید", + "auth.sending": "در حال ارسال…", + "auth.sendFailed": "ارسال پیامک ناموفق بود، دوباره تلاش کنید", + "auth.codeSent": "کد به شماره شما پیامک شد", + "auth.changeNumber": "تغییر شماره", "auth.otherSoon": "سایر روش‌های ورود به‌زودی فعال می‌شوند", "reward.title": "پاداش بازی", @@ -646,6 +651,11 @@ const en: Dict = { "auth.toggleSignup": "No account? Sign up", "auth.toggleSignin": "Have an account? Sign in", "auth.invalidCode": "Invalid code", + "auth.invalidPhone": "Enter a valid mobile number", + "auth.sending": "Sending…", + "auth.sendFailed": "Couldn't send the SMS, try again", + "auth.codeSent": "Code sent to your number", + "auth.changeNumber": "Change number", "auth.otherSoon": "Other sign-in methods coming soon", "reward.title": "Match rewards", diff --git a/src/lib/online/signalr-service.ts b/src/lib/online/signalr-service.ts index 7cfe9e1..c0a9867 100644 --- a/src/lib/online/signalr-service.ts +++ b/src/lib/online/signalr-service.ts @@ -231,6 +231,7 @@ export class SignalrService implements OnlineService { async signOut() { this.session = null; this.token = null; + this.cachedProfile = null; // drop the signed-in profile so it can't leak post-logout if (typeof window !== "undefined") localStorage.removeItem(LS_SESSION); await this.conn?.stop(); this.conn = null; diff --git a/src/lib/session-store.ts b/src/lib/session-store.ts index ba628a2..df124f5 100644 --- a/src/lib/session-store.ts +++ b/src/lib/session-store.ts @@ -88,7 +88,9 @@ export const useSessionStore = create((set, get) => ({ signOut: async () => { await getService().signOut(); - set({ session: null, isAuthed: false }); + // Reset to a fresh guest profile so the old name/avatar don't linger. + const profile = await getService().getProfile(); + set({ session: null, isAuthed: false, profile }); }, updateProfile: async (patch) => {