feat(P5B.T3): useGame hook with realtime + makeMove
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { getGame, makeGameMove, type GameRecord } from '@chat-app/shared/chat';
|
||||
|
||||
import { supabase } from '../lib/supabase';
|
||||
|
||||
export function useGame(gameId: string | null): {
|
||||
game: GameRecord | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
makeMove: (move: object) => Promise<void>;
|
||||
} {
|
||||
const [game, setGame] = useState<GameRecord | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!gameId) {
|
||||
setGame(null);
|
||||
setLoading(false);
|
||||
setError(null);
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
void (async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const fresh = await getGame(supabase, gameId);
|
||||
if (!cancelled) {
|
||||
setGame(fresh);
|
||||
setLoading(false);
|
||||
}
|
||||
} catch (err) {
|
||||
if (!cancelled) {
|
||||
setLoading(false);
|
||||
setError(err instanceof Error ? err.message : 'failed to load game');
|
||||
}
|
||||
}
|
||||
})();
|
||||
const channel = supabase
|
||||
.channel('game:' + gameId)
|
||||
.on(
|
||||
'postgres_changes',
|
||||
{
|
||||
event: 'UPDATE',
|
||||
schema: 'public',
|
||||
table: 'conversation_games',
|
||||
filter: 'id=eq.' + gameId,
|
||||
},
|
||||
() => {
|
||||
void getGame(supabase, gameId)
|
||||
.then((fresh) => { if (fresh) setGame(fresh); })
|
||||
.catch((err) => { console.warn('game realtime refetch failed', err); });
|
||||
},
|
||||
)
|
||||
.subscribe();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
void supabase.removeChannel(channel);
|
||||
};
|
||||
}, [gameId]);
|
||||
|
||||
const makeMove = useCallback(async (move: object) => {
|
||||
if (!gameId) return;
|
||||
try {
|
||||
await makeGameMove(supabase, { gameId, move });
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'move failed');
|
||||
throw err;
|
||||
}
|
||||
}, [gameId]);
|
||||
|
||||
return { game, loading, error, makeMove };
|
||||
}
|
||||
Reference in New Issue
Block a user