Triviaverse / src /hooks /usePlayerSync.ts
Simeon Garratt
feat: per-session location tracking + admin player intelligence
dcfeb77
Raw
History Blame Contribute Delete
2.14 kB
"use client";
import { useEffect, useState } from "react";
import {
getStoredPlayer,
storePlayer,
type StoredPlayer,
} from "@/lib/player-store";
import { isSupabaseConfigured } from "@/lib/supabase/client";
import { getOrCreatePlayer, recordPlayerVisit } from "@/lib/supabase/queries";
/**
* Hook that ensures the locally-stored player has a real Supabase UUID.
*
* On mount it reads the player from localStorage. If the player exists but
* has an empty `id` and Supabase is configured, it calls
* `getOrCreatePlayer(anonymousId, displayName)` and updates localStorage
* with the returned UUID.
*
* @returns `{ player, isLoading }` -- player may be null if none is stored,
* and isLoading is true while the Supabase sync is in-flight.
*/
export function usePlayerSync(): {
player: StoredPlayer | null;
isLoading: boolean;
} {
const [player, setPlayer] = useState<StoredPlayer | null>(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
let cancelled = false;
async function sync() {
const stored = getStoredPlayer();
if (!stored) {
setIsLoading(false);
return;
}
setPlayer(stored);
// Always verify player exists in DB -- heals stale/reset UUIDs in localStorage
if (!isSupabaseConfigured) {
setIsLoading(false);
return;
}
try {
const dbPlayer = await getOrCreatePlayer(
stored.anonymousId,
stored.displayName
);
if (cancelled) return;
if (dbPlayer) {
const synced: StoredPlayer = {
...stored,
id: dbPlayer.id,
};
storePlayer(synced);
setPlayer(synced);
// Record this session visit in the background
recordPlayerVisit(dbPlayer.id).catch(() => {});
}
} catch (err) {
console.error("[usePlayerSync] failed to sync player:", err);
} finally {
if (!cancelled) {
setIsLoading(false);
}
}
}
sync();
return () => {
cancelled = true;
};
}, []);
return { player, isLoading };
}