Merge branch 'master' into e2ee-minifix
This commit is contained in:
commit
11287d9566
|
@ -9,29 +9,32 @@
|
|||
*/
|
||||
|
||||
import BuiltinModule from './BuiltinModule';
|
||||
import { Patcher, MonkeyPatch, WebpackModules, ReactComponents } from 'modules';
|
||||
import { Reflection } from 'modules';
|
||||
|
||||
const twelveHour = new RegExp(`([0-9]{1,2}):([0-9]{1,2})\\s(AM|PM)`);
|
||||
|
||||
export default new class TwentyFourHour extends BuiltinModule {
|
||||
|
||||
get settingPath() {
|
||||
return ['ui', 'default', '24-hour'];
|
||||
/* Getters */
|
||||
get moduleName() { return 'TwentyFourHour' }
|
||||
|
||||
get settingPath() { return ['ui', 'default', '24-hour'] }
|
||||
|
||||
/* Patches */
|
||||
applyPatches() {
|
||||
if (this.patches.length) return;
|
||||
const { TimeFormatter } = Reflection.modules;
|
||||
this.patch(TimeFormatter, 'calendarFormat', this.convertTimeStamps);
|
||||
}
|
||||
|
||||
async enabled(e) {
|
||||
if (Patcher.getPatchesByCaller('BD:TwentyFourHour').length) return;
|
||||
const TimeFormatter = WebpackModules.getModuleByName('TimeFormatter');
|
||||
MonkeyPatch('BD:TwentyFourHour', TimeFormatter).after('calendarFormat', (thisObject, args, returnValue) => {
|
||||
const matched = returnValue.match(twelveHour);
|
||||
if (!matched || matched.length != 4) return;
|
||||
if (matched[3] == 'AM') return returnValue.replace(matched[0], `${matched[1] == '12' ? '00' : matched[1].padStart(2, '0')}:${matched[2]}`)
|
||||
return returnValue.replace(matched[0], `${parseInt(matched[1]) + 12}:${matched[2]}`)
|
||||
});
|
||||
/**
|
||||
* Convert 12 hours timestamps to 24 hour timestamps
|
||||
*/
|
||||
convertTimeStamps(that, args, returnValue) {
|
||||
const matched = returnValue.match(twelveHour);
|
||||
if (!matched || matched.length !== 4) return;
|
||||
if (matched[3] === 'AM') return returnValue.replace(matched[0], `${matched[1] === '12' ? '00' : matched[1].padStart(2, '0')}:${matched[2]}`)
|
||||
return returnValue.replace(matched[0], `${parseInt(matched[1]) + 12}:${matched[2]}`)
|
||||
}
|
||||
|
||||
disabled(e) {
|
||||
Patcher.unpatchAll('BD:TwentyFourHour');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -9,41 +9,48 @@
|
|||
*/
|
||||
|
||||
import BuiltinModule from './BuiltinModule';
|
||||
import { Patcher, MonkeyPatch, WebpackModules, ReactComponents } from 'modules';
|
||||
import { Reflection } from 'modules';
|
||||
|
||||
export default new class BlockedMessages extends BuiltinModule {
|
||||
|
||||
get settingPath() {
|
||||
return ['ui', 'default', 'blocked-messages'];
|
||||
}
|
||||
/* Getters */
|
||||
get moduleName() { return 'BlockedMessages' }
|
||||
|
||||
static isBlocked(id) {
|
||||
const RelationshipStore = WebpackModules.getModuleByName('RelationshipStore');
|
||||
return RelationshipStore.isBlocked(id);
|
||||
}
|
||||
get settingPath() { return ['ui', 'default', 'blocked-messages'] }
|
||||
|
||||
async enabled(e) {
|
||||
if (Patcher.getPatchesByCaller('BD:BlockedMessages').length) return;
|
||||
const MessageActions = WebpackModules.getModuleByName('MessageActions');
|
||||
MonkeyPatch('BD:BlockedMessages', MessageActions).instead('receiveMessage', this.processMessage);
|
||||
|
||||
const MessageListComponents = WebpackModules.getModuleByProps(['BlockedMessageGroup']);
|
||||
const MessageListComponents = Reflection.module.byProps('BlockedMessageGroup');
|
||||
MessageListComponents.OriginalBlockedMessageGroup = MessageListComponents.BlockedMessageGroup;
|
||||
MessageListComponents.BlockedMessageGroup = () => {return null;};
|
||||
MessageListComponents.BlockedMessageGroup = () => { return null; };
|
||||
this.cancelBlockedMessages = () => {
|
||||
MessageListComponents.BlockedMessageGroup = MessageListComponents.OriginalBlockedMessageGroup;
|
||||
delete MessageListComponents.OriginalBlockedMessageGroup;
|
||||
}
|
||||
}
|
||||
|
||||
processMessage(thisObject, args, originalFunction) {
|
||||
disabled(e) {
|
||||
if (this.cancelBlockedMessages) this.cancelBlockedMessages();
|
||||
}
|
||||
|
||||
/* Methods */
|
||||
static isBlocked(id) {
|
||||
const { RelationshipStore } = Reflection.modules;
|
||||
return RelationshipStore.isBlocked(id);
|
||||
}
|
||||
|
||||
/* Patches */
|
||||
applyPatches() {
|
||||
if (this.patches.length) return;
|
||||
const { MessageActions } = Reflection.modules;
|
||||
this.patch(MessageActions, 'receiveMessage', this.processMessage, 'instead');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore blocked messages completely
|
||||
*/
|
||||
processMessage(that, args, originalFunction) {
|
||||
if (args[1] && args[1].author && args[1].author.id && BlockedMessages.isBlocked(args[1].author.id)) return;
|
||||
return originalFunction(...args);
|
||||
}
|
||||
|
||||
disabled(e) {
|
||||
Patcher.unpatchAll('BD:BlockedMessages');
|
||||
if (this.cancelBlockedMessages) this.cancelBlockedMessages();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,58 +11,57 @@
|
|||
import BuiltinModule from './BuiltinModule';
|
||||
|
||||
import { Utils } from 'common';
|
||||
import { Settings, Patcher, MonkeyPatch, WebpackModules, ReactComponents, DiscordApi } from 'modules';
|
||||
import { Settings, Reflection, ReactComponents, DiscordApi } from 'modules';
|
||||
|
||||
export default new class ColoredText extends BuiltinModule {
|
||||
|
||||
/* Getters */
|
||||
get moduleName() { return 'ColoredText' }
|
||||
|
||||
get settingPath() { return ['ui', 'default', 'colored-text'] }
|
||||
|
||||
get intensityPath() { return ['ui', 'advanced', 'colored-text-intensity'] }
|
||||
|
||||
get intensitySetting() { return Settings.getSetting(...this.intensityPath) }
|
||||
|
||||
get intensity() { return 100 - this.intensitySetting.value }
|
||||
|
||||
get defaultColor() { return DiscordApi.UserSettings.theme == 'light' ? '#747f8d' : '#dcddde' }
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._intensityUpdated = this._intensityUpdated.bind(this);
|
||||
this.injectColoredText = this.injectColoredText.bind(this);
|
||||
}
|
||||
|
||||
get settingPath() {
|
||||
return ['ui', 'default', 'colored-text'];
|
||||
async enabled(e) {
|
||||
this.intensitySetting.off('setting-updated', this._intensityUpdated);
|
||||
this.intensitySetting.on('setting-updated', this._intensityUpdated);
|
||||
}
|
||||
|
||||
get intensityPath() {
|
||||
return ['ui', 'advanced', 'colored-text-intensity'];
|
||||
}
|
||||
|
||||
get intensitySetting() {
|
||||
return Settings.getSetting(...this.intensityPath);
|
||||
}
|
||||
|
||||
get intensity() {
|
||||
return 100 - this.intensitySetting.value;
|
||||
disabled(e) {
|
||||
this.intensitySetting.off('setting-updated', this._intensityUpdated);
|
||||
}
|
||||
|
||||
/* Methods */
|
||||
_intensityUpdated() {
|
||||
this.MessageContent.forceUpdateAll();
|
||||
}
|
||||
|
||||
async enabled(e) {
|
||||
if (Patcher.getPatchesByCaller('BD:ColoredText').length) return;
|
||||
this.intensitySetting.on('setting-updated', this._intensityUpdated);
|
||||
this.MessageContent = await ReactComponents.getComponent('MessageContent', { selector: WebpackModules.getSelector('container', 'containerCozy', 'containerCompact', 'edited') });
|
||||
MonkeyPatch('BD:ColoredText', this.MessageContent.component.prototype).after('render', this.injectColoredText);
|
||||
/* Patches */
|
||||
async applyPatches() {
|
||||
if (this.patches.length) return;
|
||||
this.MessageContent = await ReactComponents.getComponent('MessageContent', { selector: Reflection.resolve('container', 'containerCozy', 'containerCompact', 'edited').selector });
|
||||
this.patch(this.MessageContent.component.prototype, 'render', this.injectColoredText);
|
||||
this.MessageContent.forceUpdateAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set markup text colour to match role colour
|
||||
*/
|
||||
injectColoredText(thisObject, args, returnValue) {
|
||||
const TinyColor = WebpackModules.getModuleByName('TinyColor');
|
||||
const { TinyColor } = Reflection.modules;
|
||||
const markup = Utils.findInReactTree(returnValue, m => m && m.props && m.props.className && m.props.className.includes('da-markup'));
|
||||
const roleColor = thisObject.props.message.colorString;
|
||||
if (markup && roleColor) markup.props.style = {color: TinyColor.mix(roleColor, this.defaultColor, this.intensity)};
|
||||
}
|
||||
|
||||
get defaultColor() {
|
||||
return DiscordApi.UserSettings.theme == 'light' ? '#747f8d' : '#dcddde';
|
||||
}
|
||||
|
||||
disabled(e) {
|
||||
Patcher.unpatchAll('BD:ColoredText');
|
||||
this.intensitySetting.off('setting-updated', this._intensityUpdated);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
|
||||
import { Settings, Cache, Events } from 'modules';
|
||||
import BuiltinModule from './BuiltinModule';
|
||||
import { WebpackModules, ReactComponents, MonkeyPatch, Patcher, DiscordApi, Security } from 'modules';
|
||||
import { VueInjector, Reflection, Modals, Toasts } from 'ui';
|
||||
import { Reflection, ReactComponents, MonkeyPatch, Patcher, DiscordApi, Security } from 'modules';
|
||||
import { VueInjector, Modals, Toasts } from 'ui';
|
||||
import { ClientLogger as Logger, ClientIPC } from 'common';
|
||||
import { request } from 'vendor';
|
||||
import { Utils } from 'common';
|
||||
|
@ -30,6 +30,14 @@ let seed;
|
|||
|
||||
export default new class E2EE extends BuiltinModule {
|
||||
|
||||
/* Getters */
|
||||
|
||||
get moduleName() { return 'E2EE' }
|
||||
|
||||
get settingPath() { return ['security', 'default', 'e2ee'] }
|
||||
|
||||
get database() { return Settings.getSetting('security', 'e2eedb', 'e2ekvps').value }
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.encryptNewMessages = true;
|
||||
|
@ -40,26 +48,18 @@ export default new class E2EE extends BuiltinModule {
|
|||
|
||||
async enabled(e) {
|
||||
await this.fetchMasterKey();
|
||||
Settings.getSetting('security', 'default', 'use-keytar').on('setting-updated', this.fetchMasterKey);
|
||||
|
||||
Events.on('discord:MESSAGE_CREATE', this.handlePublicKey);
|
||||
this.patchDispatcher();
|
||||
this.patchMessageContent();
|
||||
const selector = `.${WebpackModules.getClassName('channelTextArea', 'emojiButton')}`;
|
||||
const cta = await ReactComponents.getComponent('ChannelTextArea', { selector });
|
||||
this.patchChannelTextArea(cta);
|
||||
this.patchChannelTextAreaSubmit(cta);
|
||||
cta.forceUpdateAll();
|
||||
Settings.getSetting('security', 'default', 'use-keytar').on('setting-updated', this.fetchMasterKey);
|
||||
}
|
||||
|
||||
async disabled(e) {
|
||||
Settings.getSetting('security', 'default', 'use-keytar').off('setting-updated', this.fetchMasterKey);
|
||||
Events.off('discord:MESSAGE_CREATE', this.handlePublicKey);
|
||||
for (const patch of Patcher.getPatchesByCaller('BD:E2EE')) patch.unpatch();
|
||||
const ctaComponent = await ReactComponents.getComponent('ChannelTextArea');
|
||||
ctaComponent.forceUpdateAll();
|
||||
}
|
||||
|
||||
/* Methods */
|
||||
async fetchMasterKey() {
|
||||
try {
|
||||
if (Settings.get('security', 'default', 'use-keytar')) {
|
||||
|
@ -95,14 +95,6 @@ export default new class E2EE extends BuiltinModule {
|
|||
return (this.master = newMaster);
|
||||
}
|
||||
|
||||
get settingPath() {
|
||||
return ['security', 'default', 'e2ee'];
|
||||
}
|
||||
|
||||
get database() {
|
||||
return Settings.getSetting('security', 'e2eedb', 'e2ekvps').value;
|
||||
}
|
||||
|
||||
encrypt(key, content, prefix = '') {
|
||||
if (!key) {
|
||||
// Encrypt something with master
|
||||
|
@ -138,7 +130,7 @@ export default new class E2EE extends BuiltinModule {
|
|||
const items = Settings.getSetting('security', 'e2eedb', 'e2ekvps').items;
|
||||
const index = items.findIndex(kvp => kvp.value.key === channelId);
|
||||
if (index > -1) {
|
||||
items[index].value = {key: channelId, value: key};
|
||||
items[index].value = { key: channelId, value: key };
|
||||
return;
|
||||
}
|
||||
Settings.getSetting('security', 'e2eedb', 'e2ekvps').addItem({ value: { key: channelId, value: key } });
|
||||
|
@ -172,6 +164,49 @@ export default new class E2EE extends BuiltinModule {
|
|||
}
|
||||
}
|
||||
|
||||
/* Patches */
|
||||
async applyPatches() {
|
||||
if (this.patches.length) return;
|
||||
|
||||
const { Dispatcher } = Reflection.modules;
|
||||
this.patch(Dispatcher, 'dispatch', this.dispatcherPatch, 'before');
|
||||
this.patchMessageContent();
|
||||
|
||||
const ChannelTextArea = await ReactComponents.getComponent('ChannelTextArea', { selector: Reflection.resolve('channelTextArea', 'emojiButton').selector });
|
||||
this.patchChannelTextArea(ChannelTextArea);
|
||||
this.patchChannelTextAreaSubmit(ChannelTextArea);
|
||||
ChannelTextArea.forceUpdateAll();
|
||||
}
|
||||
|
||||
dispatcherPatch(_, [event]) {
|
||||
if (!event || event.type !== 'MESSAGE_CREATE') return;
|
||||
|
||||
const key = this.getKey(event.message.channel_id);
|
||||
if (!key) return; // We don't have a key for this channel
|
||||
|
||||
if (typeof event.message.content !== 'string') return; // Ignore any non string content
|
||||
if (!event.message.content.startsWith('$:')) return; // Not an encrypted string
|
||||
if (!Security.isBase64(event.message.content.slice(2))) return;
|
||||
let decrypt;
|
||||
try {
|
||||
decrypt = this.decrypt(this.decrypt(this.decrypt(seed, this.master), key), event.message.content);
|
||||
} catch (err) { return } // Ignore errors such as non empty
|
||||
|
||||
const { MessageParser, Permissions, DiscordConstants } = Reflection.modules;
|
||||
|
||||
const currentChannel = DiscordApi.Channel.fromId(event.message.channel_id).discordObject;
|
||||
|
||||
// Create a generic message object to parse mentions with
|
||||
const parsed = MessageParser.parse(currentChannel, decrypt).content;
|
||||
|
||||
if (userMentionPattern.test(parsed))
|
||||
event.message.mentions = parsed.match(userMentionPattern).map(m => { return { id: m.replace(/[^0-9]/g, '') } });
|
||||
if (roleMentionPattern.test(parsed))
|
||||
event.message.mention_roles = parsed.match(roleMentionPattern).map(m => m.replace(/[^0-9]/g, ''));
|
||||
if (everyoneMentionPattern.test(parsed))
|
||||
event.message.mention_everyone = Permissions.can(DiscordConstants.Permissions.MENTION_EVERYONE, currentChannel);
|
||||
}
|
||||
|
||||
async handlePublicKey(e) {
|
||||
if (!DiscordApi.currentChannel) return;
|
||||
if (DiscordApi.currentChannel.type !== 'DM') return;
|
||||
|
@ -204,7 +239,7 @@ export default new class E2EE extends BuiltinModule {
|
|||
if (!ECDH_STORAGE.hasOwnProperty(channelId)) {
|
||||
const publicKeyMessage = `\`\`\`\n-----BEGIN PUBLIC KEY-----\n${this.createKeyExchange(channelId)}\n-----END PUBLIC KEY-----\n\`\`\``;
|
||||
if (this.encryptNewMessages) this.encryptNewMessages = false;
|
||||
WebpackModules.getModuleByName('DraftActions').saveDraft(channelId, publicKeyMessage);
|
||||
Reflection.modules.DraftActions.saveDraft(channelId, publicKeyMessage);
|
||||
}
|
||||
const secret = this.computeSecret(channelId, key);
|
||||
this.setKey(channelId, secret);
|
||||
|
@ -217,46 +252,13 @@ export default new class E2EE extends BuiltinModule {
|
|||
}
|
||||
}
|
||||
|
||||
patchDispatcher() {
|
||||
const Dispatcher = WebpackModules.getModuleByName('Dispatcher');
|
||||
MonkeyPatch('BD:E2EE', Dispatcher).before('dispatch', (_, [event]) => {
|
||||
if (event.type !== 'MESSAGE_CREATE') return;
|
||||
|
||||
const key = this.getKey(event.message.channel_id);
|
||||
if (!key) return; // We don't have a key for this channel
|
||||
|
||||
if (typeof event.message.content !== 'string') return; // Ignore any non string content
|
||||
if (!event.message.content.startsWith('$:')) return; // Not an encrypted string
|
||||
if (!Security.isBase64(event.message.content.slice(2))) return;
|
||||
let decrypt;
|
||||
try {
|
||||
decrypt = this.decrypt(this.decrypt(this.decrypt(seed, this.master), key), event.message.content);
|
||||
} catch (err) { return } // Ignore errors such as non empty
|
||||
|
||||
const MessageParser = WebpackModules.getModuleByName('MessageParser');
|
||||
const Permissions = WebpackModules.getModuleByName('GuildPermissions');
|
||||
const DiscordConstants = WebpackModules.getModuleByName('DiscordConstants');
|
||||
const currentChannel = DiscordApi.Channel.fromId(event.message.channel_id).discordObject;
|
||||
|
||||
// Create a generic message object to parse mentions with
|
||||
const parsed = MessageParser.parse(currentChannel, decrypt).content;
|
||||
|
||||
if (userMentionPattern.test(parsed))
|
||||
event.message.mentions = parsed.match(userMentionPattern).map(m => {return {id: m.replace(/[^0-9]/g, '')}});
|
||||
if (roleMentionPattern.test(parsed))
|
||||
event.message.mention_roles = parsed.match(roleMentionPattern).map(m => m.replace(/[^0-9]/g, ''));
|
||||
if (everyoneMentionPattern.test(parsed))
|
||||
event.message.mention_everyone = Permissions.can(DiscordConstants.Permissions.MENTION_EVERYONE, currentChannel);
|
||||
});
|
||||
}
|
||||
|
||||
async patchMessageContent() {
|
||||
const selector = `.${WebpackModules.getClassName('container', 'containerCozy', 'containerCompact', 'edited')}`;
|
||||
const MessageContent = await ReactComponents.getComponent('MessageContent', { selector });
|
||||
MonkeyPatch('BD:E2EE', MessageContent.component.prototype).before('render', this.beforeRenderMessageContent.bind(this));
|
||||
MonkeyPatch('BD:E2EE', MessageContent.component.prototype).after('render', this.renderMessageContent.bind(this));
|
||||
const ImageWrapper = await ReactComponents.getComponent('ImageWrapper', { selector: `.${WebpackModules.getClassName('imageWrapper')}` });
|
||||
MonkeyPatch('BD:E2EE', ImageWrapper.component.prototype).before('render', this.beforeRenderImageWrapper.bind(this));
|
||||
const MessageContent = await ReactComponents.getComponent('MessageContent', { selector: Reflection.resolve('container', 'containerCozy', 'containerCompact', 'edited').selector });
|
||||
this.patch(MessageContent.component.prototype, 'render', this.beforeRenderMessageContent, 'before');
|
||||
this.patch(MessageContent.component.prototype, 'render', this.afterRenderMessageContent);
|
||||
|
||||
const ImageWrapper = await ReactComponents.getComponent('ImageWrapper', { selector: Reflection.resolve('imageWrapper').selector });
|
||||
this.patch(ImageWrapper.component.prototype, 'render', this.beforeRenderImageWrapper, 'before');
|
||||
}
|
||||
|
||||
beforeRenderMessageContent(component) {
|
||||
|
@ -265,10 +267,8 @@ export default new class E2EE extends BuiltinModule {
|
|||
const key = this.getKey(component.props.message.channel_id);
|
||||
if (!key) return; // We don't have a key for this channel
|
||||
|
||||
const Message = WebpackModules.getModuleByPrototypes(['isMentioned']);
|
||||
const MessageParser = WebpackModules.getModuleByName('MessageParser');
|
||||
const Permissions = WebpackModules.getModuleByName('GuildPermissions');
|
||||
const DiscordConstants = WebpackModules.getModuleByName('DiscordConstants');
|
||||
const Message = Reflection.module.byPrototypes('isMentioned');
|
||||
const { MessageParser, Permissions, DiscordConstants } = Reflection.modules;
|
||||
const currentChannel = DiscordApi.Channel.fromId(component.props.message.channel_id).discordObject;
|
||||
|
||||
if (typeof component.props.message.content !== 'string') return; // Ignore any non string content
|
||||
|
@ -285,7 +285,7 @@ export default new class E2EE extends BuiltinModule {
|
|||
const message = MessageParser.createMessage(currentChannel.id, MessageParser.parse(currentChannel, decrypt).content);
|
||||
|
||||
if (userMentionPattern.test(message.content))
|
||||
message.mentions = message.content.match(userMentionPattern).map(m => {return {id: m.replace(/[^0-9]/g, '')}});
|
||||
message.mentions = message.content.match(userMentionPattern).map(m => { return { id: m.replace(/[^0-9]/g, '') } });
|
||||
if (roleMentionPattern.test(message.content))
|
||||
message.mention_roles = message.content.match(roleMentionPattern).map(m => m.replace(/[^0-9]/g, ''));
|
||||
if (everyoneMentionPattern.test(message.content))
|
||||
|
@ -303,7 +303,7 @@ export default new class E2EE extends BuiltinModule {
|
|||
component.props.message.contentParsed = create.contentParsed;
|
||||
}
|
||||
|
||||
renderMessageContent(component, args, retVal) {
|
||||
afterRenderMessageContent(component, args, retVal) {
|
||||
if (!component.props.message.bd_encrypted) return;
|
||||
const buttons = Utils.findInReactTree(retVal, m => Array.isArray(m) && m[1] && m[1].props && m[1].props.currentUserId);
|
||||
if (!buttons) return;
|
||||
|
@ -384,7 +384,7 @@ export default new class E2EE extends BuiltinModule {
|
|||
}
|
||||
|
||||
patchChannelTextArea(cta) {
|
||||
MonkeyPatch('BD:E2EE', cta.component.prototype).after('render', this.renderChannelTextArea);
|
||||
this.patch(cta.component.prototype, 'render', this.renderChannelTextArea);
|
||||
}
|
||||
|
||||
renderChannelTextArea(component, args, retVal) {
|
||||
|
@ -394,7 +394,7 @@ export default new class E2EE extends BuiltinModule {
|
|||
}
|
||||
|
||||
patchChannelTextAreaSubmit(cta) {
|
||||
MonkeyPatch('BD:E2EE', cta.component.prototype).before('handleSubmit', this.handleChannelTextAreaSubmit.bind(this));
|
||||
this.patch(cta.component.prototype, 'handleSubmit', this.handleChannelTextAreaSubmit, 'before');
|
||||
}
|
||||
|
||||
handleChannelTextAreaSubmit(component, args, retVal) {
|
||||
|
|
|
@ -10,7 +10,11 @@
|
|||
|
||||
<template>
|
||||
<div class="bd-e2eeTaContainer">
|
||||
<v-popover popoverClass="bd-popover bd-e2eePopover" placement="top">
|
||||
<v-popover :popoverClass="['bd-popover', 'bd-e2eePopover', {'bd-e2eePopoverOver': popoutPositionSetting.value === 'over'}]"
|
||||
:trigger="popoutPositionSetting.value === 'over' && popoutTriggerSetting.value === 'hover' ? 'hover' : 'click'"
|
||||
:placement="popoutPositionSetting.value === 'over' ? 'top-start' : 'top'"
|
||||
:disabled="error && DiscordApi.currentChannel.type !== 'DM'">
|
||||
|
||||
<div v-if="error" class="bd-e2eeTaBtn bd-e2eeLock bd-error">
|
||||
<MiLock v-tooltip="error" />
|
||||
</div>
|
||||
|
@ -27,9 +31,9 @@
|
|||
</div>
|
||||
|
||||
<template slot="popover">
|
||||
<div @click="toggleEncrypt" :class="{'bd-warn': !E2EE.encryptNewMessages, 'bd-ok': E2EE.encryptNewMessages}"><MiLock size="16" v-tooltip="'Toggle Encryption'" /></div>
|
||||
<div v-close-popover @click="showUploadDialog" v-if="!error"><MiImagePlus size="16" v-tooltip="'Upload Encrypted Image'" /></div>
|
||||
<div v-close-popover @click="generatePublicKey" v-if="DiscordApi.currentChannel.type === 'DM'"><MiIcVpnKey size="16" v-tooltip="'Begin Key Exchange'" /></div>
|
||||
<div @click="toggleEncrypt" :class="{'bd-warn': !E2EE.encryptNewMessages, 'bd-ok': E2EE.encryptNewMessages}"><MiLock v-tooltip="'Toggle Encryption'" /></div>
|
||||
<div v-close-popover @click="showUploadDialog" v-if="!error"><MiImagePlus v-tooltip="'Upload Encrypted Image'" /></div>
|
||||
<div v-close-popover @click="generatePublicKey" v-if="DiscordApi.currentChannel.type === 'DM'"><MiIcVpnKey v-tooltip="'Begin Key Exchange'" /></div>
|
||||
</template>
|
||||
</v-popover>
|
||||
<div class="bd-taDivider"></div>
|
||||
|
@ -39,7 +43,7 @@
|
|||
<script>
|
||||
import { Utils, FileUtils, ClientIPC } from 'common';
|
||||
import { E2EE } from 'builtin';
|
||||
import { DiscordApi, WebpackModules } from 'modules';
|
||||
import { Settings, DiscordApi, Reflection } from 'modules';
|
||||
import { Toasts } from 'ui';
|
||||
import { MiLock, MiImagePlus, MiIcVpnKey } from '../ui/components/common/MaterialIcon';
|
||||
|
||||
|
@ -50,9 +54,11 @@
|
|||
data() {
|
||||
return {
|
||||
E2EE,
|
||||
DiscordApi,
|
||||
state: 'loading',
|
||||
error: null,
|
||||
DiscordApi
|
||||
popoutPositionSetting: Settings.getSetting('security', 'e2ee-popout', 'position'),
|
||||
popoutTriggerSetting: Settings.getSetting('security', 'e2ee-popout', 'trigger')
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
@ -61,8 +67,8 @@
|
|||
if (!dialogResult || !dialogResult.length) return;
|
||||
|
||||
const readFile = await FileUtils.readFileBuffer(dialogResult[0]);
|
||||
const FileActions = WebpackModules.getModuleByProps(['makeFile']);
|
||||
const Uploader = WebpackModules.getModuleByProps(['instantBatchUpload']);
|
||||
const FileActions = Reflection.module.byProps('makeFile');
|
||||
const Uploader = Reflection.module.byProps('instantBatchUpload');
|
||||
|
||||
const img = await Utils.getImageFromBuffer(readFile);
|
||||
|
||||
|
@ -93,7 +99,7 @@
|
|||
E2EE.preExchangeState = E2EE.encryptNewMessages;
|
||||
E2EE.encryptNewMessages = false; // Disable encrypting new messages so we won't encrypt public keys
|
||||
const publicKeyMessage = `\`\`\`\n-----BEGIN PUBLIC KEY-----\n${keyExchange}\n-----END PUBLIC KEY-----\n\`\`\``;
|
||||
WebpackModules.getModuleByName('DraftActions').saveDraft(DiscordApi.currentChannel.id, publicKeyMessage);
|
||||
Reflection.modules.DraftActions.saveDraft(DiscordApi.currentChannel.id, publicKeyMessage);
|
||||
Toasts.info('Key exchange started. Expires in 30 seconds');
|
||||
}
|
||||
},
|
||||
|
|
|
@ -13,6 +13,7 @@ import { Settings } from 'modules';
|
|||
import BuiltinModule from './BuiltinModule';
|
||||
import EmoteModule from './EmoteModule';
|
||||
import GlobalAc from '../ui/autocomplete';
|
||||
import { BdContextMenu } from 'ui';
|
||||
|
||||
const EMOTE_SOURCES = [
|
||||
'https://static-cdn.jtvnw.net/emoticons/v1/:id/1.0',
|
||||
|
@ -22,14 +23,46 @@ const EMOTE_SOURCES = [
|
|||
|
||||
export default new class EmoteAc extends BuiltinModule {
|
||||
|
||||
/* Getters */
|
||||
get moduleName() { return 'EmoteAC' }
|
||||
get settingPath() { return ['emotes', 'default', 'emoteac'] }
|
||||
|
||||
async enabled(e) {
|
||||
GlobalAc.add(';', this);
|
||||
window.removeEventListener('contextmenu', this.acCm);
|
||||
window.addEventListener('contextmenu', this.acCm);
|
||||
}
|
||||
|
||||
disabled(e) {
|
||||
GlobalAc.remove(';');
|
||||
window.removeEventListener('contextmenu', this.acCm);
|
||||
}
|
||||
|
||||
/* Methods */
|
||||
acCm(e) {
|
||||
const row = e.target.closest('.bd-emotAc');
|
||||
if (!row) return;
|
||||
const img = row.querySelector('img');
|
||||
if (!img || !img.alt) return;
|
||||
|
||||
BdContextMenu.show(e, [
|
||||
{
|
||||
text: 'Test',
|
||||
items: [
|
||||
{
|
||||
text: 'Favourite',
|
||||
type: 'toggle',
|
||||
checked: EmoteModule.isFavourite(img.alt.replace(/;/g, '')),
|
||||
onChange: checked => {
|
||||
if (!img || !img.alt) return;
|
||||
const emote = img.alt.replace(/;/g, '');
|
||||
if (!checked) return EmoteModule.removeFavourite(emote), false;
|
||||
return EmoteModule.addFavourite(emote), true;
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,7 +84,8 @@ export default new class EmoteAc extends BuiltinModule {
|
|||
hint: mu.useCount ? `Used ${mu.useCount} times` : null
|
||||
}
|
||||
}
|
||||
})
|
||||
}),
|
||||
extraClasses: ['bd-emotAc']
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,7 +97,8 @@ export default new class EmoteAc extends BuiltinModule {
|
|||
result.value.src = EMOTE_SOURCES[result.value.type].replace(':id', result.value.id);
|
||||
result.value.replaceWith = `;${result.key};`;
|
||||
return result;
|
||||
})
|
||||
}),
|
||||
extraClasses: ['bd-emotAc']
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,9 +12,9 @@ import BuiltinModule from './BuiltinModule';
|
|||
import path from 'path';
|
||||
import { request } from 'vendor';
|
||||
|
||||
import { Utils, FileUtils, ClientLogger as Logger } from 'common';
|
||||
import { DiscordApi, Settings, Globals, WebpackModules, ReactComponents, MonkeyPatch, Cache, Patcher, Database } from 'modules';
|
||||
import { VueInjector, DiscordContextMenu } from 'ui';
|
||||
import { Utils, FileUtils } from 'common';
|
||||
import { DiscordApi, Settings, Globals, Reflection, ReactComponents, Database } from 'modules';
|
||||
import { DiscordContextMenu } from 'ui';
|
||||
|
||||
import Emote from './EmoteComponent.js';
|
||||
import Autocomplete from '../ui/components/common/Autocomplete.vue';
|
||||
|
@ -29,6 +29,10 @@ const EMOTE_SOURCES = [
|
|||
|
||||
export default new class EmoteModule extends BuiltinModule {
|
||||
|
||||
/* Getters */
|
||||
|
||||
get moduleName() { return 'EmoteModule' }
|
||||
|
||||
/**
|
||||
* @returns {String} Path to local emote database
|
||||
*/
|
||||
|
@ -76,13 +80,14 @@ export default new class EmoteModule extends BuiltinModule {
|
|||
|
||||
// Read favourites and most used from database
|
||||
await this.loadUserData();
|
||||
|
||||
this.patchMessageContent();
|
||||
this.patchSendAndEdit();
|
||||
const ImageWrapper = await ReactComponents.getComponent('ImageWrapper', { selector: WebpackModules.getSelector('imageWrapper') });
|
||||
MonkeyPatch('BD:EMOTEMODULE', ImageWrapper.component.prototype).after('render', this.beforeRenderImageWrapper.bind(this));
|
||||
}
|
||||
|
||||
async disabled() {
|
||||
DiscordContextMenu.remove(this.favCm);
|
||||
}
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* Adds an emote to favourites.
|
||||
* @param {Object|String} emote
|
||||
|
@ -115,12 +120,6 @@ export default new class EmoteModule extends BuiltinModule {
|
|||
return !!this.favourites.find(e => e.name === emote || e.name === emote.name);
|
||||
}
|
||||
|
||||
async disabled() {
|
||||
// Unpatch all patches
|
||||
for (const patch of Patcher.getPatchesByCaller('BD:EMOTEMODULE')) patch.unpatch();
|
||||
DiscordContextMenu.remove(this.favCm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load emotes from local database
|
||||
*/
|
||||
|
@ -151,15 +150,96 @@ export default new class EmoteModule extends BuiltinModule {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add/update emote to most used
|
||||
* @param {Object} emote emote to add/update
|
||||
* @return {Promise}
|
||||
*/
|
||||
addToMostUsed(emote) {
|
||||
const isMostUsed = this.mostUsed.find(mu => mu.key === emote.name);
|
||||
if (isMostUsed) {
|
||||
isMostUsed.useCount += 1;
|
||||
} else {
|
||||
this.mostUsed.push({
|
||||
key: emote.name,
|
||||
id: emote.id,
|
||||
type: emote.type,
|
||||
useCount: 1
|
||||
});
|
||||
}
|
||||
// Save most used to database
|
||||
// TODO only save first n
|
||||
return this.saveUserData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an emote by name
|
||||
* @param {String} name Emote name
|
||||
* @param {Boolean} simple Simple object or Emote instance
|
||||
* @returns {Object|Emote}
|
||||
*/
|
||||
findByName(name, simple = false) {
|
||||
const emote = this.database.get(name);
|
||||
if (!emote) return null;
|
||||
return this.parseEmote(name, emote, simple);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse emote object
|
||||
* @param {String} name Emote name
|
||||
* @param {Object} emote Emote object
|
||||
* @param {Boolean} simple Simple object or Emote instance
|
||||
* @returns {Object|Emote}
|
||||
*/
|
||||
parseEmote(name, emote, simple = false) {
|
||||
const { type, id } = emote;
|
||||
if (type < 0 || type > 2) return null;
|
||||
return simple ? { type, id, name } : new Emote(type, id, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for anything else
|
||||
* @param {any} regex
|
||||
* @param {any} limit
|
||||
*/
|
||||
search(regex, limit = 10) {
|
||||
if (typeof regex === 'string') regex = new RegExp(regex, 'i');
|
||||
const matching = [];
|
||||
|
||||
for (const [key, value] of this.database.entries()) {
|
||||
if (matching.length >= limit) break;
|
||||
if (regex.test(key)) matching.push({ key, value })
|
||||
}
|
||||
|
||||
return matching;
|
||||
}
|
||||
|
||||
/* Patches */
|
||||
async applyPatches() {
|
||||
this.patchMessageContent();
|
||||
this.patchSendAndEdit();
|
||||
const ImageWrapper = await ReactComponents.getComponent('ImageWrapper', { selector: Reflection.resolve('imageWrapper').selector });
|
||||
this.patch(ImageWrapper.component.prototype, 'render', this.beforeRenderImageWrapper, 'before');
|
||||
}
|
||||
|
||||
/**
|
||||
* Patches MessageContent render method
|
||||
*/
|
||||
async patchMessageContent() {
|
||||
const MessageContent = await ReactComponents.getComponent('MessageContent', { selector: WebpackModules.getSelector('container', 'containerCozy', 'containerCompact', 'edited') });
|
||||
MonkeyPatch('BD:EMOTEMODULE', MessageContent.component.prototype).after('render', this.afterRenderMessageContent.bind(this));
|
||||
const MessageContent = await ReactComponents.getComponent('MessageContent', { selector: Reflection.resolve('container', 'containerCozy', 'containerCompact', 'edited').selector });
|
||||
this.patch(MessageContent.component.prototype, 'render', this.afterRenderMessageContent);
|
||||
MessageContent.forceUpdateAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Patches MessageActions send and edit
|
||||
*/
|
||||
patchSendAndEdit() {
|
||||
const { MessageActions } = Reflection.modules;
|
||||
this.patch(MessageActions, 'sendMessage', this.handleSendMessage, 'instead');
|
||||
this.patch(MessageActions, 'editMessage', this.handleEditMessage, 'instead');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle message render
|
||||
*/
|
||||
|
@ -173,14 +253,6 @@ export default new class EmoteModule extends BuiltinModule {
|
|||
markup.children[1] = this.processMarkup(markup.children[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Patches MessageActions send and edit
|
||||
*/
|
||||
patchSendAndEdit() {
|
||||
MonkeyPatch('BD:EMOTEMODULE', WebpackModules.getModuleByName('MessageActions')).instead('sendMessage', this.handleSendMessage.bind(this));
|
||||
MonkeyPatch('BD:EMOTEMODULE', WebpackModules.getModuleByName('MessageActions')).instead('editMessage', this.handleEditMessage.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle send message
|
||||
*/
|
||||
|
@ -213,8 +285,8 @@ export default new class EmoteModule extends BuiltinModule {
|
|||
if (!emote) return orig(...args);
|
||||
this.addToMostUsed(emote);
|
||||
|
||||
const FileActions = WebpackModules.getModuleByProps(['makeFile']);
|
||||
const Uploader = WebpackModules.getModuleByProps(['instantBatchUpload']);
|
||||
const FileActions = Reflection.module.byProps('makeFile');
|
||||
const Uploader = Reflection.module.byProps('instantBatchUpload');
|
||||
|
||||
request.get(emote.props.src, { encoding: 'binary' }).then(res => {
|
||||
const arr = new Uint8Array(new ArrayBuffer(res.length));
|
||||
|
@ -252,28 +324,6 @@ export default new class EmoteModule extends BuiltinModule {
|
|||
retVal.props.children = emote.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add/update emote to most used
|
||||
* @param {Object} emote emote to add/update
|
||||
* @return {Promise}
|
||||
*/
|
||||
addToMostUsed(emote) {
|
||||
const isMostUsed = this.mostUsed.find(mu => mu.key === emote.name);
|
||||
if (isMostUsed) {
|
||||
isMostUsed.useCount += 1;
|
||||
} else {
|
||||
this.mostUsed.push({
|
||||
key: emote.name,
|
||||
id: emote.id,
|
||||
type: emote.type,
|
||||
useCount: 1
|
||||
});
|
||||
}
|
||||
// Save most used to database
|
||||
// TODO only save first n
|
||||
return this.saveUserData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject emotes into markup
|
||||
*/
|
||||
|
@ -329,46 +379,4 @@ export default new class EmoteModule extends BuiltinModule {
|
|||
return newMarkup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an emote by name
|
||||
* @param {String} name Emote name
|
||||
* @param {Boolean} simple Simple object or Emote instance
|
||||
* @returns {Object|Emote}
|
||||
*/
|
||||
findByName(name, simple = false) {
|
||||
const emote = this.database.get(name);
|
||||
if (!emote) return null;
|
||||
return this.parseEmote(name, emote, simple);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse emote object
|
||||
* @param {String} name Emote name
|
||||
* @param {Object} emote Emote object
|
||||
* @param {Boolean} simple Simple object or Emote instance
|
||||
* @returns {Object|Emote}
|
||||
*/
|
||||
parseEmote(name, emote, simple = false) {
|
||||
const { type, id } = emote;
|
||||
if (type < 0 || type > 2) return null;
|
||||
return simple ? { type, id, name } : new Emote(type, id, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for anything else
|
||||
* @param {any} regex
|
||||
* @param {any} limit
|
||||
*/
|
||||
search(regex, limit = 10) {
|
||||
if (typeof regex === 'string') regex = new RegExp(regex, 'i');
|
||||
const matching = [];
|
||||
|
||||
for (const [key, value] of this.database.entries()) {
|
||||
if (matching.length >= limit) break;
|
||||
if (regex.test(key)) matching.push({ key, value })
|
||||
}
|
||||
|
||||
return matching;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,22 +9,20 @@
|
|||
*/
|
||||
|
||||
import BuiltinModule from './BuiltinModule';
|
||||
import { Patcher, MonkeyPatch, WebpackModules } from 'modules';
|
||||
import { Reflection } from 'modules';
|
||||
|
||||
export default new class KillClyde extends BuiltinModule {
|
||||
|
||||
get settingPath() {
|
||||
return ['ui', 'default', 'kill-clyde'];
|
||||
}
|
||||
/* Getters */
|
||||
get moduleName() { return 'KillClyde' }
|
||||
|
||||
async enabled(e) {
|
||||
if (Patcher.getPatchesByCaller('BD:KillClyde').length) return;
|
||||
const MessageActions = WebpackModules.getModuleByName('MessageActions');
|
||||
MonkeyPatch('BD:KillClyde', MessageActions).instead('sendBotMessage', void 0);
|
||||
}
|
||||
get settingPath() { return ['ui', 'default', 'kill-clyde'] }
|
||||
|
||||
disabled(e) {
|
||||
Patcher.unpatchAll('BD:KillClyde');
|
||||
/* Patches */
|
||||
applyPatches() {
|
||||
if (this.patches.length) return;
|
||||
const { MessageActions } = Reflection.modules;
|
||||
this.patch(MessageActions, 'sendBotMessage', () => void 0, 'instead');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,15 +18,16 @@ import { Toasts } from 'ui';
|
|||
|
||||
export default new class ReactDevtoolsModule extends BuiltinModule {
|
||||
|
||||
/* Getters */
|
||||
get moduleName() { return 'ReactDevTools' }
|
||||
|
||||
get settingPath() { return ['core', 'advanced', 'react-devtools'] }
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.devToolsOpened = this.devToolsOpened.bind(this);
|
||||
}
|
||||
|
||||
get settingPath() {
|
||||
return ['core', 'advanced', 'react-devtools'];
|
||||
}
|
||||
|
||||
enabled(e) {
|
||||
electron.remote.getCurrentWindow().webContents.on('devtools-opened', this.devToolsOpened);
|
||||
if (electron.remote.getCurrentWindow().isDevToolsOpened) this.devToolsOpened();
|
||||
|
|
|
@ -10,27 +10,27 @@
|
|||
|
||||
import BuiltinModule from './BuiltinModule';
|
||||
|
||||
import { Patcher, MonkeyPatch, WebpackModules } from 'modules';
|
||||
import { Reflection } from 'modules';
|
||||
|
||||
export default new class E2EE extends BuiltinModule {
|
||||
|
||||
get settingPath() {
|
||||
return ['security', 'default', 'tracking-protection'];
|
||||
/* Getters */
|
||||
get moduleName() { return 'TrackingProtection' }
|
||||
|
||||
get settingPath() { return ['security', 'default', 'tracking-protection'] }
|
||||
|
||||
/* Patches */
|
||||
applyPatches() {
|
||||
if (this.patches.length) return;
|
||||
const TrackingModule = Reflection.module.byProps('track');
|
||||
if (!TrackingModule) {
|
||||
this.warn('Tracking module not found!');
|
||||
return;
|
||||
}
|
||||
this.patch(TrackingModule, 'track', this.track, 'instead');
|
||||
}
|
||||
|
||||
track(e) {
|
||||
// console.log('Blocked Tracking');
|
||||
this.debug('Tracking blocked');
|
||||
}
|
||||
|
||||
enabled(e) {
|
||||
if (Patcher.getPatchesByCaller('BD:TrackingProtection').length) return;
|
||||
const trackingModule = WebpackModules.getModuleByProps(['track']);
|
||||
if (!trackingModule) return; // TODO Log it
|
||||
MonkeyPatch('BD:TrackingProtection', trackingModule).instead('track', this.track);
|
||||
}
|
||||
|
||||
disabled(e) {
|
||||
for (const patch of Patcher.getPatchesByCaller('BD:TrackingProtection')) patch.unpatch();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,25 +9,27 @@
|
|||
*/
|
||||
|
||||
import BuiltinModule from './BuiltinModule';
|
||||
import { WebpackModules } from 'modules';
|
||||
import { Reflection } from 'modules';
|
||||
|
||||
export default new class VoiceDisconnect extends BuiltinModule {
|
||||
|
||||
get settingPath() {
|
||||
return ['core', 'default', 'voice-disconnect'];
|
||||
}
|
||||
/* Getters */
|
||||
get moduleName() { return 'VoiceDisconnect' }
|
||||
|
||||
get settingPath() { return ['core', 'default', 'voice-disconnect'] }
|
||||
|
||||
async enabled(e) {
|
||||
window.addEventListener('beforeunload', this.listener);
|
||||
}
|
||||
|
||||
listener() {
|
||||
const VoiceChannelActions = WebpackModules.getModuleByName('VoiceChannelActions');
|
||||
VoiceChannelActions.selectVoiceChannel(null, null);
|
||||
}
|
||||
|
||||
disabled(e) {
|
||||
window.removeEventListener('beforeunload', this.listener);
|
||||
}
|
||||
|
||||
/* Methods */
|
||||
listener() {
|
||||
const { VoiceChannelActions } = Reflection.modules;
|
||||
VoiceChannelActions.selectVoiceChannel(null, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,15 +18,16 @@ import { Toasts } from 'ui';
|
|||
|
||||
export default new class VueDevtoolsModule extends BuiltinModule {
|
||||
|
||||
/* Getters */
|
||||
get moduleName() { return 'VueDevTools' }
|
||||
|
||||
get settingPath() { return ['core', 'advanced', 'vue-devtools'] }
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.devToolsOpened = this.devToolsOpened.bind(this);
|
||||
}
|
||||
|
||||
get settingPath() {
|
||||
return ['core', 'advanced', 'vue-devtools'];
|
||||
}
|
||||
|
||||
enabled(e) {
|
||||
electron.remote.getCurrentWindow().webContents.on('devtools-opened', this.devToolsOpened);
|
||||
if (electron.remote.getCurrentWindow().isDevToolsOpened) this.devToolsOpened();
|
||||
|
|
|
@ -252,10 +252,52 @@
|
|||
"settings": [
|
||||
{
|
||||
"id": "e2ekvps",
|
||||
"type": [ "securekvp" ],
|
||||
"type": ["securekvp"],
|
||||
"value": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "e2ee-popout",
|
||||
"name": "E2EE Popout",
|
||||
"type": "drawer",
|
||||
"settings": [
|
||||
{
|
||||
"id": "position",
|
||||
"type": "radio",
|
||||
"text": "Position",
|
||||
"value": "above",
|
||||
"fullwidth": true,
|
||||
"options": [
|
||||
{
|
||||
"text": "Above the lock icon",
|
||||
"value": "above"
|
||||
},
|
||||
{
|
||||
"text": "Over the lock icon",
|
||||
"value": "over"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "trigger",
|
||||
"type": "radio",
|
||||
"text": "Trigger",
|
||||
"hint": "Only applies when the popout is over the lock icon",
|
||||
"value": "click",
|
||||
"fullwidth": true,
|
||||
"options": [
|
||||
{
|
||||
"text": "After clicking the lock icon",
|
||||
"value": "click"
|
||||
},
|
||||
{
|
||||
"text": "When hovering over the lock icon",
|
||||
"value": "hover"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { DOM, BdUI, BdMenu, Modals, Reflection, Toasts, Notifications, BdContextMenu, DiscordContextMenu } from 'ui';
|
||||
import { DOM, BdUI, BdMenu, Modals, Toasts, Notifications, BdContextMenu, DiscordContextMenu } from 'ui';
|
||||
import BdCss from './styles/index.scss';
|
||||
import { Events, CssEditor, Globals, Settings, Database, Updater, ModuleManager, PluginManager, ThemeManager, ExtModuleManager, Vendor, WebpackModules, Patcher, MonkeyPatch, ReactComponents, ReactHelpers, ReactAutoPatcher, DiscordApi, BdWebApi, Connectivity, Cache } from 'modules';
|
||||
import { Events, CssEditor, Globals, Settings, Database, Updater, ModuleManager, PluginManager, ThemeManager, ExtModuleManager, Vendor, Patcher, MonkeyPatch, ReactComponents, ReactHelpers, ReactAutoPatcher, DiscordApi, BdWebApi, Connectivity, Cache, Reflection } from 'modules';
|
||||
import { ClientLogger as Logger, ClientIPC, Utils } from 'common';
|
||||
import { BuiltinManager, EmoteModule, ReactDevtoolsModule, VueDevtoolsModule, TrackingProtection, E2EE } from 'builtin';
|
||||
import electron from 'electron';
|
||||
|
@ -34,7 +34,7 @@ class BetterDiscord {
|
|||
ModuleManager, PluginManager, ThemeManager, ExtModuleManager,
|
||||
Vendor,
|
||||
|
||||
WebpackModules, Patcher, MonkeyPatch, ReactComponents, ReactHelpers, ReactAutoPatcher, DiscordApi,
|
||||
Patcher, MonkeyPatch, ReactComponents, ReactHelpers, ReactAutoPatcher, DiscordApi,
|
||||
EmoteModule,
|
||||
BdWebApi,
|
||||
Connectivity,
|
||||
|
@ -49,7 +49,7 @@ class BetterDiscord {
|
|||
module: Globals.require.cache[__filename],
|
||||
require: Globals.require,
|
||||
webpack_require: __webpack_require__, // eslint-disable-line no-undef
|
||||
get discord_require() { return WebpackModules.require }
|
||||
get discord_require() { return Reflection.require }
|
||||
};
|
||||
|
||||
const developermode = Settings.getSetting('core', 'advanced', 'developer-mode');
|
||||
|
|
|
@ -10,11 +10,11 @@
|
|||
|
||||
import { List } from 'structs';
|
||||
import { User, Channel, Guild, Message } from 'discordstructs';
|
||||
import { WebpackModules } from './webpackmodules';
|
||||
import Reflection from './reflection/index';
|
||||
|
||||
export const Modules = {
|
||||
_getModule(name) {
|
||||
const foundModule = WebpackModules.getModuleByName(name);
|
||||
const foundModule = Reflection.module.byName(name);
|
||||
if (!foundModule) return null;
|
||||
delete this[name];
|
||||
return this[name] = foundModule;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { WebpackModules } from './webpackmodules';
|
||||
import Reflection from './reflection/index';
|
||||
import { MonkeyPatch } from './patcher';
|
||||
import Events from './events';
|
||||
import EventListener from './eventlistener';
|
||||
|
@ -37,7 +37,7 @@ export default class extends EventListener {
|
|||
}
|
||||
|
||||
hook() {
|
||||
const Events = WebpackModules.getModuleByName('Events');
|
||||
const { Events } = Reflection.modules;
|
||||
MonkeyPatch('BD:EVENTS', Events.prototype).after('emit', (obj, args, retVal) => {
|
||||
const eventId = args.length >= 3 ? args[2].id || -1 : -1;
|
||||
if (eventId === this.ignoreMultiple) return;
|
||||
|
|
|
@ -26,3 +26,4 @@ export { default as BdWebApi } from './bdwebapi';
|
|||
export { default as Connectivity } from './connectivity';
|
||||
export { default as Security } from './security';
|
||||
export { default as Cache } from './cache';
|
||||
export { default as Reflection } from './reflection/index';
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { WebpackModules } from './webpackmodules';
|
||||
import Reflection from './reflection/index';
|
||||
import { ClientLogger as Logger } from 'common';
|
||||
|
||||
/**
|
||||
|
@ -64,7 +64,7 @@ export class Patcher {
|
|||
|
||||
static resolveModule(module) {
|
||||
if (module instanceof Function || (module instanceof Object)) return module;
|
||||
if (typeof module === 'string') return WebpackModules.getModuleByName(module);
|
||||
if (typeof module === 'string') return Reflection.module.byName(module);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import { EmoteModule } from 'builtin';
|
||||
import { SettingsSet, SettingsCategory, Setting, SettingsScheme } from 'structs';
|
||||
import { BdMenu, Modals, DOM, DOMObserver, Reflection, VueInjector, Toasts, Notifications, BdContextMenu, DiscordContextMenu } from 'ui';
|
||||
import { BdMenu, Modals, DOM, DOMObserver, VueInjector, Toasts, Notifications, BdContextMenu, DiscordContextMenu } from 'ui';
|
||||
import * as CommonComponents from 'commoncomponents';
|
||||
import { Utils, Filters, ClientLogger as Logger, ClientIPC, AsyncEventEmitter } from 'common';
|
||||
import Settings from './settings';
|
||||
|
@ -19,7 +19,7 @@ import PluginManager from './pluginmanager';
|
|||
import ThemeManager from './thememanager';
|
||||
import Events from './events';
|
||||
import EventsWrapper from './eventswrapper';
|
||||
import { WebpackModules } from './webpackmodules';
|
||||
import Reflection from './reflection/index';
|
||||
import DiscordApi from './discordapi';
|
||||
import { ReactComponents, ReactHelpers } from './reactcomponents';
|
||||
import { Patcher, MonkeyPatch } from './patcher';
|
||||
|
@ -542,92 +542,6 @@ export default class PluginApi {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* WebpackModules
|
||||
*/
|
||||
|
||||
get webpackRequire() {
|
||||
return WebpackModules.require;
|
||||
}
|
||||
getWebpackModule(filter, first = true) {
|
||||
return WebpackModules.getModule(filter, first);
|
||||
}
|
||||
getWebpackModuleByName(name, fallback) {
|
||||
return WebpackModules.getModuleByName(name, fallback);
|
||||
}
|
||||
getWebpackModuleByDisplayName(name) {
|
||||
return WebpackModules.getModuleByDisplayName(name);
|
||||
}
|
||||
getWebpackModuleByRegex(regex) {
|
||||
return WebpackModules.getModuleByRegex(regex, true);
|
||||
}
|
||||
getWebpackModulesByRegex(regex) {
|
||||
return WebpackModules.getModuleByRegex(regex, false);
|
||||
}
|
||||
getWebpackModuleByProperties(...props) {
|
||||
return WebpackModules.getModuleByProps(props, true);
|
||||
}
|
||||
getWebpackModuleByPrototypeFields(...props) {
|
||||
return WebpackModules.getModuleByPrototypes(props, true);
|
||||
}
|
||||
getWebpackModulesByProperties(...props) {
|
||||
return WebpackModules.getModuleByProps(props, false);
|
||||
}
|
||||
getWebpackModulesByPrototypeFields(...props) {
|
||||
return WebpackModules.getModuleByPrototypes(props, false);
|
||||
}
|
||||
waitForWebpackModule(filter) {
|
||||
return WebpackModules.waitForModule(filter);
|
||||
}
|
||||
waitForWebpackModuleByName(name, fallback) {
|
||||
return WebpackModules.waitForModuleByName(name, fallback);
|
||||
}
|
||||
waitForWebpackModuleByDisplayName(name) {
|
||||
return WebpackModules.waitForModuleByDisplayName(name);
|
||||
}
|
||||
waitForWebpackModuleByRegex(regex) {
|
||||
return WebpackModules.waitForModuleByRegex(regex);
|
||||
}
|
||||
waitForWebpackModuleByProperties(...props) {
|
||||
return WebpackModules.waitForModuleByProps(props);
|
||||
}
|
||||
waitForWebpackModuleByPrototypeFields(...props) {
|
||||
return WebpackModules.waitForModuleByPrototypes(props);
|
||||
}
|
||||
getWebpackClassName(...classes) {
|
||||
return WebpackModules.getClassName(...classes);
|
||||
}
|
||||
waitForWebpackClassName(...classes) {
|
||||
return WebpackModules.waitForClassName(...classes);
|
||||
}
|
||||
get WebpackModules() {
|
||||
return new Proxy({
|
||||
getModule: this.getWebpackModule.bind(this),
|
||||
getModuleByName: this.getWebpackModuleByName.bind(this),
|
||||
getModuleByDisplayName: this.getWebpackModuleByDisplayName.bind(this),
|
||||
getModuleByRegex: this.getWebpackModuleByRegex.bind(this),
|
||||
getModulesByRegex: this.getWebpackModulesByRegex.bind(this),
|
||||
getModuleByProperties: this.getWebpackModuleByProperties.bind(this),
|
||||
getModuleByPrototypeFields: this.getWebpackModuleByPrototypeFields.bind(this),
|
||||
getModulesByProperties: this.getWebpackModulesByProperties.bind(this),
|
||||
getModulesByPrototypeFields: this.getWebpackModulesByPrototypeFields.bind(this),
|
||||
waitForModule: this.waitForWebpackModule.bind(this),
|
||||
waitForModuleByName: this.waitForWebpackModuleByName.bind(this),
|
||||
waitForModuleByDisplayName: this.waitForWebpackModuleByDisplayName.bind(this),
|
||||
waitForModuleByRegex: this.waitForWebpackModuleByRegex.bind(this),
|
||||
waitForModuleByProperties: this.waitForWebpackModuleByProperties.bind(this),
|
||||
waitForModuleByPrototypeFields: this.waitForWebpackModuleByPrototypeFields.bind(this),
|
||||
getClassName: this.getWebpackClassName.bind(this),
|
||||
waitForClassName: this.waitForWebpackClassName.bind(this),
|
||||
get KnownModules() { return WebpackModules.KnownModules },
|
||||
get require() { return WebpackModules.require }
|
||||
}, {
|
||||
get(WebpackModules, property) {
|
||||
return WebpackModules[property] || WebpackModules.getModuleByName(property);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Patcher
|
||||
*/
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { DOM, Reflection, Modals } from 'ui';
|
||||
import { DOM, Modals } from 'ui';
|
||||
import { Utils, Filters, ClientLogger as Logger } from 'common';
|
||||
import { MonkeyPatch } from './patcher';
|
||||
import { WebpackModules } from './webpackmodules';
|
||||
import Reflection from './reflection/index';
|
||||
import DiscordApi from './discordapi';
|
||||
|
||||
class Helpers {
|
||||
|
@ -153,11 +153,11 @@ class Helpers {
|
|||
}
|
||||
|
||||
static get React() {
|
||||
return WebpackModules.getModuleByName('React');
|
||||
return Reflection.modules.React;
|
||||
}
|
||||
|
||||
static get ReactDOM() {
|
||||
return WebpackModules.getModuleByName('ReactDOM');
|
||||
return Reflection.modules.ReactDOM;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,7 +174,7 @@ class ReactComponent {
|
|||
forceUpdateAll() {
|
||||
if (!this.important || !this.important.selector) return;
|
||||
for (const e of document.querySelectorAll(this.important.selector)) {
|
||||
Reflection(e).forceUpdate(this);
|
||||
Reflection.DOM(e).forceUpdate(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -236,7 +236,7 @@ export class ReactComponents {
|
|||
|
||||
let component, reflect;
|
||||
for (const element of elements) {
|
||||
reflect = Reflection(element);
|
||||
reflect = Reflection.DOM(element);
|
||||
component = filter ? reflect.components.find(filter) : reflect.component;
|
||||
if (component) break;
|
||||
}
|
||||
|
@ -310,7 +310,7 @@ export class ReactAutoPatcher {
|
|||
* Also patches some known components.
|
||||
*/
|
||||
static async autoPatch() {
|
||||
const React = await WebpackModules.waitForModuleByName('React');
|
||||
const React = await Reflection.module.waitForModuleByName('React');
|
||||
|
||||
this.unpatchCreateElement = MonkeyPatch('BD:ReactComponents:createElement', React).before('createElement', (component, args) => ReactComponents.push(args[0]));
|
||||
|
||||
|
@ -326,7 +326,7 @@ export class ReactAutoPatcher {
|
|||
}
|
||||
|
||||
static async patchMessage() {
|
||||
const selector = `.${WebpackModules.getClassName('message', 'messageCozy', 'messageCompact')}`;
|
||||
const { selector } = Reflection.resolve('message', 'messageCozy', 'messageCompact');
|
||||
this.Message = await ReactComponents.getComponent('Message', {selector}, m => m.prototype && m.prototype.renderCozy);
|
||||
|
||||
this.unpatchMessageRender = MonkeyPatch('BD:ReactComponents', this.Message.component.prototype).after('render', (component, args, retVal) => {
|
||||
|
@ -350,7 +350,7 @@ export class ReactAutoPatcher {
|
|||
}
|
||||
|
||||
static async patchMessageGroup() {
|
||||
const selector = `.${WebpackModules.getClassName('container', 'message', 'messageCozy')}`;
|
||||
const { selector } = Reflection.resolve('container', 'message', 'messageCozy');
|
||||
this.MessageGroup = await ReactComponents.getComponent('MessageGroup', {selector});
|
||||
|
||||
this.unpatchMessageGroupRender = MonkeyPatch('BD:ReactComponents', this.MessageGroup.component.prototype).after('render', (component, args, retVal) => {
|
||||
|
@ -368,7 +368,7 @@ export class ReactAutoPatcher {
|
|||
}
|
||||
|
||||
static async patchChannelMember() {
|
||||
const selector = `.${WebpackModules.getClassName('member', 'memberInner', 'activity')}`;
|
||||
const { selector } = Reflection.resolve('member', 'memberInner', 'activity');
|
||||
this.ChannelMember = await ReactComponents.getComponent('ChannelMember', {selector}, m => m.prototype.renderActivity);
|
||||
|
||||
this.unpatchChannelMemberRender = MonkeyPatch('BD:ReactComponents', this.ChannelMember.component.prototype).after('render', (component, args, retVal) => {
|
||||
|
@ -384,7 +384,7 @@ export class ReactAutoPatcher {
|
|||
}
|
||||
|
||||
static async patchGuild() {
|
||||
const selector = `div.${WebpackModules.getClassName('guild', 'guildsWrapper')}:not(:first-child)`;
|
||||
const selector = `div.${Reflection.resolve('guild', 'guildsWrapper').className}:not(:first-child)`;
|
||||
this.Guild = await ReactComponents.getComponent('Guild', {selector}, m => m.prototype.renderBadge);
|
||||
|
||||
this.unpatchGuild = MonkeyPatch('BD:ReactComponents', this.Guild.component.prototype).after('render', (component, args, retVal) => {
|
||||
|
@ -421,7 +421,7 @@ export class ReactAutoPatcher {
|
|||
* The GuildTextChannel component represents a text channel in the guild channel list.
|
||||
*/
|
||||
static async patchGuildTextChannel() {
|
||||
const selector = `.${WebpackModules.getClassName('containerDefault', 'actionIcon')}`;
|
||||
const { selector } = Reflection.resolve('containerDefault', 'actionIcon');
|
||||
this.GuildTextChannel = await ReactComponents.getComponent('GuildTextChannel', {selector}, c => c.prototype.renderMentionBadge);
|
||||
|
||||
this.unpatchGuildTextChannel = MonkeyPatch('BD:ReactComponents', this.GuildTextChannel.component.prototype).after('render', this._afterChannelRender);
|
||||
|
@ -433,7 +433,7 @@ export class ReactAutoPatcher {
|
|||
* The GuildVoiceChannel component represents a voice channel in the guild channel list.
|
||||
*/
|
||||
static async patchGuildVoiceChannel() {
|
||||
const selector = `.${WebpackModules.getClassName('containerDefault', 'actionIcon')}`;
|
||||
const { selector } = Reflection.resolve('containerDefault', 'actionIcon');
|
||||
this.GuildVoiceChannel = await ReactComponents.getComponent('GuildVoiceChannel', {selector}, c => c.prototype.handleVoiceConnect);
|
||||
|
||||
this.unpatchGuildVoiceChannel = MonkeyPatch('BD:ReactComponents', this.GuildVoiceChannel.component.prototype).after('render', this._afterChannelRender);
|
||||
|
@ -467,7 +467,7 @@ export class ReactAutoPatcher {
|
|||
}
|
||||
|
||||
static async patchUserProfileModal() {
|
||||
const selector = `.${WebpackModules.getClassName('root', 'topSectionNormal')}`;
|
||||
const { selector } = Reflection.resolve('root', 'topSectionNormal');
|
||||
this.UserProfileModal = await ReactComponents.getComponent('UserProfileModal', {selector}, Filters.byPrototypeFields(['renderHeader', 'renderBadges']));
|
||||
|
||||
this.unpatchUserProfileModal = MonkeyPatch('BD:ReactComponents', this.UserProfileModal.component.prototype).after('render', (component, args, retVal) => {
|
||||
|
@ -482,7 +482,7 @@ export class ReactAutoPatcher {
|
|||
}
|
||||
|
||||
static async patchUserPopout() {
|
||||
const selector = `.${WebpackModules.getClassName('userPopout', 'headerNormal')}`;
|
||||
const { selector } = Reflection.resolve('userPopout', 'headerNormal');
|
||||
this.UserPopout = await ReactComponents.getComponent('UserPopout', {selector});
|
||||
|
||||
this.unpatchUserPopout = MonkeyPatch('BD:ReactComponents', this.UserPopout.component.prototype).after('render', (component, args, retVal) => {
|
||||
|
@ -501,10 +501,10 @@ export class ReactAutoPatcher {
|
|||
}
|
||||
|
||||
static async patchUploadArea() {
|
||||
const selector = `.${WebpackModules.getClassName('uploadArea')}`;
|
||||
const { selector } = Reflection.resolve('uploadArea');
|
||||
this.UploadArea = await ReactComponents.getComponent('UploadArea', {selector});
|
||||
|
||||
const reflect = Reflection(selector);
|
||||
const reflect = Reflection.DOM(selector);
|
||||
const stateNode = reflect.getComponentStateNode(this.UploadArea);
|
||||
const callback = function(e) {
|
||||
if (!e.dataTransfer.files.length || !e.dataTransfer.files[0].name.endsWith('.bd')) return;
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* BetterDiscord Reflection 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.
|
||||
*/
|
||||
|
||||
import { Module, Modules } from './modules';
|
||||
import { Reflection as DOM } from 'ui';
|
||||
import Resolver from './resolver';
|
||||
|
||||
export default class Reflection {
|
||||
|
||||
static get module() {
|
||||
return Module;
|
||||
}
|
||||
|
||||
static get modules() {
|
||||
return Modules;
|
||||
}
|
||||
|
||||
static get resolve() {
|
||||
return Resolver.resolve;
|
||||
}
|
||||
|
||||
static get resolveAsync() {
|
||||
return Resolver.resolveAsync;
|
||||
}
|
||||
|
||||
static get resolver() {
|
||||
return Resolver;
|
||||
}
|
||||
|
||||
static get DOM() {
|
||||
return DOM;
|
||||
}
|
||||
|
||||
static get require() {
|
||||
return Module.require;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,433 @@
|
|||
/**
|
||||
* BetterDiscord Reflection Modules
|
||||
* 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 { Utils, Filters } from 'common';
|
||||
import Events from '../events';
|
||||
|
||||
const KnownModules = {
|
||||
React: Filters.byProperties(['createElement', 'cloneElement']),
|
||||
ReactDOM: Filters.byProperties(['render', 'findDOMNode']),
|
||||
|
||||
Events: Filters.byPrototypeFields(['setMaxListeners', 'emit']),
|
||||
|
||||
/* Guild Info, Stores, and Utilities */
|
||||
GuildStore: Filters.byProperties(['getGuild']),
|
||||
SortedGuildStore: Filters.byProperties(['getSortedGuilds']),
|
||||
SelectedGuildStore: Filters.byProperties(['getLastSelectedGuildId']),
|
||||
GuildSync: Filters.byProperties(['getSyncedGuilds']),
|
||||
GuildInfo: Filters.byProperties(['getAcronym']),
|
||||
GuildChannelsStore: Filters.byProperties(['getChannels', 'getDefaultChannel']),
|
||||
GuildMemberStore: Filters.byProperties(['getMember']),
|
||||
MemberCountStore: Filters.byProperties(['getMemberCounts']),
|
||||
GuildEmojiStore: Filters.byProperties(['getEmojis']),
|
||||
GuildActions: Filters.byProperties(['markGuildAsRead']),
|
||||
GuildPermissions: Filters.byProperties(['getGuildPermissions']),
|
||||
|
||||
/* Channel Store & Actions */
|
||||
ChannelStore: Filters.byProperties(['getChannels', 'getDMFromUserId']),
|
||||
SelectedChannelStore: Filters.byProperties(['getLastSelectedChannelId']),
|
||||
ChannelActions: Filters.byProperties(['selectChannel']),
|
||||
PrivateChannelActions: Filters.byProperties(['openPrivateChannel']),
|
||||
ChannelSelector: Filters.byProperties(['selectGuild', 'selectChannel']),
|
||||
VoiceChannelActions: Filters.byProperties(['selectVoiceChannel']),
|
||||
|
||||
/* Current User Info, State and Settings */
|
||||
UserInfoStore: Filters.byProperties(['getToken']),
|
||||
UserSettingsStore: Filters.byProperties(['guildPositions']),
|
||||
AccountManager: Filters.byProperties(['register', 'login']),
|
||||
UserSettingsUpdater: Filters.byProperties(['updateRemoteSettings']),
|
||||
OnlineWatcher: Filters.byProperties(['isOnline']),
|
||||
CurrentUserIdle: Filters.byProperties(['getIdleTime']),
|
||||
RelationshipStore: Filters.byProperties(['isBlocked', 'isFriend']),
|
||||
RelationshipManager: Filters.byProperties(['addRelationship']),
|
||||
MentionStore: Filters.byProperties(['getMentions']),
|
||||
|
||||
/* User Stores and Utils */
|
||||
UserStore: Filters.byProperties(['getCurrentUser']),
|
||||
UserStatusStore: Filters.byProperties(['getStatuses']),
|
||||
UserTypingStore: Filters.byProperties(['isTyping']),
|
||||
UserActivityStore: Filters.byProperties(['getActivity']),
|
||||
UserNameResolver: Filters.byProperties(['getName']),
|
||||
UserNoteStore: Filters.byProperties(['getNote']),
|
||||
UserNoteActions: Filters.byProperties(['updateNote']),
|
||||
DraftActions: Filters.byProperties(['changeDraft']),
|
||||
|
||||
/* Emoji Store and Utils */
|
||||
EmojiInfo: Filters.byProperties(['isEmojiDisabled']),
|
||||
EmojiUtils: Filters.byProperties(['getGuildEmoji']),
|
||||
EmojiStore: Filters.byProperties(['getByCategory', 'EMOJI_NAME_RE']),
|
||||
|
||||
/* Invite Store and Utils */
|
||||
InviteStore: Filters.byProperties(['getInvites']),
|
||||
InviteResolver: Filters.byProperties(['findInvite']),
|
||||
InviteActions: Filters.byProperties(['acceptInvite']),
|
||||
|
||||
/* Discord Objects & Utils */
|
||||
DiscordConstants: Filters.byProperties(['Permissions', 'ActivityTypes', 'StatusTypes']),
|
||||
Permissions: Filters.byProperties(['getHighestRole']),
|
||||
ColorConverter: Filters.byProperties(['hex2int']),
|
||||
ColorShader: Filters.byProperties(['darken']),
|
||||
TinyColor: Filters.byPrototypeFields(['toRgb']),
|
||||
ClassResolver: Filters.byProperties(['getClass']),
|
||||
ButtonData: Filters.byProperties(['ButtonSizes']),
|
||||
IconNames: Filters.byProperties(['IconNames']),
|
||||
NavigationUtils: Filters.byProperties(['transitionTo', 'replaceWith', 'getHistory']),
|
||||
|
||||
/* Discord Messages */
|
||||
MessageStore: Filters.byProperties(['getMessages']),
|
||||
MessageActions: Filters.byProperties(['jumpToMessage', '_sendMessage']),
|
||||
MessageQueue: Filters.byProperties(['enqueue']),
|
||||
MessageParser: Filters.byProperties(['createMessage', 'parse', 'unparse']),
|
||||
|
||||
/* In-Game Overlay */
|
||||
OverlayUserPopoutSettings: Filters.byProperties(['openUserPopout']),
|
||||
OverlayUserPopoutInfo: Filters.byProperties(['getOpenedUserPopout']),
|
||||
|
||||
/* Experiments */
|
||||
ExperimentStore: Filters.byProperties(['getExperimentOverrides']),
|
||||
ExperimentsManager: Filters.byProperties(['isDeveloper']),
|
||||
CurrentExperiment: Filters.byProperties(['getExperimentId']),
|
||||
|
||||
/* Images, Avatars and Utils */
|
||||
ImageResolver: Filters.byProperties(['getUserAvatarURL']),
|
||||
ImageUtils: Filters.byProperties(['getSizedImageSrc']),
|
||||
AvatarDefaults: Filters.byProperties(['getUserAvatarURL', 'DEFAULT_AVATARS']),
|
||||
|
||||
/* Drag & Drop */
|
||||
DNDActions: Filters.byProperties(['beginDrag']),
|
||||
DNDSources: Filters.byProperties(['addTarget']),
|
||||
DNDObjects: Filters.byProperties(['DragSource']),
|
||||
|
||||
/* Electron & Other Internals with Utils */
|
||||
ElectronModule: Filters.byProperties(['_getMainWindow']),
|
||||
Dispatcher: Filters.byProperties(['dirtyDispatch']),
|
||||
PathUtils: Filters.byProperties(['hasBasename']),
|
||||
NotificationModule: Filters.byProperties(['showNotification']),
|
||||
RouterModule: Filters.byProperties(['Router']),
|
||||
APIModule: Filters.byProperties(['getAPIBaseURL']),
|
||||
AnalyticEvents: Filters.byProperties(['AnalyticEventConfigs']),
|
||||
KeyGenerator: Filters.byCode(/"binary"/),
|
||||
Buffers: Filters.byProperties(['Buffer', 'kMaxLength']),
|
||||
DeviceStore: Filters.byProperties(['getDevices']),
|
||||
SoftwareInfo: Filters.byProperties(['os']),
|
||||
CurrentContext: Filters.byProperties(['setTagsContext']),
|
||||
|
||||
/* Media Stuff (Audio/Video) */
|
||||
MediaDeviceInfo: Filters.byProperties(['Codecs', 'SUPPORTED_BROWSERS']),
|
||||
MediaInfo: Filters.byProperties(['getOutputVolume']),
|
||||
MediaEngineInfo: Filters.byProperties(['MediaEngineFeatures']),
|
||||
VoiceInfo: Filters.byProperties(['EchoCancellation']),
|
||||
VideoStream: Filters.byProperties(['getVideoStream']),
|
||||
SoundModule: Filters.byProperties(['playSound']),
|
||||
|
||||
/* Window, DOM, HTML */
|
||||
WindowInfo: Filters.byProperties(['isFocused', 'windowSize']),
|
||||
TagInfo: Filters.byProperties(['VALID_TAG_NAMES']),
|
||||
DOMInfo: Filters.byProperties(['canUseDOM']),
|
||||
|
||||
/* Locale/Location and Time */
|
||||
LocaleManager: Filters.byProperties(['setLocale']),
|
||||
Moment: Filters.byProperties(['parseZone']),
|
||||
LocationManager: Filters.byProperties(['createLocation']),
|
||||
Timestamps: Filters.byProperties(['fromTimestamp']),
|
||||
TimeFormatter: Filters.byProperties(['dateFormat']),
|
||||
|
||||
/* Strings and Utils */
|
||||
Strings: Filters.byProperties(['TEXT', 'TEXTAREA_PLACEHOLDER']),
|
||||
StringFormats: Filters.byProperties(['a', 'z']),
|
||||
StringUtils: Filters.byProperties(['toASCII']),
|
||||
|
||||
/* URLs and Utils */
|
||||
URLParser: Filters.byProperties(['Url', 'parse']),
|
||||
ExtraURLs: Filters.byProperties(['getArticleURL']),
|
||||
|
||||
/* Text Processing */
|
||||
hljs: Filters.byProperties(['highlight', 'highlightBlock']),
|
||||
SimpleMarkdown: Filters.byProperties(['parseBlock', 'parseInline', 'defaultOutput']),
|
||||
|
||||
/* DOM/React Components */
|
||||
/* ==================== */
|
||||
LayerManager: Filters.byProperties(['popLayer', 'pushLayer']),
|
||||
UserSettingsWindow: Filters.byProperties(['open', 'updateAccount']),
|
||||
ChannelSettingsWindow: Filters.byProperties(['open', 'updateChannel']),
|
||||
GuildSettingsWindow: Filters.byProperties(['open', 'updateGuild']),
|
||||
|
||||
/* Modals */
|
||||
ModalStack: Filters.byProperties(['push', 'update', 'pop', 'popWithKey']),
|
||||
ConfirmModal: Filters.byPrototypeFields(['handleCancel', 'handleSubmit', 'handleMinorConfirm']),
|
||||
UserProfileModal: Filters.byProperties(['fetchMutualFriends', 'setSection']),
|
||||
ChangeNicknameModal: Filters.byProperties(['open', 'changeNickname']),
|
||||
CreateChannelModal: Filters.byProperties(['open', 'createChannel']),
|
||||
PruneMembersModal: Filters.byProperties(['open', 'prune']),
|
||||
NotificationSettingsModal: Filters.byProperties(['open', 'updateNotificationSettings']),
|
||||
PrivacySettingsModal: Filters.byCode(/PRIVACY_SETTINGS_MODAL_OPEN/, m => m.open),
|
||||
CreateInviteModal: Filters.byProperties(['open', 'createInvite']),
|
||||
|
||||
/* Popouts */
|
||||
PopoutStack: Filters.byProperties(['open', 'close', 'closeAll']),
|
||||
PopoutOpener: Filters.byProperties(['openPopout']),
|
||||
EmojiPicker: Filters.byPrototypeFields(['onHoverEmoji', 'selectEmoji']),
|
||||
|
||||
/* Context Menus */
|
||||
ContextMenuActions: Filters.byCode(/CONTEXT_MENU_CLOSE/, c => c.close),
|
||||
ContextMenuItemsGroup: Filters.byCode(/itemGroup/),
|
||||
ContextMenuItem: Filters.byCode(/\.label\b.*\.hint\b.*\.action\b/),
|
||||
|
||||
/* In-Message Links */
|
||||
ExternalLink: Filters.byCode(/\.trusted\b/)
|
||||
};
|
||||
|
||||
class Module {
|
||||
|
||||
/**
|
||||
* Finds a module using a filter function.
|
||||
* @param {Function} filter A function to use to filter modules
|
||||
* @param {Boolean} first Whether to return only the first matching module
|
||||
* @param {Array} modules An array of modules to search in
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModule(filter, first = true, _modules) {
|
||||
const modules = _modules || this.getAllModules();
|
||||
const rm = [];
|
||||
for (const index in modules) {
|
||||
if (!modules.hasOwnProperty(index)) continue;
|
||||
const module = modules[index];
|
||||
const { exports } = module;
|
||||
let foundModule = null;
|
||||
|
||||
if (!exports) continue;
|
||||
if (exports.__esModule && exports.default && filter(exports.default)) foundModule = exports.default;
|
||||
if (filter(exports)) foundModule = exports;
|
||||
if (!foundModule) continue;
|
||||
if (first) return foundModule;
|
||||
rm.push(foundModule);
|
||||
}
|
||||
return first ? undefined : rm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module by it's name.
|
||||
* @param {String} name The name of the module
|
||||
* @param {Function} fallback A function to use to filter modules if not finding a known module
|
||||
* @return {Any}
|
||||
*/
|
||||
static byName(name, fallback) {
|
||||
if (Cache.hasOwnProperty(name)) return Cache[name];
|
||||
if (KnownModules.hasOwnProperty(name)) fallback = KnownModules[name];
|
||||
if (!fallback) return undefined;
|
||||
const module = this.getModule(fallback, true);
|
||||
return module ? Cache[name] = module : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module by it's display name.
|
||||
* @param {String} name The display name of the module
|
||||
* @return {Any}
|
||||
*/
|
||||
static byDisplayName(name) {
|
||||
return this.getModule(Filters.byDisplayName(name), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module using it's code.
|
||||
* @param {RegEx} regex A regular expression to use to filter modules
|
||||
* @param {Boolean} first Whether to return the only the first matching module
|
||||
* @return {Any}
|
||||
*/
|
||||
static byRegex(regex, first = true) {
|
||||
return this.getModule(Filters.byCode(regex), first);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first module using properties on it's prototype.
|
||||
* @param {any} props Properties to use to filter modules
|
||||
* @return {Any}
|
||||
*/
|
||||
static byPrototypes(...prototypes) {
|
||||
return this.getModule(Filters.byPrototypeFields(prototypes), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all modules using properties on it's prototype.
|
||||
* @param {any} props Properties to use to filter modules
|
||||
* @return {Any}
|
||||
*/
|
||||
static allByPrototypes(...prototypes) {
|
||||
return this.getModule(Filters.byPrototypeFields(prototypes), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first module using it's own properties.
|
||||
* @param {any} props Properties to use to filter modules
|
||||
* @return {Any}
|
||||
*/
|
||||
static byProps(...props) {
|
||||
return this.getModule(Filters.byProperties(props), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all modules using it's own properties.
|
||||
* @param {any} props Properties to use to filter modules
|
||||
* @return {Any}
|
||||
*/
|
||||
static allByProps(...props) {
|
||||
return this.getModule(Filters.byProperties(props), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Discord's __webpack_require__ function.
|
||||
*/
|
||||
static get require() {
|
||||
if (this._require) return this._require;
|
||||
|
||||
const __webpack_require__ = this.getWebpackRequire();
|
||||
if (!__webpack_require__) return;
|
||||
|
||||
this.hookWebpackRequireCache(__webpack_require__);
|
||||
return this._require = __webpack_require__;
|
||||
}
|
||||
|
||||
static getWebpackRequire() {
|
||||
const id = 'bd-webpackmodules';
|
||||
|
||||
if (typeof window.webpackJsonp === 'function') {
|
||||
const __webpack_require__ = window['webpackJsonp']([], {
|
||||
[id]: (module, exports, __webpack_require__) => exports.default = __webpack_require__
|
||||
}, [id]).default;
|
||||
delete __webpack_require__.m[id];
|
||||
delete __webpack_require__.c[id];
|
||||
return __webpack_require__;
|
||||
} else if (window.webpackJsonp && window.webpackJsonp.push) {
|
||||
const __webpack_require__ = window['webpackJsonp'].push([[], {
|
||||
[id]: (module, exports, req) => exports.default = req
|
||||
}, [[id]]]).default;
|
||||
window['webpackJsonp'].pop();
|
||||
delete __webpack_require__.m[id];
|
||||
delete __webpack_require__.c[id];
|
||||
return __webpack_require__;
|
||||
}
|
||||
}
|
||||
|
||||
static hookWebpackRequireCache(__webpack_require__) {
|
||||
__webpack_require__.c = new Proxy(__webpack_require__.c, {
|
||||
set(module_cache, module_id, module) {
|
||||
// Add it to our emitter cache and emit a module-loading event
|
||||
this.moduleLoading(module_id, module);
|
||||
Events.emit('module-loading', module);
|
||||
|
||||
// Add the module to the cache as normal
|
||||
module_cache[module_id] = module;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static moduleLoading(module_id, module) {
|
||||
if (this.require.c[module_id]) return;
|
||||
|
||||
if (!this.moduleLoadedEventTimeout) {
|
||||
this.moduleLoadedEventTimeout = setTimeout(() => {
|
||||
this.moduleLoadedEventTimeout = undefined;
|
||||
|
||||
// Emit a module-loaded event for every module
|
||||
for (const module of this.modulesLoadingCache) {
|
||||
Events.emit('module-loaded', module);
|
||||
}
|
||||
|
||||
// Emit a modules-loaded event
|
||||
Events.emit('modules-loaded', this.modulesLoadingCache);
|
||||
|
||||
this.modulesLoadedCache = [];
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// Add this to our own cache
|
||||
if (!this.modulesLoadingCache) this.modulesLoadingCache = [];
|
||||
this.modulesLoadingCache.push(module);
|
||||
}
|
||||
|
||||
static waitForWebpackRequire() {
|
||||
return Utils.until(() => this.require, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a module to load.
|
||||
* This only returns a single module, as it can't guarentee there are no more modules that could
|
||||
* match the filter, which is pretty much what that would be asking for.
|
||||
* @param {Function} filter The name of a known module or a filter function
|
||||
* @return {Any}
|
||||
*/
|
||||
static async waitForModule(filter) {
|
||||
const module = this.getModule(filter);
|
||||
if (module) return module;
|
||||
|
||||
while (this.require.m.length > this.require.c.length) {
|
||||
const additionalModules = await Events.once('modules-loaded');
|
||||
|
||||
const module = this.getModule(filter, true, additionalModules);
|
||||
if (module) return module;
|
||||
}
|
||||
|
||||
throw new Error('All modules have now been loaded. None match the passed filter.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module by it's name.
|
||||
* @param {String} name The name of the module
|
||||
* @param {Function} fallback A function to use to filter modules if not finding a known module
|
||||
* @return {Any}
|
||||
*/
|
||||
static async waitForModuleByName(name, fallback) {
|
||||
if (Cache.hasOwnProperty(name)) return Cache[name];
|
||||
if (KnownModules.hasOwnProperty(name)) fallback = KnownModules[name];
|
||||
if (!fallback) return undefined;
|
||||
const module = await this.waitForModule(fallback, true);
|
||||
return module ? Cache[name] = module : undefined;
|
||||
}
|
||||
|
||||
static waitForModuleByDisplayName(props) {
|
||||
return this.waitForModule(Filters.byDisplayName(props));
|
||||
}
|
||||
static waitForModuleByRegex(props) {
|
||||
return this.waitForModule(Filters.byCode(props));
|
||||
}
|
||||
static waitForModuleByProps(props) {
|
||||
return this.waitForModule(Filters.byProperties(props));
|
||||
}
|
||||
static waitForModuleByPrototypes(props) {
|
||||
return this.waitForModule(Filters.byPrototypeFields(props));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all loaded modules.
|
||||
* @return {Array}
|
||||
*/
|
||||
static getAllModules() {
|
||||
return this.require.c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of known modules.
|
||||
* @return {Array}
|
||||
*/
|
||||
static listKnownModules() {
|
||||
return Object.keys(KnownModules);
|
||||
}
|
||||
|
||||
static get KnownModules() { return KnownModules }
|
||||
|
||||
}
|
||||
|
||||
const Modules = new Proxy(Module, {
|
||||
get(Module, name) {
|
||||
return Module.byName(name);
|
||||
}
|
||||
});
|
||||
|
||||
export { Module, Modules }
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* BetterDiscord Reflection Resolver
|
||||
* 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 { Module } from './modules';
|
||||
|
||||
class Resolved {
|
||||
|
||||
constructor(module, ...classes) {
|
||||
this.module = Module.byProps(...classes);
|
||||
this.classes = classes;
|
||||
}
|
||||
|
||||
get className() {
|
||||
return this.module && this.module[this.classes[0]] ? this.module[this.classes[0]].split(' ')[0] : this.classes[0];
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return `.${this.className}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default class Resolver {
|
||||
|
||||
static resolve(...classes) {
|
||||
return new Resolved(Module.byProps(...classes), ...classes);
|
||||
}
|
||||
|
||||
static async resolveAsync(...classes) {
|
||||
const module = await Module.waitForModuleByProps([...classes]);
|
||||
return new Resolved(module, ...classes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a class module and returns a class from it.
|
||||
* @param {String} base The first part of the class to find
|
||||
* @param {String} ...additional_classes Additional classes to look for to filter duplicate class modules
|
||||
* @return {String}
|
||||
*/
|
||||
static getClassName(base, ...additional_classes) {
|
||||
const class_module = Module.byProps([base, ...additional_classes]);
|
||||
if (class_module && class_module[base]) return class_module[base].split(' ')[0];
|
||||
}
|
||||
static async waitForClassName(base, ...additional_classes) {
|
||||
const class_module = await Module.waitForModuleByProps([base, ...additional_classes]);
|
||||
if (class_module && class_module[base]) return class_module[base].split(' ')[0];
|
||||
}
|
||||
static getSelector(base, ...additional_classes) {
|
||||
const gcn = this.getClassName(base, ...additional_classes);
|
||||
if (gcn) return `.${gcn}`;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,430 @@
|
|||
/**
|
||||
* BetterDiscord WebpackModules 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.
|
||||
*/
|
||||
|
||||
import { Utils, Filters } from 'common';
|
||||
import Events from '../events';
|
||||
|
||||
const KnownModules = {
|
||||
React: Filters.byProperties(['createElement', 'cloneElement']),
|
||||
ReactDOM: Filters.byProperties(['render', 'findDOMNode']),
|
||||
|
||||
Events: Filters.byPrototypeFields(['setMaxListeners', 'emit']),
|
||||
|
||||
/* Guild Info, Stores, and Utilities */
|
||||
GuildStore: Filters.byProperties(['getGuild']),
|
||||
SortedGuildStore: Filters.byProperties(['getSortedGuilds']),
|
||||
SelectedGuildStore: Filters.byProperties(['getLastSelectedGuildId']),
|
||||
GuildSync: Filters.byProperties(['getSyncedGuilds']),
|
||||
GuildInfo: Filters.byProperties(['getAcronym']),
|
||||
GuildChannelsStore: Filters.byProperties(['getChannels', 'getDefaultChannel']),
|
||||
GuildMemberStore: Filters.byProperties(['getMember']),
|
||||
MemberCountStore: Filters.byProperties(['getMemberCounts']),
|
||||
GuildEmojiStore: Filters.byProperties(['getEmojis']),
|
||||
GuildActions: Filters.byProperties(['markGuildAsRead']),
|
||||
GuildPermissions: Filters.byProperties(['getGuildPermissions']),
|
||||
|
||||
/* Channel Store & Actions */
|
||||
ChannelStore: Filters.byProperties(['getChannels', 'getDMFromUserId']),
|
||||
SelectedChannelStore: Filters.byProperties(['getLastSelectedChannelId']),
|
||||
ChannelActions: Filters.byProperties(['selectChannel']),
|
||||
PrivateChannelActions: Filters.byProperties(['openPrivateChannel']),
|
||||
ChannelSelector: Filters.byProperties(['selectGuild', 'selectChannel']),
|
||||
VoiceChannelActions: Filters.byProperties(['selectVoiceChannel']),
|
||||
|
||||
/* Current User Info, State and Settings */
|
||||
UserInfoStore: Filters.byProperties(['getToken']),
|
||||
UserSettingsStore: Filters.byProperties(['guildPositions']),
|
||||
AccountManager: Filters.byProperties(['register', 'login']),
|
||||
UserSettingsUpdater: Filters.byProperties(['updateRemoteSettings']),
|
||||
OnlineWatcher: Filters.byProperties(['isOnline']),
|
||||
CurrentUserIdle: Filters.byProperties(['getIdleTime']),
|
||||
RelationshipStore: Filters.byProperties(['isBlocked', 'isFriend']),
|
||||
RelationshipManager: Filters.byProperties(['addRelationship']),
|
||||
MentionStore: Filters.byProperties(['getMentions']),
|
||||
|
||||
/* User Stores and Utils */
|
||||
UserStore: Filters.byProperties(['getCurrentUser']),
|
||||
UserStatusStore: Filters.byProperties(['getStatuses']),
|
||||
UserTypingStore: Filters.byProperties(['isTyping']),
|
||||
UserActivityStore: Filters.byProperties(['getActivity']),
|
||||
UserNameResolver: Filters.byProperties(['getName']),
|
||||
UserNoteStore: Filters.byProperties(['getNote']),
|
||||
UserNoteActions: Filters.byProperties(['updateNote']),
|
||||
DraftActions: Filters.byProperties(['changeDraft']),
|
||||
|
||||
/* Emoji Store and Utils */
|
||||
EmojiInfo: Filters.byProperties(['isEmojiDisabled']),
|
||||
EmojiUtils: Filters.byProperties(['getGuildEmoji']),
|
||||
EmojiStore: Filters.byProperties(['getByCategory', 'EMOJI_NAME_RE']),
|
||||
|
||||
/* Invite Store and Utils */
|
||||
InviteStore: Filters.byProperties(['getInvites']),
|
||||
InviteResolver: Filters.byProperties(['findInvite']),
|
||||
InviteActions: Filters.byProperties(['acceptInvite']),
|
||||
|
||||
/* Discord Objects & Utils */
|
||||
DiscordConstants: Filters.byProperties(['Permissions', 'ActivityTypes', 'StatusTypes']),
|
||||
Permissions: Filters.byProperties(['getHighestRole']),
|
||||
ColorConverter: Filters.byProperties(['hex2int']),
|
||||
ColorShader: Filters.byProperties(['darken']),
|
||||
TinyColor: Filters.byPrototypeFields(['toRgb']),
|
||||
ClassResolver: Filters.byProperties(['getClass']),
|
||||
ButtonData: Filters.byProperties(['ButtonSizes']),
|
||||
IconNames: Filters.byProperties(['IconNames']),
|
||||
NavigationUtils: Filters.byProperties(['transitionTo', 'replaceWith', 'getHistory']),
|
||||
|
||||
/* Discord Messages */
|
||||
MessageStore: Filters.byProperties(['getMessages']),
|
||||
MessageActions: Filters.byProperties(['jumpToMessage', '_sendMessage']),
|
||||
MessageQueue: Filters.byProperties(['enqueue']),
|
||||
MessageParser: Filters.byProperties(['createMessage', 'parse', 'unparse']),
|
||||
|
||||
/* In-Game Overlay */
|
||||
OverlayUserPopoutSettings: Filters.byProperties(['openUserPopout']),
|
||||
OverlayUserPopoutInfo: Filters.byProperties(['getOpenedUserPopout']),
|
||||
|
||||
/* Experiments */
|
||||
ExperimentStore: Filters.byProperties(['getExperimentOverrides']),
|
||||
ExperimentsManager: Filters.byProperties(['isDeveloper']),
|
||||
CurrentExperiment: Filters.byProperties(['getExperimentId']),
|
||||
|
||||
/* Images, Avatars and Utils */
|
||||
ImageResolver: Filters.byProperties(['getUserAvatarURL']),
|
||||
ImageUtils: Filters.byProperties(['getSizedImageSrc']),
|
||||
AvatarDefaults: Filters.byProperties(['getUserAvatarURL', 'DEFAULT_AVATARS']),
|
||||
|
||||
/* Drag & Drop */
|
||||
DNDActions: Filters.byProperties(['beginDrag']),
|
||||
DNDSources: Filters.byProperties(['addTarget']),
|
||||
DNDObjects: Filters.byProperties(['DragSource']),
|
||||
|
||||
/* Electron & Other Internals with Utils */
|
||||
ElectronModule: Filters.byProperties(['_getMainWindow']),
|
||||
Dispatcher: Filters.byProperties(['dirtyDispatch']),
|
||||
PathUtils: Filters.byProperties(['hasBasename']),
|
||||
NotificationModule: Filters.byProperties(['showNotification']),
|
||||
RouterModule: Filters.byProperties(['Router']),
|
||||
APIModule: Filters.byProperties(['getAPIBaseURL']),
|
||||
AnalyticEvents: Filters.byProperties(['AnalyticEventConfigs']),
|
||||
KeyGenerator: Filters.byCode(/"binary"/),
|
||||
Buffers: Filters.byProperties(['Buffer', 'kMaxLength']),
|
||||
DeviceStore: Filters.byProperties(['getDevices']),
|
||||
SoftwareInfo: Filters.byProperties(['os']),
|
||||
CurrentContext: Filters.byProperties(['setTagsContext']),
|
||||
|
||||
/* Media Stuff (Audio/Video) */
|
||||
MediaDeviceInfo: Filters.byProperties(['Codecs', 'SUPPORTED_BROWSERS']),
|
||||
MediaInfo: Filters.byProperties(['getOutputVolume']),
|
||||
MediaEngineInfo: Filters.byProperties(['MediaEngineFeatures']),
|
||||
VoiceInfo: Filters.byProperties(['EchoCancellation']),
|
||||
VideoStream: Filters.byProperties(['getVideoStream']),
|
||||
SoundModule: Filters.byProperties(['playSound']),
|
||||
|
||||
/* Window, DOM, HTML */
|
||||
WindowInfo: Filters.byProperties(['isFocused', 'windowSize']),
|
||||
TagInfo: Filters.byProperties(['VALID_TAG_NAMES']),
|
||||
DOMInfo: Filters.byProperties(['canUseDOM']),
|
||||
|
||||
/* Locale/Location and Time */
|
||||
LocaleManager: Filters.byProperties(['setLocale']),
|
||||
Moment: Filters.byProperties(['parseZone']),
|
||||
LocationManager: Filters.byProperties(['createLocation']),
|
||||
Timestamps: Filters.byProperties(['fromTimestamp']),
|
||||
TimeFormatter: Filters.byProperties(['dateFormat']),
|
||||
|
||||
/* Strings and Utils */
|
||||
Strings: Filters.byProperties(['TEXT', 'TEXTAREA_PLACEHOLDER']),
|
||||
StringFormats: Filters.byProperties(['a', 'z']),
|
||||
StringUtils: Filters.byProperties(['toASCII']),
|
||||
|
||||
/* URLs and Utils */
|
||||
URLParser: Filters.byProperties(['Url', 'parse']),
|
||||
ExtraURLs: Filters.byProperties(['getArticleURL']),
|
||||
|
||||
/* Text Processing */
|
||||
hljs: Filters.byProperties(['highlight', 'highlightBlock']),
|
||||
SimpleMarkdown: Filters.byProperties(['parseBlock', 'parseInline', 'defaultOutput']),
|
||||
|
||||
/* DOM/React Components */
|
||||
/* ==================== */
|
||||
LayerManager: Filters.byProperties(['popLayer', 'pushLayer']),
|
||||
UserSettingsWindow: Filters.byProperties(['open', 'updateAccount']),
|
||||
ChannelSettingsWindow: Filters.byProperties(['open', 'updateChannel']),
|
||||
GuildSettingsWindow: Filters.byProperties(['open', 'updateGuild']),
|
||||
|
||||
/* Modals */
|
||||
ModalStack: Filters.byProperties(['push', 'update', 'pop', 'popWithKey']),
|
||||
ConfirmModal: Filters.byPrototypeFields(['handleCancel', 'handleSubmit', 'handleMinorConfirm']),
|
||||
UserProfileModal: Filters.byProperties(['fetchMutualFriends', 'setSection']),
|
||||
ChangeNicknameModal: Filters.byProperties(['open', 'changeNickname']),
|
||||
CreateChannelModal: Filters.byProperties(['open', 'createChannel']),
|
||||
PruneMembersModal: Filters.byProperties(['open', 'prune']),
|
||||
NotificationSettingsModal: Filters.byProperties(['open', 'updateNotificationSettings']),
|
||||
PrivacySettingsModal: Filters.byCode(/PRIVACY_SETTINGS_MODAL_OPEN/, m => m.open),
|
||||
CreateInviteModal: Filters.byProperties(['open', 'createInvite']),
|
||||
|
||||
/* Popouts */
|
||||
PopoutStack: Filters.byProperties(['open', 'close', 'closeAll']),
|
||||
PopoutOpener: Filters.byProperties(['openPopout']),
|
||||
EmojiPicker: Filters.byPrototypeFields(['onHoverEmoji', 'selectEmoji']),
|
||||
|
||||
/* Context Menus */
|
||||
ContextMenuActions: Filters.byCode(/CONTEXT_MENU_CLOSE/, c => c.close),
|
||||
ContextMenuItemsGroup: Filters.byCode(/itemGroup/),
|
||||
ContextMenuItem: Filters.byCode(/\.label\b.*\.hint\b.*\.action\b/),
|
||||
|
||||
/* In-Message Links */
|
||||
ExternalLink: Filters.byCode(/\.trusted\b/)
|
||||
};
|
||||
|
||||
class WebpackModules {
|
||||
|
||||
/**
|
||||
* Finds a module using a filter function.
|
||||
* @param {Function} filter A function to use to filter modules
|
||||
* @param {Boolean} first Whether to return only the first matching module
|
||||
* @param {Array} modules An array of modules to search in
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModule(filter, first = true, _modules) {
|
||||
const modules = _modules || this.getAllModules();
|
||||
const rm = [];
|
||||
for (const index in modules) {
|
||||
if (!modules.hasOwnProperty(index)) continue;
|
||||
const module = modules[index];
|
||||
const { exports } = module;
|
||||
let foundModule = null;
|
||||
|
||||
if (!exports) continue;
|
||||
if (exports.__esModule && exports.default && filter(exports.default)) foundModule = exports.default;
|
||||
if (filter(exports)) foundModule = exports;
|
||||
if (!foundModule) continue;
|
||||
if (first) return foundModule;
|
||||
rm.push(foundModule);
|
||||
}
|
||||
return first ? undefined : rm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module by it's name.
|
||||
* @param {String} name The name of the module
|
||||
* @param {Function} fallback A function to use to filter modules if not finding a known module
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModuleByName(name, fallback) {
|
||||
if (Cache.hasOwnProperty(name)) return Cache[name];
|
||||
if (KnownModules.hasOwnProperty(name)) fallback = KnownModules[name];
|
||||
if (!fallback) return undefined;
|
||||
const module = this.getModule(fallback, true);
|
||||
return module ? Cache[name] = module : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module by it's display name.
|
||||
* @param {String} name The display name of the module
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModuleByDisplayName(name) {
|
||||
return this.getModule(Filters.byDisplayName(name), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module using it's code.
|
||||
* @param {RegEx} regex A regular expression to use to filter modules
|
||||
* @param {Boolean} first Whether to return the only the first matching module
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModuleByRegex(regex, first = true) {
|
||||
return this.getModule(Filters.byCode(regex), first);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module using properties on it's prototype.
|
||||
* @param {Array} props Properties to use to filter modules
|
||||
* @param {Boolean} first Whether to return only the first matching module
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModuleByPrototypes(prototypes, first = true) {
|
||||
return this.getModule(Filters.byPrototypeFields(prototypes), first);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module using it's own properties.
|
||||
* @param {Array} props Properties to use to filter modules
|
||||
* @param {Boolean} first Whether to return only the first matching module
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModuleByProps(props, first = true) {
|
||||
return this.getModule(Filters.byProperties(props), first);
|
||||
}
|
||||
|
||||
/**
|
||||
* Discord's __webpack_require__ function.
|
||||
*/
|
||||
static get require() {
|
||||
if (this._require) return this._require;
|
||||
|
||||
const __webpack_require__ = this.getWebpackRequire();
|
||||
if (!__webpack_require__) return;
|
||||
|
||||
this.hookWebpackRequireCache(__webpack_require__);
|
||||
return this._require = __webpack_require__;
|
||||
}
|
||||
|
||||
static getWebpackRequire() {
|
||||
const id = 'bd-webpackmodules';
|
||||
|
||||
if (typeof window.webpackJsonp === 'function') {
|
||||
const __webpack_require__ = window['webpackJsonp']([], {
|
||||
[id]: (module, exports, __webpack_require__) => exports.default = __webpack_require__
|
||||
}, [id]).default;
|
||||
delete __webpack_require__.m[id];
|
||||
delete __webpack_require__.c[id];
|
||||
return __webpack_require__;
|
||||
} else if (window.webpackJsonp && window.webpackJsonp.push) {
|
||||
const __webpack_require__ = window['webpackJsonp'].push([[], {
|
||||
[id]: (module, exports, req) => exports.default = req
|
||||
}, [[id]]]).default;
|
||||
window['webpackJsonp'].pop();
|
||||
delete __webpack_require__.m[id];
|
||||
delete __webpack_require__.c[id];
|
||||
return __webpack_require__;
|
||||
}
|
||||
}
|
||||
|
||||
static hookWebpackRequireCache(__webpack_require__) {
|
||||
__webpack_require__.c = new Proxy(__webpack_require__.c, {
|
||||
set(module_cache, module_id, module) {
|
||||
// Add it to our emitter cache and emit a module-loading event
|
||||
this.moduleLoading(module_id, module);
|
||||
Events.emit('module-loading', module);
|
||||
|
||||
// Add the module to the cache as normal
|
||||
module_cache[module_id] = module;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static moduleLoading(module_id, module) {
|
||||
if (this.require.c[module_id]) return;
|
||||
|
||||
if (!this.moduleLoadedEventTimeout) {
|
||||
this.moduleLoadedEventTimeout = setTimeout(() => {
|
||||
this.moduleLoadedEventTimeout = undefined;
|
||||
|
||||
// Emit a module-loaded event for every module
|
||||
for (const module of this.modulesLoadingCache) {
|
||||
Events.emit('module-loaded', module);
|
||||
}
|
||||
|
||||
// Emit a modules-loaded event
|
||||
Events.emit('modules-loaded', this.modulesLoadingCache);
|
||||
|
||||
this.modulesLoadedCache = [];
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// Add this to our own cache
|
||||
if (!this.modulesLoadingCache) this.modulesLoadingCache = [];
|
||||
this.modulesLoadingCache.push(module);
|
||||
}
|
||||
|
||||
static waitForWebpackRequire() {
|
||||
return Utils.until(() => this.require, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a module to load.
|
||||
* This only returns a single module, as it can't guarentee there are no more modules that could
|
||||
* match the filter, which is pretty much what that would be asking for.
|
||||
* @param {Function} filter The name of a known module or a filter function
|
||||
* @return {Any}
|
||||
*/
|
||||
static async waitForModule(filter) {
|
||||
const module = this.getModule(filter);
|
||||
if (module) return module;
|
||||
|
||||
while (this.require.m.length > this.require.c.length) {
|
||||
const additionalModules = await Events.once('modules-loaded');
|
||||
|
||||
const module = this.getModule(filter, true, additionalModules);
|
||||
if (module) return module;
|
||||
}
|
||||
|
||||
throw new Error('All modules have now been loaded. None match the passed filter.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module by it's name.
|
||||
* @param {String} name The name of the module
|
||||
* @param {Function} fallback A function to use to filter modules if not finding a known module
|
||||
* @return {Any}
|
||||
*/
|
||||
static async waitForModuleByName(name, fallback) {
|
||||
if (Cache.hasOwnProperty(name)) return Cache[name];
|
||||
if (KnownModules.hasOwnProperty(name)) fallback = KnownModules[name];
|
||||
if (!fallback) return undefined;
|
||||
const module = await this.waitForModule(fallback, true);
|
||||
return module ? Cache[name] = module : undefined;
|
||||
}
|
||||
|
||||
static waitForModuleByDisplayName(props) {
|
||||
return this.waitForModule(Filters.byDisplayName(props));
|
||||
}
|
||||
static waitForModuleByRegex(props) {
|
||||
return this.waitForModule(Filters.byCode(props));
|
||||
}
|
||||
static waitForModuleByProps(props) {
|
||||
return this.waitForModule(Filters.byProperties(props));
|
||||
}
|
||||
static waitForModuleByPrototypes(props) {
|
||||
return this.waitForModule(Filters.byPrototypeFields(props));
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a class module and returns a class from it.
|
||||
* @param {String} base The first part of the class to find
|
||||
* @param {String} ...additional_classes Additional classes to look for to filter duplicate class modules
|
||||
* @return {String}
|
||||
*/
|
||||
static getClassName(base, ...additional_classes) {
|
||||
const class_module = this.getModuleByProps([base, ...additional_classes]);
|
||||
if (class_module && class_module[base]) return class_module[base].split(' ')[0];
|
||||
}
|
||||
static async waitForClassName(base, ...additional_classes) {
|
||||
const class_module = await this.waitForModuleByProps([base, ...additional_classes]);
|
||||
if (class_module && class_module[base]) return class_module[base].split(' ')[0];
|
||||
}
|
||||
static getSelector(base, ...additional_classes) {
|
||||
const gcn = this.getClassName(base, ...additional_classes);
|
||||
if (gcn) return `.${gcn}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all loaded modules.
|
||||
* @return {Array}
|
||||
*/
|
||||
static getAllModules() {
|
||||
return this.require.c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of known modules.
|
||||
* @return {Array}
|
||||
*/
|
||||
static listKnownModules() {
|
||||
return Object.keys(KnownModules);
|
||||
}
|
||||
|
||||
static get KnownModules() { return KnownModules }
|
||||
|
||||
}
|
||||
|
||||
export { WebpackModules }
|
|
@ -1,436 +1,12 @@
|
|||
/**
|
||||
* BetterDiscord WebpackModules 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.
|
||||
*/
|
||||
/* Deprecation Notice */
|
||||
import { WebpackModules } from './reflection/wpm.depr.js';
|
||||
import { ClientLogger as Logger } from 'common';
|
||||
|
||||
import { Utils, Filters } from 'common';
|
||||
import Events from './events';
|
||||
|
||||
const KnownModules = {
|
||||
React: Filters.byProperties(['createElement', 'cloneElement']),
|
||||
ReactDOM: Filters.byProperties(['render', 'findDOMNode']),
|
||||
|
||||
Events: Filters.byPrototypeFields(['setMaxListeners', 'emit']),
|
||||
|
||||
/* Guild Info, Stores, and Utilities */
|
||||
GuildStore: Filters.byProperties(['getGuild']),
|
||||
SortedGuildStore: Filters.byProperties(['getSortedGuilds']),
|
||||
SelectedGuildStore: Filters.byProperties(['getLastSelectedGuildId']),
|
||||
GuildSync: Filters.byProperties(['getSyncedGuilds']),
|
||||
GuildInfo: Filters.byProperties(['getAcronym']),
|
||||
GuildChannelsStore: Filters.byProperties(['getChannels', 'getDefaultChannel']),
|
||||
GuildMemberStore: Filters.byProperties(['getMember']),
|
||||
MemberCountStore: Filters.byProperties(['getMemberCounts']),
|
||||
GuildEmojiStore: Filters.byProperties(['getEmojis']),
|
||||
GuildActions: Filters.byProperties(['markGuildAsRead']),
|
||||
GuildPermissions: Filters.byProperties(['getGuildPermissions']),
|
||||
|
||||
/* Channel Store & Actions */
|
||||
ChannelStore: Filters.byProperties(['getChannels', 'getDMFromUserId']),
|
||||
SelectedChannelStore: Filters.byProperties(['getLastSelectedChannelId']),
|
||||
ChannelActions: Filters.byProperties(['selectChannel']),
|
||||
PrivateChannelActions: Filters.byProperties(['openPrivateChannel']),
|
||||
ChannelSelector: Filters.byProperties(['selectGuild', 'selectChannel']),
|
||||
VoiceChannelActions: Filters.byProperties(['selectVoiceChannel']),
|
||||
|
||||
/* Current User Info, State and Settings */
|
||||
UserInfoStore: Filters.byProperties(['getToken']),
|
||||
UserSettingsStore: Filters.byProperties(['guildPositions']),
|
||||
AccountManager: Filters.byProperties(['register', 'login']),
|
||||
UserSettingsUpdater: Filters.byProperties(['updateRemoteSettings']),
|
||||
OnlineWatcher: Filters.byProperties(['isOnline']),
|
||||
CurrentUserIdle: Filters.byProperties(['getIdleTime']),
|
||||
RelationshipStore: Filters.byProperties(['isBlocked', 'isFriend']),
|
||||
RelationshipManager: Filters.byProperties(['addRelationship']),
|
||||
MentionStore: Filters.byProperties(['getMentions']),
|
||||
|
||||
/* User Stores and Utils */
|
||||
UserStore: Filters.byProperties(['getCurrentUser']),
|
||||
UserStatusStore: Filters.byProperties(['getStatuses']),
|
||||
UserTypingStore: Filters.byProperties(['isTyping']),
|
||||
UserActivityStore: Filters.byProperties(['getActivity']),
|
||||
UserNameResolver: Filters.byProperties(['getName']),
|
||||
UserNoteStore: Filters.byProperties(['getNote']),
|
||||
UserNoteActions: Filters.byProperties(['updateNote']),
|
||||
DraftActions: Filters.byProperties(['changeDraft']),
|
||||
|
||||
/* Emoji Store and Utils */
|
||||
EmojiInfo: Filters.byProperties(['isEmojiDisabled']),
|
||||
EmojiUtils: Filters.byProperties(['getGuildEmoji']),
|
||||
EmojiStore: Filters.byProperties(['getByCategory', 'EMOJI_NAME_RE']),
|
||||
|
||||
/* Invite Store and Utils */
|
||||
InviteStore: Filters.byProperties(['getInvites']),
|
||||
InviteResolver: Filters.byProperties(['findInvite']),
|
||||
InviteActions: Filters.byProperties(['acceptInvite']),
|
||||
|
||||
/* Discord Objects & Utils */
|
||||
DiscordConstants: Filters.byProperties(['Permissions', 'ActivityTypes', 'StatusTypes']),
|
||||
Permissions: Filters.byProperties(['getHighestRole']),
|
||||
ColorConverter: Filters.byProperties(['hex2int']),
|
||||
ColorShader: Filters.byProperties(['darken']),
|
||||
TinyColor: Filters.byPrototypeFields(['toRgb']),
|
||||
ClassResolver: Filters.byProperties(['getClass']),
|
||||
ButtonData: Filters.byProperties(['ButtonSizes']),
|
||||
IconNames: Filters.byProperties(['IconNames']),
|
||||
NavigationUtils: Filters.byProperties(['transitionTo', 'replaceWith', 'getHistory']),
|
||||
|
||||
/* Discord Messages */
|
||||
MessageStore: Filters.byProperties(['getMessages']),
|
||||
MessageActions: Filters.byProperties(['jumpToMessage', '_sendMessage']),
|
||||
MessageQueue: Filters.byProperties(['enqueue']),
|
||||
MessageParser: Filters.byProperties(['createMessage', 'parse', 'unparse']),
|
||||
|
||||
/* In-Game Overlay */
|
||||
OverlayUserPopoutSettings: Filters.byProperties(['openUserPopout']),
|
||||
OverlayUserPopoutInfo: Filters.byProperties(['getOpenedUserPopout']),
|
||||
|
||||
/* Experiments */
|
||||
ExperimentStore: Filters.byProperties(['getExperimentOverrides']),
|
||||
ExperimentsManager: Filters.byProperties(['isDeveloper']),
|
||||
CurrentExperiment: Filters.byProperties(['getExperimentId']),
|
||||
|
||||
/* Images, Avatars and Utils */
|
||||
ImageResolver: Filters.byProperties(['getUserAvatarURL']),
|
||||
ImageUtils: Filters.byProperties(['getSizedImageSrc']),
|
||||
AvatarDefaults: Filters.byProperties(['getUserAvatarURL', 'DEFAULT_AVATARS']),
|
||||
|
||||
/* Drag & Drop */
|
||||
DNDActions: Filters.byProperties(['beginDrag']),
|
||||
DNDSources: Filters.byProperties(['addTarget']),
|
||||
DNDObjects: Filters.byProperties(['DragSource']),
|
||||
|
||||
/* Electron & Other Internals with Utils */
|
||||
ElectronModule: Filters.byProperties(['_getMainWindow']),
|
||||
Dispatcher: Filters.byProperties(['dirtyDispatch']),
|
||||
PathUtils: Filters.byProperties(['hasBasename']),
|
||||
NotificationModule: Filters.byProperties(['showNotification']),
|
||||
RouterModule: Filters.byProperties(['Router']),
|
||||
APIModule: Filters.byProperties(['getAPIBaseURL']),
|
||||
AnalyticEvents: Filters.byProperties(['AnalyticEventConfigs']),
|
||||
KeyGenerator: Filters.byCode(/"binary"/),
|
||||
Buffers: Filters.byProperties(['Buffer', 'kMaxLength']),
|
||||
DeviceStore: Filters.byProperties(['getDevices']),
|
||||
SoftwareInfo: Filters.byProperties(['os']),
|
||||
CurrentContext: Filters.byProperties(['setTagsContext']),
|
||||
|
||||
/* Media Stuff (Audio/Video) */
|
||||
MediaDeviceInfo: Filters.byProperties(['Codecs', 'SUPPORTED_BROWSERS']),
|
||||
MediaInfo: Filters.byProperties(['getOutputVolume']),
|
||||
MediaEngineInfo: Filters.byProperties(['MediaEngineFeatures']),
|
||||
VoiceInfo: Filters.byProperties(['EchoCancellation']),
|
||||
VideoStream: Filters.byProperties(['getVideoStream']),
|
||||
SoundModule: Filters.byProperties(['playSound']),
|
||||
|
||||
/* Window, DOM, HTML */
|
||||
WindowInfo: Filters.byProperties(['isFocused', 'windowSize']),
|
||||
TagInfo: Filters.byProperties(['VALID_TAG_NAMES']),
|
||||
DOMInfo: Filters.byProperties(['canUseDOM']),
|
||||
|
||||
/* Locale/Location and Time */
|
||||
LocaleManager: Filters.byProperties(['setLocale']),
|
||||
Moment: Filters.byProperties(['parseZone']),
|
||||
LocationManager: Filters.byProperties(['createLocation']),
|
||||
Timestamps: Filters.byProperties(['fromTimestamp']),
|
||||
TimeFormatter: Filters.byProperties(['dateFormat']),
|
||||
|
||||
/* Strings and Utils */
|
||||
Strings: Filters.byProperties(['TEXT', 'TEXTAREA_PLACEHOLDER']),
|
||||
StringFormats: Filters.byProperties(['a', 'z']),
|
||||
StringUtils: Filters.byProperties(['toASCII']),
|
||||
|
||||
/* URLs and Utils */
|
||||
URLParser: Filters.byProperties(['Url', 'parse']),
|
||||
ExtraURLs: Filters.byProperties(['getArticleURL']),
|
||||
|
||||
/* Text Processing */
|
||||
hljs: Filters.byProperties(['highlight', 'highlightBlock']),
|
||||
SimpleMarkdown: Filters.byProperties(['parseBlock', 'parseInline', 'defaultOutput']),
|
||||
|
||||
/* DOM/React Components */
|
||||
/* ==================== */
|
||||
LayerManager: Filters.byProperties(['popLayer', 'pushLayer']),
|
||||
UserSettingsWindow: Filters.byProperties(['open', 'updateAccount']),
|
||||
ChannelSettingsWindow: Filters.byProperties(['open', 'updateChannel']),
|
||||
GuildSettingsWindow: Filters.byProperties(['open', 'updateGuild']),
|
||||
|
||||
/* Modals */
|
||||
ModalStack: Filters.byProperties(['push', 'update', 'pop', 'popWithKey']),
|
||||
ConfirmModal: Filters.byPrototypeFields(['handleCancel', 'handleSubmit', 'handleMinorConfirm']),
|
||||
UserProfileModal: Filters.byProperties(['fetchMutualFriends', 'setSection']),
|
||||
ChangeNicknameModal: Filters.byProperties(['open', 'changeNickname']),
|
||||
CreateChannelModal: Filters.byProperties(['open', 'createChannel']),
|
||||
PruneMembersModal: Filters.byProperties(['open', 'prune']),
|
||||
NotificationSettingsModal: Filters.byProperties(['open', 'updateNotificationSettings']),
|
||||
PrivacySettingsModal: Filters.byCode(/PRIVACY_SETTINGS_MODAL_OPEN/, m => m.open),
|
||||
CreateInviteModal: Filters.byProperties(['open', 'createInvite']),
|
||||
|
||||
/* Popouts */
|
||||
PopoutStack: Filters.byProperties(['open', 'close', 'closeAll']),
|
||||
PopoutOpener: Filters.byProperties(['openPopout']),
|
||||
EmojiPicker: Filters.byPrototypeFields(['onHoverEmoji', 'selectEmoji']),
|
||||
|
||||
/* Context Menus */
|
||||
ContextMenuActions: Filters.byCode(/CONTEXT_MENU_CLOSE/, c => c.close),
|
||||
ContextMenuItemsGroup: Filters.byCode(/itemGroup/),
|
||||
ContextMenuItem: Filters.byCode(/\.label\b.*\.hint\b.*\.action\b/),
|
||||
|
||||
/* In-Message Links */
|
||||
ExternalLink: Filters.byCode(/\.trusted\b/)
|
||||
};
|
||||
|
||||
class WebpackModules {
|
||||
|
||||
/**
|
||||
* Finds a module using a filter function.
|
||||
* @param {Function} filter A function to use to filter modules
|
||||
* @param {Boolean} first Whether to return only the first matching module
|
||||
* @param {Array} modules An array of modules to search in
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModule(filter, first = true, _modules) {
|
||||
const modules = _modules || this.getAllModules();
|
||||
const rm = [];
|
||||
for (const index in modules) {
|
||||
if (!modules.hasOwnProperty(index)) continue;
|
||||
const module = modules[index];
|
||||
const { exports } = module;
|
||||
let foundModule = null;
|
||||
|
||||
if (!exports) continue;
|
||||
if (exports.__esModule && exports.default && filter(exports.default)) foundModule = exports.default;
|
||||
if (filter(exports)) foundModule = exports;
|
||||
if (!foundModule) continue;
|
||||
if (first) return foundModule;
|
||||
rm.push(foundModule);
|
||||
}
|
||||
return first ? undefined : rm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module by it's name.
|
||||
* @param {String} name The name of the module
|
||||
* @param {Function} fallback A function to use to filter modules if not finding a known module
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModuleByName(name, fallback) {
|
||||
if (Cache.hasOwnProperty(name)) return Cache[name];
|
||||
if (KnownModules.hasOwnProperty(name)) fallback = KnownModules[name];
|
||||
if (!fallback) return undefined;
|
||||
const module = this.getModule(fallback, true);
|
||||
return module ? Cache[name] = module : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module by it's display name.
|
||||
* @param {String} name The display name of the module
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModuleByDisplayName(name) {
|
||||
return this.getModule(Filters.byDisplayName(name), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module using it's code.
|
||||
* @param {RegEx} regex A regular expression to use to filter modules
|
||||
* @param {Boolean} first Whether to return the only the first matching module
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModuleByRegex(regex, first = true) {
|
||||
return this.getModule(Filters.byCode(regex), first);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module using properties on it's prototype.
|
||||
* @param {Array} props Properties to use to filter modules
|
||||
* @param {Boolean} first Whether to return only the first matching module
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModuleByPrototypes(prototypes, first = true) {
|
||||
return this.getModule(Filters.byPrototypeFields(prototypes), first);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module using it's own properties.
|
||||
* @param {Array} props Properties to use to filter modules
|
||||
* @param {Boolean} first Whether to return only the first matching module
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModuleByProps(props, first = true) {
|
||||
return this.getModule(Filters.byProperties(props), first);
|
||||
}
|
||||
|
||||
/**
|
||||
* Discord's __webpack_require__ function.
|
||||
*/
|
||||
static get require() {
|
||||
if (this._require) return this._require;
|
||||
|
||||
const __webpack_require__ = this.getWebpackRequire();
|
||||
if (!__webpack_require__) return;
|
||||
|
||||
this.hookWebpackRequireCache(__webpack_require__);
|
||||
return this._require = __webpack_require__;
|
||||
}
|
||||
|
||||
static getWebpackRequire() {
|
||||
const id = 'bd-webpackmodules';
|
||||
|
||||
if (typeof window.webpackJsonp === 'function') {
|
||||
const __webpack_require__ = window['webpackJsonp']([], {
|
||||
[id]: (module, exports, __webpack_require__) => exports.default = __webpack_require__
|
||||
}, [id]).default;
|
||||
delete __webpack_require__.m[id];
|
||||
delete __webpack_require__.c[id];
|
||||
return __webpack_require__;
|
||||
} else if (window.webpackJsonp && window.webpackJsonp.push) {
|
||||
const __webpack_require__ = window['webpackJsonp'].push([[], {
|
||||
[id]: (module, exports, req) => exports.default = req
|
||||
}, [[id]]]).default;
|
||||
window['webpackJsonp'].pop();
|
||||
delete __webpack_require__.m[id];
|
||||
delete __webpack_require__.c[id];
|
||||
return __webpack_require__;
|
||||
}
|
||||
}
|
||||
|
||||
static hookWebpackRequireCache(__webpack_require__) {
|
||||
__webpack_require__.c = new Proxy(__webpack_require__.c, {
|
||||
set(module_cache, module_id, module) {
|
||||
// Add it to our emitter cache and emit a module-loading event
|
||||
this.moduleLoading(module_id, module);
|
||||
Events.emit('module-loading', module);
|
||||
|
||||
// Add the module to the cache as normal
|
||||
module_cache[module_id] = module;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static moduleLoading(module_id, module) {
|
||||
if (this.require.c[module_id]) return;
|
||||
|
||||
if (!this.moduleLoadedEventTimeout) {
|
||||
this.moduleLoadedEventTimeout = setTimeout(() => {
|
||||
this.moduleLoadedEventTimeout = undefined;
|
||||
|
||||
// Emit a module-loaded event for every module
|
||||
for (const module of this.modulesLoadingCache) {
|
||||
Events.emit('module-loaded', module);
|
||||
}
|
||||
|
||||
// Emit a modules-loaded event
|
||||
Events.emit('modules-loaded', this.modulesLoadingCache);
|
||||
|
||||
this.modulesLoadedCache = [];
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// Add this to our own cache
|
||||
if (!this.modulesLoadingCache) this.modulesLoadingCache = [];
|
||||
this.modulesLoadingCache.push(module);
|
||||
}
|
||||
|
||||
static waitForWebpackRequire() {
|
||||
return Utils.until(() => this.require, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a module to load.
|
||||
* This only returns a single module, as it can't guarentee there are no more modules that could
|
||||
* match the filter, which is pretty much what that would be asking for.
|
||||
* @param {Function} filter The name of a known module or a filter function
|
||||
* @return {Any}
|
||||
*/
|
||||
static async waitForModule(filter) {
|
||||
const module = this.getModule(filter);
|
||||
if (module) return module;
|
||||
|
||||
while (this.require.m.length > this.require.c.length) {
|
||||
const additionalModules = await Events.once('modules-loaded');
|
||||
|
||||
const module = this.getModule(filter, true, additionalModules);
|
||||
if (module) return module;
|
||||
}
|
||||
|
||||
throw new Error('All modules have now been loaded. None match the passed filter.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module by it's name.
|
||||
* @param {String} name The name of the module
|
||||
* @param {Function} fallback A function to use to filter modules if not finding a known module
|
||||
* @return {Any}
|
||||
*/
|
||||
static async waitForModuleByName(name, fallback) {
|
||||
if (Cache.hasOwnProperty(name)) return Cache[name];
|
||||
if (KnownModules.hasOwnProperty(name)) fallback = KnownModules[name];
|
||||
if (!fallback) return undefined;
|
||||
const module = await this.waitForModule(fallback, true);
|
||||
return module ? Cache[name] = module : undefined;
|
||||
}
|
||||
|
||||
static waitForModuleByDisplayName(props) {
|
||||
return this.waitForModule(Filters.byDisplayName(props));
|
||||
}
|
||||
static waitForModuleByRegex(props) {
|
||||
return this.waitForModule(Filters.byCode(props));
|
||||
}
|
||||
static waitForModuleByProps(props) {
|
||||
return this.waitForModule(Filters.byProperties(props));
|
||||
}
|
||||
static waitForModuleByPrototypes(props) {
|
||||
return this.waitForModule(Filters.byPrototypeFields(props));
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a class module and returns a class from it.
|
||||
* @param {String} base The first part of the class to find
|
||||
* @param {String} ...additional_classes Additional classes to look for to filter duplicate class modules
|
||||
* @return {String}
|
||||
*/
|
||||
static getClassName(base, ...additional_classes) {
|
||||
const class_module = this.getModuleByProps([base, ...additional_classes]);
|
||||
if (class_module && class_module[base]) return class_module[base].split(' ')[0];
|
||||
}
|
||||
static async waitForClassName(base, ...additional_classes) {
|
||||
const class_module = await this.waitForModuleByProps([base, ...additional_classes]);
|
||||
if (class_module && class_module[base]) return class_module[base].split(' ')[0];
|
||||
}
|
||||
static getSelector(base, ...additional_classes) {
|
||||
const gcn = this.getClassName(base, ...additional_classes);
|
||||
if (gcn) return `.${gcn}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all loaded modules.
|
||||
* @return {Array}
|
||||
*/
|
||||
static getAllModules() {
|
||||
return this.require.c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of known modules.
|
||||
* @return {Array}
|
||||
*/
|
||||
static listKnownModules() {
|
||||
return Object.keys(KnownModules);
|
||||
}
|
||||
|
||||
static get KnownModules() { return KnownModules }
|
||||
|
||||
}
|
||||
|
||||
const WebpackModulesProxy = new Proxy(WebpackModules, {
|
||||
const DeprecationWarning = new Proxy(WebpackModules, {
|
||||
get(WebpackModules, property) {
|
||||
Logger.warn('DEPR', 'WebpackModules is deprecated. Use Reflection.Modules instead.');
|
||||
return WebpackModules[property] || WebpackModules.getModuleByName(property);
|
||||
}
|
||||
});
|
||||
|
||||
export { WebpackModulesProxy as WebpackModules };
|
||||
export { DeprecationWarning as WebpackModules };
|
||||
|
|
|
@ -108,8 +108,10 @@
|
|||
|
||||
.bd-e2eePopover {
|
||||
background: #484b51;
|
||||
margin: 0;
|
||||
margin-top: 15px;
|
||||
|
||||
&.bd-e2eePopoverOver {
|
||||
transform: translate(-2px, 52px);
|
||||
}
|
||||
|
||||
.bd-ok {
|
||||
svg {
|
||||
|
@ -123,9 +125,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.bd-popoverWrapper,
|
||||
.bd-popoverWrapper {
|
||||
.bd-popoverInner,
|
||||
.bd-popoverInner {
|
||||
display: flex;
|
||||
|
||||
|
@ -141,7 +141,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.bd-materialDesignIcon,
|
||||
.bd-materialDesignIcon {
|
||||
display: flex;
|
||||
fill: #7e8084;
|
||||
|
@ -154,7 +153,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.bd-popoverArrow,
|
||||
.bd-popoverArrow {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Settings, Globals, WebpackModules, ReactComponents, MonkeyPatch, Cache } from 'modules';
|
||||
import { Settings, Globals, Reflection, ReactComponents, MonkeyPatch, Cache } from 'modules';
|
||||
import { VueInjector } from 'ui';
|
||||
|
||||
import AutocompleteComponent from './components/common/Autocomplete.vue';
|
||||
|
@ -11,7 +11,7 @@ export default new class Autocomplete {
|
|||
}
|
||||
|
||||
async init() {
|
||||
this.cta = await ReactComponents.getComponent('ChannelTextArea', { selector: WebpackModules.getSelector('channelTextArea', 'emojiButton') });
|
||||
this.cta = await ReactComponents.getComponent('ChannelTextArea', { selector: Reflection.resolve('channelTextArea', 'emojiButton').selector });
|
||||
MonkeyPatch('BD:Autocomplete', this.cta.component.prototype).after('render', this.channelTextAreaAfterRender.bind(this));
|
||||
this.initialized = true;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { Module, WebpackModules } from 'modules';
|
||||
import { Module, Reflection } from 'modules';
|
||||
|
||||
const normalizedPrefix = 'da';
|
||||
const randClass = new RegExp(`^(?!${normalizedPrefix}-)((?:[A-Za-z]|[0-9]|-)+)-(?:[A-Za-z]|[0-9]|-|_){6}$`);
|
||||
|
@ -16,7 +16,7 @@ const randClass = new RegExp(`^(?!${normalizedPrefix}-)((?:[A-Za-z]|[0-9]|-)+)-(
|
|||
export default class ClassNormaliser extends Module {
|
||||
|
||||
init() {
|
||||
this.patchClassModules(WebpackModules.getModule(this.moduleFilter.bind(this), false));
|
||||
this.patchClassModules(Reflection.module.getModule(this.moduleFilter.bind(this), false));
|
||||
this.normalizeElement(document.querySelector('#app-mount'));
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
<script>
|
||||
// Imports
|
||||
import { WebpackModules } from 'modules';
|
||||
import { Reflection } from 'modules';
|
||||
import { BdMenu } from 'ui';
|
||||
import { shell } from 'electron';
|
||||
import { MiGithubCircle, MiWeb, MiTwitterCircle, MiDiscord } from '../common';
|
||||
|
@ -44,7 +44,7 @@
|
|||
e.preventDefault();
|
||||
},
|
||||
openUserProfileModal(discord_id) {
|
||||
WebpackModules.getModuleByName('UserProfileModal').open(discord_id);
|
||||
Reflection.modules.UserProfileModal.open(discord_id);
|
||||
BdMenu.close();
|
||||
},
|
||||
openGitHub() {
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { WebpackModules } from 'modules';
|
||||
export default {
|
||||
props: ['item'],
|
||||
data() {
|
||||
|
@ -44,7 +43,6 @@
|
|||
},
|
||||
methods: {
|
||||
resolveThumb() {
|
||||
window.momentTest = WebpackModules;
|
||||
return `${this.item.repository.rawUri}/${this.item.files.previews[0].thumb}`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="bd-acScroller" ref="scroller">
|
||||
<div v-for="(item, index) in search.items" class="bd-acRow" @mouseover="selectedIndex = index" @click="inject">
|
||||
<div v-for="(item, index) in search.items" class="bd-acRow" :class="search.extraClasses" @mouseover="selectedIndex = index" @click="inject">
|
||||
<div class="bd-acSelector bd-selectable" :class="{'bd-selected': index === selectedIndex}">
|
||||
<div class="bd-acField">
|
||||
<img v-if="search.type === 'imagetext'" :src="item.src || item.value.src" :alt="item.key || item.text || item.alt" />
|
||||
|
@ -36,8 +36,8 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { WebpackModules, DiscordApi, Events } from 'modules';
|
||||
let wtf = null;
|
||||
import { Reflection, DiscordApi, Events } from 'modules';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
*/
|
||||
|
||||
import { Utils, ClientLogger as Logger } from 'common';
|
||||
import { ReactComponents, WebpackModules, MonkeyPatch } from 'modules';
|
||||
import { ReactComponents, Reflection, MonkeyPatch } from 'modules';
|
||||
import { VueInjector, Toasts } from 'ui';
|
||||
import CMGroup from './components/contextmenu/Group.vue';
|
||||
|
||||
|
@ -70,7 +70,7 @@ export class DiscordContextMenu {
|
|||
if (this.patched) return;
|
||||
this.patched = true;
|
||||
const self = this;
|
||||
MonkeyPatch('BD:DiscordCMOCM', WebpackModules.getModuleByProps(['openContextMenu'])).instead('openContextMenu', (_, [e, fn], originalFn) => {
|
||||
MonkeyPatch('BD:DiscordCMOCM', Reflection.module.byProps('openContextMenu')).instead('openContextMenu', (_, [e, fn], originalFn) => {
|
||||
const overrideFn = function () {
|
||||
const res = fn.apply(this, arguments);
|
||||
if (!res.hasOwnProperty('type')) return res;
|
||||
|
@ -96,7 +96,7 @@ export class DiscordContextMenu {
|
|||
target,
|
||||
top,
|
||||
left,
|
||||
closeMenu: () => WebpackModules.getModuleByProps(['closeContextMenu']).closeContextMenu(),
|
||||
closeMenu: () => Reflection.module.byProps('closeContextMenu').closeContextMenu(),
|
||||
items: typeof menu.items === 'function' ? menu.items(target) : menu.items
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -8,8 +8,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { Module, ReactComponents, ReactHelpers, MonkeyPatch, WebpackModules } from 'modules';
|
||||
import { Reflection } from 'ui';
|
||||
import { Module, ReactComponents, ReactHelpers, MonkeyPatch, Reflection } from 'modules';
|
||||
import { ClientLogger as Logger } from 'common';
|
||||
import { BdBadge } from './components/bd';
|
||||
import VueInjector from './vueinjector';
|
||||
|
@ -88,7 +87,7 @@ export default class extends Module {
|
|||
async patchNameTag() {
|
||||
if (this.PatchedNameTag) return this.PatchedNameTag;
|
||||
|
||||
const selector = `.${WebpackModules.getClassName('nameTag', 'username', 'discriminator', 'ownerIcon')}`;
|
||||
const selector = Reflection.resolve('nameTag', 'username', 'discriminator', 'ownerIcon').selector;
|
||||
const NameTag = await ReactComponents.getComponent('NameTag', {selector});
|
||||
|
||||
this.PatchedNameTag = class extends NameTag.component {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { WebpackModules } from 'modules';
|
||||
import { Reflection } from 'modules';
|
||||
import Vue from 'vue';
|
||||
|
||||
export default class {
|
||||
|
@ -37,15 +37,14 @@ export default class {
|
|||
* @return {React.Element}
|
||||
*/
|
||||
static createReactElement(component, props, mountAtTop) {
|
||||
const React = WebpackModules.getModuleByName('React');
|
||||
const { React } = Reflection.modules;
|
||||
return React.createElement(this.ReactCompatibility, {component, mountAtTop, props});
|
||||
}
|
||||
|
||||
static get ReactCompatibility() {
|
||||
if (this._ReactCompatibility) return this._ReactCompatibility;
|
||||
|
||||
const React = WebpackModules.getModuleByName('React');
|
||||
const ReactDOM = WebpackModules.getModuleByName('ReactDOM');
|
||||
const { React, ReactDOM} = Reflection.modules;
|
||||
|
||||
return this._ReactCompatibility = class VueComponent extends React.Component {
|
||||
render() {
|
||||
|
@ -105,12 +104,12 @@ export const ReactComponent = {
|
|||
return createElement('div');
|
||||
},
|
||||
mounted() {
|
||||
const { React, ReactDOM } = WebpackModules;
|
||||
const { React, ReactDOM } = Reflection.modules;
|
||||
|
||||
ReactDOM.unmountComponentAtNode(this.$el);
|
||||
ReactDOM.render(this.reactElement || React.createElement(this.component, this.componentProps, ...(this.componentChildren || [])), this.$el);
|
||||
},
|
||||
beforeDestroy() {
|
||||
WebpackModules.ReactDOM.unmountComponentAtNode(this.$el);
|
||||
Reflection.modules.ReactDOM.unmountComponentAtNode(this.$el);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -81,7 +81,7 @@ gulp.task('node-sass-bindings', gulp.series(function () {
|
|||
gulp.src('other/node_sass_bindings/**/*'),
|
||||
copy('release/node_modules/node-sass/vendor', { prefix: 2 })
|
||||
]);
|
||||
});
|
||||
}));
|
||||
|
||||
gulp.task('keytar-bindings', gulp.series(function () {
|
||||
return del(['release/node_modules/keytar/build']);
|
||||
|
|
Loading…
Reference in New Issue