@bbki.ng/site 5.6.1 → 5.6.3
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 +12 -0
- package/package.json +1 -1
- package/src/blog/components/BaseLayout.tsx +37 -34
- package/src/blog/context/bbcontext.tsx +1 -4
- package/src/core/hooks/use_plugins.ts +5 -1
- package/src/core/pluginManager.ts +12 -0
- package/src/plugins/fx/components/index.tsx +39 -0
- package/src/plugins/fx/context/index.ts +12 -0
- package/src/plugins/fx/index.ts +29 -0
- package/src/plugins/manifest.ts +7 -1
- package/src/plugins/store/components/storePage.tsx +19 -9
- package/src/types/hostApi.ts +5 -1
- package/src/types/plugin.ts +10 -1
- package/src/types/slots.ts +1 -0
- package/src/blog/components/effect-layer/EffectContextProvider.tsx +0 -27
- package/src/blog/hooks/use_fingerprint.ts +0 -55
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -19,39 +19,42 @@ export const BaseLayout = () => {
|
|
|
19
19
|
);
|
|
20
20
|
|
|
21
21
|
return (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
<
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
22
|
+
<>
|
|
23
|
+
<Page
|
|
24
|
+
nav={
|
|
25
|
+
<Nav
|
|
26
|
+
paths={transformedPaths}
|
|
27
|
+
className="gradient-blur-cover select-none"
|
|
28
|
+
loading={isLoading}
|
|
29
|
+
customLogo={<Slot name="logo" data={defaultLogo} placeholder={defaultLogo} />}
|
|
30
|
+
style={{
|
|
31
|
+
paddingTop: 'calc(var(--safe-top) + 4px)',
|
|
32
|
+
transition: 'all .2s ease-in-out',
|
|
33
|
+
}}
|
|
34
|
+
/>
|
|
35
|
+
}
|
|
36
|
+
main={
|
|
37
|
+
<Grid
|
|
38
|
+
leftAside={
|
|
39
|
+
<div className="py-32 px-6">
|
|
40
|
+
<Slot name="leftCol" data={paths} />
|
|
41
|
+
</div>
|
|
42
|
+
}
|
|
43
|
+
rightAside={
|
|
44
|
+
<div className="py-32 px-6">
|
|
45
|
+
<Slot name="rightCol" data={paths} />
|
|
46
|
+
</div>
|
|
47
|
+
}
|
|
48
|
+
>
|
|
49
|
+
<Container className="py-48">
|
|
50
|
+
<ErrorBoundary>
|
|
51
|
+
<Outlet />
|
|
52
|
+
</ErrorBoundary>
|
|
53
|
+
</Container>
|
|
54
|
+
</Grid>
|
|
55
|
+
}
|
|
56
|
+
/>
|
|
57
|
+
<Slot name="pageFooter" />
|
|
58
|
+
</>
|
|
56
59
|
);
|
|
57
60
|
};
|
|
@@ -2,14 +2,11 @@ import React, { ReactNode } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { GlobalLoadingStateProvider } from '@/context/global_loading_state_provider';
|
|
4
4
|
import { GlobalRoutesProvider } from '@/context/global_routes_provider';
|
|
5
|
-
import { EffectContextProvider } from '@/components/effect-layer/EffectContextProvider';
|
|
6
5
|
|
|
7
6
|
export const BBContext = (props: { children: ReactNode }) => {
|
|
8
7
|
return (
|
|
9
8
|
<GlobalLoadingStateProvider>
|
|
10
|
-
<GlobalRoutesProvider>
|
|
11
|
-
<EffectContextProvider>{props.children}</EffectContextProvider>
|
|
12
|
-
</GlobalRoutesProvider>
|
|
9
|
+
<GlobalRoutesProvider>{props.children}</GlobalRoutesProvider>
|
|
13
10
|
</GlobalLoadingStateProvider>
|
|
14
11
|
);
|
|
15
12
|
};
|
|
@@ -7,7 +7,11 @@ import { PluginID } from '#/types/plugin';
|
|
|
7
7
|
import { PluginStore } from '../pluginStore';
|
|
8
8
|
|
|
9
9
|
const usePluginsLoading = () => {
|
|
10
|
-
const { setGlobalLoading } = useGlobalLoading();
|
|
10
|
+
const { setGlobalLoading, isLoading } = useGlobalLoading();
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
pluginManager.notifySiteLoadingChanged(isLoading);
|
|
14
|
+
}, [isLoading]);
|
|
11
15
|
|
|
12
16
|
useEffect(() => {
|
|
13
17
|
let unregister: (() => void) | undefined;
|
|
@@ -30,6 +30,9 @@ class PluginManager {
|
|
|
30
30
|
setLoading: (id: PluginID, loading: boolean) => {
|
|
31
31
|
this.bus.emit('plugin:loading:changed', { id, loading });
|
|
32
32
|
},
|
|
33
|
+
onLoadingChanged: (listener: (payload: PluginEvents['site:loading:changed']) => void) => {
|
|
34
|
+
return this.bus.on('site:loading:changed', listener);
|
|
35
|
+
},
|
|
33
36
|
registerMiddleware: <T>(
|
|
34
37
|
point: HookPoint,
|
|
35
38
|
fn: (data: T) => Promise<T> | T,
|
|
@@ -39,6 +42,11 @@ class PluginManager {
|
|
|
39
42
|
registry.registerMiddleware(point, fn, pluginId, weight);
|
|
40
43
|
},
|
|
41
44
|
getDeviceId: getStableDeviceId,
|
|
45
|
+
getVersionHash: () => {
|
|
46
|
+
const hashStr: string =
|
|
47
|
+
typeof GLOBAL_COMMIT_HASH === 'string' ? GLOBAL_COMMIT_HASH : '0000000';
|
|
48
|
+
return hashStr;
|
|
49
|
+
},
|
|
42
50
|
registerSlot: (
|
|
43
51
|
slotName: SlotName,
|
|
44
52
|
component: React.ComponentType<IComPropsRegisteredToSlot>,
|
|
@@ -53,6 +61,10 @@ class PluginManager {
|
|
|
53
61
|
|
|
54
62
|
private loading = new Set<string>();
|
|
55
63
|
|
|
64
|
+
notifySiteLoadingChanged(loading: boolean) {
|
|
65
|
+
this.bus.emit('site:loading:changed', loading);
|
|
66
|
+
}
|
|
67
|
+
|
|
56
68
|
subscribePluginLoading(listener: (payload: PluginEvents['plugin:loading:changed']) => void) {
|
|
57
69
|
return this.bus.on('plugin:loading:changed', listener);
|
|
58
70
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { EffectLayer, grain, paper, spiral, watermark, Effect } from '@bbki.ng/ui';
|
|
3
|
+
|
|
4
|
+
import { IComPropsRegisteredToSlot } from '#/types/slots';
|
|
5
|
+
|
|
6
|
+
import { FxContext } from '../context';
|
|
7
|
+
|
|
8
|
+
const useLoadingState = () => {
|
|
9
|
+
const ctx = FxContext.useCtx();
|
|
10
|
+
const [isLoading, setIsLoading] = React.useState(false);
|
|
11
|
+
|
|
12
|
+
React.useEffect(() => {
|
|
13
|
+
const unsubscribe = ctx.subscribeToLoading(setIsLoading);
|
|
14
|
+
return () => {
|
|
15
|
+
unsubscribe();
|
|
16
|
+
};
|
|
17
|
+
}, [ctx]);
|
|
18
|
+
|
|
19
|
+
return isLoading;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const FxCom = (_: IComPropsRegisteredToSlot) => {
|
|
23
|
+
const ctx = FxContext.useCtx();
|
|
24
|
+
const hashStr = ctx.versionHash;
|
|
25
|
+
const deviceId = ctx.deviceId;
|
|
26
|
+
const isLoading = useLoadingState();
|
|
27
|
+
|
|
28
|
+
console.log('is loading', isLoading);
|
|
29
|
+
|
|
30
|
+
const effects: Effect[] = useMemo(() => {
|
|
31
|
+
const wmLines = [hashStr];
|
|
32
|
+
if (deviceId) wmLines.push(deviceId);
|
|
33
|
+
wmLines.push('hello world');
|
|
34
|
+
|
|
35
|
+
return [grain(), paper(), spiral({ active: isLoading }), watermark({ lines: wmLines })];
|
|
36
|
+
}, [isLoading, deviceId, hashStr]);
|
|
37
|
+
|
|
38
|
+
return <EffectLayer effects={effects} />;
|
|
39
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createPluginCtx } from '#/core/context';
|
|
2
|
+
import { PluginEvents } from '#/types/plugin';
|
|
3
|
+
|
|
4
|
+
export interface IFxContext {
|
|
5
|
+
versionHash: string;
|
|
6
|
+
deviceId: string;
|
|
7
|
+
subscribeToLoading: (
|
|
8
|
+
listener: (payload: PluginEvents['site:loading:changed']) => void
|
|
9
|
+
) => () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const FxContext = createPluginCtx<IFxContext>();
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { BBPlugin } from '#/core/bbplugin';
|
|
2
|
+
import { IHostContext } from '#/types/hostApi';
|
|
3
|
+
import { PluginID } from '#/types/plugin';
|
|
4
|
+
|
|
5
|
+
import { FxCom } from './components';
|
|
6
|
+
import { FxContext } from './context';
|
|
7
|
+
|
|
8
|
+
class FxPlugin extends BBPlugin {
|
|
9
|
+
id: PluginID = 'fx';
|
|
10
|
+
|
|
11
|
+
override onInstall = async (ctx: IHostContext) => {
|
|
12
|
+
ctx.api.registerSlot(
|
|
13
|
+
'pageFooter',
|
|
14
|
+
FxContext.withCtx(
|
|
15
|
+
{
|
|
16
|
+
versionHash: await ctx.api.getVersionHash(),
|
|
17
|
+
deviceId: (await ctx.api.getDeviceId()).id,
|
|
18
|
+
subscribeToLoading: listener => {
|
|
19
|
+
return ctx.api.onLoadingChanged(listener);
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
FxCom
|
|
23
|
+
),
|
|
24
|
+
this.id
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default new FxPlugin();
|
package/src/plugins/manifest.ts
CHANGED
|
@@ -25,7 +25,13 @@ export const PLUGIN_MANIFEST: Array<IPluginManifestEntry> = [
|
|
|
25
25
|
id: 'extra-cd',
|
|
26
26
|
version: '0.1.0',
|
|
27
27
|
description:
|
|
28
|
-
'
|
|
28
|
+
'提供额外的页面跳转链接。例如在标题列表末尾添加一个 "cd ~" 的选项,点击后跳转到主页',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: '特效',
|
|
32
|
+
id: 'fx',
|
|
33
|
+
version: '0.1.0',
|
|
34
|
+
description: '提供一些额外的视觉效果。例如版本、设备信息水印,加载状态螺旋线、背景纹理等',
|
|
29
35
|
},
|
|
30
36
|
// {
|
|
31
37
|
// name: 'extra-entry',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
2
|
-
import
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { Button, Table, Link } from '@bbki.ng/ui';
|
|
3
4
|
|
|
4
5
|
import { IComPropsRegisteredToSlot } from '#/types/slots';
|
|
5
6
|
import { IPluginStoreEntry, PluginID } from '#/types/plugin';
|
|
@@ -163,13 +164,22 @@ export const StorePage = (_: IComPropsRegisteredToSlot) => {
|
|
|
163
164
|
}
|
|
164
165
|
|
|
165
166
|
return (
|
|
166
|
-
|
|
167
|
-
<
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
167
|
+
<>
|
|
168
|
+
<div className="prose">
|
|
169
|
+
<Table
|
|
170
|
+
className="w-full"
|
|
171
|
+
rowCount={plugins.length}
|
|
172
|
+
headerRenderer={headerRenderer}
|
|
173
|
+
rowRenderer={rowRenderer}
|
|
174
|
+
/>
|
|
175
|
+
</div>
|
|
176
|
+
<Link
|
|
177
|
+
className={classNames('w-fit relative transition-all duration-300 mt-16')}
|
|
178
|
+
to="/"
|
|
179
|
+
style={{ left: -4 }}
|
|
180
|
+
>
|
|
181
|
+
cd ..
|
|
182
|
+
</Link>
|
|
183
|
+
</>
|
|
174
184
|
);
|
|
175
185
|
};
|
package/src/types/hostApi.ts
CHANGED
|
@@ -4,11 +4,15 @@ import { type FingerprintData } from '@/utils/fingerprints';
|
|
|
4
4
|
import { PluginStore } from '#/core/pluginStore';
|
|
5
5
|
|
|
6
6
|
import { HookPoint, IComPropsRegisteredToSlot, SlotName } from './slots';
|
|
7
|
-
import { PluginID } from './plugin';
|
|
7
|
+
import { PluginEvents, PluginID } from './plugin';
|
|
8
8
|
|
|
9
9
|
export interface IHostApi {
|
|
10
10
|
getDeviceId: () => Promise<{ id: string; fp: FingerprintData }>;
|
|
11
|
+
getVersionHash: () => Promise<string> | string;
|
|
11
12
|
setLoading: (id: PluginID, loading: boolean) => void;
|
|
13
|
+
onLoadingChanged: (
|
|
14
|
+
listener: (payload: PluginEvents['site:loading:changed']) => void
|
|
15
|
+
) => () => void;
|
|
12
16
|
registerMiddleware: <T>(
|
|
13
17
|
point: HookPoint,
|
|
14
18
|
fn: (baseData: T) => T,
|
package/src/types/plugin.ts
CHANGED
|
@@ -13,7 +13,15 @@ export interface IPlugin {
|
|
|
13
13
|
onDestroy?: () => void;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export type PluginID =
|
|
16
|
+
export type PluginID =
|
|
17
|
+
| 'sticker'
|
|
18
|
+
| 'xwy'
|
|
19
|
+
| 'extra-cd'
|
|
20
|
+
| 'extra-entry'
|
|
21
|
+
| 'store'
|
|
22
|
+
| 'now'
|
|
23
|
+
| 'default'
|
|
24
|
+
| 'fx';
|
|
17
25
|
|
|
18
26
|
export interface IPluginEntry {
|
|
19
27
|
path: string;
|
|
@@ -23,6 +31,7 @@ export interface IPluginEntry {
|
|
|
23
31
|
|
|
24
32
|
export type PluginEvents = {
|
|
25
33
|
'plugin:loading:changed': IPluginLoadingPayload;
|
|
34
|
+
'site:loading:changed': boolean;
|
|
26
35
|
};
|
|
27
36
|
|
|
28
37
|
export interface IPluginLoadingPayload {
|
package/src/types/slots.ts
CHANGED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import React, { ReactNode, useContext, useMemo } from 'react';
|
|
2
|
-
import { EffectLayer, grain, paper, spiral, watermark, Effect } from '@bbki.ng/ui';
|
|
3
|
-
|
|
4
|
-
import { GlobalLoadingContext } from '@/context/global_loading_state_provider';
|
|
5
|
-
import { useFingerprint } from '@/hooks/use_fingerprint';
|
|
6
|
-
|
|
7
|
-
const hashStr: string = typeof GLOBAL_COMMIT_HASH === 'string' ? GLOBAL_COMMIT_HASH : '0000000';
|
|
8
|
-
|
|
9
|
-
export const EffectContextProvider = (props: { children: ReactNode }) => {
|
|
10
|
-
const { isLoading } = useContext(GlobalLoadingContext);
|
|
11
|
-
const { deviceId } = useFingerprint();
|
|
12
|
-
|
|
13
|
-
const effects: Effect[] = useMemo(() => {
|
|
14
|
-
const wmLines = [hashStr];
|
|
15
|
-
if (deviceId) wmLines.push(deviceId);
|
|
16
|
-
wmLines.push('hello world');
|
|
17
|
-
|
|
18
|
-
return [grain(), paper(), spiral({ active: isLoading }), watermark({ lines: wmLines })];
|
|
19
|
-
}, [isLoading, deviceId]);
|
|
20
|
-
|
|
21
|
-
return (
|
|
22
|
-
<>
|
|
23
|
-
<EffectLayer effects={effects} />
|
|
24
|
-
{props.children}
|
|
25
|
-
</>
|
|
26
|
-
);
|
|
27
|
-
};
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState, useCallback } from 'react';
|
|
2
|
-
import { getStableDeviceId, FingerprintData } from '@/utils/fingerprints';
|
|
3
|
-
|
|
4
|
-
interface UseFingerprintReturn {
|
|
5
|
-
deviceId: string | null;
|
|
6
|
-
fingerprint: FingerprintData | null;
|
|
7
|
-
loading: boolean;
|
|
8
|
-
error: Error | null;
|
|
9
|
-
refresh: () => Promise<void>;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function useFingerprint(): UseFingerprintReturn {
|
|
13
|
-
const [state, setState] = useState<{
|
|
14
|
-
deviceId: string | null;
|
|
15
|
-
fingerprint: FingerprintData | null;
|
|
16
|
-
loading: boolean;
|
|
17
|
-
error: Error | null;
|
|
18
|
-
}>({
|
|
19
|
-
deviceId: null,
|
|
20
|
-
fingerprint: null,
|
|
21
|
-
loading: true,
|
|
22
|
-
error: null,
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
const refresh = useCallback(async () => {
|
|
26
|
-
setState(prev => ({ ...prev, loading: true, error: null }));
|
|
27
|
-
try {
|
|
28
|
-
const { id, fp } = await getStableDeviceId();
|
|
29
|
-
setState({
|
|
30
|
-
deviceId: id,
|
|
31
|
-
fingerprint: fp,
|
|
32
|
-
loading: false,
|
|
33
|
-
error: null,
|
|
34
|
-
});
|
|
35
|
-
} catch (err) {
|
|
36
|
-
setState(prev => ({
|
|
37
|
-
...prev,
|
|
38
|
-
loading: false,
|
|
39
|
-
error: err instanceof Error ? err : new Error('Failed to get fingerprint'),
|
|
40
|
-
}));
|
|
41
|
-
}
|
|
42
|
-
}, []);
|
|
43
|
-
|
|
44
|
-
useEffect(() => {
|
|
45
|
-
refresh();
|
|
46
|
-
}, [refresh]);
|
|
47
|
-
|
|
48
|
-
return {
|
|
49
|
-
deviceId: state.deviceId,
|
|
50
|
-
fingerprint: state.fingerprint,
|
|
51
|
-
loading: state.loading,
|
|
52
|
-
error: state.error,
|
|
53
|
-
refresh,
|
|
54
|
-
};
|
|
55
|
-
}
|