diff --git a/client/src/modules/pluginapi.js b/client/src/modules/pluginapi.js index cd4ec5bd..55c739ff 100644 --- a/client/src/modules/pluginapi.js +++ b/client/src/modules/pluginapi.js @@ -23,6 +23,7 @@ import { WebpackModules } from './webpackmodules'; import DiscordApi from './discordapi'; import { ReactComponents, ReactHelpers } from './reactcomponents'; import { Patcher, MonkeyPatch } from './patcher'; +import GlobalAc from '../ui/autocomplete'; export default class PluginApi { @@ -349,6 +350,43 @@ export default class PluginApi { }; } + /** + * Autocomplete + */ + + get autocompleteSets() { + return this._autocompleteSets || (this._autocompleteSets = new Map()); + } + addAutocompleteController(prefix, controller) { + if (!controller) controller = this.plugin; + if (GlobalAc.validPrefix(prefix)) return; + GlobalAc.add(prefix, controller); + this.autocompleteSets.set(prefix, controller); + } + removeAutocompleteController(prefix) { + if (this.autocompleteSets.get(prefix) !== GlobalAc.sets.get(prefix)) return; + GlobalAc.remove(prefix); + this.autocompleteSets.delete(prefix); + } + removeAllAutocompleteControllers() { + for (let [prefix] of this.autocompleteSets) { + this.removeAutocompleteController(prefix); + } + } + validAutocompletePrefix(prefix) { + return GlobalAc.validPrefix(prefix); + } + get Autocomplete() { + return Object.defineProperty({ + add: this.addAutocompleteController.bind(this), + remove: this.removeAutocompleteController.bind(this), + removeAll: this.removeAllAutocompleteControllers.bind(this), + validPrefix: this.validAutocompletePrefix.bind(this) + }, 'sets', { + get: () => this.autocompleteSets + }); + } + /** * Emotes */ diff --git a/client/src/ui/autocomplete.js b/client/src/ui/autocomplete.js index 3745c0f6..7adad277 100644 --- a/client/src/ui/autocomplete.js +++ b/client/src/ui/autocomplete.js @@ -4,15 +4,15 @@ import { VueInjector } from 'ui'; import AutocompleteComponent from './components/common/Autocomplete.vue'; import { Utils } from 'common'; -export default new class AutoComplete { +export default new class Autocomplete { get sets() { - return this._sets || (this._sets = {}); + return this._sets || (this._sets = new Map()); } async init() { this.cta = await ReactComponents.getComponent('ChannelTextArea', { selector: WebpackModules.getSelector('channelTextArea', 'emojiButton') }); - MonkeyPatch('BD:EMOTEMODULE', this.cta.component.prototype).after('render', this.channelTextAreaAfterRender.bind(this)); + MonkeyPatch('BD:Autocomplete', this.cta.component.prototype).after('render', this.channelTextAreaAfterRender.bind(this)); this.initialized = true; } @@ -32,21 +32,21 @@ export default new class AutoComplete { add(prefix, controller) { if (!this.initialized) this.init(); - if (this.sets.hasOwnProperty(prefix)) return; - this.sets[prefix] = controller; + if (this.validPrefix(prefix)) return; + this.sets.set(prefix, controller); } remove(prefix) { - if (this.sets.hasOwnProperty(prefix)) delete this.sets[prefix]; + this.sets.delete(prefix); } validPrefix(prefix) { - return this.sets.hasOwnProperty(prefix); + return this.sets.has(prefix); } items(prefix, sterm) { if (!this.validPrefix(prefix)) return []; - return this.sets[prefix].acsearch(sterm); + return this.sets.get(prefix).acsearch(sterm); } } diff --git a/tests/ext/plugins/Example 4/index.js b/tests/ext/plugins/Example 4/index.js index c37e5522..4c9f07d6 100644 --- a/tests/ext/plugins/Example 4/index.js +++ b/tests/ext/plugins/Example 4/index.js @@ -1,4 +1,4 @@ -exports.main = (Plugin, { Logger, Settings, Modals, BdMenu: { BdMenuItems }, CommonComponents, DiscordContextMenu, Api }) => class extends Plugin { +exports.main = (Plugin, { Logger, Settings, Modals, BdMenu: { BdMenuItems }, CommonComponents, DiscordContextMenu, Autocomplete, Api }) => class extends Plugin { async onstart() { this.keybindEvent = this.keybindEvent.bind(this); @@ -89,6 +89,13 @@ exports.main = (Plugin, { Logger, Settings, Modals, BdMenu: { BdMenuItems }, Com onClick: () => Modals.basic('Test', 'Hello from Plugin 4') } ]); + + /** + * Autocomplete. + * This calls `acsearch` on the controller (the plugin object). You can add multiple autocomplete sets by passing another controller. + */ + + Autocomplete.add('|'); } onstop() { @@ -97,10 +104,33 @@ exports.main = (Plugin, { Logger, Settings, Modals, BdMenu: { BdMenuItems }, Com BdMenuItems.removeAll(); DiscordContextMenu.removeAll(); + Autocomplete.removeAll(); } keybindEvent(event) { Logger.log('Keybind pressed', event); Modals.basic('Example Plugin 4', 'Test keybind activated.'); } + + acsearch(sterm) { + // sterm is the text after the prefix + Logger.log('Searching for', sterm); + + return { + title: ['Plugin 4 autocomplete'], + items: [ + {key: 'Item 1', value: {replaceWith: 'Something to insert when selected'}}, + {key: 'Item 2', value: {replaceWith: 'Something to insert when selected'}}, + {key: 'Item 3', value: {replaceWith: 'Something to insert when selected'}}, + {key: 'Item 4', value: {replaceWith: 'Something to insert when selected'}} + ] + + // `title` can also be an array - the second item will be white + // You can also add `type: 'imagetext'` here and add an `src` property to each item's value to show an image + }; + } + + get api() { + return Api; + } };