mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-07 23:00:54 +00:00
4.15.2
* Added: Option to display all emotes in the same tab of the emote menu. (Closes #684) * Added: Button in the emote menu that opens the FFZ Control Center to `Chat > Emote Menu`. * Changed: Start using Twitch for all emote information rather than the FFZ socket cluster. * Fixed: When an emoji skin tone is set, use that skin tone for category icons if applicable. * Fixed: When reduced padding for the emote menu is enabled, reduce the padding around the navigation buttons.
This commit is contained in:
parent
a6ee3e5013
commit
742da82515
8 changed files with 396 additions and 247 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "frankerfacez",
|
"name": "frankerfacez",
|
||||||
"author": "Dan Salvato LLC",
|
"author": "Dan Salvato LLC",
|
||||||
"version": "4.15.1",
|
"version": "4.15.2",
|
||||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -5,13 +5,12 @@ query FFZ_GetEmoteInfo($id: ID!) {
|
||||||
text
|
text
|
||||||
subscriptionProduct {
|
subscriptionProduct {
|
||||||
id
|
id
|
||||||
|
state
|
||||||
owner {
|
owner {
|
||||||
id
|
id
|
||||||
login
|
login
|
||||||
displayName
|
displayName
|
||||||
}
|
}
|
||||||
tier
|
|
||||||
url
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
15
src/modules/chat/emote_set_info.gql
Normal file
15
src/modules/chat/emote_set_info.gql
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
query FFZ_GetEmoteSetInfo($id: ID!) {
|
||||||
|
emoteSet(id: $id) {
|
||||||
|
id
|
||||||
|
owner {
|
||||||
|
id
|
||||||
|
login
|
||||||
|
displayName
|
||||||
|
subscriptionProducts {
|
||||||
|
id
|
||||||
|
emoteSetID
|
||||||
|
state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,14 +7,13 @@
|
||||||
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} from 'utilities/object';
|
||||||
import {CLIENT_ID, NEW_API, API_SERVER, IS_OSX} from 'utilities/constants';
|
import {NEW_API, API_SERVER, 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';
|
||||||
|
|
||||||
const MOD_KEY = IS_OSX ? 'metaKey' : 'ctrlKey';
|
const MOD_KEY = IS_OSX ? 'metaKey' : 'ctrlKey';
|
||||||
|
|
||||||
//const EXTRA_INVENTORY = [33563];
|
|
||||||
|
|
||||||
const MODIFIERS = {
|
const MODIFIERS = {
|
||||||
59847: {
|
59847: {
|
||||||
modifier_offset: '0 15px 15px 0',
|
modifier_offset: '0 15px 15px 0',
|
||||||
|
@ -60,6 +59,8 @@ export default class Emotes extends Module {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
super(...args);
|
super(...args);
|
||||||
|
|
||||||
|
this.EmoteTypes = EmoteTypes;
|
||||||
|
|
||||||
this.inject('socket');
|
this.inject('socket');
|
||||||
this.inject('settings');
|
this.inject('settings');
|
||||||
this.inject('experiments');
|
this.inject('experiments');
|
||||||
|
@ -126,9 +127,6 @@ export default class Emotes extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
onEnable() {
|
onEnable() {
|
||||||
// Just in case there's a weird load order going on.
|
|
||||||
// this.on('site:enabled', this.loadTwitchInventory);
|
|
||||||
|
|
||||||
this.style = new ManagedStyle('emotes');
|
this.style = new ManagedStyle('emotes');
|
||||||
|
|
||||||
if ( Object.keys(this.emote_sets).length ) {
|
if ( Object.keys(this.emote_sets).length ) {
|
||||||
|
@ -146,7 +144,6 @@ export default class Emotes extends Module {
|
||||||
this.socket.on(':command:follow_sets', this.updateFollowSets, this);
|
this.socket.on(':command:follow_sets', this.updateFollowSets, this);
|
||||||
|
|
||||||
this.loadGlobalSets();
|
this.loadGlobalSets();
|
||||||
//this.loadTwitchInventory();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -755,39 +752,6 @@ export default class Emotes extends Module {
|
||||||
// Twitch Data Lookup
|
// Twitch Data Lookup
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
||||||
async loadTwitchInventory() {
|
|
||||||
const user = this.resolve('site').getUser();
|
|
||||||
if ( ! user )
|
|
||||||
return;
|
|
||||||
|
|
||||||
let data;
|
|
||||||
try {
|
|
||||||
data = await fetch('https://api.twitch.tv/v5/inventory/emoticons', {
|
|
||||||
headers: {
|
|
||||||
'Client-ID': CLIENT_ID,
|
|
||||||
'Authorization': `OAuth ${user.authToken}`
|
|
||||||
}
|
|
||||||
}).then(r => {
|
|
||||||
if ( r.ok )
|
|
||||||
return r.json();
|
|
||||||
|
|
||||||
throw r.status;
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch(err) {
|
|
||||||
this.log.error('Error loading Twitch inventory.', err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sets = this.twitch_inventory_sets = new Set(EXTRA_INVENTORY);
|
|
||||||
for(const set in data.emoticon_sets)
|
|
||||||
if ( has(data.emoticon_sets, set) )
|
|
||||||
sets.add(parseInt(set, 10));
|
|
||||||
|
|
||||||
this.log.info('Twitch Inventory Sets:', this.twitch_inventory_sets);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
setTwitchEmoteSet(emote_id, set_id) {
|
setTwitchEmoteSet(emote_id, set_id) {
|
||||||
if ( isNaN(emote_id) || ! isFinite(emote_id) )
|
if ( isNaN(emote_id) || ! isFinite(emote_id) )
|
||||||
return;
|
return;
|
||||||
|
@ -802,127 +766,201 @@ export default class Emotes extends Module {
|
||||||
this.__twitch_set_to_channel.set(set_id, channel);
|
this.__twitch_set_to_channel.set(set_id, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_getTwitchEmoteSet(emote_id) {
|
||||||
getTwitchEmoteSet(emote_id, callback) {
|
const tes = this.__twitch_emote_to_set,
|
||||||
const tes = this.__twitch_emote_to_set;
|
tsc = this.__twitch_set_to_channel;
|
||||||
|
|
||||||
if ( isNaN(emote_id) || ! isFinite(emote_id) )
|
if ( isNaN(emote_id) || ! isFinite(emote_id) )
|
||||||
return null;
|
return Promise.resolve(null);
|
||||||
|
|
||||||
if ( tes.has(emote_id) )
|
if ( tes.has(emote_id) ) {
|
||||||
return tes.get(emote_id);
|
const val = tes.get(emote_id);
|
||||||
|
if ( Array.isArray(val) )
|
||||||
|
return new Promise(s => val.push(s));
|
||||||
|
else
|
||||||
|
return Promise.resolve(val);
|
||||||
|
}
|
||||||
|
|
||||||
tes.set(emote_id, null);
|
const apollo = this.resolve('site.apollo');
|
||||||
|
if ( ! apollo?.client )
|
||||||
|
return Promise.resolve(null);
|
||||||
|
|
||||||
|
return new Promise(s => {
|
||||||
|
const promises = [s];
|
||||||
|
tes.set(emote_id, promises);
|
||||||
|
|
||||||
/*const apollo = this.resolve('site.apollo');
|
|
||||||
if ( apollo?.client ) {
|
|
||||||
timeout(apollo.client.query({
|
timeout(apollo.client.query({
|
||||||
query: GET_EMOTE,
|
query: GET_EMOTE,
|
||||||
variables: {
|
variables: {
|
||||||
id: `${emote_id}`
|
id: `${emote_id}`
|
||||||
}
|
}
|
||||||
}), 1000).then(result => {
|
}), 2000).then(data => {
|
||||||
const emote = result?.data?.emote;
|
const emote = data?.data?.emote;
|
||||||
|
let set_id = null;
|
||||||
|
|
||||||
if ( ! emote ) {
|
if ( emote ) {
|
||||||
tes.delete(emote_id);
|
set_id = parseInt(emote.setID, 10);
|
||||||
return;
|
|
||||||
|
if ( set_id && ! tsc.has(set_id) ) {
|
||||||
|
const type = determineEmoteType(emote);
|
||||||
|
|
||||||
|
tsc.set(set_id, {
|
||||||
|
id: set_id,
|
||||||
|
type,
|
||||||
|
owner: emote?.subscriptionProduct?.owner
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const set_id = parseInt(emote.setID, 10),
|
|
||||||
channel = emote?.subscriptionProduct?.owner;
|
|
||||||
|
|
||||||
this.__twitch_set_to_channel.set(set_id, {
|
|
||||||
s_id: set_id,
|
|
||||||
c_id: channel ? channel.id : null,
|
|
||||||
c_name: channel ? channel.login : null,
|
|
||||||
c_title: channel ? channel.displayName : null
|
|
||||||
});
|
|
||||||
|
|
||||||
tes.set(emote_id, set_id);
|
tes.set(emote_id, set_id);
|
||||||
if ( callback )
|
for(const fn of promises)
|
||||||
callback(set_id);
|
fn(set_id);
|
||||||
|
|
||||||
}).catch(() => tes.delete(emote_id));
|
}).catch(() => {
|
||||||
|
tes.set(emote_id, null);
|
||||||
|
for(const fn of promises)
|
||||||
|
fn(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
getTwitchEmoteSet(emote_id, callback) {
|
||||||
}*/
|
const promise = this._getTwitchEmoteSet(emote_id);
|
||||||
|
if ( callback )
|
||||||
timeout(this.socket.call('get_emote', emote_id), 1000).then(data => {
|
promise.then(callback);
|
||||||
const set_id = data['s_id'];
|
else
|
||||||
tes.set(emote_id, set_id);
|
return promise;
|
||||||
this.__twitch_set_to_channel.set(set_id, data);
|
|
||||||
|
|
||||||
if ( callback )
|
|
||||||
callback(data['s_id']);
|
|
||||||
|
|
||||||
}).catch(() => tes.delete(emote_id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async awaitTwitchSetChannel(set_id, perform_lookup = true) {
|
_getTwitchSetChannel(set_id) {
|
||||||
const tes = this.__twitch_set_to_channel,
|
const tsc = this.__twitch_set_to_channel;
|
||||||
inv = this.twitch_inventory_sets;
|
|
||||||
|
|
||||||
if ( isNaN(set_id) || ! isFinite(set_id) )
|
if ( isNaN(set_id) || ! isFinite(set_id) )
|
||||||
return null;
|
return Promise.resolve(null);
|
||||||
|
|
||||||
if ( tes.has(set_id) )
|
if ( tsc.has(set_id) ) {
|
||||||
return tes.get(set_id);
|
const val = tsc.get(set_id);
|
||||||
|
if ( Array.isArray(val) )
|
||||||
|
return new Promise(s => val.push(s));
|
||||||
|
else
|
||||||
|
return Promise.resolve(val);
|
||||||
|
}
|
||||||
|
|
||||||
if ( inv.has(set_id) )
|
const apollo = this.resolve('site.apollo');
|
||||||
return {s_id: set_id, c_id: null, c_name: 'twitch-inventory'}
|
if ( ! apollo?.client )
|
||||||
|
return Promise.resolve(null);
|
||||||
|
|
||||||
if ( ! perform_lookup )
|
return new Promise(s => {
|
||||||
return null;
|
const promises = [s];
|
||||||
|
tsc.set(set_id, promises);
|
||||||
|
|
||||||
tes.set(set_id, null);
|
timeout(apollo.client.query({
|
||||||
|
query: GET_EMOTE_SET,
|
||||||
|
variables: {
|
||||||
|
id: `${set_id}`
|
||||||
|
}
|
||||||
|
}), 2000).then(data => {
|
||||||
|
const set = data?.data?.emoteSet;
|
||||||
|
let result = null;
|
||||||
|
|
||||||
try {
|
if ( set ) {
|
||||||
const data = await timeout(this.socket.call('get_emote_set', set_id), 1000);
|
result = {
|
||||||
tes.set(set_id, data);
|
id: set_id,
|
||||||
return data;
|
type: determineSetType(set),
|
||||||
|
owner: set.owner ? {
|
||||||
|
id: set.owner.id,
|
||||||
|
login: set.owner.login,
|
||||||
|
displayName: set.owner.displayName
|
||||||
|
} : null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
} catch(err) {
|
tsc.set(set_id, result);
|
||||||
if ( err === 'No known Twitch emote set with that ID.' )
|
for(const fn of promises)
|
||||||
return null;
|
fn(result);
|
||||||
|
|
||||||
tes.delete(set_id);
|
}).catch(() => {
|
||||||
|
tsc.set(set_id, null);
|
||||||
|
for(const fn of promises)
|
||||||
|
fn(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getTwitchSetChannel(set_id, callback) {
|
||||||
|
const promise = this._getTwitchSetChannel(set_id);
|
||||||
|
if ( callback )
|
||||||
|
promise.then(callback);
|
||||||
|
else
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function determineEmoteType(emote) {
|
||||||
|
const product = emote.subscriptionProduct;
|
||||||
|
if ( product ) {
|
||||||
|
if ( product.id == 12658 )
|
||||||
|
return EmoteTypes.Prime;
|
||||||
|
else if ( product.id == 324 )
|
||||||
|
return EmoteTypes.Turbo;
|
||||||
|
|
||||||
|
// TODO: Care about Overwatch League
|
||||||
|
|
||||||
|
const owner = product.owner;
|
||||||
|
if ( owner ) {
|
||||||
|
if ( owner.id == 139075904 || product.state === 'INACTIVE' )
|
||||||
|
return EmoteTypes.LimitedTime;
|
||||||
|
|
||||||
|
return EmoteTypes.Subscription;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( emote.setID == 300238151 )
|
||||||
|
return EmoteTypes.ChannelPoints;
|
||||||
|
|
||||||
getTwitchSetChannel(set_id, callback, perform_lookup = true) {
|
return EmoteTypes.Global;
|
||||||
const tes = this.__twitch_set_to_channel,
|
}
|
||||||
inv = this.twitch_inventory_sets;
|
|
||||||
|
|
||||||
if ( isNaN(set_id) || ! isFinite(set_id) )
|
function determineSetType(set) {
|
||||||
return null;
|
const id = parseInt(set.id, 10);
|
||||||
|
|
||||||
if ( tes.has(set_id) )
|
if ( TWITCH_GLOBAL_SETS.includes(id) )
|
||||||
return tes.get(set_id);
|
return EmoteTypes.Global;
|
||||||
|
|
||||||
if ( inv.has(set_id) )
|
if ( TWITCH_POINTS_SETS.includes(id) )
|
||||||
return {s_id: set_id, c_id: null, c_name: 'twitch-inventory'}
|
return EmoteTypes.ChannelPoints;
|
||||||
|
|
||||||
if ( ! perform_lookup )
|
if ( TWITCH_PRIME_SETS.includes(id) )
|
||||||
return null;
|
return EmoteTypes.Prime;
|
||||||
|
|
||||||
tes.set(set_id, null);
|
const owner = set.owner;
|
||||||
timeout(this.socket.call('get_emote_set', set_id), 1000).then(data => {
|
if ( owner ) {
|
||||||
tes.set(set_id, data);
|
if ( owner.id == 139075904 )
|
||||||
if ( callback )
|
return EmoteTypes.LimitedTime;
|
||||||
callback(data);
|
|
||||||
|
let product;
|
||||||
}).catch(err => {
|
if ( Array.isArray(owner.subscriptionProducts) )
|
||||||
if ( err === 'No known Twitch emote set with that ID.' ) {
|
for(const prod of owner.subscriptionProducts)
|
||||||
if ( callback )
|
if ( set.id == prod.emoteSetID ) {
|
||||||
callback(null);
|
product = prod;
|
||||||
|
break;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
if ( product ) {
|
||||||
tes.delete(set_id)
|
if ( product.id == 12658 )
|
||||||
});
|
return EmoteTypes.Prime;
|
||||||
}
|
else if ( product.id == 324 )
|
||||||
|
return EmoteTypes.Turbo;
|
||||||
|
else if ( product.state === 'INACTIVE' )
|
||||||
|
return EmoteTypes.LimitedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EmoteTypes.Subscription;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EmoteTypes.Global;
|
||||||
}
|
}
|
|
@ -7,7 +7,7 @@
|
||||||
import {sanitize, createElement} from 'utilities/dom';
|
import {sanitize, createElement} from 'utilities/dom';
|
||||||
import {has, split_chars} from 'utilities/object';
|
import {has, split_chars} from 'utilities/object';
|
||||||
|
|
||||||
import {TWITCH_EMOTE_BASE, REPLACEMENT_BASE, REPLACEMENTS} from 'utilities/constants';
|
import {TWITCH_EMOTE_BASE, EmoteTypes, REPLACEMENT_BASE, REPLACEMENTS} from 'utilities/constants';
|
||||||
|
|
||||||
|
|
||||||
const EMOTE_CLASS = 'chat-image chat-line__message--emote',
|
const EMOTE_CLASS = 'chat-image chat-line__message--emote',
|
||||||
|
@ -1011,7 +1011,7 @@ export const AddonEmotes = {
|
||||||
</span>);
|
</span>);
|
||||||
},
|
},
|
||||||
|
|
||||||
tooltip(target, tip) {
|
async tooltip(target, tip) {
|
||||||
const ds = target.dataset,
|
const ds = target.dataset,
|
||||||
provider = ds.provider,
|
provider = ds.provider,
|
||||||
modifiers = ds.modifierInfo;
|
modifiers = ds.modifierInfo;
|
||||||
|
@ -1036,23 +1036,30 @@ export const AddonEmotes = {
|
||||||
|
|
||||||
if ( provider === 'twitch' ) {
|
if ( provider === 'twitch' ) {
|
||||||
emote_id = parseInt(ds.id, 10);
|
emote_id = parseInt(ds.id, 10);
|
||||||
const set_id = this.emotes.getTwitchEmoteSet(emote_id, tip.rerender),
|
const set_id = hide_source ? null : await this.emotes.getTwitchEmoteSet(emote_id),
|
||||||
emote_set = set_id != null && this.emotes.getTwitchSetChannel(set_id, tip.rerender);
|
emote_set = set_id != null && await this.emotes.getTwitchSetChannel(set_id);
|
||||||
|
|
||||||
preview = `//static-cdn.jtvnw.net/emoticons/v1/${ds.id}/3.0?_=preview`;
|
preview = `${TWITCH_EMOTE_BASE}${ds.id}/3.0?_=preview`;
|
||||||
fav_source = 'twitch';
|
fav_source = 'twitch';
|
||||||
|
|
||||||
if ( emote_set ) {
|
if ( emote_set ) {
|
||||||
source = emote_set.c_name;
|
const type = emote_set.type;
|
||||||
|
if ( type === EmoteTypes.Global )
|
||||||
if ( source === '--global--' || emote_id === 80393 )
|
|
||||||
source = this.i18n.t('emote.global', 'Twitch Global');
|
source = this.i18n.t('emote.global', 'Twitch Global');
|
||||||
|
|
||||||
else if ( source === '--twitch-turbo--' || source === 'turbo' || source === '--turbo-faces--' || source === '--prime--' || source === '--prime-faces--' )
|
else if ( type === EmoteTypes.Prime || type === EmoteTypes.Turbo )
|
||||||
source = this.i18n.t('emote.prime', 'Twitch Prime');
|
source = this.i18n.t('emote.prime', 'Twitch Prime');
|
||||||
|
|
||||||
else
|
else if ( type === EmoteTypes.LimitedTime )
|
||||||
source = this.i18n.t('tooltip.channel', 'Channel: {source}', {source});
|
source = this.i18n.t('emote.limited', 'Limited-Time Only Emote');
|
||||||
|
|
||||||
|
else if ( type === EmoteTypes.ChannelPoints )
|
||||||
|
source = this.i18n.t('emote.points', 'Channel Points Emote');
|
||||||
|
|
||||||
|
else if ( type === EmoteTypes.Subscription && emote_set.owner?.login )
|
||||||
|
source = this.i18n.t('tooltip.channel', 'Channel: {source}', {
|
||||||
|
source: emote_set.owner.displayName || emote_set.owner.login
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ( provider === 'ffz' ) {
|
} else if ( provider === 'ffz' ) {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
import {has, get, once, maybe_call, set_equals} from 'utilities/object';
|
import {has, get, once, maybe_call, set_equals} from 'utilities/object';
|
||||||
import {WEBKIT_CSS as WEBKIT, IS_OSX, KNOWN_CODES, TWITCH_EMOTE_BASE, REPLACEMENT_BASE, REPLACEMENTS} from 'utilities/constants';
|
import {TWITCH_GLOBAL_SETS, EmoteTypes, TWITCH_POINTS_SETS, TWITCH_PRIME_SETS, WEBKIT_CSS as WEBKIT, IS_OSX, KNOWN_CODES, TWITCH_EMOTE_BASE, REPLACEMENT_BASE, REPLACEMENTS} from 'utilities/constants';
|
||||||
import {ClickOutside} from 'utilities/dom';
|
import {ClickOutside} from 'utilities/dom';
|
||||||
|
|
||||||
import Twilight from 'site';
|
import Twilight from 'site';
|
||||||
|
@ -13,23 +13,6 @@ import Module from 'utilities/module';
|
||||||
|
|
||||||
import SUB_STATUS from './sub_status.gql';
|
import SUB_STATUS from './sub_status.gql';
|
||||||
|
|
||||||
const GLOBAL_SETS = [
|
|
||||||
0,
|
|
||||||
33,
|
|
||||||
42
|
|
||||||
];
|
|
||||||
|
|
||||||
const POINTS_SETS = [
|
|
||||||
300238151
|
|
||||||
];
|
|
||||||
|
|
||||||
const PRIME_SETS = [
|
|
||||||
457,
|
|
||||||
793,
|
|
||||||
19151,
|
|
||||||
19194
|
|
||||||
];
|
|
||||||
|
|
||||||
const TIERS = {
|
const TIERS = {
|
||||||
1000: 'Tier 1',
|
1000: 'Tier 1',
|
||||||
2000: 'Tier 2',
|
2000: 'Tier 2',
|
||||||
|
@ -202,6 +185,16 @@ export default class EmoteMenu extends Module {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
this.settings.add('chat.emote-menu.combine-tabs', {
|
||||||
|
default: false,
|
||||||
|
ui: {
|
||||||
|
path: 'Chat > Emote Menu >> General',
|
||||||
|
title: 'Display all emotes on one tab.',
|
||||||
|
component: 'setting-check-box'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
this.settings.add('chat.emote-menu.sort-emotes', {
|
this.settings.add('chat.emote-menu.sort-emotes', {
|
||||||
default: 0,
|
default: 0,
|
||||||
ui: {
|
ui: {
|
||||||
|
@ -263,6 +256,7 @@ export default class EmoteMenu extends Module {
|
||||||
this.chat.context.on('changed:chat.emote-menu.show-heading', fup);
|
this.chat.context.on('changed:chat.emote-menu.show-heading', fup);
|
||||||
this.chat.context.on('changed:chat.emote-menu.show-search', fup);
|
this.chat.context.on('changed:chat.emote-menu.show-search', fup);
|
||||||
this.chat.context.on('changed:chat.emote-menu.reduced-padding', fup);
|
this.chat.context.on('changed:chat.emote-menu.reduced-padding', fup);
|
||||||
|
this.chat.context.on('changed:chat.emote-menu.combine-tabs', fup);
|
||||||
|
|
||||||
this.chat.context.on('changed:chat.emoji.style', this.updateEmojiVariables, this);
|
this.chat.context.on('changed:chat.emoji.style', this.updateEmojiVariables, this);
|
||||||
|
|
||||||
|
@ -545,7 +539,7 @@ export default class EmoteMenu extends Module {
|
||||||
const data = this.props.data,
|
const data = this.props.data,
|
||||||
filtered = this.props.filtered;
|
filtered = this.props.filtered;
|
||||||
|
|
||||||
let show_heading = ! data.is_favorites && t.chat.context.get('chat.emote-menu.show-heading');
|
let show_heading = ! (data.is_favorites && ! t.chat.context.get('chat.emote-menu.combine-tabs')) && t.chat.context.get('chat.emote-menu.show-heading');
|
||||||
if ( show_heading === 2 )
|
if ( show_heading === 2 )
|
||||||
show_heading = ! filtered;
|
show_heading = ! filtered;
|
||||||
else
|
else
|
||||||
|
@ -592,6 +586,10 @@ export default class EmoteMenu extends Module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let source = data.source_i18n ? t.i18n.t(data.source_i18n, data.source) : data.source;
|
||||||
|
if ( source == null )
|
||||||
|
source = 'FrankerFaceZ';
|
||||||
|
|
||||||
return (<section ref={this.saveRef} data-key={data.key} class={filtered ? 'filtered' : ''} onMouseEnter={this.mouseEnter}>
|
return (<section ref={this.saveRef} data-key={data.key} class={filtered ? 'filtered' : ''} onMouseEnter={this.mouseEnter}>
|
||||||
{show_heading ? (<heading class="tw-pd-1 tw-border-b tw-flex tw-flex-nowrap" onClick={this.clickHeading}>
|
{show_heading ? (<heading class="tw-pd-1 tw-border-b tw-flex tw-flex-nowrap" onClick={this.clickHeading}>
|
||||||
{image}
|
{image}
|
||||||
|
@ -604,7 +602,7 @@ export default class EmoteMenu extends Module {
|
||||||
/>)}
|
/>)}
|
||||||
</div>
|
</div>
|
||||||
<div class="tw-flex-grow-1" />
|
<div class="tw-flex-grow-1" />
|
||||||
{(data.source_i18n ? t.i18n.t(data.source_i18n, data.source) : data.source) || 'FrankerFaceZ'}
|
{source}
|
||||||
{filtered ? '' : <figure class={`tw-pd-l-05 ffz-i-${collapsed ? 'left' : 'down'}-dir`} />}
|
{filtered ? '' : <figure class={`tw-pd-l-05 ffz-i-${collapsed ? 'left' : 'down'}-dir`} />}
|
||||||
</heading>) : null}
|
</heading>) : null}
|
||||||
{collapsed || this.renderBody(show_heading)}
|
{collapsed || this.renderBody(show_heading)}
|
||||||
|
@ -958,11 +956,64 @@ export default class EmoteMenu extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
clickTab(event) {
|
clickTab(event) {
|
||||||
|
const tab = event.currentTarget.dataset.tab;
|
||||||
|
if ( t.chat.context.get('chat.emote-menu.combine-tabs') ) {
|
||||||
|
let sets;
|
||||||
|
switch(tab) {
|
||||||
|
case 'fav':
|
||||||
|
sets = this.state.filtered_fav_sets;
|
||||||
|
break;
|
||||||
|
case 'channel':
|
||||||
|
sets = this.state.filtered_channel_sets;
|
||||||
|
break;
|
||||||
|
case 'emoji':
|
||||||
|
sets = this.state.filtered_emoji_sets;
|
||||||
|
break;
|
||||||
|
case 'all':
|
||||||
|
default:
|
||||||
|
sets = this.state.filtered_all_sets;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const set = sets && sets[0],
|
||||||
|
el = set && this.ref?.querySelector?.(`section[data-key="${set.key}"]`);
|
||||||
|
|
||||||
|
if ( el )
|
||||||
|
el.scrollIntoView();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
tab: event.currentTarget.dataset.tab
|
tab
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clickSettings(event) { // eslint-disable-line class-methods-use-this
|
||||||
|
const layout = t.resolve('site.layout');
|
||||||
|
if ( (layout && layout.is_minimal) || (event && (event.ctrlKey || event.shiftKey)) ) {
|
||||||
|
const win = window.open(
|
||||||
|
'https://twitch.tv/popout/frankerfacez/chat?ffz-settings',
|
||||||
|
'_blank',
|
||||||
|
'resizable=yes,scrollbars=yes,width=850,height=600'
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( win )
|
||||||
|
win.focus();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const menu = t.resolve('main_menu');
|
||||||
|
|
||||||
|
if ( menu ) {
|
||||||
|
menu.requestPage('chat.emote_menu');
|
||||||
|
if ( menu.showing )
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
t.emit('site.menu_button:clicked');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*clickRefresh(event) {
|
/*clickRefresh(event) {
|
||||||
const target = event.currentTarget,
|
const target = event.currentTarget,
|
||||||
tt = target && target._ffz_tooltip$0;
|
tt = target && target._ffz_tooltip$0;
|
||||||
|
@ -1109,6 +1160,11 @@ export default class EmoteMenu extends Module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const is_fav = emoji_favorites.includes(emoji.code),
|
||||||
|
toned = emoji.variants && emoji.variants[tone],
|
||||||
|
has_tone = toned && toned.has[style],
|
||||||
|
source = has_tone ? toned : emoji;
|
||||||
|
|
||||||
let cat = categories[emoji.category];
|
let cat = categories[emoji.category];
|
||||||
if ( ! cat ) {
|
if ( ! cat ) {
|
||||||
cat = categories[emoji.category] = [];
|
cat = categories[emoji.category] = [];
|
||||||
|
@ -1116,7 +1172,7 @@ export default class EmoteMenu extends Module {
|
||||||
sets.push({
|
sets.push({
|
||||||
key: `emoji-${emoji.category}`,
|
key: `emoji-${emoji.category}`,
|
||||||
emoji: true,
|
emoji: true,
|
||||||
image: t.emoji.getFullImage(emoji.image),
|
image: t.emoji.getFullImage(source.image),
|
||||||
i18n: `emoji.category.${emoji.category.toSnakeCase()}`,
|
i18n: `emoji.category.${emoji.category.toSnakeCase()}`,
|
||||||
title: emoji.category,
|
title: emoji.category,
|
||||||
source: 'Emoji',
|
source: 'Emoji',
|
||||||
|
@ -1125,31 +1181,26 @@ export default class EmoteMenu extends Module {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const is_fav = emoji_favorites.includes(emoji.code),
|
const em = {
|
||||||
toned = emoji.variants && emoji.variants[tone],
|
provider: 'emoji',
|
||||||
has_tone = toned && toned.has[style],
|
emoji: true,
|
||||||
source = has_tone ? toned : emoji,
|
code: emoji.code,
|
||||||
|
name: source.raw,
|
||||||
|
variant: has_tone && tone,
|
||||||
|
|
||||||
em = {
|
search: emoji.names[0],
|
||||||
provider: 'emoji',
|
|
||||||
emoji: true,
|
|
||||||
code: emoji.code,
|
|
||||||
name: source.raw,
|
|
||||||
variant: has_tone && tone,
|
|
||||||
|
|
||||||
search: emoji.names[0],
|
height: 18,
|
||||||
|
width: 18,
|
||||||
|
|
||||||
height: 18,
|
x: source.sheet_x,
|
||||||
width: 18,
|
y: source.sheet_y,
|
||||||
|
|
||||||
x: source.sheet_x,
|
favorite: is_fav,
|
||||||
y: source.sheet_y,
|
|
||||||
|
|
||||||
favorite: is_fav,
|
src: t.emoji.getFullImage(source.image),
|
||||||
|
srcSet: t.emoji.getFullImageSet(source.image)
|
||||||
src: t.emoji.getFullImage(source.image),
|
};
|
||||||
srcSet: t.emoji.getFullImageSet(source.image)
|
|
||||||
};
|
|
||||||
|
|
||||||
cat.push(em);
|
cat.push(em);
|
||||||
|
|
||||||
|
@ -1161,6 +1212,12 @@ export default class EmoteMenu extends Module {
|
||||||
|
|
||||||
state.fav_sets = [{
|
state.fav_sets = [{
|
||||||
key: 'favorites',
|
key: 'favorites',
|
||||||
|
|
||||||
|
title: 'Favorites',
|
||||||
|
i18n: 'emote-menu.favorites',
|
||||||
|
icon: 'star',
|
||||||
|
source: '',
|
||||||
|
|
||||||
is_favorites: true,
|
is_favorites: true,
|
||||||
emotes: favorites
|
emotes: favorites
|
||||||
}];
|
}];
|
||||||
|
@ -1232,7 +1289,7 @@ export default class EmoteMenu extends Module {
|
||||||
|
|
||||||
const set_id = parseInt(emote_set.id, 10),
|
const set_id = parseInt(emote_set.id, 10),
|
||||||
owner = emote_set.owner,
|
owner = emote_set.owner,
|
||||||
is_points = owner?.login === 'channel_points',
|
is_points = TWITCH_POINTS_SETS.includes(set_id) || owner?.login === 'channel_points',
|
||||||
chan = is_points ? null : owner,
|
chan = is_points ? null : owner,
|
||||||
set_data = data[set_id];
|
set_data = data[set_id];
|
||||||
|
|
||||||
|
@ -1251,27 +1308,54 @@ export default class EmoteMenu extends Module {
|
||||||
icon = 'twitch',
|
icon = 'twitch',
|
||||||
title = chan && (chan.displayName || chan.login);
|
title = chan && (chan.displayName || chan.login);
|
||||||
|
|
||||||
if ( title )
|
if ( title ) {
|
||||||
key = `twitch-${chan?.id}`;
|
key = `twitch-${chan?.id}`;
|
||||||
|
t.emotes.setTwitchSetChannel(set_id, {
|
||||||
|
id: set_id,
|
||||||
|
type: EmoteTypes.Subscription,
|
||||||
|
owner: {
|
||||||
|
id: chan.id,
|
||||||
|
login: chan.login,
|
||||||
|
displayName: chan.displayName
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
else if ( ! chan ) {
|
} else if ( ! chan ) {
|
||||||
if ( is_points || POINTS_SETS.includes(set_id) ) {
|
if ( is_points ) {
|
||||||
title = t.i18n.t('emote-menu.points', 'Unlocked with Points');
|
title = t.i18n.t('emote-menu.points', 'Unlocked with Points');
|
||||||
key = 'twitch-points';
|
key = 'twitch-points';
|
||||||
icon = 'channel-points';
|
icon = 'channel-points';
|
||||||
sort_key = 45;
|
sort_key = 45;
|
||||||
|
|
||||||
} else if ( GLOBAL_SETS.includes(set_id) ) {
|
/*t.emotes.setTwitchSetChannel(set_id, {
|
||||||
|
id: set_id,
|
||||||
|
type: EmoteTypes.ChannelPoints,
|
||||||
|
owner: null
|
||||||
|
});*/
|
||||||
|
|
||||||
|
} else if ( TWITCH_GLOBAL_SETS.includes(set_id) ) {
|
||||||
title = t.i18n.t('emote-menu.global', 'Global Emotes');
|
title = t.i18n.t('emote-menu.global', 'Global Emotes');
|
||||||
key = 'twitch-global';
|
key = 'twitch-global';
|
||||||
sort_key = 100;
|
sort_key = 100;
|
||||||
|
|
||||||
} else if ( PRIME_SETS.includes(set_id) ) {
|
t.emotes.setTwitchSetChannel(set_id, {
|
||||||
|
id: set_id,
|
||||||
|
type: EmoteTypes.Global,
|
||||||
|
owner: null
|
||||||
|
});
|
||||||
|
|
||||||
|
} else if ( TWITCH_PRIME_SETS.includes(set_id) ) {
|
||||||
title = t.i18n.t('emote_menu.prime', 'Prime');
|
title = t.i18n.t('emote_menu.prime', 'Prime');
|
||||||
key = 'twitch-prime';
|
key = 'twitch-prime';
|
||||||
icon = 'crown';
|
icon = 'crown';
|
||||||
sort_key = 75;
|
sort_key = 75;
|
||||||
|
|
||||||
|
t.emotes.setTwitchSetChannel(set_id, {
|
||||||
|
id: set_id,
|
||||||
|
type: EmoteTypes.Prime,
|
||||||
|
owner: null
|
||||||
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
title = t.i18n.t('emote-menu.misc', 'Miscellaneous');
|
title = t.i18n.t('emote-menu.misc', 'Miscellaneous');
|
||||||
key = 'twitch-misc';
|
key = 'twitch-misc';
|
||||||
|
@ -1352,7 +1436,9 @@ export default class EmoteMenu extends Module {
|
||||||
favorite: is_fav
|
favorite: is_fav
|
||||||
};
|
};
|
||||||
|
|
||||||
t.emotes.setTwitchEmoteSet(id, set_id);
|
if ( ! is_points )
|
||||||
|
t.emotes.setTwitchEmoteSet(id, set_id);
|
||||||
|
|
||||||
emotes.push(em);
|
emotes.push(em);
|
||||||
|
|
||||||
if ( is_fav && ! twitch_seen.has(id) )
|
if ( is_fav && ! twitch_seen.has(id) )
|
||||||
|
@ -1381,7 +1467,7 @@ export default class EmoteMenu extends Module {
|
||||||
locks = {},
|
locks = {},
|
||||||
section = {
|
section = {
|
||||||
sort_key: -10,
|
sort_key: -10,
|
||||||
key: `twitch-${user.id}`,
|
key: `twitch-current-channel`,
|
||||||
image: badge && badge.image1x,
|
image: badge && badge.image1x,
|
||||||
image_set: badge && `${badge.image1x} 1x, ${badge.image2x} 2x, ${badge.image4x} 4x`,
|
image_set: badge && `${badge.image1x} 1x, ${badge.image2x} 2x, ${badge.image4x} 4x`,
|
||||||
icon: 'twitch',
|
icon: 'twitch',
|
||||||
|
@ -1647,28 +1733,41 @@ export default class EmoteMenu extends Module {
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
const loading = this.state.loading || this.props.loading,
|
const loading = this.state.loading || this.props.loading,
|
||||||
padding = t.chat.context.get('chat.emote-menu.reduced-padding');
|
padding = t.chat.context.get('chat.emote-menu.reduced-padding'),
|
||||||
|
no_tabs = t.chat.context.get('chat.emote-menu.combine-tabs');
|
||||||
|
|
||||||
let tab = this.state.tab || t.chat.context.get('chat.emote-menu.default-tab'), sets;
|
let tab, sets, is_emoji;
|
||||||
if ( (tab === 'channel' && ! this.state.has_channel_tab) || (tab === 'emoji' && ! this.state.has_emoji_tab) )
|
|
||||||
tab = 'all';
|
|
||||||
|
|
||||||
const is_emoji = tab === 'emoji';
|
if ( no_tabs ) {
|
||||||
|
sets = [
|
||||||
|
this.state.filtered_fav_sets,
|
||||||
|
this.state.filtered_channel_sets,
|
||||||
|
this.state.filtered_all_sets,
|
||||||
|
this.state.filtered_emoji_sets
|
||||||
|
].flat();
|
||||||
|
|
||||||
switch(tab) {
|
} else {
|
||||||
case 'fav':
|
tab = this.state.tab || t.chat.context.get('chat.emote-menu.default-tab');
|
||||||
sets = this.state.filtered_fav_sets;
|
if ( (tab === 'channel' && ! this.state.has_channel_tab) || (tab === 'emoji' && ! this.state.has_emoji_tab) )
|
||||||
break;
|
tab = 'all';
|
||||||
case 'channel':
|
|
||||||
sets = this.state.filtered_channel_sets;
|
is_emoji = tab === 'emoji';
|
||||||
break;
|
|
||||||
case 'emoji':
|
switch(tab) {
|
||||||
sets = this.state.filtered_emoji_sets;
|
case 'fav':
|
||||||
break;
|
sets = this.state.filtered_fav_sets;
|
||||||
case 'all':
|
break;
|
||||||
default:
|
case 'channel':
|
||||||
sets = this.state.filtered_all_sets;
|
sets = this.state.filtered_channel_sets;
|
||||||
break;
|
break;
|
||||||
|
case 'emoji':
|
||||||
|
sets = this.state.filtered_emoji_sets;
|
||||||
|
break;
|
||||||
|
case 'all':
|
||||||
|
default:
|
||||||
|
sets = this.state.filtered_all_sets;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (<div
|
return (<div
|
||||||
|
@ -1684,7 +1783,7 @@ export default class EmoteMenu extends Module {
|
||||||
<div ref={this.saveScrollRef} class="simplebar-scroll-content">
|
<div ref={this.saveScrollRef} class="simplebar-scroll-content">
|
||||||
<div class="simplebar-content">
|
<div class="simplebar-content">
|
||||||
{loading && this.renderLoading()}
|
{loading && this.renderLoading()}
|
||||||
{!loading && sets && sets.map(data => createElement(
|
{!loading && sets && sets.map(data => data && createElement(
|
||||||
data.emoji ? t.EmojiSection : t.MenuSection,
|
data.emoji ? t.EmojiSection : t.MenuSection,
|
||||||
{
|
{
|
||||||
key: data.key,
|
key: data.key,
|
||||||
|
@ -1717,7 +1816,7 @@ export default class EmoteMenu extends Module {
|
||||||
onChange={this.handleFilterChange}
|
onChange={this.handleFilterChange}
|
||||||
onKeyDown={this.handleKeyDown}
|
onKeyDown={this.handleKeyDown}
|
||||||
/>
|
/>
|
||||||
{is_emoji && <t.EmojiTonePicker
|
{(no_tabs || is_emoji) && <t.EmojiTonePicker
|
||||||
tone={this.state.tone}
|
tone={this.state.tone}
|
||||||
choices={this.state.tone_emoji}
|
choices={this.state.tone_emoji}
|
||||||
pickTone={this.pickTone}
|
pickTone={this.pickTone}
|
||||||
|
@ -1781,19 +1880,19 @@ export default class EmoteMenu extends Module {
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>}
|
</div>}
|
||||||
{/*<div class="tw-flex-grow-1" />
|
<div class="tw-flex-grow-1" />
|
||||||
{!loading && (<div class="emote-picker-tab-item tw-relative">
|
<div class="emote-picker-tab-item tw-relative">
|
||||||
<button
|
<button
|
||||||
class="ffz-tooltip tw-block tw-full-width tw-interactable tw-interactable--hover-enabled tw-interactable--inverted tw-interactive"
|
class="ffz-tooltip tw-block tw-full-width tw-interactable tw-interactable--hover-enabled tw-interactable--inverted tw-interactive"
|
||||||
data-tooltip-type="html"
|
data-tooltip-type="html"
|
||||||
data-title={t.i18n.t('emote-menu.refresh', 'Refresh Data')}
|
data-title={t.i18n.t('emote-menu.settings', 'Open Settings')}
|
||||||
onClick={this.clickRefresh}
|
onClick={this.clickSettings}
|
||||||
>
|
>
|
||||||
<div class="tw-inline-flex tw-pd-x-1 tw-pd-y-05 tw-font-size-4">
|
<div class="tw-inline-flex tw-pd-x-1 tw-pd-y-05 tw-font-size-4">
|
||||||
<figure class="ffz-i-arrows-cw" />
|
<figure class="ffz-i-cog" />
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>)*/}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1861,39 +1960,6 @@ export default class EmoteMenu extends Module {
|
||||||
type: product.type,
|
type: product.type,
|
||||||
gift: node.gift?.isGift
|
gift: node.gift?.isGift
|
||||||
};
|
};
|
||||||
|
|
||||||
/*const owner = product.owner || {},
|
|
||||||
badges = owner.broadcastBadges;
|
|
||||||
|
|
||||||
let image, image_set;
|
|
||||||
if ( badges )
|
|
||||||
for(const badge of badges)
|
|
||||||
if ( badge.setID === 'subscriber' && badge.version === '0' ) {
|
|
||||||
image = badge.imageURL;
|
|
||||||
|
|
||||||
if ( image.endsWith('/1') ) {
|
|
||||||
const base = image.slice(0, -2);
|
|
||||||
image_set = `${base}/1 1x, ${base}/2 2x, ${base}/3 4x`;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
out[set_id] = {
|
|
||||||
ends: maybe_date(node.endsAt),
|
|
||||||
renews: maybe_date(node.renewsAt),
|
|
||||||
prime: node.purchasedWithPrime,
|
|
||||||
|
|
||||||
set_id: parseInt(set_id, 10),
|
|
||||||
type: product.type,
|
|
||||||
image,
|
|
||||||
image_set,
|
|
||||||
user: {
|
|
||||||
id: owner.id,
|
|
||||||
login: owner.login,
|
|
||||||
display_name: owner.displayName
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._data_sets = sets;
|
this._data_sets = sets;
|
||||||
|
|
|
@ -288,6 +288,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.reduced-padding {
|
&.reduced-padding {
|
||||||
|
.emote-picker-tab-item button > div {
|
||||||
|
padding: 0.25rem 0.5rem !important;
|
||||||
|
font-size: 1.6rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
.tw-pd-1 {
|
.tw-pd-1 {
|
||||||
padding: 0.5rem !important;
|
padding: 0.5rem !important;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
import {make_enum} from 'utilities/object';
|
||||||
|
|
||||||
|
|
||||||
export const DEBUG = localStorage.ffzDebugMode === 'true' && document.body.classList.contains('ffz-dev');
|
export const DEBUG = localStorage.ffzDebugMode === 'true' && document.body.classList.contains('ffz-dev');
|
||||||
export const SERVER = DEBUG ? '//localhost:8000' : 'https://cdn.frankerfacez.com';
|
export const SERVER = DEBUG ? '//localhost:8000' : 'https://cdn.frankerfacez.com';
|
||||||
|
|
||||||
|
@ -88,3 +91,19 @@ export const IS_WEBKIT = navigator.userAgent.indexOf('AppleWebKit/') !== -1 && n
|
||||||
export const IS_FIREFOX = navigator.userAgent.indexOf('Firefox/') !== -1;
|
export const IS_FIREFOX = navigator.userAgent.indexOf('Firefox/') !== -1;
|
||||||
|
|
||||||
export const WEBKIT_CSS = IS_WEBKIT ? '-webkit-' : '';
|
export const WEBKIT_CSS = IS_WEBKIT ? '-webkit-' : '';
|
||||||
|
|
||||||
|
|
||||||
|
export const TWITCH_GLOBAL_SETS = [0, 33, 42];
|
||||||
|
export const TWITCH_POINTS_SETS = [300238151];
|
||||||
|
export const TWITCH_PRIME_SETS = [457, 793, 19151, 19194];
|
||||||
|
|
||||||
|
export const EmoteTypes = make_enum(
|
||||||
|
'Unknown',
|
||||||
|
'Prime',
|
||||||
|
'Turbo',
|
||||||
|
'LimitedTime',
|
||||||
|
'ChannelPoints',
|
||||||
|
'Unavailable',
|
||||||
|
'Subscription',
|
||||||
|
'Global'
|
||||||
|
);
|
Loading…
Add table
Add a link
Reference in a new issue