@bbki.ng/site 5.4.21 → 5.4.24

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 (103) 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 +32 -44
  5. package/src/blog/components/article/index.tsx +8 -17
  6. package/src/blog/components/index.tsx +0 -12
  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 +16 -16
  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 +3 -20
  20. package/src/blog/pages/login/index.tsx +18 -42
  21. package/src/blog/swr.tsx +4 -7
  22. package/src/blog/utils/index.ts +9 -172
  23. package/vite.config.js +2 -1
  24. package/src/blog/articles/anti-logic.mdx +0 -61
  25. package/src/blog/articles/bbking-manual.mdx +0 -7
  26. package/src/blog/articles/black-river.mdx +0 -8
  27. package/src/blog/articles/celebration.mdx +0 -21
  28. package/src/blog/articles/cloth.mdx +0 -11
  29. package/src/blog/articles/cooking.mdx +0 -7
  30. package/src/blog/articles/cooldown.mdx +0 -12
  31. package/src/blog/articles/cousin.mdx +0 -15
  32. package/src/blog/articles/fall.mdx +0 -8
  33. package/src/blog/articles/img.mdx +0 -104
  34. package/src/blog/articles/leaves.mdx +0 -7
  35. package/src/blog/articles/liqiu.mdx +0 -7
  36. package/src/blog/articles/loading.mdx +0 -144
  37. package/src/blog/articles/love.mdx +0 -19
  38. package/src/blog/articles/major-cold.mdx +0 -14
  39. package/src/blog/articles/marshroom.mdx +0 -17
  40. package/src/blog/articles/men-without-women.mdx +0 -19
  41. package/src/blog/articles/moment.mdx +0 -9
  42. package/src/blog/articles/movie-day.mdx +0 -15
  43. package/src/blog/articles/photos.mdx +0 -13
  44. package/src/blog/articles/projects.mdx +0 -8
  45. package/src/blog/articles/pseudo-spring.mdx +0 -7
  46. package/src/blog/articles/quote.mdx +0 -26
  47. package/src/blog/articles/red-gun.mdx +0 -19
  48. package/src/blog/articles/rice-noodle.mdx +0 -21
  49. package/src/blog/articles/spring-cooldown.mdx +0 -8
  50. package/src/blog/articles/spring-rain.mdx +0 -10
  51. package/src/blog/articles/travel.mdx +0 -22
  52. package/src/blog/articles/warming-up.mdx +0 -10
  53. package/src/blog/articles/web-burnning.mdx +0 -10
  54. package/src/blog/articles/woke-up.mdx +0 -7
  55. package/src/blog/articles/xwy-and-snowing.mdx +0 -13
  56. package/src/blog/articles/xwy.mdx +0 -9
  57. package/src/blog/components/ImageUploader.tsx +0 -55
  58. package/src/blog/components/Img_ctx_menu/index.tsx +0 -67
  59. package/src/blog/components/Logger.tsx +0 -9
  60. package/src/blog/components/Pochacco/Pochacco.tsx +0 -29
  61. package/src/blog/components/Pochacco/idle.tsx +0 -31
  62. package/src/blog/components/Pochacco/watch.tsx +0 -28
  63. package/src/blog/components/Version.tsx +0 -14
  64. package/src/blog/components/app_ctx_menu/LoginMenuItem.tsx +0 -72
  65. package/src/blog/components/app_ctx_menu/PostMenuItem.tsx +0 -22
  66. package/src/blog/components/app_ctx_menu/VersionMenuItem.tsx +0 -13
  67. package/src/blog/components/app_ctx_menu/ViewSourceMenuItem.tsx +0 -34
  68. package/src/blog/components/app_ctx_menu/index.tsx +0 -35
  69. package/src/blog/components/article_ctx_menu/index.tsx +0 -58
  70. package/src/blog/components/aspect_ratio_box/index.tsx +0 -29
  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/components/video_player/index.tsx +0 -82
  79. package/src/blog/global/mdx.d.ts +0 -9
  80. package/src/blog/hooks/useLoadingIndicator.ts +0 -12
  81. package/src/blog/hooks/useScrollToTop.ts +0 -24
  82. package/src/blog/hooks/useTransitionCls.ts +0 -36
  83. package/src/blog/hooks/use_authed.ts +0 -7
  84. package/src/blog/hooks/use_authed_fetcher.ts +0 -8
  85. package/src/blog/hooks/use_authed_string_post.ts +0 -42
  86. package/src/blog/hooks/use_clipboard_content.ts +0 -21
  87. package/src/blog/hooks/use_clipboard_to_post.ts +0 -48
  88. package/src/blog/hooks/use_del_img.ts +0 -22
  89. package/src/blog/hooks/use_delete_post.ts +0 -22
  90. package/src/blog/hooks/use_file_to_post.ts +0 -38
  91. package/src/blog/hooks/use_img_loading.ts +0 -16
  92. package/src/blog/hooks/use_post.ts +0 -26
  93. package/src/blog/hooks/use_projects.ts +0 -67
  94. package/src/blog/hooks/use_route_name.ts +0 -7
  95. package/src/blog/hooks/use_shared_string_to_post.ts +0 -23
  96. package/src/blog/hooks/use_supa_session.ts +0 -31
  97. package/src/blog/hooks/use_text_plain_file.ts +0 -36
  98. package/src/blog/hooks/use_uploader.ts +0 -34
  99. package/src/blog/hooks/use_video_controls.ts +0 -71
  100. package/src/blog/pages/upload/index.tsx +0 -39
  101. package/src/blog/types/supabase.ts +0 -12
  102. package/src/blog/types/upload.ts +0 -16
  103. package/src/blog/utils/tags.ts +0 -21
@@ -1,105 +0,0 @@
1
- import React, {
2
- ReactNode,
3
- useContext,
4
- useEffect,
5
- useRef,
6
- useState,
7
- } from "react";
8
- import { GlobalLoadingContext } from "@/context/global_loading_state_provider";
9
- import { BlinkDot, Button, ButtonType } from "@bbki.ng/components";
10
- import { faces, hearts, ReactionEmojiPair, sadFaces } from "./emojis";
11
- import { ShareBtn } from "../share/share-btn";
12
-
13
- declare global {
14
- namespace JSX {
15
- interface IntrinsicElements {
16
- "open-heart": React.DetailedHTMLProps<
17
- React.HTMLAttributes<HTMLElement> & {
18
- href?: string;
19
- emoji?: string;
20
- ariaDisabled?: boolean;
21
- onClick?: React.MouseEventHandler<HTMLElement>;
22
- },
23
- HTMLElement
24
- >;
25
- }
26
- }
27
- }
28
-
29
- const useHeartSent = (val: string) => {
30
- const [sent, setSent] = useState(false);
31
- const { setIsLoading } = useContext(GlobalLoadingContext);
32
-
33
- const handleSent = (evt: Event) => {
34
- const targetVal = (evt.target as HTMLElement).getAttribute("emoji");
35
-
36
- if (targetVal == val) setSent(true);
37
- setIsLoading(false);
38
- };
39
-
40
- useEffect(() => {
41
- addEventListener("open-heart", handleSent);
42
-
43
- return () => {
44
- removeEventListener("open-heart", handleSent);
45
- };
46
- }, []);
47
-
48
- return sent;
49
- };
50
-
51
- export const OpenHeartReaction = (props: {
52
- title: string;
53
- emojiPair: ReactionEmojiPair;
54
- }) => {
55
- const { title, emojiPair = hearts } = props;
56
- const sent = useHeartSent(emojiPair.val);
57
-
58
- const { setIsLoading } = useContext(GlobalLoadingContext);
59
-
60
- const ohRef = useRef<HTMLElement>(null);
61
-
62
- const pressed = () => ohRef.current?.getAttribute("aria-pressed");
63
-
64
- const handleHeartClick = () => {
65
- if (sent || pressed()) {
66
- return;
67
- }
68
-
69
- setIsLoading(true);
70
- };
71
-
72
- return (
73
- <open-heart
74
- ref={ohRef}
75
- href={`https://oh.bbki.ng/?id=${title}`}
76
- emoji={emojiPair.val}
77
- >
78
- <Button
79
- size="small"
80
- type={ButtonType.GHOST}
81
- className="text-gray-400 hover:text-gray-600 transition-colors ease-in duration-200"
82
- onClick={handleHeartClick}
83
- >
84
- {sent || pressed() ? emojiPair.on : emojiPair.off}
85
- </Button>
86
- </open-heart>
87
- );
88
- };
89
-
90
- export const Reaction = (props: { title: string; url: string }) => {
91
- return (
92
- <div className="flex items-center">
93
- <OpenHeartReaction title={props.title} emojiPair={hearts} />
94
- <OpenHeartReaction title={props.title} emojiPair={faces} />
95
- <OpenHeartReaction title={props.title} emojiPair={sadFaces} />
96
-
97
- <ShareBtn
98
- shareInfo={{
99
- title: props.title,
100
- url: props.url,
101
- }}
102
- />
103
- </div>
104
- );
105
- };
@@ -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,82 +0,0 @@
1
- import React, { ReactEventHandler, useEffect, useState } from "react";
2
- import classnames from "classnames";
3
- import { useVideoControls, useVideoEleHeight, useVideoProgress } from "@/hooks";
4
- import { BlurCover, ProgressBar } from "@/components";
5
- import { BgColors, TextColors } from "@/types/color";
6
- import { AspectRatioBox } from "@/components/aspect_ratio_box";
7
-
8
- const VIDEO_TAG_ASPECT_RATIO = 0.5624910522548318;
9
-
10
- type videoPlayProps = {
11
- src: string;
12
- className?: string;
13
- };
14
-
15
- export const VideoPlayer = (props: videoPlayProps) => {
16
- const { src, className } = props;
17
- const [hovered, setHovered] = useState(false);
18
- const [showPlayer, setShowPlayer] = useState(false);
19
- const { videoRef, toggle, isPlay } = useVideoControls();
20
- const { progress, onTimeUpdate } = useVideoProgress();
21
- const VideoHeight = useVideoEleHeight(videoRef);
22
-
23
- useEffect(() => {
24
- if (!videoRef.current) {
25
- return;
26
- }
27
- videoRef.current.load();
28
- }, []);
29
-
30
- const onPlayerReady: ReactEventHandler = () => {
31
- setShowPlayer(true);
32
- };
33
-
34
- const cls = classnames({ hidden: !showPlayer });
35
-
36
- return (
37
- <div
38
- className={classnames(
39
- "flex flex-col relative cursor-pointer",
40
- className,
41
- BgColors.LIGHT_GRAY
42
- )}
43
- onClick={async () => {
44
- await toggle();
45
- if (!isPlay) {
46
- setHovered(false);
47
- }
48
- }}
49
- onMouseEnter={() => {
50
- setHovered(true);
51
- }}
52
- onMouseLeave={() => {
53
- setHovered(false);
54
- }}
55
- >
56
- <AspectRatioBox
57
- width="100%"
58
- hwRatio={VIDEO_TAG_ASPECT_RATIO}
59
- className={BgColors.LIGHT_GRAY}
60
- >
61
- <video
62
- playsInline
63
- ref={videoRef}
64
- src={src}
65
- className={cls}
66
- onTimeUpdate={onTimeUpdate}
67
- onCanPlayThrough={onPlayerReady}
68
- />
69
- </AspectRatioBox>
70
- {hovered && showPlayer && (
71
- <BlurCover
72
- textColor={isPlay ? TextColors.RED : TextColors.BLUE}
73
- height={VideoHeight}
74
- visibleOnHover
75
- >
76
- {isPlay ? "pause" : "play"}
77
- </BlurCover>
78
- )}
79
- {showPlayer && <ProgressBar progress={progress} />}
80
- </div>
81
- );
82
- };
@@ -1,9 +0,0 @@
1
- declare module "*.mdx" {
2
- let MDXComponent: (props: any) => JSX.Element;
3
- export default MDXComponent;
4
- export const meta: {
5
- tags?: string[];
6
- title: string;
7
- created_at?: string;
8
- };
9
- }
@@ -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,8 +0,0 @@
1
- import { useSupabaseSession } from "@/hooks/use_supa_session";
2
- import { apiFetcher, withToken } from "@/utils";
3
- import { useCallback } from "react";
4
-
5
- export const useAuthedFetcher = () => {
6
- const { access_token: token } = useSupabaseSession() || {};
7
- return useCallback(withToken(apiFetcher)(token), [token]);
8
- };
@@ -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
- };