|
| 1 | +/** |
| 2 | + * This is an example of how you should add custom elements instead of manipulating the DOM directly |
| 3 | + */ |
| 4 | + |
| 5 | +// Import custom components |
| 6 | +const customVueComponent = require('./components/vuecomponent'); |
| 7 | +const customReactComponent = require('./components/reactcomponent'); |
| 8 | + |
| 9 | +module.exports = (Plugin, Api, Vendor) => { |
| 10 | + |
| 11 | + // Destructure some apis |
| 12 | + const { Logger, ReactComponents, Patcher, monkeyPatch, Reflection, Utils, CssUtils, VueInjector, Vuewrap, requireUncached } = Api; |
| 13 | + const { Vue } = Vendor; |
| 14 | + const { React } = Reflection.modules; // This should be in vendor |
| 15 | + |
| 16 | + return class extends Plugin { |
| 17 | + |
| 18 | + async onStart() { |
| 19 | + this.injectStyle(); |
| 20 | + this.patchGuildTextChannel(); |
| 21 | + this.patchMessages(); |
| 22 | + return true; |
| 23 | + } |
| 24 | + |
| 25 | + async onStop() { |
| 26 | + // The automatic unpatcher is not there yet |
| 27 | + Patcher.unpatchAll(); |
| 28 | + CssUtils.deleteAllStyles(); |
| 29 | + |
| 30 | + // Force update elements to remove our changes |
| 31 | + const GuildTextChannel = await ReactComponents.getComponent('GuildTextChannel'); |
| 32 | + GuildTextChannel.forceUpdateAll(); |
| 33 | + const MessageContent = await ReactComponents.getComponent('MessageContent', { selector: Reflection.resolve('container', 'containerCozy', 'containerCompact', 'edited').selector }); |
| 34 | + MessageContent.forceUpdateAll(); |
| 35 | + return true; |
| 36 | + } |
| 37 | + |
| 38 | + /* Inject some style for our custom element */ |
| 39 | + async injectStyle() { |
| 40 | + const css = ` |
| 41 | + .exampleCustomElement { |
| 42 | + background: #7a7d82; |
| 43 | + color: #FFF; |
| 44 | + border-radius: 5px; |
| 45 | + font-size: 12px; |
| 46 | + font-weight: 600; |
| 47 | + opacity: .5; |
| 48 | + &:hover { |
| 49 | + opacity: 1; |
| 50 | + } |
| 51 | + } |
| 52 | + .exampleBtnGroup { |
| 53 | + .bd-button { |
| 54 | + font-size: 14px; |
| 55 | + padding: 5px; |
| 56 | + } |
| 57 | + } |
| 58 | + `; |
| 59 | + await CssUtils.injectSass(css); |
| 60 | + } |
| 61 | + |
| 62 | + async patchGuildTextChannel() { |
| 63 | + // Get the GuildTextChannel component and patch it's render function |
| 64 | + const GuildTextChannel = await ReactComponents.getComponent('GuildTextChannel'); |
| 65 | + monkeyPatch(GuildTextChannel.component.prototype).after('render', this.injectCustomElements.bind(this)); |
| 66 | + // Force update to see our changes immediatly |
| 67 | + GuildTextChannel.forceUpdateAll(); |
| 68 | + } |
| 69 | + |
| 70 | + async patchMessages() { |
| 71 | + // Get Message component and patch it's render function |
| 72 | + const MessageContent = await ReactComponents.getComponent('MessageContent', { selector: Reflection.resolve('container', 'containerCozy', 'containerCompact', 'edited').selector }); |
| 73 | + monkeyPatch(MessageContent.component.prototype).after('render', this.injectGenericComponents.bind(this)); |
| 74 | + // Force update to see our changes immediatly |
| 75 | + MessageContent.forceUpdateAll(); |
| 76 | + } |
| 77 | + |
| 78 | + /* |
| 79 | + * Injecting a custom React element using React.createElement |
| 80 | + * https://reactjs.org/docs/react-api.html#createelement |
| 81 | + * Injecting a custom Vue element using Vue.component |
| 82 | + * https://vuejs.org/v2/guide/render-function.html |
| 83 | + **/ |
| 84 | + injectCustomElements(that, args, returnValue) { |
| 85 | + // Get the child we want using a treewalker since we know the child we want has a channel property and children. |
| 86 | + const child = Utils.findInReactTree(returnValue, filter => filter.hasOwnProperty('channel') && filter.children); |
| 87 | + if (!child) return; |
| 88 | + // If children is not an array make it into one |
| 89 | + if (!child.children instanceof Array) child.children = [child.children]; |
| 90 | + |
| 91 | + // Add our custom components to children |
| 92 | + child.children.push(customReactComponent(React, { onClick: e => this.handleClick(e, child.channel) })); |
| 93 | + child.children.push(customVueComponent(Vuewrap, { onClick: e => this.handleClick(e, child.channel) })); |
| 94 | + } |
| 95 | + |
| 96 | + /** |
| 97 | + * Inject generic components provided by BD |
| 98 | + */ |
| 99 | + injectGenericComponents(that, args, returnValue) { |
| 100 | + // If children is not an array make it into one |
| 101 | + if (!returnValue.props.children instanceof Array) returnValue.props.children = [returnValue.props.children]; |
| 102 | + // Add a generic Button component provided by BD |
| 103 | + returnValue.props.children.push(Api.Components.ButtonGroup({ |
| 104 | + classes: [ 'exampleBtnGroup' ], // Additional classes for button group |
| 105 | + buttons: [ |
| 106 | + { |
| 107 | + classes: ['exampleBtn'], // Additional classes for button |
| 108 | + text: 'Hello World!', // Text for button |
| 109 | + onClick: e => Logger.log('Hello World!') // Button click handler |
| 110 | + }, |
| 111 | + { |
| 112 | + classes: ['exampleBtn'], |
| 113 | + text: 'Button', |
| 114 | + onClick: e => Logger.log('Button!') |
| 115 | + } |
| 116 | + ] |
| 117 | + }).render()); // Render will return the wrapped component that can then be displayed |
| 118 | + } |
| 119 | + |
| 120 | + /** |
| 121 | + * Will log the channel object |
| 122 | + */ |
| 123 | + handleClick(e, channel) { |
| 124 | + Logger.log('Clicked!', channel); |
| 125 | + } |
| 126 | + } |
| 127 | + |
| 128 | +}; |
0 commit comments