diff --git a/src/battle-animations.ts b/src/battle-animations.ts index d55c620c7..5a5322e27 100644 --- a/src/battle-animations.ts +++ b/src/battle-animations.ts @@ -2792,6 +2792,19 @@ const BattleSound = new class { bgmVolume = 50; muted = false; + constructor() { + if (typeof PS === 'object') { + PS.prefs.subscribeAndRun(key => { + if (!key || key === 'musicvolume' || key === 'effectvolume' || key === 'mute') { + BattleSound.effectVolume = PS.prefs.effectvolume; + BattleSound.bgmVolume = PS.prefs.musicvolume; + BattleSound.muted = PS.prefs.mute; + BattleBGM.update(); + } + }); + } + } + loadEffect(url: string) { if (this.effectCache[url] && !this.effectCache[url].isSoundPlaceholder) { return this.effectCache[url]; diff --git a/src/client-core.ts b/src/client-core.ts index d71585fe7..82b958c90 100644 --- a/src/client-core.ts +++ b/src/client-core.ts @@ -146,6 +146,11 @@ class PSStreamModel { } return subscription; } + subscribeAndRun(listener: (value: T) => void) { + const subscription = this.subscribe(listener); + subscription.listener(null); + return subscription; + } update(value: T) { if (!this.subscriptions.length) { // save updates for later diff --git a/src/client-main.ts b/src/client-main.ts index 859bdb95b..b4a848904 100644 --- a/src/client-main.ts +++ b/src/client-main.ts @@ -24,8 +24,10 @@ const PSPrefsDefaults: {[key: string]: any} = {}; * Tracks user preferences, stored in localStorage. Contains most local * data, with the exception of backgrounds, teams, and session data, * which get their own models. + * + * Updates will name the key updated, so you don't need to overreact. */ -class PSPrefs extends PSModel { +class PSPrefs extends PSStreamModel { /** * Dark mode! */ @@ -50,6 +52,11 @@ class PSPrefs extends PSModel { */ onepanel = false; + mute = false; + effectvolume = 50; + musicvolume = 50; + notifvolume = 50; + storageEngine: 'localStorage' | 'iframeLocalStorage' | '' = ''; storage: {[k: string]: any} = {}; readonly origin = 'https://play.pokemonshowdown.com'; @@ -74,7 +81,7 @@ class PSPrefs extends PSModel { /** * Change a preference. */ - set(key: string, value: any) { + set(key: T, value: PSPrefs[T]) { if (value === null) { delete this.storage[key]; (this as any)[key] = PSPrefsDefaults[key]; @@ -82,14 +89,14 @@ class PSPrefs extends PSModel { this.storage[key] = value; (this as any)[key] = value; } - this.update(); + this.update(key); this.save(); } load(newPrefs: object, noSave?: boolean) { this.fixPrefs(newPrefs); Object.assign(this, PSPrefsDefaults); this.storage = newPrefs; - this.update(); + this.update(null); if (!noSave) this.save(); } save() { diff --git a/src/panel-topbar.tsx b/src/panel-topbar.tsx index 9691f16d1..c62625576 100644 --- a/src/panel-topbar.tsx +++ b/src/panel-topbar.tsx @@ -109,10 +109,10 @@ class PSHeader extends preact.Component<{style: {}}> { {PS.user.name} {} - {} - @@ -252,3 +252,83 @@ PS.roomTypes['user'] = { Model: UserRoom, Component: UserPanel, }; + +class VolumePanel extends PSRoomPanel { + setVolume = (e: Event) => { + const slider = e.currentTarget as HTMLInputElement; + PS.prefs.set(slider.name as 'effectvolume', Number(slider.value)); + this.forceUpdate(); + }; + setMute = (e: Event) => { + const checkbox = e.currentTarget as HTMLInputElement; + PS.prefs.set('mute', !!checkbox.checked); + PS.update(); + }; + componentDidMount() { + super.componentDidMount(); + this.subscriptions.push(PS.prefs.subscribe(() => { + this.forceUpdate(); + })); + } + render() { + const room = this.props.room; + return +

Volume

+

+ + {PS.prefs.mute ? + (muted) : + } +

+

+ + {PS.prefs.mute ? + (muted) : + } +

+

+ + {PS.prefs.mute ? + (muted) : + } +

+

+ +

+
; + } +} + +PS.roomTypes['volume'] = { + Component: VolumePanel, +}; + +class OptionsPanel extends PSRoomPanel { + setCheckbox = (e: Event) => { + const checkbox = e.currentTarget as HTMLInputElement; + PS.prefs.set(checkbox.name as 'dark', !!checkbox.checked); + this.forceUpdate(); + }; + render() { + const room = this.props.room; + return +

Graphics

+

+ +

+
; + } +} + +PS.roomTypes['options'] = { + Component: OptionsPanel, +}; diff --git a/src/panels.tsx b/src/panels.tsx index 60b190fc5..c0fe9bc67 100644 --- a/src/panels.tsx +++ b/src/panels.tsx @@ -259,8 +259,10 @@ class PSMain extends preact.Component { } }); - PS.prefs.subscribeAndRun(() => { - document.body.className = PS.prefs.dark ? 'dark' : ''; + PS.prefs.subscribeAndRun(key => { + if (!key || key === 'dark') { + document.body.className = PS.prefs.dark ? 'dark' : ''; + } }); } getRoom(elem: HTMLElement) { diff --git a/style/STYLING.html b/style/STYLING.html index 017641637..4aa0fe052 100644 --- a/style/STYLING.html +++ b/style/STYLING.html @@ -17,6 +17,7 @@ function customCssSuccess() { customCssLoaded = 'success'; updateCustomCss(); } +