@bbki.ng/site 1.3.0 → 1.4.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @bbki.ng/site
2
2
 
3
+ ## 1.4.1
4
+
5
+ ## 1.4.0
6
+
3
7
  ## 1.3.0
4
8
 
5
9
  ## 1.2.23
package/dev-dist/sw.js CHANGED
@@ -98,7 +98,7 @@ define(["./workbox-baccbcc1"], function (workbox) {
98
98
  [
99
99
  {
100
100
  url: "index.html",
101
- revision: "0.bokbbik67o8",
101
+ revision: "0.a0iqm7b3nfg",
102
102
  },
103
103
  ],
104
104
  {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbki.ng/site",
3
- "version": "1.3.0",
3
+ "version": "1.4.1",
4
4
  "description": "code behind bbki.ng",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -16,8 +16,8 @@
16
16
  "url": "git+https://github.com/bbbottle/bbki.ng.git"
17
17
  },
18
18
  "dependencies": {
19
- "@bbki.ng/components": "workspace:2.3.0",
20
- "@supabase/supabase-js": "^1.30.6",
19
+ "@bbki.ng/components": "workspace:2.4.0",
20
+ "@supabase/supabase-js": "^1.35.4",
21
21
  "classnames": "2.3.1",
22
22
  "react": "^18.0.0",
23
23
  "react-cusdis": "^2.1.3",
@@ -28,7 +28,7 @@
28
28
  "swr": "^2.2.5"
29
29
  },
30
30
  "devDependencies": {
31
- "@bbki.ng/stylebase": "workspace:0.2.0",
31
+ "@bbki.ng/stylebase": "workspace:0.3.0",
32
32
  "@mdx-js/mdx": "2.0.0-next.9",
33
33
  "@mdx-js/react": "^1.6.22",
34
34
  "@mdx-js/rollup": "3.0.0",
package/src/app.tsx CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  GlobalLoadingStateProvider,
22
22
  } from "@/global_loading_state_provider";
23
23
  import { UploadPage } from "@/pages/upload";
24
+ import { AppCtxMenu } from "@/components/app_ctx_menu";
24
25
 
25
26
  const Layout = () => {
26
27
  const { isLoading } = useContext(GlobalLoadingContext);
@@ -28,7 +29,13 @@ const Layout = () => {
28
29
  <>
29
30
  <Page
30
31
  nav={
31
- <Nav paths={usePaths()} className="blur-cover" loading={isLoading} />
32
+ <AppCtxMenu>
33
+ <Nav
34
+ paths={usePaths()}
35
+ className="blur-cover select-none"
36
+ loading={isLoading}
37
+ />
38
+ </AppCtxMenu>
32
39
  }
33
40
  main={<Outlet />}
34
41
  />
@@ -10,11 +10,10 @@ import React from "react";
10
10
  import { useNavigate } from "react-router-dom";
11
11
 
12
12
  export const LoginMenuItem = () => {
13
- const isKing = useAuthed();
14
13
  const sess = useSupabaseSession();
15
14
  const nav = useNavigate();
16
15
 
17
- if (isKing) {
16
+ if (sess?.user != null) {
18
17
  return <ContextMenuLabel inset>{sess?.user?.email ?? ""}</ContextMenuLabel>;
19
18
  }
20
19
 
@@ -0,0 +1,52 @@
1
+ import React, { ReactElement, useCallback } from "react";
2
+ import { useAuthed } from "@/hooks/use_authed";
3
+ import {
4
+ ContextMenu,
5
+ ContextMenuContent,
6
+ ContextMenuTrigger,
7
+ ContextMenuItem,
8
+ } from "@bbki.ng/components";
9
+ import { useNavigate, useParams } from "react-router-dom";
10
+ import { useDelPost } from "@/hooks/use_delete_post";
11
+ import { toast } from "sonner";
12
+ import { confirm } from "@/utils";
13
+
14
+ export const ArticleCtxMenu = (props: { children: ReactElement }) => {
15
+ const auth = useAuthed();
16
+ const del = useDelPost();
17
+ const routeParams = useParams();
18
+ const nav = useNavigate();
19
+ const title = routeParams.title;
20
+
21
+ if (!auth) {
22
+ return props.children;
23
+ }
24
+
25
+ if (title == null || title === "") {
26
+ return props.children;
27
+ }
28
+
29
+ const doDel = useCallback(() => {
30
+ del(title)
31
+ .then(() => {
32
+ toast.success("删除成功");
33
+ nav("/blog");
34
+ })
35
+ .catch(console.log);
36
+ }, [title]);
37
+
38
+ return (
39
+ <ContextMenu>
40
+ <ContextMenuTrigger>{props.children}</ContextMenuTrigger>
41
+ <ContextMenuContent className="w-128">
42
+ <ContextMenuItem
43
+ onClick={() => {
44
+ confirm("确认删除?", doDel);
45
+ }}
46
+ >
47
+ remove
48
+ </ContextMenuItem>
49
+ </ContextMenuContent>
50
+ </ContextMenu>
51
+ );
52
+ };
@@ -21,6 +21,7 @@ export const API = {
21
21
  PROJECTS: "projects",
22
22
  UPLOAD_IMG: "upload",
23
23
  POST: "post",
24
+ REMOVE_POST: "remove_post",
24
25
  MOVIES: "movies",
25
26
  IMAGES: "images",
26
27
  BOOKS: "books",
@@ -0,0 +1,22 @@
1
+ import { useAuthedFetcher } from "@/hooks/use_authed_fetcher";
2
+ import { useCallback } from "react";
3
+ import { API } from "@/constants/routes";
4
+
5
+ export const useDelPost = () => {
6
+ const authedFetcher = useAuthedFetcher();
7
+
8
+ const req = useCallback(
9
+ async (url: string, { arg }: { arg: { title: string } }) => {
10
+ return authedFetcher(url, {
11
+ method: "POST",
12
+ body: JSON.stringify(arg),
13
+ });
14
+ },
15
+ [authedFetcher]
16
+ );
17
+
18
+ return useCallback(
19
+ (title: string) => req(API.REMOVE_POST, { arg: { title } }),
20
+ [req]
21
+ );
22
+ };
package/src/main.tsx CHANGED
@@ -15,9 +15,7 @@ const root = createRoot(container);
15
15
  root.render(
16
16
  <React.StrictMode>
17
17
  <Router>
18
- <AppCtxMenu>
19
- <App />
20
- </AppCtxMenu>
18
+ <App />
21
19
  <Toaster />
22
20
  <Logger />
23
21
  <ReloadPrompt />
@@ -9,6 +9,7 @@ import { ArticlePage } from "@/components/article";
9
9
  import { GlobalLoadingContext } from "@/global_loading_state_provider";
10
10
  import { useFile2Post } from "@/hooks/use_file_to_post";
11
11
  import { useAuthed } from "@/hooks/use_authed";
12
+ import { ArticleCtxMenu } from "@/components/article_ctx_menu";
12
13
 
13
14
  type TArticleMap = {
14
15
  [key: string]: ReactElement;
@@ -55,7 +56,9 @@ export default () => {
55
56
  return (
56
57
  <DropZone onDrop={reader} disabled={!isKing}>
57
58
  <ArticlePage title={title}>
58
- <div dangerouslySetInnerHTML={{ __html: posts.content }} />
59
+ <ArticleCtxMenu>
60
+ <div dangerouslySetInnerHTML={{ __html: posts.content }} />
61
+ </ArticleCtxMenu>
59
62
  </ArticlePage>
60
63
  </DropZone>
61
64
  );
@@ -1,13 +1,14 @@
1
- import React, { useState } from "react";
1
+ import React, { useContext, useState } from "react";
2
2
  import { Button, ButtonType } from "@bbki.ng/components";
3
3
  import { OauthProvider } from "@/types/supabase";
4
4
  import { supabase } from "@/constants";
5
5
  import { ArticlePage } from "@/components/article";
6
6
  import { useSupabaseSession } from "@/hooks/use_supa_session";
7
7
  import { Navigate } from "react-router-dom";
8
+ import { GlobalLoadingContext } from "@/global_loading_state_provider";
8
9
 
9
10
  export const Login = () => {
10
- const [loading, setLoading] = useState(false);
11
+ const { isLoading, setIsLoading } = useContext(GlobalLoadingContext);
11
12
 
12
13
  const session = useSupabaseSession();
13
14
  if (session) {
@@ -16,18 +17,32 @@ export const Login = () => {
16
17
 
17
18
  return (
18
19
  <ArticlePage title="第三方账号登录">
19
- <Button
20
- type={loading ? ButtonType.DISABLED : ButtonType.PRIMARY}
21
- className="ml-8"
22
- onClick={async () => {
23
- setLoading(true);
24
- return supabase.auth.signIn({
25
- provider: OauthProvider.GITHUB,
26
- });
27
- }}
28
- >
29
- GitHub
30
- </Button>
20
+ <>
21
+ <Button
22
+ type={isLoading ? ButtonType.DISABLED : ButtonType.PRIMARY}
23
+ className="ml-8"
24
+ onClick={async () => {
25
+ setIsLoading(true);
26
+ return supabase.auth.signIn({
27
+ provider: OauthProvider.GITHUB,
28
+ });
29
+ }}
30
+ >
31
+ GitHub
32
+ </Button>
33
+ <Button
34
+ type={isLoading ? ButtonType.DISABLED : ButtonType.PRIMARY}
35
+ className="ml-8"
36
+ onClick={async () => {
37
+ setIsLoading(true);
38
+ return supabase.auth.signIn({
39
+ provider: OauthProvider.Twitter,
40
+ });
41
+ }}
42
+ >
43
+ Twitter
44
+ </Button>
45
+ </>
31
46
  </ArticlePage>
32
47
  );
33
48
  };
@@ -6,4 +6,5 @@ export interface BBKingSession extends Session {
6
6
 
7
7
  export enum OauthProvider {
8
8
  GITHUB = "github",
9
+ Twitter = "twitter",
9
10
  }
@@ -3,6 +3,7 @@ import { ossProcessType } from "@/types/oss";
3
3
  import { API_ENDPOINT, OSS_ADDRESS } from "@/constants/routes";
4
4
  import { DEFAULT_DELAY } from "@/constants";
5
5
  import useSWR from "swr";
6
+ import { toast } from "sonner";
6
7
 
7
8
  type Fetcher = (resource: string, init?: any) => Promise<any>;
8
9
 
@@ -153,3 +154,19 @@ export const getRandomInt = (min: number, max: number) => {
153
154
  export const copyToClipboard = async (value: string) => {
154
155
  await navigator.clipboard.writeText(value);
155
156
  };
157
+
158
+ export const confirm = (message: string, exec: () => void) => {
159
+ toast("", {
160
+ description: message,
161
+ actionButtonStyle: {
162
+ backgroundColor: "#fff",
163
+ color: "rgb(37,99,235)",
164
+ },
165
+ action: {
166
+ label: "是",
167
+ onClick: () => {
168
+ exec();
169
+ },
170
+ },
171
+ });
172
+ };