import type { Howl, HowlOptions } from 'howler' import { useEffect, useRef, useState } from 'preact/hooks' import { Btn, NumberInput, RangeInput, TextInput } from '..' import { useLocale, useVersion } from '../../contexts' import type { SoundEvents } from '../../services' import { getSoundUrl } from '../../services' export interface SoundConfig { id: string, sound: string, delay: number, pitch: number, volume: number, } type SoundConfigProps = SoundConfig & { howler: (options: HowlOptions) => Howl, sounds: SoundEvents, onEdit: (changes: Partial) => unknown, onDelete: () => unknown, delayedPlay?: number, } export function SoundConfig({ howler, sounds, sound, delay, pitch, volume, onEdit, onDelete, delayedPlay }: SoundConfigProps) { const { locale } = useLocale() const { version } = useVersion() const [loading, setLoading] = useState(true) const [playing, setPlaying] = useState(false) const [invalid, setInvalid] = useState(false) const howls = useRef([]) const command = `playsound minecraft:${sound} master @s ~ ~ ~ ${volume} ${pitch}` useEffect(() => { const soundEvent = sounds[sound] setInvalid((soundEvent?.sounds?.length ?? 0) === 0) howls.current.forEach(h => h.stop()) howls.current = (soundEvent?.sounds ?? []).map(entry => { const soundPath = typeof entry === 'string' ? entry : entry.name const url = getSoundUrl(version, soundPath) const howl = howler({ src: [url], format: ['ogg'], volume, rate: pitch, }) howl.on('end', () => { setPlaying(false) }) const completed = () => { if (loading && howls.current.every(h => h.state() === 'loaded')) { setLoading(false) } } if (howl.state() === 'loaded') { setTimeout(() => completed()) } else { howl.on('load', () => { completed() }) } return howl }) setLoading(true) }, [sound, sounds]) useEffect(() => { howls.current.forEach(h => h.rate(pitch)) }, [pitch]) useEffect(() => { howls.current.forEach(h => h.volume(volume)) }, [volume]) const play = () => { if (loading || invalid) return stop() const howl = Math.floor(Math.random() * howls.current.length) howls.current[howl].play() setPlaying(true) } const stop = () => { howls.current.forEach(h => h.stop()) } useEffect(() => { if (delayedPlay) setTimeout(() => play(), delay * 50) }, [delayedPlay]) useEffect(() => { return () => stop() }, []) const [copyActive, setCopyActive] = useState(false) const copyTimeout = useRef(undefined) const copy = () => { navigator.clipboard.writeText(command) setCopyActive(true) if (copyTimeout.current !== undefined) clearTimeout(copyTimeout.current) copyTimeout.current = setTimeout(() => { setCopyActive(false) }, 2000) as any } return
onEdit({ sound })} /> onEdit({ delay })} /> onEdit({ pitch })} /> onEdit({ volume })} /> {onDelete(); stop()}} />
}