diff --git a/ui/src/App.tsx b/ui/src/App.tsx index e1c48a4f..fa8ced58 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -1,6 +1,8 @@ import { RouterProvider, createBrowserRouter } from 'react-router-dom'; import './i18n/init'; + +import '@/utils/pluginKit'; import routes from '@/router'; function App() { diff --git a/ui/src/components/Editor/index.tsx b/ui/src/components/Editor/index.tsx index 5d08706e..66f9e33b 100644 --- a/ui/src/components/Editor/index.tsx +++ b/ui/src/components/Editor/index.tsx @@ -9,12 +9,12 @@ import { import classNames from 'classnames'; +import PluginRender from '../PluginRender'; + import { BlockQuote, Bold, - Chart, Code, - Formula, Heading, Help, Hr, @@ -127,7 +127,9 @@ const MDEditor: ForwardRefRenderFunction = (
{context && ( -
+ @@ -136,10 +138,7 @@ const MDEditor: ForwardRefRenderFunction = (
-
- -
      @@ -148,7 +147,7 @@ const MDEditor: ForwardRefRenderFunction = (
      -
      + )} diff --git a/ui/src/components/PluginRender/index.tsx b/ui/src/components/PluginRender/index.tsx index 3ed8a20d..1e23b4b1 100644 --- a/ui/src/components/PluginRender/index.tsx +++ b/ui/src/components/PluginRender/index.tsx @@ -1,8 +1,6 @@ -import { FC, ReactNode, memo } from 'react'; +import React, { FC, ReactNode, memo } from 'react'; -import builtin from '@/plugins/builtin'; -import * as plugins from '@/plugins'; -import { Plugin, PluginType } from '@/utils/pluginKit'; +import PluginKite, { Plugin, PluginType } from '@/utils/pluginKit'; /** * Note:Please set at least either of the `slug_name` and `type` attributes, otherwise no plugins will be rendered. @@ -22,47 +20,60 @@ interface Props { [prop: string]: any; } -const findPlugin: (s, k: 'slug_name' | 'type', v) => Plugin[] = ( - source, - k, - v, -) => { - const ret: Plugin[] = []; - if (source) { - 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 = ({ 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]; + const pluginSlice: Plugin[] = []; + const plugins = PluginKite.getPlugins(); + + plugins.forEach((plugin) => { + if (type && slug_name) { + if (plugin.info.slug_name === slug_name && plugin.info.type === type) { + pluginSlice.push(plugin); + } + } else if (type) { + if (plugin.info.type === type) { + pluginSlice.push(plugin); + } + } else if (slug_name) { + if (plugin.info.slug_name === slug_name) { + pluginSlice.push(plugin); + } + } + }); - if (!pluginSlice.length) { - return null; - } /** * TODO: Rendering control for non-builtin plug-ins * ps: Logic such as version compatibility determination can be placed here */ + if (type === 'editor') { + const nodes = React.Children.map(children, (child, index) => { + if (index === 0) { + return ( + <> + {child} + {pluginSlice.map((ps) => { + const PluginFC = ps.component; + return ( + // @ts-ignore + + ); + })} + + ); + } + return child; + }); + + return
      {nodes}
      ; + } + return ( <> {pluginSlice.map((ps) => { const PluginFC = ps.component; return ( // @ts-ignore - - {children} - + ); })} diff --git a/ui/src/pages/Search/components/Head/index.tsx b/ui/src/pages/Search/components/Head/index.tsx index fceb113d..d6200fc4 100644 --- a/ui/src/pages/Search/components/Head/index.tsx +++ b/ui/src/pages/Search/components/Head/index.tsx @@ -38,7 +38,7 @@ const Index: FC = ({ data }) => {

      {t('title')}

      - +

      {t('keywords')} diff --git a/ui/src/pages/Users/Login/index.tsx b/ui/src/pages/Users/Login/index.tsx index 17357251..0bff90f7 100644 --- a/ui/src/pages/Users/Login/index.tsx +++ b/ui/src/pages/Users/Login/index.tsx @@ -144,9 +144,17 @@ const Index: React.FC = () => { {step === 1 ? (

{ucAgentInfo ? ( - + ) : ( - + )} {canOriginalLogin ? ( <> diff --git a/ui/src/pages/Users/Register/index.tsx b/ui/src/pages/Users/Register/index.tsx index be646ecc..2fbb4619 100644 --- a/ui/src/pages/Users/Register/index.tsx +++ b/ui/src/pages/Users/Register/index.tsx @@ -35,7 +35,11 @@ const Index: React.FC = () => { {showForm ? ( - + {showSignupForm ? : null}
diff --git a/ui/src/plugins/Demo/i18n/index.ts b/ui/src/plugins/Demo/i18n/index.ts index b693440a..68eb4590 100644 --- a/ui/src/plugins/Demo/i18n/index.ts +++ b/ui/src/plugins/Demo/i18n/index.ts @@ -1,9 +1,9 @@ -import pluginKit from '@/utils/pluginKit'; +import { initI18nResource } from '@/utils/pluginKit/utils'; import en_US from './en_US.yaml'; import zh_CN from './zh_CN.yaml'; -pluginKit.initI18nResource({ +initI18nResource({ en_US, zh_CN, }); diff --git a/ui/src/plugins/Demo/index.tsx b/ui/src/plugins/Demo/index.tsx index 5d6b57f1..016d9421 100644 --- a/ui/src/plugins/Demo/index.tsx +++ b/ui/src/plugins/Demo/index.tsx @@ -2,18 +2,21 @@ import { memo, FC } from 'react'; import { useTranslation } from 'react-i18next'; import { Alert } from 'react-bootstrap'; -import pluginKit, { PluginInfo } from '@/utils/pluginKit'; +import { PluginInfo } from '@/utils/pluginKit'; +import { getTransNs, getTransKeyPrefix } from '@/utils/pluginKit/utils'; + import './i18n'; import info from './info.yaml'; const pluginInfo: PluginInfo = { slug_name: info.slug_name, + type: info.type, }; const Index: FC = () => { - const { t } = useTranslation(pluginKit.getTransNs(), { - keyPrefix: pluginKit.getTransKeyPrefix(pluginInfo), + const { t } = useTranslation(getTransNs(), { + keyPrefix: getTransKeyPrefix(pluginInfo), }); return {t('msg')}; diff --git a/ui/src/plugins/builtin/Algolia/i18n/index.ts b/ui/src/plugins/builtin/Algolia/i18n/index.ts index b693440a..68eb4590 100644 --- a/ui/src/plugins/builtin/Algolia/i18n/index.ts +++ b/ui/src/plugins/builtin/Algolia/i18n/index.ts @@ -1,9 +1,9 @@ -import pluginKit from '@/utils/pluginKit'; +import { initI18nResource } from '@/utils/pluginKit/utils'; import en_US from './en_US.yaml'; import zh_CN from './zh_CN.yaml'; -pluginKit.initI18nResource({ +initI18nResource({ en_US, zh_CN, }); diff --git a/ui/src/plugins/builtin/Algolia/index.tsx b/ui/src/plugins/builtin/Algolia/index.tsx index 83cea6bd..31463ce5 100644 --- a/ui/src/plugins/builtin/Algolia/index.tsx +++ b/ui/src/plugins/builtin/Algolia/index.tsx @@ -1,7 +1,8 @@ import { memo, FC } from 'react'; import { useTranslation } from 'react-i18next'; -import pluginKit, { PluginInfo } from '@/utils/pluginKit'; +import { PluginInfo } from '@/utils/pluginKit'; +import { getTransNs, getTransKeyPrefix } from '@/utils/pluginKit/utils'; import { SvgIcon } from '@/components'; import info from './info.yaml'; @@ -10,11 +11,12 @@ import './i18n'; const pluginInfo: PluginInfo = { slug_name: info.slug_name, + type: info.type, }; const Index: FC = () => { - const { t } = useTranslation(pluginKit.getTransNs(), { - keyPrefix: pluginKit.getTransKeyPrefix(pluginInfo), + const { t } = useTranslation(getTransNs(), { + keyPrefix: getTransKeyPrefix(pluginInfo), }); const { data } = useGetAlgoliaInfo(); diff --git a/ui/src/plugins/builtin/Algolia/info.yaml b/ui/src/plugins/builtin/Algolia/info.yaml index 5d6038b5..d9a85ab8 100644 --- a/ui/src/plugins/builtin/Algolia/info.yaml +++ b/ui/src/plugins/builtin/Algolia/info.yaml @@ -1,3 +1,4 @@ slug_name: algolia +type: search version: 0.0.1 author: Answer.dev diff --git a/ui/src/plugins/builtin/UcLogin/i18n/en_US.yaml b/ui/src/plugins/builtin/HostingConnector/i18n/en_US.yaml similarity index 93% rename from ui/src/plugins/builtin/UcLogin/i18n/en_US.yaml rename to ui/src/plugins/builtin/HostingConnector/i18n/en_US.yaml index a141b60a..077a0a26 100644 --- a/ui/src/plugins/builtin/UcLogin/i18n/en_US.yaml +++ b/ui/src/plugins/builtin/HostingConnector/i18n/en_US.yaml @@ -1,5 +1,5 @@ plugin: - uc_login: + hosting_connector: ui: connect: Connect with {{ auth_name }} login: Login diff --git a/ui/src/plugins/builtin/Connector/i18n/index.ts b/ui/src/plugins/builtin/HostingConnector/i18n/index.ts similarity index 53% rename from ui/src/plugins/builtin/Connector/i18n/index.ts rename to ui/src/plugins/builtin/HostingConnector/i18n/index.ts index b693440a..68eb4590 100644 --- a/ui/src/plugins/builtin/Connector/i18n/index.ts +++ b/ui/src/plugins/builtin/HostingConnector/i18n/index.ts @@ -1,9 +1,9 @@ -import pluginKit from '@/utils/pluginKit'; +import { initI18nResource } from '@/utils/pluginKit/utils'; import en_US from './en_US.yaml'; import zh_CN from './zh_CN.yaml'; -pluginKit.initI18nResource({ +initI18nResource({ en_US, zh_CN, }); diff --git a/ui/src/plugins/builtin/UcLogin/i18n/zh_CN.yaml b/ui/src/plugins/builtin/HostingConnector/i18n/zh_CN.yaml similarity index 92% rename from ui/src/plugins/builtin/UcLogin/i18n/zh_CN.yaml rename to ui/src/plugins/builtin/HostingConnector/i18n/zh_CN.yaml index 492041a8..d415c8df 100644 --- a/ui/src/plugins/builtin/UcLogin/i18n/zh_CN.yaml +++ b/ui/src/plugins/builtin/HostingConnector/i18n/zh_CN.yaml @@ -1,5 +1,5 @@ plugin: - uc_login: + hosting_connector: ui: connect: 连接到 {{ auth_name }} login: 登录 diff --git a/ui/src/plugins/builtin/UcLogin/index.tsx b/ui/src/plugins/builtin/HostingConnector/index.tsx similarity index 82% rename from ui/src/plugins/builtin/UcLogin/index.tsx rename to ui/src/plugins/builtin/HostingConnector/index.tsx index 7c98a8e7..820f34dc 100644 --- a/ui/src/plugins/builtin/UcLogin/index.tsx +++ b/ui/src/plugins/builtin/HostingConnector/index.tsx @@ -4,7 +4,8 @@ import { useTranslation } from 'react-i18next'; import classnames from 'classnames'; -import pluginKit, { PluginInfo } from '@/utils/pluginKit'; +import { PluginInfo } from '@/utils/pluginKit'; +import { getTransNs, getTransKeyPrefix } from '@/utils/pluginKit/utils'; import { SvgIcon } from '@/components'; import { userCenterStore } from '@/stores'; import './i18n'; @@ -17,11 +18,12 @@ interface Props { const pluginInfo: PluginInfo = { slug_name: info.slug_name, + type: info.type, }; const Index: FC = ({ className }) => { - const { t } = useTranslation(pluginKit.getTransNs(), { - keyPrefix: pluginKit.getTransKeyPrefix(pluginInfo), + const { t } = useTranslation(getTransNs(), { + keyPrefix: getTransKeyPrefix(pluginInfo), }); const ucAgent = userCenterStore().agent; const ucLoginRedirect = diff --git a/ui/src/plugins/builtin/HostingConnector/info.yaml b/ui/src/plugins/builtin/HostingConnector/info.yaml new file mode 100644 index 00000000..83d6924c --- /dev/null +++ b/ui/src/plugins/builtin/HostingConnector/info.yaml @@ -0,0 +1,5 @@ +slug_name: hosting_connector +type: connector +version: 0.0.1 +author: Answer.dev + diff --git a/ui/src/plugins/builtin/Connector/i18n/en_US.yaml b/ui/src/plugins/builtin/ThirdPartyConnector/i18n/en_US.yaml similarity index 79% rename from ui/src/plugins/builtin/Connector/i18n/en_US.yaml rename to ui/src/plugins/builtin/ThirdPartyConnector/i18n/en_US.yaml index fe679444..b22fa316 100644 --- a/ui/src/plugins/builtin/Connector/i18n/en_US.yaml +++ b/ui/src/plugins/builtin/ThirdPartyConnector/i18n/en_US.yaml @@ -1,5 +1,5 @@ plugin: - connector: + third_party_connector: ui: connect: Connect with {{ auth_name }} remove: Remove {{ auth_name }} diff --git a/ui/src/plugins/builtin/UcLogin/i18n/index.ts b/ui/src/plugins/builtin/ThirdPartyConnector/i18n/index.ts similarity index 53% rename from ui/src/plugins/builtin/UcLogin/i18n/index.ts rename to ui/src/plugins/builtin/ThirdPartyConnector/i18n/index.ts index b693440a..68eb4590 100644 --- a/ui/src/plugins/builtin/UcLogin/i18n/index.ts +++ b/ui/src/plugins/builtin/ThirdPartyConnector/i18n/index.ts @@ -1,9 +1,9 @@ -import pluginKit from '@/utils/pluginKit'; +import { initI18nResource } from '@/utils/pluginKit/utils'; import en_US from './en_US.yaml'; import zh_CN from './zh_CN.yaml'; -pluginKit.initI18nResource({ +initI18nResource({ en_US, zh_CN, }); diff --git a/ui/src/plugins/builtin/Connector/i18n/zh_CN.yaml b/ui/src/plugins/builtin/ThirdPartyConnector/i18n/zh_CN.yaml similarity index 79% rename from ui/src/plugins/builtin/Connector/i18n/zh_CN.yaml rename to ui/src/plugins/builtin/ThirdPartyConnector/i18n/zh_CN.yaml index aede9204..b47f4db3 100644 --- a/ui/src/plugins/builtin/Connector/i18n/zh_CN.yaml +++ b/ui/src/plugins/builtin/ThirdPartyConnector/i18n/zh_CN.yaml @@ -1,5 +1,5 @@ plugin: - connector: + third_party_connector: ui: connect: 连接到 {{ auth_name }} remove: 解绑 {{ auth_name }} diff --git a/ui/src/plugins/builtin/Connector/index.tsx b/ui/src/plugins/builtin/ThirdPartyConnector/index.tsx similarity index 82% rename from ui/src/plugins/builtin/Connector/index.tsx rename to ui/src/plugins/builtin/ThirdPartyConnector/index.tsx index af6b4f75..ffbf0c45 100644 --- a/ui/src/plugins/builtin/Connector/index.tsx +++ b/ui/src/plugins/builtin/ThirdPartyConnector/index.tsx @@ -4,7 +4,8 @@ import { useTranslation } from 'react-i18next'; import classnames from 'classnames'; -import pluginKit, { PluginInfo } from '@/utils/pluginKit'; +import { PluginInfo } from '@/utils/pluginKit'; +import { getTransNs, getTransKeyPrefix } from '@/utils/pluginKit/utils'; import { SvgIcon } from '@/components'; import info from './info.yaml'; @@ -19,8 +20,8 @@ interface Props { className?: string; } const Index: FC = ({ className }) => { - const { t } = useTranslation(pluginKit.getTransNs(), { - keyPrefix: pluginKit.getTransKeyPrefix(pluginInfo), + const { t } = useTranslation(getTransNs(), { + keyPrefix: getTransKeyPrefix(pluginInfo), }); const { data } = useGetStartUseOauthConnector(); diff --git a/ui/src/plugins/builtin/Connector/info.yaml b/ui/src/plugins/builtin/ThirdPartyConnector/info.yaml similarity index 67% rename from ui/src/plugins/builtin/Connector/info.yaml rename to ui/src/plugins/builtin/ThirdPartyConnector/info.yaml index 7155daec..e370929c 100644 --- a/ui/src/plugins/builtin/Connector/info.yaml +++ b/ui/src/plugins/builtin/ThirdPartyConnector/info.yaml @@ -1,5 +1,5 @@ -slug_name: connector -type: Connector +slug_name: third_party_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/builtin/Connector/services.ts b/ui/src/plugins/builtin/ThirdPartyConnector/services.ts similarity index 100% rename from ui/src/plugins/builtin/Connector/services.ts rename to ui/src/plugins/builtin/ThirdPartyConnector/services.ts diff --git a/ui/src/plugins/builtin/UcLogin/info.yaml b/ui/src/plugins/builtin/UcLogin/info.yaml deleted file mode 100644 index d181d870..00000000 --- a/ui/src/plugins/builtin/UcLogin/info.yaml +++ /dev/null @@ -1,4 +0,0 @@ -slug_name: uc_login -version: 0.0.1 -author: Answer.dev - diff --git a/ui/src/plugins/builtin/index.ts b/ui/src/plugins/builtin/index.ts index 54418eb6..515b8d3d 100644 --- a/ui/src/plugins/builtin/index.ts +++ b/ui/src/plugins/builtin/index.ts @@ -1,9 +1,9 @@ -import Connector from './Connector'; -import UcLogin from './UcLogin'; +import ThirdPartyConnector from './ThirdPartyConnector'; +import HostingConnector from './HostingConnector'; import Algolia from './Algolia'; export default { - Connector, - UcLogin, + ThirdPartyConnector, + HostingConnector, Algolia, }; diff --git a/ui/src/utils/pluginKit/index.ts b/ui/src/utils/pluginKit/index.ts new file mode 100644 index 00000000..3ca00491 --- /dev/null +++ b/ui/src/utils/pluginKit/index.ts @@ -0,0 +1,94 @@ +import { NamedExoticComponent, FC } from 'react'; + +import builtin from '@/plugins/builtin'; +import * as allPlugins from '@/plugins'; + +/** + * This information is to be defined for all components. + * 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 + */ + +export type PluginType = 'connector' | 'search' | 'editor'; +export interface PluginInfo { + slug_name: string; + type: PluginType; + name?: string; + description?: string; +} + +export interface Plugin { + info: PluginInfo; + component: NamedExoticComponent | FC; +} + +class Plugins { + plugins: Plugin[] = []; + + constructor() { + this.registerBuiltin(); + this.registerPlugins(); + } + + validate(plugin: Plugin) { + if (!plugin) { + return false; + } + const { info } = plugin; + const { slug_name, type } = info; + + if (!slug_name) { + return false; + } + + if (!type) { + return false; + } + + return true; + } + + registerBuiltin() { + Object.keys(builtin).forEach((key) => { + const plugin = builtin[key]; + this.register(plugin); + }); + } + + registerPlugins() { + Object.keys(allPlugins).forEach((key) => { + const plugin = allPlugins[key]; + this.register(plugin); + }); + } + + register(plugin: Plugin) { + const bool = this.validate(plugin); + if (!bool) { + return; + } + this.plugins.push(plugin); + } + + getPlugin(slug_name: string) { + return this.plugins.find((p) => p.info.slug_name === slug_name); + } + + getPluginsByType(type: PluginType) { + return this.plugins.filter((p) => p.info.type === type); + } + + getPlugins() { + return this.plugins; + } +} + +const plugins = new Plugins(); + +export default plugins; diff --git a/ui/src/utils/pluginKit.ts b/ui/src/utils/pluginKit/utils.ts similarity index 90% rename from ui/src/utils/pluginKit.ts rename to ui/src/utils/pluginKit/utils.ts index 3b88b582..f7de8564 100644 --- a/ui/src/utils/pluginKit.ts +++ b/ui/src/utils/pluginKit/utils.ts @@ -8,7 +8,7 @@ import i18next from 'i18next'; * * @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. + * 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 @@ -16,10 +16,10 @@ import i18next from 'i18next'; const I18N_NS = 'plugin'; -export type PluginType = 'Connector'; +export type PluginType = 'connector' | 'search' | 'editor'; export interface PluginInfo { slug_name: string; - type?: PluginType; + type: PluginType; name?: string; description?: string; } @@ -71,8 +71,4 @@ const getTransKeyPrefix = (info: PluginInfo) => { return kp; }; -export default { - initI18nResource, - getTransNs, - getTransKeyPrefix, -}; +export { initI18nResource, getTransNs, getTransKeyPrefix };