New util functions and pluginmanager
This commit is contained in:
parent
2a8c6189e4
commit
5ad5eeff64
|
@ -10,17 +10,25 @@
|
||||||
|
|
||||||
import { DOM, BdUI } from 'ui';
|
import { DOM, BdUI } from 'ui';
|
||||||
import BdCss from './styles/index.scss';
|
import BdCss from './styles/index.scss';
|
||||||
import { Events, CssEditor, Globals, PluginManager } from 'modules';
|
import { Events, CssEditor, Globals, PluginManager, ThemeManager } from 'modules';
|
||||||
|
|
||||||
class BetterDiscord {
|
class BetterDiscord {
|
||||||
constructor() {
|
constructor() {
|
||||||
window.pm = PluginManager;
|
|
||||||
DOM.injectStyle(BdCss, 'bdmain');
|
DOM.injectStyle(BdCss, 'bdmain');
|
||||||
Events.on('global-ready', this.globalReady.bind(this));
|
Events.on('global-ready', this.globalReady.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
await PluginManager.loadAllPlugins();
|
||||||
|
await ThemeManager.loadAllThemes();
|
||||||
|
Events.emit('ready');
|
||||||
|
}
|
||||||
|
|
||||||
globalReady() {
|
globalReady() {
|
||||||
this.vueInstance = BdUI.injectUi();
|
this.vueInstance = BdUI.injectUi();
|
||||||
|
(async () => {
|
||||||
|
this.init();
|
||||||
|
})();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,5 +36,4 @@ if (window.BetterDiscord) {
|
||||||
Logger.log('main', 'Attempting to inject again?');
|
Logger.log('main', 'Attempting to inject again?');
|
||||||
} else {
|
} else {
|
||||||
let bdInstance = new BetterDiscord();
|
let bdInstance = new BetterDiscord();
|
||||||
// window.BetterDiscord = { 'vendor': Vendor };
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
/**
|
||||||
|
* BetterDiscord Content Manager 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 Globals from './globals';
|
||||||
|
import { FileUtils, ClientLogger as Logger } from 'common';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
export default class {
|
||||||
|
|
||||||
|
static get localContent() {
|
||||||
|
return this._localContent ? this._localContent : (this._localContent = []);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get contentPath() {
|
||||||
|
return this._contentPath ? this._contentPath : (this._contentPath = Globals.getObject('paths').find(path => path.id === this.pathId).path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async loadAllContent() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
await FileUtils.ensureDirectory(this.contentPath);
|
||||||
|
const directories = await FileUtils.listDirectory(this.contentPath);
|
||||||
|
|
||||||
|
for (let dir of directories) {
|
||||||
|
try {
|
||||||
|
await this.preloadContent(dir);
|
||||||
|
} catch (err) {
|
||||||
|
//We don't want every plugin/theme to fail loading when one does
|
||||||
|
Logger.err(this.moduleName, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.localContent;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async preloadContent(dirName, reload = false, index) {
|
||||||
|
try {
|
||||||
|
const contentPath = path.join(this.contentPath, dirName);
|
||||||
|
|
||||||
|
await FileUtils.directoryExists(contentPath);
|
||||||
|
|
||||||
|
if (!reload) {
|
||||||
|
const loaded = this.localContent.find(content => content.contentPath === contentPath);
|
||||||
|
if (loaded) {
|
||||||
|
throw { 'message': `Attempted to load already loaded user content: ${path}` };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const readConfig = await this.readConfig(contentPath);
|
||||||
|
const mainPath = path.join(contentPath, readConfig.main);
|
||||||
|
|
||||||
|
const userConfig = {
|
||||||
|
enabled: false,
|
||||||
|
config: readConfig.defaultConfig
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const readUserConfig = await this.readUserConfig(contentPath);
|
||||||
|
userConfig.config = userConfig.defaultConfig.map(config => {
|
||||||
|
const userSet = readUserConfig.config.find(c => c.id === config.id);
|
||||||
|
return userSet || config;
|
||||||
|
});
|
||||||
|
} catch (err) {/*We don't care if this fails it either means that user config doesn't exist or there's something wrong with it so we revert to default config*/ }
|
||||||
|
|
||||||
|
const configs = {
|
||||||
|
defaultConfig: readConfig.defaultConfig,
|
||||||
|
userConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
const paths = {
|
||||||
|
contentPath,
|
||||||
|
dirName,
|
||||||
|
mainPath
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = await this.loadContent(paths, configs, readConfig.info, readConfig.main);
|
||||||
|
this.localContent.push(content);
|
||||||
|
return content;
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async readConfig(configPath) {
|
||||||
|
configPath = path.resolve(configPath, 'config.json');
|
||||||
|
return FileUtils.readJsonFromFile(configPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async readUserConfig(configPath) {
|
||||||
|
configPath = path.resolve(configPath, 'user.config.json');
|
||||||
|
return FileUtils.readJsonFromFile(configPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -30,6 +30,13 @@ export default new class extends Module {
|
||||||
const config = await ClientIPC.send('getConfig');
|
const config = await ClientIPC.send('getConfig');
|
||||||
this.setState(config);
|
this.setState(config);
|
||||||
|
|
||||||
|
// This is for Discord to stop error reporting :3
|
||||||
|
window.BetterDiscord = {
|
||||||
|
'version': config.version,
|
||||||
|
'v': config.version
|
||||||
|
};
|
||||||
|
window.jQuery = {};
|
||||||
|
|
||||||
Events.emit('global-ready');
|
Events.emit('global-ready');
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -40,7 +47,6 @@ export default new class extends Module {
|
||||||
}
|
}
|
||||||
Events.emit('socket-created', this.state.wsHook);
|
Events.emit('socket-created', this.state.wsHook);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setWS(wSocket) {
|
setWS(wSocket) {
|
||||||
|
|
|
@ -2,5 +2,6 @@ export { default as Events } from './events';
|
||||||
export { default as Settings } from './settings';
|
export { default as Settings } from './settings';
|
||||||
export { default as CssEditor } from './csseditor';
|
export { default as CssEditor } from './csseditor';
|
||||||
export { default as PluginManager } from './pluginmanager';
|
export { default as PluginManager } from './pluginmanager';
|
||||||
|
export { default as ThemeManager } from './thememanager';
|
||||||
export { default as Globals } from './globals';
|
export { default as Globals } from './globals';
|
||||||
export { default as Vendor } from './vendor';
|
export { default as Vendor } from './vendor';
|
|
@ -0,0 +1,58 @@
|
||||||
|
/**
|
||||||
|
* BetterDiscord Plugin Base
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default class {
|
||||||
|
|
||||||
|
constructor(pluginInternals) {
|
||||||
|
this.__pluginInternals = pluginInternals;
|
||||||
|
this.hasSettings = this.pluginConfig && this.pluginConfig.length > 0;
|
||||||
|
this.start = this.start.bind(this);
|
||||||
|
this.stop = this.stop.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
get configs() { return this.__pluginInternals.configs }
|
||||||
|
get info() { return this.__pluginInternals.info }
|
||||||
|
get paths() { return this.__pluginInternals.paths }
|
||||||
|
get main() { return this.__pluginInternals.main }
|
||||||
|
get defaultConfig() { return this.configs.defaultConfig }
|
||||||
|
get userConfig() { return this.configs.userConfig }
|
||||||
|
get name() { return this.info.name }
|
||||||
|
get authors() { return this.info.authors }
|
||||||
|
get version() { return this.info.version }
|
||||||
|
get pluginPath() { return this.paths.pluginPath }
|
||||||
|
get dirName() { return this.paths.dirName }
|
||||||
|
get enabled() { return this.userConfig.enabled }
|
||||||
|
get pluginConfig() { return this.userConfig.pluginConfig }
|
||||||
|
|
||||||
|
start() {
|
||||||
|
if (this.onStart) {
|
||||||
|
const started = this.onStart();
|
||||||
|
if (started) {
|
||||||
|
return this.userConfig.enabled = true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.userConfig.enabled = true; //Assume plugin started since it doesn't have onStart
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
if (this.onStop) {
|
||||||
|
const stopped = this.onStop();
|
||||||
|
if (stopped) {
|
||||||
|
this.userConfig.enabled = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.userConfig.enabled = false;
|
||||||
|
return true; //Assume plugin stopped since it doesn't have onStop
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -8,39 +8,34 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Globals from './globals';
|
import ContentManager from './contentmanager';
|
||||||
import { FileUtils, ClientLogger as Logger } from 'common';
|
import Plugin from './plugin';
|
||||||
|
|
||||||
const localPlugins = [];
|
export default class extends ContentManager {
|
||||||
|
|
||||||
export default class {
|
|
||||||
|
|
||||||
static get localPlugins() {
|
static get localPlugins() {
|
||||||
return localPlugins;
|
return this.localContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get pluginsPath() {
|
static get moduleName() {
|
||||||
Logger.log('PluginManager', 'hi!');
|
return 'PluginManager';
|
||||||
return Globals.getObject('paths').find(path => path.id === 'plugins').path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async loadAllPlugins() {
|
static get pathId() {
|
||||||
try {
|
return 'plugins';
|
||||||
const directories = await FileUtils.readDir(this.pluginsPath);
|
}
|
||||||
|
|
||||||
for (let dir of directories) {
|
static get loadAllPlugins() {
|
||||||
try {
|
return this.loadAllContent;
|
||||||
await this.loadPlugin(dir);
|
}
|
||||||
} catch (err) {
|
|
||||||
//We don't want every plugin to fail loading when one does
|
|
||||||
Logger.err('PluginManager', err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.plugins;
|
static get loadContent() { return this.loadPlugin }
|
||||||
} catch (err) {
|
static async loadPlugin(paths, configs, info, main) {
|
||||||
throw err;
|
const plugin = window.require(paths.mainPath)(Plugin, {}, {});
|
||||||
}
|
const instance = new plugin({ configs, info, main, paths: { pluginPath: paths.contentPath, dirName: paths.dirName } });
|
||||||
|
|
||||||
|
if (instance.enabled) instance.start();
|
||||||
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* BetterDiscord Theme Manager 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 ContentManager from './contentmanager';
|
||||||
|
|
||||||
|
export default class extends ContentManager {
|
||||||
|
|
||||||
|
static get localThemes() {
|
||||||
|
return this.localContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get pathId() {
|
||||||
|
return 'themes';
|
||||||
|
}
|
||||||
|
|
||||||
|
static get loadAllThemes() {
|
||||||
|
return this.loadAllContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -45,7 +45,7 @@
|
||||||
// Imports
|
// Imports
|
||||||
import { Settings } from 'modules';
|
import { Settings } from 'modules';
|
||||||
import { SidebarView, Sidebar, SidebarItem, ContentColumn } from './sidebar';
|
import { SidebarView, Sidebar, SidebarItem, ContentColumn } from './sidebar';
|
||||||
import { CoreSettings, UISettings, EmoteSettings, CssEditorView } from './bd';
|
import { CoreSettings, UISettings, EmoteSettings, CssEditorView, PluginsView } from './bd';
|
||||||
import { SvgX } from './common';
|
import { SvgX } from './common';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
|
@ -79,7 +79,7 @@
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
SidebarView, Sidebar, SidebarItem, ContentColumn,
|
SidebarView, Sidebar, SidebarItem, ContentColumn,
|
||||||
CoreSettings, UISettings, EmoteSettings, CssEditorView,
|
CoreSettings, UISettings, EmoteSettings, CssEditorView, PluginsView,
|
||||||
SvgX
|
SvgX
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -48,7 +48,6 @@
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
Events.on('ready', e => this.loaded = true);
|
Events.on('ready', e => this.loaded = true);
|
||||||
this.loaded = true;
|
|
||||||
window.addEventListener('keyup', this.keyupListener);
|
window.addEventListener('keyup', this.keyupListener);
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<template>
|
||||||
|
<div>PluginCard</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {}
|
||||||
|
</script>
|
|
@ -0,0 +1,75 @@
|
||||||
|
/**
|
||||||
|
* BetterDiscord Plugins View Component
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SettingsWrapper headertext="Plugins">
|
||||||
|
<div class="bd-flex bd-flex-col bd-pluginsView">
|
||||||
|
<div class="bd-flex bd-tabheader">
|
||||||
|
<div class="bd-flex-grow bd-button" :class="{'bd-active': local}" @click="showLocal">
|
||||||
|
<h3>Local</h3>
|
||||||
|
<div class="bd-material-button" @click="refreshLocal">
|
||||||
|
<refresh />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bd-flex-grow bd-button" :class="{'bd-active': !local}" @click="showOnline">
|
||||||
|
<h3>Online</h3>
|
||||||
|
<div class="bd-material-button">
|
||||||
|
<refresh />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="local" class="bd-flex bd-flex-grow bd-flex-col bd-plugins-container bd-local-plugins">
|
||||||
|
<PluginCard v-for="plugin in localPlugins" :plugin="plugin" :key="plugin.id" :togglePlugin="togglePlugin" :reloadPlugin="reloadPlugin" :showSettings="showSettings" />
|
||||||
|
</div>
|
||||||
|
<div v-if="!local" class="bd-spinner-container">
|
||||||
|
<div class="bd-spinner-2"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="settingsOpen !== null" class="bd-backdrop" @click="settingsOpen = null"></div>
|
||||||
|
<div v-if="settingsOpen !== null" class="bd-modal">
|
||||||
|
<PluginSettingsModal :plugin="settingsOpen" />
|
||||||
|
</div>
|
||||||
|
</SettingsWrapper>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Imports
|
||||||
|
import { PluginManager } from 'modules';
|
||||||
|
import { SettingsWrapper } from './';
|
||||||
|
import PluginCard from './PluginCard.vue';
|
||||||
|
import Refresh from 'vue-material-design-icons/refresh.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
local: true,
|
||||||
|
pluginManager: PluginManager,
|
||||||
|
settingsOpen: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
SettingsWrapper, PluginCard, Refresh
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
localPlugins() {
|
||||||
|
return this.pluginManager.localPlugins;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
showLocal() { },
|
||||||
|
showOnline() { },
|
||||||
|
refreshLocal() { },
|
||||||
|
togglePlugin() { },
|
||||||
|
reloadPlugin() { },
|
||||||
|
showSettings() { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
|
@ -2,4 +2,5 @@ export { default as SettingsWrapper } from './SettingsWrapper.vue';
|
||||||
export { default as CoreSettings } from './CoreSettings.vue';
|
export { default as CoreSettings } from './CoreSettings.vue';
|
||||||
export { default as UISettings } from './UISettings.vue';
|
export { default as UISettings } from './UISettings.vue';
|
||||||
export { default as EmoteSettings } from './EmoteSettings.vue';
|
export { default as EmoteSettings } from './EmoteSettings.vue';
|
||||||
export { default as CssEditorView } from './CssEditor.vue';
|
export { default as CssEditorView } from './CssEditor.vue';
|
||||||
|
export { default as PluginsView } from './PluginsView.vue';
|
|
@ -72,6 +72,32 @@ export class FileUtils {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async createDirectory(path) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fs.mkdir(path, err => {
|
||||||
|
if (err) {
|
||||||
|
if (err.code === 'EEXIST') return resolve();
|
||||||
|
else return reject(err);
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static async ensureDirectory(path) {
|
||||||
|
try {
|
||||||
|
await this.directoryExists(path);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
try {
|
||||||
|
await this.createDirectory(path);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static async readFile(path) {
|
static async readFile(path) {
|
||||||
try {
|
try {
|
||||||
await this.fileExists(path);
|
await this.fileExists(path);
|
||||||
|
@ -120,7 +146,7 @@ export class FileUtils {
|
||||||
return this.writeFile(path, JSON.stringify(json));
|
return this.writeFile(path, JSON.stringify(json));
|
||||||
}
|
}
|
||||||
|
|
||||||
static async readDir(path) {
|
static async listDirectory(path) {
|
||||||
try {
|
try {
|
||||||
await this.directoryExists(path);
|
await this.directoryExists(path);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
@ -133,6 +159,10 @@ export class FileUtils {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async readDir(path) {
|
||||||
|
return this.listDirectory(path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const logs = [];
|
const logs = [];
|
||||||
|
|
Loading…
Reference in New Issue