mirror of
https://github.com/Xevion/dynamic-preauth.git
synced 2025-12-06 07:14:54 -06:00
Rename StatefulDemo, move socket handling to useSocket, Emboldened skeleton
This commit is contained in:
91
frontend/src/components/Demo.tsx
Normal file
91
frontend/src/components/Demo.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import Badge from "@/components/Badge";
|
||||
import Emboldened from "@/components/Emboldened";
|
||||
import useSocket from "@/components/useSocket";
|
||||
import { cn, plural, type ClassValue } from "@/util";
|
||||
import { useRef, useState } from "preact/hooks";
|
||||
|
||||
type DemoProps = {
|
||||
class?: ClassValue;
|
||||
};
|
||||
|
||||
type SessionData = {
|
||||
id: string;
|
||||
downloads: string[];
|
||||
};
|
||||
|
||||
const Demo = ({ class: className }: DemoProps) => {
|
||||
const { id, downloads } = useSocket();
|
||||
// TODO: Toasts
|
||||
|
||||
const randomBits = (bits: number) =>
|
||||
Math.floor(Math.random() * 2 ** bits)
|
||||
.toString(16)
|
||||
.padStart(bits / 4, "0")
|
||||
.toUpperCase();
|
||||
|
||||
const [highlightedIndex, setHighlightedIndex] = useState<number | null>(null);
|
||||
const highlightedTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
function highlight(index: number) {
|
||||
setHighlightedIndex(index);
|
||||
|
||||
if (highlightedTimeoutRef.current != null) {
|
||||
clearTimeout(highlightedTimeoutRef.current);
|
||||
}
|
||||
|
||||
highlightedTimeoutRef.current = setTimeout(() => {
|
||||
highlightedTimeoutRef.current = null;
|
||||
setHighlightedIndex(null);
|
||||
}, 1250);
|
||||
}
|
||||
|
||||
return (
|
||||
<div class={cn(className, "px-5 leading-6")}>
|
||||
<p class="mt-3 mb-3">
|
||||
This demo uses websockets to communicate between the server and the
|
||||
browser. Each download gets a unique identifier bound to the user
|
||||
session.
|
||||
<br />
|
||||
Your session is{" "}
|
||||
<Emboldened skeletonWidth="0x12345678" copyable={true}>
|
||||
{id}
|
||||
</Emboldened>
|
||||
. You have{" "}
|
||||
<Emboldened className="text-teal-400 font-inter">
|
||||
{downloads?.length ?? null}
|
||||
</Emboldened>{" "}
|
||||
known {plural("download", downloads?.length ?? 0)}.
|
||||
</p>
|
||||
<div class="flex flex-wrap justify-center gap-y-2.5">
|
||||
{downloads?.map((download, i) => (
|
||||
<Badge
|
||||
className={cn(
|
||||
"transition-colors border hover:border-zinc-500 duration-100 ease-in border-transparent",
|
||||
{
|
||||
"!border-zinc-300 dark:bg-zinc-600": i === highlightedIndex,
|
||||
}
|
||||
)}
|
||||
onClick={function onClick() {
|
||||
highlight(i);
|
||||
const audio = new Audio("/notify.wav");
|
||||
audio.volume = 0.5;
|
||||
audio.play();
|
||||
}}
|
||||
>
|
||||
{download}
|
||||
</Badge>
|
||||
))}
|
||||
<Badge>download</Badge>
|
||||
</div>
|
||||
<div class="mt-4 p-2 bg-zinc-900/90 rounded-md border border-zinc-700">
|
||||
<p class="my-0">
|
||||
The server running this is completely ephemeral, can restart at any
|
||||
time, and purges data on regular intervals - at which point the
|
||||
executables you've downloaded will no longer function.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Demo;
|
||||
Reference in New Issue
Block a user