feat(don): add volume slider to moderator
This commit is contained in:
@@ -13,7 +13,7 @@ import {
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import posthog from "posthog-js";
|
import posthog from "posthog-js";
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect } from "react";
|
||||||
import { useEllipsisVisible } from "react-hook-text-overflow";
|
import { useEllipsisVisible } from "react-hook-text-overflow";
|
||||||
import { DataTable } from "@/components/tantable";
|
import { DataTable } from "@/components/tantable";
|
||||||
import { SimpleTooltip } from "@/components/tooltip";
|
import { SimpleTooltip } from "@/components/tooltip";
|
||||||
@@ -183,23 +183,15 @@ export function DonateAdminPage({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// component cuz simplicity sake
|
// component cuz simplicity sake
|
||||||
export function DonateWatcher({ sfx = false }: { sfx?: boolean }) {
|
export function DonateWatcher() {
|
||||||
const player = useRef<HTMLAudioElement | null>(null);
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!router) return;
|
if (!router) return;
|
||||||
return sse.donate.subMany({
|
return sse.donate.subMany({
|
||||||
ping: () => {
|
ping: router.refresh.bind(router),
|
||||||
router.refresh();
|
update: router.refresh.bind(router),
|
||||||
player.current?.play();
|
|
||||||
},
|
|
||||||
update: () => router.refresh(),
|
|
||||||
}).clean;
|
}).clean;
|
||||||
}, [router]);
|
}, [router]);
|
||||||
|
|
||||||
if (sfx)
|
return undefined;
|
||||||
return (
|
|
||||||
<audio src="/assets/donate-mod-sfx.wav" preload="auto" ref={player} />
|
|
||||||
);
|
|
||||||
else return undefined;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { Button } from "@/components/ui/button";
|
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 { sse } from "@/lib/db/sse-endpoints";
|
||||||
import { ExternalLink, RadioTower } from "lucide-react";
|
import { ExternalLink, RadioTower } from "lucide-react";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
@@ -39,10 +41,22 @@ export function PIP(props: ComponentProps<typeof ReactPIP>) {
|
|||||||
|
|
||||||
export function ClientTracker() {
|
export function ClientTracker() {
|
||||||
const [connected, setConnected] = useState(0);
|
const [connected, setConnected] = useState(0);
|
||||||
|
const player = useRef<HTMLAudioElement>(null);
|
||||||
const tracking = useRef(0);
|
const tracking = useRef(0);
|
||||||
|
|
||||||
|
shared.signal(
|
||||||
|
"moderator.volume",
|
||||||
|
(ev) => player.current && (player.current.volume = ev ?? 1),
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(
|
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(() => {
|
useEffect(() => {
|
||||||
@@ -54,7 +68,28 @@ export function ClientTracker() {
|
|||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
<Button variant="outline" disabled size="sm">
|
<Button variant="outline" disabled size="sm">
|
||||||
<RadioTower /> Connected: {connected}
|
<RadioTower /> {connected}
|
||||||
|
<audio
|
||||||
|
src="/assets/donate-mod-sfx.wav"
|
||||||
|
preload="auto"
|
||||||
|
ref={player}
|
||||||
|
hidden
|
||||||
|
/>
|
||||||
</Button>
|
</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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { Spinner } from "@/components/ui/spinner";
|
|||||||
import { ActionButton } from "@/components/action-button";
|
import { ActionButton } from "@/components/action-button";
|
||||||
import { reloadWidget } from "../api";
|
import { reloadWidget } from "../api";
|
||||||
import { markDone } from "@/app/widget/donate/api";
|
import { markDone } from "@/app/widget/donate/api";
|
||||||
import { ClientTracker, PIP } from "./client";
|
import { ClientTracker, PIP, VolumeChanger } from "./client";
|
||||||
|
|
||||||
export default async function DonateModeratorPage() {
|
export default async function DonateModeratorPage() {
|
||||||
if (!(await adminCheck()))
|
if (!(await adminCheck()))
|
||||||
@@ -32,7 +32,7 @@ export default async function DonateModeratorPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PIP width={400} height={600}>
|
<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>
|
<span>Donate Moderator</span>
|
||||||
{sub.image ? (
|
{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">
|
<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>
|
</ActionButton>
|
||||||
<ClientTracker />
|
<ClientTracker />
|
||||||
</div>
|
</div>
|
||||||
<DonateWatcher sfx />
|
<VolumeChanger />
|
||||||
|
<DonateWatcher />
|
||||||
</div>
|
</div>
|
||||||
</PIP>
|
</PIP>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -168,6 +168,13 @@ export const DocumentPictureInPicture = forwardRef<
|
|||||||
|
|
||||||
div.style.width = "100%";
|
div.style.width = "100%";
|
||||||
div.style.height = "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);
|
win.document.body.appendChild(div);
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ type SharedStates = Partial<{
|
|||||||
warning: "showcase" | "nf" | "guide" | "private";
|
warning: "showcase" | "nf" | "guide" | "private";
|
||||||
"warning.uid": string;
|
"warning.uid": string;
|
||||||
"warning.src": "submit" | "input";
|
"warning.src": "submit" | "input";
|
||||||
|
"moderator.volume": number;
|
||||||
_raw: Record<string, unknown>;
|
_raw: Record<string, unknown>;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user