diff --git a/src/stores/auth/auth-status-checker.tsx b/src/stores/auth/auth-status-checker.tsx
index 1a250b75..03433049 100644
--- a/src/stores/auth/auth-status-checker.tsx
+++ b/src/stores/auth/auth-status-checker.tsx
@@ -1,25 +1,74 @@
"use client";
/**
- * AuthStatusChecker 启动检查器
+ * AuthStatusChecker 启动 + 状态变化检查器
*
- * App 启动时派发一次 `AuthStatusCheckSubmitted`,
- * 让 auth machine 走"拿 deviceId → 查 token → 必要时游客登录"流程。
+ * 触发时机:
+ * 1. App 启动时(mount)派发一次 `AuthStatusCheckSubmitted`
+ * 2. loginStatus 每次变化时(外部触发)也派发一次 —— 重新读 storage 校准
+ * 例:OAuth sync 完成 → loginStatus 变 "facebook" → re-check 一次确认 storage 一致
+ * 邮箱登录成功 → loginStatus 变 "email" → re-check 确认 token 已落盘
+ *
+ * 死循环防护(关键 —— 不加会无限递归):
+ * - 状态机收 `AuthStatusCheckSubmitted` → 进 `checkingAuthStatus` → 读 storage → onDone 写回 loginStatus
+ * - 如果 useEffect 直接依赖 `loginStatus`,写回本身触发 effect → 再 dispatch → 死循环
+ * - 用 `skipNextChangeRef` 抑制一次:
+ * 每次我们自己 dispatch 前把 flag 置 true;
+ * effect 见到 loginStatus 变化且 flag 为 true 时跳过并清 flag。
*
* 与 平级:
- * - AuthStatusChecker: 一次性 init
- * - OAuthSessionSync: 持续监听 NextAuth session
+ * - AuthStatusChecker: 启动 + 状态变化时 re-check("自己也能感知")
+ * - OAuthSessionSync: 持续监听 NextAuth session("别人也在写")
*/
-import { useEffect } from "react";
+import { useEffect, useRef } from "react";
-import { useAuthDispatch } from "./auth-context";
+import type { LoginStatus } from "@/models/auth/login-status";
+
+import { useAuthDispatch, useAuthState } from "./auth-context";
export function AuthStatusChecker() {
const dispatch = useAuthDispatch();
+ const loginStatus = useAuthState().loginStatus;
+ // 上一次的 loginStatus(首次 mount 留 null —— 用来跳过首次 effect 跑)
+ const prevLoginStatusRef = useRef(null);
+ // 抑制下一次 loginStatus 变化触发的 effect(自己 dispatch 引起的写回要跳过)
+ const skipNextChangeRef = useRef(false);
+
+ // ─────────────────────────────────────────────────────────────
+ // ① App 启动时:派发一次
+ // ─────────────────────────────────────────────────────────────
useEffect(() => {
+ console.log("[auth-status-checker] mount → dispatch AuthStatusCheckSubmitted");
+ skipNextChangeRef.current = true; // 抑制启动 check 写回 loginStatus 引起的 effect
dispatch({ type: "AuthStatusCheckSubmitted" });
}, [dispatch]);
+ // ─────────────────────────────────────────────────────────────
+ // ② loginStatus 变化时:派发一次(但跳过我们自己 check 写回的那次)
+ // ─────────────────────────────────────────────────────────────
+ useEffect(() => {
+ const prev = prevLoginStatusRef.current;
+ prevLoginStatusRef.current = loginStatus;
+ if (prev === null) return; // 首次 mount —— 启动那次由 ① 处理
+ if (prev === loginStatus) return; // loginStatus 没变 —— 不触发
+ if (skipNextChangeRef.current) {
+ // 这是我们自己 check 写回的 —— 抑制本次
+ skipNextChangeRef.current = false;
+ console.log(
+ "[auth-status-checker] loginStatus change suppressed (self-triggered)",
+ { from: prev, to: loginStatus },
+ );
+ return;
+ }
+ // 外部触发(OAuth sync / 邮箱登录 / 登出 等)的 loginStatus 变化 → 再 check 一次校准
+ skipNextChangeRef.current = true;
+ console.log(
+ "[auth-status-checker] loginStatus changed externally → re-check",
+ { from: prev, to: loginStatus },
+ );
+ dispatch({ type: "AuthStatusCheckSubmitted" });
+ }, [loginStatus, dispatch]);
+
// 无 UI
return null;
-}
+}
\ No newline at end of file