@bbki.ng/site 5.8.11 → 6.0.1

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 (93) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/package.json +6 -3
  3. package/src/app/app.tsx +8 -7
  4. package/src/app/components/BaseLayout.tsx +6 -4
  5. package/src/app/components/cover/index.tsx +9 -5
  6. package/src/app/context/bbcontext.tsx +1 -1
  7. package/src/app/index.tsx +18 -1
  8. package/src/types/hostApi.ts +1 -2
  9. package/src/app/context/global_loading_state_provider.tsx +0 -65
  10. package/src/app/hooks/use_global_loading.ts +0 -47
  11. package/src/app/hooks/use_plugin_entries.ts +0 -34
  12. package/src/core/components/SlotComp.tsx +0 -14
  13. package/src/core/const/index.ts +0 -3
  14. package/src/core/context/createPluginCtx.tsx +0 -60
  15. package/src/core/context/index.ts +0 -1
  16. package/src/core/hooks/index.ts +0 -6
  17. package/src/core/hooks/useMiddlewareTransData.ts +0 -13
  18. package/src/core/hooks/useSlotComp.ts +0 -4
  19. package/src/core/hooks/use_plugins.ts +0 -115
  20. package/src/core/plugin-system/bbplugin.ts +0 -15
  21. package/src/core/plugin-system/manifest.ts +0 -56
  22. package/src/core/plugin-system/pluginManager.ts +0 -126
  23. package/src/core/plugin-system/pluginManifestService.ts +0 -97
  24. package/src/core/plugin-system/pluginStore.ts +0 -200
  25. package/src/core/plugin-system/registry.ts +0 -17
  26. package/src/core/plugin-system/services/coreService.ts +0 -75
  27. package/src/core/plugin-system/services/systemUIService.ts +0 -64
  28. package/src/core/shared-service/contract/ICoreService.ts +0 -28
  29. package/src/core/shared-service/contract/IUIService.ts +0 -53
  30. package/src/core/shared-service/factory/createUIService.ts +0 -121
  31. package/src/core/shared-service/factory/createUIServiceReactKit.tsx +0 -149
  32. package/src/core/shared-service/service-proxy.ts +0 -28
  33. package/src/core/shared-service/service-registry.ts +0 -54
  34. package/src/core/utils/eventBus.ts +0 -29
  35. package/src/plugins/blog/components/BlogLink.tsx +0 -11
  36. package/src/plugins/blog/components/BlogSlotCom.tsx +0 -14
  37. package/src/plugins/blog/components/app.tsx +0 -15
  38. package/src/plugins/blog/components/article/index.tsx +0 -51
  39. package/src/plugins/blog/components/index.tsx +0 -10
  40. package/src/plugins/blog/constants/index.ts +0 -1
  41. package/src/plugins/blog/context/index.ts +0 -7
  42. package/src/plugins/blog/hooks/useMiddlewareTransData.ts +0 -14
  43. package/src/plugins/blog/hooks/use_blog_scroll_pos_restoration.ts +0 -89
  44. package/src/plugins/blog/hooks/use_blog_slot_com.ts +0 -5
  45. package/src/plugins/blog/hooks/use_posts.ts +0 -74
  46. package/src/plugins/blog/index.ts +0 -79
  47. package/src/plugins/blog/pages/extensions/txt/article.tsx +0 -42
  48. package/src/plugins/blog/pages/extensions/txt/index.tsx +0 -58
  49. package/src/plugins/blog/services/BlogUIService.ts +0 -27
  50. package/src/plugins/blog/services/IBlogUIService.ts +0 -16
  51. package/src/plugins/extra-cd/index.ts +0 -36
  52. package/src/plugins/extra-entry/components/page.tsx +0 -14
  53. package/src/plugins/extra-entry/index.ts +0 -28
  54. package/src/plugins/fx/components/index.tsx +0 -45
  55. package/src/plugins/fx/context/index.ts +0 -12
  56. package/src/plugins/fx/hooks/useTextEffects.ts +0 -39
  57. package/src/plugins/fx/index.ts +0 -53
  58. package/src/plugins/fx/services/FxService.ts +0 -47
  59. package/src/plugins/fx/services/IFxService.ts +0 -15
  60. package/src/plugins/notification/components/index.tsx +0 -26
  61. package/src/plugins/notification/index.ts +0 -26
  62. package/src/plugins/notification/services/INotificationService.ts +0 -24
  63. package/src/plugins/notification/services/NotificationService.ts +0 -29
  64. package/src/plugins/now/components/NowLink.tsx +0 -7
  65. package/src/plugins/now/components/index.tsx +0 -9
  66. package/src/plugins/now/components/streaming/arrow-down.tsx +0 -26
  67. package/src/plugins/now/components/streaming/index.tsx +0 -99
  68. package/src/plugins/now/components/streaming/useScrollBtnVisibility.ts +0 -28
  69. package/src/plugins/now/context/index.ts +0 -10
  70. package/src/plugins/now/hooks/use_streaming.ts +0 -88
  71. package/src/plugins/now/index.ts +0 -62
  72. package/src/plugins/now/utils/streaming.ts +0 -33
  73. package/src/plugins/sticker/components/StickerCom.tsx +0 -45
  74. package/src/plugins/sticker/const.ts +0 -76
  75. package/src/plugins/sticker/context.ts +0 -7
  76. package/src/plugins/sticker/index.ts +0 -43
  77. package/src/plugins/sticker/types.ts +0 -10
  78. package/src/plugins/store/components/ArrowCom.tsx +0 -34
  79. package/src/plugins/store/components/ArrowSvg.tsx +0 -1133
  80. package/src/plugins/store/components/storeIcon.tsx +0 -16
  81. package/src/plugins/store/components/storePage.tsx +0 -202
  82. package/src/plugins/store/context/index.ts +0 -15
  83. package/src/plugins/store/index.ts +0 -57
  84. package/src/plugins/store/utils/index.ts +0 -26
  85. package/src/plugins/version/index.ts +0 -28
  86. package/src/plugins/xwy/components/XwyLink.tsx +0 -7
  87. package/src/plugins/xwy/components/article.tsx +0 -13
  88. package/src/plugins/xwy/components/logo.tsx +0 -28
  89. package/src/plugins/xwy/const/index.ts +0 -28
  90. package/src/plugins/xwy/index.ts +0 -103
  91. package/src/plugins/xwy/transformers/index.ts +0 -67
  92. package/src/plugins/xwy/types/index.ts +0 -17
  93. package/src/plugins/xwy/utils/index.ts +0 -43
@@ -1,121 +0,0 @@
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
- }
@@ -1,149 +0,0 @@
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,28 +0,0 @@
1
- import { ServiceIdentifierMap, ServiceRegistry } from '#/core/shared-service/service-registry';
2
-
3
- const empty = {};
4
-
5
- // core/ServiceProxy.ts
6
- export function createServiceProxy<T extends object, K extends keyof ServiceIdentifierMap>(
7
- serviceId: K,
8
- container: ServiceRegistry
9
- ): T {
10
- return new Proxy({} as T, {
11
- get(target, prop, receiver) {
12
- const actualService = container.getRawInstance(serviceId);
13
-
14
- if (!actualService) {
15
- return (..._: unknown[]) => {
16
- console.warn(
17
- `[System] Service ${serviceId} is currently unavailable. Call to ${String(prop)} ignored.`
18
- );
19
- return empty;
20
- };
21
- }
22
-
23
- // 3. 正常转发调用
24
- const value = Reflect.get(actualService, prop, receiver);
25
- return typeof value === 'function' ? value.bind(actualService) : value;
26
- },
27
- });
28
- }
@@ -1,54 +0,0 @@
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>(
39
- name: T
40
- ): ServiceIdentifierMap[T] | Record<string, never> {
41
- // const target = this.services.get(name);
42
- // if (!target) {
43
- // console.warn(`Service with name ${name} is not registered.`);
44
- // return undefined;
45
- // }
46
-
47
- // return target as ServiceIdentifierMap[T];
48
- return createServiceProxy(name, this) as ServiceIdentifierMap[T];
49
- }
50
-
51
- unregister<T extends keyof ServiceIdentifierMap>(name: T): void {
52
- this.services.delete(name);
53
- }
54
- }
@@ -1,29 +0,0 @@
1
- export interface EventBus<Events extends Record<string, unknown>> {
2
- on<K extends keyof Events>(event: K, callback: (data: Events[K]) => void): () => void;
3
- emit<K extends keyof Events>(event: K, data: Events[K]): void;
4
- }
5
-
6
- export function createEventBus<Events extends Record<string, unknown>>(): EventBus<Events> {
7
- const listeners = new Map<keyof Events, Set<(data: Events[keyof Events]) => void>>();
8
-
9
- return {
10
- on<K extends keyof Events>(event: K, callback: (data: Events[K]) => void): () => void {
11
- if (!listeners.has(event)) {
12
- listeners.set(event, new Set());
13
- }
14
- const set = listeners.get(event)!;
15
- const cb = callback as (data: Events[keyof Events]) => void;
16
- set.add(cb);
17
-
18
- return () => {
19
- set.delete(cb);
20
- };
21
- },
22
-
23
- emit<K extends keyof Events>(event: K, data: Events[K]): void {
24
- const set = listeners.get(event);
25
- if (!set) return;
26
- set.forEach(callback => callback(data as Events[keyof Events]));
27
- },
28
- };
29
- }
@@ -1,11 +0,0 @@
1
- import { Link } from '@bbki.ng/ui';
2
-
3
- const LinkCom = () => {
4
- return (
5
- <Link to="/blog" className="ml-[var(--toast-button-margin-start)]">
6
- 打开
7
- </Link>
8
- );
9
- };
10
-
11
- export const BlogLink = <LinkCom />;
@@ -1,14 +0,0 @@
1
- import React from 'react';
2
-
3
- import { createSlotComponent } from '#/core/shared-service/factory/createUIServiceReactKit';
4
-
5
- import { BlogUIService } from '../services/BlogUIService';
6
- import { BlogSlotName } from '../services/IBlogUIService';
7
-
8
- export interface ISlotProps {
9
- name: BlogSlotName;
10
- data?: unknown;
11
- placeholder?: React.ReactNode;
12
- }
13
-
14
- export const Slot = createSlotComponent<BlogSlotName>(BlogUIService.getInstance());
@@ -1,15 +0,0 @@
1
- import React from 'react';
2
- import { useRoutes } from 'react-router-dom';
3
-
4
- import ArticlePage from '#/plugins/blog/pages/extensions/txt/article';
5
- import Txt from '#/plugins/blog/pages/extensions/txt';
6
- import { IComPropsRegisteredToSlot } from '#/types/slots';
7
-
8
- export const BlogPageApp = (_: IComPropsRegisteredToSlot) => {
9
- const element = useRoutes([
10
- { path: '/', element: <Txt /> },
11
- { path: '/:title', element: <ArticlePage /> },
12
- ]);
13
-
14
- return <>{element}</>;
15
- };
@@ -1,51 +0,0 @@
1
- import React, { ReactElement, ReactNode } from 'react';
2
- import { Article, Link } from '@bbki.ng/ui';
3
- import classNames from 'classnames';
4
-
5
- import { Slot } from '../BlogSlotCom';
6
-
7
- export type ArticlePageProps = {
8
- tags?: string[];
9
- title: string;
10
- date?: string;
11
- description?: ReactNode;
12
- headless?: boolean;
13
- className?: string;
14
- children: ReactElement;
15
- };
16
-
17
- export const ArticlePage = (props: ArticlePageProps) => {
18
- const { title, description, headless } = props;
19
- // const navgation = useNavigate();
20
-
21
- if (headless) {
22
- return props.children;
23
- }
24
-
25
- const articleCls = classNames('prose', 'mb-16');
26
-
27
- return (
28
- <>
29
- <Article
30
- title={<Slot name="blog:articleTitle" data={title} placeholder={<>{title}</>} />}
31
- date={props.date}
32
- description={description}
33
- className={`${props.className || ''}`}
34
- loading={false}
35
- >
36
- <article className={articleCls}>{props.children}</article>
37
- {/*<div className="relative -left-8">
38
- <Reaction title={title} url={window.location.href} />
39
- </div>*/}
40
- </Article>
41
- <div className="flex flex-col gap-4" style={{ position: 'relative', left: -4 }}>
42
- <Link className="w-fit" to="/blog">
43
- cd ..
44
- </Link>
45
- <Link className="w-fit" to="/">
46
- cd ~
47
- </Link>
48
- </div>
49
- </>
50
- );
51
- };
@@ -1,10 +0,0 @@
1
- import React from 'react';
2
- import { LinkList, LinkListProps } from '@bbki.ng/ui';
3
-
4
- export const CenterLinkList = (props: LinkListProps) => {
5
- return (
6
- <div className="flex justify-center relative h-full">
7
- <LinkList {...props} />
8
- </div>
9
- );
10
- };
@@ -1 +0,0 @@
1
- export const PLUGIN_NAME = 'blog';
@@ -1,7 +0,0 @@
1
- import { createPluginCtx } from '#/core/context';
2
-
3
- export interface IBlogContext {
4
- setLoading: (loading: boolean) => void;
5
- }
6
-
7
- export const BlogContext = createPluginCtx<IBlogContext>();
@@ -1,14 +0,0 @@
1
- import {
2
- createMiddlewareRunnerHook,
3
- createMiddlewareTransformedDataHook,
4
- } from '#/core/shared-service/factory/createUIServiceReactKit';
5
-
6
- import { BlogUIService } from '../services/BlogUIService';
7
-
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,89 +0,0 @@
1
- import { useCallback, useEffect, useRef } from 'react';
2
-
3
- const SCROLL_STORAGE_KEY = 'div-scroll-positions';
4
-
5
- function getScrollPositions(): Record<string, number> {
6
- const stored = sessionStorage.getItem(SCROLL_STORAGE_KEY);
7
- return stored ? JSON.parse(stored) : {};
8
- }
9
-
10
- function saveScrollPosition(key: string, position: number) {
11
- const positions = getScrollPositions();
12
- positions[key] = position;
13
- sessionStorage.setItem(SCROLL_STORAGE_KEY, JSON.stringify(positions));
14
- }
15
-
16
- export function useBlogScrollReset() {
17
- useEffect(() => {
18
- const element = document.getElementById('app');
19
- if (!element) return;
20
-
21
- element.scrollTop = 0;
22
- }, []);
23
- }
24
-
25
- export function useBlogScroll() {
26
- const element = document.getElementById('app');
27
-
28
- const gotoTop = useCallback(() => {
29
- if (!element) return;
30
-
31
- const id = setTimeout(() => {
32
- element.scrollTo({ top: 0, behavior: 'smooth' });
33
- }, 150);
34
-
35
- return () => clearTimeout(id);
36
- }, [element]);
37
-
38
- return {
39
- gotoTop,
40
- };
41
- }
42
-
43
- export function useBlogScrollRestoration(debounceMs: number = 100) {
44
- const isFirstRender = useRef(true);
45
- const scrollTimeoutRef = useRef<number>();
46
- const element = document.getElementById('blog');
47
- const scrollKey = `blog`;
48
-
49
- // Restore scroll position on mount
50
- useEffect(() => {
51
- if (!isFirstRender.current || !element) return;
52
-
53
- const positions = getScrollPositions();
54
- const savedPosition = positions[scrollKey];
55
- if (!savedPosition) {
56
- return;
57
- }
58
-
59
- requestAnimationFrame(() => {
60
- element.scrollTop = savedPosition;
61
- });
62
- isFirstRender.current = false;
63
- }, [element, scrollKey]);
64
-
65
- // Save scroll position with debouncing
66
- useEffect(() => {
67
- if (!element) return;
68
-
69
- const handleScroll = () => {
70
- // Clear previous timeout
71
- if (scrollTimeoutRef.current) {
72
- clearTimeout(scrollTimeoutRef.current);
73
- }
74
-
75
- // Debounce the save operation
76
- scrollTimeoutRef.current = window.setTimeout(() => {
77
- if (element) {
78
- saveScrollPosition(scrollKey, element.scrollTop);
79
- }
80
- }, debounceMs);
81
- };
82
-
83
- element.addEventListener('scroll', handleScroll, { passive: true });
84
-
85
- return () => {
86
- element.removeEventListener('scroll', handleScroll);
87
- };
88
- }, [debounceMs, element, scrollKey]);
89
- }
@@ -1,5 +0,0 @@
1
- import { createSlotHook } from '#/core/shared-service/factory/createUIServiceReactKit';
2
-
3
- import { BlogUIService } from '../services/BlogUIService';
4
-
5
- export const useBlogSlotComp = createSlotHook(BlogUIService.getInstance());
@@ -1,74 +0,0 @@
1
- import useSWR from 'swr';
2
- import { useEffect, useMemo, useState } from 'react';
3
-
4
- import { IPost } from '#/types/posts';
5
-
6
- import { BlogContext } from '../context';
7
-
8
- import { useMiddlewareRunner } from './useMiddlewareTransData';
9
-
10
- interface PostsApiResponse {
11
- data: IPost[];
12
- // 可能还有其他字段如 status, message 等
13
- }
14
-
15
- export interface TitleListItem {
16
- name: string;
17
- to: string;
18
- children: string;
19
- className?: string;
20
- }
21
-
22
- export const usePosts = (name: string = '', suspense?: boolean) => {
23
- const { data: response, error: swrError } = useSWR<PostsApiResponse>('posts', {
24
- revalidateOnFocus: false,
25
- suspense,
26
- });
27
-
28
- const { setLoading } = BlogContext.useCtx();
29
-
30
- const data = response?.data;
31
- const isDataLoading = !data && !swrError;
32
-
33
- const baseTitleList: TitleListItem[] = useMemo(() => {
34
- if (!data || swrError) return [];
35
-
36
- return data.map((p: IPost) => ({
37
- name: p.title,
38
- to: p.title,
39
- children: p.title,
40
- }));
41
- }, [data, swrError]);
42
-
43
- const [fullTitleList, setFullTitleList] = useState<TitleListItem[]>(baseTitleList);
44
-
45
- const {
46
- loading: isTransforming,
47
- error: transformError,
48
- run,
49
- } = useMiddlewareRunner<TitleListItem[]>({
50
- hookPoint: 'blog:transformTitleList',
51
- });
52
-
53
- useEffect(() => {
54
- if (baseTitleList.length > 0) {
55
- run(baseTitleList).then(setFullTitleList);
56
- }
57
- }, [baseTitleList, run]);
58
-
59
- useEffect(() => {
60
- setLoading(isDataLoading || isTransforming);
61
- }, [isDataLoading, isTransforming, setLoading]);
62
-
63
- const posts =
64
- isDataLoading || name === '' || swrError || !data
65
- ? data
66
- : data.find((p: IPost) => p.title === name);
67
-
68
- return {
69
- posts,
70
- titleList: fullTitleList ?? [],
71
- isError: swrError || transformError,
72
- isLoading: isDataLoading || isTransforming,
73
- };
74
- };