mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-07-25 03:58:30 +00:00
4.41.0
I'm almost finished implementing a replacement for emote cards, but they aren't quite ready yet. Please wait just a bit longer. * Added: Support for modifier emote effects, as well as settings to disable them. * Changed: Update the chat types enum to match changes to Twitch's internals. * Changed: Implement a new data structure for more efficiently storing bulk user to emote set mappings. * Changed: Implement support for loading data from staging. * Experiments: Push the new chat line rendering experiment to 20%. Let's see if it works properly.
This commit is contained in:
parent
8e48021c43
commit
e433aa3340
17 changed files with 575 additions and 32 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "frankerfacez",
|
"name": "frankerfacez",
|
||||||
"author": "Dan Salvato LLC",
|
"author": "Dan Salvato LLC",
|
||||||
"version": "4.40.0",
|
"version": "4.41.0",
|
||||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
"name": "Modular Chat Line Rendering",
|
"name": "Modular Chat Line Rendering",
|
||||||
"description": "Enable a newer, modular chat line renderer.",
|
"description": "Enable a newer, modular chat line renderer.",
|
||||||
"groups": [
|
"groups": [
|
||||||
{"value": true, "weight": 0},
|
{"value": true, "weight": 20},
|
||||||
{"value": false, "weight": 100}
|
{"value": false, "weight": 80}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"api_load": {
|
"api_load": {
|
||||||
|
|
|
@ -17,6 +17,7 @@ import SocketClient from './socket';
|
||||||
//import PubSubClient from './pubsub';
|
//import PubSubClient from './pubsub';
|
||||||
import Site from 'site';
|
import Site from 'site';
|
||||||
import Vue from 'utilities/vue';
|
import Vue from 'utilities/vue';
|
||||||
|
import StagingSelector from './staging';
|
||||||
//import Timing from 'utilities/timing';
|
//import Timing from 'utilities/timing';
|
||||||
|
|
||||||
class FrankerFaceZ extends Module {
|
class FrankerFaceZ extends Module {
|
||||||
|
@ -56,6 +57,7 @@ class FrankerFaceZ extends Module {
|
||||||
this.inject('settings', SettingsManager);
|
this.inject('settings', SettingsManager);
|
||||||
this.inject('experiments', ExperimentManager);
|
this.inject('experiments', ExperimentManager);
|
||||||
this.inject('i18n', TranslationManager);
|
this.inject('i18n', TranslationManager);
|
||||||
|
this.inject('staging', StagingSelector);
|
||||||
this.inject('socket', SocketClient);
|
this.inject('socket', SocketClient);
|
||||||
//this.inject('pubsub', PubSubClient);
|
//this.inject('pubsub', PubSubClient);
|
||||||
this.inject('site', Site);
|
this.inject('site', Site);
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
// Badge Handling
|
// Badge Handling
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
import {NEW_API, SERVER, API_SERVER, IS_WEBKIT, IS_FIREFOX, WEBKIT_CSS as WEBKIT} from 'utilities/constants';
|
import {NEW_API, SERVER, IS_WEBKIT, IS_FIREFOX, WEBKIT_CSS as WEBKIT} from 'utilities/constants';
|
||||||
|
|
||||||
import {createElement, ManagedStyle} from 'utilities/dom';
|
import {createElement, ManagedStyle} from 'utilities/dom';
|
||||||
import {has, maybe_call, SourcedSet} from 'utilities/object';
|
import {has, maybe_call, SourcedSet} from 'utilities/object';
|
||||||
|
@ -184,6 +184,7 @@ export default class Badges extends Module {
|
||||||
this.inject('settings');
|
this.inject('settings');
|
||||||
this.inject('tooltips');
|
this.inject('tooltips');
|
||||||
this.inject('experiments');
|
this.inject('experiments');
|
||||||
|
this.inject('staging');
|
||||||
|
|
||||||
this.style = new ManagedStyle('badges');
|
this.style = new ManagedStyle('badges');
|
||||||
|
|
||||||
|
@ -958,7 +959,7 @@ export default class Badges extends Module {
|
||||||
} catch(err) { /* do nothing */ }
|
} catch(err) { /* do nothing */ }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
response = await fetch(`${API_SERVER}/v1/badges/ids`);
|
response = await fetch(`${this.staging.api}/v1/badges/ids`);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
tries++;
|
tries++;
|
||||||
if ( tries < 10 )
|
if ( tries < 10 )
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
|
|
||||||
import Module from 'utilities/module';
|
import Module from 'utilities/module';
|
||||||
import {ManagedStyle} from 'utilities/dom';
|
import {ManagedStyle} from 'utilities/dom';
|
||||||
import {get, has, timeout, SourcedSet} from 'utilities/object';
|
import {get, has, timeout, SourcedSet, make_enum_flags} from 'utilities/object';
|
||||||
import {NEW_API, API_SERVER, IS_OSX, EmoteTypes, TWITCH_GLOBAL_SETS, TWITCH_POINTS_SETS, TWITCH_PRIME_SETS} from 'utilities/constants';
|
import {NEW_API, IS_OSX, EmoteTypes, TWITCH_GLOBAL_SETS, TWITCH_POINTS_SETS, TWITCH_PRIME_SETS} from 'utilities/constants';
|
||||||
|
|
||||||
import GET_EMOTE from './emote_info.gql';
|
import GET_EMOTE from './emote_info.gql';
|
||||||
import GET_EMOTE_SET from './emote_set_info.gql';
|
import GET_EMOTE_SET from './emote_set_info.gql';
|
||||||
|
@ -17,6 +17,148 @@ const HoverState = Symbol('FFZ:Hover:State');
|
||||||
|
|
||||||
const MOD_KEY = IS_OSX ? 'metaKey' : 'ctrlKey';
|
const MOD_KEY = IS_OSX ? 'metaKey' : 'ctrlKey';
|
||||||
|
|
||||||
|
export const MODIFIER_FLAGS = make_enum_flags(
|
||||||
|
'Hidden',
|
||||||
|
'FlipX',
|
||||||
|
'FlipY',
|
||||||
|
'GrowX',
|
||||||
|
'GrowY',
|
||||||
|
'ShrinkX',
|
||||||
|
'ShrinkY',
|
||||||
|
'Rotate45',
|
||||||
|
'Rotate90',
|
||||||
|
'Greyscale',
|
||||||
|
'Sepia',
|
||||||
|
'Rainbow',
|
||||||
|
'HyperRed',
|
||||||
|
'Shake',
|
||||||
|
'Photocopy'
|
||||||
|
);
|
||||||
|
|
||||||
|
export const MODIFIER_KEYS = Object.values(MODIFIER_FLAGS).filter(x => typeof x === 'number');
|
||||||
|
|
||||||
|
const MODIFIER_FLAG_CSS = {
|
||||||
|
FlipX: {
|
||||||
|
title: 'Flip Horizontal',
|
||||||
|
transform: 'scaleX(-1)'
|
||||||
|
},
|
||||||
|
FlipY: {
|
||||||
|
title: 'Flip Vertical',
|
||||||
|
transform: 'scaleY(-1)'
|
||||||
|
},
|
||||||
|
ShrinkX: {
|
||||||
|
title: 'Squish Horizontal',
|
||||||
|
transform: 'scaleX(0.5)'
|
||||||
|
},
|
||||||
|
GrowX: {
|
||||||
|
title: 'Stretch Horizontal',
|
||||||
|
transform: 'scaleX(2)'
|
||||||
|
},
|
||||||
|
ShrinkY: {
|
||||||
|
title: 'Squish Vertical',
|
||||||
|
transform: 'scaleY(0.5)'
|
||||||
|
},
|
||||||
|
GrowY: {
|
||||||
|
title: 'Stretch Vertical',
|
||||||
|
transform: 'scaleY(2)'
|
||||||
|
},
|
||||||
|
Rotate45: {
|
||||||
|
title: 'Rotate 45 Degrees',
|
||||||
|
transform: 'rotate(45deg)'
|
||||||
|
},
|
||||||
|
Rotate90: {
|
||||||
|
title: 'Rotate 90 Degrees',
|
||||||
|
transform: 'rotate(90deg)'
|
||||||
|
},
|
||||||
|
Greyscale: {
|
||||||
|
title: 'Grayscale',
|
||||||
|
filter: 'grayscale(1)'
|
||||||
|
},
|
||||||
|
Sepia: {
|
||||||
|
title: 'Sepia',
|
||||||
|
filter: 'sepia(1)'
|
||||||
|
},
|
||||||
|
Rainbow: {
|
||||||
|
title: 'Rainbow Animation',
|
||||||
|
animation: 'ffz-effect-rainbow 2s linear infinite',
|
||||||
|
animationFilter: 'ffz-effect-rainbow-filter 2s linear infinite',
|
||||||
|
raw: `@keyframes ffz-effect-rainbow {
|
||||||
|
0% { filter: hue-rotate(0deg) }
|
||||||
|
100% { filter: hue-rotate(360deg) }
|
||||||
|
}
|
||||||
|
@keyframes ffz-effect-rainbow-filter {
|
||||||
|
0% { filter: var(--ffz-effect-filters) hue-rotate(0deg) }
|
||||||
|
100% { filter: var(--ffz-effect-filters) hue-rotate(360deg) }
|
||||||
|
}`
|
||||||
|
},
|
||||||
|
HyperRed: {
|
||||||
|
title: 'Hyper Red',
|
||||||
|
filter: 'brightness(0.2) sepia(1) brightness(2.2) contrast(3) saturate(8)'
|
||||||
|
},
|
||||||
|
Shake: {
|
||||||
|
title: 'Hyper Shake Animation',
|
||||||
|
animation: 'ffz-effect-shake 0.1s linear infinite',
|
||||||
|
animationTransform: 'ffz-effect-shake-transform 0.1s linear infinite',
|
||||||
|
raw: `@keyframes ffz-effect-shake-transform {
|
||||||
|
0% { transform: var(--ffz-effect-transforms) translate(1px, 1px); }
|
||||||
|
10% { transform: var(--ffz-effect-transforms) translate(-1px, -2px); }
|
||||||
|
20% { transform: var(--ffz-effect-transforms) translate(-3px, 0px); }
|
||||||
|
30% { transform: var(--ffz-effect-transforms) translate(3px, 2px); }
|
||||||
|
40% { transform: var(--ffz-effect-transforms) translate(1px, -1px); }
|
||||||
|
50% { transform: var(--ffz-effect-transforms) translate(-1px, 2px); }
|
||||||
|
60% { transform: var(--ffz-effect-transforms) translate(-3px, 1px); }
|
||||||
|
70% { transform: var(--ffz-effect-transforms) translate(3px, 1px); }
|
||||||
|
80% { transform: var(--ffz-effect-transforms) translate(-1px, -1px); }
|
||||||
|
90% { transform: var(--ffz-effect-transforms) translate(1px, 2px); }
|
||||||
|
100% { transform: var(--ffz-effect-transforms) translate(1px, -2px); }
|
||||||
|
}
|
||||||
|
@keyframes ffz-effect-shake {
|
||||||
|
0% { transform: translate(1px, 1px); }
|
||||||
|
10% { transform: translate(-1px, -2px); }
|
||||||
|
20% { transform: translate(-3px, 0px); }
|
||||||
|
30% { transform: translate(3px, 2px); }
|
||||||
|
40% { transform: translate(1px, -1px); }
|
||||||
|
50% { transform: translate(-1px, 2px); }
|
||||||
|
60% { transform: translate(-3px, 1px); }
|
||||||
|
70% { transform: translate(3px, 1px); }
|
||||||
|
80% { transform: translate(-1px, -1px); }
|
||||||
|
90% { transform: translate(1px, 2px); }
|
||||||
|
100% { transform: translate(1px, -2px); }
|
||||||
|
}`
|
||||||
|
},
|
||||||
|
Photocopy: {
|
||||||
|
title: 'Photocopy',
|
||||||
|
filter: 'grayscale(1) brightness(0.65) contrast(5)'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function generateBaseFilterCss() {
|
||||||
|
console.log('flags', MODIFIER_FLAGS);
|
||||||
|
console.log('css', MODIFIER_FLAG_CSS);
|
||||||
|
|
||||||
|
const out = [
|
||||||
|
`.modified-emote[data-effects] > img {
|
||||||
|
--ffz-effect-filters: none;
|
||||||
|
--ffz-effect-transforms: initial;
|
||||||
|
--ffz-effect-animations: initial;
|
||||||
|
}`/*,
|
||||||
|
`.modified-emote[data-effects] > img {
|
||||||
|
filter: var(--ffz-effect-filters);
|
||||||
|
transform: var(--ffz-effect-transforms);
|
||||||
|
animation: var(--ffz-effect-animations);
|
||||||
|
}`*/
|
||||||
|
];
|
||||||
|
|
||||||
|
for(const [key, val] of Object.entries(MODIFIER_FLAG_CSS)) {
|
||||||
|
if ( val.raw )
|
||||||
|
out.push(val.raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const MODIFIERS = {
|
const MODIFIERS = {
|
||||||
59847: {
|
59847: {
|
||||||
modifier_offset: '0 15px 15px 0',
|
modifier_offset: '0 15px 15px 0',
|
||||||
|
@ -62,14 +204,25 @@ export default class Emotes extends Module {
|
||||||
super(...args);
|
super(...args);
|
||||||
|
|
||||||
this.EmoteTypes = EmoteTypes;
|
this.EmoteTypes = EmoteTypes;
|
||||||
|
this.ModifierFlags = MODIFIER_FLAGS;
|
||||||
|
|
||||||
this.inject('settings');
|
this.inject('settings');
|
||||||
this.inject('experiments');
|
this.inject('experiments');
|
||||||
|
this.inject('staging');
|
||||||
|
|
||||||
this.twitch_inventory_sets = new Set; //(EXTRA_INVENTORY);
|
this.twitch_inventory_sets = new Set; //(EXTRA_INVENTORY);
|
||||||
this.__twitch_emote_to_set = {};
|
this.__twitch_emote_to_set = {};
|
||||||
this.__twitch_set_to_channel = {};
|
this.__twitch_set_to_channel = {};
|
||||||
|
|
||||||
|
// Bulk data structure for collections applied to a lot of users.
|
||||||
|
// This lets us avoid allocating lots of individual user
|
||||||
|
// objects when we don't need to do so.
|
||||||
|
this.bulk = new Map;
|
||||||
|
|
||||||
|
this.effects_enabled = {};
|
||||||
|
this.pending_effects = new Set();
|
||||||
|
this.applyEffects = this.applyEffects.bind(this);
|
||||||
|
|
||||||
this.default_sets = new SourcedSet;
|
this.default_sets = new SourcedSet;
|
||||||
this.global_sets = new SourcedSet;
|
this.global_sets = new SourcedSet;
|
||||||
|
|
||||||
|
@ -170,6 +323,42 @@ export default class Emotes extends Module {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.settings.add('chat.effects.enable', {
|
||||||
|
default: true,
|
||||||
|
ui: {
|
||||||
|
path: 'Chat > Emote Effects >> General',
|
||||||
|
title: 'Enable the use of emote effects.',
|
||||||
|
description: 'Emote Effects are special effects that can be applied to some emotes using special modifiers.',
|
||||||
|
component: 'setting-check-box'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for(const [key, val] of Object.entries(MODIFIER_FLAG_CSS)) {
|
||||||
|
const setting = {
|
||||||
|
default: val.animation
|
||||||
|
? null
|
||||||
|
: true,
|
||||||
|
ui: {
|
||||||
|
path: 'Chat > Emote Effects >> Specific Effect @{"description": "**Note:** Animated effects are, by default, only enabled when [Animated Emotes](~chat.appearance.emotes) are enabled."}',
|
||||||
|
title: `Enable the effect "${val.title ?? key}".`,
|
||||||
|
component: 'setting-check-box',
|
||||||
|
force_seen: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( val.animation ) {
|
||||||
|
setting.default = null;
|
||||||
|
setting.requires = ['chat.emotes.animated'];
|
||||||
|
setting.process = function(ctx, val) {
|
||||||
|
if ( val == null )
|
||||||
|
return ctx.get('chat.emotes.animated') === 1;
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.settings.add(`chat.effects.${key}`, setting);
|
||||||
|
}
|
||||||
|
|
||||||
// Because this may be used elsewhere.
|
// Because this may be used elsewhere.
|
||||||
this.handleClick = this.handleClick.bind(this);
|
this.handleClick = this.handleClick.bind(this);
|
||||||
this.animHover = this.animHover.bind(this);
|
this.animHover = this.animHover.bind(this);
|
||||||
|
@ -178,6 +367,16 @@ export default class Emotes extends Module {
|
||||||
|
|
||||||
onEnable() {
|
onEnable() {
|
||||||
this.style = new ManagedStyle('emotes');
|
this.style = new ManagedStyle('emotes');
|
||||||
|
this.effect_style = new ManagedStyle('effects');
|
||||||
|
|
||||||
|
// Generate the base filter CSS.
|
||||||
|
this.base_effect_css = generateBaseFilterCss();
|
||||||
|
|
||||||
|
this.parent.context.on('changed:chat.effects.enable', this.updateEffects, this);
|
||||||
|
for(const key of Object.keys(MODIFIER_FLAG_CSS))
|
||||||
|
this.parent.context.on(`changed:chat.effects.${key}`, this.updateEffects, this);
|
||||||
|
|
||||||
|
this.updateEffects();
|
||||||
|
|
||||||
// Fix numeric Twitch favorite IDs.
|
// Fix numeric Twitch favorite IDs.
|
||||||
const favs = this.getFavorites('twitch');
|
const favs = this.getFavorites('twitch');
|
||||||
|
@ -210,6 +409,103 @@ export default class Emotes extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// Load Modifier Effects
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
ensureEffect(flags) {
|
||||||
|
if ( ! this.effect_style.has(`${flags}`) ) {
|
||||||
|
this.pending_effects.add(flags);
|
||||||
|
if ( ! this._effect_timer )
|
||||||
|
this._effect_timer = requestAnimationFrame(this.applyEffects);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applyEffects() {
|
||||||
|
this._effect_timer = null;
|
||||||
|
const effects = this.pending_effects;
|
||||||
|
this.pending_effects = new Set;
|
||||||
|
|
||||||
|
for(const flags of effects) {
|
||||||
|
const result = this.generateFilterCss(flags);
|
||||||
|
this.effect_style.set(`${flags}`, result ?? '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generateFilterCss(flags) {
|
||||||
|
if ( ! this.parent.context.get('chat.effects.enable') )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let filter, transform, animation, animations = [];
|
||||||
|
|
||||||
|
for(const key of MODIFIER_KEYS) {
|
||||||
|
if ( (flags & key) !== key || ! this.effects_enabled[key] )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const input = MODIFIER_FLAG_CSS[MODIFIER_FLAGS[key]];
|
||||||
|
if ( ! input )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ( input.animation )
|
||||||
|
animations.push(input);
|
||||||
|
|
||||||
|
if ( input.filter )
|
||||||
|
filter = filter
|
||||||
|
? `${filter} ${input.filter}`
|
||||||
|
: input.filter;
|
||||||
|
|
||||||
|
if ( input.transform )
|
||||||
|
transform = transform
|
||||||
|
? `${transform} ${input.transform}`
|
||||||
|
: input.transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( animations.length )
|
||||||
|
for(const input of animations) {
|
||||||
|
if ( filter && input.animationFilter )
|
||||||
|
animation = animation
|
||||||
|
? `${animation}, ${input.animationFilter}`
|
||||||
|
: input.animationFilter;
|
||||||
|
else if ( transform && input.animationTransform )
|
||||||
|
animation = animation
|
||||||
|
? `${animation}, ${input.animationTransform}`
|
||||||
|
: input.animationTransform;
|
||||||
|
else
|
||||||
|
animation = animation
|
||||||
|
? `${animation}, ${input.animation}`
|
||||||
|
: input.animation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! filter && ! transform && ! animation )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return `.modified-emote[data-effects="${flags}"] > img {${filter ? `
|
||||||
|
--ffz-effect-filters: ${filter};
|
||||||
|
filter: var(--ffz-effect-filters);` : ''}${transform ? `
|
||||||
|
--ffz-effect-transforms: ${transform};
|
||||||
|
transform: var(--ffz-effect-transforms);` : ''}${animation ? `
|
||||||
|
--ffz-effect-animations: ${animation};
|
||||||
|
animation: var(--ffz-effect-animations);` : ''}
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateEffects() {
|
||||||
|
// TODO: Smarter logic so it does less work.
|
||||||
|
const enabled = this.parent.context.get('chat.effects.enable');
|
||||||
|
|
||||||
|
this.effects_enabled = {};
|
||||||
|
for(const key of Object.keys(MODIFIER_FLAG_CSS))
|
||||||
|
this.effects_enabled[MODIFIER_FLAGS[key]] = this.effects_enabled[key] = this.parent.context.get(`chat.effects.${key}`);
|
||||||
|
|
||||||
|
this.effect_style.clear();
|
||||||
|
if ( ! enabled )
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.effect_style.set('base', this.base_effect_css);
|
||||||
|
this.emit(':update-effects');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// Featured Sets
|
// Featured Sets
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
@ -513,7 +809,7 @@ export default class Emotes extends Module {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const line = fine.searchParent(target, n => n.props && n.props.message),
|
const line = fine.searchParent(target, n => n.props && n.props.message),
|
||||||
opener = fine.searchParent(target, n => n.onShowEmoteCard, 250);
|
opener = fine.searchParent(target, n => n.onShowEmoteCard, 500);
|
||||||
|
|
||||||
if ( ! line || ! opener )
|
if ( ! line || ! opener )
|
||||||
return;
|
return;
|
||||||
|
@ -545,11 +841,21 @@ export default class Emotes extends Module {
|
||||||
room_user = room && room.getUser(user_id, user_login, true),
|
room_user = room && room.getUser(user_id, user_login, true),
|
||||||
user = this.parent.getUser(user_id, user_login, true);
|
user = this.parent.getUser(user_id, user_login, true);
|
||||||
|
|
||||||
return (user?.emote_sets ? user.emote_sets._cache : []).concat(
|
const out = (user?.emote_sets ? user.emote_sets._cache : []).concat(
|
||||||
room_user?.emote_sets ? room_user.emote_sets._cache : [],
|
room_user?.emote_sets ? room_user.emote_sets._cache : [],
|
||||||
room?.emote_sets ? room.emote_sets._cache : [],
|
room?.emote_sets ? room.emote_sets._cache : [],
|
||||||
this.default_sets._cache
|
this.default_sets._cache
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ( this.bulk.size ) {
|
||||||
|
const str_user = String(user_id);
|
||||||
|
for(const [set_id, users] of this.bulk) {
|
||||||
|
if ( users?._cache.has(str_user) )
|
||||||
|
out.push(set_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSets(user_id, user_login, room_id, room_login) {
|
getSets(user_id, user_login, room_id, room_login) {
|
||||||
|
@ -621,6 +927,20 @@ export default class Emotes extends Module {
|
||||||
if ( user )
|
if ( user )
|
||||||
this._withSources(out, seen, user.emote_sets);
|
this._withSources(out, seen, user.emote_sets);
|
||||||
|
|
||||||
|
if ( this.bulk.size ) {
|
||||||
|
const str_user = String(user_id);
|
||||||
|
for(const [set_id, users] of this.bulk) {
|
||||||
|
if ( ! seen.has(set_id) && users?._cache.has(str_user) ) {
|
||||||
|
for(const [provider, data] of users._sources) {
|
||||||
|
if ( data && data.includes(str_user) ) {
|
||||||
|
out.push([set_id, provider]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -631,10 +951,21 @@ export default class Emotes extends Module {
|
||||||
|
|
||||||
getGlobalSetIDs(user_id, user_login) {
|
getGlobalSetIDs(user_id, user_login) {
|
||||||
const user = this.parent.getUser(user_id, user_login, true);
|
const user = this.parent.getUser(user_id, user_login, true);
|
||||||
if ( ! user?.emote_sets )
|
|
||||||
return this.default_sets._cache;
|
|
||||||
|
|
||||||
return user.emote_sets._cache.concat(this.default_sets._cache);
|
const out = (user?.emote_sets ? user.emote_sets._cache : []).concat(
|
||||||
|
this.default_sets._cache
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( this.bulk.size ) {
|
||||||
|
const str_user = String(user_id);
|
||||||
|
for(const [set_id, users] of this.bulk) {
|
||||||
|
if ( users?._cache.has(str_user) )
|
||||||
|
out.push(set_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getGlobalSets(user_id, user_login) {
|
getGlobalSets(user_id, user_login) {
|
||||||
|
@ -653,6 +984,52 @@ export default class Emotes extends Module {
|
||||||
return emotes;
|
return emotes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// Bulk Management
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
setBulk(source, set_id, entries) {
|
||||||
|
let set = this.bulk.get(set_id);
|
||||||
|
if ( ! set )
|
||||||
|
this.bulk.set(set_id, set = new SourcedSet(true));
|
||||||
|
|
||||||
|
const size = set._cache.size;
|
||||||
|
set.set(source, entries);
|
||||||
|
const new_size = set._cache.size;
|
||||||
|
|
||||||
|
if ( ! size && new_size )
|
||||||
|
this.refSet(set_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteBulk(source, set_id) {
|
||||||
|
const set = this.bulk.get(set_id);
|
||||||
|
if ( ! set )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const size = set._cache.size;
|
||||||
|
set.delete(source);
|
||||||
|
const new_size = set._cache.size;
|
||||||
|
|
||||||
|
if ( size && ! new_size )
|
||||||
|
this.unrefSet(set_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
extendBulk(source, set_id, entries) {
|
||||||
|
let set = this.bulk.get(set_id);
|
||||||
|
if ( ! set )
|
||||||
|
this.bulk.set(set_id, set = new SourcedSet(true));
|
||||||
|
|
||||||
|
if ( ! Array.isArray(entries) )
|
||||||
|
entries = [entries];
|
||||||
|
|
||||||
|
const size = set._cache.size;
|
||||||
|
set.extend(source, ...entries);
|
||||||
|
const new_size = set._cache.size;
|
||||||
|
|
||||||
|
if ( ! size && new_size )
|
||||||
|
this.refSet(set_id);
|
||||||
|
}
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// Emote Set Ref Counting
|
// Emote Set Ref Counting
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
@ -724,7 +1101,7 @@ export default class Emotes extends Module {
|
||||||
} catch(err) { /* do nothing */ }
|
} catch(err) { /* do nothing */ }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
response = await fetch(`${API_SERVER}/v1/set/global`)
|
response = await fetch(`${this.staging.api}/v1/set/global${this.staging.active ? '/ids' : ''}`)
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
tries++;
|
tries++;
|
||||||
if ( tries < 10 )
|
if ( tries < 10 )
|
||||||
|
@ -753,7 +1130,9 @@ export default class Emotes extends Module {
|
||||||
if ( has(sets, set_id) )
|
if ( has(sets, set_id) )
|
||||||
this.loadSetData(set_id, sets[set_id]);
|
this.loadSetData(set_id, sets[set_id]);
|
||||||
|
|
||||||
if ( data.users )
|
if ( data.user_ids )
|
||||||
|
this.loadSetUserIds(data.user_ids);
|
||||||
|
else if ( data.users )
|
||||||
this.loadSetUsers(data.users);
|
this.loadSetUsers(data.users);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -769,7 +1148,7 @@ export default class Emotes extends Module {
|
||||||
} catch(err) { /* do nothing */ }
|
} catch(err) { /* do nothing */ }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
response = await fetch(`${API_SERVER}/v1/set/${set_id}`)
|
response = await fetch(`${this.staging.api}/v1/set/${set_id}${this.staging.active ? '/ids' : ''}`)
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
tries++;
|
tries++;
|
||||||
if ( tries < 10 )
|
if ( tries < 10 )
|
||||||
|
@ -793,13 +1172,28 @@ export default class Emotes extends Module {
|
||||||
if ( set )
|
if ( set )
|
||||||
this.loadSetData(set.id, set, suppress_log);
|
this.loadSetData(set.id, set, suppress_log);
|
||||||
|
|
||||||
if ( data.users )
|
if ( data.user_ids )
|
||||||
|
this.loadSetUserIds(data.user_ids);
|
||||||
|
else if ( data.users )
|
||||||
this.loadSetUsers(data.users);
|
this.loadSetUsers(data.users);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
loadSetUserIds(data, suppress_log = false) {
|
||||||
|
for(const set_id in data)
|
||||||
|
if ( has(data, set_id) ) {
|
||||||
|
const emote_set = this.emote_sets[set_id],
|
||||||
|
users = data[set_id];
|
||||||
|
|
||||||
|
this.setBulk('ffz-global', set_id, users.map(x => String(x)));
|
||||||
|
if ( ! suppress_log )
|
||||||
|
this.log.info(`Added "${emote_set ? emote_set.title : set_id}" emote set to ${users.length} users.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
loadSetUsers(data, suppress_log = false) {
|
loadSetUsers(data, suppress_log = false) {
|
||||||
for(const set_id in data)
|
for(const set_id in data)
|
||||||
if ( has(data, set_id) ) {
|
if ( has(data, set_id) ) {
|
||||||
|
@ -867,7 +1261,8 @@ export default class Emotes extends Module {
|
||||||
animSrcSet2: emote.animSrcSet2,
|
animSrcSet2: emote.animSrcSet2,
|
||||||
text: emote.hidden ? '???' : emote.name,
|
text: emote.hidden ? '???' : emote.name,
|
||||||
length: emote.name.length,
|
length: emote.name.length,
|
||||||
height: emote.height
|
height: emote.height,
|
||||||
|
source_modifier_flags: emote.modifier_flags ?? 0
|
||||||
};
|
};
|
||||||
|
|
||||||
if ( has(MODIFIERS, emote.id) )
|
if ( has(MODIFIERS, emote.id) )
|
||||||
|
|
|
@ -70,6 +70,7 @@ export default class Chat extends Module {
|
||||||
this.inject('i18n');
|
this.inject('i18n');
|
||||||
this.inject('tooltips');
|
this.inject('tooltips');
|
||||||
this.inject('experiments');
|
this.inject('experiments');
|
||||||
|
this.inject('staging');
|
||||||
|
|
||||||
this.inject(Badges);
|
this.inject(Badges);
|
||||||
this.inject(Emotes);
|
this.inject(Emotes);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import User from './user';
|
import User from './user';
|
||||||
|
|
||||||
import {NEW_API, API_SERVER, WEBKIT_CSS as WEBKIT, IS_FIREFOX} from 'utilities/constants';
|
import {NEW_API, WEBKIT_CSS as WEBKIT, IS_FIREFOX} from 'utilities/constants';
|
||||||
|
|
||||||
import {ManagedStyle} from 'utilities/dom';
|
import {ManagedStyle} from 'utilities/dom';
|
||||||
import {has, SourcedSet, set_equals} from 'utilities/object';
|
import {has, SourcedSet, set_equals} from 'utilities/object';
|
||||||
|
@ -260,7 +260,7 @@ export default class Room {
|
||||||
|
|
||||||
let response, data;
|
let response, data;
|
||||||
try {
|
try {
|
||||||
response = await fetch(`${API_SERVER}/v1/room/${this.id ? `id/${this.id}` : this.login}`);
|
response = await fetch(`${this.manager.staging.api}/v1/room/${this.id ? `id/${this.id}` : this.login}`);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
tries++;
|
tries++;
|
||||||
if ( tries < 10 )
|
if ( tries < 10 )
|
||||||
|
|
|
@ -1242,6 +1242,10 @@ export const AddonEmotes = {
|
||||||
return (<div class="ffz--inline" data-test-selector="emote-button">{emote}</div>);
|
return (<div class="ffz--inline" data-test-selector="emote-button">{emote}</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const effects = token.modifier_flags;
|
||||||
|
if ( effects )
|
||||||
|
this.emotes.ensureEffect(effects);
|
||||||
|
|
||||||
return (<div
|
return (<div
|
||||||
class="ffz--inline ffz--pointer-events modified-emote"
|
class="ffz--inline ffz--pointer-events modified-emote"
|
||||||
data-test-selector="emote-button"
|
data-test-selector="emote-button"
|
||||||
|
@ -1249,10 +1253,17 @@ export const AddonEmotes = {
|
||||||
data-id={token.id}
|
data-id={token.id}
|
||||||
data-set={token.set}
|
data-set={token.set}
|
||||||
data-modifiers={ml ? mods.map(x => x.id).join(' ') : null}
|
data-modifiers={ml ? mods.map(x => x.id).join(' ') : null}
|
||||||
|
data-effects={effects ? effects : undefined}
|
||||||
onClick={this.emotes.handleClick}
|
onClick={this.emotes.handleClick}
|
||||||
>
|
>
|
||||||
{emote}
|
{emote}
|
||||||
{mods.map(t => <span key={t.text}>{this.tokenizers.emote.render.call(this, t, createElement, true)}</span>)}
|
{mods.map(t => {
|
||||||
|
if ( (t.source_modifier_flags & 1) === 1)
|
||||||
|
return null;
|
||||||
|
return <span key={t.text}>
|
||||||
|
{this.tokenizers.emote.render.call(this, t, createElement, true)}
|
||||||
|
</span>
|
||||||
|
})}
|
||||||
</div>);
|
</div>);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1443,8 +1454,10 @@ export const AddonEmotes = {
|
||||||
|
|
||||||
if ( token.type !== 'text' ) {
|
if ( token.type !== 'text' ) {
|
||||||
if ( token.type === 'emote' ) {
|
if ( token.type === 'emote' ) {
|
||||||
if ( ! token.modifiers )
|
if ( ! token.modifiers ) {
|
||||||
token.modifiers = [];
|
token.modifiers = [];
|
||||||
|
token.modifier_flags = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out.push(token);
|
out.push(token);
|
||||||
|
@ -1461,6 +1474,9 @@ export const AddonEmotes = {
|
||||||
// Is this emote a modifier?
|
// Is this emote a modifier?
|
||||||
if ( emote.modifier && last_token && last_token.modifiers && (!text.length || (text.length === 1 && text[0] === '')) ) {
|
if ( emote.modifier && last_token && last_token.modifiers && (!text.length || (text.length === 1 && text[0] === '')) ) {
|
||||||
if ( last_token.modifiers.indexOf(emote.token) === -1 ) {
|
if ( last_token.modifiers.indexOf(emote.token) === -1 ) {
|
||||||
|
if ( emote.modifier_flags )
|
||||||
|
last_token.modifier_flags |= emote.modifier_flags;
|
||||||
|
|
||||||
last_token.modifiers.push(
|
last_token.modifiers.push(
|
||||||
Object.assign({
|
Object.assign({
|
||||||
big,
|
big,
|
||||||
|
@ -1486,6 +1502,7 @@ export const AddonEmotes = {
|
||||||
|
|
||||||
const t = Object.assign({
|
const t = Object.assign({
|
||||||
modifiers: [],
|
modifiers: [],
|
||||||
|
modifier_flags: 0,
|
||||||
big,
|
big,
|
||||||
anim
|
anim
|
||||||
}, emote.token);
|
}, emote.token);
|
||||||
|
@ -1582,7 +1599,8 @@ export const Emoji = {
|
||||||
|
|
||||||
text: match[0],
|
text: match[0],
|
||||||
length,
|
length,
|
||||||
modifiers: []
|
modifiers: [],
|
||||||
|
modifier_flags: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
idx = start + match[0].length;
|
idx = start + match[0].length;
|
||||||
|
@ -1734,7 +1752,8 @@ export const TwitchEmotes = {
|
||||||
can_big,
|
can_big,
|
||||||
height: 28, // Not always accurate but close enough.
|
height: 28, // Not always accurate but close enough.
|
||||||
text: text.slice(e_start - t_start, e_end - t_start).join(''),
|
text: text.slice(e_start - t_start, e_end - t_start).join(''),
|
||||||
modifiers: []
|
modifiers: [],
|
||||||
|
modifier_flags: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
idx = e_end;
|
idx = e_end;
|
||||||
|
|
|
@ -373,6 +373,7 @@ export default class EmoteMenu extends Module {
|
||||||
this.on('chat.emotes:update-default-sets', this.maybeUpdate, this);
|
this.on('chat.emotes:update-default-sets', this.maybeUpdate, this);
|
||||||
this.on('chat.emotes:update-user-sets', this.maybeUpdate, this);
|
this.on('chat.emotes:update-user-sets', this.maybeUpdate, this);
|
||||||
this.on('chat.emotes:update-room-sets', this.maybeUpdate, this);
|
this.on('chat.emotes:update-room-sets', this.maybeUpdate, this);
|
||||||
|
this.on('chat.emotes:loaded', this.maybeUpdate, this);
|
||||||
this.on('chat.emotes:change-favorite', this.maybeUpdate, this);
|
this.on('chat.emotes:change-favorite', this.maybeUpdate, this);
|
||||||
this.on('chat.emotes:change-hidden', this.maybeUpdate, this);
|
this.on('chat.emotes:change-hidden', this.maybeUpdate, this);
|
||||||
this.on('chat.emoji:populated', this.maybeUpdate, this);
|
this.on('chat.emoji:populated', this.maybeUpdate, this);
|
||||||
|
|
|
@ -104,9 +104,6 @@ const CHAT_TYPES = make_enum(
|
||||||
'Connected',
|
'Connected',
|
||||||
'Disconnected',
|
'Disconnected',
|
||||||
'Reconnect',
|
'Reconnect',
|
||||||
'Hosting',
|
|
||||||
'Unhost',
|
|
||||||
'Hosted',
|
|
||||||
'Subscription',
|
'Subscription',
|
||||||
'Resubscription',
|
'Resubscription',
|
||||||
'GiftPaidUpgrade',
|
'GiftPaidUpgrade',
|
||||||
|
@ -121,7 +118,6 @@ const CHAT_TYPES = make_enum(
|
||||||
'RoomState',
|
'RoomState',
|
||||||
'Raid',
|
'Raid',
|
||||||
'Unraid',
|
'Unraid',
|
||||||
'Ritual',
|
|
||||||
'Notice',
|
'Notice',
|
||||||
'Info',
|
'Info',
|
||||||
'BadgesUpdated',
|
'BadgesUpdated',
|
||||||
|
@ -139,10 +135,16 @@ const CHAT_TYPES = make_enum(
|
||||||
'InlinePrivateCallout',
|
'InlinePrivateCallout',
|
||||||
'ChannelPointsReward',
|
'ChannelPointsReward',
|
||||||
'CommunityChallengeContribution',
|
'CommunityChallengeContribution',
|
||||||
'CelebrationPurchase',
|
|
||||||
'LiveMessageSeparator',
|
'LiveMessageSeparator',
|
||||||
'RestrictedLowTrustUserMessage',
|
'RestrictedLowTrustUserMessage',
|
||||||
'CommunityIntroduction'
|
'CommunityIntroduction',
|
||||||
|
'Shoutout',
|
||||||
|
'AnnouncementMessage',
|
||||||
|
'MidnightSquid',
|
||||||
|
'CharityDonation',
|
||||||
|
'MessageIdUpdate',
|
||||||
|
'PinnedChat',
|
||||||
|
'ViewerMilestone'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ export default class ChatLine extends Module {
|
||||||
|
|
||||||
this.inject('chat.actions');
|
this.inject('chat.actions');
|
||||||
this.inject('chat.overrides');
|
this.inject('chat.overrides');
|
||||||
|
this.inject('chat.emotes');
|
||||||
|
|
||||||
this.line_types = {};
|
this.line_types = {};
|
||||||
|
|
||||||
|
@ -349,6 +350,7 @@ export default class ChatLine extends Module {
|
||||||
this.on('chat:update-line-tokens', this.updateLineTokens, this);
|
this.on('chat:update-line-tokens', this.updateLineTokens, this);
|
||||||
this.on('chat:update-line-badges', this.updateLineBadges, this);
|
this.on('chat:update-line-badges', this.updateLineBadges, this);
|
||||||
this.on('i18n:update', this.rerenderLines, this);
|
this.on('i18n:update', this.rerenderLines, this);
|
||||||
|
this.on('chat.emotes:update-effects', this.checkEffects, this);
|
||||||
|
|
||||||
this.on('experiments:changed:line_renderer', () => {
|
this.on('experiments:changed:line_renderer', () => {
|
||||||
const value = this.experiments.get('line_renderer'),
|
const value = this.experiments.get('line_renderer'),
|
||||||
|
@ -1766,6 +1768,31 @@ other {# messages were deleted by a moderator.}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
checkEffects() {
|
||||||
|
for(const inst of this.ChatLine.instances) {
|
||||||
|
const msg = inst.props.message,
|
||||||
|
tokens = msg?.ffz_tokens;
|
||||||
|
|
||||||
|
if ( tokens )
|
||||||
|
for(const token of tokens) {
|
||||||
|
if ( token.type === 'emote' && token.modifier_flags )
|
||||||
|
this.emotes.ensureEffect(token.modifier_flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const inst of this.WhisperLine.instances) {
|
||||||
|
const msg = inst.props.message?._ffz_message,
|
||||||
|
tokens = msg?.ffz_tokens;
|
||||||
|
|
||||||
|
if ( tokens )
|
||||||
|
for(const token of tokens) {
|
||||||
|
if ( token.type === 'emote' && token.modifier_flags )
|
||||||
|
this.emotes.ensureEffect(token.modifier_flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
updateLinesByUser(id, login, clear_tokens = true, clear_badges = true) {
|
updateLinesByUser(id, login, clear_tokens = true, clear_badges = true) {
|
||||||
for(const inst of this.ChatLine.instances) {
|
for(const inst of this.ChatLine.instances) {
|
||||||
const msg = inst.props.message,
|
const msg = inst.props.message,
|
||||||
|
|
|
@ -47,6 +47,7 @@ export default class VideoChatHook extends Module {
|
||||||
this.inject('site.web_munch');
|
this.inject('site.web_munch');
|
||||||
|
|
||||||
this.inject('chat');
|
this.inject('chat');
|
||||||
|
this.inject('chat.emotes');
|
||||||
this.inject('chat.overrides');
|
this.inject('chat.overrides');
|
||||||
this.injectAs('site_chat', 'site.chat');
|
this.injectAs('site_chat', 'site.chat');
|
||||||
this.inject('site.chat.chat_line.rich_content');
|
this.inject('site.chat.chat_line.rich_content');
|
||||||
|
@ -104,6 +105,7 @@ export default class VideoChatHook extends Module {
|
||||||
this.on('chat:update-line-tokens', this.updateLineTokens, this);
|
this.on('chat:update-line-tokens', this.updateLineTokens, this);
|
||||||
this.on('chat:update-line-badges', this.updateLineBadges, this);
|
this.on('chat:update-line-badges', this.updateLineBadges, this);
|
||||||
this.on('i18n:update', this.rerenderLines, this);
|
this.on('i18n:update', this.rerenderLines, this);
|
||||||
|
this.on('chat.emotes:update-effects', this.checkEffects, this);
|
||||||
|
|
||||||
for(const setting of RERENDER_SETTINGS)
|
for(const setting of RERENDER_SETTINGS)
|
||||||
this.chat.context.on(`changed:${setting}`, this.rerenderLines, this);
|
this.chat.context.on(`changed:${setting}`, this.rerenderLines, this);
|
||||||
|
@ -466,6 +468,21 @@ export default class VideoChatHook extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
checkEffects() {
|
||||||
|
for(const inst of this.VideoChatLine.instances) {
|
||||||
|
const context = inst.props.messageContext,
|
||||||
|
msg = context?.comment?._ffz_message,
|
||||||
|
tokens = msg?.ffz_tokens;
|
||||||
|
|
||||||
|
if ( tokens )
|
||||||
|
for(const token of tokens) {
|
||||||
|
if ( token.type === 'emote' && token.modifier_flags )
|
||||||
|
this.emotes.ensureEffect(token.modifier_flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// Message Standardization
|
// Message Standardization
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
|
@ -8,6 +8,14 @@
|
||||||
margin: -.5rem 0;
|
margin: -.5rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modified-emote {
|
||||||
|
margin: -.5rem 0;
|
||||||
|
|
||||||
|
& > .chat-line__message--emote {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.chat-author__display-name,
|
.chat-author__display-name,
|
||||||
.chat-author__intl-login {
|
.chat-author__intl-login {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
46
src/staging.jsx
Normal file
46
src/staging.jsx
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Staging Selector
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
import Module from 'utilities/module';
|
||||||
|
import { API_SERVER, SERVER, STAGING_API, STAGING_CDN } from './utilities/constants';
|
||||||
|
|
||||||
|
export default class StagingSelector extends Module {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
|
||||||
|
this.inject('settings');
|
||||||
|
|
||||||
|
this.settings.add('data.use-staging', {
|
||||||
|
default: false,
|
||||||
|
ui: {
|
||||||
|
path: 'Debugging > Data Sources >> Staging @{"sort": -1}',
|
||||||
|
force_seen: true,
|
||||||
|
title: 'Use staging as data source.',
|
||||||
|
component: 'setting-check-box'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.updateStaging(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
onEnable() {
|
||||||
|
this.settings.getChanges('data.use-staging', this.updateStaging, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStaging(val) {
|
||||||
|
this.active = val;
|
||||||
|
|
||||||
|
this.api = val
|
||||||
|
? STAGING_API
|
||||||
|
: API_SERVER;
|
||||||
|
|
||||||
|
this.cdn = val
|
||||||
|
? STAGING_CDN
|
||||||
|
: SERVER;
|
||||||
|
|
||||||
|
this.emit(':updated', this.api, this.cdn);
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,15 +8,14 @@ export const SERVER = DEBUG ? '//localhost:8000' : 'https://cdn.frankerfacez.com
|
||||||
|
|
||||||
export const CLIENT_ID = 'a3bc9znoz6vi8ozsoca0inlcr4fcvkl';
|
export const CLIENT_ID = 'a3bc9znoz6vi8ozsoca0inlcr4fcvkl';
|
||||||
export const API_SERVER = '//api.frankerfacez.com';
|
export const API_SERVER = '//api.frankerfacez.com';
|
||||||
|
export const STAGING_API = '//api-staging.frankerfacez.com';
|
||||||
|
export const STAGING_CDN = '//cdn-staging.frankerfacez.com';
|
||||||
export const NEW_API = '//api2.frankerfacez.com';
|
export const NEW_API = '//api2.frankerfacez.com';
|
||||||
|
|
||||||
//export const SENTRY_ID = 'https://1c3b56f127254d3ba1bd1d6ad8805eee@sentry.io/1186960';
|
//export const SENTRY_ID = 'https://1c3b56f127254d3ba1bd1d6ad8805eee@sentry.io/1186960';
|
||||||
//export const SENTRY_ID = 'https://07ded545d3224ca59825daee02dc7745@catbag.frankerfacez.com:444/2';
|
//export const SENTRY_ID = 'https://07ded545d3224ca59825daee02dc7745@catbag.frankerfacez.com:444/2';
|
||||||
export const SENTRY_ID = 'https://74b46b3894114f399d51949c6d237489@sentry.frankerfacez.com/2';
|
export const SENTRY_ID = 'https://74b46b3894114f399d51949c6d237489@sentry.frankerfacez.com/2';
|
||||||
|
|
||||||
export const LV_SERVER = 'https://cbenni.com/api';
|
|
||||||
export const LV_SOCKET_SERVER = 'wss://cbenni.com/socket.io/';
|
|
||||||
|
|
||||||
export const WORD_SEPARATORS = '[\\s`~<>!-#%-\\x2A,-/:;\\x3F@\\x5B-\\x5D_\\x7B}\\u00A1\\u00A7\\u00AB\\u00B6\\u00B7\\u00BB\\u00BF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E3B\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]';
|
export const WORD_SEPARATORS = '[\\s`~<>!-#%-\\x2A,-/:;\\x3F@\\x5B-\\x5D_\\x7B}\\u00A1\\u00A7\\u00AB\\u00B6\\u00B7\\u00BB\\u00BF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E3B\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]';
|
||||||
|
|
||||||
export const BAD_HOTKEYS = [
|
export const BAD_HOTKEYS = [
|
||||||
|
|
|
@ -271,6 +271,11 @@ export class ManagedStyle {
|
||||||
this._style = null;
|
this._style = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this._blocks = {};
|
||||||
|
this._style.innerHTML = '';
|
||||||
|
}
|
||||||
|
|
||||||
get(key) {
|
get(key) {
|
||||||
const block = this._blocks[key];
|
const block = this._blocks[key];
|
||||||
if ( block )
|
if ( block )
|
||||||
|
@ -278,6 +283,10 @@ export class ManagedStyle {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
has(key) {
|
||||||
|
return !! this._blocks[key];
|
||||||
|
}
|
||||||
|
|
||||||
set(key, value, force) {
|
set(key, value, force) {
|
||||||
const block = this._blocks[key];
|
const block = this._blocks[key];
|
||||||
if ( block ) {
|
if ( block ) {
|
||||||
|
|
|
@ -66,6 +66,22 @@ export function make_enum(...array) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function make_enum_flags(...array) {
|
||||||
|
const out = {};
|
||||||
|
|
||||||
|
out.None = 0;
|
||||||
|
out[0] = 'None';
|
||||||
|
|
||||||
|
for(let i = 0; i < array.length; i++) {
|
||||||
|
const word = array[i],
|
||||||
|
value = Math.pow(2, i);
|
||||||
|
out[word] = value;
|
||||||
|
out[value] = word;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export function timeout(promise, delay) {
|
export function timeout(promise, delay) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue