Merge pull request #184 from samuelthomas2774/refactor
Refactor and comment
This commit is contained in:
commit
a4ceb8bd2c
|
@ -2,6 +2,7 @@ root = true
|
||||||
|
|
||||||
[*]
|
[*]
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
|
|
@ -1,27 +1,11 @@
|
||||||
.idea/*
|
# Generated files
|
||||||
*.name
|
|
||||||
devjs/.idea/devjs.iml
|
|
||||||
*.bak
|
|
||||||
*.bak.*
|
|
||||||
*.xpi
|
|
||||||
Firefox/data/js/jquery-2.1.4.min.js
|
|
||||||
*.dev.*
|
|
||||||
/nbproject/private/
|
|
||||||
node_modules
|
node_modules
|
||||||
.sass-cache
|
dist
|
||||||
/*.jiiks
|
etc
|
||||||
Installers/dotNet/bin/
|
|
||||||
Installers/dotNet/packages/
|
|
||||||
Installers/dotNet/dlls/
|
|
||||||
v2/dist/vendor/
|
|
||||||
v2/lib/static.js
|
|
||||||
**/*.suo
|
|
||||||
Installers/**/*/bin
|
|
||||||
Installers/**/*/obj
|
|
||||||
Installers/**/*/packages
|
|
||||||
.vs
|
|
||||||
dist/
|
|
||||||
user.config.json
|
|
||||||
tests/data
|
|
||||||
/tests/themes/SimplerFlat
|
|
||||||
release
|
release
|
||||||
|
|
||||||
|
tests/log.txt
|
||||||
|
|
||||||
|
# User data
|
||||||
|
tests/data
|
||||||
|
user.config.json
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
# BetterDiscordApp [![Travis][build-badge]][build]
|
# BetterDiscordApp [![Travis][build-badge]][build]
|
||||||
|
|
||||||
[build-badge]: https://img.shields.io/travis/JsSucks/BetterDiscordApp/master.svg
|
[build-badge]: https://img.shields.io/travis/JsSucks/BetterDiscordApp/master.svg
|
||||||
[build]: https://travis-ci.org/JsSucks/BetterDiscordApp
|
[build]: https://travis-ci.org/JsSucks/BetterDiscordApp
|
|
@ -5,21 +5,19 @@
|
||||||
"version": "2.0.0b",
|
"version": "2.0.0b",
|
||||||
"homepage": "https://betterdiscord.net",
|
"homepage": "https://betterdiscord.net",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "index.js",
|
"main": "dist/betterdiscord.client.js",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
"Jiiks",
|
"Jiiks",
|
||||||
"Pohky"
|
"Pohky"
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Jiiks/BetterDiscordApp.git"
|
"url": "https://github.com/JsSucks/BetterDiscordApp.git"
|
||||||
},
|
},
|
||||||
"private": false,
|
"private": false,
|
||||||
"devDependencies": {
|
|
||||||
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --progress --colors",
|
"build": "webpack --progress --colors",
|
||||||
"watch": "webpack --progress --colors --watch"
|
"watch": "webpack --progress --colors --watch",
|
||||||
|
"release": "webpack --progress --colors --config=webpack.production.config.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,10 @@
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
favourite: false
|
favourite: false
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
props: ['src', 'name'],
|
props: ['src', 'name'],
|
||||||
methods: {
|
methods: {},
|
||||||
},
|
|
||||||
beforeMount() {
|
beforeMount() {
|
||||||
// Check favourite state
|
// Check favourite state
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,26 +7,34 @@
|
||||||
* This source code is licensed under the MIT license found in the
|
* This source code is licensed under the MIT license found in the
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
import { FileUtils, ClientLogger as Logger } from 'common';
|
|
||||||
import { Events, Globals, WebpackModules, ReactComponents, MonkeyPatch } from 'modules';
|
import { Events, Globals, WebpackModules, ReactComponents, MonkeyPatch } from 'modules';
|
||||||
import { DOM, VueInjector, Reflection } from 'ui';
|
import { DOM, VueInjector, Reflection } from 'ui';
|
||||||
|
import { FileUtils, ClientLogger as Logger } from 'common';
|
||||||
|
import path from 'path';
|
||||||
import EmoteComponent from './EmoteComponent.vue';
|
import EmoteComponent from './EmoteComponent.vue';
|
||||||
|
|
||||||
let emotes = null;
|
let emotes = null;
|
||||||
const emotesEnabled = true;
|
const emotesEnabled = true;
|
||||||
|
|
||||||
export default class {
|
export default class {
|
||||||
|
|
||||||
static get searchCache() {
|
static get searchCache() {
|
||||||
return this._searchCache || (this._searchCache = {});
|
return this._searchCache || (this._searchCache = {});
|
||||||
}
|
}
|
||||||
|
|
||||||
static get emoteDb() {
|
static get emoteDb() {
|
||||||
return emotes;
|
return emotes;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get React() {
|
static get React() {
|
||||||
return WebpackModules.getModuleByName('React');
|
return WebpackModules.getModuleByName('React');
|
||||||
}
|
}
|
||||||
|
|
||||||
static get ReactDOM() {
|
static get ReactDOM() {
|
||||||
return WebpackModules.getModuleByName('ReactDOM');
|
return WebpackModules.getModuleByName('ReactDOM');
|
||||||
}
|
}
|
||||||
|
|
||||||
static processMarkup(markup) {
|
static processMarkup(markup) {
|
||||||
if (!emotesEnabled) return markup; // TODO Get it from setttings
|
if (!emotesEnabled) return markup; // TODO Get it from setttings
|
||||||
const newMarkup = [];
|
const newMarkup = [];
|
||||||
|
@ -92,13 +100,20 @@ export default class {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async observe() {
|
static async observe() {
|
||||||
const dataPath = Globals.getObject('paths').find(path => path.id === 'data').path;
|
const dataPath = Globals.getPath('data');
|
||||||
|
try {
|
||||||
|
emotes = await FileUtils.readJsonFromFile(path.join(dataPath, 'emotes.json'));
|
||||||
|
} catch (err) {
|
||||||
|
Logger.err('EmoteModule', [`Failed to load emote data. Make sure you've downloaded the emote data and placed it in ${dataPath}:`, err]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
emotes = await FileUtils.readJsonFromFile(dataPath + '/emotes.json');
|
|
||||||
const Message = await ReactComponents.getComponent('Message');
|
const Message = await ReactComponents.getComponent('Message');
|
||||||
this.unpatchRender = MonkeyPatch('BD:EmoteModule', Message.component.prototype).after('render', (component, args, retVal) => {
|
this.unpatchRender = MonkeyPatch('BD:EmoteModule', Message.component.prototype).after('render', (component, args, retVal) => {
|
||||||
try {
|
try {
|
||||||
const markup = this.findByProp(retVal, 'className', 'markup'); // First child has all the actual text content, second is the edited timestamp
|
// First child has all the actual text content, second is the edited timestamp
|
||||||
|
const markup = this.findByProp(retVal, 'className', 'markup');
|
||||||
if (!markup) return;
|
if (!markup) return;
|
||||||
markup.children[0] = this.processMarkup(markup.children[0]);
|
markup.children[0] = this.processMarkup(markup.children[0]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -131,12 +146,11 @@ export default class {
|
||||||
}
|
}
|
||||||
const { bdemoteName, bdemoteSrc } = root.dataset;
|
const { bdemoteName, bdemoteSrc } = root.dataset;
|
||||||
if (!bdemoteName || !bdemoteSrc) return;
|
if (!bdemoteName || !bdemoteSrc) return;
|
||||||
VueInjector.inject(
|
VueInjector.inject(root, {
|
||||||
root,
|
components: { EmoteComponent },
|
||||||
DOM.createElement('span'),
|
data: { src: bdemoteSrc, name: bdemoteName },
|
||||||
{ EmoteComponent },
|
template: '<EmoteComponent :src="src" :name="name" />'
|
||||||
`<EmoteComponent src="${bdemoteSrc}" name="${bdemoteName}"/>`
|
}, DOM.createElement('span'));
|
||||||
);
|
|
||||||
root.classList.add('bd-is-emote');
|
root.classList.add('bd-is-emote');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,4 +193,5 @@ export default class {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"__user": "Jiiks#5000",
|
||||||
|
"id": "81388395867156480",
|
||||||
|
"developer": true,
|
||||||
|
"webdev": true,
|
||||||
|
"contributor": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__user": "Pohky#0156",
|
||||||
|
"id": "98003542823944192",
|
||||||
|
"developer": true,
|
||||||
|
"webdev": false,
|
||||||
|
"contributor": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__user": "Hammock#3110",
|
||||||
|
"id": "138850472541814784",
|
||||||
|
"developer": false,
|
||||||
|
"webdev": true,
|
||||||
|
"contributor": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__user": "Zerebos#7790",
|
||||||
|
"id": "249746236008169473",
|
||||||
|
"developer": true,
|
||||||
|
"webdev": false,
|
||||||
|
"contributor": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__user": "Pierce#1337",
|
||||||
|
"id": "125367412370440192",
|
||||||
|
"developer": true,
|
||||||
|
"webdev": false,
|
||||||
|
"contributor": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__user": "Samuel Elliott#2764",
|
||||||
|
"id": "284056145272766465",
|
||||||
|
"developer": true,
|
||||||
|
"webdev": false,
|
||||||
|
"contributor": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__user": "Lilian Tedone#6223",
|
||||||
|
"id": "184021060562321419",
|
||||||
|
"developer": false,
|
||||||
|
"webdev": false,
|
||||||
|
"contributor": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__user": "samfun123#8972",
|
||||||
|
"id": "76052829285916672",
|
||||||
|
"developer": false,
|
||||||
|
"webdev": false,
|
||||||
|
"contributor": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__user": "samogot#4379",
|
||||||
|
"id": "171005991272316937",
|
||||||
|
"developer": false,
|
||||||
|
"webdev": false,
|
||||||
|
"contributor": true
|
||||||
|
}
|
||||||
|
]
|
|
@ -51,6 +51,12 @@
|
||||||
"hint": "Adds some of BetterDiscord's internal modules to `global._bd`.",
|
"hint": "Adds some of BetterDiscord's internal modules to `global._bd`.",
|
||||||
"value": false
|
"value": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "debugger-keybind",
|
||||||
|
"type": "keybind",
|
||||||
|
"text": "Debugger keybind",
|
||||||
|
"hint": "When this keybind is activated the developer tools will be opened and Discord will be paused."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "ignore-content-manager-errors",
|
"id": "ignore-content-manager-errors",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
|
@ -91,7 +97,6 @@
|
||||||
{
|
{
|
||||||
"id": "css",
|
"id": "css",
|
||||||
"text": "CSS Editor",
|
"text": "CSS Editor",
|
||||||
"hidden": true,
|
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"category": "default",
|
"category": "default",
|
||||||
|
|
|
@ -10,39 +10,32 @@
|
||||||
|
|
||||||
import { DOM, BdUI, Modals, Reflection } from 'ui';
|
import { DOM, BdUI, Modals, Reflection } from 'ui';
|
||||||
import BdCss from './styles/index.scss';
|
import BdCss from './styles/index.scss';
|
||||||
import { Patcher, MonkeyPatch, Vendor, Events, CssEditor, Globals, ExtModuleManager, PluginManager, ThemeManager, ModuleManager, WebpackModules, Settings, Database, ReactComponents, ReactAutoPatcher, DiscordApi } from 'modules';
|
import { Events, CssEditor, Globals, Settings, Database, Updater, ModuleManager, PluginManager, ThemeManager, ExtModuleManager, Vendor, WebpackModules, Patcher, MonkeyPatch, ReactComponents, ReactAutoPatcher, DiscordApi } from 'modules';
|
||||||
import { ClientLogger as Logger, ClientIPC, Utils } from 'common';
|
import { ClientLogger as Logger, ClientIPC, Utils } from 'common';
|
||||||
import { EmoteModule } from 'builtin';
|
import { EmoteModule } from 'builtin';
|
||||||
|
import electron from 'electron';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
const tests = typeof PRODUCTION === 'undefined';
|
||||||
const ignoreExternal = false;
|
const ignoreExternal = false;
|
||||||
const DEV = true;
|
|
||||||
|
|
||||||
class BetterDiscord {
|
class BetterDiscord {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
Logger.file = tests ? path.resolve(__dirname, '..', '..', 'tests', 'log.txt') : path.join(__dirname, 'log.txt');
|
||||||
|
Logger.log('main', 'BetterDiscord starting');
|
||||||
|
|
||||||
this._bd = {
|
this._bd = {
|
||||||
DOM,
|
DOM, BdUI, Modals, Reflection,
|
||||||
BdUI,
|
|
||||||
Modals,
|
Events, CssEditor, Globals, Settings, Database, Updater,
|
||||||
Reflection,
|
ModuleManager, PluginManager, ThemeManager, ExtModuleManager,
|
||||||
Patcher,
|
|
||||||
MonkeyPatch,
|
|
||||||
Vendor,
|
Vendor,
|
||||||
Events,
|
|
||||||
CssEditor,
|
WebpackModules, Patcher, MonkeyPatch, ReactComponents, DiscordApi,
|
||||||
Globals,
|
EmoteModule,
|
||||||
ExtModuleManager,
|
|
||||||
PluginManager,
|
Logger, ClientIPC, Utils
|
||||||
ThemeManager,
|
|
||||||
ModuleManager,
|
|
||||||
WebpackModules,
|
|
||||||
Settings,
|
|
||||||
Database,
|
|
||||||
ReactComponents,
|
|
||||||
DiscordApi,
|
|
||||||
Logger,
|
|
||||||
ClientIPC,
|
|
||||||
Utils,
|
|
||||||
EmoteModule
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const developermode = Settings.getSetting('core', 'advanced', 'developer-mode');
|
const developermode = Settings.getSetting('core', 'advanced', 'developer-mode');
|
||||||
|
@ -52,6 +45,14 @@ class BetterDiscord {
|
||||||
else if (window._bd) delete window._bd;
|
else if (window._bd) delete window._bd;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const debuggerkeybind = Settings.getSetting('core', 'advanced', 'debugger-keybind');
|
||||||
|
debuggerkeybind.on('keybind-activated', () => {
|
||||||
|
const currentWindow = electron.remote.getCurrentWindow();
|
||||||
|
if (currentWindow.isDevToolsOpened()) return eval('debugger;');
|
||||||
|
currentWindow.openDevTools();
|
||||||
|
setTimeout(() => eval('debugger;'), 1000);
|
||||||
|
});
|
||||||
|
|
||||||
DOM.injectStyle(BdCss, 'bdmain');
|
DOM.injectStyle(BdCss, 'bdmain');
|
||||||
this.globalReady = this.globalReady.bind(this);
|
this.globalReady = this.globalReady.bind(this);
|
||||||
Events.on('global-ready', this.globalReady);
|
Events.on('global-ready', this.globalReady);
|
||||||
|
@ -63,14 +64,16 @@ class BetterDiscord {
|
||||||
await Database.init();
|
await Database.init();
|
||||||
await Settings.loadSettings();
|
await Settings.loadSettings();
|
||||||
await ModuleManager.initModules();
|
await ModuleManager.initModules();
|
||||||
Modals.showContentManagerErrors();
|
|
||||||
if (!ignoreExternal) {
|
if (!ignoreExternal) {
|
||||||
await ExtModuleManager.loadAllModules(true);
|
await ExtModuleManager.loadAllModules(true);
|
||||||
await PluginManager.loadAllPlugins(true);
|
await PluginManager.loadAllPlugins(true);
|
||||||
await ThemeManager.loadAllThemes(true);
|
await ThemeManager.loadAllThemes(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Settings.get('core', 'advanced', 'ignore-content-manager-errors'))
|
if (!Settings.get('core', 'advanced', 'ignore-content-manager-errors'))
|
||||||
Modals.showContentManagerErrors();
|
Modals.showContentManagerErrors();
|
||||||
|
|
||||||
Events.emit('ready');
|
Events.emit('ready');
|
||||||
Events.emit('discord-ready');
|
Events.emit('discord-ready');
|
||||||
EmoteModule.observe();
|
EmoteModule.observe();
|
||||||
|
@ -90,7 +93,7 @@ class BetterDiscord {
|
||||||
if (window.BetterDiscord) {
|
if (window.BetterDiscord) {
|
||||||
Logger.log('main', 'Attempting to inject again?');
|
Logger.log('main', 'Attempting to inject again?');
|
||||||
} else {
|
} else {
|
||||||
let instance = null;
|
let instance;
|
||||||
Events.on('autopatcher', () => instance = new BetterDiscord());
|
Events.on('autopatcher', () => instance = new BetterDiscord());
|
||||||
ReactAutoPatcher.autoPatch().then(() => Events.emit('autopatcher'));
|
ReactAutoPatcher.autoPatch().then(() => Events.emit('autopatcher'));
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@ export default class Content {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a settings modal for this content.
|
* Opens a settings modal for this content.
|
||||||
|
* @return {Modal}
|
||||||
*/
|
*/
|
||||||
showSettingsModal() {
|
showSettingsModal() {
|
||||||
return Modals.contentSettings(this);
|
return Modals.contentSettings(this);
|
||||||
|
@ -73,20 +74,13 @@ export default class Content {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the content's current configuration.
|
* Saves the content's current configuration.
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
async saveConfiguration() {
|
async saveConfiguration() {
|
||||||
try {
|
try {
|
||||||
/*
|
Database.insertOrUpdate({ type: `${this.type}-config`, id: this.id }, {
|
||||||
await FileUtils.writeFile(`${this.contentPath}/user.config.json`, JSON.stringify({
|
type: `${this.type}-config`,
|
||||||
enabled: this.enabled,
|
|
||||||
config: this.settings.strip().settings,
|
|
||||||
data: this.data
|
|
||||||
}));
|
|
||||||
*/
|
|
||||||
Database.insertOrUpdate({ type: 'contentconfig', $or: [{ id: this.id }, { name: this.name }] }, {
|
|
||||||
type: 'contentconfig',
|
|
||||||
id: this.id,
|
id: this.id,
|
||||||
name: this.name,
|
|
||||||
enabled: this.enabled,
|
enabled: this.enabled,
|
||||||
config: this.settings.strip().settings,
|
config: this.settings.strip().settings,
|
||||||
data: this.data
|
data: this.data
|
||||||
|
@ -143,15 +137,6 @@ export default class Content {
|
||||||
return this.events.on(...args);
|
return this.events.on(...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes an event listener.
|
|
||||||
* @param {String} event The event to remove the listener from
|
|
||||||
* @param {Function} callback The bound callback (optional)
|
|
||||||
*/
|
|
||||||
off(...args) {
|
|
||||||
return this.events.removeListener(...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an event listener that removes itself when called, therefore only being called once.
|
* Adds an event listener that removes itself when called, therefore only being called once.
|
||||||
* @param {String} event The event to add the listener to
|
* @param {String} event The event to add the listener to
|
||||||
|
@ -162,6 +147,15 @@ export default class Content {
|
||||||
return this.events.once(...args);
|
return this.events.once(...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an event listener.
|
||||||
|
* @param {String} event The event to remove the listener from
|
||||||
|
* @param {Function} callback The bound callback (optional)
|
||||||
|
*/
|
||||||
|
off(...args) {
|
||||||
|
return this.events.removeListener(...args);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emits an event.
|
* Emits an event.
|
||||||
* @param {String} event The event to emit
|
* @param {String} event The event to emit
|
||||||
|
|
|
@ -24,31 +24,52 @@ import Combokeys from 'combokeys';
|
||||||
export default class {
|
export default class {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Any errors that happened
|
* Any errors that happened.
|
||||||
* returns {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
static get errors() {
|
static get errors() {
|
||||||
return this._errors || (this._errors = []);
|
return this._errors || (this._errors = []);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Locallly stored content
|
* Locally stored content.
|
||||||
* returns {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
static get localContent() {
|
static get localContent() {
|
||||||
return this._localContent ? this._localContent : (this._localContent = []);
|
return this._localContent ? this._localContent : (this._localContent = []);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local path for content
|
* The type of content this content manager manages.
|
||||||
* returns {String}
|
*/
|
||||||
|
static get contentType() {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of this content manager.
|
||||||
|
*/
|
||||||
|
static get moduleName() {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path used to store this content manager's content.
|
||||||
|
*/
|
||||||
|
static get pathId() {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local path for content.
|
||||||
|
* @return {String}
|
||||||
*/
|
*/
|
||||||
static get contentPath() {
|
static get contentPath() {
|
||||||
return Globals.getPath(this.pathId);
|
return Globals.getPath(this.pathId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load all locally stored content
|
* Load all locally stored content.
|
||||||
* @param {bool} suppressErrors Suppress any errors that occur during loading of content
|
* @param {bool} suppressErrors Suppress any errors that occur during loading of content
|
||||||
*/
|
*/
|
||||||
static async loadAllContent(suppressErrors = false) {
|
static async loadAllContent(suppressErrors = false) {
|
||||||
|
@ -83,8 +104,6 @@ export default class {
|
||||||
});
|
});
|
||||||
this._errors = [];
|
this._errors = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.localContent;
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
@ -102,7 +121,7 @@ export default class {
|
||||||
const directories = await FileUtils.listDirectory(this.contentPath);
|
const directories = await FileUtils.listDirectory(this.contentPath);
|
||||||
|
|
||||||
for (let dir of directories) {
|
for (let dir of directories) {
|
||||||
// If content is already loaded this should resolve.
|
// If content is already loaded this should resolve
|
||||||
if (this.getContentByDirName(dir)) continue;
|
if (this.getContentByDirName(dir)) continue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -150,8 +169,6 @@ export default class {
|
||||||
});
|
});
|
||||||
this._errors = [];
|
this._errors = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.localContent;
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
@ -169,15 +186,12 @@ export default class {
|
||||||
|
|
||||||
await FileUtils.directoryExists(contentPath);
|
await FileUtils.directoryExists(contentPath);
|
||||||
|
|
||||||
if (!reload) {
|
if (!reload && this.getContentByPath(contentPath))
|
||||||
const loaded = this.localContent.find(content => content.contentPath === contentPath);
|
|
||||||
if (loaded) {
|
|
||||||
throw { 'message': `Attempted to load already loaded user content: ${path}` };
|
throw { 'message': `Attempted to load already loaded user content: ${path}` };
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const readConfig = await this.readConfig(contentPath);
|
const configPath = path.resolve(contentPath, 'config.json');
|
||||||
const mainPath = path.join(contentPath, readConfig.main);
|
const readConfig = await FileUtils.readJsonFromFile(configPath);
|
||||||
|
const mainPath = path.join(contentPath, readConfig.main || 'index.js');
|
||||||
|
|
||||||
const defaultConfig = new SettingsSet({
|
const defaultConfig = new SettingsSet({
|
||||||
settings: readConfig.defaultConfig,
|
settings: readConfig.defaultConfig,
|
||||||
|
@ -191,19 +205,16 @@ export default class {
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//const readUserConfig = await this.readUserConfig(contentPath);
|
const id = readConfig.info.id || readConfig.info.name.toLowerCase().replace(/[^a-zA-Z0-9-]/g, '-').replace(/--/g, '-');
|
||||||
const readUserConfig = await Database.find({ type: 'contentconfig', name: readConfig.info.name });
|
const readUserConfig = await Database.find({ type: `${this.contentType}-config`, id });
|
||||||
if (readUserConfig.length) {
|
if (readUserConfig.length) {
|
||||||
userConfig.enabled = readUserConfig[0].enabled || false;
|
userConfig.enabled = readUserConfig[0].enabled || false;
|
||||||
// await userConfig.config.merge({ settings: readUserConfig.config });
|
|
||||||
// userConfig.config.setSaved();
|
|
||||||
// userConfig.config = userConfig.config.clone({ settings: readUserConfig.config });
|
|
||||||
userConfig.config = readUserConfig[0].config;
|
userConfig.config = readUserConfig[0].config;
|
||||||
userConfig.data = readUserConfig[0].data || {};
|
userConfig.data = readUserConfig[0].data || {};
|
||||||
}
|
}
|
||||||
} 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*/
|
} catch (err) {
|
||||||
Logger.info(this.moduleName, `Failed reading config for ${this.contentType} ${readConfig.info.name} in ${dirName}`);
|
// 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
|
||||||
Logger.err(this.moduleName, err);
|
Logger.warn(this.moduleName, [`Failed reading config for ${this.contentType} ${readConfig.info.name} in ${dirName}`, err]);
|
||||||
}
|
}
|
||||||
|
|
||||||
userConfig.config = defaultConfig.clone({ settings: userConfig.config });
|
userConfig.config = defaultConfig.clone({ settings: userConfig.config });
|
||||||
|
@ -244,9 +255,10 @@ export default class {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unload content
|
* Unload content.
|
||||||
* @param {any} content Content to unload
|
* @param {Content|String} content Content to unload
|
||||||
* @param {bool} reload Whether to reload the content after
|
* @param {Boolean} reload Whether to reload the content after
|
||||||
|
* @return {Content}
|
||||||
*/
|
*/
|
||||||
static async unloadContent(content, reload) {
|
static async unloadContent(content, reload) {
|
||||||
content = this.findContent(content);
|
content = this.findContent(content);
|
||||||
|
@ -275,34 +287,18 @@ export default class {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reload content
|
* Reload content.
|
||||||
* @param {any} content Content to reload
|
* @param {Content|String} content Content to reload
|
||||||
|
* @return {Content}
|
||||||
*/
|
*/
|
||||||
static reloadContent(content) {
|
static reloadContent(content) {
|
||||||
return this.unloadContent(content, true);
|
return this.unloadContent(content, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Read content config file
|
|
||||||
* @param {any} configPath Config file path
|
|
||||||
*/
|
|
||||||
static async readConfig(configPath) {
|
|
||||||
configPath = path.resolve(configPath, 'config.json');
|
|
||||||
return FileUtils.readJsonFromFile(configPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read content user config file
|
|
||||||
* @param {any} configPath User config file path
|
|
||||||
*/
|
|
||||||
static async readUserConfig(configPath) {
|
|
||||||
configPath = path.resolve(configPath, 'user.config.json');
|
|
||||||
return FileUtils.readJsonFromFile(configPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the passed object is an instance of this content type.
|
* Checks if the passed object is an instance of this content type.
|
||||||
* @param {any} content Object to check
|
* @param {Any} content Object to check
|
||||||
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
static isThisContent(content) {
|
static isThisContent(content) {
|
||||||
return content instanceof Content;
|
return content instanceof Content;
|
||||||
|
@ -318,8 +314,9 @@ export default class {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wildcard content finder
|
* Wildcard content finder
|
||||||
* @param {any} wild Content ID / directory name / path / name
|
* @param {String} wild Content ID / directory name / path / name
|
||||||
* @param {bool} nonunique Allow searching attributes that may not be unique
|
* @param {Boolean} nonunique Allow searching attributes that may not be unique
|
||||||
|
* @return {Content}
|
||||||
*/
|
*/
|
||||||
static findContent(wild, nonunique) {
|
static findContent(wild, nonunique) {
|
||||||
if (this.isThisContent(wild)) return wild;
|
if (this.isThisContent(wild)) return wild;
|
||||||
|
@ -338,7 +335,8 @@ export default class {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait for content to load
|
* Wait for content to load
|
||||||
* @param {any} content_id
|
* @param {String} content_id
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
static waitForContent(content_id) {
|
static waitForContent(content_id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
|
@ -32,24 +32,24 @@ export default new class {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init css editor
|
* Init css editor.
|
||||||
*/
|
*/
|
||||||
init() {
|
init() {
|
||||||
ClientIPC.on('bd-get-scss', () => this.sendToEditor('set-scss', { scss: this.scss }));
|
ClientIPC.on('bd-get-scss', () => this.scss, true);
|
||||||
ClientIPC.on('bd-update-scss', (e, scss) => this.updateScss(scss));
|
ClientIPC.on('bd-update-scss', (e, scss) => this.updateScss(scss));
|
||||||
ClientIPC.on('bd-save-csseditor-bounds', (e, bounds) => this.saveEditorBounds(bounds));
|
ClientIPC.on('bd-save-csseditor-bounds', (e, bounds) => this.saveEditorBounds(bounds));
|
||||||
|
|
||||||
ClientIPC.on('bd-save-scss', async (e, scss) => {
|
ClientIPC.on('bd-save-scss', async (e, scss) => {
|
||||||
await this.updateScss(scss);
|
await this.updateScss(scss);
|
||||||
await this.save();
|
await this.save();
|
||||||
});
|
}, true);
|
||||||
|
|
||||||
this.liveupdate = Settings.getSetting('css', 'default', 'live-update');
|
this.liveupdate = Settings.getSetting('css', 'default', 'live-update');
|
||||||
this.liveupdate.on('setting-updated', event => {
|
this.liveupdate.on('setting-updated', event => {
|
||||||
this.sendToEditor('set-liveupdate', event.value);
|
this.sendToEditor('set-liveupdate', event.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
ClientIPC.on('bd-get-liveupdate', () => this.sendToEditor('set-liveupdate', this.liveupdate.value));
|
ClientIPC.on('bd-get-liveupdate', () => this.liveupdate.value, true);
|
||||||
ClientIPC.on('bd-set-liveupdate', (e, value) => this.liveupdate.value = value);
|
ClientIPC.on('bd-set-liveupdate', (e, value) => this.liveupdate.value = value);
|
||||||
|
|
||||||
this.watchfilessetting = Settings.getSetting('css', 'default', 'watch-files');
|
this.watchfilessetting = Settings.getSetting('css', 'default', 'watch-files');
|
||||||
|
@ -60,20 +60,20 @@ export default new class {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show css editor, flashes if already visible
|
* Show css editor, flashes if already visible.
|
||||||
*/
|
*/
|
||||||
async show() {
|
async show() {
|
||||||
await ClientIPC.send('openCssEditor', this.editor_bounds);
|
await ClientIPC.send('openCssEditor', this.editor_bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update css in client
|
* Update css in client.
|
||||||
* @param {String} scss scss to compile
|
* @param {String} scss SCSS to compile
|
||||||
* @param {bool} sendSource send to css editor instance
|
* @param {bool} sendSource Whether to send to css editor instance
|
||||||
*/
|
*/
|
||||||
async updateScss(scss, sendSource) {
|
async updateScss(scss, sendSource) {
|
||||||
if (sendSource)
|
if (sendSource)
|
||||||
this.sendToEditor('set-scss', { scss });
|
this.sendToEditor('set-scss', scss);
|
||||||
|
|
||||||
if (!scss) {
|
if (!scss) {
|
||||||
this._scss = this.css = '';
|
this._scss = this.css = '';
|
||||||
|
@ -97,24 +97,26 @@ export default new class {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save css to file
|
* Save css to file.
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
async save() {
|
save() {
|
||||||
Settings.saveSettings();
|
return Settings.saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save current editor bounds
|
* Save current editor bounds.
|
||||||
* @param {Rectangle} bounds editor bounds
|
* @param {Rectangle} bounds Editor bounds
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
saveEditorBounds(bounds) {
|
saveEditorBounds(bounds) {
|
||||||
this.editor_bounds = bounds;
|
this.editor_bounds = bounds;
|
||||||
Settings.saveSettings();
|
return Settings.saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send scss to core for compilation
|
* Send SCSS to core for compilation.
|
||||||
* @param {String} scss scss string
|
* @param {String} scss SCSS string
|
||||||
*/
|
*/
|
||||||
async compile(scss) {
|
async compile(scss) {
|
||||||
return await ClientIPC.send('bd-compileSass', {
|
return await ClientIPC.send('bd-compileSass', {
|
||||||
|
@ -124,7 +126,7 @@ export default new class {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recompile the current SCSS
|
* Recompile the current SCSS.
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
async recompile() {
|
async recompile() {
|
||||||
|
@ -132,16 +134,18 @@ export default new class {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send data to open editor
|
* Send data to open editor.
|
||||||
* @param {any} channel
|
* @param {String} channel
|
||||||
* @param {any} data
|
* @param {Any} data
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
async sendToEditor(channel, data) {
|
async sendToEditor(channel, data) {
|
||||||
return await ClientIPC.send('sendToCssEditor', { channel, data });
|
return ClientIPC.sendToCssEditor(channel, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens an SCSS file in a system editor
|
* Opens an SCSS file in a system editor.
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
async openSystemEditor() {
|
async openSystemEditor() {
|
||||||
try {
|
try {
|
||||||
|
@ -160,7 +164,8 @@ export default new class {
|
||||||
throw {message: 'Failed to open system editor.'};
|
throw {message: 'Failed to open system editor.'};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set current state
|
/**
|
||||||
|
* Set current state
|
||||||
* @param {String} scss Current uncompiled SCSS
|
* @param {String} scss Current uncompiled SCSS
|
||||||
* @param {String} css Current compiled CSS
|
* @param {String} css Current compiled CSS
|
||||||
* @param {String} files Files imported in the SCSS
|
* @param {String} files Files imported in the SCSS
|
||||||
|
@ -168,36 +173,35 @@ export default new class {
|
||||||
*/
|
*/
|
||||||
setState(scss, css, files, err) {
|
setState(scss, css, files, err) {
|
||||||
this._scss = scss;
|
this._scss = scss;
|
||||||
this.sendToEditor('set-scss', { scss });
|
this.sendToEditor('set-scss', scss);
|
||||||
this.css = css;
|
this.css = css;
|
||||||
this.files = files;
|
this.files = files;
|
||||||
this.error = err;
|
this.error = err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current uncompiled scss
|
* Current uncompiled scss.
|
||||||
*/
|
*/
|
||||||
get scss() {
|
get scss() {
|
||||||
return this._scss || '';
|
return this._scss || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set current scss
|
* Set current scss.
|
||||||
*/
|
*/
|
||||||
set scss(scss) {
|
set scss(scss) {
|
||||||
this.updateScss(scss, true);
|
this.updateScss(scss, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current compiled css
|
* Current compiled css.
|
||||||
*/
|
*/
|
||||||
get css() {
|
get css() {
|
||||||
return this._css || '';
|
return this._css || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject compiled css to head
|
* Inject compiled css to head.
|
||||||
* {DOM}
|
|
||||||
*/
|
*/
|
||||||
set css(css) {
|
set css(css) {
|
||||||
this._css = css;
|
this._css = css;
|
||||||
|
@ -205,15 +209,14 @@ export default new class {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current error
|
* Current error.
|
||||||
*/
|
*/
|
||||||
get error() {
|
get error() {
|
||||||
return this._error || undefined;
|
return this._error || undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set current error
|
* Set current error.
|
||||||
* {DOM}
|
|
||||||
*/
|
*/
|
||||||
set error(err) {
|
set error(err) {
|
||||||
this._error = err;
|
this._error = err;
|
||||||
|
@ -293,7 +296,7 @@ export default new class {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the system editor's file exists.
|
* Checks if the system editor's file exists.
|
||||||
* @return {Boolean}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
async fileExists() {
|
async fileExists() {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ClientIPC } from 'bdipc';
|
import { ClientIPC } from 'common';
|
||||||
|
|
||||||
export default class {
|
export default class {
|
||||||
|
|
||||||
|
@ -16,6 +16,12 @@ export default class {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts or updates data in the database.
|
||||||
|
* @param {Object} args The record to find
|
||||||
|
* @param {Object} data The new record
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
static async insertOrUpdate(args, data) {
|
static async insertOrUpdate(args, data) {
|
||||||
try {
|
try {
|
||||||
return ClientIPC.send('bd-dba', { action: 'update', args, data });
|
return ClientIPC.send('bd-dba', { action: 'update', args, data });
|
||||||
|
@ -24,6 +30,11 @@ export default class {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds data in the database.
|
||||||
|
* @param {Object} args The record to find
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
static async find(args) {
|
static async find(args) {
|
||||||
try {
|
try {
|
||||||
return ClientIPC.send('bd-dba', { action: 'find', args });
|
return ClientIPC.send('bd-dba', { action: 'find', args });
|
||||||
|
@ -31,4 +42,5 @@ export default class {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -402,7 +402,7 @@ export default class DiscordApi {
|
||||||
|
|
||||||
static get currentChannel() {
|
static get currentChannel() {
|
||||||
const channel = Modules.ChannelStore.getChannel(Modules.SelectedChannelStore.getChannelId());
|
const channel = Modules.ChannelStore.getChannel(Modules.SelectedChannelStore.getChannelId());
|
||||||
return channel.isPrivate ? new PrivateChannel(channel) : new GuildChannel(channel);
|
if (channel) return channel.isPrivate() ? new PrivateChannel(channel) : new GuildChannel(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
static get currentUser() {
|
static get currentUser() {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* BetterDiscord WebpackModules Module
|
* BetterDiscord Event Hook
|
||||||
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
|
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
* https://betterdiscord.net
|
* https://betterdiscord.net
|
||||||
|
@ -8,14 +8,13 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import EventListener from './eventlistener';
|
import { Utils, ClientLogger as Logger } from 'common';
|
||||||
import { Utils } from 'common';
|
|
||||||
import Events from './events';
|
|
||||||
import { WebpackModules } from './webpackmodules';
|
import { WebpackModules } from './webpackmodules';
|
||||||
|
import Events from './events';
|
||||||
|
import EventListener from './eventlistener';
|
||||||
|
|
||||||
import * as SocketStructs from '../structs/socketstructs';
|
import * as SocketStructs from '../structs/socketstructs';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discord socket event hook
|
* Discord socket event hook
|
||||||
* @extends {EventListener}
|
* @extends {EventListener}
|
||||||
|
@ -23,7 +22,7 @@ import * as SocketStructs from '../structs/socketstructs';
|
||||||
export default class extends EventListener {
|
export default class extends EventListener {
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
console.log(SocketStructs);
|
Logger.log('EventHook', SocketStructs);
|
||||||
this.hook();
|
this.hook();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,16 +43,16 @@ export default class extends EventListener {
|
||||||
orig.call(this, ...args);
|
orig.call(this, ...args);
|
||||||
self.wsc = this;
|
self.wsc = this;
|
||||||
self.emit(...args);
|
self.emit(...args);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get eventsModule() {
|
get eventsModule() {
|
||||||
return WebpackModules.getModuleByPrototypes(['setMaxListeners', 'emit']);
|
return WebpackModules.getModuleByName('Events');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discord emit overload
|
* Discord emit overload
|
||||||
* @param {any} e
|
* @param {any} event
|
||||||
* @param {any} action
|
* @param {any} action
|
||||||
* @param {any} data
|
* @param {any} data
|
||||||
*/
|
*/
|
||||||
|
@ -66,8 +65,8 @@ export default class extends EventListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit callback
|
* Emit callback
|
||||||
* @param {any} e Event Action
|
* @param {any} event Event
|
||||||
* @param {any} d Event Args
|
* @param {any} data Event data
|
||||||
*/
|
*/
|
||||||
dispatch(e, d) {
|
dispatch(e, d) {
|
||||||
Events.emit('raw-event', { type: e, data: d });
|
Events.emit('raw-event', { type: e, data: d });
|
||||||
|
@ -143,7 +142,7 @@ export default class extends EventListener {
|
||||||
LFG_LISTING_CREATE: 'LFG_LISTING_CREATE', // No groups here
|
LFG_LISTING_CREATE: 'LFG_LISTING_CREATE', // No groups here
|
||||||
LFG_LISTING_DELETE: 'LFG_LISTING_DELETE', // Thank you
|
LFG_LISTING_DELETE: 'LFG_LISTING_DELETE', // Thank you
|
||||||
BRAINTREE_POPUP_BRIDGE_CALLBACK: 'BRAINTREE_POPUP_BRIDGE_CALLBACK' // What
|
BRAINTREE_POPUP_BRIDGE_CALLBACK: 'BRAINTREE_POPUP_BRIDGE_CALLBACK' // What
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,14 +12,39 @@ import { EventEmitter } from 'events';
|
||||||
const emitter = new EventEmitter();
|
const emitter = new EventEmitter();
|
||||||
|
|
||||||
export default class {
|
export default class {
|
||||||
static on(eventName, callBack) {
|
|
||||||
emitter.on(eventName, callBack);
|
/**
|
||||||
|
* Adds an event listener.
|
||||||
|
* @param {String} event The event to listen for
|
||||||
|
* @param {Function} callback The function to call when the event is emitted
|
||||||
|
*/
|
||||||
|
static on(event, callback) {
|
||||||
|
emitter.on(event, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
static off(eventName, callBack) {
|
/**
|
||||||
emitter.removeListener(eventName, callBack);
|
* Adds an event listener that is only called once.
|
||||||
|
* @param {String} event The event to listen for
|
||||||
|
* @param {Function} callback The function to call when the event is emitted
|
||||||
|
*/
|
||||||
|
static once(event, callback) {
|
||||||
|
emitter.once(event, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an event listener.
|
||||||
|
* @param {String} event The event to remove
|
||||||
|
* @param {Function} callback The listener to remove
|
||||||
|
*/
|
||||||
|
static off(event, callback) {
|
||||||
|
emitter.removeListener(event, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits an event
|
||||||
|
* @param {String} event The event to emit
|
||||||
|
* @param {Any} ...data Data to pass to the event listeners
|
||||||
|
*/
|
||||||
static emit(...args) {
|
static emit(...args) {
|
||||||
emitter.emit(...args);
|
emitter.emit(...args);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
const eventemitters = new WeakMap();
|
const eventemitters = new WeakMap();
|
||||||
|
|
||||||
export default class EventsWrapper {
|
export default class EventsWrapper {
|
||||||
constructor(eventemitter) {
|
|
||||||
|
constructor(eventemitter, bind) {
|
||||||
eventemitters.set(this, eventemitter);
|
eventemitters.set(this, eventemitter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,26 +20,33 @@ export default class EventsWrapper {
|
||||||
return this._eventSubs || (this._eventSubs = []);
|
return this._eventSubs || (this._eventSubs = []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get on() { return this.subscribe }
|
||||||
subscribe(event, callback) {
|
subscribe(event, callback) {
|
||||||
if (this.eventSubs.find(e => e.event === event && e.callback === callback)) return;
|
if (this.eventSubs.find(e => e.event === event && e.callback === callback)) return;
|
||||||
this.eventSubs.push({
|
const boundCallback = () => callback.apply(this.bind, arguments);
|
||||||
event,
|
this.eventSubs.push({ event, callback, boundCallback });
|
||||||
callback
|
eventemitters.get(this).on(event, boundCallback);
|
||||||
});
|
|
||||||
eventemitters.get(this).on(event, callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
once(event, callback) {
|
||||||
|
if (this.eventSubs.find(e => e.event === event && e.callback === callback)) return;
|
||||||
|
const boundCallback = () => this.off(event, callback) && callback.apply(this.bind, arguments);
|
||||||
|
this.eventSubs.push({ event, callback, boundCallback });
|
||||||
|
eventemitters.get(this).on(event, boundCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
get off() { return this.unsubscribe }
|
||||||
unsubscribe(event, callback) {
|
unsubscribe(event, callback) {
|
||||||
for (let index of this.eventSubs) {
|
for (let index of this.eventSubs) {
|
||||||
if (this.eventSubs[index].event !== event || (callback && this.eventSubs[index].callback === callback)) return;
|
if (this.eventSubs[index].event !== event || (callback && this.eventSubs[index].callback === callback)) continue;
|
||||||
eventemitters.get(this).off(event, this.eventSubs[index].callback);
|
eventemitters.get(this).off(event, this.eventSubs[index].boundCallback);
|
||||||
this.eventSubs.splice(index, 1);
|
this.eventSubs.splice(index, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsubscribeAll() {
|
unsubscribeAll() {
|
||||||
for (let event of this.eventSubs) {
|
for (let event of this.eventSubs) {
|
||||||
eventemitters.get(this).off(event.event, event.callback);
|
eventemitters.get(this).off(event.event, event.boundCallback);
|
||||||
}
|
}
|
||||||
this.eventSubs.splice(0, this.eventSubs.length);
|
this.eventSubs.splice(0, this.eventSubs.length);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,10 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import sparkplug from 'sparkplug';
|
||||||
|
import { ClientIPC } from 'common';
|
||||||
import Module from './module';
|
import Module from './module';
|
||||||
import Events from './events';
|
import Events from './events';
|
||||||
import { ClientIPC } from 'bdipc';
|
|
||||||
|
|
||||||
export default new class extends Module {
|
export default new class extends Module {
|
||||||
|
|
||||||
|
@ -28,28 +29,24 @@ export default new class extends Module {
|
||||||
this.getObject = this.getObject.bind(this);
|
this.getObject = this.getObject.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
first() {
|
async first() {
|
||||||
(async() => {
|
|
||||||
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
|
// This is for Discord to stop error reporting :3
|
||||||
window.BetterDiscord = {
|
window.BetterDiscord = {
|
||||||
'version': config.version,
|
version: config.version,
|
||||||
'v': config.version
|
v: config.version
|
||||||
};
|
};
|
||||||
window.jQuery = {};
|
window.jQuery = {};
|
||||||
|
|
||||||
if (window.__bd) {
|
if (sparkplug.bd) {
|
||||||
this.setState(window.__bd);
|
this.setState({ bd: sparkplug.bd });
|
||||||
window.__bd = {
|
sparkplug.bd.setWS = this.setWS;
|
||||||
setWS: this.setWS
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Events.emit('global-ready');
|
Events.emit('global-ready');
|
||||||
Events.emit('socket-created', this.state.wsHook);
|
Events.emit('socket-created', this.state.wsHook);
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setWS(wSocket) {
|
setWS(wSocket) {
|
||||||
|
@ -60,19 +57,43 @@ export default new class extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
getObject(name) {
|
getObject(name) {
|
||||||
return this.state[name];
|
return this.config[name] || this.bd[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
get bd() {
|
||||||
|
return this.state.bd;
|
||||||
|
}
|
||||||
|
|
||||||
|
get localStorage() {
|
||||||
|
return this.bd.localStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
get webSocket() {
|
||||||
|
return this.bd.wsHook;
|
||||||
|
}
|
||||||
|
|
||||||
|
get WebSocket() {
|
||||||
|
return this.bd.wsOrig;
|
||||||
|
}
|
||||||
|
|
||||||
|
get ignited() {
|
||||||
|
return this.bd.ignited;
|
||||||
|
}
|
||||||
|
|
||||||
|
get config() {
|
||||||
|
return this.state.config;
|
||||||
|
}
|
||||||
|
|
||||||
|
get paths() {
|
||||||
|
return this.config.paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPath(id) {
|
getPath(id) {
|
||||||
return this.state.paths.find(path => path.id === id).path;
|
return this.paths.find(path => path.id === id).path;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get paths() {
|
get version() {
|
||||||
return this.state.paths;
|
return this.config.version;
|
||||||
}
|
|
||||||
|
|
||||||
static get version() {
|
|
||||||
return this.state.version;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,17 +8,16 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Base Module that every non-static module should extend
|
* Base Module that every non-static module should extend
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default class Module {
|
export default class Module {
|
||||||
|
|
||||||
constructor(args) {
|
constructor(args) {
|
||||||
this.__ = {
|
this.__ = {
|
||||||
state: args || {},
|
state: args || {},
|
||||||
args
|
args
|
||||||
}
|
};
|
||||||
this.setState = this.setState.bind(this);
|
this.setState = this.setState.bind(this);
|
||||||
this.initialize();
|
this.initialize();
|
||||||
}
|
}
|
||||||
|
@ -38,7 +37,6 @@ export default class Module {
|
||||||
set args(t) { }
|
set args(t) { }
|
||||||
get args() { return this.__.args; }
|
get args() { return this.__.args; }
|
||||||
|
|
||||||
|
|
||||||
set state(state) { return this.__.state = state; }
|
set state(state) { return this.__.state = state; }
|
||||||
get state() { return this.__.state; }
|
get state() { return this.__.state; }
|
||||||
|
|
||||||
|
|
|
@ -8,30 +8,39 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*Module Manager initializes all modules when everything is ready*/
|
import { ClientLogger as Logger } from 'common';
|
||||||
|
|
||||||
import { Events, SocketProxy, EventHook, CssEditor } from 'modules';
|
import { Events, SocketProxy, EventHook, CssEditor } from 'modules';
|
||||||
import { ProfileBadges } from 'ui';
|
import { ProfileBadges } from 'ui';
|
||||||
import Updater from './updater';
|
import Updater from './updater';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module Manager initializes all modules when everything is ready
|
||||||
|
*/
|
||||||
export default class {
|
export default class {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array of modules.
|
||||||
|
*/
|
||||||
static get modules() {
|
static get modules() {
|
||||||
return this._modules ? this._modules : (this._modules = [
|
return this._modules ? this._modules : (this._modules = [
|
||||||
new ProfileBadges(),
|
new ProfileBadges(),
|
||||||
new SocketProxy(),
|
new SocketProxy(),
|
||||||
new EventHook(),
|
new EventHook(),
|
||||||
CssEditor,
|
CssEditor,
|
||||||
new Updater()
|
Updater
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes all modules.
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
static async initModules() {
|
static async initModules() {
|
||||||
for (let module of this.modules) {
|
for (let module of this.modules) {
|
||||||
try {
|
try {
|
||||||
if (module.init && module.init instanceof Function) module.init();
|
if (module.init && module.init instanceof Function) module.init();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(`Failed to initialize module: ${err}`);
|
Logger.err('Module Manager', ['Failed to initialize module:', err]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
export { default as Events } from './events';
|
export { default as Events } from './events';
|
||||||
export { default as Settings } from './settings';
|
|
||||||
export { default as CssEditor } from './csseditor';
|
export { default as CssEditor } from './csseditor';
|
||||||
export { default as ExtModuleManager } from './extmodulemanager';
|
export { default as Globals } from './globals';
|
||||||
|
export { default as Settings } from './settings';
|
||||||
|
export { default as Database } from './database';
|
||||||
|
export { default as Updater } from './updater';
|
||||||
|
|
||||||
|
export { default as ModuleManager } from './modulemanager';
|
||||||
export { default as PluginManager } from './pluginmanager';
|
export { default as PluginManager } from './pluginmanager';
|
||||||
export { default as ThemeManager } from './thememanager';
|
export { default as ThemeManager } from './thememanager';
|
||||||
export { default as Globals } from './globals';
|
export { default as ExtModuleManager } from './extmodulemanager';
|
||||||
|
export { default as Permissions } from './permissionmanager';
|
||||||
|
|
||||||
|
export { default as EventsWrapper } from './eventswrapper';
|
||||||
export { default as Vendor } from './vendor';
|
export { default as Vendor } from './vendor';
|
||||||
|
|
||||||
export * from './webpackmodules';
|
export * from './webpackmodules';
|
||||||
export { default as ModuleManager } from './modulemanager';
|
export * from './patcher';
|
||||||
|
export * from './reactcomponents';
|
||||||
export { default as EventListener } from './eventlistener';
|
export { default as EventListener } from './eventlistener';
|
||||||
export { default as SocketProxy } from './socketproxy';
|
export { default as SocketProxy } from './socketproxy';
|
||||||
export { default as EventHook } from './eventhook';
|
export { default as EventHook } from './eventhook';
|
||||||
export { default as Permissions } from './permissionmanager';
|
|
||||||
export { default as Database } from './database';
|
|
||||||
export { default as EventsWrapper } from './eventswrapper';
|
|
||||||
export { default as DiscordApi } from './discordapi';
|
export { default as DiscordApi } from './discordapi';
|
||||||
export * from './patcher';
|
|
||||||
export * from './reactcomponents';
|
|
||||||
|
|
|
@ -12,7 +12,9 @@ import { WebpackModules } from './webpackmodules';
|
||||||
import { ClientLogger as Logger, Utils } from 'common';
|
import { ClientLogger as Logger, Utils } from 'common';
|
||||||
|
|
||||||
export class Patcher {
|
export class Patcher {
|
||||||
|
|
||||||
static get patches() { return this._patches || (this._patches = {}) }
|
static get patches() { return this._patches || (this._patches = {}) }
|
||||||
|
|
||||||
static getPatchesByCaller(id) {
|
static getPatchesByCaller(id) {
|
||||||
const patches = [];
|
const patches = [];
|
||||||
for (const patch in this.patches) {
|
for (const patch in this.patches) {
|
||||||
|
@ -22,16 +24,21 @@ export class Patcher {
|
||||||
}
|
}
|
||||||
return patches;
|
return patches;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unpatchAll(patches) {
|
static unpatchAll(patches) {
|
||||||
|
if (typeof patches === 'string')
|
||||||
|
patches = this.getPatchesByCaller(patches);
|
||||||
|
|
||||||
for (const patch of patches) {
|
for (const patch of patches) {
|
||||||
for (const child of patch.children) {
|
for (const child of patch.children) {
|
||||||
child.unpatch();
|
child.unpatch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static resolveModule(module) {
|
static resolveModule(module) {
|
||||||
if (module instanceof Function || (module instanceof Object && !(module instanceof Array))) return module;
|
if (module instanceof Function || (module instanceof Object && !(module instanceof Array))) return module;
|
||||||
if ('string' === typeof module) return WebpackModules.getModuleByName(module);
|
if (typeof module === 'string') return WebpackModules.getModuleByName(module);
|
||||||
if (module instanceof Array) return WebpackModules.getModuleByProps(module);
|
if (module instanceof Array) return WebpackModules.getModuleByProps(module);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -99,10 +106,12 @@ export class Patcher {
|
||||||
static before() { return this.pushChildPatch(...arguments, 'before') }
|
static before() { return this.pushChildPatch(...arguments, 'before') }
|
||||||
static after() { return this.pushChildPatch(...arguments, 'after') }
|
static after() { return this.pushChildPatch(...arguments, 'after') }
|
||||||
static instead() { return this.pushChildPatch(...arguments, 'instead') }
|
static instead() { return this.pushChildPatch(...arguments, 'instead') }
|
||||||
|
|
||||||
static pushChildPatch(caller, unresolvedModule, functionName, callback, displayName, type = 'after') {
|
static pushChildPatch(caller, unresolvedModule, functionName, callback, displayName, type = 'after') {
|
||||||
const module = this.resolveModule(unresolvedModule);
|
const module = this.resolveModule(unresolvedModule);
|
||||||
if (!module || !module[functionName] || !(module[functionName] instanceof Function)) return null;
|
if (!module || !module[functionName] || !(module[functionName] instanceof Function)) return null;
|
||||||
displayName = 'string' === typeof unresolvedModule ? unresolvedModule : displayName || module.displayName || module.name || module.constructor.displayName || module.constructor.name;
|
displayName = typeof unresolvedModule === 'string' ? unresolvedModule :
|
||||||
|
displayName || module.displayName || module.name || module.constructor.displayName || module.constructor.name;
|
||||||
const patchId = `${displayName}:${functionName}:${caller}`;
|
const patchId = `${displayName}:${functionName}:${caller}`;
|
||||||
|
|
||||||
const patch = this.patches[patchId] || this.pushPatch(caller, patchId, module, functionName);
|
const patch = this.patches[patchId] || this.pushPatch(caller, patchId, module, functionName);
|
||||||
|
|
|
@ -15,15 +15,15 @@ export default class Plugin extends Content {
|
||||||
|
|
||||||
get type() { return 'plugin' }
|
get type() { return 'plugin' }
|
||||||
|
|
||||||
// Don't use - these will eventually be removed!
|
|
||||||
get pluginPath() { return this.contentPath }
|
|
||||||
get pluginConfig() { return this.config }
|
|
||||||
|
|
||||||
get start() { return this.enable }
|
get start() { return this.enable }
|
||||||
get stop() { return this.disable }
|
get stop() { return this.disable }
|
||||||
|
|
||||||
|
reload() {
|
||||||
|
return PluginManager.reloadPlugin(this);
|
||||||
|
}
|
||||||
|
|
||||||
unload() {
|
unload() {
|
||||||
PluginManager.unloadPlugin(this);
|
return PluginManager.unloadPlugin(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* BetterDiscord Plugin Api
|
* BetterDiscord Plugin API
|
||||||
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
|
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
* https://betterdiscord.net
|
* https://betterdiscord.net
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Utils, ClientLogger as Logger, ClientIPC } from 'common';
|
import { Utils, ClientLogger as Logger, ClientIPC, AsyncEventEmitter } from 'common';
|
||||||
import Settings from './settings';
|
import Settings from './settings';
|
||||||
import ExtModuleManager from './extmodulemanager';
|
import ExtModuleManager from './extmodulemanager';
|
||||||
import PluginManager from './pluginmanager';
|
import PluginManager from './pluginmanager';
|
||||||
|
@ -20,31 +20,24 @@ import { SettingsSet, SettingsCategory, Setting, SettingsScheme } from 'structs'
|
||||||
import { BdMenuItems, Modals, DOM, Reflection } from 'ui';
|
import { BdMenuItems, Modals, DOM, Reflection } from 'ui';
|
||||||
import DiscordApi from './discordapi';
|
import DiscordApi from './discordapi';
|
||||||
import { ReactComponents } from './reactcomponents';
|
import { ReactComponents } from './reactcomponents';
|
||||||
import { MonkeyPatch } from './patcher';
|
import { Patcher, MonkeyPatch } from './patcher';
|
||||||
|
|
||||||
export default class PluginApi {
|
export default class PluginApi {
|
||||||
|
|
||||||
constructor(pluginInfo) {
|
constructor(pluginInfo, pluginPath) {
|
||||||
this.pluginInfo = pluginInfo;
|
this.pluginInfo = pluginInfo;
|
||||||
|
this.pluginPath = pluginPath;
|
||||||
|
|
||||||
this.Events = new EventsWrapper(Events);
|
this.Events = new EventsWrapper(Events);
|
||||||
|
Utils.defineSoftGetter(this.Events, 'bind', () => this.plugin);
|
||||||
|
|
||||||
this._menuItems = undefined;
|
this._menuItems = undefined;
|
||||||
this._injectedStyles = undefined;
|
this._injectedStyles = undefined;
|
||||||
this._modalStack = undefined;
|
this._modalStack = undefined;
|
||||||
}
|
}
|
||||||
get Discord() {
|
|
||||||
return DiscordApi;
|
|
||||||
}
|
|
||||||
get ReactComponents() {
|
|
||||||
return ReactComponents;
|
|
||||||
}
|
|
||||||
get Reflection() {
|
|
||||||
return Reflection;
|
|
||||||
}
|
|
||||||
get MonkeyPatch() {
|
|
||||||
return module => MonkeyPatch(this.pluginInfo.id, module);
|
|
||||||
}
|
|
||||||
get plugin() {
|
get plugin() {
|
||||||
return PluginManager.getPluginById(this.pluginInfo.id || this.pluginInfo.name.toLowerCase().replace(/[^a-zA-Z0-9-]/g, '-').replace(/--/g, '-'));
|
return PluginManager.getPluginByPath(this.pluginPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
async bridge(plugin_id) {
|
async bridge(plugin_id) {
|
||||||
|
@ -61,15 +54,18 @@ export default class PluginApi {
|
||||||
|
|
||||||
get Api() { return this }
|
get Api() { return this }
|
||||||
|
|
||||||
|
get AsyncEventEmitter() { return AsyncEventEmitter }
|
||||||
|
get EventsWrapper() { return EventsWrapper }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logger
|
* Logger
|
||||||
*/
|
*/
|
||||||
|
|
||||||
loggerLog(...message) { Logger.log(this.pluginInfo.name, message) }
|
loggerLog(...message) { Logger.log(this.plugin.name, message) }
|
||||||
loggerErr(...message) { Logger.err(this.pluginInfo.name, message) }
|
loggerErr(...message) { Logger.err(this.plugin.name, message) }
|
||||||
loggerWarn(...message) { Logger.warn(this.pluginInfo.name, message) }
|
loggerWarn(...message) { Logger.warn(this.plugin.name, message) }
|
||||||
loggerInfo(...message) { Logger.info(this.pluginInfo.name, message) }
|
loggerInfo(...message) { Logger.info(this.plugin.name, message) }
|
||||||
loggerDbg(...message) { Logger.dbg(this.pluginInfo.name, message) }
|
loggerDbg(...message) { Logger.dbg(this.plugin.name, message) }
|
||||||
get Logger() {
|
get Logger() {
|
||||||
return {
|
return {
|
||||||
log: this.loggerLog.bind(this),
|
log: this.loggerLog.bind(this),
|
||||||
|
@ -381,6 +377,53 @@ export default class PluginApi {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DiscordApi
|
||||||
|
*/
|
||||||
|
|
||||||
|
get Discord() {
|
||||||
|
return DiscordApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
get ReactComponents() {
|
||||||
|
return ReactComponents;
|
||||||
|
}
|
||||||
|
|
||||||
|
get Reflection() {
|
||||||
|
return Reflection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Patcher
|
||||||
|
*/
|
||||||
|
|
||||||
|
get patches() {
|
||||||
|
return Patcher.getPatchesByCaller(this.plugin.id);
|
||||||
|
}
|
||||||
|
patchBefore(...args) { return this.pushChildPatch(...args, 'before') }
|
||||||
|
patchAfter(...args) { return this.pushChildPatch(...args, 'after') }
|
||||||
|
patchInstead(...args) { return this.pushChildPatch(...args, 'instead') }
|
||||||
|
pushChildPatch(...args) {
|
||||||
|
return Patcher.pushChildPatch(this.plugin.id, ...args);
|
||||||
|
}
|
||||||
|
unpatchAll(patches) {
|
||||||
|
return Patcher.unpatchAll(patches || this.plugin.id);
|
||||||
|
}
|
||||||
|
get Patcher() {
|
||||||
|
return Object.defineProperty({
|
||||||
|
before: this.patchBefore.bind(this),
|
||||||
|
after: this.patchAfter.bind(this),
|
||||||
|
instead: this.patchInstead.bind(this),
|
||||||
|
pushChildPatch: this.pushChildPatch.bind(this),
|
||||||
|
unpatchAll: this.unpatchAll.bind(this),
|
||||||
|
}, 'patches', {
|
||||||
|
get: () => this.patches
|
||||||
|
});
|
||||||
|
}
|
||||||
|
get monkeyPatch() {
|
||||||
|
return module => MonkeyPatch(this.plugin.id, module);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop plugins from modifying the plugin API for all plugins
|
// Stop plugins from modifying the plugin API for all plugins
|
||||||
|
|
|
@ -76,12 +76,12 @@ export default class extends ContentManager {
|
||||||
static async loadPlugin(paths, configs, info, main, dependencies, permissions) {
|
static async loadPlugin(paths, configs, info, main, dependencies, permissions) {
|
||||||
if (permissions && permissions.length > 0) {
|
if (permissions && permissions.length > 0) {
|
||||||
for (let perm of permissions) {
|
for (let perm of permissions) {
|
||||||
console.log(`Permission: ${Permissions.permissionText(perm).HEADER} - ${Permissions.permissionText(perm).BODY}`);
|
Logger.log(this.moduleName, `Permission: ${Permissions.permissionText(perm).HEADER} - ${Permissions.permissionText(perm).BODY}`);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const allowed = await Modals.permissions(`${info.name} wants to:`, info.name, permissions).promise;
|
const allowed = await Modals.permissions(`${info.name} wants to:`, info.name, permissions).promise;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,15 +90,13 @@ export default class extends ContentManager {
|
||||||
for (const [key, value] of Object.entries(dependencies)) {
|
for (const [key, value] of Object.entries(dependencies)) {
|
||||||
const extModule = ExtModuleManager.findModule(key);
|
const extModule = ExtModuleManager.findModule(key);
|
||||||
if (!extModule) {
|
if (!extModule) {
|
||||||
throw {
|
throw {message: `Dependency ${key}:${value} is not loaded.`};
|
||||||
'message': `Dependency: ${key}:${value} is not loaded`
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
deps[key] = extModule.__require;
|
deps[key] = extModule.__require;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const plugin = window.require(paths.mainPath)(Plugin, new PluginApi(info), Vendor, deps);
|
const plugin = window.require(paths.mainPath)(Plugin, new PluginApi(info, paths.contentPath), Vendor, deps);
|
||||||
if (!(plugin.prototype instanceof Plugin))
|
if (!(plugin.prototype instanceof Plugin))
|
||||||
throw {message: `Plugin ${info.name} did not return a class that extends Plugin.`};
|
throw {message: `Plugin ${info.name} did not return a class that extends Plugin.`};
|
||||||
|
|
||||||
|
@ -121,24 +119,24 @@ export default class extends ContentManager {
|
||||||
static get unloadPlugin() { return this.unloadContent }
|
static get unloadPlugin() { return this.unloadContent }
|
||||||
static get reloadPlugin() { return this.reloadContent }
|
static get reloadPlugin() { return this.reloadContent }
|
||||||
|
|
||||||
static stopPlugin(name) {
|
/**
|
||||||
const plugin = name instanceof Plugin ? name : this.getPluginByName(name);
|
* Stops a plugin.
|
||||||
try {
|
* @param {Plugin|String} plugin
|
||||||
if (plugin) return plugin.stop();
|
* @return {Promise}
|
||||||
} catch (err) {
|
*/
|
||||||
// Logger.err('PluginManager', err);
|
static stopPlugin(plugin) {
|
||||||
}
|
plugin = this.isPlugin(plugin) ? plugin : this.getPluginById(plugin);
|
||||||
return true; //Return true anyways since plugin doesn't exist
|
return plugin.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
static startPlugin(name) {
|
/**
|
||||||
const plugin = name instanceof Plugin ? name : this.getPluginByName(name);
|
* Starts a plugin.
|
||||||
try {
|
* @param {Plugin|String} plugin
|
||||||
if (plugin) return plugin.start();
|
* @return {Promise}
|
||||||
} catch (err) {
|
*/
|
||||||
// Logger.err('PluginManager', err);
|
static startPlugin(plugin) {
|
||||||
}
|
plugin = this.isPlugin(plugin) ? plugin : this.getPluginById(plugin);
|
||||||
return true; //Return true anyways since plugin doesn't exist
|
return plugin.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
static get isPlugin() { return this.isThisContent }
|
static get isPlugin() { return this.isThisContent }
|
||||||
|
|
|
@ -19,6 +19,7 @@ class Helpers {
|
||||||
static get plannedActions() {
|
static get plannedActions() {
|
||||||
return this._plannedActions || (this._plannedActions = new Map());
|
return this._plannedActions || (this._plannedActions = new Map());
|
||||||
}
|
}
|
||||||
|
|
||||||
static recursiveArray(parent, key, count = 1) {
|
static recursiveArray(parent, key, count = 1) {
|
||||||
let index = 0;
|
let index = 0;
|
||||||
function* innerCall(parent, key) {
|
function* innerCall(parent, key) {
|
||||||
|
@ -34,6 +35,7 @@ class Helpers {
|
||||||
|
|
||||||
return innerCall(parent, key);
|
return innerCall(parent, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
static recursiveArrayCount(parent, key) {
|
static recursiveArrayCount(parent, key) {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
// eslint-disable-next-line no-empty-pattern
|
// eslint-disable-next-line no-empty-pattern
|
||||||
|
@ -41,6 +43,7 @@ class Helpers {
|
||||||
++count;
|
++count;
|
||||||
return this.recursiveArray(parent, key, count);
|
return this.recursiveArray(parent, key, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
static get recursiveChildren() {
|
static get recursiveChildren() {
|
||||||
return function* (parent, key, index = 0, count = 1) {
|
return function* (parent, key, index = 0, count = 1) {
|
||||||
const item = parent[key];
|
const item = parent[key];
|
||||||
|
@ -52,12 +55,14 @@ class Helpers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static returnFirst(iterator, process) {
|
static returnFirst(iterator, process) {
|
||||||
for (let child of iterator) {
|
for (let child of iterator) {
|
||||||
const retVal = process(child);
|
const retVal = process(child);
|
||||||
if (retVal !== undefined) return retVal;
|
if (retVal !== undefined) return retVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static getFirstChild(rootParent, rootKey, selector) {
|
static getFirstChild(rootParent, rootKey, selector) {
|
||||||
const getDirectChild = (item, selector) => {
|
const getDirectChild = (item, selector) => {
|
||||||
if (item && item.props && item.props.children) {
|
if (item && item.props && item.props.children) {
|
||||||
|
@ -116,11 +121,13 @@ class Helpers {
|
||||||
};
|
};
|
||||||
return this.returnFirst(this.recursiveChildren(rootParent, rootKey), checkFilter.bind(null, selector)) || {};
|
return this.returnFirst(this.recursiveChildren(rootParent, rootKey), checkFilter.bind(null, selector)) || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static parseSelector(selector) {
|
static parseSelector(selector) {
|
||||||
if (selector.startsWith('.')) return { className: selector.substr(1) }
|
if (selector.startsWith('.')) return { className: selector.substr(1) }
|
||||||
if (selector.startsWith('#')) return { id: selector.substr(1) }
|
if (selector.startsWith('#')) return { id: selector.substr(1) }
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
static findByProp(obj, what, value) {
|
static findByProp(obj, what, value) {
|
||||||
if (obj.hasOwnProperty(what) && obj[what] === value) return obj;
|
if (obj.hasOwnProperty(what) && obj[what] === value) return obj;
|
||||||
if (obj.props && !obj.children) return this.findByProp(obj.props, what, value);
|
if (obj.props && !obj.children) return this.findByProp(obj.props, what, value);
|
||||||
|
@ -132,6 +139,7 @@ class Helpers {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static findProp(obj, what) {
|
static findProp(obj, what) {
|
||||||
if (obj.hasOwnProperty(what)) return obj[what];
|
if (obj.hasOwnProperty(what)) return obj[what];
|
||||||
if (obj.props && !obj.children) return this.findProp(obj.props, what);
|
if (obj.props && !obj.children) return this.findProp(obj.props, what);
|
||||||
|
@ -144,6 +152,7 @@ class Helpers {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get ReactDOM() {
|
static get ReactDOM() {
|
||||||
return WebpackModules.getModuleByName('ReactDOM');
|
return WebpackModules.getModuleByName('ReactDOM');
|
||||||
}
|
}
|
||||||
|
@ -155,12 +164,15 @@ class ReactComponent {
|
||||||
this._component = component;
|
this._component = component;
|
||||||
this._retVal = retVal;
|
this._retVal = retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
get id() {
|
get id() {
|
||||||
return this._id;
|
return this._id;
|
||||||
}
|
}
|
||||||
|
|
||||||
get component() {
|
get component() {
|
||||||
return this._component;
|
return this._component;
|
||||||
}
|
}
|
||||||
|
|
||||||
get retVal() {
|
get retVal() {
|
||||||
return this._retVal;
|
return this._retVal;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,22 +8,23 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import defaultSettings from '../data/user.settings.default';
|
|
||||||
import Globals from './globals';
|
|
||||||
import CssEditor from './csseditor';
|
|
||||||
import Events from './events';
|
|
||||||
import { Utils, FileUtils, ClientLogger as Logger } from 'common';
|
import { Utils, FileUtils, ClientLogger as Logger } from 'common';
|
||||||
import { SettingsSet, SettingUpdatedEvent } from 'structs';
|
import { SettingsSet, SettingUpdatedEvent } from 'structs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import Globals from './globals';
|
||||||
|
import CssEditor from './csseditor';
|
||||||
|
import Events from './events';
|
||||||
|
import defaultSettings from '../data/user.settings.default';
|
||||||
|
|
||||||
export default new class Settings {
|
export default new class Settings {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.settings = defaultSettings.map(_set => {
|
this.settings = defaultSettings.map(_set => {
|
||||||
const set = new SettingsSet(_set);
|
const set = new SettingsSet(_set);
|
||||||
|
|
||||||
set.on('setting-updated', event => {
|
set.on('setting-updated', event => {
|
||||||
const { category, setting, value, old_value } = event;
|
const { category, setting, value, old_value } = event;
|
||||||
Logger.log('Settings', `${set.id}/${category.id}/${setting.id} was changed from ${old_value} to ${value}`);
|
Logger.log('Settings', [`${set.id}/${category.id}/${setting.id} was changed from`, old_value, 'to', value]);
|
||||||
Events.emit('setting-updated', event);
|
Events.emit('setting-updated', event);
|
||||||
Events.emit(`setting-updated-${set.id}_${category.id}_${setting.id}`, event);
|
Events.emit(`setting-updated-${set.id}_${category.id}_${setting.id}`, event);
|
||||||
});
|
});
|
||||||
|
@ -37,6 +38,9 @@ export default new class Settings {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads BetterDiscord's settings.
|
||||||
|
*/
|
||||||
async loadSettings() {
|
async loadSettings() {
|
||||||
try {
|
try {
|
||||||
await FileUtils.ensureDirectory(this.dataPath);
|
await FileUtils.ensureDirectory(this.dataPath);
|
||||||
|
@ -48,7 +52,7 @@ export default new class Settings {
|
||||||
for (let set of this.settings) {
|
for (let set of this.settings) {
|
||||||
const newSet = settings.find(s => s.id === set.id);
|
const newSet = settings.find(s => s.id === set.id);
|
||||||
if (!newSet) continue;
|
if (!newSet) continue;
|
||||||
set.merge(newSet);
|
await set.merge(newSet);
|
||||||
set.setSaved();
|
set.setSaved();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,10 +61,13 @@ export default new class Settings {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// There was an error loading settings
|
// There was an error loading settings
|
||||||
// This probably means that the user doesn't have any settings yet
|
// This probably means that the user doesn't have any settings yet
|
||||||
Logger.err('Settings', err);
|
Logger.warn('Settings', ['Failed to load internal settings', err]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves BetterDiscord's settings including CSS editor data.
|
||||||
|
*/
|
||||||
async saveSettings() {
|
async saveSettings() {
|
||||||
try {
|
try {
|
||||||
await FileUtils.ensureDirectory(this.dataPath);
|
await FileUtils.ensureDirectory(this.dataPath);
|
||||||
|
@ -72,15 +79,10 @@ export default new class Settings {
|
||||||
css: CssEditor.css,
|
css: CssEditor.css,
|
||||||
css_editor_files: CssEditor.files,
|
css_editor_files: CssEditor.files,
|
||||||
scss_error: CssEditor.error,
|
scss_error: CssEditor.error,
|
||||||
css_editor_bounds: {
|
css_editor_bounds: CssEditor.editor_bounds
|
||||||
width: CssEditor.editor_bounds.width,
|
|
||||||
height: CssEditor.editor_bounds.height,
|
|
||||||
x: CssEditor.editor_bounds.x,
|
|
||||||
y: CssEditor.editor_bounds.y
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
for (let set of this.getSettings) {
|
for (let set of this.settings) {
|
||||||
set.setSaved();
|
set.setSaved();
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -90,8 +92,13 @@ export default new class Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds one of BetterDiscord's settings sets.
|
||||||
|
* @param {String} set_id The ID of the set to find
|
||||||
|
* @return {SettingsSet}
|
||||||
|
*/
|
||||||
getSet(set_id) {
|
getSet(set_id) {
|
||||||
return this.getSettings.find(s => s.id === set_id);
|
return this.settings.find(s => s.id === set_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
get core() { return this.getSet('core') }
|
get core() { return this.getSet('core') }
|
||||||
|
@ -100,39 +107,46 @@ export default new class Settings {
|
||||||
get css() { return this.getSet('css') }
|
get css() { return this.getSet('css') }
|
||||||
get security() { return this.getSet('security') }
|
get security() { return this.getSet('security') }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a category in one of BetterDiscord's settings sets.
|
||||||
|
* @param {String} set_id The ID of the set to look in
|
||||||
|
* @param {String} category_id The ID of the category to find
|
||||||
|
* @return {SettingsCategory}
|
||||||
|
*/
|
||||||
getCategory(set_id, category_id) {
|
getCategory(set_id, category_id) {
|
||||||
const set = this.getSet(set_id);
|
const set = this.getSet(set_id);
|
||||||
return set ? set.getCategory(category_id) : undefined;
|
return set ? set.getCategory(category_id) : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a setting in one of BetterDiscord's settings sets.
|
||||||
|
* @param {String} set_id The ID of the set to look in
|
||||||
|
* @param {String} category_id The ID of the category to look in
|
||||||
|
* @param {String} setting_id The ID of the setting to find
|
||||||
|
* @return {Setting}
|
||||||
|
*/
|
||||||
getSetting(set_id, category_id, setting_id) {
|
getSetting(set_id, category_id, setting_id) {
|
||||||
const set = this.getSet(set_id);
|
const set = this.getSet(set_id);
|
||||||
return set ? set.getSetting(category_id, setting_id) : undefined;
|
return set ? set.getSetting(category_id, setting_id) : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a setting's value in one of BetterDiscord's settings sets.
|
||||||
|
* @param {String} set_id The ID of the set to look in
|
||||||
|
* @param {String} category_id The ID of the category to look in
|
||||||
|
* @param {String} setting_id The ID of the setting to find
|
||||||
|
* @return {Any}
|
||||||
|
*/
|
||||||
get(set_id, category_id, setting_id) {
|
get(set_id, category_id, setting_id) {
|
||||||
const set = this.getSet(set_id);
|
const set = this.getSet(set_id);
|
||||||
return set ? set.get(category_id, setting_id) : undefined;
|
return set ? set.get(category_id, setting_id) : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
async mergeSettings(set_id, newSettings) {
|
/**
|
||||||
const set = this.getSet(set_id);
|
* The path to store user data in.
|
||||||
if (!set) return;
|
*/
|
||||||
|
|
||||||
return await set.merge(newSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
setSetting(set_id, category_id, setting_id, value) {
|
|
||||||
const setting = this.getSetting(set_id, category_id, setting_id);
|
|
||||||
if (!setting) throw {message: `Tried to set ${set_id}/${category_id}/${setting_id}, which doesn't exist`};
|
|
||||||
setting.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
get getSettings() {
|
|
||||||
return this.settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
get dataPath() {
|
get dataPath() {
|
||||||
return Globals.getPath('data');
|
return Globals.getPath('data');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { ClientLogger as Logger } from 'common';
|
||||||
import EventListener from './eventlistener';
|
import EventListener from './eventlistener';
|
||||||
|
|
||||||
export default class SocketProxy extends EventListener {
|
export default class SocketProxy extends EventListener {
|
||||||
|
@ -19,7 +20,7 @@ export default class SocketProxy extends EventListener {
|
||||||
|
|
||||||
get eventBindings() {
|
get eventBindings() {
|
||||||
return [
|
return [
|
||||||
{ id: 'socket-created', 'callback': this.socketCreated }
|
{ id: 'socket-created', callback: this.socketCreated }
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ export default class SocketProxy extends EventListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
onMessage(e) {
|
onMessage(e) {
|
||||||
console.info(e);
|
Logger.info('SocketProxy', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,10 +31,6 @@ export default class Theme extends Content {
|
||||||
get type() { return 'theme' }
|
get type() { return 'theme' }
|
||||||
get css() { return this.data.css }
|
get css() { return this.data.css }
|
||||||
|
|
||||||
// Don't use - these will eventually be removed!
|
|
||||||
get themePath() { return this.contentPath }
|
|
||||||
get themeConfig() { return this.config }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when settings are updated.
|
* Called when settings are updated.
|
||||||
* This can be overridden by other content types.
|
* This can be overridden by other content types.
|
||||||
|
@ -63,7 +59,7 @@ export default class Theme extends Content {
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
async compile() {
|
async compile() {
|
||||||
console.log('Compiling CSS');
|
Logger.log(this.name, 'Compiling CSS');
|
||||||
|
|
||||||
if (this.info.type === 'sass') {
|
if (this.info.type === 'sass') {
|
||||||
const config = await ThemeManager.getConfigAsSCSS(this.settings);
|
const config = await ThemeManager.getConfigAsSCSS(this.settings);
|
||||||
|
@ -121,6 +117,7 @@ export default class Theme extends Content {
|
||||||
*/
|
*/
|
||||||
set files(files) {
|
set files(files) {
|
||||||
this.data.files = files;
|
this.data.files = files;
|
||||||
|
|
||||||
if (Settings.get('css', 'default', 'watch-files'))
|
if (Settings.get('css', 'default', 'watch-files'))
|
||||||
this.watchfiles = files;
|
this.watchfiles = files;
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,11 +62,11 @@ export default class ThemeManager extends ContentManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
static enableTheme(theme) {
|
static enableTheme(theme) {
|
||||||
theme.enable();
|
return theme.enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
static disableTheme(theme) {
|
static disableTheme(theme) {
|
||||||
theme.disable();
|
return theme.disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
static get isTheme() { return this.isThisContent }
|
static get isTheme() { return this.isThisContent }
|
||||||
|
@ -74,6 +74,11 @@ export default class ThemeManager extends ContentManager {
|
||||||
return theme instanceof Theme;
|
return theme instanceof Theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a representation of a settings set's values in SCSS.
|
||||||
|
* @param {SettingsSet} settingsset The set to convert to SCSS
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
static async getConfigAsSCSS(settingsset) {
|
static async getConfigAsSCSS(settingsset) {
|
||||||
const variables = [];
|
const variables = [];
|
||||||
|
|
||||||
|
@ -87,6 +92,11 @@ export default class ThemeManager extends ContentManager {
|
||||||
return variables.join('\n');
|
return variables.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a representation of a settings set's values as an SCSS map.
|
||||||
|
* @param {SettingsSet} settingsset The set to convert to an SCSS map
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
static async getConfigAsSCSSMap(settingsset) {
|
static async getConfigAsSCSSMap(settingsset) {
|
||||||
const variables = [];
|
const variables = [];
|
||||||
|
|
||||||
|
@ -100,6 +110,11 @@ export default class ThemeManager extends ContentManager {
|
||||||
return '(' + variables.join(', ') + ')';
|
return '(' + variables.join(', ') + ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a setting's name and value as a string that can be included in SCSS.
|
||||||
|
* @param {Setting} setting The setting to convert to SCSS
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
static async parseSetting(setting) {
|
static async parseSetting(setting) {
|
||||||
const { type, id, value } = setting;
|
const { type, id, value } = setting;
|
||||||
const name = id.replace(/[^a-zA-Z0-9-]/g, '-').replace(/--/g, '-');
|
const name = id.replace(/[^a-zA-Z0-9-]/g, '-').replace(/--/g, '-');
|
||||||
|
@ -108,6 +123,11 @@ export default class ThemeManager extends ContentManager {
|
||||||
if (scss) return [name, scss];
|
if (scss) return [name, scss];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes a string so it can be included in SCSS.
|
||||||
|
* @param {String} value The string to escape
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
static toSCSSString(value) {
|
static toSCSSString(value) {
|
||||||
if (typeof value !== 'string' && value.toString) value = value.toString();
|
if (typeof value !== 'string' && value.toString) value = value.toString();
|
||||||
return `'${typeof value === 'string' ? value.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') : ''}'`;
|
return `'${typeof value === 'string' ? value.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') : ''}'`;
|
||||||
|
|
|
@ -13,15 +13,20 @@ import Globals from './globals';
|
||||||
import { $ } from 'vendor';
|
import { $ } from 'vendor';
|
||||||
import { ClientLogger as Logger } from 'common';
|
import { ClientLogger as Logger } from 'common';
|
||||||
|
|
||||||
export default class {
|
export default new class {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
window.updater = this;
|
|
||||||
this.updatesAvailable = false;
|
this.updatesAvailable = false;
|
||||||
|
this.latestVersion = undefined;
|
||||||
|
this.error = undefined;
|
||||||
|
|
||||||
this.init = this.init.bind(this);
|
this.init = this.init.bind(this);
|
||||||
this.checkForUpdates = this.checkForUpdates.bind(this);
|
this.checkForUpdates = this.checkForUpdates.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interval to wait before checking for updates.
|
||||||
|
*/
|
||||||
get interval() {
|
get interval() {
|
||||||
return 60 * 1000 * 30;
|
return 60 * 1000 * 30;
|
||||||
}
|
}
|
||||||
|
@ -30,34 +35,61 @@ export default class {
|
||||||
this.updateInterval = setInterval(this.checkForUpdates, this.interval);
|
this.updateInterval = setInterval(this.checkForUpdates, this.interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
/**
|
||||||
// TODO
|
* Installs an update.
|
||||||
|
* TODO
|
||||||
|
*/
|
||||||
|
async update() {
|
||||||
|
try {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||||
|
|
||||||
this.updatesAvailable = false;
|
this.updatesAvailable = false;
|
||||||
|
this.latestVersion = Globals.version;
|
||||||
Events.emit('update-check-end');
|
Events.emit('update-check-end');
|
||||||
|
} catch (err) {
|
||||||
|
this.error = err;
|
||||||
|
this.checkForUpdates();
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for updates.
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
checkForUpdates() {
|
checkForUpdates() {
|
||||||
if (this.updatesAvailable) return;
|
return new Promise((resolve, reject) => {
|
||||||
|
if (this.updatesAvailable) return resolve(true);
|
||||||
Events.emit('update-check-start');
|
Events.emit('update-check-start');
|
||||||
Logger.info('Updater', 'Checking for updates');
|
Logger.info('Updater', 'Checking for updates');
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
url: 'https://rawgit.com/JsSucks/BetterDiscordApp/master/package.json',
|
url: 'https://rawgit.com/JsSucks/BetterDiscordApp/master/package.json',
|
||||||
cache: false,
|
cache: false,
|
||||||
success: e => {
|
success: e => {
|
||||||
try {
|
try {
|
||||||
|
this.latestVersion = e.version;
|
||||||
Events.emit('update-check-end');
|
Events.emit('update-check-end');
|
||||||
Logger.info('Updater',
|
Logger.info('Updater', `Latest Version: ${e.version} - Current Version: ${Globals.version}`);
|
||||||
`Latest Version: ${e.version} - Current Version: ${Globals.version}`);
|
|
||||||
if (e.version !== Globals.version) {
|
if (this.latestVersion !== Globals.version) {
|
||||||
this.updatesAvailable = true;
|
this.updatesAvailable = true;
|
||||||
Events.emit('updates-available');
|
Events.emit('updates-available');
|
||||||
|
resolve(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resolve(false);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Events.emit('update-check-fail', err);
|
Events.emit('update-check-fail', err);
|
||||||
|
reject(err);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fail: e => Events.emit('update-check-fail', e)
|
fail: err => {
|
||||||
|
Events.emit('update-check-fail', err);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,29 +11,36 @@
|
||||||
import { WebpackModules } from './webpackmodules';
|
import { WebpackModules } from './webpackmodules';
|
||||||
import jQuery from 'jquery';
|
import jQuery from 'jquery';
|
||||||
import lodash from 'lodash';
|
import lodash from 'lodash';
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
export { jQuery as $ };
|
export { jQuery as $ };
|
||||||
|
|
||||||
export default class {
|
export default class {
|
||||||
|
|
||||||
static get jQuery() {
|
/**
|
||||||
return jQuery;
|
* jQuery
|
||||||
}
|
*/
|
||||||
|
static get jQuery() { return jQuery }
|
||||||
|
static get $() { return this.jQuery }
|
||||||
|
|
||||||
static get $() {
|
/**
|
||||||
return this.jQuery;
|
* Lodash
|
||||||
}
|
*/
|
||||||
|
static get lodash() { return lodash }
|
||||||
static get lodash() {
|
static get _() { return this.lodash }
|
||||||
return lodash;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get _() {
|
|
||||||
return this.lodash;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moment
|
||||||
|
*/
|
||||||
static get moment() {
|
static get moment() {
|
||||||
return WebpackModules.getModuleByName('Moment');
|
return WebpackModules.getModuleByName('Moment');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vue
|
||||||
|
*/
|
||||||
|
static get Vue() {
|
||||||
|
return Vue;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,8 @@ const KnownModules = {
|
||||||
React: Filters.byProperties(['createElement', 'cloneElement']),
|
React: Filters.byProperties(['createElement', 'cloneElement']),
|
||||||
ReactDOM: Filters.byProperties(['render', 'findDOMNode']),
|
ReactDOM: Filters.byProperties(['render', 'findDOMNode']),
|
||||||
|
|
||||||
|
Events: Filters.byPrototypeFields(['setMaxListeners', 'emit']),
|
||||||
|
|
||||||
/* Guild Info, Stores, and Utilities */
|
/* Guild Info, Stores, and Utilities */
|
||||||
GuildStore: Filters.byProperties(['getGuild']),
|
GuildStore: Filters.byProperties(['getGuild']),
|
||||||
SortedGuildStore: Filters.byProperties(['getSortedGuilds']),
|
SortedGuildStore: Filters.byProperties(['getSortedGuilds']),
|
||||||
|
@ -89,7 +91,6 @@ const KnownModules = {
|
||||||
UserActivityStore: Filters.byProperties(['getActivity']),
|
UserActivityStore: Filters.byProperties(['getActivity']),
|
||||||
UserNameResolver: Filters.byProperties(['getName']),
|
UserNameResolver: Filters.byProperties(['getName']),
|
||||||
|
|
||||||
|
|
||||||
/* Emoji Store and Utils */
|
/* Emoji Store and Utils */
|
||||||
EmojiInfo: Filters.byProperties(['isEmojiDisabled']),
|
EmojiInfo: Filters.byProperties(['isEmojiDisabled']),
|
||||||
EmojiUtils: Filters.byProperties(['getGuildEmoji']),
|
EmojiUtils: Filters.byProperties(['getGuildEmoji']),
|
||||||
|
@ -100,7 +101,6 @@ const KnownModules = {
|
||||||
InviteResolver: Filters.byProperties(['findInvite']),
|
InviteResolver: Filters.byProperties(['findInvite']),
|
||||||
InviteActions: Filters.byProperties(['acceptInvite']),
|
InviteActions: Filters.byProperties(['acceptInvite']),
|
||||||
|
|
||||||
|
|
||||||
/* Discord Objects & Utils */
|
/* Discord Objects & Utils */
|
||||||
DiscordConstants: Filters.byProperties(["Permissions", "ActivityTypes", "StatusTypes"]),
|
DiscordConstants: Filters.byProperties(["Permissions", "ActivityTypes", "StatusTypes"]),
|
||||||
Permissions: Filters.byProperties(['getHighestRole']),
|
Permissions: Filters.byProperties(['getHighestRole']),
|
||||||
|
@ -126,7 +126,6 @@ const KnownModules = {
|
||||||
ExperimentsManager: Filters.byProperties(['isDeveloper']),
|
ExperimentsManager: Filters.byProperties(['isDeveloper']),
|
||||||
CurrentExperiment: Filters.byProperties(['getExperimentId']),
|
CurrentExperiment: Filters.byProperties(['getExperimentId']),
|
||||||
|
|
||||||
|
|
||||||
/* Images, Avatars and Utils */
|
/* Images, Avatars and Utils */
|
||||||
ImageResolver: Filters.byProperties(["getUserAvatarURL"]),
|
ImageResolver: Filters.byProperties(["getUserAvatarURL"]),
|
||||||
ImageUtils: Filters.byProperties(['getSizedImageSrc']),
|
ImageUtils: Filters.byProperties(['getSizedImageSrc']),
|
||||||
|
@ -180,7 +179,6 @@ const KnownModules = {
|
||||||
URLParser: Filters.byProperties(['Url', 'parse']),
|
URLParser: Filters.byProperties(['Url', 'parse']),
|
||||||
ExtraURLs: Filters.byProperties(['getArticleURL']),
|
ExtraURLs: Filters.byProperties(['getArticleURL']),
|
||||||
|
|
||||||
|
|
||||||
/* DOM/React Components */
|
/* DOM/React Components */
|
||||||
/* ==================== */
|
/* ==================== */
|
||||||
UserSettingsWindow: Filters.byProperties(['open', 'updateAccount']),
|
UserSettingsWindow: Filters.byProperties(['open', 'updateAccount']),
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
export default class {
|
|
||||||
|
|
||||||
}
|
|
|
@ -17,22 +17,37 @@ export default class ErrorEvent extends Event {
|
||||||
this.showStack = false; // For error modal
|
this.showStack = false; // For error modal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The module the error occured in.
|
||||||
|
*/
|
||||||
get module() {
|
get module() {
|
||||||
return this.args.module;
|
return this.args.module;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A message describing the error.
|
||||||
|
*/
|
||||||
get message() {
|
get message() {
|
||||||
return this.args.message;
|
return this.args.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The original error object.
|
||||||
|
*/
|
||||||
get err() {
|
get err() {
|
||||||
return this.args.err;
|
return this.args.err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A trace showing which functions were called when the error occured.
|
||||||
|
*/
|
||||||
get stackTrace() {
|
get stackTrace() {
|
||||||
return this.err.stack;
|
return this.err.stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of event.
|
||||||
|
*/
|
||||||
get __eventType() {
|
get __eventType() {
|
||||||
return 'error';
|
return 'error';
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,14 +17,23 @@ export default class Event {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object containing information about the event.
|
||||||
|
*/
|
||||||
get event() {
|
get event() {
|
||||||
return this.__eventInfo;
|
return this.__eventInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The first argument that was passed to the constructor, which contains information about the event.
|
||||||
|
*/
|
||||||
get args() {
|
get args() {
|
||||||
return this.event.args[0];
|
return this.event.args[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
get __eventType() { return null; }
|
/**
|
||||||
|
* The type of event.
|
||||||
|
*/
|
||||||
|
get __eventType() { return undefined; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,10 +12,16 @@ import Event from './event';
|
||||||
|
|
||||||
export default class SettingsUpdatedEvent extends Event {
|
export default class SettingsUpdatedEvent extends Event {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array of SettingUpdated events.
|
||||||
|
*/
|
||||||
get updatedSettings() {
|
get updatedSettings() {
|
||||||
return this.args.updatedSettings;
|
return this.args.updatedSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of event.
|
||||||
|
*/
|
||||||
get __eventType() {
|
get __eventType() {
|
||||||
return 'settings-updated';
|
return 'settings-updated';
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,38 +12,65 @@ import Event from './event';
|
||||||
|
|
||||||
export default class SettingUpdatedEvent extends Event {
|
export default class SettingUpdatedEvent extends Event {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The set containing the setting that was updated.
|
||||||
|
*/
|
||||||
get set() {
|
get set() {
|
||||||
return this.args.set;
|
return this.args.set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ID of the set containing the setting that was updated.
|
||||||
|
*/
|
||||||
get set_id() {
|
get set_id() {
|
||||||
return this.args.set.id;
|
return this.set.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The category containing the setting that was updated.
|
||||||
|
*/
|
||||||
get category() {
|
get category() {
|
||||||
return this.args.category;
|
return this.args.category;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ID of the category containing the setting that was updated.
|
||||||
|
*/
|
||||||
get category_id() {
|
get category_id() {
|
||||||
return this.args.category.id;
|
return this.category.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The setting that was updated.
|
||||||
|
*/
|
||||||
get setting() {
|
get setting() {
|
||||||
return this.args.setting;
|
return this.args.setting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ID of the setting that was updated.
|
||||||
|
*/
|
||||||
get setting_id() {
|
get setting_id() {
|
||||||
return this.args.setting.id;
|
return this.setting.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The setting's new value.
|
||||||
|
*/
|
||||||
get value() {
|
get value() {
|
||||||
return this.args.value;
|
return this.args.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The setting's old value.
|
||||||
|
*/
|
||||||
get old_value() {
|
get old_value() {
|
||||||
return this.args.old_value;
|
return this.args.old_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of event.
|
||||||
|
*/
|
||||||
get __eventType() {
|
get __eventType() {
|
||||||
return 'setting-updated';
|
return 'setting-updated';
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,3 +2,4 @@ export { default as SettingsSet } from './settingsset';
|
||||||
export { default as SettingsCategory } from './settingscategory';
|
export { default as SettingsCategory } from './settingscategory';
|
||||||
export { default as Setting } from './setting';
|
export { default as Setting } from './setting';
|
||||||
export { default as SettingsScheme } from './settingsscheme';
|
export { default as SettingsScheme } from './settingsscheme';
|
||||||
|
export * from './types';
|
||||||
|
|
|
@ -14,22 +14,29 @@ export default class MultipleChoiceOption {
|
||||||
|
|
||||||
constructor(args) {
|
constructor(args) {
|
||||||
this.args = args.args || args;
|
this.args = args.args || args;
|
||||||
|
|
||||||
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This option's ID.
|
||||||
|
*/
|
||||||
get id() {
|
get id() {
|
||||||
return this.args.id || this.value;
|
return this.args.id || this.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string describing this option.
|
||||||
|
*/
|
||||||
get text() {
|
get text() {
|
||||||
return this.args.text;
|
return this.args.text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value to return when this option is active.
|
||||||
|
*/
|
||||||
get value() {
|
get value() {
|
||||||
return this.args.value;
|
return this.args.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
clone() {
|
|
||||||
return new MultipleChoiceOption(Utils.deepclone(this.args));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,35 +15,49 @@ export default class SettingsScheme {
|
||||||
constructor(args) {
|
constructor(args) {
|
||||||
this.args = args.args || args;
|
this.args = args.args || args;
|
||||||
|
|
||||||
this.args.settings = this.settings.map(({ category, settings }) => ({
|
|
||||||
category, settings: settings.map(({ id, value }) => ({
|
|
||||||
id, value
|
|
||||||
}))
|
|
||||||
}));
|
|
||||||
|
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The scheme's ID.
|
||||||
|
*/
|
||||||
get id() {
|
get id() {
|
||||||
return this.args.id;
|
return this.args.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URL of the scheme's icon. This should be a base64 encoded data URI.
|
||||||
|
*/
|
||||||
get icon_url() {
|
get icon_url() {
|
||||||
return this.args.icon_url;
|
return this.args.icon_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The scheme's name.
|
||||||
|
*/
|
||||||
get name() {
|
get name() {
|
||||||
return this.args.name;
|
return this.args.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string to be displayed under the scheme.
|
||||||
|
*/
|
||||||
get hint() {
|
get hint() {
|
||||||
return this.args.hint;
|
return this.args.hint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array of stripped settings categories this scheme manages.
|
||||||
|
*/
|
||||||
get settings() {
|
get settings() {
|
||||||
return this.args.settings || [];
|
return this.args.settings || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this scheme's values are currently applied to a set.
|
||||||
|
* @param {SettingsSet} set The set to check
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
isActive(set) {
|
isActive(set) {
|
||||||
for (let schemeCategory of this.settings) {
|
for (let schemeCategory of this.settings) {
|
||||||
const category = set.categories.find(c => c.category === schemeCategory.category);
|
const category = set.categories.find(c => c.category === schemeCategory.category);
|
||||||
|
@ -66,12 +80,13 @@ export default class SettingsScheme {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies this scheme's values to a set.
|
||||||
|
* @param {SettingsSet} set The set to merge this scheme's values into
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
applyTo(set) {
|
applyTo(set) {
|
||||||
return set.merge({ settings: this.settings });
|
return set.merge(this);
|
||||||
}
|
|
||||||
|
|
||||||
clone() {
|
|
||||||
return new SettingsScheme(Utils.deepclone(this.args));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -446,7 +446,7 @@ export default class SettingsSet {
|
||||||
text: this.text,
|
text: this.text,
|
||||||
headertext: this.headertext,
|
headertext: this.headertext,
|
||||||
settings: this.categories.map(category => category.clone()),
|
settings: this.categories.map(category => category.clone()),
|
||||||
schemes: this.schemes.map(scheme => scheme.clone())
|
schemes: this.schemes
|
||||||
}, ...merge);
|
}, ...merge);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
export { default as BoolSetting } from './bool';
|
||||||
|
export { default as StringSetting } from './text';
|
||||||
|
export { default as NumberSetting } from './number';
|
||||||
|
export { default as DropdownSetting } from './dropdown';
|
||||||
|
export { default as RadioSetting } from './radio';
|
||||||
|
export { default as SliderSetting } from './slider';
|
||||||
|
export { default as ColourSetting } from './colour';
|
||||||
|
export { default as KeybindSetting } from './keybind';
|
||||||
|
export { default as FileSetting } from './file';
|
||||||
|
export { default as GuildSetting } from './guild';
|
||||||
|
export { default as ArraySetting } from './array';
|
||||||
|
export { default as CustomSetting } from './custom';
|
|
@ -11,18 +11,42 @@
|
||||||
import Setting from './basesetting';
|
import Setting from './basesetting';
|
||||||
import Combokeys from 'combokeys';
|
import Combokeys from 'combokeys';
|
||||||
|
|
||||||
|
let keybindsPaused = false;
|
||||||
|
|
||||||
export default class KeybindSetting extends Setting {
|
export default class KeybindSetting extends Setting {
|
||||||
|
|
||||||
constructor(args, ...merge) {
|
constructor(args, ...merge) {
|
||||||
super(args, ...merge);
|
super(args, ...merge);
|
||||||
|
|
||||||
|
this.__keybind_activated = this.__keybind_activated.bind(this);
|
||||||
|
|
||||||
this.combokeys = new Combokeys(document);
|
this.combokeys = new Combokeys(document);
|
||||||
this.combokeys.bind(this.value, event => this.emit('keybind-activated', event));
|
this.combokeys.bind(this.value, this.__keybind_activated);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value to use when the setting doesn't have a value.
|
||||||
|
*/
|
||||||
|
get defaultValue() {
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
setValueHook() {
|
setValueHook() {
|
||||||
this.combokeys.reset();
|
this.combokeys.reset();
|
||||||
this.combokeys.bind(this.value, event => this.emit('keybind-activated', event));
|
this.combokeys.bind(this.value, this.__keybind_activated);
|
||||||
|
}
|
||||||
|
|
||||||
|
__keybind_activated(event) {
|
||||||
|
if (KeybindSetting.paused) return;
|
||||||
|
this.emit('keybind-activated', event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get paused() {
|
||||||
|
return keybindsPaused;
|
||||||
|
}
|
||||||
|
|
||||||
|
static set paused(paused) {
|
||||||
|
keybindsPaused = paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,16 +25,21 @@
|
||||||
width: 16px;
|
width: 16px;
|
||||||
filter: brightness(10);
|
filter: brightness(10);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
.theme-light [class*="topSectionNormal-"] & {
|
}
|
||||||
|
|
||||||
|
.theme-light [class*="topSectionNormal-"] .bd-profile-badge-developer,
|
||||||
|
.theme-light [class*="topSectionNormal-"] .bd-profile-badge-contributor,
|
||||||
|
.theme-light .bd-message-badge-developer,
|
||||||
|
.theme-light .bd-message-badge-contributor {
|
||||||
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iQ2FscXVlXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjAwMCAyMDAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAyMDAwIDIwMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxnPjxwYXRoIGZpbGw9IiMzRTgyRTUiIGQ9Ik0xNDAyLjIsNjMxLjdjLTkuNy0zNTMuNC0yODYuMi00OTYtNjQyLjYtNDk2SDY4LjR2NzE0LjFsNDQyLDM5OFY0OTAuN2gyNTdjMjc0LjUsMCwyNzQuNSwzNDQuOSwwLDM0NC45SDU5Ny42djMyOS41aDE2OS44YzI3NC41LDAsMjc0LjUsMzQ0LjgsMCwzNDQuOGgtNjk5djM1NC45aDY5MS4yYzM1Ni4zLDAsNjMyLjgtMTQyLjYsNjQyLjYtNDk2YzAtMTYyLjYtNDQuNS0yODQuMS0xMjIuOS0zNjguNkMxMzU3LjcsOTE1LjgsMTQwMi4yLDc5NC4zLDE0MDIuMiw2MzEuN3oiLz48cGF0aCBmaWxsPSIjYmJiYmJiIiBkPSJNMTI2Mi41LDEzNS4yTDEyNjIuNSwxMzUuMmwtNzYuOCwwYzI2LjYsMTMuMyw1MS43LDI4LjEsNzUsNDQuM2M3MC43LDQ5LjEsMTI2LjEsMTExLjUsMTY0LjYsMTg1LjNjMzkuOSw3Ni42LDYxLjUsMTY1LjYsNjQuMywyNjQuNmwwLDEuMnYxLjJjMCwxNDEuMSwwLDU5Ni4xLDAsNzM3LjF2MS4ybDAsMS4yYy0yLjcsOTktMjQuMywxODgtNjQuMywyNjQuNmMtMzguNSw3My44LTkzLjgsMTM2LjItMTY0LjYsMTg1LjNjLTIyLjYsMTUuNy00Ni45LDMwLjEtNzIuNiw0My4xaDcyLjVjMzQ2LjIsMS45LDY3MS0xNzEuMiw2NzEtNTY3LjlWNzE2LjdDMTkzMy41LDMxMi4yLDE2MDguNywxMzUuMiwxMjYyLjUsMTM1LjJ6Ii8+PC9nPjwvc3ZnPg==');
|
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iQ2FscXVlXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjAwMCAyMDAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAyMDAwIDIwMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxnPjxwYXRoIGZpbGw9IiMzRTgyRTUiIGQ9Ik0xNDAyLjIsNjMxLjdjLTkuNy0zNTMuNC0yODYuMi00OTYtNjQyLjYtNDk2SDY4LjR2NzE0LjFsNDQyLDM5OFY0OTAuN2gyNTdjMjc0LjUsMCwyNzQuNSwzNDQuOSwwLDM0NC45SDU5Ny42djMyOS41aDE2OS44YzI3NC41LDAsMjc0LjUsMzQ0LjgsMCwzNDQuOGgtNjk5djM1NC45aDY5MS4yYzM1Ni4zLDAsNjMyLjgtMTQyLjYsNjQyLjYtNDk2YzAtMTYyLjYtNDQuNS0yODQuMS0xMjIuOS0zNjguNkMxMzU3LjcsOTE1LjgsMTQwMi4yLDc5NC4zLDE0MDIuMiw2MzEuN3oiLz48cGF0aCBmaWxsPSIjYmJiYmJiIiBkPSJNMTI2Mi41LDEzNS4yTDEyNjIuNSwxMzUuMmwtNzYuOCwwYzI2LjYsMTMuMyw1MS43LDI4LjEsNzUsNDQuM2M3MC43LDQ5LjEsMTI2LjEsMTExLjUsMTY0LjYsMTg1LjNjMzkuOSw3Ni42LDYxLjUsMTY1LjYsNjQuMywyNjQuNmwwLDEuMnYxLjJjMCwxNDEuMSwwLDU5Ni4xLDAsNzM3LjF2MS4ybDAsMS4yYy0yLjcsOTktMjQuMywxODgtNjQuMywyNjQuNmMtMzguNSw3My44LTkzLjgsMTM2LjItMTY0LjYsMTg1LjNjLTIyLjYsMTUuNy00Ni45LDMwLjEtNzIuNiw0My4xaDcyLjVjMzQ2LjIsMS45LDY3MS0xNzEuMiw2NzEtNTY3LjlWNzE2LjdDMTkzMy41LDMxMi4yLDE2MDguNywxMzUuMiwxMjYyLjUsMTM1LjJ6Ii8+PC9nPjwvc3ZnPg==');
|
||||||
filter: none;
|
filter: none;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.bd-message-badges-wrap {
|
.bd-message-badges-wrap {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 6px;
|
margin-left: 6px;
|
||||||
height: 11px;
|
height: 11px;
|
||||||
|
|
||||||
.bd-message-badge-developer,
|
.bd-message-badge-developer,
|
||||||
.bd-message-badge-contributor {
|
.bd-message-badge-contributor {
|
||||||
width: 12px;
|
width: 12px;
|
||||||
|
|
|
@ -52,12 +52,12 @@
|
||||||
|
|
||||||
.bd-settings-button-btn {
|
.bd-settings-button-btn {
|
||||||
background-image: $logoBigBw;
|
background-image: $logoBigBw;
|
||||||
|
background-size: 100% 100%;
|
||||||
filter: none;
|
filter: none;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
width: 130px;
|
width: 130px;
|
||||||
height: 80px;
|
height: 43px;
|
||||||
background-size: 100% 100%;
|
margin: 18px 0 17px 20px;
|
||||||
margin-left: 20px;
|
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,3 +4,4 @@
|
||||||
@import './card.scss';
|
@import './card.scss';
|
||||||
@import './tooltips.scss';
|
@import './tooltips.scss';
|
||||||
@import './settings-schemes.scss';
|
@import './settings-schemes.scss';
|
||||||
|
@import './updater.scss';
|
||||||
|
|
|
@ -19,18 +19,53 @@ bd-tooltips {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
z-index: 9001;
|
z-index: 9001;
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bd-tooltip:after {
|
.bd-tooltip-arrow {
|
||||||
border: 5px solid transparent;
|
border: 5px solid transparent;
|
||||||
content: " ";
|
content: " ";
|
||||||
height: 0;
|
height: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
width: 0;
|
width: 0;
|
||||||
border-top-color: #000;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -5px;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 100%;
|
}
|
||||||
|
|
||||||
|
&[x-placement^="top"] {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.bd-tooltip-arrow {
|
||||||
|
margin-left: -5px;
|
||||||
|
border-top-color: #000;
|
||||||
|
bottom: -10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[x-placement^="bottom"] {
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
|
.bd-tooltip-arrow {
|
||||||
|
border-width: 0 5px 5px 5px;
|
||||||
|
top: -5px;
|
||||||
|
border-bottom-color: #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[x-placement^="right"] {
|
||||||
|
margin-left: 10px;
|
||||||
|
|
||||||
|
.bd-tooltip-arrow {
|
||||||
|
border-width: 5px 5px 5px 0;
|
||||||
|
left: -5px;
|
||||||
|
border-right-color: #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[x-placement^="left"] {
|
||||||
|
margin-right: 10px;
|
||||||
|
|
||||||
|
.bd-tooltip-arrow {
|
||||||
|
border-width: 5px 0 5px 5px;
|
||||||
|
right: -5px;
|
||||||
|
border-left-color: #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
.bd-updaterview {
|
||||||
|
p {
|
||||||
|
margin: 0 0 10px;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,37 +8,14 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Events, WebpackModules, EventListener, ReactComponents, Renderer } from 'modules';
|
import { Events, WebpackModules, EventListener, DiscordApi, ReactComponents, Renderer } from 'modules';
|
||||||
|
import { ClientLogger as Logger } from 'common';
|
||||||
import Reflection from './reflection';
|
import Reflection from './reflection';
|
||||||
import DOM from './dom';
|
import DOM from './dom';
|
||||||
import VueInjector from './vueinjector';
|
import VueInjector from './vueinjector';
|
||||||
import EditedTimeStamp from './components/common/EditedTimestamp.vue';
|
import EditedTimeStamp from './components/common/EditedTimestamp.vue';
|
||||||
import Autocomplete from './components/common/Autocomplete.vue';
|
import Autocomplete from './components/common/Autocomplete.vue';
|
||||||
|
|
||||||
class TempApi {
|
|
||||||
static get currentGuildId() {
|
|
||||||
try {
|
|
||||||
return WebpackModules.getModuleByName('SelectedGuildStore').getGuildId();
|
|
||||||
} catch (err) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static get currentChannelId() {
|
|
||||||
try {
|
|
||||||
return WebpackModules.getModuleByName('SelectedChannelStore').getChannelId();
|
|
||||||
} catch (err) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static get currentUserId() {
|
|
||||||
try {
|
|
||||||
return WebpackModules.getModuleByName('UserStore').getCurrentUser().id;
|
|
||||||
} catch (err) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class extends EventListener {
|
export default class extends EventListener {
|
||||||
|
|
||||||
constructor(args) {
|
constructor(args) {
|
||||||
|
@ -54,22 +31,19 @@ export default class extends EventListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
get eventBindings() {
|
get eventBindings() {
|
||||||
return [{ id: 'gkh:keyup', callback: this.injectAutocomplete }];
|
|
||||||
/*
|
|
||||||
return [
|
return [
|
||||||
{ id: 'server-switch', callback: this.manipAll },
|
// { id: 'server-switch', callback: this.manipAll },
|
||||||
{ id: 'channel-switch', callback: this.manipAll },
|
// { id: 'channel-switch', callback: this.manipAll },
|
||||||
{ id: 'discord:MESSAGE_CREATE', callback: this.markupInjector },
|
// { id: 'discord:MESSAGE_CREATE', callback: this.markupInjector },
|
||||||
{ id: 'discord:MESSAGE_UPDATE', callback: this.markupInjector },
|
// { id: 'discord:MESSAGE_UPDATE', callback: this.markupInjector },
|
||||||
{ id: 'gkh:keyup', callback: this.injectAutocomplete }
|
{ id: 'gkh:keyup', callback: this.injectAutocomplete }
|
||||||
];
|
];
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
manipAll() {
|
manipAll() {
|
||||||
try {
|
try {
|
||||||
this.appMount.setAttribute('guild-id', TempApi.currentGuildId);
|
this.appMount.setAttribute('guild-id', DiscordApi.currentGuild.id);
|
||||||
this.appMount.setAttribute('channel-id', TempApi.currentChannelId);
|
this.appMount.setAttribute('channel-id', DiscordApi.currentChannel.id);
|
||||||
this.setIds();
|
this.setIds();
|
||||||
this.makeMutable();
|
this.makeMutable();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -128,13 +102,11 @@ export default class extends EventListener {
|
||||||
if (markup.ets) {
|
if (markup.ets) {
|
||||||
const etsRoot = document.createElement('span');
|
const etsRoot = document.createElement('span');
|
||||||
markup.clone.appendChild(etsRoot);
|
markup.clone.appendChild(etsRoot);
|
||||||
VueInjector.inject(
|
VueInjector.inject(etsRoot, {
|
||||||
etsRoot,
|
components: { EditedTimeStamp },
|
||||||
DOM.createElement('span', null, 'test'),
|
data: { ets: markup.ets },
|
||||||
{ EditedTimeStamp },
|
template: '<EditedTimeStamp :ets="ets" />'
|
||||||
`<EditedTimeStamp ets="${markup.ets}"/>`,
|
});
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Events.emit('ui:mutable:.markup', markup.clone);
|
Events.emit('ui:mutable:.markup', markup.clone);
|
||||||
|
@ -174,14 +146,14 @@ export default class extends EventListener {
|
||||||
const userTest = Reflection(msgGroup).prop('user');
|
const userTest = Reflection(msgGroup).prop('user');
|
||||||
if (!userTest) return;
|
if (!userTest) return;
|
||||||
msgGroup.setAttribute('data-author-id', userTest.id);
|
msgGroup.setAttribute('data-author-id', userTest.id);
|
||||||
if (userTest.id === TempApi.currentUserId) msgGroup.setAttribute('data-currentuser', true);
|
if (userTest.id === DiscordApi.currentUserId) msgGroup.setAttribute('data-currentuser', true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
msg.setAttribute('data-message-id', messageid);
|
msg.setAttribute('data-message-id', messageid);
|
||||||
const msgGroup = msg.closest('.message-group');
|
const msgGroup = msg.closest('.message-group');
|
||||||
if (!msgGroup) return;
|
if (!msgGroup) return;
|
||||||
msgGroup.setAttribute('data-author-id', authorid);
|
msgGroup.setAttribute('data-author-id', authorid);
|
||||||
if (authorid === TempApi.currentUserId) msgGroup.setAttribute('data-currentuser', true);
|
if (authorid === DiscordApi.currentUser.id) msgGroup.setAttribute('data-currentuser', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
setUserId(user) {
|
setUserId(user) {
|
||||||
|
@ -189,7 +161,7 @@ export default class extends EventListener {
|
||||||
const userid = Reflection(user).prop('user.id');
|
const userid = Reflection(user).prop('user.id');
|
||||||
if (!userid) return;
|
if (!userid) return;
|
||||||
user.setAttribute('data-user-id', userid);
|
user.setAttribute('data-user-id', userid);
|
||||||
const currentUser = userid === TempApi.currentUserId;
|
const currentUser = userid === DiscordApi.currentUser.id;
|
||||||
if (currentUser) user.setAttribute('data-currentuser', true);
|
if (currentUser) user.setAttribute('data-currentuser', true);
|
||||||
Events.emit('ui:useridset', user);
|
Events.emit('ui:useridset', user);
|
||||||
}
|
}
|
||||||
|
@ -214,12 +186,11 @@ export default class extends EventListener {
|
||||||
const parent = document.querySelector('[class*="channelTextArea"] > [class*="inner"]');
|
const parent = document.querySelector('[class*="channelTextArea"] > [class*="inner"]');
|
||||||
if (!parent) return;
|
if (!parent) return;
|
||||||
parent.append(root);
|
parent.append(root);
|
||||||
VueInjector.inject(
|
VueInjector.inject(root, {
|
||||||
root,
|
components: { Autocomplete },
|
||||||
DOM.createElement('span'),
|
data: { initial: e.target.value },
|
||||||
{ Autocomplete },
|
template: '<Autocomplete :initial="initial" />'
|
||||||
`<Autocomplete initial="${e.target.value}"/>`,
|
});
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,17 +8,22 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Events } from 'modules';
|
||||||
import { Utils } from 'common';
|
import { Utils } from 'common';
|
||||||
|
|
||||||
let items = 0;
|
let items = 0;
|
||||||
|
|
||||||
const BdMenuItems = new class {
|
export const BdMenuItems = new class {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
window.bdmenu = this;
|
window.bdmenu = this;
|
||||||
|
|
||||||
this.items = [];
|
this.items = [];
|
||||||
|
|
||||||
|
const updater = this.add({category: 'Updates', contentid: 'updater', text: 'Updates available!', hidden: true});
|
||||||
|
Events.on('update-check-end', () => updater.hidden = true);
|
||||||
|
Events.on('updates-available', () => updater.hidden = false);
|
||||||
|
|
||||||
this.addSettingsSet('Internal', 'core', 'Core');
|
this.addSettingsSet('Internal', 'core', 'Core');
|
||||||
this.addSettingsSet('Internal', 'ui', 'UI');
|
this.addSettingsSet('Internal', 'ui', 'UI');
|
||||||
this.addSettingsSet('Internal', 'emotes', 'Emotes');
|
this.addSettingsSet('Internal', 'emotes', 'Emotes');
|
||||||
|
@ -28,7 +33,14 @@ const BdMenuItems = new class {
|
||||||
this.add({category: 'External', contentid: 'themes', text: 'Themes'});
|
this.add({category: 'External', contentid: 'themes', text: 'Themes'});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an item to the menu.
|
||||||
|
* @param {Object} item The item to add to the menu
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
add(item) {
|
add(item) {
|
||||||
|
if (this.items.includes(item)) return item;
|
||||||
|
|
||||||
item.id = items++;
|
item.id = items++;
|
||||||
item.contentid = item.contentid || (items++ + '');
|
item.contentid = item.contentid || (items++ + '');
|
||||||
item.active = false;
|
item.active = false;
|
||||||
|
@ -39,6 +51,13 @@ const BdMenuItems = new class {
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a settings set to the menu.
|
||||||
|
* @param {String} category The category to display this item under
|
||||||
|
* @param {SettingsSet} set The settings set to display when this item is active
|
||||||
|
* @param {String} text The text to display in the menu (optional)
|
||||||
|
* @return {Object} The item that was added
|
||||||
|
*/
|
||||||
addSettingsSet(category, set, text) {
|
addSettingsSet(category, set, text) {
|
||||||
return this.add({
|
return this.add({
|
||||||
category, set,
|
category, set,
|
||||||
|
@ -46,16 +65,25 @@ const BdMenuItems = new class {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a Vue component to the menu.
|
||||||
|
* @param {String} category The category to display this item under
|
||||||
|
* @param {String} text The text to display in the menu
|
||||||
|
* @param {Object} component The Vue component to display when this item is active
|
||||||
|
* @return {Object} The item that was added
|
||||||
|
*/
|
||||||
addVueComponent(category, text, component) {
|
addVueComponent(category, text, component) {
|
||||||
return this.add({
|
return this.add({
|
||||||
category, text, component
|
category, text, component
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an item from the menu.
|
||||||
|
* @param {Object} item The item to remove from the menu
|
||||||
|
*/
|
||||||
remove(item) {
|
remove(item) {
|
||||||
Utils.removeFromArray(this.items, item);
|
Utils.removeFromArray(this.items, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export { BdMenuItems };
|
|
||||||
|
|
|
@ -8,48 +8,21 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Events, WebpackModules, DiscordApi } from 'modules';
|
||||||
|
import { Utils } from 'common';
|
||||||
|
import { remote } from 'electron';
|
||||||
import DOM from './dom';
|
import DOM from './dom';
|
||||||
import Vue from './vue';
|
import Vue from './vue';
|
||||||
import { BdSettingsWrapper } from './components';
|
|
||||||
import BdModals from './components/bd/BdModals.vue';
|
|
||||||
import { Events, WebpackModules } from 'modules';
|
|
||||||
import { Utils } from 'common';
|
|
||||||
import AutoManip from './automanip';
|
import AutoManip from './automanip';
|
||||||
import { remote } from 'electron';
|
import { BdSettingsWrapper, BdModals } from './components';
|
||||||
|
|
||||||
class TempApi {
|
|
||||||
static get currentGuild() {
|
|
||||||
try {
|
|
||||||
const currentGuildId = WebpackModules.getModuleByName('SelectedGuildStore').getGuildId();
|
|
||||||
return WebpackModules.getModuleByName('GuildStore').getGuild(currentGuildId);
|
|
||||||
} catch (err) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static get currentChannel() {
|
|
||||||
try {
|
|
||||||
const currentChannelId = WebpackModules.getModuleByName('SelectedChannelStore').getChannelId();
|
|
||||||
return WebpackModules.getModuleByName('ChannelStore').getChannel(currentChannelId);
|
|
||||||
} catch (err) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static get currentUserId() {
|
|
||||||
try {
|
|
||||||
return WebpackModules.getModuleByName('UserStore').getCurrentUser().id;
|
|
||||||
} catch (err) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class {
|
export default class {
|
||||||
|
|
||||||
static initUiEvents() {
|
static initUiEvents() {
|
||||||
this.pathCache = {
|
this.pathCache = {
|
||||||
isDm: null,
|
isDm: null,
|
||||||
server: TempApi.currentGuild,
|
server: DiscordApi.currentGuild,
|
||||||
channel: TempApi.currentChannel
|
channel: DiscordApi.currentChannel
|
||||||
};
|
};
|
||||||
window.addEventListener('keyup', e => Events.emit('gkh:keyup', e));
|
window.addEventListener('keyup', e => Events.emit('gkh:keyup', e));
|
||||||
this.autoManip = new AutoManip();
|
this.autoManip = new AutoManip();
|
||||||
|
@ -67,9 +40,9 @@ export default class {
|
||||||
if (!remote.BrowserWindow.getFocusedWindow()) return;
|
if (!remote.BrowserWindow.getFocusedWindow()) return;
|
||||||
clearInterval(ehookInterval);
|
clearInterval(ehookInterval);
|
||||||
remote.BrowserWindow.getFocusedWindow().webContents.on('did-navigate-in-page', (e, url, isMainFrame) => {
|
remote.BrowserWindow.getFocusedWindow().webContents.on('did-navigate-in-page', (e, url, isMainFrame) => {
|
||||||
const { currentGuild, currentChannel } = TempApi;
|
const { currentGuild, currentChannel } = DiscordApi;
|
||||||
if (!this.pathCache.server) {
|
if (!this.pathCache.server) {
|
||||||
Events.emit('server-switch', { 'server': currentGuild, 'channel': currentChannel });
|
Events.emit('server-switch', { server: currentGuild, channel: currentChannel });
|
||||||
this.pathCache.server = currentGuild;
|
this.pathCache.server = currentGuild;
|
||||||
this.pathCache.channel = currentChannel;
|
this.pathCache.channel = currentChannel;
|
||||||
return;
|
return;
|
||||||
|
@ -84,7 +57,7 @@ export default class {
|
||||||
currentGuild.id &&
|
currentGuild.id &&
|
||||||
this.pathCache.server &&
|
this.pathCache.server &&
|
||||||
this.pathCache.server.id !== currentGuild.id) {
|
this.pathCache.server.id !== currentGuild.id) {
|
||||||
Events.emit('server-switch', { 'server': currentGuild, 'channel': currentChannel });
|
Events.emit('server-switch', { server: currentGuild, channel: currentChannel });
|
||||||
this.pathCache.server = currentGuild;
|
this.pathCache.server = currentGuild;
|
||||||
this.pathCache.channel = currentChannel;
|
this.pathCache.channel = currentChannel;
|
||||||
return;
|
return;
|
||||||
|
@ -110,19 +83,19 @@ export default class {
|
||||||
DOM.createElement('div', null, 'bd-modals').appendTo(DOM.bdModals);
|
DOM.createElement('div', null, 'bd-modals').appendTo(DOM.bdModals);
|
||||||
DOM.createElement('bd-tooltips').appendTo(DOM.bdBody);
|
DOM.createElement('bd-tooltips').appendTo(DOM.bdBody);
|
||||||
|
|
||||||
const modals = new Vue({
|
this.modals = new Vue({
|
||||||
el: '#bd-modals',
|
el: '#bd-modals',
|
||||||
components: { BdModals },
|
components: { BdModals },
|
||||||
template: '<BdModals />'
|
template: '<BdModals />'
|
||||||
});
|
});
|
||||||
|
|
||||||
const vueInstance = new Vue({
|
this.vueInstance = new Vue({
|
||||||
el: '#bd-settings',
|
el: '#bd-settings',
|
||||||
components: { BdSettingsWrapper },
|
components: { BdSettingsWrapper },
|
||||||
template: '<BdSettingsWrapper />'
|
template: '<BdSettingsWrapper />'
|
||||||
});
|
});
|
||||||
|
|
||||||
return vueInstance;
|
return this.vueInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,13 +19,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Imports
|
// Imports
|
||||||
import { Events } from 'modules';
|
import { Events } from 'modules';
|
||||||
import { Modals } from 'ui';
|
import { Modals } from 'ui';
|
||||||
import { Modal } from '../common';
|
import { Modal } from './common';
|
||||||
import { MiError } from '../common/MaterialIcon';
|
import { MiError } from './common/MaterialIcon';
|
||||||
import ErrorModal from './modals/ErrorModal.vue';
|
import ErrorModal from './bd/modals/ErrorModal.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
|
@ -49,6 +49,7 @@
|
||||||
<CssEditorView v-if="item.contentid === 'css'" />
|
<CssEditorView v-if="item.contentid === 'css'" />
|
||||||
<PluginsView v-if="item.contentid === 'plugins'" />
|
<PluginsView v-if="item.contentid === 'plugins'" />
|
||||||
<ThemesView v-if="item.contentid === 'themes'" />
|
<ThemesView v-if="item.contentid === 'themes'" />
|
||||||
|
<UpdaterView v-if="item.contentid === 'updater'" />
|
||||||
</div>
|
</div>
|
||||||
</ContentColumn>
|
</ContentColumn>
|
||||||
</SidebarView>
|
</SidebarView>
|
||||||
|
@ -60,7 +61,7 @@
|
||||||
import { Settings } from 'modules';
|
import { Settings } from 'modules';
|
||||||
import { BdMenuItems } from 'ui';
|
import { BdMenuItems } from 'ui';
|
||||||
import { SidebarView, Sidebar, SidebarItem, ContentColumn } from './sidebar';
|
import { SidebarView, Sidebar, SidebarItem, ContentColumn } from './sidebar';
|
||||||
import { SettingsWrapper, SettingsPanel, CssEditorView, PluginsView, ThemesView } from './bd';
|
import { SettingsWrapper, SettingsPanel, CssEditorView, PluginsView, ThemesView, UpdaterView } from './bd';
|
||||||
import { SvgX, MiGithubCircle, MiWeb, MiClose, MiTwitterCircle } from './common';
|
import { SvgX, MiGithubCircle, MiWeb, MiClose, MiTwitterCircle } from './common';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -79,7 +80,7 @@
|
||||||
props: ['active', 'close'],
|
props: ['active', 'close'],
|
||||||
components: {
|
components: {
|
||||||
SidebarView, Sidebar, SidebarItem, ContentColumn,
|
SidebarView, Sidebar, SidebarItem, ContentColumn,
|
||||||
SettingsWrapper, SettingsPanel, CssEditorView, PluginsView, ThemesView,
|
SettingsWrapper, SettingsPanel, CssEditorView, PluginsView, ThemesView, UpdaterView,
|
||||||
MiGithubCircle, MiWeb, MiClose, MiTwitterCircle
|
MiGithubCircle, MiWeb, MiClose, MiTwitterCircle
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
@ -9,11 +9,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="{'bd-profile-badges-wrap': hasBadges == 'false'}">
|
<div :class="{'bd-profile-badges-wrap': !hasBadges}">
|
||||||
<div class="bd-profile-badges">
|
<div class="bd-profile-badges">
|
||||||
<div v-if="developer == 'true'" v-tooltip="'BetterDiscord Developer'" class="bd-profile-badge bd-profile-badge-developer" @click="onClick"></div>
|
<div v-if="developer" v-tooltip="'BetterDiscord Developer'" class="bd-profile-badge bd-profile-badge-developer" @click="onClick"></div>
|
||||||
<div v-else-if="webdev == 'true'" v-tooltip="'BetterDiscord Web Developer'" class="bd-profile-badge bd-profile-badge-developer" @click="onClick"></div>
|
<div v-else-if="webdev" v-tooltip="'BetterDiscord Web Developer'" class="bd-profile-badge bd-profile-badge-developer" @click="onClick"></div>
|
||||||
<div v-else-if="contributor == 'true'" v-tooltip="'BetterDiscord Contributor'" class="bd-profile-badge bd-profile-badge-contributor" @click="onClick"></div>
|
<div v-else-if="contributor" v-tooltip="'BetterDiscord Contributor'" class="bd-profile-badge bd-profile-badge-contributor" @click="onClick"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -10,9 +10,9 @@
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="bd-message-badges-wrap">
|
<div class="bd-message-badges-wrap">
|
||||||
<div v-if="developer == 'true'" v-tooltip="'BetterDiscord Developer'" class="bd-message-badge bd-message-badge-developer" @click="onClick"></div>
|
<div v-if="developer" v-tooltip="'BetterDiscord Developer'" class="bd-message-badge bd-message-badge-developer" @click="onClick"></div>
|
||||||
<div v-else-if="webdev == 'true'" v-tooltip="'BetterDiscord Web Developer'" class="bd-message-badge bd-message-badge-developer" @click="onClick"></div>
|
<div v-else-if="webdev" v-tooltip="'BetterDiscord Web Developer'" class="bd-message-badge bd-message-badge-developer" @click="onClick"></div>
|
||||||
<div v-else-if="contributor == 'true'" v-tooltip="'BetterDiscord Contributor'" class="bd-message-badge bd-message-badge-contributor" @click="onClick"></div>
|
<div v-else-if="contributor" v-tooltip="'BetterDiscord Contributor'" class="bd-message-badge bd-message-badge-contributor" @click="onClick"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
|
|
@ -11,9 +11,9 @@
|
||||||
<template>
|
<template>
|
||||||
<SettingsWrapper headertext="CSS Editor">
|
<SettingsWrapper headertext="CSS Editor">
|
||||||
<div class="bd-css-editor">
|
<div class="bd-css-editor">
|
||||||
<div v-if="CssEditor.error" class="bd-form-item">
|
<div v-if="error" class="bd-form-item">
|
||||||
<h5 style="margin-bottom: 10px;">Compiler error</h5>
|
<h5 style="margin-bottom: 10px;">Compiler error</h5>
|
||||||
<div class="bd-err bd-pre-wrap"><div class="bd-pre">{{ CssEditor.error.formatted }}</div></div>
|
<div class="bd-err bd-pre-wrap"><div class="bd-pre">{{ error.formatted }}</div></div>
|
||||||
<div class="bd-form-divider"></div>
|
<div class="bd-form-divider"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -21,16 +21,12 @@
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
// Imports
|
// Imports
|
||||||
|
import { ClientLogger as Logger } from 'common';
|
||||||
import { shell } from 'electron';
|
import { shell } from 'electron';
|
||||||
import Card from './Card.vue';
|
import Card from './Card.vue';
|
||||||
import { Button, ButtonGroup, SettingSwitch, MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension } from '../common';
|
import { Button, ButtonGroup, SettingSwitch, MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension } from '../common';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
settingsOpen: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
props: ['plugin', 'togglePlugin', 'reloadPlugin', 'deletePlugin', 'showSettings'],
|
props: ['plugin', 'togglePlugin', 'reloadPlugin', 'deletePlugin', 'showSettings'],
|
||||||
components: {
|
components: {
|
||||||
Card, Button, ButtonGroup, SettingSwitch, MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension
|
Card, Button, ButtonGroup, SettingSwitch, MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension
|
||||||
|
@ -38,9 +34,9 @@
|
||||||
methods: {
|
methods: {
|
||||||
editPlugin() {
|
editPlugin() {
|
||||||
try {
|
try {
|
||||||
shell.openItem(this.plugin.pluginPath);
|
shell.openItem(this.plugin.contentPath);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
Logger.err('PluginCard', [`Error opening plugin directory ${this.plugin.contentPath}:`, err]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,17 +37,19 @@
|
||||||
// Imports
|
// Imports
|
||||||
import { PluginManager } from 'modules';
|
import { PluginManager } from 'modules';
|
||||||
import { Modals } from 'ui';
|
import { Modals } from 'ui';
|
||||||
import { SettingsWrapper } from './';
|
import { ClientLogger as Logger } from 'common';
|
||||||
import PluginCard from './PluginCard.vue';
|
|
||||||
import { MiRefresh } from '../common';
|
import { MiRefresh } from '../common';
|
||||||
|
import SettingsWrapper from './SettingsWrapper.vue';
|
||||||
|
import PluginCard from './PluginCard.vue';
|
||||||
import RefreshBtn from '../common/RefreshBtn.vue';
|
import RefreshBtn from '../common/RefreshBtn.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
PluginManager,
|
||||||
local: true,
|
local: true,
|
||||||
localPlugins: PluginManager.localPlugins
|
localPlugins: PluginManager.localPlugins
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
SettingsWrapper, PluginCard,
|
SettingsWrapper, PluginCard,
|
||||||
|
@ -62,32 +64,32 @@
|
||||||
this.local = false;
|
this.local = false;
|
||||||
},
|
},
|
||||||
async refreshLocal() {
|
async refreshLocal() {
|
||||||
await PluginManager.refreshPlugins();
|
await this.PluginManager.refreshPlugins();
|
||||||
},
|
},
|
||||||
async refreshOnline() {
|
async refreshOnline() {
|
||||||
|
// TODO
|
||||||
},
|
},
|
||||||
async togglePlugin(plugin) {
|
async togglePlugin(plugin) {
|
||||||
// TODO Display error if plugin fails to start/stop
|
// TODO: display error if plugin fails to start/stop
|
||||||
|
const enabled = plugin.enabled;
|
||||||
try {
|
try {
|
||||||
await plugin.enabled ? PluginManager.stopPlugin(plugin) : PluginManager.startPlugin(plugin);
|
await enabled ? this.PluginManager.stopPlugin(plugin) : this.PluginManager.startPlugin(plugin);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
Logger.err('PluginsView', [`Error ${enabled ? 'stopp' : 'start'}ing plugin ${plugin.name}:`, err]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async reloadPlugin(plugin) {
|
async reloadPlugin(plugin) {
|
||||||
try {
|
try {
|
||||||
await PluginManager.reloadPlugin(plugin);
|
await this.PluginManager.reloadPlugin(plugin);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
Logger.err('PluginsView', [`Error reloading plugin ${plugin.name}:`, err]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async deletePlugin(plugin, unload) {
|
async deletePlugin(plugin, unload) {
|
||||||
try {
|
try {
|
||||||
if (unload) await PluginManager.unloadPlugin(plugin);
|
await unload ? this.PluginManager.unloadPlugin(plugin) : this.PluginManager.deletePlugin(plugin);
|
||||||
else await PluginManager.deletePlugin(plugin);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
Logger.err('PluginsView', [`Error ${unload ? 'unload' : 'delet'}ing plugin ${plugin.name}:`, err]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
showSettings(plugin, dont_clone) {
|
showSettings(plugin, dont_clone) {
|
||||||
|
|
|
@ -26,11 +26,6 @@
|
||||||
import { Button, ButtonGroup, SettingSwitch, MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension } from '../common';
|
import { Button, ButtonGroup, SettingSwitch, MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension } from '../common';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
settingsOpen: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
props: ['theme', 'toggleTheme', 'reloadTheme', 'deleteTheme', 'showSettings'],
|
props: ['theme', 'toggleTheme', 'reloadTheme', 'deleteTheme', 'showSettings'],
|
||||||
components: {
|
components: {
|
||||||
Card, Button, ButtonGroup, SettingSwitch, MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension
|
Card, Button, ButtonGroup, SettingSwitch, MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension
|
||||||
|
@ -40,7 +35,7 @@
|
||||||
try {
|
try {
|
||||||
shell.openItem(this.theme.themePath);
|
shell.openItem(this.theme.themePath);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
Logger.err('ThemeCard', [`Error opening theme directory ${this.theme.contentPath}:`, err]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,17 +37,19 @@
|
||||||
// Imports
|
// Imports
|
||||||
import { ThemeManager } from 'modules';
|
import { ThemeManager } from 'modules';
|
||||||
import { Modals } from 'ui';
|
import { Modals } from 'ui';
|
||||||
import { SettingsWrapper } from './';
|
import { ClientLogger as Logger } from 'common';
|
||||||
import { MiRefresh } from '../common';
|
import { MiRefresh } from '../common';
|
||||||
|
import SettingsWrapper from './SettingsWrapper.vue';
|
||||||
import ThemeCard from './ThemeCard.vue';
|
import ThemeCard from './ThemeCard.vue';
|
||||||
import RefreshBtn from '../common/RefreshBtn.vue';
|
import RefreshBtn from '../common/RefreshBtn.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
ThemeManager,
|
||||||
local: true,
|
local: true,
|
||||||
localThemes: ThemeManager.localThemes
|
localThemes: ThemeManager.localThemes
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
SettingsWrapper, ThemeCard,
|
SettingsWrapper, ThemeCard,
|
||||||
|
@ -62,33 +64,31 @@
|
||||||
this.local = false;
|
this.local = false;
|
||||||
},
|
},
|
||||||
async refreshLocal() {
|
async refreshLocal() {
|
||||||
await ThemeManager.refreshThemes();
|
await this.ThemeManager.refreshThemes();
|
||||||
},
|
},
|
||||||
async refreshOnline() {
|
async refreshOnline() {
|
||||||
|
// TODO
|
||||||
},
|
},
|
||||||
async toggleTheme(theme) {
|
async toggleTheme(theme) {
|
||||||
// TODO Display error if theme fails to enable/disable
|
// TODO: display error if theme fails to enable/disable
|
||||||
try {
|
try {
|
||||||
await theme.enabled ? ThemeManager.disableTheme(theme) : ThemeManager.enableTheme(theme);
|
await theme.enabled ? this.ThemeManager.disableTheme(theme) : this.ThemeManager.enableTheme(theme);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
Logger.err('ThemesView', [`Error ${enabled ? 'stopp' : 'start'}ing theme ${theme.name}:`, err]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async reloadTheme(theme, reload) {
|
async reloadTheme(theme, reload) {
|
||||||
try {
|
try {
|
||||||
if (reload) await ThemeManager.reloadTheme(theme);
|
await reload ? this.ThemeManager.reloadTheme(theme) : theme.recompile();
|
||||||
else await theme.recompile();
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
Logger.err('ThemesView', [`Error ${reload ? 'reload' : 'recompil'}ing theme ${theme.name}:`, err]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async deleteTheme(theme, unload) {
|
async deleteTheme(theme, unload) {
|
||||||
try {
|
try {
|
||||||
if (unload) await ThemeManager.unloadTheme(theme);
|
await unload ? this.ThemeManager.unloadTheme(theme) : this.ThemeManager.deleteTheme(theme);
|
||||||
else await ThemeManager.deleteTheme(theme);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
Logger.err('ThemesView', [`Error ${unload ? 'unload' : 'delet'}ing theme ${theme.name}:`, err]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
showSettings(theme, dont_clone) {
|
showSettings(theme, dont_clone) {
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
/**
|
||||||
|
* BetterDiscord Updater 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="Updates">
|
||||||
|
<div class="bd-flex bd-flex-col bd-updaterview">
|
||||||
|
<div v-if="error" class="bd-form-item">
|
||||||
|
<h5 style="margin-bottom: 10px;">Error installing updates</h5>
|
||||||
|
<div class="bd-err bd-pre-wrap"><div class="bd-pre">{{ error.formatted }}</div></div>
|
||||||
|
<div class="bd-form-divider"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-if="updatesAvailable">
|
||||||
|
<p>Version {{ newVersion }} is available. You are currently running version {{ currentVersion }}.</p>
|
||||||
|
<FormButton :onClick="install" :loading="updating">Install</FormButton>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<p>You're all up to date!</p>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</SettingsWrapper>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Globals, Updater } from 'modules';
|
||||||
|
import { ClientLogger as Logger } from 'common';
|
||||||
|
import SettingsWrapper from './SettingsWrapper.vue';
|
||||||
|
import { FormButton } from '../common';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentVersion: Globals.version,
|
||||||
|
updating: false,
|
||||||
|
updater: Updater
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
SettingsWrapper,
|
||||||
|
FormButton
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
updatesAvailable() {
|
||||||
|
return this.updater.updatesAvailable;
|
||||||
|
},
|
||||||
|
newVersion() {
|
||||||
|
return this.updater.latestVersion;
|
||||||
|
},
|
||||||
|
error() {
|
||||||
|
return this.updater.error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async install() {
|
||||||
|
this.updating = true;
|
||||||
|
try {
|
||||||
|
await this.updater.update();
|
||||||
|
} catch (err) {}
|
||||||
|
this.updating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -3,5 +3,6 @@ export { default as SettingsPanel } from './SettingsPanel.vue';
|
||||||
export { default as CssEditorView } from './CssEditor.vue';
|
export { default as CssEditorView } from './CssEditor.vue';
|
||||||
export { default as PluginsView } from './PluginsView.vue';
|
export { default as PluginsView } from './PluginsView.vue';
|
||||||
export { default as ThemesView } from './ThemesView.vue';
|
export { default as ThemesView } from './ThemesView.vue';
|
||||||
|
export { default as UpdaterView } from './UpdaterView.vue';
|
||||||
export { default as BdBadge } from './BdBadge.vue';
|
export { default as BdBadge } from './BdBadge.vue';
|
||||||
export { default as BdMessageBadge } from './BdMessageBadge.vue';
|
export { default as BdMessageBadge } from './BdMessageBadge.vue';
|
||||||
|
|
|
@ -24,8 +24,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { KeybindSetting } from 'structs';
|
||||||
|
import { ClientIPC, ClientLogger as Logger } from 'common';
|
||||||
import { shell } from 'electron';
|
import { shell } from 'electron';
|
||||||
import { ClientIPC } from 'common';
|
|
||||||
import Combokeys from 'combokeys';
|
import Combokeys from 'combokeys';
|
||||||
import CombokeysRecord from 'combokeys/plugins/record';
|
import CombokeysRecord from 'combokeys/plugins/record';
|
||||||
|
|
||||||
|
@ -49,6 +50,7 @@
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
active(active) {
|
active(active) {
|
||||||
|
KeybindSetting.paused = active;
|
||||||
if (active) combokeys.record(this.recorded);
|
if (active) combokeys.record(this.recorded);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -65,7 +67,7 @@
|
||||||
this.active = false;
|
this.active = false;
|
||||||
this.recordingValue = undefined;
|
this.recordingValue = undefined;
|
||||||
this.setting.value = sequence.join(' ');
|
this.setting.value = sequence.join(' ');
|
||||||
console.log('keypress', sequence);
|
Logger.log('Keybind', ['Recorded sequence', sequence]);
|
||||||
},
|
},
|
||||||
getDisplayString(value) {
|
getDisplayString(value) {
|
||||||
if (!value) return;
|
if (!value) return;
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
import { EmoteModule } from 'builtin';
|
import { EmoteModule } from 'builtin';
|
||||||
import { Events } from 'modules';
|
import { Events } from 'modules';
|
||||||
import { DOM } from 'ui';
|
import { DOM } from 'ui';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -44,7 +45,7 @@
|
||||||
open: false,
|
open: false,
|
||||||
selectedIndex: 0,
|
selectedIndex: 0,
|
||||||
sterm: ''
|
sterm: ''
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
props: ['initial'],
|
props: ['initial'],
|
||||||
beforeMount() {
|
beforeMount() {
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
export { default as BdSettingsWrapper } from './BdSettingsWrapper.vue';
|
export { default as BdSettingsWrapper } from './BdSettingsWrapper.vue';
|
||||||
export { default as BdSettings } from './BdSettings.vue';
|
export { default as BdSettings } from './BdSettings.vue';
|
||||||
|
export { default as BdModals } from './BdModals.vue';
|
||||||
|
|
|
@ -186,4 +186,5 @@ export default class DOM {
|
||||||
node.setAttribute(attribute.name, attribute.value);
|
node.setAttribute(attribute.name, attribute.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { EventListener } from 'modules';
|
||||||
import DOM from './dom';
|
import DOM from './dom';
|
||||||
import { BdBadge, BdMessageBadge } from './components/bd';
|
import { BdBadge, BdMessageBadge } from './components/bd';
|
||||||
import VueInjector from './vueinjector';
|
import VueInjector from './vueinjector';
|
||||||
|
import contributors from '../data/contributors';
|
||||||
|
|
||||||
export default class extends EventListener {
|
export default class extends EventListener {
|
||||||
|
|
||||||
|
@ -55,41 +56,37 @@ export default class extends EventListener {
|
||||||
if (msgGroup.dataset.hasBadges) return;
|
if (msgGroup.dataset.hasBadges) return;
|
||||||
msgGroup.setAttribute('data-has-badges', true);
|
msgGroup.setAttribute('data-has-badges', true);
|
||||||
if (!msgGroup.dataset.authorId) return;
|
if (!msgGroup.dataset.authorId) return;
|
||||||
const c = this.contributors.find(c => c.id === msgGroup.dataset.authorId);
|
const c = contributors.find(c => c.id === msgGroup.dataset.authorId);
|
||||||
if (!c) return;
|
if (!c) return;
|
||||||
const root = document.createElement('span');
|
const root = document.createElement('span');
|
||||||
const wrapperParent = msgGroup.querySelector('.username-wrapper').parentElement;
|
const usernameWrapper = msgGroup.querySelector('.username-wrapper');
|
||||||
|
if (!usernameWrapper) return;
|
||||||
|
const wrapperParent = usernameWrapper.parentElement;
|
||||||
if (!wrapperParent || wrapperParent.children.length < 2) return;
|
if (!wrapperParent || wrapperParent.children.length < 2) return;
|
||||||
wrapperParent.insertBefore(root, wrapperParent.children[1]);
|
wrapperParent.insertBefore(root, wrapperParent.children[1]);
|
||||||
const { developer, contributor, webdev } = c;
|
VueInjector.inject(root, {
|
||||||
VueInjector.inject(
|
components: { BdMessageBadge },
|
||||||
root,
|
data: { c },
|
||||||
DOM.createElement('div', null, 'bdmessagebadges'),
|
template: '<BdMessageBadge :developer="c.developer" :webdev="c.webdev" :contributor="c.contributor" />'
|
||||||
{ BdMessageBadge },
|
});
|
||||||
`<BdMessageBadge developer="${developer}" webdev="${webdev}" contributor="${contributor}"/>`,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
userlistBadge(e) {
|
userlistBadge(e) {
|
||||||
const c = this.contributors.find(c => c.id === e.dataset.userId);
|
const c = contributors.find(c => c.id === e.dataset.userId);
|
||||||
if (!c) return;
|
if (!c) return;
|
||||||
const memberUsername = e.querySelector('.member-username');
|
const memberUsername = e.querySelector('.member-username');
|
||||||
if (!memberUsername) return;
|
if (!memberUsername) return;
|
||||||
const root = document.createElement('span');
|
const root = document.createElement('span');
|
||||||
memberUsername.append(root);
|
memberUsername.append(root);
|
||||||
const { developer, contributor, webdev } = c;
|
VueInjector.inject(root, {
|
||||||
VueInjector.inject(
|
components: { BdMessageBadge },
|
||||||
root,
|
data: { c },
|
||||||
DOM.createElement('div', null, 'bdmessagebadges'),
|
template: '<BdMessageBadge :developer="c.developer" :webdev="c.webdev" :contributor="c.contributor" />'
|
||||||
{ BdMessageBadge },
|
});
|
||||||
`<BdMessageBadge developer="${developer}" webdev="${webdev}" contributor="${contributor}"/>`,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inject(userid) {
|
inject(userid) {
|
||||||
const c = this.contributors.find(c => c.id === userid);
|
const c = contributors.find(c => c.id === userid);
|
||||||
if (!c) return;
|
if (!c) return;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -101,29 +98,16 @@ export default class extends EventListener {
|
||||||
root = document.querySelector('[class*="headerInfo"]');
|
root = document.querySelector('[class*="headerInfo"]');
|
||||||
}
|
}
|
||||||
|
|
||||||
const { developer, contributor, webdev } = c;
|
VueInjector.inject(root, {
|
||||||
|
components: { BdBadge },
|
||||||
VueInjector.inject(
|
data: { hasBadges, c },
|
||||||
root,
|
template: '<BdBadge :hasBadges="hasBadges" :developer="c.developer" :webdev="c.webdev" :contributor="c.contributor" />',
|
||||||
DOM.createElement('div', null, 'bdprofilebadges'),
|
}, DOM.createElement('div', null, 'bdprofilebadges'));
|
||||||
{ BdBadge },
|
|
||||||
`<BdBadge hasBadges="${hasBadges}" developer="${developer}" webdev="${webdev}" contributor="${contributor}"/>`
|
|
||||||
);
|
|
||||||
}, 400);
|
}, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
get contributors() {
|
get contributors() {
|
||||||
return [
|
return contributors;
|
||||||
{ 'id': '81388395867156480', 'webdev': true, 'developer': true, 'contributor': true }, // Jiiks
|
|
||||||
{ 'id': '98003542823944192', 'webdev': false, 'developer': true, 'contributor': true }, // Pohky
|
|
||||||
{ 'id': '138850472541814784', 'webdev': true, 'developer': false, 'contributor': true }, // Hammock
|
|
||||||
{ 'id': '249746236008169473', 'webdev': false, 'developer': true, 'contributor': true }, // Zerebos
|
|
||||||
{ 'id': '125367412370440192', 'webdev': false, 'developer': true, 'contributor': true }, // Pierce
|
|
||||||
{ 'id': '284056145272766465', 'webdev': false, 'developer': false, 'contributor': true }, // Samuel Elliott
|
|
||||||
{ 'id': '184021060562321419', 'webdev': false, 'developer': false, 'contributor': true }, // Lilian Tedone
|
|
||||||
{ 'id': '76052829285916672', 'webdev': false, 'developer': false, 'contributor': true }, // samfun123
|
|
||||||
{ 'id': '171005991272316937', 'webdev': false, 'developer': false, 'contributor': true }, // samogot
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,9 @@ Vue.use(VTooltip, {
|
||||||
defaultContainer: 'bd-tooltips',
|
defaultContainer: 'bd-tooltips',
|
||||||
defaultClass: 'bd-tooltip',
|
defaultClass: 'bd-tooltip',
|
||||||
defaultTargetClass: 'bd-has-tooltip',
|
defaultTargetClass: 'bd-has-tooltip',
|
||||||
|
defaultArrowSelector: '.bd-tooltip-arrow',
|
||||||
defaultInnerSelector: '.bd-tooltip-inner',
|
defaultInnerSelector: '.bd-tooltip-inner',
|
||||||
defaultTemplate: '<div class="bd-tooltip"><span class="bd-tooltip-inner"></span></div>',
|
defaultTemplate: '<div class="bd-tooltip"><div class="bd-tooltip-arrow"></div><span class="bd-tooltip-inner"></span></div>',
|
||||||
defaultBoundariesElement: DOM.getElement('#app-mount')
|
defaultBoundariesElement: DOM.getElement('#app-mount')
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* BetterDiscord VUE Injector Module
|
* BetterDiscord Vue Injector Module
|
||||||
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
|
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
* https://betterdiscord.net
|
* https://betterdiscord.net
|
||||||
|
@ -12,14 +12,20 @@ import Vue from './vue';
|
||||||
|
|
||||||
export default class {
|
export default class {
|
||||||
|
|
||||||
static inject(root, bdnode, components, template, replaceRoot) {
|
/**
|
||||||
if(!replaceRoot) bdnode.appendTo(root);
|
* Creates a new Vue object and mounts it in the passed element.
|
||||||
|
* @param {HTMLElement} root The element to mount the new Vue object at
|
||||||
|
* @param {Object} options Options to pass to Vue
|
||||||
|
* @param {BdNode} bdnode The element to append
|
||||||
|
* @return {Vue}
|
||||||
|
*/
|
||||||
|
static inject(root, options, bdnode) {
|
||||||
|
if(bdnode) bdnode.appendTo(root);
|
||||||
|
|
||||||
return new Vue({
|
const vue = new Vue(options);
|
||||||
el: replaceRoot ? root : bdnode.element,
|
|
||||||
components,
|
vue.$mount(bdnode ? bdnode.element : root);
|
||||||
template
|
return vue;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
const
|
const path = require('path');
|
||||||
path = require('path'),
|
const webpack = require('webpack');
|
||||||
webpack = require('webpack');
|
|
||||||
|
|
||||||
const jsLoader = {
|
const jsLoader = {
|
||||||
test: /\.(js|jsx)$/,
|
test: /\.(js|jsx)$/,
|
||||||
|
@ -9,18 +8,18 @@ const jsLoader = {
|
||||||
query: {
|
query: {
|
||||||
presets: ['react']
|
presets: ['react']
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const vueLoader = {
|
const vueLoader = {
|
||||||
test: /\.(vue)$/,
|
test: /\.(vue)$/,
|
||||||
loader: 'vue-loader'
|
loader: 'vue-loader'
|
||||||
}
|
};
|
||||||
|
|
||||||
const scssLoader = {
|
const scssLoader = {
|
||||||
test: /\.scss$/,
|
test: /\.scss$/,
|
||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
loader: ['css-loader', 'sass-loader']
|
loader: ['css-loader', 'sass-loader']
|
||||||
}
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: './src/index.js',
|
entry: './src/index.js',
|
||||||
|
@ -32,9 +31,11 @@ module.exports = {
|
||||||
loaders: [jsLoader, vueLoader, scssLoader]
|
loaders: [jsLoader, vueLoader, scssLoader]
|
||||||
},
|
},
|
||||||
externals: {
|
externals: {
|
||||||
'electron': 'window.require("electron")',
|
electron: 'window.require("electron")',
|
||||||
'fs': 'window.require("fs")',
|
fs: 'window.require("fs")',
|
||||||
'path': 'window.require("path")'
|
path: 'window.require("path")',
|
||||||
|
node_utils: 'window.require("util")',
|
||||||
|
sparkplug: 'require("../../core/dist/sparkplug")'
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
@ -49,15 +50,10 @@ module.exports = {
|
||||||
path.resolve('src', 'structs'),
|
path.resolve('src', 'structs'),
|
||||||
path.resolve('src', 'builtin')
|
path.resolve('src', 'builtin')
|
||||||
]
|
]
|
||||||
}
|
|
||||||
/* resolve: {
|
|
||||||
alias: {
|
|
||||||
'momentjs': 'vendor/moment.min.js'
|
|
||||||
},
|
},
|
||||||
modules: [
|
node: {
|
||||||
path.resolve('./node_modules'),
|
process: false,
|
||||||
path.resolve(__dirname, '..'),
|
__filename: false,
|
||||||
path.resolve(__dirname, '..', 'node_modules')
|
__dirname: false
|
||||||
]
|
}
|
||||||
}*/
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
const path = require('path');
|
||||||
|
const webpack = require('webpack');
|
||||||
|
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
|
||||||
|
|
||||||
|
const jsLoader = {
|
||||||
|
test: /\.(js|jsx)$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
loader: 'babel-loader',
|
||||||
|
query: {
|
||||||
|
presets: ['react']
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const vueLoader = {
|
||||||
|
test: /\.(vue)$/,
|
||||||
|
loader: 'vue-loader'
|
||||||
|
};
|
||||||
|
|
||||||
|
const scssLoader = {
|
||||||
|
test: /\.scss$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
loader: ['css-loader', 'sass-loader']
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: './src/index.js',
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
filename: 'betterdiscord.client-release.js'
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
loaders: [jsLoader, vueLoader, scssLoader]
|
||||||
|
},
|
||||||
|
externals: {
|
||||||
|
electron: 'window.require("electron")',
|
||||||
|
fs: 'window.require("fs")',
|
||||||
|
path: 'window.require("path")',
|
||||||
|
node_utils: 'window.require("util")',
|
||||||
|
sparkplug: 'require("./sparkplug")'
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
vue$: path.resolve('..', 'node_modules', 'vue', 'dist', 'vue.esm.js')
|
||||||
|
},
|
||||||
|
modules: [
|
||||||
|
path.resolve('..', 'node_modules'),
|
||||||
|
path.resolve('..', 'common', 'modules'),
|
||||||
|
path.resolve('src', 'modules'),
|
||||||
|
path.resolve('src', 'ui'),
|
||||||
|
path.resolve('src', 'plugins'),
|
||||||
|
path.resolve('src', 'structs'),
|
||||||
|
path.resolve('src', 'builtin')
|
||||||
|
]
|
||||||
|
},
|
||||||
|
node: {
|
||||||
|
process: false,
|
||||||
|
__filename: false,
|
||||||
|
__dirname: false
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
PRODUCTION: JSON.stringify(true)
|
||||||
|
}),
|
||||||
|
new UglifyJsPlugin()
|
||||||
|
]
|
||||||
|
};
|
|
@ -8,51 +8,155 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { ipcRenderer, ipcMain } = require('electron');
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
export class ClientIPC {
|
const callbacks = new WeakMap();
|
||||||
static on(channel, cb) {
|
|
||||||
ipcRenderer.on(channel, (event, message) => cb(event, message));
|
const ClientIPC = new class ClientIPC {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.on('ping', () => 'pong', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async send(channel, message) {
|
/**
|
||||||
|
* Adds an IPC event listener.
|
||||||
|
* @param {String} channel The channel to listen on
|
||||||
|
* @param {Function} callback A function that will be called when a message is received
|
||||||
|
* @param {Boolean} reply Whether to automatically reply to the message with the callback's return value
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
on(channel, callback, reply) {
|
||||||
channel = channel.startsWith('bd-') ? channel : `bd-${channel}`;
|
channel = channel.startsWith('bd-') ? channel : `bd-${channel}`;
|
||||||
const __eid = Date.now().toString();
|
|
||||||
ipcRenderer.send(channel, Object.assign(message ? message : {}, { __eid }));
|
const boundCallback = async (event, args) => {
|
||||||
return new Promise((resolve, reject) => {
|
const ipcevent = new BDIpcEvent(event, args);
|
||||||
ipcRenderer.once(__eid, (event, arg) => {
|
try {
|
||||||
if (arg.err) {
|
const r = callback(ipcevent, ipcevent.message);
|
||||||
return reject(arg.err);
|
if (reply) ipcevent.reply(await r);
|
||||||
}
|
} catch (err) {
|
||||||
resolve(arg);
|
console.error('Error in IPC callback:', err);
|
||||||
});
|
if (reply) ipcevent.reject(err);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
callbacks.set(callback, boundCallback);
|
||||||
|
ipcRenderer.on(channel, boundCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
off(channel, callback) {
|
||||||
|
ipcRenderer.removeListener(channel, callbacks.get(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a message to the main process and returns a promise that is resolved when the main process replies.
|
||||||
|
* @param {String} channel The channel to send a message to
|
||||||
|
* @param {Any} message Data to send to the main process
|
||||||
|
* @param {Boolean} error Whether to mark the message as an error
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
async send(channel, message, error) {
|
||||||
|
channel = channel.startsWith('bd-') ? channel : `bd-${channel}`;
|
||||||
|
|
||||||
|
const eid = 'bd-' + Date.now().toString();
|
||||||
|
ipcRenderer.send(channel, { eid, message, error });
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
ipcRenderer.once(eid, (event, arg) => {
|
||||||
|
if (arg.error) reject(arg.message);
|
||||||
|
else resolve(arg.message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a message to the Discord window and returns a promise that is resolved when it replies.
|
||||||
|
* @param {String} channel The channel to send a message to
|
||||||
|
* @param {Any} message Data to send to the renderer process
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
sendToDiscord(channel, message) {
|
||||||
|
return this.send('bd-sendToDiscord', {
|
||||||
|
channel, message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a message to the CSS editor window and returns a promise that is resolved when it replies.
|
||||||
|
* @param {String} channel The channel to send a message to
|
||||||
|
* @param {Any} message Data to send to the CSS editor window
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
sendToCssEditor(channel, message) {
|
||||||
|
return this.send('bd-sendToCssEditor', {
|
||||||
|
channel, message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ping() {
|
||||||
|
return this.send('ping');
|
||||||
|
}
|
||||||
|
|
||||||
|
getConfig() {
|
||||||
|
return this.send('getConfig');
|
||||||
|
}
|
||||||
|
|
||||||
|
showOpenDialog(options) {
|
||||||
|
return this.send('native-open', options);
|
||||||
|
}
|
||||||
|
|
||||||
|
compileSass(options) {
|
||||||
|
return this.send('compileSass', options);
|
||||||
|
}
|
||||||
|
|
||||||
|
dba(command) {
|
||||||
|
return this.send('dba', command);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ClientIPC;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An IPC event.
|
||||||
|
*/
|
||||||
class BDIpcEvent {
|
class BDIpcEvent {
|
||||||
|
|
||||||
constructor(event, args) {
|
constructor(event, args) {
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.ipcEvent = event;
|
this.ipcEvent = event;
|
||||||
|
this.replied = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bindings() {
|
bindings() {
|
||||||
this.send = this.send.bind(this);
|
|
||||||
this.reply = this.reply.bind(this);
|
this.reply = this.reply.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
send(message) {
|
/**
|
||||||
this.ipcEvent.sender.send(this.args.__eid, message);
|
* Sends a message back to the message's sender.
|
||||||
|
* @param {Any} message Data to send to this message's sender
|
||||||
|
*/
|
||||||
|
get send() { return this.reply }
|
||||||
|
reply(message, error) {
|
||||||
|
if (this.replied)
|
||||||
|
throw {message: 'This message has already been replied to.'};
|
||||||
|
|
||||||
|
this.replied = true;
|
||||||
|
return ClientIPC.send(this.eid, message, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
reply(message) {
|
reject(err) {
|
||||||
this.send(message);
|
return this.reply(err, true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CoreIPC {
|
get message() {
|
||||||
static on(channel, cb) {
|
return this.args.message;
|
||||||
ipcMain.on(channel, (event, args) => cb(new BDIpcEvent(event, args)));
|
}
|
||||||
|
|
||||||
|
get error() {
|
||||||
|
return this.args.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
get eid() {
|
||||||
|
return this.args.eid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export { ClientIPC } from './bdipc';
|
export { default as ClientIPC } from './bdipc';
|
||||||
export * from './utils';
|
export * from './utils';
|
||||||
export { ClientLogger } from './logger';
|
export { ClientLogger } from './logger';
|
||||||
export { default as AsyncEventEmitter } from './async-eventemitter';
|
export { default as AsyncEventEmitter } from './async-eventemitter';
|
||||||
|
|
|
@ -9,47 +9,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Vendor } from 'modules';
|
import { Vendor } from 'modules';
|
||||||
|
import { FileUtils } from './utils';
|
||||||
|
import node_utils from 'node_utils';
|
||||||
|
|
||||||
const logs = [];
|
export const logLevels = {
|
||||||
|
|
||||||
export class ClientLogger {
|
|
||||||
|
|
||||||
static err(module, message) { this.log(module, message, 'err'); }
|
|
||||||
static warn(module, message) { this.log(module, message, 'warn'); }
|
|
||||||
static info(module, message) { this.log(module, message, 'info'); }
|
|
||||||
static dbg(module, message) { this.log(module, message, 'dbg'); }
|
|
||||||
|
|
||||||
static log(module, message, level = 'log') {
|
|
||||||
level = this.parseLevel(level);
|
|
||||||
if (typeof message === 'object' && !(message instanceof Array)) {
|
|
||||||
console[level]('[%cBetter%cDiscord:%s]', 'color: #3E82E5', '', `${module}${level === 'debug' ? '|DBG' : ''}`, message);
|
|
||||||
let message_string = message.toString();
|
|
||||||
if (message_string === '[object Object]')
|
|
||||||
message_string += ' ' + JSON.stringify(message, null, 4);
|
|
||||||
|
|
||||||
logs.push(`${level.toUpperCase()} : [${Vendor.moment().format('DD/MM/YY hh:mm:ss')}|${module}] ${message_string}${message_string === '[object Object]' ? ' ' + JSON.stringify(message, null, 4) : ''}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
message = typeof message === 'object' && message instanceof Array ? message : [message];
|
|
||||||
console[level]('[%cBetter%cDiscord:%s]', 'color: #3E82E5', '', `${module}${level === 'debug' ? '|DBG' : ''}`, ...message);
|
|
||||||
logs.push(`${level.toUpperCase()} : [${Vendor.moment().format('DD/MM/YY hh:mm:ss')}|${module}] ${message.join(' ')}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
static logError(err) {
|
|
||||||
if (!err.module && !err.message) {
|
|
||||||
console.log(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.err(err.module, err.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
static get logs() {
|
|
||||||
return logs;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get levels() {
|
|
||||||
return {
|
|
||||||
'log': 'log',
|
'log': 'log',
|
||||||
'warn': 'warn',
|
'warn': 'warn',
|
||||||
'err': 'error',
|
'err': 'error',
|
||||||
|
@ -58,10 +21,48 @@ export class ClientLogger {
|
||||||
'dbg': 'debug',
|
'dbg': 'debug',
|
||||||
'info': 'info'
|
'info': 'info'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default class Logger {
|
||||||
|
|
||||||
|
constructor(file) {
|
||||||
|
this.logs = [];
|
||||||
|
this.file = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
err(module, message) { this.log(module, message, 'err'); }
|
||||||
|
warn(module, message) { this.log(module, message, 'warn'); }
|
||||||
|
info(module, message) { this.log(module, message, 'info'); }
|
||||||
|
dbg(module, message) { this.log(module, message, 'dbg'); }
|
||||||
|
|
||||||
|
log(module, message, level = 'log') {
|
||||||
|
level = Logger.parseLevel(level);
|
||||||
|
|
||||||
|
message = typeof message === 'object' && message instanceof Array ? message : [message];
|
||||||
|
console[level]('[%cBetter%cDiscord:%s]', 'color: #3E82E5', '', `${module}${level === 'debug' ? '|DBG' : ''}`, ...message);
|
||||||
|
|
||||||
|
const message_string = message.map(m => typeof m === 'string' ? m : node_utils.inspect(m)).join(' ');
|
||||||
|
this.logs.push(`${level.toUpperCase()} : [${Logger.timestamp}|${module}] ${message_string}`);
|
||||||
|
|
||||||
|
if (this.file)
|
||||||
|
FileUtils.appendToFile(this.file, `${level.toUpperCase()} : [${Logger.timestamp}|${module}] ${message_string}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
logError(err) {
|
||||||
|
if (!err.module && !err.message) {
|
||||||
|
console.log(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.err(err.module, err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
static parseLevel(level) {
|
static parseLevel(level) {
|
||||||
return this.levels.hasOwnProperty(level) ? this.levels[level] : 'log';
|
return logLevels.hasOwnProperty(level) ? logLevels[level] : 'log';
|
||||||
|
}
|
||||||
|
|
||||||
|
static get timestamp() {
|
||||||
|
return Vendor.moment().format('DD/MM/YY hh:mm:ss');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ClientLogger = new Logger();
|
||||||
|
|
|
@ -8,19 +8,13 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const
|
|
||||||
path = require('path'),
|
|
||||||
fs = require('fs'),
|
|
||||||
_ = require('lodash');
|
|
||||||
|
|
||||||
import { PatchedFunction, Patch } from './monkeypatch';
|
import { PatchedFunction, Patch } from './monkeypatch';
|
||||||
import { Vendor } from 'modules';
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import _ from 'lodash';
|
||||||
import filetype from 'file-type';
|
import filetype from 'file-type';
|
||||||
|
|
||||||
export class Utils {
|
export class Utils {
|
||||||
static isArrowFunction(fn) {
|
|
||||||
return !fn.toString().startsWith('function');
|
|
||||||
}
|
|
||||||
static overload(fn, cb) {
|
static overload(fn, cb) {
|
||||||
const orig = fn;
|
const orig = fn;
|
||||||
return function (...args) {
|
return function (...args) {
|
||||||
|
@ -31,6 +25,10 @@ export class Utils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Monkey-patches an object's method.
|
* Monkey-patches an object's method.
|
||||||
|
* @param {Object} object The object containing the function to monkey patch
|
||||||
|
* @param {String} methodName The name of the method to monkey patch
|
||||||
|
* @param {Object|String|Function} options Options to pass to the Patch constructor
|
||||||
|
* @param {Function} function If {options} is either "before" or "after", this function will be used as that hook
|
||||||
*/
|
*/
|
||||||
static monkeyPatch(object, methodName, options, f) {
|
static monkeyPatch(object, methodName, options, f) {
|
||||||
const patchedFunction = new PatchedFunction(object, methodName);
|
const patchedFunction = new PatchedFunction(object, methodName);
|
||||||
|
@ -41,12 +39,31 @@ export class Utils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Monkey-patches an object's method and returns a promise that will be resolved with the data object when the method is called.
|
* Monkey-patches an object's method and returns a promise that will be resolved with the data object when the method is called.
|
||||||
* You will have to call data.callOriginalMethod() if it wants the original method to be called.
|
* This can only be used to get the arguments and return data. If you want to change anything, call Utils.monkeyPatch with the once option set to true.
|
||||||
*/
|
*/
|
||||||
static monkeyPatchOnce(object, methodName) {
|
static monkeyPatchOnce(object, methodName) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.monkeyPatch(object, methodName, 'after', data => {
|
||||||
|
data.patch.cancel();
|
||||||
|
resolve(data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Monkey-patches an object's method and returns a promise that will be resolved with the data object when the method is called.
|
||||||
|
* You will have to call data.callOriginalMethod() if you wants the original method to be called.
|
||||||
|
*/
|
||||||
|
static monkeyPatchAsync(object, methodName, callback) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.monkeyPatch(object, methodName, data => {
|
this.monkeyPatch(object, methodName, data => {
|
||||||
data.patch.cancel();
|
data.patch.cancel();
|
||||||
|
|
||||||
|
data.promise = data.return = callback ? Promise.all(callback.call(global, data, ...data.arguments)) : new Promise((resolve, reject) => {
|
||||||
|
data.resolve = resolve;
|
||||||
|
data.reject = reject;
|
||||||
|
});
|
||||||
|
|
||||||
resolve(data);
|
resolve(data);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -81,15 +98,20 @@ export class Utils {
|
||||||
};
|
};
|
||||||
|
|
||||||
const patch = this.monkeyPatch(what, methodName, {
|
const patch = this.monkeyPatch(what, methodName, {
|
||||||
before: before ? compatible_function(before) : undefined,
|
before: !instead && before ? compatible_function(before) : undefined,
|
||||||
instead: instead ? compatible_function(instead) : undefined,
|
instead: instead ? compatible_function(instead) : undefined,
|
||||||
after: after ? compatible_function(after) : undefined,
|
after: !instead && after ? compatible_function(after) : undefined,
|
||||||
once
|
once
|
||||||
});
|
});
|
||||||
|
|
||||||
return cancelPatch;
|
return cancelPatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to parse a string as JSON.
|
||||||
|
* @param {String} json The string to parse
|
||||||
|
* @return {Any}
|
||||||
|
*/
|
||||||
static async tryParseJson(jsonString) {
|
static async tryParseJson(jsonString) {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(jsonString);
|
return JSON.parse(jsonString);
|
||||||
|
@ -101,6 +123,11 @@ export class Utils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new object with normalised keys.
|
||||||
|
* @param {Object} object
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
static toCamelCase(o) {
|
static toCamelCase(o) {
|
||||||
const camelCased = {};
|
const camelCased = {};
|
||||||
_.forEach(o, (value, key) => {
|
_.forEach(o, (value, key) => {
|
||||||
|
@ -112,17 +139,20 @@ export class Utils {
|
||||||
return camelCased;
|
return camelCased;
|
||||||
}
|
}
|
||||||
|
|
||||||
static compare(value1, value2) {
|
/**
|
||||||
|
* Checks if two or more values contain the same data.
|
||||||
|
* @param {Any} ...value The value to compare
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
static compare(value1, value2, ...values) {
|
||||||
// Check to see if value1 and value2 contain the same data
|
// Check to see if value1 and value2 contain the same data
|
||||||
if (typeof value1 !== typeof value2) return false;
|
if (typeof value1 !== typeof value2) return false;
|
||||||
if (value1 === null && value2 === null) return true;
|
if (value1 === null && value2 === null) return true;
|
||||||
if (value1 === null || value2 === null) return false;
|
if (value1 === null || value2 === null) return false;
|
||||||
|
|
||||||
if (typeof value1 === 'object' || typeof value1 === 'array') {
|
if (typeof value1 === 'object') {
|
||||||
// Loop through the object and check if everything's the same
|
// Loop through the object and check if everything's the same
|
||||||
let value1array = typeof value1 === 'array' ? value1 : Object.keys(value1);
|
if (Object.keys(value1).length !== Object.keys(value2).length) return false;
|
||||||
let value2array = typeof value2 === 'array' ? value2 : Object.keys(value2);
|
|
||||||
if (value1array.length !== value2array.length) return false;
|
|
||||||
|
|
||||||
for (let key in value1) {
|
for (let key in value1) {
|
||||||
if (!this.compare(value1[key], value2[key])) return false;
|
if (!this.compare(value1[key], value2[key])) return false;
|
||||||
|
@ -130,9 +160,20 @@ export class Utils {
|
||||||
} else if (value1 !== value2) return false;
|
} else if (value1 !== value2) return false;
|
||||||
|
|
||||||
// value1 and value2 contain the same data
|
// value1 and value2 contain the same data
|
||||||
|
// Check any more values
|
||||||
|
for (let value3 of values) {
|
||||||
|
if (!this.compare(value1, value3))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones an object and all it's properties.
|
||||||
|
* @param {Any} value The value to clone
|
||||||
|
* @return {Any} The cloned value
|
||||||
|
*/
|
||||||
static deepclone(value) {
|
static deepclone(value) {
|
||||||
if (typeof value === 'object') {
|
if (typeof value === 'object') {
|
||||||
if (value instanceof Array) return value.map(i => this.deepclone(i));
|
if (value instanceof Array) return value.map(i => this.deepclone(i));
|
||||||
|
@ -149,6 +190,11 @@ export class Utils {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Freezes an object and all it's properties.
|
||||||
|
* @param {Any} object The object to freeze
|
||||||
|
* @param {Function} exclude A function to filter object that shouldn't be frozen
|
||||||
|
*/
|
||||||
static deepfreeze(object, exclude) {
|
static deepfreeze(object, exclude) {
|
||||||
if (exclude && exclude(object)) return;
|
if (exclude && exclude(object)) return;
|
||||||
|
|
||||||
|
@ -165,38 +211,57 @@ export class Utils {
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
static filterArray(array, filter) {
|
/**
|
||||||
const indexes = [];
|
* Removes an item from an array. This differs from Array.prototype.filter as it mutates the original array instead of creating a new one.
|
||||||
for (let index in array) {
|
* @param {Array} array The array to filter
|
||||||
if (!filter(array[index], index))
|
* @param {Any} item The item to remove from the array
|
||||||
indexes.push(index);
|
* @return {Array}
|
||||||
}
|
*/
|
||||||
|
|
||||||
for (let i in indexes)
|
|
||||||
array.splice(indexes[i] - i, 1);
|
|
||||||
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
static removeFromArray(array, item) {
|
static removeFromArray(array, item) {
|
||||||
let index;
|
let index;
|
||||||
while ((index = array.indexOf(item)) > -1)
|
while ((index = array.indexOf(item)) > -1)
|
||||||
array.splice(index, 1);
|
array.splice(index, 1);
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a property with a getter that can be changed like a normal property.
|
||||||
|
* @param {Object} object The object to define a property on
|
||||||
|
* @param {String} property The property to define
|
||||||
|
* @param {Function} getter The property's getter
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
|
static defineSoftGetter(object, property, get) {
|
||||||
|
return Object.defineProperty(object, property, {
|
||||||
|
get,
|
||||||
|
set: value => Object.defineProperty(object, property, {
|
||||||
|
value,
|
||||||
|
writable: true,
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true
|
||||||
|
}),
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FileUtils {
|
export class FileUtils {
|
||||||
|
/**
|
||||||
|
* Checks if a file exists and is a file.
|
||||||
|
* @param {String} path The file's path
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
static async fileExists(path) {
|
static async fileExists(path) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.stat(path, (err, stats) => {
|
fs.stat(path, (err, stats) => {
|
||||||
if (err) return reject({
|
if (err) return reject({
|
||||||
'message': `No such file or directory: ${err.path}`,
|
message: `No such file or directory: ${err.path}`,
|
||||||
err
|
err
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!stats.isFile()) return reject({
|
if (!stats.isFile()) return reject({
|
||||||
'message': `Not a file: ${path}`,
|
message: `Not a file: ${path}`,
|
||||||
stats
|
stats
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -205,16 +270,21 @@ export class FileUtils {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a directory exists and is a directory.
|
||||||
|
* @param {String} path The directory's path
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
static async directoryExists(path) {
|
static async directoryExists(path) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.stat(path, (err, stats) => {
|
fs.stat(path, (err, stats) => {
|
||||||
if (err) return reject({
|
if (err) return reject({
|
||||||
'message': `Directory does not exist: ${path}`,
|
message: `Directory does not exist: ${path}`,
|
||||||
err
|
err
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!stats.isDirectory()) return reject({
|
if (!stats.isDirectory()) return reject({
|
||||||
'message': `Not a directory: ${path}`,
|
message: `Not a directory: ${path}`,
|
||||||
stats
|
stats
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -223,18 +293,25 @@ export class FileUtils {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a directory.
|
||||||
|
* @param {String} path The directory's path
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
static async createDirectory(path) {
|
static async createDirectory(path) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.mkdir(path, err => {
|
fs.mkdir(path, err => {
|
||||||
if (err) {
|
if (err) reject(err);
|
||||||
if (err.code === 'EEXIST') return resolve();
|
else resolve();
|
||||||
else return reject(err);
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a directory exists and creates it if it doesn't.
|
||||||
|
* @param {String} path The directory's path
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
static async ensureDirectory(path) {
|
static async ensureDirectory(path) {
|
||||||
try {
|
try {
|
||||||
await this.directoryExists(path);
|
await this.directoryExists(path);
|
||||||
|
@ -249,17 +326,22 @@ export class FileUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the contents of a file.
|
||||||
|
* @param {String} path The file's path
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
static async readFile(path) {
|
static async readFile(path) {
|
||||||
try {
|
try {
|
||||||
await this.fileExists(path);
|
await this.fileExists(path);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw (err);
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.readFile(path, 'utf-8', (err, data) => {
|
fs.readFile(path, 'utf-8', (err, data) => {
|
||||||
if (err) reject({
|
if (err) return reject({
|
||||||
'message': `Could not read file: ${path}`,
|
message: `Could not read file: ${path}`,
|
||||||
err
|
err
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -268,24 +350,62 @@ export class FileUtils {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the contents of a file.
|
||||||
|
* @param {String} path The file's path
|
||||||
|
* @param {Object} options Additional options to pass to fs.readFile
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
static async readFileBuffer(path, options) {
|
static async readFileBuffer(path, options) {
|
||||||
|
try {
|
||||||
|
await this.fileExists(path);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.readFile(path, options || {}, (err, data) => {
|
fs.readFile(path, options || {}, (err, data) => {
|
||||||
if (err) return reject(err);
|
if (err) reject(err);
|
||||||
resolve(data);
|
else resolve(data);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes to a file.
|
||||||
|
* @param {String} path The file's path
|
||||||
|
* @param {String} data The file's new contents
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
static async writeFile(path, data) {
|
static async writeFile(path, data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.writeFile(path, data, err => {
|
fs.writeFile(path, data, err => {
|
||||||
if (err) return reject(err);
|
if (err) reject(err);
|
||||||
resolve();
|
else resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes to the end of a file.
|
||||||
|
* @param {String} path The file's path
|
||||||
|
* @param {String} data The data to append to the file
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
static async appendToFile(path, data) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fs.appendFile(path, data, err => {
|
||||||
|
if (err) reject(err);
|
||||||
|
else resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the contents of a file parsed as JSON.
|
||||||
|
* @param {String} path The file's path
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
static async readJsonFromFile(path) {
|
static async readJsonFromFile(path) {
|
||||||
let readFile;
|
let readFile;
|
||||||
try {
|
try {
|
||||||
|
@ -295,41 +415,57 @@ export class FileUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const parsed = await Utils.tryParseJson(readFile);
|
return await Utils.tryParseJson(readFile);
|
||||||
return parsed;
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw (Object.assign(err, { path }));
|
throw Object.assign(err, { path });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes to a file as JSON.
|
||||||
|
* @param {String} path The file's path
|
||||||
|
* @param {Any} data The file's new contents
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
static async writeJsonToFile(path, json) {
|
static async writeJsonToFile(path, json) {
|
||||||
return this.writeFile(path, JSON.stringify(json));
|
return this.writeFile(path, JSON.stringify(json));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of items in a directory.
|
||||||
|
* @param {String} path The directory's path
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
static async listDirectory(path) {
|
static async listDirectory(path) {
|
||||||
try {
|
|
||||||
await this.directoryExists(path);
|
await this.directoryExists(path);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.readdir(path, (err, files) => {
|
fs.readdir(path, (err, files) => {
|
||||||
if (err) return reject(err);
|
if (err) reject(err);
|
||||||
resolve(files);
|
else resolve(files);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async readDir(path) {
|
static async readDir(path) {
|
||||||
return this.listDirectory(path);
|
return this.listDirectory(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a file or buffer's MIME type and typical file extension.
|
||||||
|
* @param {String|Buffer} buffer A buffer or the path of a file
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
static async getFileType(buffer) {
|
static async getFileType(buffer) {
|
||||||
if (typeof buffer === 'string') buffer = await this.readFileBuffer(buffer);
|
if (typeof buffer === 'string') buffer = await this.readFileBuffer(buffer);
|
||||||
|
|
||||||
return filetype(buffer);
|
return filetype(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a file's contents as a data URI.
|
||||||
|
* @param {String} path The directory's path
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
static async toDataURI(buffer, type) {
|
static async toDataURI(buffer, type) {
|
||||||
if (typeof buffer === 'string') buffer = await this.readFileBuffer(buffer);
|
if (typeof buffer === 'string') buffer = await this.readFileBuffer(buffer);
|
||||||
if (!type) type = this.getFileType(buffer).mime;
|
if (!type) type = this.getFileType(buffer).mime;
|
||||||
|
|
|
@ -5,7 +5,7 @@ const
|
||||||
plumber = require('gulp-plumber'),
|
plumber = require('gulp-plumber'),
|
||||||
watch = require('gulp-watch');
|
watch = require('gulp-watch');
|
||||||
|
|
||||||
const task_babel = function () {
|
const task_build = function () {
|
||||||
return pump([
|
return pump([
|
||||||
gulp.src('src/**/*js'),
|
gulp.src('src/**/*js'),
|
||||||
plumber(),
|
plumber(),
|
||||||
|
@ -14,7 +14,7 @@ const task_babel = function () {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const watch_babel = function () {
|
const task_watch = function () {
|
||||||
return pump([
|
return pump([
|
||||||
watch('src/**/*js'),
|
watch('src/**/*js'),
|
||||||
plumber(),
|
plumber(),
|
||||||
|
@ -23,5 +23,5 @@ const watch_babel = function () {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
gulp.task('build', task_babel);
|
gulp.task('build', task_build);
|
||||||
gulp.task('watch', watch_babel);
|
gulp.task('watch', task_watch);
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
module.exports = require('./main.js');
|
|
|
@ -5,19 +5,16 @@
|
||||||
"version": "2.0.0b",
|
"version": "2.0.0b",
|
||||||
"homepage": "https://betterdiscord.net",
|
"homepage": "https://betterdiscord.net",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "index.js",
|
"main": "dist/main.js",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
"Jiiks",
|
"Jiiks",
|
||||||
"Pohky"
|
"Pohky"
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Jiiks/BetterDiscordApp.git"
|
"url": "https://github.com/JsSucks/BetterDiscordApp.git"
|
||||||
},
|
},
|
||||||
"private": false,
|
"private": false,
|
||||||
"devDependencies": {
|
|
||||||
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "gulp build",
|
"build": "gulp build",
|
||||||
"watch": "gulp watch"
|
"watch": "gulp watch"
|
||||||
|
|
196
core/src/main.js
196
core/src/main.js
|
@ -10,48 +10,47 @@
|
||||||
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const sass = require('node-sass');
|
const sass = require('node-sass');
|
||||||
|
|
||||||
const { FileUtils, BDIpc, Config, WindowUtils, CSSEditor, Database } = require('./modules');
|
|
||||||
const { BrowserWindow, dialog } = require('electron');
|
const { BrowserWindow, dialog } = require('electron');
|
||||||
|
|
||||||
const tests = true;
|
const { FileUtils, BDIpc, Config, WindowUtils, CSSEditor, Database } = require('./modules');
|
||||||
const _basePath = __dirname;
|
|
||||||
|
const tests = typeof PRODUCTION === 'undefined';
|
||||||
|
|
||||||
|
const _basePath = tests ? path.resolve(__dirname, '..', '..') : __dirname;
|
||||||
|
const _baseDataPath = tests ? path.resolve(_basePath, 'tests') : _basePath;
|
||||||
|
|
||||||
|
const sparkplug = path.resolve(__dirname, 'sparkplug.js');
|
||||||
|
|
||||||
const _clientScript = tests
|
const _clientScript = tests
|
||||||
? path.resolve(__dirname, '..', '..', 'client', 'dist', 'betterdiscord.client.js')
|
? path.resolve(_basePath, 'client', 'dist', 'betterdiscord.client.js')
|
||||||
: path.resolve(__dirname, 'betterdiscord.client.js');
|
: path.resolve(_basePath, 'betterdiscord.client.js');
|
||||||
const _dataPath = tests
|
|
||||||
? path.resolve(__dirname, '..', '..', 'tests', 'data')
|
|
||||||
: path.resolve(__dirname, 'data');
|
|
||||||
const _extPath = tests
|
|
||||||
? path.resolve(__dirname, '..', '..', 'tests', 'ext')
|
|
||||||
: path.resolve(__dirname, 'ext');
|
|
||||||
const _pluginPath = path.resolve(_extPath, 'plugins');
|
|
||||||
const _themePath = path.resolve(_extPath, 'themes');
|
|
||||||
const _modulePath = path.resolve(_extPath, 'modules');
|
|
||||||
const _cssEditorPath = tests
|
const _cssEditorPath = tests
|
||||||
? path.resolve(__dirname, '..', '..', 'csseditor', 'dist')
|
? path.resolve(__dirname, '..', '..', 'csseditor', 'dist')
|
||||||
: path.resolve(__dirname, 'csseditor');
|
: path.resolve(__dirname, 'csseditor');
|
||||||
|
|
||||||
|
const _dataPath = path.resolve(_baseDataPath, 'data');
|
||||||
|
const _extPath = path.resolve(_baseDataPath, 'ext');
|
||||||
|
const _pluginPath = path.resolve(_extPath, 'plugins');
|
||||||
|
const _themePath = path.resolve(_extPath, 'themes');
|
||||||
|
const _modulePath = path.resolve(_extPath, 'modules');
|
||||||
|
|
||||||
|
const version = require(path.resolve(_basePath, 'package.json')).version;
|
||||||
|
|
||||||
const paths = [
|
const paths = [
|
||||||
{ id: 'base', path: _basePath.replace(/\\/g, '/') },
|
{ id: 'base', path: _basePath },
|
||||||
{ id: 'cs', path: _clientScript.replace(/\\/g, '/') },
|
{ id: 'cs', path: _clientScript },
|
||||||
{ id: 'data', path: _dataPath.replace(/\\/g, '/') },
|
{ id: 'data', path: _dataPath },
|
||||||
{ id: 'ext', path: _extPath.replace(/\\/g, '/') },
|
{ id: 'ext', path: _extPath },
|
||||||
{ id: 'plugins', path: _pluginPath.replace(/\\/g, '/') },
|
{ id: 'plugins', path: _pluginPath },
|
||||||
{ id: 'themes', path: _themePath.replace(/\\/g, '/') },
|
{ id: 'themes', path: _themePath },
|
||||||
{ id: 'modules', path: _modulePath.replace(/\\/g, '/') },
|
{ id: 'modules', path: _modulePath },
|
||||||
{ id: 'csseditor', path: _cssEditorPath.replace(/\\/g, '/') }
|
{ id: 'csseditor', path: _cssEditorPath }
|
||||||
];
|
];
|
||||||
|
|
||||||
const sparkplug = path.resolve(__dirname, 'sparkplug.js').replace(/\\/g, '/');
|
|
||||||
|
|
||||||
const Common = {};
|
|
||||||
const globals = {
|
const globals = {
|
||||||
version: '2.0.0a',
|
version,
|
||||||
paths
|
paths
|
||||||
}
|
};
|
||||||
|
|
||||||
const dbInstance = new Database(paths.find(path => path.id === 'data').path);
|
|
||||||
|
|
||||||
class Comms {
|
class Comms {
|
||||||
|
|
||||||
|
@ -61,67 +60,48 @@ class Comms {
|
||||||
}
|
}
|
||||||
|
|
||||||
initListeners() {
|
initListeners() {
|
||||||
BDIpc.on('bd-getConfig', o => {
|
BDIpc.on('ping', () => 'pong', true);
|
||||||
o.reply(Common.Config.config);
|
|
||||||
});
|
|
||||||
|
|
||||||
BDIpc.on('bd-sendToDiscord', event => this.bd.windowUtils.send(event.args.channel, event.args.message));
|
BDIpc.on('bd-getConfig', () => this.bd.config.config, true);
|
||||||
|
|
||||||
BDIpc.on('bd-openCssEditor', o => this.bd.csseditor.openEditor(o));
|
BDIpc.on('bd-sendToDiscord', (event, m) => this.sendToDiscord(m.channel, m.message), true);
|
||||||
// BDIpc.on('bd-setScss', o => this.bd.csseditor.setSCSS(o.args.scss));
|
|
||||||
BDIpc.on('bd-sendToCssEditor', o => this.bd.csseditor.send(o.args.channel, o.args.data));
|
|
||||||
|
|
||||||
BDIpc.on('bd-readFile', this.readFile);
|
BDIpc.on('bd-openCssEditor', (event, options) => this.bd.csseditor.openEditor(options), true);
|
||||||
BDIpc.on('bd-readJson', o => this.readFile(o, true));
|
BDIpc.on('bd-sendToCssEditor', (event, m) => this.sendToCssEditor(m.channel, m.message), true);
|
||||||
|
|
||||||
BDIpc.on('bd-native-open', o => {
|
BDIpc.on('bd-native-open', (event, options) => {
|
||||||
dialog.showOpenDialog(BrowserWindow.fromWebContents(o.ipcEvent.sender), o.args, filenames => {
|
dialog.showOpenDialog(BrowserWindow.fromWebContents(event.ipcEvent.sender), options, filenames => {
|
||||||
o.reply(filenames);
|
event.reply(filenames);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
BDIpc.on('bd-compileSass', o => {
|
BDIpc.on('bd-compileSass', (event, options) => {
|
||||||
if (!o.args.path && !o.args.data) return o.reply('');
|
if (typeof options.path === 'string' && typeof options.data === 'string') {
|
||||||
if (typeof o.args.path === 'string' && typeof o.args.data === 'string') {
|
options.data = `${options.data} @import '${options.path.replace(/\\/g, '\\\\').replace(/'/g, '\\\'')}';`;
|
||||||
o.args.data = `${o.args.data} @import '${o.args.path.replace(/\\/g, '\\\\').replace(/'/g, '\\\'')}';`;
|
options.path = undefined;
|
||||||
o.args.path = undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sass.render(o.args, (err, result) => {
|
sass.render(options, (err, result) => {
|
||||||
if (err) {
|
if (err) event.reject(err);
|
||||||
o.reply({ err });
|
else event.reply(result);
|
||||||
return;
|
|
||||||
}
|
|
||||||
o.reply(result);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
BDIpc.on('bd-dba', o => {
|
BDIpc.on('bd-dba', (event, options) => this.bd.dbInstance.exec(options), true);
|
||||||
(async () => {
|
|
||||||
try {
|
|
||||||
const ret = await dbInstance.exec(o.args);
|
|
||||||
o.reply(ret);
|
|
||||||
} catch (err) {
|
|
||||||
o.reply({err});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async readFile(o, json) {
|
|
||||||
const { path } = o.args;
|
|
||||||
try {
|
|
||||||
const readFile = json ? await FileUtils.readJsonFromFile(path) : await FileUtils.readFile(path);
|
|
||||||
o.reply(readFile);
|
|
||||||
} catch (err) {
|
|
||||||
o.reply(err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async send(channel, message) {
|
async send(channel, message) {
|
||||||
BDIpc.send(channel, message);
|
BDIpc.send(channel, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sendToDiscord(channel, message) {
|
||||||
|
return this.bd.windowUtils.send(channel, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendToCssEditor(channel, message) {
|
||||||
|
return this.bd.csseditor.send(channel, message);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class BetterDiscord {
|
class BetterDiscord {
|
||||||
|
@ -135,23 +115,32 @@ class BetterDiscord {
|
||||||
|
|
||||||
this.injectScripts = this.injectScripts.bind(this);
|
this.injectScripts = this.injectScripts.bind(this);
|
||||||
this.ignite = this.ignite.bind(this);
|
this.ignite = this.ignite.bind(this);
|
||||||
Common.Config = new Config(globals);
|
|
||||||
|
this.config = new Config(args || globals);
|
||||||
|
this.dbInstance = new Database(this.config.getPath('data'));
|
||||||
this.comms = new Comms(this);
|
this.comms = new Comms(this);
|
||||||
|
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
const window = await this.waitForWindow();
|
await this.waitForWindowUtils();
|
||||||
this.windowUtils = new WindowUtils({ window });
|
|
||||||
|
|
||||||
await FileUtils.ensureDirectory(paths.find(path => path.id === 'ext').path);
|
if (!tests) {
|
||||||
|
const basePath = this.config.getPath('base');
|
||||||
|
const files = await FileUtils.listDirectory(basePath);
|
||||||
|
const latestCs = FileUtils.resolveLatest(files, file => file.endsWith('.js') && file.startsWith('client.'), file => file.replace('client.', '').replace('.js', ''), 'client.', '.js');
|
||||||
|
this.config.getPath('cs', true).path = path.resolve(basePath, latestCs);
|
||||||
|
}
|
||||||
|
|
||||||
this.csseditor = new CSSEditor(this, paths.find(path => path.id === 'csseditor').path);
|
await FileUtils.ensureDirectory(this.config.getPath('ext'));
|
||||||
|
|
||||||
this.windowUtils.events('did-get-response-details', () => this.ignite(this.windowUtils.window));
|
this.csseditor = new CSSEditor(this, this.config.getPath('csseditor'));
|
||||||
this.windowUtils.events('did-finish-load', e => this.injectScripts(true));
|
|
||||||
|
|
||||||
this.windowUtils.events('did-navigate-in-page', (event, url, isMainFrame) => {
|
this.windowUtils.on('did-get-response-details', () => this.ignite());
|
||||||
|
this.windowUtils.on('did-finish-load', () => this.injectScripts(true));
|
||||||
|
|
||||||
|
this.windowUtils.on('did-navigate-in-page', (event, url, isMainFrame) => {
|
||||||
this.windowUtils.send('did-navigate-in-page', { event, url, isMainFrame });
|
this.windowUtils.send('did-navigate-in-page', { event, url, isMainFrame });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -166,10 +155,8 @@ class BetterDiscord {
|
||||||
const defer = setInterval(() => {
|
const defer = setInterval(() => {
|
||||||
const windows = BrowserWindow.getAllWindows();
|
const windows = BrowserWindow.getAllWindows();
|
||||||
|
|
||||||
if (windows.length > 0) {
|
for (let window of windows) {
|
||||||
windows.forEach(window => {
|
if (window) BetterDiscord.ignite(window);
|
||||||
self.ignite(window);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (windows.length === 1 && windows[0].webContents.getURL().includes('discordapp.com')) {
|
if (windows.length === 1 && windows[0].webContents.getURL().includes('discordapp.com')) {
|
||||||
|
@ -180,22 +167,39 @@ class BetterDiscord {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ignite(window) {
|
async waitForWindowUtils() {
|
||||||
//Hook things that Discord removes from global. These will be removed again in the client script
|
if (this.windowUtils) return this.windowUtils;
|
||||||
window.webContents.executeJavaScript(`require("${sparkplug}");`);
|
const window = await this.waitForWindow();
|
||||||
|
return this.windowUtils = new WindowUtils({ window });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get window() {
|
||||||
|
return this.windowUtils ? this.windowUtils.window : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks things that Discord removes from global. These will be removed again in the client script.
|
||||||
|
*/
|
||||||
|
ignite() {
|
||||||
|
return BetterDiscord.ignite(this.window);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks things that Discord removes from global. These will be removed again in the client script.
|
||||||
|
* @param {BrowserWindow} window The window to inject the sparkplug script into
|
||||||
|
*/
|
||||||
|
static ignite(window) {
|
||||||
|
return WindowUtils.injectScript(window, sparkplug);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects the client script into the main window.
|
||||||
|
* @param {Boolean} reload Whether the main window was reloaded
|
||||||
|
*/
|
||||||
async injectScripts(reload = false) {
|
async injectScripts(reload = false) {
|
||||||
console.log(`RELOAD? ${reload}`);
|
console.log(`RELOAD? ${reload}`);
|
||||||
if (!tests) {
|
return this.windowUtils.injectScript(this.config.getPath('cs'));
|
||||||
const files = await FileUtils.listDirectory(paths.find(path => path.id === 'base').path);
|
|
||||||
const latestCs = FileUtils.resolveLatest(files, file => file.endsWith('.js') && file.startsWith('client.'), file => file.replace('client.', '').replace('.js', ''), 'client.', '.js');
|
|
||||||
paths.find(path => path.id === 'cs').path = path.resolve(paths.find(path => path.id === 'base').path, latestCs).replace(/\\/g, '/');
|
|
||||||
}
|
}
|
||||||
this.windowUtils.injectScript(paths.find(path => path.id === 'cs').path);
|
|
||||||
}
|
|
||||||
|
|
||||||
get fileUtils() { return FileUtils; }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,37 +7,115 @@
|
||||||
* This source code is licensed under the MIT license found in the
|
* This source code is licensed under the MIT license found in the
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
const { Module } = require('./modulebase');
|
|
||||||
|
|
||||||
const { ipcMain } = require('electron');
|
const { ipcMain } = require('electron');
|
||||||
|
|
||||||
|
const { Module } = require('./modulebase');
|
||||||
|
|
||||||
|
const callbacks = new WeakMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The IPC module used in the main process.
|
||||||
|
*/
|
||||||
|
class BDIpc {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an IPC event listener.
|
||||||
|
* @param {String} channel The channel to listen on
|
||||||
|
* @param {Function} callback A function that will be called when a message is received
|
||||||
|
* @param {Boolean} reply Whether to automatically reply to the message with the callback's return value
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
static on(channel, callback, reply) {
|
||||||
|
channel = channel.startsWith('bd-') ? channel : `bd-${channel}`;
|
||||||
|
|
||||||
|
const boundCallback = async (event, args) => {
|
||||||
|
const ipcevent = new BDIpcEvent(event, args);
|
||||||
|
try {
|
||||||
|
const r = callback(ipcevent, ipcevent.message);
|
||||||
|
if (reply) ipcevent.reply(await r);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error in IPC callback:', err);
|
||||||
|
if (reply) ipcevent.reject(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
callbacks.set(callback, boundCallback);
|
||||||
|
ipcMain.on(channel, boundCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
static off(channel, callback) {
|
||||||
|
ipcMain.removeListener(channel, callbacks.get(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a message to the main process and returns a promise that is resolved when the main process replies.
|
||||||
|
* @param {BrowserWindow} window The window to send a message to
|
||||||
|
* @param {String} channel The channel to send a message to
|
||||||
|
* @param {Any} message Data to send to the main process
|
||||||
|
* @param {Boolean} error Whether to mark the message as an error
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
static send(window, channel, message, error) {
|
||||||
|
channel = channel.startsWith('bd-') ? channel : `bd-${channel}`;
|
||||||
|
|
||||||
|
const eid = 'bd-' + Date.now().toString();
|
||||||
|
window.send(channel, { eid, message, error });
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
ipcMain.once(eid, (event, arg) => {
|
||||||
|
if (arg.error) reject(arg.message);
|
||||||
|
else resolve(arg.message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static ping(window) {
|
||||||
|
return this.send(window, 'ping');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
class BDIpcEvent extends Module {
|
class BDIpcEvent extends Module {
|
||||||
|
|
||||||
constructor(event, args) {
|
constructor(event, args) {
|
||||||
super(args);
|
super(args);
|
||||||
this.ipcEvent = event;
|
this.ipcEvent = event;
|
||||||
|
this.replied = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bindings() {
|
bindings() {
|
||||||
this.send = this.send.bind(this);
|
|
||||||
this.reply = this.reply.bind(this);
|
this.reply = this.reply.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
send(message) {
|
/**
|
||||||
this.ipcEvent.sender.send(this.args.__eid, message);
|
* Sends a message back to the message's sender.
|
||||||
|
* @param {Any} message Data to send to this message's sender
|
||||||
|
*/
|
||||||
|
reply(message, error) {
|
||||||
|
if (this.replied)
|
||||||
|
throw {message: 'This message has already been replied to.'};
|
||||||
|
|
||||||
|
this.replied = true;
|
||||||
|
return BDIpc.send(this.ipcEvent.sender, this.eid, message, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
reply(message) {
|
reject(err) {
|
||||||
this.send(message);
|
return this.reply(err, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get message() {
|
||||||
|
return this.args.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
class BDIpc {
|
get error() {
|
||||||
|
return this.args.error;
|
||||||
static on(channel, cb) {
|
|
||||||
ipcMain.on(channel, (event, args) => cb(new BDIpcEvent(event, args)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get eid() {
|
||||||
|
return this.args.eid;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { BDIpc };
|
module.exports = { BDIpc };
|
|
@ -20,10 +20,15 @@ class Config extends Module {
|
||||||
return this.args.paths;
|
return this.args.paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPath(id, full) {
|
||||||
|
const path = this.paths.find(path => path.id === id);
|
||||||
|
return full ? path : path.path;
|
||||||
|
}
|
||||||
|
|
||||||
get config() {
|
get config() {
|
||||||
return {
|
return {
|
||||||
'version': this.version,
|
version: this.version,
|
||||||
'paths': this.paths
|
paths: this.paths
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ const { BrowserWindow } = require('electron');
|
||||||
|
|
||||||
const { Module } = require('./modulebase');
|
const { Module } = require('./modulebase');
|
||||||
const { WindowUtils } = require('./utils');
|
const { WindowUtils } = require('./utils');
|
||||||
|
const { BDIpc } = require('./bdipc');
|
||||||
|
|
||||||
class CSSEditor extends Module {
|
class CSSEditor extends Module {
|
||||||
|
|
||||||
|
@ -22,22 +23,21 @@ class CSSEditor extends Module {
|
||||||
this.bd = bd;
|
this.bd = bd;
|
||||||
}
|
}
|
||||||
|
|
||||||
openEditor(o) {
|
/**
|
||||||
|
* Opens an editor.
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
openEditor(options) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
if (this.editor) {
|
if (this.editor) {
|
||||||
if (this.editor.isFocused()) return;
|
if (this.editor.isFocused()) return;
|
||||||
|
|
||||||
this.editor.focus();
|
this.editor.focus();
|
||||||
this.editor.flashFrame(true);
|
this.editor.flashFrame(true);
|
||||||
o.reply(true);
|
return resolve(true);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = this.options;
|
options = Object.assign({}, this.options, options);
|
||||||
for (let option in o.args) {
|
|
||||||
if (o.args.hasOwnProperty(option)) {
|
|
||||||
options[option] = o.args[option];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.editor = new BrowserWindow(options);
|
this.editor = new BrowserWindow(options);
|
||||||
this.editor.loadURL('about:blank');
|
this.editor.loadURL('about:blank');
|
||||||
|
@ -55,24 +55,32 @@ class CSSEditor extends Module {
|
||||||
|
|
||||||
this.editor.webContents.on('did-finish-load', () => {
|
this.editor.webContents.on('did-finish-load', () => {
|
||||||
this.editorUtils.injectScript(path.join(this.editorPath, 'csseditor.js'));
|
this.editorUtils.injectScript(path.join(this.editorPath, 'csseditor.js'));
|
||||||
o.reply(true);
|
resolve(true);
|
||||||
});
|
});
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
setSCSS(scss) {
|
/**
|
||||||
this.send('set-scss', scss);
|
* Sends data to the editor.
|
||||||
}
|
* @param {String} channel
|
||||||
|
* @param {Any} data
|
||||||
|
*/
|
||||||
send(channel, data) {
|
send(channel, data) {
|
||||||
if (!this.editor) return;
|
if (!this.editor) throw {message: 'The CSS editor is not open.'};
|
||||||
this.editor.webContents.send(channel, data);
|
return BDIpc.send(this.editor, channel, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the CSS editor's always on top flag.
|
||||||
|
*/
|
||||||
set alwaysOnTop(state) {
|
set alwaysOnTop(state) {
|
||||||
if (!this.editor) return;
|
if (!this.editor) return;
|
||||||
this.editor.setAlwaysOnTop(state);
|
this.editor.setAlwaysOnTop(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default options to pass to BrowserWindow.
|
||||||
|
*/
|
||||||
get options() {
|
get options() {
|
||||||
return {
|
return {
|
||||||
width: 800,
|
width: 800,
|
||||||
|
|
|
@ -8,17 +8,16 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Base Module that every non-static module should extend
|
* Base Module that every non-static module should extend.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class Module {
|
class Module {
|
||||||
|
|
||||||
constructor(args) {
|
constructor(args) {
|
||||||
this.__ = {
|
this.__ = {
|
||||||
state: args,
|
state: args,
|
||||||
args
|
args
|
||||||
}
|
};
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,43 +10,36 @@
|
||||||
|
|
||||||
// TODO Use common
|
// TODO Use common
|
||||||
|
|
||||||
const
|
const path = require('path');
|
||||||
path = require('path'),
|
const fs = require('fs');
|
||||||
fs = require('fs');
|
|
||||||
|
|
||||||
const { Module } = require('./modulebase');
|
const { Module } = require('./modulebase');
|
||||||
|
const { BDIpc } = require('./bdipc');
|
||||||
|
|
||||||
class Utils {
|
class Utils {
|
||||||
|
|
||||||
static async tryParseJson(jsonString) {
|
static async tryParseJson(jsonString) {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(jsonString);
|
return JSON.parse(jsonString);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw ({
|
throw ({
|
||||||
'message': 'Failed to parse json',
|
message: 'Failed to parse json',
|
||||||
err
|
err
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get timestamp() {
|
|
||||||
return 'Timestamp';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class FileUtils {
|
class FileUtils {
|
||||||
|
|
||||||
static async fileExists(path) {
|
static async fileExists(path) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.stat(path, (err, stats) => {
|
fs.stat(path, (err, stats) => {
|
||||||
if(err) return reject({
|
if(err) return reject({
|
||||||
'message': `No such file or directory: ${err.path}`,
|
message: `No such file or directory: ${err.path}`,
|
||||||
err
|
err
|
||||||
});
|
});
|
||||||
|
|
||||||
if(!stats.isFile()) return reject({
|
if(!stats.isFile()) return reject({
|
||||||
'message': `Not a file: ${path}`,
|
message: `Not a file: ${path}`,
|
||||||
stats
|
stats
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -59,12 +52,12 @@ class FileUtils {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.stat(path, (err, stats) => {
|
fs.stat(path, (err, stats) => {
|
||||||
if(err) return reject({
|
if(err) return reject({
|
||||||
'message': `Directory does not exist: ${path}`,
|
message: `Directory does not exist: ${path}`,
|
||||||
err
|
err
|
||||||
});
|
});
|
||||||
|
|
||||||
if(!stats.isDirectory()) return reject({
|
if(!stats.isDirectory()) return reject({
|
||||||
'message': `Not a directory: ${path}`,
|
message: `Not a directory: ${path}`,
|
||||||
stats
|
stats
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -77,13 +70,13 @@ class FileUtils {
|
||||||
try {
|
try {
|
||||||
await this.fileExists(path);
|
await this.fileExists(path);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw(err);
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.readFile(path, 'utf-8', (err, data) => {
|
fs.readFile(path, 'utf-8', (err, data) => {
|
||||||
if(err) reject({
|
if(err) return reject({
|
||||||
'message': `Could not read file: ${path}`,
|
message: `Could not read file: ${path}`,
|
||||||
err
|
err
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -97,14 +90,13 @@ class FileUtils {
|
||||||
try {
|
try {
|
||||||
readFile = await this.readFile(path);
|
readFile = await this.readFile(path);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
throw(err);
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const parsed = await Utils.tryParseJson(readFile);
|
return await Utils.tryParseJson(readFile);
|
||||||
return parsed;
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw(Object.assign(err, { path }));
|
throw Object.assign(err, { path });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,8 +105,8 @@ class FileUtils {
|
||||||
await this.directoryExists(path);
|
await this.directoryExists(path);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.readdir(path, (err, files) => {
|
fs.readdir(path, (err, files) => {
|
||||||
if (err) return reject(err);
|
if (err) reject(err);
|
||||||
resolve(files);
|
else resolve(files);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -145,11 +137,8 @@ class FileUtils {
|
||||||
static async createDirectory(path) {
|
static async createDirectory(path) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.mkdir(path, err => {
|
fs.mkdir(path, err => {
|
||||||
if (err) {
|
if (err) reject(err);
|
||||||
if (err.code === 'EEXIST') return resolve();
|
else resolve();
|
||||||
else return reject(err);
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -170,7 +159,6 @@ class FileUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
class WindowUtils extends Module {
|
class WindowUtils extends Module {
|
||||||
|
|
||||||
bindings() {
|
bindings() {
|
||||||
this.openDevTools = this.openDevTools.bind(this);
|
this.openDevTools = this.openDevTools.bind(this);
|
||||||
this.executeJavascript = this.executeJavascript.bind(this);
|
this.executeJavascript = this.executeJavascript.bind(this);
|
||||||
|
@ -190,28 +178,32 @@ class WindowUtils extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
executeJavascript(script) {
|
executeJavascript(script) {
|
||||||
this.webContents.executeJavaScript(script);
|
return this.webContents.executeJavaScript(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
injectScript(fpath, variable) {
|
injectScript(fpath, variable) {
|
||||||
console.log(`Injecting: ${fpath}`);
|
return WindowUtils.injectScript(this.window, fpath, variable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static injectScript(window, fpath, variable) {
|
||||||
|
window = window.webContents || window;
|
||||||
|
if (!window) return;
|
||||||
|
// console.log(`Injecting: ${fpath} to`, window);
|
||||||
|
|
||||||
const escaped_path = fpath.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
const escaped_path = fpath.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
||||||
const escaped_variable = variable ? variable.replace(/\\/g, '\\\\').replace(/"/g, '\\"') : null;
|
const escaped_variable = variable ? variable.replace(/\\/g, '\\\\').replace(/"/g, '\\"') : null;
|
||||||
|
|
||||||
if (variable) this.executeJavascript(`window["${escaped_variable}"] = require("${escaped_path}");`);
|
if (variable) return window.executeJavaScript(`window["${escaped_variable}"] = require("${escaped_path}");`);
|
||||||
else this.executeJavascript(`require("${escaped_path}");`);
|
else return window.executeJavaScript(`require("${escaped_path}");`);
|
||||||
}
|
}
|
||||||
|
|
||||||
events(event, callback) {
|
on(event, callback) {
|
||||||
this.webContents.on(event, callback);
|
this.webContents.on(event, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
send(channel, message) {
|
send(channel, message) {
|
||||||
channel = channel.startsWith('bd-') ? channel : `bd-${channel}`;
|
return BDIpc.send(this.window, channel, message);
|
||||||
this.webContents.send(channel, message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
|
@ -1,5 +1,17 @@
|
||||||
|
/**
|
||||||
|
* BetterDiscord Sparkplug
|
||||||
|
* Copyright (c) 2015-present JsSucks - https://github.com/JsSucks
|
||||||
|
* All rights reserved.
|
||||||
|
* https://github.com/JsSucks - 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.
|
||||||
|
*
|
||||||
|
* This file is evaluated in the renderer process!
|
||||||
|
*/
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
if (window.__bd && window.__bd.ignited) return;
|
if (module.exports.bd) return;
|
||||||
|
|
||||||
console.log('[BetterDiscord|Sparkplug]');
|
console.log('[BetterDiscord|Sparkplug]');
|
||||||
|
|
||||||
|
@ -7,12 +19,12 @@
|
||||||
if (!ls) console.warn('[BetterDiscord|Sparkplug] Failed to hook localStorage :(');
|
if (!ls) console.warn('[BetterDiscord|Sparkplug] Failed to hook localStorage :(');
|
||||||
const wsOrig = window.WebSocket;
|
const wsOrig = window.WebSocket;
|
||||||
|
|
||||||
window.__bd = {
|
const bd = module.exports.bd = {
|
||||||
localStorage: ls,
|
localStorage: ls,
|
||||||
wsHook: null,
|
wsHook: null,
|
||||||
wsOrig,
|
wsOrig,
|
||||||
ignited: true
|
ignited: true
|
||||||
}
|
};
|
||||||
|
|
||||||
class WSHook extends window.WebSocket {
|
class WSHook extends window.WebSocket {
|
||||||
|
|
||||||
|
@ -25,14 +37,14 @@
|
||||||
console.info(`[BetterDiscord|WebSocket Proxy] new WebSocket detected, url: ${url}`);
|
console.info(`[BetterDiscord|WebSocket Proxy] new WebSocket detected, url: ${url}`);
|
||||||
if (!url.includes('gateway.discord.gg')) return;
|
if (!url.includes('gateway.discord.gg')) return;
|
||||||
|
|
||||||
if (window.__bd.setWS) {
|
if (bd.setWS) {
|
||||||
window.__bd.setWS(this);
|
bd.setWS(this);
|
||||||
console.info(`[BetterDiscord|WebSocket Proxy] WebSocket sent to instance`);
|
console.info(`[BetterDiscord|WebSocket Proxy] WebSocket sent to instance`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.info(`[BetterDiscord|WebSocket Proxy] WebSocket stored to __bd['wsHook']`);
|
console.info(`[BetterDiscord|WebSocket Proxy] WebSocket stored to bd.wsHook`);
|
||||||
window.__bd.wsHook = this;
|
bd.wsHook = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,21 +5,19 @@
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"homepage": "https://betterdiscord.net",
|
"homepage": "https://betterdiscord.net",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "index.js",
|
"main": "dist/csseditor.js",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
"Jiiks",
|
"Jiiks",
|
||||||
"Pohky"
|
"Pohky"
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Jiiks/BetterDiscordApp.git"
|
"url": "https://github.com/JsSucks/BetterDiscordApp.git"
|
||||||
},
|
},
|
||||||
"private": false,
|
"private": false,
|
||||||
"devDependencies": {
|
|
||||||
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --progress --colors",
|
"build": "webpack --progress --colors",
|
||||||
"watch": "webpack --progress --colors --watch"
|
"watch": "webpack --progress --colors --watch",
|
||||||
|
"release": "webpack --progress --colors --config=webpack.production.config.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
const { ipcRenderer } = window.require('electron');
|
|
||||||
|
|
||||||
export default class {
|
|
||||||
|
|
||||||
static on(channel, cb) {
|
|
||||||
ipcRenderer.on(channel, (event, args) => cb(event, args));
|
|
||||||
}
|
|
||||||
|
|
||||||
static async send(channel, message) {
|
|
||||||
const __eid = Date.now().toString();
|
|
||||||
ipcRenderer.send(
|
|
||||||
channel.startsWith('bd-') ? channel: `bd-${channel}`,
|
|
||||||
message === undefined ? { __eid } : Object.assign(message, { __eid })
|
|
||||||
);
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
ipcRenderer.once(__eid, (event, arg) => {
|
|
||||||
if (arg.err) return reject(arg);
|
|
||||||
resolve(arg);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static sendToDiscord(channel, message) {
|
|
||||||
this.send('bd-sendToDiscord', { channel, message });
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -33,18 +33,18 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import '../../node_modules/codemirror/addon/scroll/simplescrollbars.js';
|
import ClientIPC from 'bdipc';
|
||||||
import '../../node_modules/codemirror/mode/css/css.js';
|
|
||||||
import '../../node_modules/codemirror/addon/hint/css-hint.js';
|
|
||||||
import '../../node_modules/codemirror/addon/search/search.js';
|
|
||||||
import '../../node_modules/codemirror/addon/search/searchcursor.js';
|
|
||||||
import '../../node_modules/codemirror/addon/search/jump-to-line.js';
|
|
||||||
import '../../node_modules/codemirror/addon/dialog/dialog.js';
|
|
||||||
import '../../node_modules/codemirror/addon/hint/show-hint.js';
|
|
||||||
|
|
||||||
import BDIpc from './BDIpc';
|
import { remote } from 'electron';
|
||||||
|
|
||||||
const { remote } = window.require('electron');
|
import 'codemirror/addon/scroll/simplescrollbars.js';
|
||||||
|
import 'codemirror/mode/css/css.js';
|
||||||
|
import 'codemirror/addon/hint/css-hint.js';
|
||||||
|
import 'codemirror/addon/search/search.js';
|
||||||
|
import 'codemirror/addon/search/searchcursor.js';
|
||||||
|
import 'codemirror/addon/search/jump-to-line.js';
|
||||||
|
import 'codemirror/addon/dialog/dialog.js';
|
||||||
|
import 'codemirror/addon/hint/show-hint.js';
|
||||||
|
|
||||||
const ExcludedIntelliSenseTriggerKeys = {
|
const ExcludedIntelliSenseTriggerKeys = {
|
||||||
'8': 'backspace',
|
'8': 'backspace',
|
||||||
|
@ -131,42 +131,38 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
BDIpc.on('set-scss', (_, data) => {
|
ClientIPC.on('set-scss', (_, scss) => this.setScss(scss));
|
||||||
if (data.error) {
|
|
||||||
console.log(data.error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log(data);
|
|
||||||
this.setScss(data.scss);
|
|
||||||
});
|
|
||||||
|
|
||||||
BDIpc.on('scss-error', (_, err) => {
|
ClientIPC.on('scss-error', (_, err) => {
|
||||||
this.error = err;
|
this.error = err;
|
||||||
this.$forceUpdate();
|
this.$forceUpdate();
|
||||||
if (err)
|
if (err)
|
||||||
console.error('SCSS parse error:', err);
|
console.error('SCSS parse error:', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
BDIpc.on('set-liveupdate', (e, liveUpdate) => this.liveUpdate = liveUpdate);
|
ClientIPC.on('set-liveupdate', (e, liveUpdate) => this.liveUpdate = liveUpdate);
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.codemirror.on('keyup', this.cmOnKeyUp);
|
this.codemirror.on('keyup', this.cmOnKeyUp);
|
||||||
BDIpc.sendToDiscord('get-scss');
|
|
||||||
BDIpc.sendToDiscord('get-liveupdate');
|
(async () => {
|
||||||
|
this.setScss(await ClientIPC.sendToDiscord('get-scss'));
|
||||||
|
this.liveUpdate = await ClientIPC.sendToDiscord('get-liveupdate');
|
||||||
|
})();
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
liveUpdate(liveUpdate) {
|
liveUpdate(liveUpdate) {
|
||||||
BDIpc.sendToDiscord('set-liveupdate', liveUpdate);
|
ClientIPC.sendToDiscord('set-liveupdate', liveUpdate);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
save() {
|
save() {
|
||||||
const scss = this.codemirror.getValue();
|
const scss = this.codemirror.getValue();
|
||||||
BDIpc.sendToDiscord('save-scss', scss);
|
ClientIPC.sendToDiscord('save-scss', scss);
|
||||||
},
|
},
|
||||||
update() {
|
update() {
|
||||||
const scss = this.codemirror.getValue();
|
const scss = this.codemirror.getValue();
|
||||||
BDIpc.sendToDiscord('update-scss', scss);
|
ClientIPC.sendToDiscord('update-scss', scss);
|
||||||
},
|
},
|
||||||
toggleaot() {
|
toggleaot() {
|
||||||
this.alwaysOnTop = !this.alwaysOnTop;
|
this.alwaysOnTop = !this.alwaysOnTop;
|
||||||
|
@ -180,7 +176,7 @@
|
||||||
this.codemirror.setValue(scss || '');
|
this.codemirror.setValue(scss || '');
|
||||||
},
|
},
|
||||||
cmOnChange(value) {
|
cmOnChange(value) {
|
||||||
if(this.liveUpdate) BDIpc.sendToDiscord('update-scss', value);
|
if(this.liveUpdate) ClientIPC.sendToDiscord('update-scss', value);
|
||||||
},
|
},
|
||||||
cmOnKeyUp(editor, event) {
|
cmOnKeyUp(editor, event) {
|
||||||
if (event.ctrlKey) return;
|
if (event.ctrlKey) return;
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
const
|
const path = require('path');
|
||||||
path = require('path'),
|
const webpack = require('webpack');
|
||||||
webpack = require('webpack');
|
|
||||||
const vueLoader = {
|
const vueLoader = {
|
||||||
test: /\.(vue)$/,
|
test: /\.(vue)$/,
|
||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
loader: 'vue-loader'
|
loader: 'vue-loader'
|
||||||
}
|
};
|
||||||
|
|
||||||
const scssLoader = {
|
const scssLoader = {
|
||||||
test: /\.(css|scss)$/,
|
test: /\.(css|scss)$/,
|
||||||
loader: ['css-loader', 'sass-loader']
|
loader: ['css-loader', 'sass-loader']
|
||||||
}
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: './src/index.js',
|
entry: './src/index.js',
|
||||||
|
@ -21,9 +21,17 @@ module.exports = {
|
||||||
module: {
|
module: {
|
||||||
loaders: [vueLoader, scssLoader]
|
loaders: [vueLoader, scssLoader]
|
||||||
},
|
},
|
||||||
|
externals: {
|
||||||
|
electron: 'window.require("electron")',
|
||||||
|
fs: 'window.require("fs")'
|
||||||
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
vue$: path.resolve('..', 'node_modules', 'vue', 'dist', 'vue.esm.js')
|
vue$: path.resolve('..', 'node_modules', 'vue', 'dist', 'vue.esm.js')
|
||||||
}
|
},
|
||||||
|
modules: [
|
||||||
|
path.resolve('..', 'node_modules'),
|
||||||
|
path.resolve('..', 'common', 'modules')
|
||||||
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
const path = require('path');
|
||||||
|
const webpack = require('webpack');
|
||||||
|
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
|
||||||
|
|
||||||
|
const vueLoader = {
|
||||||
|
test: /\.(vue)$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
loader: 'vue-loader'
|
||||||
|
};
|
||||||
|
|
||||||
|
const scssLoader = {
|
||||||
|
test: /\.(css|scss)$/,
|
||||||
|
loader: ['css-loader', 'sass-loader']
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: './src/index.js',
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
filename: 'csseditor-release.js'
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
loaders: [vueLoader, scssLoader]
|
||||||
|
},
|
||||||
|
externals: {
|
||||||
|
electron: 'window.require("electron")',
|
||||||
|
fs: 'window.require("fs")'
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
vue$: path.resolve('..', 'node_modules', 'vue', 'dist', 'vue.esm.js')
|
||||||
|
},
|
||||||
|
modules: [
|
||||||
|
path.resolve('..', 'node_modules'),
|
||||||
|
path.resolve('..', 'common', 'modules')
|
||||||
|
]
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
PRODUCTION: JSON.stringify(true)
|
||||||
|
}),
|
||||||
|
new UglifyJsPlugin()
|
||||||
|
]
|
||||||
|
};
|
53
gulpfile.js
53
gulpfile.js
|
@ -6,64 +6,75 @@ const
|
||||||
merge = require('gulp-merge'),
|
merge = require('gulp-merge'),
|
||||||
copy = require('gulp-copy'),
|
copy = require('gulp-copy'),
|
||||||
rename = require('gulp-rename'),
|
rename = require('gulp-rename'),
|
||||||
|
inject = require('gulp-inject-string'),
|
||||||
copydeps = require('gulp-npm-copy-deps');
|
copydeps = require('gulp-npm-copy-deps');
|
||||||
|
|
||||||
|
const mainpkg = require('./package.json');
|
||||||
const corepkg = require('./core/package.json');
|
const corepkg = require('./core/package.json');
|
||||||
const clientpkg = require('./client/package.json');
|
const clientpkg = require('./client/package.json');
|
||||||
const editorpkg = require('./csseditor/package.json');
|
const editorpkg = require('./csseditor/package.json');
|
||||||
|
|
||||||
|
const releasepkg = function() {
|
||||||
|
delete mainpkg.main;
|
||||||
|
delete mainpkg.devDependencies;
|
||||||
|
delete mainpkg.scripts;
|
||||||
|
return fs.writeFileSync('./release/package.json', JSON.stringify(mainpkg, null, 2));
|
||||||
|
};
|
||||||
|
|
||||||
const client = function() {
|
const client = function() {
|
||||||
return pump([
|
return pump([
|
||||||
gulp.src('./client/dist/*.client.js'),
|
gulp.src('./client/dist/*.client-release.js'),
|
||||||
rename(`client.${clientpkg.version}.js`),
|
rename(`client.${clientpkg.version}.js`),
|
||||||
gulp.dest('./release')
|
gulp.dest('./release')
|
||||||
]);
|
]);
|
||||||
}
|
};
|
||||||
|
|
||||||
const core = function() {
|
const core = function() {
|
||||||
return pump([
|
|
||||||
gulp.src('./core/dist/modules/**/*'),
|
|
||||||
copy('release/', { prefix: 2 })
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const core2 = function() {
|
|
||||||
return pump([
|
return pump([
|
||||||
gulp.src('./core/dist/main.js'),
|
gulp.src('./core/dist/main.js'),
|
||||||
|
inject.after("'use strict';\n", 'const PRODUCTION = true;\n'),
|
||||||
rename(`core.${corepkg.version}.js`),
|
rename(`core.${corepkg.version}.js`),
|
||||||
gulp.dest('./release')
|
gulp.dest('./release')
|
||||||
]);
|
]);
|
||||||
}
|
};
|
||||||
|
|
||||||
const core3 = function() {
|
|
||||||
return fs.writeFileSync('./release/index.js', `module.exports = require('./core.${corepkg.version}.js');`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const sparkplug = function() {
|
const sparkplug = function() {
|
||||||
return pump([
|
return pump([
|
||||||
gulp.src('./core/dist/sparkplug.js'),
|
gulp.src('./core/dist/sparkplug.js'),
|
||||||
gulp.dest('./release')
|
gulp.dest('./release')
|
||||||
]);
|
]);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
const core_modules = function() {
|
||||||
|
return pump([
|
||||||
|
gulp.src('./core/dist/modules/**/*'),
|
||||||
|
copy('release/', { prefix: 2 })
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const index = function() {
|
||||||
|
return fs.writeFileSync('./release/index.js', `module.exports = require('./core.${corepkg.version}.js');`);
|
||||||
|
};
|
||||||
|
|
||||||
const cssEditor = function() {
|
const cssEditor = function() {
|
||||||
return pump([
|
return pump([
|
||||||
gulp.src('./csseditor/dist/**/*'),
|
gulp.src('./csseditor/dist/csseditor-release.js'),
|
||||||
|
rename('csseditor.js'),
|
||||||
copy('release/csseditor', { prefix: 2 })
|
copy('release/csseditor', { prefix: 2 })
|
||||||
]);
|
]);
|
||||||
}
|
};
|
||||||
|
|
||||||
const deps = function() {
|
const deps = function() {
|
||||||
return copydeps('./', './release');
|
return copydeps('./', './release');
|
||||||
}
|
};
|
||||||
|
|
||||||
const bindings = function() {
|
const node_sass_bindings = function() {
|
||||||
return pump([
|
return pump([
|
||||||
gulp.src('./other/node_sass_bindings/**/*'),
|
gulp.src('./other/node_sass_bindings/**/*'),
|
||||||
copy('release/node_modules/node-sass/vendor', { prefix: 2 })
|
copy('release/node_modules/node-sass/vendor', { prefix: 2 })
|
||||||
]);
|
]);
|
||||||
}
|
};
|
||||||
|
|
||||||
gulp.task('release', function () {
|
gulp.task('release', function () {
|
||||||
del(['./release/**/*']).then(() => merge(client(), core(), core2(), core3(), sparkplug(), cssEditor(), deps(), bindings()));
|
del(['./release/**/*']).then(() => merge(releasepkg(), client(), core(), sparkplug(), core_modules(), index(), cssEditor(), deps(), node_sass_bindings()));
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,19 +5,16 @@
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"homepage": "https://betterdiscord.net",
|
"homepage": "https://betterdiscord.net",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "index.js",
|
"main": "dist/installer.js",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
"Jiiks",
|
"Jiiks",
|
||||||
"Pohky"
|
"Pohky"
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Jiiks/BetterDiscordApp.git"
|
"url": "https://github.com/JsSucks/BetterDiscordApp.git"
|
||||||
},
|
},
|
||||||
"private": false,
|
"private": false,
|
||||||
"devDependencies": {
|
|
||||||
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --progress --colors",
|
"build": "webpack --progress --colors",
|
||||||
"watch": "webpack --progress --colors --watch"
|
"watch": "webpack --progress --colors --watch"
|
||||||
|
|
|
@ -2,16 +2,17 @@ const
|
||||||
path = require('path'),
|
path = require('path'),
|
||||||
webpack = require('webpack'),
|
webpack = require('webpack'),
|
||||||
HtmlWebpackPlugin = require('html-webpack-plugin');
|
HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
|
||||||
const vueLoader = {
|
const vueLoader = {
|
||||||
test: /\.(vue)$/,
|
test: /\.(vue)$/,
|
||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
loader: 'vue-loader'
|
loader: 'vue-loader'
|
||||||
}
|
};
|
||||||
|
|
||||||
const scssLoader = {
|
const scssLoader = {
|
||||||
test: /\.(css|scss)$/,
|
test: /\.(css|scss)$/,
|
||||||
loader: ['css-loader', 'sass-loader']
|
loader: ['css-loader', 'sass-loader']
|
||||||
}
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: './src/index.js',
|
entry: './src/index.js',
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -12,7 +12,7 @@
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Jiiks/BetterDiscordApp.git"
|
"url": "https://github.com/JsSucks/BetterDiscordApp.git"
|
||||||
},
|
},
|
||||||
"private": false,
|
"private": false,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -38,6 +38,7 @@
|
||||||
"gulp": "^3.9.1",
|
"gulp": "^3.9.1",
|
||||||
"gulp-babel": "^7.0.0",
|
"gulp-babel": "^7.0.0",
|
||||||
"gulp-copy": "^1.1.0",
|
"gulp-copy": "^1.1.0",
|
||||||
|
"gulp-inject-string": "^1.1.1",
|
||||||
"gulp-merge": "^0.1.1",
|
"gulp-merge": "^0.1.1",
|
||||||
"gulp-npm-copy-deps": "^1.0.2",
|
"gulp-npm-copy-deps": "^1.0.2",
|
||||||
"gulp-plumber": "^1.2.0",
|
"gulp-plumber": "^1.2.0",
|
||||||
|
@ -46,11 +47,10 @@
|
||||||
"html-webpack-plugin": "^3.0.6",
|
"html-webpack-plugin": "^3.0.6",
|
||||||
"jquery": "^3.2.1",
|
"jquery": "^3.2.1",
|
||||||
"lodash": "^4.17.4",
|
"lodash": "^4.17.4",
|
||||||
"nedb": "^1.8.0",
|
|
||||||
"node-gyp": "^3.6.2",
|
"node-gyp": "^3.6.2",
|
||||||
"node-sass": "^4.7.2",
|
|
||||||
"pump": "^2.0.0",
|
"pump": "^2.0.0",
|
||||||
"sass-loader": "^6.0.6",
|
"sass-loader": "^6.0.6",
|
||||||
|
"uglifyjs-webpack-plugin": "^1.2.4",
|
||||||
"v-tooltip": "^2.0.0-rc.30",
|
"v-tooltip": "^2.0.0-rc.30",
|
||||||
"vue": "^2.5.13",
|
"vue": "^2.5.13",
|
||||||
"vue-codemirror": "^4.0.3",
|
"vue-codemirror": "^4.0.3",
|
||||||
|
@ -73,6 +73,7 @@
|
||||||
"lint": "eslint -f unix client/src core/src csseditor/src",
|
"lint": "eslint -f unix client/src core/src csseditor/src",
|
||||||
"test": "npm run build && npm run lint",
|
"test": "npm run build && npm run lint",
|
||||||
"build_node-sass": "node scripts/build-node-sass.js",
|
"build_node-sass": "node scripts/build-node-sass.js",
|
||||||
"release": "npm run lint && npm run build && gulp release"
|
"build_release": "npm run release --prefix client && npm run build --prefix core && npm run release --prefix csseditor && npm run build --prefix installer",
|
||||||
|
"release": "npm run lint && npm run build_release && gulp release"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,11 @@ set "ELECTRON=1.6.15"
|
||||||
set "PLATFORM=win32"
|
set "PLATFORM=win32"
|
||||||
set "ARCH=ia32"
|
set "ARCH=ia32"
|
||||||
set "VER=53"
|
set "VER=53"
|
||||||
set "VENDOR_PATH=..\node_modules\node-sass\vendor"
|
set "VENDOR_PATH=.\node_modules\node-sass\vendor"
|
||||||
set "BUILD_PATH=..\node_modules\node-sass\build\Release\binding.node"
|
set "BUILD_PATH=.\node_modules\node-sass\build\Release\binding.node"
|
||||||
|
|
||||||
echo Building %PLATFORM%-%ARCH% bindings
|
echo Building %PLATFORM%-%ARCH% bindings
|
||||||
call ../node_modules/.bin/electron-rebuild -v=%ELECTRON% -a=%ARCH% -m ../node_modules/node-sass
|
call ./node_modules/.bin/electron-rebuild -v=%ELECTRON% -a=%ARCH% -m ./node_modules/node-sass
|
||||||
|
|
||||||
if exist %VENDOR_PATH%\%PLATFORM%-%ARCH%-%VER%\binding.node (
|
if exist %VENDOR_PATH%\%PLATFORM%-%ARCH%-%VER%\binding.node (
|
||||||
echo Deleting old %VENDOR_PATH%\%PLATFORM%-%ARCH%-%VER%\binding.node
|
echo Deleting old %VENDOR_PATH%\%PLATFORM%-%ARCH%-%VER%\binding.node
|
||||||
|
@ -30,7 +30,7 @@ if not exist %BUILD_PATH% (
|
||||||
set "ARCH=x64"
|
set "ARCH=x64"
|
||||||
|
|
||||||
echo Building %PLATFORM%-%ARCH% bindings
|
echo Building %PLATFORM%-%ARCH% bindings
|
||||||
call ../node_modules/.bin/electron-rebuild -v=%ELECTRON% -a=%ARCH% -m ../node_modules/node-sass
|
call ./node_modules/.bin/electron-rebuild -v=%ELECTRON% -a=%ARCH% -m ./node_modules/node-sass
|
||||||
|
|
||||||
if exist %VENDOR_PATH%\%PLATFORM%-%ARCH%-%VER%\binding.node (
|
if exist %VENDOR_PATH%\%PLATFORM%-%ARCH%-%VER%\binding.node (
|
||||||
echo Deleting old %VENDOR_PATH%\%PLATFORM%-%ARCH%-%VER%\binding.node
|
echo Deleting old %VENDOR_PATH%\%PLATFORM%-%ARCH%-%VER%\binding.node
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
"version": "0.3.2",
|
|
||||||
"paths": []
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>CSS Editor</title>
|
|
||||||
<link rel="stylesheet" href="../../node_modules/codemirror/lib/codemirror.css" />
|
|
||||||
<link rel="stylesheet" href="../../node_modules/codemirror/theme/material.css" />
|
|
||||||
<link rel="stylesheet" href="../../node_modules/codemirror/addon/scroll/simplescrollbars.css" />
|
|
||||||
<link rel="stylesheet" href="../../node_modules/codemirror/addon/dialog/dialog.css" />
|
|
||||||
<link rel="stylesheet" href="../../node_modules/codemirror/addon/hint/show-hint.css" />
|
|
||||||
<link rel="stylesheet" href="./main.css" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<div class="titlebar">
|
|
||||||
<div class="draggable"></div>
|
|
||||||
<div class="icon">
|
|
||||||
<div class="inner"></div>
|
|
||||||
</div>
|
|
||||||
<div class="title">CSS Editor</div>
|
|
||||||
<div class="flex-spacer"></div>
|
|
||||||
<div class="controls">
|
|
||||||
<button title="Toggle always on top" id="toggleaot">P</button>
|
|
||||||
<button title="Close CSS Editor" id="closeeditor">X</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="spinner"><div class="valign">Loading Please Wait...</div></div>
|
|
||||||
<div class="editor" id="editor">
|
|
||||||
</div>
|
|
||||||
<div class="tools">
|
|
||||||
<div class="flex-row">
|
|
||||||
<button id="btnSave">Save</button>
|
|
||||||
<button id="btnUpdate">Update</button>
|
|
||||||
<div class="flex-spacer"></div>
|
|
||||||
<div id="chkboxLiveUpdate"><input type="checkbox"><span>Live Update</span></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>const $ = require('../../node_modules/jquery/dist/jquery.min.js');</script>
|
|
||||||
<script src="../../node_modules/codemirror/lib/codemirror.js"></script>
|
|
||||||
<script src="../../node_modules/codemirror/mode/css/css.js"></script>
|
|
||||||
<script src="../../node_modules/codemirror/addon/scroll/simplescrollbars.js"></script>
|
|
||||||
<script src="../../node_modules/codemirror/addon/search/search.js"></script>
|
|
||||||
<script src="../../node_modules/codemirror/addon/search/searchcursor.js"></script>
|
|
||||||
<script src="../../node_modules/codemirror/addon/search/jump-to-line.js"></script>
|
|
||||||
<script src="../../node_modules/codemirror/addon/dialog/dialog.js"></script>
|
|
||||||
<script src="../../node_modules/codemirror/addon/hint/show-hint.js"></script>
|
|
||||||
<script src="../../node_modules/codemirror/addon/hint/css-hint.js"></script>
|
|
||||||
<script src="./main.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue