Merge pull request #47 from JsSucks/ui

Merge
This commit is contained in:
Alexei Stukov 2018-01-25 09:56:47 +02:00 committed by GitHub
commit b7aab16185
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 397 additions and 74 deletions

View File

@ -17,7 +17,6 @@ class Plugin {
constructor(pluginInternals) {
this.__pluginInternals = pluginInternals;
this.userConfig.enabled = this.userConfig.enabled || false;
this.start = this.start.bind(this);
this.stop = this.stop.bind(this);
}
@ -34,6 +33,7 @@ class Plugin {
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) {
@ -134,26 +134,35 @@ class PluginManager extends Module {
}
}
async loadPlugin(pluginPath) {
async loadPlugin(pluginPath, reload = false, index) {
const { plugins } = this.state;
const dirName = pluginPath;
try {
pluginPath = path.join(this.pluginsPath, pluginPath);
if (!reload) {
const loaded = plugins.find(plugin => plugin.pluginPath === pluginPath);
if (loaded) {
throw { 'message': 'Attempted to load an already loaded plugin' };
}
}
const readConfig = await this.readConfig(pluginPath);
const mainPath = path.join(pluginPath, readConfig.main);
const userConfigPath = path.join(pluginPath, 'user.config.json');
let userConfig = readConfig.defaultConfig;
const userConfig = {
enabled: false,
pluginConfig: readConfig.defaultConfig
};
try {
const readUserConfig = await FileUtils.readJsonFromFile(userConfigPath);
userConfig = Object.assign({}, userConfig, readUserConfig);
//userConfig = Object.assign({}, userConfig, readUserConfig);
userConfig.pluginConfig = readConfig.defaultConfig.map(config => {
const userSet = readUserConfig.pluginConfig.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 = {
@ -165,8 +174,8 @@ class PluginManager extends Module {
const instance = new plugin({configs, info: readConfig.info, main: readConfig.main, paths: { pluginPath, dirName }});
if (instance.enabled) instance.start();
plugins.push(instance);
if (reload) plugins[index] = instance;
else plugins.push(instance);
this.setState(plugins);
@ -177,16 +186,16 @@ class PluginManager extends Module {
}
async reloadPlugin(plugin) {
const _plugin = this.findPlugin(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)];
this.plugins.splice(index, 1);
// this.plugins.splice(index, 1);
return this.loadPlugin(dirName);
return this.loadPlugin(dirName, true, index);
}
//TODO make this nicer
@ -207,7 +216,7 @@ class PluginManager extends Module {
getPluginByDirName(dirName) { return this.plugins.find(p => p.dirName === dirName) }
stopPlugin(name) {
const plugin = this.getPluginByName(name);
const plugin = name instanceof Plugin ? name : this.getPluginByName(name);
try {
if (plugin) return plugin.stop();
} catch (err) {
@ -217,7 +226,7 @@ class PluginManager extends Module {
}
startPlugin(name) {
const plugin = this.getPluginByName(name);
const plugin = name instanceof Plugin ? name : this.getPluginByName(name);
try {
if (plugin) return plugin.start();
} catch (err) {
@ -259,4 +268,4 @@ async function pluginManager(pluginName) {
if (window.bdTests) window.bdTests.pluginManager = pluginManager;
else window.bdTests = { pluginManager };
module.exports = { PluginManager: _instance }
module.exports = { PluginManager: _instance, Plugin }

View File

@ -1,6 +1,6 @@
export { Global } from './core/global';
export { Logger, Utils, FileUtils } from './core/utils';
export { PluginManager } from './core/pluginmanager';
export { PluginManager, Plugin } from './core/pluginmanager';
export { Pluging } from './core/plugin';
export { BDIpc } from './core/bdipc';
export { WebpackModules } from './core/webpackmodules';

View File

@ -1,8 +1,29 @@
<template src="./templates/PluginCard.html"></template>
<script>
/*Imports*/
import { Button, ButtonGroup, SettingSwitch } from '../generic';
import MiSettings from 'vue-material-design-icons/settings.vue';
import MiReload from 'vue-material-design-icons/refresh.vue';
import MiEdit from 'vue-material-design-icons/pencil.vue';
import MiDelete from 'vue-material-design-icons/delete.vue';
const components = { MiSettings, Button, ButtonGroup, SettingSwitch, MiReload, MiEdit, MiDelete };
/*Methods*/
function showSettings() {
this.settingsOpen = true;
}
const methods = { };
export default {
props: ['plugin', 'togglePlugin'],
name: "PluginCard"
props: ['plugin', 'togglePlugin', 'reloadPlugin', 'showSettings'],
components,
name: "PluginCard",
methods,
data() {
return {
'settingsOpen': false
}
}
}
</script>

View File

@ -1,12 +1,13 @@
<template src="./templates/PluginsView.html"></template>
<script>
const { PluginManager } = require('../../../'); //#1 require of 2018~ :3
const { PluginManager, Plugin } = require('../../../'); //#1 require of 2018~ :3
/*Imports*/
import { SettingsWrapper } from './';
import { Modal } from '../generic';
import PluginCard from './PluginCard.vue';
import Refresh from 'vue-material-design-icons/refresh.vue';
const components = { SettingsWrapper, PluginCard, Refresh };
const components = { SettingsWrapper, PluginCard, Refresh, Modal };
/*Variables*/
@ -29,20 +30,29 @@
function togglePlugin(plugin) {
if (plugin.enabled) {
this.pluginManager.stopPlugin(plugin.name);
this.pluginManager.stopPlugin(plugin);
} else {
this.pluginManager.startPlugin(plugin.name);
this.pluginManager.startPlugin(plugin);
}
}
const methods = { showLocal, showOnline, refreshLocalPlugins, togglePlugin };
function reloadPlugin(plugin) {
this.pluginManager.reloadPlugin(plugin.name);
}
function showSettings(plugin) {
this.settingsOpen = plugin;
}
const methods = { showLocal, showOnline, refreshLocalPlugins, togglePlugin, reloadPlugin, showSettings };
export default {
components,
data() {
return {
local: true,
pluginManager: PluginManager
pluginManager: PluginManager,
settingsOpen: null
}
},
computed: {
@ -50,9 +60,6 @@
return this.pluginManager.plugins;
}
},
methods,
created: function () {
this.refreshLocalPlugins();
}
methods
}
</script>

View File

@ -7,11 +7,28 @@
<div class="bd-switch" :class="{'bd-checked': plugin.enabled}" />
</label>
</div>
<div class="bd-plugin-body">
<div class="bd-plugin-description">{{plugin.description}}</div>
<div class="bd-plugin-footer">
<div class="bd-plugin-extra">v{{plugin.version}} by {{plugin.authors.join(', ').replace(/,(?!.*,)/gmi, ' and')}}</div>
<div class="bd-controls"></div>
<div class="bd-controls">
<ButtonGroup>
<Button :onClick="() => showSettings(plugin)">
<MiSettings/>
</Button>
<Button :onClick="() => reloadPlugin(plugin)">
<MiReload/>
</Button>
<Button>
<MiEdit/>
</Button>
<Button type="err">
<MiDelete/>
</Button>
</ButtonGroup>
</div>
</div>
</div>
</div>

View File

@ -1,6 +1,7 @@
<SettingsWrapper headertext="Plugins">
<div class="bd-flex bd-flex-col bd-pluginsView">
<div class="bd-flex">
<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="refreshLocalPlugins">
@ -15,10 +16,34 @@
</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"/>
<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">
<Modal :headerText="settingsOpen.name + ' Settings'" :close="() => { settingsOpen = null }">
<div slot="body" v-for="setting in settingsOpen.pluginConfig" class="bd-plugin-settings-body">
<div class="bd-form-item" v-if="setting.type === 'bool'">
<div class="bd-setting-switch">
<div class="bd-title">
<h3>{{setting.text}}</h3>
<label class="bd-switch-wrapper">
<input type="checkbox" class="bd-switch-checkbox"/>
<div class="bd-switch" :class="{'bd-checked': setting.value}"/>
</label>
</div>
<div class="bd-hint">{{setting.hint}}</div>
</div>
<div class="bd-form-divider"></div>
</div>
<div v-else-if="setting.type === 'text'">
</div>
</div>
</Modal>
</div>
</SettingsWrapper>

View File

@ -0,0 +1,11 @@
<template>
<div class="bd-button" :class="[{'bd-disabled': disabled}, {'bd-err': type === 'err'}]" @click="!disabled && !loading ? onClick() : null">
<div v-if="loading" class="bd-spinner-7"></div>
<slot v-else />
</div>
</template>
<script>
export default {
props: ['loading', 'disabled', 'type', 'onClick', 'type']
}
</script>

View File

@ -0,0 +1,9 @@
<template>
<div class="bd-button-group">
<slot/>
</div>
</template>
<script>
export default {
}
</script>

View File

@ -0,0 +1,24 @@
<template>
<div class="bd-modal">
<div class="bd-modal-inner">
<div class="bd-modal-header">
<span>{{headerText}}</span>
<div class="bd-modal-x" @click="close">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 12 12"><g fill="none" fill-rule="evenodd"><path d="M0 0h12v12H0"></path><path class="fill" fill="#dcddde" d="M9.5 3.205L8.795 2.5 6 5.295 3.205 2.5l-.705.705L5.295 6 2.5 8.795l.705.705L6 6.705 8.795 9.5l.705-.705L6.705 6"></path></g></svg>
</div>
</div>
<div class="bd-modal-body">
<slot name="body"></slot>
</div>
<div class="bd-modal-footer">
<slot name="footer"></slot>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['headerText', 'close']
}
</script>

View File

@ -4,7 +4,7 @@
<h3>{{setting.title || setting.text}}</h3>
<label class="bd-switch-wrapper" @click="!disabled ? onClick(setting) : null">
<input type="checkbox" class="bd-switch-checkbox" />
<div class="bd-switch" :class="{'bd-checked': (setting.checked || setting.enabled)}" />
<div class="bd-switch" :class="{'bd-checked': (setting.checked || setting.enabled || setting.value)}" />
</label>
</div>
<div class="bd-hint">{{setting.hint}}</div>

View File

@ -1,3 +1,6 @@
export { default as ScrollerWrap } from './ScrollerWrap.vue';
export { default as SettingSwitch } from './SettingSwitch.vue';
export { default as FormButton } from './FormButton.vue';
export { default as ButtonGroup } from './ButtonGroup.vue';
export { default as Button } from './Button.vue';
export { default as Modal } from './Modal.vue';

View File

@ -2,7 +2,7 @@
<SidebarView :contentVisible="this.activeIndex >= 0" :animating="this.animating">
<Sidebar slot="sidebar">
<div class="bd-settings-x" @click="close">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 12 12"><g fill="none" fill-rule="evenodd"><path d="M0 0h12v12H0"></path><path class="fill" fill="#dcddde" d="M9.5 3.205L8.795 2.5 6 5.295 3.205 2.5l-.705.705L5.295 6 2.5 8.795l.705.705L6 6.705 8.795 9.5l.705-.705L6.705 6"></path></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="17" height="17" viewBox="0 0 12 12"><g fill="none" fill-rule="evenodd"><path d="M0 0h12v12H0"></path><path class="fill" fill="#dcddde" d="M9.5 3.205L8.795 2.5 6 5.295 3.205 2.5l-.705.705L5.295 6 2.5 8.795l.705.705L6 6.705 8.795 9.5l.705-.705L6.705 6"></path></g></svg>
<span>ESC</span>
</div>
<SidebarItem v-for="item in sidebarItems" :item="item" :key="item.id" :onClick="itemOnClick" />
@ -11,19 +11,19 @@
</div>
</Sidebar>
<ContentColumn slot="content">
<div :class="{active: activeContent('core'), animating: animatingContent('core')}">
<div v-if="activeContent('core') || animatingContent('core')" :class="{active: activeContent('core'), animating: animatingContent('core')}">
<CoreSettings :settings="coreSettings" :enableSetting="enableSetting" :disableSetting="disableSetting"/>
</div>
<div :class="{active: activeContent('ui'), animating: animatingContent('ui')}">
<div v-if="activeContent('ui') || animatingContent('ui')" :class="{active: activeContent('ui'), animating: animatingContent('ui')}">
<UISettings />
</div>
<div :class="{active: activeContent('css'), animating: animatingContent('css')}">
<div v-if="activeContent('css') || animatingContent('css')" :class="{active: activeContent('css'), animating: animatingContent('css')}">
<CssEditorView />
</div>
<div :class="{active: activeContent('emotes'), animating: animatingContent('emotes')}">
<div v-if="activeContent('emotes') || animatingContent('emotes')" :class="{active: activeContent('emotes'), animating: animatingContent('emotes')}">
<EmoteSettings />
</div>
<div :class="{active: activeContent('plugins'), animating: animatingContent('plugins')}">
<div v-if="activeContent('plugins') || animatingContent('plugins')" :class="{active: activeContent('plugins'), animating: animatingContent('plugins')}">
<PluginsView />
</div>
</ContentColumn>

View File

@ -2,12 +2,11 @@
display: flex;
flex-direction: column;
flex-grow: 1;
background: #2f3136;
border: 1px solid #202225;
background: transparent;
border-bottom: 1px solid rgba(114, 118, 126, 0.3);
padding: 10px 5px;
min-height: 150px;
color: #b9bbbe;
border-radius: 8px;
margin-top: 10px;
.bd-plugin-header {
@ -30,7 +29,6 @@
color: #8a8c90;
font-size: 12px;
font-weight: 600;
background: rgba(0,0,0,.05);
padding: 5px;
border-radius: 8px;
margin-top: 5px;
@ -38,7 +36,6 @@
.bd-plugin-footer {
display: flex;
justify-content: flex-end;
flex-grow: 1;
align-items: flex-end;
@ -46,6 +43,17 @@
color: rgba(255, 255, 255, 0.15);
font-size: 10px;
font-weight: 700;
flex-grow: 1;
}
.bd-controls {
.bd-button-group {
.bd-button {
fill: #FFF;
width: 30px;
height: 30px;
}
}
}
}
}
@ -91,6 +99,71 @@
transition: all .15s ease;
box-shadow: 0 3px 1px 0 rgba(0,0,0,.05), 0 2px 2px 0 rgba(0,0,0,.1), 0 3px 3px 0 rgba(0,0,0,.05);
}
&.bd-checked {
background: $colbdblue;
&::before {
transform: translateX(20px);
}
}
}
}
}
.bd-plugin-settings-body {
.bd-setting-switch {
.bd-switch-wrapper {
flex: 0 0 auto;
user-select: none;
position: relative;
width: 40px;
height: 20px;
display: block;
input {
position: absolute;
opacity: 0;
cursor: pointer;
width: 100%;
height: 100%;
z-index: 1;
}
.bd-switch {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: #72767d;
border-radius: 14px;
transition: background .15s ease-in-out,box-shadow .15s ease-in-out,border .15s ease-in-out;
&:before {
content: "";
display: block;
width: 14px;
height: 14px;
position: absolute;
top: 3px;
left: 3px;
bottom: 3px;
background: #f6f6f7;
border-radius: 10px;
transition: all .15s ease;
box-shadow: 0 3px 1px 0 rgba(0,0,0,.05), 0 2px 2px 0 rgba(0,0,0,.1), 0 3px 3px 0 rgba(0,0,0,.05);
}
&.bd-checked {
background: $colbdblue;
&::before {
transform: translateX(20px);
}
}
}
}
}
}

View File

@ -1,4 +1,4 @@
.bd-pluginsView {
/*.bd-pluginsView {
.bd-button {
text-align: center;
background: transparent;
@ -45,4 +45,11 @@
height: 200px;
}
}
}
*/
.bd-pluginsView {
}

View File

@ -1,24 +1,48 @@
.bd-button {
display: flex;
align-items: center;
justify-content: center;
.bd-settings-button {
position: absolute;
z-index: 1;
top: 22px;
width: 70px;
height: 48px;
left: 0;
background: #202225;
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 2px 0 rgba(0, 0, 0, 0.06);
opacity: 1;
.bd-settings-button-btn {
background-image: $logoSmallBw;
background-size: 50% 50%;
background-repeat: no-repeat;
background-position: center;
width: 100%;
height: 100%;
z-index: 3000;
cursor: pointer;
color: #FFF;
text-align: center;
user-select: none;
font-weight: 500;
background: $colbdblue;
filter: grayscale(100%);
opacity: 0.5;
transition: all 0.4s ease-in-out;
&:not(.bd-disabled):hover {
background: darken($colbdblue, 5%);
&:hover {
filter: none;
opacity: 1;
}
}
&.bd-disabled {
filter: grayscale(90%);
cursor: not-allowed;
}
&.active {
background: transparent;
opacity: 1;
box-shadow: none;
z-index: 90000;
.bd-spinner-7 {
opacity: .3;
.bd-settings-button-btn {
background-image: $logoBigBw;
filter: none;
opacity: 1;
width: 130px;
height: 80px;
background-size: 100% 100%;
margin-left: 20px;
cursor: default;
}
}
}

View File

@ -4,3 +4,4 @@
@import './buttons.scss';
@import './forms.scss';
@import './material-buttons.scss';
@import './modals.scss';

View File

@ -0,0 +1,77 @@
.bd-backdrop {
position: absolute;
right: 0px;
left: 0px;
top: 0px;
bottom: 0px;
background: #000;
opacity: .85;
padding: 20px;
}
.bd-modal {
position: fixed;
align-content: space-around;
display: flex;
border-radius: 8px;
width: 100%;
height: 100%;
left: 0;
top: 0;
user-select: none;
padding: 60px;
transform: scale(1) translateZ(0px);
-webkit-box-direction: normal;
-webkit-box-orient: vertical;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
align-items: center;
box-sizing: border-box;
pointer-events: none;
}
.bd-modal .bd-modal-inner {
background: #36393e;
contain: layout;
flex-direction: column;
pointer-events: auto;
-webkit-box-direction: normal;
-webkit-box-orient: vertical;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
flex-grow: 1;
padding: 15px;
border-radius: 8px;
}
.bd-modal {
.bd-modal-header {
display: flex;
span {
color: #FFF;
font-weight: 700;
margin-bottom: 15px;
padding-bottom: 15px;
flex-grow: 1;
}
.bd-modal-x {
display: flex;
width: 20px;
height: 20px;
border: 1px solid transparent;
border-radius: 3px;
cursor: pointer;
align-content: center;
justify-content: center;
align-items: center;
&:hover {
background: #2d2f34;
}
}
}
}

View File

@ -6,7 +6,9 @@
"description": "Example Plugin 2 Description"
},
"main": "index.js",
"defaultConfig": {
"defaultConfig": [
{
"foo": "bar"
}
]
}

View File

@ -6,7 +6,20 @@
"description": "Example Plugin Description"
},
"main": "index.js",
"defaultConfig": {
"foo": "bar"
"defaultConfig": [
{
"id": "test-setting-1",
"type": "bool",
"value": false,
"text": "Bool Test Setting",
"hint": "Bool Test Setting Hint"
},
{
"id": "test-setting-2",
"type": "text",
"value": "defaultValue",
"text": "Text Test Setting",
"hint": "Text Test Setting Hint"
}
]
}