Adds facelist for addon content
- Redesign of addon cards - Addon settings moved to modals - Fixes error with extra title in PS - Fixes error with pagination in PS - Fixes several issues with detached windows
This commit is contained in:
parent
67c29bbce5
commit
5b8f2f2de3
|
@ -33,7 +33,6 @@ export default [
|
|||
shown: false,
|
||||
settings: [
|
||||
{type: "switch", id: "addonErrors", value: true},
|
||||
{type: "switch", id: "autoScroll", value: true},
|
||||
{type: "switch", id: "autoReload", value: true},
|
||||
{type: "dropdown", id: "editAction", value: "detached", options: [{value: "detached"}, {value: "system"}]}
|
||||
]
|
||||
|
|
|
@ -71,10 +71,6 @@ export default {
|
|||
name: "Show Addon Errors",
|
||||
note: "Shows a modal with plugin/theme errors"
|
||||
},
|
||||
autoScroll: {
|
||||
name: "Scroll To Settings",
|
||||
note: "Auto-scrolls to a plugin's settings when the button is clicked (only if out of view)"
|
||||
},
|
||||
autoReload: {
|
||||
name: "Automatic Loading",
|
||||
note: "Automatically loads, reloads, and unloads plugins and themes"
|
||||
|
@ -223,6 +219,7 @@ export default {
|
|||
couldNotDisable: "{{name}} could not be disabled.",
|
||||
couldNotStart: "{{name}} could not be started.",
|
||||
couldNotStop: "{{name}} could not be stopped.",
|
||||
settingsError: "Could not open settings for {{name}}",
|
||||
methodError: "{{method}} could not be fired.",
|
||||
unknownAuthor: "Unknown Author",
|
||||
noDescription: "Description not provided.",
|
||||
|
@ -276,6 +273,7 @@ export default {
|
|||
Modals: {
|
||||
confirmAction: "Are You Sure?",
|
||||
okay: "Okay",
|
||||
done: "Done",
|
||||
cancel: "Cancel",
|
||||
nevermind: "Nevermind",
|
||||
close: "Close",
|
||||
|
|
|
@ -46,6 +46,7 @@ export default class AddonManager {
|
|||
this.timeCache = {};
|
||||
this.addonList = [];
|
||||
this.state = {};
|
||||
this.windows = new Set();
|
||||
}
|
||||
|
||||
initialize() {
|
||||
|
@ -343,9 +344,12 @@ export default class AddonManager {
|
|||
const fullPath = path.resolve(this.addonFolder, addon.filename);
|
||||
const content = fs.readFileSync(fullPath).toString();
|
||||
|
||||
if (this.windows.has(fullPath)) return;
|
||||
this.windows.add(fullPath);
|
||||
|
||||
const editorRef = React.createRef();
|
||||
const editor = React.createElement(AddonEditor, {
|
||||
id: "bd-floating-editor-" + addon.name,
|
||||
id: "bd-floating-editor-" + addon.id,
|
||||
ref: editorRef,
|
||||
content: content,
|
||||
save: this.saveAddon.bind(this, addon),
|
||||
|
@ -355,14 +359,14 @@ export default class AddonManager {
|
|||
|
||||
FloatingWindows.open({
|
||||
onClose: () => {
|
||||
this.isDetached = false;
|
||||
this.windows.delete(fullPath);
|
||||
},
|
||||
onResize: () => {
|
||||
if (!editorRef || !editorRef.current || !editorRef.current.resize) return;
|
||||
editorRef.current.resize();
|
||||
},
|
||||
title: addon.name,
|
||||
id: content.id,
|
||||
id: "bd-floating-window-" + addon.id,
|
||||
className: "floating-addon-window",
|
||||
height: 470,
|
||||
width: 410,
|
||||
|
|
|
@ -60,7 +60,7 @@ export default new class DataStore {
|
|||
const newSettings = {
|
||||
general: {publicServers: oldSettings["bda-gs-1"], voiceDisconnect: oldSettings["bda-dc-0"], classNormalizer: oldSettings["fork-ps-4"], showToasts: oldSettings["fork-ps-2"]},
|
||||
appearance: {twentyFourHour: oldSettings["bda-gs-6"], voiceMode: oldSettings["bda-gs-4"], minimalMode: oldSettings["bda-gs-2"], hideChannels: oldSettings["bda-gs-3"], darkMode: oldSettings["bda-gs-5"], coloredText: oldSettings["bda-gs-7"]},
|
||||
addons: {addonErrors: oldSettings["fork-ps-1"], autoScroll: oldSettings["fork-ps-3"], autoReload: oldSettings["fork-ps-5"]},
|
||||
addons: {addonErrors: oldSettings["fork-ps-1"], autoReload: oldSettings["fork-ps-5"]},
|
||||
developer: {debuggerHotkey: oldSettings["bda-gs-8"], copySelector: oldSettings["fork-dm-1"], reactDevTools: oldSettings.reactDevTools}
|
||||
};
|
||||
|
||||
|
|
|
@ -241,4 +241,8 @@
|
|||
.bd-pagination button[disabled] {
|
||||
opacity: 0.2;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.bd-pagination + .bd-settings-title {
|
||||
margin-top: 20px;
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
color: #fff;
|
||||
border-radius: 3px;
|
||||
padding: 3px 6px;
|
||||
transition: opacity 250ms ease;
|
||||
}
|
||||
|
||||
.bd-button:hover {
|
||||
|
@ -23,4 +24,24 @@
|
|||
|
||||
.bd-button.bd-button-success:active {
|
||||
background-color: rgb(46, 154, 74);
|
||||
}
|
||||
|
||||
.bd-button.bd-button-danger {
|
||||
background-color: #f04747;
|
||||
}
|
||||
|
||||
.bd-button.bd-button-danger:hover {
|
||||
background-color: rgb(237, 42, 42);
|
||||
}
|
||||
|
||||
.bd-button.bd-button-danger:active {
|
||||
background-color: rgb(230, 18, 18);
|
||||
}
|
||||
|
||||
.bd-button-disabled {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.bd-button-disabled:hover {
|
||||
cursor: not-allowed;
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
@import "./ui/*";
|
||||
@import "./buttons.css";
|
||||
@import "./spinner.css";
|
||||
@import "./search.css";
|
||||
|
||||
.bd-chat-badge {
|
||||
vertical-align: bottom;
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
.bd-search-wrapper {
|
||||
padding: 3px;
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
border: 0;
|
||||
background-color: var(--background-tertiary);
|
||||
color: var(--text-muted);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bd-search {
|
||||
padding: 2px 3px;
|
||||
background: none;
|
||||
border: 0;
|
||||
color: var(--text-normal);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.bd-search::-webkit-input-placeholder {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.bd-search-wrapper > svg {
|
||||
margin-right: 2px;
|
||||
fill: var(--interactive-normal);
|
||||
}
|
|
@ -27,8 +27,7 @@
|
|||
.bd-addon-list .bd-addon-card {
|
||||
max-height: 175px;
|
||||
margin-bottom: 20px;
|
||||
padding: 5px 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 12px;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
@ -36,13 +35,11 @@
|
|||
.theme-dark .bd-addon-list .bd-addon-card {
|
||||
background-color: rgba(32, 34, 37, 0.6);
|
||||
color: #f6f6f7;
|
||||
border-color: #202225;
|
||||
}
|
||||
|
||||
.theme-light .bd-addon-list .bd-addon-card {
|
||||
background-color: #f8f9f9;
|
||||
color: #4f545c;
|
||||
border-color: #dcddde;
|
||||
}
|
||||
|
||||
.bd-addon-list .bd-addon-card.settings-open {
|
||||
|
@ -51,24 +48,20 @@
|
|||
}
|
||||
|
||||
.bd-addon-list .bd-addon-header {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-bottom: 5px;
|
||||
border-bottom: 1px solid transparent;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.theme-dark .bd-addon-list .bd-addon-header {
|
||||
color: #f6f6f7;
|
||||
border-bottom-color: rgba(114, 118, 125, 0.3);
|
||||
}
|
||||
|
||||
.theme-light .bd-addon-list .bd-addon-header {
|
||||
color: #4f545c;
|
||||
border-bottom-color: rgba(185, 187, 190, 0.3);
|
||||
}
|
||||
|
||||
.bd-addon-list .bd-description {
|
||||
|
@ -77,6 +70,7 @@
|
|||
margin: 5px 0;
|
||||
padding: 5px 0;
|
||||
overflow-y: auto;
|
||||
line-height: 1.125em;
|
||||
}
|
||||
|
||||
.theme-dark .bd-addon-list .bd-description {
|
||||
|
@ -99,7 +93,7 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-top: 5px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px solid transparent;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
@ -112,15 +106,6 @@
|
|||
border-top-color: rgba(185, 187, 190, 0.3);
|
||||
}
|
||||
|
||||
.bd-addon-list .bd-footer button {
|
||||
padding: 3px 16px;
|
||||
transition: opacity 250ms ease;
|
||||
}
|
||||
|
||||
.bd-addon-list .bd-footer button:disabled {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.bd-addon-list .bd-footer a {
|
||||
color: #3e82e5;
|
||||
}
|
||||
|
@ -129,44 +114,48 @@
|
|||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.bd-controls + .bd-addon-list {
|
||||
margin-top: 10px;
|
||||
.bd-controls > .bd-addon-button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.bd-addon-button {
|
||||
cursor: pointer;
|
||||
.bd-links .bd-addon-button + .bd-addon-button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.bd-addon-button + .bd-addon-button {
|
||||
margin-left: 5px;
|
||||
.bd-controls > .bd-addon-button svg {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.bd-search-wrapper {
|
||||
padding: 3px;
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
border: 0;
|
||||
background-color: var(--background-tertiary);
|
||||
color: var(--text-muted);
|
||||
.bd-controls > .bd-addon-button:first-of-type {
|
||||
border-radius: 5px 0 0 5px;
|
||||
}
|
||||
|
||||
.bd-controls > .bd-addon-button:last-of-type {
|
||||
border-radius: 0 5px 5px 0;
|
||||
}
|
||||
|
||||
.bd-addon-list .bd-footer .bd-links,
|
||||
.bd-addon-list .bd-footer .bd-links a,
|
||||
.bd-addon-list .bd-footer .bd-addon-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bd-search {
|
||||
padding: 2px 3px;
|
||||
background: none;
|
||||
border: 0;
|
||||
color: var(--text-normal);
|
||||
flex: 1;
|
||||
.bd-addon-list .bd-footer .bd-links .bd-addon-button {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.bd-search::-webkit-input-placeholder {
|
||||
color: var(--text-muted);
|
||||
.bd-controls + .bd-addon-list {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.bd-search-wrapper > svg {
|
||||
margin-right: 2px;
|
||||
fill: var(--interactive-normal);
|
||||
.bd-links .bd-addon-button svg {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.bd-links .bd-addon-button:active svg,
|
||||
.bd-links .bd-addon-button:hover svg {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.bd-addon-controls {
|
||||
|
@ -196,4 +185,28 @@
|
|||
.settings-open .bd-close {
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.bd-addon-modal {
|
||||
min-height: unset;
|
||||
}
|
||||
|
||||
.bd-addon-modal-settings {
|
||||
/* padding: 16px; */
|
||||
}
|
||||
|
||||
.bd-addon-modal-footer .bd-button {
|
||||
background-color: #3e82e5;
|
||||
color: #fff;
|
||||
border-radius: 3px;
|
||||
padding: 3px 6px;
|
||||
transition: opacity 250ms ease;
|
||||
}
|
||||
|
||||
.bd-addon-modal-footer .bd-button:hover {
|
||||
background-color: rgb(56, 117, 206);
|
||||
}
|
||||
|
||||
.bd-addon-modal-footer .bd-button:active {
|
||||
background-color: rgb(50, 104, 183);
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import {React, Logger} from "modules";
|
||||
import {remote} from "electron";
|
||||
|
||||
export default class ErrorBoundary extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
|
@ -17,24 +17,27 @@ class FloatingWindowContainer extends React.Component {
|
|||
|
||||
render() {
|
||||
return this.state.windows.map(window =>
|
||||
<FloatingWindow {...window} close={this.close.bind(this, window.id)} minY={this.minY}>
|
||||
<FloatingWindow {...window} close={this.close.bind(this, window.id)} minY={this.minY} key={window.id}>
|
||||
{window.children}
|
||||
</FloatingWindow>
|
||||
);
|
||||
}
|
||||
|
||||
open(window) {
|
||||
this.setState({
|
||||
windows: [...this.state.windows, window]
|
||||
this.setState(state => {
|
||||
state.windows.push(window);
|
||||
return {windows: state.windows};
|
||||
});
|
||||
}
|
||||
|
||||
close(id) {
|
||||
this.setState({
|
||||
windows: this.state.windows.filter(w => {
|
||||
if (w.id == id && w.onClose) w.onClose();
|
||||
return w.id != id;
|
||||
})
|
||||
this.setState(state => {
|
||||
return {
|
||||
windows: state.windows.filter(w => {
|
||||
if (w.id == id && w.onClose) w.onClose();
|
||||
return w.id != id;
|
||||
})
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import {React} from "modules";
|
||||
|
||||
export default class DollarSign extends React.Component {
|
||||
render() {
|
||||
const size = this.props.size || "18px";
|
||||
return <svg viewBox="2 2 20 20" fill="#FFFFFF" style={{width: size, height: size}} onClick={this.props.onClick}>
|
||||
<path d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1.41 16.09V20h-2.67v-1.93c-1.71-.36-3.16-1.46-3.27-3.4h1.96c.1 1.05.82 1.87 2.65 1.87 1.96 0 2.4-.98 2.4-1.59 0-.83-.44-1.61-2.67-2.14-2.48-.6-4.18-1.62-4.18-3.67 0-1.72 1.39-2.84 3.11-3.21V4h2.67v1.95c1.86.45 2.79 1.86 2.85 3.39H14.3c-.05-1.11-.64-1.87-2.22-1.87-1.5 0-2.4.68-2.4 1.64 0 .84.65 1.39 2.67 1.91s4.18 1.39 4.18 3.91c-.01 1.83-1.38 2.83-3.12 3.16z"/>
|
||||
</svg>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import {React} from "modules";
|
||||
|
||||
export default class GitHub extends React.Component {
|
||||
render() {
|
||||
const size = this.props.size || "18px";
|
||||
return <svg viewBox="0 0 24 24" fill="#FFFFFF" style={{width: size, height: size}} onClick={this.props.onClick}>
|
||||
<path d="m12 .5c-6.63 0-12 5.28-12 11.792 0 5.211 3.438 9.63 8.205 11.188.6.111.82-.254.82-.567 0-.28-.01-1.022-.015-2.005-3.338.711-4.042-1.582-4.042-1.582-.546-1.361-1.335-1.725-1.335-1.725-1.087-.731.084-.716.084-.716 1.205.082 1.838 1.215 1.838 1.215 1.07 1.803 2.809 1.282 3.495.981.108-.763.417-1.282.76-1.577-2.665-.295-5.466-1.309-5.466-5.827 0-1.287.465-2.339 1.235-3.164-.135-.298-.54-1.497.105-3.121 0 0 1.005-.316 3.3 1.209.96-.262 1.98-.392 3-.398 1.02.006 2.04.136 3 .398 2.28-1.525 3.285-1.209 3.285-1.209.645 1.624.24 2.823.12 3.121.765.825 1.23 1.877 1.23 3.164 0 4.53-2.805 5.527-5.475 5.817.42.354.81 1.077.81 2.182 0 1.578-.015 2.846-.015 3.229 0 .309.21.678.825.56 4.801-1.548 8.236-5.97 8.236-11.173 0-6.512-5.373-11.792-12-11.792z" />
|
||||
</svg>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import {React} from "modules";
|
||||
|
||||
export default class Globe extends React.Component {
|
||||
render() {
|
||||
const size = this.props.size || "18px";
|
||||
return <svg viewBox="2 2 20 20" fill="#FFFFFF" style={{width: size, height: size}} onClick={this.props.onClick}>
|
||||
<path d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95c-.32-1.25-.78-2.45-1.38-3.56 1.84.63 3.37 1.91 4.33 3.56zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96zM4.26 14C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2 0 .68.06 1.34.14 2H4.26zm.82 2h2.95c.32 1.25.78 2.45 1.38 3.56-1.84-.63-3.37-1.9-4.33-3.56zm2.95-8H5.08c.96-1.66 2.49-2.93 4.33-3.56C8.81 5.55 8.35 6.75 8.03 8zM12 19.96c-.83-1.2-1.48-2.53-1.91-3.96h3.82c-.43 1.43-1.08 2.76-1.91 3.96zM14.34 14H9.66c-.09-.66-.16-1.32-.16-2 0-.68.07-1.35.16-2h4.68c.09.65.16 1.32.16 2 0 .68-.07 1.34-.16 2zm.25 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95c-.96 1.65-2.49 2.93-4.33 3.56zM16.36 14c.08-.66.14-1.32.14-2 0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2h-3.38z"/>
|
||||
</svg>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import {React} from "modules";
|
||||
|
||||
export default class Patreon extends React.Component {
|
||||
render() {
|
||||
const size = this.props.size || "18px";
|
||||
return <svg viewBox="0 0 24 24" fill="#FFFFFF" style={{width: size, height: size}} onClick={this.props.onClick}>
|
||||
<path d="m0 .5h4.219v23h-4.219z"/>
|
||||
<path d="m15.384.5c-4.767 0-8.644 3.873-8.644 8.633 0 4.75 3.877 8.61 8.644 8.61 4.754 0 8.616-3.865 8.616-8.61 0-4.759-3.863-8.633-8.616-8.633z"/>
|
||||
</svg>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import {React} from "modules";
|
||||
|
||||
export default class Support extends React.Component {
|
||||
render() {
|
||||
const size = this.props.size || "18px";
|
||||
return <svg viewBox="2 2 20 20" fill="#FFFFFF" style={{width: size, height: size}} onClick={this.props.onClick}>
|
||||
<path d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"/>
|
||||
</svg>;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import {Config} from "data";
|
||||
import {Logger, WebpackModules, React, Settings, Strings, DOM, DiscordModules} from "modules";
|
||||
import FormattableString from "../structs/string";
|
||||
import ErrorBoundary from "./errorboundary";
|
||||
|
||||
export default class Modals {
|
||||
|
||||
|
@ -8,10 +9,15 @@ export default class Modals {
|
|||
|
||||
static get ModalActions() {return WebpackModules.getByProps("openModal", "updateModal");}
|
||||
static get ModalStack() {return WebpackModules.getByProps("push", "update", "pop", "popWithKey");}
|
||||
static get ModalComponents() {return WebpackModules.getByProps("ModalRoot");}
|
||||
static get ModalClasses() {return WebpackModules.getByProps("modal", "content");}
|
||||
static get AlertModal() {return WebpackModules.getByPrototypes("handleCancel", "handleSubmit", "handleMinorConfirm");}
|
||||
static get FlexElements() {return WebpackModules.getByProps("Child", "Align");}
|
||||
static get FormTitle() {return WebpackModules.findByDisplayName("FormTitle");}
|
||||
static get TextElement() {return WebpackModules.getByProps("Sizes", "Weights");}
|
||||
static get ConfirmationModal() {return WebpackModules.findByDisplayName("ConfirmModal");}
|
||||
static get Markdown() {return WebpackModules.findByDisplayName("Markdown");}
|
||||
static get Buttons() {return WebpackModules.getByProps("ButtonColors");}
|
||||
|
||||
static default(title, content) {
|
||||
const modal = DOM.createElement(`<div class="bd-modal-wrapper theme-dark">
|
||||
|
@ -78,7 +84,7 @@ export default class Modals {
|
|||
return ModalActions.openModal(props => {
|
||||
return React.createElement(ConfirmationModal, Object.assign({
|
||||
header: title,
|
||||
red: danger,
|
||||
confirmButtonColor: danger ? this.Buttons.ButtonColors.RED : this.Buttons.ButtonColors.PRIMARY,
|
||||
confirmText: confirmText,
|
||||
cancelText: cancelText,
|
||||
onConfirm: onConfirm,
|
||||
|
@ -230,4 +236,55 @@ export default class Modals {
|
|||
};
|
||||
return key;
|
||||
}
|
||||
|
||||
static showAddonSettingsModal(name, panel) {
|
||||
|
||||
let child = panel;
|
||||
if (panel instanceof Node || typeof(panel) === "string") {
|
||||
child = class ReactWrapper extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.elementRef = React.createRef();
|
||||
this.element = panel;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.element instanceof Node) this.elementRef.current.appendChild(this.element);
|
||||
// if (typeof(this.element) === "string") this.elementRef.current.appendChild(this.element);
|
||||
}
|
||||
|
||||
render() {
|
||||
const props = {
|
||||
className: "bd-addon-settings-wrap",
|
||||
ref: this.elementRef
|
||||
};
|
||||
if (typeof(this.element) === "string") props.dangerouslySetInnerHTML = {__html: this.element};
|
||||
return React.createElement("div", props);
|
||||
}
|
||||
};
|
||||
}
|
||||
if (typeof(child) === "function") child = React.createElement(child);
|
||||
|
||||
const mc = this.ModalComponents;
|
||||
const modal = props => {
|
||||
return React.createElement(mc.ModalRoot, Object.assign({size: mc.ModalSize.MEDIUM, className: "bd-addon-modal"}, props),
|
||||
React.createElement(mc.ModalHeader, {separator: false, className: "bd-addon-modal-header"},
|
||||
React.createElement(this.FormTitle, {tag: "h4"}, `${name} Settings`),
|
||||
React.createElement(this.FlexElements.Child, {grow: 0},
|
||||
React.createElement(mc.ModalCloseButton, {onClick: props.onClose})
|
||||
)
|
||||
),
|
||||
React.createElement(mc.ModalContent, {className: "bd-addon-modal-settings"},
|
||||
React.createElement(ErrorBoundary, {}, child)
|
||||
),
|
||||
React.createElement(mc.ModalFooter, {className: "bd-addon-modal-footer"},
|
||||
React.createElement(this.Buttons.default, {onClick: props.onClose, className: "bd-button"}, Strings.Modals.done)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
return this.ModalActions.openModal(props => {
|
||||
return React.createElement(modal, props);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -164,10 +164,10 @@ export default class PublicServers extends React.Component {
|
|||
else if (this.state.results.total) content = React.createElement("div", {className: "bd-card-list"}, servers);
|
||||
|
||||
return [React.createElement(SettingsTitle, {text: this.title, button: connectButton}),
|
||||
(this.state.tab !== "Featured" && this.state.tab !== "Popular") && this.pagination,
|
||||
this.state.results.numPages > 1 && this.pagination,
|
||||
content,
|
||||
(this.state.tab !== "Featured" && this.state.tab !== "Popular") && this.pagination,
|
||||
this.state.results.servers.length > 0 && React.createElement(SettingsTitle, {text: this.title})
|
||||
this.state.results.numPages > 1 && this.pagination,
|
||||
this.state.results.numPages > 1 && this.state.query && React.createElement(SettingsTitle, {text: this.title})
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,25 @@
|
|||
import {React, Logger, Strings, WebpackModules, DOM, DiscordModules} from "modules";
|
||||
import CloseButton from "../icons/close";
|
||||
import {React, Logger, Strings, WebpackModules, DiscordModules} from "modules";
|
||||
import ReloadIcon from "../icons/reload";
|
||||
import EditIcon from "../icons/edit";
|
||||
import DeleteIcon from "../icons/delete";
|
||||
import CogIcon from "../icons/cog";
|
||||
import Switch from "./components/switch";
|
||||
import ErrorBoundary from "../errorboundary";
|
||||
|
||||
import GitHubIcon from "../icons/github";
|
||||
import MoneyIcon from "../icons/dollarsign";
|
||||
import WebIcon from "../icons/globe";
|
||||
import PatreonIcon from "../icons/patreon";
|
||||
import SupportIcon from "../icons/support";
|
||||
import Modals from "../modals";
|
||||
import Toasts from "../toasts";
|
||||
|
||||
const LinkIcons = {
|
||||
website: WebIcon,
|
||||
source: GitHubIcon,
|
||||
invite: SupportIcon,
|
||||
donate: MoneyIcon,
|
||||
patreon: PatreonIcon
|
||||
};
|
||||
|
||||
const Tooltip = WebpackModules.getByDisplayName("Tooltip");
|
||||
|
||||
|
@ -22,7 +37,18 @@ export default class AddonCard extends React.Component {
|
|||
this.onChange = this.onChange.bind(this);
|
||||
this.reload = this.reload.bind(this);
|
||||
this.showSettings = this.showSettings.bind(this);
|
||||
this.closeSettings = this.closeSettings.bind(this);
|
||||
}
|
||||
|
||||
showSettings() {
|
||||
if (!this.props.hasSettings || !this.props.enabled) return;
|
||||
const name = this.getString(this.props.addon.name);
|
||||
try {
|
||||
Modals.showAddonSettingsModal(name, this.props.getSettingsPanel());
|
||||
}
|
||||
catch (err) {
|
||||
Toasts.show(Strings.Addons.settingsError.format({name}), {type: "error"});
|
||||
Logger.stacktrace("Addon Settings", "Unable to get settings panel for " + name + ".", err);
|
||||
}
|
||||
}
|
||||
|
||||
reload() {
|
||||
|
@ -31,36 +57,6 @@ export default class AddonCard extends React.Component {
|
|||
this.forceUpdate();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (!this.state.settingsOpen) return;
|
||||
if (this.settingsPanel instanceof Node) this.panelRef.current.appendChild(this.settingsPanel);
|
||||
|
||||
setImmediate(() => {
|
||||
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 = this.panelRef.current;
|
||||
const container = thisNode.closest(".scrollerBase-289Jih");
|
||||
if (!container || !isHidden(container, thisNode)) return;
|
||||
const thisNodeOffset = DOM.offset(thisNode);
|
||||
const containerOffset = DOM.offset(container);
|
||||
const original = container.scrollTop;
|
||||
const endPoint = thisNodeOffset.top - containerOffset.top + container.scrollTop - 30;
|
||||
DOM.animate({
|
||||
duration: 300,
|
||||
update: function(progress) {
|
||||
if (endPoint > original) container.scrollTop = original + (progress * (endPoint - original));
|
||||
else container.scrollTop = original - (progress * (original - endPoint));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getString(value) {return typeof value == "string" ? value : value.toString();}
|
||||
|
||||
onChange() {
|
||||
|
@ -69,16 +65,6 @@ export default class AddonCard extends React.Component {
|
|||
this.forceUpdate();
|
||||
}
|
||||
|
||||
showSettings() {
|
||||
if (!this.props.hasSettings) return;
|
||||
this.setState({settingsOpen: true});
|
||||
}
|
||||
|
||||
closeSettings() {
|
||||
if (this.settingsPanel instanceof Node) this.panelRef.current.innerHTML = "";
|
||||
this.setState({settingsOpen: false});
|
||||
}
|
||||
|
||||
buildTitle(name, version, author) {
|
||||
const title = Strings.Addons.title.split(/({{[A-Za-z]+}})/);
|
||||
const nameIndex = title.findIndex(s => s == "{{name}}");
|
||||
|
@ -90,35 +76,11 @@ export default class AddonCard extends React.Component {
|
|||
return title.flat();
|
||||
}
|
||||
|
||||
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(this.settingsPanel) == "string") {
|
||||
Logger.warn("Addon Settings", "Using a DOMString is officially deprecated.");
|
||||
props.dangerouslySetInnerHTML = {__html: this.settingsPanel};
|
||||
}
|
||||
|
||||
let child;
|
||||
if (typeof(this.settingsPanel) === "function") child = <this.settingsPanel />;
|
||||
if (this.settingsPanel.$$typeof && this.settingsPanel.$$typeof === Symbol.for("react.element")) child = this.settingsPanel;
|
||||
if (child) child = <ErrorBoundary>{child}</ErrorBoundary>;
|
||||
|
||||
return <div className="bd-addon-card settings-open bd-switch-item">
|
||||
<div className="bd-close" onClick={this.closeSettings}><CloseButton /></div>
|
||||
<div {...props}>
|
||||
{child}
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
buildLink(which) {
|
||||
const url = this.props.addon[which];
|
||||
if (!url) return null;
|
||||
const link = <a className="bd-link bd-link-website" href={url} target="_blank" rel="noopener noreferrer">{Strings.Addons[which]}</a>;
|
||||
const icon = React.createElement(LinkIcons[which]);
|
||||
const link = <a className="bd-link bd-link-website" href={url} target="_blank" rel="noopener noreferrer">{icon}</a>;
|
||||
if (which == "invite") {
|
||||
link.props.onClick = function(event) {
|
||||
event.preventDefault();
|
||||
|
@ -130,16 +92,24 @@ export default class AddonCard extends React.Component {
|
|||
DiscordModules.InviteActions.acceptInviteAndTransitionToInviteChannel(code);
|
||||
};
|
||||
}
|
||||
return link;
|
||||
return this.makeButton(Strings.Addons[which], link);
|
||||
}
|
||||
|
||||
get controls() { // {this.props.hasSettings && <button onClick={this.showSettings} className="bd-button bd-button-addon-settings" disabled={!this.props.enabled}>{Strings.Addons.addonSettings}</button>}
|
||||
return <div className="bd-controls">
|
||||
{this.props.hasSettings && this.makeControlButton(Strings.Addons.addonSettings, <CogIcon size={"24px"} />, this.showSettings, {disabled: !this.props.enabled})}
|
||||
{this.props.showReloadIcon && this.makeControlButton(Strings.Addons.reload, <ReloadIcon />, this.reload)}
|
||||
{this.props.editAddon && this.makeControlButton(Strings.Addons.editAddon, <EditIcon />, this.props.editAddon)}
|
||||
{this.props.deleteAddon && this.makeControlButton(Strings.Addons.deleteAddon, <DeleteIcon />, this.props.deleteAddon, {danger: true})}
|
||||
</div>;
|
||||
}
|
||||
|
||||
get footer() {
|
||||
const links = ["website", "source", "invite", "donate", "patreon"];
|
||||
if (!links.some(l => this.props.addon[l]) && !this.props.hasSettings) return null;
|
||||
const linkComponents = links.map(this.buildLink.bind(this)).filter(c => c);
|
||||
const linkComponents = links.map(this.buildLink.bind(this)).filter(c => c);// linkComponents.map((comp, i) => i < linkComponents.length - 1 ? [comp, " | "] : comp).flat()
|
||||
return <div className="bd-footer">
|
||||
<span className="bd-links">{linkComponents.map((comp, i) => i < linkComponents.length - 1 ? [comp, " | "] : comp).flat()}</span>
|
||||
{this.props.hasSettings && <button onClick={this.showSettings} className="bd-button bd-button-addon-settings" disabled={!this.props.enabled}>{Strings.Addons.addonSettings}</button>}
|
||||
<span className="bd-links">{linkComponents}</span>
|
||||
{this.controls}
|
||||
</div>;
|
||||
}
|
||||
|
||||
|
@ -151,9 +121,15 @@ export default class AddonCard extends React.Component {
|
|||
</Tooltip>;
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.settingsOpen) return this.settingsComponent;
|
||||
makeControlButton(title, children, action, {danger = false, disabled = false} = {}) {
|
||||
return <Tooltip color="black" position="top" text={title}>
|
||||
{(props) => {
|
||||
return <button {...props} className={"bd-button bd-addon-button" + (danger ? " bd-button-danger" : "") + (disabled ? " bd-button-disabled" : "")} onClick={action}>{children}</button>;
|
||||
}}
|
||||
</Tooltip>;
|
||||
}
|
||||
|
||||
render() {
|
||||
const addon = this.props.addon;
|
||||
const name = this.getString(addon.name);
|
||||
const author = this.getString(addon.author);
|
||||
|
@ -163,12 +139,7 @@ export default class AddonCard extends React.Component {
|
|||
return <div id={`${addon.id}-card`} className="bd-addon-card settings-closed">
|
||||
<div className="bd-addon-header">
|
||||
<span className="bd-title">{this.buildTitle(name, version, author)}</span>
|
||||
<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>
|
||||
<Switch checked={this.props.enabled} onChange={this.onChange} />
|
||||
</div>
|
||||
<div className="bd-description-wrap scroller-wrap fade"><div className="bd-description scroller">{description}</div></div>
|
||||
{this.footer}
|
||||
|
|
Loading…
Reference in New Issue