mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-09-10 12:59:04 -05:00
Add volume/dark mode controls
This is a stub for the Options screens in the Preact client.
This commit is contained in:
@@ -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];
|
||||
|
||||
@@ -146,6 +146,11 @@ class PSStreamModel<T = string> {
|
||||
}
|
||||
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
|
||||
|
||||
@@ -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<string | null> {
|
||||
/**
|
||||
* 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<T extends keyof PSPrefs>(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() {
|
||||
|
||||
@@ -109,10 +109,10 @@ class PSHeader extends preact.Component<{style: {}}> {
|
||||
<span class="username" data-name={PS.user.name} style={userColor}>
|
||||
<i class="fa fa-user" style="color:#779EC5"></i> {PS.user.name}
|
||||
</span> {}
|
||||
<button class="icon button disabled" name="joinRoom" value="volume" title="Sound" aria-label="Sound">
|
||||
<i class="fa fa-volume-up"></i>
|
||||
<button class="icon button" name="joinRoom" value="volume" title="Sound" aria-label="Sound">
|
||||
<i class={PS.prefs.mute ? 'fa fa-volume-off' : 'fa fa-volume-up'}></i>
|
||||
</button> {}
|
||||
<button class="icon button disabled" name="joinRoom" value="options" title="Options" aria-label="Options">
|
||||
<button class="icon button" name="joinRoom" value="options" title="Options" aria-label="Options">
|
||||
<i class="fa fa-cog"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -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 <PSPanelWrapper room={room}>
|
||||
<h3>Volume</h3>
|
||||
<p class="volume">
|
||||
<label class="optlabel">Effects: <span class="value">{!PS.prefs.mute && PS.prefs.effectvolume ? `${PS.prefs.effectvolume}%` : `muted`}</span></label>
|
||||
{PS.prefs.mute ?
|
||||
<em>(muted)</em> :
|
||||
<input
|
||||
type="range" min="0" max="100" step="1" name="effectvolume" value={PS.prefs.effectvolume}
|
||||
onChange={this.setVolume} onInput={this.setVolume} onKeyUp={this.setVolume}
|
||||
/>}
|
||||
</p>
|
||||
<p class="volume">
|
||||
<label class="optlabel">Music: <span class="value">{!PS.prefs.mute && PS.prefs.musicvolume ? `${PS.prefs.musicvolume}%` : `muted`}</span></label>
|
||||
{PS.prefs.mute ?
|
||||
<em>(muted)</em> :
|
||||
<input
|
||||
type="range" min="0" max="100" step="1" name="musicvolume" value={PS.prefs.musicvolume}
|
||||
onChange={this.setVolume} onInput={this.setVolume} onKeyUp={this.setVolume}
|
||||
/>}
|
||||
</p>
|
||||
<p class="volume">
|
||||
<label class="optlabel">Notifications: <span class="value">{!PS.prefs.mute && PS.prefs.notifvolume ? `${PS.prefs.notifvolume}%` : `muted`}</span></label>
|
||||
{PS.prefs.mute ?
|
||||
<em>(muted)</em> :
|
||||
<input
|
||||
type="range" min="0" max="100" step="1" name="notifvolume" value={PS.prefs.notifvolume}
|
||||
onChange={this.setVolume} onInput={this.setVolume} onKeyUp={this.setVolume}
|
||||
/>}
|
||||
</p>
|
||||
<p>
|
||||
<label class="checkbox"><input type="checkbox" name="mute" checked={PS.prefs.mute} onChange={this.setMute} /> Mute all</label>
|
||||
</p>
|
||||
</PSPanelWrapper>;
|
||||
}
|
||||
}
|
||||
|
||||
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 <PSPanelWrapper room={room}>
|
||||
<h3>Graphics</h3>
|
||||
<p>
|
||||
<label class="checkbox"><input type="checkbox" name="dark" checked={PS.prefs.dark} onChange={this.setCheckbox} /> Dark mode</label>
|
||||
</p>
|
||||
</PSPanelWrapper>;
|
||||
}
|
||||
}
|
||||
|
||||
PS.roomTypes['options'] = {
|
||||
Component: OptionsPanel,
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -17,6 +17,7 @@ function customCssSuccess() { customCssLoaded = 'success'; updateCustomCss(); }
|
||||
|
||||
<link rel="stylesheet" href="./battle.css" />
|
||||
<link rel="stylesheet" href="./custom.css" onerror="customCssErrored()" onload="customCssSuccess()" />
|
||||
<link rel="stylesheet" href="./font-awesome.css" />
|
||||
<style>
|
||||
html {
|
||||
background: #EEE;
|
||||
@@ -230,6 +231,23 @@ h1, h2, p {
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
|
||||
<p><code>.checkbox</code></p>
|
||||
|
||||
<p>
|
||||
This makes checkboxes look slightly nicer. We don't currently try to override the built-in checkbox widget, just make it a bit bigger, add the usual finger-cursor, and add a hover effect.
|
||||
</p>
|
||||
|
||||
<div class="light-container">
|
||||
<label class="checkbox"><input type="checkbox" /> This is a checkbox</label>
|
||||
</div>
|
||||
<div class="dark-container dark">
|
||||
<label class="checkbox"><input type="checkbox" /> This is a checkbox</label>
|
||||
</div>
|
||||
<div class="dark-container code dark">
|
||||
<label class="checkbox"><input type="checkbox" /> This is a checkbox</label>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
|
||||
<h2>Chat features</h2>
|
||||
|
||||
<p><code>code</code></p>
|
||||
|
||||
@@ -132,7 +132,7 @@ button:disabled {
|
||||
* Forms
|
||||
*********************************************************/
|
||||
|
||||
.label {
|
||||
.label {
|
||||
font-size: 9pt;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
@@ -141,6 +141,39 @@ button:disabled {
|
||||
font-size: 9pt;
|
||||
display: block;
|
||||
}
|
||||
.value {
|
||||
float: right;
|
||||
}
|
||||
.checkbox {
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
border: 0 none;
|
||||
font-family: Verdana, Helvetica, Arial, sans-serif;
|
||||
font-size: 10pt;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
padding: 1px 6px 3px 2px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.checkbox input {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
vertical-align: 1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.checkbox i {
|
||||
font-size: 12pt;
|
||||
vertical-align: -2px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
.checkbox:hover {
|
||||
background: #D5D5D5;
|
||||
color: black;
|
||||
}
|
||||
.dark .checkbox:hover {
|
||||
background: #383838;
|
||||
color: white;
|
||||
}
|
||||
.label strong {
|
||||
font-size: 11pt;
|
||||
display: block;
|
||||
|
||||
@@ -2805,23 +2805,25 @@ a.ilink.yours {
|
||||
box-shadow: 1px 1px 1px #D5D5D5;
|
||||
}
|
||||
|
||||
.effect-volume,
|
||||
.music-volume,
|
||||
.notif-volume {
|
||||
.volume {
|
||||
min-height: 34px;
|
||||
}
|
||||
.effect-volume label,
|
||||
.music-volume label,
|
||||
.notif-volume label {
|
||||
font-size: 8pt;
|
||||
.volume label {
|
||||
font-size: 9pt;
|
||||
}
|
||||
.effect-volume em,
|
||||
.music-volume em,
|
||||
.notif-volume em {
|
||||
.volume em {
|
||||
display: block;
|
||||
font-size: 9pt;
|
||||
padding-left: 5px;
|
||||
color: #555555;
|
||||
width: 150px;
|
||||
height: 20px;
|
||||
margin: 2px;
|
||||
}
|
||||
.volume input {
|
||||
display: block;
|
||||
width: 150px;
|
||||
height: 20px;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.preview {
|
||||
|
||||
Reference in New Issue
Block a user