@comapeo/core-react 6.1.0 → 6.2.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.
@@ -430,6 +430,47 @@ export declare function useUpdateProjectSettings({ projectId }: {
430
430
  reset: () => void;
431
431
  status: "pending" | "success" | "idle";
432
432
  };
433
+ /**
434
+ * Change a project member's role.
435
+ *
436
+ * @param opts.projectId Project public ID
437
+ *
438
+ * @example
439
+ * ```tsx
440
+ * function BasicExample() {
441
+ * const { mutate } = useChangeMemberRole({ projectId: '...' })
442
+ * // Use one of: COORDINATOR_ROLE_ID, MEMBER_ROLE_ID, BLOCKED_ROLE_ID
443
+ * mutate({ deviceId: '...', roleId: COORDINATOR_ROLE_ID })
444
+ * }
445
+ * ```
446
+ */
447
+ export declare function useChangeMemberRole({ projectId }: {
448
+ projectId: string;
449
+ }): {
450
+ error: Error;
451
+ mutate: import("@tanstack/react-query").UseMutateFunction<void, Error, {
452
+ deviceId: string;
453
+ roleId: import("@comapeo/core", { with: { "resolution-mode": "import" } }).MemberApi.RoleIdAssignableToOthers;
454
+ }, unknown>;
455
+ mutateAsync: import("@tanstack/react-query").UseMutateAsyncFunction<void, Error, {
456
+ deviceId: string;
457
+ roleId: import("@comapeo/core", { with: { "resolution-mode": "import" } }).MemberApi.RoleIdAssignableToOthers;
458
+ }, unknown>;
459
+ reset: () => void;
460
+ status: "error";
461
+ } | {
462
+ error: null;
463
+ mutate: import("@tanstack/react-query").UseMutateFunction<void, Error, {
464
+ deviceId: string;
465
+ roleId: import("@comapeo/core", { with: { "resolution-mode": "import" } }).MemberApi.RoleIdAssignableToOthers;
466
+ }, unknown>;
467
+ mutateAsync: import("@tanstack/react-query").UseMutateAsyncFunction<void, Error, {
468
+ deviceId: string;
469
+ roleId: import("@comapeo/core", { with: { "resolution-mode": "import" } }).MemberApi.RoleIdAssignableToOthers;
470
+ }, unknown>;
471
+ reset: () => void;
472
+ status: "pending" | "success" | "idle";
473
+ };
433
474
  /**
434
475
  * Create a blob for a project.
435
476
  *
@@ -626,13 +667,7 @@ export declare function useExportGeoJSON({ projectId }: {
626
667
  mutate: import("@tanstack/react-query").UseMutateFunction<string, Error, {
627
668
  path: string;
628
669
  exportOptions: {
629
- observations
630
- /**
631
- * Update the settings of a project.
632
- *
633
- * @param opts.projectId Public ID of the project to apply changes to.
634
- */
635
- ?: boolean;
670
+ observations?: boolean;
636
671
  tracks?: boolean;
637
672
  lang?: string;
638
673
  };
@@ -640,13 +675,7 @@ export declare function useExportGeoJSON({ projectId }: {
640
675
  mutateAsync: import("@tanstack/react-query").UseMutateAsyncFunction<string, Error, {
641
676
  path: string;
642
677
  exportOptions: {
643
- observations
644
- /**
645
- * Update the settings of a project.
646
- *
647
- * @param opts.projectId Public ID of the project to apply changes to.
648
- */
649
- ?: boolean;
678
+ observations?: boolean;
650
679
  tracks?: boolean;
651
680
  lang?: string;
652
681
  };
@@ -658,13 +687,7 @@ export declare function useExportGeoJSON({ projectId }: {
658
687
  mutate: import("@tanstack/react-query").UseMutateFunction<string, Error, {
659
688
  path: string;
660
689
  exportOptions: {
661
- observations
662
- /**
663
- * Update the settings of a project.
664
- *
665
- * @param opts.projectId Public ID of the project to apply changes to.
666
- */
667
- ?: boolean;
690
+ observations?: boolean;
668
691
  tracks?: boolean;
669
692
  lang?: string;
670
693
  };
@@ -672,13 +695,7 @@ export declare function useExportGeoJSON({ projectId }: {
672
695
  mutateAsync: import("@tanstack/react-query").UseMutateAsyncFunction<string, Error, {
673
696
  path: string;
674
697
  exportOptions: {
675
- observations
676
- /**
677
- * Update the settings of a project.
678
- *
679
- * @param opts.projectId Public ID of the project to apply changes to.
680
- */
681
- ?: boolean;
698
+ observations?: boolean;
682
699
  tracks?: boolean;
683
700
  lang?: string;
684
701
  };
@@ -15,6 +15,7 @@ exports.useCreateProject = useCreateProject;
15
15
  exports.useLeaveProject = useLeaveProject;
16
16
  exports.useImportProjectConfig = useImportProjectConfig;
17
17
  exports.useUpdateProjectSettings = useUpdateProjectSettings;
18
+ exports.useChangeMemberRole = useChangeMemberRole;
18
19
  exports.useCreateBlob = useCreateBlob;
19
20
  exports.useSyncState = useSyncState;
20
21
  exports.useDataSyncProgress = useDataSyncProgress;
@@ -186,9 +187,13 @@ function useManyMembers({ projectId }) {
186
187
  */
187
188
  function useIconUrl({ projectId, iconId, ...mimeBasedOpts }) {
188
189
  const { data: projectApi } = useSingleProject({ projectId });
189
- const { data: port, error, isRefetching } = useMediaServerPort({ projectApi });
190
- const baseUrl = `http://127.0.0.1:${port}`;
191
- const iconUrl = (0, urls_js_1.getIconUrl)(baseUrl, iconId, mimeBasedOpts);
190
+ const { data: serverOrigin, error, isRefetching, } = useMediaServerOrigin({ projectApi });
191
+ const iconUrl = (0, urls_js_1.getIconUrl)({
192
+ serverOrigin,
193
+ iconId,
194
+ projectId,
195
+ mimeBasedOpts,
196
+ });
192
197
  return { data: iconUrl, error, isRefetching };
193
198
  }
194
199
  /**
@@ -244,17 +249,16 @@ function useIconUrl({ projectId, iconId, ...mimeBasedOpts }) {
244
249
  */
245
250
  function useAttachmentUrl({ projectId, blobId, }) {
246
251
  const { data: projectApi } = useSingleProject({ projectId });
247
- const { data: port, error, isRefetching } = useMediaServerPort({ projectApi });
248
- const baseUrl = `http://127.0.0.1:${port}`;
249
- const blobUrl = (0, urls_js_1.getBlobUrl)(baseUrl, blobId);
252
+ const { data: serverOrigin, error, isRefetching, } = useMediaServerOrigin({ projectApi });
253
+ const blobUrl = (0, urls_js_1.getBlobUrl)({ serverOrigin, projectId, blobId });
250
254
  return { data: blobUrl, error, isRefetching };
251
255
  }
252
256
  /**
253
257
  * @internal
254
- * Hack to retrieve the media server port.
258
+ * Hack to retrieve the media server origin (protocol + host).
255
259
  */
256
- function useMediaServerPort({ projectApi }) {
257
- const { data, error, isRefetching } = (0, react_query_1.useSuspenseQuery)((0, projects_js_1.mediaServerPortQueryOptions)({
260
+ function useMediaServerOrigin({ projectApi }) {
261
+ const { data, error, isRefetching } = (0, react_query_1.useSuspenseQuery)((0, projects_js_1.mediaServerOriginQueryOptions)({
258
262
  projectApi,
259
263
  }));
260
264
  return { data, error, isRefetching };
@@ -365,6 +369,28 @@ function useUpdateProjectSettings({ projectId }) {
365
369
  ? { error, mutate, mutateAsync, reset, status }
366
370
  : { error: null, mutate, mutateAsync, reset, status };
367
371
  }
372
+ /**
373
+ * Change a project member's role.
374
+ *
375
+ * @param opts.projectId Project public ID
376
+ *
377
+ * @example
378
+ * ```tsx
379
+ * function BasicExample() {
380
+ * const { mutate } = useChangeMemberRole({ projectId: '...' })
381
+ * // Use one of: COORDINATOR_ROLE_ID, MEMBER_ROLE_ID, BLOCKED_ROLE_ID
382
+ * mutate({ deviceId: '...', roleId: COORDINATOR_ROLE_ID })
383
+ * }
384
+ * ```
385
+ */
386
+ function useChangeMemberRole({ projectId }) {
387
+ const queryClient = (0, react_query_1.useQueryClient)();
388
+ const { data: projectApi } = useSingleProject({ projectId });
389
+ const { error, mutate, mutateAsync, reset, status } = (0, react_query_1.useMutation)((0, projects_js_1.changeMemberRoleMutationOptions)({ projectApi, projectId, queryClient }));
390
+ return status === 'error'
391
+ ? { error, mutate, mutateAsync, reset, status }
392
+ : { error: null, mutate, mutateAsync, reset, status };
393
+ }
368
394
  /**
369
395
  * Create a blob for a project.
370
396
  *
@@ -3,6 +3,6 @@ export { useClientApi, useIsArchiveDevice, useOwnDeviceInfo, useSetIsArchiveDevi
3
3
  export { useCreateDocument, useDeleteDocument, useManyDocs, useSingleDocByDocId, useSingleDocByVersionId, useUpdateDocument, } from './hooks/documents.js';
4
4
  export { useAcceptInvite, useManyInvites, useRejectInvite, useRequestCancelInvite, useSendInvite, useSetUpInvitesListeners, useSingleInvite, } from './hooks/invites.js';
5
5
  export { useMapStyleUrl } from './hooks/maps.js';
6
- export { useAddServerPeer, useAttachmentUrl, useConnectSyncServers, useCreateBlob, useCreateProject, useDataSyncProgress, useDisconnectSyncServers, useDocumentCreatedBy, useIconUrl, useImportProjectConfig, useLeaveProject, useManyMembers, useManyProjects, useOwnRoleInProject, useProjectSettings, useRemoveServerPeer, useSetAutostopDataSyncTimeout, useSingleMember, useSingleProject, useStartSync, useStopSync, useSyncState, useUpdateProjectSettings, useExportGeoJSON, useExportZipFile, } from './hooks/projects.js';
6
+ export { useAddServerPeer, useAttachmentUrl, useConnectSyncServers, useCreateBlob, useCreateProject, useDataSyncProgress, useDisconnectSyncServers, useDocumentCreatedBy, useIconUrl, useImportProjectConfig, useLeaveProject, useManyMembers, useManyProjects, useOwnRoleInProject, useProjectSettings, useRemoveServerPeer, useSetAutostopDataSyncTimeout, useSingleMember, useSingleProject, useStartSync, useStopSync, useSyncState, useUpdateProjectSettings, useChangeMemberRole, useExportGeoJSON, useExportZipFile, } from './hooks/projects.js';
7
7
  export { type SyncState } from './lib/sync.js';
8
8
  export { type WriteableDocument, type WriteableDocumentType, type WriteableValue, } from './lib/types.js';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useExportZipFile = exports.useExportGeoJSON = exports.useUpdateProjectSettings = exports.useSyncState = exports.useStopSync = exports.useStartSync = exports.useSingleProject = exports.useSingleMember = exports.useSetAutostopDataSyncTimeout = exports.useRemoveServerPeer = exports.useProjectSettings = exports.useOwnRoleInProject = exports.useManyProjects = exports.useManyMembers = exports.useLeaveProject = exports.useImportProjectConfig = exports.useIconUrl = exports.useDocumentCreatedBy = exports.useDisconnectSyncServers = exports.useDataSyncProgress = exports.useCreateProject = exports.useCreateBlob = exports.useConnectSyncServers = exports.useAttachmentUrl = exports.useAddServerPeer = exports.useMapStyleUrl = exports.useSingleInvite = exports.useSetUpInvitesListeners = exports.useSendInvite = exports.useRequestCancelInvite = exports.useRejectInvite = exports.useManyInvites = exports.useAcceptInvite = exports.useUpdateDocument = exports.useSingleDocByVersionId = exports.useSingleDocByDocId = exports.useManyDocs = exports.useDeleteDocument = exports.useCreateDocument = exports.useSetOwnDeviceInfo = exports.useSetIsArchiveDevice = exports.useOwnDeviceInfo = exports.useIsArchiveDevice = exports.useClientApi = exports.ClientApiProvider = exports.ClientApiContext = void 0;
3
+ exports.useExportZipFile = exports.useExportGeoJSON = exports.useChangeMemberRole = exports.useUpdateProjectSettings = exports.useSyncState = exports.useStopSync = exports.useStartSync = exports.useSingleProject = exports.useSingleMember = exports.useSetAutostopDataSyncTimeout = exports.useRemoveServerPeer = exports.useProjectSettings = exports.useOwnRoleInProject = exports.useManyProjects = exports.useManyMembers = exports.useLeaveProject = exports.useImportProjectConfig = exports.useIconUrl = exports.useDocumentCreatedBy = exports.useDisconnectSyncServers = exports.useDataSyncProgress = exports.useCreateProject = exports.useCreateBlob = exports.useConnectSyncServers = exports.useAttachmentUrl = exports.useAddServerPeer = exports.useMapStyleUrl = exports.useSingleInvite = exports.useSetUpInvitesListeners = exports.useSendInvite = exports.useRequestCancelInvite = exports.useRejectInvite = exports.useManyInvites = exports.useAcceptInvite = exports.useUpdateDocument = exports.useSingleDocByVersionId = exports.useSingleDocByDocId = exports.useManyDocs = exports.useDeleteDocument = exports.useCreateDocument = exports.useSetOwnDeviceInfo = exports.useSetIsArchiveDevice = exports.useOwnDeviceInfo = exports.useIsArchiveDevice = exports.useClientApi = exports.ClientApiProvider = exports.ClientApiContext = void 0;
4
4
  var ClientApi_js_1 = require("./contexts/ClientApi.js");
5
5
  Object.defineProperty(exports, "ClientApiContext", { enumerable: true, get: function () { return ClientApi_js_1.ClientApiContext; } });
6
6
  Object.defineProperty(exports, "ClientApiProvider", { enumerable: true, get: function () { return ClientApi_js_1.ClientApiProvider; } });
@@ -51,5 +51,6 @@ Object.defineProperty(exports, "useStartSync", { enumerable: true, get: function
51
51
  Object.defineProperty(exports, "useStopSync", { enumerable: true, get: function () { return projects_js_1.useStopSync; } });
52
52
  Object.defineProperty(exports, "useSyncState", { enumerable: true, get: function () { return projects_js_1.useSyncState; } });
53
53
  Object.defineProperty(exports, "useUpdateProjectSettings", { enumerable: true, get: function () { return projects_js_1.useUpdateProjectSettings; } });
54
+ Object.defineProperty(exports, "useChangeMemberRole", { enumerable: true, get: function () { return projects_js_1.useChangeMemberRole; } });
54
55
  Object.defineProperty(exports, "useExportGeoJSON", { enumerable: true, get: function () { return projects_js_1.useExportGeoJSON; } });
55
56
  Object.defineProperty(exports, "useExportZipFile", { enumerable: true, get: function () { return projects_js_1.useExportZipFile; } });
@@ -1,4 +1,4 @@
1
- import type { BlobApi } from '@comapeo/core' with { 'resolution-mode': 'import' };
1
+ import type { BlobApi, MemberApi } from '@comapeo/core' with { 'resolution-mode': 'import' };
2
2
  import type { MapeoClientApi, MapeoProjectApi } from '@comapeo/ipc' with { 'resolution-mode': 'import' };
3
3
  import type { ProjectSettings } from '@comapeo/schema' with { 'resolution-mode': 'import' };
4
4
  import { type QueryClient, type UnusedSkipTokenOptions } from '@tanstack/react-query';
@@ -28,7 +28,7 @@ export declare function getDocumentCreatedByQueryKey({ projectId, originalVersio
28
28
  * exposed right now, but it is the same for all projects, so no need for
29
29
  * scoping the query key to the project
30
30
  */
31
- export declare function getMediaServerPortQueryKey(): readonly ["@comapeo/core-react", "media_server_port"];
31
+ export declare function getMediaServerOriginQueryKey(): readonly ["@comapeo/core-react", "media_server_origin"];
32
32
  export declare function projectsQueryOptions({ clientApi, }: {
33
33
  clientApi: MapeoClientApi;
34
34
  }): import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<import("@comapeo/core/dist/mapeo-manager.js", { with: { "resolution-mode": "import" } }).ListedProject[], Error, import("@comapeo/core/dist/mapeo-manager.js", { with: { "resolution-mode": "import" } }).ListedProject[], readonly ["@comapeo/core-react", "projects"]>, "queryFn"> & {
@@ -100,12 +100,12 @@ export declare function documentCreatedByQueryOptions({ projectApi, projectId, o
100
100
  [dataTagErrorSymbol]: Error;
101
101
  };
102
102
  };
103
- export declare function mediaServerPortQueryOptions({ projectApi, }: {
103
+ export declare function mediaServerOriginQueryOptions({ projectApi, }: {
104
104
  projectApi: MapeoProjectApi;
105
- }): import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<string, Error, string, readonly ["@comapeo/core-react", "media_server_port"]>, "queryFn"> & {
106
- queryFn?: import("@tanstack/react-query").QueryFunction<string, readonly ["@comapeo/core-react", "media_server_port"], never> | undefined;
105
+ }): import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<string, Error, string, readonly ["@comapeo/core-react", "media_server_origin"]>, "queryFn"> & {
106
+ queryFn?: import("@tanstack/react-query").QueryFunction<string, readonly ["@comapeo/core-react", "media_server_origin"], never> | undefined;
107
107
  } & {
108
- queryKey: readonly ["@comapeo/core-react", "media_server_port"] & {
108
+ queryKey: readonly ["@comapeo/core-react", "media_server_origin"] & {
109
109
  [dataTagSymbol]: string;
110
110
  [dataTagErrorSymbol]: Error;
111
111
  };
@@ -186,6 +186,19 @@ export declare function updateProjectSettingsMutationOptions({ projectApi, query
186
186
  networkMode: "always";
187
187
  retry: false;
188
188
  };
189
+ export declare function changeMemberRoleMutationOptions({ projectApi, projectId, queryClient, }: {
190
+ projectApi: MapeoProjectApi;
191
+ projectId: string;
192
+ queryClient: QueryClient;
193
+ }): {
194
+ mutationFn: ({ deviceId, roleId }: {
195
+ deviceId: string;
196
+ roleId: MemberApi.RoleIdAssignableToOthers;
197
+ }) => Promise<void>;
198
+ onSuccess: () => void;
199
+ networkMode: "always";
200
+ retry: false;
201
+ };
189
202
  export declare function createBlobMutationOptions({ projectApi, }: {
190
203
  projectApi: MapeoProjectApi;
191
204
  }): {
@@ -7,7 +7,7 @@ exports.getProjectRoleQueryKey = getProjectRoleQueryKey;
7
7
  exports.getMembersQueryKey = getMembersQueryKey;
8
8
  exports.getMemberByIdQueryKey = getMemberByIdQueryKey;
9
9
  exports.getDocumentCreatedByQueryKey = getDocumentCreatedByQueryKey;
10
- exports.getMediaServerPortQueryKey = getMediaServerPortQueryKey;
10
+ exports.getMediaServerOriginQueryKey = getMediaServerOriginQueryKey;
11
11
  exports.projectsQueryOptions = projectsQueryOptions;
12
12
  exports.projectByIdQueryOptions = projectByIdQueryOptions;
13
13
  exports.projectSettingsQueryOptions = projectSettingsQueryOptions;
@@ -15,13 +15,14 @@ exports.projectMembersQueryOptions = projectMembersQueryOptions;
15
15
  exports.projectMemberByIdQueryOptions = projectMemberByIdQueryOptions;
16
16
  exports.projectOwnRoleQueryOptions = projectOwnRoleQueryOptions;
17
17
  exports.documentCreatedByQueryOptions = documentCreatedByQueryOptions;
18
- exports.mediaServerPortQueryOptions = mediaServerPortQueryOptions;
18
+ exports.mediaServerOriginQueryOptions = mediaServerOriginQueryOptions;
19
19
  exports.addServerPeerMutationOptions = addServerPeerMutationOptions;
20
20
  exports.removeServerPeerMutationOptions = removeServerPeerMutationOptions;
21
21
  exports.createProjectMutationOptions = createProjectMutationOptions;
22
22
  exports.leaveProjectMutationOptions = leaveProjectMutationOptions;
23
23
  exports.importProjectConfigMutationOptions = importProjectConfigMutationOptions;
24
24
  exports.updateProjectSettingsMutationOptions = updateProjectSettingsMutationOptions;
25
+ exports.changeMemberRoleMutationOptions = changeMemberRoleMutationOptions;
25
26
  exports.createBlobMutationOptions = createBlobMutationOptions;
26
27
  exports.startSyncMutationOptions = startSyncMutationOptions;
27
28
  exports.stopSyncMutationOptions = stopSyncMutationOptions;
@@ -64,8 +65,8 @@ function getDocumentCreatedByQueryKey({ projectId, originalVersionId, }) {
64
65
  * exposed right now, but it is the same for all projects, so no need for
65
66
  * scoping the query key to the project
66
67
  */
67
- function getMediaServerPortQueryKey() {
68
- return [shared_js_1.ROOT_QUERY_KEY, 'media_server_port'];
68
+ function getMediaServerOriginQueryKey() {
69
+ return [shared_js_1.ROOT_QUERY_KEY, 'media_server_origin'];
69
70
  }
70
71
  function projectsQueryOptions({ clientApi, }) {
71
72
  return (0, react_query_1.queryOptions)({
@@ -142,17 +143,17 @@ const FAKE_BLOB_ID = {
142
143
  name: 'name',
143
144
  driveId: 'drive-id',
144
145
  };
145
- function mediaServerPortQueryOptions({ projectApi, }) {
146
+ function mediaServerOriginQueryOptions({ projectApi, }) {
146
147
  return (0, react_query_1.queryOptions)({
147
148
  ...(0, shared_js_1.baseQueryOptions)(),
148
- // HACK: The server doesn't yet expose a method to get its port, so we use
149
- // the existing $blobs.getUrl() to get the port with a fake BlobId. The port
149
+ // HACK: The server doesn't yet expose a method to get its origin, so we use
150
+ // the existing $blobs.getUrl() to get the origin with a fake BlobId. The origin
150
151
  // is the same regardless of the blobId, so it's not necessary to include it
151
152
  // as a dep for the query key.
152
- queryKey: getMediaServerPortQueryKey(),
153
+ queryKey: getMediaServerOriginQueryKey(),
153
154
  queryFn: async () => {
154
155
  const url = await projectApi.$blobs.getUrl(FAKE_BLOB_ID);
155
- return new URL(url).port;
156
+ return new URL(url).origin;
156
157
  },
157
158
  staleTime: 'static',
158
159
  gcTime: Infinity,
@@ -247,6 +248,22 @@ function updateProjectSettingsMutationOptions({ projectApi, queryClient, }) {
247
248
  },
248
249
  };
249
250
  }
251
+ function changeMemberRoleMutationOptions({ projectApi, projectId, queryClient, }) {
252
+ return {
253
+ ...(0, shared_js_1.baseMutationOptions)(),
254
+ mutationFn: async ({ deviceId, roleId }) => {
255
+ return projectApi.$member.assignRole(deviceId, roleId);
256
+ },
257
+ onSuccess: () => {
258
+ queryClient.invalidateQueries({
259
+ queryKey: getMembersQueryKey({ projectId }),
260
+ });
261
+ queryClient.invalidateQueries({
262
+ queryKey: getProjectRoleQueryKey({ projectId }),
263
+ });
264
+ },
265
+ };
266
+ }
250
267
  function createBlobMutationOptions({ projectApi, }) {
251
268
  return {
252
269
  ...(0, shared_js_1.baseMutationOptions)(),
@@ -2,11 +2,14 @@ import type { BlobApi, IconApi } from '@comapeo/core';
2
2
  /**
3
3
  * Get a url for a blob based on its BlobId
4
4
  */
5
- export declare function getBlobUrl(baseUrl: string, blobId: BlobApi.BlobId): string;
6
- /**
7
- * @param {string} iconId
8
- * @param {BitmapOpts | SvgOpts} opts
9
- *
10
- * @returns {Promise<string>}
11
- */
12
- export declare function getIconUrl(baseUrl: string, iconId: string, opts: IconApi.BitmapOpts | IconApi.SvgOpts): string;
5
+ export declare function getBlobUrl({ serverOrigin, projectId, blobId, }: {
6
+ serverOrigin: string;
7
+ projectId: string;
8
+ blobId: BlobApi.BlobId;
9
+ }): string;
10
+ export declare function getIconUrl({ serverOrigin, iconId, projectId, mimeBasedOpts, }: {
11
+ serverOrigin: string;
12
+ iconId: string;
13
+ projectId: string;
14
+ mimeBasedOpts: IconApi.BitmapOpts | IconApi.SvgOpts;
15
+ }): string;
@@ -1,6 +1,12 @@
1
1
  "use strict";
2
2
  // TODO: Move these into a separate "@comapeo/asset-server" module which can
3
3
  // export them to be imported directly in a client.
4
+ //
5
+ // NB: The URL construction is fragile right now - it must match the
6
+ // implementation in the @comapeo/core fastify plugins
7
+ // [blobServerPlugin](https://github.com/digidem/comapeo-core/blob/main/src/fastify-plugins/blobs.js)
8
+ // and
9
+ // [iconServerPlugin](https://github.com/digidem/comapeo-core/blob/main/src/fastify-plugins/icons.js)
4
10
  Object.defineProperty(exports, "__esModule", { value: true });
5
11
  exports.getBlobUrl = getBlobUrl;
6
12
  exports.getIconUrl = getIconUrl;
@@ -11,34 +17,22 @@ const MIME_TO_EXTENSION = {
11
17
  /**
12
18
  * Get a url for a blob based on its BlobId
13
19
  */
14
- function getBlobUrl(baseUrl, blobId) {
20
+ function getBlobUrl({ serverOrigin, projectId, blobId, }) {
15
21
  const { driveId, type, variant, name } = blobId;
16
- if (!baseUrl.endsWith('/')) {
17
- baseUrl += '/';
18
- }
19
- return baseUrl + `${driveId}/${type}/${variant}/${name}`;
22
+ return `${serverOrigin}/blobs/${projectId}/${driveId}/${type}/${variant}/${name}`;
20
23
  }
21
- /**
22
- * @param {string} iconId
23
- * @param {BitmapOpts | SvgOpts} opts
24
- *
25
- * @returns {Promise<string>}
26
- */
27
- function getIconUrl(baseUrl, iconId, opts) {
28
- if (!baseUrl.endsWith('/')) {
29
- baseUrl += '/';
30
- }
31
- const mimeExtension = MIME_TO_EXTENSION[opts.mimeType];
32
- const pixelDensity = opts.mimeType === 'image/svg+xml' ||
24
+ function getIconUrl({ serverOrigin, iconId, projectId, mimeBasedOpts, }) {
25
+ const mimeExtension = MIME_TO_EXTENSION[mimeBasedOpts.mimeType];
26
+ const pixelDensity = mimeBasedOpts.mimeType === 'image/svg+xml' ||
33
27
  // if the pixel density is 1, we can omit the density suffix in the resulting url
34
28
  // and assume the pixel density is 1 for applicable mime types when using the url
35
- opts.pixelDensity === 1
29
+ mimeBasedOpts.pixelDensity === 1
36
30
  ? undefined
37
- : opts.pixelDensity;
38
- return (baseUrl +
31
+ : mimeBasedOpts.pixelDensity;
32
+ return (`${serverOrigin}/icons/${projectId}/` +
39
33
  constructIconPath({
40
34
  pixelDensity,
41
- size: opts.size,
35
+ size: mimeBasedOpts.size,
42
36
  extension: mimeExtension,
43
37
  iconId,
44
38
  }));
@@ -430,6 +430,47 @@ export declare function useUpdateProjectSettings({ projectId }: {
430
430
  reset: () => void;
431
431
  status: "pending" | "success" | "idle";
432
432
  };
433
+ /**
434
+ * Change a project member's role.
435
+ *
436
+ * @param opts.projectId Project public ID
437
+ *
438
+ * @example
439
+ * ```tsx
440
+ * function BasicExample() {
441
+ * const { mutate } = useChangeMemberRole({ projectId: '...' })
442
+ * // Use one of: COORDINATOR_ROLE_ID, MEMBER_ROLE_ID, BLOCKED_ROLE_ID
443
+ * mutate({ deviceId: '...', roleId: COORDINATOR_ROLE_ID })
444
+ * }
445
+ * ```
446
+ */
447
+ export declare function useChangeMemberRole({ projectId }: {
448
+ projectId: string;
449
+ }): {
450
+ error: Error;
451
+ mutate: import("@tanstack/react-query").UseMutateFunction<void, Error, {
452
+ deviceId: string;
453
+ roleId: import("@comapeo/core").MemberApi.RoleIdAssignableToOthers;
454
+ }, unknown>;
455
+ mutateAsync: import("@tanstack/react-query").UseMutateAsyncFunction<void, Error, {
456
+ deviceId: string;
457
+ roleId: import("@comapeo/core").MemberApi.RoleIdAssignableToOthers;
458
+ }, unknown>;
459
+ reset: () => void;
460
+ status: "error";
461
+ } | {
462
+ error: null;
463
+ mutate: import("@tanstack/react-query").UseMutateFunction<void, Error, {
464
+ deviceId: string;
465
+ roleId: import("@comapeo/core").MemberApi.RoleIdAssignableToOthers;
466
+ }, unknown>;
467
+ mutateAsync: import("@tanstack/react-query").UseMutateAsyncFunction<void, Error, {
468
+ deviceId: string;
469
+ roleId: import("@comapeo/core").MemberApi.RoleIdAssignableToOthers;
470
+ }, unknown>;
471
+ reset: () => void;
472
+ status: "pending" | "success" | "idle";
473
+ };
433
474
  /**
434
475
  * Create a blob for a project.
435
476
  *
@@ -626,13 +667,7 @@ export declare function useExportGeoJSON({ projectId }: {
626
667
  mutate: import("@tanstack/react-query").UseMutateFunction<string, Error, {
627
668
  path: string;
628
669
  exportOptions: {
629
- observations
630
- /**
631
- * Update the settings of a project.
632
- *
633
- * @param opts.projectId Public ID of the project to apply changes to.
634
- */
635
- ?: boolean;
670
+ observations?: boolean;
636
671
  tracks?: boolean;
637
672
  lang?: string;
638
673
  };
@@ -640,13 +675,7 @@ export declare function useExportGeoJSON({ projectId }: {
640
675
  mutateAsync: import("@tanstack/react-query").UseMutateAsyncFunction<string, Error, {
641
676
  path: string;
642
677
  exportOptions: {
643
- observations
644
- /**
645
- * Update the settings of a project.
646
- *
647
- * @param opts.projectId Public ID of the project to apply changes to.
648
- */
649
- ?: boolean;
678
+ observations?: boolean;
650
679
  tracks?: boolean;
651
680
  lang?: string;
652
681
  };
@@ -658,13 +687,7 @@ export declare function useExportGeoJSON({ projectId }: {
658
687
  mutate: import("@tanstack/react-query").UseMutateFunction<string, Error, {
659
688
  path: string;
660
689
  exportOptions: {
661
- observations
662
- /**
663
- * Update the settings of a project.
664
- *
665
- * @param opts.projectId Public ID of the project to apply changes to.
666
- */
667
- ?: boolean;
690
+ observations?: boolean;
668
691
  tracks?: boolean;
669
692
  lang?: string;
670
693
  };
@@ -672,13 +695,7 @@ export declare function useExportGeoJSON({ projectId }: {
672
695
  mutateAsync: import("@tanstack/react-query").UseMutateAsyncFunction<string, Error, {
673
696
  path: string;
674
697
  exportOptions: {
675
- observations
676
- /**
677
- * Update the settings of a project.
678
- *
679
- * @param opts.projectId Public ID of the project to apply changes to.
680
- */
681
- ?: boolean;
698
+ observations?: boolean;
682
699
  tracks?: boolean;
683
700
  lang?: string;
684
701
  };
@@ -1,6 +1,6 @@
1
1
  import { useMutation, useQueryClient, useSuspenseQuery, } from '@tanstack/react-query';
2
2
  import { useSyncExternalStore } from 'react';
3
- import { addServerPeerMutationOptions, connectSyncServersMutationOptions, createBlobMutationOptions, createProjectMutationOptions, disconnectSyncServersMutationOptions, documentCreatedByQueryOptions, exportGeoJSONMutationOptions, exportZipFileMutationOptions, importProjectConfigMutationOptions, leaveProjectMutationOptions, mediaServerPortQueryOptions, projectByIdQueryOptions, projectMemberByIdQueryOptions, projectMembersQueryOptions, projectOwnRoleQueryOptions, projectSettingsQueryOptions, projectsQueryOptions, removeServerPeerMutationOptions, setAutostopDataSyncTimeoutMutationOptions, startSyncMutationOptions, stopSyncMutationOptions, updateProjectSettingsMutationOptions, } from '../lib/react-query/projects.js';
3
+ import { addServerPeerMutationOptions, changeMemberRoleMutationOptions, connectSyncServersMutationOptions, createBlobMutationOptions, createProjectMutationOptions, disconnectSyncServersMutationOptions, documentCreatedByQueryOptions, exportGeoJSONMutationOptions, exportZipFileMutationOptions, importProjectConfigMutationOptions, leaveProjectMutationOptions, mediaServerOriginQueryOptions, projectByIdQueryOptions, projectMemberByIdQueryOptions, projectMembersQueryOptions, projectOwnRoleQueryOptions, projectSettingsQueryOptions, projectsQueryOptions, removeServerPeerMutationOptions, setAutostopDataSyncTimeoutMutationOptions, startSyncMutationOptions, stopSyncMutationOptions, updateProjectSettingsMutationOptions, } from '../lib/react-query/projects.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';
@@ -159,9 +159,13 @@ export function useManyMembers({ projectId }) {
159
159
  */
160
160
  export function useIconUrl({ projectId, iconId, ...mimeBasedOpts }) {
161
161
  const { data: projectApi } = useSingleProject({ projectId });
162
- const { data: port, error, isRefetching } = useMediaServerPort({ projectApi });
163
- const baseUrl = `http://127.0.0.1:${port}`;
164
- const iconUrl = getIconUrl(baseUrl, iconId, mimeBasedOpts);
162
+ const { data: serverOrigin, error, isRefetching, } = useMediaServerOrigin({ projectApi });
163
+ const iconUrl = getIconUrl({
164
+ serverOrigin,
165
+ iconId,
166
+ projectId,
167
+ mimeBasedOpts,
168
+ });
165
169
  return { data: iconUrl, error, isRefetching };
166
170
  }
167
171
  /**
@@ -217,17 +221,16 @@ export function useIconUrl({ projectId, iconId, ...mimeBasedOpts }) {
217
221
  */
218
222
  export function useAttachmentUrl({ projectId, blobId, }) {
219
223
  const { data: projectApi } = useSingleProject({ projectId });
220
- const { data: port, error, isRefetching } = useMediaServerPort({ projectApi });
221
- const baseUrl = `http://127.0.0.1:${port}`;
222
- const blobUrl = getBlobUrl(baseUrl, blobId);
224
+ const { data: serverOrigin, error, isRefetching, } = useMediaServerOrigin({ projectApi });
225
+ const blobUrl = getBlobUrl({ serverOrigin, projectId, blobId });
223
226
  return { data: blobUrl, error, isRefetching };
224
227
  }
225
228
  /**
226
229
  * @internal
227
- * Hack to retrieve the media server port.
230
+ * Hack to retrieve the media server origin (protocol + host).
228
231
  */
229
- function useMediaServerPort({ projectApi }) {
230
- const { data, error, isRefetching } = useSuspenseQuery(mediaServerPortQueryOptions({
232
+ function useMediaServerOrigin({ projectApi }) {
233
+ const { data, error, isRefetching } = useSuspenseQuery(mediaServerOriginQueryOptions({
231
234
  projectApi,
232
235
  }));
233
236
  return { data, error, isRefetching };
@@ -338,6 +341,28 @@ export function useUpdateProjectSettings({ projectId }) {
338
341
  ? { error, mutate, mutateAsync, reset, status }
339
342
  : { error: null, mutate, mutateAsync, reset, status };
340
343
  }
344
+ /**
345
+ * Change a project member's role.
346
+ *
347
+ * @param opts.projectId Project public ID
348
+ *
349
+ * @example
350
+ * ```tsx
351
+ * function BasicExample() {
352
+ * const { mutate } = useChangeMemberRole({ projectId: '...' })
353
+ * // Use one of: COORDINATOR_ROLE_ID, MEMBER_ROLE_ID, BLOCKED_ROLE_ID
354
+ * mutate({ deviceId: '...', roleId: COORDINATOR_ROLE_ID })
355
+ * }
356
+ * ```
357
+ */
358
+ export function useChangeMemberRole({ projectId }) {
359
+ const queryClient = useQueryClient();
360
+ const { data: projectApi } = useSingleProject({ projectId });
361
+ const { error, mutate, mutateAsync, reset, status } = useMutation(changeMemberRoleMutationOptions({ projectApi, projectId, queryClient }));
362
+ return status === 'error'
363
+ ? { error, mutate, mutateAsync, reset, status }
364
+ : { error: null, mutate, mutateAsync, reset, status };
365
+ }
341
366
  /**
342
367
  * Create a blob for a project.
343
368
  *
@@ -3,6 +3,6 @@ export { useClientApi, useIsArchiveDevice, useOwnDeviceInfo, useSetIsArchiveDevi
3
3
  export { useCreateDocument, useDeleteDocument, useManyDocs, useSingleDocByDocId, useSingleDocByVersionId, useUpdateDocument, } from './hooks/documents.js';
4
4
  export { useAcceptInvite, useManyInvites, useRejectInvite, useRequestCancelInvite, useSendInvite, useSetUpInvitesListeners, useSingleInvite, } from './hooks/invites.js';
5
5
  export { useMapStyleUrl } from './hooks/maps.js';
6
- export { useAddServerPeer, useAttachmentUrl, useConnectSyncServers, useCreateBlob, useCreateProject, useDataSyncProgress, useDisconnectSyncServers, useDocumentCreatedBy, useIconUrl, useImportProjectConfig, useLeaveProject, useManyMembers, useManyProjects, useOwnRoleInProject, useProjectSettings, useRemoveServerPeer, useSetAutostopDataSyncTimeout, useSingleMember, useSingleProject, useStartSync, useStopSync, useSyncState, useUpdateProjectSettings, useExportGeoJSON, useExportZipFile, } from './hooks/projects.js';
6
+ export { useAddServerPeer, useAttachmentUrl, useConnectSyncServers, useCreateBlob, useCreateProject, useDataSyncProgress, useDisconnectSyncServers, useDocumentCreatedBy, useIconUrl, useImportProjectConfig, useLeaveProject, useManyMembers, useManyProjects, useOwnRoleInProject, useProjectSettings, useRemoveServerPeer, useSetAutostopDataSyncTimeout, useSingleMember, useSingleProject, useStartSync, useStopSync, useSyncState, useUpdateProjectSettings, useChangeMemberRole, useExportGeoJSON, useExportZipFile, } from './hooks/projects.js';
7
7
  export { type SyncState } from './lib/sync.js';
8
8
  export { type WriteableDocument, type WriteableDocumentType, type WriteableValue, } from './lib/types.js';
package/dist/esm/index.js CHANGED
@@ -3,4 +3,4 @@ export { useClientApi, useIsArchiveDevice, useOwnDeviceInfo, useSetIsArchiveDevi
3
3
  export { useCreateDocument, useDeleteDocument, useManyDocs, useSingleDocByDocId, useSingleDocByVersionId, useUpdateDocument, } from './hooks/documents.js';
4
4
  export { useAcceptInvite, useManyInvites, useRejectInvite, useRequestCancelInvite, useSendInvite, useSetUpInvitesListeners, useSingleInvite, } from './hooks/invites.js';
5
5
  export { useMapStyleUrl } from './hooks/maps.js';
6
- export { useAddServerPeer, useAttachmentUrl, useConnectSyncServers, useCreateBlob, useCreateProject, useDataSyncProgress, useDisconnectSyncServers, useDocumentCreatedBy, useIconUrl, useImportProjectConfig, useLeaveProject, useManyMembers, useManyProjects, useOwnRoleInProject, useProjectSettings, useRemoveServerPeer, useSetAutostopDataSyncTimeout, useSingleMember, useSingleProject, useStartSync, useStopSync, useSyncState, useUpdateProjectSettings, useExportGeoJSON, useExportZipFile, } from './hooks/projects.js';
6
+ export { useAddServerPeer, useAttachmentUrl, useConnectSyncServers, useCreateBlob, useCreateProject, useDataSyncProgress, useDisconnectSyncServers, useDocumentCreatedBy, useIconUrl, useImportProjectConfig, useLeaveProject, useManyMembers, useManyProjects, useOwnRoleInProject, useProjectSettings, useRemoveServerPeer, useSetAutostopDataSyncTimeout, useSingleMember, useSingleProject, useStartSync, useStopSync, useSyncState, useUpdateProjectSettings, useChangeMemberRole, useExportGeoJSON, useExportZipFile, } from './hooks/projects.js';
@@ -1,4 +1,4 @@
1
- import type { BlobApi } from '@comapeo/core' with { 'resolution-mode': 'import' };
1
+ import type { BlobApi, MemberApi } from '@comapeo/core' with { 'resolution-mode': 'import' };
2
2
  import type { MapeoClientApi, MapeoProjectApi } from '@comapeo/ipc' with { 'resolution-mode': 'import' };
3
3
  import type { ProjectSettings } from '@comapeo/schema' with { 'resolution-mode': 'import' };
4
4
  import { type QueryClient, type UnusedSkipTokenOptions } from '@tanstack/react-query';
@@ -28,7 +28,7 @@ export declare function getDocumentCreatedByQueryKey({ projectId, originalVersio
28
28
  * exposed right now, but it is the same for all projects, so no need for
29
29
  * scoping the query key to the project
30
30
  */
31
- export declare function getMediaServerPortQueryKey(): readonly ["@comapeo/core-react", "media_server_port"];
31
+ export declare function getMediaServerOriginQueryKey(): readonly ["@comapeo/core-react", "media_server_origin"];
32
32
  export declare function projectsQueryOptions({ clientApi, }: {
33
33
  clientApi: MapeoClientApi;
34
34
  }): import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<import("@comapeo/core/dist/mapeo-manager.js").ListedProject[], Error, import("@comapeo/core/dist/mapeo-manager.js").ListedProject[], readonly ["@comapeo/core-react", "projects"]>, "queryFn"> & {
@@ -100,12 +100,12 @@ export declare function documentCreatedByQueryOptions({ projectApi, projectId, o
100
100
  [dataTagErrorSymbol]: Error;
101
101
  };
102
102
  };
103
- export declare function mediaServerPortQueryOptions({ projectApi, }: {
103
+ export declare function mediaServerOriginQueryOptions({ projectApi, }: {
104
104
  projectApi: MapeoProjectApi;
105
- }): import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<string, Error, string, readonly ["@comapeo/core-react", "media_server_port"]>, "queryFn"> & {
106
- queryFn?: import("@tanstack/react-query").QueryFunction<string, readonly ["@comapeo/core-react", "media_server_port"], never> | undefined;
105
+ }): import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<string, Error, string, readonly ["@comapeo/core-react", "media_server_origin"]>, "queryFn"> & {
106
+ queryFn?: import("@tanstack/react-query").QueryFunction<string, readonly ["@comapeo/core-react", "media_server_origin"], never> | undefined;
107
107
  } & {
108
- queryKey: readonly ["@comapeo/core-react", "media_server_port"] & {
108
+ queryKey: readonly ["@comapeo/core-react", "media_server_origin"] & {
109
109
  [dataTagSymbol]: string;
110
110
  [dataTagErrorSymbol]: Error;
111
111
  };
@@ -186,6 +186,19 @@ export declare function updateProjectSettingsMutationOptions({ projectApi, query
186
186
  networkMode: "always";
187
187
  retry: false;
188
188
  };
189
+ export declare function changeMemberRoleMutationOptions({ projectApi, projectId, queryClient, }: {
190
+ projectApi: MapeoProjectApi;
191
+ projectId: string;
192
+ queryClient: QueryClient;
193
+ }): {
194
+ mutationFn: ({ deviceId, roleId }: {
195
+ deviceId: string;
196
+ roleId: MemberApi.RoleIdAssignableToOthers;
197
+ }) => Promise<void>;
198
+ onSuccess: () => void;
199
+ networkMode: "always";
200
+ retry: false;
201
+ };
189
202
  export declare function createBlobMutationOptions({ projectApi, }: {
190
203
  projectApi: MapeoProjectApi;
191
204
  }): {
@@ -32,8 +32,8 @@ export function getDocumentCreatedByQueryKey({ projectId, originalVersionId, })
32
32
  * exposed right now, but it is the same for all projects, so no need for
33
33
  * scoping the query key to the project
34
34
  */
35
- export function getMediaServerPortQueryKey() {
36
- return [ROOT_QUERY_KEY, 'media_server_port'];
35
+ export function getMediaServerOriginQueryKey() {
36
+ return [ROOT_QUERY_KEY, 'media_server_origin'];
37
37
  }
38
38
  export function projectsQueryOptions({ clientApi, }) {
39
39
  return queryOptions({
@@ -110,17 +110,17 @@ const FAKE_BLOB_ID = {
110
110
  name: 'name',
111
111
  driveId: 'drive-id',
112
112
  };
113
- export function mediaServerPortQueryOptions({ projectApi, }) {
113
+ export function mediaServerOriginQueryOptions({ projectApi, }) {
114
114
  return queryOptions({
115
115
  ...baseQueryOptions(),
116
- // HACK: The server doesn't yet expose a method to get its port, so we use
117
- // the existing $blobs.getUrl() to get the port with a fake BlobId. The port
116
+ // HACK: The server doesn't yet expose a method to get its origin, so we use
117
+ // the existing $blobs.getUrl() to get the origin with a fake BlobId. The origin
118
118
  // is the same regardless of the blobId, so it's not necessary to include it
119
119
  // as a dep for the query key.
120
- queryKey: getMediaServerPortQueryKey(),
120
+ queryKey: getMediaServerOriginQueryKey(),
121
121
  queryFn: async () => {
122
122
  const url = await projectApi.$blobs.getUrl(FAKE_BLOB_ID);
123
- return new URL(url).port;
123
+ return new URL(url).origin;
124
124
  },
125
125
  staleTime: 'static',
126
126
  gcTime: Infinity,
@@ -215,6 +215,22 @@ export function updateProjectSettingsMutationOptions({ projectApi, queryClient,
215
215
  },
216
216
  };
217
217
  }
218
+ export function changeMemberRoleMutationOptions({ projectApi, projectId, queryClient, }) {
219
+ return {
220
+ ...baseMutationOptions(),
221
+ mutationFn: async ({ deviceId, roleId }) => {
222
+ return projectApi.$member.assignRole(deviceId, roleId);
223
+ },
224
+ onSuccess: () => {
225
+ queryClient.invalidateQueries({
226
+ queryKey: getMembersQueryKey({ projectId }),
227
+ });
228
+ queryClient.invalidateQueries({
229
+ queryKey: getProjectRoleQueryKey({ projectId }),
230
+ });
231
+ },
232
+ };
233
+ }
218
234
  export function createBlobMutationOptions({ projectApi, }) {
219
235
  return {
220
236
  ...baseMutationOptions(),
@@ -2,11 +2,14 @@ import type { BlobApi, IconApi } from '@comapeo/core';
2
2
  /**
3
3
  * Get a url for a blob based on its BlobId
4
4
  */
5
- export declare function getBlobUrl(baseUrl: string, blobId: BlobApi.BlobId): string;
6
- /**
7
- * @param {string} iconId
8
- * @param {BitmapOpts | SvgOpts} opts
9
- *
10
- * @returns {Promise<string>}
11
- */
12
- export declare function getIconUrl(baseUrl: string, iconId: string, opts: IconApi.BitmapOpts | IconApi.SvgOpts): string;
5
+ export declare function getBlobUrl({ serverOrigin, projectId, blobId, }: {
6
+ serverOrigin: string;
7
+ projectId: string;
8
+ blobId: BlobApi.BlobId;
9
+ }): string;
10
+ export declare function getIconUrl({ serverOrigin, iconId, projectId, mimeBasedOpts, }: {
11
+ serverOrigin: string;
12
+ iconId: string;
13
+ projectId: string;
14
+ mimeBasedOpts: IconApi.BitmapOpts | IconApi.SvgOpts;
15
+ }): string;
@@ -1,5 +1,11 @@
1
1
  // TODO: Move these into a separate "@comapeo/asset-server" module which can
2
2
  // export them to be imported directly in a client.
3
+ //
4
+ // NB: The URL construction is fragile right now - it must match the
5
+ // implementation in the @comapeo/core fastify plugins
6
+ // [blobServerPlugin](https://github.com/digidem/comapeo-core/blob/main/src/fastify-plugins/blobs.js)
7
+ // and
8
+ // [iconServerPlugin](https://github.com/digidem/comapeo-core/blob/main/src/fastify-plugins/icons.js)
3
9
  const MIME_TO_EXTENSION = {
4
10
  'image/png': '.png',
5
11
  'image/svg+xml': '.svg',
@@ -7,34 +13,22 @@ const MIME_TO_EXTENSION = {
7
13
  /**
8
14
  * Get a url for a blob based on its BlobId
9
15
  */
10
- export function getBlobUrl(baseUrl, blobId) {
16
+ export function getBlobUrl({ serverOrigin, projectId, blobId, }) {
11
17
  const { driveId, type, variant, name } = blobId;
12
- if (!baseUrl.endsWith('/')) {
13
- baseUrl += '/';
14
- }
15
- return baseUrl + `${driveId}/${type}/${variant}/${name}`;
18
+ return `${serverOrigin}/blobs/${projectId}/${driveId}/${type}/${variant}/${name}`;
16
19
  }
17
- /**
18
- * @param {string} iconId
19
- * @param {BitmapOpts | SvgOpts} opts
20
- *
21
- * @returns {Promise<string>}
22
- */
23
- export function getIconUrl(baseUrl, iconId, opts) {
24
- if (!baseUrl.endsWith('/')) {
25
- baseUrl += '/';
26
- }
27
- const mimeExtension = MIME_TO_EXTENSION[opts.mimeType];
28
- const pixelDensity = opts.mimeType === 'image/svg+xml' ||
20
+ export function getIconUrl({ serverOrigin, iconId, projectId, mimeBasedOpts, }) {
21
+ const mimeExtension = MIME_TO_EXTENSION[mimeBasedOpts.mimeType];
22
+ const pixelDensity = mimeBasedOpts.mimeType === 'image/svg+xml' ||
29
23
  // if the pixel density is 1, we can omit the density suffix in the resulting url
30
24
  // and assume the pixel density is 1 for applicable mime types when using the url
31
- opts.pixelDensity === 1
25
+ mimeBasedOpts.pixelDensity === 1
32
26
  ? undefined
33
- : opts.pixelDensity;
34
- return (baseUrl +
27
+ : mimeBasedOpts.pixelDensity;
28
+ return (`${serverOrigin}/icons/${projectId}/` +
35
29
  constructIconPath({
36
30
  pixelDensity,
37
- size: opts.size,
31
+ size: mimeBasedOpts.size,
38
32
  extension: mimeExtension,
39
33
  iconId,
40
34
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comapeo/core-react",
3
- "version": "6.1.0",
3
+ "version": "6.2.0",
4
4
  "description": "React wrapper for working with @comapeo/core",
5
5
  "repository": {
6
6
  "type": "git",
@@ -57,16 +57,16 @@
57
57
  "types": "tsc"
58
58
  },
59
59
  "peerDependencies": {
60
- "@comapeo/core": "^4.1.3",
60
+ "@comapeo/core": "^4.3.0",
61
61
  "@comapeo/ipc": "^5.0.0",
62
62
  "@comapeo/schema": "*",
63
63
  "@tanstack/react-query": "^5",
64
64
  "react": "^18 || ^19"
65
65
  },
66
66
  "devDependencies": {
67
- "@comapeo/core": "4.1.3",
67
+ "@comapeo/core": "4.3.0",
68
68
  "@comapeo/ipc": "5.0.0",
69
- "@comapeo/schema": "2.0.0",
69
+ "@comapeo/schema": "2.1.1",
70
70
  "@eslint/compat": "1.3.0",
71
71
  "@eslint/js": "9.29.0",
72
72
  "@ianvs/prettier-plugin-sort-imports": "4.4.2",
@@ -84,6 +84,7 @@
84
84
  "eslint-plugin-testing-library": "7.5.3",
85
85
  "fastify": "4.29.1",
86
86
  "globals": "16.2.0",
87
+ "happy-dom": "18.0.1",
87
88
  "husky": "9.1.7",
88
89
  "lint-staged": "15.5.1",
89
90
  "npm-run-all2": "7.0.2",