Merge pull request #326 from rauenzi/modularize
Modularize the codebase and maybe add a feature or two
This commit is contained in:
commit
c80ceed509
28
.eslintrc
28
.eslintrc
|
@ -1,5 +1,8 @@
|
|||
{
|
||||
"extends": "eslint:recommended",
|
||||
"extends": ["eslint:recommended", "plugin:react/recommended"],
|
||||
"plugins": [
|
||||
"react"
|
||||
],
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
|
@ -7,7 +10,10 @@
|
|||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 8,
|
||||
"sourceType": "module"
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"semi": 2,
|
||||
|
@ -22,7 +28,14 @@
|
|||
"yoda": 2,
|
||||
"linebreak-style": ["error", "windows"],
|
||||
"quote-props": ["error", "consistent-as-needed", {"keywords": true}],
|
||||
"object-curly-spacing": ["error", "never", { "objectsInObjects": false }]
|
||||
"object-curly-spacing": ["error", "never", { "objectsInObjects": false }],
|
||||
"no-var": "error",
|
||||
"prefer-const": "error",
|
||||
"react/jsx-uses-react": "error",
|
||||
"react/jsx-uses-vars": "error",
|
||||
"react/prop-types": "off",
|
||||
"react/jsx-no-target-blank": "error",
|
||||
"react/jsx-key": "off"
|
||||
},
|
||||
"globals": {
|
||||
"webpackJsonp": false,
|
||||
|
@ -30,16 +43,11 @@
|
|||
"Set": false,
|
||||
"WeakMap": false,
|
||||
"Promise": false,
|
||||
"bdplugins": false,
|
||||
"bdthemes": false,
|
||||
"betterDiscordIPC": false,
|
||||
"bdVersion": false,
|
||||
"version": false,
|
||||
"_bdhash": true,
|
||||
"ace": false,
|
||||
"Reflect": false,
|
||||
"DiscordNative": false,
|
||||
"self": "off",
|
||||
"name": "off"
|
||||
"name": "off",
|
||||
"__non_webpack_require__": false
|
||||
}
|
||||
}
|
|
@ -1,4 +1 @@
|
|||
node_modules
|
||||
src
|
||||
webpack.config.js
|
||||
current.js
|
||||
node_modules
|
329
css/main.css
329
css/main.css
|
@ -1,3 +1,10 @@
|
|||
:root {
|
||||
--bd-blue: #3E82E5;
|
||||
--bd-blue-hover: rgb(56,117,206);
|
||||
--bd-blue-active: rgb(50,104,183);
|
||||
--blurple: #7289DA;
|
||||
}
|
||||
|
||||
/* BEGIN V2 LOADER */
|
||||
/* =============== */
|
||||
|
||||
|
@ -25,6 +32,42 @@
|
|||
/* END V2 LOADER */
|
||||
|
||||
|
||||
.bd-button {
|
||||
background-color: var(--bd-blue);
|
||||
color: white;
|
||||
border-radius: 3px;
|
||||
padding: 2px 6px;
|
||||
}
|
||||
|
||||
.bd-button:hover {
|
||||
background-color: var(--bd-blue-hover);
|
||||
}
|
||||
|
||||
.bd-button:active {
|
||||
background-color: var(--bd-blue-active);
|
||||
}
|
||||
|
||||
.bd-button.bd-button-success {
|
||||
background-color: #3ac15c;
|
||||
}
|
||||
|
||||
.bd-button.bd-button-success:hover {
|
||||
background-color: rgb(52,174,83);
|
||||
}
|
||||
|
||||
.bd-button.bd-button-success:active {
|
||||
background-color: rgb(46,154,74);
|
||||
}
|
||||
|
||||
.bd-button + .bd-button {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.bd-button-outline {
|
||||
background-color: transparent;
|
||||
border: 2px solid white;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -326,7 +369,7 @@
|
|||
|
||||
#bda-qem button.active,
|
||||
#bda-qem button.active:hover {
|
||||
background-color: #7289da;
|
||||
background-color: var(--bd-blue);
|
||||
}
|
||||
|
||||
.theme-light #bda-qem button.active,
|
||||
|
@ -401,7 +444,7 @@
|
|||
}
|
||||
|
||||
.ui-card.ui-card-primary.bd-server-card:first-child:after {
|
||||
border: 3px solid #7289da;
|
||||
border: 3px solid var(--bd-blue);
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
|
@ -505,7 +548,7 @@
|
|||
|
||||
#pubslayer .ui-tab-bar-item.selected {
|
||||
color: #fff;
|
||||
background-color: #7289da;
|
||||
background-color: var(--bd-blue);
|
||||
}
|
||||
|
||||
#pubslayer .ui-tab-bar-header {
|
||||
|
@ -541,7 +584,7 @@
|
|||
}
|
||||
|
||||
#pubslayer button {
|
||||
background: #7289da;
|
||||
background: var(--bd-blue);
|
||||
color: #FFF;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
|
@ -619,7 +662,7 @@ color: #f6f6f7;
|
|||
margin: auto;
|
||||
margin-left: calc(50% - 100px);
|
||||
margin-top: 20px;
|
||||
background: #7289da;
|
||||
background: var(--bd-blue);
|
||||
color: #FFF;
|
||||
font-weight: 600;
|
||||
border-radius: 5px;
|
||||
|
@ -851,7 +894,7 @@ body .ace_closeButton:active {
|
|||
}
|
||||
|
||||
.bd-pfbtn {
|
||||
background: #7289da;
|
||||
background: var(--bd-blue);
|
||||
color: #FFF;
|
||||
border-radius: 5px;
|
||||
margin-left: 10px;
|
||||
|
@ -881,7 +924,7 @@ body .ace_closeButton:active {
|
|||
color: #f6f6f7;
|
||||
}
|
||||
.theme-dark #bd-settings-sidebar .ui-tab-bar-item.selected {
|
||||
background-color: #7289da;
|
||||
background-color: var(--bd-blue);
|
||||
color: #fff;
|
||||
}
|
||||
.theme-light #bd-settings-sidebar .ui-tab-bar-item {
|
||||
|
@ -892,7 +935,7 @@ body .ace_closeButton:active {
|
|||
color: #4f545c;
|
||||
}
|
||||
.theme-light #bd-settings-sidebar .ui-tab-bar-item.selected {
|
||||
background-color: #7289da;
|
||||
background-color: var(--bd-blue);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
@ -999,7 +1042,7 @@ body .ace_closeButton:active {
|
|||
}
|
||||
|
||||
#bd-settingspane-container .ui-switch-item .ui-switch-wrapper .ui-switch {
|
||||
background: #7289da;
|
||||
background: var(--bd-blue);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
@ -1026,7 +1069,7 @@ body .ace_closeButton:active {
|
|||
}
|
||||
|
||||
#bd-settingspane-container .ui-switch-item .ui-switch-wrapper .ui-switch.checked {
|
||||
background: #7289da;
|
||||
background: var(--bd-blue);
|
||||
}
|
||||
|
||||
#bd-settingspane-container .ui-switch-item .ui-switch-wrapper .ui-switch.checked:before {
|
||||
|
@ -1074,11 +1117,11 @@ body .ace_closeButton:active {
|
|||
display: flex;
|
||||
}
|
||||
|
||||
.bda-slist {
|
||||
.bd-addon-list {
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.bda-slist li {
|
||||
.bd-addon-list .bd-addon-card {
|
||||
max-height: 175px;
|
||||
margin-bottom: 20px;
|
||||
padding: 5px 8px;
|
||||
|
@ -1086,23 +1129,23 @@ body .ace_closeButton:active {
|
|||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.theme-dark .bda-slist li {
|
||||
.theme-dark .bd-addon-list .bd-addon-card {
|
||||
background-color: rgba(32,34,37,.6);
|
||||
color: #f6f6f7;
|
||||
border-color: #202225;
|
||||
}
|
||||
.theme-light .bda-slist li {
|
||||
.theme-light .bd-addon-list .bd-addon-card {
|
||||
background-color: #f8f9f9;
|
||||
color: #4f545c;
|
||||
border-color: #dcddde;
|
||||
}
|
||||
|
||||
.bda-slist li.settings-open {
|
||||
.bd-addon-list .bd-addon-card.settings-open {
|
||||
max-height: 800px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.bda-slist .bda-header {
|
||||
.bd-addon-list .bda-header {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
|
@ -1112,36 +1155,36 @@ body .ace_closeButton:active {
|
|||
border-bottom: 1px solid transparent;
|
||||
overflow: hidden;
|
||||
}
|
||||
.theme-dark .bda-slist .bda-header {
|
||||
.theme-dark .bd-addon-list .bda-header {
|
||||
color: #f6f6f7;
|
||||
border-bottom-color: rgba(114,118,125,.3);
|
||||
}
|
||||
.theme-light .bda-slist .bda-header {
|
||||
.theme-light .bd-addon-list .bda-header {
|
||||
color: #4f545c;
|
||||
border-bottom-color: rgba(185,187,190,.3);
|
||||
}
|
||||
|
||||
.bda-slist .bda-description {
|
||||
.bd-addon-list .bda-description {
|
||||
word-break: break-word;
|
||||
max-height: 100px;
|
||||
margin: 5px 0;
|
||||
padding: 5px 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.theme-dark .bda-slist .bda-description {
|
||||
.theme-dark .bd-addon-list .bda-description {
|
||||
color: #b9bbbe;
|
||||
}
|
||||
.theme-light .bda-slist .bda-description {
|
||||
.theme-light .bd-addon-list .bda-description {
|
||||
color: #72767d;
|
||||
}
|
||||
|
||||
.bda-slist .scroller::-webkit-scrollbar-track-piece,
|
||||
.bda-slist .scroller::-webkit-scrollbar-thumb {
|
||||
.bd-addon-list .scroller::-webkit-scrollbar-track-piece,
|
||||
.bd-addon-list .scroller::-webkit-scrollbar-thumb {
|
||||
border-radius:0 !important;
|
||||
border-color:transparent;
|
||||
}
|
||||
|
||||
.bda-slist .bda-footer {
|
||||
.bd-addon-list .bd-card-footer {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
|
@ -1151,15 +1194,15 @@ body .ace_closeButton:active {
|
|||
border-top: 1px solid transparent;
|
||||
overflow: hidden;
|
||||
}
|
||||
.theme-dark .bda-slist .bda-footer {
|
||||
.theme-dark .bd-addon-list .bd-card-footer {
|
||||
border-top-color: rgba(114,118,125,.3);
|
||||
}
|
||||
.theme-light .bda-slist .bda-footer {
|
||||
.theme-light .bd-addon-list .bd-card-footer {
|
||||
border-top-color: rgba(185,187,190,.3);
|
||||
}
|
||||
|
||||
.bda-slist .bda-footer button {
|
||||
background: #7289da;
|
||||
.bd-addon-list .bd-card-footer button {
|
||||
background: var(--bd-blue);
|
||||
color: #FFF;
|
||||
border-radius: 5px;
|
||||
display: block;
|
||||
|
@ -1168,15 +1211,27 @@ body .ace_closeButton:active {
|
|||
transition: opacity 250ms ease;
|
||||
}
|
||||
|
||||
.bda-slist .bda-footer button:disabled {
|
||||
.theme-dark .bd-addon-list .bd-card-footer {
|
||||
border-top-color: rgba(114,118,125,.3);
|
||||
}
|
||||
.theme-light .bd-addon-list .bd-card-footer {
|
||||
border-top-color: rgba(185,187,190,.3);
|
||||
}
|
||||
|
||||
.bd-addon-list .bd-card-footer button {
|
||||
padding: 3px 16px;
|
||||
transition: opacity 250ms ease;
|
||||
}
|
||||
|
||||
.bd-addon-list .bd-card-footer button:disabled {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.bda-slist .bda-footer a {
|
||||
color: #7289da;
|
||||
.bd-addon-list a {
|
||||
color: var(--bd-blue);
|
||||
}
|
||||
|
||||
.bda-slist .bda-footer a:hover {
|
||||
.bd-addon-list a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
/* ======================= */
|
||||
|
@ -1307,7 +1362,7 @@ body .ace_closeButton:active {
|
|||
}
|
||||
|
||||
.bd-modal-wrapper .footer button {
|
||||
background-color: #7289da;
|
||||
background-color: var(--bd-blue);
|
||||
color: #fff;
|
||||
min-height: 32px;
|
||||
min-width: 60px;
|
||||
|
@ -1834,3 +1889,211 @@ body .ace_closeButton:active {
|
|||
|
||||
/* =============== */
|
||||
/* END DARK MODE */
|
||||
|
||||
|
||||
|
||||
.bd-switch {
|
||||
background-color: #72767d;
|
||||
border-radius: 14px;
|
||||
width: 42px;
|
||||
height: 24px;
|
||||
opacity: 1;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
display: block;
|
||||
flex: 0 0 auto;
|
||||
transition: background .15s ease-in-out,box-shadow .15s ease-in-out,border .15s ease-in-out,opacity .15s ease-in-out;
|
||||
margin-left: 10px;
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.15);
|
||||
}
|
||||
|
||||
.bd-switch::before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 0;
|
||||
opacity: 0;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
.bd-switch::after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
background-color: #fff;
|
||||
z-index: 1;
|
||||
width: 18px;
|
||||
margin: 3px;
|
||||
border-radius: 9px;
|
||||
height: 18px;
|
||||
left: 0;
|
||||
transition: transform .15s ease-in-out,width .1s ease-in-out,left .1s ease-in-out;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,.3);
|
||||
}
|
||||
|
||||
.bd-switch .bd-checkbox {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
z-index: 2;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.bd-switch-checked {
|
||||
background-color: var(--bd-blue);
|
||||
}
|
||||
|
||||
.bd-switch-checked::after {
|
||||
transform: translate3d(18px,0,0);
|
||||
}
|
||||
|
||||
.bd-switch-disabled {
|
||||
opacity: .3;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.bd-addon-button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.bd-addon-button + .bd-addon-button {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
|
||||
.bd-addon-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.bd-addon-controls .bd-search {
|
||||
font-size: 13px;
|
||||
margin: 0;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.bd-addon-dropdowns {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bd-select-wrapper + .bd-select-wrapper {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.bd-select-wrapper {
|
||||
color: #f6f6f7;
|
||||
font-size: 13px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bd-select-wrapper label {
|
||||
opacity: .3;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.bd-select {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
color: #f6f6f7;
|
||||
font-size: 13px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||
border-radius: 3px;
|
||||
padding: 5px 5px 5px 0;
|
||||
}
|
||||
|
||||
.bd-select.bd-select-transparent {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.bd-select-value {
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.bd-select-arrow {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.bd-select .bd-select-options {
|
||||
position: absolute;
|
||||
background: #2F3136;
|
||||
border-radius: 0 0 3px 3px;
|
||||
max-height: 300px;
|
||||
min-width: calc(100% + 2px);
|
||||
overflow-y: auto;
|
||||
box-shadow: rgba(0, 0, 0, 0.3) 0px 1px 5px 0px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||
border-top: 0;
|
||||
margin-top: -1px;
|
||||
margin-left: -1px;
|
||||
z-index: 3;
|
||||
top: 100%;
|
||||
}
|
||||
|
||||
.bd-select-transparent .bd-select-options {
|
||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||
margin-top: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.bd-select .bd-select-option {
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.bd-select .bd-select-option:hover,
|
||||
.bd-select .bd-select-option.selected {
|
||||
background: #26272B;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.bd-search-wrapper {
|
||||
padding: 3px;
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
border: 0;
|
||||
background-color: #202225;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bd-search {
|
||||
padding: 2px 3px;
|
||||
background: none;
|
||||
border: 0;
|
||||
color: #fff;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.bd-search-wrapper > svg {
|
||||
margin-right: 2px;
|
||||
}
|
File diff suppressed because one or more lines are too long
5951
js/main.js
5951
js/main.js
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
|
@ -4,11 +4,13 @@
|
|||
"description": "Enhances Discord adding functionality and themes.",
|
||||
"main": "js/main.js",
|
||||
"scripts": {
|
||||
"build": "webpack --progress --colors",
|
||||
"watch": "webpack --progress --colors --watch",
|
||||
"build-prod": "webpack --progress --colors --mode production -o js/main.min.js --devtool none",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"minify": "gulp minify-js && gulp minify-css",
|
||||
"minify-js": "gulp minify-js",
|
||||
"minify-css": "gulp minify-css",
|
||||
"watch": "gulp watch",
|
||||
"watch-js": "gulp watch-js",
|
||||
"watch-css": "gulp watch-css"
|
||||
},
|
||||
|
@ -23,9 +25,17 @@
|
|||
},
|
||||
"homepage": "https://github.com/rauenzi/BetterDiscordApp#readme",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.3.4",
|
||||
"@babel/preset-env": "^7.3.4",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@babel/register": "^7.0.0",
|
||||
"babel-loader": "^8.0.6",
|
||||
"circular-dependency-plugin": "^5.0.2",
|
||||
"gulp": "^4.0.0",
|
||||
"gulp-babel-minify": "^0.5.0",
|
||||
"gulp-csso": "^3.0.1",
|
||||
"gulp-rename": "^1.4.0"
|
||||
"gulp-rename": "^1.4.0",
|
||||
"gulp-babel-minify": "^0.5.1",
|
||||
"webpack": "^4.29.6",
|
||||
"webpack-cli": "^3.2.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
// var settingsPanel, emoteModule, quickEmoteMenu, voiceMode, pluginModule, themeModule, dMode, publicServersModule, mainCore, BDV2;
|
||||
export const minSupportedVersion = "0.3.0";
|
||||
export const bbdVersion = "0.3.0";
|
||||
export const bbdChangelog = {
|
||||
description: "There's some pretty big things in this one, and even bigger things coming.",
|
||||
changes: [
|
||||
{
|
||||
title: "What's New?",
|
||||
items: [
|
||||
"**RepoControls** is now integrated into BD and everyone can enjoy sorting and searching their plugins and themes.",
|
||||
"BandagedBD has undergone an internal restructure--it's no longer one messy file but *several*. Users shouldn't see any breaking changes (hopefully).",
|
||||
"**Plugin Developers:** You'll begin (or continue for some) to see deprecation notices for several BD globals. Please take these seriously and update your plugins to use `BdApi`, the globals **_will_** be removed. If you find the API to be missing something that you could do before, please message me in BD2's <#603225817262194699> chat."
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Minor Stuff",
|
||||
type: "improved",
|
||||
items: [
|
||||
"**BD Blue** is now the default accent color for BBD elements in settings. Themes can customize it easily through some new variables: `--bd-blue`, `--bd-blue-hover`, `--bd-blue-active`",
|
||||
"Some improvements have been made to the plugins and themes pages that should hopefully prevent plugins from causing errors when displaying."
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const settings = {
|
||||
"Custom css live update": {id: "bda-css-0", info: "", implemented: true, hidden: true, cat: "core"},
|
||||
"Custom css auto udpate": {id: "bda-css-1", info: "", implemented: true, hidden: true, cat: "core"},
|
||||
"BetterDiscord Blue": {id: "bda-gs-b", info: "Replace Discord blue with BD Blue", implemented: false, hidden: false, cat: "core"},
|
||||
|
||||
/* Core */
|
||||
/* ====== */
|
||||
"Public Servers": {id: "bda-gs-1", info: "Display public servers button", implemented: true, hidden: false, cat: "core", category: "modules"},
|
||||
"Minimal Mode": {id: "bda-gs-2", info: "Hide elements and reduce the size of elements.", implemented: true, hidden: false, cat: "core", category: "modules"},
|
||||
"Voice Mode": {id: "bda-gs-4", info: "Only show voice chat", implemented: true, hidden: false, cat: "core", category: "modules"},
|
||||
"Hide Channels": {id: "bda-gs-3", info: "Hide channels in minimal mode", implemented: true, hidden: false, cat: "core", category: "modules"},
|
||||
"Dark Mode": {id: "bda-gs-5", info: "Make certain elements dark by default(wip)", implemented: true, hidden: false, cat: "core", category: "modules"},
|
||||
"Voice Disconnect": {id: "bda-dc-0", info: "Disconnect from voice server when closing Discord", implemented: true, hidden: false, cat: "core", category: "modules"},
|
||||
"24 Hour Timestamps": {id: "bda-gs-6", info: "Replace 12hr timestamps with proper ones", implemented: true, hidden: false, cat: "core", category: "modules"},
|
||||
"Colored Text": {id: "bda-gs-7", info: "Make text color the same as role color", implemented: true, hidden: false, cat: "core", category: "modules"},
|
||||
"Normalize Classes": {id: "fork-ps-4", info: "Adds stable classes to elements to help themes. (e.g. adds .da-channels to .channels-Ie2l6A)", implemented: true, hidden: false, cat: "core", category: "modules"},
|
||||
|
||||
/* Content */
|
||||
"Content Error Modal": {id: "fork-ps-1", info: "Shows a modal with plugin/theme errors", implemented: true, hidden: false, cat: "core", category: "content manager"},
|
||||
"Show Toasts": {id: "fork-ps-2", info: "Shows a small notification for important information", implemented: true, hidden: false, cat: "core", category: "content manager"},
|
||||
"Scroll To Settings": {id: "fork-ps-3", info: "Auto-scrolls to a plugin's settings when the button is clicked (only if out of view)", implemented: true, hidden: false, cat: "core", category: "content manager"},
|
||||
"Automatic Loading": {id: "fork-ps-5", info: "Automatically loads, reloads, and unloads plugins and themes", implemented: true, hidden: false, cat: "core", category: "content manager"},
|
||||
|
||||
/* Developer */
|
||||
"Developer Mode": {id: "bda-gs-8", info: "Developer Mode", implemented: true, hidden: false, cat: "core", category: "developer settings"},
|
||||
"Copy Selector": {id: "fork-dm-1", info: "Adds a \"Copy Selector\" option to context menus when developer mode is active", implemented: true, hidden: false, cat: "core", category: "developer settings"},
|
||||
|
||||
/* Window Prefs */
|
||||
"Enable Transparency": {id: "fork-wp-1", info: "Enables the main window to be see-through (requires restart)", implemented: true, hidden: false, cat: "core", category: "window preferences"},
|
||||
"Window Frame": {id: "fork-wp-2", info: "Adds the native os window frame to the main window", implemented: false, hidden: true, cat: "core", category: "window preferences"},
|
||||
|
||||
|
||||
/* Emotes */
|
||||
/* ====== */
|
||||
"Download Emotes": {id: "fork-es-3", info: "Download emotes when the cache is expired", implemented: true, hidden: false, cat: "emote"},
|
||||
"Twitch Emotes": {id: "bda-es-7", info: "Show Twitch emotes", implemented: true, hidden: false, cat: "emote"},
|
||||
"FrankerFaceZ Emotes": {id: "bda-es-1", info: "Show FrankerFaceZ Emotes", implemented: true, hidden: false, cat: "emote"},
|
||||
"BetterTTV Emotes": {id: "bda-es-2", info: "Show BetterTTV Emotes", implemented: true, hidden: false, cat: "emote"},
|
||||
"Emote Menu": {id: "bda-es-0", info: "Show Twitch/Favourite emotes in emote menu", implemented: true, hidden: false, cat: "emote"},
|
||||
"Emoji Menu": {id: "bda-es-9", info: "Show Discord emoji menu", implemented: true, hidden: false, cat: "emote"},
|
||||
"Emote Auto Capitalization": {id: "bda-es-4", info: "Autocapitalize emote commands", implemented: true, hidden: false, cat: "emote"},
|
||||
"Show Names": {id: "bda-es-6", info: "Show emote names on hover", implemented: true, hidden: false, cat: "emote"},
|
||||
"Show emote modifiers": {id: "bda-es-8", info: "Enable emote mods (flip, spin, pulse, spin2, spin3, 1spin, 2spin, 3spin, tr, bl, br, shake, shake2, shake3, flap)", implemented: true, hidden: false, cat: "emote"},
|
||||
"Animate On Hover": {id: "fork-es-2", info: "Only animate the emote modifiers on hover", implemented: true, hidden: false, cat: "emote"}
|
||||
};
|
||||
|
||||
export const defaultCookie = {
|
||||
"bda-gs-1": true,
|
||||
"bda-gs-2": false,
|
||||
"bda-gs-3": false,
|
||||
"bda-gs-4": false,
|
||||
"bda-gs-5": true,
|
||||
"bda-gs-6": false,
|
||||
"bda-gs-7": false,
|
||||
"bda-gs-8": false,
|
||||
"bda-es-0": true,
|
||||
"bda-es-1": true,
|
||||
"bda-es-2": true,
|
||||
"bda-es-4": false,
|
||||
"bda-es-6": true,
|
||||
"bda-es-7": true,
|
||||
"bda-gs-b": false,
|
||||
"bda-es-8": true,
|
||||
"bda-dc-0": false,
|
||||
"bda-css-0": false,
|
||||
"bda-css-1": false,
|
||||
"bda-es-9": true,
|
||||
"fork-dm-1": false,
|
||||
"fork-ps-1": true,
|
||||
"fork-ps-2": true,
|
||||
"fork-ps-3": true,
|
||||
"fork-ps-4": true,
|
||||
"fork-ps-5": true,
|
||||
"fork-es-2": false,
|
||||
"fork-es-3": true,
|
||||
"fork-wp-1": false,
|
||||
"fork-wp-2": false,
|
||||
"fork-beta": true
|
||||
};
|
||||
|
||||
|
||||
export const settingsCookie = {};
|
||||
|
||||
export const bdpluginErrors = [];
|
||||
export const bdthemeErrors = []; // define for backwards compatibility
|
||||
|
||||
export const bdConfig = {};
|
||||
|
||||
export const bemotes = [];
|
||||
export const emotesFfz = {};
|
||||
export const emotesBTTV = {};
|
||||
export const emotesBTTV2 = {};
|
||||
export const emotesTwitch = {};
|
||||
export const subEmotesTwitch = {};
|
||||
|
||||
export const bdEmotes = {
|
||||
TwitchGlobal: {},
|
||||
TwitchSubscriber: {},
|
||||
BTTV: {},
|
||||
FrankerFaceZ: {},
|
||||
BTTV2: {}
|
||||
};
|
||||
|
||||
export const bdEmoteSettingIDs = {
|
||||
TwitchGlobal: "bda-es-7",
|
||||
TwitchSubscriber: "bda-es-7",
|
||||
BTTV: "bda-es-2",
|
||||
FrankerFaceZ: "bda-es-1",
|
||||
BTTV2: "bda-es-2"
|
||||
};
|
||||
|
||||
export const bdthemes = {};
|
||||
export const bdplugins = {};
|
||||
|
||||
export const pluginCookie = {};
|
||||
export const themeCookie = {};
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
import localStorageFix from "./localStorageFix";
|
||||
import loadingIcon from "./loadingIcon";
|
||||
localStorageFix();
|
||||
loadingIcon();
|
||||
|
||||
const deprecateGlobal = (key, value) => {
|
||||
Object.defineProperty(window, key, {
|
||||
get() {
|
||||
Utils.warn("Deprecation Notice", `"${key}" may be removed in future versions. Please only use BdApi.`);
|
||||
return value;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
import * as Globals from "./0globals";
|
||||
|
||||
const globalKeys = Object.keys(Globals);
|
||||
for (const key of globalKeys) deprecateGlobal(key, Globals[key]);
|
||||
|
||||
|
||||
import BdApi from "./modules/bdApi";
|
||||
import BDV2 from "./modules/v2";
|
||||
import pluginModule from "./modules/pluginModule";
|
||||
import themeModule from "./modules/themeModule";
|
||||
import Utils from "./modules/utils";
|
||||
import BDEvents from "./modules/bdEvents";
|
||||
import settingsPanel from "./modules/settingsPanel";
|
||||
import DataStore from "./modules/dataStore";
|
||||
import emoteModule from "./modules/emoteModule";
|
||||
import ContentManager from "./modules/contentManager";
|
||||
import ClassNormalizer from "./modules/classNormalizer";
|
||||
|
||||
deprecateGlobal("BDV2", BDV2);
|
||||
deprecateGlobal("pluginModule", pluginModule);
|
||||
deprecateGlobal("themeModule", themeModule);
|
||||
deprecateGlobal("Utils", Utils);
|
||||
deprecateGlobal("BDEvents", BDEvents);
|
||||
deprecateGlobal("settingsPanel", settingsPanel);
|
||||
deprecateGlobal("DataStore", DataStore);
|
||||
deprecateGlobal("emoteModule", emoteModule);
|
||||
deprecateGlobal("ContentManager", ContentManager);
|
||||
deprecateGlobal("ClassNormalizer", ClassNormalizer);
|
||||
|
||||
window.BdApi = BdApi;
|
||||
|
||||
import Core from "./modules/core";
|
||||
export default class CoreWrapper {
|
||||
constructor(bdConfig) {
|
||||
this.mainCore = new Core(bdConfig);
|
||||
}
|
||||
|
||||
init() {
|
||||
// deprecateGlobal("mainCore", this.mainCore);
|
||||
this.mainCore.init();
|
||||
}
|
||||
}
|
||||
|
||||
// function patchModuleLoad() {
|
||||
// const namespace = "betterdiscord";
|
||||
// const prefix = `${namespace}/`;
|
||||
// const Module = require("module");
|
||||
// const load = Module._load;
|
||||
// // const resolveFilename = Module._resolveFilename;
|
||||
|
||||
// Module._load = function(request) {
|
||||
// if (request === namespace || request.startsWith(prefix)) {
|
||||
// const requested = request.substr(prefix.length);
|
||||
// if (requested == "api") return BdApi;
|
||||
// }
|
||||
|
||||
// return load.apply(this, arguments);
|
||||
// };
|
||||
|
||||
// // Module._resolveFilename = function (request, parent, isMain) {
|
||||
// // if (request === "betterdiscord" || request.startsWith("betterdiscord/")) {
|
||||
// // const contentPath = PluginManager.getPluginPathByModule(parent);
|
||||
// // if (contentPath) return request;
|
||||
// // }
|
||||
|
||||
// // return resolveFilename.apply(this, arguments);
|
||||
// // };
|
||||
|
||||
// return function() {
|
||||
// Module._load = load;
|
||||
// };
|
||||
// }
|
||||
|
||||
// patchModuleLoad();
|
||||
|
||||
// var settingsPanel, emoteModule, quickEmoteMenu, voiceMode,, dMode, publicServersModule;
|
||||
// var bdConfig = null;
|
|
@ -0,0 +1,6 @@
|
|||
export default () => {
|
||||
const v2Loader = document.createElement("div");
|
||||
v2Loader.className = "bd-loaderv2";
|
||||
v2Loader.title = "BandagedBD is loading...";
|
||||
document.body.appendChild(v2Loader);
|
||||
};
|
|
@ -0,0 +1,27 @@
|
|||
export default function() {
|
||||
const contentWindowGetter = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, "contentWindow").get;
|
||||
Object.defineProperty(HTMLIFrameElement.prototype, "contentWindow", {
|
||||
get: function () {
|
||||
const contentWindow = Reflect.apply(contentWindowGetter, this, arguments);
|
||||
return new Proxy(contentWindow, {
|
||||
get: function (obj, prop) {
|
||||
if (prop === "localStorage") return null;
|
||||
const val = obj[prop];
|
||||
if (typeof val === "function") return val.bind(obj);
|
||||
return val;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Prevent interception by patching Reflect.apply and Function.prototype.bind
|
||||
Object.defineProperty(Reflect, "apply", {value: Reflect.apply, writable: false, configurable: false});
|
||||
Object.defineProperty(Function.prototype, "bind", {value: Function.prototype.bind, writable: false, configurable: false});
|
||||
|
||||
const oOpen = XMLHttpRequest.prototype.open;
|
||||
XMLHttpRequest.prototype.open = function() {
|
||||
const url = arguments[1];
|
||||
if (url.toLowerCase().includes("api/webhooks")) return null;
|
||||
return Reflect.apply(oOpen, this, arguments);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import {settingsCookie} from "../0globals";
|
||||
import BDV2 from "./v2";
|
||||
import Utils from "./utils";
|
||||
|
||||
export default new class TFHour {
|
||||
inject24Hour() {
|
||||
if (this.cancel24Hour) return;
|
||||
|
||||
const twelveHour = new RegExp(`([0-9]{1,2}):([0-9]{1,2})\\s(AM|PM)`);
|
||||
const convert = (data) => {
|
||||
if (!settingsCookie["bda-gs-6"]) return;
|
||||
const matched = data.returnValue.match(twelveHour);
|
||||
if (!matched || matched.length !== 4) return;
|
||||
if (matched[3] === "AM") return data.returnValue = data.returnValue.replace(matched[0], `${matched[1] === "12" ? "00" : matched[1].padStart(2, "0")}:${matched[2]}`);
|
||||
return data.returnValue = data.returnValue.replace(matched[0], `${matched[1] === "12" ? "12" : parseInt(matched[1]) + 12}:${matched[2]}`);
|
||||
};
|
||||
|
||||
const cancelCozy = Utils.monkeyPatch(BDV2.TimeFormatter, "calendarFormat", {after: convert}); // Called in Cozy mode
|
||||
const cancelCompact = Utils.monkeyPatch(BDV2.TimeFormatter, "dateFormat", {after: convert}); // Called in Compact mode
|
||||
this.cancel24Hour = () => {cancelCozy(); cancelCompact();}; // Cancel both
|
||||
}
|
||||
|
||||
remove24Hour() {
|
||||
if (this.cancel24Hour) this.cancel24Hour();
|
||||
}
|
||||
};
|
|
@ -0,0 +1,297 @@
|
|||
import {pluginCookie, themeCookie, bdplugins, bdthemes, settingsCookie, settings, bdEmotes} from "../0globals";
|
||||
import mainCore from "./core";
|
||||
import Utils from "./utils";
|
||||
import BDV2 from "./v2";
|
||||
import DataStore from "./dataStore";
|
||||
import pluginModule from "./pluginModule";
|
||||
import themeModule from "./themeModule";
|
||||
import settingsPanel from "./settingsPanel";
|
||||
|
||||
const BdApi = {
|
||||
get React() { return BDV2.React; },
|
||||
get ReactDOM() { return BDV2.ReactDom; },
|
||||
get ReactComponent() {return BDV2.ReactComponent;},
|
||||
get WindowConfigFile() {
|
||||
if (this._windowConfigFile) return this._windowConfigFile;
|
||||
const electron = require("electron").remote.app;
|
||||
const path = require("path");
|
||||
const base = electron.getAppPath();
|
||||
const roamingBase = electron.getPath("userData");
|
||||
const roamingLocation = path.resolve(roamingBase, electron.getVersion(), "modules", "discord_desktop_core", "injector", "config.json");
|
||||
const location = path.resolve(base, "..", "app", "config.json");
|
||||
const fs = require("fs");
|
||||
const realLocation = fs.existsSync(location) ? location : fs.existsSync(roamingLocation) ? roamingLocation : null;
|
||||
if (!realLocation) return this._windowConfigFile = null;
|
||||
return this._windowConfigFile = realLocation;
|
||||
},
|
||||
get settings() {return settings;},
|
||||
get emotes() {return bdEmotes;},
|
||||
get screenWidth() { return Math.max(document.documentElement.clientWidth, window.innerWidth || 0); },
|
||||
get screenHeight() { return Math.max(document.documentElement.clientHeight, window.innerHeight || 0); }
|
||||
};
|
||||
|
||||
BdApi.getAllWindowPreferences = function() {
|
||||
if (!this.WindowConfigFile) return {}; // Tempfix until new injection on other platforms
|
||||
return __non_webpack_require__(this.WindowConfigFile);
|
||||
};
|
||||
|
||||
BdApi.getWindowPreference = function(key) {
|
||||
if (!this.WindowConfigFile) return undefined; // Tempfix until new injection on other platforms
|
||||
return this.getAllWindowPreferences()[key];
|
||||
};
|
||||
|
||||
BdApi.setWindowPreference = function(key, value) {
|
||||
if (!this.WindowConfigFile) return; // Tempfix until new injection on other platforms
|
||||
const fs = require("fs");
|
||||
const prefs = this.getAllWindowPreferences();
|
||||
prefs[key] = value;
|
||||
delete __non_webpack_require__.cache[this.WindowConfigFile];
|
||||
fs.writeFileSync(this.WindowConfigFile, JSON.stringify(prefs, null, 4));
|
||||
};
|
||||
|
||||
//Inject CSS to document head
|
||||
//id = id of element
|
||||
//css = custom css
|
||||
BdApi.injectCSS = function (id, css) {
|
||||
$("head").append($("<style>", {id: Utils.escapeID(id), text: css}));
|
||||
};
|
||||
|
||||
//Clear css/remove any element
|
||||
//id = id of element
|
||||
BdApi.clearCSS = function (id) {
|
||||
$("#" + Utils.escapeID(id)).remove();
|
||||
};
|
||||
|
||||
//Inject CSS to document head
|
||||
//id = id of element
|
||||
//css = custom css
|
||||
BdApi.linkJS = function (id, url) {
|
||||
$("head").append($("<script>", {id: Utils.escapeID(id), src: url, type: "text/javascript"}));
|
||||
};
|
||||
|
||||
//Clear css/remove any element
|
||||
//id = id of element
|
||||
BdApi.unlinkJS = function (id) {
|
||||
$("#" + Utils.escapeID(id)).remove();
|
||||
};
|
||||
|
||||
//Get another plugin
|
||||
//name = name of plugin
|
||||
BdApi.getPlugin = function (name) {
|
||||
if (bdplugins.hasOwnProperty(name)) {
|
||||
return bdplugins[name].plugin;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
//Get BetterDiscord Core
|
||||
BdApi.getCore = function () {
|
||||
return mainCore;
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows a generic but very customizable modal.
|
||||
* @param {string} title - title of the modal
|
||||
* @param {string} content - a string of text to display in the modal
|
||||
*/
|
||||
BdApi.alert = function (title, content) {
|
||||
const ModalStack = BdApi.findModuleByProps("push", "update", "pop", "popWithKey");
|
||||
const AlertModal = BdApi.findModuleByPrototypes("handleCancel", "handleSubmit", "handleMinorConfirm");
|
||||
if (!ModalStack || !AlertModal) return mainCore.alert(title, content);
|
||||
|
||||
ModalStack.push(function(props) {
|
||||
return BdApi.React.createElement(AlertModal, Object.assign({
|
||||
title: title,
|
||||
body: content,
|
||||
}, props));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows a generic but very customizable confirmation modal with optional confirm and cancel callbacks.
|
||||
* @param {string} title - title of the modal
|
||||
* @param {(string|ReactElement|Array<string|ReactElement>)} children - a single or mixed array of react elements and strings. Everything is wrapped in Discord's `TextElement` component so strings will show and render properly.
|
||||
* @param {object} [options] - options to modify the modal
|
||||
* @param {boolean} [options.danger=false] - whether the main button should be red or not
|
||||
* @param {string} [options.confirmText=Okay] - text for the confirmation/submit button
|
||||
* @param {string} [options.cancelText=Cancel] - text for the cancel button
|
||||
* @param {callable} [options.onConfirm=NOOP] - callback to occur when clicking the submit button
|
||||
* @param {callable} [options.onCancel=NOOP] - callback to occur when clicking the cancel button
|
||||
*/
|
||||
BdApi.showConfirmationModal = function (title, content, options = {}) {
|
||||
const ModalStack = BdApi.findModuleByProps("push", "update", "pop", "popWithKey");
|
||||
const TextElement = BdApi.findModuleByProps("Sizes", "Weights");
|
||||
const ConfirmationModal = BdApi.findModule(m => m.defaultProps && m.key && m.key() == "confirm-modal");
|
||||
if (!ModalStack || !ConfirmationModal || !TextElement) return mainCore.alert(title, content);
|
||||
|
||||
const {onConfirm, onCancel, confirmText, cancelText, danger = false} = options;
|
||||
if (typeof(content) == "string") content = TextElement({color: TextElement.Colors.PRIMARY, children: [content]});
|
||||
else if (Array.isArray(content)) content = TextElement({color: TextElement.Colors.PRIMARY, children: content});
|
||||
content = [content];
|
||||
|
||||
const emptyFunction = () => {};
|
||||
ModalStack.push(function(props) {
|
||||
return BdApi.React.createElement(ConfirmationModal, Object.assign({
|
||||
header: title,
|
||||
children: content,
|
||||
red: danger,
|
||||
confirmText: confirmText ? confirmText : "Okay",
|
||||
cancelText: cancelText ? cancelText : "Cancel",
|
||||
onConfirm: onConfirm ? onConfirm : emptyFunction,
|
||||
onCancel: onCancel ? onCancel : emptyFunction
|
||||
}, props));
|
||||
});
|
||||
};
|
||||
|
||||
//Show toast alert
|
||||
BdApi.showToast = function(content, options = {}) {
|
||||
Utils.showToast(content, options);
|
||||
};
|
||||
|
||||
// Finds module
|
||||
BdApi.findModule = function(filter) {
|
||||
return BDV2.WebpackModules.find(filter);
|
||||
};
|
||||
|
||||
// Finds module
|
||||
BdApi.findAllModules = function(filter) {
|
||||
return BDV2.WebpackModules.findAll(filter);
|
||||
};
|
||||
|
||||
// Finds module
|
||||
BdApi.findModuleByProps = function(...props) {
|
||||
return BDV2.WebpackModules.findByUniqueProperties(props);
|
||||
};
|
||||
|
||||
BdApi.findModuleByPrototypes = function(...protos) {
|
||||
return BDV2.WebpackModules.findByPrototypes(protos);
|
||||
};
|
||||
|
||||
BdApi.findModuleByDisplayName = function(name) {
|
||||
return BDV2.WebpackModules.findByDisplayName(name);
|
||||
};
|
||||
|
||||
// Gets react instance
|
||||
BdApi.getInternalInstance = function(node) {
|
||||
if (!(node instanceof window.jQuery) && !(node instanceof Element)) return undefined;
|
||||
if (node instanceof jQuery) node = node[0];
|
||||
return BDV2.getInternalInstance(node);
|
||||
};
|
||||
|
||||
// Gets data
|
||||
BdApi.loadData = function(pluginName, key) {
|
||||
return DataStore.getPluginData(pluginName, key);
|
||||
};
|
||||
|
||||
BdApi.getData = BdApi.loadData;
|
||||
|
||||
// Sets data
|
||||
BdApi.saveData = function(pluginName, key, data) {
|
||||
return DataStore.setPluginData(pluginName, key, data);
|
||||
};
|
||||
|
||||
BdApi.setData = BdApi.saveData;
|
||||
|
||||
// Deletes data
|
||||
BdApi.deleteData = function(pluginName, key) {
|
||||
return DataStore.deletePluginData(pluginName, key);
|
||||
};
|
||||
|
||||
// Patches other functions
|
||||
BdApi.monkeyPatch = function(what, methodName, options) {
|
||||
return Utils.monkeyPatch(what, methodName, options);
|
||||
};
|
||||
|
||||
// Event when element is removed
|
||||
BdApi.onRemoved = function(node, callback) {
|
||||
return Utils.onRemoved(node, callback);
|
||||
};
|
||||
|
||||
// Wraps function in try..catch
|
||||
BdApi.suppressErrors = function(method, message) {
|
||||
return Utils.suppressErrors(method, message);
|
||||
};
|
||||
|
||||
// Tests for valid JSON
|
||||
BdApi.testJSON = function(data) {
|
||||
return Utils.testJSON(data);
|
||||
};
|
||||
|
||||
BdApi.isPluginEnabled = function(name) {
|
||||
return !!pluginCookie[name];
|
||||
};
|
||||
|
||||
BdApi.isThemeEnabled = function(name) {
|
||||
return !!themeCookie[name];
|
||||
};
|
||||
|
||||
BdApi.isSettingEnabled = function(id) {
|
||||
return !!settingsCookie[id];
|
||||
};
|
||||
|
||||
BdApi.enableSetting = function(id) {
|
||||
return settingsPanel.onChange(id, true);
|
||||
};
|
||||
|
||||
BdApi.disableSetting = function(id) {
|
||||
return settingsPanel.onChange(id, false);
|
||||
};
|
||||
|
||||
BdApi.toggleSetting = function(id) {
|
||||
return settingsPanel.onChange(id, !settingsCookie[id]);
|
||||
};
|
||||
|
||||
// Gets data
|
||||
BdApi.getBDData = function(key) {
|
||||
return DataStore.getBDData(key);
|
||||
};
|
||||
|
||||
// Sets data
|
||||
BdApi.setBDData = function(key, data) {
|
||||
return DataStore.setBDData(key, data);
|
||||
};
|
||||
|
||||
|
||||
|
||||
const makeAddonAPI = (cookie, list, manager) => new class AddonAPI {
|
||||
|
||||
isEnabled(name) {
|
||||
return !!cookie[name];
|
||||
}
|
||||
|
||||
enable(name) {
|
||||
return manager.enable(name);
|
||||
}
|
||||
|
||||
disable(name) {
|
||||
return manager.disable(name);
|
||||
}
|
||||
|
||||
toggle(name) {
|
||||
if (cookie[name]) this.disable(name);
|
||||
else this.enable(name);
|
||||
}
|
||||
|
||||
reload(name) {
|
||||
return manager.reload(name);
|
||||
}
|
||||
|
||||
get(name) {
|
||||
if (list.hasOwnProperty(name)) {
|
||||
if (list[name].plugin) return list[name].plugin;
|
||||
return list[name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getAll() {
|
||||
return Object.keys(list).map(k => this.get(k)).filter(a => a);
|
||||
}
|
||||
};
|
||||
|
||||
BdApi.Plugins = makeAddonAPI(pluginCookie, bdplugins, pluginModule);
|
||||
BdApi.Themes = makeAddonAPI(themeCookie, bdthemes, themeModule);
|
||||
|
||||
Object.freeze(BdApi);
|
||||
|
||||
export default BdApi;
|
|
@ -0,0 +1,6 @@
|
|||
/* BDEvents */
|
||||
const EventEmitter = require("events");
|
||||
export default new class BDEvents extends EventEmitter {
|
||||
dispatch(eventName, ...args) {this.emit(eventName, ...args);}
|
||||
off(eventName, eventAction) {this.removeListener(eventName, eventAction);}
|
||||
};
|
|
@ -0,0 +1,119 @@
|
|||
import WebpackModules from "./webpackModules";
|
||||
|
||||
const normalizedPrefix = "da";
|
||||
const randClass = new RegExp(`^(?!${normalizedPrefix}-)((?:[A-Za-z]|[0-9]|-)+)-(?:[A-Za-z]|[0-9]|-|_){6}$`);
|
||||
|
||||
export default new class ClassNormalizer {
|
||||
|
||||
stop() {
|
||||
if (!this.hasPatched) return;
|
||||
this.unpatchClassModules(WebpackModules.findAll(this.moduleFilter.bind(this)));
|
||||
this.revertElement(document.querySelector("#app-mount"));
|
||||
this.hasPatched = false;
|
||||
}
|
||||
|
||||
start() {
|
||||
if (this.hasPatched) return;
|
||||
this.patchClassModules(WebpackModules.findAll(this.moduleFilter.bind(this)));
|
||||
this.normalizeElement(document.querySelector("#app-mount"));
|
||||
this.hasPatched = true;
|
||||
this.patchDOMMethods();
|
||||
}
|
||||
|
||||
patchClassModules(modules) {
|
||||
for (const module of modules) {
|
||||
this.patchClassModule(normalizedPrefix, module);
|
||||
}
|
||||
}
|
||||
|
||||
unpatchClassModules(modules) {
|
||||
for (const module of modules) {
|
||||
this.unpatchClassModule(normalizedPrefix, module);
|
||||
}
|
||||
}
|
||||
|
||||
shouldIgnore(value) {
|
||||
if (!isNaN(value)) return true;
|
||||
if (value.endsWith("px") || value.endsWith("ch") || value.endsWith("em") || value.endsWith("ms")) return true;
|
||||
if (value.startsWith("layerContainer-")) return true;
|
||||
if (value.startsWith("#") && (value.length == 7 || value.length == 4)) return true;
|
||||
if (value.includes("calc(") || value.includes("rgba")) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
moduleFilter(module) {
|
||||
if (typeof module !== "object" || Array.isArray(module)) return false;
|
||||
if (module.__esModule) return false;
|
||||
if (!Object.keys(module).length) return false;
|
||||
for (const baseClassName in module) {
|
||||
const value = module[baseClassName];
|
||||
if (typeof value !== "string") return false;
|
||||
if (this.shouldIgnore(value)) continue;
|
||||
if (value.split("-").length === 1) return false;
|
||||
if (!randClass.test(value.split(" ")[0])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
patchClassModule(componentName, classNames) {
|
||||
for (const baseClassName in classNames) {
|
||||
const value = classNames[baseClassName];
|
||||
if (this.shouldIgnore(value)) continue;
|
||||
const classList = value.split(" ");
|
||||
for (const normalClass of classList) {
|
||||
const match = normalClass.match(randClass);
|
||||
if (!match || !match.length || match.length < 2) continue; // Shouldn't ever happen since they passed the moduleFilter, but you never know
|
||||
const camelCase = match[1].split("-").map((s, i) => i ? s[0].toUpperCase() + s.slice(1) : s).join("");
|
||||
classNames[baseClassName] += ` ${componentName}-${camelCase}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unpatchClassModule(componentName, classNames) {
|
||||
for (const baseClassName in classNames) {
|
||||
const value = classNames[baseClassName];
|
||||
if (this.shouldIgnore(value)) continue;
|
||||
let newString = "";
|
||||
const classList = value.split(" ");
|
||||
for (const normalClass of classList) {
|
||||
if (normalClass.startsWith(`${componentName}-`)) continue;
|
||||
newString += ` ${normalClass}`;
|
||||
}
|
||||
classNames[baseClassName] = newString.trim();
|
||||
}
|
||||
}
|
||||
|
||||
normalizeElement(element) {
|
||||
if (!(element instanceof Element)) return;
|
||||
const classes = element.classList;
|
||||
for (let c = 0, clen = classes.length; c < clen; c++) {
|
||||
if (!randClass.test(classes[c])) continue;
|
||||
const match = classes[c].match(randClass)[1];
|
||||
const newClass = match.split("-").map((s, i) => i ? s[0].toUpperCase() + s.slice(1) : s).join("");
|
||||
element.classList.add(`${normalizedPrefix}-${newClass}`);
|
||||
}
|
||||
for (const child of element.children) this.normalizeElement(child);
|
||||
}
|
||||
|
||||
revertElement(element) {
|
||||
if (!(element instanceof Element)) return;
|
||||
if (element.children && element.children.length) this.revertElement(element.children[0]);
|
||||
if (element.nextElementSibling) this.revertElement(element.nextElementSibling);
|
||||
const classes = element.classList;
|
||||
const toRemove = [];
|
||||
for (let c = 0; c < classes.length; c++) {
|
||||
if (classes[c].startsWith(`${normalizedPrefix}-`)) toRemove.push(classes[c]);
|
||||
}
|
||||
element.classList.remove(...toRemove);
|
||||
}
|
||||
|
||||
patchDOMMethods() {
|
||||
const contains = DOMTokenList.prototype.contains;
|
||||
DOMTokenList.prototype.contains = function(token) {
|
||||
const tokens = token.split(" ");
|
||||
return tokens.every(t => contains.call(this, t));
|
||||
};
|
||||
}
|
||||
|
||||
};
|
|
@ -0,0 +1,35 @@
|
|||
import {settingsCookie} from "../0globals";
|
||||
import BDV2 from "./v2";
|
||||
import Utils from "./utils";
|
||||
|
||||
export default new class ColoredText {
|
||||
injectColoredText() {
|
||||
if (this.cancelColoredText) return;
|
||||
if (!BDV2.MessageComponent) return;
|
||||
|
||||
this.cancelColoredText = Utils.monkeyPatch(BDV2.MessageComponent, "default", {before: (data) => {
|
||||
const props = data.methodArguments[0];
|
||||
if (!props || !props.childrenMessageContent) return;
|
||||
const messageContent = props.childrenMessageContent;
|
||||
|
||||
if (!messageContent.type || !messageContent.type.type || messageContent.type.type.displayName != "MessageContent") return;
|
||||
const originalType = messageContent.type.type;
|
||||
if (originalType.__originalMethod) return; // Don't patch again
|
||||
messageContent.type.type = function(props) {
|
||||
const returnValue = originalType(props);
|
||||
const roleColor = settingsCookie["bda-gs-7"] ? props.message.colorString || "" : "";
|
||||
returnValue.props.style = {color: roleColor};
|
||||
return returnValue;
|
||||
};
|
||||
|
||||
messageContent.type.type.__originalMethod = originalType;
|
||||
Object.assign(messageContent.type.type, originalType);
|
||||
}});
|
||||
}
|
||||
|
||||
removeColoredText() {
|
||||
document.querySelectorAll(".markup-2BOw-j").forEach(elem => {
|
||||
elem.style.setProperty("color", "");
|
||||
});
|
||||
}
|
||||
};
|
|
@ -0,0 +1,247 @@
|
|||
|
||||
import {bdConfig, bdplugins, bdthemes} from "../0globals";
|
||||
import pluginModule from "./pluginModule";
|
||||
import themeModule from "./themeModule";
|
||||
import Utils from "./utils";
|
||||
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const Module = require("module").Module;
|
||||
Module.globalPaths.push(path.resolve(require("electron").remote.app.getAppPath(), "node_modules"));
|
||||
class MetaError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = "MetaError";
|
||||
}
|
||||
}
|
||||
const originalJSRequire = Module._extensions[".js"];
|
||||
const originalCSSRequire = Module._extensions[".css"] ? Module._extensions[".css"] : () => {return null;};
|
||||
const splitRegex = /[^\S\r\n]*?(?:\r\n|\n)[^\S\r\n]*?\*[^\S\r\n]?/;
|
||||
const escapedAtRegex = /^\\@/;
|
||||
|
||||
|
||||
export default new class ContentManager {
|
||||
|
||||
constructor() {
|
||||
this.timeCache = {};
|
||||
this.watchers = {};
|
||||
Module._extensions[".js"] = this.getContentRequire("plugin");
|
||||
Module._extensions[".css"] = this.getContentRequire("theme");
|
||||
}
|
||||
|
||||
get pluginsFolder() {return this._pluginsFolder || (this._pluginsFolder = fs.realpathSync(path.resolve(bdConfig.dataPath + "plugins/")));}
|
||||
get themesFolder() {return this._themesFolder || (this._themesFolder = fs.realpathSync(path.resolve(bdConfig.dataPath + "themes/")));}
|
||||
|
||||
watchContent(contentType) {
|
||||
if (this.watchers[contentType]) return;
|
||||
const isPlugin = contentType === "plugin";
|
||||
const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder;
|
||||
const fileEnding = isPlugin ? ".plugin.js" : ".theme.css";
|
||||
this.watchers[contentType] = fs.watch(baseFolder, {persistent: false}, async (eventType, filename) => {
|
||||
if (!eventType || !filename || !filename.endsWith(fileEnding)) return;
|
||||
await new Promise(r => setTimeout(r, 50));
|
||||
try {fs.statSync(path.resolve(baseFolder, filename));}
|
||||
catch (err) {
|
||||
if (err.code !== "ENOENT") return;
|
||||
delete this.timeCache[filename];
|
||||
if (isPlugin) return pluginModule.unloadPlugin(filename);
|
||||
return themeModule.unloadTheme(filename);
|
||||
}
|
||||
if (!fs.statSync(path.resolve(baseFolder, filename)).isFile()) return;
|
||||
const stats = fs.statSync(path.resolve(baseFolder, filename));
|
||||
if (!stats || !stats.mtime || !stats.mtime.getTime()) return;
|
||||
if (typeof(stats.mtime.getTime()) !== "number") return;
|
||||
if (this.timeCache[filename] == stats.mtime.getTime()) return;
|
||||
this.timeCache[filename] = stats.mtime.getTime();
|
||||
if (eventType == "rename") {
|
||||
if (isPlugin) pluginModule.loadPlugin(filename);
|
||||
else themeModule.loadTheme(filename);
|
||||
}
|
||||
if (eventType == "change") {
|
||||
if (isPlugin) pluginModule.reloadPlugin(filename);
|
||||
else themeModule.reloadTheme(filename);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
unwatchContent(contentType) {
|
||||
if (!this.watchers[contentType]) return;
|
||||
this.watchers[contentType].close();
|
||||
delete this.watchers[contentType];
|
||||
}
|
||||
|
||||
extractMeta(content) {
|
||||
const firstLine = content.split("\n")[0];
|
||||
const hasOldMeta = firstLine.includes("//META");
|
||||
if (hasOldMeta) return this.parseOldMeta(content);
|
||||
const hasNewMeta = firstLine.includes("/**");
|
||||
if (hasNewMeta) return this.parseNewMeta(content);
|
||||
throw new MetaError("META was not found.");
|
||||
}
|
||||
|
||||
parseOldMeta(content) {
|
||||
const meta = content.split("\n")[0];
|
||||
const rawMeta = meta.substring(meta.lastIndexOf("//META") + 6, meta.lastIndexOf("*//"));
|
||||
if (meta.indexOf("META") < 0) throw new MetaError("META was not found.");
|
||||
const parsed = Utils.testJSON(rawMeta);
|
||||
if (!parsed) throw new MetaError("META could not be parsed.");
|
||||
if (!parsed.name) throw new MetaError("META missing name data.");
|
||||
parsed.format = "json";
|
||||
return parsed;
|
||||
}
|
||||
|
||||
parseNewMeta(content) {
|
||||
const block = content.split("/**", 2)[1].split("*/", 1)[0];
|
||||
const out = {};
|
||||
let field = "";
|
||||
let accum = "";
|
||||
for (const line of block.split(splitRegex)) {
|
||||
if (line.length === 0) continue;
|
||||
if (line.charAt(0) === "@" && line.charAt(1) !== " ") {
|
||||
out[field] = accum;
|
||||
const l = line.indexOf(" ");
|
||||
field = line.substr(1, l - 1);
|
||||
accum = line.substr(l + 1);
|
||||
}
|
||||
else {
|
||||
accum += " " + line.replace("\\n", "\n").replace(escapedAtRegex, "@");
|
||||
}
|
||||
}
|
||||
out[field] = accum.trim();
|
||||
delete out[""];
|
||||
out.format = "jsdoc";
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
getContentRequire(type) {
|
||||
const isPlugin = type === "plugin";
|
||||
const self = this;
|
||||
const originalRequire = isPlugin ? originalJSRequire : originalCSSRequire;
|
||||
return function(module, filename) {
|
||||
const baseFolder = isPlugin ? self.pluginsFolder : self.themesFolder;
|
||||
const possiblePath = path.resolve(baseFolder, path.basename(filename));
|
||||
if (!fs.existsSync(possiblePath) || filename !== fs.realpathSync(possiblePath)) return Reflect.apply(originalRequire, this, arguments);
|
||||
let content = fs.readFileSync(filename, "utf8");
|
||||
content = Utils.stripBOM(content);
|
||||
|
||||
const stats = fs.statSync(filename);
|
||||
const meta = self.extractMeta(content);
|
||||
meta.filename = path.basename(filename);
|
||||
meta.added = stats.atimeMs;
|
||||
meta.modified = stats.mtimeMs;
|
||||
meta.size = stats.size;
|
||||
if (!isPlugin) {
|
||||
meta.css = content;
|
||||
if (meta.format == "json") meta.css = meta.css.split("\n").slice(1).join("\n");
|
||||
content = `module.exports = ${JSON.stringify(meta)};`;
|
||||
}
|
||||
if (isPlugin) {
|
||||
module._compile(content, module.filename);
|
||||
const didExport = !Utils.isEmpty(module.exports);
|
||||
if (didExport) {
|
||||
meta.type = module.exports;
|
||||
module.exports = meta;
|
||||
content = "";
|
||||
}
|
||||
else {
|
||||
content += `\nmodule.exports = ${JSON.stringify(meta)};\nmodule.exports.type = ${meta.exports || meta.name};`;
|
||||
}
|
||||
}
|
||||
module._compile(content, filename);
|
||||
};
|
||||
}
|
||||
|
||||
makePlaceholderPlugin(data) {
|
||||
return {plugin: {
|
||||
start: () => {},
|
||||
getName: () => {return data.name || data.filename;},
|
||||
getAuthor: () => {return "???";},
|
||||
getDescription: () => {return data.message ? data.message : "This plugin was unable to be loaded. Check the author's page for updates.";},
|
||||
getVersion: () => {return "???";}
|
||||
},
|
||||
name: data.name || data.filename,
|
||||
filename: data.filename,
|
||||
source: data.source ? data.source : "",
|
||||
website: data.website ? data.website : ""
|
||||
};
|
||||
}
|
||||
|
||||
loadContent(filename, type) {
|
||||
if (typeof(filename) === "undefined" || typeof(type) === "undefined") return;
|
||||
const isPlugin = type === "plugin";
|
||||
const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder;
|
||||
try {__non_webpack_require__(path.resolve(baseFolder, filename));}
|
||||
catch (error) {return {name: filename, file: filename, message: "Could not be compiled.", error: {message: error.message, stack: error.stack}};}
|
||||
const content = __non_webpack_require__(path.resolve(baseFolder, filename));
|
||||
content.id = Utils.escapeID(content.name);
|
||||
if (isPlugin) {
|
||||
if (!content.type) return;
|
||||
try {
|
||||
content.plugin = new content.type();
|
||||
delete bdplugins[content.plugin.getName()];
|
||||
bdplugins[content.plugin.getName()] = content;
|
||||
}
|
||||
catch (error) {return {name: filename, file: filename, message: "Could not be constructed.", error: {message: error.message, stack: error.stack}};}
|
||||
}
|
||||
else {
|
||||
delete bdthemes[content.name];
|
||||
bdthemes[content.name] = content;
|
||||
}
|
||||
}
|
||||
|
||||
unloadContent(filename, type) {
|
||||
if (typeof(filename) === "undefined" || typeof(type) === "undefined") return;
|
||||
const isPlugin = type === "plugin";
|
||||
const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder;
|
||||
try {
|
||||
delete __non_webpack_require__.cache[__non_webpack_require__.resolve(path.resolve(baseFolder, filename))];
|
||||
}
|
||||
catch (err) {return {name: filename, file: filename, message: "Could not be unloaded.", error: {message: err.message, stack: err.stack}};}
|
||||
}
|
||||
|
||||
isLoaded(filename, type) {
|
||||
const isPlugin = type === "plugin";
|
||||
const baseFolder = isPlugin ? this.pluginsFolder : this.themesFolder;
|
||||
try {__non_webpack_require__.cache[__non_webpack_require__.resolve(path.resolve(baseFolder, filename))];}
|
||||
catch (err) {return false;}
|
||||
return true;
|
||||
}
|
||||
|
||||
reloadContent(filename, type) {
|
||||
const cantUnload = this.unloadContent(filename, type);
|
||||
if (cantUnload) return cantUnload;
|
||||
return this.loadContent(filename, type);
|
||||
}
|
||||
|
||||
loadNewContent(type) {
|
||||
const isPlugin = type === "plugin";
|
||||
const fileEnding = isPlugin ? ".plugin.js" : ".theme.css";
|
||||
const basedir = isPlugin ? this.pluginsFolder : this.themesFolder;
|
||||
const files = fs.readdirSync(basedir);
|
||||
const contentList = Object.values(isPlugin ? bdplugins : bdthemes);
|
||||
const removed = contentList.filter(t => !files.includes(t.filename)).map(c => isPlugin ? c.plugin.getName() : c.name);
|
||||
const added = files.filter(f => !contentList.find(t => t.filename == f) && f.endsWith(fileEnding) && fs.statSync(path.resolve(basedir, f)).isFile());
|
||||
return {added, removed};
|
||||
}
|
||||
|
||||
loadAllContent(type) {
|
||||
const isPlugin = type === "plugin";
|
||||
const fileEnding = isPlugin ? ".plugin.js" : ".theme.css";
|
||||
const basedir = isPlugin ? this.pluginsFolder : this.themesFolder;
|
||||
const errors = [];
|
||||
const files = fs.readdirSync(basedir);
|
||||
|
||||
for (const filename of files) {
|
||||
if (!fs.statSync(path.resolve(basedir, filename)).isFile() || !filename.endsWith(fileEnding)) continue;
|
||||
const error = this.loadContent(filename, type);
|
||||
if (error) errors.push(error);
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
loadPlugins() {return this.loadAllContent("plugin");}
|
||||
loadThemes() {return this.loadAllContent("theme");}
|
||||
};
|
|
@ -0,0 +1,335 @@
|
|||
import {bdConfig, minSupportedVersion, bbdVersion, settingsCookie, bdpluginErrors, bdthemeErrors, bbdChangelog, defaultCookie} from "../0globals";
|
||||
import Utils from "./utils";
|
||||
import emoteModule from "./emoteModule";
|
||||
import quickEmoteMenu from "./quickEmoteMenu";
|
||||
// import publicServersModule from "./publicServers";
|
||||
// import voiceMode from "./voiceMode";
|
||||
// import dMode from "./devMode";
|
||||
import BDV2 from "./v2";
|
||||
import settingsPanel from "./settingsPanel";
|
||||
import pluginModule from "./pluginModule";
|
||||
import themeModule from "./themeModule";
|
||||
import DataStore from "./dataStore";
|
||||
import WebpackModules from "./webpackModules";
|
||||
|
||||
import BDLogo from "../ui/bdLogo";
|
||||
|
||||
function Core(config) {
|
||||
Object.assign(bdConfig, config);
|
||||
}
|
||||
|
||||
Core.prototype.init = async function() {
|
||||
if (bdConfig.version < minSupportedVersion) {
|
||||
Utils.alert("Not Supported", "BetterDiscord v" + bdConfig.version + " (your version)" + " is not supported by the latest js (" + bbdVersion + ").<br><br> Please download the latest version from <a href='https://github.com/rauenzi/BetterDiscordApp/releases/latest' target='_blank'>GitHub</a>");
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.ED) {
|
||||
Utils.alert("Not Supported", "BandagedBD does not work with EnhancedDiscord. Please uninstall one of them.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.WebSocket && window.WebSocket.name && window.WebSocket.name.includes("Patched")) {
|
||||
Utils.alert("Not Supported", "BandagedBD does not work with Powercord. Please uninstall one of them.");
|
||||
return;
|
||||
}
|
||||
|
||||
const latestLocalVersion = bdConfig.updater ? bdConfig.updater.LatestVersion : bdConfig.latestVersion;
|
||||
if (latestLocalVersion > bdConfig.version) {
|
||||
Utils.alert("Update Available", `
|
||||
An update for BandagedBD is available (${latestLocalVersion})! Please Reinstall!<br /><br />
|
||||
<a href='https://github.com/rauenzi/BetterDiscordApp/releases/latest' target='_blank'>Download Installer</a>
|
||||
`);
|
||||
}
|
||||
|
||||
Utils.log("Startup", "Initializing Settings");
|
||||
this.initSettings();
|
||||
// emoteModule = new EmoteModule();
|
||||
// quickEmoteMenu = new QuickEmoteMenu();
|
||||
Utils.log("Startup", "Initializing EmoteModule");
|
||||
window.emotePromise = emoteModule.init().then(() => {
|
||||
emoteModule.initialized = true;
|
||||
Utils.log("Startup", "Initializing QuickEmoteMenu");
|
||||
quickEmoteMenu.init();
|
||||
});
|
||||
// publicServersModule = new V2_PublicServers();
|
||||
|
||||
// voiceMode = new VoiceMode();
|
||||
// dMode = new devMode();
|
||||
|
||||
this.injectExternals();
|
||||
|
||||
await this.checkForGuilds();
|
||||
BDV2.initialize();
|
||||
Utils.log("Startup", "Updating Settings");
|
||||
// settingsPanel = new V2_SettingsPanel();
|
||||
settingsPanel.initializeSettings();
|
||||
|
||||
Utils.log("Startup", "Loading Plugins");
|
||||
// pluginModule = new PluginModule();
|
||||
pluginModule.loadPlugins();
|
||||
|
||||
Utils.log("Startup", "Loading Themes");
|
||||
// themeModule = new ThemeModule();
|
||||
themeModule.loadThemes();
|
||||
|
||||
$("#customcss").detach().appendTo(document.head);
|
||||
|
||||
window.addEventListener("beforeunload", function() {
|
||||
if (settingsCookie["bda-dc-0"]) document.querySelector(".btn.btn-disconnect").click();
|
||||
});
|
||||
|
||||
emoteModule.autoCapitalize();
|
||||
|
||||
Utils.log("Startup", "Removing Loading Icon");
|
||||
if (document.getElementsByClassName("bd-loaderv2").length) document.getElementsByClassName("bd-loaderv2")[0].remove();
|
||||
Utils.log("Startup", "Initializing Main Observer");
|
||||
this.initObserver();
|
||||
|
||||
// Show loading errors
|
||||
if (settingsCookie["fork-ps-1"]) {
|
||||
Utils.log("Startup", "Collecting Startup Errors");
|
||||
Utils.showContentErrors({plugins: bdpluginErrors, themes: bdthemeErrors});
|
||||
}
|
||||
|
||||
const previousVersion = DataStore.getBDData("version");
|
||||
if (bbdVersion > previousVersion) {
|
||||
if (bbdChangelog) this.showChangelogModal(bbdChangelog);
|
||||
DataStore.setBDData("version", bbdVersion);
|
||||
}
|
||||
|
||||
Utils.suppressErrors(this.patchSocial.bind(this), "BD Social Patch")();
|
||||
Utils.suppressErrors(this.patchGuildPills.bind(this), "BD Guild Pills Patch")();
|
||||
Utils.suppressErrors(this.patchGuildListItems.bind(this), "BD Guild List Items Patch")();
|
||||
Utils.suppressErrors(this.patchGuildSeparator.bind(this), "BD Guild Separator Patch")();
|
||||
};
|
||||
|
||||
Core.prototype.checkForGuilds = function() {
|
||||
let timesChecked = 0;
|
||||
return new Promise(resolve => {
|
||||
const checkForGuilds = function() {
|
||||
const wrapper = BDV2.guildClasses.wrapper.split(" ")[0];
|
||||
if (document.querySelectorAll(`.${wrapper}`).length > 0) timesChecked++;
|
||||
const guild = BDV2.guildClasses.listItem.split(" ")[0];
|
||||
const blob = BDV2.guildClasses.blobContainer.split(" ")[0];
|
||||
if (document.querySelectorAll(`.${wrapper} .${guild} .${blob}`).length > 0) return resolve(bdConfig.deferLoaded = true);
|
||||
else if (timesChecked >= 50) return resolve(bdConfig.deferLoaded = true);
|
||||
setTimeout(checkForGuilds, 100);
|
||||
};
|
||||
$(document).ready(function () {
|
||||
setTimeout(checkForGuilds, 100);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Core.prototype.injectExternals = async function() {
|
||||
await Utils.injectJs("https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.9/ace.js");
|
||||
if (window.require.original) window.require = window.require.original;
|
||||
};
|
||||
|
||||
Core.prototype.initSettings = function () {
|
||||
DataStore.initialize();
|
||||
if (!DataStore.getSettingGroup("settings")) {
|
||||
Object.assign(settingsCookie, defaultCookie);
|
||||
settingsPanel.saveSettings();
|
||||
}
|
||||
else {
|
||||
settingsPanel.loadSettings();
|
||||
$("<style id=\"customcss\">").text(atob(DataStore.getBDData("bdcustomcss"))).appendTo(document.head);
|
||||
for (const setting in defaultCookie) {
|
||||
if (settingsCookie[setting] == undefined) {
|
||||
settingsCookie[setting] = defaultCookie[setting];
|
||||
settingsPanel.saveSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Core.prototype.initObserver = function () {
|
||||
const mainObserver = new MutationObserver((mutations) => {
|
||||
|
||||
for (let i = 0, mlen = mutations.length; i < mlen; i++) {
|
||||
const mutation = mutations[i];
|
||||
if (typeof pluginModule !== "undefined") pluginModule.rawObserver(mutation);
|
||||
|
||||
// if there was nothing added, skip
|
||||
if (!mutation.addedNodes.length || !(mutation.addedNodes[0] instanceof Element)) continue;
|
||||
|
||||
const node = mutation.addedNodes[0];
|
||||
|
||||
if (node.classList.contains("layer-3QrUeG")) {
|
||||
if (node.getElementsByClassName("guild-settings-base-section").length) node.setAttribute("layer-id", "server-settings");
|
||||
|
||||
if (node.getElementsByClassName("socialLinks-3jqNFy").length) {
|
||||
node.setAttribute("layer-id", "user-settings");
|
||||
node.setAttribute("id", "user-settings");
|
||||
if (!document.getElementById("bd-settings-sidebar")) settingsPanel.renderSidebar();
|
||||
}
|
||||
}
|
||||
|
||||
if (node.parentElement == document.body && node.querySelector("#ace_settingsmenu")) node.id = "ace_settingsmenu_container";
|
||||
|
||||
// Emoji Picker
|
||||
//node.getElementsByClassName("emojiPicker-3m1S-j").length && !node.querySelector(".emojiPicker-3m1S-j").parentElement.classList.contains("animatorLeft-1EQxU0")
|
||||
if (node.classList.contains("layer-v9HyYc") && node.getElementsByClassName("emojiPicker-3m1S-j").length && !node.querySelector(".emojiPicker-3m1S-j").parentElement.classList.contains("animatorLeft-1EQxU0")) quickEmoteMenu.obsCallback(node);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
mainObserver.observe(document, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
};
|
||||
|
||||
Core.prototype.showChangelogModal = function(options = {}) {
|
||||
const ModalStack = WebpackModules.findByProps("push", "update", "pop", "popWithKey");
|
||||
const ChangelogClasses = WebpackModules.findByProps("fixed", "improved");
|
||||
const TextElement = WebpackModules.findByProps("Sizes", "Weights");
|
||||
const FlexChild = WebpackModules.findByProps("Child");
|
||||
const Titles = WebpackModules.findByProps("Tags", "default");
|
||||
const Changelog = WebpackModules.find(m => m.defaultProps && m.defaultProps.selectable == false);
|
||||
const MarkdownParser = WebpackModules.findByProps("defaultRules", "parse");
|
||||
if (!Changelog || !ModalStack || !ChangelogClasses || !TextElement || !FlexChild || !Titles || !MarkdownParser) return;
|
||||
|
||||
const {image = "https://repository-images.githubusercontent.com/105473537/957b5480-7c26-11e9-8401-50fa820cbae5", description = "", changes = [], title = "BandagedBD", subtitle = `v${bbdVersion}`, footer} = options;
|
||||
const ce = BDV2.React.createElement;
|
||||
const changelogItems = [ce("img", {src: image})];
|
||||
if (description) changelogItems.push(ce("p", null, MarkdownParser.parse(description)));
|
||||
for (let c = 0; c < changes.length; c++) {
|
||||
const entry = changes[c];
|
||||
const type = ChangelogClasses[entry.type] ? ChangelogClasses[entry.type] : ChangelogClasses.added;
|
||||
const margin = c == 0 ? ChangelogClasses.marginTop : "";
|
||||
changelogItems.push(ce("h1", {className: `${type} ${margin}`,}, entry.title));
|
||||
const list = ce("ul", null, entry.items.map(i => ce("li", null, MarkdownParser.parse(i))));
|
||||
changelogItems.push(list);
|
||||
}
|
||||
const renderHeader = function() {
|
||||
return ce(FlexChild.Child, {grow: 1, shrink: 1},
|
||||
ce(Titles.default, {tag: Titles.Tags.H4}, title),
|
||||
ce(TextElement,{size: TextElement.Sizes.SMALL, color: TextElement.Colors.PRIMARY, className: ChangelogClasses.date}, subtitle)
|
||||
);
|
||||
};
|
||||
|
||||
const renderFooter = () => {
|
||||
const Anchor = WebpackModules.find(m => m.displayName == "Anchor");
|
||||
const AnchorClasses = WebpackModules.findByProps("anchorUnderlineOnHover") || {anchor: "anchor-3Z-8Bb", anchorUnderlineOnHover: "anchorUnderlineOnHover-2ESHQB"};
|
||||
const joinSupportServer = (click) => {
|
||||
click.preventDefault();
|
||||
click.stopPropagation();
|
||||
ModalStack.pop();
|
||||
BDV2.joinBD2();
|
||||
};
|
||||
const supportLink = Anchor ? ce(Anchor, {onClick: joinSupportServer}, "Join our Discord Server.") : ce("a", {className: `${AnchorClasses.anchor} ${AnchorClasses.anchorUnderlineOnHover}`, onClick: joinSupportServer}, "Join our Discord Server.");
|
||||
const defaultFooter = ce(TextElement,{size: TextElement.Sizes.SMALL, color: TextElement.Colors.PRIMARY}, "Need support? ", supportLink);
|
||||
return ce(FlexChild.Child, {grow: 1, shrink: 1}, footer ? footer : defaultFooter);
|
||||
};
|
||||
|
||||
ModalStack.push(function(props) {
|
||||
return ce(Changelog, Object.assign({
|
||||
className: ChangelogClasses.container,
|
||||
selectable: true,
|
||||
onScroll: _ => _,
|
||||
onClose: _ => _,
|
||||
renderHeader: renderHeader,
|
||||
renderFooter: renderFooter,
|
||||
children: changelogItems
|
||||
}, props));
|
||||
});
|
||||
};
|
||||
|
||||
Core.prototype.patchSocial = function() {
|
||||
if (this.socialPatch) return;
|
||||
const TabBar = WebpackModules.find(m => m.displayName == "TabBar");
|
||||
const Anchor = WebpackModules.find(m => m.displayName == "Anchor");
|
||||
if (!TabBar || !Anchor) return;
|
||||
this.socialPatch = Utils.monkeyPatch(TabBar.prototype, "render", {after: (data) => {
|
||||
const children = data.returnValue.props.children;
|
||||
if (!children || !children.length || children.length < 3) return;
|
||||
if (children[children.length - 3].type.displayName !== "Separator") return;
|
||||
if (!children[children.length - 2].type.toString().includes("socialLinks")) return;
|
||||
const original = children[children.length - 2].type;
|
||||
const newOne = function() {
|
||||
const returnVal = original(...arguments);
|
||||
returnVal.props.children.push(BDV2.React.createElement(Anchor, {className: "bd-social-link", href: "https://github.com/rauenzi/BetterDiscordApp", title: "BandagedBD", target: "_blank"},
|
||||
BDV2.React.createElement(BDLogo, {size: "16px", className: "bd-social-logo"})
|
||||
));
|
||||
return returnVal;
|
||||
};
|
||||
children[children.length - 2].type = newOne;
|
||||
|
||||
const BBDLink = BDV2.React.createElement(Anchor, {className: "bd-social-link", href: "https://twitter.com/BandagedBD", title: "BandagedBD", target: "_blank"}, "BandagedBD");
|
||||
const AuthorLink = BDV2.React.createElement(Anchor, {className: "bd-social-link", href: "https://twitter.com/ZackRauen", title: "Zerebos", target: "_blank"}, "Zerebos");
|
||||
const additional = BDV2.react.createElement("div", {className: "colorMuted-HdFt4q size12-3cLvbJ"}, [BBDLink, ` ${bbdVersion} by `, AuthorLink]);
|
||||
const injector = BDV2.react.createElement("div", {className: "colorMuted-HdFt4q size12-3cLvbJ"}, ["BBD Injector", ` ${bdConfig.version} by `, AuthorLink]);
|
||||
|
||||
const originalVersions = children[children.length - 1].type;
|
||||
children[children.length - 1].type = function() {
|
||||
const returnVal = originalVersions(...arguments);
|
||||
returnVal.props.children.push(injector);
|
||||
returnVal.props.children.push(additional);
|
||||
return returnVal;
|
||||
};
|
||||
}});
|
||||
};
|
||||
|
||||
const getGuildClasses = function() {
|
||||
const guildsWrapper = WebpackModules.findByProps("wrapper", "unreadMentionsBar");
|
||||
const guilds = WebpackModules.findByProps("guildsError", "selected");
|
||||
const pill = WebpackModules.findByProps("blobContainer");
|
||||
return Object.assign({}, guildsWrapper, guilds, pill);
|
||||
};
|
||||
|
||||
Core.prototype.patchGuildListItems = function() {
|
||||
if (this.guildListItemsPatch) return;
|
||||
const GuildClasses = getGuildClasses();
|
||||
const listItemClass = GuildClasses.listItem.split(" ")[0];
|
||||
const blobClass = GuildClasses.blobContainer.split(" ")[0];
|
||||
const reactInstance = BDV2.getInternalInstance(document.querySelector(`.${listItemClass} .${blobClass}`).parentElement);
|
||||
const GuildComponent = reactInstance.return.type;
|
||||
if (!GuildComponent) return;
|
||||
this.guildListItemsPatch = Utils.monkeyPatch(GuildComponent.prototype, "render", {after: (data) => {
|
||||
if (data.returnValue && data.thisObject) {
|
||||
const returnValue = data.returnValue;
|
||||
const guildData = data.thisObject.props;
|
||||
returnValue.props.className += " bd-guild";
|
||||
if (guildData.unread) returnValue.props.className += " bd-unread";
|
||||
if (guildData.selected) returnValue.props.className += " bd-selected";
|
||||
if (guildData.audio) returnValue.props.className += " bd-audio";
|
||||
if (guildData.video) returnValue.props.className += " bd-video";
|
||||
if (guildData.badge) returnValue.props.className += " bd-badge";
|
||||
if (guildData.animatable) returnValue.props.className += " bd-animatable";
|
||||
return returnValue;
|
||||
}
|
||||
}});
|
||||
};
|
||||
|
||||
Core.prototype.patchGuildPills = function() {
|
||||
if (this.guildPillPatch) return;
|
||||
const guildPill = WebpackModules.find(m => m.default && !m.default.displayName && m.default.toString && m.default.toString().includes("translate3d"));
|
||||
if (!guildPill) return;
|
||||
this.guildPillPatch = Utils.monkeyPatch(guildPill, "default", {after: (data) => {
|
||||
const props = data.methodArguments[0];
|
||||
if (props.unread) data.returnValue.props.className += " bd-unread";
|
||||
if (props.selected) data.returnValue.props.className += " bd-selected";
|
||||
if (props.hovered) data.returnValue.props.className += " bd-hovered";
|
||||
return data.returnValue;
|
||||
}});
|
||||
};
|
||||
|
||||
Core.prototype.patchGuildSeparator = function() {
|
||||
if (this.guildSeparatorPatch) return;
|
||||
const Guilds = WebpackModules.findByDisplayName("Guilds");
|
||||
const guildComponents = WebpackModules.findByProps("renderListItem");
|
||||
if (!guildComponents || !Guilds) return;
|
||||
const GuildSeparator = function() {
|
||||
const returnValue = guildComponents.Separator(...arguments);
|
||||
returnValue.props.className += " bd-guild-separator";
|
||||
return returnValue;
|
||||
};
|
||||
this.guildSeparatorPatch = Utils.monkeyPatch(Guilds.prototype, "render", {after: (data) => {
|
||||
data.returnValue.props.children[1].props.children[3].type = GuildSeparator;
|
||||
}});
|
||||
};
|
||||
|
||||
export default Core;
|
|
@ -0,0 +1,74 @@
|
|||
import {bdConfig} from "../0globals";
|
||||
import Utils from "./utils";
|
||||
import ContentManager from "./contentManager";
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const releaseChannel = DiscordNative.globals.releaseChannel;
|
||||
|
||||
export default new class DataStore {
|
||||
constructor() {
|
||||
this.data = {settings: {stable: {}, canary: {}, ptb: {}}};
|
||||
this.pluginData = {};
|
||||
}
|
||||
|
||||
initialize() {
|
||||
try {
|
||||
if (!fs.existsSync(this.BDFile)) fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4));
|
||||
const data = __non_webpack_require__(this.BDFile);
|
||||
if (data.hasOwnProperty("settings")) this.data = data;
|
||||
if (!fs.existsSync(this.settingsFile)) return;
|
||||
let settings = __non_webpack_require__(this.settingsFile);
|
||||
fs.unlinkSync(this.settingsFile);
|
||||
if (settings.hasOwnProperty("settings")) settings = Object.assign({stable: {}, canary: {}, ptb: {}}, {[releaseChannel]: settings});
|
||||
else settings = Object.assign({stable: {}, canary: {}, ptb: {}}, settings);
|
||||
this.setBDData("settings", settings);
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
Utils.alert("Corrupt Storage", "The bd storage has somehow become corrupt. You may either try to salvage the file or delete it then reload.");
|
||||
}
|
||||
}
|
||||
|
||||
get BDFile() {return this._BDFile || (this._BDFile = path.resolve(bdConfig.dataPath, "bdstorage.json"));}
|
||||
get settingsFile() {return this._settingsFile || (this._settingsFile = path.resolve(bdConfig.dataPath, "bdsettings.json"));}
|
||||
getPluginFile(pluginName) {return path.resolve(ContentManager.pluginsFolder, pluginName + ".config.json");}
|
||||
|
||||
getSettingGroup(key) {
|
||||
return this.data.settings[releaseChannel][key] || null;
|
||||
}
|
||||
|
||||
setSettingGroup(key, data) {
|
||||
this.data.settings[releaseChannel][key] = data;
|
||||
fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4));
|
||||
}
|
||||
|
||||
getBDData(key) {
|
||||
return this.data[key] || "";
|
||||
}
|
||||
|
||||
setBDData(key, value) {
|
||||
this.data[key] = value;
|
||||
fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4));
|
||||
}
|
||||
|
||||
getPluginData(pluginName, key) {
|
||||
if (this.pluginData[pluginName] !== undefined) return this.pluginData[pluginName][key] || undefined;
|
||||
if (!fs.existsSync(this.getPluginFile(pluginName))) return undefined;
|
||||
this.pluginData[pluginName] = JSON.parse(fs.readFileSync(this.getPluginFile(pluginName)));
|
||||
return this.pluginData[pluginName][key] || undefined;
|
||||
}
|
||||
|
||||
setPluginData(pluginName, key, value) {
|
||||
if (value === undefined) return;
|
||||
if (this.pluginData[pluginName] === undefined) this.pluginData[pluginName] = {};
|
||||
this.pluginData[pluginName][key] = value;
|
||||
fs.writeFileSync(this.getPluginFile(pluginName), JSON.stringify(this.pluginData[pluginName], null, 4));
|
||||
}
|
||||
|
||||
deletePluginData(pluginName, key) {
|
||||
if (this.pluginData[pluginName] === undefined) this.pluginData[pluginName] = {};
|
||||
delete this.pluginData[pluginName][key];
|
||||
fs.writeFileSync(this.getPluginFile(pluginName), JSON.stringify(this.pluginData[pluginName], null, 4));
|
||||
}
|
||||
};
|
|
@ -0,0 +1,89 @@
|
|||
import BDV2 from "./v2";
|
||||
|
||||
function devMode() {}
|
||||
|
||||
devMode.prototype.enable = function(selectorMode) {
|
||||
const self = this;
|
||||
this.disable();
|
||||
$(document).on("keydown.bdDevmode", function(e) {
|
||||
if (e.which === 119 || e.which == 118) {//F8
|
||||
console.log("%c[%cDevMode%c] %cBreak/Resume", "color: red;", "color: #303030; font-weight:700;", "color:red;", "");
|
||||
debugger; // eslint-disable-line no-debugger
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
});
|
||||
|
||||
if (!selectorMode) return;
|
||||
$(document).on("contextmenu.bdDevmode", function(e) {
|
||||
self.lastSelector = self.getSelector(e.toElement);
|
||||
|
||||
function attach() {
|
||||
let cm = $(".contextMenu-HLZMGh");
|
||||
if (cm.length <= 0) {
|
||||
cm = $("<div class=\"contextMenu-HLZMGh bd-context-menu\"></div>");
|
||||
cm.addClass($(".app, .app-2rEoOp").hasClass("theme-dark") ? "theme-dark" : "theme-light");
|
||||
cm.appendTo(".app, .app-2rEoOp");
|
||||
cm.css("top", e.clientY);
|
||||
cm.css("left", e.clientX);
|
||||
$(document).on("click.bdDevModeCtx", () => {
|
||||
cm.remove();
|
||||
$(document).off(".bdDevModeCtx");
|
||||
});
|
||||
$(document).on("contextmenu.bdDevModeCtx", () => {
|
||||
cm.remove();
|
||||
$(document).off(".bdDevModeCtx");
|
||||
});
|
||||
$(document).on("keyup.bdDevModeCtx", (e) => {
|
||||
if (e.keyCode === 27) {
|
||||
cm.remove();
|
||||
$(document).off(".bdDevModeCtx");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const cmo = $("<div/>", {
|
||||
"class": "itemGroup-1tL0uz"
|
||||
});
|
||||
const cmi = $("<div/>", {
|
||||
"class": "item-1Yvehc",
|
||||
"click": function() {
|
||||
BDV2.NativeModule.copy(self.lastSelector);
|
||||
cm.hide();
|
||||
}
|
||||
}).append($("<span/>", {text: "Copy Selector"}));
|
||||
cmo.append(cmi);
|
||||
cm.append(cmo);
|
||||
if (cm.hasClass("undefined")) cm.css("top", "-=" + cmo.outerHeight());
|
||||
}
|
||||
|
||||
setImmediate(attach);
|
||||
|
||||
e.stopPropagation();
|
||||
});
|
||||
};
|
||||
|
||||
devMode.prototype.getRules = function(element, css = element.ownerDocument.styleSheets) {
|
||||
//if (window.getMatchedCSSRules) return window.getMatchedCSSRules(element);
|
||||
const sheets = [...css].filter(s => !s.href || !s.href.includes("BetterDiscordApp"));
|
||||
const rules = sheets.map(s => [...(s.cssRules || [])]).flat();
|
||||
const elementRules = rules.filter(r => r && r.selectorText && element.matches(r.selectorText) && r.style.length && r.selectorText.split(", ").length < 8 && !r.selectorText.split(", ").includes("*"));
|
||||
return elementRules;
|
||||
};
|
||||
|
||||
devMode.prototype.getSelector = function(element) {
|
||||
if (element.id) return `#${element.id}`;
|
||||
const rules = this.getRules(element);
|
||||
const latestRule = rules[rules.length - 1];
|
||||
if (latestRule) return latestRule.selectorText;
|
||||
else if (element.classList.length) return `.${Array.from(element.classList).join(".")}`;
|
||||
return `.${Array.from(element.parentElement.classList).join(".")}`;
|
||||
};
|
||||
|
||||
devMode.prototype.disable = function() {
|
||||
$(document).off("keydown.bdDevmode");
|
||||
$(document).off("contextmenu.bdDevmode");
|
||||
$(document).off("contextmenu.bdDevModeCtx");
|
||||
};
|
||||
|
||||
export default new devMode();
|
|
@ -0,0 +1,288 @@
|
|||
import {bdConfig, settingsCookie, bemotes, bdEmoteSettingIDs, bdEmotes} from "../0globals";
|
||||
import DataStore from "./dataStore";
|
||||
import BDV2 from "./v2";
|
||||
import Utils from "./utils";
|
||||
|
||||
import BDEmote from "../ui/bdEmote";
|
||||
|
||||
function EmoteModule() {
|
||||
Object.defineProperty(this, "categories", {
|
||||
get: function() {
|
||||
const cats = [];
|
||||
for (const current in bdEmoteSettingIDs) {
|
||||
if (settingsCookie[bdEmoteSettingIDs[current]]) cats.push(current);
|
||||
}
|
||||
return cats;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
EmoteModule.prototype.init = async function () {
|
||||
this.modifiers = ["flip", "spin", "pulse", "spin2", "spin3", "1spin", "2spin", "3spin", "tr", "bl", "br", "shake", "shake2", "shake3", "flap"];
|
||||
this.overrides = ["twitch", "bttv", "ffz"];
|
||||
|
||||
const emoteInfo = {
|
||||
TwitchGlobal: {
|
||||
url: `https://rauenzi.github.io/BetterDiscordApp/data/emotedata_twitch_global.json`,
|
||||
variable: "TwitchGlobal",
|
||||
oldVariable: "emotesTwitch",
|
||||
getEmoteURL: (e) => `https://static-cdn.jtvnw.net/emoticons/v1/${e}/1.0`
|
||||
},
|
||||
TwitchSubscriber: {
|
||||
url: `https://rauenzi.github.io/BetterDiscordApp/data/emotedata_twitch_subscriber.json`,
|
||||
variable: "TwitchSubscriber",
|
||||
oldVariable: "subEmotesTwitch",
|
||||
getEmoteURL: (e) => `https://static-cdn.jtvnw.net/emoticons/v1/${e}/1.0`
|
||||
},
|
||||
FrankerFaceZ: {
|
||||
url: `https://rauenzi.github.io/BetterDiscordApp/data/emotedata_ffz.json`,
|
||||
variable: "FrankerFaceZ",
|
||||
oldVariable: "emotesFfz",
|
||||
getEmoteURL: (e) => `https://cdn.frankerfacez.com/emoticon/${e}/1`
|
||||
},
|
||||
BTTV: {
|
||||
url: `https://rauenzi.github.io/BetterDiscordApp/data/emotedata_bttv.json`,
|
||||
variable: "BTTV",
|
||||
oldVariable: "emotesBTTV",
|
||||
getEmoteURL: (e) => `https://cdn.betterttv.net/emote/${e}/1x`
|
||||
},
|
||||
BTTV2: {
|
||||
url: `https://rauenzi.github.io/BetterDiscordApp/data/emotedata_bttv2.json`,
|
||||
variable: "BTTV2",
|
||||
oldVariable: "emotesBTTV2",
|
||||
getEmoteURL: (e) => `https://cdn.betterttv.net/emote/${e}/1x`
|
||||
}
|
||||
};
|
||||
|
||||
await this.getBlacklist();
|
||||
await this.loadEmoteData(emoteInfo);
|
||||
|
||||
while (!BDV2.MessageComponent) await new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
if (this.cancelEmoteRender) return;
|
||||
this.cancelEmoteRender = Utils.monkeyPatch(BDV2.MessageComponent, "default", {before: ({methodArguments}) => {
|
||||
const nodes = methodArguments[0].childrenMessageContent.props.content;
|
||||
if (!nodes || !nodes.length) return;
|
||||
for (let n = 0; n < nodes.length; n++) {
|
||||
const node = nodes[n];
|
||||
if (typeof(node) !== "string") continue;
|
||||
const words = node.split(/([^\s]+)([\s]|$)/g);
|
||||
for (let c = 0, clen = this.categories.length; c < clen; c++) {
|
||||
for (let w = 0, wlen = words.length; w < wlen; w++) {
|
||||
const emote = words[w];
|
||||
const emoteSplit = emote.split(":");
|
||||
const emoteName = emoteSplit[0];
|
||||
let emoteModifier = emoteSplit[1] ? emoteSplit[1] : "";
|
||||
let emoteOverride = emoteModifier.slice(0);
|
||||
|
||||
if (emoteName.length < 4 || bemotes.includes(emoteName)) continue;
|
||||
if (!this.modifiers.includes(emoteModifier) || !settingsCookie["bda-es-8"]) emoteModifier = "";
|
||||
if (!this.overrides.includes(emoteOverride)) emoteOverride = "";
|
||||
else emoteModifier = emoteOverride;
|
||||
|
||||
let current = this.categories[c];
|
||||
if (emoteOverride === "twitch") {
|
||||
if (bdEmotes.TwitchGlobal[emoteName]) current = "TwitchGlobal";
|
||||
else if (bdEmotes.TwitchSubscriber[emoteName]) current = "TwitchSubscriber";
|
||||
}
|
||||
else if (emoteOverride === "bttv") {
|
||||
if (bdEmotes.BTTV[emoteName]) current = "BTTV";
|
||||
else if (bdEmotes.BTTV2[emoteName]) current = "BTTV2";
|
||||
}
|
||||
else if (emoteOverride === "ffz") {
|
||||
if (bdEmotes.FrankerFaceZ[emoteName]) current = "FrankerFaceZ";
|
||||
}
|
||||
|
||||
if (!bdEmotes[current][emoteName] || !settingsCookie[bdEmoteSettingIDs[current]]) continue;
|
||||
const results = nodes[n].match(new RegExp(`([\\s]|^)${Utils.escape(emoteModifier ? emoteName + ":" + emoteModifier : emoteName)}([\\s]|$)`));
|
||||
if (!results) continue;
|
||||
const pre = nodes[n].substring(0, results.index + results[1].length);
|
||||
const post = nodes[n].substring(results.index + results[0].length - results[2].length);
|
||||
nodes[n] = pre;
|
||||
const emoteComponent = BDV2.react.createElement(BDEmote, {name: emoteName, url: bdEmotes[current][emoteName], modifier: emoteModifier});
|
||||
nodes.splice(n + 1, 0, post);
|
||||
nodes.splice(n + 1, 0, emoteComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
const onlyEmotes = nodes.every(r => {
|
||||
if (typeof(r) == "string" && r.replace(/\s*/, "") == "") return true;
|
||||
else if (r.type && r.type.name == "BDEmote") return true;
|
||||
else if (r.props && r.props.children && r.props.children.props && r.props.children.props.emojiName) return true;
|
||||
return false;
|
||||
});
|
||||
if (!onlyEmotes) return;
|
||||
|
||||
for (const node of nodes) {
|
||||
if (typeof(node) != "object") continue;
|
||||
if (node.type.name == "BDEmote") node.props.jumboable = true;
|
||||
else if (node.props && node.props.children && node.props.children.props && node.props.children.props.emojiName) node.props.children.props.jumboable = true;
|
||||
}
|
||||
}});
|
||||
};
|
||||
|
||||
EmoteModule.prototype.disable = function() {
|
||||
this.disableAutoCapitalize();
|
||||
if (this.cancelEmoteRender) return;
|
||||
this.cancelEmoteRender();
|
||||
this.cancelEmoteRender = null;
|
||||
};
|
||||
|
||||
EmoteModule.prototype.clearEmoteData = async function() {
|
||||
const _fs = require("fs");
|
||||
const emoteFile = "emote_data.json";
|
||||
const file = bdConfig.dataPath + emoteFile;
|
||||
const exists = _fs.existsSync(file);
|
||||
if (exists) _fs.unlinkSync(file);
|
||||
DataStore.setBDData("emoteCacheDate", (new Date()).toJSON());
|
||||
|
||||
Object.assign(bdEmotes, {
|
||||
TwitchGlobal: {},
|
||||
TwitchSubscriber: {},
|
||||
BTTV: {},
|
||||
FrankerFaceZ: {},
|
||||
BTTV2: {}
|
||||
});
|
||||
};
|
||||
|
||||
EmoteModule.prototype.isCacheValid = function() {
|
||||
const cacheLength = DataStore.getBDData("emoteCacheDays") || DataStore.setBDData("emoteCacheDays", 7) || 7;
|
||||
const cacheDate = new Date(DataStore.getBDData("emoteCacheDate") || null);
|
||||
const currentDate = new Date();
|
||||
const daysBetween = Math.round(Math.abs((currentDate.getTime() - cacheDate.getTime()) / (24 * 60 * 60 * 1000)));
|
||||
if (daysBetween > cacheLength) {
|
||||
DataStore.setBDData("emoteCacheDate", currentDate.toJSON());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
EmoteModule.prototype.loadEmoteData = async function(emoteInfo) {
|
||||
const fs = require("fs");
|
||||
const emoteFile = "emote_data.json";
|
||||
const file = bdConfig.dataPath + emoteFile;
|
||||
const exists = await new Promise(r => fs.exists(file, r));
|
||||
|
||||
if (exists && this.isCacheValid()) {
|
||||
if (settingsCookie["fork-ps-2"]) Utils.showToast("Loading emotes from cache.", {type: "info"});
|
||||
Utils.log("Emotes", "Loading emotes from local cache.");
|
||||
|
||||
const data = await new Promise(resolve => {
|
||||
fs.readFile(file, "utf8", (err, data) => {
|
||||
Utils.log("Emotes", "Emote file read.");
|
||||
if (err) data = {};
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
|
||||
const parsed = Utils.testJSON(data);
|
||||
let isValid = !!parsed;
|
||||
if (isValid) Object.assign(bdEmotes, parsed);
|
||||
|
||||
for (const e in emoteInfo) {
|
||||
isValid = Object.keys(bdEmotes[emoteInfo[e].variable]).length > 0;
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
if (settingsCookie["fork-ps-2"]) Utils.showToast("Emotes successfully loaded.", {type: "success"});
|
||||
return;
|
||||
}
|
||||
|
||||
Utils.log("Emotes", "Cache was corrupt, downloading...");
|
||||
await new Promise(r => fs.unlink(file, r));
|
||||
}
|
||||
|
||||
if (!settingsCookie["fork-es-3"]) return;
|
||||
if (settingsCookie["fork-ps-2"]) Utils.showToast("Downloading emotes in the background do not reload.", {type: "info"});
|
||||
|
||||
for (const e in emoteInfo) {
|
||||
await new Promise(r => setTimeout(r, 1000));
|
||||
const data = await this.downloadEmotes(emoteInfo[e]);
|
||||
bdEmotes[emoteInfo[e].variable] = data;
|
||||
}
|
||||
|
||||
if (settingsCookie["fork-ps-2"]) Utils.showToast("All emotes successfully downloaded.", {type: "success"});
|
||||
|
||||
try { await new Promise(r => fs.writeFile(file, JSON.stringify(bdEmotes), "utf8", r)); }
|
||||
catch (err) { Utils.err("Emotes", "Could not save emote data.", err); }
|
||||
};
|
||||
|
||||
EmoteModule.prototype.downloadEmotes = function(emoteMeta) {
|
||||
const request = require("request");
|
||||
const options = {
|
||||
url: emoteMeta.url,
|
||||
timeout: emoteMeta.timeout ? emoteMeta.timeout : 5000,
|
||||
json: true
|
||||
};
|
||||
|
||||
Utils.log("Emotes", `Downloading: ${emoteMeta.variable} (${emoteMeta.url})`);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
request(options, (error, response, parsedData) => {
|
||||
if (error) {
|
||||
Utils.err("Emotes", "Could not download " + emoteMeta.variable, error);
|
||||
if (emoteMeta.backup) {
|
||||
emoteMeta.url = emoteMeta.backup;
|
||||
emoteMeta.backup = null;
|
||||
if (emoteMeta.backupParser) emoteMeta.parser = emoteMeta.backupParser;
|
||||
return resolve(this.downloadEmotes(emoteMeta));
|
||||
}
|
||||
return reject({});
|
||||
}
|
||||
|
||||
if (typeof(emoteMeta.parser) === "function") parsedData = emoteMeta.parser(parsedData);
|
||||
|
||||
for (const emote in parsedData) {
|
||||
if (emote.length < 4 || bemotes.includes(emote)) {
|
||||
delete parsedData[emote];
|
||||
continue;
|
||||
}
|
||||
parsedData[emote] = emoteMeta.getEmoteURL(parsedData[emote]);
|
||||
}
|
||||
resolve(parsedData);
|
||||
Utils.log("Emotes", "Downloaded: " + emoteMeta.variable);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
EmoteModule.prototype.getBlacklist = function () {
|
||||
return new Promise(resolve => {
|
||||
$.getJSON(`https://rauenzi.github.io/BetterDiscordApp/data/emotefilter.json`, function (data) {
|
||||
resolve(bemotes.concat(data.blacklist));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
EmoteModule.prototype.autoCapitalize = function () {
|
||||
if (!settingsCookie["bda-es-4"] || this.autoCapitalizeActive) return;
|
||||
$("body").on("keyup.bdac change.bdac paste.bdac", $(".channelTextArea-rNsIhG textarea:first"), () => {
|
||||
const text = $(".channelTextArea-rNsIhG textarea:first").val();
|
||||
if (text == undefined) return;
|
||||
|
||||
const lastWord = text.split(" ").pop();
|
||||
if (lastWord.length > 3) {
|
||||
if (lastWord == "danSgame") return;
|
||||
const ret = this.capitalize(lastWord.toLowerCase());
|
||||
if (ret !== null && ret !== undefined) {
|
||||
Utils.insertText(Utils.getTextArea()[0], text.replace(lastWord, ret));
|
||||
}
|
||||
}
|
||||
});
|
||||
this.autoCapitalizeActive = true;
|
||||
};
|
||||
|
||||
EmoteModule.prototype.capitalize = function (value) {
|
||||
const res = bdEmotes.TwitchGlobal;
|
||||
for (const p in res) {
|
||||
if (res.hasOwnProperty(p) && value == (p + "").toLowerCase()) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EmoteModule.prototype.disableAutoCapitalize = function() {
|
||||
this.autoCapitalizeActive = false;
|
||||
$("body").off(".bdac");
|
||||
};
|
||||
|
||||
export default new EmoteModule();
|
|
@ -0,0 +1,230 @@
|
|||
import {bdpluginErrors, pluginCookie, settingsCookie, bdplugins} from "../0globals";
|
||||
import ContentManager from "./contentManager";
|
||||
import DataStore from "./dataStore";
|
||||
import BDEvents from "./bdEvents";
|
||||
import Utils from "./utils";
|
||||
|
||||
function PluginModule() {
|
||||
|
||||
}
|
||||
|
||||
PluginModule.prototype.loadPlugins = function () {
|
||||
this.loadPluginData();
|
||||
bdpluginErrors.concat(ContentManager.loadPlugins());
|
||||
const plugins = Object.keys(bdplugins);
|
||||
for (let i = 0; i < plugins.length; i++) {
|
||||
let plugin, name;
|
||||
|
||||
try {
|
||||
plugin = bdplugins[plugins[i]].plugin;
|
||||
name = plugin.getName();
|
||||
if (plugin.load && typeof(plugin.load) == "function") plugin.load();
|
||||
}
|
||||
catch (err) {
|
||||
pluginCookie[name] = false;
|
||||
Utils.err("Plugins", name + " could not be loaded.", err);
|
||||
bdpluginErrors.push({name: name, file: bdplugins[plugins[i]].filename, message: "load() could not be fired.", error: {message: err.message, stack: err.stack}});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!pluginCookie[name]) pluginCookie[name] = false;
|
||||
|
||||
if (pluginCookie[name]) {
|
||||
try {
|
||||
plugin.start();
|
||||
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${plugin.getName()} v${plugin.getVersion()} has started.`);
|
||||
}
|
||||
catch (err) {
|
||||
pluginCookie[name] = false;
|
||||
Utils.err("Plugins", name + " could not be started.", err);
|
||||
bdpluginErrors.push({name: name, file: bdplugins[plugins[i]].filename, message: "start() could not be fired.", error: {message: err.message, stack: err.stack}});
|
||||
}
|
||||
}
|
||||
}
|
||||
this.savePluginData();
|
||||
|
||||
require("electron").remote.getCurrentWebContents().on("did-navigate-in-page", this.channelSwitch.bind(this));
|
||||
// if (settingsCookie["fork-ps-5"]) ContentManager.watchContent("plugin");
|
||||
};
|
||||
|
||||
PluginModule.prototype.startPlugin = function(plugin, reload = false) {
|
||||
try {
|
||||
bdplugins[plugin].plugin.start();
|
||||
if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${bdplugins[plugin].plugin.getName()} v${bdplugins[plugin].plugin.getVersion()} has started.`);
|
||||
}
|
||||
catch (err) {
|
||||
if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${bdplugins[plugin].plugin.getName()} v${bdplugins[plugin].plugin.getVersion()} could not be started.`, {type: "error"});
|
||||
pluginCookie[plugin] = false;
|
||||
this.savePluginData();
|
||||
Utils.err("Plugins", plugin + " could not be started.", err);
|
||||
}
|
||||
};
|
||||
|
||||
PluginModule.prototype.stopPlugin = function(plugin, reload = false) {
|
||||
try {
|
||||
bdplugins[plugin].plugin.stop();
|
||||
if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${bdplugins[plugin].plugin.getName()} v${bdplugins[plugin].plugin.getVersion()} has stopped.`);
|
||||
}
|
||||
catch (err) {
|
||||
if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${bdplugins[plugin].plugin.getName()} v${bdplugins[plugin].plugin.getVersion()} could not be stopped.`, {type: "error"});
|
||||
Utils.err("Plugins", bdplugins[plugin].plugin.getName() + " could not be stopped.", err);
|
||||
}
|
||||
};
|
||||
|
||||
PluginModule.prototype.enablePlugin = function (plugin, reload = false) {
|
||||
if (pluginCookie[plugin]) return;
|
||||
pluginCookie[plugin] = true;
|
||||
this.savePluginData();
|
||||
this.startPlugin(plugin, reload);
|
||||
};
|
||||
|
||||
PluginModule.prototype.enable = function (plugin, reload = false) {
|
||||
return this.enablePlugin(plugin, reload);
|
||||
};
|
||||
|
||||
PluginModule.prototype.disablePlugin = function (plugin, reload = false) {
|
||||
if (!pluginCookie[plugin]) return;
|
||||
pluginCookie[plugin] = false;
|
||||
this.savePluginData();
|
||||
this.stopPlugin(plugin, reload);
|
||||
};
|
||||
|
||||
PluginModule.prototype.disable = function (plugin, reload = false) {
|
||||
return this.disablePlugin(plugin, reload);
|
||||
};
|
||||
|
||||
PluginModule.prototype.togglePlugin = function (plugin) {
|
||||
if (pluginCookie[plugin]) this.disablePlugin(plugin);
|
||||
else this.enablePlugin(plugin);
|
||||
};
|
||||
|
||||
PluginModule.prototype.toggle = function (plugin, reload = false) {
|
||||
return this.togglePlugin(plugin, reload);
|
||||
};
|
||||
|
||||
PluginModule.prototype.loadPlugin = function(filename) {
|
||||
const error = ContentManager.loadContent(filename, "plugin");
|
||||
if (error) {
|
||||
if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({plugins: [error]});
|
||||
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${filename} could not be loaded.`, {type: "error"});
|
||||
return Utils.err("ContentManager", `${filename} could not be loaded.`, error);
|
||||
}
|
||||
const plugin = Object.values(bdplugins).find(p => p.filename == filename).plugin;
|
||||
try { if (plugin.load && typeof(plugin.load) == "function") plugin.load();}
|
||||
catch (err) {if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({plugins: [err]});}
|
||||
Utils.log("ContentManager", `${plugin.getName()} v${plugin.getVersion()} was loaded.`);
|
||||
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${plugin.getName()} v${plugin.getVersion()} was loaded.`, {type: "success"});
|
||||
BDEvents.dispatch("plugin-loaded", plugin.getName());
|
||||
};
|
||||
|
||||
PluginModule.prototype.unloadPlugin = function(filenameOrName) {
|
||||
const bdplugin = Object.values(bdplugins).find(p => p.filename == filenameOrName) || bdplugins[filenameOrName];
|
||||
if (!bdplugin) return;
|
||||
const plugin = bdplugin.plugin.getName();
|
||||
if (pluginCookie[plugin]) this.disablePlugin(plugin, true);
|
||||
const error = ContentManager.unloadContent(bdplugins[plugin].filename, "plugin");
|
||||
delete bdplugins[plugin];
|
||||
if (error) {
|
||||
if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({plugins: [error]});
|
||||
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${plugin} could not be unloaded. It may have not been loaded yet.`, {type: "error"});
|
||||
return Utils.err("ContentManager", `${plugin} could not be unloaded. It may have not been loaded yet.`, error);
|
||||
}
|
||||
Utils.log("ContentManager", `${plugin} was unloaded.`);
|
||||
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${plugin} was unloaded.`, {type: "success"});
|
||||
BDEvents.dispatch("plugin-unloaded", plugin);
|
||||
};
|
||||
|
||||
PluginModule.prototype.delete = function(filenameOrName) {
|
||||
const bdplugin = Object.values(bdplugins).find(p => p.filename == filenameOrName) || bdplugins[filenameOrName];
|
||||
if (!bdplugin) return;
|
||||
this.unloadPlugin(bdplugin.filename);
|
||||
const fullPath = require("path").resolve(ContentManager.pluginsFolder, bdplugin.filename);
|
||||
require("fs").unlinkSync(fullPath);
|
||||
};
|
||||
|
||||
PluginModule.prototype.reloadPlugin = function(filenameOrName) {
|
||||
const bdplugin = Object.values(bdplugins).find(p => p.filename == filenameOrName) || bdplugins[filenameOrName];
|
||||
if (!bdplugin) return this.loadPlugin(filenameOrName);
|
||||
const plugin = bdplugin.plugin.getName();
|
||||
const enabled = pluginCookie[plugin];
|
||||
if (enabled) this.stopPlugin(plugin, true);
|
||||
const error = ContentManager.reloadContent(bdplugins[plugin].filename, "plugin");
|
||||
if (error) {
|
||||
if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({plugins: [error]});
|
||||
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${plugin} could not be reloaded.`, {type: "error"});
|
||||
return Utils.err("ContentManager", `${plugin} could not be reloaded.`, error);
|
||||
}
|
||||
if (bdplugins[plugin].plugin.load && typeof(bdplugins[plugin].plugin.load) == "function") bdplugins[plugin].plugin.load();
|
||||
if (enabled) this.startPlugin(plugin, true);
|
||||
Utils.log("ContentManager", `${plugin} v${bdplugins[plugin].plugin.getVersion()} was reloaded.`);
|
||||
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${plugin} v${bdplugins[plugin].plugin.getVersion()} was reloaded.`, {type: "success"});
|
||||
BDEvents.dispatch("plugin-reloaded", plugin);
|
||||
};
|
||||
|
||||
PluginModule.prototype.reload = function(name) {
|
||||
return this.reloadPlugin(name);
|
||||
};
|
||||
|
||||
PluginModule.prototype.edit = function(filenameOrName) {
|
||||
console.log("Edit " + filenameOrName);
|
||||
const bdplugin = Object.values(bdplugins).find(p => p.filename == filenameOrName) || bdplugins[filenameOrName];
|
||||
if (!bdplugin) return;
|
||||
const fullPath = require("path").resolve(ContentManager.pluginsFolder, bdplugin.filename);
|
||||
console.log("Edit " + fullPath);
|
||||
require("electron").shell.openItem(`${fullPath}`);
|
||||
};
|
||||
|
||||
PluginModule.prototype.updatePluginList = function() {
|
||||
const results = ContentManager.loadNewContent("plugin");
|
||||
for (const filename of results.added) this.loadPlugin(filename);
|
||||
for (const name of results.removed) this.unloadPlugin(name);
|
||||
};
|
||||
|
||||
PluginModule.prototype.loadPluginData = function () {
|
||||
const saved = DataStore.getSettingGroup("plugins");
|
||||
if (saved) {
|
||||
Object.assign(pluginCookie, saved);
|
||||
}
|
||||
};
|
||||
|
||||
PluginModule.prototype.savePluginData = function () {
|
||||
DataStore.setSettingGroup("plugins", pluginCookie);
|
||||
};
|
||||
|
||||
PluginModule.prototype.newMessage = function () {
|
||||
const plugins = Object.keys(bdplugins);
|
||||
for (let i = 0; i < plugins.length; i++) {
|
||||
const plugin = bdplugins[plugins[i]].plugin;
|
||||
if (!pluginCookie[plugin.getName()]) continue;
|
||||
if (typeof plugin.onMessage === "function") {
|
||||
try { plugin.onMessage(); }
|
||||
catch (err) { Utils.err("Plugins", "Unable to fire onMessage for " + plugin.getName() + ".", err); }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
PluginModule.prototype.channelSwitch = function () {
|
||||
const plugins = Object.keys(bdplugins);
|
||||
for (let i = 0; i < plugins.length; i++) {
|
||||
const plugin = bdplugins[plugins[i]].plugin;
|
||||
if (!pluginCookie[plugin.getName()]) continue;
|
||||
if (typeof plugin.onSwitch === "function") {
|
||||
try { plugin.onSwitch(); }
|
||||
catch (err) { Utils.err("Plugins", "Unable to fire onSwitch for " + plugin.getName() + ".", err); }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
PluginModule.prototype.rawObserver = function(e) {
|
||||
const plugins = Object.keys(bdplugins);
|
||||
for (let i = 0; i < plugins.length; i++) {
|
||||
const plugin = bdplugins[plugins[i]].plugin;
|
||||
if (!pluginCookie[plugin.getName()]) continue;
|
||||
if (typeof plugin.observer === "function") {
|
||||
try { plugin.observer(e); }
|
||||
catch (err) { Utils.err("Plugins", "Unable to fire observer for " + plugin.getName() + ".", err); }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default new PluginModule();
|
|
@ -0,0 +1,80 @@
|
|||
import {settingsCookie} from "../0globals";
|
||||
import BDV2 from "./v2";
|
||||
import webpackModules from "./webpackModules";
|
||||
import Utils from "./utils";
|
||||
|
||||
import V2C_PublicServers from "../ui/publicServers";
|
||||
import Layer from "../ui/layer";
|
||||
|
||||
export default new class V2_PublicServers {
|
||||
|
||||
constructor() {
|
||||
this._appendButton = this._appendButton.bind(this);
|
||||
}
|
||||
|
||||
get component() {
|
||||
return BDV2.react.createElement(Layer, {rootId: "pubslayerroot", id: "pubslayer", children: BDV2.react.createElement(V2C_PublicServers, {rootId: "pubslayerroot"})});
|
||||
}
|
||||
|
||||
get root() {
|
||||
const _root = document.getElementById("pubslayerroot");
|
||||
if (!_root) {
|
||||
if (!this.injectRoot()) return null;
|
||||
return this.root;
|
||||
}
|
||||
return _root;
|
||||
}
|
||||
|
||||
injectRoot() {
|
||||
if (!$(".layers, .layers-3iHuyZ").length) return false;
|
||||
$(".layers, .layers-3iHuyZ").append($("<div/>", {
|
||||
id: "pubslayerroot"
|
||||
}));
|
||||
return true;
|
||||
}
|
||||
|
||||
render() {
|
||||
// BdApi.alert("Broken", "Sorry but the Public Servers modules is currently broken, I recommend disabling this feature for now.");
|
||||
const root = this.root;
|
||||
if (!root) {
|
||||
console.log("FAILED TO LOCATE ROOT: .layers");
|
||||
return;
|
||||
}
|
||||
BDV2.reactDom.render(this.component, root);
|
||||
}
|
||||
|
||||
get button() {
|
||||
const btn = $("<div/>", {
|
||||
"class": BDV2.guildClasses.listItem,
|
||||
"id": "bd-pub-li",
|
||||
"style": settingsCookie["bda-gs-1"] ? "" : "display: none;"
|
||||
}).append($("<div/>", {
|
||||
"class": "wrapper-25eVIn " + BDV2.guildClasses.circleButtonMask,
|
||||
"text": "public",
|
||||
"id": "bd-pub-button",
|
||||
"click": () => { this.render(); }
|
||||
}));
|
||||
|
||||
return btn;
|
||||
}
|
||||
|
||||
_appendButton() {
|
||||
if ($("#bd-pub-li").length) return;
|
||||
const wrapper = BDV2.guildClasses.wrapper.split(" ")[0];
|
||||
const guilds = $(`.${wrapper} .scroller-2FKFPG >:first-child`);
|
||||
guilds.after(this.button);
|
||||
}
|
||||
|
||||
addButton() {
|
||||
if (this.guildPatch) return;
|
||||
const GuildList = webpackModules.findByDisplayName("Guilds");
|
||||
this.guildPatch = Utils.monkeyPatch(GuildList.prototype, "render", {after: this._appendButton});
|
||||
this._appendButton();
|
||||
}
|
||||
|
||||
removeButton() {
|
||||
this.guildPatch();
|
||||
delete this.guildPatch;
|
||||
$("#bd-pub-li").remove();
|
||||
}
|
||||
};
|
|
@ -0,0 +1,195 @@
|
|||
import {settingsCookie, bdEmotes} from "../0globals";
|
||||
import DataStore from "./dataStore";
|
||||
import BDV2 from "./v2";
|
||||
import Utils from "./utils";
|
||||
|
||||
function QuickEmoteMenu() {
|
||||
|
||||
}
|
||||
|
||||
QuickEmoteMenu.prototype.init = function() {
|
||||
this.initialized = true;
|
||||
$(document).on("mousedown", function(e) {
|
||||
if (e.target.id != "rmenu") $("#rmenu").remove();
|
||||
});
|
||||
this.favoriteEmotes = {};
|
||||
const fe = DataStore.getBDData("bdfavemotes");
|
||||
if (fe !== "" && fe !== null) {
|
||||
this.favoriteEmotes = JSON.parse(atob(fe));
|
||||
}
|
||||
|
||||
let qmeHeader = "";
|
||||
qmeHeader += "<div id=\"bda-qem\">";
|
||||
qmeHeader += " <button class=\"active\" id=\"bda-qem-twitch\" onclick='quickEmoteMenu.switchHandler(this); return false;'>Twitch</button>";
|
||||
qmeHeader += " <button id=\"bda-qem-favourite\" onclick='quickEmoteMenu.switchHandler(this); return false;'>Favourite</button>";
|
||||
qmeHeader += " <button id=\"bda-qem-emojis\" onclick='quickEmoteMenu.switchHandler(this); return false;'>Emojis</buttond>";
|
||||
qmeHeader += "</div>";
|
||||
this.qmeHeader = qmeHeader;
|
||||
|
||||
let teContainer = "";
|
||||
teContainer += "<div id=\"bda-qem-twitch-container\">";
|
||||
teContainer += " <div class=\"scroller-wrap scrollerWrap-2lJEkd fade\">";
|
||||
teContainer += " <div class=\"scroller scroller-2FKFPG\">";
|
||||
teContainer += " <div class=\"emote-menu-inner\">";
|
||||
let url = "";
|
||||
for (const emote in bdEmotes.TwitchGlobal) {
|
||||
if (bdEmotes.TwitchGlobal.hasOwnProperty(emote)) {
|
||||
url = bdEmotes.TwitchGlobal[emote];
|
||||
teContainer += "<div class=\"emote-container\">";
|
||||
teContainer += " <img class=\"emote-icon\" alt=\"\" src=\"" + url + "\" title=\"" + emote + "\">";
|
||||
teContainer += " </img>";
|
||||
teContainer += "</div>";
|
||||
}
|
||||
}
|
||||
teContainer += " </div>";
|
||||
teContainer += " </div>";
|
||||
teContainer += " </div>";
|
||||
teContainer += "</div>";
|
||||
this.teContainer = teContainer;
|
||||
|
||||
let faContainer = "";
|
||||
faContainer += "<div id=\"bda-qem-favourite-container\">";
|
||||
faContainer += " <div class=\"scroller-wrap scrollerWrap-2lJEkd fade\">";
|
||||
faContainer += " <div class=\"scroller scroller-2FKFPG\">";
|
||||
faContainer += " <div class=\"emote-menu-inner\">";
|
||||
for (const emote in this.favoriteEmotes) {
|
||||
url = this.favoriteEmotes[emote];
|
||||
faContainer += "<div class=\"emote-container\">";
|
||||
faContainer += " <img class=\"emote-icon\" alt=\"\" src=\"" + url + "\" title=\"" + emote + "\" oncontextmenu='quickEmoteMenu.favContext(event, this);'>";
|
||||
faContainer += " </img>";
|
||||
faContainer += "</div>";
|
||||
}
|
||||
faContainer += " </div>";
|
||||
faContainer += " </div>";
|
||||
faContainer += " </div>";
|
||||
faContainer += "</div>";
|
||||
this.faContainer = faContainer;
|
||||
};
|
||||
|
||||
QuickEmoteMenu.prototype.favContext = function(e, em) {
|
||||
e.stopPropagation();
|
||||
const menu = $("<div>", {"id": "removemenu", "data-emoteid": $(em).prop("title"), "text": "Remove", "class": "bd-context-menu context-menu theme-dark"});
|
||||
menu.css({
|
||||
top: e.pageY - $("#bda-qem-favourite-container").offset().top,
|
||||
left: e.pageX - $("#bda-qem-favourite-container").offset().left
|
||||
});
|
||||
$(em).parent().append(menu);
|
||||
const self = this;
|
||||
menu.on("click", function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
$(this).remove();
|
||||
|
||||
delete self.favoriteEmotes[$(this).data("emoteid")];
|
||||
self.updateFavorites();
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
};
|
||||
|
||||
QuickEmoteMenu.prototype.switchHandler = function(e) {
|
||||
this.switchQem($(e).attr("id"));
|
||||
};
|
||||
|
||||
QuickEmoteMenu.prototype.switchQem = function(id) {
|
||||
const twitch = $("#bda-qem-twitch");
|
||||
const fav = $("#bda-qem-favourite");
|
||||
const emojis = $("#bda-qem-emojis");
|
||||
twitch.removeClass("active");
|
||||
fav.removeClass("active");
|
||||
emojis.removeClass("active");
|
||||
|
||||
$(".emojiPicker-3m1S-j").hide();
|
||||
$("#bda-qem-favourite-container").hide();
|
||||
$("#bda-qem-twitch-container").hide();
|
||||
|
||||
switch (id) {
|
||||
case "bda-qem-twitch":
|
||||
twitch.addClass("active");
|
||||
$("#bda-qem-twitch-container").show();
|
||||
break;
|
||||
case "bda-qem-favourite":
|
||||
fav.addClass("active");
|
||||
$("#bda-qem-favourite-container").show();
|
||||
break;
|
||||
case "bda-qem-emojis":
|
||||
emojis.addClass("active");
|
||||
$(".emojiPicker-3m1S-j").show();
|
||||
$(".emojiPicker-3m1S-j input").focus();
|
||||
break;
|
||||
}
|
||||
this.lastTab = id;
|
||||
|
||||
const emoteIcon = $(".emote-icon");
|
||||
emoteIcon.off();
|
||||
emoteIcon.on("click", function () {
|
||||
const emote = $(this).attr("title");
|
||||
const newTextarea = document.querySelector(`.${BDV2.slateEditorClasses.slateTextArea.split(" ")[0]}`);
|
||||
if (newTextarea) {
|
||||
const instance = BDV2.getInternalInstance(newTextarea);
|
||||
const insert = Utils.getNestedProp(instance, "memoizedProps.children.props.editor.insertText");
|
||||
if (insert) insert(` ${emote} `);
|
||||
}
|
||||
else {
|
||||
const ta = Utils.getTextArea();
|
||||
Utils.insertText(ta[0], ta.val().slice(-1) == " " ? ta.val() + emote : ta.val() + " " + emote);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
QuickEmoteMenu.prototype.obsCallback = function (elem) {
|
||||
if (!this.initialized) return;
|
||||
const e = $(elem);
|
||||
if (!settingsCookie["bda-es-9"]) {
|
||||
e.addClass("bda-qme-hidden");
|
||||
}
|
||||
else {
|
||||
e.removeClass("bda-qme-hidden");
|
||||
}
|
||||
|
||||
if (!settingsCookie["bda-es-0"]) return;
|
||||
|
||||
e.prepend(this.qmeHeader);
|
||||
e.append(this.teContainer);
|
||||
e.append(this.faContainer);
|
||||
|
||||
if (this.lastTab == undefined) {
|
||||
this.lastTab = "bda-qem-emojis";
|
||||
}
|
||||
this.switchQem(this.lastTab);
|
||||
};
|
||||
|
||||
QuickEmoteMenu.prototype.favorite = function (name, url) {
|
||||
|
||||
if (!this.favoriteEmotes.hasOwnProperty(name)) {
|
||||
this.favoriteEmotes[name] = url;
|
||||
}
|
||||
|
||||
this.updateFavorites();
|
||||
};
|
||||
|
||||
QuickEmoteMenu.prototype.updateFavorites = function () {
|
||||
|
||||
let faContainer = "";
|
||||
faContainer += "<div id=\"bda-qem-favourite-container\">";
|
||||
faContainer += " <div class=\"scroller-wrap scrollerWrap-2lJEkd fade\">";
|
||||
faContainer += " <div class=\"scroller scroller-2FKFPG\">";
|
||||
faContainer += " <div class=\"emote-menu-inner\">";
|
||||
for (const emote in this.favoriteEmotes) {
|
||||
const url = this.favoriteEmotes[emote];
|
||||
faContainer += "<div class=\"emote-container\">";
|
||||
faContainer += " <img class=\"emote-icon\" alt=\"\" src=\"" + url + "\" title=\"" + emote + "\" oncontextmenu=\"quickEmoteMenu.favContext(event, this);\">";
|
||||
faContainer += " </img>";
|
||||
faContainer += "</div>";
|
||||
}
|
||||
faContainer += " </div>";
|
||||
faContainer += " </div>";
|
||||
faContainer += " </div>";
|
||||
faContainer += "</div>";
|
||||
this.faContainer = faContainer;
|
||||
|
||||
$("#bda-qem-favourite-container").replaceWith(faContainer);
|
||||
DataStore.setBDData("bdfavemotes", btoa(JSON.stringify(this.favoriteEmotes)));
|
||||
};
|
||||
|
||||
export default new QuickEmoteMenu();
|
|
@ -0,0 +1,363 @@
|
|||
import {settings, settingsCookie, bdplugins, bdthemes} from "../0globals";
|
||||
import DataStore from "./dataStore";
|
||||
import V2_SettingsPanel_Sidebar from "./settingsPanelSidebar";
|
||||
import Utils from "./utils";
|
||||
import BDV2 from "./v2";
|
||||
import ContentManager from "./contentManager";
|
||||
import BDEvents from "./bdEvents";
|
||||
import pluginModule from "./pluginModule";
|
||||
import themeModule from "./themeModule";
|
||||
import coloredText from "./coloredText";
|
||||
import tfHour from "./24hour";
|
||||
|
||||
import publicServersModule from "./publicServers";
|
||||
import voiceMode from "./voiceMode";
|
||||
import emoteModule from "./emoteModule";
|
||||
import ClassNormalizer from "./classNormalizer";
|
||||
import dMode from "./devMode";
|
||||
import quickEmoteMenu from "./quickEmoteMenu";
|
||||
|
||||
import Tools from "../ui/tools";
|
||||
import ReloadIcon from "../ui/reloadIcon";
|
||||
import TooltipWrap from "../ui/tooltipWrap";
|
||||
import Scroller from "../ui/scroller";
|
||||
import List from "../ui/list";
|
||||
import PluginCard from "../ui/pluginCard";
|
||||
import ThemeCard from "../ui/themeCard";
|
||||
import SectionedSettingsPanel from "../ui/sectionedSettingsPanel";
|
||||
import SettingsPanel from "../ui/settingsPanel";
|
||||
import CssEditor from "../ui/cssEditor";
|
||||
import ContentColumn from "../ui/contentColumn";
|
||||
import ErrorBoundary from "../ui/errorBoundary";
|
||||
|
||||
import CardList from "../ui/addonlist";
|
||||
|
||||
export default new class V2_SettingsPanel {
|
||||
|
||||
constructor() {
|
||||
const self = this;
|
||||
self.sideBarOnClick = self.sideBarOnClick.bind(self);
|
||||
self.onChange = self.onChange.bind(self);
|
||||
self.updateSettings = this.updateSettings.bind(self);
|
||||
self.sidebar = new V2_SettingsPanel_Sidebar(self.sideBarOnClick);
|
||||
this.buildPluginProps = this.buildPluginProps.bind(this);
|
||||
}
|
||||
|
||||
get root() {
|
||||
const _root = $("#bd-settingspane-container");
|
||||
if (!_root.length) {
|
||||
if (!this.injectRoot()) return null;
|
||||
return this.root;
|
||||
}
|
||||
return _root[0];
|
||||
}
|
||||
|
||||
injectRoot() {
|
||||
if (!$(".layer-3QrUeG .standardSidebarView-3F1I7i, .layer-3QrUeG .ui-standard-sidebar-view").length) return false;
|
||||
const root = $("<div/>", {
|
||||
"class": "contentRegion-3nDuYy content-region",
|
||||
"id": "bd-settingspane-container"
|
||||
});
|
||||
$(".layer-3QrUeG .standardSidebarView-3F1I7i, .layer-3QrUeG .ui-standard-sidebar-view").append(root);
|
||||
|
||||
Utils.onRemoved(root[0], () => {
|
||||
BDV2.reactDom.unmountComponentAtNode(root[0]);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
get coreSettings() {
|
||||
const settings = this.getSettings("core");
|
||||
const categories = [...new Set(settings.map(s => s.category))];
|
||||
const sections = categories.map(c => {return {title: c, settings: settings.filter(s => s.category == c)};});
|
||||
return sections;
|
||||
}
|
||||
|
||||
get emoteSettings() {
|
||||
return this.getSettings("emote");
|
||||
}
|
||||
getSettings(category) {
|
||||
return Object.keys(settings).reduce((arr, key) => {
|
||||
const setting = settings[key];
|
||||
if (setting.cat === category && setting.implemented && !setting.hidden) {
|
||||
setting.text = key;
|
||||
arr.push(setting);
|
||||
}
|
||||
return arr;
|
||||
}, []);
|
||||
}
|
||||
|
||||
sideBarOnClick(id) {
|
||||
const self = this;
|
||||
$(".contentRegion-3nDuYy, .content-region").first().hide();
|
||||
$(self.root).show();
|
||||
switch (id) {
|
||||
case "core":
|
||||
self.renderCoreSettings();
|
||||
break;
|
||||
case "emotes":
|
||||
self.renderEmoteSettings();
|
||||
break;
|
||||
case "customcss":
|
||||
self.renderCustomCssEditor();
|
||||
break;
|
||||
case "plugins":
|
||||
self.renderPluginPane();
|
||||
break;
|
||||
case "themes":
|
||||
self.renderThemePane();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onClick() {}
|
||||
|
||||
onChange(id, checked) {
|
||||
this.updateSettings(id, checked);
|
||||
}
|
||||
|
||||
updateSettings(id, enabled) {
|
||||
settingsCookie[id] = enabled;
|
||||
|
||||
// if (id == "bda-gs-b") {
|
||||
// if (enabled) $("body").addClass("bd-blue");
|
||||
// else $("body").removeClass("bd-blue");
|
||||
// }
|
||||
|
||||
if (id == "bda-gs-2") {
|
||||
if (enabled) $("body").addClass("bd-minimal");
|
||||
else $("body").removeClass("bd-minimal");
|
||||
}
|
||||
|
||||
if (id == "bda-gs-3") {
|
||||
if (enabled) $("body").addClass("bd-minimal-chan");
|
||||
else $("body").removeClass("bd-minimal-chan");
|
||||
}
|
||||
|
||||
if (id == "bda-gs-1") {
|
||||
if (enabled) publicServersModule.addButton();
|
||||
else publicServersModule.removeButton();
|
||||
}
|
||||
|
||||
if (id == "bda-gs-4") {
|
||||
if (enabled) voiceMode.enable();
|
||||
else voiceMode.disable();
|
||||
}
|
||||
|
||||
if (id == "bda-gs-5") {
|
||||
if (enabled) $("#app-mount").addClass("bda-dark");
|
||||
else $("#app-mount").removeClass("bda-dark");
|
||||
}
|
||||
|
||||
if (enabled && id == "bda-gs-6") tfHour.inject24Hour();
|
||||
|
||||
if (id == "bda-gs-7") {
|
||||
if (enabled) coloredText.injectColoredText();
|
||||
else coloredText.removeColoredText();
|
||||
}
|
||||
|
||||
if (id == "bda-es-4") {
|
||||
if (enabled) emoteModule.autoCapitalize();
|
||||
else emoteModule.disableAutoCapitalize();
|
||||
}
|
||||
|
||||
if (id == "fork-ps-4") {
|
||||
if (enabled) ClassNormalizer.start();
|
||||
else ClassNormalizer.stop();
|
||||
}
|
||||
|
||||
if (id == "fork-ps-5") {
|
||||
if (enabled) {
|
||||
ContentManager.watchContent("plugin");
|
||||
ContentManager.watchContent("theme");
|
||||
}
|
||||
else {
|
||||
ContentManager.unwatchContent("plugin");
|
||||
ContentManager.unwatchContent("theme");
|
||||
}
|
||||
}
|
||||
|
||||
if (id == "fork-wp-1") {
|
||||
// BdApi.setWindowPreference("transparent", enabled);
|
||||
// if (enabled) BdApi.setWindowPreference("backgroundColor", null);
|
||||
// else BdApi.setWindowPreference("backgroundColor", "#2f3136");
|
||||
}
|
||||
|
||||
/*if (_c["fork-wp-2"]) {
|
||||
const current = BdApi.getWindowPreference("frame");
|
||||
if (current != _c["fork-wp-2"]) BdApi.setWindowPreference("frame", _c["fork-wp-2"]);
|
||||
}*/
|
||||
|
||||
|
||||
if (id == "bda-gs-8") {
|
||||
if (enabled) dMode.enable(settingsCookie["fork-dm-1"]);
|
||||
else dMode.disable();
|
||||
}
|
||||
|
||||
if (id == "fork-dm-1") {
|
||||
if (settingsCookie["bda-gs-8"]) dMode.enable(enabled);
|
||||
}
|
||||
|
||||
this.saveSettings();
|
||||
}
|
||||
|
||||
async initializeSettings() {
|
||||
// if (settingsCookie["bda-gs-b"]) $("body").addClass("bd-blue");
|
||||
if (settingsCookie["bda-gs-2"]) $("body").addClass("bd-minimal");
|
||||
if (settingsCookie["bda-gs-3"]) $("body").addClass("bd-minimal-chan");
|
||||
if (settingsCookie["bda-gs-1"]) publicServersModule.addButton();
|
||||
if (settingsCookie["bda-gs-4"]) voiceMode.enable();
|
||||
if (settingsCookie["bda-gs-5"]) $("#app-mount").addClass("bda-dark");
|
||||
if (settingsCookie["bda-gs-6"]) tfHour.inject24Hour();
|
||||
if (settingsCookie["bda-gs-7"]) coloredText.injectColoredText();
|
||||
if (settingsCookie["bda-es-4"]) emoteModule.autoCapitalize();
|
||||
if (settingsCookie["fork-ps-4"]) ClassNormalizer.start();
|
||||
|
||||
if (settingsCookie["fork-ps-5"]) {
|
||||
ContentManager.watchContent("plugin");
|
||||
ContentManager.watchContent("theme");
|
||||
}
|
||||
|
||||
if (settingsCookie["bda-gs-8"]) dMode.enable(settingsCookie["fork-dm-1"]);
|
||||
|
||||
this.saveSettings();
|
||||
}
|
||||
|
||||
saveSettings() {
|
||||
DataStore.setSettingGroup("settings", settingsCookie);
|
||||
}
|
||||
|
||||
loadSettings() {
|
||||
Object.assign(settingsCookie, DataStore.getSettingGroup("settings"));
|
||||
}
|
||||
|
||||
renderSidebar() {
|
||||
const self = this;
|
||||
$("[class*='side-'] > [class*='item-']").off("click.v2settingspanel").on("click.v2settingspanel", () => {
|
||||
BDV2.reactDom.unmountComponentAtNode(self.root);
|
||||
$(self.root).hide();
|
||||
$(".contentRegion-3nDuYy, .content-region").first().show();
|
||||
});
|
||||
self.sidebar.render();
|
||||
}
|
||||
|
||||
get coreComponent() {
|
||||
return BDV2.react.createElement(Scroller, {contentColumn: true, fade: true, dark: true, children: [
|
||||
BDV2.react.createElement(SectionedSettingsPanel, {key: "cspanel", onChange: this.onChange, sections: this.coreSettings}),
|
||||
BDV2.react.createElement(Tools, {key: "tools"})
|
||||
]});
|
||||
}
|
||||
|
||||
get emoteComponent() {
|
||||
return BDV2.react.createElement(Scroller, {
|
||||
contentColumn: true, fade: true, dark: true, children: [
|
||||
BDV2.react.createElement(SettingsPanel, {key: "espanel", title: "Emote Settings", onChange: this.onChange, settings: this.emoteSettings, button: {
|
||||
title: "Clear Emote Cache",
|
||||
onClick: () => { emoteModule.clearEmoteData(); emoteModule.init(); quickEmoteMenu.init(); }
|
||||
}}),
|
||||
BDV2.react.createElement(Tools, {key: "tools"})
|
||||
]});
|
||||
}
|
||||
|
||||
get customCssComponent() {
|
||||
return BDV2.react.createElement(Scroller, {contentColumn: true, fade: true, dark: true, children: [BDV2.react.createElement(CssEditor, {key: "csseditor"}), BDV2.react.createElement(Tools, {key: "tools"})]});
|
||||
}
|
||||
|
||||
contentComponent(type) {
|
||||
const componentElement = this.getAddonList(type);
|
||||
const prefix = type.replace("s", "");
|
||||
const settingsList = this;
|
||||
class ContentList extends BDV2.react.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
BDEvents.on(`${prefix}-reloaded`, this.onChange);
|
||||
BDEvents.on(`${prefix}-loaded`, this.onChange);
|
||||
BDEvents.on(`${prefix}-unloaded`, this.onChange);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
BDEvents.off(`${prefix}-reloaded`, this.onChange);
|
||||
BDEvents.off(`${prefix}-loaded`, this.onChange);
|
||||
BDEvents.off(`${prefix}-unloaded`, this.onChange);
|
||||
}
|
||||
|
||||
onChange() {
|
||||
settingsList.sideBarOnClick(type);
|
||||
}
|
||||
|
||||
render() {return componentElement;}
|
||||
}
|
||||
return BDV2.react.createElement(ContentList);
|
||||
}
|
||||
|
||||
getString(value) {
|
||||
if (!value) return "???";
|
||||
return typeof value == "string" ? value : value.toString();
|
||||
}
|
||||
|
||||
buildPluginProps(key) {
|
||||
const plugin = bdplugins[key].plugin;
|
||||
return Object.assign({}, bdplugins[key], {
|
||||
name: this.getString(plugin.getName()),
|
||||
author: this.getString(plugin.getAuthor()),
|
||||
description: this.getString(plugin.getDescription()),
|
||||
version: this.getString(plugin.getVersion()),
|
||||
getSettingsPanel: plugin.getSettingsPanel && plugin.getSettingsPanel.bind(plugin)
|
||||
});
|
||||
}
|
||||
|
||||
getAddonList(type) {
|
||||
const isPlugins = type === "plugins";
|
||||
const list = isPlugins ? Object.keys(bdplugins).map(this.buildPluginProps) : Object.values(bdthemes);
|
||||
return BDV2.react.createElement(CardList, {type, list});
|
||||
}
|
||||
|
||||
renderCoreSettings() {
|
||||
const root = this.root;
|
||||
if (!root) {
|
||||
console.log("FAILED TO LOCATE ROOT: .layer-3QrUeG .standardSidebarView-3F1I7i");
|
||||
return;
|
||||
}
|
||||
BDV2.reactDom.render(this.coreComponent, root);
|
||||
}
|
||||
|
||||
renderEmoteSettings() {
|
||||
const root = this.root;
|
||||
if (!root) {
|
||||
console.log("FAILED TO LOCATE ROOT: .layer-3QrUeG .standardSidebarView-3F1I7i");
|
||||
return;
|
||||
}
|
||||
BDV2.reactDom.render(this.emoteComponent, root);
|
||||
}
|
||||
|
||||
renderCustomCssEditor() {
|
||||
const root = this.root;
|
||||
if (!root) {
|
||||
console.log("FAILED TO LOCATE ROOT: .layer-3QrUeG .standardSidebarView-3F1I7i");
|
||||
return;
|
||||
}
|
||||
BDV2.reactDom.render(this.customCssComponent, root);
|
||||
}
|
||||
|
||||
renderPluginPane() {
|
||||
const root = this.root;
|
||||
if (!root) {
|
||||
console.log("FAILED TO LOCATE ROOT: .layer-3QrUeG .standardSidebarView-3F1I7i");
|
||||
return;
|
||||
}
|
||||
BDV2.reactDom.render(this.contentComponent("plugins"), root);
|
||||
}
|
||||
|
||||
renderThemePane() {
|
||||
const root = this.root;
|
||||
if (!root) {
|
||||
console.log("FAILED TO LOCATE ROOT: .layer-3QrUeG .standardSidebarView-3F1I7i");
|
||||
return;
|
||||
}
|
||||
BDV2.reactDom.render(this.contentComponent("themes"), root);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
import Utils from "./utils";
|
||||
import BDV2 from "./v2";
|
||||
|
||||
import SideBar from "../ui/sidebar";
|
||||
|
||||
export default class V2_SettingsPanel_Sidebar {
|
||||
|
||||
constructor(onClick) {
|
||||
this.onClick = onClick;
|
||||
}
|
||||
|
||||
get items() {
|
||||
return [{text: "Settings", id: "core"}, {text: "Emotes", id: "emotes"}, {text: "Plugins", id: "plugins"}, {text: "Themes", id: "themes"}, {text: "Custom CSS", id: "customcss"}];
|
||||
}
|
||||
|
||||
get component() {
|
||||
return BDV2.react.createElement("span", null, BDV2.react.createElement(SideBar, {onClick: this.onClick, headerText: "Bandaged BD", items: this.items}));
|
||||
}
|
||||
|
||||
get root() {
|
||||
const _root = $("#bd-settings-sidebar");
|
||||
if (!_root.length) {
|
||||
if (!this.injectRoot()) return null;
|
||||
return this.root;
|
||||
}
|
||||
return _root[0];
|
||||
}
|
||||
|
||||
injectRoot() {
|
||||
const changeLog = $("[class*='side-'] > [class*='item-']:not([class*=Danger])").last();
|
||||
if (!changeLog.length) return false;
|
||||
$("<span/>", {id: "bd-settings-sidebar"}).insertBefore(changeLog.prev());
|
||||
return true;
|
||||
}
|
||||
|
||||
render() {
|
||||
const root = this.root;
|
||||
if (!root) {
|
||||
console.log("FAILED TO LOCATE ROOT: [class*='side-'] > [class*='item-']:not([class*=Danger])");
|
||||
return;
|
||||
}
|
||||
BDV2.reactDom.render(this.component, root);
|
||||
Utils.onRemoved(root, () => {
|
||||
BDV2.reactDom.unmountComponentAtNode(root);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
import {bdthemeErrors, themeCookie, settingsCookie, bdthemes} from "../0globals";
|
||||
import ContentManager from "./contentManager";
|
||||
import DataStore from "./dataStore";
|
||||
import BDEvents from "./bdEvents";
|
||||
import Utils from "./utils";
|
||||
|
||||
function ThemeModule() {
|
||||
|
||||
}
|
||||
|
||||
ThemeModule.prototype.loadThemes = function () {
|
||||
this.loadThemeData();
|
||||
bdthemeErrors.concat(ContentManager.loadThemes());
|
||||
const themes = Object.keys(bdthemes);
|
||||
|
||||
for (let i = 0; i < themes.length; i++) {
|
||||
const theme = bdthemes[themes[i]];
|
||||
if (!themeCookie[theme.name]) themeCookie[theme.name] = false;
|
||||
if (themeCookie[theme.name]) $("head").append($("<style>", {id: theme.id, text: unescape(theme.css)}));
|
||||
}
|
||||
for (const theme in themeCookie) {
|
||||
if (!bdthemes[theme]) delete themeCookie[theme];
|
||||
}
|
||||
this.saveThemeData();
|
||||
// if (settingsCookie["fork-ps-5"]) ContentManager.watchContent("theme");
|
||||
};
|
||||
|
||||
ThemeModule.prototype.enableTheme = function(name, reload = false) {
|
||||
themeCookie[name] = true;
|
||||
this.saveThemeData();
|
||||
const theme = bdthemes[name];
|
||||
$("head").append($("<style>", {id: theme.id, text: unescape(theme.css)}));
|
||||
if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been applied.`);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.enable = function (name, reload = false) {
|
||||
return this.enableTheme(name, reload);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.disableTheme = function(name, reload = false) {
|
||||
themeCookie[name] = false;
|
||||
this.saveThemeData();
|
||||
const theme = bdthemes[name];
|
||||
$(`#${theme.id}`).remove();
|
||||
if (settingsCookie["fork-ps-2"] && !reload) Utils.showToast(`${theme.name} v${theme.version} has been disabled.`);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.disable = function (name, reload = false) {
|
||||
return this.disableTheme(name, reload);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.toggleTheme = function(theme) {
|
||||
if (themeCookie[theme]) this.disableTheme(theme);
|
||||
else this.enableTheme(theme);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.toggle = function (name, reload = false) {
|
||||
return this.toggleTheme(name, reload);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.loadTheme = function(filename) {
|
||||
const error = ContentManager.loadContent(filename, "theme");
|
||||
if (error) {
|
||||
if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]});
|
||||
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${filename} could not be loaded. It may not have been loaded.`, {type: "error"});
|
||||
return Utils.err("ContentManager", `${filename} could not be loaded.`, error);
|
||||
}
|
||||
const theme = Object.values(bdthemes).find(p => p.filename == filename);
|
||||
Utils.log("ContentManager", `${theme.name} v${theme.version} was loaded.`);
|
||||
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme.name} v${theme.version} was loaded.`, {type: "success"});
|
||||
BDEvents.dispatch("theme-loaded", theme.name);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.unloadTheme = function(filenameOrName) {
|
||||
const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
|
||||
if (!bdtheme) return;
|
||||
const theme = bdtheme.name;
|
||||
if (themeCookie[theme]) this.disableTheme(theme, true);
|
||||
const error = ContentManager.unloadContent(bdthemes[theme].filename, "theme");
|
||||
delete bdthemes[theme];
|
||||
if (error) {
|
||||
if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]});
|
||||
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} could not be unloaded. It may have not been loaded yet.`, {type: "error"});
|
||||
return Utils.err("ContentManager", `${theme} could not be unloaded. It may have not been loaded yet.`, error);
|
||||
}
|
||||
Utils.log("ContentManager", `${theme} was unloaded.`);
|
||||
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} was unloaded.`, {type: "success"});
|
||||
BDEvents.dispatch("theme-unloaded", theme);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.delete = function(filenameOrName) {
|
||||
const bdplugin = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
|
||||
if (!bdplugin) return;
|
||||
this.unloadTheme(bdplugin.filename);
|
||||
const fullPath = require("path").resolve(ContentManager.pluginsFolder, bdplugin.filename);
|
||||
require("fs").unlinkSync(fullPath);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.reloadTheme = function(filenameOrName) {
|
||||
const bdtheme = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
|
||||
if (!bdtheme) return this.loadTheme(filenameOrName);
|
||||
const theme = bdtheme.name;
|
||||
const error = ContentManager.reloadContent(bdthemes[theme].filename, "theme");
|
||||
if (themeCookie[theme]) this.disableTheme(theme, true), this.enableTheme(theme, true);
|
||||
if (error) {
|
||||
if (settingsCookie["fork-ps-1"]) Utils.showContentErrors({themes: [error]});
|
||||
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} could not be reloaded.`, {type: "error"});
|
||||
return Utils.err("ContentManager", `${theme} could not be reloaded.`, error);
|
||||
}
|
||||
Utils.log("ContentManager", `${theme} v${bdthemes[theme].version} was reloaded.`);
|
||||
if (settingsCookie["fork-ps-2"]) Utils.showToast(`${theme} v${bdthemes[theme].version} was reloaded.`, {type: "success"});
|
||||
BDEvents.dispatch("theme-reloaded", theme);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.reload = function(name) {
|
||||
return this.reloadTheme(name);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.edit = function(filenameOrName) {
|
||||
const bdplugin = Object.values(bdthemes).find(p => p.filename == filenameOrName) || bdthemes[filenameOrName];
|
||||
if (!bdplugin) return;
|
||||
const fullPath = require("path").resolve(ContentManager.themesFolder, bdplugin.filename);
|
||||
require("electron").shell.openItem(`${fullPath}`);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.updateThemeList = function() {
|
||||
const results = ContentManager.loadNewContent("theme");
|
||||
for (const filename of results.added) this.loadTheme(filename);
|
||||
for (const name of results.removed) this.unloadTheme(name);
|
||||
};
|
||||
|
||||
ThemeModule.prototype.loadThemeData = function() {
|
||||
const saved = DataStore.getSettingGroup("themes");
|
||||
if (saved) {
|
||||
Object.assign(themeCookie, saved);
|
||||
}
|
||||
};
|
||||
|
||||
ThemeModule.prototype.saveThemeData = function () {
|
||||
DataStore.setSettingGroup("themes", themeCookie);
|
||||
};
|
||||
|
||||
export default new ThemeModule();
|
|
@ -0,0 +1,301 @@
|
|||
export default class Utils {
|
||||
/** Document/window width */
|
||||
static get screenWidth() { return Math.max(document.documentElement.clientWidth, window.innerWidth || 0); }
|
||||
/** Document/window height */
|
||||
static get screenHeight() { return Math.max(document.documentElement.clientHeight, window.innerHeight || 0); }
|
||||
|
||||
static stripBOM(content) {
|
||||
if (content.charCodeAt(0) === 0xFEFF) {
|
||||
content = content.slice(1);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
static getTextArea() {
|
||||
return $(".channelTextArea-rNsIhG textarea");
|
||||
}
|
||||
|
||||
static insertText(textarea, text) {
|
||||
textarea.focus();
|
||||
textarea.selectionStart = 0;
|
||||
textarea.selectionEnd = textarea.value.length;
|
||||
document.execCommand("insertText", false, text);
|
||||
}
|
||||
|
||||
static injectCss(uri) {
|
||||
$("<link/>", {
|
||||
type: "text/css",
|
||||
rel: "stylesheet",
|
||||
href: uri
|
||||
}).appendTo($("head"));
|
||||
}
|
||||
|
||||
static injectJs(uri) {
|
||||
return new Promise(resolve => {
|
||||
$("<script/>", {
|
||||
type: "text/javascript",
|
||||
src: uri,
|
||||
onload: resolve
|
||||
}).appendTo($("body"));
|
||||
});
|
||||
}
|
||||
|
||||
static escapeID(id) {
|
||||
return id.replace(/^[^a-z]+|[^\w-]+/gi, "-");
|
||||
}
|
||||
|
||||
static log(moduleName, message) {
|
||||
console.log(`%c[BandagedBD]%c [${moduleName}]%c ${message}`, "color: #3a71c1; font-weight: 700;", "color: #3a71c1;", "");
|
||||
}
|
||||
|
||||
static warn(moduleName, message) {
|
||||
console.warn(`%c[BandagedBD]%c [${moduleName}]%c ${message}`, "color: #E8A400; font-weight: 700;", "color: #E8A400;", "");
|
||||
}
|
||||
|
||||
static err(moduleName, message, error) {
|
||||
console.log(`%c[BandagedBD]%c [${moduleName}]%c ${message}`, "color: red; font-weight: 700;", "color: red;", "");
|
||||
if (error) {
|
||||
console.groupCollapsed("%cError: " + error.message, "color: red;");
|
||||
console.error(error.stack);
|
||||
console.groupEnd();
|
||||
}
|
||||
}
|
||||
|
||||
static escape(s) {
|
||||
return s.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
||||
}
|
||||
|
||||
static testJSON(data) {
|
||||
try {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static isEmpty(obj) {
|
||||
if (obj == null || obj == undefined || obj == "") return true;
|
||||
if (typeof(obj) !== "object") return false;
|
||||
if (Array.isArray(obj)) return obj.length == 0;
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static suppressErrors(method, message) {
|
||||
return (...params) => {
|
||||
try { return method(...params); }
|
||||
catch (e) { this.err("SuppressedError", "Error occurred in " + message, e); }
|
||||
};
|
||||
}
|
||||
|
||||
static monkeyPatch(what, methodName, options) {
|
||||
const {before, after, instead, once = false, silent = false, force = false} = options;
|
||||
const displayName = options.displayName || what.displayName || what[methodName].displayName || what.name || what.constructor.displayName || what.constructor.name;
|
||||
if (!silent) console.log("patch", methodName, "of", displayName); // eslint-disable-line no-console
|
||||
if (!what[methodName]) {
|
||||
if (force) what[methodName] = function() {};
|
||||
else return console.error(methodName, "does not exist for", displayName); // eslint-disable-line no-console
|
||||
}
|
||||
const origMethod = what[methodName];
|
||||
const cancel = () => {
|
||||
if (!silent) console.log("unpatch", methodName, "of", displayName); // eslint-disable-line no-console
|
||||
what[methodName] = origMethod;
|
||||
};
|
||||
what[methodName] = function() {
|
||||
const data = {
|
||||
thisObject: this,
|
||||
methodArguments: arguments,
|
||||
cancelPatch: cancel,
|
||||
originalMethod: origMethod,
|
||||
callOriginalMethod: () => data.returnValue = data.originalMethod.apply(data.thisObject, data.methodArguments)
|
||||
};
|
||||
if (instead) {
|
||||
const tempRet = Utils.suppressErrors(instead, "`instead` callback of " + what[methodName].displayName)(data);
|
||||
if (tempRet !== undefined) data.returnValue = tempRet;
|
||||
}
|
||||
else {
|
||||
if (before) Utils.suppressErrors(before, "`before` callback of " + what[methodName].displayName)(data);
|
||||
data.callOriginalMethod();
|
||||
if (after) Utils.suppressErrors(after, "`after` callback of " + what[methodName].displayName)(data);
|
||||
}
|
||||
if (once) cancel();
|
||||
return data.returnValue;
|
||||
};
|
||||
Object.assign(what[methodName], origMethod);
|
||||
what[methodName].__monkeyPatched = true;
|
||||
what[methodName].displayName = displayName;
|
||||
if (!what[methodName].__originalMethod) {
|
||||
what[methodName].__originalMethod = origMethod;
|
||||
what[methodName].toString = function() {return origMethod.toString();};
|
||||
}
|
||||
return cancel;
|
||||
}
|
||||
|
||||
static onRemoved(node, callback) {
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
for (let m = 0; m < mutations.length; m++) {
|
||||
const mutation = mutations[m];
|
||||
const nodes = Array.from(mutation.removedNodes);
|
||||
const directMatch = nodes.indexOf(node) > -1;
|
||||
const parentMatch = nodes.some(parent => parent.contains(node));
|
||||
if (directMatch || parentMatch) {
|
||||
observer.disconnect();
|
||||
callback();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.body, {subtree: true, childList: true});
|
||||
}
|
||||
|
||||
static getNestedProp(obj, path) {
|
||||
return path.split(/\s?\.\s?/).reduce(function(obj, prop) {
|
||||
return obj && obj[prop];
|
||||
}, obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* This shows a toast similar to android towards the bottom of the screen.
|
||||
*
|
||||
* @param {string} content The string to show in the toast.
|
||||
* @param {object} options Options object. Optional parameter.
|
||||
* @param {string} options.type Changes the type of the toast stylistically and semantically. Choices: "", "info", "success", "danger"/"error", "warning"/"warn". Default: ""
|
||||
* @param {boolean} options.icon Determines whether the icon should show corresponding to the type. A toast without type will always have no icon. Default: true
|
||||
* @param {number} options.timeout Adjusts the time (in ms) the toast should be shown for before disappearing automatically. Default: 3000
|
||||
*/
|
||||
static showToast(content, options = {}) {
|
||||
// if (!bdConfig.deferLoaded) return;
|
||||
if (!document.querySelector(".bd-toasts")) {
|
||||
const toastWrapper = document.createElement("div");
|
||||
toastWrapper.classList.add("bd-toasts");
|
||||
const boundingElement = document.querySelector(".chat-3bRxxu form, #friends, .noChannel-Z1DQK7, .activityFeed-28jde9");
|
||||
toastWrapper.style.setProperty("left", boundingElement ? boundingElement.getBoundingClientRect().left + "px" : "0px");
|
||||
toastWrapper.style.setProperty("width", boundingElement ? boundingElement.offsetWidth + "px" : "100%");
|
||||
toastWrapper.style.setProperty("bottom", (document.querySelector(".chat-3bRxxu form") ? document.querySelector(".chat-3bRxxu form").offsetHeight : 80) + "px");
|
||||
document.querySelector(".app, .app-2rEoOp").appendChild(toastWrapper);
|
||||
}
|
||||
const {type = "", icon = true, timeout = 3000} = options;
|
||||
const toastElem = document.createElement("div");
|
||||
toastElem.classList.add("bd-toast");
|
||||
if (type) toastElem.classList.add("toast-" + type);
|
||||
if (type && icon) toastElem.classList.add("icon");
|
||||
toastElem.innerText = content;
|
||||
document.querySelector(".bd-toasts").appendChild(toastElem);
|
||||
setTimeout(() => {
|
||||
toastElem.classList.add("closing");
|
||||
setTimeout(() => {
|
||||
toastElem.remove();
|
||||
if (!document.querySelectorAll(".bd-toasts .bd-toast").length) document.querySelector(".bd-toasts").remove();
|
||||
}, 300);
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
static alert(title, content) {
|
||||
const modal = $(`<div class="bd-modal-wrapper theme-dark">
|
||||
<div class="bd-backdrop backdrop-1wrmKB"></div>
|
||||
<div class="bd-modal modal-1UGdnR">
|
||||
<div class="bd-modal-inner inner-1JeGVc">
|
||||
<div class="header header-1R_AjF">
|
||||
<div class="title">${title}</div>
|
||||
</div>
|
||||
<div class="bd-modal-body">
|
||||
<div class="scroller-wrap fade">
|
||||
<div class="scroller">
|
||||
${content}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer footer-2yfCgX footer-3rDWdC footer-2gL1pp">
|
||||
<button type="button">Okay</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`);
|
||||
modal.find(".footer button").on("click", () => {
|
||||
modal.addClass("closing");
|
||||
setTimeout(() => { modal.remove(); }, 300);
|
||||
});
|
||||
modal.find(".bd-backdrop").on("click", () => {
|
||||
modal.addClass("closing");
|
||||
setTimeout(() => { modal.remove(); }, 300);
|
||||
});
|
||||
modal.appendTo("#app-mount");
|
||||
}
|
||||
|
||||
static showContentErrors({plugins: pluginErrors = [], themes: themeErrors = []}) {
|
||||
if (!pluginErrors || !themeErrors) return;
|
||||
if (!pluginErrors.length && !themeErrors.length) return;
|
||||
const modal = $(`<div class="bd-modal-wrapper theme-dark">
|
||||
<div class="bd-backdrop backdrop-1wrmKB"></div>
|
||||
<div class="bd-modal bd-content-modal modal-1UGdnR">
|
||||
<div class="bd-modal-inner inner-1JeGVc">
|
||||
<div class="header header-1R_AjF"><div class="title">Content Errors</div></div>
|
||||
<div class="bd-modal-body">
|
||||
<div class="tab-bar-container">
|
||||
<div class="tab-bar TOP">
|
||||
<div class="tab-bar-item">Plugins</div>
|
||||
<div class="tab-bar-item">Themes</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-header">
|
||||
<div class="table-column column-name">Name</div>
|
||||
<div class="table-column column-message">Message</div>
|
||||
<div class="table-column column-error">Error</div>
|
||||
</div>
|
||||
<div class="scroller-wrap fade">
|
||||
<div class="scroller">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer footer-2yfCgX">
|
||||
<button type="button">Okay</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`);
|
||||
|
||||
function generateTab(errors) {
|
||||
const container = $(`<div class="errors">`);
|
||||
for (const err of errors) {
|
||||
const error = $(`<div class="error">
|
||||
<div class="table-column column-name">${err.name ? err.name : err.file}</div>
|
||||
<div class="table-column column-message">${err.message}</div>
|
||||
<div class="table-column column-error"><a class="error-link" href="">${err.error ? err.error.message : ""}</a></div>
|
||||
</div>`);
|
||||
container.append(error);
|
||||
if (err.error) {
|
||||
error.find("a").on("click", (e) => {
|
||||
e.preventDefault();
|
||||
Utils.err("ContentManager", `Error details for ${err.name ? err.name : err.file}.`, err.error);
|
||||
});
|
||||
}
|
||||
}
|
||||
return container;
|
||||
}
|
||||
|
||||
const tabs = [generateTab(pluginErrors), generateTab(themeErrors)];
|
||||
|
||||
modal.find(".tab-bar-item").on("click", (e) => {
|
||||
e.preventDefault();
|
||||
modal.find(".tab-bar-item").removeClass("selected");
|
||||
$(e.target).addClass("selected");
|
||||
modal.find(".scroller").empty().append(tabs[$(e.target).index()]);
|
||||
});
|
||||
|
||||
modal.find(".footer button").on("click", () => {
|
||||
modal.addClass("closing");
|
||||
setTimeout(() => { modal.remove(); }, 300);
|
||||
});
|
||||
modal.find(".bd-backdrop").on("click", () => {
|
||||
modal.addClass("closing");
|
||||
setTimeout(() => { modal.remove(); }, 300);
|
||||
});
|
||||
modal.appendTo("#app-mount");
|
||||
if (pluginErrors.length) modal.find(".tab-bar-item")[0].click();
|
||||
else modal.find(".tab-bar-item")[1].click();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
import {settings} from "../0globals";
|
||||
|
||||
export default new class V2 {
|
||||
|
||||
constructor() {
|
||||
this.editorDetached = false;
|
||||
this.WebpackModules = (() => {
|
||||
const req = webpackJsonp.push([[], {__extra_id__: (module, exports, req) => module.exports = req}, [["__extra_id__"]]]);
|
||||
delete req.m.__extra_id__;
|
||||
delete req.c.__extra_id__;
|
||||
|
||||
const protect = theModule => {
|
||||
if (theModule.remove && theModule.set && theModule.clear && theModule.get && !theModule.sort) return null;
|
||||
if (!theModule.getToken && !theModule.getEmail && !theModule.showToken) return theModule;
|
||||
return new Proxy(theModule, {
|
||||
get: function(obj, func) {
|
||||
if (func == "getToken") return () => "mfa.XCnbKzo0CLIqdJzBnL0D8PfDruqkJNHjwHXtr39UU3F8hHx43jojISyi5jdjO52e9_e9MjmafZFFpc-seOMa";
|
||||
if (func == "getEmail") return () => "puppet11112@gmail.com";
|
||||
if (func == "showToken") return () => true;
|
||||
return obj[func];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const find = (filter) => {
|
||||
for (const i in req.c) {
|
||||
if (req.c.hasOwnProperty(i)) {
|
||||
const m = req.c[i].exports;
|
||||
if (m && m.__esModule && m.default && filter(m.default)) return protect(m.default);
|
||||
if (m && filter(m)) return protect(m);
|
||||
}
|
||||
}
|
||||
// console.warn("Cannot find loaded module in cache");
|
||||
return null;
|
||||
};
|
||||
|
||||
const findAll = (filter) => {
|
||||
const modules = [];
|
||||
for (const i in req.c) {
|
||||
if (req.c.hasOwnProperty(i)) {
|
||||
const m = req.c[i].exports;
|
||||
if (m && m.__esModule && m.default && filter(m.default)) modules.push(protect(m.default));
|
||||
else if (m && filter(m)) modules.push(protect(m));
|
||||
}
|
||||
}
|
||||
return modules;
|
||||
};
|
||||
|
||||
const findByUniqueProperties = (propNames) => find(module => propNames.every(prop => module[prop] !== undefined));
|
||||
const findByPrototypes = (protoNames) => find(module => module.prototype && protoNames.every(protoProp => module.prototype[protoProp] !== undefined));
|
||||
const findByDisplayName = (displayName) => find(module => module.displayName === displayName);
|
||||
|
||||
return {find, findAll, findByUniqueProperties, findByPrototypes, findByDisplayName};
|
||||
})();
|
||||
|
||||
this.internal = {
|
||||
react: this.WebpackModules.findByUniqueProperties(["Component", "PureComponent", "Children", "createElement", "cloneElement"]),
|
||||
reactDom: this.WebpackModules.findByUniqueProperties(["findDOMNode"])
|
||||
};
|
||||
this.getInternalInstance = e => e[Object.keys(e).find(k => k.startsWith("__reactInternalInstance"))];
|
||||
}
|
||||
|
||||
initialize() {
|
||||
|
||||
}
|
||||
|
||||
joinBD1() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("0Tmfo5ZbORCRqbAd");}
|
||||
leaveBD1() {this.GuildActions.leaveGuild("86004744966914048");}
|
||||
|
||||
joinBD2() {this.InviteActions.acceptInviteAndTransitionToInviteChannel("2HScm8j");}
|
||||
leaveBD2() {this.GuildActions.leaveGuild("280806472928198656");}
|
||||
|
||||
get react() {return this.internal.react;}
|
||||
get React() {return this.internal.react;}
|
||||
get reactDom() {return this.internal.reactDom;}
|
||||
get ReactDom() {return this.internal.reactDom;}
|
||||
get reactComponent() {return this.internal.react.Component;}
|
||||
get ReactComponent() {return this.internal.react.Component;}
|
||||
|
||||
get anchorClasses() {return this.WebpackModules.findByUniqueProperties(["anchorUnderlineOnHover"]) || {anchor: "anchor-3Z-8Bb", anchorUnderlineOnHover: "anchorUnderlineOnHover-2ESHQB"};}
|
||||
get slateEditorClasses() {return this.WebpackModules.findByUniqueProperties(["slateTextArea"]);}
|
||||
get messageClasses() {return this.WebpackModules.findByUniqueProperties(["message", "containerCozy"]);}
|
||||
get guildClasses() {
|
||||
const guildsWrapper = this.WebpackModules.findByUniqueProperties(["wrapper", "unreadMentionsBar"]);
|
||||
const guilds = this.WebpackModules.findByUniqueProperties(["guildsError", "selected"]);
|
||||
const pill = this.WebpackModules.findByUniqueProperties(["blobContainer"]);
|
||||
return Object.assign({}, guildsWrapper, guilds, pill);
|
||||
}
|
||||
|
||||
get MessageContentComponent() {return this.WebpackModules.find(m => m.defaultProps && m.defaultProps.hasOwnProperty("disableButtons"));}
|
||||
get MessageComponent() {return this.WebpackModules.find(m => m.default && m.default.displayName && m.default.displayName == "Message");}
|
||||
get TimeFormatter() {return this.WebpackModules.findByUniqueProperties(["dateFormat"]);}
|
||||
get TooltipWrapper() {return this.WebpackModules.findByDisplayName("Tooltip");}
|
||||
get NativeModule() {return this.WebpackModules.findByUniqueProperties(["setBadge"]);}
|
||||
get InviteActions() {return this.WebpackModules.findByUniqueProperties(["acceptInvite"]);}
|
||||
get GuildActions() {return this.WebpackModules.findByUniqueProperties(["leaveGuild"]);}
|
||||
get Tooltips() {return this.WebpackModules.find(m => m.hide && m.show && !m.search && !m.submit && !m.search && !m.activateRagingDemon && !m.dismiss);}
|
||||
get KeyGenerator() {return this.WebpackModules.find(m => m.toString && /"binary"/.test(m.toString()));}
|
||||
get LayerStack() {return this.WebpackModules.findByUniqueProperties(["popLayer"]);}
|
||||
get UserStore() {return this.WebpackModules.findByUniqueProperties(["getCurrentUser"]);}
|
||||
get ChannelStore() {return this.WebpackModules.findByUniqueProperties(["getChannel"]);}
|
||||
get ChannelActions() {return this.WebpackModules.findByUniqueProperties(["openPrivateChannel"]);}
|
||||
get PrivateChannelActions() {return this.WebpackModules.findByUniqueProperties(["selectPrivateChannel"]);}
|
||||
|
||||
openDM(userId) {
|
||||
const selfId = this.UserStore.getCurrentUser().id;
|
||||
if (selfId == userId) return;
|
||||
const privateChannelId = this.ChannelStore.getDMFromUserId(userId);
|
||||
if (privateChannelId) return this.PrivateChannelActions.selectPrivateChannel(privateChannelId);
|
||||
this.ChannelActions.openPrivateChannel(selfId, userId);
|
||||
}
|
||||
|
||||
parseSettings(cat) {
|
||||
return Object.keys(settings).reduce((arr, key) => {
|
||||
const setting = settings[key];
|
||||
if (setting.cat === cat && setting.implemented && !setting.hidden) {
|
||||
setting.text = key;
|
||||
arr.push(setting);
|
||||
} return arr;
|
||||
}, []);
|
||||
}
|
||||
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
function VoiceMode() {
|
||||
|
||||
}
|
||||
|
||||
VoiceMode.prototype.enable = function () {
|
||||
$(".scroller.guild-channels ul").first().css("display", "none");
|
||||
$(".scroller.guild-channels header").first().css("display", "none");
|
||||
$(".app.flex-vertical, .app-2rEoOp").first().css("overflow", "hidden");
|
||||
$(".chat-3bRxxu").first().css("visibility", "hidden").css("min-width", "0px");
|
||||
$(".flex-vertical.channels-wrap").first().css("flex-grow", "100000");
|
||||
$(".guild-header .btn.btn-hamburger").first().css("visibility", "hidden");
|
||||
};
|
||||
|
||||
VoiceMode.prototype.disable = function () {
|
||||
$(".scroller.guild-channels ul").first().css("display", "");
|
||||
$(".scroller.guild-channels header").first().css("display", "");
|
||||
$(".app.flex-vertical, .app-2rEoOp").first().css("overflow", "");
|
||||
$(".chat-3bRxxu").first().css("visibility", "").css("min-width", "");
|
||||
$(".flex-vertical.channels-wrap").first().css("flex-grow", "");
|
||||
$(".guild-header .btn.btn-hamburger").first().css("visibility", "");
|
||||
};
|
||||
|
||||
export default new VoiceMode();
|
|
@ -0,0 +1,32 @@
|
|||
const req = webpackJsonp.push([[], {__extra_id__: (module, exports, req) => module.exports = req}, [["__extra_id__"]]]);
|
||||
delete req.m.__extra_id__;
|
||||
delete req.c.__extra_id__;
|
||||
const find = (filter) => {
|
||||
for (const i in req.c) {
|
||||
if (req.c.hasOwnProperty(i)) {
|
||||
const m = req.c[i].exports;
|
||||
if (m && m.__esModule && m.default && filter(m.default)) return m.default;
|
||||
if (m && filter(m)) return m;
|
||||
}
|
||||
}
|
||||
// console.warn("Cannot find loaded module in cache");
|
||||
return null;
|
||||
};
|
||||
|
||||
const findAll = (filter) => {
|
||||
const modules = [];
|
||||
for (const i in req.c) {
|
||||
if (req.c.hasOwnProperty(i)) {
|
||||
const m = req.c[i].exports;
|
||||
if (m && m.__esModule && m.default && filter(m.default)) modules.push(m.default);
|
||||
else if (m && filter(m)) modules.push(m);
|
||||
}
|
||||
}
|
||||
return modules;
|
||||
};
|
||||
|
||||
const findByProps = (...propNames) => find(module => propNames.every(prop => module[prop] !== undefined));
|
||||
const findByPrototypes = (...protoNames) => find(module => module.prototype && protoNames.every(protoProp => module.prototype[protoProp] !== undefined));
|
||||
const findByDisplayName = (displayName) => find(module => module.displayName === displayName);
|
||||
|
||||
export default {find, findAll, findByProps, findByPrototypes, findByDisplayName};
|
|
@ -0,0 +1,229 @@
|
|||
import {settingsCookie} from "../0globals";
|
||||
import BDV2 from "../modules/v2";
|
||||
import Utils from "../modules/utils";
|
||||
|
||||
import XSvg from "./xSvg";
|
||||
import ReloadIcon from "./reloadIcon";
|
||||
import EditIcon from "./icons/edit";
|
||||
import DeleteIcon from "./icons/delete";
|
||||
import Switch from "./components/switch";
|
||||
import TooltipWrap from "./tooltipWrap";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class V2C_PluginCard extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.showSettings = this.showSettings.bind(this);
|
||||
this.setInitialState();
|
||||
this.hasSettings = typeof this.props.addon.getSettingsPanel === "function";
|
||||
this.settingsPanel = "";
|
||||
|
||||
this.edit = this.edit.bind(this);
|
||||
this.delete = this.delete.bind(this);
|
||||
this.reload = this.reload.bind(this);
|
||||
}
|
||||
|
||||
setInitialState() {
|
||||
this.state = {
|
||||
checked: this.props.enabled,
|
||||
settings: false,
|
||||
reloads: 0
|
||||
};
|
||||
}
|
||||
|
||||
showSettings() {
|
||||
if (!this.hasSettings) return;
|
||||
this.setState({settings: true});
|
||||
}
|
||||
|
||||
closeSettings() {
|
||||
this.panelRef.current.innerHTML = "";
|
||||
this.setState({settingsOpen: false});
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (!this.state.settings) return;
|
||||
if (typeof this.settingsPanel === "object") {
|
||||
this.refs.settingspanel.appendChild(this.settingsPanel);
|
||||
}
|
||||
|
||||
if (!settingsCookie["fork-ps-3"]) return;
|
||||
const isHidden = (container, element) => {
|
||||
const cTop = container.scrollTop;
|
||||
const cBottom = cTop + container.clientHeight;
|
||||
const eTop = element.offsetTop;
|
||||
const eBottom = eTop + element.clientHeight;
|
||||
return (eTop < cTop || eBottom > cBottom);
|
||||
};
|
||||
|
||||
const thisNode = $(BDV2.reactDom.findDOMNode(this));
|
||||
const container = thisNode.parents(".scroller");
|
||||
if (!isHidden(container[0], thisNode[0])) return;
|
||||
container.animate({
|
||||
scrollTop: thisNode.offset().top - container.offset().top + container.scrollTop() - 30
|
||||
}, 300);
|
||||
}
|
||||
|
||||
|
||||
getString(value) {
|
||||
if (!value) return "???";
|
||||
return typeof value == "string" ? value : value.toString();
|
||||
}
|
||||
|
||||
get settingsComponent() {
|
||||
const name = this.getString(this.props.addon.name);
|
||||
|
||||
try { this.settingsPanel = this.props.addon.getSettingsPanel(); }
|
||||
catch (err) { Utils.err("Plugins", "Unable to get settings panel for " + this.props.addon.name + ".", err); }
|
||||
|
||||
return BDV2.react.createElement("div", {className: "bd-card bd-addon-card settings-open ui-switch-item"},
|
||||
BDV2.react.createElement("div", {style: {"float": "right", "cursor": "pointer"}, onClick: () => {
|
||||
this.refs.settingspanel.innerHTML = "";
|
||||
this.setState({settings: false});
|
||||
}},
|
||||
BDV2.react.createElement(XSvg, null)
|
||||
),
|
||||
typeof this.settingsPanel === "object" && BDV2.react.createElement("div", {id: `plugin-settings-${name}`, className: "plugin-settings", ref: "settingspanel"}),
|
||||
typeof this.settingsPanel !== "object" && BDV2.react.createElement("div", {id: `plugin-settings-${name}`, className: "plugin-settings", ref: "settingspanel", dangerouslySetInnerHTML: {__html: this.settingsPanel}})
|
||||
);
|
||||
}
|
||||
|
||||
buildTitle(name, version, author) {
|
||||
const title = "{{name}} v{{version}} by {{author}}".split(/({{[A-Za-z]+}})/);
|
||||
const nameIndex = title.findIndex(s => s == "{{name}}");
|
||||
if (nameIndex) title[nameIndex] = React.createElement("span", {className: "name bda-name"}, name);
|
||||
const versionIndex = title.findIndex(s => s == "{{version}}");
|
||||
if (nameIndex) title[versionIndex] = React.createElement("span", {className: "version bda-version"}, version);
|
||||
const authorIndex = title.findIndex(s => s == "{{author}}");
|
||||
if (nameIndex) {
|
||||
const props = {className: "author bda-author"};
|
||||
if (author.link || author.id) {
|
||||
props.className += ` ${BDV2.anchorClasses.anchor} ${BDV2.anchorClasses.anchorUnderlineOnHover}`;
|
||||
props.target = "_blank";
|
||||
|
||||
if (author.link) props.href = author.link;
|
||||
if (author.id) props.onClick = () => {BDV2.LayerStack.popLayer(); BDV2.openDM(author.id);};
|
||||
}
|
||||
title[authorIndex] = React.createElement(author.link || author.id ? "a" : "span", props, author.name);
|
||||
}
|
||||
return title.flat();
|
||||
}
|
||||
|
||||
makeLink(title, url) {
|
||||
const props = {className: "bda-link bda-link-website", target: "_blank"};
|
||||
if (typeof(url) == "string") props.href = url;
|
||||
if (typeof(url) == "function") props.onClick = (event) => {event.preventDefault(); event.stopPropagation(); url();};
|
||||
return BDV2.react.createElement("a", props, title);
|
||||
}
|
||||
|
||||
makeButton(title, children, action) {
|
||||
return <TooltipWrap color="black" side="top" text={title}>
|
||||
<div className="bd-addon-button" onClick={action}>{children}</div>
|
||||
</TooltipWrap>;
|
||||
// return <Tooltip color="black" position="top" text={title}>
|
||||
// {(props) => {
|
||||
// return <div {...props} className="bd-icon" onClick={action}>{children}</div>;
|
||||
// }}
|
||||
// </Tooltip>;
|
||||
}
|
||||
|
||||
get links() {
|
||||
const links = [];
|
||||
const addon = this.props.addon;
|
||||
if (addon.website) links.push(this.makeLink("Website", addon.website));
|
||||
if (addon.source) links.push(this.makeLink("Source", addon.source));
|
||||
if (addon.invite) {
|
||||
links.push(this.makeLink("Support Server", () => {
|
||||
const tester = /\.gg\/(.*)$/;
|
||||
let code = addon.invite;
|
||||
if (tester.test(code)) code = code.match(tester)[1];
|
||||
BDV2.LayerStack.popLayer();
|
||||
BDV2.InviteActions.acceptInviteAndTransitionToInviteChannel(code);
|
||||
}));
|
||||
}
|
||||
if (addon.donate) links.push(this.makeLink("Donate", addon.donate));
|
||||
if (addon.patreon) links.push(this.makeLink("Patreon", addon.patreon));
|
||||
return links;
|
||||
}
|
||||
|
||||
get footer() {
|
||||
const links = this.links;
|
||||
return (links.length || this.hasSettings) && BDV2.react.createElement("div", {className: "bd-card-footer bda-footer"},
|
||||
BDV2.react.createElement("span", {className: "bd-addon-links bda-links"},
|
||||
...(links.map((element, index) => index < links.length - 1 ? [element, " | "] : element).flat())
|
||||
),
|
||||
this.hasSettings && BDV2.react.createElement("button", {onClick: this.showSettings, className: "bd-button bda-settings-button", disabled: !this.state.checked}, "Settings")
|
||||
);
|
||||
}
|
||||
|
||||
onChange() {
|
||||
this.props.toggle && this.props.toggle(this.props.addon.name);
|
||||
this.props.enabled = !this.props.enabled;
|
||||
}
|
||||
|
||||
edit() {this.props.edit(this.props.addon.name);}
|
||||
delete() {this.props.remove(this.props.addon.name);}
|
||||
reload() {this.props.reload(this.props.addon.name);}
|
||||
|
||||
render() {
|
||||
if (this.state.settings) return this.settingsComponent;
|
||||
|
||||
const {name, author, description, version, authorId, authorLink} = this.props.addon;
|
||||
|
||||
return BDV2.react.createElement("div", {className: "bd-card bd-addon-card settings-closed ui-switch-item"},
|
||||
BDV2.react.createElement("div", {className: "bd-addon-header bda-header"},
|
||||
BDV2.react.createElement("div", {className: "bd-card-title bda-header-title"}, this.buildTitle(name, version, {name: author, id: authorId, link: authorLink})),
|
||||
BDV2.react.createElement("div", {className: "bd-addon-controls bda-controls"},
|
||||
this.props.edit && this.makeButton("Edit", <EditIcon className="bd-icon" />, this.edit),
|
||||
this.props.remove && this.makeButton("Delete", <DeleteIcon className="bd-icon" />, this.delete),
|
||||
this.props.reload && this.makeButton("Reload", <ReloadIcon className="bd-icon" />, this.reload),
|
||||
React.createElement(Switch, {onChange: this.onChange, checked: this.props.enabled})
|
||||
)
|
||||
),
|
||||
BDV2.react.createElement("div", {className: "bd-scroller-wrap bda-description-wrap scroller-wrap fade"},
|
||||
BDV2.react.createElement("div", {className: "bd-scroller bd-addon-description bda-description scroller"}, description)
|
||||
),
|
||||
this.footer
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// get settingsComponent() {
|
||||
// const addon = this.props.addon;
|
||||
// const name = this.getString(addon.name);
|
||||
// try { this.settingsPanel = this.props.getSettingsPanel(); }
|
||||
// catch (err) { Logger.stacktrace("Addon Settings", "Unable to get settings panel for " + name + ".", err); }
|
||||
|
||||
// const props = {id: `${name}-settings`, className: "addon-settings", ref: this.panelRef};
|
||||
// if (typeof(settingsPanel) == "string") props.dangerouslySetInnerHTML = this.settingsPanel;
|
||||
|
||||
// return <div className="bd-addon-card settings-open bd-switch-item">
|
||||
// <div className="bd-close" onClick={this.closeSettings}><CloseButton /></div>
|
||||
// <div {...props}>{this.settingsPanel instanceof React.Component ? this.settingsPanel : null}</div>
|
||||
// </div>;
|
||||
// }
|
||||
|
||||
// componentDidUpdate() {
|
||||
// if (!this.state.settingsOpen) return;
|
||||
// if (this.settingsPanel instanceof Node) this.panelRef.current.appendChild(this.settingsPanel);
|
||||
|
||||
// // if (!SettingsCookie["fork-ps-3"]) return;
|
||||
// const isHidden = (container, element) => {
|
||||
// const cTop = container.scrollTop;
|
||||
// const cBottom = cTop + container.clientHeight;
|
||||
// const eTop = element.offsetTop;
|
||||
// const eBottom = eTop + element.clientHeight;
|
||||
// return (eTop < cTop || eBottom > cBottom);
|
||||
// };
|
||||
|
||||
// const panel = $(this.panelRef.current);
|
||||
// const container = panel.parents(".scroller-2FKFPG");
|
||||
// if (!isHidden(container[0], panel[0])) return;
|
||||
// container.animate({
|
||||
// scrollTop: panel.offset().top - container.offset().top + container.scrollTop() - 30
|
||||
// }, 300);
|
||||
// }
|
|
@ -0,0 +1,160 @@
|
|||
import ErrorBoundary from "./errorBoundary";
|
||||
import ContentColumn from "./contentColumn";
|
||||
import Tools from "./tools";
|
||||
import ReloadIcon from "./reloadIcon";
|
||||
import AddonCard from "./addoncard";
|
||||
import Scroller from "./scroller";
|
||||
import Dropdown from "./components/dropdown";
|
||||
import Search from "./components/search";
|
||||
|
||||
import {settingsCookie, pluginCookie, themeCookie} from "../0globals";
|
||||
import ContentManager from "../modules/contentManager";
|
||||
import BDV2 from "../modules/v2";
|
||||
import pluginModule from "../modules/pluginModule";
|
||||
import themeModule from "../modules/themeModule";
|
||||
import WebpackModules from "../modules/webpackModules";
|
||||
import BdApi from "../modules/bdApi";
|
||||
|
||||
const Tooltip = WebpackModules.findByDisplayName("Tooltip");
|
||||
|
||||
const React = BDV2.react;
|
||||
|
||||
export default class CardList extends BDV2.reactComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {sort: "name", ascending: true, query: ""};
|
||||
this.isPlugins = this.props.type == "plugins";
|
||||
this.cookie = this.isPlugins ? pluginCookie : themeCookie;
|
||||
this.manager = this.isPlugins ? pluginModule : themeModule;
|
||||
|
||||
this.sort = this.sort.bind(this);
|
||||
this.reverse = this.reverse.bind(this);
|
||||
this.search = this.search.bind(this);
|
||||
}
|
||||
|
||||
openFolder() {
|
||||
require("electron").shell.openItem(this.isPlugins ? ContentManager.pluginsFolder : ContentManager.themesFolder);
|
||||
}
|
||||
|
||||
edit(name) {
|
||||
console.log(name);
|
||||
this.manager.edit(name);
|
||||
}
|
||||
|
||||
async delete(name) {
|
||||
const shouldDelete = await this.confirmDelete(name);
|
||||
if (!shouldDelete) return;
|
||||
this.manager.delete(name);
|
||||
}
|
||||
|
||||
confirmDelete(name) {
|
||||
return new Promise(resolve => {
|
||||
BdApi.showConfirmationModal("Are You Sure?", `Are you sure you want to delete ${name}?`, {
|
||||
danger: true,
|
||||
confirmText: "Delete",
|
||||
onConfirm: () => {resolve(true);},
|
||||
onCancel: () => {resolve(false);}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
get sortOptions() {
|
||||
return [
|
||||
{label: "Name", value: "name"},
|
||||
{label: "Author", value: "author"},
|
||||
{label: "Version", value: "version"},
|
||||
{label: "Recently Added", value: "added"},
|
||||
{label: "Last Modified", value: "modified"},
|
||||
{label: "File Size", value: "size"},
|
||||
];
|
||||
}
|
||||
|
||||
get directions() {
|
||||
return [
|
||||
{label: "Ascending", value: true},
|
||||
{label: "Descending", value: false}
|
||||
];
|
||||
}
|
||||
|
||||
reverse(value) {
|
||||
this.setState({ascending: value});
|
||||
}
|
||||
|
||||
sort(value) {
|
||||
this.setState({sort: value});
|
||||
}
|
||||
|
||||
search(event) {
|
||||
this.setState({query: event.target.value.toLocaleLowerCase()});
|
||||
}
|
||||
|
||||
getProps(addon) {
|
||||
return {
|
||||
key: addon.name,
|
||||
enabled: this.cookie[addon.name],
|
||||
toggle: this.manager.toggle.bind(this.manager),
|
||||
edit: this.edit.bind(this),
|
||||
remove: this.delete.bind(this),
|
||||
addon: addon
|
||||
};
|
||||
}
|
||||
|
||||
getAddons() {
|
||||
const sortedAddons = this.props.list.sort((a, b) => {
|
||||
const first = a[this.state.sort];
|
||||
const second = b[this.state.sort];
|
||||
if (typeof(first) == "string") return first.toLocaleLowerCase().localeCompare(second.toLocaleLowerCase());
|
||||
if (first > second) return 1;
|
||||
if (second > first) return -1;
|
||||
return 0;
|
||||
});
|
||||
if (!this.state.ascending) sortedAddons.reverse();
|
||||
const rendered = sortedAddons.map((addon) => {
|
||||
if (this.state.query) {
|
||||
let matches = addon.name.toLocaleLowerCase().includes(this.state.query);
|
||||
matches = matches || addon.author.toLocaleLowerCase().includes(this.state.query);
|
||||
matches = matches || addon.description.toLocaleLowerCase().includes(this.state.query);
|
||||
matches = matches || addon.version.toLocaleLowerCase().includes(this.state.query);
|
||||
if (!matches) return null;
|
||||
}
|
||||
const props = this.getProps(addon);
|
||||
return <ErrorBoundary><AddonCard {...props} reload={!settingsCookie["fork-ps-5"] && this.manager.reload.bind(this.manager)} /></ErrorBoundary>;
|
||||
});
|
||||
return rendered;
|
||||
}
|
||||
|
||||
render() {
|
||||
const refreshIcon = <Tooltip color="black" position="top" text="Reload List">
|
||||
{(props) =>
|
||||
<ReloadIcon {...props} className="bd-icon bd-reload bd-reload-header" size="18px" onClick={async () => {
|
||||
if (this.isPlugins) pluginModule.updatePluginList();
|
||||
else themeModule.updateThemeList();
|
||||
this.forceUpdate();
|
||||
}} />
|
||||
}</Tooltip>;
|
||||
const addonCards = this.getAddons().filter(c => c);
|
||||
|
||||
return <Scroller contentColumn={true} fade={true} dark={true}>
|
||||
<ContentColumn title={`${this.props.type.toUpperCase()}—${addonCards.length}`}>
|
||||
<button key="folder-button" className="bd-button bd-pfbtn" onClick={this.openFolder.bind(this)}>Open {this.isPlugins ? "Plugin" : "Theme"} Folder</button>
|
||||
{!settingsCookie["fork-ps-5"] && refreshIcon}
|
||||
<div className="bd-controls bd-addon-controls">
|
||||
<Search onChange={this.search} placeholder={`Search ${this.props.type}...`} />
|
||||
<div className="bd-addon-dropdowns">
|
||||
<div className="bd-select-wrapper">
|
||||
<label className="bd-label">Sort by:</label>
|
||||
<Dropdown options={this.sortOptions} onChange={this.sort} style="transparent" />
|
||||
</div>
|
||||
<div className="bd-select-wrapper">
|
||||
<label className="bd-label">Order:</label>
|
||||
<Dropdown options={this.directions} onChange={this.reverse} style="transparent" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div className="bda-slist bd-addon-list">{addonCards}</div>
|
||||
</ContentColumn>
|
||||
<Tools key="tools" />
|
||||
</Scroller>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
import {settingsCookie} from "../0globals";
|
||||
import quickEmoteMenu from "../modules/quickEmoteMenu";
|
||||
import BDV2 from "../modules/v2";
|
||||
|
||||
export default class BDEmote extends BDV2.reactComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const isFav = quickEmoteMenu && quickEmoteMenu.favoriteEmotes && quickEmoteMenu.favoriteEmotes[this.label] ? true : false;
|
||||
this.state = {
|
||||
shouldAnimate: !this.animateOnHover,
|
||||
isFavorite: isFav
|
||||
};
|
||||
|
||||
this.onMouseEnter = this.onMouseEnter.bind(this);
|
||||
this.onMouseLeave = this.onMouseLeave.bind(this);
|
||||
this.onClick = this.onClick.bind(this);
|
||||
}
|
||||
|
||||
get animateOnHover() {
|
||||
return settingsCookie["fork-es-2"];
|
||||
}
|
||||
|
||||
get label() {
|
||||
return this.props.modifier ? `${this.props.name}:${this.props.modifier}` : this.props.name;
|
||||
}
|
||||
|
||||
get modifierClass() {
|
||||
return this.props.modifier ? ` emote${this.props.modifier}` : "";
|
||||
}
|
||||
|
||||
onMouseEnter() {
|
||||
if (!this.state.shouldAnimate && this.animateOnHover) this.setState({shouldAnimate: true});
|
||||
if (!this.state.isFavorite && quickEmoteMenu.favoriteEmotes[this.label]) this.setState({isFavorite: true});
|
||||
else if (this.state.isFavorite && !quickEmoteMenu.favoriteEmotes[this.label]) this.setState({isFavorite: false});
|
||||
}
|
||||
|
||||
onMouseLeave() {
|
||||
if (this.state.shouldAnimate && this.animateOnHover) this.setState({shouldAnimate: false});
|
||||
}
|
||||
|
||||
onClick(e) {
|
||||
if (this.props.onClick) this.props.onClick(e);
|
||||
}
|
||||
|
||||
render() {
|
||||
return BDV2.react.createElement(BDV2.TooltipWrapper, {
|
||||
color: "black",
|
||||
position: "top",
|
||||
text: this.label,
|
||||
delay: 750
|
||||
},
|
||||
(childProps) => {
|
||||
return BDV2.react.createElement("div", Object.assign({
|
||||
className: "emotewrapper" + (this.props.jumboable ? " jumboable" : ""),
|
||||
onMouseEnter: this.onMouseEnter,
|
||||
onMouseLeave: this.onMouseLeave,
|
||||
onClick: this.onClick
|
||||
}, childProps),
|
||||
BDV2.react.createElement("img", {
|
||||
draggable: false,
|
||||
className: "emote" + this.modifierClass + (this.props.jumboable ? " jumboable" : "") + (!this.state.shouldAnimate ? " stop-animation" : ""),
|
||||
dataModifier: this.props.modifier,
|
||||
alt: this.label,
|
||||
src: this.props.url
|
||||
}),
|
||||
BDV2.react.createElement("input", {
|
||||
className: "fav" + (this.state.isFavorite ? " active" : ""),
|
||||
title: "Favorite!",
|
||||
type: "button",
|
||||
onClick: (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (this.state.isFavorite) {
|
||||
delete quickEmoteMenu.favoriteEmotes[this.label];
|
||||
quickEmoteMenu.updateFavorites();
|
||||
}
|
||||
else {
|
||||
quickEmoteMenu.favorite(this.label, this.props.url);
|
||||
}
|
||||
this.setState({isFavorite: !this.state.isFavorite});
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
export default class BDLogo extends BDV2.reactComponent {
|
||||
render() {
|
||||
return BDV2.react.createElement(
|
||||
"svg",
|
||||
{height: "100%", width: this.props.size || "16px", className: "bd-logo " + this.props.className, style: {fillRule: "evenodd", clipRule: "evenodd", strokeLinecap: "round", strokeLinejoin: "round"}, viewBox: "0 0 2000 2000"},
|
||||
BDV2.react.createElement("metadata", null),
|
||||
BDV2.react.createElement("defs", null,
|
||||
BDV2.react.createElement("filter", {id: "shadow1"}, BDV2.react.createElement("feDropShadow", {"dx": "20", "dy": "0", "stdDeviation": "20", "flood-color": "rgba(0,0,0,0.35)"})),
|
||||
BDV2.react.createElement("filter", {id: "shadow2"}, BDV2.react.createElement("feDropShadow", {"dx": "15", "dy": "0", "stdDeviation": "20", "flood-color": "rgba(255,255,255,0.15)"})),
|
||||
BDV2.react.createElement("filter", {id: "shadow3"}, BDV2.react.createElement("feDropShadow", {"dx": "10", "dy": "0", "stdDeviation": "20", "flood-color": "rgba(0,0,0,0.35)"}))
|
||||
),
|
||||
BDV2.react.createElement("g", null,
|
||||
BDV2.react.createElement("path", {style: {filter: "url(#shadow3)"}, d: "M1195.44+135.442L1195.44+135.442L997.6+136.442C1024.2+149.742+1170.34+163.542+1193.64+179.742C1264.34+228.842+1319.74+291.242+1358.24+365.042C1398.14+441.642+1419.74+530.642+1422.54+629.642L1422.54+630.842L1422.54+632.042C1422.54+773.142+1422.54+1228.14+1422.54+1369.14L1422.54+1370.34L1422.54+1371.54C1419.84+1470.54+1398.24+1559.54+1358.24+1636.14C1319.74+1709.94+1264.44+1772.34+1193.64+1821.44C1171.04+1837.14+1025.7+1850.54+1000+1863.54L1193.54+1864.54C1539.74+1866.44+1864.54+1693.34+1864.54+1296.64L1864.54+716.942C1866.44+312.442+1541.64+135.442+1195.44+135.442Z", fill: "#171717", opacity: "1"}),
|
||||
BDV2.react.createElement("path", {style: {filter: "url(#shadow2)"}, d: "M1695.54+631.442C1685.84+278.042+1409.34+135.442+1052.94+135.442L361.74+136.442L803.74+490.442L1060.74+490.442C1335.24+490.442+1335.24+835.342+1060.74+835.342L1060.74+1164.84C1150.22+1164.84+1210.53+1201.48+1241.68+1250.87C1306.07+1353+1245.76+1509.64+1060.74+1509.64L361.74+1863.54L1052.94+1864.54C1409.24+1864.54+1685.74+1721.94+1695.54+1368.54C1695.54+1205.94+1651.04+1084.44+1572.64+999.942C1651.04+915.542+1695.54+794.042+1695.54+631.442Z", fill: "#3E82E5", opacity: "1"}),
|
||||
BDV2.react.createElement("path", {style: {filter: "url(#shadow1)"}, d: "M1469.25+631.442C1459.55+278.042+1183.05+135.442+826.65+135.442L135.45+135.442L135.45+1004C135.45+1004+135.427+1255.21+355.626+1255.21C575.825+1255.21+575.848+1004+575.848+1004L577.45+490.442L834.45+490.442C1108.95+490.442+1108.95+835.342+834.45+835.342L664.65+835.342L664.65+1164.84L834.45+1164.84C923.932+1164.84+984.244+1201.48+1015.39+1250.87C1079.78+1353+1019.47+1509.64+834.45+1509.64L135.45+1509.64L135.45+1864.54L826.65+1864.54C1182.95+1864.54+1459.45+1721.94+1469.25+1368.54C1469.25+1205.94+1424.75+1084.44+1346.35+999.942C1424.75+915.542+1469.25+794.042+1469.25+631.442Z", fill: "#FFFFFF", opacity: "1"})
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
export default class V2C_Checkbox extends BDV2.reactComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onClick = this.onClick.bind(this);
|
||||
this.setInitialState();
|
||||
}
|
||||
|
||||
setInitialState() {
|
||||
this.state = {
|
||||
checked: this.props.checked || false
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return BDV2.react.createElement(
|
||||
"li",
|
||||
null,
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "checkbox checkbox-3kaeSU da-checkbox", onClick: this.onClick},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "checkbox-inner checkboxInner-3yjcPe da-checkboxInner"},
|
||||
BDV2.react.createElement("input", {className: "checkboxElement-1qV33p da-checkboxElement", checked: this.state.checked, onChange: () => {}, type: "checkbox"}),
|
||||
BDV2.react.createElement("span", null)
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"span",
|
||||
null,
|
||||
this.props.text
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
onClick() {
|
||||
this.props.onChange(this.props.id, !this.state.checked);
|
||||
this.setState({
|
||||
checked: !this.state.checked
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
import BDV2 from "../../modules/v2";
|
||||
import Arrow from "../icons/downarrow";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
|
||||
export default class Select extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {open: false, value: this.props.value || this.props.options[0].value};
|
||||
this.dropdown = React.createRef();
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.showMenu = this.showMenu.bind(this);
|
||||
this.hideMenu = this.hideMenu.bind(this);
|
||||
}
|
||||
|
||||
showMenu(event) {
|
||||
event.preventDefault();
|
||||
this.setState({open: true}, () => {
|
||||
document.addEventListener("click", this.hideMenu);
|
||||
});
|
||||
}
|
||||
|
||||
hideMenu() {
|
||||
this.setState({open: false}, () => {
|
||||
document.removeEventListener("click", this.hideMenu);
|
||||
});
|
||||
}
|
||||
|
||||
onChange(value) {
|
||||
this.setState({value});
|
||||
if (this.props.onChange) this.props.onChange(value);
|
||||
}
|
||||
|
||||
get selected() {return this.props.options.find(o => o.value == this.state.value);}
|
||||
|
||||
get options() {
|
||||
const selected = this.selected;
|
||||
return <div className="bd-select-options">
|
||||
{this.props.options.map(opt =>
|
||||
<div className={`bd-select-option${selected.value == opt.value ? " selected" : ""}`} onClick={this.onChange.bind(this, opt.value)}>{opt.label}</div>
|
||||
)}
|
||||
</div>;
|
||||
}
|
||||
|
||||
render() {
|
||||
const style = this.props.style == "transparent" ? " bd-select-transparent" : "";
|
||||
const isOpen = this.state.open ? " menu-open" : "";
|
||||
return <div className={`bd-select${style}${isOpen}`} onClick={this.showMenu} ref={this.dropdown}>
|
||||
<div className="bd-select-value">{this.selected.label}</div>
|
||||
<Arrow className="bd-select-arrow" />
|
||||
{this.state.open && this.options}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
// return <div className="bd-select-wrap">
|
||||
// <label className="bd-label">{this.props.label}</label>
|
||||
// <div className={`bd-select${style}${isOpen}`} onClick={this.showMenu} ref={this.dropdown}>
|
||||
// <div className="bd-select-controls">
|
||||
// <div className="bd-select-value">{this.selected.label}</div>
|
||||
// <Arrow className="bd-select-arrow" />
|
||||
// </div>
|
||||
// </div>
|
||||
// {this.state.open && this.options}
|
||||
// </div>;
|
|
@ -0,0 +1,13 @@
|
|||
import BDV2 from "../../modules/v2";
|
||||
import SearchIcon from "../icons/search";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class Search extends React.Component {
|
||||
render() {
|
||||
return <div className="bd-search-wrapper">
|
||||
<input onChange={this.props.onChange} onKeyDown={this.props.onKeyDown} type="text" className="bd-search" placeholder={this.props.placeholder} maxLength="50" />
|
||||
<SearchIcon />
|
||||
</div>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import BDV2 from "../../modules/v2";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class Switch extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {checked: this.props.checked};
|
||||
this.onChange = this.onChange.bind(this);
|
||||
}
|
||||
|
||||
onChange() {
|
||||
if (this.props.disabled) return;
|
||||
this.props.onChange(!this.state.checked);
|
||||
this.setState({checked: !this.state.checked});
|
||||
}
|
||||
|
||||
render() {
|
||||
const enabledClass = this.props.disabled ? " bd-switch-disabled" : "";
|
||||
const checkedClass = this.state.checked ? " bd-switch-checked" : "";
|
||||
return <div className={`bd-switch` + enabledClass + checkedClass}>
|
||||
<input type="checkbox" id={this.props.id} className={`bd-checkbox`} disabled={this.props.disabled} checked={this.state.checked} onChange={this.onChange} />
|
||||
</div>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
export default class V2C_ContentColumn extends BDV2.reactComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
static get displayName() {return "ContentColumn";}
|
||||
|
||||
render() {
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "contentColumn-2hrIYH contentColumnDefault-1VQkGM content-column default"},
|
||||
BDV2.react.createElement(
|
||||
"h2",
|
||||
{className: "ui-form-title h2 margin-reset margin-bottom-20"},
|
||||
this.props.title
|
||||
),
|
||||
this.props.children
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,237 @@
|
|||
import {settingsCookie} from "../0globals";
|
||||
import Settings from "../modules/settingsPanel";
|
||||
import BDV2 from "../modules/v2";
|
||||
import DataStore from "../modules/dataStore";
|
||||
|
||||
import SettingsTitle from "./settingsTitle";
|
||||
import Checkbox from "./checkbox";
|
||||
import V2C_CssEditorDetached from "./cssEditorDetached";
|
||||
|
||||
export default class V2C_CssEditor extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const self = this;
|
||||
self.props.lines = 0;
|
||||
self.setInitialState();
|
||||
self.attach = self.attach.bind(self);
|
||||
self.detachedEditor = BDV2.react.createElement(V2C_CssEditorDetached, {attach: self.attach});
|
||||
self.onClick = self.onClick.bind(self);
|
||||
self.updateCss = self.updateCss.bind(self);
|
||||
self.saveCss = self.saveCss.bind(self);
|
||||
self.detach = self.detach.bind(self);
|
||||
}
|
||||
|
||||
setInitialState() {
|
||||
this.state = {
|
||||
detached: this.props.detached || BDV2.editorDetached
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// this.updateLineCount();
|
||||
this.editor = ace.edit("bd-customcss-editor");
|
||||
this.editor.setTheme("ace/theme/monokai");
|
||||
this.editor.session.setMode("ace/mode/css");
|
||||
this.editor.setShowPrintMargin(false);
|
||||
this.editor.setFontSize(14);
|
||||
this.editor.on("change", () => {
|
||||
if (!settingsCookie["bda-css-0"]) return;
|
||||
this.saveCss();
|
||||
this.updateCss();
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.editor.destroy();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
const self = this;
|
||||
if (prevState.detached && !self.state.detached) {
|
||||
BDV2.reactDom.unmountComponentAtNode(self.detachedRoot);
|
||||
}
|
||||
}
|
||||
|
||||
codeMirror() {
|
||||
}
|
||||
|
||||
get options() {
|
||||
return {
|
||||
lineNumbers: true,
|
||||
mode: "css",
|
||||
indentUnit: 4,
|
||||
theme: "material",
|
||||
scrollbarStyle: "simple"
|
||||
};
|
||||
}
|
||||
|
||||
get css() {
|
||||
const _ccss = DataStore.getBDData("bdcustomcss");
|
||||
let ccss = "";
|
||||
if (_ccss && _ccss !== "") {
|
||||
ccss = atob(_ccss);
|
||||
}
|
||||
return ccss;
|
||||
}
|
||||
|
||||
updateLineCount() {
|
||||
const lineCount = this.refs.editor.value.split("\n").length;
|
||||
if (lineCount == this.props.lines) return;
|
||||
this.refs.lines.textContent = Array.from(new Array(lineCount), (_, i) => i + 1).join(".\n") + ".";
|
||||
this.props.lines = lineCount;
|
||||
}
|
||||
|
||||
render() {
|
||||
const self = this;
|
||||
|
||||
const {detached} = self.state;
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "contentColumn-2hrIYH contentColumnDefault-1VQkGM content-column default", style: {padding: "60px 40px 0px"}},
|
||||
detached && BDV2.react.createElement(
|
||||
"div",
|
||||
{id: "editor-detached"},
|
||||
BDV2.react.createElement(SettingsTitle, {text: "Custom CSS Editor"}),
|
||||
BDV2.react.createElement(
|
||||
"h3",
|
||||
null,
|
||||
"Editor Detached"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{className: "btn btn-primary", onClick: () => {
|
||||
self.attach();
|
||||
}},
|
||||
"Attach"
|
||||
)
|
||||
),
|
||||
!detached && BDV2.react.createElement(
|
||||
"div",
|
||||
null,
|
||||
BDV2.react.createElement(SettingsTitle, {text: "Custom CSS Editor"}),
|
||||
BDV2.react.createElement("div", {className: "editor-wrapper"},
|
||||
BDV2.react.createElement("div", {id: "bd-customcss-editor", className: "editor", ref: "editor"}, self.css)
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{id: "bd-customcss-attach-controls"},
|
||||
BDV2.react.createElement(
|
||||
"ul",
|
||||
{className: "checkbox-group"},
|
||||
BDV2.react.createElement(Checkbox, {id: "live-update", text: "Live Update", onChange: this.onChange, checked: settingsCookie["bda-css-0"]})
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{id: "bd-customcss-detach-controls-button"},
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {borderRadius: "3px 0 0 3px", borderRight: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
|
||||
self.onClick("update");
|
||||
}},
|
||||
"Update"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {borderRadius: "0", borderLeft: "1px solid #2d2d2d", borderRight: "1px solid #2d2d2d"}, className: "btn btn-primary", onClick: () => {
|
||||
self.onClick("save");
|
||||
}},
|
||||
"Save"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {borderRadius: "0 3px 3px 0", borderLeft: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
|
||||
self.onClick("detach");
|
||||
}},
|
||||
"Detach"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"span",
|
||||
{style: {fontSize: "10px", marginLeft: "5px"}},
|
||||
"Unsaved changes are lost on detach"
|
||||
),
|
||||
BDV2.react.createElement("div", {className: "help-text"},
|
||||
"Press ",
|
||||
BDV2.react.createElement("code", {className: "inline"}, "ctrl"),
|
||||
"+",
|
||||
BDV2.react.createElement("span", {className: "inline"}, ","),
|
||||
" with the editor focused to access the editor's settings."
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
onClick(arg) {
|
||||
const self = this;
|
||||
switch (arg) {
|
||||
case "update":
|
||||
self.updateCss();
|
||||
break;
|
||||
case "save":
|
||||
self.saveCss();
|
||||
break;
|
||||
case "detach":
|
||||
self.detach();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onChange(id, checked) {
|
||||
switch (id) {
|
||||
case "live-update":
|
||||
settingsCookie["bda-css-0"] = checked;
|
||||
Settings.saveSettings();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
updateCss() {
|
||||
if ($("#customcss").length == 0) {
|
||||
$("head").append("<style id=\"customcss\"></style>");
|
||||
}
|
||||
$("#customcss").text(this.editor.session.getValue()).detach().appendTo(document.head);
|
||||
}
|
||||
|
||||
saveCss() {
|
||||
DataStore.setBDData("bdcustomcss", btoa(this.editor.session.getValue()));
|
||||
}
|
||||
|
||||
detach() {
|
||||
const self = this;
|
||||
self.setState({
|
||||
detached: true
|
||||
});
|
||||
const droot = self.detachedRoot;
|
||||
if (!droot) {
|
||||
console.log("FAILED TO INJECT ROOT: .app");
|
||||
return;
|
||||
}
|
||||
BDV2.reactDom.render(self.detachedEditor, droot);
|
||||
}
|
||||
|
||||
get detachedRoot() {
|
||||
const _root = $("#bd-customcss-detach-container");
|
||||
if (!_root.length) {
|
||||
if (!this.injectDetachedRoot()) return null;
|
||||
return this.detachedRoot;
|
||||
}
|
||||
return _root[0];
|
||||
}
|
||||
|
||||
injectDetachedRoot() {
|
||||
if (!$(".app, .app-2rEoOp").length) return false;
|
||||
$("<div/>", {
|
||||
id: "bd-customcss-detach-container"
|
||||
}).insertAfter($(".app, .app-2rEoOp"));
|
||||
return true;
|
||||
}
|
||||
|
||||
attach() {
|
||||
const self = this;
|
||||
self.setState({
|
||||
detached: false
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
import {settingsCookie} from "../0globals";
|
||||
import Settings from "../modules/settingsPanel";
|
||||
import BDV2 from "../modules/v2";
|
||||
import DataStore from "../modules/dataStore";
|
||||
|
||||
import Checkbox from "./checkbox";
|
||||
|
||||
export default class V2C_CssEditorDetached extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const self = this;
|
||||
self.onClick = self.onClick.bind(self);
|
||||
self.updateCss = self.updateCss.bind(self);
|
||||
self.saveCss = self.saveCss.bind(self);
|
||||
self.onChange = self.onChange.bind(self);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
$("#app-mount").addClass("bd-detached-editor");
|
||||
BDV2.editorDetached = true;
|
||||
// this.updateLineCount();
|
||||
this.editor = ace.edit("bd-customcss-editor-detached");
|
||||
this.editor.setTheme("ace/theme/monokai");
|
||||
this.editor.session.setMode("ace/mode/css");
|
||||
this.editor.setShowPrintMargin(false);
|
||||
this.editor.setFontSize(14);
|
||||
this.editor.on("change", () => {
|
||||
if (!settingsCookie["bda-css-0"]) return;
|
||||
this.saveCss();
|
||||
this.updateCss();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
$("#app-mount").removeClass("bd-detached-editor");
|
||||
BDV2.editorDetached = false;
|
||||
this.editor.destroy();
|
||||
}
|
||||
|
||||
updateLineCount() {
|
||||
const lineCount = this.refs.editor.value.split("\n").length;
|
||||
if (lineCount == this.props.lines) return;
|
||||
this.refs.lines.textContent = Array.from(new Array(lineCount), (_, i) => i + 1).join(".\n") + ".";
|
||||
this.props.lines = lineCount;
|
||||
}
|
||||
|
||||
get options() {
|
||||
return {
|
||||
lineNumbers: true,
|
||||
mode: "css",
|
||||
indentUnit: 4,
|
||||
theme: "material",
|
||||
scrollbarStyle: "simple"
|
||||
};
|
||||
}
|
||||
|
||||
get css() {
|
||||
const _ccss = DataStore.getBDData("bdcustomcss");
|
||||
let ccss = "";
|
||||
if (_ccss && _ccss !== "") {
|
||||
ccss = atob(_ccss);
|
||||
}
|
||||
return ccss;
|
||||
}
|
||||
|
||||
get root() {
|
||||
const _root = $("#bd-customcss-detach-container");
|
||||
if (!_root.length) {
|
||||
if (!this.injectRoot()) return null;
|
||||
return this.detachedRoot;
|
||||
}
|
||||
return _root[0];
|
||||
}
|
||||
|
||||
injectRoot() {
|
||||
if (!$(".app, .app-2rEoOp").length) return false;
|
||||
$("<div/>", {
|
||||
id: "bd-customcss-detach-container"
|
||||
}).insertAfter($(".app, .app-2rEoOp"));
|
||||
return true;
|
||||
}
|
||||
|
||||
render() {
|
||||
const self = this;
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "bd-detached-css-editor", id: "bd-customcss-detach-editor"},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{id: "bd-customcss-innerpane"},
|
||||
BDV2.react.createElement("div", {className: "editor-wrapper"},
|
||||
BDV2.react.createElement("div", {id: "bd-customcss-editor-detached", className: "editor", ref: "editor"}, self.css)
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{id: "bd-customcss-attach-controls"},
|
||||
BDV2.react.createElement(
|
||||
"ul",
|
||||
{className: "checkbox-group"},
|
||||
BDV2.react.createElement(Checkbox, {id: "live-update", text: "Live Update", onChange: self.onChange, checked: settingsCookie["bda-css-0"]})
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{id: "bd-customcss-detach-controls-button"},
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {borderRadius: "3px 0 0 3px", borderRight: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
|
||||
self.onClick("update");
|
||||
}},
|
||||
"Update"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {borderRadius: "0", borderLeft: "1px solid #2d2d2d", borderRight: "1px solid #2d2d2d"}, className: "btn btn-primary", onClick: () => {
|
||||
self.onClick("save");
|
||||
}},
|
||||
"Save"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {borderRadius: "0 3px 3px 0", borderLeft: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
|
||||
self.onClick("attach");
|
||||
}},
|
||||
"Attach"
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"span",
|
||||
{style: {fontSize: "10px", marginLeft: "5px"}},
|
||||
"Unsaved changes are lost on attach"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
onChange(id, checked) {
|
||||
switch (id) {
|
||||
case "live-update":
|
||||
settingsCookie["bda-css-0"] = checked;
|
||||
Settings.saveSettings();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onClick(id) {
|
||||
const self = this;
|
||||
switch (id) {
|
||||
case "attach":
|
||||
if ($("#editor-detached").length) self.props.attach();
|
||||
BDV2.reactDom.unmountComponentAtNode(self.root);
|
||||
self.root.remove();
|
||||
break;
|
||||
case "update":
|
||||
self.updateCss();
|
||||
break;
|
||||
case "save":
|
||||
self.saveCss();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
updateCss() {
|
||||
if ($("#customcss").length == 0) {
|
||||
$("head").append("<style id=\"customcss\"></style>");
|
||||
}
|
||||
$("#customcss").text(this.editor.session.getValue()).detach().appendTo(document.head);
|
||||
}
|
||||
|
||||
saveCss() {
|
||||
DataStore.setBDData("bdcustomcss", btoa(this.editor.session.getValue()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
export default class BDErrorBoundary extends BDV2.reactComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {hasError: false};
|
||||
}
|
||||
|
||||
componentDidCatch() {
|
||||
this.setState({hasError: true});
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) return BDV2.react.createElement("div", {className: "react-error"}, "Component Error");
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import BDV2 from "../../modules/v2";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class Delete extends React.Component {
|
||||
render() {
|
||||
const size = this.props.size || "24px";
|
||||
return <svg className={this.props.className || ""} fill="#FFFFFF" viewBox="0 0 24 24" style={{width: size, height: size}} onClick={this.props.onClick}>
|
||||
<path fill="none" d="M0 0h24v24H0V0z"/><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zm2.46-7.12l1.41-1.41L12 12.59l2.12-2.12 1.41 1.41L13.41 14l2.12 2.12-1.41 1.41L12 15.41l-2.12 2.12-1.41-1.41L10.59 14l-2.13-2.12zM15.5 4l-1-1h-5l-1 1H5v2h14V4z"/>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
</svg>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import BDV2 from "../../modules/v2";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class DownArrow extends React.Component {
|
||||
render() {
|
||||
const size = this.props.size || "16px";
|
||||
return <svg className={this.props.className || ""} fill="#FFFFFF" viewBox="0 0 24 24" style={{width: size, height: size}}>
|
||||
<path d="M8.12 9.29L12 13.17l3.88-3.88c.39-.39 1.02-.39 1.41 0 .39.39.39 1.02 0 1.41l-4.59 4.59c-.39.39-1.02.39-1.41 0L6.7 10.7c-.39-.39-.39-1.02 0-1.41.39-.38 1.03-.39 1.42 0z"/>
|
||||
</svg>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import BDV2 from "../../modules/v2";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class Edit extends React.Component {
|
||||
render() {
|
||||
const size = this.props.size || "24px";
|
||||
return <svg viewBox="0 0 24 24" fill="#FFFFFF" style={{width: size, height: size}} onClick={this.props.onClick}>
|
||||
<path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z" />
|
||||
<path d="M0 0h24v24H0z" fill="none" />
|
||||
</svg>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import BDV2 from "../../modules/v2";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class Search extends React.Component {
|
||||
render() {
|
||||
const size = this.props.size || "16px";
|
||||
return <svg className={this.props.className || ""} fill="#FFFFFF" viewBox="0 0 24 24" style={{width: size, height: size}}>
|
||||
<path fill="none" d="M0 0h24v24H0V0z"/>
|
||||
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
|
||||
</svg>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
export default class V2C_Layer extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
$(window).on(`keyup.${this.props.id}`, e => {
|
||||
if (e.which === 27) {
|
||||
BDV2.reactDom.unmountComponentAtNode(this.refs.root.parentNode);
|
||||
}
|
||||
});
|
||||
|
||||
$(`#${this.props.id}`).animate({opacity: 1}, {
|
||||
step: function(now) {
|
||||
$(this).css("transform", `scale(${1.1 - 0.1 * now}) translateZ(0px)`);
|
||||
},
|
||||
duration: 200,
|
||||
done: () => {$(`#${this.props.id}`).css("opacity", "").css("transform", "");}
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
$(window).off(`keyup.${this.props.id}`);
|
||||
$(`#${this.props.id}`).animate({opacity: 0}, {
|
||||
step: function(now) {
|
||||
$(this).css("transform", `scale(${1.1 - 0.1 * now}) translateZ(0px)`);
|
||||
},
|
||||
duration: 200,
|
||||
done: () => {$(`#${this.props.rootId}`).remove();}
|
||||
});
|
||||
|
||||
$("[class*=\"layer-\"]").removeClass("publicServersOpen").animate({opacity: 1}, {
|
||||
step: function(now) {
|
||||
$(this).css("transform", `scale(${0.07 * now + 0.93}) translateZ(0px)`);
|
||||
},
|
||||
duration: 200,
|
||||
done: () => {$("[class*=\"layer-\"]").css("opacity", "").css("transform", "");}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
$("[class*=\"layer-\"]").addClass("publicServersOpen").animate({opacity: 0}, {
|
||||
step: function(now) {
|
||||
$(this).css("transform", `scale(${0.07 * now + 0.93}) translateZ(0px)`);
|
||||
},
|
||||
duration: 200
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "layer bd-layer layer-3QrUeG", id: this.props.id, ref: "root", style: {opacity: 0, transform: "scale(1.1) translateZ(0px)"}},
|
||||
this.props.children
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
export default class V2C_List extends BDV2.reactComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return BDV2.react.createElement(
|
||||
"ul",
|
||||
{className: this.props.className},
|
||||
this.props.children
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,257 @@
|
|||
import {settingsCookie, pluginCookie, bdplugins} from "../0globals";
|
||||
import BDV2 from "../modules/v2";
|
||||
import pluginModule from "../modules/pluginModule";
|
||||
import Utils from "../modules/utils";
|
||||
|
||||
import XSvg from "./xSvg";
|
||||
import ReloadIcon from "./reloadIcon";
|
||||
import TooltipWrap from "./tooltipWrap";
|
||||
import Switch from "./components/switch";
|
||||
|
||||
const React = BDV2.React;
|
||||
|
||||
export default class V2C_PluginCard extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const self = this;
|
||||
self.onChange = self.onChange.bind(self);
|
||||
self.showSettings = self.showSettings.bind(self);
|
||||
self.setInitialState();
|
||||
self.hasSettings = typeof self.props.plugin.getSettingsPanel === "function";
|
||||
self.settingsPanel = "";
|
||||
|
||||
this.reload = this.reload.bind(this);
|
||||
this.onReload = this.onReload.bind(this);
|
||||
}
|
||||
|
||||
setInitialState() {
|
||||
this.state = {
|
||||
checked: pluginCookie[this.props.plugin.getName()],
|
||||
settings: false,
|
||||
reloads: 0
|
||||
};
|
||||
}
|
||||
|
||||
// componentDidMount() {
|
||||
// BDEvents.on("plugin-reloaded", this.onReload);
|
||||
// }
|
||||
|
||||
// componentWillUnmount() {
|
||||
// BDEvents.off("plugin-reloaded", this.onReload);
|
||||
// }
|
||||
|
||||
onReload(pluginName) {
|
||||
if (pluginName !== this.props.plugin.getName()) return;
|
||||
this.setState({reloads: this.state.reloads + 1});
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.state.settings) {
|
||||
if (typeof this.settingsPanel === "object") {
|
||||
this.refs.settingspanel.appendChild(this.settingsPanel);
|
||||
}
|
||||
|
||||
if (!settingsCookie["fork-ps-3"]) return;
|
||||
const isHidden = (container, element) => {
|
||||
|
||||
const cTop = container.scrollTop;
|
||||
const cBottom = cTop + container.clientHeight;
|
||||
|
||||
const eTop = element.offsetTop;
|
||||
const eBottom = eTop + element.clientHeight;
|
||||
|
||||
return (eTop < cTop || eBottom > cBottom);
|
||||
};
|
||||
|
||||
const self = $(BDV2.reactDom.findDOMNode(this));
|
||||
const container = self.parents(".scroller");
|
||||
if (!isHidden(container[0], self[0])) return;
|
||||
container.animate({
|
||||
scrollTop: self.offset().top - container.offset().top + container.scrollTop() - 30
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
reload() {
|
||||
const plugin = this.props.plugin.getName();
|
||||
pluginModule.reloadPlugin(plugin);
|
||||
this.props.plugin = bdplugins[plugin].plugin;
|
||||
this.onReload(this.props.plugin.getName());
|
||||
}
|
||||
|
||||
getString(value) {
|
||||
if (!value) return "???";
|
||||
return typeof value == "string" ? value : value.toString();
|
||||
}
|
||||
|
||||
get settingsComponent() {
|
||||
const self = this;
|
||||
const {plugin} = this.props;
|
||||
const name = this.getString(plugin.getName());
|
||||
|
||||
try { self.settingsPanel = plugin.getSettingsPanel(); }
|
||||
catch (err) { Utils.err("Plugins", "Unable to get settings panel for " + plugin.getName() + ".", err); }
|
||||
|
||||
return BDV2.react.createElement("div", {className: "settings-open ui-switch-item bd-addon-card"},
|
||||
BDV2.react.createElement("div", {style: {"float": "right", "cursor": "pointer"}, onClick: () => {
|
||||
this.refs.settingspanel.innerHTML = "";
|
||||
self.setState({settings: false});
|
||||
}},
|
||||
BDV2.react.createElement(XSvg, null)
|
||||
),
|
||||
typeof self.settingsPanel === "object" && BDV2.react.createElement("div", {id: `plugin-settings-${name}`, className: "plugin-settings", ref: "settingspanel"}),
|
||||
typeof self.settingsPanel !== "object" && BDV2.react.createElement("div", {id: `plugin-settings-${name}`, className: "plugin-settings", ref: "settingspanel", dangerouslySetInnerHTML: {__html: self.settingsPanel}})
|
||||
);
|
||||
}
|
||||
|
||||
buildTitle(name, version, author) {
|
||||
const title = "{{name}} v{{version}} by {{author}}".split(/({{[A-Za-z]+}})/);
|
||||
const nameIndex = title.findIndex(s => s == "{{name}}");
|
||||
if (nameIndex) title[nameIndex] = React.createElement("span", {className: "bd-name bda-name"}, name);
|
||||
const versionIndex = title.findIndex(s => s == "{{version}}");
|
||||
if (nameIndex) title[versionIndex] = React.createElement("span", {className: "bd-version bda-version"}, version);
|
||||
const authorIndex = title.findIndex(s => s == "{{author}}");
|
||||
if (nameIndex) {
|
||||
const props = {className: "bd-author bda-author"};
|
||||
if (author.link || author.id) {
|
||||
props.className += ` ${BDV2.anchorClasses.anchor} ${BDV2.anchorClasses.anchorUnderlineOnHover}`;
|
||||
props.target = "_blank";
|
||||
|
||||
if (author.link) props.href = author.link;
|
||||
if (author.id) props.onClick = () => {BDV2.LayerStack.popLayer(); BDV2.openDM(author.id);};
|
||||
}
|
||||
title[authorIndex] = React.createElement(author.link || author.id ? "a" : "span", props, author.name);
|
||||
}
|
||||
return title.flat();
|
||||
}
|
||||
|
||||
makeLink(title, url) {
|
||||
const props = {className: "bda-link bda-link-website", target: "_blank"};
|
||||
if (typeof(url) == "string") props.href = url;
|
||||
if (typeof(url) == "function") props.onClick = (event) => {event.preventDefault(); event.stopPropagation(); url();};
|
||||
return BDV2.react.createElement("a", props, title);
|
||||
}
|
||||
|
||||
makeButton(title, children, action) {
|
||||
return <TooltipWrap color="black" position="top" text={title}>
|
||||
{(props) => {
|
||||
return <div {...props} className="bd-addon-button" onClick={action}>{children}</div>;
|
||||
}}
|
||||
</TooltipWrap>;
|
||||
}
|
||||
|
||||
getLinks(meta) {
|
||||
const links = [];
|
||||
if (meta.website) links.push(this.makeLink("Website", meta.website));
|
||||
if (meta.source) links.push(this.makeLink("Source", meta.source));
|
||||
if (meta.invite) {
|
||||
links.push(this.makeLink("Support Server", () => {
|
||||
const tester = /\.gg\/(.*)$/;
|
||||
let code = meta.invite;
|
||||
if (tester.test(code)) code = code.match(tester)[1];
|
||||
BDV2.LayerStack.popLayer();
|
||||
BDV2.InviteActions.acceptInviteAndTransitionToInviteChannel(code);
|
||||
}));
|
||||
}
|
||||
if (meta.donate) links.push(this.makeLink("Donate", meta.donate));
|
||||
if (meta.patreon) links.push(this.makeLink("Patreon", meta.patreon));
|
||||
return links;
|
||||
}
|
||||
|
||||
getFooter(meta) {
|
||||
const links = this.getLinks(meta);
|
||||
return (links.length || this.hasSettings) && BDV2.react.createElement("div", {className: "bda-footer"},
|
||||
BDV2.react.createElement("span", {className: "bda-links"},
|
||||
...(links.map((element, index) => index < links.length - 1 ? [element, " | "] : element).flat())
|
||||
),
|
||||
this.hasSettings && BDV2.react.createElement("button", {onClick: this.showSettings, className: "bda-settings-button", disabled: !this.state.checked}, "Settings")
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.settings) return this.settingsComponent;
|
||||
|
||||
const {plugin} = this.props;
|
||||
const name = this.getString(plugin.getName());
|
||||
const author = this.getString(plugin.getAuthor());
|
||||
const description = this.getString(plugin.getDescription());
|
||||
const version = this.getString(plugin.getVersion());
|
||||
const meta = bdplugins[name];
|
||||
|
||||
return BDV2.react.createElement("div", {"data-name": name, "data-version": version, "className": "settings-closed ui-switch-item bd-addon-card"},
|
||||
BDV2.react.createElement("div", {className: "bd-addon-header bda-header"},
|
||||
BDV2.react.createElement("div", {className: "bd-title bda-header-title"}, this.buildTitle(name, version, {name: author, id: meta.authorId, link: meta.authorLink})),
|
||||
BDV2.react.createElement("div", {className: "bd-addon-controls bda-controls"},
|
||||
!settingsCookie["fork-ps-5"] && BDV2.react.createElement(TooltipWrap(ReloadIcon, {color: "black", side: "top", text: "Reload"}), {className: "bd-reload-card", onClick: this.reload}),
|
||||
React.createElement(Switch, {onChange: this.onChange, checked: this.state.checked})
|
||||
// BDV2.react.createElement("label", {className: "ui-switch-wrapper ui-flex-child", style: {flex: "0 0 auto"}},
|
||||
// BDV2.react.createElement("input", {checked: this.state.checked, onChange: this.onChange, className: "ui-switch-checkbox", type: "checkbox"}),
|
||||
// BDV2.react.createElement("div", {className: this.state.checked ? "ui-switch checked" : "ui-switch"})
|
||||
// )
|
||||
)
|
||||
),
|
||||
BDV2.react.createElement("div", {className: "bd-scroller-wrap bda-description-wrap scroller-wrap fade"},
|
||||
BDV2.react.createElement("div", {className: "bd-addon-description bda-description scroller"}, description)
|
||||
),
|
||||
this.getFooter(meta)
|
||||
);
|
||||
}
|
||||
|
||||
onChange() {
|
||||
this.setState({checked: !this.state.checked});
|
||||
pluginModule.togglePlugin(this.props.plugin.getName());
|
||||
}
|
||||
|
||||
showSettings() {
|
||||
if (!this.hasSettings) return;
|
||||
this.setState({settings: true});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// <div className="bd-controls">
|
||||
// {this.props.editAddon && this.makeButton(Strings.Addons.editAddon, <EditIcon />, this.props.editAddon)}
|
||||
// {this.props.deleteAddon && this.makeButton(Strings.Addons.deleteAddon, <DeleteIcon />, this.props.deleteAddon)}
|
||||
// {this.props.showReloadIcon && this.makeButton(Strings.Addons.reload, <ReloadIcon className="bd-reload bd-reload-card" />, this.reload)}
|
||||
// <Switch checked={this.props.enabled} onChange={this.onChange} />
|
||||
// </div>
|
||||
|
||||
|
||||
|
||||
// componentDidUpdate() {
|
||||
// if (!this.state.settingsOpen) return;
|
||||
// if (this.settingsPanel instanceof Node) this.panelRef.current.appendChild(this.settingsPanel);
|
||||
|
||||
// // if (!SettingsCookie["fork-ps-3"]) return;
|
||||
// const isHidden = (container, element) => {
|
||||
// const cTop = container.scrollTop;
|
||||
// const cBottom = cTop + container.clientHeight;
|
||||
// const eTop = element.offsetTop;
|
||||
// const eBottom = eTop + element.clientHeight;
|
||||
// return (eTop < cTop || eBottom > cBottom);
|
||||
// };
|
||||
|
||||
// const panel = $(this.panelRef.current);
|
||||
// const container = panel.parents(".scroller-2FKFPG");
|
||||
// if (!isHidden(container[0], panel[0])) return;
|
||||
// container.animate({
|
||||
// scrollTop: panel.offset().top - container.offset().top + container.scrollTop() - 30
|
||||
// }, 300);
|
||||
// }
|
||||
|
||||
|
||||
// get settingsComponent() {
|
||||
// const addon = this.props.addon;
|
||||
// const name = this.getString(addon.name);
|
||||
// try { this.settingsPanel = this.props.getSettingsPanel(); }
|
||||
// catch (err) { Logger.stacktrace("Addon Settings", "Unable to get settings panel for " + name + ".", err); }
|
||||
|
||||
// const props = {id: `${name}-settings`, className: "addon-settings", ref: this.panelRef};
|
||||
// if (typeof(settingsPanel) == "string") props.dangerouslySetInnerHTML = this.settingsPanel;
|
||||
|
||||
// return <div className="bd-addon-card settings-open bd-switch-item">
|
||||
// <div className="bd-close" onClick={this.closeSettings}><CloseButton /></div>
|
||||
// <div {...props}>{this.settingsPanel instanceof React.Component ? this.settingsPanel : null}</div>
|
||||
// </div>;
|
||||
// }
|
|
@ -0,0 +1,437 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
import Tools from "./tools";
|
||||
import ServerCard from "./serverCard";
|
||||
import SidebarView from "./sidebarView";
|
||||
import SettingsTitle from "./settingsTitle";
|
||||
import TabBarSeparator from "./tabBarSeparator";
|
||||
import TabBarHeader from "./tabBarHeader";
|
||||
import TabBarItem from "./tabBarItem";
|
||||
|
||||
export default class V2C_PublicServers extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.setInitialState();
|
||||
this.close = this.close.bind(this);
|
||||
this.changeCategory = this.changeCategory.bind(this);
|
||||
this.search = this.search.bind(this);
|
||||
this.searchKeyDown = this.searchKeyDown.bind(this);
|
||||
this.checkConnection = this.checkConnection.bind(this);
|
||||
this.join = this.join.bind(this);
|
||||
this.connect = this.connect.bind(this);
|
||||
|
||||
this.GuildStore = BDV2.WebpackModules.findByUniqueProperties(["getGuilds"]);
|
||||
this.AvatarDefaults = BDV2.WebpackModules.findByUniqueProperties(["getUserAvatarURL", "DEFAULT_AVATARS"]);
|
||||
this.InviteActions = BDV2.WebpackModules.findByUniqueProperties(["acceptInvite"]);
|
||||
this.SortedGuildStore = BDV2.WebpackModules.findByUniqueProperties(["getSortedGuilds"]);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.checkConnection();
|
||||
}
|
||||
|
||||
setInitialState() {
|
||||
this.state = {
|
||||
selectedCategory: -1,
|
||||
title: "Loading...",
|
||||
loading: true,
|
||||
servers: [],
|
||||
next: null,
|
||||
connection: {
|
||||
state: 0,
|
||||
user: null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
close() {
|
||||
BDV2.reactDom.unmountComponentAtNode(document.getElementById(this.props.rootId));
|
||||
}
|
||||
|
||||
search(query, clear) {
|
||||
const self = this;
|
||||
|
||||
$.ajax({
|
||||
method: "GET",
|
||||
url: `${self.endPoint}${query}${query ? "&schema=new" : "?schema=new"}`,
|
||||
success: data => {
|
||||
let servers = data.results.reduce((arr, server) => {
|
||||
server.joined = false;
|
||||
arr.push(server);
|
||||
// arr.push(<ServerCard server={server} join={self.join}/>);
|
||||
return arr;
|
||||
}, []);
|
||||
|
||||
if (!clear) {
|
||||
servers = self.state.servers.concat(servers);
|
||||
}
|
||||
else {
|
||||
//servers.unshift(self.bdServer);
|
||||
}
|
||||
|
||||
let end = data.size + data.from;
|
||||
data.next = `?from=${end}`;
|
||||
if (self.state.term) data.next += `&term=${self.state.term}`;
|
||||
if (self.state.selectedCategory) data.next += `&category=${self.categoryButtons[self.state.selectedCategory]}`;
|
||||
if (end >= data.total) {
|
||||
end = data.total;
|
||||
data.next = null;
|
||||
}
|
||||
|
||||
let title = `Showing 1-${end} of ${data.total} results in ${self.categoryButtons[self.state.selectedCategory]}`;
|
||||
if (self.state.term) title += ` for ${self.state.term}`;
|
||||
|
||||
self.setState({
|
||||
loading: false,
|
||||
title: title,
|
||||
servers: servers,
|
||||
next: data.next
|
||||
});
|
||||
|
||||
if (clear) {
|
||||
//console.log(self);
|
||||
self.refs.sbv.refs.contentScroller.scrollTop = 0;
|
||||
}
|
||||
},
|
||||
error: () => {
|
||||
self.setState({
|
||||
loading: false,
|
||||
title: "Failed to load servers. Check console for details"
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
join(serverCard) {
|
||||
if (serverCard.props.pinned) return this.InviteActions.acceptInvite(serverCard.props.invite_code);
|
||||
$.ajax({
|
||||
method: "GET",
|
||||
url: `${this.joinEndPoint}/${serverCard.props.server.identifier}`,
|
||||
headers: {
|
||||
"Accept": "application/json;",
|
||||
"Content-Type": "application/json;" ,
|
||||
"x-discord-token": this.state.connection.user.accessToken
|
||||
},
|
||||
crossDomain: true,
|
||||
xhrFields: {
|
||||
withCredentials: true
|
||||
},
|
||||
success: () => {
|
||||
serverCard.setState({joined: true});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
connect() {
|
||||
const self = this;
|
||||
const options = self.windowOptions;
|
||||
options.x = Math.round(window.screenX + window.innerWidth / 2 - options.width / 2);
|
||||
options.y = Math.round(window.screenY + window.innerHeight / 2 - options.height / 2);
|
||||
|
||||
self.joinWindow = new (window.require("electron").remote.BrowserWindow)(options);
|
||||
const url = "https://auth.discordservers.com/connect?scopes=guilds.join&previousUrl=https://auth.discordservers.com/info";
|
||||
self.joinWindow.webContents.on("did-navigate", (event, url) => {
|
||||
if (url != "https://auth.discordservers.com/info") return;
|
||||
self.joinWindow.close();
|
||||
self.checkConnection();
|
||||
});
|
||||
self.joinWindow.loadURL(url);
|
||||
}
|
||||
|
||||
get windowOptions() {
|
||||
return {
|
||||
width: 500,
|
||||
height: 550,
|
||||
backgroundColor: "#282b30",
|
||||
show: true,
|
||||
resizable: false,
|
||||
maximizable: false,
|
||||
minimizable: false,
|
||||
alwaysOnTop: true,
|
||||
frame: false,
|
||||
center: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
get bdServer() {
|
||||
const server = {
|
||||
name: "BetterDiscord",
|
||||
online: "7500+",
|
||||
members: "20000+",
|
||||
categories: ["community", "programming", "support"],
|
||||
description: "Official BetterDiscord server for support etc",
|
||||
identifier: "86004744966914048",
|
||||
iconUrl: "https://cdn.discordapp.com/icons/86004744966914048/292e7f6bfff2b71dfd13e508a859aedd.webp",
|
||||
nativejoin: true,
|
||||
invite_code: "0Tmfo5ZbORCRqbAd",
|
||||
pinned: true
|
||||
};
|
||||
const guildList = this.SortedGuildStore.getFlattenedGuildIds();
|
||||
const defaultList = this.AvatarDefaults.DEFAULT_AVATARS;
|
||||
return BDV2.react.createElement(ServerCard, {server: server, pinned: true, join: this.join, guildList: guildList, fallback: defaultList[Math.floor(Math.random() * 5)]});
|
||||
}
|
||||
|
||||
get endPoint() {
|
||||
return "https://search.discordservers.com";
|
||||
}
|
||||
|
||||
get joinEndPoint() {
|
||||
return "https://j.discordservers.com";
|
||||
}
|
||||
|
||||
get connectEndPoint() {
|
||||
return "https://join.discordservers.com/connect";
|
||||
}
|
||||
|
||||
checkConnection() {
|
||||
const self = this;
|
||||
try {
|
||||
$.ajax({
|
||||
method: "GET",
|
||||
url: `https://auth.discordservers.com/info`,
|
||||
headers: {
|
||||
"Accept": "application/json;",
|
||||
"Content-Type": "application/json;"
|
||||
},
|
||||
crossDomain: true,
|
||||
xhrFields: {
|
||||
withCredentials: true
|
||||
},
|
||||
success: data => {
|
||||
// Utils.log("PublicServer", "Got data: " + JSON.stringify(data));
|
||||
self.setState({
|
||||
selectedCategory: 0,
|
||||
connection: {
|
||||
state: 2,
|
||||
user: data
|
||||
}
|
||||
});
|
||||
self.search("", true);
|
||||
|
||||
},
|
||||
error: () => {
|
||||
self.setState({
|
||||
title: "Not connected to discordservers.com!",
|
||||
loading: true,
|
||||
selectedCategory: -1,
|
||||
connection: {
|
||||
state: 1,
|
||||
user: null
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
self.setState({
|
||||
title: "Not connected to discordservers.com!",
|
||||
loading: true,
|
||||
selectedCategory: -1,
|
||||
connection: {
|
||||
state: 1,
|
||||
user: null
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return BDV2.react.createElement(SidebarView, {ref: "sbv", children: this.component});
|
||||
}
|
||||
|
||||
get component() {
|
||||
return {
|
||||
sidebar: {
|
||||
component: this.sidebar
|
||||
},
|
||||
content: {
|
||||
component: this.content
|
||||
},
|
||||
tools: {
|
||||
component: BDV2.react.createElement(Tools, {key: "pt", ref: "tools", onClick: this.close})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
get sidebar() {
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "sidebar", key: "ps"},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-tab-bar SIDE"},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-tab-bar-header", style: {fontSize: "16px"}},
|
||||
"Public Servers"
|
||||
),
|
||||
BDV2.react.createElement(TabBarSeparator, null),
|
||||
this.searchInput,
|
||||
BDV2.react.createElement(TabBarSeparator, null),
|
||||
BDV2.react.createElement(TabBarHeader, {text: "Categories"}),
|
||||
this.categoryButtons.map((value, index) => {
|
||||
return BDV2.react.createElement(TabBarItem, {id: index, onClick: this.changeCategory, key: index, text: value, selected: this.state.selectedCategory === index});
|
||||
}),
|
||||
BDV2.react.createElement(TabBarSeparator, null),
|
||||
this.footer,
|
||||
this.connection
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
get searchInput() {
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-form-item"},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-text-input flex-vertical", style: {width: "172px", marginLeft: "10px"}},
|
||||
BDV2.react.createElement("input", {ref: "searchinput", onKeyDown: this.searchKeyDown, onChange: () => {}, type: "text", className: "input default", placeholder: "Search...", maxLength: "50"})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
searchKeyDown(e) {
|
||||
const self = this;
|
||||
if (self.state.loading || e.which !== 13) return;
|
||||
self.setState({
|
||||
loading: true,
|
||||
title: "Loading...",
|
||||
term: e.target.value
|
||||
});
|
||||
let query = `?term=${e.target.value}`;
|
||||
if (self.state.selectedCategory !== 0) {
|
||||
query += `&category=${self.categoryButtons[self.state.selectedCategory]}`;
|
||||
}
|
||||
self.search(query, true);
|
||||
}
|
||||
|
||||
get categoryButtons() {
|
||||
return ["All", "FPS Games", "MMO Games", "Strategy Games", "MOBA Games", "RPG Games", "Tabletop Games", "Sandbox Games", "Simulation Games", "Music", "Community", "Language", "Programming", "Other"];
|
||||
}
|
||||
|
||||
changeCategory(id) {
|
||||
const self = this;
|
||||
if (self.state.loading) return;
|
||||
self.refs.searchinput.value = "";
|
||||
self.setState({
|
||||
loading: true,
|
||||
selectedCategory: id,
|
||||
title: "Loading...",
|
||||
term: null
|
||||
});
|
||||
if (id === 0) {
|
||||
self.search("", true);
|
||||
return;
|
||||
}
|
||||
self.search(`?category=${self.categoryButtons[id]}`, true);
|
||||
}
|
||||
|
||||
get content() {
|
||||
const self = this;
|
||||
const guildList = this.SortedGuildStore.getFlattenedGuildIds();
|
||||
const defaultList = this.AvatarDefaults.DEFAULT_AVATARS;
|
||||
if (self.state.connection.state === 1) return self.notConnected;
|
||||
return [BDV2.react.createElement(
|
||||
"div",
|
||||
{ref: "content", key: "pc", className: "contentColumn-2hrIYH contentColumnDefault-1VQkGM content-column default"},
|
||||
BDV2.react.createElement(SettingsTitle, {text: self.state.title}),
|
||||
self.bdServer,
|
||||
self.state.servers.map((server) => {
|
||||
return BDV2.react.createElement(ServerCard, {key: server.identifier, server: server, join: self.join, guildList: guildList, fallback: defaultList[Math.floor(Math.random() * 5)]});
|
||||
}),
|
||||
self.state.next && BDV2.react.createElement(
|
||||
"button",
|
||||
{type: "button", onClick: () => {
|
||||
if (self.state.loading) return;self.setState({loading: true}); self.search(self.state.next, false);
|
||||
}, className: "ui-button filled brand small grow", style: {width: "100%", marginTop: "10px", marginBottom: "10px"}},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-button-contents"},
|
||||
self.state.loading ? "Loading" : "Load More"
|
||||
)
|
||||
),
|
||||
self.state.servers.length > 0 && BDV2.react.createElement(SettingsTitle, {text: self.state.title})
|
||||
)];
|
||||
}
|
||||
|
||||
get notConnected() {
|
||||
const self = this;
|
||||
//return BDV2.react.createElement(SettingsTitle, { text: self.state.title });
|
||||
return [BDV2.react.createElement(
|
||||
"div",
|
||||
{key: "ncc", ref: "content", className: "contentColumn-2hrIYH contentColumnDefault-1VQkGM content-column default"},
|
||||
BDV2.react.createElement(
|
||||
"h2",
|
||||
{className: "ui-form-title h2 margin-reset margin-bottom-20"},
|
||||
"Not connected to discordservers.com!",
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{
|
||||
onClick: self.connect,
|
||||
type: "button",
|
||||
className: "ui-button filled brand small grow",
|
||||
style: {
|
||||
display: "inline-block",
|
||||
minHeight: "18px",
|
||||
marginLeft: "10px",
|
||||
lineHeight: "14px"
|
||||
}
|
||||
},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-button-contents"},
|
||||
"Connect"
|
||||
)
|
||||
)
|
||||
), self.bdServer
|
||||
)];
|
||||
}
|
||||
|
||||
get footer() {
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-tab-bar-header"},
|
||||
BDV2.react.createElement(
|
||||
"a",
|
||||
{href: "https://discordservers.com", target: "_blank"},
|
||||
"Discordservers.com"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
get connection() {
|
||||
const self = this;
|
||||
const {connection} = self.state;
|
||||
if (connection.state !== 2) return BDV2.react.createElement("span", null);
|
||||
|
||||
return BDV2.react.createElement(
|
||||
"span",
|
||||
null,
|
||||
BDV2.react.createElement(TabBarSeparator, null),
|
||||
BDV2.react.createElement(
|
||||
"span",
|
||||
{style: {color: "#b9bbbe", fontSize: "10px", marginLeft: "10px"}},
|
||||
"Connected as: ",
|
||||
`${connection.user.username}#${connection.user.discriminator}`
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{style: {padding: "5px 10px 0 10px"}},
|
||||
BDV2.react.createElement(
|
||||
"button",
|
||||
{style: {width: "100%", minHeight: "20px"}, type: "button", className: "ui-button filled brand small grow"},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-button-contents", onClick: self.connect},
|
||||
"Reconnect"
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
export default class V2C_ReloadIcon extends BDV2.reactComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return BDV2.react.createElement("svg", {
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
viewBox: "0 0 24 24",
|
||||
fill: "#dcddde",
|
||||
className: "bd-reload " + this.props.className,
|
||||
onClick: this.props.onClick,
|
||||
style: {width: this.props.size || "24px", height: this.props.size || "24px"}
|
||||
},
|
||||
BDV2.react.createElement("path", {d: "M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"}),
|
||||
BDV2.react.createElement("path", {fill: "none", d: "M0 0h24v24H0z"})
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
export default class V2C_Scroller extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
//scrollerWrap-2lJEkd scrollerThemed-2oenus themeGhostHairline-DBD-2d scrollerFade-1Ijw5y
|
||||
let wrapperClass = `scrollerWrap-2lJEkd scrollerThemed-2oenus themeGhostHairline-DBD-2d${this.props.fade ? " scrollerFade-1Ijw5y" : ""}`;
|
||||
let scrollerClass = "scroller-2FKFPG scroller"; /* fuck */
|
||||
if (this.props.sidebar) scrollerClass = "scroller-2FKFPG firefoxFixScrollFlex-cnI2ix sidebarRegionScroller-3MXcoP sidebar-region-scroller scroller";
|
||||
if (this.props.contentColumn) {
|
||||
scrollerClass = "scroller-2FKFPG firefoxFixScrollFlex-cnI2ix contentRegionScroller-26nc1e content-region-scroller scroller"; /* fuck */
|
||||
wrapperClass = "scrollerWrap-2lJEkd firefoxFixScrollFlex-cnI2ix contentRegionScrollerWrap-3YZXdm content-region-scroller-wrap scrollerThemed-2oenus themeGhost-28MSn0 scrollerTrack-1ZIpsv";
|
||||
}
|
||||
const {children} = this.props;
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{key: "scrollerwrap", className: wrapperClass},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{key: "scroller", ref: "scroller", className: scrollerClass},
|
||||
children
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
import SettingsGroup from "./settingsGroup";
|
||||
|
||||
export default class V2C_SectionedSettingsPanel extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return BDV2.react.createElement(
|
||||
"div", {className: "contentColumn-2hrIYH contentColumnDefault-1VQkGM content-column default"},
|
||||
this.props.sections.map(section => {
|
||||
return BDV2.react.createElement(SettingsGroup, Object.assign({}, section, {onChange: this.props.onChange}));
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
export default class V2C_ServerCard extends BDV2.reactComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
if (!this.props.server.iconUrl) this.props.server.iconUrl = this.props.fallback;
|
||||
this.state = {
|
||||
imageError: false,
|
||||
joined: this.props.guildList.includes(this.props.server.identifier)
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const {server} = this.props;
|
||||
return BDV2.react.createElement(
|
||||
"div", // cardPrimary-1Hv-to
|
||||
{className: `card-3Qj_Yx cardPrimary-1Hv-to marginBottom8-AtZOdT bd-server-card${server.pinned ? " bd-server-card-pinned" : ""}`},
|
||||
// BDV2.react.createElement(
|
||||
// "div",
|
||||
// { className: "flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY flex-1O1GKY directionRow-3v3tfG justifyStart-2yIZo0 alignStretch-1hwxMa noWrap-3jynv6" },
|
||||
BDV2.react.createElement("img", {ref: "img", className: "bd-server-image", src: server.iconUrl, onError: this.handleError.bind(this)}),
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "flexChild-faoVW3 bd-server-content"},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY directionRow-3v3tfG noWrap-3jynv6 bd-server-header"},
|
||||
BDV2.react.createElement(
|
||||
"h5",
|
||||
{className: "h5-18_1nd defaultColor-1_ajX0 margin-reset bd-server-name"},
|
||||
server.name
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"h5",
|
||||
{className: "h5-18_1nd defaultColor-1_ajX0 margin-reset bd-server-member-count"},
|
||||
server.members,
|
||||
" Members"
|
||||
)
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY directionRow-3v3tfG noWrap-3jynv6"},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "scrollerWrap-2lJEkd scrollerThemed-2oenus themeGhostHairline-DBD-2d scrollerFade-1Ijw5y bd-server-description-container"},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "scroller-2FKFPG scroller bd-server-description"},
|
||||
server.description
|
||||
)
|
||||
)
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "flex-1xMQg5 flex-1O1GKY horizontal-1ae9ci horizontal-2EEEnY directionRow-3v3tfG noWrap-3jynv6 bd-server-footer"},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "flexChild-faoVW3 bd-server-tags", style: {flex: "1 1 auto"}},
|
||||
server.categories.join(", ")
|
||||
),
|
||||
this.state.joined && BDV2.react.createElement(
|
||||
"button",
|
||||
{type: "button", className: "button-38aScr lookFilled-1Gx00P colorBrand-3pXr91 sizeMin-1mJd1x grow-q77ONN colorGreen-29iAKY", style: {minHeight: "12px", marginTop: "4px", backgroundColor: "#3ac15c"}},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-button-contents"},
|
||||
"Joined"
|
||||
)
|
||||
),
|
||||
server.error && BDV2.react.createElement(
|
||||
"button",
|
||||
{type: "button", className: "button-38aScr lookFilled-1Gx00P colorBrand-3pXr91 sizeMin-1mJd1x grow-q77ONN disabled-9aF2ug", style: {minHeight: "12px", marginTop: "4px", backgroundColor: "#c13a3a"}},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-button-contents"},
|
||||
"Error"
|
||||
)
|
||||
),
|
||||
!server.error && !this.state.joined && BDV2.react.createElement(
|
||||
"button",
|
||||
{type: "button", className: "button-38aScr lookFilled-1Gx00P colorBrand-3pXr91 sizeMin-1mJd1x grow-q77ONN", style: {minHeight: "12px", marginTop: "4px"}, onClick: () => {this.join();}},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-button-contents"},
|
||||
"Join"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
// )
|
||||
);
|
||||
}
|
||||
|
||||
handleError() {
|
||||
this.props.server.iconUrl = this.props.fallback;
|
||||
this.setState({imageError: true});
|
||||
}
|
||||
|
||||
join() {
|
||||
this.props.join(this);
|
||||
//this.setState({joined: true});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import {settingsCookie} from "../0globals";
|
||||
import BDV2 from "../modules/v2";
|
||||
|
||||
import SettingsTitle from "./settingsTitle";
|
||||
import Switch from "./switch";
|
||||
|
||||
export default class V2C_SettingsGroup extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {title, settings, button} = this.props;
|
||||
const buttonComponent = button ? BDV2.react.createElement("button", {key: "title-button", className: "bd-pfbtn", onClick: button.onClick}, button.title) : null;
|
||||
return [BDV2.react.createElement(SettingsTitle, {text: title}),
|
||||
buttonComponent,
|
||||
settings.map(setting => {
|
||||
return BDV2.react.createElement(Switch, {id: setting.id, key: setting.id, data: setting, checked: settingsCookie[setting.id], onChange: (id, checked) => {
|
||||
this.props.onChange(id, checked);
|
||||
}});
|
||||
})];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import {settingsCookie} from "../0globals";
|
||||
import BDV2 from "../modules/v2";
|
||||
|
||||
import SettingsTitle from "./settingsTitle";
|
||||
import Switch from "./switch";
|
||||
|
||||
export default class V2C_SettingsPanel extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {settings} = this.props;
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "contentColumn-2hrIYH contentColumnDefault-1VQkGM content-column default"},
|
||||
BDV2.react.createElement(SettingsTitle, {text: this.props.title}),
|
||||
this.props.button && BDV2.react.createElement("button", {key: "title-button", className: "bd-pfbtn", onClick: this.props.button.onClick}, this.props.button.title),
|
||||
settings.map(setting => {
|
||||
return BDV2.react.createElement(Switch, {id: setting.id, key: setting.id, data: setting, checked: settingsCookie[setting.id], onChange: (id, checked) => {
|
||||
this.props.onChange(id, checked);
|
||||
}});
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
export default class V2C_SettingsTitle extends BDV2.reactComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
//h2-2gWE-o title-3sZWYQ size16-14cGz5 height20-mO2eIN weightSemiBold-NJexzi da-h2 da-title da-size16 da-height20 da-weightSemiBold defaultColor-1_ajX0 da-defaultColor marginTop60-3PGbtK da-marginTop60 marginBottom20-32qID7 da-marginBottom20
|
||||
render() {
|
||||
return BDV2.react.createElement(
|
||||
"h2",
|
||||
{className: "ui-form-title h2 margin-reset margin-bottom-20 marginTop60-3PGbtK da-marginTop6"},
|
||||
this.props.text
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
import TabBarSeparator from "./tabBarSeparator";
|
||||
import TabBarHeader from "./tabBarHeader";
|
||||
import TabBarItem from "./tabBarItem";
|
||||
|
||||
export default class V2C_SideBar extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const self = this;
|
||||
const si = $("[class*=side-] > [class*=selected]");
|
||||
if (si.length) self.scn = si.attr("class");
|
||||
const ns = $("[class*=side-] > [class*='item-']:not([class*=selected])");
|
||||
if (ns.length) self.nscn = ns.attr("class");
|
||||
$("[class*='side-'] > [class*='item-']").on("click", () => {
|
||||
self.setState({
|
||||
selected: null
|
||||
});
|
||||
});
|
||||
self.setInitialState();
|
||||
self.onClick = self.onClick.bind(self);
|
||||
}
|
||||
|
||||
setInitialState() {
|
||||
const self = this;
|
||||
self.state = {
|
||||
selected: null,
|
||||
items: self.props.items
|
||||
};
|
||||
|
||||
const initialSelection = self.props.items.find(item => {
|
||||
return item.selected;
|
||||
});
|
||||
if (initialSelection) {
|
||||
self.state.selected = initialSelection.id;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const self = this;
|
||||
const {headerText} = self.props;
|
||||
const {items, selected} = self.state;
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
null,
|
||||
BDV2.react.createElement(TabBarSeparator, null),
|
||||
BDV2.react.createElement(TabBarHeader, {text: headerText}),
|
||||
items.map(item => {
|
||||
const {id, text} = item;
|
||||
return BDV2.react.createElement(TabBarItem, {key: id, selected: selected === id, text: text, id: id, onClick: self.onClick});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
onClick(id) {
|
||||
const self = this;
|
||||
const si = $("[class*=side] > [class*=selected]");
|
||||
if (si.length) {
|
||||
si.off("click.bdsb").on("click.bsb", e => {
|
||||
$(e.target).attr("class", self.scn);
|
||||
});
|
||||
si.attr("class", self.nscn);
|
||||
}
|
||||
|
||||
self.setState({selected: null});
|
||||
self.setState({selected: id});
|
||||
|
||||
if (self.props.onClick) self.props.onClick(id);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
import Scroller from "./scroller";
|
||||
|
||||
export default class V2C_SidebarView extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {sidebar, content, tools} = this.props.children;
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "standardSidebarView-3F1I7i ui-standard-sidebar-view"},
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "sidebarRegion-VFTUkN sidebar-region"},
|
||||
BDV2.react.createElement(Scroller, {key: "sidebarScroller", ref: "sidebarScroller", sidebar: true, fade: sidebar.fade || true, dark: sidebar.dark || true, children: sidebar.component})
|
||||
),
|
||||
BDV2.react.createElement("div", {className: "contentRegion-3nDuYy content-region"},
|
||||
BDV2.react.createElement("div", {className: "contentTransitionWrap-3hqOEW content-transition-wrap"},
|
||||
BDV2.react.createElement("div", {className: "scrollerWrap-2lJEkd firefoxFixScrollFlex-cnI2ix contentRegionScrollerWrap-3YZXdm content-region-scroller-wrap scrollerThemed-2oenus themeGhost-28MSn0 scrollerTrack-1ZIpsv"},
|
||||
BDV2.react.createElement("div", {className: "scroller-2FKFPG firefoxFixScrollFlex-cnI2ix contentRegionScroller-26nc1e content-region-scroller scroller", ref: "contentScroller"},
|
||||
BDV2.react.createElement("div", {className: "contentColumn-2hrIYH contentColumnDefault-1VQkGM content-column default"}, content.component),
|
||||
tools.component
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
import Switch from "./components/switch";
|
||||
|
||||
export default class SwitchItem extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
}
|
||||
|
||||
onChange() {
|
||||
this.props.onChange(this.props.id, !this.state.checked);
|
||||
this.props.checked = !this.props.checked;
|
||||
}
|
||||
|
||||
render() {
|
||||
const {text, info} = this.props.data;
|
||||
return BDV2.react.createElement("div", {className: "ui-flex flex-vertical flex-justify-start flex-align-stretch flex-nowrap ui-switch-item"},
|
||||
BDV2.react.createElement("div", {className: "ui-flex flex-horizontal flex-justify-start flex-align-stretch flex-nowrap"},
|
||||
BDV2.react.createElement("h3", {className: "ui-form-title h3 margin-reset margin-reset ui-flex-child"}, text),
|
||||
BDV2.react.createElement(Switch, {onChange: this.onChange, checked: this.props.checked})
|
||||
),
|
||||
BDV2.react.createElement("div", {className: "ui-form-text style-description margin-top-4", style: {flex: "1 1 auto"}}, info)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
export default class V2C_TabBarHeader extends BDV2.reactComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "ui-tab-bar-header"},
|
||||
this.props.text
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
export default class V2C_TabBarItem extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.setInitialState();
|
||||
this.onClick = this.onClick.bind(this);
|
||||
}
|
||||
|
||||
setInitialState() {
|
||||
this.state = {
|
||||
selected: this.props.selected || false
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return BDV2.react.createElement(
|
||||
"div",
|
||||
{className: `ui-tab-bar-item${this.props.selected ? " selected" : ""}`, onClick: this.onClick},
|
||||
this.props.text
|
||||
);
|
||||
}
|
||||
|
||||
onClick() {
|
||||
if (this.props.onClick) {
|
||||
this.props.onClick(this.props.id);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
export default class V2C_TabBarSeparator extends BDV2.reactComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return BDV2.react.createElement("div", {className: "ui-tab-bar-separator margin-top-8 margin-bottom-8"});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
import {settingsCookie, themeCookie, bdthemes} from "../0globals";
|
||||
import Utils from "../modules/utils";
|
||||
import BDV2 from "../modules/v2";
|
||||
import themeModule from "../modules/themeModule";
|
||||
|
||||
import ReloadIcon from "./reloadIcon";
|
||||
import TooltipWrap from "./tooltipWrap";
|
||||
|
||||
export default class V2C_ThemeCard extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.setInitialState();
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.reload = this.reload.bind(this);
|
||||
}
|
||||
|
||||
setInitialState() {
|
||||
this.state = {
|
||||
checked: themeCookie[this.props.theme.name],
|
||||
reloads: 0
|
||||
};
|
||||
}
|
||||
|
||||
// componentDidMount() {
|
||||
// BDEvents.on("theme-reloaded", this.onReload);
|
||||
// }
|
||||
|
||||
// componentWillUnmount() {
|
||||
// BDEvents.off("theme-reloaded", this.onReload);
|
||||
// }
|
||||
|
||||
onReload(themeName) {
|
||||
if (themeName !== this.props.theme.name) return;
|
||||
this.setState({reloads: this.state.reloads + 1});
|
||||
}
|
||||
|
||||
reload() {
|
||||
const theme = this.props.theme.name;
|
||||
const error = themeModule.reloadTheme(theme);
|
||||
if (error) Utils.showToast(`Could not reload ${bdthemes[theme].name}. Check console for details.`, {type: "error"});
|
||||
else Utils.showToast(`${bdthemes[theme].name} v${bdthemes[theme].version} has been reloaded.`, {type: "success"});
|
||||
// this.setState(this.state);
|
||||
this.props.theme = bdthemes[theme];
|
||||
this.onReload(this.props.theme.name);
|
||||
}
|
||||
|
||||
makeLink(title, url) {
|
||||
const props = {className: "bda-link bda-link-website", target: "_blank"};
|
||||
if (typeof(url) == "string") props.href = url;
|
||||
if (typeof(url) == "function") props.onClick = (event) => {event.preventDefault(); event.stopPropagation(); url();};
|
||||
return BDV2.react.createElement("a", props, title);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {theme} = this.props;
|
||||
const name = theme.name;
|
||||
const description = theme.description;
|
||||
const version = theme.version;
|
||||
const author = theme.author;
|
||||
const meta = bdthemes[name];
|
||||
|
||||
const links = [];
|
||||
if (meta.website) links.push(this.makeLink("Website", meta.website));
|
||||
if (meta.source) links.push(this.makeLink("Source", meta.source));
|
||||
if (meta.invite) {
|
||||
links.push(this.makeLink("Support Server", () => {
|
||||
const tester = /\.gg\/(.*)$/;
|
||||
let code = meta.invite;
|
||||
if (tester.test(code)) code = code.match(tester)[1];
|
||||
BDV2.LayerStack.popLayer();
|
||||
BDV2.InviteActions.acceptInviteAndTransitionToInviteChannel(code);
|
||||
}));
|
||||
}
|
||||
if (meta.donate) links.push(this.makeLink("Donate", meta.donate));
|
||||
if (meta.patreon) links.push(this.makeLink("Patreon", meta.patreon));
|
||||
|
||||
const authorProps = {className: "bda-author"};
|
||||
if (meta.authorLink || meta.authorId) {
|
||||
authorProps.className += ` ${BDV2.anchorClasses.anchor} ${BDV2.anchorClasses.anchorUnderlineOnHover}`;
|
||||
authorProps.target = "_blank";
|
||||
|
||||
if (meta.authorLink) authorProps.href = meta.authorLink;
|
||||
if (meta.authorId) authorProps.onClick = () => {BDV2.LayerStack.popLayer(); BDV2.openDM(meta.authorId);};
|
||||
}
|
||||
|
||||
return BDV2.react.createElement("div", {"data-name": name, "data-version": version, "className": "settings-closed ui-switch-item bd-addon-card"},
|
||||
BDV2.react.createElement("div", {className: "bda-header"},
|
||||
BDV2.react.createElement("span", {className: "bda-header-title"},
|
||||
BDV2.react.createElement("span", {className: "bda-name"}, name),
|
||||
" v",
|
||||
BDV2.react.createElement("span", {className: "bda-version"}, version),
|
||||
" by ",
|
||||
BDV2.react.createElement(meta.authorLink || meta.authorId ? "a" : "span", authorProps, author)
|
||||
),
|
||||
BDV2.react.createElement("div", {className: "bda-controls"},
|
||||
!settingsCookie["fork-ps-5"] && BDV2.react.createElement(TooltipWrap(ReloadIcon, {color: "black", side: "top", text: "Reload"}), {className: "bd-reload-card", onClick: this.reload}),
|
||||
BDV2.react.createElement("label", {className: "ui-switch-wrapper ui-flex-child", style: {flex: "0 0 auto"}},
|
||||
BDV2.react.createElement("input", {checked: this.state.checked, onChange: this.onChange, className: "ui-switch-checkbox", type: "checkbox"}),
|
||||
BDV2.react.createElement("div", {className: this.state.checked ? "ui-switch checked" : "ui-switch"})
|
||||
)
|
||||
)
|
||||
),
|
||||
BDV2.react.createElement("div", {className: "bda-description-wrap scroller-wrap fade"},
|
||||
BDV2.react.createElement("div", {className: "bda-description scroller"}, description)
|
||||
),
|
||||
(!!links.length) && BDV2.react.createElement("div", {className: "bda-footer"},
|
||||
BDV2.react.createElement("span", {className: "bda-links"},
|
||||
...(links.map((element, index) => index < links.length - 1 ? [element, " | "] : element).flat())
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
onChange() {
|
||||
this.setState({checked: !this.state.checked});
|
||||
themeModule.toggleTheme(this.props.theme.name);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
import XSvg from "./xSvg";
|
||||
|
||||
export default class V2C_Tools extends BDV2.reactComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onClick = this.onClick.bind(this);
|
||||
}
|
||||
|
||||
render() {
|
||||
return BDV2.react.createElement("div", {className: "tools-container toolsContainer-1edPuj"},
|
||||
BDV2.react.createElement("div", {className: "tools tools-3-3s-N"},
|
||||
BDV2.react.createElement("div", {className: "container-1sFeqf"},
|
||||
BDV2.react.createElement("div",
|
||||
{className: "btn-close closeButton-1tv5uR", onClick: this.onClick},
|
||||
BDV2.react.createElement(XSvg, null)
|
||||
),
|
||||
BDV2.react.createElement(
|
||||
"div",
|
||||
{className: "esc-text keybind-KpFkfr"},
|
||||
"ESC"
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
onClick() {
|
||||
if (this.props.onClick) {
|
||||
this.props.onClick();
|
||||
}
|
||||
$(".closeButton-1tv5uR").first().click();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
/**
|
||||
* Tooltip that automatically show and hide themselves on mouseenter and mouseleave events.
|
||||
* Will also remove themselves if the node to watch is removed from DOM through
|
||||
* a MutationObserver.
|
||||
*
|
||||
* Note this is not using Discord's internals but normal DOM manipulation and emulates
|
||||
* Discord's own tooltips as closely as possible.
|
||||
*
|
||||
* @module EmulatedTooltip
|
||||
* @version 0.0.1
|
||||
*/
|
||||
|
||||
import Utils from "../modules/utils";
|
||||
import WebpackModules from "../modules/webpackModules";
|
||||
|
||||
const TooltipClasses = WebpackModules.findByProps("tooltip", "tooltipBlack");
|
||||
const TooltipLayers = WebpackModules.findByProps("layer", "layerContainer");
|
||||
|
||||
const getClass = function(sideOrColor) {
|
||||
const upperCase = sideOrColor[0].toUpperCase() + sideOrColor.slice(1);
|
||||
const tooltipClass = TooltipClasses[`tooltip${upperCase}`];
|
||||
if (tooltipClass) return tooltipClass;
|
||||
return null;
|
||||
};
|
||||
|
||||
const classExists = function(sideOrColor) {
|
||||
return getClass(sideOrColor) ? true : false;
|
||||
};
|
||||
|
||||
const toPx = function(value) {
|
||||
return `${value}px`;
|
||||
};
|
||||
|
||||
/* <div class="layer-v9HyYc da-layer" style="left: 234.5px; bottom: 51px;">
|
||||
<div class="tooltip-2QfLtc da-tooltip tooltipTop-XDDSxx tooltipBlack-PPG47z">
|
||||
<div class="tooltipPointer-3ZfirK da-tooltipPointer"></div>
|
||||
User Settings
|
||||
</div>
|
||||
</div> */
|
||||
|
||||
export default class EmulatedTooltip {
|
||||
/**
|
||||
*
|
||||
* @constructor
|
||||
* @param {(HTMLElement|jQuery)} node - DOM node to monitor and show the tooltip on
|
||||
* @param {string} tip - string to show in the tooltip
|
||||
* @param {object} options - additional options for the tooltip
|
||||
* @param {string} [options.style=black] - correlates to the discord styling/colors (black, brand, green, grey, red, yellow)
|
||||
* @param {string} [options.side=top] - can be any of top, right, bottom, left
|
||||
* @param {boolean} [options.preventFlip=false] - prevents moving the tooltip to the opposite side if it is too big or goes offscreen
|
||||
* @param {boolean} [options.disabled=false] - whether the tooltip should be disabled from showing on hover
|
||||
*/
|
||||
constructor(node, text, options = {}) {
|
||||
const {style = "black", side = "top", preventFlip = false, disabled = false} = options;
|
||||
this.node = node instanceof jQuery ? node[0] : node;
|
||||
this.label = text;
|
||||
this.style = style.toLowerCase();
|
||||
this.side = side.toLowerCase();
|
||||
this.preventFlip = preventFlip;
|
||||
this.disabled = disabled;
|
||||
|
||||
if (!classExists(this.side)) return Utils.err("EmulatedTooltip", `Side ${this.side} does not exist.`);
|
||||
if (!classExists(this.style)) return Utils.err("EmulatedTooltip", `Style ${this.style} does not exist.`);
|
||||
|
||||
this.element = $(`<div class="${TooltipLayers.layer}">`)[0];
|
||||
this.tooltipElement = $(`<div class="${TooltipClasses.tooltip} ${getClass(this.style)}"><div class="${TooltipClasses.tooltipPointer}"></div>${this.label}</div>`)[0];
|
||||
this.labelElement = this.tooltipElement.childNodes[1];
|
||||
this.element.append(this.tooltipElement);
|
||||
|
||||
|
||||
this.node.addEventListener("mouseenter", () => {
|
||||
if (this.disabled) return;
|
||||
this.show();
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
const nodes = Array.from(mutation.removedNodes);
|
||||
const directMatch = nodes.indexOf(this.node) > -1;
|
||||
const parentMatch = nodes.some(parent => parent.contains(this.node));
|
||||
if (directMatch || parentMatch) {
|
||||
this.hide();
|
||||
observer.disconnect();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.body, {subtree: true, childList: true});
|
||||
});
|
||||
|
||||
this.node.addEventListener("mouseleave", () => {
|
||||
this.hide();
|
||||
});
|
||||
}
|
||||
|
||||
/** Container where the tooltip will be appended. */
|
||||
get container() { return document.querySelector(".popouts-2bnG9Z ~ .layerContainer-yqaFcK"); }
|
||||
/** Boolean representing if the tooltip will fit on screen above the element */
|
||||
get canShowAbove() { return this.node.getBoundingClientRect().top - this.element.offsetHeight >= 0; }
|
||||
/** Boolean representing if the tooltip will fit on screen below the element */
|
||||
get canShowBelow() { return this.node.getBoundingClientRect().top + this.node.offsetHeight + this.element.offsetHeight <= Utils.screenHeight; }
|
||||
/** Boolean representing if the tooltip will fit on screen to the left of the element */
|
||||
get canShowLeft() { return this.node.getBoundingClientRect().left - this.element.offsetWidth >= 0; }
|
||||
/** Boolean representing if the tooltip will fit on screen to the right of the element */
|
||||
get canShowRight() { return this.node.getBoundingClientRect().left + this.node.offsetWidth + this.element.offsetWidth <= Utils.screenWidth; }
|
||||
|
||||
/** Hides the tooltip. Automatically called on mouseleave. */
|
||||
hide() {
|
||||
this.element.remove();
|
||||
this.tooltipElement.className = this._className;
|
||||
}
|
||||
|
||||
/** Shows the tooltip. Automatically called on mouseenter. Will attempt to flip if position was wrong. */
|
||||
show() {
|
||||
this.tooltipElement.className = `${TooltipClasses.tooltip} ${getClass(this.style)}`;
|
||||
this.labelElement.textContent = this.label;
|
||||
this.element.appendTo(this.container);
|
||||
|
||||
if (this.side == "top") {
|
||||
if (this.canShowAbove || (!this.canShowAbove && this.preventFlip)) this.showAbove();
|
||||
else this.showBelow();
|
||||
}
|
||||
|
||||
if (this.side == "bottom") {
|
||||
if (this.canShowBelow || (!this.canShowBelow && this.preventFlip)) this.showBelow();
|
||||
else this.showAbove();
|
||||
}
|
||||
|
||||
if (this.side == "left") {
|
||||
if (this.canShowLeft || (!this.canShowLeft && this.preventFlip)) this.showLeft();
|
||||
else this.showRight();
|
||||
}
|
||||
|
||||
if (this.side == "right") {
|
||||
if (this.canShowRight || (!this.canShowRight && this.preventFlip)) this.showRight();
|
||||
else this.showLeft();
|
||||
}
|
||||
}
|
||||
|
||||
/** Force showing the tooltip above the node. */
|
||||
showAbove() {
|
||||
this.tooltipElement.addClass(getClass("top"));
|
||||
this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top - this.element.offsetHeight - 10));
|
||||
this.centerHorizontally();
|
||||
}
|
||||
|
||||
/** Force showing the tooltip below the node. */
|
||||
showBelow() {
|
||||
this.tooltipElement.addClass(getClass("bottom"));
|
||||
this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top + this.node.offsetHeight + 10));
|
||||
this.centerHorizontally();
|
||||
}
|
||||
|
||||
/** Force showing the tooltip to the left of the node. */
|
||||
showLeft() {
|
||||
this.tooltipElement.addClass(getClass("left"));
|
||||
this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left - this.element.offsetWidth - 10));
|
||||
this.centerVertically();
|
||||
}
|
||||
|
||||
/** Force showing the tooltip to the right of the node. */
|
||||
showRight() {
|
||||
this.tooltipElement.addClass(getClass("right"));
|
||||
this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left + this.node.offsetWidth + 10));
|
||||
this.centerVertically();
|
||||
}
|
||||
|
||||
centerHorizontally() {
|
||||
const nodecenter = this.node.getBoundingClientRect().left + (this.node.offsetWidth / 2);
|
||||
this.element.style.setProperty("left", toPx(nodecenter - (this.element.offsetWidth / 2)));
|
||||
}
|
||||
|
||||
centerVertically() {
|
||||
const nodecenter = this.node.getBoundingClientRect().top + (this.node.offsetHeight / 2);
|
||||
this.element.style.setProperty("top", toPx(nodecenter - (this.element.offsetHeight / 2)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
import Tooltip from "./tooltip";
|
||||
|
||||
export default class extends BDV2.reactComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onMouseEnter = this.onMouseEnter.bind(this);
|
||||
this.onMouseLeave = this.onMouseLeave.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const {style = "black", side = "top", text = ""} = this.props;
|
||||
this.node = BDV2.reactDom.findDOMNode(this);
|
||||
// this.node.addEventListener("mouseenter", this.onMouseEnter);
|
||||
// this.node.addEventListener("mouseleave", this.onMouseLeave);
|
||||
this.tooltip = new Tooltip(this.node, text, {style, side});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// this.node.removeEventListener("mouseenter", this.onMouseEnter);
|
||||
// this.node.removeEventListener("mouseleave", this.onMouseLeave);
|
||||
this.tooltip.hide();
|
||||
delete this.tooltip;
|
||||
}
|
||||
|
||||
onMouseEnter() {
|
||||
|
||||
}
|
||||
|
||||
onMouseLeave() {
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import BDV2 from "../modules/v2";
|
||||
|
||||
export default class V2C_XSvg extends BDV2.reactComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return BDV2.react.createElement(
|
||||
"svg",
|
||||
{xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 12 12", style: {width: "18px", height: "18px"}},
|
||||
BDV2.react.createElement(
|
||||
"g",
|
||||
{className: "background", fill: "none", fillRule: "evenodd"},
|
||||
BDV2.react.createElement("path", {d: "M0 0h12v12H0"}),
|
||||
BDV2.react.createElement("path", {className: "fill", fill: "#dcddde", d: "M9.5 3.205L8.795 2.5 6 5.295 3.205 2.5l-.705.705L5.295 6 2.5 8.795l.705.705L6 6.705 8.795 9.5l.705-.705L6.705 6"})
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
const path = require("path");
|
||||
const CircularDependencyPlugin = require("circular-dependency-plugin");
|
||||
|
||||
module.exports = {
|
||||
mode: "development",
|
||||
target: "node",
|
||||
devtool: "eval-cheap-source-map",
|
||||
entry: "./src/index.js",
|
||||
output: {
|
||||
filename: "main.js",
|
||||
path: path.resolve(__dirname, "js"),
|
||||
library: "Core",
|
||||
libraryTarget: "var",
|
||||
libraryExport: "default"
|
||||
},
|
||||
externals: {
|
||||
electron: `require("electron")`,
|
||||
fs: `require("fs")`,
|
||||
path: `require("path")`,
|
||||
request: `require("request")`,
|
||||
events: `require("events")`
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".js", ".jsx"],
|
||||
modules: [
|
||||
path.resolve("src", "builtins"),
|
||||
path.resolve("src", "data"),
|
||||
path.resolve("src", "modules")
|
||||
]
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /.jsx?$/,
|
||||
loader: "babel-loader",
|
||||
exclude: /node_modules/,
|
||||
query: {
|
||||
presets: [["@babel/env", {
|
||||
targets: {
|
||||
node: "12.8.1",
|
||||
chrome: "78"
|
||||
}
|
||||
}], "@babel/react"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new CircularDependencyPlugin({
|
||||
// exclude detection of files based on a RegExp
|
||||
exclude: /a\.js|node_modules/,
|
||||
// add errors to webpack instead of warnings
|
||||
// failOnError: true,
|
||||
// set the current working directory for displaying module paths
|
||||
cwd: process.cwd(),
|
||||
})
|
||||
]
|
||||
|
||||
};
|
Loading…
Reference in New Issue