mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 15:06:20 +00:00
7745c5f66b
- 39 bubble custom con relativo pointer colorato (colore campionato dal fondo di ogni bubble) + regole CSS in Chats.css (resa in-stanza border-image + anteprima nel selettore) - selezionabili via chat.styles dell'ui-config (lato server) - soundboard: paginazione 9/pagina (griglia 3x3) con frecce + indicatore, cosi la card non cresce a dismisura
101 lines
4.8 KiB
TypeScript
101 lines
4.8 KiB
TypeScript
import { AddLinkEventTracker, ILinkEventTracker, RemoveLinkEventTracker } from '@nitrots/nitro-renderer';
|
|
import { FC, useEffect, useState } from 'react';
|
|
import { LocalizeText } from '../../api';
|
|
import { Column, Flex, Text } from '../../common';
|
|
import { useSoundboard } from '../../hooks';
|
|
import { NitroCard } from '../../layout';
|
|
|
|
export const SoundboardView: FC<{}> = () =>
|
|
{
|
|
const [ isVisible, setIsVisible ] = useState(false);
|
|
const { enabled, sounds, lastPlayed, play } = useSoundboard();
|
|
|
|
const PAGE_SIZE = 9;
|
|
const [ page, setPage ] = useState(0);
|
|
const totalPages = Math.max(1, Math.ceil(sounds.length / PAGE_SIZE));
|
|
|
|
// Clamp the page if the sound list shrinks (or on first load).
|
|
useEffect(() =>
|
|
{
|
|
if(page > (totalPages - 1)) setPage(0);
|
|
}, [ totalPages, page ]);
|
|
|
|
const pageSounds = sounds.slice(page * PAGE_SIZE, (page * PAGE_SIZE) + PAGE_SIZE);
|
|
|
|
useEffect(() =>
|
|
{
|
|
const linkTracker: ILinkEventTracker = {
|
|
linkReceived: (url: string) =>
|
|
{
|
|
const parts = url.split('/');
|
|
if(parts.length < 2) return;
|
|
|
|
switch(parts[1])
|
|
{
|
|
case 'show': setIsVisible(true); return;
|
|
case 'hide': setIsVisible(false); return;
|
|
case 'toggle': setIsVisible(prev => !prev); return;
|
|
}
|
|
},
|
|
eventUrlPrefix: 'soundboard/',
|
|
};
|
|
|
|
AddLinkEventTracker(linkTracker);
|
|
|
|
return () => RemoveLinkEventTracker(linkTracker);
|
|
}, []);
|
|
|
|
// The soundboard belongs to the room — close it when the room turns it off.
|
|
useEffect(() =>
|
|
{
|
|
if(!enabled) setIsVisible(false);
|
|
}, [ enabled ]);
|
|
|
|
if(!isVisible || !enabled) return null;
|
|
|
|
return (
|
|
<NitroCard className="w-[420px] max-w-[96vw]" uniqueKey="soundboard">
|
|
<NitroCard.Header headerText={ LocalizeText('soundboard.title') } onCloseClick={ () => setIsVisible(false) } />
|
|
<NitroCard.Content>
|
|
<Column gap={ 2 }>
|
|
{ !sounds.length &&
|
|
<Text small className="text-black/50">{ LocalizeText('soundboard.empty') }</Text> }
|
|
{ !!sounds.length &&
|
|
<>
|
|
<div className="grid grid-cols-3 gap-2">
|
|
{ pageSounds.map(sound => (
|
|
<button
|
|
key={ sound.id }
|
|
onClick={ () => play(sound) }
|
|
title={ sound.name }
|
|
className="flex h-20 cursor-pointer flex-col items-center justify-center gap-1 rounded-lg bg-[#3a7bb5] px-2 text-white shadow transition-transform hover:bg-[#336ea3] active:scale-95">
|
|
<span className="text-2xl leading-none">🔊</span>
|
|
<span className="line-clamp-2 text-center text-[11px] font-bold leading-tight">{ sound.name }</span>
|
|
</button>
|
|
)) }
|
|
</div>
|
|
{ totalPages > 1 &&
|
|
<Flex alignItems="center" justifyContent="center" gap={ 2 } className="select-none pt-1">
|
|
<button
|
|
disabled={ page === 0 }
|
|
onClick={ () => setPage(p => Math.max(0, p - 1)) }
|
|
className="cursor-pointer rounded bg-[#3a7bb5] px-3 py-1 text-sm font-bold text-white hover:bg-[#336ea3] disabled:cursor-default disabled:opacity-40">◀</button>
|
|
<Text small bold className="min-w-[44px] text-center text-[#2f6f95]">{ page + 1 } / { totalPages }</Text>
|
|
<button
|
|
disabled={ page >= (totalPages - 1) }
|
|
onClick={ () => setPage(p => Math.min(totalPages - 1, p + 1)) }
|
|
className="cursor-pointer rounded bg-[#3a7bb5] px-3 py-1 text-sm font-bold text-white hover:bg-[#336ea3] disabled:cursor-default disabled:opacity-40">▶</button>
|
|
</Flex> }
|
|
</> }
|
|
{ lastPlayed &&
|
|
<Flex alignItems="center" justifyContent="center" className="pt-1">
|
|
<Text small className="text-[#2f6f95]">
|
|
{ LocalizeText('soundboard.lastplayed', [ 'user' ], [ lastPlayed.username ]) }
|
|
</Text>
|
|
</Flex> }
|
|
</Column>
|
|
</NitroCard.Content>
|
|
</NitroCard>
|
|
);
|
|
};
|