@bbki.ng/site 5.6.7 → 5.7.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.
- package/CHANGELOG.md +6 -0
- package/index.html +1 -1
- package/package.json +1 -1
- package/src/{blog → app}/app.tsx +12 -17
- package/src/{blog → app}/components/BaseLayout.tsx +2 -2
- package/src/{blog/pages → app/components}/cover/index.tsx +12 -13
- package/src/app/constants/index.ts +1 -0
- package/src/{blog → app}/context/bbcontext.tsx +2 -2
- package/src/{blog/hooks/use_loading.ts → app/hooks/use_global_loading.ts} +1 -1
- package/src/{blog → app}/hooks/use_paths.ts +14 -12
- package/src/{blog → app}/hooks/use_plugin_entries.ts +1 -3
- package/src/{blog → app}/index.tsx +2 -2
- package/src/{blog → app}/main.css +3 -3
- package/src/{blog → app}/swr.tsx +1 -1
- package/src/{blog → app}/utils/index.ts +1 -1
- package/src/core/hooks/use_plugins.ts +1 -1
- package/src/core/pluginManager.ts +64 -19
- package/src/core/pluginManifestService.ts +2 -1
- package/src/index.tsx +3 -3
- package/src/plugins/blog/components/app.tsx +15 -0
- package/src/plugins/blog/constants/index.ts +1 -0
- package/src/plugins/blog/context/index.ts +11 -0
- package/src/{blog → plugins/blog}/hooks/use_blog_scroll_pos_restoration.ts +2 -4
- package/src/{blog → plugins/blog}/hooks/use_posts.ts +8 -9
- package/src/plugins/blog/index.ts +34 -0
- package/src/{blog → plugins/blog}/pages/extensions/txt/article.tsx +3 -3
- package/src/{blog → plugins/blog}/pages/extensions/txt/index.tsx +6 -3
- package/src/plugins/manifest.ts +6 -0
- package/src/plugins/store/components/storePage.tsx +2 -2
- package/src/types/hostApi.ts +3 -2
- package/src/types/plugin.ts +1 -0
- package/src/utils/index.tsx +15 -0
- package/tsconfig.json +0 -1
- package/src/blog/constants/index.ts +0 -1
- package/src/blog/constants/routes.ts +0 -20
- package/src/blog/hooks/index.ts +0 -2
- package/src/blog/hooks/use_blog_context.ts +0 -14
- package/src/blog/hooks/use_mouse_position.ts +0 -17
- package/src/blog/hooks/use_role.ts +0 -14
- package/src/blog/hooks/use_sticker.ts +0 -11
- package/src/blog/pages/index.tsx +0 -2
- package/src/blog/pages/login/index.tsx +0 -24
- package/src/blog/types/blog-context.ts +0 -6
- package/src/blog/types/font.ts +0 -4
- package/src/blog/types/glsl.d.ts +0 -8
- /package/src/{blog/pages → app/components}/bot/index.tsx +0 -0
- /package/src/{blog → app}/context/global_loading_state_provider.tsx +0 -0
- /package/src/{blog → app}/context/global_routes_provider.tsx +0 -0
- /package/src/{blog → app}/hooks/use_pathname.ts +0 -0
- /package/src/{blog → app}/utils/fingerprints.ts +0 -0
- /package/src/{blog → plugins/blog}/components/article/index.tsx +0 -0
- /package/src/{blog → plugins/blog}/components/index.tsx +0 -0
- /package/src/{blog/types → types}/path.ts +0 -0
package/CHANGELOG.md
CHANGED
package/index.html
CHANGED
package/package.json
CHANGED
package/src/{blog → app}/app.tsx
RENAMED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { Route, Routes } from 'react-router-dom';
|
|
3
3
|
import { NotFound } from '@bbki.ng/ui';
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import { BotRedirect } from '@/pages/bot';
|
|
8
|
-
import { BBContext } from '@/context/bbcontext';
|
|
5
|
+
import { BotRedirect } from '#/app/components/bot';
|
|
6
|
+
import { BBContext } from '#/app/context/bbcontext';
|
|
9
7
|
import { Slot } from '#/core/components/SlotComp';
|
|
10
8
|
import { usePlugins } from '#/core/hooks/use_plugins';
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { BaseLayout } from './components/BaseLayout';
|
|
9
|
+
import { Cover } from '#/app/components/cover';
|
|
10
|
+
import { SWR } from '#/app/swr';
|
|
11
|
+
import { usePluginEntries } from '#/app/hooks/use_plugin_entries';
|
|
12
|
+
import { BaseLayout } from '#/app/components/BaseLayout';
|
|
16
13
|
|
|
17
14
|
const AppRoutes = () => {
|
|
18
15
|
usePlugins();
|
|
@@ -23,15 +20,13 @@ const AppRoutes = () => {
|
|
|
23
20
|
<Routes>
|
|
24
21
|
<Route path="/" element={<BaseLayout />}>
|
|
25
22
|
<Route index element={<Cover />} />
|
|
26
|
-
|
|
27
|
-
<Route path="blog" element={<Outlet />}>
|
|
28
|
-
<Route path="" element={<Txt />} />
|
|
29
|
-
<Route path=":title" element={<ArticlePage />} />
|
|
30
|
-
</Route>
|
|
31
|
-
|
|
32
23
|
<Route path="bot" element={<BotRedirect />} />
|
|
33
24
|
{pluginEntries?.map(route => (
|
|
34
|
-
<Route
|
|
25
|
+
<Route
|
|
26
|
+
key={route.path}
|
|
27
|
+
path={`${route.path}/*`}
|
|
28
|
+
element={<Slot name="route" data={route} />}
|
|
29
|
+
/>
|
|
35
30
|
))}
|
|
36
31
|
<Route path="*" element={pluginEntries?.length ? <NotFound /> : null} />
|
|
37
32
|
</Route>
|
|
@@ -3,8 +3,8 @@ import { useNavigate, Outlet } from 'react-router-dom';
|
|
|
3
3
|
import { Logo, Nav, Page, Grid, ErrorBoundary, Container } from '@bbki.ng/ui';
|
|
4
4
|
|
|
5
5
|
import { Slot } from '#/core/components/SlotComp';
|
|
6
|
-
import { usePaths } from '
|
|
7
|
-
import { GlobalLoadingContext } from '
|
|
6
|
+
import { usePaths } from '#/app/hooks/use_paths';
|
|
7
|
+
import { GlobalLoadingContext } from '#/app/context/global_loading_state_provider';
|
|
8
8
|
import { useMiddlewareTransformedData } from '#/core/hooks/useMiddlewareTransData';
|
|
9
9
|
|
|
10
10
|
export const BaseLayout = () => {
|
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
import React, { useEffect
|
|
2
|
-
import { LinkProps } from '@bbki.ng/ui';
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import { LinkProps, LinkListProps, LinkList } from '@bbki.ng/ui';
|
|
3
3
|
|
|
4
|
-
import { CenterLinkList } from '@/components';
|
|
5
4
|
import { useMiddlewareRunner } from '#/core/hooks';
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
{
|
|
11
|
-
|
|
12
|
-
children: 'cd ./blog',
|
|
13
|
-
},
|
|
14
|
-
],
|
|
15
|
-
[]
|
|
6
|
+
const CenterLinkList = (props: LinkListProps) => {
|
|
7
|
+
return (
|
|
8
|
+
<div className="flex justify-center relative h-full">
|
|
9
|
+
<LinkList {...props} />
|
|
10
|
+
</div>
|
|
16
11
|
);
|
|
12
|
+
};
|
|
17
13
|
|
|
14
|
+
const baseEntries: Array<LinkProps> = [];
|
|
15
|
+
|
|
16
|
+
export const Cover = (_: { className?: string }) => {
|
|
18
17
|
const [entries, setEntries] = React.useState<Array<LinkProps>>(baseEntries);
|
|
19
18
|
|
|
20
19
|
const { run } = useMiddlewareRunner<Array<LinkProps>>({
|
|
@@ -26,7 +25,7 @@ export const Cover = (_: { className?: string }) => {
|
|
|
26
25
|
|
|
27
26
|
useEffect(() => {
|
|
28
27
|
run(baseEntries).then(setEntries);
|
|
29
|
-
}, [
|
|
28
|
+
}, [run]);
|
|
30
29
|
|
|
31
30
|
return (
|
|
32
31
|
<>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const API_ENDPOINT = 'https://cf.bbki.ng';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { ReactNode } from 'react';
|
|
2
2
|
|
|
3
|
-
import { GlobalLoadingStateProvider } from '
|
|
4
|
-
import { GlobalRoutesProvider } from '
|
|
3
|
+
import { GlobalLoadingStateProvider } from '#/app/context/global_loading_state_provider';
|
|
4
|
+
import { GlobalRoutesProvider } from '#/app/context/global_routes_provider';
|
|
5
5
|
|
|
6
6
|
export const BBContext = (props: { children: ReactNode }) => {
|
|
7
7
|
return (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useContext, useEffect } from 'react';
|
|
2
2
|
|
|
3
|
-
import { GlobalLoadingContext } from '
|
|
3
|
+
import { GlobalLoadingContext } from '#/app/context/global_loading_state_provider';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Hook to register a loading state with the global loading context.
|
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import { useLocation } from 'react-router-dom';
|
|
2
1
|
import { useMemo } from 'react';
|
|
3
2
|
|
|
4
|
-
import { usePathName } from '
|
|
5
|
-
import { pathObj } from '
|
|
3
|
+
import { usePathName } from '#/app/hooks/use_pathname';
|
|
4
|
+
import { pathObj } from '#/types/path';
|
|
6
5
|
|
|
7
6
|
export const usePaths = (): pathObj[] => {
|
|
8
7
|
const pathname = usePathName();
|
|
9
|
-
const { pathname: locationPathname } = useLocation();
|
|
10
8
|
|
|
11
|
-
const pathNameArr = pathname.split('/');
|
|
9
|
+
const pathNameArr = useMemo(() => pathname.split('/'), [pathname]);
|
|
12
10
|
|
|
13
|
-
const pathsArr: string[] =
|
|
14
|
-
|
|
15
|
-
.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
const pathsArr: string[] = useMemo(
|
|
12
|
+
() =>
|
|
13
|
+
pathNameArr.map((p: string, index: number) => {
|
|
14
|
+
return pathNameArr
|
|
15
|
+
.slice(0, index + 1)
|
|
16
|
+
.join('/')
|
|
17
|
+
.replace(/^$/, '/');
|
|
18
|
+
}),
|
|
19
|
+
[pathNameArr]
|
|
20
|
+
);
|
|
19
21
|
|
|
20
22
|
const result = useMemo(() => {
|
|
21
23
|
return pathsArr.map((path, index) => {
|
|
@@ -28,7 +30,7 @@ export const usePaths = (): pathObj[] => {
|
|
|
28
30
|
path,
|
|
29
31
|
};
|
|
30
32
|
});
|
|
31
|
-
}, [
|
|
33
|
+
}, [pathNameArr, pathsArr]);
|
|
32
34
|
|
|
33
35
|
if (pathname === '/') {
|
|
34
36
|
return [{ name: '~' }];
|
|
@@ -3,7 +3,7 @@ import { type PathRouteProps } from 'react-router-dom';
|
|
|
3
3
|
|
|
4
4
|
import { useMiddlewareRunner } from '#/core/hooks';
|
|
5
5
|
|
|
6
|
-
const protectedRoutesSet = new Set<string>(['/bot', '/', '
|
|
6
|
+
const protectedRoutesSet = new Set<string>(['/bot', '/', 'default']);
|
|
7
7
|
|
|
8
8
|
type PluginRoute = Omit<PathRouteProps, 'element'>;
|
|
9
9
|
|
|
@@ -35,7 +35,5 @@ export const usePluginEntries = () => {
|
|
|
35
35
|
return false;
|
|
36
36
|
}) as Array<PluginRoute>;
|
|
37
37
|
|
|
38
|
-
console.log('safe plugin routes:', safeRoutes);
|
|
39
|
-
|
|
40
38
|
return safeRoutes ?? [];
|
|
41
39
|
};
|
|
@@ -2,10 +2,10 @@ import React from 'react';
|
|
|
2
2
|
import { createRoot } from 'react-dom/client';
|
|
3
3
|
import { BrowserRouter as Router } from 'react-router-dom';
|
|
4
4
|
|
|
5
|
-
import App from './app';
|
|
5
|
+
import { App } from './app';
|
|
6
6
|
import './main.css';
|
|
7
7
|
|
|
8
|
-
export const
|
|
8
|
+
export const RenderApp = (ele: Element) => {
|
|
9
9
|
const root = createRoot(ele);
|
|
10
10
|
root.render(
|
|
11
11
|
<React.StrictMode>
|
|
@@ -58,7 +58,7 @@ body {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
body,
|
|
61
|
-
#
|
|
61
|
+
#app {
|
|
62
62
|
height: 100lvh;
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -77,7 +77,7 @@ a {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
#
|
|
80
|
+
#app {
|
|
81
81
|
overflow: auto;
|
|
82
82
|
-ms-overflow-style: none; /* Internet Explorer 10+ */
|
|
83
83
|
scrollbar-width: none;
|
|
@@ -86,7 +86,7 @@ a {
|
|
|
86
86
|
box-sizing: border-box;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
#
|
|
89
|
+
#app::-webkit-scrollbar {
|
|
90
90
|
display: none;
|
|
91
91
|
}
|
|
92
92
|
|
package/src/{blog → app}/swr.tsx
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useMemo, useState } from 'react';
|
|
2
2
|
|
|
3
|
-
import { useGlobalLoading } from '
|
|
3
|
+
import { useGlobalLoading } from '#/app/hooks/use_global_loading';
|
|
4
4
|
import { pluginManager } from '#/core/pluginManager';
|
|
5
5
|
import { PluginID } from '#/types/plugin';
|
|
6
6
|
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { PathRouteProps } from 'react-router-dom';
|
|
3
|
+
import { LinkProps } from '@bbki.ng/ui';
|
|
2
4
|
|
|
3
5
|
import { IHostContext } from '#/types/hostApi';
|
|
4
6
|
import type { SlotName, HookPoint, IComPropsRegisteredToSlot } from '#/types/slots';
|
|
5
|
-
import { IPlugin, PluginEvents, PluginID, PluginPerm } from '#/types/plugin';
|
|
6
|
-
import { getStableDeviceId } from '
|
|
7
|
-
import { cfApiFetcher } from '
|
|
7
|
+
import { IPlugin, IPluginEntry, PluginEvents, PluginID, PluginPerm } from '#/types/plugin';
|
|
8
|
+
import { getStableDeviceId } from '#/app/utils/fingerprints';
|
|
9
|
+
import { cfApiFetcher } from '#/app/utils';
|
|
10
|
+
import { buildEntrySlotCom } from '#/utils';
|
|
8
11
|
|
|
9
12
|
import { registry } from './registry';
|
|
10
13
|
import { createEventBus } from './utils/eventBus';
|
|
@@ -18,6 +21,59 @@ class PluginManager {
|
|
|
18
21
|
|
|
19
22
|
private bus = createEventBus<PluginEvents>();
|
|
20
23
|
|
|
24
|
+
private registryEntry = (entry: IPluginEntry, id: string) => {
|
|
25
|
+
this.registerMiddleware(
|
|
26
|
+
'extendedRoutes',
|
|
27
|
+
(routes: Array<Omit<PathRouteProps, 'element'>>) => {
|
|
28
|
+
return [
|
|
29
|
+
...routes,
|
|
30
|
+
{
|
|
31
|
+
path: entry.path,
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
},
|
|
35
|
+
id,
|
|
36
|
+
10
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
this.registerSlot('route', buildEntrySlotCom(entry), id);
|
|
40
|
+
|
|
41
|
+
if (!entry.label) return;
|
|
42
|
+
|
|
43
|
+
this.registerMiddleware(
|
|
44
|
+
'transformCoverEntry',
|
|
45
|
+
(entries: Array<LinkProps>) => {
|
|
46
|
+
return [
|
|
47
|
+
...entries,
|
|
48
|
+
{
|
|
49
|
+
to: entry.path,
|
|
50
|
+
children: entry.label,
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
},
|
|
54
|
+
id,
|
|
55
|
+
10
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
private registerMiddleware = <T>(
|
|
60
|
+
point: HookPoint,
|
|
61
|
+
fn: (data: T) => Promise<T> | T,
|
|
62
|
+
pluginId: string,
|
|
63
|
+
weight = 0
|
|
64
|
+
) => {
|
|
65
|
+
registry.registerMiddleware(point, fn, pluginId, weight);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
private registerSlot = (
|
|
69
|
+
slotName: SlotName,
|
|
70
|
+
component: React.ComponentType<IComPropsRegisteredToSlot>,
|
|
71
|
+
pluginId: string,
|
|
72
|
+
weight = 0
|
|
73
|
+
) => {
|
|
74
|
+
registry.registerComponent(slotName, component, pluginId, weight);
|
|
75
|
+
};
|
|
76
|
+
|
|
21
77
|
private createHostContext(perm: PluginPerm = 'guest'): IHostContext {
|
|
22
78
|
const adminCtx =
|
|
23
79
|
perm === 'admin'
|
|
@@ -35,28 +91,17 @@ class PluginManager {
|
|
|
35
91
|
onLoadingChanged: (listener: (payload: PluginEvents['site:loading:changed']) => void) => {
|
|
36
92
|
return this.bus.on('site:loading:changed', listener);
|
|
37
93
|
},
|
|
38
|
-
|
|
39
|
-
point: HookPoint,
|
|
40
|
-
fn: (data: T) => Promise<T> | T,
|
|
41
|
-
pluginId: string,
|
|
42
|
-
weight = 0
|
|
43
|
-
) => {
|
|
44
|
-
registry.registerMiddleware(point, fn, pluginId, weight);
|
|
45
|
-
},
|
|
94
|
+
|
|
46
95
|
getDeviceId: getStableDeviceId,
|
|
47
96
|
getVersionHash: () => {
|
|
48
97
|
const hashStr: string =
|
|
49
98
|
typeof GLOBAL_COMMIT_HASH === 'string' ? GLOBAL_COMMIT_HASH : '0000000';
|
|
50
99
|
return hashStr;
|
|
51
100
|
},
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
weight = 0
|
|
57
|
-
) => {
|
|
58
|
-
registry.registerComponent(slotName, component, pluginId, weight);
|
|
59
|
-
},
|
|
101
|
+
|
|
102
|
+
registerSlot: this.registerSlot,
|
|
103
|
+
registerMiddleware: this.registerMiddleware,
|
|
104
|
+
registerEntry: this.registryEntry,
|
|
60
105
|
},
|
|
61
106
|
};
|
|
62
107
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { cfApiFetcher } from '
|
|
1
|
+
import { cfApiFetcher } from '#/app/utils';
|
|
2
2
|
import { FALLBACK_MANIFEST } from '#/plugins/manifest';
|
|
3
3
|
import { IPluginManifestEntry, PluginID } from '#/types/plugin';
|
|
4
4
|
|
|
@@ -11,6 +11,7 @@ const KnownPluginIDSet = new Set<string>([
|
|
|
11
11
|
'now',
|
|
12
12
|
'default',
|
|
13
13
|
'fx',
|
|
14
|
+
'blog',
|
|
14
15
|
]);
|
|
15
16
|
|
|
16
17
|
interface PluginsApiResponse {
|
package/src/index.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RenderApp } from './app';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const appContainer = document.getElementById('app') as Element;
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
RenderApp(appContainer);
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PLUGIN_NAME = 'blog';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
|
|
3
|
+
import { ISlotProps } from '#/core/components/SlotComp';
|
|
4
|
+
import { createPluginCtx } from '#/core/context';
|
|
5
|
+
|
|
6
|
+
export interface IBlogContext {
|
|
7
|
+
Slot: React.FC<ISlotProps>;
|
|
8
|
+
setLoading: (loading: boolean) => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const BlogContext = createPluginCtx<IBlogContext>();
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef } from 'react';
|
|
2
|
-
import { useLocation } from 'react-router-dom';
|
|
3
2
|
|
|
4
3
|
const SCROLL_STORAGE_KEY = 'div-scroll-positions';
|
|
5
4
|
|
|
@@ -42,7 +41,6 @@ export function useBlogScroll() {
|
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
export function useBlogScrollRestoration(debounceMs: number = 100) {
|
|
45
|
-
const location = useLocation();
|
|
46
44
|
const isFirstRender = useRef(true);
|
|
47
45
|
const scrollTimeoutRef = useRef<number>();
|
|
48
46
|
const element = document.getElementById('blog');
|
|
@@ -62,7 +60,7 @@ export function useBlogScrollRestoration(debounceMs: number = 100) {
|
|
|
62
60
|
element.scrollTop = savedPosition;
|
|
63
61
|
});
|
|
64
62
|
isFirstRender.current = false;
|
|
65
|
-
}, []);
|
|
63
|
+
}, [element, scrollKey]);
|
|
66
64
|
|
|
67
65
|
// Save scroll position with debouncing
|
|
68
66
|
useEffect(() => {
|
|
@@ -87,5 +85,5 @@ export function useBlogScrollRestoration(debounceMs: number = 100) {
|
|
|
87
85
|
return () => {
|
|
88
86
|
element.removeEventListener('scroll', handleScroll);
|
|
89
87
|
};
|
|
90
|
-
}, []);
|
|
88
|
+
}, [debounceMs, element, scrollKey]);
|
|
91
89
|
}
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import useSWR from 'swr';
|
|
2
2
|
import { useEffect, useMemo, useState } from 'react';
|
|
3
3
|
|
|
4
|
-
import { useGlobalLoading } from '@/hooks';
|
|
5
|
-
import { baseFetcher } from '@/utils';
|
|
6
|
-
import { API_ENDPOINT } from '@/constants/routes';
|
|
7
4
|
import { IPost } from '#/types/posts';
|
|
8
5
|
import { useMiddlewareRunner } from '#/core/hooks/useMiddlewareTransData';
|
|
9
6
|
|
|
10
|
-
|
|
11
|
-
const POSTS_API = !isProd ? '/api/posts' : `${API_ENDPOINT}/posts`;
|
|
7
|
+
import { BlogContext } from '../context';
|
|
12
8
|
|
|
13
9
|
interface PostsApiResponse {
|
|
14
10
|
data: IPost[];
|
|
@@ -23,11 +19,13 @@ export interface TitleListItem {
|
|
|
23
19
|
}
|
|
24
20
|
|
|
25
21
|
export const usePosts = (name: string = '', suspense?: boolean) => {
|
|
26
|
-
const { data: response, error: swrError } = useSWR<PostsApiResponse>(
|
|
22
|
+
const { data: response, error: swrError } = useSWR<PostsApiResponse>('posts', {
|
|
27
23
|
revalidateOnFocus: false,
|
|
28
24
|
suspense,
|
|
29
25
|
});
|
|
30
26
|
|
|
27
|
+
const { setLoading } = BlogContext.useCtx();
|
|
28
|
+
|
|
31
29
|
const data = response?.data;
|
|
32
30
|
const isDataLoading = !data && !swrError;
|
|
33
31
|
|
|
@@ -43,7 +41,6 @@ export const usePosts = (name: string = '', suspense?: boolean) => {
|
|
|
43
41
|
|
|
44
42
|
const [fullTitleList, setFullTitleList] = useState<TitleListItem[]>(baseTitleList);
|
|
45
43
|
|
|
46
|
-
// Use middleware hook to transform title list
|
|
47
44
|
const {
|
|
48
45
|
loading: isTransforming,
|
|
49
46
|
error: transformError,
|
|
@@ -58,7 +55,9 @@ export const usePosts = (name: string = '', suspense?: boolean) => {
|
|
|
58
55
|
}
|
|
59
56
|
}, [baseTitleList, run]);
|
|
60
57
|
|
|
61
|
-
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
setLoading(isDataLoading || isTransforming);
|
|
60
|
+
}, [isDataLoading, isTransforming, setLoading]);
|
|
62
61
|
|
|
63
62
|
const posts =
|
|
64
63
|
isDataLoading || name === '' || swrError || !data
|
|
@@ -69,6 +68,6 @@ export const usePosts = (name: string = '', suspense?: boolean) => {
|
|
|
69
68
|
posts,
|
|
70
69
|
titleList: fullTitleList ?? [],
|
|
71
70
|
isError: swrError || transformError,
|
|
72
|
-
isLoading:
|
|
71
|
+
isLoading: isDataLoading || isTransforming,
|
|
73
72
|
};
|
|
74
73
|
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { BBPlugin } from '#/core/bbplugin';
|
|
2
|
+
import { Slot } from '#/core/components/SlotComp';
|
|
3
|
+
import { IHostContext } from '#/types/hostApi';
|
|
4
|
+
import { PluginID } from '#/types/plugin';
|
|
5
|
+
|
|
6
|
+
import { BlogPageApp } from './components/app';
|
|
7
|
+
import { BlogContext } from './context';
|
|
8
|
+
|
|
9
|
+
const { withCtx } = BlogContext;
|
|
10
|
+
|
|
11
|
+
export class BlogPlugin extends BBPlugin {
|
|
12
|
+
id: PluginID = 'blog';
|
|
13
|
+
|
|
14
|
+
override onInstall = async (ctx: IHostContext) => {
|
|
15
|
+
ctx.api.registerEntry(
|
|
16
|
+
{
|
|
17
|
+
path: '/blog',
|
|
18
|
+
label: 'cd ./blog',
|
|
19
|
+
pageComponent: withCtx(
|
|
20
|
+
{
|
|
21
|
+
setLoading: loading => {
|
|
22
|
+
ctx.api.setLoading('blog', loading);
|
|
23
|
+
},
|
|
24
|
+
Slot: Slot,
|
|
25
|
+
},
|
|
26
|
+
BlogPageApp
|
|
27
|
+
),
|
|
28
|
+
},
|
|
29
|
+
this.id
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default new BlogPlugin();
|
|
@@ -2,9 +2,9 @@ import React from 'react';
|
|
|
2
2
|
import { NotFound } from '@bbki.ng/ui';
|
|
3
3
|
import { useParams } from 'react-router-dom';
|
|
4
4
|
|
|
5
|
-
import { usePosts } from '
|
|
6
|
-
import { ArticlePage } from '
|
|
7
|
-
import { useBlogScrollReset } from '
|
|
5
|
+
import { usePosts } from '#/plugins/blog/hooks/use_posts';
|
|
6
|
+
import { ArticlePage } from '#/plugins/blog/components/article';
|
|
7
|
+
import { useBlogScrollReset } from '#/plugins/blog/hooks/use_blog_scroll_pos_restoration';
|
|
8
8
|
import { useMiddlewareTransformedData } from '#/core/hooks/useMiddlewareTransData';
|
|
9
9
|
import { IPost } from '#/types/posts';
|
|
10
10
|
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { LinkProps, Button } from '@bbki.ng/ui';
|
|
3
3
|
|
|
4
|
-
import { usePosts } from '
|
|
5
|
-
import { CenterLinkList } from '
|
|
6
|
-
import {
|
|
4
|
+
import { usePosts } from '#/plugins/blog/hooks/use_posts';
|
|
5
|
+
import { CenterLinkList } from '#/plugins/blog/components';
|
|
6
|
+
import {
|
|
7
|
+
useBlogScroll,
|
|
8
|
+
useBlogScrollRestoration,
|
|
9
|
+
} from '#/plugins/blog/hooks/use_blog_scroll_pos_restoration';
|
|
7
10
|
|
|
8
11
|
type TxtProps = {
|
|
9
12
|
title?: string;
|
package/src/plugins/manifest.ts
CHANGED
|
@@ -106,8 +106,8 @@ export const StorePage = (_: IComPropsRegisteredToSlot) => {
|
|
|
106
106
|
const headerRenderer = useCallback(() => {
|
|
107
107
|
return (
|
|
108
108
|
<>
|
|
109
|
-
<Table.HCell
|
|
110
|
-
<Table.HCell
|
|
109
|
+
<Table.HCell>功能</Table.HCell>
|
|
110
|
+
<Table.HCell>描述</Table.HCell>
|
|
111
111
|
<Table.HCell style={{ textAlign: 'right' }}>安装/卸载</Table.HCell>
|
|
112
112
|
</>
|
|
113
113
|
);
|
package/src/types/hostApi.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Fetcher } from 'swr';
|
|
3
3
|
|
|
4
|
-
import { type FingerprintData } from '
|
|
4
|
+
import { type FingerprintData } from '#/app/utils/fingerprints';
|
|
5
5
|
import { PluginStore } from '#/core/pluginStore';
|
|
6
6
|
|
|
7
7
|
import { HookPoint, IComPropsRegisteredToSlot, SlotName } from './slots';
|
|
8
|
-
import { PluginEvents, PluginID } from './plugin';
|
|
8
|
+
import { IPluginEntry, PluginEvents, PluginID } from './plugin';
|
|
9
9
|
|
|
10
10
|
export interface IHostApi {
|
|
11
11
|
getDeviceId: () => Promise<{ id: string; fp: FingerprintData }>;
|
|
@@ -20,6 +20,7 @@ export interface IHostApi {
|
|
|
20
20
|
pluginId: string,
|
|
21
21
|
weight?: number
|
|
22
22
|
) => void;
|
|
23
|
+
registerEntry: (entry: IPluginEntry, id: string) => void;
|
|
23
24
|
registerSlot: (
|
|
24
25
|
slotName: SlotName,
|
|
25
26
|
component: React.ComponentType<IComPropsRegisteredToSlot>,
|
package/src/types/plugin.ts
CHANGED
package/src/utils/index.tsx
CHANGED
|
@@ -5,6 +5,21 @@ import { IHostContext } from '#/types/hostApi';
|
|
|
5
5
|
import { IPluginEntry } from '#/types/plugin';
|
|
6
6
|
import { IComPropsRegisteredToSlot } from '#/types/slots';
|
|
7
7
|
|
|
8
|
+
export const buildEntrySlotCom = (entry: IPluginEntry) => {
|
|
9
|
+
const Component = (props: IComPropsRegisteredToSlot) => {
|
|
10
|
+
const route = props.data as Omit<PathRouteProps, 'element'>;
|
|
11
|
+
if (route.path === entry.path) {
|
|
12
|
+
const Com = entry.pageComponent;
|
|
13
|
+
return <Com data={props.data} />;
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
Component.displayName = `EntrySlotCom(${entry.path})`;
|
|
19
|
+
|
|
20
|
+
return Component;
|
|
21
|
+
};
|
|
22
|
+
|
|
8
23
|
export const buildEntryCreator = (ctx: IHostContext) => (entry: IPluginEntry, id: string) => {
|
|
9
24
|
ctx.api.registerMiddleware(
|
|
10
25
|
'extendedRoutes',
|
package/tsconfig.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { ROUTES, ROUTE_NAME, GITHUB_REPO_ADDRESS } from './routes';
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export const ROUTES = {
|
|
2
|
-
INDEX: '/',
|
|
3
|
-
CONTENT: '/blog',
|
|
4
|
-
HELP: '/blog/说明书',
|
|
5
|
-
TAGS: '/tags',
|
|
6
|
-
LOGIN: '/login',
|
|
7
|
-
BLOG: '/blog',
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export const ROUTE_NAME = {
|
|
11
|
-
[ROUTES.CONTENT]: '目录',
|
|
12
|
-
[ROUTES.BLOG]: '文章',
|
|
13
|
-
unknown: '未知',
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export const GITHUB_REPO_ADDRESS = 'https://github.com/bbbottle/bottle/tree/main/apps/site';
|
|
17
|
-
export const API_ENDPOINT = 'https://cf.bbki.ng';
|
|
18
|
-
export const API = {
|
|
19
|
-
POSTS: 'posts',
|
|
20
|
-
};
|
package/src/blog/hooks/index.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { GlobalLoadingContext } from '@/context/global_loading_state_provider';
|
|
2
|
-
import { IBlogContext } from '@/types/blog-context';
|
|
3
|
-
import { useContext } from 'react';
|
|
4
|
-
import { useLocation } from 'react-router-dom';
|
|
5
|
-
|
|
6
|
-
export const useBlogContext = (): IBlogContext => {
|
|
7
|
-
const globalLoading = useContext(GlobalLoadingContext);
|
|
8
|
-
const location = useLocation();
|
|
9
|
-
|
|
10
|
-
return {
|
|
11
|
-
gloalLoading: globalLoading.isLoading,
|
|
12
|
-
location,
|
|
13
|
-
};
|
|
14
|
-
};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'react';
|
|
2
|
-
|
|
3
|
-
export const useMousePosition = () => {
|
|
4
|
-
const posRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
|
|
5
|
-
useEffect(() => {
|
|
6
|
-
const updateMousePosition = (e: MouseEvent) => {
|
|
7
|
-
posRef.current = {
|
|
8
|
-
x: e.clientX,
|
|
9
|
-
y: e.clientY,
|
|
10
|
-
};
|
|
11
|
-
};
|
|
12
|
-
window.addEventListener('mousemove', updateMousePosition);
|
|
13
|
-
return () => window.removeEventListener('mousemove', updateMousePosition);
|
|
14
|
-
}, []);
|
|
15
|
-
|
|
16
|
-
return posRef;
|
|
17
|
-
};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export enum Role {
|
|
2
|
-
KING = 'king',
|
|
3
|
-
QUEEN = 'queen',
|
|
4
|
-
ANNO = 'anno',
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Returns user role
|
|
9
|
-
* Currently always returns ANNO as role-based authentication is now handled via API keys in CLI
|
|
10
|
-
* Frontend role management may be re-implemented in the future
|
|
11
|
-
*/
|
|
12
|
-
export const useRole = (): Role => {
|
|
13
|
-
return Role.ANNO;
|
|
14
|
-
};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { pluginManager } from '#/core/pluginManager';
|
|
2
|
-
import { useEffect } from 'react';
|
|
3
|
-
export const useSticker = () => {
|
|
4
|
-
useEffect(() => {
|
|
5
|
-
pluginManager.loadPlugin('sticker');
|
|
6
|
-
|
|
7
|
-
return () => {
|
|
8
|
-
pluginManager.disablePlugin('sticker');
|
|
9
|
-
};
|
|
10
|
-
}, []);
|
|
11
|
-
};
|
package/src/blog/pages/index.tsx
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { ArticlePage } from '@/components/article';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Login page
|
|
6
|
-
* Note: OAuth authentication has been removed. Authentication is now handled
|
|
7
|
-
* via API keys in the CLI tool. Frontend authentication may be re-implemented
|
|
8
|
-
* in the future.
|
|
9
|
-
*/
|
|
10
|
-
export const Login = () => {
|
|
11
|
-
return (
|
|
12
|
-
<ArticlePage title="登录">
|
|
13
|
-
<div className="prose dark:prose-invert">
|
|
14
|
-
<p className="text-gray-600 dark:text-gray-400">网页登录功能已暂时禁用。</p>
|
|
15
|
-
<p className="text-gray-600 dark:text-gray-400">
|
|
16
|
-
如需管理内容,请使用 CLI 工具并通过 API Key 进行认证:
|
|
17
|
-
</p>
|
|
18
|
-
<pre className="bg-gray-100 dark:bg-gray-800 p-4 rounded">
|
|
19
|
-
<code>bbking login</code>
|
|
20
|
-
</pre>
|
|
21
|
-
</div>
|
|
22
|
-
</ArticlePage>
|
|
23
|
-
);
|
|
24
|
-
};
|
package/src/blog/types/font.ts
DELETED
package/src/blog/types/glsl.d.ts
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|