diff --git a/client/src/builtin/Emotes/EmoteModule.js b/client/src/builtin/Emotes/EmoteModule.js index 09ca3e15..82f287fd 100644 --- a/client/src/builtin/Emotes/EmoteModule.js +++ b/client/src/builtin/Emotes/EmoteModule.js @@ -279,8 +279,6 @@ export default new class EmoteModule extends BuiltinModule { const { content } = args[1]; if (!content) return orig(...args); - Logger.log('EmoteModule', ['Sending message', MessageActions, args, orig]); - 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)); diff --git a/client/src/data/user.settings.default.json b/client/src/data/user.settings.default.json index 7acf30aa..30129731 100644 --- a/client/src/data/user.settings.default.json +++ b/client/src/data/user.settings.default.json @@ -31,7 +31,7 @@ "id": "developer-mode", "type": "bool", "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 }, { diff --git a/client/src/index.js b/client/src/index.js index 6e69ce45..c008be88 100644 --- a/client/src/index.js +++ b/client/src/index.js @@ -22,7 +22,7 @@ const ignoreExternal = tests && true; class BetterDiscord { 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.log('main', 'BetterDiscord starting'); diff --git a/client/src/modules/content.js b/client/src/modules/content.js index cfe584dd..d56dc236 100644 --- a/client/src/modules/content.js +++ b/client/src/modules/content.js @@ -118,7 +118,7 @@ export default class Content extends AsyncEventEmitter { * @return {Promise} */ async enable(save = true) { - if (this.enabled) return; + if (this.enabled || this.unloaded) return; await this.emit('enable'); await this.emit('start'); diff --git a/client/src/modules/contentmanager.js b/client/src/modules/contentmanager.js index d0e981f2..08c2abb4 100644 --- a/client/src/modules/contentmanager.js +++ b/client/src/modules/contentmanager.js @@ -16,7 +16,7 @@ import { remote } from 'electron'; import Content from './content'; import Globals from './globals'; 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 { Modals } from 'ui'; import Combokeys from 'combokeys'; @@ -73,23 +73,23 @@ export default class { } static async packContent(path, contentPath) { - return new Promise((resolve, reject) => { - remote.dialog.showSaveDialog({ - title: 'Save Package', - defaultPath: path, - filters: [ - { - name: 'BetterDiscord Package', - extensions: ['bd'] - } - ] - }, filepath => { - if (!filepath) return; + const filepath = await ClientIPC.send('bd-native-save', { + title: 'Save Package', + defaultPath: path, + filters: [ + { + name: 'BetterDiscord Package', + extensions: ['bd'] + } + ] + }); - asar.uncache(filepath); - asar.createPackage(contentPath, filepath, () => { - resolve(filepath); - }); + if (!filepath) return; + + 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); for (const dir of directories) { - const packed = dir.endsWith('.bd'); - - if (!packed) { - try { - await FileUtils.directoryExists(path.join(this.contentPath, dir)); - } catch (err) { continue; } - } + const stat = await FileUtils.stat(path.join(this.contentPath, dir)); + const packed = stat.isFile(); try { if (packed) { @@ -148,6 +143,8 @@ export default class { * @param {bool} suppressErrors Suppress any errors that occur during loading of content */ static async refreshContent(suppressErrors = false) { + this.loaded = true; + if (!this.localContent.length) return this.loadAllContent(); try { @@ -155,18 +152,18 @@ export default class { const directories = await FileUtils.listDirectory(this.contentPath); for (const dir of directories) { - const packed = dir.endsWith('.bd'); - // If content is already loaded this should resolve if (this.getContentByDirName(dir)) continue; - try { - await FileUtils.directoryExists(path.join(this.contentPath, dir)); - } catch (err) { continue; } + const stat = await FileUtils.stat(path.join(this.contentPath, dir)); + const packed = stat.isFile(); try { - // Load if not - await this.preloadContent(dir); + if (packed) { + await this.preloadPackedContent(dir); + } else { + await this.preloadContent(dir); + } } catch (err) { // We don't want every plugin/theme to fail loading when one does this.errors.push(new ErrorEvent({ @@ -217,7 +214,8 @@ export default class { await FileUtils.fileExists(packagePath); 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); @@ -348,7 +346,7 @@ export default class { await unload; await FileUtils.recursiveDeleteDirectory(content.paths.contentPath); - if (content.packed) await FileUtils.recursiveDeleteDirectory(content.packagePath); + if (content.packed) await FileUtils.deleteFile(content.packagePath); return true; } catch (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}.`}; try { + Object.defineProperty(content, 'unloaded', {configurable: true, value: true}); + const disablePromise = content.disable(false); const unloadPromise = content.emit('unload', reload); @@ -380,8 +380,14 @@ export default class { 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); } catch (err) { Logger.err(this.moduleName, err); @@ -433,7 +439,7 @@ export default class { static getContentIndex(content) { return this.localContent.findIndex(c => c === content) } 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 getContentByName(name) { return this.localContent.find(c => c.name === name) } diff --git a/client/src/modules/reactcomponents.js b/client/src/modules/reactcomponents.js index 202adaf1..a594c107 100644 --- a/client/src/modules/reactcomponents.js +++ b/client/src/modules/reactcomponents.js @@ -262,7 +262,7 @@ export class ReactComponents { } 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; } diff --git a/client/src/modules/theme.js b/client/src/modules/theme.js index edd45406..11a1d681 100644 --- a/client/src/modules/theme.js +++ b/client/src/modules/theme.js @@ -22,10 +22,20 @@ export default class Theme extends Content { const watchfiles = Settings.getSetting('css', 'default', 'watch-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; 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' } @@ -61,7 +71,7 @@ export default class Theme extends Content { async compile() { 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 result = await ClientIPC.send('bd-compileSass', { @@ -147,7 +157,7 @@ export default class Theme extends Content { * @param {Array} files Files to watch */ set watchfiles(files) { - if (this.packed) { + if (this.unloaded || this.packed) { // Don't watch files for packed themes return; } diff --git a/client/src/modules/thememanager.js b/client/src/modules/thememanager.js index f97e4778..b02f2ec9 100644 --- a/client/src/modules/thememanager.js +++ b/client/src/modules/thememanager.js @@ -40,7 +40,7 @@ export default class ThemeManager extends ContentManager { }); if (instance.enabled) { instance.userConfig.enabled = false; - instance.enable(); + instance.enable(false); } return instance; } catch (err) { diff --git a/client/src/styles/partials/bdsettings/card.scss b/client/src/styles/partials/bdsettings/card.scss index e313b4c7..f2e9bf9e 100644 --- a/client/src/styles/partials/bdsettings/card.scss +++ b/client/src/styles/partials/bdsettings/card.scss @@ -1,7 +1,6 @@ .bd-card { display: flex; flex-direction: column; - flex-grow: 1; background: transparent; border-bottom: 1px solid rgba(114, 118, 126, .3); min-height: 150px; diff --git a/client/src/styles/partials/bdsettings/collection.scss b/client/src/styles/partials/bdsettings/collection.scss deleted file mode 100644 index d8e5bfbd..00000000 --- a/client/src/styles/partials/bdsettings/collection.scss +++ /dev/null @@ -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; - } - } - } -} diff --git a/client/src/styles/partials/bdsettings/contentview.scss b/client/src/styles/partials/bdsettings/contentview.scss index 9f5a084f..310d466e 100644 --- a/client/src/styles/partials/bdsettings/contentview.scss +++ b/client/src/styles/partials/bdsettings/contentview.scss @@ -1,16 +1,17 @@ .bd-pluginsview, .bd-themesview { - .bd-localPh { - .bd-scroller { - padding: 0 20px 0 0; - } - } + flex-grow: 1; .bd-onlinePh, .bd-localPh { display: flex; flex-direction: column; - margin: 10px 0; + flex-grow: 1; + + > .bd-scrollerWrap > .bd-scroller { + padding: 0 20px 0 0; + overflow-y: scroll; + } .bd-spinnerContainer { display: flex; @@ -19,8 +20,9 @@ .bd-onlinePhHeader { display: flex; - padding: 0 20px 0 10px; + padding: 0 20px 0 0; min-height: 80px; + margin-top: 20px; .bd-flexRow { min-height: 40px; @@ -81,16 +83,10 @@ } .bd-onlinePhBody { - margin-top: 10px; - .bd-spinnerContainer { min-height: 40px; padding: 0; } - - .bd-scroller { - padding: 0 20px 0 0; - } } h3 { diff --git a/client/src/styles/partials/bdsettings/index.scss b/client/src/styles/partials/bdsettings/index.scss index 0fbda29f..2973b4f1 100644 --- a/client/src/styles/partials/bdsettings/index.scss +++ b/client/src/styles/partials/bdsettings/index.scss @@ -8,6 +8,5 @@ @import './updater'; @import './window-preferences'; @import './kvp'; -@import './collection'; @import './e2ee'; @import './devview'; diff --git a/client/src/styles/partials/bdsettings/remotecard.scss b/client/src/styles/partials/bdsettings/remotecard.scss index 7c62e121..0ef0a596 100644 --- a/client/src/styles/partials/bdsettings/remotecard.scss +++ b/client/src/styles/partials/bdsettings/remotecard.scss @@ -1,7 +1,7 @@ .bd-remoteCard { flex-direction: column; margin-top: 10px; - padding: 10px; + padding: 10px 0; border-radius: 0; border-bottom: 1px solid rgba(114, 118, 126, .3); diff --git a/client/src/styles/partials/discordoverrides.scss b/client/src/styles/partials/discordoverrides.scss index 591dc9c3..854022bf 100644 --- a/client/src/styles/partials/discordoverrides.scss +++ b/client/src/styles/partials/discordoverrides.scss @@ -1,13 +1,17 @@ // sass-lint:disable-all body:not(.bd-hideButton) { - [class*='layer-'] > * > [class*='wrapper-'] { + [class*='layers-'] > [class*='layer-'] > * > [class*='wrapper-'] { padding-top: 49px !important; } - .platform-osx [class*='layer-'] > * > [class*='wrapper-'] { + .platform-osx [class*='layers-'] > [class*='layer-'] > * > [class*='wrapper-'] { 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; } diff --git a/client/src/styles/partials/generic/scrollable.scss b/client/src/styles/partials/generic/scrollable.scss index 8eceb202..be1b85fd 100644 --- a/client/src/styles/partials/generic/scrollable.scss +++ b/client/src/styles/partials/generic/scrollable.scss @@ -1,6 +1,8 @@ .bd-scrollerWrap { display: flex; width: 100%; + height: 100%; + flex-grow: 1; .bd-scroller { @include scrollbar; diff --git a/client/src/styles/partials/sidebarview/settingswrap.scss b/client/src/styles/partials/sidebarview/settingswrap.scss index e11cdcff..f45aef86 100644 --- a/client/src/styles/partials/sidebarview/settingswrap.scss +++ b/client/src/styles/partials/sidebarview/settingswrap.scss @@ -17,18 +17,13 @@ flex-direction: column; flex-grow: 1; - > .bd-scrollerWrap { - flex-grow: 1; - - > .bd-scroller { - overflow-y: scroll; - } + > .bd-scrollerWrap > .bd-scroller { + overflow-y: scroll; } - .bd-settings & { - .platform-darwin & { // sass-lint:disable-line class-name-format - padding-top: 22px; - } + .platform-darwin .bd-settings &.bd-settingswrapNoscroller, // 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; } .bd-settingswrapHeader { diff --git a/client/src/ui/classnormaliser.js b/client/src/ui/classnormaliser.js index 6764e084..02fab968 100644 --- a/client/src/ui/classnormaliser.js +++ b/client/src/ui/classnormaliser.js @@ -12,12 +12,14 @@ import { Module, Reflection } from 'modules'; const normalizedPrefix = 'da'; 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 { init() { this.patchClassModules(Reflection.module.getModule(this.moduleFilter.bind(this), false)); this.normalizeElement(document.querySelector('#app-mount')); + this.patchDOMMethods(); } 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) { if (!isNaN(value)) return true; if (value.endsWith('px') || value.endsWith('ch') || value.endsWith('em') || value.endsWith('ms')) return true; diff --git a/client/src/ui/components/bd/ConnectivityView.vue b/client/src/ui/components/bd/ConnectivityView.vue index 48552517..c683fafe 100644 --- a/client/src/ui/components/bd/ConnectivityView.vue +++ b/client/src/ui/components/bd/ConnectivityView.vue @@ -62,7 +62,7 @@ SettingsWrapper }, methods: { - showConnectWindow() { + showConnectWindow() { if (this.connecting) return; this.connecting = true; const x = (window.screenX + window.outerWidth / 2) - 520 / 2; diff --git a/client/src/ui/components/bd/PluginCard.vue b/client/src/ui/components/bd/PluginCard.vue index 3a485936..a1f60f86 100644 --- a/client/src/ui/components/bd/PluginCard.vue +++ b/client/src/ui/components/bd/PluginCard.vue @@ -15,7 +15,7 @@ - + @@ -33,14 +33,19 @@ export default { data() { return { - devmode: Settings.getSetting('core', 'advanced', 'developer-mode').value - } + devmodeSetting: Settings.getSetting('core', 'advanced', 'developer-mode') + }; }, props: ['plugin'], components: { Card, Button, ButtonGroup, SettingSwitch, MiSettings, MiRefresh, MiPencil, MiDelete, MiExtension, MiBoxDownload }, + computed: { + devmode() { + return this.devmodeSetting.value; + } + }, methods: { async package() { try { diff --git a/client/src/ui/components/bd/PluginsView.vue b/client/src/ui/components/bd/PluginsView.vue index 92758bc8..467fa248 100644 --- a/client/src/ui/components/bd/PluginsView.vue +++ b/client/src/ui/components/bd/PluginsView.vue @@ -34,9 +34,11 @@
{{searchHint}}
-
- -
+
+
+ +
+
@@ -49,11 +51,13 @@
- - -
-
-
+ +
@@ -94,7 +98,8 @@ }, loadingOnline: false, loadingMore: false, - searchHint: '' + searchHint: '', + searchTimeout: null }; }, components: { @@ -107,8 +112,9 @@ showLocal() { this.local = true; }, - showOnline() { + async showOnline() { this.local = false; + if (!this.onlinePlugins.pagination.total) await this.refreshOnline(); }, async refreshLocal() { await this.PluginManager.refreshPlugins(); @@ -156,11 +162,6 @@ dont_clone }); }, - searchInput(e) { - if (this.loadingOnline || this.loadingMore) return; - this.onlinePlugins.filters.sterm = e.target.value; - this.refreshOnline(); - }, async scrollend(e) { if (this.onlinePlugins.pagination.page >= this.onlinePlugins.pagination.pages) return; if (this.loadingOnline || this.loadingMore) return; @@ -193,6 +194,10 @@ } this.refreshOnline(); }, + async search() { + clearTimeout(this.searchTimeout); + this.searchTimeout = setTimeout(this.refreshOnline, 1000); + }, async searchByTag(tag) { if (this.loadingOnline || this.loadingMore) return; this.onlinePlugins.filters.sterm = tag; diff --git a/client/src/ui/components/bd/RemoteCard.vue b/client/src/ui/components/bd/RemoteCard.vue index bd000e21..4fc73e52 100644 --- a/client/src/ui/components/bd/RemoteCard.vue +++ b/client/src/ui/components/bd/RemoteCard.vue @@ -27,7 +27,7 @@
-
{{tag}}
, +
{{tag}}
,
@@ -44,7 +44,7 @@ import { shell } from 'electron'; export default { - props: ['item', 'tagClicked'], + props: ['item'], data() { return {} }, diff --git a/client/src/ui/components/bd/SettingsWrapper.vue b/client/src/ui/components/bd/SettingsWrapper.vue index 3728a942..36c34e9e 100644 --- a/client/src/ui/components/bd/SettingsWrapper.vue +++ b/client/src/ui/components/bd/SettingsWrapper.vue @@ -9,8 +9,8 @@ */