mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-27 21:05:53 +00:00
4.23.1
* Added: Add a `Reset` button to the headers of the badge visibility controls. * Changed: Move some Twitch badges into a new `Twitch: Other` category. * Changed: Do not display the badges in a category when the category is hidden to save space. * Changed: Expose more data on emotes/emoji to tab completion. * Fixed: Missing localization for certain items in Chat > Actions > Rooms. * API Fixed: Ensure that emote set and badge IDs are cast to strings for consistent comparisons. * API Fixed: Fix reference counting issues for emote sets when a set or badge is added from multiple providers. * API Changed: Newly loaded emote sets are automatically scheduled for garbage collection if they have no users. * API Changed: Added `removeAllSets(provider)` method to `Room`s and `Users`s. * API Changed: Standardize `addSet(provider, set_id, data)` to allow passing set data in `Room` and `User`. * API Changed: `addSet(...)` and `removeSet(...)` now return a boolean of whether or not the set was added or removed.
This commit is contained in:
parent
a74faa95d3
commit
2a57ecb8a7
9 changed files with 188 additions and 59 deletions
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "frankerfacez",
|
||||
"author": "Dan Salvato LLC",
|
||||
"version": "4.23.0",
|
||||
"version": "4.23.1",
|
||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
|
|
|
@ -294,6 +294,7 @@ export default class Badges extends Module {
|
|||
|
||||
getSettingsBadges(include_addons, callback) {
|
||||
const twitch = [],
|
||||
other = [],
|
||||
owl = [],
|
||||
tcon = [],
|
||||
game = [],
|
||||
|
@ -333,7 +334,9 @@ export default class Badges extends Module {
|
|||
|
||||
if ( v ) {
|
||||
let cat;
|
||||
if ( badge.__cat === 'm-owl' )
|
||||
if ( badge.__cat === 'm-other' )
|
||||
cat = other;
|
||||
else if ( badge.__cat === 'm-owl' )
|
||||
cat = owl;
|
||||
else if ( badge.__cat === 'm-tcon' )
|
||||
cat = tcon;
|
||||
|
@ -382,6 +385,7 @@ export default class Badges extends Module {
|
|||
return [
|
||||
{title: 'Twitch', id: 'm-twitch', badges: twitch},
|
||||
{title: 'Twitch: TwitchCon', id: 'm-tcon', badges: tcon},
|
||||
{title: 'Twitch: Other', id: 'm-other', badges: other},
|
||||
{title: 'Twitch: Overwatch League', id: 'm-owl', badges: owl},
|
||||
{title: 'Twitch: Game', id: 'm-game', key: 'game', badges: game},
|
||||
{title: 'FrankerFaceZ', id: 'm-ffz', badges: ffz},
|
||||
|
@ -1148,10 +1152,22 @@ export default class Badges extends Module {
|
|||
}
|
||||
|
||||
|
||||
const OTHER_BADGES = [
|
||||
'vga-champ-2017',
|
||||
'warcraft',
|
||||
'samusoffer_beta',
|
||||
'power-rangers',
|
||||
'bits-charity',
|
||||
'glhf-pledge'
|
||||
];
|
||||
|
||||
|
||||
export function getBadgeCategory(key) {
|
||||
if ( key.startsWith('overwatch-league') )
|
||||
if ( OTHER_BADGES.includes(key) )
|
||||
return 'm-other';
|
||||
else if ( key.startsWith('overwatch-league') )
|
||||
return 'm-owl';
|
||||
else if ( key.startsWith('twitchcon') )
|
||||
else if ( key.startsWith('twitchcon') || key.startsWith('glitchcon') )
|
||||
return 'm-tcon';
|
||||
else if ( /_\d+$/.test(key) )
|
||||
return 'm-game';
|
||||
|
|
|
@ -100,6 +100,8 @@ export default class Emoji extends Module {
|
|||
// For some reason, splitter is a function.
|
||||
this.splitter = splitter();
|
||||
|
||||
this.categories = CATEGORIES;
|
||||
|
||||
this.emoji = {};
|
||||
this.names = {};
|
||||
this.chars = new Map;
|
||||
|
|
|
@ -57,7 +57,6 @@ const MODIFIERS = {
|
|||
};
|
||||
|
||||
|
||||
|
||||
export default class Emotes extends Module {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
|
@ -636,30 +635,42 @@ export default class Emotes extends Module {
|
|||
// ========================================================================
|
||||
|
||||
addDefaultSet(provider, set_id, data) {
|
||||
let changed = false;
|
||||
if ( typeof set_id === 'number' )
|
||||
set_id = `${set_id}`;
|
||||
|
||||
let changed = false, added = false;
|
||||
if ( ! this.default_sets.sourceIncludes(provider, set_id) ) {
|
||||
changed = ! this.default_sets.includes(set_id);
|
||||
this.default_sets.push(provider, set_id);
|
||||
this.refSet(set_id);
|
||||
changed = true;
|
||||
added = true;
|
||||
}
|
||||
|
||||
if ( data )
|
||||
this.loadSetData(set_id, data);
|
||||
|
||||
if ( changed )
|
||||
if ( changed ) {
|
||||
this.refSet(set_id);
|
||||
this.emit(':update-default-sets', provider, set_id, true);
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
removeDefaultSet(provider, set_id) {
|
||||
let changed = false;
|
||||
if ( typeof set_id === 'number' )
|
||||
set_id = `${set_id}`;
|
||||
|
||||
if ( this.default_sets.sourceIncludes(provider, set_id) ) {
|
||||
this.default_sets.remove(provider, set_id);
|
||||
this.unrefSet(set_id);
|
||||
changed = true;
|
||||
if ( ! this.default_sets.includes(set_id) ) {
|
||||
this.unrefSet(set_id);
|
||||
this.emit(':update-default-sets', provider, set_id, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( changed )
|
||||
this.emit(':update-default-sets', provider, set_id, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
refSet(set_id) {
|
||||
|
@ -668,7 +679,6 @@ export default class Emotes extends Module {
|
|||
clearTimeout(this._set_timers[set_id]);
|
||||
this._set_timers[set_id] = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
unrefSet(set_id) {
|
||||
|
@ -885,6 +895,11 @@ export default class Emotes extends Module {
|
|||
this.log.info(`Loaded emote set #${set_id}: ${data.title} (${count} emotes)`);
|
||||
|
||||
this.emit(':loaded', set_id, data);
|
||||
|
||||
// Don't let people endlessly load unused sets.
|
||||
const refs = this._set_refs[set_id] || 0;
|
||||
if ( refs <= 0 && ! this._set_timers[set_id] )
|
||||
this._set_timers[set_id] = setTimeout(() => this.unloadSet(set_id), 5000);
|
||||
}
|
||||
|
||||
|
||||
|
@ -904,6 +919,11 @@ export default class Emotes extends Module {
|
|||
if ( ! suppress_log )
|
||||
this.log.info(`Unloaded emote set #${set_id}: ${old_set.title}`);
|
||||
|
||||
if ( this._set_timers[set_id] ) {
|
||||
clearTimeout(this._set_timers[set_id]);
|
||||
this._set_timers[set_id] = null;
|
||||
}
|
||||
|
||||
this.emit(':unloaded', set_id, old_set);
|
||||
this.emote_sets[set_id] = null;
|
||||
}
|
||||
|
|
|
@ -306,13 +306,10 @@ export default class Room {
|
|||
|
||||
this.data = d;
|
||||
|
||||
if ( d.set ) {
|
||||
if ( ! this.emote_sets )
|
||||
this.emote_sets = new SourcedSet;
|
||||
this.emote_sets.set('main', d.set);
|
||||
} else if ( this.emote_sets )
|
||||
this.emote_sets.delete('main');
|
||||
|
||||
if ( d.set )
|
||||
this.addSet('main', d.set);
|
||||
else
|
||||
this.removeAllSets('main');
|
||||
|
||||
if ( data.sets )
|
||||
for(const set_id in data.sets)
|
||||
|
@ -349,29 +346,59 @@ export default class Room {
|
|||
if ( ! this.emote_sets )
|
||||
this.emote_sets = new SourcedSet;
|
||||
|
||||
let changed = false;
|
||||
if ( typeof set_id === 'number' )
|
||||
set_id = `${set_id}`;
|
||||
|
||||
let changed = false, added = false;
|
||||
if ( ! this.emote_sets.sourceIncludes(provider, set_id) ) {
|
||||
changed = ! this.emote_sets.includes(set_id);
|
||||
this.emote_sets.push(provider, set_id);
|
||||
this.manager.emotes.refSet(set_id);
|
||||
changed = true;
|
||||
added = true;
|
||||
}
|
||||
|
||||
if ( data )
|
||||
this.manager.emotes.loadSetData(set_id, data);
|
||||
|
||||
if ( changed )
|
||||
if ( changed ) {
|
||||
this.manager.emotes.refSet(set_id);
|
||||
this.manager.emotes.emit(':update-room-sets', this, provider, set_id, true);
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
removeAllSets(provider) {
|
||||
if ( this.destroyed || ! this.emote_sets )
|
||||
return false;
|
||||
|
||||
const sets = this.emote_sets.get(provider);
|
||||
if ( ! Array.isArray(sets) || ! sets.length )
|
||||
return false;
|
||||
|
||||
for(const set_id of sets)
|
||||
this.removeSet(provider, set_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
removeSet(provider, set_id) {
|
||||
if ( this.destroyed || ! this.emote_sets )
|
||||
return;
|
||||
|
||||
if ( typeof set_id === 'number' )
|
||||
set_id = `${set_id}`;
|
||||
|
||||
if ( this.emote_sets.sourceIncludes(provider, set_id) ) {
|
||||
this.emote_sets.remove(provider, set_id);
|
||||
this.manager.emotes.unrefSet(set_id);
|
||||
this.manager.emotes.emit(':update-room-sets', this, provider, set_id, false);
|
||||
if ( ! this.emote_sets.includes(set_id) ) {
|
||||
this.manager.emotes.unrefSet(set_id);
|
||||
this.manager.emotes.emit(':update-room-sets', this, provider, set_id, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -106,6 +106,9 @@ export default class User {
|
|||
if ( this.destroyed )
|
||||
return;
|
||||
|
||||
if ( typeof badge_id === 'number' )
|
||||
badge_id = `${badge_id}`;
|
||||
|
||||
if ( data )
|
||||
data.id = badge_id;
|
||||
else
|
||||
|
@ -132,7 +135,7 @@ export default class User {
|
|||
return null;
|
||||
|
||||
for(const badge of this.badges._cache)
|
||||
if ( badge.id == badge_id )
|
||||
if ( badge.id == badge_id )
|
||||
return badge;
|
||||
}
|
||||
|
||||
|
@ -155,29 +158,64 @@ export default class User {
|
|||
// Emote Sets
|
||||
// ========================================================================
|
||||
|
||||
addSet(provider, set_id) {
|
||||
addSet(provider, set_id, data) {
|
||||
if ( this.destroyed )
|
||||
return;
|
||||
|
||||
if ( ! this.emote_sets )
|
||||
this.emote_sets = new SourcedSet;
|
||||
|
||||
if ( typeof set_id === 'number' )
|
||||
set_id = `${set_id}`;
|
||||
|
||||
let changed = false, added = false;
|
||||
if ( ! this.emote_sets.sourceIncludes(provider, set_id) ) {
|
||||
changed = ! this.emote_sets.includes(set_id);
|
||||
this.emote_sets.push(provider, set_id);
|
||||
added = true;
|
||||
}
|
||||
|
||||
if ( data )
|
||||
this.manager.emotes.loadSetData(set_id, data);
|
||||
|
||||
if ( changed ) {
|
||||
this.manager.emotes.refSet(set_id);
|
||||
this.manager.emotes.emit(':update-user-sets', this, provider, set_id, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
removeAllSets(provider) {
|
||||
if ( this.destroyed || ! this.emote_sets )
|
||||
return false;
|
||||
|
||||
const sets = this.emote_sets.get(provider);
|
||||
if ( ! Array.isArray(sets) || ! sets.length )
|
||||
return false;
|
||||
|
||||
for(const set_id of sets)
|
||||
this.removeSet(provider, set_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
removeSet(provider, set_id) {
|
||||
if ( this.destroyed || ! this.emote_sets )
|
||||
return;
|
||||
|
||||
if ( typeof set_id === 'number' )
|
||||
set_id = `${set_id}`;
|
||||
|
||||
if ( this.emote_sets.sourceIncludes(provider, set_id) ) {
|
||||
this.emote_sets.remove(provider, set_id);
|
||||
this.manager.emotes.unrefSet(set_id);
|
||||
this.manager.emotes.emit(':update-user-sets', this, provider, set_id, false);
|
||||
if ( ! this.emote_sets.includes(set_id) ) {
|
||||
this.manager.emotes.unrefSet(set_id);
|
||||
this.manager.emotes.emit(':update-user-sets', this, provider, set_id, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@
|
|||
<header
|
||||
v-if="sec.id"
|
||||
:class="{default: badgeDefault(sec.id)}"
|
||||
class="tw-flex ffz-checkbox"
|
||||
class="tw-flex ffz-checkbox tw-align-items-center tw-z-above"
|
||||
>
|
||||
<input
|
||||
:id="sec.id"
|
||||
|
@ -40,23 +40,33 @@
|
|||
type="checkbox"
|
||||
class="ffz-checkbox__input"
|
||||
@click="onChange(sec.id, $event)"
|
||||
@contextmenu.prevent="reset(sec.id)"
|
||||
>
|
||||
<label
|
||||
:for="sec.id"
|
||||
:title="t('setting.right-click-reset', 'Right-Click to Reset')"
|
||||
class="ffz-checkbox__label"
|
||||
@contextmenu.prevent="reset(sec.id)"
|
||||
>
|
||||
<span class="tw-mg-l-1">
|
||||
{{ sec.title }}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div class="ffz--reset-button">
|
||||
<button
|
||||
v-if="! badgeDefault(sec.id)"
|
||||
class="tw-mg-l-05 tw-button tw-button--text ffz-il-tooltip__container"
|
||||
@click="reset(sec.id)"
|
||||
>
|
||||
<span class="tw-button__text ffz-i-cancel" />
|
||||
<div class="ffz-il-tooltip ffz-il-tooltip--down ffz-il-tooltip--align-right">
|
||||
{{ t('setting.reset', 'Reset to Default') }}
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<header v-else>
|
||||
{{ sec.title }}
|
||||
</header>
|
||||
<ul class="tw-flex tw-flex-wrap tw-align-content-start">
|
||||
<ul v-if="! sec.id || badgeChecked(sec.id)" class="tw-flex tw-flex-wrap tw-align-content-start">
|
||||
<li
|
||||
v-for="i in sec.badges"
|
||||
:key="i.id"
|
||||
|
@ -96,7 +106,7 @@
|
|||
class="tw-mg-t-05 tw-button ffz-button--hollow ffz-il-tooltip__container"
|
||||
@click="reset(i.id)"
|
||||
>
|
||||
<span class="tw-button__text">Reset</span>
|
||||
<span class="tw-button__text ffz-i-cancel" />
|
||||
<span class="ffz-il-tooltip ffz-il-tooltip--down ffz-il-tooltip--align-right">
|
||||
{{ t('setting.reset', 'Reset to Default') }}
|
||||
</span>
|
||||
|
@ -106,6 +116,11 @@
|
|||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-else class="tw-c-text-alt-2 tw-font-size-4 tw-align-center tw-pd-05">
|
||||
{{ t('setting.badges.hidden', '{count,plural,one{# badge is} other{# badges are} } hidden in this set', {
|
||||
count: sec.badges.length
|
||||
}) }}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -472,6 +472,7 @@ export default {
|
|||
if ( ! this.item.inline ) {
|
||||
out.push({
|
||||
title: 'New Line',
|
||||
title_i18n: 'setting.new-line',
|
||||
value: {
|
||||
v: {type: 'new-line'}
|
||||
}
|
||||
|
@ -479,6 +480,7 @@ export default {
|
|||
|
||||
out.push({
|
||||
title: 'Space (Small)',
|
||||
title_i18n: 'setting.space-small',
|
||||
value: {
|
||||
v: {type: 'space-small'}
|
||||
}
|
||||
|
@ -486,6 +488,7 @@ export default {
|
|||
|
||||
out.push({
|
||||
title: 'Space (Expanding)',
|
||||
title_i18n: 'setting.space',
|
||||
value: {
|
||||
v: {type: 'space'}
|
||||
}
|
||||
|
|
|
@ -588,28 +588,29 @@ export default class Input extends Module {
|
|||
favorites = this.emotes.getFavorites('twitch');
|
||||
|
||||
for(const set of emotes) {
|
||||
if ( has_hidden ) {
|
||||
const int_id = parseInt(set.id, 10),
|
||||
owner = set.owner,
|
||||
is_points = TWITCH_POINTS_SETS.includes(int_id) || owner?.login === 'channel_points',
|
||||
channel = is_points ? null : owner;
|
||||
const int_id = parseInt(set.id, 10),
|
||||
owner = set.owner,
|
||||
is_points = TWITCH_POINTS_SETS.includes(int_id) || owner?.login === 'channel_points',
|
||||
channel = is_points ? null : owner;
|
||||
|
||||
let key = `twitch-set-${set.id}`;
|
||||
let key = `twitch-set-${set.id}`;
|
||||
let extra = null;
|
||||
|
||||
if ( channel?.login )
|
||||
key = `twitch-${channel.id}`;
|
||||
else if ( is_points )
|
||||
key = 'twitch-points';
|
||||
else if ( TWITCH_GLOBAL_SETS.includes(int_id) )
|
||||
key = 'twitch-global';
|
||||
else if ( TWITCH_PRIME_SETS.includes(int_id) )
|
||||
key = 'twitch-prime';
|
||||
else
|
||||
key = 'twitch-misc';
|
||||
if ( channel?.login ) {
|
||||
key = `twitch-${channel.id}`;
|
||||
extra = channel.displayName || channel.login;
|
||||
|
||||
if ( hidden_sets.includes(key) )
|
||||
continue;
|
||||
}
|
||||
} else if ( is_points )
|
||||
key = 'twitch-points';
|
||||
else if ( TWITCH_GLOBAL_SETS.includes(int_id) )
|
||||
key = 'twitch-global';
|
||||
else if ( TWITCH_PRIME_SETS.includes(int_id) )
|
||||
key = 'twitch-prime';
|
||||
else
|
||||
key = 'twitch-misc';
|
||||
|
||||
if ( has_hidden && hidden_sets.includes(key) )
|
||||
continue;
|
||||
|
||||
for(const emote of set.emotes) {
|
||||
if ( ! emote || ! emote.id || hidden_emotes.includes(emote.id) )
|
||||
|
@ -635,6 +636,8 @@ export default class Input extends Module {
|
|||
|
||||
out.push({
|
||||
id,
|
||||
source: key,
|
||||
extra,
|
||||
setID: set.id,
|
||||
token,
|
||||
tokenLower: token.toLowerCase(),
|
||||
|
@ -721,15 +724,17 @@ export default class Input extends Module {
|
|||
included.add(source.raw);
|
||||
|
||||
const srcSet = this.emoji.getFullImageSet(source.image, style);
|
||||
const matched = `:${name}:`;
|
||||
|
||||
const favorite = favorites.includes(emoji.code);
|
||||
results.push({
|
||||
current: input,
|
||||
emoji: source,
|
||||
matched,
|
||||
srcSet,
|
||||
replacement: source.raw,
|
||||
element: inst.renderFFZEmojiSuggestion({
|
||||
token: `:${name}:`,
|
||||
token: matched,
|
||||
id: `emoji-${emoji.code}`,
|
||||
src: this.emoji.getFullImage(source.image, style),
|
||||
srcSet,
|
||||
|
@ -760,6 +765,7 @@ export default class Input extends Module {
|
|||
continue;
|
||||
|
||||
const source = set.source || 'ffz',
|
||||
source_line = set.source_line || (`${set.source || 'FFZ'} ${set.title || 'Global'}`),
|
||||
key = `${set.merge_source || source}-${set.merge_id || set.id}`;
|
||||
|
||||
if ( has_hidden && hidden_sets.includes(key) )
|
||||
|
@ -779,6 +785,8 @@ export default class Input extends Module {
|
|||
|
||||
out.push({
|
||||
id: `${source}-${emote.id}`,
|
||||
source,
|
||||
extra: source_line,
|
||||
token: emote.name,
|
||||
tokenLower: emote.name.toLowerCase(),
|
||||
srcSet: anim && emote.animSrcSet || emote.srcSet,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue