Add volume/dark mode controls

This is a stub for the Options screens in the Preact client.
This commit is contained in:
Guangcong Luo
2020-03-24 02:43:04 -07:00
parent 03c8ca84f0
commit a64279cdf5
8 changed files with 181 additions and 21 deletions

View File

@@ -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];

View File

@@ -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

View File

@@ -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() {

View File

@@ -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,
};

View File

@@ -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) {

View File

@@ -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">
&lt;label class="checkbox">&lt;input type="checkbox" /> This is a checkbox&lt;/label>
</div>
<div class="clear"></div>
<h2>Chat features</h2>
<p><code>code</code></p>

View File

@@ -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;

View File

@@ -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 {