1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-10-15 07:21:58 +00:00
* Added: Profiles can now be toggled on and off, rather than relying on rules to control them.
* Added: Right-clicking the FFZ menu button in the top right of the page will open a Profiles dialog, letting you quickly toggle a profile on or off.
* Added: Profiles can now be enabled or disabled based upon the time of day.
* Added: Polish and Serbian pluralization rules.
* Fixed: Ensure tool-tips work on the new dashboard.
* Fixed: Ensure the FFZ Control Center works on the new dashboard.
This commit is contained in:
SirStendec 2019-10-09 16:02:25 -04:00
parent 02efd61f00
commit 62bb6440f3
30 changed files with 503 additions and 52 deletions

View file

@ -0,0 +1,47 @@
<template>
<section class="tw-flex-grow-1 tw-align-self-start tw-flex tw-align-items-center">
<div class="tw-flex tw-align-items-center">
<div class="tw-mg-r-1">
{{ t(type.i18n, type.title) }}
</div>
<label :for="'start-time$' + id" class="tw-mg-l-1">
{{ t('settings.filter.time.start', 'Start:') }}
</label>
<input
:id="'start-time$' + id"
v-model="value.data[0]"
type="time"
class="ffz-min-width-unset tw-flex-grow-1 tw-border-radius-medium tw-font-size-6 tw-mg-x-1 tw-pd-x-1 tw-pd-y-05 tw-input"
>
<label :for="'end-time$' + id" class="tw-mg-l-1">
{{ t('settings.filter.time.end', 'End:') }}
</label>
<input
:id="'end-time$' + id"
v-model="value.data[1]"
type="time"
class="ffz-min-width-unset tw-flex-grow-1 tw-border-radius-medium tw-font-size-6 tw-pd-x-1 tw-pd-y-05 tw-mg-x-1 tw-input"
>
</div>
</section>
</template>
<script>
let last_id = 0;
export default {
props: ['value', 'type', 'filters', 'context'],
data() {
return {
id: last_id++
}
}
}
</script>

View file

@ -118,8 +118,9 @@ export default class SettingsContext extends EventEmitter {
selectProfiles() {
const new_profiles = [],
order = this.order = [];
for(const profile of this.manager.__profiles)
if ( profile.matches(this.__context) ) {
if ( profile.toggled && profile.matches(this.__context) ) {
new_profiles.push(profile);
order.push(profile.id);
}
@ -389,6 +390,16 @@ export default class SettingsContext extends EventEmitter {
}
hasProfile(profile) {
if ( typeof profile === 'number' )
for(const prof of this.__profiles)
if ( prof.id === profile )
return true;
return this.__profiles.includes(profile);
}
_getRaw(key, type) {
if ( ! type )
throw new Error(`non-existent type for ${key}`)

View file

@ -42,6 +42,74 @@ export const Or = {
// Context Stuff
function parseTime(time) {
if ( typeof time !== 'string' || ! time.length )
return null;
const idx = time.indexOf(':');
if ( idx === -1 )
return null;
let hours, minutes;
try {
hours = parseInt(time.slice(0, idx), 10);
minutes = parseInt(time.slice(idx + 1), 10);
} catch(err) {
return null;
}
return hours * 60 + minutes;
}
export const Time = {
_captured: new Set,
createTest(config) {
const start = parseTime(config[0]),
end = parseTime(config[1]);
if ( start == null || end == null )
return () => false;
if ( start <= end )
return () => {
Time._captured.add(start);
Time._captured.add(end + 1);
const d = new Date,
v = d.getHours() * 60 + d.getMinutes();
return v >= start && v <= end;
}
return () => {
Time._captured.add(start + 1);
Time._captured.add(end);
const d = new Date,
v = d.getHours() * 60 + d.getMinutes();
return v <= start || v >= end;
}
},
captured: () => {
const out = Array.from(Time._captured);
Time._captured = new Set;
out.sort((a, b) => a - b);
return out;
},
title: 'Time of Day',
i18n: 'settings.filter.time',
default: ['08:00', '18:00'],
editor: () => import(/* webpackChunkName: 'main-menu' */ './components/time.vue')
}
export const TheaterMode = {
createTest(config) {
return ctx => ctx.ui && ctx.ui.theatreModeEnabled === config;

View file

@ -46,7 +46,6 @@ export default class SettingsManager extends Module {
this.migrations = new MigrationManager(this);
// Also create the main context as early as possible.
this.main_context = new SettingsContext(this);
@ -98,6 +97,42 @@ export default class SettingsManager extends Module {
this.log.info(`Initialization complete after ${duration.toFixed(5)}ms -- Values: ${this.provider.size} -- Profiles: ${this.__profiles.length}`)
this.scheduleUpdates();
this.updateClock();
}
updateClock() {
const captured = require('./filters').Time.captured();
if ( ! captured?.length )
return;
if ( this._time_timer )
clearTimeout(this._time_timer);
const d = new Date,
now = d.getHours() * 60 + d.getMinutes();
let next = this._time_next != null ? this._time_next : null;
for(const value of captured) {
if ( value <= now )
continue;
if ( next == null || value < next )
next = value;
}
// There's no time waiting for today. Skip to the next day.
if ( next == null )
next = captured[0] + 1440;
// Determine how long it'll take to reach the next time period.
const delta = (next - now) * 60 * 1000 - 59750 + (60000 - Date.now() % 60000);
this._time_timer = setTimeout(() => {
for(const context of this.__contexts)
context.selectProfiles();
this.updateClock();
}, delta);
}
@ -186,6 +221,8 @@ export default class SettingsManager extends Module {
// Look up the profile it belongs to and emit a changed event from
// that profile, thus notifying any contexts or UI instances.
key = key.substr(2);
// Is it a value?
const idx = key.indexOf(':');
if ( idx === -1 )
return;
@ -193,8 +230,12 @@ export default class SettingsManager extends Module {
const profile = this.__profile_ids[key.slice(0, idx)],
s_key = key.slice(idx + 1);
if ( profile )
profile.emit('changed', s_key, new_value, deleted);
if ( profile ) {
if ( s_key === ':enabled' )
profile.emit('toggled', profile, deleted ? true : new_value);
else
profile.emit('changed', s_key, new_value, deleted);
}
}
@ -210,6 +251,17 @@ export default class SettingsManager extends Module {
// And then re-select the active profiles.
for(const context of this.__contexts)
context.selectProfiles();
this.updateClock();
}
_onProfileToggled(profile, val) {
for(const context of this.__contexts)
context.selectProfiles();
this.updateClock();
this.emit(':profile-toggled', profile, val);
}
@ -250,6 +302,9 @@ export default class SettingsManager extends Module {
let reordered = false,
changed = false;
for(const profile of old_profiles)
profile.off('toggled', this._onProfileToggled, this);
for(const profile_data of raw_profiles) {
const id = profile_data.id,
slot_id = profiles.length,
@ -293,12 +348,17 @@ export default class SettingsManager extends Module {
changed = true;
}
for(const profile of profiles)
profile.on('toggled', this._onProfileToggled, this);
if ( ! changed && ! old_ids.size || suppress_events )
return;
for(const context of this.__contexts)
context.selectProfiles();
this.updateClock();
for(const id of new_ids)
this.emit(':profile-created', profile_ids[id]);
@ -327,9 +387,10 @@ export default class SettingsManager extends Module {
options.name = `Unnamed Profile ${i}`;
const profile = this.__profile_ids[i] = new SettingsProfile(this, options);
this.__profiles.unshift(profile);
profile.on('toggled', this._onProfileToggled, this);
this._saveProfiles();
this.emit(':profile-created', profile);
return profile;
@ -351,6 +412,7 @@ export default class SettingsManager extends Module {
if ( profile.id === 0 )
throw new Error('cannot delete default profile');
profile.off('toggled', this._onProfileToggled, this);
profile.clear();
this.__profile_ids[id] = null;
@ -400,6 +462,8 @@ export default class SettingsManager extends Module {
this.provider.set('profiles', this.__profiles.map(prof => prof.data));
for(const context of this.__contexts)
context.selectProfiles();
this.updateClock();
}

View file

@ -24,6 +24,7 @@ export default class SettingsProfile extends EventEmitter {
this.data = data;
this.prefix = `p:${this.id}:`;
this.enabled_key = `${this.prefix}:enabled`;
}
get data() {
@ -38,6 +39,7 @@ export default class SettingsProfile extends EventEmitter {
desc_i18n_key: this.desc_i18n_key,
url: this.url,
show_toggle: this.show_toggle,
context: this.context
}
@ -72,6 +74,7 @@ export default class SettingsProfile extends EventEmitter {
version: 2,
type: 'profile',
profile: this.data,
toggled: this.toggled,
values: {}
};
@ -107,6 +110,23 @@ export default class SettingsProfile extends EventEmitter {
}
// ========================================================================
// Toggled
// ========================================================================
get toggled() {
return this.provider.get(this.enabled_key, true);
}
set toggled(val) {
if ( val === this.toggleState )
return;
this.provider.set(this.enabled_key, val);
this.emit('toggled', this, val);
}
// ========================================================================
// Context
// ========================================================================
@ -158,7 +178,7 @@ export default class SettingsProfile extends EventEmitter {
len = p.length;
for(const key of this.provider.keys())
if ( key.startsWith(p) )
if ( key.startsWith(p) && key !== this.enabled_key )
out.push(key.slice(len));
return out;
@ -168,7 +188,7 @@ export default class SettingsProfile extends EventEmitter {
const p = this.prefix,
len = p.length;
for(const key of this.provider.keys())
if ( key.startsWith(p) ) {
if ( key.startsWith(p) && key !== this.enabled_key ) {
this.provider.delete(key);
this.emit('changed', key.slice(len), undefined, true);
}
@ -179,7 +199,7 @@ export default class SettingsProfile extends EventEmitter {
len = p.length;
for(const key of this.provider.keys())
if ( key.startsWith(p) )
if ( key.startsWith(p) && key !== this.enabled_key )
yield [key.slice(len), this.provider.get(key)];
}
@ -188,7 +208,7 @@ export default class SettingsProfile extends EventEmitter {
let count = 0;
for(const key of this.provider.keys())
if ( key.startsWith(p) )
if ( key.startsWith(p) && key !== this.enabled_key )
count++;
return count;