/** * 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); } } };