Content reloading

This commit is contained in:
Samuel Elliott 2018-02-21 17:46:27 +00:00
parent b9145b5b85
commit 4cfa4ee466
No known key found for this signature in database
GPG Key ID: 8420C7CDE43DC4D6
13 changed files with 165 additions and 78 deletions

View File

@ -185,6 +185,9 @@ export default class {
};
const content = await this.loadContent(paths, configs, readConfig.info, readConfig.main, readConfig.dependencies);
if (!reload && this.getContentById(content.id))
throw {message: `A ${this.contentType} with the ID ${content.id} already exists.`};
if (reload) this.localContent[index] = content;
else this.localContent.push(content);
return content;
@ -194,6 +197,43 @@ export default class {
}
}
/**
* Unload content
* @param {any} content Content to unload
* @param {bool} reload Whether to reload the content after
*/
static async unloadContent(content, reload) {
content = this.findContent(content);
if (!content) throw {message: `Could not find a ${this.contentType} from ${content}.`};
try {
if (content.enabled && content.disable) content.disable(false);
if (content.enabled && content.stop) content.stop(false);
if (content.onunload) content.onunload(reload);
if (content.onUnload) content.onUnload(reload);
const index = this.getContentIndex(content);
delete window.require.cache[window.require.resolve(content.paths.mainPath)];
if (reload) {
const newcontent = await this.preloadContent(content.dirName, true, index);
if (newcontent.enabled && newcontent.start) newcontent.start(false);
return newcontent;
} else this.localContent.splice(index, 1);
} catch (err) {
Logger.err(this.moduleName, err);
throw err;
}
}
/**
* Reload content
* @param {any} content Content to reload
*/
static async reloadContent(content) {
return this.unloadContent(content, true);
}
/**
* Read content config file
* @param {any} configPath Config file path
@ -212,19 +252,26 @@ export default class {
return FileUtils.readJsonFromFile(configPath);
}
/**
* Checks if the passed object is an instance of this content type.
* @param {any} content Object to check
*/
static isThisContent(content) {
return false;
}
/**
* Wildcard content finder
* @param {any} wild Content name | id | path | dirname
* @param {bool} nonunique Allow searching attributes that may not be unique
*/
//TODO make this nicer
static findContent(wild) {
let content = this.getContentByName(wild);
if (content) return content;
content = this.getContentById(wild);
if (content) return content;
content = this.getContentByPath(wild);
if (content) return content;
return this.getContentByDirName(wild);
static findContent(wild, nonunique) {
if (this.isThisContent(wild)) return wild;
let content;
if (content = this.getContentById(wild)) return content;
if (content = this.getContentByDirName(wild)) return content;
if (content = this.getContentByPath(wild)) return content;
if (content = nonunique && this.getContentByName(wild)) return content;
}
static getContentIndex(content) { return this.localContent.findIndex(c => c === content) }

View File

@ -45,11 +45,12 @@ export default class ExtModule {
get main() { return this.__pluginInternals.main }
get defaultConfig() { return this.configs.defaultConfig }
get userConfig() { return this.configs.userConfig }
get id() { return this.info.id || this.info.name.replace(/[^a-zA-Z0-9-]/g, '-').replace(/--/g, '-') }
get id() { return this.info.id || this.info.name.toLowerCase().replace(/[^a-zA-Z0-9-]/g, '-').replace(/--/g, '-') }
get name() { return this.info.name }
get authors() { return this.info.authors }
get version() { return this.info.version }
get pluginPath() { return this.paths.contentPath }
get contentPath() { return this.paths.contentPath }
get modulePath() { return this.paths.contentPath }
get dirName() { return this.paths.dirName }
get enabled() { return true }
get config() { return this.userConfig.config || [] }

View File

@ -39,7 +39,19 @@ export default class extends ContentManager {
static get loadContent() { return this.loadModule }
static async loadModule(paths, configs, info, main) {
return new ExtModule({ configs, info, main, paths: { contentPath: paths.contentPath, dirName: paths.dirName, mainPath: paths.mainPath } });
return new ExtModule({
configs, info, main,
paths: {
contentPath: paths.contentPath,
dirName: paths.dirName,
mainPath: paths.mainPath
}
});
}
static get isExtModule() { return this.isThisContent }
static isThisContent(module) {
return module instanceof ExtModule;
}
static get findModule() { return this.findContent }

View File

@ -52,7 +52,7 @@ export default class Plugin {
get defaultConfig() { return this.configs.defaultConfig }
get userConfig() { return this.configs.userConfig }
get configSchemes() { return this.configs.schemes }
get id() { return this.info.id || this.name.replace(/[^a-zA-Z0-9-]/g, '-').replace(/--/g, '-') }
get id() { return this.info.id || this.name.toLowerCase().replace(/[^a-zA-Z0-9-]/g, '-').replace(/--/g, '-') }
get name() { return this.info.name }
get authors() { return this.info.authors }
get version() { return this.info.version }
@ -123,25 +123,32 @@ export default class Plugin {
}
}
start() {
start(save = true) {
if (this.onstart && !this.onstart()) return false;
if (this.onStart && !this.onStart()) return false;
if (!this.enabled) {
this.userConfig.enabled = true;
this.saveConfiguration();
if (save) this.saveConfiguration();
}
return true;
}
stop() {
stop(save = true) {
if (this.onstop && !this.onstop()) return false;
if (this.onStop && !this.onStop()) return false;
this.userConfig.enabled = false;
this.saveConfiguration();
if (this.enabled) {
this.userConfig.enabled = false;
if (save) this.saveConfiguration();
}
return true;
}
unload() {
PluginManager.unloadPlugin(this);
}
}

View File

@ -35,10 +35,12 @@ export default class extends ContentManager {
}
static async loadAllPlugins(suppressErrors) {
this.loaded = false;
const loadAll = await this.loadAllContent(suppressErrors);
this.localPlugins.forEach(plugin => {
this.loaded = true;
for (let plugin of this.localPlugins) {
if (plugin.enabled) plugin.start();
});
}
return loadAll;
}
@ -46,7 +48,6 @@ export default class extends ContentManager {
static get loadContent() { return this.loadPlugin }
static async loadPlugin(paths, configs, info, main, dependencies) {
const deps = [];
if (dependencies) {
for (const [key, value] of Object.entries(dependencies)) {
@ -61,36 +62,21 @@ export default class extends ContentManager {
}
const plugin = window.require(paths.mainPath)(Plugin, new PluginApi(info), Vendor, deps);
const instance = new plugin({ configs, info, main, paths: { contentPath: paths.contentPath, dirName: paths.dirName, mainPath: paths.mainPath } });
const instance = new plugin({
configs, info, main,
paths: {
contentPath: paths.contentPath,
dirName: paths.dirName,
mainPath: paths.mainPath
}
});
if (instance.enabled && this.loaded) instance.start();
return instance;
}
static get unloadContent() { return this.unloadPlugin }
static async unloadPlugin(plugin) {
try {
if (plugin.enabled) plugin.stop();
const { pluginPath } = plugin;
const index = this.getPluginIndex(plugin);
delete window.require.cache[window.require.resolve(pluginPath)];
this.localPlugins.splice(index, 1);
} catch (err) {
//This might fail but we don't have any other option at this point
Logger.err('PluginManager', err);
}
}
static async reloadPlugin(plugin) {
const _plugin = plugin instanceof Plugin ? plugin : this.findPlugin(plugin);
if (!_plugin) throw { 'message': 'Attempted to reload a plugin that is not loaded?' };
if (!_plugin.stop()) throw { 'message': 'Plugin failed to stop!' };
const index = this.getPluginIndex(_plugin);
const { pluginPath, dirName } = _plugin;
delete window.require.cache[window.require.resolve(pluginPath)];
return this.preloadContent(dirName, true, index);
}
static get unloadPlugin() { return this.unloadContent }
static get reloadPlugin() { return this.reloadContent }
static stopPlugin(name) {
const plugin = name instanceof Plugin ? name : this.getPluginByName(name);
@ -112,6 +98,11 @@ export default class extends ContentManager {
return true; //Return true anyways since plugin doesn't exist
}
static get isPlugin() { return this.isThisContent }
static isThisContent(plugin) {
return plugin instanceof Plugin;
}
static get findPlugin() { return this.findContent }
static get getPluginIndex() { return this.getContentIndex }
static get getPluginByName() { return this.getContentByName }

View File

@ -49,6 +49,7 @@ export default class Theme {
get icon() { return this.info.icon }
get paths() { return this.__themeInternals.paths }
get main() { return this.__themeInternals.main }
get loaded() { return this.__themeInternals.loaded }
get defaultConfig() { return this.configs.defaultConfig }
get userConfig() { return this.configs.userConfig }
get configSchemes() { return this.configs.schemes }
@ -56,6 +57,7 @@ export default class Theme {
get name() { return this.info.name }
get authors() { return this.info.authors }
get version() { return this.info.version }
get contentPath() { return this.paths.contentPath }
get themePath() { return this.paths.contentPath }
get dirName() { return this.paths.dirName }
get enabled() { return this.userConfig.enabled }
@ -115,17 +117,17 @@ export default class Theme {
}
}
enable() {
enable(save = true) {
if (!this.enabled) {
this.userConfig.enabled = true;
this.saveConfiguration();
if (save) this.saveConfiguration();
}
DOM.injectTheme(this.css, this.id);
}
disable() {
disable(save = true) {
this.userConfig.enabled = false;
this.saveConfiguration();
if (save) this.saveConfiguration();
DOM.deleteTheme(this.id);
}

View File

@ -30,9 +30,8 @@ export default class ThemeManager extends ContentManager {
return 'themes';
}
static get loadAllThemes() {
return this.loadAllContent;
}
static get loadAllThemes() { return this.loadAllContent }
static get refreshThemes() { return this.refreshContent }
static get loadContent() { return this.loadTheme }
static async loadTheme(paths, configs, info, main) {
@ -53,6 +52,9 @@ export default class ThemeManager extends ContentManager {
}
}
static get unloadTheme() { return this.unloadContent }
static get reloadTheme() { return this.reloadContent }
static enableTheme(theme) {
theme.enable();
}
@ -61,10 +63,17 @@ export default class ThemeManager extends ContentManager {
theme.disable();
}
static reloadTheme(theme) {
static get unloadTheme() { return this.unloadContent }
static async reloadTheme(theme) {
theme = await this.reloadContent(theme);
theme.recompile();
}
static get isTheme() { return this.isThisContent }
static isThisContent(theme) {
return theme instanceof Theme;
}
static async getConfigAsSCSS(config) {
const variables = [];

View File

@ -15,7 +15,7 @@
<Button v-tooltip="'Settings'" v-if="plugin.hasSettings" :onClick="() => showSettings(plugin)"><MiSettings size="18" /></Button>
<Button v-tooltip="'Reload'" :onClick="reloadPlugin"><MiRefresh size="18" /></Button>
<Button v-tooltip="'Edit'" :onClick="editPlugin"><MiPencil size="18" /></Button>
<Button v-tooltip="'Uninstall'" type="err"><MiDelete size="18" /></Button>
<Button v-tooltip="'Uninstall (shift + click to unload)'" :onClick="deletePlugin" type="err"><MiDelete size="18" /></Button>
</ButtonGroup>
</Card>
</template>
@ -31,7 +31,7 @@
settingsOpen: false
}
},
props: ['plugin', 'togglePlugin', 'reloadPlugin', 'showSettings'],
props: ['plugin', 'togglePlugin', 'reloadPlugin', 'deletePlugin', 'showSettings'],
components: {
Card, Button, ButtonGroup, SettingSwitch, MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension
},

View File

@ -23,7 +23,7 @@
<div class="bd-flex bd-flex-col bd-pluginsView">
<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(plugin)" :reloadPlugin="() => reloadPlugin(plugin)" :showSettings="() => showSettings(plugin)" />
<PluginCard v-for="plugin in localPlugins" :plugin="plugin" :key="plugin.id" :togglePlugin="() => togglePlugin(plugin)" :reloadPlugin="() => reloadPlugin(plugin)" :deletePlugin="e => deletePlugin(plugin, e.shiftKey)" :showSettings="() => showSettings(plugin)" />
</div>
<div v-if="!local" class="bd-spinner-container">
<div class="bd-spinner-2"></div>
@ -67,11 +67,8 @@
async togglePlugin(plugin) {
// TODO Display error if plugin fails to start/stop
try {
if (plugin.enabled) {
await PluginManager.stopPlugin(plugin);
} else {
await PluginManager.startPlugin(plugin);
}
await plugin.enabled ? PluginManager.stopPlugin(plugin) : PluginManager.startPlugin(plugin);
this.$forceUpdate();
} catch (err) {
console.log(err);
}
@ -84,6 +81,15 @@
console.log(err);
}
},
async deletePlugin(plugin, unload) {
try {
if (unload) await PluginManager.unloadPlugin(plugin);
else await PluginManager.deletePlugin(plugin);
this.$forceUpdate();
} catch (err) {
console.error(err);
}
},
showSettings(plugin) {
return Modals.contentSettings(plugin);
}

View File

@ -13,9 +13,9 @@
<SettingSwitch slot="toggle" :checked="theme.enabled" :change="toggleTheme" />
<ButtonGroup slot="controls">
<Button v-tooltip="'Settings'" v-if="theme.hasSettings" :onClick="showSettings"><MiSettings size="18" /></Button>
<Button v-tooltip="'Reload'" :onClick="reloadTheme"><MiRefresh size="18" /></Button>
<Button v-tooltip="'Recompile (shift + click to reload)'" :onClick="reloadTheme"><MiRefresh size="18" /></Button>
<Button v-tooltip="'Edit'" :onClick="editTheme"><MiPencil size="18" /></Button>
<Button v-tooltip="'Uninstall'" type="err"><MiDelete size="18" /></Button>
<Button v-tooltip="'Uninstall (shift + click to unload)'" :onClick="deleteTheme" type="err"><MiDelete size="18" /></Button>
</ButtonGroup>
</Card>
</template>
@ -31,7 +31,7 @@
settingsOpen: false
}
},
props: ['theme', 'toggleTheme', 'reloadTheme', 'showSettings'],
props: ['theme', 'toggleTheme', 'reloadTheme', 'deleteTheme', 'showSettings'],
components: {
Card, Button, ButtonGroup, SettingSwitch, MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension
},

View File

@ -23,7 +23,7 @@
<div class="bd-flex bd-flex-col bd-themesView">
<div v-if="local" class="bd-flex bd-flex-grow bd-flex-col bd-themes-container bd-local-themes">
<ThemeCard v-for="theme in localThemes" :theme="theme" :key="theme.id" :toggleTheme="() => toggleTheme(theme)" :reloadTheme="() => reloadTheme(theme)" :showSettings="() => showSettings(theme)" />
<ThemeCard v-for="theme in localThemes" :theme="theme" :key="theme.id" :toggleTheme="() => toggleTheme(theme)" :reloadTheme="e => reloadTheme(theme, e.shiftKey)" :showSettings="() => showSettings(theme)" :deleteTheme="e => deleteTheme(theme, e.shiftKey)" />
</div>
<div v-if="!local" class="bd-spinner-container">
<div class="bd-spinner-2"></div>
@ -59,31 +59,38 @@
this.local = false;
},
async refreshLocal() {
await ThemeManager.refreshTheme();
await ThemeManager.refreshThemes();
},
async refreshOnline() {
},
toggleTheme(theme) {
async toggleTheme(theme) {
// TODO Display error if theme fails to enable/disable
try {
if (theme.enabled) {
ThemeManager.disableTheme(theme);
} else {
ThemeManager.enableTheme(theme);
}
await theme.enabled ? ThemeManager.disableTheme(theme) : ThemeManager.enableTheme(theme);
this.$forceUpdate();
} catch (err) {
console.log(err);
}
},
async reloadTheme(theme) {
async reloadTheme(theme, reload) {
try {
await ThemeManager.reloadTheme(theme);
if (reload) await ThemeManager.reloadTheme(theme);
else await theme.recompile();
this.$forceUpdate();
} catch (err) {
console.log(err);
}
},
async deleteTheme(theme, unload) {
try {
if (unload) await ThemeManager.unloadTheme(theme);
else await ThemeManager.deleteTheme(theme);
this.$forceUpdate();
} catch (err) {
console.error(err);
}
},
showSettings(theme) {
return Modals.contentSettings(theme);
}

View File

@ -9,7 +9,7 @@
*/
<template>
<div class="bd-button" :class="[{'bd-disabled': disabled}, {'bd-err': type === 'err'}]" @click="!disabled && !loading ? onClick() : null">
<div class="bd-button" :class="[{'bd-disabled': disabled}, {'bd-err': type === 'err'}]" @click="!disabled && !loading ? onClick($event) : null">
<div v-if="loading" class="bd-spinner-7"></div>
<slot v-else />
</div>

View File

@ -33,6 +33,11 @@ module.exports = (Plugin, Api, Vendor, Dependencies) => {
return true;
}
onUnload(reload) {
Logger.log('Unloading plugin');
delete require.cache[require.resolve('./component')];
}
eventTest(e) {
Logger.log(e);
}