@bbki.ng/site 5.4.22 → 5.4.25

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 (100) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/index.html +12 -19
  3. package/package.json +5 -8
  4. package/src/blog/app.tsx +21 -33
  5. package/src/blog/components/article/index.tsx +8 -17
  6. package/src/blog/components/index.tsx +0 -6
  7. package/src/blog/components/share/share-btn.tsx +5 -8
  8. package/src/blog/constants/index.ts +1 -16
  9. package/src/blog/constants/routes.ts +12 -22
  10. package/src/blog/context/bbcontext.tsx +7 -7
  11. package/src/blog/hooks/index.ts +3 -18
  12. package/src/blog/hooks/use_dynamic_logo.tsx +6 -11
  13. package/src/blog/hooks/use_posts.ts +19 -10
  14. package/src/blog/hooks/use_role.ts +9 -14
  15. package/src/blog/hooks/use_streaming.ts +2 -2
  16. package/src/blog/index.tsx +5 -11
  17. package/src/blog/pages/cover/index.tsx +0 -1
  18. package/src/blog/pages/extensions/txt/article.tsx +7 -21
  19. package/src/blog/pages/extensions/txt/index.tsx +2 -17
  20. package/src/blog/pages/login/index.tsx +18 -42
  21. package/src/blog/pages/streaming/index.tsx +28 -28
  22. package/src/blog/swr.tsx +4 -7
  23. package/src/blog/utils/index.ts +9 -172
  24. package/vite.config.js +2 -1
  25. package/src/blog/articles/anti-logic.mdx +0 -61
  26. package/src/blog/articles/bbking-manual.mdx +0 -7
  27. package/src/blog/articles/black-river.mdx +0 -8
  28. package/src/blog/articles/celebration.mdx +0 -21
  29. package/src/blog/articles/cloth.mdx +0 -11
  30. package/src/blog/articles/cooking.mdx +0 -7
  31. package/src/blog/articles/cooldown.mdx +0 -12
  32. package/src/blog/articles/cousin.mdx +0 -15
  33. package/src/blog/articles/fall.mdx +0 -8
  34. package/src/blog/articles/img.mdx +0 -104
  35. package/src/blog/articles/leaves.mdx +0 -7
  36. package/src/blog/articles/liqiu.mdx +0 -7
  37. package/src/blog/articles/loading.mdx +0 -144
  38. package/src/blog/articles/love.mdx +0 -19
  39. package/src/blog/articles/major-cold.mdx +0 -14
  40. package/src/blog/articles/marshroom.mdx +0 -17
  41. package/src/blog/articles/men-without-women.mdx +0 -19
  42. package/src/blog/articles/moment.mdx +0 -9
  43. package/src/blog/articles/movie-day.mdx +0 -15
  44. package/src/blog/articles/photos.mdx +0 -13
  45. package/src/blog/articles/projects.mdx +0 -8
  46. package/src/blog/articles/pseudo-spring.mdx +0 -7
  47. package/src/blog/articles/quote.mdx +0 -26
  48. package/src/blog/articles/red-gun.mdx +0 -19
  49. package/src/blog/articles/rice-noodle.mdx +0 -21
  50. package/src/blog/articles/spring-cooldown.mdx +0 -8
  51. package/src/blog/articles/spring-rain.mdx +0 -10
  52. package/src/blog/articles/travel.mdx +0 -22
  53. package/src/blog/articles/warming-up.mdx +0 -10
  54. package/src/blog/articles/web-burnning.mdx +0 -10
  55. package/src/blog/articles/woke-up.mdx +0 -7
  56. package/src/blog/articles/xwy-and-snowing.mdx +0 -13
  57. package/src/blog/articles/xwy.mdx +0 -9
  58. package/src/blog/components/ImageUploader.tsx +0 -55
  59. package/src/blog/components/Img_ctx_menu/index.tsx +0 -67
  60. package/src/blog/components/Logger.tsx +0 -9
  61. package/src/blog/components/Pochacco/Pochacco.tsx +0 -29
  62. package/src/blog/components/Pochacco/idle.tsx +0 -31
  63. package/src/blog/components/Pochacco/watch.tsx +0 -28
  64. package/src/blog/components/Version.tsx +0 -14
  65. package/src/blog/components/app_ctx_menu/LoginMenuItem.tsx +0 -72
  66. package/src/blog/components/app_ctx_menu/PostMenuItem.tsx +0 -22
  67. package/src/blog/components/app_ctx_menu/VersionMenuItem.tsx +0 -13
  68. package/src/blog/components/app_ctx_menu/ViewSourceMenuItem.tsx +0 -34
  69. package/src/blog/components/app_ctx_menu/index.tsx +0 -35
  70. package/src/blog/components/article_ctx_menu/index.tsx +0 -58
  71. package/src/blog/components/blur_cover/index.tsx +0 -28
  72. package/src/blog/components/hotkey_nav/index.tsx +0 -51
  73. package/src/blog/components/progress_bar/index.tsx +0 -31
  74. package/src/blog/components/reaction/emojis.tsx +0 -143
  75. package/src/blog/components/reaction/oh_reaction.tsx +0 -105
  76. package/src/blog/components/reload_prompt/index.tsx +0 -51
  77. package/src/blog/components/tags/index.tsx +0 -52
  78. package/src/blog/hooks/useLoadingIndicator.ts +0 -12
  79. package/src/blog/hooks/useScrollToTop.ts +0 -24
  80. package/src/blog/hooks/useTransitionCls.ts +0 -36
  81. package/src/blog/hooks/use_authed.ts +0 -7
  82. package/src/blog/hooks/use_authed_fetcher.ts +0 -9
  83. package/src/blog/hooks/use_authed_string_post.ts +0 -42
  84. package/src/blog/hooks/use_clipboard_content.ts +0 -21
  85. package/src/blog/hooks/use_clipboard_to_post.ts +0 -48
  86. package/src/blog/hooks/use_del_img.ts +0 -22
  87. package/src/blog/hooks/use_delete_post.ts +0 -22
  88. package/src/blog/hooks/use_file_to_post.ts +0 -38
  89. package/src/blog/hooks/use_img_loading.ts +0 -16
  90. package/src/blog/hooks/use_post.ts +0 -26
  91. package/src/blog/hooks/use_projects.ts +0 -67
  92. package/src/blog/hooks/use_route_name.ts +0 -7
  93. package/src/blog/hooks/use_shared_string_to_post.ts +0 -23
  94. package/src/blog/hooks/use_supa_session.ts +0 -32
  95. package/src/blog/hooks/use_text_plain_file.ts +0 -36
  96. package/src/blog/hooks/use_uploader.ts +0 -34
  97. package/src/blog/hooks/use_video_controls.ts +0 -71
  98. package/src/blog/types/supabase.ts +0 -12
  99. package/src/blog/types/upload.ts +0 -16
  100. package/src/blog/utils/tags.ts +0 -21
@@ -1,51 +0,0 @@
1
- import { useEffect } from "react";
2
- import { toast } from "sonner";
3
-
4
- // @ts-ignore
5
- import { useRegisterSW } from "virtual:pwa-register/react";
6
-
7
- export const ReloadPrompt = () => {
8
- const {
9
- needRefresh: [needRefresh, setNeedRefresh],
10
- updateServiceWorker,
11
- } = useRegisterSW({
12
- onRegisterError(error: any) {
13
- console.log("SW registration error", error);
14
- },
15
- onRegisteredSW(swScriptUrl: string, r: ServiceWorkerRegistration) {
16
- console.log("SW registered: ", swScriptUrl, r);
17
- },
18
- onOfflineReady() {
19
- console.log("App is offline-ready");
20
- },
21
- });
22
-
23
- useEffect(() => {
24
- if (needRefresh) {
25
- toast("", {
26
- description: "发现新版本,是否更新?",
27
- duration: 10000,
28
- position: "bottom-right",
29
- actionButtonStyle: {
30
- backgroundColor: "#fff",
31
- color: "rgb(37,99,235)",
32
- },
33
- action: {
34
- label: "是",
35
- onClick: () => {
36
- updateServiceWorker(false).then(() => {
37
- // @ts-ignore
38
- toast("", {
39
- description: `已更新`,
40
- position: "bottom-right",
41
- });
42
- setNeedRefresh(false);
43
- });
44
- },
45
- },
46
- });
47
- }
48
- }, [needRefresh]);
49
-
50
- return null;
51
- };
@@ -1,52 +0,0 @@
1
- import React from "react";
2
- import classnames from "classnames";
3
- import { List, Tag, Link, TitledList } from "@bbki.ng/components";
4
- import { ROUTES } from "@/constants";
5
-
6
- type MyTag =
7
- | {
8
- path: string;
9
- name: string;
10
- }
11
- | string;
12
-
13
- type Tags = {
14
- tags: MyTag[];
15
- className?: string;
16
- inline?: boolean;
17
- };
18
-
19
- export const Tags = (props: Tags) => {
20
- const { inline, className, tags } = props;
21
-
22
- const renderTag = (tag: MyTag) => {
23
- const TagComp = inline ? Tag : Link;
24
- if (typeof tag === "string") {
25
- return <TagComp to={`${ROUTES.TAGS}/${tag}`}>{tag}</TagComp>;
26
- }
27
-
28
- return <TagComp to={tag.path}>{tag.name}</TagComp>;
29
- };
30
-
31
- if (inline) {
32
- return (
33
- <>
34
- <List
35
- items={tags}
36
- itemRenderer={renderTag}
37
- horizontal={inline}
38
- className={classnames(className, "inline-flex", "flex-wrap")}
39
- />
40
- </>
41
- );
42
- }
43
- return (
44
- <TitledList
45
- title={"标签"}
46
- items={tags}
47
- itemRenderer={renderTag}
48
- horizontal={inline}
49
- className={className}
50
- />
51
- );
52
- };
@@ -1,12 +0,0 @@
1
- import { GlobalLoadingContext } from "@/context/global_loading_state_provider";
2
- import { useContext } from "react";
3
-
4
- export const useLoadingIndicator = () => {
5
- const globalCtx = useContext(GlobalLoadingContext);
6
-
7
- return {
8
- setVisibility: globalCtx.setIsLoading,
9
- isVisible: globalCtx.isLoading,
10
- isFontLoading: globalCtx.isFontLoading,
11
- };
12
- };
@@ -1,24 +0,0 @@
1
- import { useLocation } from "react-router-dom";
2
- import { useEffect } from "react";
3
-
4
- export const useScrollToTop = (excludePath = "") => {
5
- const { pathname } = useLocation();
6
-
7
- const resetScroll = () => {
8
- if (excludePath && pathname.includes(excludePath)) {
9
- return;
10
- }
11
- setTimeout(() => {
12
- window.scrollTo(0, 0);
13
- }, 0);
14
- };
15
-
16
- useEffect(() => {
17
- window.addEventListener("popstate", resetScroll);
18
- return () => {
19
- window.removeEventListener("popstate", resetScroll);
20
- };
21
- }, []);
22
-
23
- useEffect(resetScroll, [pathname]);
24
- };
@@ -1,36 +0,0 @@
1
- import React, { useEffect } from "react";
2
- import classnames from "classnames";
3
-
4
- interface TransOption {
5
- originCls?: string;
6
- opacity?: boolean;
7
- offset?: boolean;
8
- blur?: boolean;
9
- }
10
-
11
- const defaultOpt = {
12
- originCls: "",
13
- opacity: true,
14
- offset: true,
15
- blur: true,
16
- };
17
-
18
- export const useTransitionCls = (options: TransOption = defaultOpt) => {
19
- const [visible, setVisible] = React.useState(false);
20
- const { originCls, opacity, offset, blur } = options;
21
-
22
- const cls = classnames(originCls, "transition-all", "duration-500", {
23
- "blur-xs": !visible && blur,
24
- "blur-none": visible && blur,
25
- "translate-y-0": visible && offset,
26
- "translate-y-8": !visible && offset,
27
- "opacity-10": !visible && opacity,
28
- "opacity-100": visible && opacity,
29
- });
30
-
31
- useEffect(() => {
32
- setVisible(true);
33
- }, []);
34
-
35
- return cls;
36
- };
@@ -1,7 +0,0 @@
1
- import { useSupabaseSession } from "@/hooks/use_supa_session";
2
-
3
- export const useAuthed = (): boolean => {
4
- const { access_token: token, isKing } = useSupabaseSession() || {};
5
-
6
- return !(!token || !isKing);
7
- };
@@ -1,9 +0,0 @@
1
- import { useSupabaseSession } from '@/hooks/use_supa_session';
2
- import { apiFetcher, withToken } from '@/utils';
3
- import { useCallback, useMemo } from 'react';
4
-
5
- export const useAuthedFetcher = () => {
6
- const { access_token: token } = useSupabaseSession() || {};
7
- const fetcherWithToken = useMemo(() => withToken(apiFetcher), []);
8
- return useCallback(fetcherWithToken(token), [token, fetcherWithToken]);
9
- };
@@ -1,42 +0,0 @@
1
- import {useSWRConfig} from "swr";
2
- import {useAuthed} from "@/hooks/use_authed";
3
- import {useContext} from "react";
4
- import {GlobalLoadingContext} from "@/context/global_loading_state_provider";
5
- import {usePost} from "@/hooks/use_post";
6
- import {splitPost} from "@/utils";
7
- import {API} from "@/constants/routes";
8
-
9
- export const useAuthedStringPost = () => {
10
- const { mutate } = useSWRConfig();
11
-
12
- const isKing = useAuthed();
13
-
14
- const { setIsLoading } = useContext(GlobalLoadingContext);
15
-
16
- const post = usePost();
17
-
18
- return (content: string) => {
19
- if (!content) {
20
- return;
21
- }
22
-
23
- if (!isKing) {
24
- return;
25
- }
26
-
27
- const postObj = splitPost(content);
28
- if (!postObj?.content || !postObj?.title) {
29
- return;
30
- }
31
-
32
- setIsLoading(true);
33
-
34
- post(postObj.title, postObj.content)
35
- .then((r) => {
36
- mutate(API.POSTS).then();
37
- })
38
- .finally(() => {
39
- setIsLoading(false);
40
- });
41
- }
42
- }
@@ -1,21 +0,0 @@
1
- import React from 'react';
2
-
3
- export const useClipboardContent = () => {
4
- const [clipboardContent, setClipboardContent] = React.useState<string | null>(null);
5
-
6
- React.useEffect(() => {
7
- const handlePaste = (event: ClipboardEvent) => {
8
- if (event.clipboardData) {
9
- setClipboardContent(event.clipboardData.getData('text/plain'));
10
- }
11
- };
12
-
13
- document.addEventListener('paste', handlePaste);
14
-
15
- return () => {
16
- document.removeEventListener('paste', handlePaste);
17
- };
18
- }, []);
19
-
20
- return clipboardContent;
21
- };
@@ -1,48 +0,0 @@
1
- import {useClipboardContent} from "@/hooks/use_clipboard_content";
2
- import {useSWRConfig} from "swr";
3
- import {useContext, useEffect} from "react";
4
- import {GlobalLoadingContext} from "@/context/global_loading_state_provider";
5
- import {usePost} from "@/hooks/use_post";
6
- import {API} from "@/constants/routes";
7
- import {useAuthed} from "@/hooks/use_authed";
8
- import {toast} from "sonner";
9
-
10
- export const useClipboardToPost = () => {
11
- const clipboardContent = useClipboardContent();
12
-
13
- const { mutate } = useSWRConfig();
14
-
15
- const { setIsLoading } = useContext(GlobalLoadingContext);
16
-
17
- const post = usePost();
18
-
19
- const firstLine = clipboardContent ? clipboardContent.split("\n")[0] : "";
20
- const title = firstLine ? firstLine.trim() : "";
21
-
22
- const restContent = clipboardContent ? clipboardContent.slice(title.length).trim() : "";
23
-
24
- const isKing = useAuthed();
25
-
26
- useEffect(() => {
27
- if (!isKing || !restContent || !title) {
28
- return;
29
- }
30
-
31
- setIsLoading(true);
32
-
33
- post(title, restContent)
34
- .then((r) => {
35
- mutate(API.POSTS).then(() => {
36
- toast("", {
37
- description: `已更新`,
38
- position: "bottom-right",
39
- });
40
- });
41
- })
42
- .finally(() => {
43
- setIsLoading(false);
44
- });
45
- }, [title, restContent]);
46
-
47
- return null;
48
- }
@@ -1,22 +0,0 @@
1
- import { useAuthedFetcher } from "@/hooks/use_authed_fetcher";
2
- import { useCallback } from "react";
3
- import { API } from "@/constants/routes";
4
-
5
- export const useDelImg = () => {
6
- const authedFetcher = useAuthedFetcher();
7
-
8
- const req = useCallback(
9
- async (url: string, { arg }: { arg: { id: number } }) => {
10
- return authedFetcher(url, {
11
- method: "POST",
12
- body: JSON.stringify(arg),
13
- });
14
- },
15
- [authedFetcher]
16
- );
17
-
18
- return useCallback(
19
- (id: number) => req(API.REMOVE_IMG, { arg: { id } }),
20
- [req]
21
- );
22
- };
@@ -1,22 +0,0 @@
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
- };
@@ -1,38 +0,0 @@
1
- import { useTextPlainFile } from "@/hooks/use_text_plain_file";
2
- import { useContext, useEffect } from "react";
3
- import { usePost } from "@/hooks/use_post";
4
- import { GlobalLoadingContext } from "@/context/global_loading_state_provider";
5
- import { useAuthedFetcher } from "@/hooks/use_authed_fetcher";
6
- import { preload, useSWRConfig } from "swr";
7
- import { API } from "@/constants/routes";
8
-
9
- export type fileReader = (f: File) => void;
10
- export const useFile2Post = (): fileReader => {
11
- const { content, title, reader } = useTextPlainFile();
12
-
13
- const { mutate } = useSWRConfig();
14
-
15
- const { setIsLoading } = useContext(GlobalLoadingContext);
16
-
17
- const post = usePost();
18
-
19
- useEffect(() => {
20
- if (!content || !title) {
21
- return;
22
- }
23
-
24
- setIsLoading(true);
25
-
26
- post(title, content)
27
- .then((r) => {
28
- mutate(API.POSTS).then();
29
- })
30
- .finally(() => {
31
- setIsLoading(false);
32
- });
33
- }, [title, content]);
34
-
35
- return (f: File) => {
36
- reader(f);
37
- };
38
- };
@@ -1,16 +0,0 @@
1
- import { useEffect, useState } from "react";
2
-
3
- export const useImgLoading = (src: string) => {
4
- const [loading, setLoading] = useState(true);
5
- useEffect(() => {
6
- let img = new Image();
7
- img.src = src;
8
- img.onload = () => {
9
- setLoading(false);
10
- };
11
- return () => {
12
- img.src = "";
13
- };
14
- }, []);
15
- return loading;
16
- };
@@ -1,26 +0,0 @@
1
- import { useCallback } from "react";
2
- import { API } from "@/constants/routes";
3
- import { useAuthedFetcher } from "@/hooks/use_authed_fetcher";
4
-
5
- export const usePost = () => {
6
- const authedFetcher = useAuthedFetcher();
7
-
8
- const req = useCallback(
9
- async (
10
- url: string,
11
- { arg }: { arg: { title: string; content: string } }
12
- ) => {
13
- return authedFetcher(url, {
14
- method: "POST",
15
- body: JSON.stringify(arg),
16
- });
17
- },
18
- [authedFetcher]
19
- );
20
-
21
- return useCallback(
22
- (title: string, content: string) =>
23
- req(API.POST, { arg: { title, content } }),
24
- [req]
25
- );
26
- };
@@ -1,67 +0,0 @@
1
- import useSWR, { useSWRConfig } from "swr";
2
- import { API } from "@/constants/routes";
3
- import { useCallback, useContext, useEffect } from "react";
4
- import { Photo } from "@/types/photo";
5
- import { GlobalLoadingContext } from "@/context/global_loading_state_provider";
6
-
7
- export const useProjects = (name: string = "", suspense?: boolean) => {
8
- const URL = `${API.PROJECTS}${name ? "/" : ""}${name}`;
9
-
10
- const { data, error } = useSWR(URL, {
11
- revalidateOnFocus: false,
12
- suspense,
13
- });
14
-
15
- let isLoading = !data && !error;
16
-
17
- const { setIsLoading } = useContext(GlobalLoadingContext);
18
-
19
- useEffect(() => {
20
- setIsLoading(isLoading);
21
- }, [isLoading]);
22
-
23
- const { mutate, cache } = useSWRConfig();
24
-
25
- const getCachedProjects = (n: string) => {
26
- if (!n) {
27
- return;
28
- }
29
- return cache
30
- .get("projects")
31
- ?.data.find((p: { name: string }) => p.name === n);
32
- };
33
-
34
- const refresh = useCallback(() => {
35
- return mutate(URL);
36
- }, [URL]);
37
-
38
- const addLocalPhotoImmediately = useCallback(
39
- (photo: Photo) => {
40
- if (!name) {
41
- return;
42
- }
43
-
44
- if (!data || error) {
45
- return;
46
- }
47
-
48
- return mutate(
49
- URL,
50
- {
51
- ...data,
52
- images: [photo, ...data.images],
53
- },
54
- false
55
- );
56
- },
57
- [data, error]
58
- );
59
-
60
- return {
61
- refresh,
62
- addLocalPhotoImmediately,
63
- projects: data || getCachedProjects(name),
64
- isError: error,
65
- isLoading: !data && !error,
66
- };
67
- };
@@ -1,7 +0,0 @@
1
- import { ROUTE_NAME } from "@/constants";
2
- import { usePathName } from "@/hooks/use_pathname";
3
-
4
- export const useRouteName = () => {
5
- const pathname = usePathName();
6
- return ROUTE_NAME[pathname.replace(/\/$/, "")] || ROUTE_NAME.unknown;
7
- };
@@ -1,23 +0,0 @@
1
- import { useEffect } from 'react';
2
- import { useAuthedStringPost } from '@/hooks/use_authed_string_post';
3
-
4
- export const useSharedStringToPost = () => {
5
- const post = useAuthedStringPost();
6
-
7
- useEffect(() => {
8
- const handleSharedString = (event: MessageEvent) => {
9
- if (event.data && typeof event.data === 'string') {
10
- const content = event.data;
11
- if (content) {
12
- post(content);
13
- }
14
- }
15
- };
16
-
17
- window.addEventListener('message', handleSharedString);
18
-
19
- return () => {
20
- window.removeEventListener('message', handleSharedString);
21
- };
22
- }, [post]);
23
- };
@@ -1,32 +0,0 @@
1
- import { createClient, Session } from '@supabase/supabase-js';
2
- import { SUPABASE } from '@/constants';
3
- import { useEffect, useMemo, useState } from 'react';
4
- import { BBKingSession } from '@/types/supabase';
5
-
6
- export const useSupabaseSession = (): BBKingSession | null => {
7
- const supabase = useMemo(() => createClient(SUPABASE.URL, SUPABASE.ANNO), []);
8
-
9
- const extendSess = (sess: Session | null) => {
10
- if (!sess) {
11
- return sess;
12
- }
13
- return {
14
- ...sess,
15
- isKing: sess.user?.id === SUPABASE.BB_KING_ID,
16
- isQueen: sess.user?.id === SUPABASE.BB_QUEEN_ID,
17
- };
18
- };
19
-
20
- const [session, setSession] = useState(extendSess(supabase.auth.session()));
21
-
22
- useEffect(() => {
23
- const { data: subscription } = supabase.auth.onAuthStateChange((event, session) => {
24
- if (event === 'SIGNED_IN') {
25
- setSession(extendSess(session));
26
- }
27
- });
28
- return () => subscription?.unsubscribe();
29
- }, [supabase]);
30
-
31
- return session;
32
- };
@@ -1,36 +0,0 @@
1
- import { useEffect, useState } from "react";
2
-
3
- export type Result = {
4
- content: string;
5
- title: string;
6
- reader: (f: File) => void;
7
- };
8
-
9
- export const useTextPlainFile = (): Result => {
10
- const [content, setContent] = useState("");
11
- const [file, setFile] = useState<File | null>(null);
12
-
13
- const reader = new FileReader();
14
-
15
- useEffect(() => {
16
- if (file != null) {
17
- reader.readAsText(file, "utf-8");
18
- }
19
-
20
- reader.onload = function () {
21
- setContent(reader.result as string);
22
- };
23
-
24
- return () => {
25
- reader.onload = null;
26
- };
27
- }, [file]);
28
-
29
- return {
30
- content,
31
- title: file?.name ?? "",
32
- reader: (f: File) => {
33
- setFile(f);
34
- },
35
- };
36
- };
@@ -1,34 +0,0 @@
1
- import { useCallback } from "react";
2
- import { useSupabaseSession } from "@/hooks/use_supa_session";
3
- import { apiFetcher, getImageFileSize, withToken } from "@/utils";
4
- import { Photo } from "@/types/photo";
5
- import { API } from "@/constants/routes";
6
- import { UploadResult } from "@/types/oss";
7
-
8
- export const useUploader = () => {
9
- const { access_token: token } = useSupabaseSession() || {};
10
- const authedApiFetcher = withToken(apiFetcher)(token);
11
-
12
- return useCallback(
13
- async (pid: string, projectName: string, file: File): Promise<Photo> => {
14
- const { width, height } = await getImageFileSize(file);
15
-
16
- const url = `${API.UPLOAD_IMG}?pid=${pid}&fileName=${file.name}&projectName=${projectName}&width=${width}&height=${height}`;
17
-
18
- const result = (await authedApiFetcher(url, {
19
- method: "POST",
20
- headers: {
21
- "Content-Type": "application/octet-stream",
22
- },
23
- body: file,
24
- })) as UploadResult;
25
-
26
- return {
27
- width,
28
- height,
29
- src: result.url,
30
- };
31
- },
32
- [token]
33
- );
34
- };