mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-27 16:58:46 +00:00
87 lines
3.4 KiB
TypeScript
87 lines
3.4 KiB
TypeScript
import type { Howl, HowlOptions } from 'howler'
|
|
import { useEffect, useMemo, useRef, useState } from 'preact/hooks'
|
|
import { Btn, ErrorPanel, Footer, SoundConfig, TextInput, VersionSwitcher } from '../components/index.js'
|
|
import { useLocale, useTitle, useVersion } from '../contexts/index.js'
|
|
import { useAsync } from '../hooks/index.js'
|
|
import { fetchSounds } from '../services/index.js'
|
|
import { hexId } from '../Utils.js'
|
|
|
|
interface Props {
|
|
path?: string,
|
|
}
|
|
export function Sounds({}: Props) {
|
|
const { locale } = useLocale()
|
|
const { version, changeVersion } = useVersion()
|
|
useTitle(locale('title.sounds'))
|
|
|
|
const [howler, setHowler] = useState<undefined | ((options: HowlOptions) => Howl)>(undefined)
|
|
useEffect(() => {
|
|
(async () => {
|
|
const howler = await import('howler')
|
|
setHowler(() => (options: HowlOptions) => new howler.Howl(options))
|
|
})()
|
|
}, [])
|
|
|
|
const { value: sounds, error } = useAsync(async () => {
|
|
return await fetchSounds(version)
|
|
}, [version])
|
|
const soundKeys = useMemo(() => Object.keys(sounds ?? {}), [sounds])
|
|
|
|
const [search, setSearch] = useState('')
|
|
const [configs, setConfigs] = useState<SoundConfig[]>([])
|
|
const addConfig = () => {
|
|
setConfigs([{ id: hexId(), sound: search, delay: 0, pitch: 1, volume: 1 }, ...configs])
|
|
}
|
|
const editConfig = (id: string) => (changes: Partial<SoundConfig>) => {
|
|
setConfigs(configs.map(c => c.id === id ? { ...c, ...changes } : c))
|
|
}
|
|
const deleteConfig = (id: string) => () => {
|
|
setConfigs(configs.filter(c => c.id !== id))
|
|
}
|
|
|
|
const [delayedPlay, setDelayedPlay] = useState(0)
|
|
const playAll = () => {
|
|
setDelayedPlay(delayedPlay + 1)
|
|
}
|
|
|
|
const download = useRef<HTMLAnchorElement>(null)
|
|
const downloadFunction = () => {
|
|
if (!download.current) return
|
|
const hasDelay = configs.some(c => c.delay > 0)
|
|
const content = configs
|
|
.sort((a, b) => a.delay - b.delay)
|
|
.map(c => `${hasDelay ? `execute if score @s delay matches ${c.delay} run ` : ''}playsound minecraft:${c.sound} master @s ~ ~ ~ ${c.volume} ${c.pitch}`)
|
|
.join('\n')
|
|
download.current.setAttribute('href', 'data:text/plain;charset=utf-8,' + content + '%0A')
|
|
download.current.setAttribute('download', 'sounds.mcfunction')
|
|
download.current.click()
|
|
}
|
|
|
|
return <main>
|
|
{error && <ErrorPanel error={error} />}
|
|
{soundKeys.length > 0 && <>
|
|
<div class="sounds-controls">
|
|
<div class="sound-search-group">
|
|
<TextInput class="btn btn-input sound-search" list="sound-list" placeholder={locale('sounds.search')}
|
|
value={search} onChange={setSearch} onEnter={addConfig} />
|
|
<Btn icon="plus" tooltip={locale('sounds.add_sound')} class="add-sound" onClick={addConfig} />
|
|
</div>
|
|
{configs.length > 1 && <Btn icon="play" label={ locale('sounds.play_all')} class="play-all-sounds" onClick={playAll} />}
|
|
<div class="spacer"></div>
|
|
<Btn icon="download" label={locale('download')} tooltip={locale('sounds.download_function')} tooltipLoc="se" class="download-sounds" onClick={downloadFunction} />
|
|
<VersionSwitcher value={version} onChange={changeVersion} />
|
|
</div>
|
|
<div class="sounds">
|
|
{sounds && howler && configs.map(c =>
|
|
<SoundConfig key={c.id} {...c} {...{ howler, sounds, delayedPlay }} onEdit={editConfig(c.id)} onDelete={deleteConfig(c.id)} />
|
|
)}
|
|
</div>
|
|
<a ref={download} style="display: none;"></a>
|
|
<datalist id="sound-list">
|
|
{soundKeys.map(s => <option key={s} value={s} />)}
|
|
</datalist>
|
|
</>}
|
|
<Footer donate={false} />
|
|
</main>
|
|
}
|