Finish up highlight, minor increase in time, switch to react-use-webhook, client only rendering

This commit is contained in:
2025-01-02 15:21:26 -06:00
parent 178eb40c1a
commit f18f4a0c7c
6 changed files with 61 additions and 83 deletions

View File

@@ -22,6 +22,7 @@
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-tooltip": "^5.28.0", "react-tooltip": "^5.28.0",
"react-use-websocket": "^4.11.1",
"tailwind-merge": "^2.5.5", "tailwind-merge": "^2.5.5",
"tailwindcss": "^3.4.17" "tailwindcss": "^3.4.17"
}, },

View File

@@ -47,6 +47,9 @@ importers:
react-tooltip: react-tooltip:
specifier: ^5.28.0 specifier: ^5.28.0
version: 5.28.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) version: 5.28.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react-use-websocket:
specifier: ^4.11.1
version: 4.11.1
tailwind-merge: tailwind-merge:
specifier: ^2.5.5 specifier: ^2.5.5
version: 2.5.5 version: 2.5.5
@@ -1964,6 +1967,9 @@ packages:
react: '>=16.14.0' react: '>=16.14.0'
react-dom: '>=16.14.0' react-dom: '>=16.14.0'
react-use-websocket@4.11.1:
resolution: {integrity: sha512-39e8mK2a2A1h8uY3ePF45b2q0vwMOmaEy7J5qEhQg4n7vYa5oDLmqutG36kZQgAQ/3KCZS0brlGRbbZJ0+zfKQ==}
react@19.0.0: react@19.0.0:
resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -4668,6 +4674,8 @@ snapshots:
react: 19.0.0 react: 19.0.0
react-dom: 19.0.0(react@19.0.0) react-dom: 19.0.0(react@19.0.0)
react-use-websocket@4.11.1: {}
react@19.0.0: {} react@19.0.0: {}
read-cache@1.0.0: read-cache@1.0.0:

View File

@@ -41,7 +41,7 @@ const Demo = ({ class: className }: DemoProps) => {
highlightedTimeoutRef.current = setTimeout(() => { highlightedTimeoutRef.current = setTimeout(() => {
highlightedTimeoutRef.current = null; highlightedTimeoutRef.current = null;
setHighlightedToken(null); setHighlightedToken(null);
}, 1250); }, 1500);
} }
return ( return (
@@ -74,7 +74,7 @@ const Demo = ({ class: className }: DemoProps) => {
className={cn( className={cn(
"transition-colors border hover:border-zinc-500 duration-100 ease-in border-transparent", "transition-colors border hover:border-zinc-500 duration-100 ease-in border-transparent",
{ {
"bg-zinc-500 animate-pulse-border border-white text-zinc-50": "bg-zinc-500 animate-pulse-border border-zinc-300 text-zinc-50":
highlightedToken === download.token, highlightedToken === download.token,
} }
)} )}

View File

@@ -1,5 +1,6 @@
import { withBackend } from "@/util"; import { withBackend } from "@/util";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import useWebSocket from "react-use-websocket";
export interface Download { export interface Download {
token: number; token: number;
@@ -16,107 +17,78 @@ export interface Executable {
export interface UseSocketResult { export interface UseSocketResult {
id: number | null; id: number | null;
status: Status;
executables: Executable[] | null; executables: Executable[] | null;
downloads: Download[] | null; downloads: Download[] | null;
buildLog: string | null; buildLog: string | null;
deleteDownload: (id: number) => void; deleteDownload: (id: number) => void;
} }
export interface UseSocketProps {
notify?: (token: number) => void;
}
export type Status = "connected" | "disconnected" | "connecting"; export type Status = "connected" | "disconnected" | "connecting";
function useSocket(): UseSocketResult { function useSocket({ notify }: UseSocketProps): UseSocketResult {
const { sendMessage, lastMessage, readyState } = useWebSocket(
withBackend(
window.location.protocol === "https:" ? "wss://" : "ws://",
"/ws"
)
);
const [id, setId] = useState<number | null>(null); const [id, setId] = useState<number | null>(null);
const [downloads, setDownloads] = useState<Download[] | null>(null); const [downloads, setDownloads] = useState<Download[] | null>(null);
const [executables, setExecutables] = useState<{ const [executables, setExecutables] = useState<{
build_log: string | null; build_log: string | null;
executables: Executable[]; executables: Executable[];
} | null>(null); } | null>(null);
const [status, setStatus] = useState<Status>("connecting");
const socketRef = useRef<WebSocket | null>(null); useEffect(() => {
const allowReconnectRef = useRef<boolean>(true); {
if (lastMessage == null) return;
const data = JSON.parse(lastMessage.data);
if (data.type == undefined)
throw new Error("Received message without type");
switch (data.type) {
case "notify":
const token = data.token as number;
if (notify != null) notify(token);
break;
case "state":
setId(data.session.id as number);
setDownloads(data.session.downloads as Download[]);
break;
case "executables":
setExecutables({
build_log: data.build_log,
executables: data.executables as Executable[],
});
break;
default:
console.warn("Received unknown message type", data.type);
}
}
}, [lastMessage]);
function deleteDownload(download_token: number) { function deleteDownload(download_token: number) {
if (socketRef.current == null) { if (readyState !== WebSocket.OPEN) return;
console.error("Socket is null");
return; sendMessage(
} else if (socketRef.current.readyState !== WebSocket.OPEN) {
console.error("Socket is not open", socketRef.current.readyState);
return;
}
socketRef.current.send(
JSON.stringify({ JSON.stringify({
type: "delete-download-token", type: "delete-download-token",
token: download_token, id: download_token,
}) })
); );
} }
useEffect(() => {
function connect() {
const socket = new WebSocket(
withBackend(
window.location.protocol === "https:" ? "wss://" : "ws://",
"/ws"
)
);
socketRef.current = socket;
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type == undefined)
throw new Error("Received message without type");
switch (data.type) {
case "state":
setId(data.session.id as number);
setDownloads(data.session.downloads as Download[]);
break;
case "executables":
setExecutables({
build_log: data.build_log,
executables: data.executables as Executable[],
});
break;
default:
console.warn("Received unknown message type", data.type);
}
};
socket.onclose = (event) => {
console.warn("WebSocket connection closed", event);
socketRef.current = null;
if (allowReconnectRef.current) {
setId(null);
setDownloads(null);
setExecutables(null);
setTimeout(() => {
connect();
}, 3000);
}
};
}
connect();
return () => {
// Close the socket when the component is unmounted
console.debug("Unmounting, closing WebSocket connection");
socketRef.current?.close();
allowReconnectRef.current = false;
};
}, []);
return { return {
id, id,
downloads, downloads,
status,
executables: executables?.executables ?? null, executables: executables?.executables ?? null,
buildLog: executables?.build_log, buildLog: executables?.build_log ?? null,
deleteDownload, deleteDownload,
}; };
} }

View File

@@ -6,7 +6,7 @@ import Demo from "@/components/Demo";
<Base> <Base>
<div class="w-screen h-screen flex flex-col items-center middle"> <div class="w-screen h-screen flex flex-col items-center middle">
<div <div
class="noise-card rounded-sm relative z-20 border-zinc-700 md:border w-full max-w-[40rem] md:mt-16 mb-8 shadow-lg" class="noise-card rounded-sm relative z-20 border-zinc-700 md:border w-full max-w-[40rem] min-h-[41.25rem] md:mt-16 mb-8 shadow-lg"
style={{ style={{
backgroundPosition: backgroundPosition:
Math.random() * 100 + "px " + Math.random() * 100 + "px", Math.random() * 100 + "px " + Math.random() * 100 + "px",
@@ -43,7 +43,7 @@ import Demo from "@/components/Demo";
<span class="px-3 text-2xl font-bebas tracking-wide">Demo</span> <span class="px-3 text-2xl font-bebas tracking-wide">Demo</span>
<hr class="w-32 h-px border-0 bg-zinc-600 my-0" /> <hr class="w-32 h-px border-0 bg-zinc-600 my-0" />
</div> </div>
<Demo client:load /> <Demo client:only />
</div> </div>
</div> </div>
</div> </div>

View File

@@ -4,16 +4,13 @@ export default {
theme: { theme: {
extend: { extend: {
animation: { animation: {
"pulse-border": "pulse-border 1s ease-in-out infinite", "pulse-border": "pulse-border 1s cubic-bezier(0.4, 0, 0.6, 1) infinite",
"pulse-dark": "pulse-dark 2.5s ease-in-out infinite", "pulse-dark": "pulse-dark 2.5s ease-in-out infinite",
}, },
keyframes: { keyframes: {
"pulse-border": { "pulse-border": {
"0%, 100%": {
"--tw-border-opacity": "1",
},
"50%": { "50%": {
"--tw-border-opacity": "0.5", borderColor: "rgba(100, 100, 100)",
}, },
}, },
"pulse-dark": { "pulse-dark": {