@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.cjs.js CHANGED
@@ -8,10 +8,10 @@ var $583VT$muistylesmakeStyles = require("@mui/styles/makeStyles");
8
8
  var $583VT$muiiconsmaterialSend = require("@mui/icons-material/Send");
9
9
  var $583VT$semappssemanticdataprovider = require("@semapps/semantic-data-provider");
10
10
  var $583VT$semappsauthprovider = require("@semapps/auth-provider");
11
+ var $583VT$reactquery = require("react-query");
11
12
  var $583VT$tiptapcore = require("@tiptap/core");
12
13
  var $583VT$tiptapextensionmention = require("@tiptap/extension-mention");
13
14
  var $583VT$semappsfieldcomponents = require("@semapps/field-components");
14
- var $583VT$reactquery = require("react-query");
15
15
  var $583VT$tiptapreact = require("@tiptap/react");
16
16
  var $583VT$tippyjs = require("tippy.js");
17
17
 
@@ -27,10 +27,10 @@ function $parcel$interopDefault(a) {
27
27
  $parcel$export(module.exports, "CommentsField", () => $2e5504cc4159ca8d$export$2e2bcd8739ae039);
28
28
  $parcel$export(module.exports, "CollectionList", () => $505d598a33288aad$export$2e2bcd8739ae039);
29
29
  $parcel$export(module.exports, "ReferenceCollectionField", () => $b0c94a9bdea99da5$export$2e2bcd8739ae039);
30
- $parcel$export(module.exports, "useCollection", () => $2b75a2f49c9ef165$export$2e2bcd8739ae039);
31
- $parcel$export(module.exports, "useInbox", () => $97664763db3c0a46$export$2e2bcd8739ae039);
32
- $parcel$export(module.exports, "useNodeinfo", () => $30fd77858150739b$export$2e2bcd8739ae039);
33
- $parcel$export(module.exports, "useOutbox", () => $decfdd34cc00b80e$export$2e2bcd8739ae039);
30
+ $parcel$export(module.exports, "useCollection", () => $5ca5f7e9fc1c3544$export$2e2bcd8739ae039);
31
+ $parcel$export(module.exports, "useInbox", () => $486f741c94cd8f74$export$2e2bcd8739ae039);
32
+ $parcel$export(module.exports, "useNodeinfo", () => $59fc2d2cba62bd8e$export$2e2bcd8739ae039);
33
+ $parcel$export(module.exports, "useOutbox", () => $456aea3814dded7d$export$2e2bcd8739ae039);
34
34
  $parcel$export(module.exports, "useWebfinger", () => $5b61553556e35016$export$2e2bcd8739ae039);
35
35
  $parcel$export(module.exports, "useMentions", () => $968ea07fb81eda0b$export$2e2bcd8739ae039);
36
36
  $parcel$export(module.exports, "ACTIVITY_TYPES", () => $ebea823cebb2f44b$export$1ec8e53e7d982d22);
@@ -76,7 +76,7 @@ const $ebea823cebb2f44b$export$1ec8e53e7d982d22 = {
76
76
  REMOVE: "Remove",
77
77
  TENTATIVE_REJECT: "TentativeReject",
78
78
  TENTATIVE_ACCEPT: "TentativeAccept",
79
- TRAVAL: "Travel",
79
+ TRAVEL: "Travel",
80
80
  UNDO: "Undo",
81
81
  UPDATE: "Update",
82
82
  VIEW: "View"
@@ -108,78 +108,341 @@ const $ebea823cebb2f44b$export$4d8d554031975581 = "https://www.w3.org/ns/activit
108
108
 
109
109
 
110
110
 
111
- const $decfdd34cc00b80e$var$useOutbox = ()=>{
111
+
112
+
113
+
114
+ const $03510abb28fd3d8a$export$e57ff0f701c44363 = (value)=>{
115
+ // If the field is null-ish, we suppose there are no values.
116
+ if (value === null || value === undefined) return [];
117
+ // Return as is.
118
+ if (Array.isArray(value)) return value;
119
+ // Single value is made an array.
120
+ return [
121
+ value
122
+ ];
123
+ };
124
+ var $03510abb28fd3d8a$export$2e2bcd8739ae039 = {
125
+ arrayOf: $03510abb28fd3d8a$export$e57ff0f701c44363
126
+ };
127
+ const $03510abb28fd3d8a$export$34aed805e991a647 = (iterable, predicate)=>{
128
+ const seen = new Set();
129
+ return iterable.filter((item)=>{
130
+ const key = predicate(item);
131
+ if (seen.has(key)) return false;
132
+ seen.add(key);
133
+ return true;
134
+ });
135
+ };
136
+
137
+
138
+ const $5ca5f7e9fc1c3544$var$useItemsFromPages = (pages, dereferenceItems)=>{
139
+ const dataProvider = (0, $583VT$reactadmin.useDataProvider)();
140
+ const items = (0, $583VT$react.useMemo)(()=>pages.flatMap((p)=>(0, $03510abb28fd3d8a$export$e57ff0f701c44363)(p.orderedItems || p.items)), [
141
+ pages
142
+ ]);
143
+ // We will force dereference, if some items are not URI string references.
144
+ const shouldDereference = (0, $583VT$react.useMemo)(()=>{
145
+ return dereferenceItems || items.some((item)=>typeof item !== "string");
146
+ }, [
147
+ dereferenceItems,
148
+ items
149
+ ]);
150
+ // Dereference all items, if necessary (even if shouldDereference is false, the hook needs to be called).
151
+ const itemQueries = (0, $583VT$reactquery.useQueries)(!shouldDereference ? [] : items.filter((item)=>typeof item === "string").map((itemUri)=>({
152
+ queryKey: [
153
+ "resource",
154
+ itemUri
155
+ ],
156
+ queryFn: async ()=>(await dataProvider.fetch(itemUri)).json,
157
+ staleTime: Infinity
158
+ })));
159
+ if (!shouldDereference) return {
160
+ loadedItems: items,
161
+ isLoading: false,
162
+ isFetching: false
163
+ };
164
+ // Put all loaded items together (might be dereferenced already, so concatenate).
165
+ const loadedItems = items.filter((item)=>typeof item !== "string").concat(itemQueries.flatMap((itemQuery)=>{
166
+ return itemQuery.isSuccess && itemQuery.data || [];
167
+ }));
168
+ const errors = itemQueries.filter((q)=>q.error);
169
+ return {
170
+ loadedItems: loadedItems,
171
+ isLoading: itemQueries.some((q)=>q.isLoading),
172
+ isFetching: itemQueries.some((q)=>q.isFetching),
173
+ errors: errors.length > 0 ? errors : undefined
174
+ };
175
+ };
176
+ /**
177
+ * Subscribe a collection. Supports pagination.
178
+ * @param predicateOrUrl The collection URI or the predicate to get the collection URI from the identity (webId).
179
+ * @param {UseCollectionOptions} options Defaults to `{ dereferenceItems: false, liveUpdates: true }`
180
+ */ const $5ca5f7e9fc1c3544$var$useCollection = (predicateOrUrl, options = {})=>{
181
+ const { dereferenceItems: dereferenceItems = false, liveUpdates: liveUpdates = true } = options;
112
182
  const { data: identity } = (0, $583VT$reactadmin.useGetIdentity)();
113
- const outboxUrl = (0, $583VT$react.useMemo)(()=>{
114
- if (identity?.webIdData) return identity?.webIdData?.outbox;
183
+ const [totalItems, setTotalItems] = (0, $583VT$react.useState)();
184
+ const queryClient = (0, $583VT$reactquery.useQueryClient)();
185
+ const [hasLiveUpdates, setHasLiveUpdates] = (0, $583VT$react.useState)({
186
+ status: "connecting"
187
+ });
188
+ const dataProvider = (0, $583VT$reactadmin.useDataProvider)();
189
+ // Get collectionUrl from webId predicate or URL.
190
+ const collectionUrl = (0, $583VT$react.useMemo)(()=>{
191
+ if (predicateOrUrl) {
192
+ if (predicateOrUrl.startsWith("http")) return predicateOrUrl;
193
+ if (identity?.webIdData) return identity?.webIdData?.[predicateOrUrl];
194
+ }
195
+ return undefined;
196
+ // throw new Error(`No URL available for useCollection: ${predicateOrUrl}.`);
197
+ }, [
198
+ identity,
199
+ predicateOrUrl
200
+ ]);
201
+ // Fetch page of collection item references (if pageParam provided)
202
+ // or default to `collectionUrl` (which should give you the first page).
203
+ const fetchCollection = (0, $583VT$react.useCallback)(async ({ pageParam: nextPageUrl })=>{
204
+ // Fetch page or first page (collectionUrl)
205
+ let { json: json } = await dataProvider.fetch(nextPageUrl || collectionUrl);
206
+ if (json.totalItems) setTotalItems(json.totalItems);
207
+ // If first page, handle this here.
208
+ if ((json.type === "OrderedCollection" || json.type === "Collection") && json.first) {
209
+ if (json.first?.items) {
210
+ if (json.first?.items.length === 0 && json.first?.next) // Special case where the first property is an object without items
211
+ ({ json: json } = await dataProvider.fetch(json.first?.next));
212
+ else json = json.first;
213
+ } else // Fetch the first page
214
+ ({ json: json } = await dataProvider.fetch(json.first));
215
+ }
216
+ return json;
115
217
  }, [
116
- identity
218
+ dataProvider,
219
+ collectionUrl,
220
+ identity,
221
+ setTotalItems
117
222
  ]);
118
- const sparqlEndpoint = (0, $583VT$react.useMemo)(()=>{
119
- if (identity?.webIdData) return identity?.webIdData?.endpoints?.["void:sparqlEndpoint"] || `${identity?.id}/sparql`;
223
+ // Use infiniteQuery to handle pagination, fetching, etc.
224
+ const { data: pageData, error: collectionError, fetchNextPage: fetchNextPage, refetch: refetch, hasNextPage: hasNextPage, isLoading: isLoadingPage, isFetching: isFetchingPage, isFetchingNextPage: isFetchingNextPage } = (0, $583VT$reactquery.useInfiniteQuery)([
225
+ "collection",
226
+ {
227
+ collectionUrl: collectionUrl
228
+ }
229
+ ], fetchCollection, {
230
+ enabled: !!(collectionUrl && identity?.id),
231
+ getNextPageParam: (lastPage)=>lastPage.next,
232
+ getPreviousPageParam: (firstPage)=>firstPage.prev
233
+ });
234
+ // Put all items together in a list (and dereference, if required).
235
+ const { loadedItems: items, isLoading: isLoadingItems, isFetching: isFetchingItems, errors: itemErrors } = $5ca5f7e9fc1c3544$var$useItemsFromPages(pageData?.pages ?? [], dereferenceItems);
236
+ const allErrors = (0, $03510abb28fd3d8a$export$e57ff0f701c44363)(collectionError).concat((0, $03510abb28fd3d8a$export$e57ff0f701c44363)(itemErrors));
237
+ const addItem = (0, $583VT$react.useCallback)((item, shouldRefetch = true)=>{
238
+ queryClient.setQueryData([
239
+ "collection",
240
+ {
241
+ collectionUrl: collectionUrl
242
+ }
243
+ ], (oldData)=>{
244
+ if (!oldData) return oldData;
245
+ setTotalItems(totalItems && totalItems + 1);
246
+ // Destructure, so react knows, it needs to re-render the pages.
247
+ const pages = [
248
+ ...oldData.pages
249
+ ];
250
+ const firstPageItems = pages?.[0]?.orderedItems || pages?.[0]?.items || [];
251
+ firstPageItems.unshift(item);
252
+ oldData.pages = pages;
253
+ return oldData;
254
+ });
255
+ if (shouldRefetch) setTimeout(()=>queryClient.refetchQueries([
256
+ "collection",
257
+ {
258
+ collectionUrl: collectionUrl
259
+ }
260
+ ], {
261
+ active: true,
262
+ exact: true
263
+ }), typeof shouldRefetch === "number" ? shouldRefetch : 2000);
264
+ }, [
265
+ queryClient,
266
+ collectionUrl
267
+ ]);
268
+ const removeItem = (0, $583VT$react.useCallback)((item, shouldRefetch = true)=>{
269
+ queryClient.setQueryData([
270
+ "collection",
271
+ {
272
+ collectionUrl: collectionUrl
273
+ }
274
+ ], (oldData)=>{
275
+ if (!oldData) return oldData;
276
+ setTotalItems(totalItems && totalItems - 1);
277
+ // Destructure, so react knows, it needs to re-render the pages array.
278
+ const pages = [
279
+ ...oldData.pages
280
+ ];
281
+ // Find the item in all pages and remove the item to be removed (either item.id or just item)
282
+ pages.forEach((page)=>{
283
+ if (page.orderedItems) page.orderedItems = page.orderedItems.filter((i)=>(i.id || i) !== (item.id || item));
284
+ else if (page.items) page.items = page.items.filter((i)=>(i.id || i) !== (item?.id || item));
285
+ });
286
+ oldData.pages = pages;
287
+ return oldData;
288
+ });
289
+ if (shouldRefetch) setTimeout(()=>queryClient.refetchQueries([
290
+ "collection",
291
+ {
292
+ collectionUrl: collectionUrl
293
+ }
294
+ ], {
295
+ active: true,
296
+ exact: true
297
+ }), typeof shouldRefetch === "number" ? shouldRefetch : 2000);
298
+ }, [
299
+ queryClient,
300
+ collectionUrl
301
+ ]);
302
+ // Live Updates
303
+ (0, $583VT$react.useEffect)(()=>{
304
+ if (liveUpdates && collectionUrl) // Create ws that listens to collectionUri changes
305
+ (0, $583VT$semappssemanticdataprovider.getOrCreateWsChannel)(dataProvider.fetch, collectionUrl).then((webSocket)=>{
306
+ webSocket.addEventListener("message", (e)=>{
307
+ const data = JSON.parse(e.data);
308
+ if (data.type === "Add") addItem(data.object, true);
309
+ else if (data.type === "Remove") removeItem(data.object, true);
310
+ });
311
+ webSocket.addEventListener("error", (e)=>{
312
+ setHasLiveUpdates({
313
+ status: "error",
314
+ error: e
315
+ });
316
+ // TODO: Retry after a while
317
+ });
318
+ webSocket.addEventListener("close", (e)=>{
319
+ if (!hasLiveUpdates.error) setHasLiveUpdates({
320
+ ...hasLiveUpdates,
321
+ status: "closed"
322
+ });
323
+ });
324
+ setHasLiveUpdates({
325
+ status: "connected",
326
+ webSocket: webSocket
327
+ });
328
+ }).catch(()=>{}); // If it fails, we won't receive live updates. But that's okay.
329
+ }, [
330
+ collectionUrl,
331
+ liveUpdates,
332
+ dataProvider
333
+ ]);
334
+ return {
335
+ items: items,
336
+ totalItems: totalItems,
337
+ error: allErrors.length > 0 && allErrors,
338
+ refetch: refetch,
339
+ fetchNextPage: fetchNextPage,
340
+ addItem: addItem,
341
+ removeItem: removeItem,
342
+ hasNextPage: hasNextPage,
343
+ isLoading: isLoadingPage || isLoadingItems,
344
+ isFetching: isFetchingPage || isFetchingItems,
345
+ isFetchingNextPage: isFetchingNextPage,
346
+ url: collectionUrl,
347
+ hasLiveUpdates: hasLiveUpdates
348
+ };
349
+ };
350
+ var $5ca5f7e9fc1c3544$export$2e2bcd8739ae039 = $5ca5f7e9fc1c3544$var$useCollection;
351
+
352
+
353
+
354
+
355
+ /**
356
+ * Hook used internally by useInbox and useOutbox. This is not exported.
357
+ * @param webSocket WebSocket which allow to listen to the inbox or outbox
358
+ * @param existingActivities Partial list of activities already received in the inbox and outbox
359
+ */ const $5e70f9d0635e25dd$var$useAwaitActivity = (webSocket, existingActivities)=>{
360
+ const dataProvider = (0, $583VT$reactadmin.useDataProvider)();
361
+ // TODO Allow to pass an object, and automatically dereference it if required, like on the @semapps/activitypub matchActivity util
362
+ return (0, $583VT$react.useCallback)((matchActivity, options = {})=>{
363
+ const { timeout: timeout = 30000, checkExistingActivities: checkExistingActivities = false } = options;
364
+ return new Promise((resolve, reject)=>{
365
+ if (webSocket) {
366
+ const onMessage = (event)=>{
367
+ const data = JSON.parse(event.data);
368
+ if (data.type === "Add") dataProvider.fetch(data.object).then(({ json: json })=>{
369
+ if (matchActivity(json)) {
370
+ webSocket.removeEventListener("message", onMessage);
371
+ return resolve(json);
372
+ }
373
+ });
374
+ };
375
+ webSocket.addEventListener("message", onMessage);
376
+ // TODO reconnect if connection closed
377
+ webSocket.addEventListener("error", (e)=>{
378
+ reject(e);
379
+ });
380
+ webSocket.addEventListener("close", (e)=>{
381
+ reject(new Error(`${e.reason} (Code: ${e.code})`));
382
+ });
383
+ // If a list of activities is already loaded, verify if there is a match
384
+ if (existingActivities && checkExistingActivities) for (const a of existingActivities){
385
+ if (typeof a !== "string") {
386
+ if (matchActivity(a)) {
387
+ webSocket.removeEventListener("message", onMessage);
388
+ return resolve(a);
389
+ }
390
+ }
391
+ }
392
+ setTimeout(()=>{
393
+ webSocket.removeEventListener("message", onMessage);
394
+ reject(new Error("Timeout"));
395
+ }, timeout);
396
+ } else throw new Error("WebSocket is not initialized !");
397
+ });
120
398
  }, [
121
- identity
399
+ webSocket,
400
+ existingActivities,
401
+ dataProvider
122
402
  ]);
403
+ };
404
+ var $5e70f9d0635e25dd$export$2e2bcd8739ae039 = $5e70f9d0635e25dd$var$useAwaitActivity;
405
+
406
+
407
+ /**
408
+ * Hook to fetch and post to the outbox of the logged user.
409
+ * Returns the same data as the useCollection hooks, plus:
410
+ * - `post`: a function to post a new activity in the user's outbox
411
+ * - `awaitActivity`: a function to wait for a certain activity to be posted
412
+ * - `owner`: the WebID of the outbox's owner
413
+ * See https://semapps.org/docs/frontend/activitypub-components#useoutbox for usage
414
+ * @param {UseCollectionOptions} options Defaults to `{ dereferenceItems: false, liveUpdates: true }`
415
+ */ const $456aea3814dded7d$var$useOutbox = (options = {})=>{
416
+ const dataProvider = (0, $583VT$reactadmin.useDataProvider)();
417
+ const { data: identity } = (0, $583VT$reactadmin.useGetIdentity)();
418
+ const { url: url, hasLiveUpdates: hasLiveUpdates, items: items, ...rest } = (0, $5ca5f7e9fc1c3544$export$2e2bcd8739ae039)("outbox", options);
419
+ const awaitActivity = (0, $5e70f9d0635e25dd$export$2e2bcd8739ae039)(hasLiveUpdates.webSocket, items);
123
420
  // Post an activity to the logged user's outbox and return its URI
124
421
  const post = (0, $583VT$react.useCallback)(async (activity)=>{
125
- if (!outboxUrl) throw new Error("Cannot post to outbox before user identity is loaded. Please use the loaded argument of useOutbox");
126
- const token = localStorage.getItem("token");
127
- const { headers: headers } = await (0, $583VT$reactadmin.fetchUtils).fetchJson(outboxUrl, {
422
+ if (!url) throw new Error("Cannot post to outbox before user identity is loaded. Please use the isLoading argument of useOutbox");
423
+ const { headers: headers } = await dataProvider.fetch(url, {
128
424
  method: "POST",
129
425
  body: JSON.stringify({
130
426
  "@context": "https://www.w3.org/ns/activitystreams",
131
427
  ...activity
132
- }),
133
- headers: new Headers({
134
- "Content-Type": "application/ld+json",
135
- Authorization: `Bearer ${token}`
136
428
  })
137
429
  });
138
430
  return headers.get("Location");
139
431
  }, [
140
- outboxUrl
141
- ]);
142
- const fetch = (0, $583VT$react.useCallback)(async ()=>{
143
- if (!sparqlEndpoint || !outboxUrl) return;
144
- const token = localStorage.getItem("token");
145
- const blankNodesQuery = (0, $583VT$semappssemanticdataprovider.buildBlankNodesQuery)([
146
- "as:object"
147
- ]);
148
- const query = `
149
- PREFIX as: <https://www.w3.org/ns/activitystreams#>
150
- CONSTRUCT {
151
- ?s1 ?p1 ?o1 .
152
- ${blankNodesQuery.construct}
153
- }
154
- WHERE {
155
- <${outboxUrl}> as:items ?s1 .
156
- ?s1 ?p1 ?o1 .
157
- ${blankNodesQuery.where}
158
- }
159
- `;
160
- const { json: json } = await (0, $583VT$reactadmin.fetchUtils).fetchJson(sparqlEndpoint, {
161
- method: "POST",
162
- body: query,
163
- headers: new Headers({
164
- Accept: "application/ld+json",
165
- Authorization: token ? `Bearer ${token}` : undefined
166
- })
167
- });
168
- if (json["@graph"]) return json["@graph"];
169
- return null;
170
- }, [
171
- sparqlEndpoint,
172
- outboxUrl
432
+ url,
433
+ dataProvider
173
434
  ]);
174
435
  return {
436
+ url: url,
437
+ hasLiveUpdates: hasLiveUpdates,
438
+ items: items,
175
439
  post: post,
176
- fetch: fetch,
177
- url: outboxUrl,
178
- loaded: !!outboxUrl,
179
- owner: identity?.id
440
+ awaitActivity: awaitActivity,
441
+ owner: identity?.id,
442
+ ...rest
180
443
  };
181
444
  };
182
- var $decfdd34cc00b80e$export$2e2bcd8739ae039 = $decfdd34cc00b80e$var$useOutbox;
445
+ var $456aea3814dded7d$export$2e2bcd8739ae039 = $456aea3814dded7d$var$useOutbox;
183
446
 
184
447
 
185
448
 
@@ -284,7 +547,7 @@ const $703eba3e46be7ee5$var$PostCommentForm = ({ context: context, placeholder:
284
547
  const userDataModel = (0, $583VT$semappssemanticdataprovider.useDataModel)(userResource);
285
548
  const classes = $703eba3e46be7ee5$var$useStyles();
286
549
  const notify = (0, $583VT$reactadmin.useNotify)();
287
- const outbox = (0, $decfdd34cc00b80e$export$2e2bcd8739ae039)();
550
+ const outbox = (0, $456aea3814dded7d$export$2e2bcd8739ae039)();
288
551
  const [expanded, setExpanded] = (0, $583VT$react.useState)(false);
289
552
  const [openAuth, setOpenAuth] = (0, $583VT$react.useState)(false);
290
553
  const onSubmit = (0, $583VT$react.useCallback)(async (values)=>{
@@ -544,143 +807,9 @@ var $d68cd57b2d06b6d5$export$2e2bcd8739ae039 = $d68cd57b2d06b6d5$var$CommentsLis
544
807
 
545
808
 
546
809
 
547
-
548
-
549
- const $3ff23aa25753c478$export$e57ff0f701c44363 = (value)=>{
550
- // If the field is null-ish, we suppose there are no values.
551
- if (value === null || value === undefined) return [];
552
- // Return as is.
553
- if (Array.isArray(value)) return value;
554
- // Single value is made an array.
555
- return [
556
- value
557
- ];
558
- };
559
- var $3ff23aa25753c478$export$2e2bcd8739ae039 = {
560
- arrayOf: $3ff23aa25753c478$export$e57ff0f701c44363
561
- };
562
-
563
-
564
- const $2b75a2f49c9ef165$var$useCollection = (predicateOrUrl, options = {})=>{
565
- const { dereferenceItems: dereferenceItems = false } = options;
566
- const { data: identity } = (0, $583VT$reactadmin.useGetIdentity)();
567
- const [items, setItems] = (0, $583VT$react.useState)();
568
- const [totalItems, setTotalItems] = (0, $583VT$react.useState)();
569
- const dataProvider = (0, $583VT$reactadmin.useDataProvider)();
570
- const queryClient = (0, $583VT$reactquery.useQueryClient)();
571
- const collectionUrl = (0, $583VT$react.useMemo)(()=>{
572
- if (predicateOrUrl) {
573
- if (predicateOrUrl.startsWith("http")) return predicateOrUrl;
574
- if (identity?.webIdData) return identity?.webIdData?.[predicateOrUrl];
575
- }
576
- }, [
577
- identity,
578
- predicateOrUrl
579
- ]);
580
- const fetchCollection = (0, $583VT$react.useCallback)(async ({ pageParam: nextPageUrl })=>{
581
- let { json: json } = await dataProvider.fetch(nextPageUrl || collectionUrl);
582
- if (json.totalItems) setTotalItems(json.totalItems);
583
- if ((json.type === "OrderedCollection" || json.type === "Collection") && json.first) {
584
- if (json.first?.items) {
585
- if (json.first?.items.length === 0 && json.first?.next) // Special case where the first property is an object without items
586
- ({ json: json } = await dataProvider.fetch(json.first?.next));
587
- else json = json.first;
588
- } else // Fetch the first page
589
- ({ json: json } = await dataProvider.fetch(json.first));
590
- }
591
- // Force dereference of items
592
- if (dereferenceItems) {
593
- const itemPredicate = json.items ? "items" : "orderedItems";
594
- json[itemPredicate] = json[itemPredicate] && await Promise.all((0, $3ff23aa25753c478$export$e57ff0f701c44363)(json[itemPredicate]).map(async (item)=>{
595
- if (typeof item === "string") {
596
- const { json: json } = await dataProvider.fetch(item);
597
- return json;
598
- }
599
- return item;
600
- }));
601
- }
602
- return json;
603
- }, [
604
- dataProvider,
605
- collectionUrl,
606
- identity,
607
- setTotalItems
608
- ]);
609
- const { data: data, error: error, fetchNextPage: fetchNextPage, refetch: refetch, hasNextPage: hasNextPage, isLoading: isLoading, isFetching: isFetching, isFetchingNextPage: isFetchingNextPage, status: status } = (0, $583VT$reactquery.useInfiniteQuery)([
610
- "Collection",
611
- {
612
- collectionUrl: collectionUrl
613
- }
614
- ], fetchCollection, {
615
- enabled: !!(collectionUrl && identity?.id),
616
- getNextPageParam: (lastPage)=>lastPage.next,
617
- getPreviousPageParam: (firstPage)=>firstPage.prev
618
- });
619
- (0, $583VT$react.useEffect)(()=>{
620
- if (data?.pages) setItems([].concat(...data.pages.map((p)=>(0, $3ff23aa25753c478$export$e57ff0f701c44363)(p.orderedItems || p.items))));
621
- }, [
622
- data,
623
- setItems
624
- ]);
625
- const addItem = (0, $583VT$react.useCallback)((item)=>{
626
- setItems((oldItems)=>[
627
- ...oldItems,
628
- item
629
- ]);
630
- // TODO use queryClient.setQueryData to update items directly in react-query cache
631
- setTimeout(()=>queryClient.refetchQueries([
632
- "Collection",
633
- {
634
- collectionUrl: collectionUrl
635
- }
636
- ], {
637
- active: true,
638
- exact: true
639
- }), 2000);
640
- }, [
641
- setItems,
642
- queryClient,
643
- collectionUrl
644
- ]);
645
- const removeItem = (0, $583VT$react.useCallback)((itemId)=>{
646
- setItems((oldItems)=>oldItems.filter((item)=>typeof item === "string" ? item !== itemId : item.id !== itemId));
647
- // TODO use queryClient.setQueryData to update items directly in react-query cache
648
- setTimeout(()=>queryClient.refetchQueries([
649
- "Collection",
650
- {
651
- collectionUrl: collectionUrl
652
- }
653
- ], {
654
- active: true,
655
- exact: true
656
- }), 2000);
657
- }, [
658
- setItems,
659
- queryClient,
660
- collectionUrl
661
- ]);
662
- return {
663
- items: items,
664
- totalItems: totalItems,
665
- error: error,
666
- refetch: refetch,
667
- fetchNextPage: fetchNextPage,
668
- hasNextPage: hasNextPage,
669
- isLoading: isLoading,
670
- isFetching: isFetching,
671
- isFetchingNextPage: isFetchingNextPage,
672
- status: status,
673
- addItem: addItem,
674
- removeItem: removeItem,
675
- url: collectionUrl
676
- };
677
- };
678
- var $2b75a2f49c9ef165$export$2e2bcd8739ae039 = $2b75a2f49c9ef165$var$useCollection;
679
-
680
-
681
810
  const $2e5504cc4159ca8d$var$CommentsField = ({ source: source, context: context, helperText: helperText, placeholder: placeholder, userResource: userResource, mentions: mentions })=>{
682
811
  const record = (0, $583VT$reactadmin.useRecordContext)();
683
- const { items: comments, loading: loading, addItem: addItem, removeItem: removeItem } = (0, $2b75a2f49c9ef165$export$2e2bcd8739ae039)(record.replies);
812
+ const { items: comments, loading: loading, addItem: addItem, removeItem: removeItem } = (0, $5ca5f7e9fc1c3544$export$2e2bcd8739ae039)(record.replies);
684
813
  if (!userResource) throw new Error("No userResource defined for CommentsField");
685
814
  return /*#__PURE__*/ (0, $583VT$reactjsxruntime.jsxs)((0, $583VT$reactjsxruntime.Fragment), {
686
815
  children: [
@@ -716,7 +845,7 @@ var $2e5504cc4159ca8d$export$2e2bcd8739ae039 = $2e5504cc4159ca8d$var$CommentsFie
716
845
 
717
846
  const $505d598a33288aad$var$CollectionList = ({ collectionUrl: collectionUrl, resource: resource, children: children })=>{
718
847
  if ((0, ($parcel$interopDefault($583VT$react))).Children.count(children) !== 1) throw new Error("<CollectionList> only accepts a single child");
719
- const { items: actorsUris } = (0, $2b75a2f49c9ef165$export$2e2bcd8739ae039)(collectionUrl);
848
+ const { items: actorsUris } = (0, $5ca5f7e9fc1c3544$export$2e2bcd8739ae039)(collectionUrl);
720
849
  const { data: data, isLoading: isLoading, isFetching: isFetching } = (0, $583VT$reactadmin.useGetMany)(resource, {
721
850
  ids: Array.isArray(actorsUris) ? actorsUris : [
722
851
  actorsUris
@@ -759,84 +888,49 @@ var $b0c94a9bdea99da5$export$2e2bcd8739ae039 = $b0c94a9bdea99da5$var$ReferenceCo
759
888
 
760
889
 
761
890
 
762
- const $97664763db3c0a46$var$useInbox = ()=>{
891
+ /**
892
+ * Hook to fetch the inbox of the logged user.
893
+ * Returns the same data as the useCollection hooks, plus:
894
+ * - `awaitActivity`: a function to wait for a certain activity to be received
895
+ * - `owner`: the WebID of the inbox's owner
896
+ * See https://semapps.org/docs/frontend/activitypub-components#useinbox for usage
897
+ * @param {UseCollectionOptions} options Defaults to `{ dereferenceItems: false, liveUpdates: true }`
898
+ */ const $486f741c94cd8f74$var$useInbox = (options = {})=>{
763
899
  const { data: identity } = (0, $583VT$reactadmin.useGetIdentity)();
764
- const inboxUrl = (0, $583VT$react.useMemo)(()=>{
765
- if (identity?.webIdData) return identity?.webIdData?.inbox;
766
- }, [
767
- identity
768
- ]);
769
- const sparqlEndpoint = (0, $583VT$react.useMemo)(()=>{
770
- if (identity?.webIdData) return identity?.webIdData?.endpoints?.["void:sparqlEndpoint"] || `${identity?.id}/sparql`;
771
- }, [
772
- identity
773
- ]);
774
- const fetch = (0, $583VT$react.useCallback)(async ({ filters: filters })=>{
775
- if (!sparqlEndpoint || !inboxUrl) return;
776
- const token = localStorage.getItem("token");
777
- const blankNodesQuery = (0, $583VT$semappssemanticdataprovider.buildBlankNodesQuery)([
778
- "as:object"
779
- ]);
780
- let filtersWhereQuery = "";
781
- if (filters) Object.keys(filters).forEach((predicate)=>{
782
- if (filters[predicate]) {
783
- const object = filters[predicate].startsWith("http") ? `<${filters[predicate]}>` : filters[predicate];
784
- filtersWhereQuery += `?s1 ${predicate} ${object} .`;
785
- }
786
- });
787
- const query = `
788
- PREFIX as: <https://www.w3.org/ns/activitystreams#>
789
- CONSTRUCT {
790
- ?s1 ?p1 ?o1 .
791
- ${blankNodesQuery.construct}
792
- }
793
- WHERE {
794
- <${inboxUrl}> as:items ?s1 .
795
- ?s1 ?p1 ?o1 .
796
- FILTER( (isIRI(?s1)) ) .
797
- ${filtersWhereQuery}
798
- ${blankNodesQuery.where}
799
- }
800
- `;
801
- const { json: json } = await (0, $583VT$reactadmin.fetchUtils).fetchJson(sparqlEndpoint, {
802
- method: "POST",
803
- body: query,
804
- headers: new Headers({
805
- Accept: "application/ld+json",
806
- Authorization: token ? `Bearer ${token}` : undefined
807
- })
808
- });
809
- if (json["@graph"]) return json["@graph"];
810
- return null;
811
- }, [
812
- sparqlEndpoint,
813
- inboxUrl
814
- ]);
900
+ const { items: items, url: url, hasLiveUpdates: hasLiveUpdates, ...rest } = (0, $5ca5f7e9fc1c3544$export$2e2bcd8739ae039)("inbox", options);
901
+ const awaitActivity = (0, $5e70f9d0635e25dd$export$2e2bcd8739ae039)(hasLiveUpdates.webSocket);
815
902
  return {
816
- fetch: fetch,
817
- url: inboxUrl,
818
- owner: identity?.id
903
+ items: items,
904
+ url: url,
905
+ hasLiveUpdates: hasLiveUpdates,
906
+ awaitActivity: awaitActivity,
907
+ owner: identity?.id,
908
+ ...rest
819
909
  };
820
910
  };
821
- var $97664763db3c0a46$export$2e2bcd8739ae039 = $97664763db3c0a46$var$useInbox;
911
+ var $486f741c94cd8f74$export$2e2bcd8739ae039 = $486f741c94cd8f74$var$useInbox;
822
912
 
823
913
 
824
914
 
825
915
 
826
- const $30fd77858150739b$var$useNodeinfo = (host, rel = "http://nodeinfo.diaspora.software/ns/schema/2.1")=>{
916
+ const $59fc2d2cba62bd8e$var$useNodeinfo = (host, rel = "http://nodeinfo.diaspora.software/ns/schema/2.1")=>{
827
917
  const [schema, setSchema] = (0, $583VT$react.useState)();
828
918
  (0, $583VT$react.useEffect)(()=>{
829
919
  (async ()=>{
830
- const protocol = host.includes(":") ? "http" : "https"; // If the host has a port, we are likely on HTTP
831
- const nodeinfoUrl = `${protocol}://${host}/.well-known/nodeinfo`;
832
- try {
833
- const { json: links } = await (0, $583VT$reactadmin.fetchUtils).fetchJson(nodeinfoUrl);
834
- // Accept any version of the nodeinfo protocol
835
- const link = links?.links?.find((l)=>l.rel === rel);
836
- const { json: json } = await (0, $583VT$reactadmin.fetchUtils).fetchJson(link.href);
837
- setSchema(json);
838
- } catch (e) {
839
- // Do nothing if nodeinfo can't be fetched
920
+ if (host && rel) {
921
+ const protocol = host.includes(":") ? "http" : "https"; // If the host has a port, we are likely on HTTP
922
+ const nodeinfoUrl = `${protocol}://${host}/.well-known/nodeinfo`;
923
+ try {
924
+ const { json: links } = await (0, $583VT$reactadmin.fetchUtils).fetchJson(nodeinfoUrl);
925
+ // Accept any version of the nodeinfo protocol
926
+ const link = links?.links?.find((l)=>l.rel === rel);
927
+ if (link) {
928
+ const { json: json } = await (0, $583VT$reactadmin.fetchUtils).fetchJson(link.href);
929
+ setSchema(json);
930
+ }
931
+ } catch (e) {
932
+ // Do nothing if nodeinfo can't be fetched
933
+ }
840
934
  }
841
935
  })();
842
936
  }, [
@@ -846,7 +940,7 @@ const $30fd77858150739b$var$useNodeinfo = (host, rel = "http://nodeinfo.diaspora
846
940
  ]);
847
941
  return schema;
848
942
  };
849
- var $30fd77858150739b$export$2e2bcd8739ae039 = $30fd77858150739b$var$useNodeinfo;
943
+ var $59fc2d2cba62bd8e$export$2e2bcd8739ae039 = $59fc2d2cba62bd8e$var$useNodeinfo;
850
944
 
851
945
 
852
946