@bbki.ng/site 5.4.14 → 5.4.15

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,7 @@
1
1
  # @bbki.ng/site
2
2
 
3
+ ## 5.4.15
4
+
3
5
  ## 5.4.14
4
6
 
5
7
  ## 5.4.13
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbki.ng/site",
3
- "version": "5.4.14",
3
+ "version": "5.4.15",
4
4
  "description": "code behind bbki.ng",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -20,8 +20,8 @@
20
20
  "sonner": "1.4.0",
21
21
  "swr": "^2.2.5",
22
22
  "vaul": "1.1.2",
23
- "@bbki.ng/stylebase": "3.1.1",
24
- "@bbki.ng/components": "5.2.6"
23
+ "@bbki.ng/components": "5.2.7",
24
+ "@bbki.ng/stylebase": "3.1.1"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@mdx-js/mdx": "2.0.0-next.9",
@@ -1,14 +1,13 @@
1
1
  import useSWR from "swr";
2
+ import useSWRInfinite from "swr/infinite";
2
3
  import { baseFetcher, withBBApi } from "@/utils";
3
4
  import { API_CF_ENDPOINT } from "@/constants/routes";
4
- import { useContext, useEffect, useState } from "react";
5
+ import { useContext, useEffect, useState, useCallback } from "react";
5
6
  import { GlobalLoadingContext } from "@/context/global_loading_state_provider";
6
7
 
7
8
  // In dev, use /api prefix to leverage Vite proxy to localhost:8787
8
9
  const isProd = typeof window !== "undefined" && /^https:\/\/bbki.ng/.test(window.location.href);
9
- const streamingFetcher = !isProd
10
- ? (resource: string, init?: RequestInit) => baseFetcher(`/api/${resource}`, init)
11
- : withBBApi(baseFetcher)(API_CF_ENDPOINT);
10
+ const API_BASE = !isProd ? "/api" : API_CF_ENDPOINT;
12
11
 
13
12
  export type StreamingItem = {
14
13
  id: string;
@@ -18,10 +17,76 @@ export type StreamingItem = {
18
17
  createdAt: string;
19
18
  };
20
19
 
21
- export const useStreaming = () => {
22
- const { data, error } = useSWR("streaming", streamingFetcher, {
20
+ interface StreamingResponse {
21
+ status: string;
22
+ data: StreamingItem[];
23
+ }
24
+
25
+ export interface StreamingQueryParams {
26
+ /** Fetch records older than this ID (for pagination / next page) */
27
+ before?: string;
28
+ /** Fetch records newer than this ID (for polling / new messages) */
29
+ after?: string;
30
+ /** Number of records to fetch (default: 8, max: 100) */
31
+ offset?: number;
32
+ }
33
+
34
+ /**
35
+ * Build streaming API URL with query parameters
36
+ */
37
+ function buildStreamingUrl(params: StreamingQueryParams = {}): string {
38
+ const url = new URL(`${API_BASE}/streaming`, !isProd ? window.location.origin : undefined);
39
+
40
+ if (params.before) {
41
+ url.searchParams.set("before", params.before);
42
+ }
43
+ if (params.after) {
44
+ url.searchParams.set("after", params.after);
45
+ }
46
+ if (params.offset) {
47
+ url.searchParams.set("offset", params.offset.toString());
48
+ }
49
+
50
+ return !isProd ? url.pathname + url.search : url.toString();
51
+ }
52
+
53
+ /**
54
+ * Fetch streaming data from API
55
+ */
56
+ async function fetchStreaming(params: StreamingQueryParams = {}): Promise<StreamingResponse> {
57
+ const url = buildStreamingUrl(params);
58
+ const response = await baseFetcher(url);
59
+ return response as StreamingResponse;
60
+ }
61
+
62
+ // SWR key generator for streaming queries
63
+ const getStreamingKey = (params: StreamingQueryParams) => {
64
+ const parts = ["streaming"];
65
+ if (params.before) parts.push(`before=${params.before}`);
66
+ if (params.after) parts.push(`after=${params.after}`);
67
+ if (params.offset) parts.push(`offset=${params.offset}`);
68
+ return parts.join("?");
69
+ };
70
+
71
+ interface UseStreamingOptions {
72
+ /** Polling interval in ms (default: 1000, set to 0 to disable) */
73
+ refreshInterval?: number;
74
+ /** Initial offset for first fetch (default: 8) */
75
+ offset?: number;
76
+ }
77
+
78
+ /**
79
+ * Hook for fetching streaming data with polling
80
+ * Use this for simple cases where you just need the latest data
81
+ */
82
+ export function useStreaming(options: UseStreamingOptions = {}) {
83
+ const { refreshInterval = 1000, offset = 8 } = options;
84
+
85
+ const key = getStreamingKey({ offset });
86
+
87
+ const { data, error, mutate } = useSWR(key, () => fetchStreaming({ offset }), {
23
88
  revalidateOnFocus: true,
24
- refreshInterval: 1000,
89
+ refreshInterval,
25
90
  });
26
91
 
27
92
  const isLoading = !data && !error;
@@ -33,7 +98,7 @@ export const useStreaming = () => {
33
98
  customElements.whenDefined("bb-msg-history").then(() => {
34
99
  forceUpdate((prev) => prev + 1);
35
100
  });
36
- }, [])
101
+ }, []);
37
102
 
38
103
  const { setIsLoading } = useContext(GlobalLoadingContext);
39
104
 
@@ -42,8 +107,10 @@ export const useStreaming = () => {
42
107
  }, [isLoading, setIsLoading]);
43
108
 
44
109
  return {
45
- streaming: data?.data as StreamingItem[] | undefined,
110
+ streaming: data?.data ?? [],
46
111
  isError: error,
47
112
  isLoading,
113
+ /** Refetch data manually */
114
+ mutate,
48
115
  };
49
- };
116
+ }
@@ -55,7 +55,7 @@ const Streaming = () => {
55
55
  </bb-msg-history>
56
56
  </Panel>
57
57
  <Button
58
- className="mt-128"
58
+ className="mt-64"
59
59
  transparent={!showScrollBtn}
60
60
  onClick={() => {
61
61
  bbMsgHistoryRef.current?.scrollToBottom();
@@ -69,7 +69,7 @@ const Streaming = () => {
69
69
  style={{
70
70
  rotate: '180deg'
71
71
  }}
72
- className={classNames("transition-opacity duration-300 inline-block", {
72
+ className={classNames("transition-opacity duration-200 inline-block", {
73
73
  "opacity-0": !showScrollBtn,
74
74
  "opacity-100": showScrollBtn,
75
75
  })}