Comments and fix tooltip arrow positioning
This commit is contained in:
parent
994faf94d6
commit
b4bd9e9c7b
|
@ -91,9 +91,9 @@ export default class Content {
|
|||
config: this.settings.strip().settings,
|
||||
data: this.data
|
||||
});
|
||||
this.settings.setSaved();
|
||||
this.settings.setSaved();
|
||||
} catch (err) {
|
||||
Logger.err(this.name, ['Failed to save configuration', err]);
|
||||
Logger.err(this.name, ['Failed to save configuration', err]);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,12 @@ export default class {
|
|||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
return ClientIPC.send('bd-dba', { action: 'find', args });
|
||||
|
|
|
@ -402,7 +402,7 @@ export default class DiscordApi {
|
|||
|
||||
static get currentChannel() {
|
||||
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() {
|
||||
|
@ -415,5 +415,5 @@ export default class DiscordApi {
|
|||
for (const id of friends) returnUsers.push(User.fromId(id));
|
||||
return returnUsers;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* BetterDiscord WebpackModules Module
|
||||
* BetterDiscord Event Hook
|
||||
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
|
||||
* All rights reserved.
|
||||
* https://betterdiscord.net
|
||||
|
@ -8,14 +8,13 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import EventListener from './eventlistener';
|
||||
import { Utils } from 'common';
|
||||
import Events from './events';
|
||||
import { Utils, ClientLogger as Logger } from 'common';
|
||||
import { WebpackModules } from './webpackmodules';
|
||||
import Events from './events';
|
||||
import EventListener from './eventlistener';
|
||||
|
||||
import * as SocketStructs from '../structs/socketstructs';
|
||||
|
||||
|
||||
/**
|
||||
* Discord socket event hook
|
||||
* @extends {EventListener}
|
||||
|
@ -23,7 +22,7 @@ import * as SocketStructs from '../structs/socketstructs';
|
|||
export default class extends EventListener {
|
||||
|
||||
init() {
|
||||
console.log(SocketStructs);
|
||||
Logger.log('EventHook', SocketStructs);
|
||||
this.hook();
|
||||
}
|
||||
|
||||
|
@ -44,11 +43,11 @@ export default class extends EventListener {
|
|||
orig.call(this, ...args);
|
||||
self.wsc = this;
|
||||
self.emit(...args);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
get eventsModule() {
|
||||
return WebpackModules.getModuleByPrototypes(['setMaxListeners', 'emit']);
|
||||
return WebpackModules.getModuleByName('Events');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -66,8 +65,8 @@ export default class extends EventListener {
|
|||
|
||||
/**
|
||||
* Emit callback
|
||||
* @param {any} e Event Action
|
||||
* @param {any} d Event Args
|
||||
* @param {any} event Event
|
||||
* @param {any} data Event data
|
||||
*/
|
||||
dispatch(e, 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_DELETE: 'LFG_LISTING_DELETE', // Thank you
|
||||
BRAINTREE_POPUP_BRIDGE_CALLBACK: 'BRAINTREE_POPUP_BRIDGE_CALLBACK' // What
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,14 +12,39 @@ import { EventEmitter } from 'events';
|
|||
const emitter = new EventEmitter();
|
||||
|
||||
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) {
|
||||
emitter.emit(...args);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
const eventemitters = new WeakMap();
|
||||
|
||||
export default class EventsWrapper {
|
||||
constructor(eventemitter) {
|
||||
|
||||
constructor(eventemitter, bind) {
|
||||
eventemitters.set(this, eventemitter);
|
||||
}
|
||||
|
||||
|
@ -19,26 +20,33 @@ export default class EventsWrapper {
|
|||
return this._eventSubs || (this._eventSubs = []);
|
||||
}
|
||||
|
||||
get on() { return this.subscribe }
|
||||
subscribe(event, callback) {
|
||||
if (this.eventSubs.find(e => e.event === event && e.callback === callback)) return;
|
||||
this.eventSubs.push({
|
||||
event,
|
||||
callback
|
||||
});
|
||||
eventemitters.get(this).on(event, callback);
|
||||
const boundCallback = () => callback.apply(this.bind, arguments);
|
||||
this.eventSubs.push({ event, callback, boundCallback });
|
||||
eventemitters.get(this).on(event, boundCallback);
|
||||
}
|
||||
|
||||
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) {
|
||||
for (let index of this.eventSubs) {
|
||||
if (this.eventSubs[index].event !== event || (callback && this.eventSubs[index].callback === callback)) return;
|
||||
eventemitters.get(this).off(event, this.eventSubs[index].callback);
|
||||
if (this.eventSubs[index].event !== event || (callback && this.eventSubs[index].callback === callback)) continue;
|
||||
eventemitters.get(this).off(event, this.eventSubs[index].boundCallback);
|
||||
this.eventSubs.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
unsubscribeAll() {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
export default class Module {
|
||||
|
||||
constructor(args) {
|
||||
this.__ = {
|
||||
state: args || {},
|
||||
args
|
||||
}
|
||||
};
|
||||
this.setState = this.setState.bind(this);
|
||||
this.initialize();
|
||||
}
|
||||
|
@ -38,7 +37,6 @@ export default class Module {
|
|||
set args(t) { }
|
||||
get args() { return this.__.args; }
|
||||
|
||||
|
||||
set state(state) { return this.__.state = state; }
|
||||
get state() { return this.__.state; }
|
||||
|
||||
|
|
|
@ -18,6 +18,9 @@ import Updater from './updater';
|
|||
*/
|
||||
export default class {
|
||||
|
||||
/**
|
||||
* An array of modules.
|
||||
*/
|
||||
static get modules() {
|
||||
return this._modules ? this._modules : (this._modules = [
|
||||
new ProfileBadges(),
|
||||
|
@ -28,6 +31,10 @@ export default class {
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all modules.
|
||||
* @return {Promise}
|
||||
*/
|
||||
static async initModules() {
|
||||
for (let module of this.modules) {
|
||||
try {
|
||||
|
|
|
@ -15,12 +15,8 @@ export default class Plugin extends Content {
|
|||
|
||||
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 stop() { return this.disable }
|
||||
get stop() { return this.disable }
|
||||
|
||||
unload() {
|
||||
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
|
||||
* All rights reserved.
|
||||
* https://betterdiscord.net
|
||||
|
@ -8,7 +8,7 @@
|
|||
* 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 ExtModuleManager from './extmodulemanager';
|
||||
import PluginManager from './pluginmanager';
|
||||
|
@ -24,27 +24,20 @@ import { MonkeyPatch } from './patcher';
|
|||
|
||||
export default class PluginApi {
|
||||
|
||||
constructor(pluginInfo) {
|
||||
constructor(pluginInfo, pluginPath) {
|
||||
this.pluginInfo = pluginInfo;
|
||||
this.pluginPath = pluginPath;
|
||||
|
||||
this.Events = new EventsWrapper(Events);
|
||||
Utils.defineSoftGetter(this.Events, 'bind', () => this.plugin);
|
||||
|
||||
this._menuItems = undefined;
|
||||
this._injectedStyles = 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() {
|
||||
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) {
|
||||
|
@ -61,15 +54,18 @@ export default class PluginApi {
|
|||
|
||||
get Api() { return this }
|
||||
|
||||
get AsyncEventEmitter() { return AsyncEventEmitter }
|
||||
get EventsWrapper() { return EventsWrapper }
|
||||
|
||||
/**
|
||||
* Logger
|
||||
*/
|
||||
|
||||
loggerLog(...message) { Logger.log(this.pluginInfo.name, message) }
|
||||
loggerErr(...message) { Logger.err(this.pluginInfo.name, message) }
|
||||
loggerWarn(...message) { Logger.warn(this.pluginInfo.name, message) }
|
||||
loggerInfo(...message) { Logger.info(this.pluginInfo.name, message) }
|
||||
loggerDbg(...message) { Logger.dbg(this.pluginInfo.name, message) }
|
||||
loggerLog(...message) { Logger.log(this.plugin.name, message) }
|
||||
loggerErr(...message) { Logger.err(this.plugin.name, message) }
|
||||
loggerWarn(...message) { Logger.warn(this.plugin.name, message) }
|
||||
loggerInfo(...message) { Logger.info(this.plugin.name, message) }
|
||||
loggerDbg(...message) { Logger.dbg(this.plugin.name, message) }
|
||||
get Logger() {
|
||||
return {
|
||||
log: this.loggerLog.bind(this),
|
||||
|
@ -381,6 +377,24 @@ export default class PluginApi {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* DiscordApi
|
||||
*/
|
||||
|
||||
get Discord() {
|
||||
return DiscordApi;
|
||||
}
|
||||
|
||||
get ReactComponents() {
|
||||
return ReactComponents;
|
||||
}
|
||||
get Reflection() {
|
||||
return Reflection;
|
||||
}
|
||||
get MonkeyPatch() {
|
||||
return module => MonkeyPatch(this.plugin.id, module);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (permissions && permissions.length > 0) {
|
||||
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 {
|
||||
const allowed = await Modals.permissions(`${info.name} wants to:`, info.name, permissions).promise;
|
||||
} catch (err) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,7 @@ export default class extends ContentManager {
|
|||
}
|
||||
}
|
||||
|
||||
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))
|
||||
throw {message: `Plugin ${info.name} did not return a class that extends Plugin.`};
|
||||
|
||||
|
|
|
@ -8,22 +8,23 @@
|
|||
* 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 { SettingsSet, SettingUpdatedEvent } from 'structs';
|
||||
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 {
|
||||
|
||||
constructor() {
|
||||
this.settings = defaultSettings.map(_set => {
|
||||
const set = new SettingsSet(_set);
|
||||
|
||||
set.on('setting-updated', 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-${set.id}_${category.id}_${setting.id}`, event);
|
||||
});
|
||||
|
@ -37,6 +38,9 @@ export default new class Settings {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads BetterDiscord's settings.
|
||||
*/
|
||||
async loadSettings() {
|
||||
try {
|
||||
await FileUtils.ensureDirectory(this.dataPath);
|
||||
|
@ -48,7 +52,7 @@ export default new class Settings {
|
|||
for (let set of this.settings) {
|
||||
const newSet = settings.find(s => s.id === set.id);
|
||||
if (!newSet) continue;
|
||||
set.merge(newSet);
|
||||
await set.merge(newSet);
|
||||
set.setSaved();
|
||||
}
|
||||
|
||||
|
@ -61,6 +65,9 @@ export default new class Settings {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves BetterDiscord's settings including CSS editor data.
|
||||
*/
|
||||
async saveSettings() {
|
||||
try {
|
||||
await FileUtils.ensureDirectory(this.dataPath);
|
||||
|
@ -72,15 +79,10 @@ export default new class Settings {
|
|||
css: CssEditor.css,
|
||||
css_editor_files: CssEditor.files,
|
||||
scss_error: CssEditor.error,
|
||||
css_editor_bounds: {
|
||||
width: CssEditor.editor_bounds.width,
|
||||
height: CssEditor.editor_bounds.height,
|
||||
x: CssEditor.editor_bounds.x,
|
||||
y: CssEditor.editor_bounds.y
|
||||
}
|
||||
css_editor_bounds: CssEditor.editor_bounds
|
||||
});
|
||||
|
||||
for (let set of this.getSettings) {
|
||||
for (let set of this.settings) {
|
||||
set.setSaved();
|
||||
}
|
||||
} 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) {
|
||||
return this.getSettings.find(s => s.id === set_id);
|
||||
return this.settings.find(s => s.id === set_id);
|
||||
}
|
||||
|
||||
get core() { return this.getSet('core') }
|
||||
|
@ -100,39 +107,46 @@ export default new class Settings {
|
|||
get css() { return this.getSet('css') }
|
||||
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) {
|
||||
const set = this.getSet(set_id);
|
||||
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) {
|
||||
const set = this.getSet(set_id);
|
||||
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) {
|
||||
const set = this.getSet(set_id);
|
||||
return set ? set.get(category_id, setting_id) : undefined;
|
||||
}
|
||||
|
||||
async mergeSettings(set_id, newSettings) {
|
||||
const set = this.getSet(set_id);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* The path to store user data in.
|
||||
*/
|
||||
get dataPath() {
|
||||
return Globals.getPath('data');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { ClientLogger as Logger } from 'common';
|
||||
import EventListener from './eventlistener';
|
||||
|
||||
export default class SocketProxy extends EventListener {
|
||||
|
@ -19,7 +20,7 @@ export default class SocketProxy extends EventListener {
|
|||
|
||||
get eventBindings() {
|
||||
return [
|
||||
{ id: 'socket-created', 'callback': this.socketCreated }
|
||||
{ id: 'socket-created', callback: this.socketCreated }
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -29,11 +30,11 @@ export default class SocketProxy extends EventListener {
|
|||
|
||||
socketCreated(socket) {
|
||||
this.activeSocket = socket;
|
||||
// socket.addEventListener('message', this.onMessage);
|
||||
// socket.addEventListener('message', this.onMessage);
|
||||
}
|
||||
|
||||
onMessage(e) {
|
||||
console.info(e);
|
||||
Logger.info('SocketProxy', e);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,10 +31,6 @@ export default class Theme extends Content {
|
|||
get type() { return 'theme' }
|
||||
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.
|
||||
* This can be overridden by other content types.
|
||||
|
@ -63,7 +59,7 @@ export default class Theme extends Content {
|
|||
* @return {Promise}
|
||||
*/
|
||||
async compile() {
|
||||
console.log('Compiling CSS');
|
||||
Logger.log(this.name, 'Compiling CSS');
|
||||
|
||||
if (this.info.type === 'sass') {
|
||||
const config = await ThemeManager.getConfigAsSCSS(this.settings);
|
||||
|
@ -76,7 +72,7 @@ export default class Theme extends Content {
|
|||
Logger.log(this.name, ['Finished compiling theme', new class Info {
|
||||
get SCSS_variables() { console.log(config); }
|
||||
get Compiled_SCSS() { console.log(result.css.toString()); }
|
||||
get Result() { console.log(result); }
|
||||
get Result() { console.log(result); }
|
||||
}]);
|
||||
|
||||
return {
|
||||
|
@ -121,6 +117,7 @@ export default class Theme extends Content {
|
|||
*/
|
||||
set files(files) {
|
||||
this.data.files = files;
|
||||
|
||||
if (Settings.get('css', 'default', 'watch-files'))
|
||||
this.watchfiles = files;
|
||||
}
|
||||
|
|
|
@ -74,6 +74,11 @@ export default class ThemeManager extends ContentManager {
|
|||
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) {
|
||||
const variables = [];
|
||||
|
||||
|
@ -87,6 +92,11 @@ export default class ThemeManager extends ContentManager {
|
|||
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) {
|
||||
const variables = [];
|
||||
|
||||
|
@ -100,6 +110,11 @@ export default class ThemeManager extends ContentManager {
|
|||
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) {
|
||||
const { type, id, value } = setting;
|
||||
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];
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes a string so it can be included in SCSS.
|
||||
* @param {String} value The string to escape
|
||||
* @return {String}
|
||||
*/
|
||||
static toSCSSString(value) {
|
||||
if (typeof value !== 'string' && value.toString) value = value.toString();
|
||||
return `'${typeof value === 'string' ? value.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') : ''}'`;
|
||||
|
|
|
@ -22,6 +22,9 @@ export default class {
|
|||
this.checkForUpdates = this.checkForUpdates.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* The interval to wait before checking for updates.
|
||||
*/
|
||||
get interval() {
|
||||
return 60 * 1000 * 30;
|
||||
}
|
||||
|
@ -30,34 +33,51 @@ export default class {
|
|||
this.updateInterval = setInterval(this.checkForUpdates, this.interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs an update.
|
||||
* TODO
|
||||
*/
|
||||
update() {
|
||||
// TODO
|
||||
this.updatesAvailable = false;
|
||||
Events.emit('update-check-end');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for updates.
|
||||
* @return {Promise}
|
||||
*/
|
||||
checkForUpdates() {
|
||||
if (this.updatesAvailable) return;
|
||||
Events.emit('update-check-start');
|
||||
Logger.info('Updater', 'Checking for updates');
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: 'https://rawgit.com/JsSucks/BetterDiscordApp/master/package.json',
|
||||
cache: false,
|
||||
success: e => {
|
||||
try {
|
||||
Events.emit('update-check-end');
|
||||
Logger.info('Updater',
|
||||
`Latest Version: ${e.version} - Current Version: ${Globals.version}`);
|
||||
if (e.version !== Globals.version) {
|
||||
this.updatesAvailable = true;
|
||||
Events.emit('updates-available');
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.updatesAvailable) return resolve(true);
|
||||
Events.emit('update-check-start');
|
||||
Logger.info('Updater', 'Checking for updates');
|
||||
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: 'https://rawgit.com/JsSucks/BetterDiscordApp/master/package.json',
|
||||
cache: false,
|
||||
success: e => {
|
||||
try {
|
||||
Events.emit('update-check-end');
|
||||
Logger.info('Updater', `Latest Version: ${e.version} - Current Version: ${Globals.getObject('version')}`);
|
||||
|
||||
if (e.version !== Globals.getObject('version')) {
|
||||
this.updatesAvailable = true;
|
||||
Events.emit('updates-available');
|
||||
resolve(true);
|
||||
}
|
||||
|
||||
resolve(false);
|
||||
} catch (err) {
|
||||
Events.emit('update-check-fail', err);
|
||||
reject(err);
|
||||
}
|
||||
} catch (err) {
|
||||
},
|
||||
fail: err => {
|
||||
Events.emit('update-check-fail', err);
|
||||
reject(err);
|
||||
}
|
||||
},
|
||||
fail: e => Events.emit('update-check-fail', e)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -16,22 +16,21 @@ export { jQuery as $ };
|
|||
|
||||
export default class {
|
||||
|
||||
static get jQuery() {
|
||||
return jQuery;
|
||||
}
|
||||
/**
|
||||
* jQuery
|
||||
*/
|
||||
static get jQuery() { return jQuery }
|
||||
static get $() { return this.jQuery }
|
||||
|
||||
static get $() {
|
||||
return this.jQuery;
|
||||
}
|
||||
|
||||
static get lodash() {
|
||||
return lodash;
|
||||
}
|
||||
|
||||
static get _() {
|
||||
return this.lodash;
|
||||
}
|
||||
/**
|
||||
* Lodash
|
||||
*/
|
||||
static get lodash() { return lodash }
|
||||
static get _() { return this.lodash }
|
||||
|
||||
/**
|
||||
* Moment
|
||||
*/
|
||||
static get moment() {
|
||||
return WebpackModules.getModuleByName('Moment');
|
||||
}
|
||||
|
|
|
@ -51,6 +51,8 @@ const KnownModules = {
|
|||
React: Filters.byProperties(['createElement', 'cloneElement']),
|
||||
ReactDOM: Filters.byProperties(['render', 'findDOMNode']),
|
||||
|
||||
Events: Filters.byPrototypeFields(['setMaxListeners', 'emit']),
|
||||
|
||||
/* Guild Info, Stores, and Utilities */
|
||||
GuildStore: Filters.byProperties(['getGuild']),
|
||||
SortedGuildStore: Filters.byProperties(['getSortedGuilds']),
|
||||
|
@ -207,37 +209,37 @@ const KnownModules = {
|
|||
|
||||
export class WebpackModules {
|
||||
|
||||
/**
|
||||
* Finds a module using a filter function.
|
||||
* @param {Function} filter A function to use to filter modules
|
||||
* @param {Boolean} first Whether to return only the first matching module
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModule(filter, first = true) {
|
||||
const modules = this.getAllModules();
|
||||
const rm = [];
|
||||
for (let index in modules) {
|
||||
if (!modules.hasOwnProperty(index)) continue;
|
||||
const module = modules[index];
|
||||
const { exports } = module;
|
||||
let foundModule = null;
|
||||
/**
|
||||
* Finds a module using a filter function.
|
||||
* @param {Function} filter A function to use to filter modules
|
||||
* @param {Boolean} first Whether to return only the first matching module
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModule(filter, first = true) {
|
||||
const modules = this.getAllModules();
|
||||
const rm = [];
|
||||
for (let index in modules) {
|
||||
if (!modules.hasOwnProperty(index)) continue;
|
||||
const module = modules[index];
|
||||
const { exports } = module;
|
||||
let foundModule = null;
|
||||
|
||||
if (!exports) continue;
|
||||
if (exports.__esModule && exports.default && filter(exports.default)) foundModule = exports.default;
|
||||
if (filter(exports)) foundModule = exports;
|
||||
if (!foundModule) continue;
|
||||
if (first) return foundModule;
|
||||
rm.push(foundModule);
|
||||
}
|
||||
return first || rm.length == 0 ? undefined : rm;
|
||||
}
|
||||
if (!exports) continue;
|
||||
if (exports.__esModule && exports.default && filter(exports.default)) foundModule = exports.default;
|
||||
if (filter(exports)) foundModule = exports;
|
||||
if (!foundModule) continue;
|
||||
if (first) return foundModule;
|
||||
rm.push(foundModule);
|
||||
}
|
||||
return first || rm.length == 0 ? undefined : rm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module by it's name.
|
||||
* @param {String} name The name of the module
|
||||
* @param {Function} fallback A function to use to filter modules if not finding a known module
|
||||
* @return {Any}
|
||||
*/
|
||||
/**
|
||||
* Finds a module by it's name.
|
||||
* @param {String} name The name of the module
|
||||
* @param {Function} fallback A function to use to filter modules if not finding a known module
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModuleByName(name, fallback) {
|
||||
if (Cache.hasOwnProperty(name)) return Cache[name];
|
||||
if (KnownModules.hasOwnProperty(name)) fallback = KnownModules[name];
|
||||
|
@ -246,48 +248,48 @@ export class WebpackModules {
|
|||
return module ? Cache[name] = module : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module by it's display name.
|
||||
* @param {String} name The display name of the module
|
||||
* @return {Any}
|
||||
*/
|
||||
/**
|
||||
* Finds a module by it's display name.
|
||||
* @param {String} name The display name of the module
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModuleByDisplayName(name) {
|
||||
return this.getModule(Filters.byDisplayName(name), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module using it's code.
|
||||
* @param {RegEx} regex A regular expression to use to filter modules
|
||||
* @param {Boolean} first Whether to return the only the first matching module
|
||||
* @return {Any}
|
||||
*/
|
||||
/**
|
||||
* Finds a module using it's code.
|
||||
* @param {RegEx} regex A regular expression to use to filter modules
|
||||
* @param {Boolean} first Whether to return the only the first matching module
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModuleByRegex(regex, first = true) {
|
||||
return this.getModule(Filters.byCode(regex), first);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module using properties on it's prototype.
|
||||
* @param {Array} props Properties to use to filter modules
|
||||
* @param {Boolean} first Whether to return only the first matching module
|
||||
* @return {Any}
|
||||
*/
|
||||
/**
|
||||
* Finds a module using properties on it's prototype.
|
||||
* @param {Array} props Properties to use to filter modules
|
||||
* @param {Boolean} first Whether to return only the first matching module
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModuleByPrototypes(prototypes, first = true) {
|
||||
return this.getModule(Filters.byPrototypeFields(prototypes), first);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a module using it's own properties.
|
||||
* @param {Array} props Properties to use to filter modules
|
||||
* @param {Boolean} first Whether to return only the first matching module
|
||||
* @return {Any}
|
||||
*/
|
||||
/**
|
||||
* Finds a module using it's own properties.
|
||||
* @param {Array} props Properties to use to filter modules
|
||||
* @param {Boolean} first Whether to return only the first matching module
|
||||
* @return {Any}
|
||||
*/
|
||||
static getModuleByProps(props, first = true) {
|
||||
return this.getModule(Filters.byProperties(props), first);
|
||||
}
|
||||
|
||||
/**
|
||||
* Discord's __webpack_require__ function.
|
||||
*/
|
||||
/**
|
||||
* Discord's __webpack_require__ function.
|
||||
*/
|
||||
static get require() {
|
||||
if (this._require) return this._require;
|
||||
const id = 'bd-webpackmodules';
|
||||
|
|
|
@ -17,22 +17,37 @@ export default class ErrorEvent extends Event {
|
|||
this.showStack = false; // For error modal
|
||||
}
|
||||
|
||||
/**
|
||||
* The module the error occured in.
|
||||
*/
|
||||
get module() {
|
||||
return this.args.module;
|
||||
}
|
||||
|
||||
/**
|
||||
* A message describing the error.
|
||||
*/
|
||||
get message() {
|
||||
return this.args.message;
|
||||
}
|
||||
|
||||
/**
|
||||
* The original error object.
|
||||
*/
|
||||
get err() {
|
||||
return this.args.err;
|
||||
}
|
||||
|
||||
/**
|
||||
* A trace showing which functions were called when the error occured.
|
||||
*/
|
||||
get stackTrace() {
|
||||
return this.err.stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of event.
|
||||
*/
|
||||
get __eventType() {
|
||||
return 'error';
|
||||
}
|
||||
|
|
|
@ -17,14 +17,23 @@ export default class Event {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* An object containing information about the event.
|
||||
*/
|
||||
get event() {
|
||||
return this.__eventInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* The first argument that was passed to the constructor, which contains information about the event.
|
||||
*/
|
||||
get args() {
|
||||
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 {
|
||||
|
||||
/**
|
||||
* An array of SettingUpdated events.
|
||||
*/
|
||||
get updatedSettings() {
|
||||
return this.args.updatedSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of event.
|
||||
*/
|
||||
get __eventType() {
|
||||
return 'settings-updated';
|
||||
}
|
||||
|
|
|
@ -12,38 +12,65 @@ import Event from './event';
|
|||
|
||||
export default class SettingUpdatedEvent extends Event {
|
||||
|
||||
/**
|
||||
* The set containing the setting that was updated.
|
||||
*/
|
||||
get set() {
|
||||
return this.args.set;
|
||||
}
|
||||
|
||||
/**
|
||||
* The ID of the set containing the setting that was updated.
|
||||
*/
|
||||
get set_id() {
|
||||
return this.args.set.id;
|
||||
return this.set.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The category containing the setting that was updated.
|
||||
*/
|
||||
get category() {
|
||||
return this.args.category;
|
||||
}
|
||||
|
||||
/**
|
||||
* The ID of the category containing the setting that was updated.
|
||||
*/
|
||||
get category_id() {
|
||||
return this.args.category.id;
|
||||
return this.category.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The setting that was updated.
|
||||
*/
|
||||
get setting() {
|
||||
return this.args.setting;
|
||||
}
|
||||
|
||||
/**
|
||||
* The ID of the setting that was updated.
|
||||
*/
|
||||
get setting_id() {
|
||||
return this.args.setting.id;
|
||||
return this.setting.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The setting's new value.
|
||||
*/
|
||||
get value() {
|
||||
return this.args.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The setting's old value.
|
||||
*/
|
||||
get old_value() {
|
||||
return this.args.old_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of event.
|
||||
*/
|
||||
get __eventType() {
|
||||
return 'setting-updated';
|
||||
}
|
||||
|
|
|
@ -14,22 +14,29 @@ export default class MultipleChoiceOption {
|
|||
|
||||
constructor(args) {
|
||||
this.args = args.args || args;
|
||||
|
||||
Object.freeze(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* This option's ID.
|
||||
*/
|
||||
get id() {
|
||||
return this.args.id || this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* A string describing this option.
|
||||
*/
|
||||
get text() {
|
||||
return this.args.text;
|
||||
}
|
||||
|
||||
/**
|
||||
* The value to return when this option is active.
|
||||
*/
|
||||
get value() {
|
||||
return this.args.value;
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new MultipleChoiceOption(Utils.deepclone(this.args));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,26 +24,46 @@ export default class SettingsScheme {
|
|||
Object.freeze(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* The scheme's ID.
|
||||
*/
|
||||
get id() {
|
||||
return this.args.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL of the scheme's icon. This should be a base64 encoded data URI.
|
||||
*/
|
||||
get icon_url() {
|
||||
return this.args.icon_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* The scheme's name.
|
||||
*/
|
||||
get name() {
|
||||
return this.args.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* A string to be displayed under the scheme.
|
||||
*/
|
||||
get hint() {
|
||||
return this.args.hint;
|
||||
}
|
||||
|
||||
/**
|
||||
* An array of stripped settings categories this scheme manages.
|
||||
*/
|
||||
get 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) {
|
||||
for (let schemeCategory of this.settings) {
|
||||
const category = set.categories.find(c => c.category === schemeCategory.category);
|
||||
|
@ -66,12 +86,13 @@ export default class SettingsScheme {
|
|||
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) {
|
||||
return set.merge({ settings: this.settings });
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new SettingsScheme(Utils.deepclone(this.args));
|
||||
return set.merge(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -446,7 +446,7 @@ export default class SettingsSet {
|
|||
text: this.text,
|
||||
headertext: this.headertext,
|
||||
settings: this.categories.map(category => category.clone()),
|
||||
schemes: this.schemes.map(scheme => scheme.clone())
|
||||
schemes: this.schemes
|
||||
}, ...merge);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,16 +25,21 @@
|
|||
width: 16px;
|
||||
filter: brightness(10);
|
||||
cursor: pointer;
|
||||
.theme-light [class*="topSectionNormal-"] & {
|
||||
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iQ2FscXVlXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjAwMCAyMDAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAyMDAwIDIwMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxnPjxwYXRoIGZpbGw9IiMzRTgyRTUiIGQ9Ik0xNDAyLjIsNjMxLjdjLTkuNy0zNTMuNC0yODYuMi00OTYtNjQyLjYtNDk2SDY4LjR2NzE0LjFsNDQyLDM5OFY0OTAuN2gyNTdjMjc0LjUsMCwyNzQuNSwzNDQuOSwwLDM0NC45SDU5Ny42djMyOS41aDE2OS44YzI3NC41LDAsMjc0LjUsMzQ0LjgsMCwzNDQuOGgtNjk5djM1NC45aDY5MS4yYzM1Ni4zLDAsNjMyLjgtMTQyLjYsNjQyLjYtNDk2YzAtMTYyLjYtNDQuNS0yODQuMS0xMjIuOS0zNjguNkMxMzU3LjcsOTE1LjgsMTQwMi4yLDc5NC4zLDE0MDIuMiw2MzEuN3oiLz48cGF0aCBmaWxsPSIjYmJiYmJiIiBkPSJNMTI2Mi41LDEzNS4yTDEyNjIuNSwxMzUuMmwtNzYuOCwwYzI2LjYsMTMuMyw1MS43LDI4LjEsNzUsNDQuM2M3MC43LDQ5LjEsMTI2LjEsMTExLjUsMTY0LjYsMTg1LjNjMzkuOSw3Ni42LDYxLjUsMTY1LjYsNjQuMywyNjQuNmwwLDEuMnYxLjJjMCwxNDEuMSwwLDU5Ni4xLDAsNzM3LjF2MS4ybDAsMS4yYy0yLjcsOTktMjQuMywxODgtNjQuMywyNjQuNmMtMzguNSw3My44LTkzLjgsMTM2LjItMTY0LjYsMTg1LjNjLTIyLjYsMTUuNy00Ni45LDMwLjEtNzIuNiw0My4xaDcyLjVjMzQ2LjIsMS45LDY3MS0xNzEuMiw2NzEtNTY3LjlWNzE2LjdDMTkzMy41LDMxMi4yLDE2MDguNywxMzUuMiwxMjYyLjUsMTM1LjJ6Ii8+PC9nPjwvc3ZnPg==');
|
||||
filter: none;
|
||||
}
|
||||
}
|
||||
|
||||
.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==');
|
||||
filter: none;
|
||||
}
|
||||
|
||||
.bd-message-badges-wrap {
|
||||
display: inline-block;
|
||||
margin-left: 6px;
|
||||
height: 11px;
|
||||
|
||||
.bd-message-badge-developer,
|
||||
.bd-message-badge-contributor {
|
||||
width: 12px;
|
||||
|
|
|
@ -20,17 +20,17 @@ bd-tooltips {
|
|||
word-wrap: break-word;
|
||||
z-index: 9001;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.bd-tooltip:after {
|
||||
border: 5px solid transparent;
|
||||
content: " ";
|
||||
height: 0;
|
||||
pointer-events: none;
|
||||
width: 0;
|
||||
border-top-color: #000;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
.bd-tooltip-arrow {
|
||||
border: 5px solid transparent;
|
||||
content: " ";
|
||||
height: 0;
|
||||
pointer-events: none;
|
||||
width: 0;
|
||||
border-top-color: #000;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,37 +8,14 @@
|
|||
* 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 DOM from './dom';
|
||||
import VueInjector from './vueinjector';
|
||||
import EditedTimeStamp from './components/common/EditedTimestamp.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 {
|
||||
|
||||
constructor(args) {
|
||||
|
@ -54,22 +31,19 @@ export default class extends EventListener {
|
|||
}
|
||||
|
||||
get eventBindings() {
|
||||
return [{ id: 'gkh:keyup', callback: this.injectAutocomplete }];
|
||||
/*
|
||||
return [
|
||||
{ id: 'server-switch', callback: this.manipAll },
|
||||
{ id: 'channel-switch', callback: this.manipAll },
|
||||
{ id: 'discord:MESSAGE_CREATE', callback: this.markupInjector },
|
||||
{ id: 'discord:MESSAGE_UPDATE', callback: this.markupInjector },
|
||||
// { id: 'server-switch', callback: this.manipAll },
|
||||
// { id: 'channel-switch', callback: this.manipAll },
|
||||
// { id: 'discord:MESSAGE_CREATE', callback: this.markupInjector },
|
||||
// { id: 'discord:MESSAGE_UPDATE', callback: this.markupInjector },
|
||||
{ id: 'gkh:keyup', callback: this.injectAutocomplete }
|
||||
];
|
||||
*/
|
||||
}
|
||||
|
||||
manipAll() {
|
||||
try {
|
||||
this.appMount.setAttribute('guild-id', TempApi.currentGuildId);
|
||||
this.appMount.setAttribute('channel-id', TempApi.currentChannelId);
|
||||
this.appMount.setAttribute('guild-id', DiscordApi.currentGuild.id);
|
||||
this.appMount.setAttribute('channel-id', DiscordApi.currentChannel.id);
|
||||
this.setIds();
|
||||
this.makeMutable();
|
||||
} catch (err) {
|
||||
|
@ -172,14 +146,14 @@ export default class extends EventListener {
|
|||
const userTest = Reflection(msgGroup).prop('user');
|
||||
if (!userTest) return;
|
||||
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;
|
||||
}
|
||||
msg.setAttribute('data-message-id', messageid);
|
||||
const msgGroup = msg.closest('.message-group');
|
||||
if (!msgGroup) return;
|
||||
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) {
|
||||
|
@ -187,7 +161,7 @@ export default class extends EventListener {
|
|||
const userid = Reflection(user).prop('user.id');
|
||||
if (!userid) return;
|
||||
user.setAttribute('data-user-id', userid);
|
||||
const currentUser = userid === TempApi.currentUserId;
|
||||
const currentUser = userid === DiscordApi.currentUser.id;
|
||||
if (currentUser) user.setAttribute('data-currentuser', true);
|
||||
Events.emit('ui:useridset', user);
|
||||
}
|
||||
|
@ -218,4 +192,5 @@ export default class extends EventListener {
|
|||
template: '<Autocomplete :initial="initial" />'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,7 +28,14 @@ const BdMenuItems = new class {
|
|||
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) {
|
||||
if (this.items.includes(item)) return item;
|
||||
|
||||
item.id = items++;
|
||||
item.contentid = item.contentid || (items++ + '');
|
||||
item.active = false;
|
||||
|
@ -39,6 +46,13 @@ const BdMenuItems = new class {
|
|||
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) {
|
||||
return this.add({
|
||||
category, set,
|
||||
|
@ -46,12 +60,23 @@ 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) {
|
||||
return this.add({
|
||||
category, text, component
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the menu.
|
||||
* @param {Object} item The item to remove from the menu
|
||||
*/
|
||||
remove(item) {
|
||||
Utils.removeFromArray(this.items, item);
|
||||
}
|
||||
|
|
|
@ -8,48 +8,21 @@
|
|||
* 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 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 { remote } from 'electron';
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
import { BdSettingsWrapper, BdModals } from './components';
|
||||
|
||||
export default class {
|
||||
|
||||
static initUiEvents() {
|
||||
this.pathCache = {
|
||||
isDm: null,
|
||||
server: TempApi.currentGuild,
|
||||
channel: TempApi.currentChannel
|
||||
server: DiscordApi.currentGuild,
|
||||
channel: DiscordApi.currentChannel
|
||||
};
|
||||
window.addEventListener('keyup', e => Events.emit('gkh:keyup', e));
|
||||
this.autoManip = new AutoManip();
|
||||
|
@ -67,9 +40,9 @@ export default class {
|
|||
if (!remote.BrowserWindow.getFocusedWindow()) return;
|
||||
clearInterval(ehookInterval);
|
||||
remote.BrowserWindow.getFocusedWindow().webContents.on('did-navigate-in-page', (e, url, isMainFrame) => {
|
||||
const { currentGuild, currentChannel } = TempApi;
|
||||
const { currentGuild, currentChannel } = DiscordApi;
|
||||
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.channel = currentChannel;
|
||||
return;
|
||||
|
@ -84,7 +57,7 @@ export default class {
|
|||
currentGuild.id &&
|
||||
this.pathCache.server &&
|
||||
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.channel = currentChannel;
|
||||
return;
|
||||
|
@ -110,19 +83,19 @@ export default class {
|
|||
DOM.createElement('div', null, 'bd-modals').appendTo(DOM.bdModals);
|
||||
DOM.createElement('bd-tooltips').appendTo(DOM.bdBody);
|
||||
|
||||
const modals = new Vue({
|
||||
this.modals = new Vue({
|
||||
el: '#bd-modals',
|
||||
components: { BdModals },
|
||||
template: '<BdModals/>'
|
||||
template: '<BdModals />'
|
||||
});
|
||||
|
||||
const vueInstance = new Vue({
|
||||
this.vueInstance = new Vue({
|
||||
el: '#bd-settings',
|
||||
components: { BdSettingsWrapper },
|
||||
template: '<BdSettingsWrapper/>'
|
||||
template: '<BdSettingsWrapper />'
|
||||
});
|
||||
|
||||
return vueInstance;
|
||||
return this.vueInstance;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,13 +19,14 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// Imports
|
||||
import { Events } from 'modules';
|
||||
import { Modals } from 'ui';
|
||||
import { Modal } from '../common';
|
||||
import { MiError } from '../common/MaterialIcon';
|
||||
import ErrorModal from './modals/ErrorModal.vue';
|
||||
import { Modal } from './common';
|
||||
import { MiError } from './common/MaterialIcon';
|
||||
import ErrorModal from './bd/modals/ErrorModal.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
|
@ -131,7 +131,7 @@
|
|||
this.timeout = setTimeout(() => {
|
||||
this.animating = false;
|
||||
this.lastActiveIndex = -1;
|
||||
this.timeout = null;
|
||||
this.timeout = null;
|
||||
}, 400);
|
||||
},
|
||||
openGithub() {
|
||||
|
|
|
@ -21,16 +21,12 @@
|
|||
</template>
|
||||
<script>
|
||||
// Imports
|
||||
import { ClientLogger as Logger } from 'common';
|
||||
import { shell } from 'electron';
|
||||
import Card from './Card.vue';
|
||||
import { Button, ButtonGroup, SettingSwitch, MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension } from '../common';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
settingsOpen: false
|
||||
}
|
||||
},
|
||||
props: ['plugin', 'togglePlugin', 'reloadPlugin', 'deletePlugin', 'showSettings'],
|
||||
components: {
|
||||
Card, Button, ButtonGroup, SettingSwitch, MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension
|
||||
|
@ -38,9 +34,9 @@
|
|||
methods: {
|
||||
editPlugin() {
|
||||
try {
|
||||
shell.openItem(this.plugin.pluginPath);
|
||||
shell.openItem(this.plugin.contentPath);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
Logger.err('PluginCard', [`Error opening plugin directory ${this.plugin.contentPath}:`, err]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,17 +37,19 @@
|
|||
// Imports
|
||||
import { PluginManager } from 'modules';
|
||||
import { Modals } from 'ui';
|
||||
import { SettingsWrapper } from './';
|
||||
import PluginCard from './PluginCard.vue';
|
||||
import { ClientLogger as Logger } from 'common';
|
||||
import { MiRefresh } from '../common';
|
||||
import SettingsWrapper from './SettingsWrapper.vue';
|
||||
import PluginCard from './PluginCard.vue';
|
||||
import RefreshBtn from '../common/RefreshBtn.vue';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
PluginManager,
|
||||
local: true,
|
||||
localPlugins: PluginManager.localPlugins
|
||||
}
|
||||
};
|
||||
},
|
||||
components: {
|
||||
SettingsWrapper, PluginCard,
|
||||
|
@ -62,32 +64,32 @@
|
|||
this.local = false;
|
||||
},
|
||||
async refreshLocal() {
|
||||
await PluginManager.refreshPlugins();
|
||||
await this.PluginManager.refreshPlugins();
|
||||
},
|
||||
async refreshOnline() {
|
||||
|
||||
// TODO
|
||||
},
|
||||
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 {
|
||||
await plugin.enabled ? PluginManager.stopPlugin(plugin) : PluginManager.startPlugin(plugin);
|
||||
await enabled ? this.PluginManager.stopPlugin(plugin) : this.PluginManager.startPlugin(plugin);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
Logger.err('PluginsView', [`Error ${enabled ? 'stopp' : 'start'}ing plugin ${plugin.name}:`, err]);
|
||||
}
|
||||
},
|
||||
async reloadPlugin(plugin) {
|
||||
try {
|
||||
await PluginManager.reloadPlugin(plugin);
|
||||
await this.PluginManager.reloadPlugin(plugin);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
Logger.err('PluginsView', [`Error reloading plugin ${plugin.name}:`, err]);
|
||||
}
|
||||
},
|
||||
async deletePlugin(plugin, unload) {
|
||||
try {
|
||||
if (unload) await PluginManager.unloadPlugin(plugin);
|
||||
else await PluginManager.deletePlugin(plugin);
|
||||
await unload ? this.PluginManager.unloadPlugin(plugin) : this.PluginManager.deletePlugin(plugin);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
Logger.err('PluginsView', [`Error ${unload ? 'unload' : 'delet'}ing plugin ${plugin.name}:`, err]);
|
||||
}
|
||||
},
|
||||
showSettings(plugin, dont_clone) {
|
||||
|
|
|
@ -26,11 +26,6 @@
|
|||
import { Button, ButtonGroup, SettingSwitch, MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension } from '../common';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
settingsOpen: false
|
||||
}
|
||||
},
|
||||
props: ['theme', 'toggleTheme', 'reloadTheme', 'deleteTheme', 'showSettings'],
|
||||
components: {
|
||||
Card, Button, ButtonGroup, SettingSwitch, MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension
|
||||
|
@ -40,7 +35,7 @@
|
|||
try {
|
||||
shell.openItem(this.theme.themePath);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
Logger.err('ThemeCard', [`Error opening theme directory ${this.theme.contentPath}:`, err]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,17 +37,19 @@
|
|||
// Imports
|
||||
import { ThemeManager } from 'modules';
|
||||
import { Modals } from 'ui';
|
||||
import { SettingsWrapper } from './';
|
||||
import { ClientLogger as Logger } from 'common';
|
||||
import { MiRefresh } from '../common';
|
||||
import SettingsWrapper from './SettingsWrapper.vue';
|
||||
import ThemeCard from './ThemeCard.vue';
|
||||
import RefreshBtn from '../common/RefreshBtn.vue';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
ThemeManager,
|
||||
local: true,
|
||||
localThemes: ThemeManager.localThemes
|
||||
}
|
||||
};
|
||||
},
|
||||
components: {
|
||||
SettingsWrapper, ThemeCard,
|
||||
|
@ -62,33 +64,31 @@
|
|||
this.local = false;
|
||||
},
|
||||
async refreshLocal() {
|
||||
await ThemeManager.refreshThemes();
|
||||
await this.ThemeManager.refreshThemes();
|
||||
},
|
||||
async refreshOnline() {
|
||||
|
||||
// TODO
|
||||
},
|
||||
async toggleTheme(theme) {
|
||||
// TODO Display error if theme fails to enable/disable
|
||||
// TODO: display error if theme fails to enable/disable
|
||||
try {
|
||||
await theme.enabled ? ThemeManager.disableTheme(theme) : ThemeManager.enableTheme(theme);
|
||||
await theme.enabled ? this.ThemeManager.disableTheme(theme) : this.ThemeManager.enableTheme(theme);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
Logger.err('ThemesView', [`Error ${enabled ? 'stopp' : 'start'}ing theme ${theme.name}:`, err]);
|
||||
}
|
||||
},
|
||||
async reloadTheme(theme, reload) {
|
||||
try {
|
||||
if (reload) await ThemeManager.reloadTheme(theme);
|
||||
else await theme.recompile();
|
||||
await reload ? this.ThemeManager.reloadTheme(theme) : theme.recompile();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
Logger.err('ThemesView', [`Error ${reload ? 'reload' : 'recompil'}ing theme ${theme.name}:`, err]);
|
||||
}
|
||||
},
|
||||
async deleteTheme(theme, unload) {
|
||||
try {
|
||||
if (unload) await ThemeManager.unloadTheme(theme);
|
||||
else await ThemeManager.deleteTheme(theme);
|
||||
await unload ? this.ThemeManager.unloadTheme(theme) : this.ThemeManager.deleteTheme(theme);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
Logger.err('ThemesView', [`Error ${unload ? 'unload' : 'delet'}ing theme ${theme.name}:`, err]);
|
||||
}
|
||||
},
|
||||
showSettings(theme, dont_clone) {
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
<script>
|
||||
import { shell } from 'electron';
|
||||
import { ClientIPC } from 'common';
|
||||
import { ClientIPC, ClientLogger as Logger } from 'common';
|
||||
import Combokeys from 'combokeys';
|
||||
import CombokeysRecord from 'combokeys/plugins/record';
|
||||
|
||||
|
@ -65,7 +65,7 @@
|
|||
this.active = false;
|
||||
this.recordingValue = undefined;
|
||||
this.setting.value = sequence.join(' ');
|
||||
console.log('keypress', sequence);
|
||||
Logger.log('Keybind', ['Recorded sequence', sequence]);
|
||||
},
|
||||
getDisplayString(value) {
|
||||
if (!value) return;
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
export { default as BdSettingsWrapper } from './BdSettingsWrapper.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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -209,7 +209,7 @@ export default class Modals {
|
|||
ThemeManager._errors = [];
|
||||
}
|
||||
|
||||
return modal;
|
||||
return modal;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,9 @@ Vue.use(VTooltip, {
|
|||
defaultContainer: 'bd-tooltips',
|
||||
defaultClass: 'bd-tooltip',
|
||||
defaultTargetClass: 'bd-has-tooltip',
|
||||
defaultArrowSelector: '.bd-tooltip-arrow',
|
||||
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')
|
||||
});
|
||||
|
||||
|
|
|
@ -12,17 +12,14 @@ import Vue from './vue';
|
|||
|
||||
export default class {
|
||||
|
||||
static inject(root, bdnode, components, template, replaceRoot) {
|
||||
if(!replaceRoot) bdnode.appendTo(root);
|
||||
|
||||
return new Vue({
|
||||
el: replaceRoot ? root : bdnode.element,
|
||||
components,
|
||||
template
|
||||
});
|
||||
}
|
||||
|
||||
static _inject(root, options, bdnode) {
|
||||
/**
|
||||
* 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 to
|
||||
* @return {Vue}
|
||||
*/
|
||||
static inject(root, options, bdnode) {
|
||||
if(bdnode) bdnode.appendTo(root);
|
||||
|
||||
const vue = new Vue(options);
|
||||
|
|
|
@ -8,19 +8,13 @@
|
|||
* 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 { Vendor } from 'modules';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import _ from 'lodash';
|
||||
import filetype from 'file-type';
|
||||
|
||||
export class Utils {
|
||||
static isArrowFunction(fn) {
|
||||
return !fn.toString().startsWith('function');
|
||||
}
|
||||
static overload(fn, cb) {
|
||||
const orig = fn;
|
||||
return function (...args) {
|
||||
|
@ -31,6 +25,10 @@ export class Utils {
|
|||
|
||||
/**
|
||||
* 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) {
|
||||
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.
|
||||
* 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) {
|
||||
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) => {
|
||||
this.monkeyPatch(object, methodName, data => {
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
@ -81,15 +98,20 @@ export class Utils {
|
|||
};
|
||||
|
||||
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,
|
||||
after: after ? compatible_function(after) : undefined,
|
||||
after: !instead && after ? compatible_function(after) : undefined,
|
||||
once
|
||||
});
|
||||
|
||||
return cancelPatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to parse a string as JSON.
|
||||
* @param {String} json The string to parse
|
||||
* @return {Any}
|
||||
*/
|
||||
static async tryParseJson(jsonString) {
|
||||
try {
|
||||
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) {
|
||||
const camelCased = {};
|
||||
_.forEach(o, (value, key) => {
|
||||
|
@ -112,17 +139,20 @@ export class Utils {
|
|||
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
|
||||
if (typeof value1 !== typeof value2) return false;
|
||||
if (value1 === null && value2 === null) return true;
|
||||
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
|
||||
let value1array = typeof value1 === 'array' ? value1 : Object.keys(value1);
|
||||
let value2array = typeof value2 === 'array' ? value2 : Object.keys(value2);
|
||||
if (value1array.length !== value2array.length) return false;
|
||||
if (Object.keys(value1).length !== Object.keys(value2).length) return false;
|
||||
|
||||
for (let key in value1) {
|
||||
if (!this.compare(value1[key], value2[key])) return false;
|
||||
|
@ -130,9 +160,20 @@ export class Utils {
|
|||
} else if (value1 !== value2) return false;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones an object and all it's properties.
|
||||
* @param {Any} value The value to clone
|
||||
* @return {Any} The cloned value
|
||||
*/
|
||||
static deepclone(value) {
|
||||
if (typeof value === 'object') {
|
||||
if (value instanceof Array) return value.map(i => this.deepclone(i));
|
||||
|
@ -149,6 +190,11 @@ export class Utils {
|
|||
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) {
|
||||
if (exclude && exclude(object)) return;
|
||||
|
||||
|
@ -165,38 +211,57 @@ export class Utils {
|
|||
return object;
|
||||
}
|
||||
|
||||
static filterArray(array, filter) {
|
||||
const indexes = [];
|
||||
for (let index in array) {
|
||||
if (!filter(array[index], index))
|
||||
indexes.push(index);
|
||||
}
|
||||
|
||||
for (let i in indexes)
|
||||
array.splice(indexes[i] - i, 1);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from an array. This differs from Array.prototype.filter as it mutates the original array instead of creating a new one.
|
||||
* @param {Array} array The array to filter
|
||||
* @param {Any} item The item to remove from the array
|
||||
* @return {Array}
|
||||
*/
|
||||
static removeFromArray(array, item) {
|
||||
let index;
|
||||
while ((index = array.indexOf(item)) > -1)
|
||||
array.splice(index, 1);
|
||||
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 {
|
||||
/**
|
||||
* Checks if a file exists and is a file.
|
||||
* @param {String} path The file's path
|
||||
* @return {Promise}
|
||||
*/
|
||||
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
|
||||
});
|
||||
|
||||
|
@ -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) {
|
||||
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
|
||||
});
|
||||
|
||||
|
@ -223,18 +293,25 @@ export class FileUtils {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a directory.
|
||||
* @param {String} path The directory's path
|
||||
* @return {Promise}
|
||||
*/
|
||||
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();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
await this.fileExists(path);
|
||||
} catch (err) {
|
||||
throw (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
|
||||
});
|
||||
|
||||
|
@ -268,24 +350,47 @@ 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) {
|
||||
try {
|
||||
await this.fileExists(path);
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.readFile(path, options || {}, (err, data) => {
|
||||
if (err) return reject(err);
|
||||
resolve(data);
|
||||
if (err) reject(err);
|
||||
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) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.writeFile(path, data, err => {
|
||||
if (err) return reject(err);
|
||||
resolve();
|
||||
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) {
|
||||
let readFile;
|
||||
try {
|
||||
|
@ -295,41 +400,57 @@ export class FileUtils {
|
|||
}
|
||||
|
||||
try {
|
||||
const parsed = await Utils.tryParseJson(readFile);
|
||||
return parsed;
|
||||
return await Utils.tryParseJson(readFile);
|
||||
} 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) {
|
||||
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) {
|
||||
try {
|
||||
await this.directoryExists(path);
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.readdir(path, (err, files) => {
|
||||
if (err) return reject(err);
|
||||
resolve(files);
|
||||
});
|
||||
await this.directoryExists(path);
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.readdir(path, (err, files) => {
|
||||
if (err) reject(err);
|
||||
else resolve(files);
|
||||
});
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static async readDir(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) {
|
||||
if (typeof buffer === 'string') buffer = await this.readFileBuffer(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) {
|
||||
if (typeof buffer === 'string') buffer = await this.readFileBuffer(buffer);
|
||||
if (!type) type = this.getFileType(buffer).mime;
|
||||
|
|
Loading…
Reference in New Issue