2018-03-07 00:37:14 +01:00
|
|
|
/**
|
|
|
|
* BetterDiscord Emote Module
|
|
|
|
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
|
|
|
|
* All rights reserved.
|
|
|
|
* https://betterdiscord.net
|
|
|
|
*
|
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
|
|
|
*/
|
2018-03-21 21:47:46 +01:00
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
import BuiltinModule from './BuiltinModule';
|
2018-03-22 03:13:32 +01:00
|
|
|
import path from 'path';
|
2018-08-16 13:33:22 +02:00
|
|
|
import { Utils, FileUtils, ClientLogger as Logger } from 'common';
|
|
|
|
import { Settings, Globals, WebpackModules, ReactComponents, MonkeyPatch, Cache } from 'modules';
|
2018-03-21 21:47:46 +01:00
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
import Emote from './EmoteComponent.js';
|
2018-03-07 00:37:14 +01:00
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
export default new class EmoteModule extends BuiltinModule {
|
2018-03-22 03:13:32 +01:00
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
get dbpath() { return path.join(Globals.getPath('data'), 'emotes.json') }
|
|
|
|
|
|
|
|
get database() { return this._db || (this._db = new Map()) }
|
2018-03-31 02:17:42 +02:00
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
get favourites() { return this._favourites || (this._favourites = []) }
|
2018-06-12 21:56:40 +02:00
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
get settingPath() { return ['emotes', 'default', 'enable'] }
|
2018-06-12 21:56:40 +02:00
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
async enabled() {
|
2018-03-31 05:37:26 +02:00
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
if (!this.database.size) {
|
|
|
|
await this.loadLocalDb();
|
2018-03-31 05:37:26 +02:00
|
|
|
}
|
2018-03-31 02:17:42 +02:00
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
this.patchMessageContent();
|
|
|
|
const selector = `.${WebpackModules.getClassName('channelTextArea', 'emojiButton')}`;
|
|
|
|
const cta = await ReactComponents.getComponent('ChannelTextArea', { selector });
|
|
|
|
MonkeyPatch('BD:EMOTEMODULE', cta.component.prototype).before('handleSubmit', this.handleChannelTextAreaSubmit.bind(this));
|
2018-03-31 02:17:42 +02:00
|
|
|
}
|
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
async disabled() {
|
|
|
|
for (const patch of Patcher.getPatchesByCaller('BD:EMOTEMODULE')) patch.unpatch();
|
2018-03-31 02:17:42 +02:00
|
|
|
}
|
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
processMarkup(markup) {
|
|
|
|
const newMarkup = [];
|
|
|
|
window.markup = markup;
|
|
|
|
const jumboable = !markup.some(child => {
|
|
|
|
if (typeof child !== 'string') return false;
|
2018-03-22 03:13:32 +01:00
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
return / \w+/g.test(child);
|
|
|
|
});
|
2018-03-31 02:17:42 +02:00
|
|
|
|
2018-03-15 18:07:16 +01:00
|
|
|
for (const child of markup) {
|
2018-03-31 02:17:42 +02:00
|
|
|
if (typeof child !== 'string') {
|
2018-08-16 13:33:22 +02:00
|
|
|
if (typeof child === 'object') {
|
|
|
|
const isEmoji = Utils.findInReactTree(child, 'emojiName');
|
|
|
|
if (isEmoji) child.props.children.props.jumboable = jumboable;
|
|
|
|
}
|
2018-03-15 18:07:16 +01:00
|
|
|
newMarkup.push(child);
|
2018-03-07 00:37:14 +01:00
|
|
|
continue;
|
|
|
|
}
|
2018-08-16 13:33:22 +02:00
|
|
|
|
|
|
|
if (!/:(\w+):/g.test(child)) {
|
2018-03-15 18:07:16 +01:00
|
|
|
newMarkup.push(child);
|
|
|
|
continue;
|
|
|
|
}
|
2018-05-14 17:55:18 +02:00
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
const words = child.split(/([^\s]+)([\s]|$)/g).filter(f => f !== '');
|
|
|
|
|
|
|
|
let s = '';
|
|
|
|
for (const word of words) {
|
|
|
|
const isemote = /:(.*?):/g.exec(word);
|
|
|
|
if (!isemote) {
|
|
|
|
s += word;
|
2018-03-07 00:37:14 +01:00
|
|
|
continue;
|
|
|
|
}
|
2018-08-16 13:33:22 +02:00
|
|
|
|
|
|
|
const emote = this.findByName(isemote[1]);
|
|
|
|
if (!emote) {
|
|
|
|
s += word;
|
|
|
|
continue;
|
2018-03-07 00:37:14 +01:00
|
|
|
}
|
2018-08-16 13:33:22 +02:00
|
|
|
|
|
|
|
newMarkup.push(s);
|
|
|
|
s = '';
|
|
|
|
|
|
|
|
emote.jumboable = jumboable;
|
|
|
|
newMarkup.push(emote.render());
|
2018-03-07 00:37:14 +01:00
|
|
|
}
|
2018-08-16 13:33:22 +02:00
|
|
|
if (s !== '') newMarkup.push(s);
|
2018-03-07 00:37:14 +01:00
|
|
|
}
|
2018-08-16 13:33:22 +02:00
|
|
|
|
2018-03-13 23:34:03 +01:00
|
|
|
return newMarkup;
|
|
|
|
}
|
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
async patchMessageContent() {
|
|
|
|
const selector = `.${WebpackModules.getClassName('container', 'containerCozy', 'containerCompact', 'edited')}`;
|
|
|
|
const MessageContent = await ReactComponents.getComponent('MessageContent', { selector });
|
|
|
|
MonkeyPatch('BD:EMOTEMODULE', MessageContent.component.prototype).after('render', this.afterRenderMessageContent.bind(this));
|
|
|
|
MessageContent.forceUpdateAll();
|
2018-03-13 23:34:03 +01:00
|
|
|
}
|
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
afterRenderMessageContent(component, args, retVal) {
|
2018-08-17 09:40:19 +02:00
|
|
|
console.log(component);
|
2018-08-16 13:33:22 +02:00
|
|
|
const markup = Utils.findInReactTree(retVal, filter =>
|
|
|
|
filter &&
|
|
|
|
filter.className &&
|
|
|
|
filter.className.includes('markup') &&
|
|
|
|
filter.children.length >= 2);
|
2018-03-17 17:05:44 +01:00
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
if (!markup) return;
|
|
|
|
markup.children[1] = this.processMarkup(markup.children[1]);
|
2018-03-07 00:37:14 +01:00
|
|
|
}
|
2018-03-10 04:29:04 +01:00
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
handleChannelTextAreaSubmit(component, args, retVal) {
|
|
|
|
component.props.value = component.props.value.split(' ').map(word => {
|
|
|
|
const isEmote = /;(.*?);/g.exec(word);
|
|
|
|
return isEmote ? `:${isEmote[1]}:` : word;
|
|
|
|
}).join(' ');
|
2018-03-10 04:29:04 +01:00
|
|
|
}
|
2018-03-22 03:13:32 +01:00
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
async loadLocalDb() {
|
|
|
|
const emotes = await FileUtils.readJsonFromFile(this.dbpath);
|
|
|
|
for (const [index, emote] of emotes.entries()) {
|
|
|
|
const { type, id, src, value } = emote;
|
|
|
|
if (index % 10000 === 0) await Utils.wait();
|
2018-05-29 17:58:57 +02:00
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
this.database.set(id, { id: emote.value.id || value, type });
|
|
|
|
}
|
2018-05-29 17:58:57 +02:00
|
|
|
}
|
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
findByName(name) {
|
|
|
|
const emote = this.database.get(name);
|
|
|
|
if (!emote) return null;
|
|
|
|
return this.parseEmote(name, emote);
|
|
|
|
}
|
2018-05-29 17:58:57 +02:00
|
|
|
|
2018-08-16 13:33:22 +02:00
|
|
|
parseEmote(name, emote) {
|
|
|
|
const { type, id } = emote;
|
|
|
|
if (type < 0 || type > 2) return null;
|
|
|
|
return new Emote(type, id, name);
|
2018-05-29 17:58:57 +02:00
|
|
|
}
|
|
|
|
|
2018-03-07 00:37:14 +01:00
|
|
|
}
|