diff --git a/.gitignore b/.gitignore index b7f2ac85a..0a886e032 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ user.config.json /.vs /npm-debug.log /tests/ext/extensions +/tests/userdata diff --git a/client/src/index.js b/client/src/index.js index c64134a0b..de27de87d 100644 --- a/client/src/index.js +++ b/client/src/index.js @@ -10,7 +10,7 @@ import { DOM, BdUI, BdMenu, Modals, Toasts, Notifications, BdContextMenu, DiscordContextMenu } from 'ui'; import BdCss from './styles/index.scss'; -import { Events, CssEditor, Globals, Settings, Database, Updater, ModuleManager, PluginManager, ThemeManager, ExtModuleManager, Vendor, Patcher, MonkeyPatch, ReactComponents, ReactHelpers, ReactAutoPatcher, DiscordApi, BdWebApi, Connectivity, Cache, Reflection, PackageInstaller } from 'modules'; +import { Events, Globals, Settings, Database, Updater, ModuleManager, PluginManager, ThemeManager, ExtModuleManager, Vendor, Patcher, MonkeyPatch, ReactComponents, ReactHelpers, ReactAutoPatcher, DiscordApi, BdWebApi, Connectivity, Cache, Reflection, PackageInstaller } from 'modules'; import { ClientLogger as Logger, ClientIPC, Utils } from 'common'; import { BuiltinManager, EmoteModule, ReactDevtoolsModule, VueDevtoolsModule, TrackingProtection, E2EE } from 'builtin'; import electron from 'electron'; @@ -18,7 +18,7 @@ import path from 'path'; import { setTimeout } from 'timers'; const tests = typeof PRODUCTION === 'undefined'; -const ignoreExternal = false; +const ignoreExternal = true; class BetterDiscord { @@ -30,7 +30,7 @@ class BetterDiscord { this._bd = { DOM, BdUI, BdMenu, Modals, Reflection, Toasts, Notifications, BdContextMenu, DiscordContextMenu, - Events, CssEditor, Globals, Settings, Database, Updater, + Events, Globals, Settings, Database, Updater, ModuleManager, PluginManager, ThemeManager, ExtModuleManager, PackageInstaller, Vendor, diff --git a/client/src/modules/csseditor.js b/client/src/modules/csseditor.js index 6534532b0..873a49436 100644 --- a/client/src/modules/csseditor.js +++ b/client/src/modules/csseditor.js @@ -38,6 +38,14 @@ export default new class { ClientIPC.on('bd-get-scss', () => this.scss, true); ClientIPC.on('bd-update-scss', (e, scss) => this.updateScss(scss)); ClientIPC.on('bd-save-csseditor-bounds', (e, bounds) => this.saveEditorBounds(bounds)); + ClientIPC.on('bd-editor-runScript', (e, script) => { + try { + new Function(script)(); + e.reply('ok'); + } catch (err) { + e.reply({ err: err.stack || err }); + } + }); ClientIPC.on('bd-save-scss', async (e, scss) => { await this.updateScss(scss); diff --git a/client/src/modules/editor.js b/client/src/modules/editor.js new file mode 100644 index 000000000..34f9fdf31 --- /dev/null +++ b/client/src/modules/editor.js @@ -0,0 +1,66 @@ +/** + * BetterDiscord Editor Module + * Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks + * All rights reserved. + * https://betterdiscord.net + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. +*/ + +import Module from './imodule'; +import { DOM } from 'ui'; + +export default new class extends Module { + + get name() { return 'Editor' } + get delay() { return false; } + + setInitialState(state) { + return { + editorBounds: undefined + }; + } + + initialize() { + super.initialize(); + (async () => { + try { + // TODO this is temporary + const userScss = await this.send('readDataFile', 'user.scss'); + const compiled = await this.send('compileSass', { data: userScss }); + this.injectStyle('customcss', compiled.css.toString()); + } catch (err) { + console.warn('SCSS Compilation error', err); + } + })(); + } + + events(ipc) { + ipc.on('editor-runScript', (e, script) => { + try { + new Function(script)(); + e.reply('ok'); + } catch (err) { + e.reply({ err: err.stack || err }); + } + }); + + ipc.on('editor-injectStyle', (e, { id, style }) => { + this.injectStyle(id, style); + e.reply('ok'); + }); + } + + injectStyle(id, style) { + return DOM.injectStyle(style, `userstyle-${id}`); + } + + /** + * Show editor, flashes if already visible. + */ + async show() { + await this.send('editor-open', this.state.editorBounds); + } + +} diff --git a/client/src/modules/imodule.js b/client/src/modules/imodule.js new file mode 100644 index 000000000..b16b9ee85 --- /dev/null +++ b/client/src/modules/imodule.js @@ -0,0 +1,73 @@ +/** + * BetterDiscord Module Base + * Copyright (c) 2015-present JsSucks - https://github.com/JsSucks + * All rights reserved. + * https://github.com/JsSucks - https://betterdiscord.net + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. +*/ + +/** + * Base Module that every non-static module should extend + */ + +import { ClientLogger as Logger, ClientIPC } from 'common'; + +export default class Module { + + constructor(args) { + this.__ = { + state: args || {}, + args + }; + this.setState = this.setState.bind(this); + + if (this.delay) { // If delay is set then module is set to load delayed from modulemanager + this.initialize = this.initialize.bind(this); + this.init = this.initialize; + } else { + this.initialize(); + this.init = () => { }; + } + } + + initialize() { + if (this.bindings) this.bindings(); + if (this.setInitialState) this.setState(this.setInitialState(this.state)); + if (this.events) this.events(ClientIPC); + } + + setState(newState) { + const oldState = this.state; + Object.assign(this.state, newState); + if (this.stateChanged) this.stateChanged(oldState, newState); + } + + set args(t) { } + get args() { return this.__.args; } + + set state(state) { return this.__.state = state; } + get state() { return this.__.state; } + + async send(channel, message) { + return ClientIPC.send(channel, message); + } + + log(msg) { + Logger.log(this.name, msg); + } + + warn(msg) { + Logger.log(this.name, msg); + } + + err(msg) { + Logger.log(this.name, msg); + } + + info(msg) { + Logger.log(this.name, msg); + } + +} diff --git a/client/src/modules/modulemanager.js b/client/src/modules/modulemanager.js index 962b7e934..13b948712 100644 --- a/client/src/modules/modulemanager.js +++ b/client/src/modules/modulemanager.js @@ -9,7 +9,7 @@ */ import { ClientLogger as Logger } from 'common'; -import { SocketProxy, EventHook, CssEditor } from 'modules'; +import { SocketProxy, EventHook } from 'modules'; import { ProfileBadges, ClassNormaliser } from 'ui'; import Updater from './updater'; @@ -27,7 +27,6 @@ export default class { new ClassNormaliser(), new SocketProxy(), new EventHook(), - CssEditor, Updater ]); } diff --git a/client/src/modules/modules.js b/client/src/modules/modules.js index 239e89f8b..437f54fc1 100644 --- a/client/src/modules/modules.js +++ b/client/src/modules/modules.js @@ -1,5 +1,6 @@ export { default as Events } from './events'; export { default as CssEditor } from './csseditor'; +export { default as Editor } from './editor'; export { default as Globals } from './globals'; export { default as Settings } from './settings'; export { default as Database } from './database'; diff --git a/client/src/styles/partials/bdsettings/button.scss b/client/src/styles/partials/bdsettings/button.scss index 14b72dde8..87a882b6e 100644 --- a/client/src/styles/partials/bdsettings/button.scss +++ b/client/src/styles/partials/bdsettings/button.scss @@ -1,122 +1,125 @@ .bd-settingsButton { - position: absolute; - z-index: 1; - top: 22px; - width: 70px; - height: 48px; - left: 0; - box-shadow: 0 1px 0 rgba(0, 0, 0, .2), 0 2px 0 rgba(0, 0, 0, .06); - opacity: 1; - - .platform-darwin & { // sass-lint:disable-line class-name-format - top: 27px; - } - - .platform-linux & { // sass-lint:disable-line class-name-format - top: 0; - } - - .bd-settingsButtonBtn { - backface-visibility: hidden; - -webkit-backface-visibility: hidden; - width: 70px; + position: absolute; + z-index: 1; + top: 22px; + width: 70px; height: 48px; - cursor: pointer; - filter: grayscale(100%); - opacity: .5; - position: relative; - transition: all .3s cubic-bezier(.4,0,0,1); - - &::before, - &::after { - content:""; - display: block; - position: absolute; - top:0; - left:0; - background-repeat: no-repeat; - background-position: center; - } - - &::before { - width: 70px; - height: 48px; - background-image: $logoSmallBw; - background-size: 50% 50%; - opacity: 1; - transition:all .3s cubic-bezier(.4,0,0,1), opacity .01s; - } - &::after { - width: 130px; - height: 43px; - background-image: $logoBigBw; - background-size: 100% 100%; - transform:translate(-7px,2px)scale(0.5); - opacity: 0; - transition:all .3s cubic-bezier(.4,0,0,1); - } - - &:not(.bd-loading) { - &:hover { - filter: none; - opacity: 1; - } - } - - &.bd-loading { - animation: bd-settingsButtonPulse 1.5s infinite; - } - - &.bd-updates { - filter: hue-rotate(250deg) !important; // sass-lint:disable-line no-important - opacity: 1 !important; // sass-lint:disable-line no-important - } - } - - &.bd-hideButton { - animation: bd-fadeOut .4s ease-out; - - &.bd-active { - animation: bd-fadeIn .4s ease-in; - } - - &:not(.bd-active) { - &:not(.bd-animating) { - display: none; - } - } - } - - &.bd-active { - opacity: 1; - } - - &.bd-active, - &.bd-hideButton { - background: transparent; - box-shadow: none; - - .bd-settingsButtonBtn { - filter: none; - opacity: 1; - width: 130px; - height: 43px; - transform:translate(25px,18px); - cursor: default; - &::before{ - opacity:0; - transform:translate(-16px,-3px)scale(1.9); - transition:all .3s cubic-bezier(.4,0,0,1), opacity .1s .3s; - } - &::after{ - opacity: 1; - transform:scale(1); - } - } - } - - &.bd-active, - &.bd-animating { - z-index: 3001; - } + left: 0; + box-shadow: 0 1px 0 rgba(0, 0, 0, .2), 0 2px 0 rgba(0, 0, 0, .06); + opacity: 1; + + .platform-darwin & { // sass-lint:disable-line class-name-format + top: 27px; + } + + .platform-linux & { // sass-lint:disable-line class-name-format + top: 0; + } + + .bd-settingsButtonBtn { + backface-visibility: hidden; + -webkit-backface-visibility: hidden; + width: 70px; + height: 48px; + cursor: pointer; + filter: grayscale(100%); + opacity: .5; + position: relative; + transition: all .3s cubic-bezier(.4, 0, 0, 1); + + &::before, + &::after { + content: ''; + display: block; + position: absolute; + top: 0; + left: 0; + background-repeat: no-repeat; + background-position: center; + } + + &::before { + width: 70px; + height: 48px; + background-image: $logoSmallBw; + background-size: 50% 50%; + opacity: 1; + transition: all .3s cubic-bezier(.4, 0, 0, 1), opacity .01s; + } + + &::after { + width: 130px; + height: 43px; + background-image: $logoBigBw; + background-size: 100% 100%; + transform: translate(-7px, 2px) scale(.5); + opacity: 0; + transition: all .3s cubic-bezier(.4, 0, 0, 1); + } + + &:not(.bd-loading) { + &:hover { + filter: none; + opacity: 1; + } + } + + &.bd-loading { + animation: bd-settingsButtonPulse 1.5s infinite; + } + + &.bd-updates { + filter: hue-rotate(250deg) !important; // sass-lint:disable-line no-important + opacity: 1 !important; // sass-lint:disable-line no-important + } + } + + &.bd-hideButton { + animation: bd-fadeOut .4s ease-out; + + &.bd-active { + animation: bd-fadeIn .4s ease-in; + } + + &:not(.bd-active) { + &:not(.bd-animating) { + display: none; + } + } + } + + &.bd-active { + opacity: 1; + } + + &.bd-active, + &.bd-hideButton { + background: transparent; + box-shadow: none; + + .bd-settingsButtonBtn { + filter: none; + opacity: 1; + width: 130px; + height: 43px; + transform: translate(25px, 18px); + cursor: default; + + &::before { + opacity: 0; + transform: translate(-16px, -3px) scale(1.9); + transition: all .3s cubic-bezier(.4, 0, 0, 1), opacity .1s .3s; + } + + &::after { + opacity: 1; + transform: scale(1); + } + } + } + + &.bd-active, + &.bd-animating { + z-index: 3001; + } } diff --git a/client/src/styles/partials/bdsettings/sidebarview.scss b/client/src/styles/partials/bdsettings/sidebarview.scss index a585e31ac..1781afd3b 100644 --- a/client/src/styles/partials/bdsettings/sidebarview.scss +++ b/client/src/styles/partials/bdsettings/sidebarview.scss @@ -1,180 +1,180 @@ .bd-settings { - position: absolute; - backface-visibility: hidden; - -webkit-backface-visibility: hidden; - top: 22px; - left: 0; - bottom: 0; - z-index: 1001; - width: 310px; - transform: translateX(-310px); - opacity: 0; - transition: transform .3s cubic-bezier(.4,0,0,1), opacity .25s ease; - pointer-events: none; - - &.bd-active { - width: 900px; - transform: none; - opacity: 1; - } - - .bd-settingsX { - position: absolute; - top: 18px; - left: 255px; - border: 2px solid #6e6e6e; - border-radius: 50%; - width: 25px; - height: 25px; - justify-content: center; - display: flex; - align-items: center; - cursor: pointer; - - .platform-darwin & { // sass-lint:disable-line class-name-format - top: 43px; - } - - .bd-xText { - color: #72767d; - position: absolute; - top: 32px; - font-weight: 600; - font-size: 13px; - } - - .bd-materialDesignIcon { - justify-content: center; - display: flex; - fill: #72767d; - } - - &:hover { - background-color: hsla(218, 5%, 47%, .3); - - .bd-materialDesignIcon { - fill: #fff; - } - } - } - - .bd-info { - display: flex; - align-items: flex-end; - overflow: hidden; - padding: 0 25px; - margin: 10px 0; - - .bd-vtext { - color: #414245; - font-weight: 700; - font-size: 12px; - flex-grow: 1; - height: 20px; - cursor: default; - user-select: none; - } - - .bd-materialButton { - cursor: pointer; - - &:hover { - .bd-materialDesignIcon { - fill: #fff; - } - } - } - - .bd-materialDesignIcon { - fill: #414245; - - &:hover { - fill: #fff; - } - } - } - - .bd-sidebarView { - &::after { - content: ''; - height: 100%; - width: 310px; - background-color: #202225; - top: 100%; - display: block; - position: absolute; - } - - .bd-sidebarRegion { - .bd-scroller { - padding-top: 0; - } - } - - .bd-contentRegion { - width: 590px; - } - - &.bd-active { - .bd-contentRegion { - transition: all .3s cubic-bezier(.4,0,0,1); - transform: none; - opacity: 1; - } - } - - &.bd-stop { - .bd-sidebarRegion { - z-index: 1003; - } - - .bd-contentRegion { - z-index: 1002; - } - } - } - - .platform-darwin & { // sass-lint:disable-line class-name-format - top: 0; - - .bd-sidebarView { - .bd-sidebarRegion { - padding-top: 22px; - } - } - } - - .platform-linux & { // sass-lint:disable-line class-name-format - top: 0; - } - - &:not(.bd-active) > .bd-sidebarView.bd-active, // sass-lint:disable-line force-element-nesting - &.bd-settingsOut .bd-sidebarView.bd-active { // sass-lint:disable-line force-element-nesting - .bd-contentRegion { - transform: translate(-600px, 0%); - opacity: 0; - width: 590px; - } - } - - &:not(.bd-active) { - .bd-sidebarView { - &.bd-active { - .bd-contentRegion { - transform: translate(-600px, 100%); - } - } - } - } + position: absolute; + backface-visibility: hidden; + -webkit-backface-visibility: hidden; + top: 22px; + left: 0; + bottom: 0; + z-index: 1001; + width: 310px; + transform: translateX(-310px); + opacity: 0; + transition: transform .3s cubic-bezier(.4, 0, 0, 1), opacity .25s ease; + pointer-events: none; + + &.bd-active { + width: 900px; + transform: none; + opacity: 1; + } + + .bd-settingsX { + position: absolute; + top: 18px; + left: 255px; + border: 2px solid #6e6e6e; + border-radius: 50%; + width: 25px; + height: 25px; + justify-content: center; + display: flex; + align-items: center; + cursor: pointer; + + .platform-darwin & { // sass-lint:disable-line class-name-format + top: 43px; + } + + .bd-xText { + color: #72767d; + position: absolute; + top: 32px; + font-weight: 600; + font-size: 13px; + } + + .bd-materialDesignIcon { + justify-content: center; + display: flex; + fill: #72767d; + } + + &:hover { + background-color: hsla(218, 5%, 47%, .3); + + .bd-materialDesignIcon { + fill: #fff; + } + } + } + + .bd-info { + display: flex; + align-items: flex-end; + overflow: hidden; + padding: 0 25px; + margin: 10px 0; + + .bd-vtext { + color: #414245; + font-weight: 700; + font-size: 12px; + flex-grow: 1; + height: 20px; + cursor: default; + user-select: none; + } + + .bd-materialButton { + cursor: pointer; + + &:hover { + .bd-materialDesignIcon { + fill: #fff; + } + } + } + + .bd-materialDesignIcon { + fill: #414245; + + &:hover { + fill: #fff; + } + } + } + + .bd-sidebarView { + &::after { + content: ''; + height: 100%; + width: 310px; + background-color: #202225; + top: 100%; + display: block; + position: absolute; + } + + .bd-sidebarRegion { + .bd-scroller { + padding-top: 0; + } + } + + .bd-contentRegion { + width: 590px; + } + + &.bd-active { + .bd-contentRegion { + transition: all .3s cubic-bezier(.4, 0, 0, 1); + transform: none; + opacity: 1; + } + } + + &.bd-stop { + .bd-sidebarRegion { + z-index: 1003; + } + + .bd-contentRegion { + z-index: 1002; + } + } + } + + .platform-darwin & { // sass-lint:disable-line class-name-format + top: 0; + + .bd-sidebarView { + .bd-sidebarRegion { + padding-top: 22px; + } + } + } + + .platform-linux & { // sass-lint:disable-line class-name-format + top: 0; + } + + &:not(.bd-active) > .bd-sidebarView.bd-active, // sass-lint:disable-line force-element-nesting + &.bd-settingsOut .bd-sidebarView.bd-active { // sass-lint:disable-line force-element-nesting + .bd-contentRegion { + transform: translate(-600px, 0%); + opacity: 0; + width: 590px; + } + } + + &:not(.bd-active) { + .bd-sidebarView { + &.bd-active { + .bd-contentRegion { + transform: translate(-600px, 100%); + } + } + } + } } .bd-sidebar { - .bd-settingsButton { - position: absolute; - top: 0; - - .platform-darwin & { // sass-lint:disable-line class-name-format - top: 22px; - } - } + .bd-settingsButton { + position: absolute; + top: 0; + + .platform-darwin & { // sass-lint:disable-line class-name-format + top: 22px; + } + } } diff --git a/client/src/styles/partials/generic/drawers.scss b/client/src/styles/partials/generic/drawers.scss index cc2be433a..158b0618d 100644 --- a/client/src/styles/partials/generic/drawers.scss +++ b/client/src/styles/partials/generic/drawers.scss @@ -28,21 +28,23 @@ .bd-chevron1 { position: absolute; - svg{ + + svg { transform: scale(1) translateY(0); } } - .bd-chevron2{ + .bd-chevron2 { position: absolute; - svg{ + + svg { visibility: hidden; - transform: scale(1.4,.5) translateY(6px) rotate(180deg); + transform: scale(1.4, .5) translateY(6px) rotate(180deg); } } svg { - transition: transform .2s cubic-bezier(.2,0,0,1); + transition: transform .2s cubic-bezier(.2, 0, 0, 1); } } @@ -79,7 +81,7 @@ .bd-drawerOpenButton { .bd-chevron1 { svg { - transform: scale(1.4,.5) translateY(-6px); + transform: scale(1.4, .5) translateY(-6px); visibility: hidden; } } diff --git a/client/src/styles/partials/generic/forms/switches.scss b/client/src/styles/partials/generic/forms/switches.scss index c9dba0340..bde31a755 100644 --- a/client/src/styles/partials/generic/forms/switches.scss +++ b/client/src/styles/partials/generic/forms/switches.scss @@ -47,7 +47,7 @@ bottom: 3px; background: #f6f6f7; border-radius: 10px; - transition: all .15s cubic-bezier(.2,0,0,1); + transition: all .15s cubic-bezier(.2, 0, 0, 1); box-shadow: 0 3px 1px 0 rgba(0, 0, 0, .05), 0 2px 2px 0 rgba(0, 0, 0, .1), 0 3px 3px 0 rgba(0, 0, 0, .05); } diff --git a/client/src/styles/partials/sidebarview/sidebar.scss b/client/src/styles/partials/sidebarview/sidebar.scss index b05f00182..8055d3b2b 100644 --- a/client/src/styles/partials/sidebarview/sidebar.scss +++ b/client/src/styles/partials/sidebarview/sidebar.scss @@ -1,50 +1,51 @@ .bd-sidebar { - width: 100%; - padding-right: 20px; - padding: 0; + width: 100%; + padding-right: 20px; + padding: 0; - .bd-header { - padding: 6px 0; - margin-left: 10px; - margin-top: 15px; - color: rgba(255, 255, 255, .15); - font-size: 14px; - line-height: 16px; - text-transform: uppercase; - font-weight: 600; - flex-shrink: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-family: Whitney, Helvetica Neue, Helvetica, Arial, sans-serif; - } + .bd-header { + padding: 6px 0; + margin-left: 10px; + margin-top: 15px; + color: rgba(255, 255, 255, .15); + font-size: 14px; + line-height: 16px; + text-transform: uppercase; + font-weight: 600; + flex-shrink: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-family: Whitney, Helvetica Neue, Helvetica, Arial, sans-serif; + } - .bd-item { - border-radius: 3px; - margin-bottom: 2px; - padding-bottom: 6px; - padding-top: 6px; - padding: 6px 10px; - color: $coldimwhite; - cursor: pointer; - font-size: 17px; - line-height: 20px; - position: relative; - flex-shrink: 0; - font-weight: 500; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-family: Whitney, Helvetica Neue, Helvetica, Arial, sans-serif; + .bd-item { + border-radius: 3px; + margin-bottom: 2px; + padding-bottom: 6px; + padding-top: 6px; + padding: 6px 10px; + color: $coldimwhite; + cursor: pointer; + font-size: 17px; + line-height: 20px; + position: relative; + flex-shrink: 0; + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-family: Whitney, Helvetica Neue, Helvetica, Arial, sans-serif; - &:hover { - background-color: rgba(185,185,185,.1); - color: #f6f6f6; - } - &.bd-active { - background: $colbdgreen; - color: #fff; - cursor: default; - } - } + &:hover { + background-color: rgba(185, 185, 185, .1); + color: #f6f6f6; + } + + &.bd-active { + background: $colbdgreen; + color: #fff; + cursor: default; + } + } } diff --git a/client/src/ui/components/bd/CssEditor.vue b/client/src/ui/components/bd/CssEditor.vue index 18a39f577..a56396417 100644 --- a/client/src/ui/components/bd/CssEditor.vue +++ b/client/src/ui/components/bd/CssEditor.vue @@ -8,6 +8,8 @@ * LICENSE file in the root directory of this source tree. */ +// TODO this should be remade as editor instead of css editor +