New util functions and pluginmanager
This commit is contained in:
parent
2a8c6189e4
commit
5ad5eeff64
|
@ -10,17 +10,25 @@
|
|||
|
||||
import { DOM, BdUI } from 'ui';
|
||||
import BdCss from './styles/index.scss';
|
||||
import { Events, CssEditor, Globals, PluginManager } from 'modules';
|
||||
import { Events, CssEditor, Globals, PluginManager, ThemeManager } from 'modules';
|
||||
|
||||
class BetterDiscord {
|
||||
constructor() {
|
||||
window.pm = PluginManager;
|
||||
DOM.injectStyle(BdCss, 'bdmain');
|
||||
Events.on('global-ready', this.globalReady.bind(this));
|
||||
}
|
||||
|
||||
async init() {
|
||||
await PluginManager.loadAllPlugins();
|
||||
await ThemeManager.loadAllThemes();
|
||||
Events.emit('ready');
|
||||
}
|
||||
|
||||
globalReady() {
|
||||
this.vueInstance = BdUI.injectUi();
|
||||
(async () => {
|
||||
this.init();
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,5 +36,4 @@ if (window.BetterDiscord) {
|
|||
Logger.log('main', 'Attempting to inject again?');
|
||||
} else {
|
||||
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');
|
||||
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');
|
||||
})();
|
||||
|
||||
|
@ -40,7 +47,6 @@ export default new class extends Module {
|
|||
}
|
||||
Events.emit('socket-created', this.state.wsHook);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setWS(wSocket) {
|
||||
|
|
|
@ -2,5 +2,6 @@ export { default as Events } from './events';
|
|||
export { default as Settings } from './settings';
|
||||
export { default as CssEditor } from './csseditor';
|
||||
export { default as PluginManager } from './pluginmanager';
|
||||
export { default as ThemeManager } from './thememanager';
|
||||
export { default as Globals } from './globals';
|
||||
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.
|
||||
*/
|
||||
|
||||
import Globals from './globals';
|
||||
import { FileUtils, ClientLogger as Logger } from 'common';
|
||||
import ContentManager from './contentmanager';
|
||||
import Plugin from './plugin';
|
||||
|
||||
const localPlugins = [];
|
||||
|
||||
export default class {
|
||||
export default class extends ContentManager {
|
||||
|
||||
static get localPlugins() {
|
||||
return localPlugins;
|
||||
return this.localContent;
|
||||
}
|
||||
|
||||
static get pluginsPath() {
|
||||
Logger.log('PluginManager', 'hi!');
|
||||
return Globals.getObject('paths').find(path => path.id === 'plugins').path;
|
||||
static get moduleName() {
|
||||
return 'PluginManager';
|
||||
}
|
||||
|
||||
static async loadAllPlugins() {
|
||||
try {
|
||||
const directories = await FileUtils.readDir(this.pluginsPath);
|
||||
static get pathId() {
|
||||
return 'plugins';
|
||||
}
|
||||
|
||||
for (let dir of directories) {
|
||||
try {
|
||||
await this.loadPlugin(dir);
|
||||
} catch (err) {
|
||||
//We don't want every plugin to fail loading when one does
|
||||
Logger.err('PluginManager', err);
|
||||
}
|
||||
}
|
||||
static get loadAllPlugins() {
|
||||
return this.loadAllContent;
|
||||
}
|
||||
|
||||
return this.plugins;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
static get loadContent() { return this.loadPlugin }
|
||||
static async loadPlugin(paths, configs, info, main) {
|
||||
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
|
||||
import { Settings } from 'modules';
|
||||
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';
|
||||
|
||||
// Constants
|
||||
|
@ -79,7 +79,7 @@
|
|||
},
|
||||
components: {
|
||||
SidebarView, Sidebar, SidebarItem, ContentColumn,
|
||||
CoreSettings, UISettings, EmoteSettings, CssEditorView,
|
||||
CoreSettings, UISettings, EmoteSettings, CssEditorView, PluginsView,
|
||||
SvgX
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -48,7 +48,6 @@
|
|||
},
|
||||
created() {
|
||||
Events.on('ready', e => this.loaded = true);
|
||||
this.loaded = true;
|
||||
window.addEventListener('keyup', this.keyupListener);
|
||||
},
|
||||
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 UISettings } from './UISettings.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) {
|
||||
try {
|
||||
await this.fileExists(path);
|
||||
|
@ -120,7 +146,7 @@ export class FileUtils {
|
|||
return this.writeFile(path, JSON.stringify(json));
|
||||
}
|
||||
|
||||
static async readDir(path) {
|
||||
static async listDirectory(path) {
|
||||
try {
|
||||
await this.directoryExists(path);
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -133,6 +159,10 @@ export class FileUtils {
|
|||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async readDir(path) {
|
||||
return this.listDirectory(path);
|
||||
}
|
||||
}
|
||||
|
||||
const logs = [];
|
||||
|
|
Loading…
Reference in New Issue