mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 09:31:20 -04:00
84 lines
3.2 KiB
JavaScript
84 lines
3.2 KiB
JavaScript
// Hook: useUserIdentitySync
|
|
// Purpose: Keeps local identity state synchronized with server session/auth updates. Scope: Handles identity hydration, change propagation, and persistence touch points.
|
|
import { useCallback, useEffect, useRef } from 'react';
|
|
import { useSessionActions, useSessionSelector } from '../context/SessionContext.jsx';
|
|
import { useSocket } from '../context/SocketContext.jsx';
|
|
import { useSettingsNamespace } from '../settings/index.js';
|
|
|
|
export default function useUserIdentitySync() {
|
|
const socket = useSocket();
|
|
const connected = useSessionSelector((state) => state.connected);
|
|
const { identifySession } = useSessionActions();
|
|
const { value: identity, status: identityStatus, save: saveIdentity } = useSettingsNamespace('identity', {
|
|
cookieUserId: '',
|
|
});
|
|
const { value: profile, status: profileStatus } = useSettingsNamespace('profile', { nickname: '' });
|
|
|
|
const inFlightRef = useRef(false);
|
|
const lastAckSocketRef = useRef(null);
|
|
const retryTimerRef = useRef(null);
|
|
|
|
const ready = identityStatus === 'ready' && profileStatus === 'ready';
|
|
const cookieUserId = (identity?.cookieUserId || '').trim();
|
|
const nickname = (profile?.nickname || '').trim();
|
|
|
|
const clearRetry = useCallback(() => {
|
|
if (retryTimerRef.current) {
|
|
clearTimeout(retryTimerRef.current);
|
|
retryTimerRef.current = null;
|
|
}
|
|
}, []);
|
|
|
|
const sendIdentify = useCallback(async () => {
|
|
if (!ready || !connected || !socket?.id || inFlightRef.current) return;
|
|
inFlightRef.current = true;
|
|
try {
|
|
const resp = await identifySession({ cookieUserId, nickname });
|
|
const nextKey = (resp?.cookieUserId || '').trim();
|
|
if (nextKey && nextKey !== cookieUserId) {
|
|
saveIdentity((current) => ({ ...(current || {}), cookieUserId: nextKey }));
|
|
}
|
|
lastAckSocketRef.current = socket.id;
|
|
clearRetry();
|
|
} catch {
|
|
clearRetry();
|
|
retryTimerRef.current = setTimeout(() => {
|
|
sendIdentify();
|
|
}, 2000);
|
|
} finally {
|
|
inFlightRef.current = false;
|
|
}
|
|
}, [clearRetry, connected, cookieUserId, identifySession, nickname, ready, saveIdentity, socket?.id]);
|
|
|
|
useEffect(() => {
|
|
if (!ready || !connected || !socket?.id) return;
|
|
if (lastAckSocketRef.current === socket.id) return;
|
|
sendIdentify();
|
|
}, [connected, ready, sendIdentify, socket?.id]);
|
|
|
|
useEffect(() => {
|
|
if (!ready || !connected || !socket?.id) return;
|
|
sendIdentify();
|
|
}, [ready, connected, socket?.id, cookieUserId, nickname, sendIdentify]);
|
|
|
|
useEffect(() => {
|
|
const handleOnline = () => {
|
|
if (!socket?.connected) return;
|
|
sendIdentify();
|
|
};
|
|
const handleVisibility = () => {
|
|
if (typeof document === 'undefined' || document.visibilityState !== 'visible') return;
|
|
if (!socket?.connected) return;
|
|
sendIdentify();
|
|
};
|
|
window.addEventListener('online', handleOnline);
|
|
document.addEventListener('visibilitychange', handleVisibility);
|
|
return () => {
|
|
window.removeEventListener('online', handleOnline);
|
|
document.removeEventListener('visibilitychange', handleVisibility);
|
|
};
|
|
}, [sendIdentify, socket?.connected]);
|
|
|
|
useEffect(() => () => clearRetry(), [clearRetry]);
|
|
}
|