|
|
|
@ -18,19 +18,23 @@
|
|
|
|
|
|
|
|
|
|
import Plugins from "plugins";
|
|
|
|
|
|
|
|
|
|
import { showNotice } from "../../api/Notices";
|
|
|
|
|
import { Settings, useSettings } from "../../api/settings";
|
|
|
|
|
import { startPlugin, stopPlugin } from "../../plugins";
|
|
|
|
|
import { Modals } from "../../utils";
|
|
|
|
|
import { startDependenciesRecursive, startPlugin, stopPlugin } from "../../plugins";
|
|
|
|
|
import { Logger, Modals } from "../../utils";
|
|
|
|
|
import { ChangeList } from "../../utils/ChangeList";
|
|
|
|
|
import { classes, lazyWebpack } from "../../utils/misc";
|
|
|
|
|
import { Plugin } from "../../utils/types";
|
|
|
|
|
import { filters } from "../../webpack";
|
|
|
|
|
import { Alerts, Button, Forms, Margins, Parser, React, Switch, Text, TextInput, Toasts, Tooltip } from "../../webpack/common";
|
|
|
|
|
import ErrorBoundary from "../ErrorBoundary";
|
|
|
|
|
import { ErrorCard } from "../ErrorCard";
|
|
|
|
|
import { Flex } from "../Flex";
|
|
|
|
|
import PluginModal from "./PluginModal";
|
|
|
|
|
import * as styles from "./styles";
|
|
|
|
|
|
|
|
|
|
const logger = new Logger("PluginSettings", "#a6d189");
|
|
|
|
|
|
|
|
|
|
const Select = lazyWebpack(filters.byCode("optionClassName", "popoutPosition", "autoFocus", "maxVisibleItems"));
|
|
|
|
|
const InputStyles = lazyWebpack(filters.byProps(["inputDefault", "inputWrapper"]));
|
|
|
|
|
|
|
|
|
@ -48,37 +52,91 @@ function showErrorToast(message: string) {
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface ReloadRequiredCardProps extends React.HTMLProps<HTMLDivElement> {
|
|
|
|
|
plugins: string[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ReloadRequiredCard({ plugins, ...props }: ReloadRequiredCardProps) {
|
|
|
|
|
if (plugins.length === 0) return null;
|
|
|
|
|
|
|
|
|
|
const pluginPrefix = plugins.length === 1 ? "The plugin" : "The following plugins require a reload to apply changes:";
|
|
|
|
|
const pluginSuffix = plugins.length === 1 ? " requires a reload to apply changes." : ".";
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<ErrorCard {...props} style={{ padding: "1em", display: "grid", gridTemplateColumns: "1fr auto", gap: 8, ...props.style }}>
|
|
|
|
|
<span style={{ margin: "auto 0" }}>
|
|
|
|
|
{pluginPrefix} <code>{plugins.join(", ")}</code>{pluginSuffix}
|
|
|
|
|
</span>
|
|
|
|
|
<Button look={Button.Looks.INVERTED} onClick={() => location.reload()}>Reload</Button>
|
|
|
|
|
</ErrorCard>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface PluginCardProps extends React.HTMLProps<HTMLDivElement> {
|
|
|
|
|
plugin: Plugin;
|
|
|
|
|
disabled: boolean;
|
|
|
|
|
onRestartNeeded(): void;
|
|
|
|
|
onRestartNeeded(name: string): void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLeave }: PluginCardProps) {
|
|
|
|
|
const settings = useSettings().plugins[plugin.name];
|
|
|
|
|
const settings = useSettings();
|
|
|
|
|
const pluginSettings = settings.plugins[plugin.name];
|
|
|
|
|
|
|
|
|
|
const [iconHover, setIconHover] = React.useState(false);
|
|
|
|
|
|
|
|
|
|
function isEnabled() {
|
|
|
|
|
return settings?.enabled || plugin.started;
|
|
|
|
|
return pluginSettings?.enabled || plugin.started;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function openModal() {
|
|
|
|
|
Modals.openModalLazy(async () => {
|
|
|
|
|
return modalProps => {
|
|
|
|
|
return <PluginModal {...modalProps} plugin={plugin} onRestartNeeded={onRestartNeeded} />;
|
|
|
|
|
return <PluginModal {...modalProps} plugin={plugin} onRestartNeeded={() => onRestartNeeded(plugin.name)} />;
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function toggleEnabled() {
|
|
|
|
|
const enabled = isEnabled();
|
|
|
|
|
const result = enabled ? stopPlugin(plugin) : startPlugin(plugin);
|
|
|
|
|
const action = enabled ? "stop" : "start";
|
|
|
|
|
const wasEnabled = isEnabled();
|
|
|
|
|
|
|
|
|
|
// If we're enabling a plugin, make sure all deps are enabled recursively.
|
|
|
|
|
if (!wasEnabled) {
|
|
|
|
|
const { restartNeeded, failures } = startDependenciesRecursive(plugin);
|
|
|
|
|
if (failures.length) {
|
|
|
|
|
logger.error(`Failed to start dependencies for ${plugin.name}: ${failures.join(", ")}`);
|
|
|
|
|
showNotice("Failed to start dependencies: " + failures.join(", "), "Close", () => null);
|
|
|
|
|
return;
|
|
|
|
|
} else if (restartNeeded) {
|
|
|
|
|
// If any dependencies have patches, don't start the plugin yet.
|
|
|
|
|
pluginSettings.enabled = true;
|
|
|
|
|
onRestartNeeded(plugin.name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if the plugin has patches, dont use stopPlugin/startPlugin. Wait for restart to apply changes.
|
|
|
|
|
if (plugin.patches) {
|
|
|
|
|
pluginSettings.enabled = !wasEnabled;
|
|
|
|
|
onRestartNeeded(plugin.name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the plugin is enabled, but hasn't been started, then we can just toggle it off.
|
|
|
|
|
if (wasEnabled && !plugin.started) {
|
|
|
|
|
pluginSettings.enabled = !wasEnabled;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const result = wasEnabled ? stopPlugin(plugin) : startPlugin(plugin);
|
|
|
|
|
const action = wasEnabled ? "stop" : "start";
|
|
|
|
|
|
|
|
|
|
if (!result) {
|
|
|
|
|
logger.error(`Failed to ${action} plugin ${plugin.name}`);
|
|
|
|
|
showErrorToast(`Failed to ${action} plugin: ${plugin.name}`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
settings.enabled = !settings.enabled;
|
|
|
|
|
if (plugin.patches) onRestartNeeded();
|
|
|
|
|
|
|
|
|
|
pluginSettings.enabled = !wasEnabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
@ -93,7 +151,18 @@ function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLe
|
|
|
|
|
<Flex style={{ marginTop: "auto", width: "100%", height: "100%", alignItems: "center" }}>
|
|
|
|
|
<Text variant="text-md/bold" style={{ flexGrow: "1" }}>{plugin.name}</Text>
|
|
|
|
|
<button role="switch" onClick={() => openModal()} style={styles.SettingsIcon} className="button-12Fmur">
|
|
|
|
|
{plugin.options ? <CogWheel /> : <InfoIcon width="24" height="24" />}
|
|
|
|
|
{plugin.options
|
|
|
|
|
? <CogWheel
|
|
|
|
|
style={{ color: iconHover ? "" : "var(--text-muted)" }}
|
|
|
|
|
onMouseEnter={() => setIconHover(true)}
|
|
|
|
|
onMouseLeave={() => setIconHover(false)}
|
|
|
|
|
/>
|
|
|
|
|
: <InfoIcon
|
|
|
|
|
width="24" height="24"
|
|
|
|
|
style={{ color: iconHover ? "" : "var(--text-muted)" }}
|
|
|
|
|
onMouseEnter={() => setIconHover(true)}
|
|
|
|
|
onMouseLeave={() => setIconHover(false)}
|
|
|
|
|
/>}
|
|
|
|
|
</button>
|
|
|
|
|
</Flex>
|
|
|
|
|
</Switch>
|
|
|
|
@ -170,6 +239,9 @@ export default ErrorBoundary.wrap(function Settings() {
|
|
|
|
|
<Forms.FormTitle tag="h5" className={classes(Margins.marginTop20, Margins.marginBottom8)}>
|
|
|
|
|
Plugins
|
|
|
|
|
</Forms.FormTitle>
|
|
|
|
|
|
|
|
|
|
<ReloadRequiredCard plugins={[...changes.getChanges()]} style={{ marginBottom: 16 }} />
|
|
|
|
|
|
|
|
|
|
<div style={styles.FiltersBar}>
|
|
|
|
|
<TextInput value={searchValue.value} placeholder={"Search for a plugin..."} onChange={onSearch} style={{ marginBottom: 24 }} />
|
|
|
|
|
<div className={InputStyles.inputWrapper}>
|
|
|
|
@ -195,9 +267,7 @@ export default ErrorBoundary.wrap(function Settings() {
|
|
|
|
|
const enabledDependants = depMap[plugin.name]?.filter(d => settings.plugins[d].enabled);
|
|
|
|
|
const dependency = enabledDependants?.length;
|
|
|
|
|
return <PluginCard
|
|
|
|
|
onRestartNeeded={() => {
|
|
|
|
|
changes.handleChange(plugin.name);
|
|
|
|
|
}}
|
|
|
|
|
onRestartNeeded={name => changes.add(name)}
|
|
|
|
|
disabled={plugin.required || !!dependency}
|
|
|
|
|
plugin={plugin}
|
|
|
|
|
/>;
|
|
|
|
@ -223,9 +293,7 @@ export default ErrorBoundary.wrap(function Settings() {
|
|
|
|
|
<PluginCard
|
|
|
|
|
onMouseLeave={onMouseLeave}
|
|
|
|
|
onMouseEnter={onMouseEnter}
|
|
|
|
|
onRestartNeeded={() => {
|
|
|
|
|
changes.handleChange(plugin.name);
|
|
|
|
|
}}
|
|
|
|
|
onRestartNeeded={name => changes.add(name)}
|
|
|
|
|
disabled={plugin.required || !!dependency}
|
|
|
|
|
plugin={plugin}
|
|
|
|
|
/>
|
|
|
|
|