attempt fix reactivity
This commit is contained in:
parent
aea6926a97
commit
91726d85b8
6 changed files with 99 additions and 43 deletions
|
|
@ -24,6 +24,7 @@ export type PartyState = {
|
|||
|
||||
export type PartySocketOutgoing =
|
||||
| { type: "ping" }
|
||||
| { type: "subscribe_party"; partyId: string }
|
||||
| { type: "member_payload"; payload: unknown };
|
||||
|
||||
type BaseQuestion = {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,40 @@ export const pubsub = {
|
|||
},
|
||||
};
|
||||
|
||||
async function subscribeWsToParty(
|
||||
ws: {
|
||||
subscribe: (topic: string) => void;
|
||||
unsubscribe: (topic: string) => void;
|
||||
send: (message: string) => void;
|
||||
},
|
||||
userId: string,
|
||||
) {
|
||||
const membership = await getMemberRecord(db, userId);
|
||||
if (!membership) return null;
|
||||
|
||||
const nextPartyId = membership.partyId;
|
||||
const currentPartyId = socketPartyId.get(ws as object);
|
||||
if (currentPartyId && currentPartyId !== nextPartyId) {
|
||||
ws.unsubscribe(partyTopic(currentPartyId));
|
||||
}
|
||||
|
||||
socketPartyId.set(ws as object, nextPartyId);
|
||||
ws.subscribe(partyTopic(nextPartyId));
|
||||
|
||||
const snapshot = await getPartyStatus(nextPartyId);
|
||||
if (snapshot) {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type: "party_status",
|
||||
party: snapshot.party,
|
||||
members: snapshot.members,
|
||||
} satisfies PartySocketEvent),
|
||||
);
|
||||
}
|
||||
|
||||
return nextPartyId;
|
||||
}
|
||||
|
||||
export async function broadcastQuizState(
|
||||
ws: { publish: (topic: string, message: string) => void },
|
||||
partyId: string,
|
||||
|
|
@ -73,8 +107,8 @@ export const partySocketApp = new Elysia()
|
|||
|
||||
ws.subscribe(userTopic(user.id));
|
||||
|
||||
const membership = await getMemberRecord(db, user.id);
|
||||
if (!membership) {
|
||||
const subscribedPartyId = await subscribeWsToParty(ws, user.id);
|
||||
if (!subscribedPartyId) {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type: "party_status",
|
||||
|
|
@ -85,21 +119,7 @@ export const partySocketApp = new Elysia()
|
|||
return;
|
||||
}
|
||||
|
||||
socketPartyId.set(ws, membership.partyId);
|
||||
ws.subscribe(partyTopic(membership.partyId));
|
||||
|
||||
const snapshot = await getPartyStatus(membership.partyId);
|
||||
if (snapshot) {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type: "party_status",
|
||||
party: snapshot.party,
|
||||
members: snapshot.members,
|
||||
} as PartySocketEvent),
|
||||
);
|
||||
|
||||
await broadcastQuizState(ws, membership.partyId);
|
||||
}
|
||||
await broadcastQuizState(ws, subscribedPartyId);
|
||||
},
|
||||
message: async (ws, message) => {
|
||||
const data = ws.data;
|
||||
|
|
@ -121,6 +141,16 @@ export const partySocketApp = new Elysia()
|
|||
return;
|
||||
}
|
||||
|
||||
if (parsed.type === "subscribe_party") {
|
||||
const payload = parsed as {
|
||||
type: "subscribe_party";
|
||||
partyId: string;
|
||||
};
|
||||
if (typeof payload.partyId !== "string") return;
|
||||
await subscribeWsToParty(ws, user.id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (parsed.type !== "member_payload") return;
|
||||
|
||||
const MAX_MEMBER_PAYLOAD_SIZE = 8_000;
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import { eq } from "drizzle-orm";
|
|||
import Elysia, { t } from "elysia";
|
||||
import { betterAuthElysia } from "../auth";
|
||||
import { db } from "../db";
|
||||
import { party, partyMember } from "../db/schema";
|
||||
import { getMemberRecord } from "../party-data";
|
||||
import { party } from "../db/schema";
|
||||
import { getMemberRecord, getPartyStatus } from "../party-data";
|
||||
import type { QuizState } from "../party-types";
|
||||
import { QuizWorkflow, quizQueue } from "../workflows/quiz";
|
||||
import { pubsub } from "./party-socket";
|
||||
|
|
@ -49,22 +49,17 @@ export const quizRoutes = new Elysia()
|
|||
})
|
||||
.where(eq(party.id, params.partyId));
|
||||
|
||||
const members = await db
|
||||
.select({
|
||||
id: partyMember.id,
|
||||
userId: partyMember.userId,
|
||||
})
|
||||
.from(partyMember)
|
||||
.where(eq(partyMember.partyId, params.partyId));
|
||||
|
||||
pubsub.publish(
|
||||
`party:${params.partyId}`,
|
||||
JSON.stringify({
|
||||
type: "party_status",
|
||||
party: { status: "started" },
|
||||
members,
|
||||
}),
|
||||
);
|
||||
const status = await getPartyStatus(params.partyId);
|
||||
if (status) {
|
||||
pubsub.publish(
|
||||
`party:${params.partyId}`,
|
||||
JSON.stringify({
|
||||
type: "party_status",
|
||||
party: status.party,
|
||||
members: status.members,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
message: "Quiz started",
|
||||
|
|
|
|||
|
|
@ -155,13 +155,17 @@ export class QuizWorkflow extends ConfiguredInstance {
|
|||
},
|
||||
});
|
||||
const analytics = (partyRecord?.analysisData ?? null) as PartyAnalytics;
|
||||
return generatePartyQuestion({
|
||||
const question = await generatePartyQuestion({
|
||||
db,
|
||||
partyId,
|
||||
quizState,
|
||||
analytics,
|
||||
index,
|
||||
});
|
||||
if (!question) {
|
||||
throw new Error("Failed to generate quiz question");
|
||||
}
|
||||
return question;
|
||||
}
|
||||
|
||||
private static scoreRound(round: QuizRound): Array<[string, number]> {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import type { PartySocketEvent } from "../../../api/src/party-types";
|
||||
import type {
|
||||
PartySocketEvent,
|
||||
PartySocketOutgoing,
|
||||
} from "../../../api/src/party-types";
|
||||
|
||||
type Handler = (event: PartySocketEvent) => void;
|
||||
|
||||
|
|
@ -24,6 +27,12 @@ export function usePartySocket({
|
|||
const reconnectAttemptRef = useRef(0);
|
||||
const handlerRef = useRef(onMessage);
|
||||
|
||||
const send = useCallback((message: PartySocketOutgoing) => {
|
||||
const ws = wsRef.current;
|
||||
if (!ws || ws.readyState !== WebSocket.OPEN) return;
|
||||
ws.send(JSON.stringify(message));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
handlerRef.current = onMessage;
|
||||
}, [onMessage]);
|
||||
|
|
@ -42,7 +51,6 @@ export function usePartySocket({
|
|||
|
||||
ws.onmessage = (event) => {
|
||||
const parsed = JSON.parse(event.data) as PartySocketEvent;
|
||||
console.log(parsed);
|
||||
handlerRef.current?.(parsed);
|
||||
};
|
||||
|
||||
|
|
@ -120,8 +128,9 @@ export function usePartySocket({
|
|||
isConnected: connectionState === "connected",
|
||||
isConnecting: connectionState === "connecting",
|
||||
isReconnecting: connectionState === "reconnecting",
|
||||
send,
|
||||
}),
|
||||
[connectionState],
|
||||
[connectionState, send],
|
||||
);
|
||||
|
||||
return state;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {
|
|||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import type {
|
||||
|
|
@ -75,6 +76,11 @@ export function PartyProvider({ children }: { children: ReactNode }) {
|
|||
if (!url) return null;
|
||||
return url;
|
||||
}, []);
|
||||
const subscribedPartyIdRef = useRef<string | null>(null);
|
||||
const wsState = usePartySocket({
|
||||
apiUrl: userId ? apiUrl : null,
|
||||
onMessage: userId ? handleMessage : null,
|
||||
});
|
||||
|
||||
const resetParty = useCallback(() => {
|
||||
setState(emptyPartyState);
|
||||
|
|
@ -84,10 +90,21 @@ export function PartyProvider({ children }: { children: ReactNode }) {
|
|||
if (!userId) resetParty();
|
||||
}, [resetParty, userId]);
|
||||
|
||||
const wsState = usePartySocket({
|
||||
apiUrl: userId ? apiUrl : null,
|
||||
onMessage: userId ? handleMessage : null,
|
||||
});
|
||||
useEffect(() => {
|
||||
if (wsState.connectionState !== "connected") {
|
||||
subscribedPartyIdRef.current = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userId || !state.party?.id) return;
|
||||
if (subscribedPartyIdRef.current === state.party.id) return;
|
||||
|
||||
wsState.send({
|
||||
type: "subscribe_party",
|
||||
partyId: state.party.id,
|
||||
});
|
||||
subscribedPartyIdRef.current = state.party.id;
|
||||
}, [state.party?.id, userId, wsState.connectionState, wsState.send]);
|
||||
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
|
|
|
|||
Loading…
Reference in a new issue