@comapeo/core-react 9.0.2 → 10.0.1

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.
Files changed (73) hide show
  1. package/README.md +1 -1
  2. package/dist/commonjs/contexts/ClientApi.d.ts +1 -3
  3. package/dist/commonjs/contexts/ClientApi.js +2 -2
  4. package/dist/commonjs/hooks/client.d.ts +74 -33
  5. package/dist/commonjs/hooks/client.js +40 -11
  6. package/dist/commonjs/hooks/documents.d.ts +42 -429
  7. package/dist/commonjs/hooks/documents.js +100 -51
  8. package/dist/commonjs/hooks/invites.d.ts +153 -55
  9. package/dist/commonjs/hooks/invites.js +69 -20
  10. package/dist/commonjs/hooks/maps.d.ts +96 -225
  11. package/dist/commonjs/hooks/maps.js +104 -37
  12. package/dist/commonjs/hooks/projects.d.ts +666 -223
  13. package/dist/commonjs/hooks/projects.js +264 -131
  14. package/dist/commonjs/index.d.ts +2 -2
  15. package/dist/commonjs/lib/map-shares-stores.d.ts +1 -1
  16. package/dist/commonjs/lib/map-shares-stores.js +2 -2
  17. package/dist/commonjs/lib/presets.d.ts +1 -3
  18. package/dist/commonjs/lib/react-query.d.ts +103 -0
  19. package/dist/commonjs/lib/react-query.js +187 -0
  20. package/dist/commonjs/lib/sync.d.ts +2 -5
  21. package/dist/commonjs/lib/sync.js +0 -1
  22. package/dist/commonjs/lib/types.d.ts +4 -6
  23. package/dist/esm/contexts/ClientApi.d.ts +1 -3
  24. package/dist/esm/contexts/ClientApi.js +1 -1
  25. package/dist/esm/hooks/client.d.ts +74 -33
  26. package/dist/esm/hooks/client.js +40 -11
  27. package/dist/esm/hooks/documents.d.ts +42 -429
  28. package/dist/esm/hooks/documents.js +100 -51
  29. package/dist/esm/hooks/invites.d.ts +153 -55
  30. package/dist/esm/hooks/invites.js +69 -20
  31. package/dist/esm/hooks/maps.d.ts +96 -225
  32. package/dist/esm/hooks/maps.js +105 -38
  33. package/dist/esm/hooks/projects.d.ts +666 -223
  34. package/dist/esm/hooks/projects.js +262 -129
  35. package/dist/esm/index.d.ts +2 -2
  36. package/dist/esm/lib/map-shares-stores.d.ts +1 -1
  37. package/dist/esm/lib/map-shares-stores.js +1 -1
  38. package/dist/esm/lib/presets.d.ts +1 -3
  39. package/dist/esm/lib/react-query.d.ts +103 -0
  40. package/dist/esm/lib/react-query.js +162 -0
  41. package/dist/esm/lib/sync.d.ts +2 -5
  42. package/dist/esm/lib/sync.js +1 -1
  43. package/dist/esm/lib/types.d.ts +4 -6
  44. package/docs/API.md +137 -81
  45. package/package.json +39 -35
  46. package/dist/commonjs/lib/react-query/client.d.ts +0 -65
  47. package/dist/commonjs/lib/react-query/client.js +0 -68
  48. package/dist/commonjs/lib/react-query/documents.d.ts +0 -1484
  49. package/dist/commonjs/lib/react-query/documents.js +0 -149
  50. package/dist/commonjs/lib/react-query/invites.d.ts +0 -88
  51. package/dist/commonjs/lib/react-query/invites.js +0 -95
  52. package/dist/commonjs/lib/react-query/maps.d.ts +0 -104
  53. package/dist/commonjs/lib/react-query/maps.js +0 -129
  54. package/dist/commonjs/lib/react-query/mutation-result.d.ts +0 -8
  55. package/dist/commonjs/lib/react-query/mutation-result.js +0 -22
  56. package/dist/commonjs/lib/react-query/projects.d.ts +0 -316
  57. package/dist/commonjs/lib/react-query/projects.js +0 -359
  58. package/dist/commonjs/lib/react-query/shared.d.ts +0 -9
  59. package/dist/commonjs/lib/react-query/shared.js +0 -23
  60. package/dist/esm/lib/react-query/client.d.ts +0 -65
  61. package/dist/esm/lib/react-query/client.js +0 -59
  62. package/dist/esm/lib/react-query/documents.d.ts +0 -1484
  63. package/dist/esm/lib/react-query/documents.js +0 -137
  64. package/dist/esm/lib/react-query/invites.d.ts +0 -88
  65. package/dist/esm/lib/react-query/invites.js +0 -85
  66. package/dist/esm/lib/react-query/maps.d.ts +0 -104
  67. package/dist/esm/lib/react-query/maps.js +0 -119
  68. package/dist/esm/lib/react-query/mutation-result.d.ts +0 -8
  69. package/dist/esm/lib/react-query/mutation-result.js +0 -19
  70. package/dist/esm/lib/react-query/projects.d.ts +0 -316
  71. package/dist/esm/lib/react-query/projects.js +0 -324
  72. package/dist/esm/lib/react-query/shared.d.ts +0 -9
  73. package/dist/esm/lib/react-query/shared.js +0 -18
@@ -1,6 +1,6 @@
1
1
  import { useMutation, useQueryClient, useSuspenseQuery, } from '@tanstack/react-query';
2
2
  import { useEffect, useSyncExternalStore } from 'react';
3
- import { addServerPeerMutationOptions, changeMemberRoleMutationOptions, connectSyncServersMutationOptions, createBlobMutationOptions, createProjectMutationOptions, disconnectSyncServersMutationOptions, documentCreatedByQueryOptions, exportGeoJSONMutationOptions, exportZipFileMutationOptions, getMembersQueryKey, getProjectRoleQueryKey, importProjectCategoriesMutationOptions, importProjectConfigMutationOptions, leaveProjectMutationOptions, mediaServerOriginQueryOptions, projectByIdQueryOptions, projectMemberByIdQueryOptions, projectMembersQueryOptions, projectOwnRoleQueryOptions, projectSettingsQueryOptions, projectsQueryOptions, removeProjectMemberMutationOptions, removeServerPeerMutationOptions, setAutostopDataSyncTimeoutMutationOptions, startSyncMutationOptions, stopSyncMutationOptions, updateProjectSettingsMutationOptions, } from '../lib/react-query/projects.js';
3
+ import { baseMutationOptions, baseQueryOptions, filterMutationResult, getDocumentCreatedByQueryKey, getMediaServerOriginQueryKey, getMemberByIdQueryKey, getMembersQueryKey, getProjectByIdQueryKey, getProjectRoleQueryKey, getProjectSettingsQueryKey, getProjectsQueryKey, } from '../lib/react-query.js';
4
4
  import { SyncStore } from '../lib/sync.js';
5
5
  import { getBlobUrl, getIconUrl } from '../lib/urls.js';
6
6
  import { useClientApi } from './client.js';
@@ -18,16 +18,15 @@ import { useClientApi } from './client.js';
18
18
  * }
19
19
  * ```
20
20
  */
21
- export function useProjectSettings({ projectId }) {
22
- const clientApi = useClientApi();
23
- const { data: projectApi } = useSuspenseQuery(projectByIdQueryOptions({
24
- projectId,
25
- clientApi,
26
- }));
27
- const { data, error, isRefetching } = useSuspenseQuery(projectSettingsQueryOptions({
28
- projectApi,
29
- projectId,
30
- }));
21
+ export function useProjectSettings({ projectId, }) {
22
+ const { data: projectApi } = useSingleProject({ projectId });
23
+ const { data, error, isRefetching } = useSuspenseQuery({
24
+ ...baseQueryOptions(),
25
+ queryKey: getProjectSettingsQueryKey({ projectId }),
26
+ queryFn: async () => {
27
+ return projectApi.$getProjectSettings();
28
+ },
29
+ });
31
30
  return { data, error, isRefetching };
32
31
  }
33
32
  /**
@@ -44,13 +43,14 @@ export function useProjectSettings({ projectId }) {
44
43
  * }
45
44
  * ```
46
45
  */
47
- export function useSingleProject({ projectId }) {
46
+ export function useSingleProject({ projectId, }) {
48
47
  const clientApi = useClientApi();
49
48
  const { data, error, isRefetching } = useSuspenseQuery({
50
- ...projectByIdQueryOptions({
51
- clientApi,
52
- projectId,
53
- }),
49
+ ...baseQueryOptions(),
50
+ queryKey: getProjectByIdQueryKey({ projectId }),
51
+ queryFn: async () => {
52
+ return clientApi.getProject(projectId);
53
+ },
54
54
  // Keep project instances around indefinitely - shouldn't be a memory
55
55
  // problem because these are only lightweight proxy objects, and project
56
56
  // references are kept indefinitely on the backend anyway once they are
@@ -74,9 +74,13 @@ export function useSingleProject({ projectId }) {
74
74
  */
75
75
  export function useManyProjects() {
76
76
  const clientApi = useClientApi();
77
- const { data, error, isRefetching } = useSuspenseQuery(projectsQueryOptions({
78
- clientApi,
79
- }));
77
+ const { data, error, isRefetching } = useSuspenseQuery({
78
+ ...baseQueryOptions(),
79
+ queryKey: getProjectsQueryKey(),
80
+ queryFn: async () => {
81
+ return clientApi.listProjects();
82
+ },
83
+ });
80
84
  return { data, error, isRefetching };
81
85
  }
82
86
  /**
@@ -96,11 +100,13 @@ export function useManyProjects() {
96
100
  */
97
101
  export function useSingleMember({ projectId, deviceId, }) {
98
102
  const { data: projectApi } = useSingleProject({ projectId });
99
- const { data, error, isRefetching } = useSuspenseQuery(projectMemberByIdQueryOptions({
100
- projectApi,
101
- projectId,
102
- deviceId,
103
- }));
103
+ const { data, error, isRefetching } = useSuspenseQuery({
104
+ ...baseQueryOptions(),
105
+ queryKey: getMemberByIdQueryKey({ projectId, deviceId }),
106
+ queryFn: async () => {
107
+ return projectApi.$member.getById(deviceId);
108
+ },
109
+ });
104
110
  return { data, error, isRefetching };
105
111
  }
106
112
  /**
@@ -111,15 +117,22 @@ export function useSingleMember({ projectId, deviceId, }) {
111
117
  * @example
112
118
  * ```tsx
113
119
  * function BasicExample() {
114
- * const { data } = useManyMembers({ projectId: '...' })
120
+ * const activeMembers1 = useManyMembers({ projectId: '...' })
121
+ * const activeMembers2 = useManyMembers({ projectId: '...', includeLeft: false })
115
122
  *
116
- * console.log(data.role)
123
+ * const allMembers = useManyMembers({ projectId: '...', includeLeft: true })
117
124
  * }
118
125
  * ```
119
126
  */
120
- export function useManyMembers({ projectId }) {
127
+ export function useManyMembers({ projectId, includeLeft, }) {
121
128
  const { data: projectApi } = useSingleProject({ projectId });
122
- const { data, error, isRefetching } = useSuspenseQuery(projectMembersQueryOptions({ projectApi, projectId }));
129
+ const { data, error, isRefetching } = useSuspenseQuery({
130
+ ...baseQueryOptions(),
131
+ queryKey: getMembersQueryKey({ projectId, includeLeft }),
132
+ queryFn: async () => {
133
+ return projectApi.$member.getMany({ includeLeft });
134
+ },
135
+ });
123
136
  return { data, error, isRefetching };
124
137
  }
125
138
  /**
@@ -225,14 +238,32 @@ export function useAttachmentUrl({ projectId, blobId, }) {
225
238
  const blobUrl = getBlobUrl({ serverOrigin, projectId, blobId });
226
239
  return { data: blobUrl, error, isRefetching };
227
240
  }
241
+ // Used as a placeholder so that we can read the server port from the $blobs.getUrl() method
242
+ const FAKE_BLOB_ID = {
243
+ type: 'photo',
244
+ variant: 'original',
245
+ name: 'name',
246
+ driveId: 'drive-id',
247
+ };
228
248
  /**
229
249
  * @internal
230
250
  * Hack to retrieve the media server origin (protocol + host).
231
251
  */
232
252
  function useMediaServerOrigin({ projectApi }) {
233
- const { data, error, isRefetching } = useSuspenseQuery(mediaServerOriginQueryOptions({
234
- projectApi,
235
- }));
253
+ const { data, error, isRefetching } = useSuspenseQuery({
254
+ ...baseQueryOptions(),
255
+ // HACK: The server doesn't yet expose a method to get its origin, so we use
256
+ // the existing $blobs.getUrl() to get the origin with a fake BlobId. The origin
257
+ // is the same regardless of the blobId, so it's not necessary to include it
258
+ // as a dep for the query key.
259
+ queryKey: getMediaServerOriginQueryKey(),
260
+ queryFn: async () => {
261
+ const url = await projectApi.$blobs.getUrl(FAKE_BLOB_ID);
262
+ return new URL(url).origin;
263
+ },
264
+ staleTime: 'static',
265
+ gcTime: Infinity,
266
+ });
236
267
  return { data, error, isRefetching };
237
268
  }
238
269
  // TODO: Eventually remove in favor of this information being provided by the backend when retrieving documents
@@ -256,7 +287,18 @@ function useMediaServerOrigin({ projectApi }) {
256
287
  */
257
288
  export function useDocumentCreatedBy({ projectId, originalVersionId, }) {
258
289
  const { data: projectApi } = useSingleProject({ projectId });
259
- const { data, error, isRefetching } = useSuspenseQuery(documentCreatedByQueryOptions({ projectApi, projectId, originalVersionId }));
290
+ const { data, error, isRefetching } = useSuspenseQuery({
291
+ ...baseQueryOptions(),
292
+ queryKey: getDocumentCreatedByQueryKey({
293
+ projectId,
294
+ originalVersionId,
295
+ }),
296
+ queryFn: async () => {
297
+ return projectApi.$originalVersionIdToDeviceId(originalVersionId);
298
+ },
299
+ staleTime: 'static',
300
+ gcTime: Infinity,
301
+ });
260
302
  return { data, error, isRefetching };
261
303
  }
262
304
  /**
@@ -274,26 +316,50 @@ export function useDocumentCreatedBy({ projectId, originalVersionId, }) {
274
316
  * }
275
317
  * ```
276
318
  */
277
- export function useOwnRoleInProject({ projectId }) {
319
+ export function useOwnRoleInProject({ projectId, }) {
278
320
  const { data: projectApi } = useSingleProject({ projectId });
279
- const { data, error, isRefetching } = useSuspenseQuery(projectOwnRoleQueryOptions({ projectApi, projectId }));
321
+ const { data, error, isRefetching } = useSuspenseQuery({
322
+ ...baseQueryOptions(),
323
+ queryKey: getProjectRoleQueryKey({ projectId }),
324
+ queryFn: async () => {
325
+ return projectApi.$getOwnRole();
326
+ },
327
+ });
280
328
  return { data, error, isRefetching };
281
329
  }
282
330
  export function useAddServerPeer({ projectId }) {
283
331
  const queryClient = useQueryClient();
284
332
  const { data: projectApi } = useSingleProject({ projectId });
285
- const { error, mutate, mutateAsync, reset, status } = useMutation(addServerPeerMutationOptions({ projectApi, projectId, queryClient }));
286
- return status === 'error'
287
- ? { error, mutate, mutateAsync, reset, status }
288
- : { error: null, mutate, mutateAsync, reset, status };
333
+ return filterMutationResult(useMutation({
334
+ ...baseMutationOptions(),
335
+ mutationFn: async ({ baseUrl, dangerouslyAllowInsecureConnections, }) => {
336
+ return projectApi.$member.addServerPeer(baseUrl, {
337
+ dangerouslyAllowInsecureConnections,
338
+ });
339
+ },
340
+ onSuccess: () => {
341
+ queryClient.invalidateQueries({
342
+ queryKey: getMembersQueryKey({ projectId }),
343
+ });
344
+ },
345
+ }));
289
346
  }
290
347
  export function useRemoveServerPeer({ projectId }) {
291
348
  const queryClient = useQueryClient();
292
349
  const { data: projectApi } = useSingleProject({ projectId });
293
- const { error, mutate, mutateAsync, reset, status } = useMutation(removeServerPeerMutationOptions({ projectApi, projectId, queryClient }));
294
- return status === 'error'
295
- ? { error, mutate, mutateAsync, reset, status }
296
- : { error: null, mutate, mutateAsync, reset, status };
350
+ return filterMutationResult(useMutation({
351
+ ...baseMutationOptions(),
352
+ mutationFn: async ({ serverDeviceId, dangerouslyAllowInsecureConnections, }) => {
353
+ return projectApi.$member.removeServerPeer(serverDeviceId, {
354
+ dangerouslyAllowInsecureConnections,
355
+ });
356
+ },
357
+ onSuccess: () => {
358
+ queryClient.invalidateQueries({
359
+ queryKey: getMembersQueryKey({ projectId }),
360
+ });
361
+ },
362
+ }));
297
363
  }
298
364
  /**
299
365
  * Create a new project.
@@ -301,10 +367,19 @@ export function useRemoveServerPeer({ projectId }) {
301
367
  export function useCreateProject() {
302
368
  const queryClient = useQueryClient();
303
369
  const clientApi = useClientApi();
304
- const { error, mutate, mutateAsync, reset, status } = useMutation(createProjectMutationOptions({ clientApi, queryClient }));
305
- return status === 'error'
306
- ? { error, mutate, mutateAsync, reset, status }
307
- : { error: null, mutate, mutateAsync, reset, status };
370
+ return filterMutationResult(useMutation({
371
+ ...baseMutationOptions(),
372
+ mutationFn: async (opts) => {
373
+ // Have to avoid passing `undefined` explicitly
374
+ // See https://github.com/digidem/rpc-reflector/issues/21
375
+ return opts ? clientApi.createProject(opts) : clientApi.createProject();
376
+ },
377
+ onSuccess: () => {
378
+ queryClient.invalidateQueries({
379
+ queryKey: getProjectsQueryKey(),
380
+ });
381
+ },
382
+ }));
308
383
  }
309
384
  /**
310
385
  * Leave an existing project.
@@ -312,10 +387,17 @@ export function useCreateProject() {
312
387
  export function useLeaveProject() {
313
388
  const queryClient = useQueryClient();
314
389
  const clientApi = useClientApi();
315
- const { error, mutate, mutateAsync, reset, status } = useMutation(leaveProjectMutationOptions({ clientApi, queryClient }));
316
- return status === 'error'
317
- ? { error, mutate, mutateAsync, reset, status }
318
- : { error: null, mutate, mutateAsync, reset, status };
390
+ return filterMutationResult(useMutation({
391
+ ...baseMutationOptions(),
392
+ mutationFn: async ({ projectId }) => {
393
+ return clientApi.leaveProject(projectId);
394
+ },
395
+ onSuccess: () => {
396
+ queryClient.invalidateQueries({
397
+ queryKey: getProjectsQueryKey(),
398
+ });
399
+ },
400
+ }));
319
401
  }
320
402
  /**
321
403
  * Update the categories of a project using an external file.
@@ -325,14 +407,17 @@ export function useLeaveProject() {
325
407
  export function useImportProjectCategories({ projectId, }) {
326
408
  const queryClient = useQueryClient();
327
409
  const { data: projectApi } = useSingleProject({ projectId });
328
- const { error, mutate, mutateAsync, reset, status } = useMutation(importProjectCategoriesMutationOptions({
329
- queryClient,
330
- projectApi,
331
- projectId,
410
+ return filterMutationResult(useMutation({
411
+ ...baseMutationOptions(),
412
+ mutationFn: ({ filePath }) => {
413
+ return projectApi.$importCategories({ filePath });
414
+ },
415
+ onSuccess: () => {
416
+ queryClient.invalidateQueries({
417
+ queryKey: getProjectByIdQueryKey({ projectId }),
418
+ });
419
+ },
332
420
  }));
333
- return status === 'error'
334
- ? { error, mutate, mutateAsync, reset, status }
335
- : { error: null, mutate, mutateAsync, reset, status };
336
421
  }
337
422
  /**
338
423
  * Update the configuration of a project using an external file.
@@ -343,23 +428,37 @@ export function useImportProjectCategories({ projectId, }) {
343
428
  export function useImportProjectConfig({ projectId }) {
344
429
  const queryClient = useQueryClient();
345
430
  const { data: projectApi } = useSingleProject({ projectId });
346
- const { error, mutate, mutateAsync, reset, status } = useMutation(importProjectConfigMutationOptions({ queryClient, projectApi, projectId }));
347
- return status === 'error'
348
- ? { error, mutate, mutateAsync, reset, status }
349
- : { error: null, mutate, mutateAsync, reset, status };
431
+ return filterMutationResult(useMutation({
432
+ ...baseMutationOptions(),
433
+ mutationFn: ({ configPath }) => {
434
+ return projectApi.importConfig({ configPath });
435
+ },
436
+ onSuccess: () => {
437
+ queryClient.invalidateQueries({
438
+ queryKey: getProjectByIdQueryKey({ projectId }),
439
+ });
440
+ },
441
+ }));
350
442
  }
351
443
  /**
352
444
  * Update the settings of a project.
353
445
  *
354
446
  * @param opts.projectId Public ID of the project to apply changes to.
355
447
  */
356
- export function useUpdateProjectSettings({ projectId }) {
448
+ export function useUpdateProjectSettings({ projectId, }) {
357
449
  const queryClient = useQueryClient();
358
450
  const { data: projectApi } = useSingleProject({ projectId });
359
- const { error, mutate, mutateAsync, reset, status } = useMutation(updateProjectSettingsMutationOptions({ projectApi, queryClient }));
360
- return status === 'error'
361
- ? { error, mutate, mutateAsync, reset, status }
362
- : { error: null, mutate, mutateAsync, reset, status };
451
+ return filterMutationResult(useMutation({
452
+ ...baseMutationOptions(),
453
+ mutationFn: async (value) => {
454
+ return projectApi.$setProjectSettings(value);
455
+ },
456
+ onSuccess: () => {
457
+ queryClient.invalidateQueries({
458
+ queryKey: getProjectsQueryKey(),
459
+ });
460
+ },
461
+ }));
363
462
  }
364
463
  /**
365
464
  * Change a project member's role.
@@ -378,10 +477,20 @@ export function useUpdateProjectSettings({ projectId }) {
378
477
  export function useChangeMemberRole({ projectId }) {
379
478
  const queryClient = useQueryClient();
380
479
  const { data: projectApi } = useSingleProject({ projectId });
381
- const { error, mutate, mutateAsync, reset, status } = useMutation(changeMemberRoleMutationOptions({ projectApi, projectId, queryClient }));
382
- return status === 'error'
383
- ? { error, mutate, mutateAsync, reset, status }
384
- : { error: null, mutate, mutateAsync, reset, status };
480
+ return filterMutationResult(useMutation({
481
+ ...baseMutationOptions(),
482
+ mutationFn: async ({ deviceId, roleId, }) => {
483
+ return projectApi.$member.assignRole(deviceId, roleId);
484
+ },
485
+ onSuccess: () => {
486
+ queryClient.invalidateQueries({
487
+ queryKey: getMembersQueryKey({ projectId }),
488
+ });
489
+ queryClient.invalidateQueries({
490
+ queryKey: getProjectRoleQueryKey({ projectId }),
491
+ });
492
+ },
493
+ }));
385
494
  }
386
495
  /**
387
496
  * Remove a member from a project, providing an optional reason for removal.
@@ -405,39 +514,55 @@ export function useChangeMemberRole({ projectId }) {
405
514
  export function useRemoveMember({ projectId }) {
406
515
  const queryClient = useQueryClient();
407
516
  const { data: projectApi } = useSingleProject({ projectId });
408
- const { error, mutate, mutateAsync, reset, status } = useMutation(removeProjectMemberMutationOptions({ projectId, projectApi, queryClient }));
409
- return status === 'error'
410
- ? { error, mutate, mutateAsync, reset, status }
411
- : { error: null, mutate, mutateAsync, reset, status };
517
+ return filterMutationResult(useMutation({
518
+ ...baseMutationOptions(),
519
+ mutationFn: async ({ deviceId, reason, }) => {
520
+ // Have to avoid passing `undefined` explicitly
521
+ // See https://github.com/digidem/rpc-reflector/issues/21
522
+ return reason
523
+ ? projectApi.$member.remove(deviceId, { reason })
524
+ : projectApi.$member.remove(deviceId);
525
+ },
526
+ onSuccess: () => {
527
+ queryClient.invalidateQueries({
528
+ queryKey: getMembersQueryKey({ projectId }),
529
+ });
530
+ },
531
+ }));
412
532
  }
413
533
  /**
414
534
  * Set up listener for changes to your own role in a project.
415
535
  * It is necessary to use this if you want the project role-related read hooks to update
416
536
  * based on role change events that are received in the background.
417
537
  *
418
- * @param opts.listener Optional listener to invoke when role changes
538
+ *
419
539
  *
420
540
  * @example
421
541
  * ```tsx
422
- * function SomeComponent({ projectId }: { projectId: string }) {
542
+ * function ListenerComponent({ projectId }: { projectId: string }) {
543
+ * // Set up the listener
423
544
  * useProjectOwnRoleChangeListener({ projectId })
424
545
  * }
425
- * ```
426
546
  *
427
- * @example
428
- * ```tsx
429
- * function ComponentWithListener({ projectId }: { projectId: string }) {
430
- * useProjectOwnRoleChangeListener({
431
- * projectId,
432
- * listener: (event) => {
433
- * // Handle role change, e.g., navigate to default project
434
- * console.log('New role:', event.role)
547
+ * // Handle role change events separately
548
+ * function EventHandlerComponent() {
549
+ * const { data: projectApi } = useSingleProject({ projectId })
550
+ *
551
+ * useEffect(() => {
552
+ * function handleRoleChangeEvent(event) {
553
+ * // Do something with event...
435
554
  * }
436
- * })
555
+ *
556
+ * projectApi.addListener('own-role-change', handleRoleChangeEvent)
557
+ *
558
+ * return () => {
559
+ * projectApi.removeListener('own-role-change', handleRoleChangeEvent)
560
+ * }
561
+ * }, [projectApi])
437
562
  * }
438
563
  * ```
439
564
  */
440
- export function useProjectOwnRoleChangeListener({ projectId, listener, }) {
565
+ export function useProjectOwnRoleChangeListener({ projectId, }) {
441
566
  const queryClient = useQueryClient();
442
567
  const { data: projectApi } = useSingleProject({ projectId });
443
568
  useEffect(() => {
@@ -454,16 +579,6 @@ export function useProjectOwnRoleChangeListener({ projectId, listener, }) {
454
579
  projectApi.removeListener('own-role-change', invalidateCache);
455
580
  };
456
581
  }, [projectApi, queryClient, projectId]);
457
- useEffect(() => {
458
- if (listener) {
459
- projectApi.addListener('own-role-change', listener);
460
- }
461
- return () => {
462
- if (listener) {
463
- projectApi.removeListener('own-role-change', listener);
464
- }
465
- };
466
- }, [projectApi, listener]);
467
582
  }
468
583
  /**
469
584
  * Create a blob for a project.
@@ -472,10 +587,12 @@ export function useProjectOwnRoleChangeListener({ projectId, listener, }) {
472
587
  */
473
588
  export function useCreateBlob({ projectId }) {
474
589
  const { data: projectApi } = useSingleProject({ projectId });
475
- const { error, mutate, mutateAsync, reset, status } = useMutation(createBlobMutationOptions({ projectApi }));
476
- return status === 'error'
477
- ? { error, mutate, mutateAsync, reset, status }
478
- : { error: null, mutate, mutateAsync, reset, status };
590
+ return filterMutationResult(useMutation({
591
+ ...baseMutationOptions(),
592
+ mutationFn: async ({ original, preview, thumbnail, metadata, }) => {
593
+ return projectApi.$blobs.create({ original, preview, thumbnail }, metadata);
594
+ },
595
+ }));
479
596
  }
480
597
  const PROJECT_SYNC_STORE_MAP = new WeakMap();
481
598
  function useSyncStore({ projectId }) {
@@ -525,38 +642,50 @@ export function useDataSyncProgress({ projectId, }) {
525
642
  }
526
643
  export function useStartSync({ projectId }) {
527
644
  const { data: projectApi } = useSingleProject({ projectId });
528
- const { error, mutate, mutateAsync, reset, status } = useMutation(startSyncMutationOptions({ projectApi }));
529
- return status === 'error'
530
- ? { error, mutate, mutateAsync, reset, status }
531
- : { error: null, mutate, mutateAsync, reset, status };
645
+ return filterMutationResult(useMutation({
646
+ ...baseMutationOptions(),
647
+ mutationFn: async (opts) => {
648
+ // Have to avoid passing `undefined` explicitly
649
+ // See https://github.com/digidem/rpc-reflector/issues/21
650
+ return opts ? projectApi.$sync.start(opts) : projectApi.$sync.start();
651
+ },
652
+ }));
532
653
  }
533
654
  export function useStopSync({ projectId }) {
534
655
  const { data: projectApi } = useSingleProject({ projectId });
535
- const { error, mutate, mutateAsync, reset, status } = useMutation(stopSyncMutationOptions({ projectApi }));
536
- return status === 'error'
537
- ? { error, mutate, mutateAsync, reset, status }
538
- : { error: null, mutate, mutateAsync, reset, status };
656
+ return filterMutationResult(useMutation({
657
+ ...baseMutationOptions(),
658
+ mutationFn: async () => {
659
+ return projectApi.$sync.stop();
660
+ },
661
+ }));
539
662
  }
540
663
  export function useConnectSyncServers({ projectId }) {
541
664
  const { data: projectApi } = useSingleProject({ projectId });
542
- const { error, mutate, mutateAsync, reset, status } = useMutation(connectSyncServersMutationOptions({ projectApi }));
543
- return status === 'error'
544
- ? { error, mutate, mutateAsync, reset, status }
545
- : { error: null, mutate, mutateAsync, reset, status };
665
+ return filterMutationResult(useMutation({
666
+ ...baseMutationOptions(),
667
+ mutationFn: async () => {
668
+ return projectApi.$sync.connectServers();
669
+ },
670
+ }));
546
671
  }
547
672
  export function useDisconnectSyncServers({ projectId }) {
548
673
  const { data: projectApi } = useSingleProject({ projectId });
549
- const { error, mutate, mutateAsync, reset, status } = useMutation(disconnectSyncServersMutationOptions({ projectApi }));
550
- return status === 'error'
551
- ? { error, mutate, mutateAsync, reset, status }
552
- : { error: null, mutate, mutateAsync, reset, status };
674
+ return filterMutationResult(useMutation({
675
+ ...baseMutationOptions(),
676
+ mutationFn: async () => {
677
+ return projectApi.$sync.disconnectServers();
678
+ },
679
+ }));
553
680
  }
554
681
  export function useSetAutostopDataSyncTimeout({ projectId, }) {
555
682
  const { data: projectApi } = useSingleProject({ projectId });
556
- const { error, mutate, mutateAsync, reset, status } = useMutation(setAutostopDataSyncTimeoutMutationOptions({ projectApi }));
557
- return status === 'error'
558
- ? { error, mutate, mutateAsync, reset, status }
559
- : { error: null, mutate, mutateAsync, reset, status };
683
+ return filterMutationResult(useMutation({
684
+ ...baseMutationOptions(),
685
+ mutationFn: async ({ after }) => {
686
+ return projectApi.$sync.setAutostopDataSyncTimeout(after);
687
+ },
688
+ }));
560
689
  }
561
690
  /**
562
691
  * Creates a GeoJson file with all the observations and/or tracks in the project.
@@ -565,10 +694,12 @@ export function useSetAutostopDataSyncTimeout({ projectId, }) {
565
694
  */
566
695
  export function useExportGeoJSON({ projectId }) {
567
696
  const { data: projectApi } = useSingleProject({ projectId });
568
- const { error, mutate, mutateAsync, reset, status } = useMutation(exportGeoJSONMutationOptions({ projectApi }));
569
- return status === 'error'
570
- ? { error, mutate, mutateAsync, reset, status }
571
- : { error: null, mutate, mutateAsync, reset, status };
697
+ return filterMutationResult(useMutation({
698
+ ...baseMutationOptions(),
699
+ mutationFn: async (opts) => {
700
+ return projectApi.exportGeoJSONFile(opts.path, opts.exportOptions);
701
+ },
702
+ }));
572
703
  }
573
704
  /**
574
705
  * Creates a zip file containing a GeoJson file with all the observations and/or tracks in the project and all associated attachments (photos and audio).
@@ -577,8 +708,10 @@ export function useExportGeoJSON({ projectId }) {
577
708
  */
578
709
  export function useExportZipFile({ projectId }) {
579
710
  const { data: projectApi } = useSingleProject({ projectId });
580
- const { error, mutate, mutateAsync, reset, status } = useMutation(exportZipFileMutationOptions({ projectApi }));
581
- return status === 'error'
582
- ? { error, mutate, mutateAsync, reset, status }
583
- : { error: null, mutate, mutateAsync, reset, status };
711
+ return filterMutationResult(useMutation({
712
+ ...baseMutationOptions(),
713
+ mutationFn: async (opts) => {
714
+ return projectApi.exportZipFile(opts.path, opts.exportOptions);
715
+ },
716
+ }));
584
717
  }
@@ -6,6 +6,6 @@ export { useMapStyleUrl, useImportCustomMapFile, useRemoveCustomMapFile, useGetC
6
6
  export type { SentMapShareState, ReceivedMapShareState, AbortMapShareOptions, CancelMapShareOptions, DeclineMapShareOptions, DownloadMapShareOptions, CreateAndSendMapShareOptions, } from './lib/map-shares-stores.js';
7
7
  export { DeclineReason } from './lib/map-shares-stores.js';
8
8
  export { useAddServerPeer, useAttachmentUrl, useConnectSyncServers, useCreateBlob, useCreateProject, useDataSyncProgress, useDisconnectSyncServers, useDocumentCreatedBy, useIconUrl, useImportProjectCategories, useImportProjectConfig, useLeaveProject, useManyMembers, useManyProjects, useOwnRoleInProject, useProjectOwnRoleChangeListener, useProjectSettings, useRemoveServerPeer, useRemoveMember, useSetAutostopDataSyncTimeout, useSingleMember, useSingleProject, useStartSync, useStopSync, useSyncState, useUpdateProjectSettings, useChangeMemberRole, useExportGeoJSON, useExportZipFile, } from './hooks/projects.js';
9
- export { type SyncState } from './lib/sync.js';
10
- export { type WriteableDocument, type WriteableDocumentType, type WriteableValue, } from './lib/types.js';
9
+ export type { SyncState } from './lib/sync.js';
10
+ export type { WriteableDocument, WriteableDocumentType, WriteableValue, } from './lib/types.js';
11
11
  export { HTTPError, isHTTPError } from './lib/http.js';
@@ -1,7 +1,7 @@
1
1
  import type { MapShare } from '@comapeo/core';
2
2
  import type { MapeoClientApi } from '@comapeo/ipc';
3
3
  import { type MapShareState as ServerMapShareState } from '@comapeo/map-server';
4
- import { type QueryClient } from '@tanstack/react-query';
4
+ import type { QueryClient } from '@tanstack/react-query';
5
5
  import type { Simplify } from 'type-fest';
6
6
  import type { MapServerApi } from '../contexts/MapServer.js';
7
7
  type DistributedIntersection<T, U> = U extends unknown ? Simplify<T & U> : never;
@@ -1,7 +1,7 @@
1
1
  import { CUSTOM_MAP_ID } from '@comapeo/map-server/constants.js';
2
2
  import { errors } from '@comapeo/map-server/errors.js';
3
3
  import ensureError from 'ensure-error';
4
- import { invalidateMapQueries } from './react-query/maps.js';
4
+ import { invalidateMapQueries } from './react-query.js';
5
5
  // ============================================
6
6
  // ACTION OPTIONS TYPES
7
7
  // These are defined here so that VSCode tooltips work for the mutation
@@ -1,4 +1,2 @@
1
- import type { Preset } from '@comapeo/schema' with {
2
- 'resolution-mode': 'import'
3
- };
1
+ import type { Preset } from '@comapeo/core/schema.js';
4
2
  export declare function getPresetsSelection(presets: Array<Preset>, orderedPresetIds?: Array<string>): Array<Preset>;