Merge e49df8952f
into 835cc3134c
This commit is contained in:
commit
e1d0b138cf
|
@ -279,8 +279,6 @@ export default new class EmoteModule extends BuiltinModule {
|
||||||
const { content } = args[1];
|
const { content } = args[1];
|
||||||
if (!content) return orig(...args);
|
if (!content) return orig(...args);
|
||||||
|
|
||||||
Logger.log('EmoteModule', ['Sending message', MessageActions, args, orig]);
|
|
||||||
|
|
||||||
const emoteAsImage = Settings.getSetting('emotes', 'default', 'emoteasimage').value &&
|
const emoteAsImage = Settings.getSetting('emotes', 'default', 'emoteasimage').value &&
|
||||||
(DiscordApi.currentChannel.type === 'DM' || DiscordApi.currentChannel.type === 'GROUP_DM' || DiscordApi.currentChannel.checkPermissions(DiscordApi.modules.DiscordPermissions.ATTACH_FILES));
|
(DiscordApi.currentChannel.type === 'DM' || DiscordApi.currentChannel.type === 'GROUP_DM' || DiscordApi.currentChannel.checkPermissions(DiscordApi.modules.DiscordPermissions.ATTACH_FILES));
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
"id": "developer-mode",
|
"id": "developer-mode",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"text": "Developer mode",
|
"text": "Developer mode",
|
||||||
"hint": "Adds some of BetterDiscord's internal modules to `global._bd`.",
|
"hint": "Adds some of BetterDiscord's internal modules to `global._bd` and enable additional options in plugin and theme settings.",
|
||||||
"value": false
|
"value": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,7 +22,7 @@ const ignoreExternal = tests && true;
|
||||||
class BetterDiscord {
|
class BetterDiscord {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
Logger.file = tests ? path.resolve(__dirname, '..', '..', 'tests', 'log.txt') : path.join(__dirname, 'log.txt');
|
Logger.file = tests ? path.resolve(__dirname, '..', '..', 'tests', 'log.txt') : `${__dirname}-log.txt`;
|
||||||
Logger.trimLogFile();
|
Logger.trimLogFile();
|
||||||
Logger.log('main', 'BetterDiscord starting');
|
Logger.log('main', 'BetterDiscord starting');
|
||||||
|
|
||||||
|
|
|
@ -118,7 +118,7 @@ export default class Content extends AsyncEventEmitter {
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
async enable(save = true) {
|
async enable(save = true) {
|
||||||
if (this.enabled) return;
|
if (this.enabled || this.unloaded) return;
|
||||||
await this.emit('enable');
|
await this.emit('enable');
|
||||||
await this.emit('start');
|
await this.emit('start');
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { remote } from 'electron';
|
||||||
import Content from './content';
|
import Content from './content';
|
||||||
import Globals from './globals';
|
import Globals from './globals';
|
||||||
import Database from './database';
|
import Database from './database';
|
||||||
import { Utils, FileUtils, ClientLogger as Logger } from 'common';
|
import { Utils, FileUtils, ClientLogger as Logger, ClientIPC } from 'common';
|
||||||
import { SettingsSet, ErrorEvent } from 'structs';
|
import { SettingsSet, ErrorEvent } from 'structs';
|
||||||
import { Modals } from 'ui';
|
import { Modals } from 'ui';
|
||||||
import Combokeys from 'combokeys';
|
import Combokeys from 'combokeys';
|
||||||
|
@ -73,23 +73,23 @@ export default class {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async packContent(path, contentPath) {
|
static async packContent(path, contentPath) {
|
||||||
return new Promise((resolve, reject) => {
|
const filepath = await ClientIPC.send('bd-native-save', {
|
||||||
remote.dialog.showSaveDialog({
|
title: 'Save Package',
|
||||||
title: 'Save Package',
|
defaultPath: path,
|
||||||
defaultPath: path,
|
filters: [
|
||||||
filters: [
|
{
|
||||||
{
|
name: 'BetterDiscord Package',
|
||||||
name: 'BetterDiscord Package',
|
extensions: ['bd']
|
||||||
extensions: ['bd']
|
}
|
||||||
}
|
]
|
||||||
]
|
});
|
||||||
}, filepath => {
|
|
||||||
if (!filepath) return;
|
|
||||||
|
|
||||||
asar.uncache(filepath);
|
if (!filepath) return;
|
||||||
asar.createPackage(contentPath, filepath, () => {
|
|
||||||
resolve(filepath);
|
return new Promise((resolve, reject) => {
|
||||||
});
|
asar.uncache(filepath);
|
||||||
|
asar.createPackage(contentPath, filepath, () => {
|
||||||
|
resolve(filepath);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -104,13 +104,8 @@ export default class {
|
||||||
const directories = await FileUtils.listDirectory(this.contentPath);
|
const directories = await FileUtils.listDirectory(this.contentPath);
|
||||||
|
|
||||||
for (const dir of directories) {
|
for (const dir of directories) {
|
||||||
const packed = dir.endsWith('.bd');
|
const stat = await FileUtils.stat(path.join(this.contentPath, dir));
|
||||||
|
const packed = stat.isFile();
|
||||||
if (!packed) {
|
|
||||||
try {
|
|
||||||
await FileUtils.directoryExists(path.join(this.contentPath, dir));
|
|
||||||
} catch (err) { continue; }
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (packed) {
|
if (packed) {
|
||||||
|
@ -148,6 +143,8 @@ export default class {
|
||||||
* @param {bool} suppressErrors Suppress any errors that occur during loading of content
|
* @param {bool} suppressErrors Suppress any errors that occur during loading of content
|
||||||
*/
|
*/
|
||||||
static async refreshContent(suppressErrors = false) {
|
static async refreshContent(suppressErrors = false) {
|
||||||
|
this.loaded = true;
|
||||||
|
|
||||||
if (!this.localContent.length) return this.loadAllContent();
|
if (!this.localContent.length) return this.loadAllContent();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -155,18 +152,18 @@ export default class {
|
||||||
const directories = await FileUtils.listDirectory(this.contentPath);
|
const directories = await FileUtils.listDirectory(this.contentPath);
|
||||||
|
|
||||||
for (const dir of directories) {
|
for (const dir of directories) {
|
||||||
const packed = dir.endsWith('.bd');
|
|
||||||
|
|
||||||
// If content is already loaded this should resolve
|
// If content is already loaded this should resolve
|
||||||
if (this.getContentByDirName(dir)) continue;
|
if (this.getContentByDirName(dir)) continue;
|
||||||
|
|
||||||
try {
|
const stat = await FileUtils.stat(path.join(this.contentPath, dir));
|
||||||
await FileUtils.directoryExists(path.join(this.contentPath, dir));
|
const packed = stat.isFile();
|
||||||
} catch (err) { continue; }
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Load if not
|
if (packed) {
|
||||||
await this.preloadContent(dir);
|
await this.preloadPackedContent(dir);
|
||||||
|
} else {
|
||||||
|
await this.preloadContent(dir);
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// We don't want every plugin/theme to fail loading when one does
|
// We don't want every plugin/theme to fail loading when one does
|
||||||
this.errors.push(new ErrorEvent({
|
this.errors.push(new ErrorEvent({
|
||||||
|
@ -217,7 +214,8 @@ export default class {
|
||||||
await FileUtils.fileExists(packagePath);
|
await FileUtils.fileExists(packagePath);
|
||||||
|
|
||||||
const config = JSON.parse(asar.extractFile(packagePath, 'config.json').toString());
|
const config = JSON.parse(asar.extractFile(packagePath, 'config.json').toString());
|
||||||
const unpackedPath = path.join(Globals.getPath('tmp'), packageName);
|
const id = config.info.id || config.info.name.toLowerCase().replace(/[^a-zA-Z0-9-]/g, '-').replace(/--/g, '-');
|
||||||
|
const unpackedPath = path.join(Globals.getPath('tmp'), this.pathId, id);
|
||||||
|
|
||||||
asar.extractAll(packagePath, unpackedPath);
|
asar.extractAll(packagePath, unpackedPath);
|
||||||
|
|
||||||
|
@ -348,7 +346,7 @@ export default class {
|
||||||
await unload;
|
await unload;
|
||||||
|
|
||||||
await FileUtils.recursiveDeleteDirectory(content.paths.contentPath);
|
await FileUtils.recursiveDeleteDirectory(content.paths.contentPath);
|
||||||
if (content.packed) await FileUtils.recursiveDeleteDirectory(content.packagePath);
|
if (content.packed) await FileUtils.deleteFile(content.packagePath);
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Logger.err(this.moduleName, err);
|
Logger.err(this.moduleName, err);
|
||||||
|
@ -368,6 +366,8 @@ export default class {
|
||||||
if (!content) throw {message: `Could not find a ${this.contentType} from ${content}.`};
|
if (!content) throw {message: `Could not find a ${this.contentType} from ${content}.`};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
Object.defineProperty(content, 'unloaded', {configurable: true, value: true});
|
||||||
|
|
||||||
const disablePromise = content.disable(false);
|
const disablePromise = content.disable(false);
|
||||||
const unloadPromise = content.emit('unload', reload);
|
const unloadPromise = content.emit('unload', reload);
|
||||||
|
|
||||||
|
@ -380,8 +380,14 @@ export default class {
|
||||||
|
|
||||||
if (this.unloadContentHook) this.unloadContentHook(content);
|
if (this.unloadContentHook) this.unloadContentHook(content);
|
||||||
|
|
||||||
if (reload) return content.packed ? this.preloadPackedContent(content.packagePath, true, index) : this.preloadContent(content.dirName, true, index);
|
if (reload) {
|
||||||
|
const newcontent = content.packed ? this.preloadPackedContent(content.dirName.pkg, true, index) :
|
||||||
|
this.preloadContent(content.dirName, true, index);
|
||||||
|
Object.defineProperty(content, 'unloaded', {value: newcontent});
|
||||||
|
return newcontent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(content, 'unloaded', {value: true});
|
||||||
this.localContent.splice(index, 1);
|
this.localContent.splice(index, 1);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Logger.err(this.moduleName, err);
|
Logger.err(this.moduleName, err);
|
||||||
|
@ -433,7 +439,7 @@ export default class {
|
||||||
|
|
||||||
static getContentIndex(content) { return this.localContent.findIndex(c => c === content) }
|
static getContentIndex(content) { return this.localContent.findIndex(c => c === content) }
|
||||||
static getContentById(id) { return this.localContent.find(c => c.id === id) }
|
static getContentById(id) { return this.localContent.find(c => c.id === id) }
|
||||||
static getContentByDirName(dirName) { return this.localContent.find(c => c.dirName === dirName) }
|
static getContentByDirName(dirName) { return this.localContent.find(c => !c.packed ? c.dirName === dirName : c.dirName.pkg === dirName) }
|
||||||
static getContentByPath(path) { return this.localContent.find(c => c.contentPath === path) }
|
static getContentByPath(path) { return this.localContent.find(c => c.contentPath === path) }
|
||||||
static getContentByName(name) { return this.localContent.find(c => c.name === name) }
|
static getContentByName(name) { return this.localContent.find(c => c.name === name) }
|
||||||
|
|
||||||
|
|
|
@ -190,15 +190,34 @@ class ReactComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReactComponent.important = Symbol('BD.ReactComponent.important');
|
||||||
|
|
||||||
export class ReactComponents {
|
export class ReactComponents {
|
||||||
|
/** @type {ReactComponent[]} */
|
||||||
static get components() { return this._components || (this._components = []) }
|
static get components() { return this._components || (this._components = []) }
|
||||||
|
|
||||||
|
/** @type {Reflection.modules.React.Component[]} */
|
||||||
static get unknownComponents() { return this._unknownComponents || (this._unknownComponents = []) }
|
static get unknownComponents() { return this._unknownComponents || (this._unknownComponents = []) }
|
||||||
|
|
||||||
|
/** @type {{id: string, listeners: function[]}[]} */
|
||||||
static get listeners() { return this._listeners || (this._listeners = []) }
|
static get listeners() { return this._listeners || (this._listeners = []) }
|
||||||
|
|
||||||
|
/** @type {<{name: string, filter: function}[]>} */
|
||||||
static get nameSetters() { return this._nameSetters || (this._nameSetters = []) }
|
static get nameSetters() { return this._nameSetters || (this._nameSetters = []) }
|
||||||
static get componentAliases() { return this._componentAliases || (this._componentAliases = []) }
|
|
||||||
|
/** @type {Object.<string, string>} */
|
||||||
|
static get componentAliases() { return this._componentAliases || (this._componentAliases = {}) }
|
||||||
|
|
||||||
static get ReactComponent() { return ReactComponent }
|
static get ReactComponent() { return ReactComponent }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes a React component.
|
||||||
|
* @param {Reflection.modules.React.Component} component The React component class
|
||||||
|
* @param {object} retVal
|
||||||
|
* @param {object} important
|
||||||
|
* @param {string} important.selector A query selector the component will render elements matching (used to select all component instances to force them to rerender)
|
||||||
|
* @return {ReactComponent}
|
||||||
|
*/
|
||||||
static push(component, retVal, important) {
|
static push(component, retVal, important) {
|
||||||
if (!(component instanceof Function)) return null;
|
if (!(component instanceof Function)) return null;
|
||||||
const { displayName } = component;
|
const { displayName } = component;
|
||||||
|
@ -212,6 +231,8 @@ export class ReactComponents {
|
||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!important) important = component[ReactComponent.important];
|
||||||
|
|
||||||
const c = new ReactComponent(displayName, component, retVal, important);
|
const c = new ReactComponent(displayName, component, retVal, important);
|
||||||
this.components.push(c);
|
this.components.push(c);
|
||||||
|
|
||||||
|
@ -226,16 +247,19 @@ export class ReactComponents {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds a component from the components array or by waiting for it to be mounted.
|
* Finds a component from the components array or by waiting for it to be mounted.
|
||||||
* @param {String} name The component's name
|
* @param {string} name The component's name
|
||||||
* @param {Object} important An object containing a selector to look for
|
* @param {object} important An object containing a selector to look for
|
||||||
* @param {Function} filter A function to filter components if a single element is rendered by multiple components
|
* @param {function} filter A function to filter components if a single element is rendered by multiple components
|
||||||
* @return {Promise => ReactComponent}
|
* @return {Promise<ReactComponent>}
|
||||||
*/
|
*/
|
||||||
static async getComponent(name, important, filter) {
|
static async getComponent(name, important, filter) {
|
||||||
name = this.getComponentName(name);
|
name = this.getComponentName(name);
|
||||||
|
|
||||||
const have = this.components.find(c => c.id === name);
|
const have = this.components.find(c => c.id === name);
|
||||||
if (have) return have;
|
if (have) {
|
||||||
|
if (!have.important) have.important = important;
|
||||||
|
return have;
|
||||||
|
}
|
||||||
|
|
||||||
if (important) {
|
if (important) {
|
||||||
const callback = () => {
|
const callback = () => {
|
||||||
|
@ -262,7 +286,7 @@ export class ReactComponents {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!component && filter) {
|
if (!component && filter) {
|
||||||
Logger.log('ReactComponents', ['Found elements matching the query selector but no components passed the filter']);
|
Logger.log('ReactComponents', ['Found elements matching the query selector but no components passed the filter', name, important, filter]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,8 +345,12 @@ export class ReactComponents {
|
||||||
return this.nameSetters.push({ name, filter });
|
return this.nameSetters.push({ name, filter });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes a React component that isn't known.
|
||||||
|
* @param {Reflection.modules.React.Component} component
|
||||||
|
* @param {} retVal
|
||||||
|
*/
|
||||||
static processUnknown(component, retVal) {
|
static processUnknown(component, retVal) {
|
||||||
const have = this.unknownComponents.find(c => c.component === component);
|
|
||||||
for (const [fi, filter] of this.nameSetters.entries()) {
|
for (const [fi, filter] of this.nameSetters.entries()) {
|
||||||
if (filter.filter.filter(component)) {
|
if (filter.filter.filter(component)) {
|
||||||
Logger.log('ReactComponents', 'Filter match!');
|
Logger.log('ReactComponents', 'Filter match!');
|
||||||
|
@ -331,9 +359,8 @@ export class ReactComponents {
|
||||||
return this.push(component, retVal);
|
return this.push(component, retVal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (have) return have;
|
|
||||||
this.unknownComponents.push(component);
|
if (!this.unknownComponents.includes(component)) this.unknownComponents.push(component);
|
||||||
return component;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,13 +468,13 @@ export class ReactAutoPatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async patchNameTag() {
|
static async patchNameTag() {
|
||||||
const { selector } = Reflection.resolve('nameTag', 'username', 'discriminator', 'ownerIcon');
|
const { selector } = Reflection.resolve('nameTag', 'username', 'discriminator', 'bot');
|
||||||
this.NameTag = await ReactComponents.getComponent('NameTag', {selector});
|
this.NameTag = await ReactComponents.getComponent('NameTag', {selector});
|
||||||
}
|
}
|
||||||
|
|
||||||
static async patchGuild() {
|
static async patchGuild() {
|
||||||
const selector = `div.${Reflection.resolve('container', 'guildIcon', 'selected', 'unread').className}:not(:first-child)`;
|
const { selector } = Reflection.resolve('listItem', 'guildSeparator', 'selected', 'friendsOnline');
|
||||||
this.Guild = await ReactComponents.getComponent('Guild', {selector}, m => m.prototype.renderBadge);
|
this.Guild = await ReactComponents.getComponent('Guild', {selector}, m => m.displayName === 'Guild');
|
||||||
|
|
||||||
this.unpatchGuild = MonkeyPatch('BD:ReactComponents', this.Guild.component.prototype).after('render', (component, args, retVal) => {
|
this.unpatchGuild = MonkeyPatch('BD:ReactComponents', this.Guild.component.prototype).after('render', (component, args, retVal) => {
|
||||||
const { guild } = component.props;
|
const { guild } = component.props;
|
||||||
|
@ -505,7 +532,7 @@ export class ReactAutoPatcher {
|
||||||
ReactComponents.componentAliases.GuildVoiceChannel = 'VoiceChannel';
|
ReactComponents.componentAliases.GuildVoiceChannel = 'VoiceChannel';
|
||||||
|
|
||||||
const { selector } = Reflection.resolve('containerDefault', 'actionIcon');
|
const { selector } = Reflection.resolve('containerDefault', 'actionIcon');
|
||||||
this.GuildVoiceChannel = await ReactComponents.getComponent('GuildVoiceChannel', {selector}, c => c.prototype.handleVoiceConnect);
|
this.GuildVoiceChannel = await ReactComponents.getComponent('GuildVoiceChannel', {selector}, c => c.prototype.renderVoiceUsers);
|
||||||
|
|
||||||
this.unpatchGuildVoiceChannel = MonkeyPatch('BD:ReactComponents', this.GuildVoiceChannel.component.prototype).after('render', this._afterChannelRender);
|
this.unpatchGuildVoiceChannel = MonkeyPatch('BD:ReactComponents', this.GuildVoiceChannel.component.prototype).after('render', this._afterChannelRender);
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,20 @@ export default class Theme extends Content {
|
||||||
|
|
||||||
const watchfiles = Settings.getSetting('css', 'default', 'watch-files');
|
const watchfiles = Settings.getSetting('css', 'default', 'watch-files');
|
||||||
if (watchfiles.value) this.watchfiles = this.files;
|
if (watchfiles.value) this.watchfiles = this.files;
|
||||||
watchfiles.on('setting-updated', event => {
|
|
||||||
|
watchfiles.on('setting-updated', this.__watchFilesSettingUpdated = event => {
|
||||||
if (event.value) this.watchfiles = this.files;
|
if (event.value) this.watchfiles = this.files;
|
||||||
else this.watchfiles = [];
|
else this.watchfiles = [];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.on('unload', () => {
|
||||||
|
watchfiles.off('setting-updated', this.__watchFilesSettingUpdated);
|
||||||
|
|
||||||
|
if (this._filewatcher) {
|
||||||
|
this._filewatcher.removeAll();
|
||||||
|
delete this._filewatcher;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get type() { return 'theme' }
|
get type() { return 'theme' }
|
||||||
|
@ -61,7 +71,7 @@ export default class Theme extends Content {
|
||||||
async compile() {
|
async compile() {
|
||||||
Logger.log(this.name, 'Compiling CSS');
|
Logger.log(this.name, 'Compiling CSS');
|
||||||
|
|
||||||
if (this.info.type === 'sass') {
|
if (this.info.type === 'sass' || this.info.type === 'scss') {
|
||||||
const config = await ThemeManager.getConfigAsSCSS(this.settings);
|
const config = await ThemeManager.getConfigAsSCSS(this.settings);
|
||||||
|
|
||||||
const result = await ClientIPC.send('bd-compileSass', {
|
const result = await ClientIPC.send('bd-compileSass', {
|
||||||
|
@ -147,7 +157,7 @@ export default class Theme extends Content {
|
||||||
* @param {Array} files Files to watch
|
* @param {Array} files Files to watch
|
||||||
*/
|
*/
|
||||||
set watchfiles(files) {
|
set watchfiles(files) {
|
||||||
if (this.packed) {
|
if (this.unloaded || this.packed) {
|
||||||
// Don't watch files for packed themes
|
// Don't watch files for packed themes
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ export default class ThemeManager extends ContentManager {
|
||||||
});
|
});
|
||||||
if (instance.enabled) {
|
if (instance.enabled) {
|
||||||
instance.userConfig.enabled = false;
|
instance.userConfig.enabled = false;
|
||||||
instance.enable();
|
instance.enable(false);
|
||||||
}
|
}
|
||||||
return instance;
|
return instance;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
.bd-card {
|
.bd-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex-grow: 1;
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border-bottom: 1px solid rgba(114, 118, 126, .3);
|
border-bottom: 1px solid rgba(114, 118, 126, .3);
|
||||||
min-height: 150px;
|
min-height: 150px;
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
.bd-formCollection {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
div {
|
|
||||||
&:first-child {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bd-collectionItem {
|
|
||||||
display: flex;
|
|
||||||
flex-grow: 1;
|
|
||||||
margin-top: 5px;
|
|
||||||
|
|
||||||
.bd-removeCollectionItem {
|
|
||||||
width: 20px;
|
|
||||||
flex: 0 1 auto;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
svg {
|
|
||||||
fill: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
svg {
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
fill: #ccc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bd-newCollectionItem {
|
|
||||||
display: flex;
|
|
||||||
cursor: pointer;
|
|
||||||
align-self: flex-end;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
margin-right: 2px;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
fill: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
svg {
|
|
||||||
fill: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +1,17 @@
|
||||||
.bd-pluginsview,
|
.bd-pluginsview,
|
||||||
.bd-themesview {
|
.bd-themesview {
|
||||||
.bd-localPh {
|
flex-grow: 1;
|
||||||
.bd-scroller {
|
|
||||||
padding: 0 20px 0 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bd-onlinePh,
|
.bd-onlinePh,
|
||||||
.bd-localPh {
|
.bd-localPh {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin: 10px 0;
|
flex-grow: 1;
|
||||||
|
|
||||||
|
> .bd-scrollerWrap > .bd-scroller {
|
||||||
|
padding: 0 20px 0 0;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
.bd-spinnerContainer {
|
.bd-spinnerContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -19,8 +20,9 @@
|
||||||
|
|
||||||
.bd-onlinePhHeader {
|
.bd-onlinePhHeader {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 0 20px 0 10px;
|
padding: 0 20px 0 0;
|
||||||
min-height: 80px;
|
min-height: 80px;
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
.bd-flexRow {
|
.bd-flexRow {
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
|
@ -81,16 +83,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.bd-onlinePhBody {
|
.bd-onlinePhBody {
|
||||||
margin-top: 10px;
|
|
||||||
|
|
||||||
.bd-spinnerContainer {
|
.bd-spinnerContainer {
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bd-scroller {
|
|
||||||
padding: 0 20px 0 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
|
|
|
@ -8,6 +8,5 @@
|
||||||
@import './updater';
|
@import './updater';
|
||||||
@import './window-preferences';
|
@import './window-preferences';
|
||||||
@import './kvp';
|
@import './kvp';
|
||||||
@import './collection';
|
|
||||||
@import './e2ee';
|
@import './e2ee';
|
||||||
@import './devview';
|
@import './devview';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
.bd-remoteCard {
|
.bd-remoteCard {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
padding: 10px;
|
padding: 10px 0;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
border-bottom: 1px solid rgba(114, 118, 126, .3);
|
border-bottom: 1px solid rgba(114, 118, 126, .3);
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
// sass-lint:disable-all
|
// sass-lint:disable-all
|
||||||
body:not(.bd-hideButton) {
|
body:not(.bd-hideButton) {
|
||||||
[class*='layer-'] > * > [class*='wrapper-'] {
|
[class*='layers-'] > [class*='layer-'] > * > [class*='wrapper-'] {
|
||||||
padding-top: 49px !important;
|
padding-top: 49px !important;
|
||||||
}
|
}
|
||||||
.platform-osx [class*='layer-'] > * > [class*='wrapper-'] {
|
.platform-osx [class*='layers-'] > [class*='layer-'] > * > [class*='wrapper-'] {
|
||||||
margin-top: 26px;
|
margin-top: 26px;
|
||||||
}
|
}
|
||||||
|
|
||||||
[class*='layer-'] > * > [class*='wrapper-'] + [class*='flex'] {
|
.platform-osx [class*='layers-'] > [class*='layer-'] > * > [class*='wrapper-'] [class*='listItem-']:first-child {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[class*='layers-'] > [class*='layer-'] > * > [class*='wrapper-'] + [class*='flex'] {
|
||||||
border-radius: 0 0 0 5px;
|
border-radius: 0 0 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
.bd-scrollerWrap {
|
.bd-scrollerWrap {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
.bd-scroller {
|
.bd-scroller {
|
||||||
@include scrollbar;
|
@include scrollbar;
|
||||||
|
|
|
@ -17,18 +17,13 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
|
||||||
> .bd-scrollerWrap {
|
> .bd-scrollerWrap > .bd-scroller {
|
||||||
flex-grow: 1;
|
overflow-y: scroll;
|
||||||
|
|
||||||
> .bd-scroller {
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.bd-settings & {
|
.platform-darwin .bd-settings &.bd-settingswrapNoscroller, // sass-lint:disable-line class-name-format
|
||||||
.platform-darwin & { // sass-lint:disable-line class-name-format
|
.platform-darwin .bd-settings & > .bd-scrollerWrap > .bd-scroller { // sass-lint:disable-line class-name-format
|
||||||
padding-top: 22px;
|
padding-top: 22px;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.bd-settingswrapHeader {
|
.bd-settingswrapHeader {
|
||||||
|
|
|
@ -12,12 +12,14 @@ import { Module, Reflection } from 'modules';
|
||||||
|
|
||||||
const normalizedPrefix = 'da';
|
const normalizedPrefix = 'da';
|
||||||
const randClass = new RegExp(`^(?!${normalizedPrefix}-)((?:[A-Za-z]|[0-9]|-)+)-(?:[A-Za-z]|[0-9]|-|_){6}$`);
|
const randClass = new RegExp(`^(?!${normalizedPrefix}-)((?:[A-Za-z]|[0-9]|-)+)-(?:[A-Za-z]|[0-9]|-|_){6}$`);
|
||||||
|
const normalisedClass = /^(([a-zA-Z0-9]+)-[^\s]{6}) da-([a-zA-Z0-9]+)$/;
|
||||||
|
|
||||||
export default class ClassNormaliser extends Module {
|
export default class ClassNormaliser extends Module {
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.patchClassModules(Reflection.module.getModule(this.moduleFilter.bind(this), false));
|
this.patchClassModules(Reflection.module.getModule(this.moduleFilter.bind(this), false));
|
||||||
this.normalizeElement(document.querySelector('#app-mount'));
|
this.normalizeElement(document.querySelector('#app-mount'));
|
||||||
|
this.patchDOMMethods();
|
||||||
}
|
}
|
||||||
|
|
||||||
patchClassModules(modules) {
|
patchClassModules(modules) {
|
||||||
|
@ -26,6 +28,36 @@ export default class ClassNormaliser extends Module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
patchDOMMethods() {
|
||||||
|
const add = DOMTokenList.prototype.add;
|
||||||
|
|
||||||
|
DOMTokenList.prototype.add = function(...tokens) {
|
||||||
|
for (const token of tokens) {
|
||||||
|
let match;
|
||||||
|
if (typeof token === 'string' && (match = token.match(normalisedClass)) && match[2] === match[3]) return add.call(this, match[1]);
|
||||||
|
return add.call(this, token);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const remove = DOMTokenList.prototype.remove;
|
||||||
|
|
||||||
|
DOMTokenList.prototype.remove = function(...tokens) {
|
||||||
|
for (const token of tokens) {
|
||||||
|
let match;
|
||||||
|
if (typeof token === 'string' && (match = token.match(normalisedClass)) && match[2] === match[3]) return remove.call(this, match[1]);
|
||||||
|
return remove.call(this, token);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const contains = DOMTokenList.prototype.contains;
|
||||||
|
|
||||||
|
DOMTokenList.prototype.contains = function(token) {
|
||||||
|
let match;
|
||||||
|
if (typeof token === 'string' && (match = token.match(normalisedClass)) && match[2] === match[3]) return contains.call(this, match[1]);
|
||||||
|
return contains.call(this, token);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
shouldIgnore(value) {
|
shouldIgnore(value) {
|
||||||
if (!isNaN(value)) return true;
|
if (!isNaN(value)) return true;
|
||||||
if (value.endsWith('px') || value.endsWith('ch') || value.endsWith('em') || value.endsWith('ms')) return true;
|
if (value.endsWith('px') || value.endsWith('ch') || value.endsWith('em') || value.endsWith('ms')) return true;
|
||||||
|
|
|
@ -62,7 +62,7 @@
|
||||||
SettingsWrapper
|
SettingsWrapper
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
showConnectWindow() {
|
showConnectWindow() {
|
||||||
if (this.connecting) return;
|
if (this.connecting) return;
|
||||||
this.connecting = true;
|
this.connecting = true;
|
||||||
const x = (window.screenX + window.outerWidth / 2) - 520 / 2;
|
const x = (window.screenX + window.outerWidth / 2) - 520 / 2;
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<Button v-if="devmode && !plugin.packed" v-tooltip="'Package Plugin'" @click="package"><MiBoxDownload size="18"/></Button>
|
<Button v-if="devmode && !plugin.packed" v-tooltip="'Package Plugin'" @click="package"><MiBoxDownload size="18"/></Button>
|
||||||
<Button v-tooltip="'Settings (shift + click to open settings without cloning the set)'" v-if="plugin.hasSettings" @click="$emit('show-settings', $event.shiftKey)"><MiSettings size="18" /></Button>
|
<Button v-tooltip="'Settings (shift + click to open settings without cloning the set)'" v-if="plugin.hasSettings" @click="$emit('show-settings', $event.shiftKey)"><MiSettings size="18" /></Button>
|
||||||
<Button v-tooltip="'Reload'" @click="$emit('reload-plugin')"><MiRefresh size="18" /></Button>
|
<Button v-tooltip="'Reload'" @click="$emit('reload-plugin')"><MiRefresh size="18" /></Button>
|
||||||
<Button v-tooltip="'Edit'" @click="editPlugin"><MiPencil size="18" /></Button>
|
<Button v-if="devmode && !plugin.packed" v-tooltip="'Edit'" @click="editPlugin"><MiPencil size="18" /></Button>
|
||||||
<Button v-tooltip="'Uninstall (shift + click to unload)'" @click="$emit('delete-plugin', $event.shiftKey)" type="err"><MiDelete size="18" /></Button>
|
<Button v-tooltip="'Uninstall (shift + click to unload)'" @click="$emit('delete-plugin', $event.shiftKey)" type="err"><MiDelete size="18" /></Button>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</Card>
|
</Card>
|
||||||
|
@ -33,14 +33,19 @@
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
devmode: Settings.getSetting('core', 'advanced', 'developer-mode').value
|
devmodeSetting: Settings.getSetting('core', 'advanced', 'developer-mode')
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
props: ['plugin'],
|
props: ['plugin'],
|
||||||
components: {
|
components: {
|
||||||
Card, Button, ButtonGroup, SettingSwitch,
|
Card, Button, ButtonGroup, SettingSwitch,
|
||||||
MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension, MiBoxDownload
|
MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension, MiBoxDownload
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
devmode() {
|
||||||
|
return this.devmodeSetting.value;
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async package() {
|
async package() {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -34,9 +34,11 @@
|
||||||
<div class="bd-spinner7" />
|
<div class="bd-spinner7" />
|
||||||
</div>
|
</div>
|
||||||
<div class="bd-searchHint">{{searchHint}}</div>
|
<div class="bd-searchHint">{{searchHint}}</div>
|
||||||
<div class="bd-fancySearch" :class="{'bd-disabled': loadingOnline, 'bd-active': loadingOnline || (onlinePlugins && onlinePlugins.docs)}">
|
<form @submit.prevent="refreshOnline">
|
||||||
<input type="text" class="bd-textInput" placeholder="Search" @keydown.enter="searchInput" @keyup.stop :value="onlinePlugins.filters.sterm" />
|
<div class="bd-fancySearch" :class="{'bd-disabled': loadingOnline, 'bd-active': loadingOnline || (onlinePlugins && onlinePlugins.docs)}">
|
||||||
</div>
|
<input type="text" class="bd-textInput" placeholder="Search" v-model="onlinePlugins.filters.sterm" :disabled="loadingOnline" @input="search" @keyup.stop/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="bd-flex bd-flexRow" v-if="onlinePlugins && onlinePlugins.docs && onlinePlugins.docs.length">
|
<div class="bd-flex bd-flexRow" v-if="onlinePlugins && onlinePlugins.docs && onlinePlugins.docs.length">
|
||||||
<div class="bd-searchSort bd-flex bd-flexGrow">
|
<div class="bd-searchSort bd-flex bd-flexGrow">
|
||||||
|
@ -49,11 +51,13 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ScrollerWrap class="bd-onlinePhBody" v-if="!loadingOnline && onlinePlugins" :scrollend="scrollend">
|
<ScrollerWrap class="bd-onlinePhBody" @scrollend="scrollend">
|
||||||
<RemoteCard v-if="onlinePlugins && onlinePlugins.docs" v-for="plugin in onlinePlugins.docs" :key="onlinePlugins.id" :item="plugin" :tagClicked="searchByTag" />
|
<template v-if="!loadingOnline && onlinePlugins">
|
||||||
<div class="bd-spinnerContainer">
|
<RemoteCard v-if="onlinePlugins && onlinePlugins.docs" v-for="plugin in onlinePlugins.docs" :key="onlinePlugins.id" :item="plugin" @tagclicked="searchByTag" />
|
||||||
<div v-if="loadingMore" class="bd-spinner7" />
|
<div class="bd-spinnerContainer">
|
||||||
</div>
|
<div v-if="loadingMore" class="bd-spinner7" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</ScrollerWrap>
|
</ScrollerWrap>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -94,7 +98,8 @@
|
||||||
},
|
},
|
||||||
loadingOnline: false,
|
loadingOnline: false,
|
||||||
loadingMore: false,
|
loadingMore: false,
|
||||||
searchHint: ''
|
searchHint: '',
|
||||||
|
searchTimeout: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
@ -107,8 +112,9 @@
|
||||||
showLocal() {
|
showLocal() {
|
||||||
this.local = true;
|
this.local = true;
|
||||||
},
|
},
|
||||||
showOnline() {
|
async showOnline() {
|
||||||
this.local = false;
|
this.local = false;
|
||||||
|
if (!this.onlinePlugins.pagination.total) await this.refreshOnline();
|
||||||
},
|
},
|
||||||
async refreshLocal() {
|
async refreshLocal() {
|
||||||
await this.PluginManager.refreshPlugins();
|
await this.PluginManager.refreshPlugins();
|
||||||
|
@ -156,11 +162,6 @@
|
||||||
dont_clone
|
dont_clone
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
searchInput(e) {
|
|
||||||
if (this.loadingOnline || this.loadingMore) return;
|
|
||||||
this.onlinePlugins.filters.sterm = e.target.value;
|
|
||||||
this.refreshOnline();
|
|
||||||
},
|
|
||||||
async scrollend(e) {
|
async scrollend(e) {
|
||||||
if (this.onlinePlugins.pagination.page >= this.onlinePlugins.pagination.pages) return;
|
if (this.onlinePlugins.pagination.page >= this.onlinePlugins.pagination.pages) return;
|
||||||
if (this.loadingOnline || this.loadingMore) return;
|
if (this.loadingOnline || this.loadingMore) return;
|
||||||
|
@ -193,6 +194,10 @@
|
||||||
}
|
}
|
||||||
this.refreshOnline();
|
this.refreshOnline();
|
||||||
},
|
},
|
||||||
|
async search() {
|
||||||
|
clearTimeout(this.searchTimeout);
|
||||||
|
this.searchTimeout = setTimeout(this.refreshOnline, 1000);
|
||||||
|
},
|
||||||
async searchByTag(tag) {
|
async searchByTag(tag) {
|
||||||
if (this.loadingOnline || this.loadingMore) return;
|
if (this.loadingOnline || this.loadingMore) return;
|
||||||
this.onlinePlugins.filters.sterm = tag;
|
this.onlinePlugins.filters.sterm = tag;
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<div class="bd-flexRow bd-flex bd-flexGrow">
|
<div class="bd-flexRow bd-flex bd-flexGrow">
|
||||||
<div class="bd-flexGrow bd-remoteCardTags">
|
<div class="bd-flexGrow bd-remoteCardTags">
|
||||||
<div v-for="(tag, index) in item.tags" class="bd-remoteCardTag">
|
<div v-for="(tag, index) in item.tags" class="bd-remoteCardTag">
|
||||||
<div @click="tagClicked(tag)">{{tag}}</div><span v-if="index + 1 < item.tags.length">, </span>
|
<div @click="$emit('tagclicked', tag)">{{tag}}</div><span v-if="index + 1 < item.tags.length">, </span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bd-buttonGroup">
|
<div class="bd-buttonGroup">
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
import { shell } from 'electron';
|
import { shell } from 'electron';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['item', 'tagClicked'],
|
props: ['item'],
|
||||||
data() {
|
data() {
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="bd-settingswrap">
|
<div class="bd-settingswrap" :class="{'bd-settingswrapNoscroller': noscroller}">
|
||||||
<div v-if="noscroller" class="bd-flex bd-flexCol">
|
<div v-if="noscroller" class="bd-flex bd-flexCol bd-flexGrow">
|
||||||
<div class="bd-settingswrapHeader">
|
<div class="bd-settingswrapHeader">
|
||||||
<span class="bd-settingswrapHeaderText">{{ headertext }}</span>
|
<span class="bd-settingswrapHeaderText">{{ headertext }}</span>
|
||||||
<slot name="header" />
|
<slot name="header" />
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ScrollerWrap v-else :scrollend="scrollend">
|
<ScrollerWrap v-else @scrollend="$emit('scrollend', $event)">
|
||||||
<div class="bd-settingswrapHeader">
|
<div class="bd-settingswrapHeader">
|
||||||
<span class="bd-settingswrapHeaderText">{{ headertext }}</span>
|
<span class="bd-settingswrapHeaderText">{{ headertext }}</span>
|
||||||
<slot name="header" />
|
<slot name="header" />
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
import { ScrollerWrap } from '../common';
|
import { ScrollerWrap } from '../common';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['headertext', 'scrollend', 'noscroller'],
|
props: ['headertext', 'noscroller'],
|
||||||
components: {
|
components: {
|
||||||
ScrollerWrap
|
ScrollerWrap
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<Button v-if="devmode && !theme.packed" v-tooltip="'Package Theme'" @click="package"><MiBoxDownload size="18" /></Button>
|
<Button v-if="devmode && !theme.packed" v-tooltip="'Package Theme'" @click="package"><MiBoxDownload size="18" /></Button>
|
||||||
<Button v-tooltip="'Settings (shift + click to open settings without cloning the set)'" v-if="theme.hasSettings" @click="$emit('show-settings', $event.shiftKey)"><MiSettings size="18" /></Button>
|
<Button v-tooltip="'Settings (shift + click to open settings without cloning the set)'" v-if="theme.hasSettings" @click="$emit('show-settings', $event.shiftKey)"><MiSettings size="18" /></Button>
|
||||||
<Button v-tooltip="'Recompile (shift + click to reload)'" @click="$emit('reload-theme', $event.shiftKey)"><MiRefresh size="18" /></Button>
|
<Button v-tooltip="'Recompile (shift + click to reload)'" @click="$emit('reload-theme', $event.shiftKey)"><MiRefresh size="18" /></Button>
|
||||||
<Button v-tooltip="'Edit'" @click="editTheme"><MiPencil size="18" /></Button>
|
<Button v-if="devmode && !theme.packed" v-tooltip="'Edit'" @click="editTheme"><MiPencil size="18" /></Button>
|
||||||
<Button v-tooltip="'Uninstall (shift + click to unload)'" @click="$emit('delete-theme', $event.shiftKey)" type="err"><MiDelete size="18" /></Button>
|
<Button v-tooltip="'Uninstall (shift + click to unload)'" @click="$emit('delete-theme', $event.shiftKey)" type="err"><MiDelete size="18" /></Button>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</Card>
|
</Card>
|
||||||
|
@ -33,14 +33,19 @@
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
devmode: Settings.getSetting('core', 'advanced', 'developer-mode').value
|
devmodeSetting: Settings.getSetting('core', 'advanced', 'developer-mode')
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
props: ['theme', 'online'],
|
props: ['theme', 'online'],
|
||||||
components: {
|
components: {
|
||||||
Card, Button, ButtonGroup, SettingSwitch,
|
Card, Button, ButtonGroup, SettingSwitch,
|
||||||
MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension, MiBoxDownload
|
MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension, MiBoxDownload
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
devmode() {
|
||||||
|
return this.devmodeSetting.value;
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async package() {
|
async package() {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -34,9 +34,11 @@
|
||||||
<div class="bd-spinner7" />
|
<div class="bd-spinner7" />
|
||||||
</div>
|
</div>
|
||||||
<div class="bd-searchHint">{{searchHint}}</div>
|
<div class="bd-searchHint">{{searchHint}}</div>
|
||||||
<div class="bd-fancySearch" :class="{'bd-disabled': loadingOnline, 'bd-active': loadingOnline || (onlineThemes && onlineThemes.docs)}">
|
<form @submit.prevent="refreshOnline">
|
||||||
<input type="text" class="bd-textInput" placeholder="Search" @keydown.enter="searchInput" @keyup.stop :value="onlineThemes.filters.sterm"/>
|
<div class="bd-fancySearch" :class="{'bd-disabled': loadingOnline, 'bd-active': loadingOnline || (onlineThemes && onlineThemes.docs)}">
|
||||||
</div>
|
<input type="text" class="bd-textInput" placeholder="Search" v-model="onlineThemes.filters.sterm" :disabled="loadingOnline" @input="search" @keyup.stop/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="bd-flex bd-flexRow" v-if="onlineThemes && onlineThemes.docs && onlineThemes.docs.length">
|
<div class="bd-flex bd-flexRow" v-if="onlineThemes && onlineThemes.docs && onlineThemes.docs.length">
|
||||||
<div class="bd-searchSort bd-flex bd-flexGrow">
|
<div class="bd-searchSort bd-flex bd-flexGrow">
|
||||||
|
@ -48,11 +50,13 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ScrollerWrap class="bd-onlinePhBody" v-if="!loadingOnline && onlineThemes" :scrollend="scrollend">
|
<ScrollerWrap class="bd-onlinePhBody" @scrollend="scrollend">
|
||||||
<RemoteCard v-if="onlineThemes && onlineThemes.docs" v-for="theme in onlineThemes.docs" :key="theme.id" :item="theme" :tagClicked="searchByTag"/>
|
<template v-if="!loadingOnline && onlineThemes">
|
||||||
<div class="bd-spinnerContainer">
|
<RemoteCard v-if="onlineThemes && onlineThemes.docs" v-for="theme in onlineThemes.docs" :key="theme.id" :item="theme" @tagclicked="searchByTag"/>
|
||||||
<div v-if="loadingMore" class="bd-spinner7"/>
|
<div class="bd-spinnerContainer">
|
||||||
</div>
|
<div v-if="loadingMore" class="bd-spinner7"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</ScrollerWrap>
|
</ScrollerWrap>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -93,7 +97,8 @@
|
||||||
},
|
},
|
||||||
loadingOnline: false,
|
loadingOnline: false,
|
||||||
loadingMore: false,
|
loadingMore: false,
|
||||||
searchHint: ''
|
searchHint: '',
|
||||||
|
searchTimeout: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
@ -108,6 +113,7 @@
|
||||||
},
|
},
|
||||||
async showOnline() {
|
async showOnline() {
|
||||||
this.local = false;
|
this.local = false;
|
||||||
|
if (!this.onlineThemes.pagination.total) await this.refreshOnline();
|
||||||
},
|
},
|
||||||
async refreshLocal() {
|
async refreshLocal() {
|
||||||
await this.ThemeManager.refreshThemes();
|
await this.ThemeManager.refreshThemes();
|
||||||
|
@ -154,11 +160,6 @@
|
||||||
dont_clone
|
dont_clone
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
searchInput(e) {
|
|
||||||
if (this.loadingOnline || this.loadingMore) return;
|
|
||||||
this.onlineThemes.filters.sterm = e.target.value;
|
|
||||||
this.refreshOnline();
|
|
||||||
},
|
|
||||||
async scrollend(e) {
|
async scrollend(e) {
|
||||||
if (this.onlineThemes.pagination.page >= this.onlineThemes.pagination.pages) return;
|
if (this.onlineThemes.pagination.page >= this.onlineThemes.pagination.pages) return;
|
||||||
if (this.loadingOnline || this.loadingMore) return;
|
if (this.loadingOnline || this.loadingMore) return;
|
||||||
|
@ -191,6 +192,10 @@
|
||||||
}
|
}
|
||||||
this.refreshOnline();
|
this.refreshOnline();
|
||||||
},
|
},
|
||||||
|
async search() {
|
||||||
|
clearTimeout(this.searchTimeout);
|
||||||
|
this.searchTimeout = setTimeout(this.refreshOnline, 1000);
|
||||||
|
},
|
||||||
async searchByTag(tag) {
|
async searchByTag(tag) {
|
||||||
if (this.loadingOnline || this.loadingMore) return;
|
if (this.loadingOnline || this.loadingMore) return;
|
||||||
this.onlineThemes.filters.sterm = tag;
|
this.onlineThemes.filters.sterm = tag;
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<div class="bd-settingSwitch">
|
<div class="bd-settingSwitch">
|
||||||
<div class="bd-title">
|
<div class="bd-title">
|
||||||
<h3>{{item.text}}</h3>
|
<h3>{{item.text}}</h3>
|
||||||
<div class="bd-switchWrapper" @click="() => toggle(item)">
|
<div class="bd-switchWrapper" @click="$emit('toggle', item)">
|
||||||
<input type="checkbox" class="bd-switchCheckbox" />
|
<input type="checkbox" class="bd-switchCheckbox" />
|
||||||
<div class="bd-switch" :class="{'bd-checked': item.status.update}" />
|
<div class="bd-switch" :class="{'bd-checked': item.status.update}" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -23,6 +23,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: ['item', 'toggle']
|
props: ['item']
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<div class="bd-formDivider"></div>
|
<div class="bd-formDivider"></div>
|
||||||
<div v-for="update in bdUpdates">
|
<div v-for="update in bdUpdates">
|
||||||
<UpdaterStatus :item="update" v-if="update.status.updating" />
|
<UpdaterStatus :item="update" v-if="update.status.updating" />
|
||||||
<UpdaterToggle :item="update" :toggle="() => updater.toggleUpdate(update)" v-else />
|
<UpdaterToggle :item="update" @toggle="updater.toggleUpdate(update)" v-else />
|
||||||
<div class="bd-formDivider"></div>
|
<div class="bd-formDivider"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
},
|
},
|
||||||
removeItem(file_path) {
|
removeItem(file_path) {
|
||||||
if (this.setting.disabled) return;
|
if (this.setting.disabled) return;
|
||||||
this.setting.value = Utils.removeFromArray(this.setting.value, file_path);
|
this.setting.value = this.setting.value.filter(f => f !== file_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,13 +10,13 @@
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="bd-dropdown" :class="{'bd-active': active, 'bd-disabled': disabled}">
|
<div class="bd-dropdown" :class="{'bd-active': active, 'bd-disabled': disabled}">
|
||||||
<div class="bd-dropdownCurrent" @click="() => active = !active && !disabled">
|
<div class="bd-dropdownCurrent" @click.stop="() => active = !active && !disabled">
|
||||||
<span class="bd-dropdownText">{{ getSelectedText() }}</span>
|
<span class="bd-dropdownText">{{ selectedText }}</span>
|
||||||
<span class="bd-dropdownArrowWrap">
|
<span class="bd-dropdownArrowWrap">
|
||||||
<span class="bd-dropdownArrow"></span>
|
<span class="bd-dropdownArrow"></span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="bd-dropdownOptions bd-flex bd-flexCol" ref="options" v-if="active">
|
<div class="bd-dropdownOptions bd-flex bd-flexCol" ref="options" v-if="active && !disabled">
|
||||||
<div class="bd-dropdownOption" v-for="option in options" :class="{'bd-dropdownOptionSelected': value === option.value}" @click="select(option)">{{ option.text }}</div>
|
<div class="bd-dropdownOption" v-for="option in options" :class="{'bd-dropdownOptionSelected': value === option.value}" @click="select(option)">{{ option.text }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -31,11 +31,15 @@
|
||||||
clickHandler: null
|
clickHandler: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
computed: {
|
||||||
getSelectedText() {
|
selectedOption() {
|
||||||
const selected_option = this.options.find(option => option.value === this.value);
|
return this.options.find(option => option.value === this.value);
|
||||||
return selected_option ? selected_option.text : this.value;
|
|
||||||
},
|
},
|
||||||
|
selectedText() {
|
||||||
|
return this.selectedOption ? this.selectedOption.text : this.value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
select(option) {
|
select(option) {
|
||||||
this.$emit('input', option.value);
|
this.$emit('input', option.value);
|
||||||
this.active = false;
|
this.active = false;
|
||||||
|
|
|
@ -18,12 +18,11 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: ['dark', 'scrollend'],
|
props: ['dark'],
|
||||||
methods: {
|
methods: {
|
||||||
onscroll(e) {
|
onscroll(e) {
|
||||||
if (!this.scrollend) return;
|
|
||||||
const { offsetHeight, scrollTop, scrollHeight } = e.target;
|
const { offsetHeight, scrollTop, scrollHeight } = e.target;
|
||||||
if (offsetHeight + scrollTop >= scrollHeight) this.scrollend(e);
|
if (offsetHeight + scrollTop >= scrollHeight) this.$emit('scrollend', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: ['item', 'onClick', 'checked']
|
props: ['item', 'checked']
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -9,13 +9,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="bd-button" :class="classes" @click="onClick">
|
<div class="bd-button" @click="$emit('click')">
|
||||||
{{text}}
|
{{text}}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: ['classes', 'text', 'onClick']
|
props: ['text']
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -9,15 +9,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="bd-buttonGroup" :class="classes">
|
<div class="bd-buttonGroup">
|
||||||
<Button v-for="(button, index) in buttons" :text="button.text" :classes="button.classes" :onClick="button.onClick" :key="index"/>
|
<Button v-for="(button, index) in buttons" :text="button.text" :classes="button.class" @click="button.onClick" :key="index"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Button from './Button.vue';
|
import Button from './Button.vue';
|
||||||
export default {
|
export default {
|
||||||
props: ['buttons', 'classes'],
|
props: ['buttons'],
|
||||||
components: { Button }
|
components: { Button }
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -84,7 +84,7 @@ export class DiscordContextMenu {
|
||||||
}
|
}
|
||||||
|
|
||||||
static renderCm(component, args, retVal, res) {
|
static renderCm(component, args, retVal, res) {
|
||||||
if (!retVal.props || !res.props) return;
|
if (!retVal.props || !retVal.props.style || !res.props) return;
|
||||||
const { target } = component.props;
|
const { target } = component.props;
|
||||||
const { top, left } = retVal.props.style;
|
const { top, left } = retVal.props.style;
|
||||||
if (!target || !top || !left) return;
|
if (!target || !top || !left) return;
|
||||||
|
|
|
@ -71,7 +71,7 @@ export default class extends Module {
|
||||||
const c = contributors.find(c => c.id === user.id);
|
const c = contributors.find(c => c.id === user.id);
|
||||||
if (!c) return;
|
if (!c) return;
|
||||||
|
|
||||||
const nameTag = retVal.props.children.props.children[1].props.children[0];
|
const nameTag = retVal.props.children[1].props.children[0].props.children[0];
|
||||||
nameTag.type = this.PatchedNameTag || nameTag.type;
|
nameTag.type = this.PatchedNameTag || nameTag.type;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -89,26 +89,24 @@ export default class extends Module {
|
||||||
|
|
||||||
const NameTag = await ReactComponents.getComponent('NameTag');
|
const NameTag = await ReactComponents.getComponent('NameTag');
|
||||||
|
|
||||||
this.PatchedNameTag = class extends NameTag.component {
|
this.PatchedNameTag = function (props) {
|
||||||
render() {
|
const retVal = NameTag.component.apply(this, arguments);
|
||||||
const retVal = NameTag.component.prototype.render.call(this, arguments);
|
try {
|
||||||
try {
|
if (!retVal.props || !retVal.props.children) return retVal;
|
||||||
if (!retVal.props || !retVal.props.children) return;
|
|
||||||
|
|
||||||
const user = ReactHelpers.findProp(this, 'user');
|
const user = ReactHelpers.findProp(props, 'user');
|
||||||
if (!user) return;
|
if (!user) return retVal;
|
||||||
const contributor = contributors.find(c => c.id === user.id);
|
const contributor = contributors.find(c => c.id === user.id);
|
||||||
if (!contributor) return;
|
if (!contributor) return retVal;
|
||||||
|
|
||||||
retVal.props.children.splice(1, 0, VueInjector.createReactElement(BdBadge, {
|
retVal.props.children.splice(1, 0, VueInjector.createReactElement(BdBadge, {
|
||||||
contributor,
|
contributor,
|
||||||
type: 'nametag'
|
type: 'nametag'
|
||||||
}));
|
}));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Logger.err('ProfileBadges', ['Error thrown while rendering a NameTag', err]);
|
Logger.err('ProfileBadges', ['Error thrown while rendering a NameTag', err]);
|
||||||
}
|
|
||||||
return retVal;
|
|
||||||
}
|
}
|
||||||
|
return retVal;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Rerender all channel members
|
// Rerender all channel members
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Reflection } from 'modules';
|
import { Reflection, ReactComponents } from 'modules';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
export default class {
|
export default class {
|
||||||
|
@ -16,12 +16,12 @@ export default class {
|
||||||
/**
|
/**
|
||||||
* Creates a new Vue object and mounts it in the passed element.
|
* 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 {HTMLElement} root The element to mount the new Vue object at
|
||||||
* @param {Object} options Options to pass to Vue
|
* @param {Object} options Options to pass to Vue (see https://vuejs.org/v2/api/#Options-Data)
|
||||||
* @param {BdNode} bdnode The element to append
|
* @param {BdNode} bdnode The element to append
|
||||||
* @return {Vue}
|
* @return {Vue}
|
||||||
*/
|
*/
|
||||||
static inject(root, options, bdnode) {
|
static inject(root, options, bdnode) {
|
||||||
if(bdnode) bdnode.appendTo(root);
|
if (bdnode) bdnode.appendTo(root);
|
||||||
|
|
||||||
const vue = new Vue(options);
|
const vue = new Vue(options);
|
||||||
|
|
||||||
|
@ -37,18 +37,18 @@ export default class {
|
||||||
* @return {React.Element}
|
* @return {React.Element}
|
||||||
*/
|
*/
|
||||||
static createReactElement(component, props, mountAtTop) {
|
static createReactElement(component, props, mountAtTop) {
|
||||||
const { React } = Reflection.modules;
|
return Reflection.modules.React.createElement(this.ReactCompatibility, {component, mountAtTop, props});
|
||||||
return React.createElement(this.ReactCompatibility, {component, mountAtTop, props});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get ReactCompatibility() {
|
static get ReactCompatibility() {
|
||||||
if (this._ReactCompatibility) return this._ReactCompatibility;
|
const { React, ReactDOM } = Reflection.modules;
|
||||||
|
|
||||||
const { React, ReactDOM} = Reflection.modules;
|
/**
|
||||||
|
* A React component that renders a Vue component.
|
||||||
return this._ReactCompatibility = class VueComponent extends React.Component {
|
*/
|
||||||
|
const ReactCompatibility = class VueComponent extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return React.createElement('span');
|
return React.createElement('span', {className: 'bd-reactVueComponent'});
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -89,7 +89,13 @@ export default class {
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// Add a name for ReactComponents
|
||||||
|
ReactCompatibility.displayName = 'BD.VueComponent';
|
||||||
|
ReactCompatibility[ReactComponents.ReactComponent.important] = {selector: '.bd-reactVueComponent'};
|
||||||
|
|
||||||
|
return Object.defineProperty(this, 'ReactCompatibility', {value: ReactCompatibility}).ReactCompatibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
static install(Vue) {
|
static install(Vue) {
|
||||||
|
@ -98,6 +104,9 @@ export default class {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Vue component that renders a React component.
|
||||||
|
*/
|
||||||
export const ReactComponent = {
|
export const ReactComponent = {
|
||||||
props: ['component', 'component-props', 'component-children', 'react-element'],
|
props: ['component', 'component-props', 'component-children', 'react-element'],
|
||||||
render(createElement) {
|
render(createElement) {
|
||||||
|
|
|
@ -17,7 +17,7 @@ const config = {
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
externals: {
|
externals: {
|
||||||
sparkplug: 'require("./sparkplug")'
|
sparkplug: 'require("../core/sparkplug")'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,6 @@ const TEST_ARGS = () => {
|
||||||
'data': path.resolve(_baseDataPath, 'data'),
|
'data': path.resolve(_baseDataPath, 'data'),
|
||||||
'editor': path.resolve(_basePath, 'editor', 'dist'),
|
'editor': path.resolve(_basePath, 'editor', 'dist'),
|
||||||
// tmp: path.join(_basePath, 'tmp')
|
// tmp: path.join(_basePath, 'tmp')
|
||||||
tmp: path.join(os.tmpdir(), 'betterdiscord', `${process.getuid()}`)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,6 +82,12 @@ class Comms {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
BDIpc.on('bd-native-save', (event, options) => {
|
||||||
|
dialog.showSaveDialog(OriginalBrowserWindow.fromWebContents(event.ipcEvent.sender), options, filename => {
|
||||||
|
event.reply(filename);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
BDIpc.on('bd-compileSass', (event, options) => {
|
BDIpc.on('bd-compileSass', (event, options) => {
|
||||||
if (typeof options.path === 'string' && typeof options.data === 'string') {
|
if (typeof options.path === 'string' && typeof options.data === 'string') {
|
||||||
options.data = `${options.data} @import '${options.path.replace(/\\/g, '\\\\').replace(/'/g, '\\\'')}';`;
|
options.data = `${options.data} @import '${options.path.replace(/\\/g, '\\\\').replace(/'/g, '\\\'')}';`;
|
||||||
|
@ -103,7 +108,7 @@ class Comms {
|
||||||
BDIpc.on('bd-keytar-find-credentials', (event, { service }) => keytar.findCredentials(service), true);
|
BDIpc.on('bd-keytar-find-credentials', (event, { service }) => keytar.findCredentials(service), true);
|
||||||
|
|
||||||
BDIpc.on('bd-readDataFile', async (event, fileName) => {
|
BDIpc.on('bd-readDataFile', async (event, fileName) => {
|
||||||
const rf = await FileUtils.readFile(path.resolve(configProxy().getPath('data'), fileName));
|
const rf = await FileUtils.readFile(path.resolve(this.bd.config.getPath('data'), fileName));
|
||||||
event.reply(rf);
|
event.reply(rf);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -197,6 +202,11 @@ class BrowserWindow extends OriginalBrowserWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some Electron APIs depend on browserWindow.constructor being BrowserWindow
|
||||||
|
Object.defineProperty(BrowserWindow.prototype, 'constructor', {
|
||||||
|
value: OriginalBrowserWindow
|
||||||
|
});
|
||||||
|
|
||||||
export class BetterDiscord {
|
export class BetterDiscord {
|
||||||
|
|
||||||
get comms() { return this._comms ? this._comms : (this._commas = new Comms(this)); }
|
get comms() { return this._comms ? this._comms : (this._commas = new Comms(this)); }
|
||||||
|
@ -207,9 +217,13 @@ export class BetterDiscord {
|
||||||
get updater() { return this._updater ? this._updater : (this._updater = new Updater(this)); }
|
get updater() { return this._updater ? this._updater : (this._updater = new Updater(this)); }
|
||||||
get sendToDiscord() { return this.windowUtils.send; }
|
get sendToDiscord() { return this.windowUtils.send; }
|
||||||
|
|
||||||
constructor(args) {
|
constructor(...args) {
|
||||||
if (TESTS) args = TEST_ARGS();
|
if (TESTS) args.unshift(TEST_ARGS());
|
||||||
|
|
||||||
|
args = deepmerge.all(args);
|
||||||
|
|
||||||
console.log('[BetterDiscord|args] ', JSON.stringify(args, null, 4));
|
console.log('[BetterDiscord|args] ', JSON.stringify(args, null, 4));
|
||||||
|
|
||||||
if (BetterDiscord.loaded) {
|
if (BetterDiscord.loaded) {
|
||||||
console.log('[BetterDiscord] Creating two BetterDiscord objects???');
|
console.log('[BetterDiscord] Creating two BetterDiscord objects???');
|
||||||
return null;
|
return null;
|
||||||
|
@ -337,6 +351,10 @@ export class BetterDiscord {
|
||||||
this.config.addPath('userfiles', userfiles);
|
this.config.addPath('userfiles', userfiles);
|
||||||
this.config.addPath('snippets', snippets);
|
this.config.addPath('snippets', snippets);
|
||||||
if (!this.config.getPath('editor')) this.config.addPath('editor', path.resolve(base, 'editor'));
|
if (!this.config.getPath('editor')) this.config.addPath('editor', path.resolve(base, 'editor'));
|
||||||
|
|
||||||
|
if (!this.config.getPath('tmp')) this.config.addPath('tmp', process.platform !== 'win32' ?
|
||||||
|
path.join(os.tmpdir(), 'betterdiscord', `${process.getuid()}`) :
|
||||||
|
path.join(os.tmpdir(), 'betterdiscord'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Module from './modulebase';
|
import Module from './modulebase';
|
||||||
|
import path from 'path';
|
||||||
|
import os from 'os';
|
||||||
|
|
||||||
export default class Config extends Module {
|
export default class Config extends Module {
|
||||||
|
|
||||||
|
@ -72,6 +74,9 @@ export default class Config extends Module {
|
||||||
|
|
||||||
// Compatibility with old client code and new installer args
|
// Compatibility with old client code and new installer args
|
||||||
compatibility() {
|
compatibility() {
|
||||||
this.args.paths = Object.entries(this.args.paths).map(([id, path]) => ({ id, path }));
|
this.args.paths = Object.entries(this.args.paths).map(([id, _path]) => ({
|
||||||
|
id, path: path.resolve(os.homedir(), _path)
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import semver from 'semver';
|
||||||
import Axi from './axi';
|
import Axi from './axi';
|
||||||
import zlib from 'zlib';
|
import zlib from 'zlib';
|
||||||
import tarfs from 'tar-fs';
|
import tarfs from 'tar-fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
const TEST_UPDATE = [
|
const TEST_UPDATE = [
|
||||||
{
|
{
|
||||||
|
@ -91,7 +92,8 @@ export default class Updater extends Module {
|
||||||
async updateBd(update) {
|
async updateBd(update) {
|
||||||
try {
|
try {
|
||||||
console.log('[BetterDiscord:Updater] Updating', update.id);
|
console.log('[BetterDiscord:Updater] Updating', update.id);
|
||||||
await this.downloadTarGz(`https://github.com/JsSucks/BetterDiscordApp${update.remote}`, this.bd.config.getPath('base'));
|
await this.downloadTarGz(`https://github.com/JsSucks/BetterDiscordApp${update.remote}`, this.bd.config.getPath('tmp'));
|
||||||
|
await FileUtils.rn(path.join(this.bd.config.getPath('tmp'), update.id), this.bd.config.getPath(update.id));
|
||||||
this.updateFinished(update);
|
this.updateFinished(update);
|
||||||
// Cleanup
|
// Cleanup
|
||||||
await FileUtils.rm(`${this.bd.config.getPath(update.id)}_old`);
|
await FileUtils.rm(`${this.bd.config.getPath(update.id)}_old`);
|
||||||
|
|
|
@ -84,14 +84,7 @@ gulp.task('client-pkg', function() {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('client-sparkplug', function() {
|
gulp.task('client-release', gulp.parallel('client-main', 'client-pkg'));
|
||||||
return pump([
|
|
||||||
gulp.src('core/dist/sparkplug.js'),
|
|
||||||
gulp.dest('release/client')
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('client-release', gulp.parallel('client-main', 'client-pkg', 'client-sparkplug'));
|
|
||||||
|
|
||||||
// Editor
|
// Editor
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
const bdinfo = require('./bd.json');
|
const bdinfo = require('./bd');
|
||||||
const { app } = require('electron');
|
const { app } = require('electron');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs');
|
const os = require('os');
|
||||||
const Module = require('module');
|
const Module = require('module');
|
||||||
|
|
||||||
const packagePath = path.join(__dirname, '..', 'app.asar');
|
const packagePath = path.resolve(__dirname, '..', 'app.asar');
|
||||||
app.getAppPath = () => packagePath;
|
app.getAppPath = () => packagePath;
|
||||||
|
|
||||||
function loadBd() {
|
function loadBd() {
|
||||||
const { paths } = bdinfo;
|
const userconfig = (() => {
|
||||||
const { BetterDiscord } = require(paths.core);
|
try {
|
||||||
const instance = new BetterDiscord(bdinfo);
|
return require(path.resolve(os.homedir(), bdinfo.paths.userconfig));
|
||||||
|
} catch (err) {}
|
||||||
|
})() || {};
|
||||||
|
|
||||||
|
const { BetterDiscord } = require(path.resolve(os.homedir(), (userconfig.paths || {}).core || bdinfo.paths.core));
|
||||||
|
const instance = new BetterDiscord(bdinfo, userconfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
app.on('ready', loadBd);
|
app.on('ready', loadBd);
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
const args = process.argv;
|
const process = require('process');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
|
const args = process.argv;
|
||||||
|
|
||||||
const useBdRelease = args[2] && args[2].toLowerCase() === 'release';
|
const useBdRelease = args[2] && args[2].toLowerCase() === 'release';
|
||||||
const releaseInput = useBdRelease ? args[3] && args[3].toLowerCase() : args[2] && args[2].toLowerCase();
|
const releaseInput = useBdRelease ? args[3] && args[3].toLowerCase() : args[2] && args[2].toLowerCase();
|
||||||
const release = releaseInput === 'canary' ? 'Discord Canary' : releaseInput === 'ptb' ? 'Discord PTB' : 'Discord';
|
const release = releaseInput === 'canary' ? 'Discord Canary' : releaseInput === 'ptb' ? 'Discord PTB' : 'Discord';
|
||||||
|
@ -16,7 +18,7 @@ const discordPath = (function() {
|
||||||
} else if (process.platform === 'darwin') {
|
} else if (process.platform === 'darwin') {
|
||||||
const appPath = releaseInput === 'canary' ? path.join('/Applications', 'Discord Canary.app')
|
const appPath = releaseInput === 'canary' ? path.join('/Applications', 'Discord Canary.app')
|
||||||
: releaseInput === 'ptb' ? path.join('/Applications', 'Discord PTB.app')
|
: releaseInput === 'ptb' ? path.join('/Applications', 'Discord PTB.app')
|
||||||
: useBdRelease && args[3] ? args[3] ? args[2] : args[2]
|
: useBdRelease && args[3] ? args[3] : !useBdRelease && args[2] ? args[2]
|
||||||
: path.join('/Applications', 'Discord.app');
|
: path.join('/Applications', 'Discord.app');
|
||||||
|
|
||||||
return path.join(appPath, 'Contents', 'Resources');
|
return path.join(appPath, 'Contents', 'Resources');
|
||||||
|
@ -31,30 +33,53 @@ console.log(`Found ${release} in ${discordPath}`);
|
||||||
const appPath = path.join(discordPath, 'app');
|
const appPath = path.join(discordPath, 'app');
|
||||||
const packageJson = path.join(appPath, 'package.json');
|
const packageJson = path.join(appPath, 'package.json');
|
||||||
const indexJs = path.join(appPath, 'index.js');
|
const indexJs = path.join(appPath, 'index.js');
|
||||||
|
const bdJson = path.join(appPath, 'bd.json');
|
||||||
|
|
||||||
if (!fs.existsSync(appPath)) fs.mkdirSync(appPath);
|
if (!fs.existsSync(appPath)) fs.mkdirSync(appPath);
|
||||||
if (fs.existsSync(packageJson)) fs.unlinkSync(packageJson);
|
if (fs.existsSync(packageJson)) fs.unlinkSync(packageJson);
|
||||||
if (fs.existsSync(indexJs)) fs.unlinkSync(indexJs);
|
if (fs.existsSync(indexJs)) fs.unlinkSync(indexJs);
|
||||||
|
if (fs.existsSync(bdJson)) fs.unlinkSync(bdJson);
|
||||||
const bdPath = useBdRelease ? path.resolve(__dirname, '..', 'release') : path.resolve(__dirname, '..');
|
|
||||||
|
|
||||||
console.log(`Writing package.json`);
|
console.log(`Writing package.json`);
|
||||||
fs.writeFileSync(packageJson, JSON.stringify({
|
fs.writeFileSync(path.join(appPath, 'package.json'), JSON.stringify({
|
||||||
name: 'betterdiscord',
|
name: 'betterdiscord',
|
||||||
description: 'BetterDiscord',
|
description: 'BetterDiscord',
|
||||||
main: 'index.js',
|
main: 'index.js',
|
||||||
private: true
|
private: true
|
||||||
}, null, 4));
|
}, null, 4));
|
||||||
|
|
||||||
console.log(`Writing index.js`);
|
if (useBdRelease) {
|
||||||
fs.writeFileSync(indexJs, `const path = require('path');
|
console.log(`Writing index.js`);
|
||||||
const fs = require('fs');
|
fs.writeFileSync(path.join(appPath, 'index.js'), fs.readFileSync(path.resolve(__dirname, '..', 'installer', 'stub.js')));
|
||||||
const Module = require('module');
|
|
||||||
const electron = require('electron');
|
console.log(`Writing bd.json`);
|
||||||
const basePath = path.join(__dirname, '..', 'app.asar');
|
fs.writeFileSync(path.join(appPath, 'bd.json'), JSON.stringify({
|
||||||
electron.app.getAppPath = () => basePath;
|
options: {
|
||||||
Module._load(basePath, null, true);
|
autoInject: true,
|
||||||
electron.app.on('ready', () => new (require('${bdPath.replace(/\\/g, '\\\\').replace(/'/g, '\\\'')}').BetterDiscord)());
|
commonCore: true,
|
||||||
`);
|
commonData: true
|
||||||
|
},
|
||||||
|
paths: {
|
||||||
|
core: path.resolve(__dirname, '..', 'release', 'core'),
|
||||||
|
client: path.resolve(__dirname, '..', 'release', 'client'),
|
||||||
|
editor: path.resolve(__dirname, '..', 'release', 'editor'),
|
||||||
|
data: path.resolve(__dirname, '..', 'release', 'data'),
|
||||||
|
// tmp: path.resolve(os.tmpdir(), 'betterdiscord', `${process.getuid()}`)
|
||||||
|
}
|
||||||
|
}, null, 4));
|
||||||
|
} else {
|
||||||
|
const bdPath = path.resolve(__dirname, '..');
|
||||||
|
|
||||||
|
console.log(`Writing index.js`);
|
||||||
|
fs.writeFileSync(path.join(appPath, 'index.js'), `const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
const Module = require('module');
|
||||||
|
const electron = require('electron');
|
||||||
|
const basePath = path.join(__dirname, '..', 'app.asar');
|
||||||
|
electron.app.getAppPath = () => basePath;
|
||||||
|
Module._load(basePath, null, true);
|
||||||
|
electron.app.on('ready', () => new (require('${bdPath.replace(/\\/g, '\\\\').replace(/'/g, '\\\'')}').BetterDiscord)());
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`Injection successful, please restart ${release}.`);
|
console.log(`Injection successful, please restart ${release}.`);
|
||||||
|
|
|
@ -101,15 +101,15 @@ module.exports = (Plugin, Api, Vendor) => {
|
||||||
if (!returnValue.props.children instanceof Array) returnValue.props.children = [returnValue.props.children];
|
if (!returnValue.props.children instanceof Array) returnValue.props.children = [returnValue.props.children];
|
||||||
// Add a generic Button component provided by BD
|
// Add a generic Button component provided by BD
|
||||||
returnValue.props.children.push(Api.Components.ButtonGroup({
|
returnValue.props.children.push(Api.Components.ButtonGroup({
|
||||||
classes: [ 'exampleBtnGroup' ], // Additional classes for button group
|
class: [ 'exampleBtnGroup' ], // Additional classes for button group
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
classes: ['exampleBtn'], // Additional classes for button
|
class: ['exampleBtn'], // Additional classes for button
|
||||||
text: 'Hello World!', // Text for button
|
text: 'Hello World!', // Text for button
|
||||||
onClick: e => Logger.log('Hello World!') // Button click handler
|
onClick: e => Logger.log('Hello World!') // Button click handler
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
classes: ['exampleBtn'],
|
class: ['exampleBtn'],
|
||||||
text: 'Button',
|
text: 'Button',
|
||||||
onClick: e => Logger.log('Button!')
|
onClick: e => Logger.log('Button!')
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue