Реал-тайм через SSE (push-to-invalidate): живые приглашения/партии, авто-обновление без перезагрузки
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useEffect } from "react";
|
||||
|
||||
import { qk } from "../api/queryKeys";
|
||||
|
||||
interface ServerEvent {
|
||||
type: "match" | "group" | "invitations";
|
||||
match_id?: number;
|
||||
group_id?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Подписка на SSE-поток `/api/events` (push-to-invalidate): по событию с сервера точечно
|
||||
* инвалидируем нужные запросы, и TanStack Query сам их перезапрашивает. EventSource
|
||||
* переподключается автоматически. Открываем одно соединение, только когда авторизованы.
|
||||
*/
|
||||
export function useServerEvents(enabled: boolean) {
|
||||
const qc = useQueryClient();
|
||||
useEffect(() => {
|
||||
if (!enabled) return;
|
||||
const base = import.meta.env.VITE_API_BASE_URL || "";
|
||||
const es = new EventSource(`${base}/api/events`, { withCredentials: true });
|
||||
|
||||
es.onmessage = (e) => {
|
||||
let ev: ServerEvent;
|
||||
try {
|
||||
ev = JSON.parse(e.data) as ServerEvent;
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (ev.type === "invitations") {
|
||||
qc.invalidateQueries({ queryKey: qk.invitations });
|
||||
} else if (ev.type === "match") {
|
||||
if (ev.match_id != null) qc.invalidateQueries({ queryKey: qk.match(ev.match_id) });
|
||||
if (ev.group_id != null) {
|
||||
qc.invalidateQueries({ queryKey: qk.groupMatches(ev.group_id) });
|
||||
qc.invalidateQueries({ queryKey: qk.groupStats(ev.group_id) });
|
||||
}
|
||||
qc.invalidateQueries({ queryKey: qk.home });
|
||||
qc.invalidateQueries({ queryKey: qk.leaderboard });
|
||||
} else if (ev.type === "group") {
|
||||
if (ev.group_id != null) {
|
||||
qc.invalidateQueries({ queryKey: qk.group(ev.group_id) });
|
||||
qc.invalidateQueries({ queryKey: qk.groupMembers(ev.group_id) });
|
||||
qc.invalidateQueries({ queryKey: qk.groupStats(ev.group_id) });
|
||||
}
|
||||
qc.invalidateQueries({ queryKey: qk.me });
|
||||
qc.invalidateQueries({ queryKey: qk.home });
|
||||
qc.invalidateQueries({ queryKey: qk.invitations });
|
||||
}
|
||||
};
|
||||
// onerror не логируем: EventSource переподключается сам.
|
||||
|
||||
return () => es.close();
|
||||
}, [enabled, qc]);
|
||||
}
|
||||
Reference in New Issue
Block a user