fix: plugin dependencies not enabling (#150)
This commit is contained in:
parent
ff9d904fcb
commit
ffbb52512c
3 changed files with 114 additions and 18 deletions
|
@ -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}
|
||||
/>
|
||||
|
|
|
@ -42,6 +42,26 @@ export function startAllPlugins() {
|
|||
}
|
||||
}
|
||||
|
||||
export function startDependenciesRecursive(p: Plugin) {
|
||||
let restartNeeded = false;
|
||||
const failures: string[] = [];
|
||||
if (p.dependencies) for (const dep of p.dependencies) {
|
||||
if (!Settings.plugins[dep].enabled) {
|
||||
startDependenciesRecursive(Plugins[dep]);
|
||||
// If the plugin has patches, don't start the plugin, just enable it.
|
||||
if (Plugins[dep].patches) {
|
||||
logger.warn(`Enabling dependency ${dep} requires restart.`);
|
||||
Settings.plugins[dep].enabled = true;
|
||||
restartNeeded = true;
|
||||
continue;
|
||||
}
|
||||
const result = startPlugin(Plugins[dep]);
|
||||
if (!result) failures.push(dep);
|
||||
}
|
||||
}
|
||||
return { restartNeeded, failures };
|
||||
}
|
||||
|
||||
export function startPlugin(p: Plugin) {
|
||||
if (p.start) {
|
||||
logger.info("Starting plugin", p.name);
|
||||
|
|
|
@ -32,6 +32,14 @@ export class ChangeList<T>{
|
|||
this.set.add(item);
|
||||
}
|
||||
|
||||
public add(item: T) {
|
||||
return this.set.add(item);
|
||||
}
|
||||
|
||||
public remove(item: T) {
|
||||
return this.set.delete(item);
|
||||
}
|
||||
|
||||
public getChanges() {
|
||||
return this.set.values();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue