@bbki.ng/site 5.8.2 → 5.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/package.json +2 -2
  3. package/src/app/app.tsx +4 -8
  4. package/src/app/components/BaseLayout.tsx +2 -3
  5. package/src/app/components/cover/index.tsx +17 -15
  6. package/src/app/hooks/use_plugin_entries.ts +17 -27
  7. package/src/core/components/SlotComp.tsx +4 -17
  8. package/src/core/hooks/index.ts +2 -2
  9. package/src/core/hooks/useMiddlewareTransData.ts +13 -82
  10. package/src/core/hooks/useSlotComp.ts +3 -26
  11. package/src/core/hooks/use_plugins.ts +4 -3
  12. package/src/core/plugin-system/pluginManager.ts +5 -2
  13. package/src/core/plugin-system/pluginManifestService.ts +2 -18
  14. package/src/core/plugin-system/pluginStore.ts +1 -0
  15. package/src/core/plugin-system/registry.ts +1 -106
  16. package/src/core/plugin-system/services/systemUIService.ts +31 -126
  17. package/src/core/shared-service/contract/IUIService.ts +15 -17
  18. package/src/core/shared-service/factory/createUIService.ts +121 -0
  19. package/src/core/shared-service/factory/createUIServiceReactKit.tsx +149 -0
  20. package/src/plugins/blog/components/BlogSlotCom.tsx +4 -17
  21. package/src/plugins/blog/hooks/useMiddlewareTransData.ts +11 -76
  22. package/src/plugins/blog/hooks/use_blog_slot_com.ts +2 -24
  23. package/src/plugins/blog/index.ts +6 -1
  24. package/src/plugins/blog/services/BlogUIService.ts +10 -103
  25. package/src/plugins/blog/services/IBlogUIService.ts +1 -16
  26. package/src/plugins/store/components/storeIcon.tsx +16 -0
  27. package/src/plugins/store/index.ts +3 -1
  28. package/src/types/slots.ts +0 -17
  29. package/tsconfig.json +1 -1
  30. package/src/utils/index.tsx +0 -20
@@ -1,66 +1,23 @@
1
- import React from 'react';
2
- import { PathRouteProps } from 'react-router-dom';
3
- import { LinkProps } from '@bbki.ng/ui';
4
-
1
+ import { createUIService } from '#/core/shared-service/factory/createUIService';
2
+ import { createEventBus } from '#/core/utils/eventBus';
5
3
  import {
6
4
  ISystemUIService,
7
5
  SystemDataHookPoint,
8
6
  SystemSlotName,
9
7
  } from '#/core/shared-service/contract/IUIService';
10
8
  import { IPluginEntry, PluginID } from '#/types/plugin';
11
- import { IComPropsRegisteredToSlot } from '#/types/slots';
12
- import { createEventBus } from '#/core/utils/eventBus';
13
- import { buildEntrySlotCom } from '#/utils';
14
-
15
- import { IMiddlewareEntry, ISlotEntry } from '../registry';
16
9
 
17
- type RegistryEvents = {
18
- 'system:slots:changed': void;
19
- 'system:middleware:changed': SystemDataHookPoint;
20
- };
10
+ const baseService = createUIService<SystemSlotName, SystemDataHookPoint>('system');
21
11
 
22
12
  export class SystemUIService implements ISystemUIService {
23
13
  private static instance: SystemUIService;
24
14
 
25
- private slots = new Map<SystemSlotName, Array<ISlotEntry>>();
26
- private middlewares = new Map<SystemDataHookPoint, IMiddlewareEntry<unknown>[]>();
15
+ private pluginEntries = new Map<string, IPluginEntry & { pluginId: PluginID }>();
27
16
 
28
- private bus = createEventBus<RegistryEvents>();
17
+ private entryBus = createEventBus<{ pluginEntriesChanged: void }>();
29
18
 
30
19
  private constructor() {}
31
20
 
32
- subscribeSlotChange(listener: () => void) {
33
- return this.bus.on('system:slots:changed', listener);
34
- }
35
-
36
- registerMiddleware: <T>(
37
- hookPoint: SystemDataHookPoint,
38
- fn: (data: T) => T | Promise<T>,
39
- pluginId: PluginID,
40
- weight?: number
41
- ) => void = <T>(
42
- hookPoint: SystemDataHookPoint,
43
- fn: (data: T) => T | Promise<T>,
44
- pluginId: PluginID,
45
- weight = 0
46
- ) => {
47
- const existing = this.middlewares.get(hookPoint) || [];
48
-
49
- const newEntry: IMiddlewareEntry<T> = {
50
- id: `${pluginId}-${hookPoint}-middleware`,
51
- pluginId,
52
- fn,
53
- weight,
54
- };
55
-
56
- const updated = [...existing, newEntry as IMiddlewareEntry<unknown>].sort(
57
- (a, b) => (b.weight || 0) - (a.weight || 0)
58
- );
59
- this.middlewares.set(hookPoint, updated);
60
-
61
- this.bus.emit('system:middleware:changed', hookPoint);
62
- };
63
-
64
21
  static getInstance(): SystemUIService {
65
22
  if (!SystemUIService.instance) {
66
23
  SystemUIService.instance = new SystemUIService();
@@ -68,92 +25,40 @@ export class SystemUIService implements ISystemUIService {
68
25
  return SystemUIService.instance;
69
26
  }
70
27
 
71
- registerPluginEntry: (entry: IPluginEntry, id: PluginID) => void = (entry, id) => {
72
- this.registerMiddleware(
73
- 'extendedRoutes',
74
- (routes: Array<Omit<PathRouteProps, 'element'>>) => {
75
- return [
76
- ...routes,
77
- {
78
- path: entry.path,
79
- },
80
- ];
81
- },
82
- id,
83
- 10
84
- );
85
-
86
- this.registerSlot('route', buildEntrySlotCom(entry), id);
87
-
88
- if (!entry.label) return;
28
+ registerSlot = baseService.registerSlot.bind(baseService);
29
+ registerMiddleware = baseService.registerMiddleware.bind(baseService);
30
+ getSlotEntries = baseService.getSlotEntries.bind(baseService);
31
+ getComponents = baseService.getComponents.bind(baseService);
32
+ subscribeSlotChange = baseService.subscribeSlotChange.bind(baseService);
33
+ subscribeMiddlewareChange = baseService.subscribeMiddlewareChange.bind(baseService);
34
+ runMiddlewares = baseService.runMiddlewares.bind(baseService);
89
35
 
90
- this.registerMiddleware(
91
- 'transformCoverEntry',
92
- (entries: Array<LinkProps>) => {
93
- return [
94
- ...entries,
95
- {
96
- to: entry.path,
97
- children: entry.label,
98
- },
99
- ];
100
- },
101
- id,
102
- 10
103
- );
36
+ registerPluginEntry: (entry: IPluginEntry, id: PluginID) => void = (entry, id) => {
37
+ this.pluginEntries.set(entry.path, { ...entry, pluginId: id });
38
+ this.entryBus.emit('pluginEntriesChanged', undefined);
104
39
  };
105
40
 
106
- registerSlot: (
107
- slotName: SystemSlotName,
108
- component: React.ComponentType<IComPropsRegisteredToSlot>,
109
- pluginId: PluginID,
110
- weight?: number
111
- ) => void = (slotName, component, pluginId, weight = 0) => {
112
- const existing = this.slots.get(slotName) || [];
113
-
114
- const newEntry: ISlotEntry = {
115
- id: `${pluginId}-${component.name || 'comp'}`,
116
- pluginId,
117
- component,
118
- weight,
119
- };
120
-
121
- // 插入并按权重排序(权重大的在前)
122
- const updated = [...existing, newEntry].sort((a, b) => b.weight - a.weight);
123
- this.slots.set(slotName, updated);
41
+ getPluginEntries = (): Array<IPluginEntry & { pluginId: PluginID }> => {
42
+ return Array.from(this.pluginEntries.values());
43
+ };
124
44
 
125
- this.bus.emit('system:slots:changed', undefined);
45
+ subscribePluginEntryChange = (listener: () => void): (() => void) => {
46
+ return this.entryBus.on('pluginEntriesChanged', listener);
126
47
  };
127
48
 
128
- unregisterAllByPluginId(pluginId: string) {
129
- this.slots.forEach((entries, slotName) => {
130
- const filtered = entries.filter(entry => entry.pluginId !== pluginId);
131
- this.slots.set(slotName, filtered);
132
- });
49
+ unregisterAllByPluginId: (pluginId: string) => void = pluginId => {
50
+ baseService.unregisterAllByPluginId(pluginId);
133
51
 
134
- this.middlewares.forEach((entries, point) => {
135
- const filtered = entries.filter(entry => entry.pluginId !== pluginId);
136
- this.middlewares.set(point, filtered);
137
- this.bus.emit('system:middleware:changed', point);
52
+ let changed = false;
53
+ this.pluginEntries.forEach((entry, path) => {
54
+ if (entry.pluginId === pluginId) {
55
+ this.pluginEntries.delete(path);
56
+ changed = true;
57
+ }
138
58
  });
139
59
 
140
- this.bus.emit('system:slots:changed', undefined);
141
- }
142
-
143
- getComponents(slotName: SystemSlotName): React.ComponentType<IComPropsRegisteredToSlot>[] {
144
- return (this.slots.get(slotName) || []).map(entry => entry.component);
145
- }
146
-
147
- subscribeMiddlewareChange(listener: (point: SystemDataHookPoint) => void) {
148
- return this.bus.on('system:middleware:changed', listener);
149
- }
150
-
151
- async runMiddlewares<T>(point: SystemDataHookPoint, data: T): Promise<T> {
152
- const fns = (this.middlewares.get(point) || []).map(entry => entry.fn);
153
- let result = data;
154
- for (const fn of fns) {
155
- result = (await fn(result)) as T;
60
+ if (changed) {
61
+ this.entryBus.emit('pluginEntriesChanged', undefined);
156
62
  }
157
- return result;
158
- }
63
+ };
159
64
  }
@@ -1,5 +1,6 @@
1
1
  import type React from 'react';
2
2
 
3
+ import type { ISlotEntry } from '#/core/plugin-system/registry';
3
4
  import { IPluginEntry, PluginID } from '#/types/plugin';
4
5
  import { IComPropsRegisteredToSlot } from '#/types/slots';
5
6
 
@@ -23,27 +24,24 @@ export interface IBaseUIService<T extends string, K extends string> {
23
24
  pluginId: PluginID,
24
25
  weight?: number
25
26
  ) => void;
27
+
28
+ unregisterAllByPluginId(pluginId: string): void;
29
+
30
+ getSlotEntries(slotName: T): ISlotEntry[];
31
+
32
+ getComponents(slotName: T): React.ComponentType<IComPropsRegisteredToSlot>[];
33
+
34
+ subscribeSlotChange(listener: () => void): () => void;
35
+
36
+ subscribeMiddlewareChange(listener: (point: K) => void): () => void;
37
+
38
+ runMiddlewares: <S>(point: K, data: S) => Promise<S>;
26
39
  }
27
40
 
28
- export type SystemSlotName = 'leftCol' | 'rightCol' | 'logo' | 'route' | 'pageFooter';
41
+ export type SystemSlotName = 'leftCol' | 'rightCol' | 'logo' | 'pageFooter' | 'nav-right';
29
42
 
30
- export type SystemDataHookPoint =
31
- | 'extendedRoutes'
32
- | 'transformBreadcrumbPath'
33
- | 'transformCoverEntry';
43
+ export type SystemDataHookPoint = 'transformBreadcrumbPath' | 'transformEntryLink';
34
44
 
35
45
  export interface ISystemUIService extends IBaseUIService<SystemSlotName, SystemDataHookPoint> {
36
- registerMiddleware: <S>(
37
- hookPoint: SystemDataHookPoint,
38
- fn: (data: S) => S | Promise<S>,
39
- pluginId: PluginID,
40
- weight?: number
41
- ) => void;
42
- registerSlot: (
43
- slotName: SystemSlotName,
44
- component: React.ComponentType<IComPropsRegisteredToSlot>,
45
- pluginId: PluginID,
46
- weight?: number
47
- ) => void;
48
46
  registerPluginEntry: (entry: IPluginEntry, id: PluginID) => void;
49
47
  }
@@ -0,0 +1,121 @@
1
+ import type React from 'react';
2
+
3
+ import type { IMiddlewareEntry, ISlotEntry } from '#/core/plugin-system/registry';
4
+ import { createEventBus } from '#/core/utils/eventBus';
5
+ import type { PluginID } from '#/types/plugin';
6
+ import type { IComPropsRegisteredToSlot } from '#/types/slots';
7
+
8
+ export interface IUIService<TSlot extends string, THook extends string> {
9
+ registerSlot(
10
+ slotName: TSlot,
11
+ component: React.ComponentType<IComPropsRegisteredToSlot>,
12
+ pluginId: PluginID,
13
+ weight?: number
14
+ ): void;
15
+
16
+ registerMiddleware<T>(
17
+ hookPoint: THook,
18
+ fn: (data: T) => T | Promise<T>,
19
+ pluginId: PluginID,
20
+ weight?: number
21
+ ): void;
22
+
23
+ unregisterAllByPluginId(pluginId: string): void;
24
+
25
+ getSlotEntries(slotName: TSlot): ISlotEntry[];
26
+
27
+ getComponents(slotName: TSlot): React.ComponentType<IComPropsRegisteredToSlot>[];
28
+
29
+ subscribeSlotChange(listener: () => void): () => void;
30
+
31
+ subscribeMiddlewareChange(listener: (point: THook) => void): () => void;
32
+
33
+ runMiddlewares<T>(point: THook, data: T): Promise<T>;
34
+ }
35
+
36
+ export function createUIService<TSlot extends string, THook extends string>(
37
+ _: string
38
+ ): IUIService<TSlot, THook> {
39
+ const slots = new Map<TSlot, Array<ISlotEntry>>();
40
+ const middlewares = new Map<THook, Array<IMiddlewareEntry<unknown>>>();
41
+ const bus = createEventBus<{
42
+ slotsChanged: void;
43
+ middlewareChanged: THook;
44
+ }>();
45
+
46
+ return {
47
+ registerSlot(slotName, component, pluginId, weight = 0) {
48
+ const existing = slots.get(slotName) || [];
49
+ const newEntry: ISlotEntry = {
50
+ id: `${pluginId}-${slotName}-${component.name || 'comp'}`,
51
+ pluginId,
52
+ component,
53
+ weight,
54
+ };
55
+ const updated = [...existing, newEntry].sort((a, b) => (b.weight || 0) - (a.weight || 0));
56
+ slots.set(slotName, updated);
57
+ bus.emit('slotsChanged', undefined);
58
+ },
59
+
60
+ registerMiddleware(hookPoint, fn, pluginId, weight = 0) {
61
+ const existing = middlewares.get(hookPoint) || [];
62
+ const newEntry: IMiddlewareEntry<unknown> = {
63
+ id: `${pluginId}-${hookPoint}-middleware`,
64
+ pluginId,
65
+ fn: fn as (data: unknown) => unknown | Promise<unknown>,
66
+ weight,
67
+ };
68
+ const updated = [...existing, newEntry].sort((a, b) => (b.weight || 0) - (a.weight || 0));
69
+ middlewares.set(hookPoint, updated);
70
+ bus.emit('middlewareChanged', hookPoint);
71
+ },
72
+
73
+ unregisterAllByPluginId(pluginId) {
74
+ let slotsChanged = false;
75
+ slots.forEach((entries, slotName) => {
76
+ const filtered = entries.filter(entry => entry.pluginId !== pluginId);
77
+ if (filtered.length !== entries.length) {
78
+ slots.set(slotName, filtered);
79
+ slotsChanged = true;
80
+ }
81
+ });
82
+
83
+ middlewares.forEach((entries, point) => {
84
+ const filtered = entries.filter(entry => entry.pluginId !== pluginId);
85
+ if (filtered.length !== entries.length) {
86
+ middlewares.set(point, filtered);
87
+ bus.emit('middlewareChanged', point);
88
+ }
89
+ });
90
+
91
+ if (slotsChanged) {
92
+ bus.emit('slotsChanged', undefined);
93
+ }
94
+ },
95
+
96
+ getSlotEntries(slotName) {
97
+ return slots.get(slotName) || [];
98
+ },
99
+
100
+ getComponents(slotName) {
101
+ return (slots.get(slotName) || []).map(entry => entry.component);
102
+ },
103
+
104
+ subscribeSlotChange(listener) {
105
+ return bus.on('slotsChanged', listener as (data: unknown) => void);
106
+ },
107
+
108
+ subscribeMiddlewareChange(listener) {
109
+ return bus.on('middlewareChanged', listener as (data: unknown) => void);
110
+ },
111
+
112
+ async runMiddlewares(point, data) {
113
+ const fns = (middlewares.get(point) || []).map(entry => entry.fn);
114
+ let result: unknown = data;
115
+ for (const fn of fns) {
116
+ result = await fn(result);
117
+ }
118
+ return result as typeof data;
119
+ },
120
+ };
121
+ }
@@ -0,0 +1,149 @@
1
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
2
+
3
+ import type { IComPropsRegisteredToSlot } from '#/types/slots';
4
+
5
+ import type { IUIService } from './createUIService';
6
+
7
+ export interface UseMiddlewareRunnerOptions<THook extends string> {
8
+ hookPoint: THook;
9
+ onMiddlewareChange?: () => void;
10
+ }
11
+
12
+ export interface UseMiddlewareRunnerResult<T> {
13
+ loading: boolean;
14
+ error: Error | null;
15
+ run: (inputData: T) => Promise<T>;
16
+ }
17
+
18
+ export function createSlotHook<TSlot extends string>(service: IUIService<TSlot, string>) {
19
+ return function useSlotComp(slotName: TSlot) {
20
+ const [components, setComponents] = useState<React.ComponentType<IComPropsRegisteredToSlot>[]>(
21
+ () => service.getComponents(slotName)
22
+ );
23
+
24
+ useEffect(() => {
25
+ setComponents(service.getComponents(slotName));
26
+
27
+ const unsubscribe = service.subscribeSlotChange(() => {
28
+ setComponents(service.getComponents(slotName));
29
+ });
30
+
31
+ return () => {
32
+ unsubscribe();
33
+ };
34
+ }, [slotName]);
35
+
36
+ return components;
37
+ };
38
+ }
39
+
40
+ export function createSlotComponent<TSlot extends string>(service: IUIService<TSlot, string>) {
41
+ return function Slot({
42
+ name,
43
+ data,
44
+ placeholder,
45
+ }: {
46
+ name: TSlot;
47
+ data?: unknown;
48
+ placeholder?: React.ReactNode;
49
+ }) {
50
+ const [entries, setEntries] = useState(() => service.getSlotEntries(name));
51
+
52
+ useEffect(() => {
53
+ setEntries(service.getSlotEntries(name));
54
+
55
+ const unsubscribe = service.subscribeSlotChange(() => {
56
+ setEntries(service.getSlotEntries(name));
57
+ });
58
+
59
+ return () => {
60
+ unsubscribe();
61
+ };
62
+ }, [name]);
63
+
64
+ if (entries.length === 0) {
65
+ return <>{placeholder}</>;
66
+ }
67
+
68
+ return (
69
+ <>
70
+ {entries.map(({ id, component: Component }) => (
71
+ <Component key={id} data={data} />
72
+ ))}
73
+ </>
74
+ );
75
+ };
76
+ }
77
+
78
+ export function createMiddlewareRunnerHook<THook extends string>(
79
+ service: IUIService<string, THook>
80
+ ) {
81
+ return function useMiddlewareRunner<T>({
82
+ hookPoint,
83
+ onMiddlewareChange,
84
+ }: UseMiddlewareRunnerOptions<THook>): UseMiddlewareRunnerResult<T> {
85
+ const [loading, setLoading] = useState(false);
86
+ const [error, setError] = useState<Error | null>(null);
87
+
88
+ const run = useCallback(
89
+ async (inputData: T) => {
90
+ setLoading(true);
91
+ setError(null);
92
+ try {
93
+ const result = await service.runMiddlewares(hookPoint, inputData);
94
+ return result;
95
+ } catch (err) {
96
+ const error = err instanceof Error ? err : new Error(String(err));
97
+ setError(error);
98
+ throw error;
99
+ } finally {
100
+ setLoading(false);
101
+ }
102
+ },
103
+ [hookPoint]
104
+ );
105
+
106
+ useEffect(() => {
107
+ const unsubscribe = service.subscribeMiddlewareChange(hook => {
108
+ if (hook === hookPoint) {
109
+ onMiddlewareChange?.();
110
+ }
111
+ });
112
+
113
+ return () => {
114
+ unsubscribe();
115
+ };
116
+ }, [hookPoint, onMiddlewareChange]);
117
+
118
+ return { loading, error, run };
119
+ };
120
+ }
121
+
122
+ export function createMiddlewareTransformedDataHook<THook extends string>(
123
+ useMiddlewareRunner: <T>(opts: UseMiddlewareRunnerOptions<THook>) => UseMiddlewareRunnerResult<T>
124
+ ) {
125
+ return function useMiddlewareTransformedData<T>(hookPoint: THook, defaultValue: T) {
126
+ const [result, setResult] = useState<T>(defaultValue);
127
+
128
+ const runRef = useRef<(input: T) => Promise<T>>(() => Promise.resolve(defaultValue));
129
+
130
+ const onMiddlewareChange = useCallback(() => {
131
+ runRef.current(defaultValue).then(setResult);
132
+ }, [defaultValue]);
133
+
134
+ const { run } = useMiddlewareRunner<T>({
135
+ hookPoint,
136
+ onMiddlewareChange,
137
+ });
138
+
139
+ useEffect(() => {
140
+ runRef.current = run;
141
+ }, [run]);
142
+
143
+ useEffect(() => {
144
+ run(defaultValue).then(setResult);
145
+ }, [defaultValue, run]);
146
+
147
+ return result;
148
+ };
149
+ }
@@ -1,27 +1,14 @@
1
1
  import React from 'react';
2
2
 
3
- import { useBlogSlotComp } from '../hooks/use_blog_slot_com';
3
+ import { createSlotComponent } from '#/core/shared-service/factory/createUIServiceReactKit';
4
+
5
+ import { BlogUIService } from '../services/BlogUIService';
4
6
  import { BlogSlotName } from '../services/IBlogUIService';
5
7
 
6
8
  export interface ISlotProps {
7
9
  name: BlogSlotName;
8
10
  data?: unknown;
9
-
10
11
  placeholder?: React.ReactNode;
11
12
  }
12
13
 
13
- export const Slot: React.FC<ISlotProps> = ({ name, data, placeholder }) => {
14
- const components = useBlogSlotComp(name);
15
-
16
- if (components.length === 0) {
17
- return <>{placeholder}</>;
18
- }
19
-
20
- return (
21
- <>
22
- {components.map((Component, index) => (
23
- <Component key={index} data={data} />
24
- ))}
25
- </>
26
- );
27
- };
14
+ export const Slot = createSlotComponent<BlogSlotName>(BlogUIService.getInstance());
@@ -1,79 +1,14 @@
1
- import { useState, useEffect, useCallback, useRef } from 'react';
1
+ import {
2
+ createMiddlewareRunnerHook,
3
+ createMiddlewareTransformedDataHook,
4
+ } from '#/core/shared-service/factory/createUIServiceReactKit';
2
5
 
3
6
  import { BlogUIService } from '../services/BlogUIService';
4
- import { BlogDataHookPoint } from '../services/IBlogUIService';
5
7
 
6
- export interface UseMiddlewareTransDataOptions {
7
- hookPoint: BlogDataHookPoint;
8
- onMiddlewareChange?: () => void; // 通知外部,由外部决定是否 run
9
- }
10
-
11
- export interface UseMiddlewareTransDataResult<T> {
12
- loading: boolean;
13
- error: Error | null;
14
- run: (inputData: T) => Promise<T>;
15
- }
16
-
17
- export function useMiddlewareRunner<T>({
18
- hookPoint,
19
- onMiddlewareChange,
20
- }: UseMiddlewareTransDataOptions): UseMiddlewareTransDataResult<T> {
21
- const [loading, setLoading] = useState(false);
22
- const [error, setError] = useState<Error | null>(null);
23
-
24
- const run = useCallback(
25
- async (inputData: T) => {
26
- setLoading(true);
27
- setError(null);
28
- try {
29
- const result = await BlogUIService.getInstance().runMiddlewares(hookPoint, inputData);
30
- return result;
31
- } catch (err) {
32
- const error = err instanceof Error ? err : new Error(String(err));
33
- setError(error);
34
- throw error;
35
- } finally {
36
- setLoading(false);
37
- }
38
- },
39
- [hookPoint]
40
- );
41
-
42
- // 仅通知,不执行
43
- useEffect(() => {
44
- const unsubscribe = BlogUIService.getInstance().subscribeMiddlewareChange(hook => {
45
- if (hook === hookPoint) {
46
- onMiddlewareChange?.();
47
- }
48
- });
49
-
50
- return () => {
51
- unsubscribe();
52
- };
53
- }, [hookPoint, onMiddlewareChange]);
54
-
55
- return { loading, error, run };
56
- }
57
-
58
- export const useMiddlewareTransformedData = <T>(hookPoint: BlogDataHookPoint, defaultValue: T) => {
59
- const [result, setResult] = useState<T>(defaultValue);
60
-
61
- const runRef = useRef<(input: T) => Promise<T>>(() => Promise.resolve(defaultValue));
62
-
63
- const onMiddlewareChange = useCallback(() => {
64
- runRef.current(defaultValue).then(setResult);
65
- }, [defaultValue]);
66
-
67
- const { run } = useMiddlewareRunner<T>({
68
- hookPoint,
69
- onMiddlewareChange,
70
- });
71
-
72
- runRef.current = run;
73
-
74
- useEffect(() => {
75
- run(defaultValue).then(setResult);
76
- }, [defaultValue, run]);
77
-
78
- return result;
79
- };
8
+ export const useMiddlewareRunner = createMiddlewareRunnerHook(BlogUIService.getInstance());
9
+ export const useMiddlewareTransformedData =
10
+ createMiddlewareTransformedDataHook(useMiddlewareRunner);
11
+ export type {
12
+ UseMiddlewareRunnerOptions,
13
+ UseMiddlewareRunnerResult as UseMiddlewareTransformedDataResult,
14
+ } from '#/core/shared-service/factory/createUIServiceReactKit';
@@ -1,27 +1,5 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import { createSlotHook } from '#/core/shared-service/factory/createUIServiceReactKit';
2
2
 
3
- import { IComPropsRegisteredToSlot } from '#/types/slots';
4
-
5
- import { BlogSlotName } from '../services/IBlogUIService';
6
3
  import { BlogUIService } from '../services/BlogUIService';
7
4
 
8
- export const useBlogSlotComp = (slotName: BlogSlotName) => {
9
- const [components, setComponents] = useState<React.ComponentType<IComPropsRegisteredToSlot>[]>(
10
- () => BlogUIService.getInstance().getComponents(slotName)
11
- );
12
-
13
- useEffect(() => {
14
- // setComponents(BlogUIService.getInstance().getComponents(slotName));
15
-
16
- const unsubscribe = BlogUIService.getInstance().subscribeSlotChange(() => {
17
- const comps = BlogUIService.getInstance().getComponents(slotName);
18
- setComponents(comps);
19
- });
20
-
21
- return () => {
22
- unsubscribe();
23
- };
24
- }, [slotName]);
25
-
26
- return components;
27
- };
5
+ export const useBlogSlotComp = createSlotHook(BlogUIService.getInstance());
@@ -15,6 +15,8 @@ export class BlogPlugin extends BBPlugin {
15
15
 
16
16
  private _serviceRegistry?: ServiceRegistry;
17
17
 
18
+ private _unsubscribePluginUninstall?: () => void;
19
+
18
20
  override onInstall = async (ctx: IHostContext) => {
19
21
  ctx.service.register('blog:uiService', BlogUIService.getInstance());
20
22
 
@@ -28,7 +30,9 @@ export class BlogPlugin extends BBPlugin {
28
30
  return;
29
31
  }
30
32
 
31
- coreService.subscribePluginUninstall(this.handlePluginUninstall);
33
+ this._unsubscribePluginUninstall = coreService.subscribePluginUninstall(
34
+ this.handlePluginUninstall
35
+ );
32
36
 
33
37
  systemUIService?.registerPluginEntry(
34
38
  {
@@ -50,6 +54,7 @@ export class BlogPlugin extends BBPlugin {
50
54
  override onDestroy?: (() => void) | undefined = () => {
51
55
  BlogUIService.getInstance().unregisterAllByPluginId(this.id);
52
56
  this._serviceRegistry?.unregister('blog:uiService');
57
+ this._unsubscribePluginUninstall?.();
53
58
  };
54
59
 
55
60
  private handlePluginUninstall = (payload: PluginID) => {