Improvements
This commit is contained in:
@@ -4,11 +4,11 @@ import * as Core from "@/modules/live-run/core";
|
|||||||
import { getSession } from "@/modules/account/core";
|
import { getSession } from "@/modules/account/core";
|
||||||
|
|
||||||
export async function POST(req: NextRequest) {
|
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(
|
return NextResponse.json(
|
||||||
{ error: "body requires runId and moves and file" },
|
{ error: "body requires runId, moves, file and language" },
|
||||||
{ status: 400 },
|
{ status: 400 },
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -27,9 +27,7 @@ export async function POST(req: NextRequest) {
|
|||||||
{ status: 401 },
|
{ status: 401 },
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(moves, file);
|
const run = await Core.addLiveRunEvent(liveRun.id, file, language, moves);
|
||||||
|
|
||||||
const run = await Core.addLiveRunMoves(liveRun.id, file, moves);
|
|
||||||
|
|
||||||
return NextResponse.json({ ok: true, run });
|
return NextResponse.json({ ok: true, run });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,14 +14,22 @@ export function UPGRADE(client: WebSocket, server: WebSocketServer) {
|
|||||||
const subscriber = new Redis();
|
const subscriber = new Redis();
|
||||||
let runId: string;
|
let runId: string;
|
||||||
|
|
||||||
client.once("message", (message) => {
|
client.once("message", async (message) => {
|
||||||
runId = message.toString();
|
runId = message.toString();
|
||||||
let file: string | null;
|
let lastEvent: LiveRunEvent | null = null;
|
||||||
|
|
||||||
const sendText = async () => {
|
const sendText = async () => {
|
||||||
const text = await subscriber.get(`liveRunText:${runId}`);
|
const text = await subscriber.get(`liveRunText:${runId}`);
|
||||||
|
|
||||||
if (client.readyState === WebSocket.OPEN) {
|
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);
|
client.on("message", sendText);
|
||||||
|
|
||||||
subscriber.subscribe(`liveRunMoves:${runId}`, (err, count) => {
|
subscriber.subscribe(`liveRunEvent:${runId}`, (err, count) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error("Failed to subscribe", err);
|
console.error("Failed to subscribe", err);
|
||||||
client.close(1011, "Redis subscription failed");
|
client.close(1011, "Redis subscription failed");
|
||||||
@@ -38,9 +46,10 @@ export function UPGRADE(client: WebSocket, server: WebSocketServer) {
|
|||||||
|
|
||||||
subscriber.on("message", (channel, message) => {
|
subscriber.on("message", (channel, message) => {
|
||||||
const data: LiveRunEvent = JSON.parse(message);
|
const data: LiveRunEvent = JSON.parse(message);
|
||||||
file = data.file;
|
|
||||||
|
lastEvent = data;
|
||||||
if (client.readyState === WebSocket.OPEN) {
|
if (client.readyState === WebSocket.OPEN) {
|
||||||
client.send(JSON.stringify({ moves: data.moves, file, text: null }));
|
client.send(JSON.stringify({ ...data, text: null }));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { json } from "@codemirror/lang-json";
|
|||||||
import { python } from "@codemirror/lang-python";
|
import { python } from "@codemirror/lang-python";
|
||||||
import { java } from "@codemirror/lang-java";
|
import { java } from "@codemirror/lang-java";
|
||||||
import { useEffect, useRef, useState } from "react";
|
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 =
|
type Language =
|
||||||
| "javascript"
|
| "javascript"
|
||||||
@@ -20,7 +20,7 @@ type Language =
|
|||||||
| "java"
|
| "java"
|
||||||
| "json";
|
| "json";
|
||||||
|
|
||||||
function getLanguageExtension(lang: Language) {
|
function getLanguageExtension(lang: Language | null) {
|
||||||
switch (lang) {
|
switch (lang) {
|
||||||
case "javascript":
|
case "javascript":
|
||||||
return javascript();
|
return javascript();
|
||||||
@@ -41,14 +41,15 @@ function getLanguageExtension(lang: Language) {
|
|||||||
|
|
||||||
export default function Editor({
|
export default function Editor({
|
||||||
run,
|
run,
|
||||||
setFile,
|
onEvent,
|
||||||
}: {
|
}: {
|
||||||
run: LiveRun | null | undefined;
|
run: LiveRun | null | undefined;
|
||||||
setFile: (s: string | null) => void;
|
onEvent: (s: LiveRunEvent) => void;
|
||||||
}) {
|
}) {
|
||||||
const [editorView, setEditorView] = useState<EditorView>();
|
const [editorView, setEditorView] = useState<EditorView>();
|
||||||
const scheduledTimeouts = useRef<NodeJS.Timeout[]>([]);
|
const scheduledTimeouts = useRef<NodeJS.Timeout[]>([]);
|
||||||
const [text, setText] = useState("");
|
const [text, setText] = useState("");
|
||||||
|
const [language, setLanguage] = useState<Language | null>(null);
|
||||||
|
|
||||||
// WebSocket connection
|
// WebSocket connection
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -61,13 +62,13 @@ export default function Editor({
|
|||||||
};
|
};
|
||||||
|
|
||||||
ws.onmessage = (m) => {
|
ws.onmessage = (m) => {
|
||||||
const data: {
|
const data: LiveRunEvent = JSON.parse(m.data);
|
||||||
moves: LiveRunMove[];
|
|
||||||
file: string | null;
|
|
||||||
text: string | null;
|
|
||||||
} = JSON.parse(m.data);
|
|
||||||
|
|
||||||
setFile(data.file);
|
onEvent(data);
|
||||||
|
|
||||||
|
if (data.language !== language) {
|
||||||
|
setLanguage(data.language as Language);
|
||||||
|
}
|
||||||
|
|
||||||
if (data.text) {
|
if (data.text) {
|
||||||
setText(data.text);
|
setText(data.text);
|
||||||
@@ -104,7 +105,7 @@ export default function Editor({
|
|||||||
className="h-full w-full"
|
className="h-full w-full"
|
||||||
value={text}
|
value={text}
|
||||||
extensions={[
|
extensions={[
|
||||||
getLanguageExtension("javascript"),
|
getLanguageExtension(language),
|
||||||
EditorState.transactionFilter.of((tr) => {
|
EditorState.transactionFilter.of((tr) => {
|
||||||
if (tr.selection && tr.isUserEvent("select")) {
|
if (tr.selection && tr.isUserEvent("select")) {
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
@@ -24,7 +24,10 @@ export default function Run() {
|
|||||||
[run],
|
[run],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [file, setFile] = useState<string | null>(null);
|
const [file, setFile] = useState<{
|
||||||
|
file: string | null;
|
||||||
|
language: string | null;
|
||||||
|
}>({ file: null, language: null });
|
||||||
|
|
||||||
const chatMessages: ChatMessage[] = [
|
const chatMessages: ChatMessage[] = [
|
||||||
{ user: "Alice", message: "Nice split!", time: "1:53" },
|
{ user: "Alice", message: "Nice split!", time: "1:53" },
|
||||||
@@ -84,10 +87,15 @@ export default function Run() {
|
|||||||
{/* Code */}
|
{/* Code */}
|
||||||
<div className="flex-1 w-full max-h-svh md:max-h-full h-full pl-0 md:pl-5 flex flex-col">
|
<div className="flex-1 w-full max-h-svh md:max-h-full h-full pl-0 md:pl-5 flex flex-col">
|
||||||
<header className="text-sm font-medium text-muted-foreground flex items-center gap-1">
|
<header className="text-sm font-medium text-muted-foreground flex items-center gap-1">
|
||||||
<File size={16} /> {file}
|
<File size={16} /> {file.file}
|
||||||
</header>
|
</header>
|
||||||
<div className="flex-1 pt-3 pb-4 md:pb-5 w-full h-full">
|
<div className="flex-1 pt-3 pb-4 md:pb-5 w-full h-full">
|
||||||
<Editor run={run} setFile={setFile} />
|
<Editor
|
||||||
|
run={run}
|
||||||
|
onEvent={(event) => {
|
||||||
|
setFile({ file: event.file, language: event.language });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export async function getLiveRuns(): Promise<LiveRun[]> {
|
|||||||
* Remove a live run from Redis
|
* Remove a live run from Redis
|
||||||
*/
|
*/
|
||||||
export async function removeLiveRun(id: string) {
|
export async function removeLiveRun(id: string) {
|
||||||
await redis.del(`liveRun:${id}`, `liveRunMoves:${id}`);
|
await redis.del(`liveRun:${id}`, `liveRunEvent:${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** -------------------- Live Run Moves -------------------- */
|
/** -------------------- Live Run Moves -------------------- */
|
||||||
@@ -50,9 +50,10 @@ export async function removeLiveRun(id: string) {
|
|||||||
/**
|
/**
|
||||||
* Add moves to a live run (ephemeral)
|
* Add moves to a live run (ephemeral)
|
||||||
*/
|
*/
|
||||||
export async function addLiveRunMoves(
|
export async function addLiveRunEvent(
|
||||||
runId: string,
|
runId: string,
|
||||||
file: string,
|
file: string,
|
||||||
|
language: string,
|
||||||
moves: LiveRunMove[],
|
moves: LiveRunMove[],
|
||||||
) {
|
) {
|
||||||
const redis = new Redis();
|
const redis = new Redis();
|
||||||
@@ -62,7 +63,10 @@ export async function addLiveRunMoves(
|
|||||||
if (!runExists) throw "Invalid live run";
|
if (!runExists) throw "Invalid live run";
|
||||||
|
|
||||||
// Publish the new moves to subscribers
|
// 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)
|
// Get the last text (from Redis)
|
||||||
const lastText = (await redis.get(`liveRunText:${runId}`)) || "";
|
const lastText = (await redis.get(`liveRunText:${runId}`)) || "";
|
||||||
|
|||||||
@@ -22,5 +22,6 @@ export interface LiveRunMove {
|
|||||||
export interface LiveRunEvent {
|
export interface LiveRunEvent {
|
||||||
moves: LiveRunMove[];
|
moves: LiveRunMove[];
|
||||||
file: string | null;
|
file: string | null;
|
||||||
|
language: string | null;
|
||||||
text: string | null;
|
text: string | null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user