Store emote database in a map
This commit is contained in:
parent
1bde3b4ec9
commit
b3442ee108
|
@ -14,17 +14,16 @@ import { Utils, FileUtils, ClientLogger as Logger } from 'common';
|
|||
import path from 'path';
|
||||
import EmoteComponent from './EmoteComponent.vue';
|
||||
|
||||
let emotes = null;
|
||||
const emotesEnabled = true;
|
||||
const enforceWrapperFrom = (new Date('2018-05-01')).valueOf();
|
||||
|
||||
export default new class EmoteModule {
|
||||
|
||||
constructor() {
|
||||
this.emotes = new Map();
|
||||
this.favourite_emotes = [];
|
||||
}
|
||||
|
||||
init() {
|
||||
async init() {
|
||||
this.enabledSetting = Settings.getSetting('emotes', 'default', 'enable');
|
||||
this.enabledSetting.on('setting-updated', event => {
|
||||
// Rerender all messages (or if we're disabling emotes, those that have emotes)
|
||||
|
@ -33,7 +32,25 @@ export default new class EmoteModule {
|
|||
}
|
||||
});
|
||||
|
||||
return this.observe();
|
||||
const dataPath = Globals.getPath('data');
|
||||
try {
|
||||
const emotes = await FileUtils.readJsonFromFile(path.join(dataPath, 'emotes.json'));
|
||||
for (let emote of emotes) {
|
||||
const uri = emote.type === 2 ? 'https://cdn.betterttv.net/emote/:id/1x' : emote.type === 1 ? 'https://cdn.frankerfacez.com/emoticon/:id/1' : 'https://static-cdn.jtvnw.net/emoticons/v1/:id/1.0';
|
||||
emote.name = emote.id;
|
||||
emote.src = uri.replace(':id', emote.value.id || emote.value);
|
||||
this.emotes.set(emote.id, emote);
|
||||
}
|
||||
} catch (err) {
|
||||
Logger.err('EmoteModule', [`Failed to load emote data. Make sure you've downloaded the emote data and placed it in ${dataPath}:`, err]);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.observe();
|
||||
} catch (err) {
|
||||
Logger.err('EmoteModule', ['Error patching Message', err]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -65,10 +82,6 @@ export default new class EmoteModule {
|
|||
return this._searchCache || (this._searchCache = {});
|
||||
}
|
||||
|
||||
get emoteDb() {
|
||||
return emotes;
|
||||
}
|
||||
|
||||
get React() {
|
||||
return WebpackModules.getModuleByName('React');
|
||||
}
|
||||
|
@ -97,16 +110,16 @@ export default new class EmoteModule {
|
|||
if (!words) continue;
|
||||
let text = null;
|
||||
for (const [wordIndex, word] of words.entries()) {
|
||||
const isEmote = this.isEmote(word);
|
||||
if (isEmote) {
|
||||
const emote = this.getEmote(word);
|
||||
if (emote) {
|
||||
if (text !== null) {
|
||||
newMarkup.push(text);
|
||||
text = null;
|
||||
}
|
||||
newMarkup.push(this.React.createElement('span', {
|
||||
className: 'bd-emote-outer',
|
||||
'data-bdemote-name': isEmote.name,
|
||||
'data-bdemote-src': isEmote.src,
|
||||
'data-bdemote-name': emote.name,
|
||||
'data-bdemote-src': emote.src,
|
||||
'data-has-wrapper': /;[\w]+;/gmi.test(word)
|
||||
}));
|
||||
continue;
|
||||
|
@ -129,7 +142,8 @@ export default new class EmoteModule {
|
|||
}
|
||||
|
||||
injectAll() {
|
||||
if (!emotesEnabled) return;
|
||||
if (!this.enabledSetting.value) return;
|
||||
|
||||
const all = document.getElementsByClassName('bd-emote-outer');
|
||||
for (const ec of all) {
|
||||
if (ec.children.length) continue;
|
||||
|
@ -150,48 +164,36 @@ export default new class EmoteModule {
|
|||
}
|
||||
|
||||
async observe() {
|
||||
const dataPath = Globals.getPath('data');
|
||||
try {
|
||||
emotes = await FileUtils.readJsonFromFile(path.join(dataPath, 'emotes.json'));
|
||||
} catch (err) {
|
||||
Logger.err('EmoteModule', [`Failed to load emote data. Make sure you've downloaded the emote data and placed it in ${dataPath}:`, err]);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const Message = await ReactComponents.getComponent('Message');
|
||||
this.unpatchRender = MonkeyPatch('BD:EmoteModule', Message.component.prototype).after('render', (component, args, retVal) => {
|
||||
try {
|
||||
// First child has all the actual text content, second is the edited timestamp
|
||||
const markup = this.findByProp(retVal, 'className', 'markup');
|
||||
if (!markup) return;
|
||||
Logger.log('EmoteModule', ['Message :', retVal, component]);
|
||||
markup.children[0] = this.processMarkup(markup.children[0], component.props.message.editedTimestamp || component.props.message.timestamp);
|
||||
} catch (err) {
|
||||
Logger.err('EmoteModule', err);
|
||||
}
|
||||
});
|
||||
for (const message of document.querySelectorAll('.message')) {
|
||||
Reflection(message).forceUpdate();
|
||||
const Message = await ReactComponents.getComponent('Message');
|
||||
this.unpatchRender = MonkeyPatch('BD:EmoteModule', Message.component.prototype).after('render', (component, args, retVal) => {
|
||||
try {
|
||||
// First child has all the actual text content, second is the edited timestamp
|
||||
const markup = this.findByProp(retVal, 'className', 'markup');
|
||||
if (!markup) return;
|
||||
markup.children[0] = this.processMarkup(markup.children[0], component.props.message.editedTimestamp || component.props.message.timestamp);
|
||||
} catch (err) {
|
||||
Logger.err('EmoteModule', err);
|
||||
}
|
||||
this.injectAll();
|
||||
this.unpatchMount = MonkeyPatch('BD:EmoteModule', Message.component.prototype).after('componentDidMount', component => {
|
||||
const element = this.ReactDOM.findDOMNode(component);
|
||||
if (!element) return;
|
||||
this.injectEmotes(element);
|
||||
});
|
||||
this.unpatchUpdate = MonkeyPatch('BD:EmoteModule', Message.component.prototype).after('componentDidUpdate', component => {
|
||||
const element = this.ReactDOM.findDOMNode(component);
|
||||
if (!element) return;
|
||||
this.injectEmotes(element);
|
||||
});
|
||||
} catch (err) {
|
||||
Logger.err('EmoteModule', err);
|
||||
});
|
||||
for (const message of document.querySelectorAll('.message')) {
|
||||
Reflection(message).forceUpdate();
|
||||
}
|
||||
this.injectAll();
|
||||
this.unpatchMount = MonkeyPatch('BD:EmoteModule', Message.component.prototype).after('componentDidMount', component => {
|
||||
const element = this.ReactDOM.findDOMNode(component);
|
||||
if (!element) return;
|
||||
this.injectEmotes(element);
|
||||
});
|
||||
this.unpatchUpdate = MonkeyPatch('BD:EmoteModule', Message.component.prototype).after('componentDidUpdate', component => {
|
||||
const element = this.ReactDOM.findDOMNode(component);
|
||||
if (!element) return;
|
||||
this.injectEmotes(element);
|
||||
});
|
||||
}
|
||||
|
||||
injectEmote(root) {
|
||||
if (!emotesEnabled) return;
|
||||
if (!this.enabledSetting.value) return;
|
||||
|
||||
while (root.firstChild) {
|
||||
root.removeChild(root.firstChild);
|
||||
}
|
||||
|
@ -206,25 +208,14 @@ export default new class EmoteModule {
|
|||
}
|
||||
|
||||
injectEmotes(element) {
|
||||
if (!emotesEnabled || !element) return;
|
||||
if (!this.enabledSetting.value || !element) return;
|
||||
|
||||
for (const beo of element.getElementsByClassName('bd-emote-outer')) this.injectEmote(beo);
|
||||
}
|
||||
|
||||
isEmote(word) {
|
||||
if (!emotes) return null;
|
||||
getEmote(word) {
|
||||
const name = word.replace(/;/g, '');
|
||||
const emote = emotes.find(emote => emote.id === name);
|
||||
if (!emote) return null;
|
||||
let { id, value } = emote;
|
||||
if (value.id) value = value.id;
|
||||
const uri = emote.type === 2 ? 'https://cdn.betterttv.net/emote/:id/1x' : emote.type === 1 ? 'https://cdn.frankerfacez.com/emoticon/:id/1' : 'https://static-cdn.jtvnw.net/emoticons/v1/:id/1.0';
|
||||
return { name, src: uri.replace(':id', value) };
|
||||
}
|
||||
|
||||
filterTest() {
|
||||
const re = new RegExp('Kappa', 'i');
|
||||
const filtered = emotes.filter(emote => re.test(emote.id));
|
||||
return filtered.slice(0, 10);
|
||||
return this.emotes.get(name);
|
||||
}
|
||||
|
||||
filter(regex, limit, start = 0) {
|
||||
|
@ -232,17 +223,21 @@ export default new class EmoteModule {
|
|||
if (this.searchCache.hasOwnProperty(key)) return this.searchCache[key];
|
||||
let index = 0;
|
||||
let startIndex = 0;
|
||||
return this.searchCache[key] = emotes.filter(emote => {
|
||||
if (index >= limit) return false;
|
||||
|
||||
const matching = this.searchCache[key] = [];
|
||||
for (let emote of this.emotes.values()) {
|
||||
if (index >= limit) break;
|
||||
if (regex.test(emote.id)) {
|
||||
if (startIndex < start) {
|
||||
startIndex++;
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
index++;
|
||||
return true;
|
||||
matching.push(emote);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return matching;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,10 +25,14 @@
|
|||
|
||||
&.bd-selectable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.bd-selected {
|
||||
background-color: #36393f;
|
||||
&:hover {
|
||||
background-color: darken(#36393f, 5%);
|
||||
}
|
||||
|
||||
&.bd-selected {
|
||||
background-color: #36393f;
|
||||
}
|
||||
}
|
||||
|
||||
.bd-autocomplete-title {
|
||||
|
|
Loading…
Reference in New Issue