diff --git a/ui/src/components/PluginRender/index.tsx b/ui/src/components/PluginRender/index.tsx index ff68f567..3ed8a20d 100644 --- a/ui/src/components/PluginRender/index.tsx +++ b/ui/src/components/PluginRender/index.tsx @@ -1,35 +1,52 @@ -import { FC, ReactNode } from 'react'; +import { FC, ReactNode, memo } from 'react'; import builtin from '@/plugins/builtin'; import * as plugins from '@/plugins'; -import { Plugin } from '@/utils/pluginKit'; +import { Plugin, PluginType } from '@/utils/pluginKit'; + +/** + * Noteļ¼Please set at least either of the `slug_name` and `type` attributes, otherwise no plugins will be rendered. + * + * @field slug_name: The `slug_name` of the plugin needs to be rendered. + * If this property is set, `PluginRender` will use it first (regardless of whether `type` is set) + * to find the corresponding plugin and render it. + * @field type: Used to formulate the rendering of all plugins of this type. + * (if the `slug_name` attribute is set, it will be ignored) + * @field prop: Any attribute you want to configure, e.g. `className` + */ interface Props { - slug_name: string; + slug_name?: string; + type?: PluginType; children?: ReactNode; [prop: string]: any; } -const findPluginBySlugName: (l, n) => Plugin | null = (source, slug_name) => { - let ret: Plugin | null = null; +const findPlugin: (s, k: 'slug_name' | 'type', v) => Plugin[] = ( + source, + k, + v, +) => { + const ret: Plugin[] = []; if (source) { - Object.keys(source).forEach((k) => { - const p = source[k]; - if (p && p.info && p.info.slug_name === slug_name && p.component) { - ret = p; + Object.keys(source).forEach((i) => { + const p = source[i]; + if (p && p.component && p.info && p.info[k] === v) { + ret.push(p); } }); } - return ret; }; -const Index: FC<Props> = ({ slug_name, children, ...props }) => { - const bp = findPluginBySlugName(builtin, slug_name); - const vp = findPluginBySlugName(plugins, slug_name); - const plugin = bp || vp; +const Index: FC<Props> = ({ slug_name, type, children, ...props }) => { + const fk = slug_name ? 'slug_name' : 'type'; + const fv = fk === 'slug_name' ? slug_name : type; + const bp = findPlugin(builtin, fk, fv); + const vp = findPlugin(plugins, fk, fv); + const pluginSlice = [...bp, ...vp]; - if (!plugin) { + if (!pluginSlice.length) { return null; } /** @@ -37,9 +54,19 @@ const Index: FC<Props> = ({ slug_name, children, ...props }) => { * ps: Logic such as version compatibility determination can be placed here */ - const PluginComponent = plugin.component; - // @ts-ignore - return <PluginComponent {...props}>{children}</PluginComponent>; + return ( + <> + {pluginSlice.map((ps) => { + const PluginFC = ps.component; + return ( + // @ts-ignore + <PluginFC key={ps.info.slug_name} {...props}> + {children} + </PluginFC> + ); + })} + </> + ); }; -export default Index; +export default memo(Index); diff --git a/ui/src/pages/Users/Login/index.tsx b/ui/src/pages/Users/Login/index.tsx index 7bb293fb..a6bedd59 100644 --- a/ui/src/pages/Users/Login/index.tsx +++ b/ui/src/pages/Users/Login/index.tsx @@ -175,15 +175,12 @@ const Index: React.FC = () => { return ( <Container style={{ paddingTop: '4rem', paddingBottom: '5rem' }}> <WelcomeTitle /> - <Col className="mx-auto" md={6} lg={4} xl={3}> - <PluginRender slug_name="ui_plugin_demo" className="mb-5" /> - </Col> {step === 1 ? ( <Col className="mx-auto" md={6} lg={4} xl={3}> {ucAgentInfo ? ( <PluginRender slug_name="uc_login" className="mb-5" /> ) : ( - <PluginRender slug_name="connector" className="mb-5" /> + <PluginRender type="Connector" className="mb-5" /> )} {canOriginalLogin ? ( <> diff --git a/ui/src/pages/Users/Register/index.tsx b/ui/src/pages/Users/Register/index.tsx index 712485ef..efc10351 100644 --- a/ui/src/pages/Users/Register/index.tsx +++ b/ui/src/pages/Users/Register/index.tsx @@ -26,7 +26,7 @@ const Index: React.FC = () => { {showForm ? ( <Col className="mx-auto" md={6} lg={4} xl={3}> - <PluginRender slug_name="connector" className="mb-5" /> + <PluginRender type="Connector" className="mb-5" /> <SignUpForm callback={onStep} /> </Col> ) : ( diff --git a/ui/src/plugins/Demo/demo.go b/ui/src/plugins/Demo/demo.go new file mode 100644 index 00000000..2f1795f1 --- /dev/null +++ b/ui/src/plugins/Demo/demo.go @@ -0,0 +1,20 @@ +package demo + +import "github.com/answerdev/answer/plugin" + +type DemoPlugin struct { +} + +func init() { + plugin.Register(&DemoPlugin{}) +} + +func (d DemoPlugin) Info() plugin.Info { + return plugin.Info{ + Name: plugin.MakeTranslator("i18n.demo.name"), + SlugName: "demo_plugin", + Description: plugin.MakeTranslator("i18n.demo.description"), + Author: "answerdev", + Version: "0.0.1", + } +} diff --git a/ui/src/plugins/builtin/Connector/index.tsx b/ui/src/plugins/builtin/Connector/index.tsx index d07624e8..c5b39431 100644 --- a/ui/src/plugins/builtin/Connector/index.tsx +++ b/ui/src/plugins/builtin/Connector/index.tsx @@ -13,6 +13,7 @@ import './i18n'; const pluginInfo: PluginInfo = { slug_name: info.slug_name, + type: info.type, }; interface Props { className?: string; diff --git a/ui/src/plugins/builtin/Connector/info.yaml b/ui/src/plugins/builtin/Connector/info.yaml index ba65c13e..7155daec 100644 --- a/ui/src/plugins/builtin/Connector/info.yaml +++ b/ui/src/plugins/builtin/Connector/info.yaml @@ -1,4 +1,5 @@ slug_name: connector +type: Connector version: 0.0.1 link: https://github.com/answerdev/plugins/tree/main/connector/ author: Answer.dev diff --git a/ui/src/plugins/index.ts b/ui/src/plugins/index.ts index 6a551eaf..bb6104ab 100644 --- a/ui/src/plugins/index.ts +++ b/ui/src/plugins/index.ts @@ -1,3 +1,3 @@ export default null; -export { default as Demo } from './Demo'; +// export { default as Demo } from './Demo'; diff --git a/ui/src/utils/pluginKit.ts b/ui/src/utils/pluginKit.ts index f65d3bf1..3b88b582 100644 --- a/ui/src/utils/pluginKit.ts +++ b/ui/src/utils/pluginKit.ts @@ -7,14 +7,19 @@ import i18next from 'i18next'; * It may be used for feature upgrades or version compatibility processing. * * @field slug_name: Unique identity string for the plugin, usually configured in `info.yaml` + * @field type: The type of plugin is defined and a single type of plugin can have multiple implementations. + * For example, a plugin of type `Connector` can have a `google` implementation and a `github` implementation. + * `PluginRender` automatically renders the plug-in types already included in `PluginType`. * @field name: Plugin name, optionally configurable. Usually read from the `i18n` file * @field description: Plugin description, optionally configurable. Usually read from the `i18n` file */ const I18N_NS = 'plugin'; +export type PluginType = 'Connector'; export interface PluginInfo { slug_name: string; + type?: PluginType; name?: string; description?: string; }