@peerbit/react 0.0.15 → 0.0.16

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.
@@ -3,6 +3,7 @@ export * from "./utils.js";
3
3
  export { FastMutex } from "./lockstorage.js";
4
4
  export { useProgram } from "./useProgram.js";
5
5
  export { useLocal } from "./useLocal.js";
6
+ export { useLocalPaginated } from "./useLocalPaginated.js";
6
7
  export { useOnline } from "./useOnline.js";
7
8
  export { useCount } from "./useCount.js";
8
9
  export { debounceLeadingTrailing } from "./utils.js";
package/lib/esm/index.js CHANGED
@@ -3,6 +3,7 @@ export * from "./utils.js";
3
3
  export { FastMutex } from "./lockstorage.js";
4
4
  export { useProgram } from "./useProgram.js";
5
5
  export { useLocal } from "./useLocal.js";
6
+ export { useLocalPaginated } from "./useLocalPaginated.js";
6
7
  export { useOnline } from "./useOnline.js";
7
8
  export { useCount } from "./useCount.js";
8
9
  export { debounceLeadingTrailing } from "./utils.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,YAAY,EACZ,WAAW,EACX,OAAO,EACP,eAAe,GAClB,MAAM,cAAc,CAAC;AACtB,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,YAAY,EACZ,WAAW,EACX,OAAO,EACP,eAAe,GAClB,MAAM,cAAc,CAAC;AACtB,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC"}
@@ -13,6 +13,8 @@ export declare const useLocal: <T extends Record<string, any>, I extends Record<
13
13
  transform?: (result: RT) => Promise<RT>;
14
14
  onChanges?: (all: RT[]) => void;
15
15
  debounce?: number;
16
- debug?: boolean;
16
+ debug?: boolean | {
17
+ id: string;
18
+ };
17
19
  } & QueryOptons) => RT[];
18
20
  export {};
@@ -5,7 +5,8 @@ export const useLocal = (db, options) => {
5
5
  const [all, setAll] = useState([]);
6
6
  const emptyResultsRef = useRef(false);
7
7
  useEffect(() => {
8
- if (!db || db.closed) {
8
+ if (!db || db.closed || options?.query === null) {
9
+ // null query means no query at all
9
10
  return;
10
11
  }
11
12
  const _l = async (args) => {
@@ -19,6 +20,12 @@ export const useLocal = (db, options) => {
19
20
  if (options?.transform) {
20
21
  results = await Promise.all(results.map((x) => options.transform(x)));
21
22
  }
23
+ if (options?.debug) {
24
+ let dbgId = typeof options.debug === "boolean"
25
+ ? undefined
26
+ : options.debug.id;
27
+ console.log(dbgId ? "fetched " + dbgId : "fetched", results, "query", options?.query);
28
+ }
22
29
  emptyResultsRef.current = results.length === 0;
23
30
  setAll(() => {
24
31
  options?.onChanges?.(results);
@@ -33,9 +40,6 @@ export const useLocal = (db, options) => {
33
40
  }
34
41
  };
35
42
  const debounced = debounceLeadingTrailing(_l, options?.debounce ?? 1000);
36
- let ts = setTimeout(() => {
37
- _l();
38
- }, 3000);
39
43
  const handleChange = () => {
40
44
  if (emptyResultsRef.current) {
41
45
  debounced.cancel();
@@ -50,7 +54,6 @@ export const useLocal = (db, options) => {
50
54
  return () => {
51
55
  db.events.removeEventListener("change", handleChange);
52
56
  debounced.cancel();
53
- clearTimeout(ts);
54
57
  };
55
58
  }, [
56
59
  db?.closed ? undefined : db?.rootAddress,
@@ -1 +1 @@
1
- {"version":3,"file":"useLocal.js","sourceRoot":"","sources":["../../src/useLocal.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useLocal.js","sourceRoot":"","sources":["../../src/useLocal.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { Documents, WithContext } from "@peerbit/document";
2
+ import * as indexerTypes from "@peerbit/indexer-interface";
3
+ type QueryLike = {
4
+ query?: indexerTypes.Query[] | indexerTypes.QueryLike;
5
+ sort?: indexerTypes.Sort[] | indexerTypes.Sort | indexerTypes.SortLike;
6
+ };
7
+ type QueryOptions = {
8
+ query: QueryLike;
9
+ id: string;
10
+ };
11
+ export declare const useLocalPaginated: <T extends Record<string, any>, I extends Record<string, any>, R extends boolean | undefined = true, RT = R extends false ? WithContext<I> : WithContext<T>>(db?: Documents<T, I>, options?: {
12
+ resolve?: R;
13
+ transform?: (result: WithContext<RT>) => Promise<WithContext<RT>>;
14
+ onChanges?: (all: RT[]) => void;
15
+ debounce?: number;
16
+ debug?: boolean | {
17
+ id: string;
18
+ };
19
+ reverse?: boolean;
20
+ batchSize?: number;
21
+ } & QueryOptions) => {
22
+ items: WithContext<RT>[];
23
+ loadMore: () => Promise<void>;
24
+ isLoading: boolean;
25
+ empty: boolean;
26
+ };
27
+ export {};
@@ -0,0 +1,135 @@
1
+ import { useState, useEffect, useRef } from "react";
2
+ import { ClosedError, } from "@peerbit/document";
3
+ export const useLocalPaginated = (db, options) => {
4
+ const [all, setAll] = useState([]);
5
+ const allRef = useRef([]);
6
+ const [isLoading, setIsLoading] = useState(false);
7
+ const iteratorRef = useRef(null);
8
+ const emptyResultsRef = useRef(false);
9
+ const updateAll = (combined) => {
10
+ if (options?.onChanges) {
11
+ options?.onChanges?.(combined);
12
+ }
13
+ if (options?.debug) {
14
+ let dbgId = typeof options.debug === "boolean"
15
+ ? undefined
16
+ : options.debug.id;
17
+ console.log("Loading more items, new combined length", dbgId, combined.length);
18
+ }
19
+ const dedub = new Set();
20
+ for (const item of combined) {
21
+ if ("idString" in item) {
22
+ if (dedub.has(item.idString)) {
23
+ throw new Error("Duplicate item found in iterator");
24
+ }
25
+ dedub.add(item.idString);
26
+ }
27
+ }
28
+ allRef.current = combined;
29
+ setAll(combined);
30
+ };
31
+ // Initialize the iterator only once or when query changes
32
+ useEffect(() => {
33
+ if (!db || db.closed || options?.query === null) {
34
+ return;
35
+ }
36
+ const initIterator = () => {
37
+ try {
38
+ // Initialize the iterator and load initial batch.
39
+ emptyResultsRef.current = false;
40
+ iteratorRef.current?.close();
41
+ iteratorRef.current = db.index.iterate(options?.query ?? {}, {
42
+ local: true,
43
+ remote: false,
44
+ resolve: options?.resolve,
45
+ }); // TODO types
46
+ if (options?.debug) {
47
+ let dbgId = typeof options.debug === "boolean"
48
+ ? undefined
49
+ : options.debug.id;
50
+ console.log("Create new iterator", dbgId);
51
+ }
52
+ loadMore(); // initial load
53
+ }
54
+ catch (error) {
55
+ console.error("Error initializing iterator", error);
56
+ }
57
+ };
58
+ // Reset state when the db or query changes.
59
+ console.log("RESET FROM", all.length);
60
+ setAll([]);
61
+ allRef.current = [];
62
+ initIterator();
63
+ const handleChange = async (e) => {
64
+ // while we are iterating, we might get new documents.. so this method inserts them where they should be
65
+ let merged = await db.index.updateResults(allRef.current, e.detail, options?.query || {}, options?.resolve ?? true);
66
+ console.log("merge result", "change: " +
67
+ (merged === allRef.current &&
68
+ merged.length &&
69
+ allRef.current.length === 0), merged, all, e.detail, allRef.current);
70
+ if (merged === allRef.current &&
71
+ merged.length &&
72
+ allRef.current.length === 0) {
73
+ // no change
74
+ }
75
+ else {
76
+ updateAll(options?.reverse ? merged.reverse() : merged);
77
+ }
78
+ };
79
+ db.events.addEventListener("change", handleChange);
80
+ return () => {
81
+ db.events.removeEventListener("change", handleChange);
82
+ iteratorRef.current?.close();
83
+ };
84
+ }, [
85
+ db?.closed ? undefined : db?.rootAddress,
86
+ options?.id,
87
+ options?.query,
88
+ options?.resolve,
89
+ ]);
90
+ // Define the loadMore function
91
+ const loadMore = async () => {
92
+ if (!iteratorRef.current || isLoading || emptyResultsRef.current)
93
+ return;
94
+ setIsLoading(true);
95
+ try {
96
+ // Fetch next batchSize number of items:
97
+ let refBefore = iteratorRef.current;
98
+ let newItems = await iteratorRef.current.next(options?.batchSize ?? 10);
99
+ if (options?.transform) {
100
+ newItems = await Promise.all(newItems.map((item) => options.transform(item)));
101
+ }
102
+ if (iteratorRef.current !== refBefore) {
103
+ // If the iterator has changed, we should not update the state
104
+ // This can happen if the iterator was closed and a new one was created
105
+ return;
106
+ }
107
+ emptyResultsRef.current = newItems.length === 0;
108
+ if (newItems.length > 0) {
109
+ let prev = allRef.current;
110
+ let prevHash = new Set(prev.map((x) => x.__context.head));
111
+ let newItemsNoHash = newItems.filter((x) => !prevHash.has(x.__context.head));
112
+ if (newItemsNoHash.length === 0) {
113
+ return;
114
+ }
115
+ const combined = options?.reverse
116
+ ? [...newItemsNoHash.reverse(), ...prev]
117
+ : [...prev, ...newItemsNoHash];
118
+ updateAll(combined);
119
+ }
120
+ }
121
+ catch (error) {
122
+ if (error instanceof ClosedError) {
123
+ // Handle closed database gracefully
124
+ }
125
+ else {
126
+ throw error;
127
+ }
128
+ }
129
+ finally {
130
+ setIsLoading(false);
131
+ }
132
+ };
133
+ return { items: all, loadMore, isLoading, empty: emptyResultsRef.current };
134
+ };
135
+ //# sourceMappingURL=useLocalPaginated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLocalPaginated.js","sourceRoot":"","sources":["../../src/useLocalPaginated.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peerbit/react",
3
- "version": "0.0.15",
3
+ "version": "0.0.16",
4
4
  "homepage": "https://dao-xyz.github.io/peerbit-examples",
5
5
  "type": "module",
6
6
  "module": "lib/esm/index.js",
@@ -70,5 +70,5 @@
70
70
  "last 1 safari version"
71
71
  ]
72
72
  },
73
- "gitHead": "bbc31ba767abdd37bc0b44c000910555b42dfd78"
73
+ "gitHead": "1b0737b523b88552b08edd24a7a967a61b4af163"
74
74
  }
package/src/index.ts CHANGED
@@ -8,6 +8,7 @@ export * from "./utils.js";
8
8
  export { FastMutex } from "./lockstorage.js";
9
9
  export { useProgram } from "./useProgram.js";
10
10
  export { useLocal } from "./useLocal.js";
11
+ export { useLocalPaginated } from "./useLocalPaginated.js";
11
12
  export { useOnline } from "./useOnline.js";
12
13
  export { useCount } from "./useCount.js";
13
14
  export { debounceLeadingTrailing } from "./utils.js";
package/src/useLocal.tsx CHANGED
@@ -24,14 +24,15 @@ export const useLocal = <
24
24
  transform?: (result: RT) => Promise<RT>;
25
25
  onChanges?: (all: RT[]) => void;
26
26
  debounce?: number;
27
- debug?: boolean; // add debug option here
27
+ debug?: boolean | { id: string };
28
28
  } & QueryOptons
29
29
  ) => {
30
30
  const [all, setAll] = useState<RT[]>([]);
31
31
  const emptyResultsRef = useRef(false);
32
32
 
33
33
  useEffect(() => {
34
- if (!db || db.closed) {
34
+ if (!db || db.closed || options?.query === null) {
35
+ // null query means no query at all
35
36
  return;
36
37
  }
37
38
 
@@ -50,6 +51,18 @@ export const useLocal = <
50
51
  results.map((x) => options.transform!(x))
51
52
  );
52
53
  }
54
+ if (options?.debug) {
55
+ let dbgId =
56
+ typeof options.debug === "boolean"
57
+ ? undefined
58
+ : options.debug.id;
59
+ console.log(
60
+ dbgId ? "fetched " + dbgId : "fetched",
61
+ results,
62
+ "query",
63
+ options?.query
64
+ );
65
+ }
53
66
  emptyResultsRef.current = results.length === 0;
54
67
  setAll(() => {
55
68
  options?.onChanges?.(results);
@@ -68,10 +81,6 @@ export const useLocal = <
68
81
  options?.debounce ?? 1000
69
82
  );
70
83
 
71
- let ts = setTimeout(() => {
72
- _l();
73
- }, 3000);
74
-
75
84
  const handleChange = () => {
76
85
  if (emptyResultsRef.current) {
77
86
  debounced.cancel();
@@ -87,7 +96,6 @@ export const useLocal = <
87
96
  return () => {
88
97
  db.events.removeEventListener("change", handleChange);
89
98
  debounced.cancel();
90
- clearTimeout(ts);
91
99
  };
92
100
  }, [
93
101
  db?.closed ? undefined : db?.rootAddress,
@@ -0,0 +1,208 @@
1
+ import { useState, useEffect, useRef } from "react";
2
+ import {
3
+ ClosedError,
4
+ Documents,
5
+ DocumentsChange,
6
+ ResultsIterator,
7
+ SearchRequest,
8
+ SearchRequestIndexed,
9
+ WithContext,
10
+ } from "@peerbit/document";
11
+ import * as indexerTypes from "@peerbit/indexer-interface";
12
+
13
+ type QueryLike = {
14
+ query?: indexerTypes.Query[] | indexerTypes.QueryLike;
15
+ sort?: indexerTypes.Sort[] | indexerTypes.Sort | indexerTypes.SortLike;
16
+ };
17
+ type QueryOptions = { query: QueryLike; id: string };
18
+
19
+ export const useLocalPaginated = <
20
+ T extends Record<string, any>,
21
+ I extends Record<string, any>,
22
+ R extends boolean | undefined = true,
23
+ RT = R extends false ? WithContext<I> : WithContext<T>
24
+ >(
25
+ db?: Documents<T, I>,
26
+ options?: {
27
+ resolve?: R;
28
+ transform?: (result: WithContext<RT>) => Promise<WithContext<RT>>;
29
+ onChanges?: (all: RT[]) => void;
30
+ debounce?: number;
31
+ debug?: boolean | { id: string };
32
+ reverse?: boolean;
33
+ batchSize?: number; // You can set a default batch size here
34
+ } & QueryOptions
35
+ ) => {
36
+ const [all, setAll] = useState<WithContext<RT>[]>([]);
37
+ const allRef = useRef<WithContext<RT>[]>([]);
38
+ const [isLoading, setIsLoading] = useState(false);
39
+ const iteratorRef = useRef<ResultsIterator<WithContext<RT>> | null>(null);
40
+ const emptyResultsRef = useRef(false);
41
+
42
+ const updateAll = (combined: WithContext<RT>[]) => {
43
+ if (options?.onChanges) {
44
+ options?.onChanges?.(combined);
45
+ }
46
+
47
+ if (options?.debug) {
48
+ let dbgId =
49
+ typeof options.debug === "boolean"
50
+ ? undefined
51
+ : options.debug.id;
52
+ console.log(
53
+ "Loading more items, new combined length",
54
+ dbgId,
55
+ combined.length
56
+ );
57
+ }
58
+
59
+ const dedub = new Set<string>();
60
+ for (const item of combined) {
61
+ if ("idString" in item) {
62
+ if (dedub.has(item.idString as string)) {
63
+ throw new Error("Duplicate item found in iterator");
64
+ }
65
+ dedub.add(item.idString as string);
66
+ }
67
+ }
68
+ allRef.current = combined;
69
+
70
+ setAll(combined);
71
+ };
72
+
73
+ // Initialize the iterator only once or when query changes
74
+ useEffect(() => {
75
+ if (!db || db.closed || options?.query === null) {
76
+ return;
77
+ }
78
+ const initIterator = () => {
79
+ try {
80
+ // Initialize the iterator and load initial batch.
81
+
82
+ emptyResultsRef.current = false;
83
+ iteratorRef.current?.close();
84
+ iteratorRef.current = db.index.iterate(options?.query ?? {}, {
85
+ local: true,
86
+ remote: false,
87
+ resolve: options?.resolve as any,
88
+ }) as any as ResultsIterator<WithContext<RT>>; // TODO types
89
+
90
+ if (options?.debug) {
91
+ let dbgId =
92
+ typeof options.debug === "boolean"
93
+ ? undefined
94
+ : options.debug.id;
95
+ console.log("Create new iterator", dbgId);
96
+ }
97
+
98
+ loadMore(); // initial load
99
+ } catch (error) {
100
+ console.error("Error initializing iterator", error);
101
+ }
102
+ };
103
+
104
+ // Reset state when the db or query changes.
105
+
106
+ console.log("RESET FROM", all.length);
107
+ setAll([]);
108
+ allRef.current = [];
109
+
110
+ initIterator();
111
+
112
+ const handleChange = async (e: CustomEvent<DocumentsChange<T>>) => {
113
+ // while we are iterating, we might get new documents.. so this method inserts them where they should be
114
+ let merged = await db.index.updateResults(
115
+ allRef.current,
116
+ e.detail,
117
+ options?.query || {},
118
+ options?.resolve ?? true
119
+ );
120
+ console.log(
121
+ "merge result",
122
+ "change: " +
123
+ (merged === allRef.current &&
124
+ merged.length &&
125
+ allRef.current.length === 0),
126
+ merged,
127
+ all,
128
+ e.detail,
129
+ allRef.current
130
+ );
131
+ if (
132
+ merged === allRef.current &&
133
+ merged.length &&
134
+ allRef.current.length === 0
135
+ ) {
136
+ // no change
137
+ } else {
138
+ updateAll(options?.reverse ? merged.reverse() : merged);
139
+ }
140
+ };
141
+
142
+ db.events.addEventListener("change", handleChange);
143
+ return () => {
144
+ db.events.removeEventListener("change", handleChange);
145
+ iteratorRef.current?.close();
146
+ };
147
+ }, [
148
+ db?.closed ? undefined : db?.rootAddress,
149
+ options?.id,
150
+ options?.query,
151
+ options?.resolve,
152
+ ]);
153
+
154
+ // Define the loadMore function
155
+ const loadMore = async () => {
156
+ if (!iteratorRef.current || isLoading || emptyResultsRef.current)
157
+ return;
158
+
159
+ setIsLoading(true);
160
+ try {
161
+ // Fetch next batchSize number of items:
162
+ let refBefore = iteratorRef.current;
163
+
164
+ let newItems: WithContext<RT>[] = await iteratorRef.current.next(
165
+ options?.batchSize ?? 10
166
+ );
167
+
168
+ if (options?.transform) {
169
+ newItems = await Promise.all(
170
+ newItems.map((item) => options.transform!(item))
171
+ );
172
+ }
173
+
174
+ if (iteratorRef.current !== refBefore) {
175
+ // If the iterator has changed, we should not update the state
176
+ // This can happen if the iterator was closed and a new one was created
177
+ return;
178
+ }
179
+
180
+ emptyResultsRef.current = newItems.length === 0;
181
+
182
+ if (newItems.length > 0) {
183
+ let prev = allRef.current;
184
+ let prevHash = new Set(prev.map((x) => x.__context.head));
185
+ let newItemsNoHash = newItems.filter(
186
+ (x) => !prevHash.has(x.__context.head)
187
+ );
188
+ if (newItemsNoHash.length === 0) {
189
+ return;
190
+ }
191
+ const combined = options?.reverse
192
+ ? [...newItemsNoHash.reverse(), ...prev]
193
+ : [...prev, ...newItemsNoHash];
194
+ updateAll(combined);
195
+ }
196
+ } catch (error) {
197
+ if (error instanceof ClosedError) {
198
+ // Handle closed database gracefully
199
+ } else {
200
+ throw error;
201
+ }
202
+ } finally {
203
+ setIsLoading(false);
204
+ }
205
+ };
206
+
207
+ return { items: all, loadMore, isLoading, empty: emptyResultsRef.current };
208
+ };