mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-10 08:10:52 +00:00
Add favorite emotes. Add emote menu sorting. Use a CSS variable for border colors for chat lines. Use fixed emote images for the emote menu.
This commit is contained in:
parent
a01b21e9d9
commit
1841ab156c
13 changed files with 450 additions and 99 deletions
|
@ -1,3 +1,11 @@
|
||||||
|
<div class="list-header">4.0.0-beta2.3<span>@a07fb33207e6659acc9f</span> <time datetime="2018-04-09">(2018-04-09)</time></div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Added: Favorite emotes by Ctrl-Clicking them! ⌘-Click for Mac users.</li>
|
||||||
|
<li>Added: Open information pages for emotes by Shift-Clicking them.</li>
|
||||||
|
<li>Added: Sorting options for the emote menu.</li>
|
||||||
|
<li>Changed: Use cleaned up versions of certain low quality global Twitch emotes in the emote menu.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<div class="list-header">4.0.0-beta2.2<span>@201497e9898b452ba698</span> <time datetime="2018-04-08">(2018-04-08)</time></div>
|
<div class="list-header">4.0.0-beta2.2<span>@201497e9898b452ba698</span> <time datetime="2018-04-08">(2018-04-08)</time></div>
|
||||||
<ul class="chat-menu-content menu-side-padding">
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
<li>Added: Support for the old Featured Channels feature.</li>
|
<li>Added: Support for the old Featured Channels feature.</li>
|
||||||
|
|
|
@ -6,6 +6,7 @@ import Module from 'utilities/module';
|
||||||
import {DEBUG} from 'utilities/constants';
|
import {DEBUG} from 'utilities/constants';
|
||||||
|
|
||||||
import SettingsManager from './settings/index';
|
import SettingsManager from './settings/index';
|
||||||
|
//import ExperimentManager from './experiments';
|
||||||
import {TranslationManager} from './i18n';
|
import {TranslationManager} from './i18n';
|
||||||
import SocketClient from './socket';
|
import SocketClient from './socket';
|
||||||
import Site from 'site';
|
import Site from 'site';
|
||||||
|
@ -34,6 +35,7 @@ class FrankerFaceZ extends Module {
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
||||||
this.inject('settings', SettingsManager);
|
this.inject('settings', SettingsManager);
|
||||||
|
//this.inject('experiments', ExperimentManager);
|
||||||
this.inject('i18n', TranslationManager);
|
this.inject('i18n', TranslationManager);
|
||||||
this.inject('socket', SocketClient);
|
this.inject('socket', SocketClient);
|
||||||
this.inject('site', Site);
|
this.inject('site', Site);
|
||||||
|
@ -95,7 +97,7 @@ class FrankerFaceZ extends Module {
|
||||||
FrankerFaceZ.Logger = Logger;
|
FrankerFaceZ.Logger = Logger;
|
||||||
|
|
||||||
const VER = FrankerFaceZ.version_info = {
|
const VER = FrankerFaceZ.version_info = {
|
||||||
major: 4, minor: 0, revision: 0, extra: '-beta2.2',
|
major: 4, minor: 0, revision: 0, extra: '-beta2.3',
|
||||||
build: __webpack_hash__,
|
build: __webpack_hash__,
|
||||||
toString: () =>
|
toString: () =>
|
||||||
`${VER.major}.${VER.minor}.${VER.revision}${VER.extra || ''}${DEBUG ? '-dev' : ''}`
|
`${VER.major}.${VER.minor}.${VER.revision}${VER.extra || ''}${DEBUG ? '-dev' : ''}`
|
||||||
|
|
|
@ -7,8 +7,9 @@
|
||||||
import Module from 'utilities/module';
|
import Module from 'utilities/module';
|
||||||
import {ManagedStyle} from 'utilities/dom';
|
import {ManagedStyle} from 'utilities/dom';
|
||||||
import {has, timeout, SourcedSet} from 'utilities/object';
|
import {has, timeout, SourcedSet} from 'utilities/object';
|
||||||
import {CLIENT_ID, API_SERVER} from 'utilities/constants';
|
import {CLIENT_ID, API_SERVER, IS_OSX} from 'utilities/constants';
|
||||||
|
|
||||||
|
const MOD_KEY = IS_OSX ? 'metaKey' : 'ctrlKey';
|
||||||
|
|
||||||
const EXTRA_INVENTORY = [33563];
|
const EXTRA_INVENTORY = [33563];
|
||||||
|
|
||||||
|
@ -88,6 +89,10 @@ export default class Emotes extends Module {
|
||||||
component: 'setting-check-box'
|
component: 'setting-check-box'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Because this may be used elsewhere.
|
||||||
|
this.handleClick = this.handleClick.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
onEnable() {
|
onEnable() {
|
||||||
|
@ -142,6 +147,116 @@ export default class Emotes extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// Favorite Checking
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
toggleFavorite(source, id, value = null) {
|
||||||
|
const key = `favorite-emotes.${source}`,
|
||||||
|
p = this.settings.provider,
|
||||||
|
favorites = p.get(key) || [],
|
||||||
|
|
||||||
|
idx = favorites.indexOf(id);
|
||||||
|
|
||||||
|
if ( value === null )
|
||||||
|
value = idx === -1;
|
||||||
|
|
||||||
|
if ( value && idx === -1 )
|
||||||
|
favorites.push(id);
|
||||||
|
else if ( ! value && idx !== -1 )
|
||||||
|
favorites.splice(idx, 1);
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( favorites.length )
|
||||||
|
p.set(key, favorites);
|
||||||
|
else
|
||||||
|
p.delete(key);
|
||||||
|
|
||||||
|
this.emit(':change-favorite', source, id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
isFavorite(source, id) {
|
||||||
|
const favorites = this.settings.provider.get(`favorite-emotes.${source}`);
|
||||||
|
return favorites && favorites.includes(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFavorites(source) {
|
||||||
|
return this.settings.provider.get(`favorite-emotes.${source}`) || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
handleClick(event) {
|
||||||
|
const target = event.target,
|
||||||
|
ds = target && target.dataset;
|
||||||
|
|
||||||
|
if ( ! ds )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const provider = ds.provider;
|
||||||
|
|
||||||
|
if ( event[MOD_KEY] ) {
|
||||||
|
// Favoriting Emotes
|
||||||
|
let source, id;
|
||||||
|
|
||||||
|
if ( provider === 'twitch' ) {
|
||||||
|
source = 'twitch';
|
||||||
|
id = parseInt(ds.id, 10);
|
||||||
|
|
||||||
|
} else if ( provider === 'ffz' ) {
|
||||||
|
const emote_set = this.emote_sets[ds.set],
|
||||||
|
emote = emote_set && emote_set.emotes[ds.id];
|
||||||
|
|
||||||
|
if ( ! emote )
|
||||||
|
return;
|
||||||
|
|
||||||
|
source = emote_set.source || 'ffz';
|
||||||
|
id = emote.id;
|
||||||
|
|
||||||
|
} else
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.toggleFavorite(source, id);
|
||||||
|
const tt = target._ffz_tooltip$0;
|
||||||
|
if ( tt && tt.visible ) {
|
||||||
|
tt.hide();
|
||||||
|
setTimeout(() => document.contains(target) && tt.show(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( event.shiftKey && this.parent.context.get('chat.click-emotes') ) {
|
||||||
|
let url;
|
||||||
|
|
||||||
|
if ( provider === 'twitch' )
|
||||||
|
url = `https://twitchemotes.com/emotes/${ds.id}`;
|
||||||
|
|
||||||
|
else if ( provider === 'ffz' ) {
|
||||||
|
const emote_set = this.emote_sets[ds.set],
|
||||||
|
emote = emote_set && emote_set.emotes[ds.id];
|
||||||
|
|
||||||
|
if ( ! emote )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( emote.click_url )
|
||||||
|
url = emote.click_url;
|
||||||
|
|
||||||
|
else if ( ! emote_set.source )
|
||||||
|
url = `https://www.frankerfacez.com/emoticons/${emote.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( url ) {
|
||||||
|
const win = window.open();
|
||||||
|
win.opener = null;
|
||||||
|
win.location = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// Access
|
// Access
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
@ -413,7 +528,14 @@ export default class Emotes extends Module {
|
||||||
data.id = set_id;
|
data.id = set_id;
|
||||||
data.emoticons = undefined;
|
data.emoticons = undefined;
|
||||||
|
|
||||||
|
const bad_emotes = [];
|
||||||
|
|
||||||
for(const emote of ems) {
|
for(const emote of ems) {
|
||||||
|
if ( ! emote.id || ! emote.name || ! emote.urls ) {
|
||||||
|
bad_emotes.push(emote);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
emote.set_id = set_id;
|
emote.set_id = set_id;
|
||||||
emote.srcSet = `${emote.urls[1]} 1x`;
|
emote.srcSet = `${emote.urls[1]} 1x`;
|
||||||
if ( emote.urls[2] )
|
if ( emote.urls[2] )
|
||||||
|
@ -443,6 +565,9 @@ export default class Emotes extends Module {
|
||||||
new_ems[emote.id] = emote;
|
new_ems[emote.id] = emote;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( bad_emotes.length )
|
||||||
|
this.log.warn(`Bad Emote Data for Set #${set_id}`, bad_emotes);
|
||||||
|
|
||||||
data.count = count;
|
data.count = count;
|
||||||
|
|
||||||
if ( this.style && (css.length || data.css) )
|
if ( this.style && (css.length || data.css) )
|
||||||
|
|
|
@ -259,6 +259,16 @@ export default class Chat extends Module {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.settings.add('chat.click-emotes', {
|
||||||
|
default: true,
|
||||||
|
|
||||||
|
ui: {
|
||||||
|
path: 'Chat > Behavior >> General',
|
||||||
|
title: 'Open emote information pages by Shift-Clicking them.',
|
||||||
|
component: 'setting-check-box'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.context.on('changed:theme.is-dark', () => {
|
this.context.on('changed:theme.is-dark', () => {
|
||||||
for(const room of this.iterateRooms())
|
for(const room of this.iterateRooms())
|
||||||
room.buildBitsCSS();
|
room.buildBitsCSS();
|
||||||
|
|
|
@ -7,27 +7,12 @@
|
||||||
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';
|
||||||
|
|
||||||
|
|
||||||
const EMOTE_CLASS = 'chat-line__message--emote',
|
const EMOTE_CLASS = 'chat-line__message--emote',
|
||||||
LINK_REGEX = /([^\w@#%\-+=:~])?((?:(https?:\/\/)?(?:[\w@#%\-+=:~]+\.)+[a-z]{2,6}(?:\/[\w./@#%&()\-+=:?~]*)?))([^\w./@#%&()\-+=:?~]|\s|$)/g,
|
LINK_REGEX = /([^\w@#%\-+=:~])?((?:(https?:\/\/)?(?:[\w@#%\-+=:~]+\.)+[a-z]{2,6}(?:\/[\w./@#%&()\-+=:?~]*)?))([^\w./@#%&()\-+=:?~]|\s|$)/g,
|
||||||
MENTION_REGEX = /([^\w@#%\-+=:~])?(@([^\u0000-\u007F]+|\w+)+)([^\w./@#%&()\-+=:?~]|\s|$)/g,
|
MENTION_REGEX = /([^\w@#%\-+=:~])?(@([^\u0000-\u007F]+|\w+)+)([^\w./@#%&()\-+=:?~]|\s|$)/g;
|
||||||
|
|
||||||
TWITCH_BASE = '//static-cdn.jtvnw.net/emoticons/v1/',
|
|
||||||
REPLACEMENT_BASE = '//cdn.frankerfacez.com/script/replacements/',
|
|
||||||
REPLACEMENTS = {
|
|
||||||
15: '15-JKanStyle.png',
|
|
||||||
16: '16-OptimizePrime.png',
|
|
||||||
17: '17-StoneLightning.png',
|
|
||||||
18: '18-TheRinger.png',
|
|
||||||
//19: '19-PazPazowitz.png',
|
|
||||||
//20: '20-EagleEye.png',
|
|
||||||
//21: '21-CougarHunt.png',
|
|
||||||
22: '22-RedCoat.png',
|
|
||||||
26: '26-JonCarnage.png',
|
|
||||||
//27: '27-PicoMause.png',
|
|
||||||
30: '30-BCWarrior.png',
|
|
||||||
33: '33-DansGame.png',
|
|
||||||
36: '36-PJSalt.png'
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
@ -462,6 +447,7 @@ export const AddonEmotes = {
|
||||||
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-modifier-info={ml ? JSON.stringify(mods.map(x => [x.set, x.id])) : null}
|
data-modifier-info={ml ? JSON.stringify(mods.map(x => [x.set, x.id])) : null}
|
||||||
|
onClick={this.emotes.handleClick}
|
||||||
/>);
|
/>);
|
||||||
|
|
||||||
if ( ! ml )
|
if ( ! ml )
|
||||||
|
@ -472,6 +458,7 @@ export const AddonEmotes = {
|
||||||
data-provider={token.provider}
|
data-provider={token.provider}
|
||||||
data-id={token.id}
|
data-id={token.id}
|
||||||
data-set={token.set}
|
data-set={token.set}
|
||||||
|
onClick={this.emotes.handleClick}
|
||||||
>
|
>
|
||||||
{emote}
|
{emote}
|
||||||
{mods.map(t => <span key={t.text}>{this.tokenizers.emote.render(t, createElement)}</span>)}
|
{mods.map(t => <span key={t.text}>{this.tokenizers.emote.render(t, createElement)}</span>)}
|
||||||
|
@ -483,7 +470,7 @@ export const AddonEmotes = {
|
||||||
provider = ds.provider,
|
provider = ds.provider,
|
||||||
modifiers = ds.modifierInfo;
|
modifiers = ds.modifierInfo;
|
||||||
|
|
||||||
let preview, source, owner, mods;
|
let preview, source, owner, mods, fav_source, emote_id;
|
||||||
|
|
||||||
if ( modifiers && modifiers !== 'null' ) {
|
if ( modifiers && modifiers !== 'null' ) {
|
||||||
mods = JSON.parse(modifiers).map(([set_id, emote_id]) => {
|
mods = JSON.parse(modifiers).map(([set_id, emote_id]) => {
|
||||||
|
@ -499,11 +486,12 @@ export const AddonEmotes = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( provider === 'twitch' ) {
|
if ( provider === 'twitch' ) {
|
||||||
const emote_id = parseInt(ds.id, 10),
|
emote_id = parseInt(ds.id, 10);
|
||||||
set_id = this.emotes.getTwitchEmoteSet(emote_id, tip.rerender),
|
const set_id = this.emotes.getTwitchEmoteSet(emote_id, tip.rerender),
|
||||||
emote_set = set_id != null && this.emotes.getTwitchSetChannel(set_id, tip.rerender);
|
emote_set = set_id != null && this.emotes.getTwitchSetChannel(set_id, tip.rerender);
|
||||||
|
|
||||||
preview = `//static-cdn.jtvnw.net/emoticons/v1/${emote_id}/4.0?_=preview`;
|
preview = `//static-cdn.jtvnw.net/emoticons/v1/${emote_id}/4.0?_=preview`;
|
||||||
|
fav_source = 'twitch';
|
||||||
|
|
||||||
if ( emote_set ) {
|
if ( emote_set ) {
|
||||||
source = emote_set.c_name;
|
source = emote_set.c_name;
|
||||||
|
@ -525,10 +513,14 @@ export const AddonEmotes = {
|
||||||
const emote_set = this.emotes.emote_sets[ds.set],
|
const emote_set = this.emotes.emote_sets[ds.set],
|
||||||
emote = emote_set && emote_set.emotes[ds.id];
|
emote = emote_set && emote_set.emotes[ds.id];
|
||||||
|
|
||||||
if ( emote_set )
|
if ( emote_set ) {
|
||||||
source = emote_set.source_line || (`${emote_set.source || 'FFZ'} ${emote_set.title || 'Global'}`);
|
source = emote_set.source_line || (`${emote_set.source || 'FFZ'} ${emote_set.title || 'Global'}`);
|
||||||
|
fav_source = emote_set.source || 'ffz';
|
||||||
|
}
|
||||||
|
|
||||||
if ( emote ) {
|
if ( emote ) {
|
||||||
|
emote_id = emote.id;
|
||||||
|
|
||||||
if ( emote.owner )
|
if ( emote.owner )
|
||||||
owner = this.i18n.t(
|
owner = this.i18n.t(
|
||||||
'emote.owner', 'By: %{owner}',
|
'emote.owner', 'By: %{owner}',
|
||||||
|
@ -539,9 +531,12 @@ export const AddonEmotes = {
|
||||||
else if ( emote.urls[2] )
|
else if ( emote.urls[2] )
|
||||||
preview = emote.urls[2];
|
preview = emote.urls[2];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
} else
|
||||||
|
return;
|
||||||
|
|
||||||
const name = ds.name || target.alt,
|
const name = ds.name || target.alt,
|
||||||
|
favorite = fav_source && this.emotes.isFavorite(fav_source, emote_id),
|
||||||
hide_source = ds.noSource === 'true';
|
hide_source = ds.noSource === 'true';
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
@ -563,7 +558,9 @@ export const AddonEmotes = {
|
||||||
|
|
||||||
ds.sellout && (<div class="tw-mg-t-05 tw-border-t tw-pd-t-05">{ds.sellout}</div>),
|
ds.sellout && (<div class="tw-mg-t-05 tw-border-t tw-pd-t-05">{ds.sellout}</div>),
|
||||||
|
|
||||||
mods && (<div class="tw-pd-t-1">{mods}</div>)
|
mods && (<div class="tw-pd-t-1">{mods}</div>),
|
||||||
|
|
||||||
|
favorite && (<figure class="ffz--favorite ffz-i-star" />)
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -729,8 +726,8 @@ export const TwitchEmotes = {
|
||||||
srcSet = '';
|
srcSet = '';
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
src = `${TWITCH_BASE}${e_id}/1.0`;
|
src = `${TWITCH_EMOTE_BASE}${e_id}/1.0`;
|
||||||
srcSet = `${TWITCH_BASE}${e_id}/1.0 1x, ${TWITCH_BASE}${e_id}/2.0 2x`;
|
srcSet = `${TWITCH_EMOTE_BASE}${e_id}/1.0 1x, ${TWITCH_EMOTE_BASE}${e_id}/2.0 2x`;
|
||||||
}
|
}
|
||||||
|
|
||||||
out.push({
|
out.push({
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
import {has, get, once, set_equals} from 'utilities/object';
|
import {has, get, once, set_equals} from 'utilities/object';
|
||||||
import {KNOWN_CODES, TWITCH_EMOTE_BASE} from 'utilities/constants';
|
import {IS_OSX, KNOWN_CODES, TWITCH_EMOTE_BASE, REPLACEMENT_BASE, REPLACEMENTS} from 'utilities/constants';
|
||||||
|
|
||||||
import Twilight from 'site';
|
import Twilight from 'site';
|
||||||
import Module from 'utilities/module';
|
import Module from 'utilities/module';
|
||||||
|
@ -24,6 +24,34 @@ function maybe_date(val) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const EMOTE_SORTERS = [
|
||||||
|
function id_asc(a, b) {
|
||||||
|
return a.id - b.id;
|
||||||
|
},
|
||||||
|
function id_desc(a, b) {
|
||||||
|
return b.id - a.id;
|
||||||
|
},
|
||||||
|
function name_asc(a, b) {
|
||||||
|
const a_n = a.name.toLowerCase(),
|
||||||
|
b_n = b.name.toLowerCase();
|
||||||
|
|
||||||
|
if ( a_n < b_n ) return -1;
|
||||||
|
if ( a_n > b_n ) return 1;
|
||||||
|
|
||||||
|
return a.id - b.id;
|
||||||
|
},
|
||||||
|
function name_desc(a, b) {
|
||||||
|
const a_n = a.name.toLowerCase(),
|
||||||
|
b_n = b.name.toLowerCase();
|
||||||
|
|
||||||
|
if ( a_n > b_n ) return -1;
|
||||||
|
if ( a_n < b_n ) return 1;
|
||||||
|
|
||||||
|
return b.id - a.id;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
function sort_sets(a, b) {
|
function sort_sets(a, b) {
|
||||||
const a_sk = a.sort_key,
|
const a_sk = a.sort_key,
|
||||||
b_sk = b.sort_key;
|
b_sk = b.sort_key;
|
||||||
|
@ -123,13 +151,39 @@ export default class EmoteMenu extends Module {
|
||||||
title: 'Default Tab',
|
title: 'Default Tab',
|
||||||
component: 'setting-select-box',
|
component: 'setting-select-box',
|
||||||
data: [
|
data: [
|
||||||
|
{value: 'fav', title: 'Favorites'},
|
||||||
{value: 'channel', title: 'Channel'},
|
{value: 'channel', title: 'Channel'},
|
||||||
{value: 'all', title: 'All'}
|
{value: 'all', title: 'My Emotes'}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
this.settings.add('chat.emote-menu.sort-emotes', {
|
||||||
|
default: 0,
|
||||||
|
ui: {
|
||||||
|
path: 'Chat > Emote Menu >> Sorting',
|
||||||
|
title: 'Sort Emotes By',
|
||||||
|
component: 'setting-select-box',
|
||||||
|
data: [
|
||||||
|
{value: 0, title: 'Order Added (ID), Ascending'},
|
||||||
|
{value: 1, title: 'Order Added (ID), Descending'},
|
||||||
|
{value: 2, title: 'Name, Ascending'},
|
||||||
|
{value: 3, title: 'Name, Descending'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.settings.add('chat.emote-menu.sort-tiers-last', {
|
||||||
|
default: true,
|
||||||
|
ui: {
|
||||||
|
path: 'Chat > Emote Menu >> Sorting',
|
||||||
|
title: 'List emotes from higher sub tiers last.',
|
||||||
|
component: 'setting-check-box'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
this.EmoteMenu = this.fine.define(
|
this.EmoteMenu = this.fine.define(
|
||||||
'chat-emote-menu',
|
'chat-emote-menu',
|
||||||
n => n.subscriptionProductHasEmotes,
|
n => n.subscriptionProductHasEmotes,
|
||||||
|
@ -147,18 +201,24 @@ 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:change-favorite', this.maybeUpdate, this);
|
||||||
|
|
||||||
this.chat.context.on('changed:chat.emote-menu.enabled', () =>
|
this.chat.context.on('changed:chat.emote-menu.enabled', () =>
|
||||||
this.EmoteMenu.forceUpdate());
|
this.EmoteMenu.forceUpdate());
|
||||||
|
|
||||||
this.chat.context.on('changed:chat.emote-menu.show-heading', () =>
|
const fup = () => this.MenuWrapper.forceUpdate();
|
||||||
this.MenuWrapper.forceUpdate());
|
const rebuild = () => {
|
||||||
|
for(const inst of this.MenuWrapper.instances)
|
||||||
|
inst.componentWillReceiveProps(inst.props);
|
||||||
|
}
|
||||||
|
|
||||||
this.chat.context.on('changed:chat.emote-menu.show-search', () =>
|
this.chat.context.on('changed:chat.fix-bad-emotes', rebuild);
|
||||||
this.MenuWrapper.forceUpdate());
|
this.chat.context.on('changed:chat.emote-menu.sort-emotes', rebuild);
|
||||||
|
this.chat.context.on('changed:chat.emote-menu.sort-tiers-last', rebuild);
|
||||||
|
|
||||||
this.chat.context.on('changed:chat.emote-menu.reduced-padding', () =>
|
this.chat.context.on('changed:chat.emote-menu.show-heading', fup);
|
||||||
this.MenuWrapper.forceUpdate());
|
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.icon', val =>
|
this.chat.context.on('changed:chat.emote-menu.icon', val =>
|
||||||
this.css_tweaks.toggle('emote-menu', val));
|
this.css_tweaks.toggle('emote-menu', val));
|
||||||
|
@ -214,17 +274,19 @@ export default class EmoteMenu extends Module {
|
||||||
this.MenuEmote = class FFZMenuEmote extends React.Component {
|
this.MenuEmote = class FFZMenuEmote extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.handleClick = props.onClickEmote.bind(this, props.data.name)
|
this.handleClick = this.handleClick.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUpdate() {
|
handleClick(event) {
|
||||||
this.handleClick = this.props.onClickEmote.bind(this, this.props.data.name);
|
if ( ! t.emotes.handleClick(event) )
|
||||||
|
this.props.onClickEmote(this.props.data.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const data = this.props.data,
|
const data = this.props.data,
|
||||||
lock = this.props.lock,
|
lock = this.props.lock,
|
||||||
locked = this.props.locked,
|
locked = this.props.locked,
|
||||||
|
favorite = data.favorite,
|
||||||
|
|
||||||
sellout = lock ?
|
sellout = lock ?
|
||||||
this.props.all_locked ?
|
this.props.all_locked ?
|
||||||
|
@ -253,6 +315,7 @@ export default class EmoteMenu extends Module {
|
||||||
alt={data.name}
|
alt={data.name}
|
||||||
/>
|
/>
|
||||||
</figure>
|
</figure>
|
||||||
|
{favorite && (<figure class="ffz--favorite ffz-i-star" />)}
|
||||||
{locked && (<figure class="ffz-i-lock" />)}
|
{locked && (<figure class="ffz-i-lock" />)}
|
||||||
</button>);
|
</button>);
|
||||||
}
|
}
|
||||||
|
@ -307,7 +370,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 = t.chat.context.get('chat.emote-menu.show-heading');
|
let show_heading = ! data.is_favorites && 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
|
||||||
|
@ -541,6 +604,7 @@ export default class EmoteMenu extends Module {
|
||||||
|
|
||||||
state.filtered_channel_sets = this.filterSets(input, state.channel_sets);
|
state.filtered_channel_sets = this.filterSets(input, state.channel_sets);
|
||||||
state.filtered_all_sets = this.filterSets(input, state.all_sets);
|
state.filtered_all_sets = this.filterSets(input, state.all_sets);
|
||||||
|
state.filtered_fav_sets = this.filterSets(input, state.fav_sets);
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -587,7 +651,8 @@ export default class EmoteMenu extends Module {
|
||||||
|
|
||||||
data = state.set_data || {},
|
data = state.set_data || {},
|
||||||
channel = state.channel_sets = [],
|
channel = state.channel_sets = [],
|
||||||
all = state.all_sets = [];
|
all = state.all_sets = [],
|
||||||
|
favorites = state.favorites = [];
|
||||||
|
|
||||||
// If we're still loading, don't set any data.
|
// If we're still loading, don't set any data.
|
||||||
if ( props.loading || props.error || state.loading )
|
if ( props.loading || props.error || state.loading )
|
||||||
|
@ -598,10 +663,32 @@ export default class EmoteMenu extends Module {
|
||||||
if ( state.set_data && this.loadData(false, props, state) )
|
if ( state.set_data && this.loadData(false, props, state) )
|
||||||
return state;
|
return state;
|
||||||
|
|
||||||
|
// Sorters
|
||||||
|
const sorter = EMOTE_SORTERS[t.chat.context.get('chat.emote-menu.sort-emotes')],
|
||||||
|
sort_tiers = t.chat.context.get('chat.emote-menu.sort-tiers-last'),
|
||||||
|
sort_emotes = (a,b) => {
|
||||||
|
if ( a.inventory || b.inventory )
|
||||||
|
return sorter(a,b);
|
||||||
|
|
||||||
|
if ( ! a.locked && b.locked ) return -1;
|
||||||
|
if ( a.locked && ! b.locked ) return 1;
|
||||||
|
|
||||||
|
if ( sort_tiers || a.locked || b.locked ) {
|
||||||
|
if ( a.set_id < b.set_id ) return -1;
|
||||||
|
if ( a.set_id > b.set_id ) return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sorter(a,b);
|
||||||
|
}
|
||||||
|
|
||||||
// Start with the All tab. Some data calculated for
|
// Start with the All tab. Some data calculated for
|
||||||
// all is re-used for the Channel tab.
|
// all is re-used for the Channel tab.
|
||||||
|
|
||||||
const emote_sets = props.emote_data && props.emote_data.emoteSets,
|
const emote_sets = props.emote_data && props.emote_data.emoteSets,
|
||||||
|
emote_map = props.emote_data && props.emote_data.emoteMap,
|
||||||
|
twitch_favorites = t.emotes.getFavorites('twitch'),
|
||||||
|
twitch_seen_favorites = new Set,
|
||||||
|
|
||||||
inventory = t.emotes.twitch_inventory_sets || new Set,
|
inventory = t.emotes.twitch_inventory_sets || new Set,
|
||||||
grouped_sets = {},
|
grouped_sets = {},
|
||||||
set_ids = new Set;
|
set_ids = new Set;
|
||||||
|
@ -612,6 +699,7 @@ export default class EmoteMenu extends Module {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const set_id = parseInt(emote_set.id, 10),
|
const set_id = parseInt(emote_set.id, 10),
|
||||||
|
is_inventory = inventory.has(set_id),
|
||||||
set_data = data[set_id] || {},
|
set_data = data[set_id] || {},
|
||||||
more_data = t.emotes.getTwitchSetChannel(set_id),
|
more_data = t.emotes.getTwitchSetChannel(set_id),
|
||||||
image = set_data.image,
|
image = set_data.image,
|
||||||
|
@ -637,7 +725,7 @@ export default class EmoteMenu extends Module {
|
||||||
key = `twitch-${chan.id}`;
|
key = `twitch-${chan.id}`;
|
||||||
|
|
||||||
else {
|
else {
|
||||||
if ( inventory.has(set_id) ) {
|
if ( is_inventory ) {
|
||||||
title = t.i18n.t('emote-menu.inventory', 'Inventory');
|
title = t.i18n.t('emote-menu.inventory', 'Inventory');
|
||||||
key = 'twitch-inventory';
|
key = 'twitch-inventory';
|
||||||
icon = 'inventory';
|
icon = 'inventory';
|
||||||
|
@ -698,21 +786,47 @@ export default class EmoteMenu extends Module {
|
||||||
|
|
||||||
for(const emote of emote_set.emotes) {
|
for(const emote of emote_set.emotes) {
|
||||||
const id = parseInt(emote.id, 10),
|
const id = parseInt(emote.id, 10),
|
||||||
base = `${TWITCH_EMOTE_BASE}${id}`,
|
name = KNOWN_CODES[emote.token] || emote.token,
|
||||||
name = KNOWN_CODES[emote.token] || emote.token;
|
mapped = emote_map && emote_map[name],
|
||||||
|
overridden = mapped && mapped.id != id,
|
||||||
|
replacement = REPLACEMENTS[id],
|
||||||
|
is_fav = twitch_favorites.includes(id);
|
||||||
|
|
||||||
emotes.push({
|
let src, srcSet;
|
||||||
|
|
||||||
|
if ( replacement && t.chat.context.get('chat.fix-bad-emotes') )
|
||||||
|
src = `${REPLACEMENT_BASE}${replacement}`;
|
||||||
|
else {
|
||||||
|
const base = `${TWITCH_EMOTE_BASE}${id}`;
|
||||||
|
src = `${base}/1.0`;
|
||||||
|
srcSet = `${src} 1x, ${base}/2.0 2x`
|
||||||
|
}
|
||||||
|
|
||||||
|
const em = {
|
||||||
provider: 'twitch',
|
provider: 'twitch',
|
||||||
id,
|
id,
|
||||||
set_id,
|
set_id,
|
||||||
name,
|
name,
|
||||||
src: `${base}/1.0`,
|
src,
|
||||||
srcSet: `${base}/1.0 1x, ${base}/2.0 2x`
|
srcSet,
|
||||||
});
|
overridden: overridden ? parseInt(mapped.id,10) : null,
|
||||||
|
inventory: is_inventory,
|
||||||
|
favorite: is_fav
|
||||||
|
};
|
||||||
|
|
||||||
|
emotes.push(em);
|
||||||
|
if ( is_fav && ! twitch_seen_favorites.has(id) ) {
|
||||||
|
favorites.push(em);
|
||||||
|
twitch_seen_favorites.add(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( emotes.length && ! all.includes(section) )
|
if ( emotes.length ) {
|
||||||
all.push(section);
|
emotes.sort(sort_emotes);
|
||||||
|
|
||||||
|
if ( ! all.includes(section) )
|
||||||
|
all.push(section);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -772,25 +886,36 @@ export default class EmoteMenu extends Module {
|
||||||
for(const emote of product.emotes) {
|
for(const emote of product.emotes) {
|
||||||
const id = parseInt(emote.id, 10),
|
const id = parseInt(emote.id, 10),
|
||||||
base = `${TWITCH_EMOTE_BASE}${id}`,
|
base = `${TWITCH_EMOTE_BASE}${id}`,
|
||||||
name = KNOWN_CODES[emote.token] || emote.token;
|
name = KNOWN_CODES[emote.token] || emote.token,
|
||||||
|
is_fav = twitch_favorites.includes(id);
|
||||||
|
|
||||||
emotes.push({
|
const em = {
|
||||||
provider: 'twitch',
|
provider: 'twitch',
|
||||||
id,
|
id,
|
||||||
set_id,
|
set_id,
|
||||||
name,
|
name,
|
||||||
locked,
|
locked,
|
||||||
src: `${base}/1.0`,
|
src: `${base}/1.0`,
|
||||||
srcSet: `${base}/1.0 1x, ${base}/2.0 2x`
|
srcSet: `${base}/1.0 1x, ${base}/2.0 2x`,
|
||||||
});
|
favorite: is_fav
|
||||||
|
};
|
||||||
|
|
||||||
|
emotes.push(em);
|
||||||
|
|
||||||
|
if ( ! locked && is_fav && ! twitch_seen_favorites.has(id) ) {
|
||||||
|
favorites.push(em);
|
||||||
|
twitch_seen_favorites.add(id);
|
||||||
|
}
|
||||||
|
|
||||||
if ( lock_set )
|
if ( lock_set )
|
||||||
lock_set.add(id);
|
lock_set.add(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( emotes.length )
|
if ( emotes.length ) {
|
||||||
|
emotes.sort(sort_emotes);
|
||||||
channel.push(section);
|
channel.push(section);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -798,18 +923,23 @@ export default class EmoteMenu extends Module {
|
||||||
const me = t.site.getUser();
|
const me = t.site.getUser();
|
||||||
if ( me ) {
|
if ( me ) {
|
||||||
const ffz_room = t.emotes.getRoomSetsWithSources(me.id, me.login, props.channel_id, null),
|
const ffz_room = t.emotes.getRoomSetsWithSources(me.id, me.login, props.channel_id, null),
|
||||||
ffz_global = t.emotes.getGlobalSetsWithSources(me.id, me.login);
|
ffz_global = t.emotes.getGlobalSetsWithSources(me.id, me.login),
|
||||||
|
seen_favorites = {};
|
||||||
|
|
||||||
for(const [emote_set, provider] of ffz_room) {
|
for(const [emote_set, provider] of ffz_room) {
|
||||||
const section = this.processFFZSet(emote_set, provider);
|
const section = this.processFFZSet(emote_set, provider, favorites, seen_favorites);
|
||||||
if ( section )
|
if ( section ) {
|
||||||
|
section.emotes.sort(sort_emotes);
|
||||||
channel.push(section);
|
channel.push(section);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const [emote_set, provider] of ffz_global) {
|
for(const [emote_set, provider] of ffz_global) {
|
||||||
const section = this.processFFZSet(emote_set, provider);
|
const section = this.processFFZSet(emote_set, provider, favorites, seen_favorites);
|
||||||
if ( section )
|
if ( section ) {
|
||||||
|
section.emotes.sort(sort_emotes);
|
||||||
all.push(section);
|
all.push(section);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -820,14 +950,27 @@ export default class EmoteMenu extends Module {
|
||||||
|
|
||||||
state.has_channel_tab = channel.length > 0;
|
state.has_channel_tab = channel.length > 0;
|
||||||
|
|
||||||
|
state.fav_sets = [{
|
||||||
|
key: 'favorites',
|
||||||
|
is_favorites: true,
|
||||||
|
emotes: favorites
|
||||||
|
}];
|
||||||
|
|
||||||
|
// We use this sorter because we don't want things grouped by sets.
|
||||||
|
favorites.sort(sorter);
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
processFFZSet(emote_set, provider) { // eslint-disable-line class-methods-use-this
|
processFFZSet(emote_set, provider, favorites, seen_favorites) { // eslint-disable-line class-methods-use-this
|
||||||
if ( ! emote_set || ! emote_set.emotes )
|
if ( ! emote_set || ! emote_set.emotes )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
const fav_key = emote_set.source || 'ffz',
|
||||||
|
known_favs = t.emotes.getFavorites(fav_key),
|
||||||
|
seen_favs = seen_favorites[fav_key] = seen_favorites[fav_key] || new Set;
|
||||||
|
|
||||||
const pdata = t.emotes.providers.get(provider),
|
const pdata = t.emotes.providers.get(provider),
|
||||||
source = pdata && pdata.name ?
|
source = pdata && pdata.name ?
|
||||||
(pdata.i18n_key ?
|
(pdata.i18n_key ?
|
||||||
|
@ -856,16 +999,22 @@ export default class EmoteMenu extends Module {
|
||||||
|
|
||||||
for(const emote of Object.values(emote_set.emotes))
|
for(const emote of Object.values(emote_set.emotes))
|
||||||
if ( ! emote.hidden ) {
|
if ( ! emote.hidden ) {
|
||||||
const em = {
|
const is_fav = known_favs.includes(emote.id),
|
||||||
provider: 'ffz',
|
em = {
|
||||||
id: emote.id,
|
provider: 'ffz',
|
||||||
set_id: emote_set.id,
|
id: emote.id,
|
||||||
src: emote.urls[1],
|
set_id: emote_set.id,
|
||||||
srcSet: emote.srcSet,
|
src: emote.urls[1],
|
||||||
name: emote.name
|
srcSet: emote.srcSet,
|
||||||
};
|
name: emote.name,
|
||||||
|
favorite: is_fav
|
||||||
|
};
|
||||||
|
|
||||||
emotes.push(em);
|
emotes.push(em);
|
||||||
|
if ( is_fav && ! seen_favs.has(emote.id) ) {
|
||||||
|
favorites.push(em);
|
||||||
|
seen_favs.add(emote.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( emotes.length )
|
if ( emotes.length )
|
||||||
|
@ -886,7 +1035,7 @@ export default class EmoteMenu extends Module {
|
||||||
renderError() {
|
renderError() {
|
||||||
return (<div class="tw-align-center tw-pd-1">
|
return (<div class="tw-align-center tw-pd-1">
|
||||||
<div class="tw-mg-b-1">
|
<div class="tw-mg-b-1">
|
||||||
<div class="tw-mg-5">
|
<div class="tw-mg-2">
|
||||||
<img
|
<img
|
||||||
src="//cdn.frankerfacez.com/emoticon/26608/2"
|
src="//cdn.frankerfacez.com/emoticon/26608/2"
|
||||||
srcSet="//cdn.frankerfacez.com/emoticon/26608/2 1x, //cdn.frankerfacez.com/emoticon/26608/4 2x"
|
srcSet="//cdn.frankerfacez.com/emoticon/26608/2 1x, //cdn.frankerfacez.com/emoticon/26608/4 2x"
|
||||||
|
@ -904,7 +1053,7 @@ export default class EmoteMenu extends Module {
|
||||||
|
|
||||||
renderEmpty() { // eslint-disable-line class-methods-use-this
|
renderEmpty() { // eslint-disable-line class-methods-use-this
|
||||||
return (<div class="tw-align-center tw-pd-1">
|
return (<div class="tw-align-center tw-pd-1">
|
||||||
<div class="tw-mg-5">
|
<div class="tw-mg-2">
|
||||||
<img
|
<img
|
||||||
src="//cdn.frankerfacez.com/emoticon/26608/2"
|
src="//cdn.frankerfacez.com/emoticon/26608/2"
|
||||||
srcSet="//cdn.frankerfacez.com/emoticon/26608/2 1x, //cdn.frankerfacez.com/emoticon/26608/4 2x"
|
srcSet="//cdn.frankerfacez.com/emoticon/26608/2 1x, //cdn.frankerfacez.com/emoticon/26608/4 2x"
|
||||||
|
@ -912,7 +1061,9 @@ export default class EmoteMenu extends Module {
|
||||||
</div>
|
</div>
|
||||||
{this.state.filtered ?
|
{this.state.filtered ?
|
||||||
t.i18n.t('emote-menu.empty-search', 'There are no matching emotes.') :
|
t.i18n.t('emote-menu.empty-search', 'There are no matching emotes.') :
|
||||||
t.i18n.t('emote-menu.empty', "There's nothing here.")}
|
this.state.tab === 'fav' ?
|
||||||
|
t.i18n.t('emote-menu.empty-favs', "You don't have any favorite emotes. To favorite an emote, find it and %{hotkey}-Click it.", {hotkey: IS_OSX ? '⌘' : 'Ctrl'}) :
|
||||||
|
t.i18n.t('emote-menu.empty', "There's nothing here.")}
|
||||||
</div>)
|
</div>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -935,6 +1086,9 @@ export default class EmoteMenu extends Module {
|
||||||
tab = 'all';
|
tab = 'all';
|
||||||
|
|
||||||
switch(tab) {
|
switch(tab) {
|
||||||
|
case 'fav':
|
||||||
|
sets = this.state.filtered_fav_sets;
|
||||||
|
break;
|
||||||
case 'channel':
|
case 'channel':
|
||||||
sets = this.state.filtered_channel_sets;
|
sets = this.state.filtered_channel_sets;
|
||||||
break;
|
break;
|
||||||
|
@ -984,9 +1138,16 @@ export default class EmoteMenu extends Module {
|
||||||
</div>
|
</div>
|
||||||
</div>)}
|
</div>)}
|
||||||
<div class="emote-picker__tabs-container tw-flex tw-border-t tw-c-background">
|
<div class="emote-picker__tabs-container tw-flex tw-border-t tw-c-background">
|
||||||
{null && (<div class="ffz-tooltip emote-picker__tab tw-pd-x-1" data-tooltip-type="html" data-title="Favorites">
|
<div
|
||||||
|
class={`ffz-tooltip emote-picker__tab tw-pd-x-1${tab === 'fav' ? ' emote-picker__tab--active' : ''}`}
|
||||||
|
id="emote-picker__fav"
|
||||||
|
data-tab="fav"
|
||||||
|
data-tooltip-type="html"
|
||||||
|
data-title={t.i18n.t('emote-menu.favorites', 'Favorites')}
|
||||||
|
onClick={this.clickTab}
|
||||||
|
>
|
||||||
<figure class="ffz-i-star" />
|
<figure class="ffz-i-star" />
|
||||||
</div>)}
|
</div>
|
||||||
{this.state.has_channel_tab && <div
|
{this.state.has_channel_tab && <div
|
||||||
class={`emote-picker__tab tw-pd-x-1${tab === 'channel' ? ' emote-picker__tab--active' : ''}`}
|
class={`emote-picker__tab tw-pd-x-1${tab === 'channel' ? ' emote-picker__tab--active' : ''}`}
|
||||||
id="emote-picker__channel"
|
id="emote-picker__channel"
|
||||||
|
@ -1001,7 +1162,7 @@ export default class EmoteMenu extends Module {
|
||||||
data-tab="all"
|
data-tab="all"
|
||||||
onClick={this.clickTab}
|
onClick={this.clickTab}
|
||||||
>
|
>
|
||||||
{t.i18n.t('emote-menu.all', 'All')}
|
{t.i18n.t('emote-menu.my-emotes', 'My Emotes')}
|
||||||
</div>
|
</div>
|
||||||
<div class="tw-flex-grow-1" />
|
<div class="tw-flex-grow-1" />
|
||||||
{!loading && (<div
|
{!loading && (<div
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
padding-top: calc(.5rem - 1px) !important;
|
padding-top: calc(.5rem - 1px) !important;
|
||||||
|
|
||||||
border-top: 1px solid #aaa;
|
border-top: 1px solid #aaa;
|
||||||
border-bottom-color: rgba(255,255,255,0.5);
|
border-bottom-color: var rgba(255,255,255,0.5);
|
||||||
|
|
||||||
.tw-theme--dark & {
|
.tw-theme--dark & {
|
||||||
border-top-color: #000;
|
border-top-color: #000;
|
||||||
|
|
|
@ -8,12 +8,7 @@
|
||||||
.chat-line__raid,
|
.chat-line__raid,
|
||||||
.chat-line__subscribe {
|
.chat-line__subscribe {
|
||||||
padding-top: calc(.5rem - 1px) !important;
|
padding-top: calc(.5rem - 1px) !important;
|
||||||
|
border-top: 1px solid var(--ffz-border-color);
|
||||||
border-top: 1px solid #dad8de;
|
|
||||||
|
|
||||||
.tw-theme--dark & {
|
|
||||||
border-top-color: #2c2541;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
border-top-color: transparent !important;
|
border-top-color: transparent !important;
|
||||||
|
|
|
@ -8,12 +8,7 @@
|
||||||
.chat-line__raid,
|
.chat-line__raid,
|
||||||
.chat-line__subscribe {
|
.chat-line__subscribe {
|
||||||
padding-bottom: calc(.5rem - 1px) !important;
|
padding-bottom: calc(.5rem - 1px) !important;
|
||||||
|
border-bottom: 1px solid var(--ffz-border-color);
|
||||||
border-bottom: 1px solid #dad8de;
|
|
||||||
|
|
||||||
.tw-theme--dark & {
|
|
||||||
border-bottom-color: #2c2541;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child:nth-child(odd) {
|
&:last-child:nth-child(odd) {
|
||||||
border-bottom-color: transparent !important;
|
border-bottom-color: transparent !important;
|
||||||
|
|
|
@ -13,9 +13,11 @@ import THEME_CSS_URL from 'site/styles/theme.scss';
|
||||||
export default class ThemeEngine extends Module {
|
export default class ThemeEngine extends Module {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
super(...args);
|
super(...args);
|
||||||
this.inject('site');
|
|
||||||
this.inject('settings');
|
this.inject('settings');
|
||||||
|
|
||||||
|
this.inject('site');
|
||||||
|
this.inject('site.css_tweaks');
|
||||||
|
|
||||||
this.should_enable = true;
|
this.should_enable = true;
|
||||||
|
|
||||||
this.settings.add('theme.dark', {
|
this.settings.add('theme.dark', {
|
||||||
|
@ -40,7 +42,7 @@ export default class ThemeEngine extends Module {
|
||||||
process(ctx) {
|
process(ctx) {
|
||||||
return ctx.get('context.ui.theme') === 1;
|
return ctx.get('context.ui.theme') === 1;
|
||||||
},
|
},
|
||||||
changed: val => document.body.classList.toggle('tw-theme--dark', val)
|
changed: () => this.updateCSS()
|
||||||
});
|
});
|
||||||
|
|
||||||
this.settings.add('theme.tooltips-dark', {
|
this.settings.add('theme.tooltips-dark', {
|
||||||
|
@ -53,6 +55,18 @@ export default class ThemeEngine extends Module {
|
||||||
this._style = null;
|
this._style = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
updateCSS() {
|
||||||
|
const dark = this.settings.get('theme.is-dark'),
|
||||||
|
gray = this.settings.get('theme.dark');
|
||||||
|
|
||||||
|
document.body.classList.toggle('tw-theme--dark', dark);
|
||||||
|
document.body.classList.toggle('tw-theme--ffz', gray);
|
||||||
|
|
||||||
|
this.css_tweaks.setVariable('border-color', dark ? (gray ? '#2a2a2a' : '#2c2541') : '#dad8de');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
toggleStyle(enable) {
|
toggleStyle(enable) {
|
||||||
if ( ! this._style ) {
|
if ( ! this._style ) {
|
||||||
if ( ! enable )
|
if ( ! enable )
|
||||||
|
@ -74,11 +88,10 @@ export default class ThemeEngine extends Module {
|
||||||
|
|
||||||
updateSetting(enable) {
|
updateSetting(enable) {
|
||||||
this.toggleStyle(enable);
|
this.toggleStyle(enable);
|
||||||
document.body.classList.toggle('tw-theme--ffz', enable);
|
this.updateCSS();
|
||||||
}
|
}
|
||||||
|
|
||||||
onEnable() {
|
onEnable() {
|
||||||
this.updateSetting(this.settings.get('theme.dark'));
|
this.updateSetting(this.settings.get('theme.dark'));
|
||||||
document.body.classList.toggle('tw-theme--dark', this.settings.get('theme.is-dark'));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import {has} from 'utilities/object';
|
|
||||||
|
|
||||||
export function hue2rgb(p, q, t) {
|
export function hue2rgb(p, q, t) {
|
||||||
if ( t < 0 ) t += 1;
|
if ( t < 0 ) t += 1;
|
||||||
if ( t > 1 ) t -= 1;
|
if ( t > 1 ) t -= 1;
|
||||||
|
@ -565,7 +563,7 @@ export class ColorAdjuster {
|
||||||
|
|
||||||
|
|
||||||
rebuildContrast() {
|
rebuildContrast() {
|
||||||
this._cache = {};
|
this._cache = new Map;
|
||||||
|
|
||||||
const base = RGBAColor.fromCSS(this._base),
|
const base = RGBAColor.fromCSS(this._base),
|
||||||
lum = base.luminance();
|
lum = base.luminance();
|
||||||
|
@ -606,8 +604,8 @@ export class ColorAdjuster {
|
||||||
if ( ! color )
|
if ( ! color )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
if ( has(this._cache, color) )
|
if ( this._cache.has(color) )
|
||||||
return this._cache[color];
|
return this._cache.get(color);
|
||||||
|
|
||||||
let rgb = RGBAColor.fromCSS(color);
|
let rgb = RGBAColor.fromCSS(color);
|
||||||
|
|
||||||
|
@ -650,7 +648,8 @@ export class ColorAdjuster {
|
||||||
rgb = rgb.brighten(-1);
|
rgb = rgb.brighten(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const out = this._cache[color] = rgb.toHex();
|
const out = rgb.toHex();
|
||||||
|
this._cache.set(color, out);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -33,6 +33,25 @@ export const KNOWN_CODES = {
|
||||||
'Gr(a|e)yFace': 'GrayFace'
|
'Gr(a|e)yFace': 'GrayFace'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const REPLACEMENT_BASE = '//cdn.frankerfacez.com/script/replacements/';
|
||||||
|
|
||||||
|
export const REPLACEMENTS = {
|
||||||
|
15: '15-JKanStyle.png',
|
||||||
|
16: '16-OptimizePrime.png',
|
||||||
|
17: '17-StoneLightning.png',
|
||||||
|
18: '18-TheRinger.png',
|
||||||
|
//19: '19-PazPazowitz.png',
|
||||||
|
//20: '20-EagleEye.png',
|
||||||
|
//21: '21-CougarHunt.png',
|
||||||
|
22: '22-RedCoat.png',
|
||||||
|
26: '26-JonCarnage.png',
|
||||||
|
//27: '27-PicoMause.png',
|
||||||
|
30: '30-BCWarrior.png',
|
||||||
|
33: '33-DansGame.png',
|
||||||
|
36: '36-PJSalt.png'
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export const WS_CLUSTERS = {
|
export const WS_CLUSTERS = {
|
||||||
Production: [
|
Production: [
|
||||||
['wss://catbag.frankerfacez.com/', 0.25],
|
['wss://catbag.frankerfacez.com/', 0.25],
|
||||||
|
|
|
@ -29,6 +29,33 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ffz-i-lock {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0; right: 0;
|
||||||
|
border-radius: .2rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
background-color: rgba(0,0,0,0.5);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ffz--favorite {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0; right: 0;
|
||||||
|
|
||||||
|
font-size: 1rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
color: gold;
|
||||||
|
|
||||||
|
background-color: rgba(0,0,0,0.5);
|
||||||
|
|
||||||
|
.ffz__tooltip & {
|
||||||
|
bottom: .1rem; right: .1rem;
|
||||||
|
padding: .2rem 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.modified-emote {
|
.modified-emote {
|
||||||
~ .chat-line__message--emote {
|
~ .chat-line__message--emote {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue