BetterDiscordApp-v2/tests/ext/plugins/Render Example/index.js

129 lines
5.5 KiB
JavaScript

/**
* This is an example of how you should add custom elements instead of manipulating the DOM directly
*/
// Import custom components
const customVueComponent = require('./components/vuecomponent');
const customReactComponent = require('./components/reactcomponent');
module.exports = (Plugin, Api, Vendor) => {
// Destructure some apis
const { Logger, ReactComponents, Patcher, monkeyPatch, Reflection, Utils, CssUtils, VueInjector, Vuewrap, requireUncached } = Api;
const { Vue } = Vendor;
const { React } = Reflection.modules; // This should be in vendor
return class extends Plugin {
async onStart() {
this.injectStyle();
this.patchGuildTextChannel();
this.patchMessages();
return true;
}
async onStop() {
// The automatic unpatcher is not there yet
Patcher.unpatchAll();
CssUtils.deleteAllStyles();
// Force update elements to remove our changes
const GuildTextChannel = await ReactComponents.getComponent('GuildTextChannel');
GuildTextChannel.forceUpdateAll();
const MessageContent = await ReactComponents.getComponent('MessageContent', { selector: Reflection.resolve('container', 'containerCozy', 'containerCompact', 'edited').selector }, m => m.defaultProps && m.defaultProps.hasOwnProperty('disableButtons'));
MessageContent.forceUpdateAll();
return true;
}
/* Inject some style for our custom element */
async injectStyle() {
const css = `
.exampleCustomElement {
background: #7a7d82;
color: #FFF;
border-radius: 5px;
font-size: 12px;
font-weight: 600;
opacity: .5;
&:hover {
opacity: 1;
}
}
.exampleBtnGroup {
.bd-button {
font-size: 14px;
padding: 5px;
}
}
`;
await CssUtils.injectSass(css);
}
async patchGuildTextChannel() {
// Get the GuildTextChannel component and patch it's render function
const GuildTextChannel = await ReactComponents.getComponent('GuildTextChannel');
monkeyPatch(GuildTextChannel.component.prototype).after('render', this.injectCustomElements.bind(this));
// Force update to see our changes immediatly
GuildTextChannel.forceUpdateAll();
}
async patchMessages() {
// Get Message component and patch it's render function
const MessageContent = await ReactComponents.getComponent('MessageContent', { selector: Reflection.resolve('container', 'containerCozy', 'containerCompact', 'edited').selector });
monkeyPatch(MessageContent.component.prototype).after('render', this.injectGenericComponents.bind(this));
// Force update to see our changes immediatly
MessageContent.forceUpdateAll();
}
/*
* Injecting a custom React element using React.createElement
* https://reactjs.org/docs/react-api.html#createelement
* Injecting a custom Vue element using Vue.component
* https://vuejs.org/v2/guide/render-function.html
**/
injectCustomElements(that, args, returnValue) {
// Get the child we want using a treewalker since we know the child we want has a channel property and children.
const child = Utils.findInReactTree(returnValue, filter => filter.hasOwnProperty('channel') && filter.children);
if (!child) return;
// If children is not an array make it into one
if (!child.children instanceof Array) child.children = [child.children];
// Add our custom components to children
child.children.push(customReactComponent(React, { onClick: e => this.handleClick(e, child.channel) }));
child.children.push(customVueComponent(Vuewrap, { onClick: e => this.handleClick(e, child.channel) }));
}
/**
* Inject generic components provided by BD
*/
injectGenericComponents(that, args, returnValue) {
// If children is not an array make it into one
if (!returnValue.props.children instanceof Array) returnValue.props.children = [returnValue.props.children];
// Add a generic Button component provided by BD
returnValue.props.children.push(Api.Components.ButtonGroup({
classes: [ 'exampleBtnGroup' ], // Additional classes for button group
buttons: [
{
classes: ['exampleBtn'], // Additional classes for button
text: 'Hello World!', // Text for button
onClick: e => Logger.log('Hello World!') // Button click handler
},
{
classes: ['exampleBtn'],
text: 'Button',
onClick: e => Logger.log('Button!')
}
]
}).render()); // Render will return the wrapped component that can then be displayed
}
/**
* Will log the channel object
*/
handleClick(e, channel) {
Logger.log('Clicked!', channel);
}
}
};