-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Plugin management page #24
Changes from all commits
3e9dffc
d308633
b68ac56
70df6c8
67c16a3
8dc0dd2
5978974
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import React, { useEffect, useState } from 'react' | ||
|
||
import { | ||
ECHO_PLUGIN_CONFIG, | ||
EchoPluginConfig, | ||
EchoPluginConfigs, | ||
EchoPluginHook | ||
} from 'echo-common' | ||
import * as path from 'path' | ||
import { importDevPlugin } from './dev-plugins' | ||
|
||
function filterPluginsByEnv(pluginConfigs: EchoPluginConfigs) { | ||
return Object.values(pluginConfigs).filter( | ||
(config) => (import.meta.env.MODE === 'development') === config.name.endsWith('-dev') | ||
) | ||
} | ||
|
||
async function getPlugin(pluginConfig: EchoPluginConfig): Promise<EchoPluginHook> { | ||
if (import.meta.env.MODE === 'development') { | ||
const entry = await importDevPlugin(pluginConfig.name) | ||
return entry.default() | ||
} else { | ||
const p = path.resolve(pluginConfig.path) | ||
const entry = module.require(p) | ||
const plugin: EchoPluginHook = entry() | ||
return plugin | ||
} | ||
} | ||
|
||
export const PluginSettingsPage: React.FC = () => { | ||
useEffect(() => { | ||
setAvailablePlugins(filterPluginsByEnv(ECHO_PLUGIN_CONFIG.loadPluginConfigs())) | ||
}, []) | ||
|
||
async function handleTogglePlugin(pluginConfig: EchoPluginConfig) { | ||
const pluginConfigs = ECHO_PLUGIN_CONFIG.loadPluginConfigs() | ||
const pluginEnabled = pluginConfigs[pluginConfig.name].enabled | ||
const plugin = await getPlugin(pluginConfig) | ||
if (!pluginEnabled) { | ||
plugin.start() | ||
} else { | ||
plugin.destroy() | ||
} | ||
Comment on lines
+41
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would make There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay I see. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, there isn't any deletion in this PR. I wouldn't delete anything except by "uninstalling" it, which isn't implemented in this PR |
||
pluginConfigs[pluginConfig.name].enabled = !pluginEnabled | ||
const updatedConfigs = ECHO_PLUGIN_CONFIG.writePluginConfigs(pluginConfigs) | ||
setAvailablePlugins(filterPluginsByEnv(updatedConfigs)) | ||
} | ||
|
||
const [availablePlugins, setAvailablePlugins] = useState<EchoPluginConfig[]>([]) | ||
|
||
return ( | ||
<> | ||
<div className="p-4 w-full h-full overflow-y-scroll"> | ||
<div className="flex flex-row"> | ||
<div className="flex flex-col"> | ||
<h1 className="font-semibold text-primary-accent">Plugins</h1> | ||
</div> | ||
</div> | ||
<div className="pt-4 flex-row flex w-full"> | ||
<table className="bg-secondary-surface table-auto border-separate border-spacing-3 text-left"> | ||
<tr className=""> | ||
<th>Plugin Name</th> | ||
<th>Version</th> | ||
<th>Enabled</th> | ||
</tr> | ||
{availablePlugins?.length > 0 && | ||
availablePlugins.map((plugin) => ( | ||
<tr key={plugin.name}> | ||
<td>{plugin.name}</td> | ||
<td>{plugin.version}</td> | ||
<td className="text-center"> | ||
<input | ||
type="checkbox" | ||
id={`${plugin.name}-enabled`} | ||
name="enabled" | ||
checked={plugin.enabled} | ||
onChange={() => handleTogglePlugin(plugin)} | ||
/> | ||
</td> | ||
</tr> | ||
))} | ||
{!availablePlugins?.length && ( | ||
<tr> | ||
<td>No Plugins installed</td> | ||
</tr> | ||
)} | ||
</table> | ||
</div> | ||
<div className="basis-1/4"></div> | ||
</div> | ||
</> | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { ECHO_DIR } from './echo-dir-service' | ||
|
||
export type EchoPluginConfig = { | ||
name: string | ||
version: string | ||
enabled: boolean | ||
path: string | ||
} | ||
|
||
export type EchoPluginConfigs = Record<string, EchoPluginConfig> | ||
|
||
export class EchoPluginConfigService { | ||
public loadPluginConfigs(): EchoPluginConfigs { | ||
if (ECHO_DIR.existsJson('plugin-config')) { | ||
const loadedPluginConfig: EchoPluginConfigs | null = ECHO_DIR.loadJson('plugin-config') | ||
if (loadedPluginConfig) { | ||
return loadedPluginConfig | ||
} | ||
} | ||
return this.writePluginConfigs({}) | ||
} | ||
|
||
public writePluginConfigs(pluginConfigObject: EchoPluginConfigs): EchoPluginConfigs { | ||
return ECHO_DIR.writeJson(['plugin-config'], pluginConfigObject) | ||
} | ||
} | ||
|
||
export const ECHO_PLUGIN_CONFIG = new EchoPluginConfigService() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure we need to do this, importing the plugin doesn't really enabled and disable them. We can import the plugins and just selectively call start on the enabled ones. This list of plugins will always just be these 3 example plugins since the list is really just here for people to add the plugin they are working on locally but that will never be committed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main reason to do this was that I need a way to get a handle on the module and it's entry so that I call it's methods on the
plugin-settings-page
. I'm sure there is another way to do this, but couldn't figure it out.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can't set a dynamic variable as import.
We may have to write the file to the system with the plugins we want to be in dev enviornment with HMR enabled. The HMR should regonize this and will rename the imports. This would be the way. Its a bit complicated but it should work, because the HMR is listening for file changes. We just have to trigger a file change with valid imports and everything should work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its because the HMR changes the module names with static replacement.
import('character-plugin')
would be =>require('entry-2416da1sd')
or something like this.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW HMR still worked with the solution of importing then one by one.
The switch statement that maps the stored plug-in configs to imports allows us to pseudo dynamically import.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but its hardcoded. With my idea, we could use your plugin to dynamially enable and disable plguins in HMR without this hardcoded switch. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, we would let your plugin to be static enabled and the others are controlled by your plugin
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its basically this:
https://github.com/PoeStack/poestack-sage/blob/main/src/echo-app/bump-version.js
But the plugins writes this dynamically. And we could read every plugin within the folder
echo-plugins
andecho-plugin-examples
.But we have to ensure, that the package.json in
echo-app
has set all plugins as dependency, or the HMR can not resolve the pluginsThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@C3ntraX I think I see what you mean, but I think I need a little more help with how that would be implemented. I think I need you to create a gist of how this would work, so I can understand the pattern better.
@zach-herridge a few options for this PR:
I think no matter what I'll probably need some assistance in implementing the dynamic imports