@bbki.ng/site 5.7.0 → 5.8.0

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 (49) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/index.html +0 -11
  3. package/package.json +1 -1
  4. package/src/app/app.tsx +1 -2
  5. package/src/core/components/SlotComp.tsx +2 -3
  6. package/src/core/hooks/useMiddlewareTransData.ts +12 -7
  7. package/src/core/hooks/useSlotComp.ts +8 -7
  8. package/src/core/hooks/use_plugins.ts +27 -14
  9. package/src/core/plugin-system/pluginManager.ts +129 -0
  10. package/src/core/{pluginManifestService.ts → plugin-system/pluginManifestService.ts} +2 -3
  11. package/src/core/plugin-system/pluginStore.ts +190 -0
  12. package/src/core/{registry.ts → plugin-system/registry.ts} +1 -3
  13. package/src/core/plugin-system/services/coreService.ts +56 -0
  14. package/src/core/plugin-system/services/systemUIService.ts +159 -0
  15. package/src/core/shared-service/contract/ICoreService.ts +23 -0
  16. package/src/core/shared-service/contract/IUIService.ts +49 -0
  17. package/src/core/shared-service/service-proxy.ts +26 -0
  18. package/src/core/shared-service/service-registry.ts +52 -0
  19. package/src/plugins/blog/components/BlogSlotCom.tsx +27 -0
  20. package/src/plugins/blog/components/article/index.tsx +2 -2
  21. package/src/plugins/blog/context/index.ts +0 -4
  22. package/src/plugins/blog/hooks/useMiddlewareTransData.ts +79 -0
  23. package/src/plugins/blog/hooks/use_blog_scroll_pos_restoration.ts +2 -2
  24. package/src/plugins/blog/hooks/use_blog_slot_com.ts +27 -0
  25. package/src/plugins/blog/hooks/use_posts.ts +3 -2
  26. package/src/plugins/blog/index.ts +32 -5
  27. package/src/plugins/blog/pages/extensions/txt/article.tsx +2 -2
  28. package/src/plugins/blog/services/BlogUIService.ts +120 -0
  29. package/src/plugins/blog/services/IBlogUIService.ts +31 -0
  30. package/src/plugins/extra-cd/index.ts +13 -2
  31. package/src/plugins/extra-entry/index.ts +8 -4
  32. package/src/plugins/fx/index.ts +18 -5
  33. package/src/plugins/manifest.ts +2 -6
  34. package/src/plugins/now/hooks/use_streaming.ts +3 -1
  35. package/src/plugins/now/index.ts +17 -6
  36. package/src/plugins/sticker/const.ts +2 -2
  37. package/src/plugins/sticker/index.ts +18 -3
  38. package/src/plugins/store/components/storePage.tsx +13 -2
  39. package/src/plugins/store/context/index.ts +1 -0
  40. package/src/plugins/store/index.ts +18 -7
  41. package/src/plugins/xwy/index.ts +24 -19
  42. package/src/plugins/xwy/types/index.ts +0 -18
  43. package/src/types/hostApi.ts +3 -34
  44. package/src/types/plugin.ts +1 -0
  45. package/src/utils/index.tsx +1 -48
  46. package/vite.config.js +1 -1
  47. package/src/core/pluginManager.ts +0 -191
  48. package/src/core/pluginStore.ts +0 -70
  49. /package/src/core/{bbplugin.ts → plugin-system/bbplugin.ts} +0 -0
@@ -0,0 +1,159 @@
1
+ import React from 'react';
2
+ import { PathRouteProps } from 'react-router-dom';
3
+ import { LinkProps } from '@bbki.ng/ui';
4
+
5
+ import {
6
+ ISystemUIService,
7
+ SystemDataHookPoint,
8
+ SystemSlotName,
9
+ } from '#/core/shared-service/contract/IUIService';
10
+ 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
+
17
+ type RegistryEvents = {
18
+ 'system:slots:changed': void;
19
+ 'system:middleware:changed': SystemDataHookPoint;
20
+ };
21
+
22
+ export class SystemUIService implements ISystemUIService {
23
+ private static instance: SystemUIService;
24
+
25
+ private slots = new Map<SystemSlotName, Array<ISlotEntry>>();
26
+ private middlewares = new Map<SystemDataHookPoint, IMiddlewareEntry<unknown>[]>();
27
+
28
+ private bus = createEventBus<RegistryEvents>();
29
+
30
+ private constructor() {}
31
+
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
+ static getInstance(): SystemUIService {
65
+ if (!SystemUIService.instance) {
66
+ SystemUIService.instance = new SystemUIService();
67
+ }
68
+ return SystemUIService.instance;
69
+ }
70
+
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;
89
+
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
+ );
104
+ };
105
+
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);
124
+
125
+ this.bus.emit('system:slots:changed', undefined);
126
+ };
127
+
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
+ });
133
+
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);
138
+ });
139
+
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;
156
+ }
157
+ return result;
158
+ }
159
+ }
@@ -0,0 +1,23 @@
1
+ import { Fetcher } from 'swr';
2
+
3
+ import { FingerprintData } from '#/app/utils/fingerprints';
4
+ import { PluginID } from '#/types/plugin';
5
+
6
+ declare module '#/core/shared-service/service-registry' {
7
+ interface ServiceIdentifierMap {
8
+ 'core:baseService': ICoreService;
9
+ }
10
+ }
11
+
12
+ export interface ICoreService {
13
+ getVersionHash(): Promise<string> | string;
14
+ getDeviceId(): Promise<{ id: string; fp: FingerprintData }>;
15
+
16
+ setLoading(id: string, loading: boolean): void;
17
+ subscribeLoadingChange(listener: (payload: boolean) => void): () => void;
18
+
19
+ subscribePluginUninstall(listener: (payload: PluginID) => void): () => void;
20
+ notifyPluginUninstall(id: PluginID): void;
21
+
22
+ fetch: Fetcher;
23
+ }
@@ -0,0 +1,49 @@
1
+ import type React from 'react';
2
+
3
+ import { IPluginEntry, PluginID } from '#/types/plugin';
4
+ import { IComPropsRegisteredToSlot } from '#/types/slots';
5
+
6
+ declare module '#/core/shared-service/service-registry' {
7
+ interface ServiceIdentifierMap {
8
+ 'core:uiService': ISystemUIService;
9
+ }
10
+ }
11
+
12
+ export interface IBaseUIService<T extends string, K extends string> {
13
+ registerMiddleware: <S>(
14
+ hookPoint: K,
15
+ fn: (data: S) => Promise<S> | S,
16
+ pluginId: PluginID,
17
+ weight?: number
18
+ ) => void;
19
+
20
+ registerSlot: (
21
+ slotName: T,
22
+ component: React.ComponentType<IComPropsRegisteredToSlot>,
23
+ pluginId: PluginID,
24
+ weight?: number
25
+ ) => void;
26
+ }
27
+
28
+ export type SystemSlotName = 'leftCol' | 'rightCol' | 'logo' | 'route' | 'pageFooter';
29
+
30
+ export type SystemDataHookPoint =
31
+ | 'extendedRoutes'
32
+ | 'transformBreadcrumbPath'
33
+ | 'transformCoverEntry';
34
+
35
+ 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
+ registerPluginEntry: (entry: IPluginEntry, id: PluginID) => void;
49
+ }
@@ -0,0 +1,26 @@
1
+ import { ServiceIdentifierMap, ServiceRegistry } from '#/core/shared-service/service-registry';
2
+
3
+ // core/ServiceProxy.ts
4
+ export function createServiceProxy<T extends object, K extends keyof ServiceIdentifierMap>(
5
+ serviceId: K,
6
+ container: ServiceRegistry
7
+ ): T {
8
+ return new Proxy({} as T, {
9
+ get(target, prop, receiver) {
10
+ const actualService = container.getRawInstance(serviceId);
11
+
12
+ if (!actualService) {
13
+ return (..._: unknown[]) => {
14
+ console.warn(
15
+ `[System] Service ${serviceId} is currently unavailable. Call to ${String(prop)} ignored.`
16
+ );
17
+ return undefined; // 或者返回一个待定 Promise
18
+ };
19
+ }
20
+
21
+ // 3. 正常转发调用
22
+ const value = Reflect.get(actualService, prop, receiver);
23
+ return typeof value === 'function' ? value.bind(actualService) : value;
24
+ },
25
+ });
26
+ }
@@ -0,0 +1,52 @@
1
+ import { createServiceProxy } from './service-proxy';
2
+
3
+ export interface ServiceIdentifierMap {}
4
+
5
+ export class ServiceRegistry {
6
+ private services: Map<string, unknown> = new Map();
7
+
8
+ private constructor() {}
9
+
10
+ private static instance: ServiceRegistry;
11
+
12
+ static getInstance(): ServiceRegistry {
13
+ if (!ServiceRegistry.instance) {
14
+ ServiceRegistry.instance = new ServiceRegistry();
15
+ }
16
+ return ServiceRegistry.instance;
17
+ }
18
+
19
+ register<T extends keyof ServiceIdentifierMap>(name: T, service: ServiceIdentifierMap[T]): void {
20
+ if (this.services.has(name)) {
21
+ console.warn(`Service with name ${name} is already registered. It will be overwritten.`);
22
+ }
23
+ this.services.set(name, service);
24
+ }
25
+
26
+ getRawInstance<T extends keyof ServiceIdentifierMap>(
27
+ name: T
28
+ ): ServiceIdentifierMap[T] | undefined {
29
+ const target = this.services.get(name);
30
+ if (!target) {
31
+ console.warn(`Service with name ${name} is not registered.`);
32
+ return undefined;
33
+ }
34
+
35
+ return target as ServiceIdentifierMap[T];
36
+ }
37
+
38
+ get<T extends keyof ServiceIdentifierMap>(name: T): ServiceIdentifierMap[T] | undefined {
39
+ // const target = this.services.get(name);
40
+ // if (!target) {
41
+ // console.warn(`Service with name ${name} is not registered.`);
42
+ // return undefined;
43
+ // }
44
+
45
+ // return target as ServiceIdentifierMap[T];
46
+ return createServiceProxy(name, this) as ServiceIdentifierMap[T];
47
+ }
48
+
49
+ unregister<T extends keyof ServiceIdentifierMap>(name: T): void {
50
+ this.services.delete(name);
51
+ }
52
+ }
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+
3
+ import { useBlogSlotComp } from '../hooks/use_blog_slot_com';
4
+ import { BlogSlotName } from '../services/IBlogUIService';
5
+
6
+ export interface ISlotProps {
7
+ name: BlogSlotName;
8
+ data?: unknown;
9
+
10
+ placeholder?: React.ReactNode;
11
+ }
12
+
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
+ };
@@ -2,7 +2,7 @@ import React, { ReactElement, ReactNode } from 'react';
2
2
  import { Article, Link } from '@bbki.ng/ui';
3
3
  import classNames from 'classnames';
4
4
 
5
- import { Slot } from '#/core/components/SlotComp';
5
+ import { Slot } from '../BlogSlotCom';
6
6
 
7
7
  export type ArticlePageProps = {
8
8
  tags?: string[];
@@ -27,7 +27,7 @@ export const ArticlePage = (props: ArticlePageProps) => {
27
27
  return (
28
28
  <>
29
29
  <Article
30
- title={<Slot name="articleTitle" data={title} placeholder={<>{title}</>} />}
30
+ title={<Slot name="blog:articleTitle" data={title} placeholder={<>{title}</>} />}
31
31
  date={props.date}
32
32
  description={description}
33
33
  className={`${props.className || ''}`}
@@ -1,10 +1,6 @@
1
- import type React from 'react';
2
-
3
- import { ISlotProps } from '#/core/components/SlotComp';
4
1
  import { createPluginCtx } from '#/core/context';
5
2
 
6
3
  export interface IBlogContext {
7
- Slot: React.FC<ISlotProps>;
8
4
  setLoading: (loading: boolean) => void;
9
5
  }
10
6
 
@@ -0,0 +1,79 @@
1
+ import { useState, useEffect, useCallback, useRef } from 'react';
2
+
3
+ import { BlogUIService } from '../services/BlogUIService';
4
+ import { BlogDataHookPoint } from '../services/IBlogUIService';
5
+
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
+ };
@@ -15,7 +15,7 @@ function saveScrollPosition(key: string, position: number) {
15
15
 
16
16
  export function useBlogScrollReset() {
17
17
  useEffect(() => {
18
- const element = document.getElementById('blog');
18
+ const element = document.getElementById('app');
19
19
  if (!element) return;
20
20
 
21
21
  element.scrollTop = 0;
@@ -23,7 +23,7 @@ export function useBlogScrollReset() {
23
23
  }
24
24
 
25
25
  export function useBlogScroll() {
26
- const element = document.getElementById('blog');
26
+ const element = document.getElementById('app');
27
27
 
28
28
  const gotoTop = useCallback(() => {
29
29
  if (!element) return;
@@ -0,0 +1,27 @@
1
+ import React, { useEffect, useState } from 'react';
2
+
3
+ import { IComPropsRegisteredToSlot } from '#/types/slots';
4
+
5
+ import { BlogSlotName } from '../services/IBlogUIService';
6
+ import { BlogUIService } from '../services/BlogUIService';
7
+
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
+ };
@@ -2,10 +2,11 @@ import useSWR from 'swr';
2
2
  import { useEffect, useMemo, useState } from 'react';
3
3
 
4
4
  import { IPost } from '#/types/posts';
5
- import { useMiddlewareRunner } from '#/core/hooks/useMiddlewareTransData';
6
5
 
7
6
  import { BlogContext } from '../context';
8
7
 
8
+ import { useMiddlewareRunner } from './useMiddlewareTransData';
9
+
9
10
  interface PostsApiResponse {
10
11
  data: IPost[];
11
12
  // 可能还有其他字段如 status, message 等
@@ -46,7 +47,7 @@ export const usePosts = (name: string = '', suspense?: boolean) => {
46
47
  error: transformError,
47
48
  run,
48
49
  } = useMiddlewareRunner<TitleListItem[]>({
49
- hookPoint: 'transformTitleList',
50
+ hookPoint: 'blog:transformTitleList',
50
51
  });
51
52
 
52
53
  useEffect(() => {
@@ -1,27 +1,44 @@
1
- import { BBPlugin } from '#/core/bbplugin';
2
- import { Slot } from '#/core/components/SlotComp';
1
+ import { BBPlugin } from '#/core/plugin-system/bbplugin';
2
+ import { SystemUIService } from '#/core/plugin-system/services/systemUIService';
3
+ import { ServiceRegistry } from '#/core/shared-service/service-registry';
3
4
  import { IHostContext } from '#/types/hostApi';
4
5
  import { PluginID } from '#/types/plugin';
5
6
 
6
7
  import { BlogPageApp } from './components/app';
7
8
  import { BlogContext } from './context';
9
+ import { BlogUIService } from './services/BlogUIService';
8
10
 
9
11
  const { withCtx } = BlogContext;
10
12
 
11
13
  export class BlogPlugin extends BBPlugin {
12
14
  id: PluginID = 'blog';
13
15
 
16
+ private _serviceRegistry?: ServiceRegistry;
17
+
14
18
  override onInstall = async (ctx: IHostContext) => {
15
- ctx.api.registerEntry(
19
+ ctx.service.register('blog:uiService', BlogUIService.getInstance());
20
+
21
+ this._serviceRegistry = ctx.service;
22
+
23
+ const coreService = ctx.service.get('core:baseService');
24
+ const systemUIService = ctx.service.get('core:uiService');
25
+
26
+ if (!coreService) {
27
+ console.error('Core service not found. BlogPlugin installation failed.');
28
+ return;
29
+ }
30
+
31
+ coreService.subscribePluginUninstall(this.handlePluginUninstall);
32
+
33
+ systemUIService?.registerPluginEntry(
16
34
  {
17
35
  path: '/blog',
18
36
  label: 'cd ./blog',
19
37
  pageComponent: withCtx(
20
38
  {
21
39
  setLoading: loading => {
22
- ctx.api.setLoading('blog', loading);
40
+ coreService.setLoading('blog', loading);
23
41
  },
24
- Slot: Slot,
25
42
  },
26
43
  BlogPageApp
27
44
  ),
@@ -29,6 +46,16 @@ export class BlogPlugin extends BBPlugin {
29
46
  this.id
30
47
  );
31
48
  };
49
+
50
+ override onDestroy?: (() => void) | undefined = () => {
51
+ BlogUIService.getInstance().unregisterAllByPluginId(this.id);
52
+ this._serviceRegistry?.unregister('blog:uiService');
53
+ };
54
+
55
+ private handlePluginUninstall = (payload: PluginID) => {
56
+ BlogUIService.getInstance().unregisterAllByPluginId(payload);
57
+ SystemUIService.getInstance().unregisterAllByPluginId(payload);
58
+ };
32
59
  }
33
60
 
34
61
  export default new BlogPlugin();
@@ -5,7 +5,7 @@ import { useParams } from 'react-router-dom';
5
5
  import { usePosts } from '#/plugins/blog/hooks/use_posts';
6
6
  import { ArticlePage } from '#/plugins/blog/components/article';
7
7
  import { useBlogScrollReset } from '#/plugins/blog/hooks/use_blog_scroll_pos_restoration';
8
- import { useMiddlewareTransformedData } from '#/core/hooks/useMiddlewareTransData';
8
+ import { useMiddlewareTransformedData } from '#/plugins/blog/hooks/useMiddlewareTransData';
9
9
  import { IPost } from '#/types/posts';
10
10
 
11
11
  function TxtArticle() {
@@ -16,7 +16,7 @@ function TxtArticle() {
16
16
 
17
17
  const p = posts as IPost;
18
18
 
19
- const transformedContent = useMiddlewareTransformedData('transformPostContent', p?.content);
19
+ const transformedContent = useMiddlewareTransformedData('blog:transformPostContent', p?.content);
20
20
 
21
21
  if (!title) {
22
22
  return <NotFound />;