Move emote autocomplete injector to the emote module
This commit is contained in:
parent
13508b449d
commit
0e99b219c1
|
@ -13,6 +13,7 @@ import { DOM, VueInjector, Reflection } from 'ui';
|
|||
import { Utils, FileUtils, ClientLogger as Logger } from 'common';
|
||||
import path from 'path';
|
||||
import EmoteComponent from './EmoteComponent.vue';
|
||||
import Autocomplete from '../ui/components/common/Autocomplete.vue';
|
||||
|
||||
const enforceWrapperFrom = (new Date('2018-05-01')).valueOf();
|
||||
|
||||
|
@ -41,9 +42,12 @@ export default new class EmoteModule {
|
|||
}
|
||||
|
||||
try {
|
||||
await this.observe();
|
||||
await Promise.all([
|
||||
this.patchMessage(),
|
||||
this.patchChannelTextArea()
|
||||
]);
|
||||
} catch (err) {
|
||||
Logger.err('EmoteModule', ['Error patching Message', err]);
|
||||
Logger.err('EmoteModule', ['Error patching Message / ChannelTextArea', err]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,23 +158,6 @@ export default new class EmoteModule {
|
|||
return null;
|
||||
}
|
||||
|
||||
async observe() {
|
||||
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 || !this.enabledSetting.value) return;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
getEmote(word) {
|
||||
const name = word.replace(/;/g, '');
|
||||
return this.emotes.get(name);
|
||||
|
@ -198,4 +185,41 @@ export default new class EmoteModule {
|
|||
return matching;
|
||||
}
|
||||
|
||||
async patchMessage() {
|
||||
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 || !this.enabledSetting.value) return;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
async patchChannelTextArea() {
|
||||
const selector = '.' + WebpackModules.getModuleByProps(['channelTextArea', 'emojiButton']).channelTextArea;
|
||||
|
||||
const ChannelTextArea = await ReactComponents.getComponent('ChannelTextArea', {selector});
|
||||
this.unpatchChannelTextArea = MonkeyPatch('BD:ReactComponents', ChannelTextArea.component.prototype).after('render', (component, args, retVal) => {
|
||||
if (!(retVal.props.children instanceof Array)) retVal.props.children = [retVal.props.children];
|
||||
|
||||
retVal.props.children.splice(0, 0, VueInjector.createReactElement({
|
||||
components: { Autocomplete },
|
||||
template: '<Autocomplete />'
|
||||
}));
|
||||
});
|
||||
|
||||
for (const e of document.querySelectorAll(selector)) {
|
||||
Reflection(e).forceUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,196 +0,0 @@
|
|||
/**
|
||||
* BetterDiscord Automated DOM Manipulations
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { Events, WebpackModules, EventListener, DiscordApi, ReactComponents, Renderer } from 'modules';
|
||||
import { ClientLogger as Logger } from 'common';
|
||||
import Reflection from './reflection';
|
||||
import DOM from './dom';
|
||||
import VueInjector from './vueinjector';
|
||||
import EditedTimeStamp from './components/common/EditedTimestamp.vue';
|
||||
import Autocomplete from './components/common/Autocomplete.vue';
|
||||
|
||||
export default class extends EventListener {
|
||||
|
||||
constructor(args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
bindings() {
|
||||
this.manipAll = this.manipAll.bind(this);
|
||||
this.markupInjector = this.markupInjector.bind(this);
|
||||
this.setIds = this.setIds.bind(this);
|
||||
this.setMessageIds = this.setMessageIds.bind(this);
|
||||
this.setUserIds = this.setUserIds.bind(this);
|
||||
}
|
||||
|
||||
get eventBindings() {
|
||||
return [
|
||||
// { id: 'server-switch', callback: this.manipAll },
|
||||
// { id: 'channel-switch', callback: this.manipAll },
|
||||
// { id: 'discord:MESSAGE_CREATE', callback: this.markupInjector },
|
||||
// { id: 'discord:MESSAGE_UPDATE', callback: this.markupInjector },
|
||||
{ id: 'gkh:keyup', callback: this.injectAutocomplete }
|
||||
];
|
||||
}
|
||||
|
||||
manipAll() {
|
||||
try {
|
||||
this.appMount.setAttribute('guild-id', DiscordApi.currentGuild.id);
|
||||
this.appMount.setAttribute('channel-id', DiscordApi.currentChannel.id);
|
||||
this.setIds();
|
||||
this.makeMutable();
|
||||
} catch (err) {
|
||||
Logger.err('AutoManip', err);
|
||||
}
|
||||
}
|
||||
|
||||
markupInjector(e) {
|
||||
if (!e.element) return;
|
||||
this.setId(e.element);
|
||||
const markup = e.element.querySelector('.markup:not(.mutable)');
|
||||
if (markup) this.injectMarkup(markup, this.cloneMarkup(markup), false);
|
||||
}
|
||||
|
||||
getEts(node) {
|
||||
try {
|
||||
const reh = Object.keys(node).find(k => k.startsWith('__reactInternalInstance'));
|
||||
return node[reh].memoizedProps.children[node[reh].memoizedProps.children.length - 1].props.text;
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
makeMutable() {
|
||||
for (const el of document.querySelectorAll('.markup:not(.mutable)')) {
|
||||
this.injectMarkup(el, this.cloneMarkup(el), false);
|
||||
}
|
||||
}
|
||||
|
||||
cloneMarkup(node) {
|
||||
const childNodes = [...node.childNodes];
|
||||
const clone = document.createElement('div');
|
||||
clone.className = 'markup mutable';
|
||||
const ets = this.getEts(node);
|
||||
for (const [cni, cn] of childNodes.entries()) {
|
||||
if (cn.nodeType !== Node.TEXT_NODE) {
|
||||
if (cn.className.includes('edited')) continue;
|
||||
}
|
||||
clone.appendChild(cn.cloneNode(true));
|
||||
}
|
||||
return { clone, ets }
|
||||
}
|
||||
|
||||
injectMarkup(sibling, markup, reinject) {
|
||||
if (sibling.className && sibling.className.includes('mutable')) return; // Ignore trying to make mutable again
|
||||
let cc = null;
|
||||
for (const cn of sibling.parentElement.childNodes) {
|
||||
if (cn.className && cn.className.includes('mutable')) cc = cn;
|
||||
}
|
||||
if (cc) sibling.parentElement.removeChild(cc);
|
||||
if (markup === true) markup = this.cloneMarkup(sibling);
|
||||
|
||||
sibling.parentElement.insertBefore(markup.clone, sibling);
|
||||
sibling.classList.add('shadow');
|
||||
sibling.style.display = 'none';
|
||||
if (markup.ets) {
|
||||
const etsRoot = document.createElement('span');
|
||||
markup.clone.appendChild(etsRoot);
|
||||
VueInjector.inject(etsRoot, {
|
||||
components: { EditedTimeStamp },
|
||||
data: { ets: markup.ets },
|
||||
template: '<EditedTimeStamp :ets="ets" />'
|
||||
});
|
||||
}
|
||||
|
||||
Events.emit('ui:mutable:.markup', markup.clone);
|
||||
}
|
||||
|
||||
setIds() {
|
||||
this.setMessageIds();
|
||||
this.setUserIds();
|
||||
this.setChannelIds();
|
||||
}
|
||||
|
||||
setMessageIds() {
|
||||
for (let msg of document.querySelectorAll('.message')) {
|
||||
this.setId(msg);
|
||||
}
|
||||
}
|
||||
|
||||
setUserIds() {
|
||||
for (let user of document.querySelectorAll('.channel-members-wrap .member, .channel-members-wrap .member-2FrNV0')) {
|
||||
this.setUserId(user);
|
||||
}
|
||||
}
|
||||
|
||||
setChannelIds() {
|
||||
for (let channel of document.querySelectorAll('[class*=channels] [class*=containerDefault]')) {
|
||||
this.setChannelId(channel);
|
||||
}
|
||||
}
|
||||
|
||||
setId(msg) {
|
||||
if (msg.hasAttribute('message-id')) return;
|
||||
const messageid = Reflection(msg).prop('message.id');
|
||||
const authorid = Reflection(msg).prop('message.author.id');
|
||||
if (!messageid || !authorid) {
|
||||
const msgGroup = msg.closest('.message-group');
|
||||
if (!msgGroup) return;
|
||||
const userTest = Reflection(msgGroup).prop('user');
|
||||
if (!userTest) return;
|
||||
msgGroup.setAttribute('data-author-id', userTest.id);
|
||||
if (userTest.id === DiscordApi.currentUserId) msgGroup.setAttribute('data-currentuser', true);
|
||||
return;
|
||||
}
|
||||
msg.setAttribute('data-message-id', messageid);
|
||||
const msgGroup = msg.closest('.message-group');
|
||||
if (!msgGroup) return;
|
||||
msgGroup.setAttribute('data-author-id', authorid);
|
||||
if (authorid === DiscordApi.currentUser.id) msgGroup.setAttribute('data-currentuser', true);
|
||||
}
|
||||
|
||||
setUserId(user) {
|
||||
if (user.hasAttribute('data-user-id')) return;
|
||||
const userid = Reflection(user).prop('user.id');
|
||||
if (!userid) return;
|
||||
user.setAttribute('data-user-id', userid);
|
||||
const currentUser = userid === DiscordApi.currentUser.id;
|
||||
if (currentUser) user.setAttribute('data-currentuser', true);
|
||||
Events.emit('ui:useridset', user);
|
||||
}
|
||||
|
||||
setChannelId(channel) {
|
||||
if (channel.hasAttribute('data-channel-id')) return;
|
||||
const channelObj = Reflection(channel).prop('channel');
|
||||
if (!channelObj) return;
|
||||
channel.setAttribute('data-channel-id', channelObj.id);
|
||||
if (channelObj.nsfw) channel.setAttribute('data-channel-nsfw', true);
|
||||
if (channelObj.type && channelObj.type === 2) channel.setAttribute('data-channel-voice', true);
|
||||
}
|
||||
|
||||
get appMount() {
|
||||
return document.getElementById('app-mount');
|
||||
}
|
||||
|
||||
injectAutocomplete(e) {
|
||||
if (document.querySelector('.bd-autocomplete')) return;
|
||||
if (!e.target.closest('[class*=channelTextArea]')) return;
|
||||
const root = document.createElement('span');
|
||||
const parent = document.querySelector('[class*="channelTextArea"] > [class*="inner"]');
|
||||
if (!parent) return;
|
||||
parent.parentElement.insertBefore(root, parent);
|
||||
VueInjector.inject(root, {
|
||||
components: { Autocomplete },
|
||||
data: { initial: e.target.value },
|
||||
template: '<Autocomplete :initial="initial" />'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -13,7 +13,6 @@ import { Utils } from 'common';
|
|||
import { remote } from 'electron';
|
||||
import DOM from './dom';
|
||||
import Vue from './vue';
|
||||
import AutoManip from './automanip';
|
||||
import { BdSettingsWrapper, BdModals } from './components';
|
||||
|
||||
export default class {
|
||||
|
@ -25,9 +24,6 @@ export default class {
|
|||
channel: DiscordApi.currentChannel
|
||||
};
|
||||
|
||||
window.addEventListener('keyup', e => Events.emit('gkh:keyup', e));
|
||||
this.autoManip = new AutoManip();
|
||||
|
||||
const ehookInterval = setInterval(() => {
|
||||
if (!remote.BrowserWindow.getFocusedWindow()) return;
|
||||
clearInterval(ehookInterval);
|
||||
|
|
|
@ -56,11 +56,6 @@
|
|||
sterm: ''
|
||||
};
|
||||
},
|
||||
props: ['initial'],
|
||||
beforeMount() {
|
||||
// this.emotes = EmoteModule.filter(new RegExp(this.initial, 'i'), 10);
|
||||
// this.open = this.emotes.length;
|
||||
},
|
||||
created() {
|
||||
const enabled = Settings.getSetting('emotes', 'default', 'enable');
|
||||
enabled.on('setting-updated', event => {
|
||||
|
|
Loading…
Reference in New Issue