@bbki.ng/site 5.4.21 → 5.4.22

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,11 @@
1
1
  # @bbki.ng/site
2
2
 
3
+ ## 5.4.22
4
+
5
+ ### Patch Changes
6
+
7
+ - f40a245: bug fix
8
+
3
9
  ## 5.4.21
4
10
 
5
11
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbki.ng/site",
3
- "version": "5.4.21",
3
+ "version": "5.4.22",
4
4
  "description": "code behind bbki.ng",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/src/blog/app.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useContext } from 'react';
1
+ import React, { useContext, useMemo } from 'react';
2
2
  import { Outlet, Route, Routes } from 'react-router-dom';
3
3
  import { Nav, NotFound, Page } from '@bbki.ng/components';
4
4
  import { HotKeyNav } from './components';
@@ -20,31 +20,31 @@ import { ThreeColLayout, ErrorBoundary } from '@bbki.ng/components';
20
20
  import { useDynamicLogo } from './hooks/use_dynamic_logo';
21
21
 
22
22
  const Layout = () => {
23
+ const paths = usePaths();
23
24
  const { isLoading } = useContext(GlobalLoadingContext);
24
25
  const logo = useDynamicLogo();
26
+
27
+ const middleRenderer = useMemo(() => {
28
+ return () => (
29
+ <ErrorBoundary>
30
+ <Outlet />
31
+ </ErrorBoundary>
32
+ );
33
+ }, []);
34
+
25
35
  return (
26
36
  <Page
27
37
  nav={
28
38
  <AppCtxMenu>
29
39
  <Nav
30
- paths={usePaths()}
40
+ paths={paths}
31
41
  className="gradient-blur-cover select-none"
32
42
  loading={isLoading}
33
43
  customLogo={logo}
34
44
  />
35
45
  </AppCtxMenu>
36
46
  }
37
- main={
38
- <ThreeColLayout
39
- middleRenderer={() => {
40
- return (
41
- <ErrorBoundary>
42
- <Outlet />
43
- </ErrorBoundary>
44
- );
45
- }}
46
- />
47
- }
47
+ main={<ThreeColLayout middleRenderer={middleRenderer} />}
48
48
  />
49
49
  );
50
50
  };
@@ -6,16 +6,10 @@ export { withArticleWrapper } from './with_wrapper';
6
6
 
7
7
  export { HotKeyNav } from './hotkey_nav';
8
8
 
9
- export { VideoPlayer } from './video_player';
10
-
11
- export { ProgressBar } from './progress_bar';
12
-
13
9
  export { BlurCover } from './blur_cover';
14
10
 
15
11
  export { ReloadPrompt } from './reload_prompt';
16
12
 
17
- export { Tags } from './tags';
18
-
19
13
  export { MySuspense } from './my_suspense';
20
14
 
21
15
  export const CenterLinkList = (props: any) => {
@@ -1,8 +1,9 @@
1
- import { useSupabaseSession } from "@/hooks/use_supa_session";
2
- import { apiFetcher, withToken } from "@/utils";
3
- import { useCallback } from "react";
1
+ import { useSupabaseSession } from '@/hooks/use_supa_session';
2
+ import { apiFetcher, withToken } from '@/utils';
3
+ import { useCallback, useMemo } from 'react';
4
4
 
5
5
  export const useAuthedFetcher = () => {
6
6
  const { access_token: token } = useSupabaseSession() || {};
7
- return useCallback(withToken(apiFetcher)(token), [token]);
7
+ const fetcherWithToken = useMemo(() => withToken(apiFetcher), []);
8
+ return useCallback(fetcherWithToken(token), [token, fetcherWithToken]);
8
9
  };
@@ -1,23 +1,23 @@
1
- import {useEffect} from "react";
2
- import {useAuthedStringPost} from "@/hooks/use_authed_string_post";
1
+ import { useEffect } from 'react';
2
+ import { useAuthedStringPost } from '@/hooks/use_authed_string_post';
3
3
 
4
4
  export const useSharedStringToPost = () => {
5
- const post = useAuthedStringPost();
5
+ const post = useAuthedStringPost();
6
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
- }
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);
15
13
  }
14
+ }
15
+ };
16
16
 
17
- window.addEventListener("message", handleSharedString);
17
+ window.addEventListener('message', handleSharedString);
18
18
 
19
- return () => {
20
- window.removeEventListener("message", handleSharedString);
21
- }
22
- }, []);
23
- }
19
+ return () => {
20
+ window.removeEventListener('message', handleSharedString);
21
+ };
22
+ }, [post]);
23
+ };
@@ -1,13 +1,13 @@
1
- import useSWR from "swr";
2
- import useSWRInfinite from "swr/infinite";
3
- import { baseFetcher, withBBApi } from "@/utils";
4
- import { API_CF_ENDPOINT } from "@/constants/routes";
5
- import { useContext, useEffect, useState, useCallback } from "react";
6
- import { GlobalLoadingContext } from "@/context/global_loading_state_provider";
1
+ import useSWR from 'swr';
2
+ import useSWRInfinite from 'swr/infinite';
3
+ import { baseFetcher, withBBApi } from '@/utils';
4
+ import { API_CF_ENDPOINT } from '@/constants/routes';
5
+ import { useContext, useEffect, useState, useCallback } from 'react';
6
+ import { GlobalLoadingContext } from '@/context/global_loading_state_provider';
7
7
 
8
8
  // In dev, use /api prefix to leverage Vite proxy to localhost:8787
9
- const isProd = typeof window !== "undefined" && /^https:\/\/bbki.ng/.test(window.location.href);
10
- const API_BASE = !isProd ? "/api" : API_CF_ENDPOINT;
9
+ const isProd = typeof window !== 'undefined' && /^https:\/\/bbki.ng/.test(window.location.href);
10
+ const API_BASE = !isProd ? '/api' : API_CF_ENDPOINT;
11
11
 
12
12
  export type StreamingItem = {
13
13
  id: string;
@@ -38,13 +38,13 @@ function buildStreamingUrl(params: StreamingQueryParams = {}): string {
38
38
  const url = new URL(`${API_BASE}/streaming`, !isProd ? window.location.origin : undefined);
39
39
 
40
40
  if (params.before) {
41
- url.searchParams.set("before", params.before);
41
+ url.searchParams.set('before', params.before);
42
42
  }
43
43
  if (params.after) {
44
- url.searchParams.set("after", params.after);
44
+ url.searchParams.set('after', params.after);
45
45
  }
46
46
  if (params.offset) {
47
- url.searchParams.set("offset", params.offset.toString());
47
+ url.searchParams.set('offset', params.offset.toString());
48
48
  }
49
49
 
50
50
  return !isProd ? url.pathname + url.search : url.toString();
@@ -61,11 +61,11 @@ async function fetchStreaming(params: StreamingQueryParams = {}): Promise<Stream
61
61
 
62
62
  // SWR key generator for streaming queries
63
63
  const getStreamingKey = (params: StreamingQueryParams) => {
64
- const parts = ["streaming"];
64
+ const parts = ['streaming'];
65
65
  if (params.before) parts.push(`before=${params.before}`);
66
66
  if (params.after) parts.push(`after=${params.after}`);
67
67
  if (params.offset) parts.push(`offset=${params.offset}`);
68
- return parts.join("?");
68
+ return parts.join('?');
69
69
  };
70
70
 
71
71
  interface UseStreamingOptions {
@@ -91,12 +91,12 @@ export function useStreaming(options: UseStreamingOptions = {}) {
91
91
 
92
92
  const isLoading = !data && !error;
93
93
 
94
- const [_, forceUpdate] = useState(0);
94
+ const [, forceUpdate] = useState(0);
95
95
 
96
96
  // make rerender when customElement defined
97
97
  useEffect(() => {
98
- customElements.whenDefined("bb-msg-history").then(() => {
99
- forceUpdate((prev) => prev + 1);
98
+ customElements.whenDefined('bb-msg-history').then(() => {
99
+ forceUpdate(prev => prev + 1);
100
100
  });
101
101
  }, []);
102
102
 
@@ -1,10 +1,10 @@
1
- import { createClient, Session } from "@supabase/supabase-js";
2
- import { SUPABASE } from "@/constants";
3
- import { useEffect, useState } from "react";
4
- import { BBKingSession } from "@/types/supabase";
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
5
 
6
6
  export const useSupabaseSession = (): BBKingSession | null => {
7
- const supabase = createClient(SUPABASE.URL, SUPABASE.ANNO);
7
+ const supabase = useMemo(() => createClient(SUPABASE.URL, SUPABASE.ANNO), []);
8
8
 
9
9
  const extendSess = (sess: Session | null) => {
10
10
  if (!sess) {
@@ -20,12 +20,13 @@ export const useSupabaseSession = (): BBKingSession | null => {
20
20
  const [session, setSession] = useState(extendSess(supabase.auth.session()));
21
21
 
22
22
  useEffect(() => {
23
- supabase.auth.onAuthStateChange((event, session) => {
24
- if (event === "SIGNED_IN") {
23
+ const { data: subscription } = supabase.auth.onAuthStateChange((event, session) => {
24
+ if (event === 'SIGNED_IN') {
25
25
  setSession(extendSess(session));
26
26
  }
27
27
  });
28
- }, []);
28
+ return () => subscription?.unsubscribe();
29
+ }, [supabase]);
29
30
 
30
31
  return session;
31
32
  };
@@ -28,11 +28,9 @@ const Posts = (props: TxtProps) => {
28
28
  return <CenterLinkList links={props.articleList} />;
29
29
  }
30
30
 
31
- const links = [...titleList];
32
-
33
31
  return (
34
32
  <CenterLinkList
35
- links={props.articleList || links}
33
+ links={props.articleList || titleList}
36
34
  loading={isLoading}
37
35
  footer={
38
36
  <Button onClick={gotoTop} className="mt-128">
@@ -1,29 +0,0 @@
1
- import classnames from "classnames";
2
- import React from "react";
3
-
4
- type aspectRatioBoxProps = {
5
- width: number | string;
6
- hwRatio: number;
7
- className?: string;
8
- children?: any;
9
- };
10
-
11
- export const AspectRatioBox = (props: aspectRatioBoxProps) => {
12
- const { className, width, hwRatio, children } = props;
13
- const innerStyle = {
14
- width: "100%",
15
- paddingTop: `${hwRatio * 100}%`,
16
- };
17
-
18
- return (
19
- <div
20
- style={{ width, maxWidth: "100%" }}
21
- className={classnames(className, "relative")}
22
- >
23
- <div style={innerStyle} />
24
- {children && (
25
- <div className="absolute top-0 bottom-0 left-0 right-0">{children}</div>
26
- )}
27
- </div>
28
- );
29
- };
@@ -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,39 +0,0 @@
1
- import React, { useState } from "react";
2
- import { ArticlePage } from "@/components/article";
3
- import { threeColWrapper } from "@/components/with_wrapper";
4
- import { ImageUploader } from "@/components/ImageUploader";
5
- import { UploadResult } from "@/types/upload";
6
- import { Button } from "@bbki.ng/components";
7
- import { copyToClipboard } from "@/utils";
8
-
9
- const Upload = () => {
10
- const [source, setSource] = useState("");
11
- const buildSource = (result: UploadResult) =>
12
- `<Img
13
- src="${result.src}"
14
- height={${result.height}}
15
- width={${result.width}}
16
- renderedWidth={${result.width}}
17
- removeBlurBgAfterLoad
18
- />`;
19
-
20
- return (
21
- <ArticlePage title="图片素材上传">
22
- <>
23
- <ImageUploader
24
- onUploadSuccess={(res) => {
25
- setSource(buildSource(res));
26
- }}
27
- />
28
- {source && <pre>{source}</pre>}
29
- {source && (
30
- <Button className="m-16" onClick={() => copyToClipboard(source)}>
31
- 复制代码
32
- </Button>
33
- )}
34
- </>
35
- </ArticlePage>
36
- );
37
- };
38
-
39
- export const UploadPage = Upload;