1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-10-18 08:52:00 +00:00

Add Emoji Rendering. Add basic emoji tab-completion. The emote menu needs a re-think for performance. Strip out more Apollo bugs. Fix tooltips being silly.

This commit is contained in:
SirStendec 2018-04-12 20:30:00 -04:00
parent 1b2ff27530
commit 9c95335743
14 changed files with 504 additions and 28 deletions

View file

@ -77,6 +77,7 @@ export default class EmoteMenu extends Module {
this.inject('chat');
this.inject('chat.badges');
this.inject('chat.emotes');
this.inject('chat.emoji');
this.inject('site');
this.inject('site.fine');
@ -201,7 +202,8 @@ export default class EmoteMenu extends Module {
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-room-sets', this.maybeUpdate, this);
this.on('chat.emotes:change-favorite', this.maybeUpdate, this);
this.on('chat.emotes:change-favorite', this.updateFavorite, this);
this.on('chat.emoji:populated', this.updateEmoji, this);
this.chat.context.on('changed:chat.emote-menu.enabled', () =>
this.EmoteMenu.forceUpdate());
@ -264,6 +266,14 @@ export default class EmoteMenu extends Module {
this.EmoteMenu.forceUpdate();
}
updateFavorite() {
this.maybeUpdate();
}
updateEmoji() {
this.maybeUpdate();
}
defineClasses() {
const t = this,
@ -300,6 +310,8 @@ export default class EmoteMenu extends Module {
data-provider={data.provider}
data-id={data.id}
data-set={data.set_id}
data-code={data.code}
data-variant={data.variant}
data-no-source={this.props.source}
data-name={data.name}
aria-label={data.name}
@ -309,7 +321,7 @@ export default class EmoteMenu extends Module {
>
<figure class="emote-picker__emote-figure">
<img
class="emote-picker__emote-image"
class={`emote-picker__emote-image${data.emoji ? ' ffz-emoji' : ''}`}
src={data.src}
srcSet={data.srcSet}
alt={data.name}
@ -331,7 +343,7 @@ export default class EmoteMenu extends Module {
super(props);
const collapsed = storage.get('emote-menu.collapsed') || [];
this.state = {collapsed: props.data && collapsed.includes(props.data.key)}
this.state = {collapsed: props.data && (collapsed.includes(props.data.key) !== props.data.collapsed)}
this.clickHeading = this.clickHeading.bind(this);
this.onMouseEnter = this.onMouseEnter.bind(this);
@ -344,11 +356,14 @@ export default class EmoteMenu extends Module {
const collapsed = storage.get('emote-menu.collapsed') || [],
key = this.props.data.key,
idx = collapsed.indexOf(key),
val = ! this.state.collapsed;
idx = collapsed.indexOf(key);
let val = ! this.state.collapsed;
this.setState({collapsed: val});
if ( this.props.data.collapsed )
val = ! val;
if ( val && idx === -1 )
collapsed.push(key);
else if ( ! val && idx !== -1 )
@ -607,6 +622,7 @@ export default class EmoteMenu extends Module {
state.filtered_channel_sets = this.filterSets(input, state.channel_sets);
state.filtered_all_sets = this.filterSets(input, state.all_sets);
state.filtered_fav_sets = this.filterSets(input, state.fav_sets);
state.filtered_emoji_sets = this.filterSets(input, state.emoji_sets);
return state;
}
@ -633,7 +649,8 @@ export default class EmoteMenu extends Module {
if ( ! filter || ! filter.length )
return true;
const emote_lower = emote.name.toLowerCase(),
const emote_name = emote.search || emote.name,
emote_lower = emote_name.toLowerCase(),
term_lower = filter.toLowerCase();
if ( ! filter.startsWith(':') )
@ -642,12 +659,74 @@ export default class EmoteMenu extends Module {
if ( emote_lower.startsWith(term_lower.slice(1)) )
return true;
const idx = emote.name.indexOf(filter.charAt(1).toUpperCase());
const idx = emote_name.indexOf(filter.charAt(1).toUpperCase());
if ( idx !== -1 )
return emote_lower.slice(idx+1).startsWith(term_lower.slice(2));
}
buildEmoji(old_state) { // eslint-disable-line class-methods-use-this
return old_state;
/*const state = Object.assign({}, old_state),
sets = state.emoji_sets = [],
emoji_favorites = t.emotes.getFavorites('emoji'),
style = t.chat.context.get('chat.emoji.style') || 'twitter',
favorites = state.favorites = state.favorites || [],
categories = {};
for(const emoji of Object.values(t.emoji.emoji)) {
if ( ! emoji.has[style] )
continue;
let cat = categories[emoji.category];
if ( ! cat ) {
cat = categories[emoji.category] = [];
sets.push({
key: `emoji-${emoji.category}`,
collapsed: true,
image: t.emoji.getFullImage(emoji.image),
title: emoji.category,
source: t.i18n.t('emote-menu.emoji', 'Emoji'),
emotes: cat
});
}
const is_fav = emoji_favorites.includes(emoji.code),
em = {
provider: 'emoji',
emoji: true,
code: emoji.code,
name: emoji.raw,
search: emoji.names[0],
height: 18,
width: 18,
x: emoji.sheet_x,
y: emoji.sheet_y,
favorite: is_fav,
src: t.emoji.getFullImage(emoji.image),
srcSet: t.emoji.getFullImageSet(emoji.image)
};
cat.push(em);
if ( is_fav )
favorites.push(em);
}
state.has_emoji_tab = sets.length > 0;
return state;*/
}
buildState(props, old_state) {
const state = Object.assign({}, old_state),
@ -961,7 +1040,7 @@ export default class EmoteMenu extends Module {
// We use this sorter because we don't want things grouped by sets.
favorites.sort(sorter);
return state;
return this.buildEmoji(state);
}
@ -1086,7 +1165,7 @@ export default class EmoteMenu extends Module {
padding = t.chat.context.get('chat.emote-menu.reduced-padding');
let tab = this.state.tab || t.chat.context.get('chat.emote-menu.default-tab'), sets;
if ( tab === 'channel' && ! this.state.has_channel_tab )
if ( (tab === 'channel' && ! this.state.has_channel_tab) || (tab === 'emoji' && ! this.state.has_emoji_tab) )
tab = 'all';
switch(tab) {
@ -1096,6 +1175,9 @@ export default class EmoteMenu extends Module {
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;
@ -1168,6 +1250,14 @@ export default class EmoteMenu extends Module {
>
{t.i18n.t('emote-menu.my-emotes', 'My Emotes')}
</div>
{this.state.has_emoji_tab && <div
class={`emote-picker__tab tw-pd-x-1${tab === 'emoji' ? ' emote-picker__tab--active' : ''}`}
id="emote-picker__emoji"
data-tab="emoji"
onClick={this.clickTab}
>
{t.i18n.t('emote-menu.emoji', 'Emoji')}
</div>}
<div class="tw-flex-grow-1" />
{!loading && (<div
class="ffz-tooltip emote-picker__tab tw-pd-x-1 tw-mg-r-0"

View file

@ -41,6 +41,7 @@ export default class ChatLine extends Module {
}
onEnable() {
this.chat.context.on('changed:chat.emoji.style', this.updateLines, this);
this.chat.context.on('changed:chat.bits.stack', this.updateLines, this);
this.chat.context.on('changed:chat.badges.style', this.updateLines, this);
this.chat.context.on('changed:chat.badges.hidden', this.updateLines, this);

View file

@ -13,6 +13,7 @@ export default class TabCompletion extends Module {
this.inject('chat');
this.inject('chat.emotes');
this.inject('chat.emoji');
this.inject('i18n');
this.inject('settings');
@ -31,6 +32,15 @@ export default class TabCompletion extends Module {
}
});
this.settings.add('chat.tab-complete.emoji', {
default: true,
ui: {
path: 'Chat > Input >> Tab Completion',
title: 'Allow tab-completion of emoji.',
component: 'setting-check-box'
}
});
// Components
@ -103,12 +113,59 @@ export default class TabCompletion extends Module {
}
inst.getMatchedEmotes = function(input) {
const results = old_get_matched.call(this, input);
if ( ! t.chat.context.get('chat.tab-complete.ffz-emotes') )
let results = old_get_matched.call(this, input);
if ( t.chat.context.get('chat.tab-complete.ffz-emotes') )
results = results.concat(t.getEmoteSuggestions(input, this));
if ( ! t.chat.context.get('chat.tab-complete.emoji') )
return results;
return results.concat(t.getEmoteSuggestions(input, this));
return results.concat(t.getEmojiSuggestions(input, this));
}
const React = this.web_munch.getModule('react'),
createElement = React && React.createElement;
inst.renderFFZEmojiSuggestion = function(data) {
return [
<div class="tw-pd-r-05">
<img
class="emote-autocomplete-provider__image ffz-emoji"
src={data.src}
srcSet={data.srcset}
/>
</div>,
<div>
{data.token}
</div>
]
}
}
getEmojiSuggestions(input, inst) {
const search = input.slice(1).toLowerCase(),
style = this.chat.context.get('chat.emoji.style'),
results = [];
for(const name in this.emoji.names)
if ( name.startsWith(search) ) {
const emoji = this.emoji.emoji[this.emoji.names[name]];
if ( emoji && (style === 0 || emoji.has[style]) )
results.push({
current: input,
replacement: emoji.raw,
element: inst.renderFFZEmojiSuggestion({
token: `:${name}:`,
id: `emoji-${emoji.code}`,
src: this.emoji.getFullImage(emoji.image, style),
srcSet: this.emoji.getFullImageSet(emoji.image, style)
})
});
}
return results;
}

View file

@ -40,6 +40,22 @@
}
}
.ffz-emoji {
width: calc(var(--ffz-chat-font-size) * 1.5);
height: calc(var(--ffz-chat-font-size) * 1.5);
&.preview-image {
width: 7.2rem;
height: 7.2rem;
}
&.emote-autocomplete-provider__image {
width: 1.8rem;
height: 1.8rem;
margin: .5rem;
}
}
.ffz--emote-picker {
section:not(.filtered) heading {
@ -90,6 +106,10 @@
pointer-events: none;
}
.emote-picker__emote-image {
max-height: 3.2rem
}
section:last-of-type {
& > div:last-child,
& > heading:last-child {