1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-09-16 01:56:55 +00:00

Fixes and improves tab-completion hook (#613)

* Fixes tab-completion for emotes added by FFZ

* Implement tab-completion of emotes without the need of the colon

If enabled this will prevent username tab-completion without the @ in-front of the name, but will allow emotes to be tab-completed without the need of the colon in-front

* Implement result limit of 25 with setting
This commit is contained in:
Lordmau5 2019-06-28 04:54:58 +02:00 committed by Mike
parent f1c527b721
commit 3120d586d2

View file

@ -42,12 +42,31 @@ export default class Input extends Module {
} }
}); });
this.settings.add('chat.tab-complete.emotes-without-colon', {
default: false,
ui: {
path: 'Chat > Input >> Tab Completion',
title: 'Allow tab-completion of emotes without typing a colon. (:)',
description: 'This will prevent the tab-completion of usernames without the @ prefix.',
component: 'setting-check-box'
}
});
this.settings.add('chat.tab-complete.limit-results', {
default: true,
ui: {
path: 'Chat > Input >> Tab Completion',
title: 'Limit tab-completion results to 25.',
component: 'setting-check-box'
}
});
// Components // Components
this.ChatInput = this.fine.define( this.ChatInput = this.fine.define(
'chat-input', 'chat-input',
n => n && n.setChatInputRef && n.setAutocompleteInputRef, n => n && n.setChatInputRef && n.setLocalAutocompleteInputRef,
Twilight.CHAT_ROUTES Twilight.CHAT_ROUTES
); );
@ -57,10 +76,24 @@ export default class Input extends Module {
n => n && n.getMatchedEmotes, n => n && n.getMatchedEmotes,
Twilight.CHAT_ROUTES Twilight.CHAT_ROUTES
); );
this.MentionSuggestions = this.fine.define(
'tab-mention-suggestions',
n => n && n.getMentions && n.renderMention,
Twilight.CHAT_ROUTES
);
} }
async onEnable() { async onEnable() {
this.chat.context.on('changed:chat.actions.room', () => this.ChatInput.forceUpdate()); this.chat.context.on('changed:chat.actions.room', () => this.ChatInput.forceUpdate());
this.chat.context.on('changed:chat.tab-complete.emotes-without-colon', enabled => {
for (const inst of this.EmoteSuggestions.instances)
inst.canBeTriggeredByTab = enabled;
for (const inst of this.MentionSuggestions.instances)
inst.canBeTriggeredByTab = !enabled;
});
const React = await this.web_munch.findModule('react'), const React = await this.web_munch.findModule('react'),
createElement = React && React.createElement; createElement = React && React.createElement;
@ -119,8 +152,14 @@ export default class Input extends Module {
this.overrideEmoteMatcher(inst); this.overrideEmoteMatcher(inst);
}); });
this.MentionSuggestions.ready((cls, instances) => {
for(const inst of instances)
this.overrideMentionMatcher(inst);
});
this.ChatInput.on('update', this.updateEmoteCompletion, this); this.ChatInput.on('update', this.updateEmoteCompletion, this);
this.EmoteSuggestions.on('mount', this.overrideEmoteMatcher, this); this.EmoteSuggestions.on('mount', this.overrideEmoteMatcher, this);
this.MentionSuggestions.on('mount', this.overrideMentionMatcher, this);
} }
@ -136,12 +175,28 @@ export default class Input extends Module {
} }
// eslint-disable-next-line class-methods-use-this
overrideMentionMatcher(inst) {
if ( inst._ffz_override )
return;
inst.canBeTriggeredByTab = !this.chat.context.get('chat.tab-complete.emotes-without-colon');
}
overrideEmoteMatcher(inst) { overrideEmoteMatcher(inst) {
if ( inst._ffz_override ) if ( inst._ffz_override )
return; return;
const t = this, const t = this;
old_get_matched = inst.getMatchedEmotes;
inst.canBeTriggeredByTab = this.chat.context.get('chat.tab-complete.emotes-without-colon');
inst.getMatches = function(input, pressedTab) {
return pressedTab
? input.length < 2 ? null : inst.getMatchedEmotes(input)
: input.startsWith(':') ? input.length < 3 ? null : inst.getMatchedEmotes(input) : null;
}
inst.doesEmoteMatchTerm = function(emote, term) { inst.doesEmoteMatchTerm = function(emote, term) {
const emote_name = emote.name || emote.token, const emote_name = emote.name || emote.token,
@ -157,7 +212,9 @@ export default class Input extends Module {
} }
inst.getMatchedEmotes = function(input) { inst.getMatchedEmotes = function(input) {
let results = old_get_matched.call(this, input); const limitResults = t.chat.context.get('chat.tab-complete.limit-results');
let results = t.getTwitchEmoteSuggestions(input, this);
if ( t.chat.context.get('chat.tab-complete.ffz-emotes') ) { if ( t.chat.context.get('chat.tab-complete.ffz-emotes') ) {
const ffz_emotes = t.getEmoteSuggestions(input, this); const ffz_emotes = t.getEmoteSuggestions(input, this);
@ -166,13 +223,13 @@ export default class Input extends Module {
} }
if ( ! t.chat.context.get('chat.tab-complete.emoji') ) if ( ! t.chat.context.get('chat.tab-complete.emoji') )
return results; return limitResults && results.length > 25 ? results.slice(0, 25) : results;
const emoji = t.getEmojiSuggestions(input, this); const emoji = t.getEmojiSuggestions(input, this);
if ( Array.isArray(emoji) && emoji.length ) if ( Array.isArray(emoji) && emoji.length )
results = Array.isArray(results) ? results.concat(emoji) : emoji; results = Array.isArray(results) ? results.concat(emoji) : emoji;
return results; return limitResults && results.length > 25 ? results.slice(0, 25) : results;
} }
const React = this.web_munch.getModule('react'), const React = this.web_munch.getModule('react'),
@ -195,7 +252,37 @@ export default class Input extends Module {
} }
// eslint-disable-next-line class-methods-use-this
getTwitchEmoteSuggestions(input, inst) {
const hydratedEmotes = inst.hydrateEmotes(inst.props.emotes);
if (!Array.isArray(hydratedEmotes)) {
return [];
}
const results = [];
const search = input.startsWith(':') ? input.slice(1) : input;
for (const set of hydratedEmotes) {
if (set && Array.isArray(set.emotes)) {
for (const emote of set.emotes) {
if (inst.doesEmoteMatchTerm(emote, search)) {
results.push({
current: input,
replacement: emote.token,
element: inst.renderEmoteSuggestion(emote)
});
}
}
}
}
return results;
}
getEmojiSuggestions(input, inst) { getEmojiSuggestions(input, inst) {
if (!input.startsWith(':')) {
return [];
}
let search = input.slice(1).toLowerCase(); let search = input.slice(1).toLowerCase();
const style = this.chat.context.get('chat.emoji.style'), const style = this.chat.context.get('chat.emoji.style'),
tone = this.settings.provider.get('emoji-tone', null), tone = this.settings.provider.get('emoji-tone', null),
@ -242,7 +329,7 @@ export default class Input extends Module {
return []; return [];
} }
const search = input.slice(1), const search = input.startsWith(':') ? input.slice(1) : input,
results = [], results = [],
emotes = this.emotes.getEmotes( emotes = this.emotes.getEmotes(
user && user.id, user && user.id,