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…
	
	Add table
		Add a link
		
	
		Reference in a new issue