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:
parent
1b2ff27530
commit
9c95335743
14 changed files with 504 additions and 28 deletions
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue