@semapps/activitypub-components 0.6.6 → 0.7.0

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/dist/index.es.js CHANGED
@@ -1,17 +1,17 @@
1
1
  import {jsxs as $85cNH$jsxs, Fragment as $85cNH$Fragment, jsx as $85cNH$jsx} from "react/jsx-runtime";
2
2
  import $85cNH$react, {useState as $85cNH$useState, useCallback as $85cNH$useCallback, useMemo as $85cNH$useMemo, useEffect as $85cNH$useEffect, forwardRef as $85cNH$forwardRef, useImperativeHandle as $85cNH$useImperativeHandle} from "react";
3
- import {useRecordContext as $85cNH$useRecordContext, useGetIdentity as $85cNH$useGetIdentity, useNotify as $85cNH$useNotify, Form as $85cNH$Form, fetchUtils as $85cNH$fetchUtils, TextField as $85cNH$TextField, DateField as $85cNH$DateField, RichTextField as $85cNH$RichTextField, useDataProvider as $85cNH$useDataProvider, useGetMany as $85cNH$useGetMany, useList as $85cNH$useList, ListContextProvider as $85cNH$ListContextProvider, useGetList as $85cNH$useGetList} from "react-admin";
3
+ import {useRecordContext as $85cNH$useRecordContext, useGetIdentity as $85cNH$useGetIdentity, useNotify as $85cNH$useNotify, Form as $85cNH$Form, useDataProvider as $85cNH$useDataProvider, TextField as $85cNH$TextField, DateField as $85cNH$DateField, RichTextField as $85cNH$RichTextField, useGetMany as $85cNH$useGetMany, useList as $85cNH$useList, ListContextProvider as $85cNH$ListContextProvider, fetchUtils as $85cNH$fetchUtils, useGetList as $85cNH$useGetList} from "react-admin";
4
4
  import {RichTextInput as $85cNH$RichTextInput, DefaultEditorOptions as $85cNH$DefaultEditorOptions} from "ra-input-rich-text";
5
5
  import $85cNH$tiptapextensionplaceholder from "@tiptap/extension-placeholder";
6
6
  import {Box as $85cNH$Box, Avatar as $85cNH$Avatar, Button as $85cNH$Button, Typography as $85cNH$Typography, CircularProgress as $85cNH$CircularProgress} from "@mui/material";
7
7
  import $85cNH$muistylesmakeStyles from "@mui/styles/makeStyles";
8
8
  import $85cNH$muiiconsmaterialSend from "@mui/icons-material/Send";
9
- import {useDataModel as $85cNH$useDataModel, buildBlankNodesQuery as $85cNH$buildBlankNodesQuery} from "@semapps/semantic-data-provider";
9
+ import {useDataModel as $85cNH$useDataModel, getOrCreateWsChannel as $85cNH$getOrCreateWsChannel} from "@semapps/semantic-data-provider";
10
10
  import {AuthDialog as $85cNH$AuthDialog} from "@semapps/auth-provider";
11
+ import {useQueries as $85cNH$useQueries, useQueryClient as $85cNH$useQueryClient, useInfiniteQuery as $85cNH$useInfiniteQuery} from "react-query";
11
12
  import {mergeAttributes as $85cNH$mergeAttributes} from "@tiptap/core";
12
13
  import $85cNH$tiptapextensionmention from "@tiptap/extension-mention";
13
14
  import {ReferenceField as $85cNH$ReferenceField, AvatarWithLabelField as $85cNH$AvatarWithLabelField} from "@semapps/field-components";
14
- import {useQueryClient as $85cNH$useQueryClient, useInfiniteQuery as $85cNH$useInfiniteQuery} from "react-query";
15
15
  import {ReactRenderer as $85cNH$ReactRenderer} from "@tiptap/react";
16
16
  import $85cNH$tippyjs from "tippy.js";
17
17
 
@@ -54,7 +54,7 @@ const $338f387df48a40d7$export$1ec8e53e7d982d22 = {
54
54
  REMOVE: "Remove",
55
55
  TENTATIVE_REJECT: "TentativeReject",
56
56
  TENTATIVE_ACCEPT: "TentativeAccept",
57
- TRAVAL: "Travel",
57
+ TRAVEL: "Travel",
58
58
  UNDO: "Undo",
59
59
  UPDATE: "Update",
60
60
  VIEW: "View"
@@ -86,78 +86,341 @@ const $338f387df48a40d7$export$4d8d554031975581 = "https://www.w3.org/ns/activit
86
86
 
87
87
 
88
88
 
89
- const $712f7f004b5f345e$var$useOutbox = ()=>{
89
+
90
+
91
+
92
+ const $577f4953dfa5de4f$export$e57ff0f701c44363 = (value)=>{
93
+ // If the field is null-ish, we suppose there are no values.
94
+ if (value === null || value === undefined) return [];
95
+ // Return as is.
96
+ if (Array.isArray(value)) return value;
97
+ // Single value is made an array.
98
+ return [
99
+ value
100
+ ];
101
+ };
102
+ var $577f4953dfa5de4f$export$2e2bcd8739ae039 = {
103
+ arrayOf: $577f4953dfa5de4f$export$e57ff0f701c44363
104
+ };
105
+ const $577f4953dfa5de4f$export$34aed805e991a647 = (iterable, predicate)=>{
106
+ const seen = new Set();
107
+ return iterable.filter((item)=>{
108
+ const key = predicate(item);
109
+ if (seen.has(key)) return false;
110
+ seen.add(key);
111
+ return true;
112
+ });
113
+ };
114
+
115
+
116
+ const $8281f3ce3b9d6123$var$useItemsFromPages = (pages, dereferenceItems)=>{
117
+ const dataProvider = (0, $85cNH$useDataProvider)();
118
+ const items = (0, $85cNH$useMemo)(()=>pages.flatMap((p)=>(0, $577f4953dfa5de4f$export$e57ff0f701c44363)(p.orderedItems || p.items)), [
119
+ pages
120
+ ]);
121
+ // We will force dereference, if some items are not URI string references.
122
+ const shouldDereference = (0, $85cNH$useMemo)(()=>{
123
+ return dereferenceItems || items.some((item)=>typeof item !== "string");
124
+ }, [
125
+ dereferenceItems,
126
+ items
127
+ ]);
128
+ // Dereference all items, if necessary (even if shouldDereference is false, the hook needs to be called).
129
+ const itemQueries = (0, $85cNH$useQueries)(!shouldDereference ? [] : items.filter((item)=>typeof item === "string").map((itemUri)=>({
130
+ queryKey: [
131
+ "resource",
132
+ itemUri
133
+ ],
134
+ queryFn: async ()=>(await dataProvider.fetch(itemUri)).json,
135
+ staleTime: Infinity
136
+ })));
137
+ if (!shouldDereference) return {
138
+ loadedItems: items,
139
+ isLoading: false,
140
+ isFetching: false
141
+ };
142
+ // Put all loaded items together (might be dereferenced already, so concatenate).
143
+ const loadedItems = items.filter((item)=>typeof item !== "string").concat(itemQueries.flatMap((itemQuery)=>{
144
+ return itemQuery.isSuccess && itemQuery.data || [];
145
+ }));
146
+ const errors = itemQueries.filter((q)=>q.error);
147
+ return {
148
+ loadedItems: loadedItems,
149
+ isLoading: itemQueries.some((q)=>q.isLoading),
150
+ isFetching: itemQueries.some((q)=>q.isFetching),
151
+ errors: errors.length > 0 ? errors : undefined
152
+ };
153
+ };
154
+ /**
155
+ * Subscribe a collection. Supports pagination.
156
+ * @param predicateOrUrl The collection URI or the predicate to get the collection URI from the identity (webId).
157
+ * @param {UseCollectionOptions} options Defaults to `{ dereferenceItems: false, liveUpdates: true }`
158
+ */ const $8281f3ce3b9d6123$var$useCollection = (predicateOrUrl, options = {})=>{
159
+ const { dereferenceItems: dereferenceItems = false, liveUpdates: liveUpdates = true } = options;
90
160
  const { data: identity } = (0, $85cNH$useGetIdentity)();
91
- const outboxUrl = (0, $85cNH$useMemo)(()=>{
92
- if (identity?.webIdData) return identity?.webIdData?.outbox;
161
+ const [totalItems, setTotalItems] = (0, $85cNH$useState)();
162
+ const queryClient = (0, $85cNH$useQueryClient)();
163
+ const [hasLiveUpdates, setHasLiveUpdates] = (0, $85cNH$useState)({
164
+ status: "connecting"
165
+ });
166
+ const dataProvider = (0, $85cNH$useDataProvider)();
167
+ // Get collectionUrl from webId predicate or URL.
168
+ const collectionUrl = (0, $85cNH$useMemo)(()=>{
169
+ if (predicateOrUrl) {
170
+ if (predicateOrUrl.startsWith("http")) return predicateOrUrl;
171
+ if (identity?.webIdData) return identity?.webIdData?.[predicateOrUrl];
172
+ }
173
+ return undefined;
174
+ // throw new Error(`No URL available for useCollection: ${predicateOrUrl}.`);
175
+ }, [
176
+ identity,
177
+ predicateOrUrl
178
+ ]);
179
+ // Fetch page of collection item references (if pageParam provided)
180
+ // or default to `collectionUrl` (which should give you the first page).
181
+ const fetchCollection = (0, $85cNH$useCallback)(async ({ pageParam: nextPageUrl })=>{
182
+ // Fetch page or first page (collectionUrl)
183
+ let { json: json } = await dataProvider.fetch(nextPageUrl || collectionUrl);
184
+ if (json.totalItems) setTotalItems(json.totalItems);
185
+ // If first page, handle this here.
186
+ if ((json.type === "OrderedCollection" || json.type === "Collection") && json.first) {
187
+ if (json.first?.items) {
188
+ if (json.first?.items.length === 0 && json.first?.next) // Special case where the first property is an object without items
189
+ ({ json: json } = await dataProvider.fetch(json.first?.next));
190
+ else json = json.first;
191
+ } else // Fetch the first page
192
+ ({ json: json } = await dataProvider.fetch(json.first));
193
+ }
194
+ return json;
195
+ }, [
196
+ dataProvider,
197
+ collectionUrl,
198
+ identity,
199
+ setTotalItems
200
+ ]);
201
+ // Use infiniteQuery to handle pagination, fetching, etc.
202
+ const { data: pageData, error: collectionError, fetchNextPage: fetchNextPage, refetch: refetch, hasNextPage: hasNextPage, isLoading: isLoadingPage, isFetching: isFetchingPage, isFetchingNextPage: isFetchingNextPage } = (0, $85cNH$useInfiniteQuery)([
203
+ "collection",
204
+ {
205
+ collectionUrl: collectionUrl
206
+ }
207
+ ], fetchCollection, {
208
+ enabled: !!(collectionUrl && identity?.id),
209
+ getNextPageParam: (lastPage)=>lastPage.next,
210
+ getPreviousPageParam: (firstPage)=>firstPage.prev
211
+ });
212
+ // Put all items together in a list (and dereference, if required).
213
+ const { loadedItems: items, isLoading: isLoadingItems, isFetching: isFetchingItems, errors: itemErrors } = $8281f3ce3b9d6123$var$useItemsFromPages(pageData?.pages ?? [], dereferenceItems);
214
+ const allErrors = (0, $577f4953dfa5de4f$export$e57ff0f701c44363)(collectionError).concat((0, $577f4953dfa5de4f$export$e57ff0f701c44363)(itemErrors));
215
+ const addItem = (0, $85cNH$useCallback)((item, shouldRefetch = true)=>{
216
+ queryClient.setQueryData([
217
+ "collection",
218
+ {
219
+ collectionUrl: collectionUrl
220
+ }
221
+ ], (oldData)=>{
222
+ if (!oldData) return oldData;
223
+ setTotalItems(totalItems && totalItems + 1);
224
+ // Destructure, so react knows, it needs to re-render the pages.
225
+ const pages = [
226
+ ...oldData.pages
227
+ ];
228
+ const firstPageItems = pages?.[0]?.orderedItems || pages?.[0]?.items || [];
229
+ firstPageItems.unshift(item);
230
+ oldData.pages = pages;
231
+ return oldData;
232
+ });
233
+ if (shouldRefetch) setTimeout(()=>queryClient.refetchQueries([
234
+ "collection",
235
+ {
236
+ collectionUrl: collectionUrl
237
+ }
238
+ ], {
239
+ active: true,
240
+ exact: true
241
+ }), typeof shouldRefetch === "number" ? shouldRefetch : 2000);
242
+ }, [
243
+ queryClient,
244
+ collectionUrl
245
+ ]);
246
+ const removeItem = (0, $85cNH$useCallback)((item, shouldRefetch = true)=>{
247
+ queryClient.setQueryData([
248
+ "collection",
249
+ {
250
+ collectionUrl: collectionUrl
251
+ }
252
+ ], (oldData)=>{
253
+ if (!oldData) return oldData;
254
+ setTotalItems(totalItems && totalItems - 1);
255
+ // Destructure, so react knows, it needs to re-render the pages array.
256
+ const pages = [
257
+ ...oldData.pages
258
+ ];
259
+ // Find the item in all pages and remove the item to be removed (either item.id or just item)
260
+ pages.forEach((page)=>{
261
+ if (page.orderedItems) page.orderedItems = page.orderedItems.filter((i)=>(i.id || i) !== (item.id || item));
262
+ else if (page.items) page.items = page.items.filter((i)=>(i.id || i) !== (item?.id || item));
263
+ });
264
+ oldData.pages = pages;
265
+ return oldData;
266
+ });
267
+ if (shouldRefetch) setTimeout(()=>queryClient.refetchQueries([
268
+ "collection",
269
+ {
270
+ collectionUrl: collectionUrl
271
+ }
272
+ ], {
273
+ active: true,
274
+ exact: true
275
+ }), typeof shouldRefetch === "number" ? shouldRefetch : 2000);
276
+ }, [
277
+ queryClient,
278
+ collectionUrl
279
+ ]);
280
+ // Live Updates
281
+ (0, $85cNH$useEffect)(()=>{
282
+ if (liveUpdates && collectionUrl) // Create ws that listens to collectionUri changes
283
+ (0, $85cNH$getOrCreateWsChannel)(dataProvider.fetch, collectionUrl).then((webSocket)=>{
284
+ webSocket.addEventListener("message", (e)=>{
285
+ const data = JSON.parse(e.data);
286
+ if (data.type === "Add") addItem(data.object, true);
287
+ else if (data.type === "Remove") removeItem(data.object, true);
288
+ });
289
+ webSocket.addEventListener("error", (e)=>{
290
+ setHasLiveUpdates({
291
+ status: "error",
292
+ error: e
293
+ });
294
+ // TODO: Retry after a while
295
+ });
296
+ webSocket.addEventListener("close", (e)=>{
297
+ if (!hasLiveUpdates.error) setHasLiveUpdates({
298
+ ...hasLiveUpdates,
299
+ status: "closed"
300
+ });
301
+ });
302
+ setHasLiveUpdates({
303
+ status: "connected",
304
+ webSocket: webSocket
305
+ });
306
+ }).catch(()=>{}); // If it fails, we won't receive live updates. But that's okay.
93
307
  }, [
94
- identity
308
+ collectionUrl,
309
+ liveUpdates,
310
+ dataProvider
95
311
  ]);
96
- const sparqlEndpoint = (0, $85cNH$useMemo)(()=>{
97
- if (identity?.webIdData) return identity?.webIdData?.endpoints?.["void:sparqlEndpoint"] || `${identity?.id}/sparql`;
312
+ return {
313
+ items: items,
314
+ totalItems: totalItems,
315
+ error: allErrors.length > 0 && allErrors,
316
+ refetch: refetch,
317
+ fetchNextPage: fetchNextPage,
318
+ addItem: addItem,
319
+ removeItem: removeItem,
320
+ hasNextPage: hasNextPage,
321
+ isLoading: isLoadingPage || isLoadingItems,
322
+ isFetching: isFetchingPage || isFetchingItems,
323
+ isFetchingNextPage: isFetchingNextPage,
324
+ url: collectionUrl,
325
+ hasLiveUpdates: hasLiveUpdates
326
+ };
327
+ };
328
+ var $8281f3ce3b9d6123$export$2e2bcd8739ae039 = $8281f3ce3b9d6123$var$useCollection;
329
+
330
+
331
+
332
+
333
+ /**
334
+ * Hook used internally by useInbox and useOutbox. This is not exported.
335
+ * @param webSocket WebSocket which allow to listen to the inbox or outbox
336
+ * @param existingActivities Partial list of activities already received in the inbox and outbox
337
+ */ const $600ca419166a1ded$var$useAwaitActivity = (webSocket, existingActivities)=>{
338
+ const dataProvider = (0, $85cNH$useDataProvider)();
339
+ // TODO Allow to pass an object, and automatically dereference it if required, like on the @semapps/activitypub matchActivity util
340
+ return (0, $85cNH$useCallback)((matchActivity, options = {})=>{
341
+ const { timeout: timeout = 30000, checkExistingActivities: checkExistingActivities = false } = options;
342
+ return new Promise((resolve, reject)=>{
343
+ if (webSocket) {
344
+ const onMessage = (event)=>{
345
+ const data = JSON.parse(event.data);
346
+ if (data.type === "Add") dataProvider.fetch(data.object).then(({ json: json })=>{
347
+ if (matchActivity(json)) {
348
+ webSocket.removeEventListener("message", onMessage);
349
+ return resolve(json);
350
+ }
351
+ });
352
+ };
353
+ webSocket.addEventListener("message", onMessage);
354
+ // TODO reconnect if connection closed
355
+ webSocket.addEventListener("error", (e)=>{
356
+ reject(e);
357
+ });
358
+ webSocket.addEventListener("close", (e)=>{
359
+ reject(new Error(`${e.reason} (Code: ${e.code})`));
360
+ });
361
+ // If a list of activities is already loaded, verify if there is a match
362
+ if (existingActivities && checkExistingActivities) for (const a of existingActivities){
363
+ if (typeof a !== "string") {
364
+ if (matchActivity(a)) {
365
+ webSocket.removeEventListener("message", onMessage);
366
+ return resolve(a);
367
+ }
368
+ }
369
+ }
370
+ setTimeout(()=>{
371
+ webSocket.removeEventListener("message", onMessage);
372
+ reject(new Error("Timeout"));
373
+ }, timeout);
374
+ } else throw new Error("WebSocket is not initialized !");
375
+ });
98
376
  }, [
99
- identity
377
+ webSocket,
378
+ existingActivities,
379
+ dataProvider
100
380
  ]);
381
+ };
382
+ var $600ca419166a1ded$export$2e2bcd8739ae039 = $600ca419166a1ded$var$useAwaitActivity;
383
+
384
+
385
+ /**
386
+ * Hook to fetch and post to the outbox of the logged user.
387
+ * Returns the same data as the useCollection hooks, plus:
388
+ * - `post`: a function to post a new activity in the user's outbox
389
+ * - `awaitActivity`: a function to wait for a certain activity to be posted
390
+ * - `owner`: the WebID of the outbox's owner
391
+ * See https://semapps.org/docs/frontend/activitypub-components#useoutbox for usage
392
+ * @param {UseCollectionOptions} options Defaults to `{ dereferenceItems: false, liveUpdates: true }`
393
+ */ const $4d1d40fdbcd30589$var$useOutbox = (options = {})=>{
394
+ const dataProvider = (0, $85cNH$useDataProvider)();
395
+ const { data: identity } = (0, $85cNH$useGetIdentity)();
396
+ const { url: url, hasLiveUpdates: hasLiveUpdates, items: items, ...rest } = (0, $8281f3ce3b9d6123$export$2e2bcd8739ae039)("outbox", options);
397
+ const awaitActivity = (0, $600ca419166a1ded$export$2e2bcd8739ae039)(hasLiveUpdates.webSocket, items);
101
398
  // Post an activity to the logged user's outbox and return its URI
102
399
  const post = (0, $85cNH$useCallback)(async (activity)=>{
103
- if (!outboxUrl) throw new Error("Cannot post to outbox before user identity is loaded. Please use the loaded argument of useOutbox");
104
- const token = localStorage.getItem("token");
105
- const { headers: headers } = await (0, $85cNH$fetchUtils).fetchJson(outboxUrl, {
400
+ if (!url) throw new Error("Cannot post to outbox before user identity is loaded. Please use the isLoading argument of useOutbox");
401
+ const { headers: headers } = await dataProvider.fetch(url, {
106
402
  method: "POST",
107
403
  body: JSON.stringify({
108
404
  "@context": "https://www.w3.org/ns/activitystreams",
109
405
  ...activity
110
- }),
111
- headers: new Headers({
112
- "Content-Type": "application/ld+json",
113
- Authorization: `Bearer ${token}`
114
406
  })
115
407
  });
116
408
  return headers.get("Location");
117
409
  }, [
118
- outboxUrl
119
- ]);
120
- const fetch = (0, $85cNH$useCallback)(async ()=>{
121
- if (!sparqlEndpoint || !outboxUrl) return;
122
- const token = localStorage.getItem("token");
123
- const blankNodesQuery = (0, $85cNH$buildBlankNodesQuery)([
124
- "as:object"
125
- ]);
126
- const query = `
127
- PREFIX as: <https://www.w3.org/ns/activitystreams#>
128
- CONSTRUCT {
129
- ?s1 ?p1 ?o1 .
130
- ${blankNodesQuery.construct}
131
- }
132
- WHERE {
133
- <${outboxUrl}> as:items ?s1 .
134
- ?s1 ?p1 ?o1 .
135
- ${blankNodesQuery.where}
136
- }
137
- `;
138
- const { json: json } = await (0, $85cNH$fetchUtils).fetchJson(sparqlEndpoint, {
139
- method: "POST",
140
- body: query,
141
- headers: new Headers({
142
- Accept: "application/ld+json",
143
- Authorization: token ? `Bearer ${token}` : undefined
144
- })
145
- });
146
- if (json["@graph"]) return json["@graph"];
147
- return null;
148
- }, [
149
- sparqlEndpoint,
150
- outboxUrl
410
+ url,
411
+ dataProvider
151
412
  ]);
152
413
  return {
414
+ url: url,
415
+ hasLiveUpdates: hasLiveUpdates,
416
+ items: items,
153
417
  post: post,
154
- fetch: fetch,
155
- url: outboxUrl,
156
- loaded: !!outboxUrl,
157
- owner: identity?.id
418
+ awaitActivity: awaitActivity,
419
+ owner: identity?.id,
420
+ ...rest
158
421
  };
159
422
  };
160
- var $712f7f004b5f345e$export$2e2bcd8739ae039 = $712f7f004b5f345e$var$useOutbox;
423
+ var $4d1d40fdbcd30589$export$2e2bcd8739ae039 = $4d1d40fdbcd30589$var$useOutbox;
161
424
 
162
425
 
163
426
 
@@ -262,7 +525,7 @@ const $3c17312a40ebf1ed$var$PostCommentForm = ({ context: context, placeholder:
262
525
  const userDataModel = (0, $85cNH$useDataModel)(userResource);
263
526
  const classes = $3c17312a40ebf1ed$var$useStyles();
264
527
  const notify = (0, $85cNH$useNotify)();
265
- const outbox = (0, $712f7f004b5f345e$export$2e2bcd8739ae039)();
528
+ const outbox = (0, $4d1d40fdbcd30589$export$2e2bcd8739ae039)();
266
529
  const [expanded, setExpanded] = (0, $85cNH$useState)(false);
267
530
  const [openAuth, setOpenAuth] = (0, $85cNH$useState)(false);
268
531
  const onSubmit = (0, $85cNH$useCallback)(async (values)=>{
@@ -522,143 +785,9 @@ var $be88b298220210d1$export$2e2bcd8739ae039 = $be88b298220210d1$var$CommentsLis
522
785
 
523
786
 
524
787
 
525
-
526
-
527
- const $e4e1b14e0441184d$export$e57ff0f701c44363 = (value)=>{
528
- // If the field is null-ish, we suppose there are no values.
529
- if (value === null || value === undefined) return [];
530
- // Return as is.
531
- if (Array.isArray(value)) return value;
532
- // Single value is made an array.
533
- return [
534
- value
535
- ];
536
- };
537
- var $e4e1b14e0441184d$export$2e2bcd8739ae039 = {
538
- arrayOf: $e4e1b14e0441184d$export$e57ff0f701c44363
539
- };
540
-
541
-
542
- const $c1e897431d8c5742$var$useCollection = (predicateOrUrl, options = {})=>{
543
- const { dereferenceItems: dereferenceItems = false } = options;
544
- const { data: identity } = (0, $85cNH$useGetIdentity)();
545
- const [items, setItems] = (0, $85cNH$useState)();
546
- const [totalItems, setTotalItems] = (0, $85cNH$useState)();
547
- const dataProvider = (0, $85cNH$useDataProvider)();
548
- const queryClient = (0, $85cNH$useQueryClient)();
549
- const collectionUrl = (0, $85cNH$useMemo)(()=>{
550
- if (predicateOrUrl) {
551
- if (predicateOrUrl.startsWith("http")) return predicateOrUrl;
552
- if (identity?.webIdData) return identity?.webIdData?.[predicateOrUrl];
553
- }
554
- }, [
555
- identity,
556
- predicateOrUrl
557
- ]);
558
- const fetchCollection = (0, $85cNH$useCallback)(async ({ pageParam: nextPageUrl })=>{
559
- let { json: json } = await dataProvider.fetch(nextPageUrl || collectionUrl);
560
- if (json.totalItems) setTotalItems(json.totalItems);
561
- if ((json.type === "OrderedCollection" || json.type === "Collection") && json.first) {
562
- if (json.first?.items) {
563
- if (json.first?.items.length === 0 && json.first?.next) // Special case where the first property is an object without items
564
- ({ json: json } = await dataProvider.fetch(json.first?.next));
565
- else json = json.first;
566
- } else // Fetch the first page
567
- ({ json: json } = await dataProvider.fetch(json.first));
568
- }
569
- // Force dereference of items
570
- if (dereferenceItems) {
571
- const itemPredicate = json.items ? "items" : "orderedItems";
572
- json[itemPredicate] = json[itemPredicate] && await Promise.all((0, $e4e1b14e0441184d$export$e57ff0f701c44363)(json[itemPredicate]).map(async (item)=>{
573
- if (typeof item === "string") {
574
- const { json: json } = await dataProvider.fetch(item);
575
- return json;
576
- }
577
- return item;
578
- }));
579
- }
580
- return json;
581
- }, [
582
- dataProvider,
583
- collectionUrl,
584
- identity,
585
- setTotalItems
586
- ]);
587
- const { data: data, error: error, fetchNextPage: fetchNextPage, refetch: refetch, hasNextPage: hasNextPage, isLoading: isLoading, isFetching: isFetching, isFetchingNextPage: isFetchingNextPage, status: status } = (0, $85cNH$useInfiniteQuery)([
588
- "Collection",
589
- {
590
- collectionUrl: collectionUrl
591
- }
592
- ], fetchCollection, {
593
- enabled: !!(collectionUrl && identity?.id),
594
- getNextPageParam: (lastPage)=>lastPage.next,
595
- getPreviousPageParam: (firstPage)=>firstPage.prev
596
- });
597
- (0, $85cNH$useEffect)(()=>{
598
- if (data?.pages) setItems([].concat(...data.pages.map((p)=>(0, $e4e1b14e0441184d$export$e57ff0f701c44363)(p.orderedItems || p.items))));
599
- }, [
600
- data,
601
- setItems
602
- ]);
603
- const addItem = (0, $85cNH$useCallback)((item)=>{
604
- setItems((oldItems)=>[
605
- ...oldItems,
606
- item
607
- ]);
608
- // TODO use queryClient.setQueryData to update items directly in react-query cache
609
- setTimeout(()=>queryClient.refetchQueries([
610
- "Collection",
611
- {
612
- collectionUrl: collectionUrl
613
- }
614
- ], {
615
- active: true,
616
- exact: true
617
- }), 2000);
618
- }, [
619
- setItems,
620
- queryClient,
621
- collectionUrl
622
- ]);
623
- const removeItem = (0, $85cNH$useCallback)((itemId)=>{
624
- setItems((oldItems)=>oldItems.filter((item)=>typeof item === "string" ? item !== itemId : item.id !== itemId));
625
- // TODO use queryClient.setQueryData to update items directly in react-query cache
626
- setTimeout(()=>queryClient.refetchQueries([
627
- "Collection",
628
- {
629
- collectionUrl: collectionUrl
630
- }
631
- ], {
632
- active: true,
633
- exact: true
634
- }), 2000);
635
- }, [
636
- setItems,
637
- queryClient,
638
- collectionUrl
639
- ]);
640
- return {
641
- items: items,
642
- totalItems: totalItems,
643
- error: error,
644
- refetch: refetch,
645
- fetchNextPage: fetchNextPage,
646
- hasNextPage: hasNextPage,
647
- isLoading: isLoading,
648
- isFetching: isFetching,
649
- isFetchingNextPage: isFetchingNextPage,
650
- status: status,
651
- addItem: addItem,
652
- removeItem: removeItem,
653
- url: collectionUrl
654
- };
655
- };
656
- var $c1e897431d8c5742$export$2e2bcd8739ae039 = $c1e897431d8c5742$var$useCollection;
657
-
658
-
659
788
  const $7ce737d4a1c88e63$var$CommentsField = ({ source: source, context: context, helperText: helperText, placeholder: placeholder, userResource: userResource, mentions: mentions })=>{
660
789
  const record = (0, $85cNH$useRecordContext)();
661
- const { items: comments, loading: loading, addItem: addItem, removeItem: removeItem } = (0, $c1e897431d8c5742$export$2e2bcd8739ae039)(record.replies);
790
+ const { items: comments, loading: loading, addItem: addItem, removeItem: removeItem } = (0, $8281f3ce3b9d6123$export$2e2bcd8739ae039)(record.replies);
662
791
  if (!userResource) throw new Error("No userResource defined for CommentsField");
663
792
  return /*#__PURE__*/ (0, $85cNH$jsxs)((0, $85cNH$Fragment), {
664
793
  children: [
@@ -694,7 +823,7 @@ var $7ce737d4a1c88e63$export$2e2bcd8739ae039 = $7ce737d4a1c88e63$var$CommentsFie
694
823
 
695
824
  const $d3be168cd1e7aaae$var$CollectionList = ({ collectionUrl: collectionUrl, resource: resource, children: children })=>{
696
825
  if ((0, $85cNH$react).Children.count(children) !== 1) throw new Error("<CollectionList> only accepts a single child");
697
- const { items: actorsUris } = (0, $c1e897431d8c5742$export$2e2bcd8739ae039)(collectionUrl);
826
+ const { items: actorsUris } = (0, $8281f3ce3b9d6123$export$2e2bcd8739ae039)(collectionUrl);
698
827
  const { data: data, isLoading: isLoading, isFetching: isFetching } = (0, $85cNH$useGetMany)(resource, {
699
828
  ids: Array.isArray(actorsUris) ? actorsUris : [
700
829
  actorsUris
@@ -737,84 +866,49 @@ var $ea214512ab1a2e8f$export$2e2bcd8739ae039 = $ea214512ab1a2e8f$var$ReferenceCo
737
866
 
738
867
 
739
868
 
740
- const $542b37cc25b8ccca$var$useInbox = ()=>{
869
+ /**
870
+ * Hook to fetch the inbox of the logged user.
871
+ * Returns the same data as the useCollection hooks, plus:
872
+ * - `awaitActivity`: a function to wait for a certain activity to be received
873
+ * - `owner`: the WebID of the inbox's owner
874
+ * See https://semapps.org/docs/frontend/activitypub-components#useinbox for usage
875
+ * @param {UseCollectionOptions} options Defaults to `{ dereferenceItems: false, liveUpdates: true }`
876
+ */ const $cc1d1cd0e97c63a2$var$useInbox = (options = {})=>{
741
877
  const { data: identity } = (0, $85cNH$useGetIdentity)();
742
- const inboxUrl = (0, $85cNH$useMemo)(()=>{
743
- if (identity?.webIdData) return identity?.webIdData?.inbox;
744
- }, [
745
- identity
746
- ]);
747
- const sparqlEndpoint = (0, $85cNH$useMemo)(()=>{
748
- if (identity?.webIdData) return identity?.webIdData?.endpoints?.["void:sparqlEndpoint"] || `${identity?.id}/sparql`;
749
- }, [
750
- identity
751
- ]);
752
- const fetch = (0, $85cNH$useCallback)(async ({ filters: filters })=>{
753
- if (!sparqlEndpoint || !inboxUrl) return;
754
- const token = localStorage.getItem("token");
755
- const blankNodesQuery = (0, $85cNH$buildBlankNodesQuery)([
756
- "as:object"
757
- ]);
758
- let filtersWhereQuery = "";
759
- if (filters) Object.keys(filters).forEach((predicate)=>{
760
- if (filters[predicate]) {
761
- const object = filters[predicate].startsWith("http") ? `<${filters[predicate]}>` : filters[predicate];
762
- filtersWhereQuery += `?s1 ${predicate} ${object} .`;
763
- }
764
- });
765
- const query = `
766
- PREFIX as: <https://www.w3.org/ns/activitystreams#>
767
- CONSTRUCT {
768
- ?s1 ?p1 ?o1 .
769
- ${blankNodesQuery.construct}
770
- }
771
- WHERE {
772
- <${inboxUrl}> as:items ?s1 .
773
- ?s1 ?p1 ?o1 .
774
- FILTER( (isIRI(?s1)) ) .
775
- ${filtersWhereQuery}
776
- ${blankNodesQuery.where}
777
- }
778
- `;
779
- const { json: json } = await (0, $85cNH$fetchUtils).fetchJson(sparqlEndpoint, {
780
- method: "POST",
781
- body: query,
782
- headers: new Headers({
783
- Accept: "application/ld+json",
784
- Authorization: token ? `Bearer ${token}` : undefined
785
- })
786
- });
787
- if (json["@graph"]) return json["@graph"];
788
- return null;
789
- }, [
790
- sparqlEndpoint,
791
- inboxUrl
792
- ]);
878
+ const { items: items, url: url, hasLiveUpdates: hasLiveUpdates, ...rest } = (0, $8281f3ce3b9d6123$export$2e2bcd8739ae039)("inbox", options);
879
+ const awaitActivity = (0, $600ca419166a1ded$export$2e2bcd8739ae039)(hasLiveUpdates.webSocket);
793
880
  return {
794
- fetch: fetch,
795
- url: inboxUrl,
796
- owner: identity?.id
881
+ items: items,
882
+ url: url,
883
+ hasLiveUpdates: hasLiveUpdates,
884
+ awaitActivity: awaitActivity,
885
+ owner: identity?.id,
886
+ ...rest
797
887
  };
798
888
  };
799
- var $542b37cc25b8ccca$export$2e2bcd8739ae039 = $542b37cc25b8ccca$var$useInbox;
889
+ var $cc1d1cd0e97c63a2$export$2e2bcd8739ae039 = $cc1d1cd0e97c63a2$var$useInbox;
800
890
 
801
891
 
802
892
 
803
893
 
804
- const $641e93142bcf5435$var$useNodeinfo = (host, rel = "http://nodeinfo.diaspora.software/ns/schema/2.1")=>{
894
+ const $6a227ddc2fd92a7a$var$useNodeinfo = (host, rel = "http://nodeinfo.diaspora.software/ns/schema/2.1")=>{
805
895
  const [schema, setSchema] = (0, $85cNH$useState)();
806
896
  (0, $85cNH$useEffect)(()=>{
807
897
  (async ()=>{
808
- const protocol = host.includes(":") ? "http" : "https"; // If the host has a port, we are likely on HTTP
809
- const nodeinfoUrl = `${protocol}://${host}/.well-known/nodeinfo`;
810
- try {
811
- const { json: links } = await (0, $85cNH$fetchUtils).fetchJson(nodeinfoUrl);
812
- // Accept any version of the nodeinfo protocol
813
- const link = links?.links?.find((l)=>l.rel === rel);
814
- const { json: json } = await (0, $85cNH$fetchUtils).fetchJson(link.href);
815
- setSchema(json);
816
- } catch (e) {
817
- // Do nothing if nodeinfo can't be fetched
898
+ if (host && rel) {
899
+ const protocol = host.includes(":") ? "http" : "https"; // If the host has a port, we are likely on HTTP
900
+ const nodeinfoUrl = `${protocol}://${host}/.well-known/nodeinfo`;
901
+ try {
902
+ const { json: links } = await (0, $85cNH$fetchUtils).fetchJson(nodeinfoUrl);
903
+ // Accept any version of the nodeinfo protocol
904
+ const link = links?.links?.find((l)=>l.rel === rel);
905
+ if (link) {
906
+ const { json: json } = await (0, $85cNH$fetchUtils).fetchJson(link.href);
907
+ setSchema(json);
908
+ }
909
+ } catch (e) {
910
+ // Do nothing if nodeinfo can't be fetched
911
+ }
818
912
  }
819
913
  })();
820
914
  }, [
@@ -824,7 +918,7 @@ const $641e93142bcf5435$var$useNodeinfo = (host, rel = "http://nodeinfo.diaspora
824
918
  ]);
825
919
  return schema;
826
920
  };
827
- var $641e93142bcf5435$export$2e2bcd8739ae039 = $641e93142bcf5435$var$useNodeinfo;
921
+ var $6a227ddc2fd92a7a$export$2e2bcd8739ae039 = $6a227ddc2fd92a7a$var$useNodeinfo;
828
922
 
829
923
 
830
924
 
@@ -1018,5 +1112,5 @@ var $51cccd331ea8b13d$export$2e2bcd8739ae039 = $51cccd331ea8b13d$var$useMentions
1018
1112
 
1019
1113
 
1020
1114
 
1021
- export {$7ce737d4a1c88e63$export$2e2bcd8739ae039 as CommentsField, $d3be168cd1e7aaae$export$2e2bcd8739ae039 as CollectionList, $ea214512ab1a2e8f$export$2e2bcd8739ae039 as ReferenceCollectionField, $c1e897431d8c5742$export$2e2bcd8739ae039 as useCollection, $542b37cc25b8ccca$export$2e2bcd8739ae039 as useInbox, $641e93142bcf5435$export$2e2bcd8739ae039 as useNodeinfo, $712f7f004b5f345e$export$2e2bcd8739ae039 as useOutbox, $2514c63dc8f4867c$export$2e2bcd8739ae039 as useWebfinger, $51cccd331ea8b13d$export$2e2bcd8739ae039 as useMentions, $338f387df48a40d7$export$1ec8e53e7d982d22 as ACTIVITY_TYPES, $338f387df48a40d7$export$9649665d7ccb0dc2 as ACTOR_TYPES, $338f387df48a40d7$export$c49cfb2681596b20 as OBJECT_TYPES, $338f387df48a40d7$export$4d8d554031975581 as PUBLIC_URI};
1115
+ export {$7ce737d4a1c88e63$export$2e2bcd8739ae039 as CommentsField, $d3be168cd1e7aaae$export$2e2bcd8739ae039 as CollectionList, $ea214512ab1a2e8f$export$2e2bcd8739ae039 as ReferenceCollectionField, $8281f3ce3b9d6123$export$2e2bcd8739ae039 as useCollection, $cc1d1cd0e97c63a2$export$2e2bcd8739ae039 as useInbox, $6a227ddc2fd92a7a$export$2e2bcd8739ae039 as useNodeinfo, $4d1d40fdbcd30589$export$2e2bcd8739ae039 as useOutbox, $2514c63dc8f4867c$export$2e2bcd8739ae039 as useWebfinger, $51cccd331ea8b13d$export$2e2bcd8739ae039 as useMentions, $338f387df48a40d7$export$1ec8e53e7d982d22 as ACTIVITY_TYPES, $338f387df48a40d7$export$9649665d7ccb0dc2 as ACTOR_TYPES, $338f387df48a40d7$export$c49cfb2681596b20 as OBJECT_TYPES, $338f387df48a40d7$export$4d8d554031975581 as PUBLIC_URI};
1022
1116
  //# sourceMappingURL=index.es.js.map