mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-09-16 18:06:55 +00:00
Add info and question icons. Bump version to beta1.7. Add chat badge visibility. Add support for setting inheritance merge strategies.
This commit is contained in:
parent
19c81bb049
commit
cc682230e2
16 changed files with 222 additions and 51 deletions
|
@ -95,7 +95,7 @@ class FrankerFaceZ extends Module {
|
|||
FrankerFaceZ.Logger = Logger;
|
||||
|
||||
const VER = FrankerFaceZ.version_info = {
|
||||
major: 4, minor: 0, revision: 0, extra: '-beta1.6',
|
||||
major: 4, minor: 0, revision: 0, extra: '-beta1.7',
|
||||
build: __webpack_hash__,
|
||||
toString: () =>
|
||||
`${VER.major}.${VER.minor}.${VER.revision}${VER.extra || ''}${DEBUG ? '-dev' : ''}`
|
||||
|
|
|
@ -147,8 +147,9 @@ export default class Badges extends Module {
|
|||
this.twitch_badges = {};
|
||||
|
||||
this.settings.add('chat.badges.hidden', {
|
||||
default: [],
|
||||
_ui: {
|
||||
default: {},
|
||||
type: 'object_merge',
|
||||
ui: {
|
||||
path: 'Chat > Badges >> tabs ~> Visibility',
|
||||
component: 'badge-visibility',
|
||||
data: () => {
|
||||
|
@ -297,7 +298,7 @@ export default class Badges extends Module {
|
|||
|
||||
|
||||
render(msg, e) { // eslint-disable-line class-methods-use-this
|
||||
const hidden_badges = this.parent.context.get('chat.badges.hidden') || [],
|
||||
const hidden_badges = this.parent.context.get('chat.badges.hidden') || {},
|
||||
badge_style = this.parent.context.get('chat.badges.style'),
|
||||
custom_mod = this.parent.context.get('chat.badges.custom-mod'),
|
||||
is_mask = badge_style > 5,
|
||||
|
@ -324,7 +325,7 @@ export default class Badges extends Module {
|
|||
const version = twitch_badges[badge_id],
|
||||
is_game = badge_id.endsWith('_1');
|
||||
|
||||
if ( hidden_badges.includes(badge_id) || (is_game && hidden_badges.includes('game')) )
|
||||
if ( hidden_badges[badge_id] || (is_game && hidden_badges.game) )
|
||||
continue;
|
||||
|
||||
if ( has(BADGE_POSITIONS, badge_id) )
|
||||
|
@ -365,7 +366,7 @@ export default class Badges extends Module {
|
|||
|
||||
for(const badge of badges)
|
||||
if ( badge && badge.id ) {
|
||||
if ( hidden_badges.includes(badge.id) )
|
||||
if ( hidden_badges[badge.id] )
|
||||
continue;
|
||||
|
||||
const full_badge = this.badges[badge.id],
|
||||
|
|
|
@ -1,14 +1,38 @@
|
|||
<template lang="html">
|
||||
<div class="ffz--badge-visibility tw-pd-t-05">
|
||||
<div
|
||||
class="tw-c-background-accent tw-c-text-overlay tw-pd-1 tw-mg-b-1"
|
||||
v-if="source && source !== profile"
|
||||
>
|
||||
<span class="ffz-i-info" />
|
||||
{{ t('setting.badge-inheritence', 'These values are being overridden by another profile and may not take effect.') }}
|
||||
</div>
|
||||
|
||||
<div class="tw-mg-b-2 tw-align-right">
|
||||
<button
|
||||
class="tw-mg-l-05 tw-button tw-button--hollow tw-tooltip-wrapper"
|
||||
@click="clear"
|
||||
:disabled="! has_value"
|
||||
>
|
||||
<span class="tw-button__icon tw-button__icon--left">
|
||||
<figure class="ffz-i-cancel" />
|
||||
</span>
|
||||
<span class="tw-button__text">
|
||||
{{ t('setting.reset-all', 'Reset All to Default') }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<section class="ffz--menu-container tw-border-t" v-for="sec in data">
|
||||
<header>{{ sec.title }}</header>
|
||||
<ul class="tw-flex tw-flex-wrap tw-align-content-start">
|
||||
<li v-for="i in sort(sec.badges)" class="ffz--badge-info tw-pd-y-1 tw-pd-r-1 tw-flex" :class="{default: isDefault}">
|
||||
<li v-for="i in sort(sec.badges)" class="ffz--badge-info tw-pd-y-1 tw-pd-r-1 tw-flex" :class="{default: badgeDefault(i.id)}">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="tw-checkbox__input"
|
||||
checked="checked"
|
||||
:checked="badgeChecked(i.id)"
|
||||
:id="i.id"
|
||||
@click="onChange(i.id, $event)"
|
||||
>
|
||||
|
||||
<label class="tw-checkbox__label tw-flex" :for="i.id">
|
||||
|
@ -18,12 +42,16 @@
|
|||
<section class="tw-mg-t-05" v-if="i.versions && i.versions.length > 1">
|
||||
<span v-for="v in i.versions" data-tooltip-type="html" class="ffz-badge ffz-tooltip" :title="v.name" :style="{backgroundColor: i.color, backgroundImage: v.styleImage}" />
|
||||
</section>
|
||||
<!--button class="tw-mg-t-05 tw-button tw-button--hollow tw-tooltip-wrapper">
|
||||
<button
|
||||
class="tw-mg-t-05 tw-button tw-button--hollow tw-tooltip-wrapper"
|
||||
@click="reset(i.id)"
|
||||
v-if="! badgeDefault(i.id)"
|
||||
>
|
||||
<span class="tw-button__text">Reset</span>
|
||||
<span class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
||||
{{ t('setting.reset', 'Reset to Default') }}
|
||||
</span>
|
||||
</button-->
|
||||
</button>
|
||||
</div>
|
||||
</label>
|
||||
</li>
|
||||
|
@ -35,12 +63,38 @@
|
|||
<script>
|
||||
|
||||
import SettingMixin from '../setting-mixin';
|
||||
import {has} from 'utilities/object';
|
||||
|
||||
export default {
|
||||
mixins: [SettingMixin],
|
||||
props: ['item', 'context'],
|
||||
|
||||
methods: {
|
||||
badgeChecked(id) {
|
||||
return ! this.value[id];
|
||||
},
|
||||
|
||||
badgeDefault(id) {
|
||||
return ! has(this.value, id);
|
||||
},
|
||||
|
||||
onChange(id, event) {
|
||||
const control = event.target,
|
||||
new_val = {[id]: ! control.checked};
|
||||
|
||||
this.set(Object.assign({}, this.value, new_val));
|
||||
},
|
||||
|
||||
reset(id) {
|
||||
const val = Object.assign({}, this.value);
|
||||
delete val[id];
|
||||
|
||||
if ( ! Object.keys(val).length )
|
||||
this.clear();
|
||||
else
|
||||
this.set(val);
|
||||
},
|
||||
|
||||
sort(items) {
|
||||
return items.sort((a, b) => {
|
||||
const an = a.name.toLowerCase(),
|
||||
|
@ -50,36 +104,8 @@ export default {
|
|||
if ( an > bn ) return 1;
|
||||
return 0;
|
||||
});
|
||||
},
|
||||
|
||||
onChange() {
|
||||
this.set(this.$refs.control.checked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ffz--badge-info {
|
||||
&.default {
|
||||
label:before, label:after {
|
||||
opacity: 0.5
|
||||
}
|
||||
}
|
||||
|
||||
.tw-checkbox__input:checked+.tw-checkbox__label:after,
|
||||
label:before, label:after {
|
||||
top: 1.05rem;
|
||||
}
|
||||
|
||||
.ffz-badge.preview-image {
|
||||
width: 7.2rem;
|
||||
height: 7.2rem;
|
||||
background-size: 7.2rem;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
width: 30rem;
|
||||
}
|
||||
</style>
|
||||
</script>
|
|
@ -1,5 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
import {deep_copy} from 'utilities/object';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
@ -65,7 +67,7 @@ export default {
|
|||
if ( typeof this.item.default === 'function' )
|
||||
return this.item.default(this.context.context);
|
||||
|
||||
return this.item.default;
|
||||
return deep_copy(this.item.default);
|
||||
},
|
||||
|
||||
isInherited() {
|
||||
|
@ -119,7 +121,7 @@ export default {
|
|||
|
||||
this.has_value = profile.has(setting);
|
||||
this.value = this.has_value ?
|
||||
profile.get(setting) :
|
||||
deep_copy(profile.get(setting)) :
|
||||
this.isInherited ?
|
||||
this.source_value :
|
||||
this.default_value;
|
||||
|
@ -131,7 +133,7 @@ export default {
|
|||
|
||||
this.has_value = deleted !== true;
|
||||
this.value = this.has_value ?
|
||||
value :
|
||||
deep_copy(value) :
|
||||
this.isInherited ?
|
||||
this.source_value :
|
||||
this.default_value;
|
||||
|
@ -141,7 +143,7 @@ export default {
|
|||
if ( key !== this.item.setting )
|
||||
return;
|
||||
|
||||
this.source_value = value;
|
||||
this.source_value = deep_copy(value);
|
||||
if ( this.isInherited )
|
||||
this.value = deleted ? this.default_value : value;
|
||||
},
|
||||
|
@ -150,12 +152,15 @@ export default {
|
|||
if ( this.source )
|
||||
this.source.off('changed', this._source_setting_changed, this);
|
||||
|
||||
// We primarily only care about the main source.
|
||||
uses = uses ? uses[0] : null;
|
||||
|
||||
const source = this.source = this.context.profile_keys[uses],
|
||||
setting = this.item.setting;
|
||||
|
||||
if ( source ) {
|
||||
source.on('changed', this._source_setting_changed, this);
|
||||
this.source_value = source.get(setting);
|
||||
this.source_value = deep_copy(source.get(setting));
|
||||
|
||||
} else
|
||||
this.source_value = undefined;
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
import {EventEmitter} from 'utilities/events';
|
||||
import {has, get as getter, array_equals} from 'utilities/object';
|
||||
|
||||
import * as DEFINITIONS from './types';
|
||||
|
||||
|
||||
/**
|
||||
* The SettingsContext class provides a context through which to read
|
||||
|
@ -128,7 +130,7 @@ export default class SettingsContext extends EventEmitter {
|
|||
this.emit(`changed:${key}`, new_value, old_value);
|
||||
}
|
||||
|
||||
if ( new_uses !== old_uses ) {
|
||||
if ( ! array_equals(new_uses, old_uses) ) {
|
||||
this.emit('uses_changed', key, new_uses, old_uses);
|
||||
this.emit(`uses_changed:${key}`, new_uses, old_uses);
|
||||
}
|
||||
|
@ -190,7 +192,7 @@ export default class SettingsContext extends EventEmitter {
|
|||
old_uses = old_meta ? old_meta.uses : null,
|
||||
new_uses = new_meta ? new_meta.uses : null;
|
||||
|
||||
if ( old_uses !== new_uses ) {
|
||||
if ( ! array_equals(new_uses, old_uses) ) {
|
||||
this.emit('uses_changed', key, new_uses, old_uses);
|
||||
this.emit(`uses_changed:${key}`, new_uses, old_uses);
|
||||
}
|
||||
|
@ -216,9 +218,15 @@ export default class SettingsContext extends EventEmitter {
|
|||
visited.push(key);
|
||||
|
||||
const definition = this.manager.definitions.get(key),
|
||||
raw_value = this._getRaw(key),
|
||||
raw_type = definition && definition.type,
|
||||
type = raw_type ? DEFINITIONS[raw_type] : DEFINITIONS.basic;
|
||||
|
||||
if ( ! type )
|
||||
throw new Error(`non-existent setting type "${raw_type}"`);
|
||||
|
||||
const raw_value = this._getRaw(key, type),
|
||||
meta = {
|
||||
uses: raw_value ? raw_value[1].id : null
|
||||
uses: raw_value ? raw_value[1] : null
|
||||
};
|
||||
|
||||
let value = raw_value ? raw_value[0] : undefined;
|
||||
|
@ -250,11 +258,22 @@ export default class SettingsContext extends EventEmitter {
|
|||
}
|
||||
|
||||
|
||||
_getRaw(key) {
|
||||
*profiles() {
|
||||
for(const profile of this.__profiles)
|
||||
yield profile;
|
||||
}
|
||||
|
||||
|
||||
_getRaw(key, type) {
|
||||
if ( ! type )
|
||||
throw new Error(`non-existent `)
|
||||
|
||||
return type.get(key, this.profiles(), this.manager.log);
|
||||
}
|
||||
/* for(const profile of this.__profiles)
|
||||
if ( profile.has(key) )
|
||||
return [profile.get(key), profile]
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
// ========================================================================
|
||||
|
|
81
src/settings/types.js
Normal file
81
src/settings/types.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
'use strict';
|
||||
|
||||
// ============================================================================
|
||||
// Settings Types
|
||||
// ============================================================================
|
||||
|
||||
export const basic = {
|
||||
get(key, profiles) {
|
||||
for(const profile of profiles)
|
||||
if ( profile.has(key) )
|
||||
return [
|
||||
profile.get(key),
|
||||
[profile.id]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const object_merge = {
|
||||
get(key, profiles, log) {
|
||||
const values = [],
|
||||
sources = [];
|
||||
|
||||
for(const profile of profiles)
|
||||
if ( profile.has(key) ) {
|
||||
const val = profile.get(key);
|
||||
if ( typeof val !== 'object' ) {
|
||||
log.warn(`Profile #${profile.id} has an invalid value for "${key}" of type ${typeof val}. Skipping.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
sources.push(profile.id);
|
||||
values.unshift(val);
|
||||
}
|
||||
|
||||
if ( values.length )
|
||||
return [
|
||||
Object.assign({}, ...values),
|
||||
sources
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const array_merge = {
|
||||
get(key, profiles, log) {
|
||||
const values = [],
|
||||
trailing = [],
|
||||
sources = [];
|
||||
|
||||
for(const profile of profiles)
|
||||
if ( profile.has(key) ) {
|
||||
const value = profile.get(key);
|
||||
if ( ! Array.isArray(value) ) {
|
||||
log.warn(`Profile #${profile.id} has an invalid value for "${key}" of type ${typeof value}. Skipping.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
sources.push(profile.id);
|
||||
let is_trailing = false;
|
||||
for(const val of value) {
|
||||
if ( val.t === 'inherit' )
|
||||
is_trailing = true;
|
||||
else if ( is_trailing )
|
||||
trailing.unshift(val.v);
|
||||
else
|
||||
values.push(val.v);
|
||||
}
|
||||
|
||||
// If we didn't run into an inherit, don't inherit.
|
||||
if ( ! is_trailing )
|
||||
break;
|
||||
}
|
||||
|
||||
if ( values.length || trailing.length )
|
||||
return [
|
||||
values.concat(trailing),
|
||||
sources
|
||||
]
|
||||
}
|
||||
}
|
|
@ -40,6 +40,7 @@ export default class ChatLine extends Module {
|
|||
onEnable() {
|
||||
this.chat.context.on('changed:chat.bits.stack', this.updateLines, this);
|
||||
this.chat.context.on('changed:chat.badges.style', this.updateLines, this);
|
||||
this.chat.context.on('changed:chat.badges.hidden', this.updateLines, this);
|
||||
this.chat.context.on('changed:chat.badges.custom-mod', this.updateLines, this);
|
||||
this.chat.context.on('changed:chat.rituals.show', this.updateLines, this);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue