feat(don): add volume slider to moderator

This commit is contained in:
2026-06-28 23:49:47 +07:00 Unverified
parent aa6e7809f0
commit 409fcfd656
5 changed files with 54 additions and 18 deletions
+5 -13
View File
@@ -13,7 +13,7 @@ import {
import Link from "next/link";
import { useRouter } from "next/navigation";
import posthog from "posthog-js";
import { useEffect, useRef } from "react";
import { useEffect } from "react";
import { useEllipsisVisible } from "react-hook-text-overflow";
import { DataTable } from "@/components/tantable";
import { SimpleTooltip } from "@/components/tooltip";
@@ -183,23 +183,15 @@ export function DonateAdminPage({
}
// component cuz simplicity sake
export function DonateWatcher({ sfx = false }: { sfx?: boolean }) {
const player = useRef<HTMLAudioElement | null>(null);
export function DonateWatcher() {
const router = useRouter();
useEffect(() => {
if (!router) return;
return sse.donate.subMany({
ping: () => {
router.refresh();
player.current?.play();
},
update: () => router.refresh(),
ping: router.refresh.bind(router),
update: router.refresh.bind(router),
}).clean;
}, [router]);
if (sfx)
return (
<audio src="/assets/donate-mod-sfx.wav" preload="auto" ref={player} />
);
else return undefined;
return undefined;
}
+37 -2
View File
@@ -1,5 +1,7 @@
"use client";
import { Button } from "@/components/ui/button";
import { Slider } from "@/components/ui/slider";
import { shared } from "@/lib/comms";
import { sse } from "@/lib/db/sse-endpoints";
import { ExternalLink, RadioTower } from "lucide-react";
import dynamic from "next/dynamic";
@@ -39,10 +41,22 @@ export function PIP(props: ComponentProps<typeof ReactPIP>) {
export function ClientTracker() {
const [connected, setConnected] = useState(0);
const player = useRef<HTMLAudioElement>(null);
const tracking = useRef(0);
shared.signal(
"moderator.volume",
(ev) => player.current && (player.current.volume = ev ?? 1),
);
useEffect(
() => sse.donate.sub("heartbeat", () => tracking.current++).clean,
() =>
player.current
? sse.donate.subMany({
heartbeat: () => tracking.current++,
ping: player.current.play.bind(player.current),
}).clean
: undefined,
[],
);
useEffect(() => {
@@ -54,7 +68,28 @@ export function ClientTracker() {
}, []);
return (
<Button variant="outline" disabled size="sm">
<RadioTower /> Connected: {connected}
<RadioTower /> {connected}
<audio
src="/assets/donate-mod-sfx.wav"
preload="auto"
ref={player}
hidden
/>
</Button>
);
}
export function VolumeChanger() {
const [volume, setVolume] = shared.state("moderator.volume");
return (
<div className="px-5 py-1">
<Slider
max={1}
step={0.01}
value={[volume ?? 0.5]}
onValueChange={(v) => setVolume(v[0])}
/>
</div>
);
}
+4 -3
View File
@@ -11,7 +11,7 @@ import { Spinner } from "@/components/ui/spinner";
import { ActionButton } from "@/components/action-button";
import { reloadWidget } from "../api";
import { markDone } from "@/app/widget/donate/api";
import { ClientTracker, PIP } from "./client";
import { ClientTracker, PIP, VolumeChanger } from "./client";
export default async function DonateModeratorPage() {
if (!(await adminCheck()))
@@ -32,7 +32,7 @@ export default async function DonateModeratorPage() {
return (
<PIP width={400} height={600}>
<div className="dark flex w-full max-w-100 flex-col gap-1 bg-background p-1 text-foreground scheme-dark">
<div className="dark flex w-full max-w-100 flex-col gap-1 rounded-md bg-background p-1 text-foreground scheme-dark">
<span>Donate Moderator</span>
{sub.image ? (
<div className="relative flex aspect-square w-full max-w-100 items-center justify-center overflow-hidden rounded-lg border bg-black/50">
@@ -67,7 +67,8 @@ export default async function DonateModeratorPage() {
</ActionButton>
<ClientTracker />
</div>
<DonateWatcher sfx />
<VolumeChanger />
<DonateWatcher />
</div>
</PIP>
);
+7
View File
@@ -168,6 +168,13 @@ export const DocumentPictureInPicture = forwardRef<
div.style.width = "100%";
div.style.height = "100%";
div.style.background = "black";
div.style.backgroundImage = "radial-gradient(#222 1px, transparent 1px)";
div.style.backgroundSize = "24px 24px";
div.style.height = "100svh";
div.style.display = "flex";
div.style.justifyContent = "center";
div.style.alignItems = "center";
win.document.body.appendChild(div);
+1
View File
@@ -28,6 +28,7 @@ type SharedStates = Partial<{
warning: "showcase" | "nf" | "guide" | "private";
"warning.uid": string;
"warning.src": "submit" | "input";
"moderator.volume": number;
_raw: Record<string, unknown>;
}>;