parent
1015a60041
commit
d3639d12ae
|
@ -185,7 +185,11 @@
|
|||
"isEnabled": "Enabled",
|
||||
"wasLoaded": "{{name}} v{{version}} was loaded.",
|
||||
"listView": "List View",
|
||||
"gridView": "Grid View"
|
||||
"gridView": "Grid View",
|
||||
"enableAll": "Enable All",
|
||||
"disableAll": "Disable All",
|
||||
"results": "{{count}} Results",
|
||||
"enableAllWarning": "Enabling all {{type}} can cause temporary lag and unexpected errors.\n\n(Hold shift while clicking to skip this prompt!)"
|
||||
},
|
||||
"CustomCSS": {
|
||||
"confirmationText": "You have unsaved changes to your Custom CSS. Closing this window will lose all those changes.",
|
||||
|
|
|
@ -3,6 +3,7 @@ import path from "path";
|
|||
|
||||
import Builtin from "@structs/builtin";
|
||||
import DataStore from "@modules/datastore";
|
||||
import Strings from "@modules/strings";
|
||||
|
||||
import Modals from "@ui/modals";
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import path from "path";
|
||||
import fs from "fs";
|
||||
import {shell} from "electron";
|
||||
|
||||
import Logger from "@common/logger";
|
||||
|
||||
|
@ -18,6 +17,8 @@ import FloatingWindows from "@ui/floatingwindows";
|
|||
import Toasts from "@ui/toasts";
|
||||
|
||||
|
||||
// const SWITCH_ANIMATION_TIME = 250;
|
||||
|
||||
const openItem = ipc.openPath;
|
||||
|
||||
const splitRegex = /[^\S\r\n]*?\r?(?:\r\n|\n)[^\S\r\n]*?\*[^\S\r\n]?/;
|
||||
|
@ -271,8 +272,21 @@ export default class AddonManager {
|
|||
if (!addon || addon.partial) return;
|
||||
if (this.state[addon.id]) return;
|
||||
this.state[addon.id] = true;
|
||||
this.emit("enabled", addon);
|
||||
// setTimeout(() => {
|
||||
this.startAddon(addon);
|
||||
this.saveState();
|
||||
// }, SWITCH_ANIMATION_TIME);
|
||||
}
|
||||
|
||||
enableAllAddons() {
|
||||
const originalSetting = Settings.get("settings", "general", "showToasts", false);
|
||||
Settings.set("settings", "general", "showToasts", false);
|
||||
for (let a = 0; a < this.addonList.length; a++) {
|
||||
this.enableAddon(this.addonList[a]);
|
||||
}
|
||||
Settings.set("settings", "general", "showToasts", originalSetting);
|
||||
this.emit("batch");
|
||||
}
|
||||
|
||||
disableAddon(idOrAddon) {
|
||||
|
@ -280,8 +294,21 @@ export default class AddonManager {
|
|||
if (!addon || addon.partial) return;
|
||||
if (!this.state[addon.id]) return;
|
||||
this.state[addon.id] = false;
|
||||
this.emit("disabled", addon);
|
||||
// setTimeout(() => {
|
||||
this.stopAddon(addon);
|
||||
this.saveState();
|
||||
// }, SWITCH_ANIMATION_TIME);
|
||||
}
|
||||
|
||||
disableAllAddons() {
|
||||
const originalSetting = Settings.get("settings", "general", "showToasts", false);
|
||||
Settings.set("settings", "general", "showToasts", false);
|
||||
for (let a = 0; a < this.addonList.length; a++) {
|
||||
this.disableAddon(this.addonList[a]);
|
||||
}
|
||||
Settings.set("settings", "general", "showToasts", originalSetting);
|
||||
this.emit("batch");
|
||||
}
|
||||
|
||||
toggleAddon(id) {
|
||||
|
|
|
@ -57,6 +57,8 @@ export default new class PluginManager extends AddonManager {
|
|||
saveAddon: this.saveAddon.bind(this),
|
||||
editAddon: this.editAddon.bind(this),
|
||||
deleteAddon: this.deleteAddon.bind(this),
|
||||
enableAll: this.enableAllAddons.bind(this),
|
||||
disableAll: this.disableAllAddons.bind(this),
|
||||
prefix: this.prefix
|
||||
})
|
||||
});
|
||||
|
@ -151,6 +153,7 @@ export default new class PluginManager extends AddonManager {
|
|||
}
|
||||
catch (err) {
|
||||
this.state[addon.id] = false;
|
||||
this.emit("disabled", addon);
|
||||
Toasts.error(Strings.Addons.couldNotStart.format({name: addon.name, version: addon.version}));
|
||||
Logger.stacktrace(this.name, `${addon.name} v${addon.version} could not be started.`, err);
|
||||
return new AddonError(addon.name, addon.filename, Strings.Addons.enabled.format({method: "start()"}), {message: err.message, stack: err.stack}, this.prefix);
|
||||
|
|
|
@ -35,6 +35,8 @@ export default new class ThemeManager extends AddonManager {
|
|||
saveAddon: this.saveAddon.bind(this),
|
||||
editAddon: this.editAddon.bind(this),
|
||||
deleteAddon: this.deleteAddon.bind(this),
|
||||
enableAll: this.enableAllAddons.bind(this),
|
||||
disableAll: this.disableAllAddons.bind(this),
|
||||
prefix: this.prefix
|
||||
})
|
||||
});
|
||||
|
|
|
@ -212,7 +212,7 @@
|
|||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.bd-addon-controls .bd-search {
|
||||
.bd-settings-title .bd-search {
|
||||
font-size: 13px;
|
||||
margin: 0;
|
||||
width: 200px;
|
||||
|
@ -261,35 +261,43 @@
|
|||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.bd-addon-views .bd-view-button {
|
||||
.bd-addon-controls .bd-button {
|
||||
background-color: transparent;
|
||||
padding: 3px 4px;
|
||||
}
|
||||
|
||||
.bd-addon-views .bd-view-button svg {
|
||||
.bd-addon-controls .bd-button svg {
|
||||
fill: var(--interactive-normal);
|
||||
}
|
||||
|
||||
.bd-addon-views .bd-view-button.selected svg {
|
||||
.bd-addon-controls .bd-button.selected svg {
|
||||
fill: #FFFFFF;
|
||||
}
|
||||
|
||||
.bd-addon-views .bd-view-button:hover {
|
||||
.bd-addon-controls .bd-button:hover {
|
||||
background-color: var(--background-modifier-selected);
|
||||
}
|
||||
|
||||
.bd-addon-views .bd-view-button:active {
|
||||
.bd-addon-controls .bd-button:active {
|
||||
background-color: var(--background-modifier-accent);
|
||||
}
|
||||
|
||||
.bd-addon-views .bd-view-button.selected {
|
||||
.bd-addon-controls .bd-button.selected {
|
||||
background-color: #3E82E5;
|
||||
}
|
||||
|
||||
.bd-addon-views .bd-view-button + .bd-view-button {
|
||||
.bd-addon-controls .bd-button + .bd-button {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.bd-controls-basic .bd-button:active svg {
|
||||
fill: #FFFFFF;
|
||||
}
|
||||
|
||||
.bd-controls-basic .bd-button:active {
|
||||
background-color: #3E82E5;
|
||||
}
|
||||
|
||||
.bd-addon-list .bd-footer .bd-links,
|
||||
.bd-addon-list .bd-footer .bd-links a,
|
||||
.bd-addon-list .bd-footer .bd-addon-button {
|
||||
|
|
|
@ -165,6 +165,8 @@
|
|||
}
|
||||
|
||||
.bd-settings-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: var(--header-primary, #FFFFFF);
|
||||
font-weight: 600;
|
||||
cursor: default;
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import React from "@modules/react";
|
||||
|
||||
export default function FullScreen(props) {
|
||||
const size = props.size || "20px";
|
||||
return <svg className={props.className || ""} fill="#FFFFFF" viewBox="0 0 24 24" style={{width: size, height: size}} onClick={props.onClick}>
|
||||
<path d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/>
|
||||
</svg>;
|
||||
}
|
|
@ -3,6 +3,7 @@ import Logger from "@common/logger";
|
|||
import SimpleMarkdown from "@structs/markdown";
|
||||
|
||||
import React from "@modules/react";
|
||||
import Events from "@modules/emitter";
|
||||
import Strings from "@modules/strings";
|
||||
import WebpackModules from "@modules/webpackmodules";
|
||||
import DiscordModules from "@modules/discordmodules";
|
||||
|
@ -25,7 +26,7 @@ import ExtIcon from "@ui/icons/extension";
|
|||
import ErrorIcon from "@ui/icons/error";
|
||||
import ThemeIcon from "@ui/icons/theme";
|
||||
|
||||
const {useState, useCallback, useMemo} = React;
|
||||
const {useState, useCallback, useMemo, useEffect} = React;
|
||||
|
||||
|
||||
const LinkIcons = {
|
||||
|
@ -88,12 +89,27 @@ function buildLink(type, url) {
|
|||
return makeButton(Strings.Addons[type], link);
|
||||
}
|
||||
|
||||
export default function AddonCard({addon, type, disabled, enabled: initialValue, onChange: parentChange, hasSettings, editAddon, deleteAddon, getSettingsPanel}) {
|
||||
export default function AddonCard({addon, prefix, type, disabled, enabled: initialValue, onChange: parentChange, hasSettings, editAddon, deleteAddon, getSettingsPanel}) {
|
||||
const [isEnabled, setEnabled] = useState(initialValue);
|
||||
|
||||
useEffect(() => {
|
||||
const onEnabled = updated => {
|
||||
if (addon.id === updated.id) setEnabled(true);
|
||||
};
|
||||
const onDisabled = updated => {
|
||||
if (addon.id === updated.id) setEnabled(false);
|
||||
};
|
||||
Events.on(`${prefix}-enabled`, onEnabled);
|
||||
Events.on(`${prefix}-disabled`, onDisabled);
|
||||
return () => {
|
||||
Events.off(`${prefix}-enabled`, onEnabled);
|
||||
Events.off(`${prefix}-disabled`, onDisabled);
|
||||
};
|
||||
}, [prefix, addon]);
|
||||
|
||||
const onChange = useCallback(() => {
|
||||
setEnabled(!isEnabled);
|
||||
if (parentChange) parentChange(addon.id);
|
||||
}, [addon.id, parentChange, isEnabled]);
|
||||
}, [addon.id, parentChange]);
|
||||
|
||||
const showSettings = useCallback(() => {
|
||||
if (!hasSettings || !isEnabled) return;
|
||||
|
@ -154,7 +170,7 @@ export default function AddonCard({addon, type, disabled, enabled: initialValue,
|
|||
<div className="bd-addon-header">
|
||||
{type === "plugin" ? <ExtIcon size="18px" className="bd-icon" /> : <ThemeIcon size="18px" className="bd-icon" />}
|
||||
<div className="bd-title">{title}</div>
|
||||
<Switch disabled={disabled} checked={isEnabled} onChange={onChange} />
|
||||
<Switch internalState={false} disabled={disabled} checked={isEnabled} onChange={onChange} />
|
||||
</div>
|
||||
<div className="bd-description-wrap">
|
||||
{disabled && <div className="banner banner-danger"><ErrorIcon className="bd-icon" />{`An error was encountered while trying to load this ${type}.`}</div>}
|
||||
|
|
|
@ -15,6 +15,9 @@ import ErrorBoundary from "@ui/errorboundary";
|
|||
|
||||
import ListIcon from "@ui/icons/list";
|
||||
import GridIcon from "@ui/icons/grid";
|
||||
import FolderIcon from "@ui/icons/folder";
|
||||
import CheckIcon from "@ui/icons/check";
|
||||
import CloseIcon from "@ui/icons/close";
|
||||
|
||||
import NoResults from "@ui/blankslates/noresults";
|
||||
import EmptyImage from "@ui/blankslates/emptyimage";
|
||||
|
@ -48,6 +51,12 @@ function blankslate(type, onClick) {
|
|||
</EmptyImage>;
|
||||
}
|
||||
|
||||
function makeBasicButton(title, children, action) {
|
||||
return <DiscordModules.Tooltip color="primary" position="top" text={title}>
|
||||
{(props) => <button {...props} className="bd-button" onClick={action}>{children}</button>}
|
||||
</DiscordModules.Tooltip>;
|
||||
}
|
||||
|
||||
function makeControlButton(title, children, action, selected = false) {
|
||||
return <DiscordModules.Tooltip color="primary" position="top" text={title}>
|
||||
{(props) => {
|
||||
|
@ -81,8 +90,28 @@ function confirmDelete(addon) {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {function} action
|
||||
* @param {string} type
|
||||
* @returns
|
||||
*/
|
||||
function confirmEnable(action, type) {
|
||||
/**
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
return function(event) {
|
||||
if (event.shiftKey) return action();
|
||||
Modals.showConfirmationModal(Strings.Modals.confirmAction, Strings.Addons.enableAllWarning.format({type: type.toLocaleLowerCase()}), {
|
||||
confirmText: Strings.Modals.okay,
|
||||
cancelText: Strings.Modals.cancel,
|
||||
danger: true,
|
||||
onConfirm: action,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default function AddonList({prefix, type, title, folder, addonList, addonState, onChange, reload, editAddon, deleteAddon}) {
|
||||
|
||||
export default function AddonList({prefix, type, title, folder, addonList, addonState, onChange, reload, editAddon, deleteAddon, enableAll, disableAll}) {
|
||||
const [query, setQuery] = useState("");
|
||||
const [sort, setSort] = useState(getState.bind(null, type, "sort", "name"));
|
||||
const [ascending, setAscending] = useState(getState.bind(null, type, "ascending", true));
|
||||
|
@ -125,7 +154,6 @@ export default function AddonList({prefix, type, title, folder, addonList, addon
|
|||
if (deleteAddon) deleteAddon(addon);
|
||||
}, [addonList, deleteAddon]);
|
||||
|
||||
const button = folder ? {title: Strings.Addons.openFolder.format({type: title}), onClick: openFolder.bind(null, folder)} : null;
|
||||
const renderedCards = useMemo(() => {
|
||||
let sorted = addonList.sort((a, b) => {
|
||||
const sortByEnabled = sort === "isEnabled";
|
||||
|
@ -154,18 +182,25 @@ export default function AddonList({prefix, type, title, folder, addonList, addon
|
|||
return sorted.map(addon => {
|
||||
const hasSettings = addon.instance && typeof(addon.instance.getSettingsPanel) === "function";
|
||||
const getSettings = hasSettings && addon.instance.getSettingsPanel.bind(addon.instance);
|
||||
return <ErrorBoundary><AddonCard disabled={addon.partial} type={type} editAddon={() => triggerEdit(addon.id)} deleteAddon={() => triggerDelete(addon.id)} key={addon.id} enabled={addonState[addon.id]} addon={addon} onChange={onChange} reload={reload} hasSettings={hasSettings} getSettingsPanel={getSettings} /></ErrorBoundary>;
|
||||
return <ErrorBoundary><AddonCard disabled={addon.partial} type={type} prefix={prefix} editAddon={() => triggerEdit(addon.id)} deleteAddon={() => triggerDelete(addon.id)} key={addon.id} enabled={addonState[addon.id]} addon={addon} onChange={onChange} reload={reload} hasSettings={hasSettings} getSettingsPanel={getSettings} /></ErrorBoundary>;
|
||||
});
|
||||
}, [addonList, addonState, onChange, reload, triggerDelete, triggerEdit, type, sort, ascending, query, forced]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
}, [addonList, addonState, onChange, reload, triggerDelete, triggerEdit, type, prefix, sort, ascending, query, forced]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const hasAddonsInstalled = addonList.length !== 0;
|
||||
const isSearching = !!query;
|
||||
const hasResults = renderedCards.length !== 0;
|
||||
|
||||
return [
|
||||
<SettingsTitle key="title" text={title} button={button} />,
|
||||
<SettingsTitle key="title" text={isSearching ? `${title} - ${Strings.Addons.results.format({count: `${renderedCards.length}`})}` : title}>
|
||||
<Search onChange={search} placeholder={`${Strings.Addons.search.format({type: `${renderedCards.length} ${title}`})}...`} />
|
||||
</SettingsTitle>,
|
||||
<div className={"bd-controls bd-addon-controls"}>
|
||||
<Search onChange={search} placeholder={`${Strings.Addons.search.format({type: title})}...`} />
|
||||
{/* <Search onChange={search} placeholder={`${Strings.Addons.search.format({type: title})}...`} /> */}
|
||||
<div className="bd-controls-basic">
|
||||
{makeBasicButton(Strings.Addons.openFolder.format({type: title}), <FolderIcon />, openFolder.bind(null, folder))}
|
||||
{makeBasicButton(Strings.Addons.enableAll, <CheckIcon size="20px" />, confirmEnable(enableAll, title))}
|
||||
{makeBasicButton(Strings.Addons.disableAll, <CloseIcon size="20px" />, disableAll)}
|
||||
</div>
|
||||
<div className="bd-controls-advanced">
|
||||
<div className="bd-addon-dropdowns">
|
||||
<div className="bd-select-wrapper">
|
||||
|
|
|
@ -3,17 +3,18 @@ import React from "@modules/react";
|
|||
const {useState, useCallback} = React;
|
||||
|
||||
|
||||
export default function Switch({id, checked: initialValue, disabled, onChange}) {
|
||||
export default function Switch({id, checked: initialValue, disabled, onChange, internalState = true}) {
|
||||
const [checked, setChecked] = useState(initialValue);
|
||||
const change = useCallback(() => {
|
||||
onChange?.(!checked);
|
||||
setChecked(!checked);
|
||||
}, [checked, onChange]);
|
||||
|
||||
const isChecked = internalState ? checked : initialValue;
|
||||
const enabledClass = disabled ? " bd-switch-disabled" : "";
|
||||
const checkedClass = checked ? " bd-switch-checked" : "";
|
||||
const checkedClass = isChecked ? " bd-switch-checked" : "";
|
||||
return <div className={`bd-switch` + enabledClass + checkedClass}>
|
||||
<input id={id} type="checkbox" disabled={disabled} checked={checked} onChange={change} />
|
||||
<input id={id} type="checkbox" disabled={disabled} checked={isChecked} onChange={change} />
|
||||
<div className="bd-switch-body">
|
||||
<svg className="bd-switch-slider" viewBox="0 0 28 20" preserveAspectRatio="xMinYMid meet">
|
||||
<rect className="bd-switch-handle" fill="white" x="4" y="0" height="20" width="20" rx="10"></rect>
|
||||
|
|
|
@ -6,7 +6,7 @@ const {useCallback} = React;
|
|||
const basicClass = "bd-settings-title";
|
||||
const groupClass = "bd-settings-title bd-settings-group-title";
|
||||
|
||||
export default function SettingsTitle({isGroup, className, button, onClick, text, otherChildren}) {
|
||||
export default function SettingsTitle({isGroup, className, button, onClick, text, children}) {
|
||||
const click = useCallback((event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
@ -19,7 +19,7 @@ export default function SettingsTitle({isGroup, className, button, onClick, text
|
|||
return <h2 className={titleClass} onClick={() => {onClick?.();}}>
|
||||
{text}
|
||||
{button && <button className="bd-button bd-button-title" onClick={click}>{button.title}</button>}
|
||||
{otherChildren}
|
||||
{children}
|
||||
</h2>;
|
||||
|
||||
}
|
Loading…
Reference in New Issue