This is an automated email from the ASF dual-hosted git repository.

robin0716 pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/incubator-answer.git

commit c0a1f4e5cd816d739f1eb98ec9ca0294008165b3
Author: Ourai Lin <[email protected]>
AuthorDate: Sat Oct 19 20:25:10 2024 +0800

    fix(ui): route plugins register failed
---
 ui/src/App.tsx                  | 29 +++++++++++++++++++++++---
 ui/src/router/index.tsx         | 14 ++++++++-----
 ui/src/utils/pluginKit/index.ts | 46 +++++++++++++++++++++++++++++++++++------
 3 files changed, 75 insertions(+), 14 deletions(-)

diff --git a/ui/src/App.tsx b/ui/src/App.tsx
index affac7a9..1b7d1b82 100644
--- a/ui/src/App.tsx
+++ b/ui/src/App.tsx
@@ -17,14 +17,37 @@
  * under the License.
  */
 
-import { RouterProvider, createBrowserRouter } from 'react-router-dom';
+import {
+  type RouteObject,
+  RouterProvider,
+  createBrowserRouter,
+} from 'react-router-dom';
+import { useState, useEffect } from 'react';
 
 import './i18n/init';
 
-import '@/utils/pluginKit';
-import routes from '@/router';
+import { subscribe, unsubscribe } from '@/utils/pluginKit';
+import resolveRoutes from '@/router';
 
 function App() {
+  const [routes, setRoutes] = useState<RouteObject[]>([]);
+
+  useEffect(() => {
+    const callback = () => {
+      setRoutes(resolveRoutes());
+    };
+
+    subscribe('registered', callback);
+
+    return () => {
+      unsubscribe('registered', callback);
+    };
+  }, []);
+
+  if (routes.length === 0) {
+    return <div>initializing</div>;
+  }
+
   const router = createBrowserRouter(routes, {
     basename: process.env.REACT_APP_BASE_URL,
   });
diff --git a/ui/src/router/index.tsx b/ui/src/router/index.tsx
index 432cb581..8c111e0f 100644
--- a/ui/src/router/index.tsx
+++ b/ui/src/router/index.tsx
@@ -27,8 +27,6 @@ import baseRoutes, { RouteNode } from './routes';
 import RouteGuard from './RouteGuard';
 import RouteErrorBoundary from './RouteErrorBoundary';
 
-const routes: RouteNode[] = [];
-
 const routeWrapper = (routeNodes: RouteNode[], root: RouteNode[]) => {
   routeNodes.forEach((rn) => {
     if (rn.page === 'pages/Layout') {
@@ -76,8 +74,14 @@ const routeWrapper = (routeNodes: RouteNode[], root: 
RouteNode[]) => {
     }
   });
 };
-const mergedRoutes = mergeRoutePlugins(baseRoutes);
 
-routeWrapper(mergedRoutes, routes);
+function resolveRoutes(): RouteObject[] {
+  const routes: RouteNode[] = [];
+  const mergedRoutes = mergeRoutePlugins(baseRoutes);
+
+  routeWrapper(mergedRoutes, routes);
+
+  return routes as RouteObject[];
+}
 
-export default routes as RouteObject[];
+export default resolveRoutes;
diff --git a/ui/src/utils/pluginKit/index.ts b/ui/src/utils/pluginKit/index.ts
index 66d5c376..43b0d691 100644
--- a/ui/src/utils/pluginKit/index.ts
+++ b/ui/src/utils/pluginKit/index.ts
@@ -42,15 +42,46 @@ import { Plugin, PluginInfo, PluginType } from 
'./interface';
  * @field description: Plugin description, optionally configurable. Usually 
read from the `i18n` file
  */
 
+type EventName = string;
+type EventHandler = () => void;
+
 class Plugins {
   plugins: Plugin[] = [];
 
   registeredPlugins: Type.ActivatedPlugin[] = [];
 
+  events: Record<EventName, EventHandler[]> = {};
+
   constructor() {
     this.init();
   }
 
+  on(name: EventName, handler: EventHandler) {
+    if (!this.events[name]) {
+      this.events[name] = [];
+    }
+
+    this.events[name].push(handler);
+  }
+
+  off(name: EventName, handler?: EventHandler) {
+    const handlers = this.events[name];
+
+    if (!handlers || handlers.length === 0) {
+      return;
+    }
+
+    if (handler) {
+      this.events[name] = handlers.filter((func) => func !== handler);
+    } else {
+      delete this.events[name];
+    }
+  }
+
+  trigger(name: EventName) {
+    (this.events[name] || []).forEach((handler) => handler());
+  }
+
   init() {
     this.registerBuiltin();
 
@@ -101,12 +132,10 @@ class Plugins {
         return func;
       })
       .filter((p) => p);
-    return new Promise((resolve) => {
-      plugins.forEach(async (p) => {
-        const plugin = await p();
-        this.register(plugin);
-      });
-      resolve(true);
+    return Promise.all(plugins.map((p) => p())).then((resolvedPlugins) => {
+      resolvedPlugins.forEach((plugin) => this.register(plugin));
+      this.trigger('registered');
+      return true;
     });
   }
 
@@ -150,6 +179,9 @@ class Plugins {
 
 const plugins = new Plugins();
 
+const subscribe = plugins.on.bind(plugins);
+const unsubscribe = plugins.off.bind(plugins);
+
 const getRoutePlugins = () => {
   return plugins
     .getPlugins()
@@ -287,5 +319,7 @@ export {
   useCaptchaPlugin,
   useRenderPlugin,
   PluginType,
+  subscribe,
+  unsubscribe,
 };
 export default plugins;

Reply via email to