+
s.hydrateFromServer);
+ const reset = useVoteStore((s) => s.reset);
+ const lastSyncedUidRef = useRef(null);
+
+ const sessionUser = data?.user as { id?: string } | undefined;
+ const uid = sessionUser?.id ?? null;
+
+ useEffect(() => {
+ if (status === "loading") return;
+
+ if (status === "unauthenticated") {
+ if (lastSyncedUidRef.current !== null) {
+ reset();
+ lastSyncedUidRef.current = null;
+ }
+ return;
+ }
+
+ // authenticated
+ if (!uid) return;
+ if (lastSyncedUidRef.current === uid) return;
+
+ let cancelled = false;
+ const ctrl = new AbortController();
+ const timer = setTimeout(() => ctrl.abort(), 8000);
+
+ fetch("/api/me", {
+ credentials: "include",
+ signal: ctrl.signal,
+ })
+ .then((r) => r.json())
+ .then((res) => {
+ if (cancelled) return;
+ if (res?.ok && Array.isArray(res.data?.votedArtists)) {
+ hydrateFromServer(res.data.votedArtists as string[]);
+ lastSyncedUidRef.current = uid;
+ }
+ })
+ .catch(() => {
+ // 网络失败容忍 —— 下次 status 变化或手动刷新会再试
+ })
+ .finally(() => clearTimeout(timer));
+
+ return () => {
+ cancelled = true;
+ ctrl.abort();
+ clearTimeout(timer);
+ };
+ }, [status, uid, hydrateFromServer, reset]);
+}