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