diff --git a/src/app/api/run/move/route.ts b/src/app/api/run/move/route.ts index 4df1e38..c0bc9cc 100644 --- a/src/app/api/run/move/route.ts +++ b/src/app/api/run/move/route.ts @@ -4,11 +4,11 @@ import * as Core from "@/modules/live-run/core"; import { getSession } from "@/modules/account/core"; export async function POST(req: NextRequest) { - const { runId, moves, file } = await req.json(); + const { runId, moves, file, language } = await req.json(); - if (!runId || !moves || file === undefined) + if (!runId || !moves || file === undefined || language === undefined) return NextResponse.json( - { error: "body requires runId and moves and file" }, + { error: "body requires runId, moves, file and language" }, { status: 400 }, ); @@ -27,9 +27,7 @@ export async function POST(req: NextRequest) { { status: 401 }, ); - console.log(moves, file); - - const run = await Core.addLiveRunMoves(liveRun.id, file, moves); + const run = await Core.addLiveRunEvent(liveRun.id, file, language, moves); return NextResponse.json({ ok: true, run }); } diff --git a/src/app/api/view-run/route.ts b/src/app/api/view-run/route.ts index ca8959a..7dc3097 100644 --- a/src/app/api/view-run/route.ts +++ b/src/app/api/view-run/route.ts @@ -14,14 +14,22 @@ export function UPGRADE(client: WebSocket, server: WebSocketServer) { const subscriber = new Redis(); let runId: string; - client.once("message", (message) => { + client.once("message", async (message) => { runId = message.toString(); - let file: string | null; + let lastEvent: LiveRunEvent | null = null; const sendText = async () => { const text = await subscriber.get(`liveRunText:${runId}`); + if (client.readyState === WebSocket.OPEN) { - client.send(JSON.stringify({ file, moves: [], text })); + client.send( + JSON.stringify({ + file: lastEvent?.file, + language: lastEvent?.language, + moves: [], + text, + }), + ); } }; @@ -29,7 +37,7 @@ export function UPGRADE(client: WebSocket, server: WebSocketServer) { client.on("message", sendText); - subscriber.subscribe(`liveRunMoves:${runId}`, (err, count) => { + subscriber.subscribe(`liveRunEvent:${runId}`, (err, count) => { if (err) { console.error("Failed to subscribe", err); client.close(1011, "Redis subscription failed"); @@ -38,9 +46,10 @@ export function UPGRADE(client: WebSocket, server: WebSocketServer) { subscriber.on("message", (channel, message) => { const data: LiveRunEvent = JSON.parse(message); - file = data.file; + + lastEvent = data; if (client.readyState === WebSocket.OPEN) { - client.send(JSON.stringify({ moves: data.moves, file, text: null })); + client.send(JSON.stringify({ ...data, text: null })); } }); }); diff --git a/src/app/runs/[runId]/components/Editor.tsx b/src/app/runs/[runId]/components/Editor.tsx index 2036ff9..b3478bf 100644 --- a/src/app/runs/[runId]/components/Editor.tsx +++ b/src/app/runs/[runId]/components/Editor.tsx @@ -9,7 +9,7 @@ import { json } from "@codemirror/lang-json"; import { python } from "@codemirror/lang-python"; import { java } from "@codemirror/lang-java"; import { useEffect, useRef, useState } from "react"; -import { LiveRun, LiveRunMove } from "@/modules/live-run/types"; +import { LiveRun, LiveRunEvent, LiveRunMove } from "@/modules/live-run/types"; type Language = | "javascript" @@ -20,7 +20,7 @@ type Language = | "java" | "json"; -function getLanguageExtension(lang: Language) { +function getLanguageExtension(lang: Language | null) { switch (lang) { case "javascript": return javascript(); @@ -41,14 +41,15 @@ function getLanguageExtension(lang: Language) { export default function Editor({ run, - setFile, + onEvent, }: { run: LiveRun | null | undefined; - setFile: (s: string | null) => void; + onEvent: (s: LiveRunEvent) => void; }) { const [editorView, setEditorView] = useState(); const scheduledTimeouts = useRef([]); const [text, setText] = useState(""); + const [language, setLanguage] = useState(null); // WebSocket connection useEffect(() => { @@ -61,13 +62,13 @@ export default function Editor({ }; ws.onmessage = (m) => { - const data: { - moves: LiveRunMove[]; - file: string | null; - text: string | null; - } = JSON.parse(m.data); + const data: LiveRunEvent = JSON.parse(m.data); - setFile(data.file); + onEvent(data); + + if (data.language !== language) { + setLanguage(data.language as Language); + } if (data.text) { setText(data.text); @@ -104,7 +105,7 @@ export default function Editor({ className="h-full w-full" value={text} extensions={[ - getLanguageExtension("javascript"), + getLanguageExtension(language), EditorState.transactionFilter.of((tr) => { if (tr.selection && tr.isUserEvent("select")) { return []; diff --git a/src/app/runs/[runId]/page.tsx b/src/app/runs/[runId]/page.tsx index cf477c5..958c5fd 100644 --- a/src/app/runs/[runId]/page.tsx +++ b/src/app/runs/[runId]/page.tsx @@ -24,7 +24,10 @@ export default function Run() { [run], ); - const [file, setFile] = useState(null); + const [file, setFile] = useState<{ + file: string | null; + language: string | null; + }>({ file: null, language: null }); const chatMessages: ChatMessage[] = [ { user: "Alice", message: "Nice split!", time: "1:53" }, @@ -84,10 +87,15 @@ export default function Run() { {/* Code */}
- {file} + {file.file}
- + { + setFile({ file: event.file, language: event.language }); + }} + />
diff --git a/src/modules/live-run/core.ts b/src/modules/live-run/core.ts index d19915a..55d4a7f 100644 --- a/src/modules/live-run/core.ts +++ b/src/modules/live-run/core.ts @@ -42,7 +42,7 @@ export async function getLiveRuns(): Promise { * Remove a live run from Redis */ export async function removeLiveRun(id: string) { - await redis.del(`liveRun:${id}`, `liveRunMoves:${id}`); + await redis.del(`liveRun:${id}`, `liveRunEvent:${id}`); } /** -------------------- Live Run Moves -------------------- */ @@ -50,9 +50,10 @@ export async function removeLiveRun(id: string) { /** * Add moves to a live run (ephemeral) */ -export async function addLiveRunMoves( +export async function addLiveRunEvent( runId: string, file: string, + language: string, moves: LiveRunMove[], ) { const redis = new Redis(); @@ -62,7 +63,10 @@ export async function addLiveRunMoves( if (!runExists) throw "Invalid live run"; // Publish the new moves to subscribers - await redis.publish(`liveRunMoves:${runId}`, JSON.stringify({ file, moves })); + await redis.publish( + `liveRunEvent:${runId}`, + JSON.stringify({ file, language, moves }), + ); // Get the last text (from Redis) const lastText = (await redis.get(`liveRunText:${runId}`)) || ""; diff --git a/src/modules/live-run/types.ts b/src/modules/live-run/types.ts index 75b9268..5e3c04a 100644 --- a/src/modules/live-run/types.ts +++ b/src/modules/live-run/types.ts @@ -22,5 +22,6 @@ export interface LiveRunMove { export interface LiveRunEvent { moves: LiveRunMove[]; file: string | null; + language: string | null; text: string | null; }