diff --git a/core/src/main.js b/core/src/main.js index c9dc6fa4..8aad0c90 100644 --- a/core/src/main.js +++ b/core/src/main.js @@ -15,43 +15,40 @@ const { FileUtils, BDIpc, Config, WindowUtils, CSSEditor, Database } = require(' const { BrowserWindow, dialog } = require('electron'); const tests = true; -const _basePath = __dirname; + +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 - ? path.resolve(__dirname, '..', '..', 'client', 'dist', 'betterdiscord.client.js') - : path.resolve(__dirname, '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'); + ? path.resolve(_basePath, 'client', 'dist', 'betterdiscord.client.js') + : path.resolve(_basePath, 'betterdiscord.client.js'); const _cssEditorPath = tests ? path.resolve(__dirname, '..', '..', 'csseditor', 'dist') : 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 paths = [ - { id: 'base', path: _basePath.replace(/\\/g, '/') }, - { id: 'cs', path: _clientScript.replace(/\\/g, '/') }, - { id: 'data', path: _dataPath.replace(/\\/g, '/') }, - { id: 'ext', path: _extPath.replace(/\\/g, '/') }, - { id: 'plugins', path: _pluginPath.replace(/\\/g, '/') }, - { id: 'themes', path: _themePath.replace(/\\/g, '/') }, - { id: 'modules', path: _modulePath.replace(/\\/g, '/') }, - { id: 'csseditor', path: _cssEditorPath.replace(/\\/g, '/') } + { id: 'base', path: _basePath }, + { id: 'cs', path: _clientScript }, + { id: 'data', path: _dataPath }, + { id: 'ext', path: _extPath }, + { id: 'plugins', path: _pluginPath }, + { id: 'themes', path: _themePath }, + { id: 'modules', path: _modulePath }, + { id: 'csseditor', path: _cssEditorPath } ]; -const sparkplug = path.resolve(__dirname, 'sparkplug.js').replace(/\\/g, '/'); - -const Common = {}; const globals = { version: '2.0.0a', paths -} - -const dbInstance = new Database(paths.find(path => path.id === 'data').path); +}; class Comms { @@ -61,9 +58,7 @@ class Comms { } initListeners() { - BDIpc.on('bd-getConfig', o => { - o.reply(Common.Config.config); - }); + BDIpc.on('bd-getConfig', o => o.reply(this.bd.config.config)); BDIpc.on('bd-sendToDiscord', event => this.bd.windowUtils.send(event.args.channel, event.args.message)); @@ -71,9 +66,6 @@ class Comms { // 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-readJson', o => this.readFile(o, true)); - BDIpc.on('bd-native-open', o => { dialog.showOpenDialog(BrowserWindow.fromWebContents(o.ipcEvent.sender), o.args, filenames => { o.reply(filenames); @@ -88,36 +80,22 @@ class Comms { } sass.render(o.args, (err, result) => { - if (err) { - o.reply({ err }); - return; - } - o.reply(result); + if (err) return o.reply({ err }); + else o.reply(result); }); }); BDIpc.on('bd-dba', o => { (async () => { try { - const ret = await dbInstance.exec(o.args); - o.reply(ret); + o.reply(await this.bd.dbInstance.exec(o.args)); } catch (err) { - o.reply({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) { BDIpc.send(channel, message); } @@ -135,23 +113,25 @@ class BetterDiscord { this.injectScripts = this.injectScripts.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.paths.find(path => path.id === 'data').path); this.comms = new Comms(this); + this.init(); } async init() { - const window = await this.waitForWindow(); - this.windowUtils = new WindowUtils({ window }); + await this.waitForWindowUtils(); - await FileUtils.ensureDirectory(paths.find(path => path.id === 'ext').path); + await FileUtils.ensureDirectory(this.config.paths.find(path => path.id === 'ext').path); - this.csseditor = new CSSEditor(this, paths.find(path => path.id === 'csseditor').path); + this.csseditor = new CSSEditor(this, this.config.paths.find(path => path.id === 'csseditor').path); - this.windowUtils.events('did-get-response-details', () => this.ignite(this.windowUtils.window)); - this.windowUtils.events('did-finish-load', e => this.injectScripts(true)); + this.windowUtils.on('did-get-response-details', () => this.ignite()); + this.windowUtils.on('did-finish-load', e => this.injectScripts(true)); - this.windowUtils.events('did-navigate-in-page', (event, url, isMainFrame) => { + this.windowUtils.on('did-navigate-in-page', (event, url, isMainFrame) => { this.windowUtils.send('did-navigate-in-page', { event, url, isMainFrame }); }); @@ -166,10 +146,8 @@ class BetterDiscord { const defer = setInterval(() => { const windows = BrowserWindow.getAllWindows(); - if (windows.length > 0) { - windows.forEach(window => { - self.ignite(window); - }); + for (let window of windows) { + BetterDiscord.ignite(window); } if (windows.length === 1 && windows[0].webContents.getURL().includes('discordapp.com')) { @@ -180,23 +158,45 @@ class BetterDiscord { }); } - ignite(window) { - //Hook things that Discord removes from global. These will be removed again in the client script - window.webContents.executeJavaScript(`require("${sparkplug}");`); + async waitForWindowUtils() { + if (this.windowUtils) return this.windowUtils; + 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) { console.log(`RELOAD? ${reload}`); if (!tests) { - const files = await FileUtils.listDirectory(paths.find(path => path.id === 'base').path); + const files = await FileUtils.listDirectory(this.config.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.config.paths.find(path => path.id === 'cs').path = path.resolve(this.config.paths.find(path => path.id === 'base').path, latestCs); } - this.windowUtils.injectScript(paths.find(path => path.id === 'cs').path); + return this.windowUtils.injectScript(this.config.paths.find(path => path.id === 'cs').path); } - get fileUtils() { return FileUtils; } - } module.exports = { diff --git a/core/src/modules/bdipc.js b/core/src/modules/bdipc.js index c9fd7d46..7cfcdb31 100644 --- a/core/src/modules/bdipc.js +++ b/core/src/modules/bdipc.js @@ -5,14 +5,15 @@ * 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. + * LICENSE file in the root directory of this source tree. */ + const { Module } = require('./modulebase'); const { ipcMain } = require('electron'); class BDIpcEvent extends Module { - + constructor(event, args) { super(args); this.ipcEvent = event; @@ -34,10 +35,9 @@ class BDIpcEvent extends Module { } class BDIpc { - static on(channel, cb) { ipcMain.on(channel, (event, args) => cb(new BDIpcEvent(event, args))); } } -module.exports = { BDIpc }; \ No newline at end of file +module.exports = { BDIpc }; diff --git a/core/src/modules/config.js b/core/src/modules/config.js index cb73e865..417de899 100644 --- a/core/src/modules/config.js +++ b/core/src/modules/config.js @@ -5,7 +5,7 @@ * 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. + * LICENSE file in the root directory of this source tree. */ const { Module } = require('./modulebase'); @@ -22,11 +22,11 @@ class Config extends Module { get config() { return { - 'version': this.version, - 'paths': this.paths + version: this.version, + paths: this.paths }; } } -module.exports = { Config }; \ No newline at end of file +module.exports = { Config }; diff --git a/core/src/modules/csseditor.js b/core/src/modules/csseditor.js index a70274a9..9e50d053 100644 --- a/core/src/modules/csseditor.js +++ b/core/src/modules/csseditor.js @@ -22,6 +22,10 @@ class CSSEditor extends Module { this.bd = bd; } + /** + * Opens an editor and replies to an IPC event. + * @param {BDIpcEvent} event + */ openEditor(o) { if (this.editor) { if (this.editor.isFocused()) return; @@ -32,12 +36,7 @@ class CSSEditor extends Module { return; } - const options = this.options; - for (let option in o.args) { - if (o.args.hasOwnProperty(option)) { - options[option] = o.args[option]; - } - } + const options = Object.assign({}, this.options, o.message); this.editor = new BrowserWindow(options); this.editor.loadURL('about:blank'); @@ -59,20 +58,34 @@ class CSSEditor extends Module { }); } + /** + * Sets the SCSS in the editor. + */ setSCSS(scss) { this.send('set-scss', scss); } + /** + * Sends data to the editor. + * @param {String} channel + * @param {Any} data + */ send(channel, data) { if (!this.editor) return; this.editor.webContents.send(channel, data); } + /** + * Sets the CSS editor's always on top flag. + */ set alwaysOnTop(state) { if (!this.editor) return; this.editor.setAlwaysOnTop(state); } + /** + * Default options to pass to BrowserWindow. + */ get options() { return { width: 800, @@ -81,7 +94,7 @@ class CSSEditor extends Module { frame: false }; } - + } module.exports = { CSSEditor }; diff --git a/core/src/modules/database.js b/core/src/modules/database.js index e0844954..6f79df86 100644 --- a/core/src/modules/database.js +++ b/core/src/modules/database.js @@ -10,7 +10,7 @@ const Datastore = require('nedb'); -class Database { +class Database { constructor(dbPath) { this.exec = this.exec.bind(this); diff --git a/core/src/modules/modulebase.js b/core/src/modules/modulebase.js index 20cba6ca..3713a162 100644 --- a/core/src/modules/modulebase.js +++ b/core/src/modules/modulebase.js @@ -5,20 +5,19 @@ * 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. -*/ - -/* -Base Module that every non-static module should extend + * LICENSE file in the root directory of this source tree. */ +/** + * Base Module that every non-static module should extend. + */ class Module { constructor(args) { this.__ = { state: args, args - } + }; this.init(); } @@ -33,4 +32,4 @@ class Module { } -module.exports = { Module }; \ No newline at end of file +module.exports = { Module }; diff --git a/core/src/modules/utils.js b/core/src/modules/utils.js index 3a31dbb0..12c205f1 100644 --- a/core/src/modules/utils.js +++ b/core/src/modules/utils.js @@ -10,43 +10,35 @@ // TODO Use common -const - path = require('path'), - fs = require('fs'); +const path = require('path'); +const fs = require('fs'); const { Module } = require('./modulebase'); class Utils { - static async tryParseJson(jsonString) { try { return JSON.parse(jsonString); - }catch(err) { + } catch (err) { throw ({ - 'message': 'Failed to parse json', + message: 'Failed to parse json', err }); } } - - static get timestamp() { - return 'Timestamp'; - } - } class FileUtils { - static async fileExists(path) { return new Promise((resolve, reject) => { fs.stat(path, (err, stats) => { if(err) return reject({ - 'message': `No such file or directory: ${err.path}`, + message: `No such file or directory: ${err.path}`, err }); if(!stats.isFile()) return reject({ - 'message': `Not a file: ${path}`, + message: `Not a file: ${path}`, stats }); @@ -59,12 +51,12 @@ class FileUtils { return new Promise((resolve, reject) => { fs.stat(path, (err, stats) => { if(err) return reject({ - 'message': `Directory does not exist: ${path}`, + message: `Directory does not exist: ${path}`, err }); if(!stats.isDirectory()) return reject({ - 'message': `Not a directory: ${path}`, + message: `Not a directory: ${path}`, stats }); @@ -76,14 +68,14 @@ class FileUtils { static async readFile(path) { try { await this.fileExists(path); - } catch(err) { - throw(err); + } catch (err) { + throw err; } return new Promise((resolve, reject) => { fs.readFile(path, 'utf-8', (err, data) => { - if(err) reject({ - 'message': `Could not read file: ${path}`, + if(err) return reject({ + message: `Could not read file: ${path}`, err }); @@ -97,14 +89,13 @@ class FileUtils { try { readFile = await this.readFile(path); } catch(err) { - throw(err); + throw err; } try { - const parsed = await Utils.tryParseJson(readFile); - return parsed; - } catch(err) { - throw(Object.assign(err, { path })); + return await Utils.tryParseJson(readFile); + } catch (err) { + throw Object.assign(err, { path }); } } @@ -113,8 +104,8 @@ class FileUtils { await this.directoryExists(path); return new Promise((resolve, reject) => { fs.readdir(path, (err, files) => { - if (err) return reject(err); - resolve(files); + if (err) reject(err); + else resolve(files); }); }); } catch (err) { @@ -145,11 +136,8 @@ class FileUtils { static async createDirectory(path) { return new Promise((resolve, reject) => { fs.mkdir(path, err => { - if (err) { - if (err.code === 'EEXIST') return resolve(); - else return reject(err); - } - resolve(); + if (err) reject(err); + else resolve(); }); }); } @@ -170,7 +158,6 @@ class FileUtils { } class WindowUtils extends Module { - bindings() { this.openDevTools = this.openDevTools.bind(this); this.executeJavascript = this.executeJavascript.bind(this); @@ -194,16 +181,20 @@ class WindowUtils extends Module { } injectScript(fpath, variable) { - console.log(`Injecting: ${fpath}`); + return WindowUtils.injectScript(this.window, fpath, variable); + } + + static injectScript(window, fpath, variable) { + // console.log(`Injecting: ${fpath}`); const escaped_path = fpath.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); const escaped_variable = variable ? variable.replace(/\\/g, '\\\\').replace(/"/g, '\\"') : null; - if (variable) this.executeJavascript(`window["${escaped_variable}"] = require("${escaped_path}");`); - else this.executeJavascript(`require("${escaped_path}");`); + if (variable) window.executeJavaScript(`window["${escaped_variable}"] = require("${escaped_path}");`); + else window.executeJavaScript(`require("${escaped_path}");`); } - events(event, callback) { + on(event, callback) { this.webContents.on(event, callback); } @@ -211,7 +202,6 @@ class WindowUtils extends Module { channel = channel.startsWith('bd-') ? channel : `bd-${channel}`; this.webContents.send(channel, message); } - } module.exports = { diff --git a/csseditor/src/Editor.vue b/csseditor/src/Editor.vue index d369aa61..2eb8de54 100644 --- a/csseditor/src/Editor.vue +++ b/csseditor/src/Editor.vue @@ -33,18 +33,18 @@