@bbki.ng/site 2.0.33 → 2.0.35
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 +4 -0
- package/index.html +1 -0
- package/package.json +2 -2
- package/src/blog/app.tsx +19 -23
- package/src/blog/components/article/index.tsx +8 -1
- package/src/blog/components/oh_reaction.tsx +114 -0
- package/src/blog/components/plugin/PluginContentPage.tsx +1 -1
- package/src/blog/components/plugin/PluginRoutes.tsx +1 -1
- package/src/blog/hooks/use_safe_loading.ts +1 -1
- package/src/blog/pages/cover/index.tsx +1 -1
- package/src/blog/pages/upload/index.tsx +1 -1
- package/src/index.tsx +1 -0
package/CHANGELOG.md
CHANGED
package/index.html
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bbki.ng/site",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.35",
|
|
4
4
|
"description": "code behind bbki.ng",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@extism/extism": "2.0.0-rc11",
|
|
20
|
-
"@bbki.ng/components": "workspace:2.6.
|
|
20
|
+
"@bbki.ng/components": "workspace:2.6.9",
|
|
21
21
|
"@supabase/supabase-js": "^1.35.4",
|
|
22
22
|
"classnames": "2.3.1",
|
|
23
23
|
"react": "^18.0.0",
|
package/src/blog/app.tsx
CHANGED
|
@@ -26,6 +26,7 @@ import { PluginContentPage } from "@/components/plugin/PluginContentPage";
|
|
|
26
26
|
import { PluginRoutes } from "@/components/plugin/PluginRoutes";
|
|
27
27
|
import { useClipboardToPost } from "@/hooks/use_clipboard_to_post";
|
|
28
28
|
import { useSharedStringToPost } from "@/hooks/use_shared_string_to_post";
|
|
29
|
+
import { ThreeColLayout, ErrorBoundary } from "@bbki.ng/components";
|
|
29
30
|
|
|
30
31
|
const Layout = () => {
|
|
31
32
|
const { isLoading, isFontLoading } = useContext(GlobalLoadingContext);
|
|
@@ -45,19 +46,21 @@ const Layout = () => {
|
|
|
45
46
|
/>
|
|
46
47
|
</AppCtxMenu>
|
|
47
48
|
}
|
|
48
|
-
main={
|
|
49
|
+
main={
|
|
50
|
+
<ThreeColLayout
|
|
51
|
+
middleRenderer={() => {
|
|
52
|
+
return (
|
|
53
|
+
<ErrorBoundary>
|
|
54
|
+
<Outlet />
|
|
55
|
+
</ErrorBoundary>
|
|
56
|
+
);
|
|
57
|
+
}}
|
|
58
|
+
/>
|
|
59
|
+
}
|
|
49
60
|
/>
|
|
50
61
|
);
|
|
51
62
|
};
|
|
52
63
|
|
|
53
|
-
const NowInMidCol = threeColWrapper(NowPage);
|
|
54
|
-
const ContentInMidCol = threeColWrapper(Txt);
|
|
55
|
-
const ArticleInMidCol = threeColWrapper(ArticlePage);
|
|
56
|
-
const TagsInMidCol = threeColWrapper(Tags);
|
|
57
|
-
const LoginInMidCol = threeColWrapper(Login);
|
|
58
|
-
const TagsResultInMidCol = threeColWrapper(TagsResult);
|
|
59
|
-
const CoverInMidCol = threeColWrapper(Cover);
|
|
60
|
-
|
|
61
64
|
export const App = () => {
|
|
62
65
|
useClipboardToPost();
|
|
63
66
|
|
|
@@ -69,25 +72,18 @@ export const App = () => {
|
|
|
69
72
|
<BBContext>
|
|
70
73
|
<Routes>
|
|
71
74
|
<Route path="/" element={<Layout />}>
|
|
72
|
-
<Route index element={<
|
|
75
|
+
<Route index element={<Cover />} />
|
|
73
76
|
|
|
74
77
|
<Route path="blog" element={<Outlet />}>
|
|
75
|
-
<Route path="" element={<
|
|
76
|
-
<Route path=":title" element={<
|
|
78
|
+
<Route path="" element={<Txt />} />
|
|
79
|
+
<Route path=":title" element={<ArticlePage />} />
|
|
77
80
|
</Route>
|
|
78
|
-
|
|
79
|
-
<Route path="tags" element={<
|
|
80
|
-
<Route path="tags/:tag" element={<
|
|
81
|
+
|
|
82
|
+
<Route path="tags" element={<Tags />} />
|
|
83
|
+
<Route path="tags/:tag" element={<TagsResult />} />
|
|
81
84
|
|
|
82
85
|
<Route path="bot" element={<BotRedirect />} />
|
|
83
|
-
<Route path="
|
|
84
|
-
<Route path="login" element={<LoginInMidCol />} />
|
|
85
|
-
<Route path="upload" element={<UploadPage />} />
|
|
86
|
-
<Route path="/plugins" element={<PluginRoutes />} />
|
|
87
|
-
<Route
|
|
88
|
-
path="/plugins/:pluginRoute"
|
|
89
|
-
element={<PluginContentPage />}
|
|
90
|
-
/>
|
|
86
|
+
<Route path="login" element={<Login />} />
|
|
91
87
|
</Route>
|
|
92
88
|
<Route path="*" element={<NotFound />} />
|
|
93
89
|
</Routes>
|
|
@@ -4,6 +4,7 @@ import { ROUTES } from "@/constants";
|
|
|
4
4
|
import classNames from "classnames";
|
|
5
5
|
import { GlobalLoadingContext } from "@/context/global_loading_state_provider";
|
|
6
6
|
import { useSafeArticleLoading } from "@/hooks/use_safe_loading";
|
|
7
|
+
import { OpenHeartReaction } from "../oh_reaction";
|
|
7
8
|
|
|
8
9
|
export type ArticlePageProps = {
|
|
9
10
|
tags?: string[];
|
|
@@ -17,10 +18,13 @@ export type ArticlePageProps = {
|
|
|
17
18
|
export const ArticlePage = (props: ArticlePageProps) => {
|
|
18
19
|
const { tags: tagNames, title, description, headless } = props;
|
|
19
20
|
const loading = useSafeArticleLoading(0.2, 5);
|
|
21
|
+
const defaultTag = { children: "目录", to: "/blog" };
|
|
20
22
|
const tags = tagNames
|
|
21
23
|
? tagNames.map((t) => ({ children: t, to: `${ROUTES.TAGS}/${t}` }))
|
|
22
24
|
: [];
|
|
23
25
|
|
|
26
|
+
const allTags = [defaultTag, ...tags];
|
|
27
|
+
|
|
24
28
|
if (headless) {
|
|
25
29
|
return props.children;
|
|
26
30
|
}
|
|
@@ -39,7 +43,10 @@ export const ArticlePage = (props: ArticlePageProps) => {
|
|
|
39
43
|
>
|
|
40
44
|
<article className={articleCls}>{props.children}</article>
|
|
41
45
|
</Article>
|
|
42
|
-
<
|
|
46
|
+
<div className="p-16">
|
|
47
|
+
<span>{<Tags tags={allTags} />}</span>
|
|
48
|
+
<OpenHeartReaction title={title} />
|
|
49
|
+
</div>
|
|
43
50
|
</>
|
|
44
51
|
);
|
|
45
52
|
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
ReactNode,
|
|
3
|
+
useContext,
|
|
4
|
+
useEffect,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from "react";
|
|
8
|
+
import { BlinkDot } from "@bbki.ng/components";
|
|
9
|
+
import { GlobalLoadingContext } from "@/context/global_loading_state_provider";
|
|
10
|
+
|
|
11
|
+
declare global {
|
|
12
|
+
namespace JSX {
|
|
13
|
+
interface IntrinsicElements {
|
|
14
|
+
"open-heart": React.DetailedHTMLProps<
|
|
15
|
+
React.HTMLAttributes<HTMLElement> & {
|
|
16
|
+
href?: string;
|
|
17
|
+
emoji?: string;
|
|
18
|
+
ariaDisabled?: boolean;
|
|
19
|
+
onClick?: React.MouseEventHandler<HTMLElement>;
|
|
20
|
+
},
|
|
21
|
+
HTMLElement
|
|
22
|
+
>;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const HEART_SIZE = 12;
|
|
28
|
+
|
|
29
|
+
const heart = (
|
|
30
|
+
<svg
|
|
31
|
+
data-testid="geist-icon"
|
|
32
|
+
height={HEART_SIZE}
|
|
33
|
+
width={HEART_SIZE}
|
|
34
|
+
stroke-linejoin="round"
|
|
35
|
+
// style={{ color: "#fecaca" }}
|
|
36
|
+
className="text-red-200 hover:text-red-400 transition-colors"
|
|
37
|
+
viewBox="0 0 16 16"
|
|
38
|
+
>
|
|
39
|
+
<path
|
|
40
|
+
fill-rule="evenodd"
|
|
41
|
+
clip-rule="evenodd"
|
|
42
|
+
d="M7.06463 3.20474C5.79164 1.93175 3.72772 1.93175 2.45474 3.20474C1.18175 4.47773 1.18175 6.54166 2.45474 7.81465L8 13.3599L13.5453 7.81465C14.8182 6.54166 14.8182 4.47773 13.5453 3.20474C12.2723 1.93175 10.2084 1.93175 8.93537 3.20474L8.53033 3.60979L8 4.14012L7.46967 3.60979L7.06463 3.20474ZM8 2.02321C6.13348 0.286219 3.21165 0.326509 1.39408 2.14408C-0.464694 4.00286 -0.464691 7.01653 1.39408 8.87531L7.46967 14.9509L8 15.4812L8.53033 14.9509L14.6059 8.87531C16.4647 7.01653 16.4647 4.00286 14.6059 2.14408C12.7884 0.326509 9.86653 0.286221 8 2.02321Z"
|
|
43
|
+
fill="currentColor"
|
|
44
|
+
></path>
|
|
45
|
+
</svg>
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const heartFill = (
|
|
49
|
+
<svg
|
|
50
|
+
data-testid="geist-icon"
|
|
51
|
+
height={HEART_SIZE}
|
|
52
|
+
width={HEART_SIZE}
|
|
53
|
+
stroke-linejoin="round"
|
|
54
|
+
className="text-red-400"
|
|
55
|
+
viewBox={`0 0 16 16`}
|
|
56
|
+
>
|
|
57
|
+
<path
|
|
58
|
+
d="M1.39408 2.14408C3.21165 0.326509 6.13348 0.286219 8 2.02321C9.86652 0.286221 12.7884 0.326509 14.6059 2.14408C16.4647 4.00286 16.4647 7.01653 14.6059 8.87531L8 15.4812L1.39408 8.87531C-0.464691 7.01653 -0.464694 4.00286 1.39408 2.14408Z"
|
|
59
|
+
fill="currentColor"
|
|
60
|
+
></path>
|
|
61
|
+
</svg>
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const useHeartSent = () => {
|
|
65
|
+
const [sent, setSent] = useState(false);
|
|
66
|
+
const { setIsLoading } = useContext(GlobalLoadingContext);
|
|
67
|
+
|
|
68
|
+
const handleSent = () => {
|
|
69
|
+
console.log("heart sent");
|
|
70
|
+
setSent(true);
|
|
71
|
+
setIsLoading(false);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
addEventListener("open-heart", handleSent);
|
|
76
|
+
|
|
77
|
+
return () => {
|
|
78
|
+
removeEventListener("open-heart", handleSent);
|
|
79
|
+
};
|
|
80
|
+
}, []);
|
|
81
|
+
|
|
82
|
+
return sent;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const OpenHeartReaction = (props: { title: string }) => {
|
|
86
|
+
const { title } = props;
|
|
87
|
+
const sent = useHeartSent();
|
|
88
|
+
|
|
89
|
+
const { setIsLoading } = useContext(GlobalLoadingContext);
|
|
90
|
+
|
|
91
|
+
const ohRef = useRef<HTMLElement>(null);
|
|
92
|
+
|
|
93
|
+
const pressed = () => ohRef.current?.getAttribute("aria-pressed");
|
|
94
|
+
|
|
95
|
+
const handleHeartClick = () => {
|
|
96
|
+
if (sent || pressed()) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
setIsLoading(true);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<open-heart
|
|
105
|
+
style={{ display: "flex", padding: 4, marginTop: "1rem" }}
|
|
106
|
+
ref={ohRef}
|
|
107
|
+
href={`https://oh.bbking.workers.dev/?id=${title}`}
|
|
108
|
+
emoji="❤️"
|
|
109
|
+
onClick={handleHeartClick}
|
|
110
|
+
>
|
|
111
|
+
{sent || pressed() ? heartFill : heart}
|
|
112
|
+
</open-heart>
|
|
113
|
+
);
|
|
114
|
+
};
|
|
@@ -3,7 +3,7 @@ import { CenterLinkList } from "@/components";
|
|
|
3
3
|
import { EffectLayer } from "@/components/effect-layer/EffectLayer";
|
|
4
4
|
import { GlobalRoutesContext } from "@/context/global_routes_provider";
|
|
5
5
|
|
|
6
|
-
export const Cover = (props: { className
|
|
6
|
+
export const Cover = (props: { className?: string }) => {
|
|
7
7
|
const globalRouteCtx = useContext(GlobalRoutesContext);
|
|
8
8
|
const routes = globalRouteCtx.globalRoutes;
|
|
9
9
|
const pluginEntry =
|