View management

This commit is contained in:
2026-02-09 16:31:28 +01:00
parent c6763fb63f
commit 0af907c1d4
5 changed files with 45 additions and 4 deletions
+5
View File
@@ -2,6 +2,7 @@ import { WebSocketServer, WebSocket } from "ws";
import Redis from "ioredis";
import { NextRequest, NextResponse } from "next/server";
import { LiveRunEvent } from "@/modules/live-run/types";
import { getLiveRun, updateLiveRunViews } from "@/modules/live-run/core";
export function GET(req: NextRequest) {
const headers = new Headers();
@@ -16,6 +17,9 @@ export function UPGRADE(client: WebSocket, server: WebSocketServer) {
client.once("message", async (message) => {
runId = message.toString();
await updateLiveRunViews(runId, 1);
let lastEvent: LiveRunEvent | null = null;
const sendText = async () => {
@@ -57,6 +61,7 @@ export function UPGRADE(client: WebSocket, server: WebSocketServer) {
client.once("close", async () => {
if (runId) await subscriber.unsubscribe(`liveRun:${runId}`);
subscriber.quit();
await updateLiveRunViews(runId, -1);
});
client.on("error", (err) => {
+8 -2
View File
@@ -4,7 +4,13 @@ import { LiveRun } from "@/modules/live-run/types";
import { Eye } from "lucide-react";
import { useEffect, useState } from "react";
export default function RunTimer({ run }: { run: LiveRun }) {
export default function RunTimer({
run,
views,
}: {
run: LiveRun;
views: number | null;
}) {
const [time, setTime] = useState("00:00:00");
useEffect(() => {
@@ -29,7 +35,7 @@ export default function RunTimer({ run }: { run: LiveRun }) {
</p>
<p className="text-muted-foreground flex gap-1 items-center">
<Eye className="w-4 h-4" />
{run.views}
{views || run.views}
</p>
</span>
</CardContent>
+5 -2
View File
@@ -7,7 +7,7 @@ import { FileTree } from "./components/FileTree";
import { File } from "lucide-react";
import Editor from "./components/Editor";
import useAction, { useActionInterval } from "@/hook/use-action";
import { getLiveRun } from "@/modules/live-run/actions";
import { getLiveRun, getLiveRunViews } from "@/modules/live-run/actions";
import ProblemCard from "@/app/problems/components/ProblemCard";
import ProfileCard from "./components/ProfileCard";
import { getProblem } from "@/modules/problems/actions";
@@ -18,6 +18,9 @@ export default function Run() {
const params = useParams<{ runId: string }>();
const [run] = useAction(() => getLiveRun(params.runId), [params]);
const [views] = useActionInterval(() => getLiveRunViews(params.runId), 2500, [
params,
]);
const [problem] = useAction(
async () => (run?.id ? getProblem(run.problem) : null),
@@ -55,7 +58,7 @@ export default function Run() {
</div>
<div className="flex-1 animate-fade-in opacity-0 stagger-2">
{run && <RunTimer run={run} />}
{run && <RunTimer run={run} views={views} />}
</div>
<div className="flex-2">
+4
View File
@@ -10,3 +10,7 @@ export async function getLiveRuns(): Promise<LiveRun[]> {
export async function getLiveRun(id: string): Promise<LiveRun | null> {
return Core.getLiveRun(id);
}
export async function getLiveRunViews(id: string): Promise<number | null> {
return Core.getLiveRunViews(id);
}
+23
View File
@@ -28,6 +28,29 @@ export async function getLiveRun(id: string): Promise<LiveRun | null> {
return JSON.parse(data) as LiveRun;
}
/**
* Get live run views from Redis
*/
export async function getLiveRunViews(id: string): Promise<number | null> {
const run = await getLiveRun(id);
if (!run) return null;
return run.views;
}
/**
* Get live run views from Redis
*/
export async function updateLiveRunViews(
id: string,
increment: number,
): Promise<number | null> {
const run = await getLiveRun(id);
if (!run) return null;
run.views += increment;
await redis.set(`liveRun:${run.id}`, JSON.stringify(run));
return run.views;
}
/**
* Get all live runs
*/