feat: 用 SSE 替代 SWR 轮询,实现房间状态实时推送
SSE 断连时自动降级为 2s 轮询,重连后切回 SSE。
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import { getRoomData } from "./store";
|
||||
import type { RoomStatus, MatchType } from "@/types";
|
||||
|
||||
export async function buildRoomStatus(
|
||||
roomId: string,
|
||||
): Promise<RoomStatus | null> {
|
||||
const data = await getRoomData(roomId);
|
||||
if (!data) return null;
|
||||
|
||||
const total = data.restaurants.length;
|
||||
const allFinished =
|
||||
data.users.length > 0 &&
|
||||
data.users.every((u) => (data.swipeCounts[u] ?? 0) >= total);
|
||||
|
||||
let match = data.match;
|
||||
let matchType: MatchType = null;
|
||||
let matchLikes = 0;
|
||||
let runnerUps: { id: string; likes: number }[] = [];
|
||||
|
||||
if (match) {
|
||||
matchType = "unanimous";
|
||||
matchLikes = data.users.length;
|
||||
} else if (allFinished && data.restaurants.length > 0) {
|
||||
const ranked = rankRestaurants(data.likes, data.restaurants);
|
||||
if (ranked[0].likes > 0) {
|
||||
match = ranked[0].id;
|
||||
matchType = "best";
|
||||
matchLikes = ranked[0].likes;
|
||||
runnerUps = ranked.slice(1, 3).filter((r) => r.likes > 0);
|
||||
} else {
|
||||
match = ranked[0].id;
|
||||
matchType = "no_match";
|
||||
matchLikes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const likeCounts: Record<string, number> = {};
|
||||
for (const [rid, users] of Object.entries(data.likes)) {
|
||||
if (users.length > 0) {
|
||||
likeCounts[rid] = users.length;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
roomId,
|
||||
userCount: data.users.length,
|
||||
match,
|
||||
matchType,
|
||||
matchLikes,
|
||||
runnerUps,
|
||||
likeCounts,
|
||||
swipeCounts: data.swipeCounts,
|
||||
restaurants: data.restaurants,
|
||||
};
|
||||
}
|
||||
|
||||
function rankRestaurants(
|
||||
likes: Record<string, string[]>,
|
||||
restaurants: { id: string; rating: number }[],
|
||||
): { id: string; likes: number }[] {
|
||||
return restaurants
|
||||
.map((r) => ({
|
||||
id: r.id,
|
||||
likes: likes[r.id]?.length ?? 0,
|
||||
rating: r.rating,
|
||||
}))
|
||||
.sort((a, b) => b.likes - a.likes || b.rating - a.rating)
|
||||
.map(({ id, likes: l }) => ({ id, likes: l }));
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
type Listener = () => void;
|
||||
|
||||
const listeners = new Map<string, Set<Listener>>();
|
||||
|
||||
export function subscribe(roomId: string, listener: Listener): () => void {
|
||||
if (!listeners.has(roomId)) {
|
||||
listeners.set(roomId, new Set());
|
||||
}
|
||||
const set = listeners.get(roomId)!;
|
||||
set.add(listener);
|
||||
|
||||
return () => {
|
||||
set.delete(listener);
|
||||
if (set.size === 0) listeners.delete(roomId);
|
||||
};
|
||||
}
|
||||
|
||||
export function notify(roomId: string) {
|
||||
const set = listeners.get(roomId);
|
||||
if (set) {
|
||||
for (const fn of set) fn();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user