@comapeo/core-react 10.0.0 → 11.0.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/commonjs/contexts/MapShares.d.ts +1 -1
- package/dist/commonjs/hooks/documents.d.ts +1 -0
- package/dist/commonjs/hooks/documents.js +0 -1
- package/dist/commonjs/hooks/maps.d.ts +33 -15
- package/dist/commonjs/hooks/maps.js +33 -15
- package/dist/commonjs/index.d.ts +1 -1
- package/dist/commonjs/index.js +6 -2
- package/dist/commonjs/lib/map-shares-stores.d.ts +120 -3
- package/dist/commonjs/lib/map-shares-stores.js +170 -8
- package/dist/esm/contexts/MapShares.d.ts +1 -1
- package/dist/esm/hooks/documents.d.ts +1 -0
- package/dist/esm/hooks/documents.js +0 -1
- package/dist/esm/hooks/maps.d.ts +33 -15
- package/dist/esm/hooks/maps.js +33 -15
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/lib/map-shares-stores.d.ts +120 -3
- package/dist/esm/lib/map-shares-stores.js +166 -7
- package/docs/API.md +4 -9
- package/package.json +6 -6
|
@@ -42,7 +42,7 @@ export declare function useReceivedMapSharesState<T>(selector: (state: Array<Rec
|
|
|
42
42
|
* @internal
|
|
43
43
|
*/
|
|
44
44
|
export declare function useSentMapSharesActions(): {
|
|
45
|
-
createAndSend({
|
|
45
|
+
createAndSend({ receiverDeviceId, mapId, }: import("../lib/map-shares-stores.js").CreateAndSendMapShareOptions): Promise<import("@comapeo/map-server", { with: { "resolution-mode": "import" } }).MapShareState>;
|
|
46
46
|
cancel({ shareId }: import("../lib/map-shares-stores.js").CancelMapShareOptions): Promise<void>;
|
|
47
47
|
};
|
|
48
48
|
/**
|
|
@@ -123,6 +123,7 @@ export declare function useUpdateDocument<D extends WriteableDocumentType>({ doc
|
|
|
123
123
|
projectId: string;
|
|
124
124
|
}): FilteredMutationResult<UseMutationResult<Awaited<ReturnType<MapeoProjectApi[D]['update']>>, Error, {
|
|
125
125
|
value: Omit<WriteableValue<D>, 'schemaName'>;
|
|
126
|
+
versionId: string;
|
|
126
127
|
}>>;
|
|
127
128
|
/**
|
|
128
129
|
* Delete a document within a project.
|
|
@@ -199,7 +199,6 @@ function useUpdateDocument({
|
|
|
199
199
|
docType, projectId, }) {
|
|
200
200
|
const queryClient = (0, react_query_1.useQueryClient)();
|
|
201
201
|
const { data: projectApi } = (0, projects_js_1.useSingleProject)({ projectId });
|
|
202
|
-
// @ts-expect-error Not sure why TS complains here
|
|
203
202
|
return (0, react_query_js_1.filterMutationResult)((0, react_query_1.useMutation)({
|
|
204
203
|
...(0, react_query_js_1.baseMutationOptions)(),
|
|
205
204
|
mutationFn: async ({ versionId, value, }) => {
|
|
@@ -156,15 +156,25 @@ export declare function useSingleReceivedMapShare({ shareId }: {
|
|
|
156
156
|
/**
|
|
157
157
|
* Accept and download a map share that has been received. The mutate promise
|
|
158
158
|
* resolves once the map _starts_ downloading, before it finishes downloading.
|
|
159
|
-
* Use `
|
|
159
|
+
* Use `useManyReceivedMapShares` or `useSingleReceivedMapShare` to track
|
|
160
|
+
* download progress and final status.
|
|
160
161
|
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
162
|
+
* If the sender canceled the share before the receiver calls this, the
|
|
163
|
+
* mutation will still resolve (the download starts), but the share status will
|
|
164
|
+
* end up as `'canceled'` rather than `'completed'`. This is the only way the
|
|
165
|
+
* receiver discovers that a share has been canceled — check `share.status`
|
|
166
|
+
* after the download settles.
|
|
167
|
+
*
|
|
168
|
+
* @throws {MapShareCanceledError} If the share is already known to be canceled
|
|
169
|
+
* (i.e. `status` is `'canceled'` in the store, e.g. after a previous
|
|
170
|
+
* download attempt discovered the cancellation).
|
|
171
|
+
* @throws {InvalidStatusTransitionError} If the share is not in a valid state
|
|
172
|
+
* to start downloading (e.g. already downloading, completed, or declined).
|
|
163
173
|
*
|
|
164
174
|
* @example
|
|
165
175
|
* ```tsx
|
|
166
176
|
* function AcceptButton({ shareId }: { shareId: string }) {
|
|
167
|
-
* const { mutate: accept } =
|
|
177
|
+
* const { mutate: accept } = useDownloadReceivedMapShare()
|
|
168
178
|
*
|
|
169
179
|
* return <button onClick={() => accept({ shareId })}>Accept</button>
|
|
170
180
|
* }
|
|
@@ -189,17 +199,25 @@ export declare function useDownloadReceivedMapShare(): Pick<import("@tanstack/re
|
|
|
189
199
|
}, "error" | "status" | "mutate" | "reset" | "mutateAsync">;
|
|
190
200
|
/**
|
|
191
201
|
* Decline a map share that has been received. Notifies the sender that the
|
|
192
|
-
* share was declined.
|
|
202
|
+
* share was declined. The share status is only updated to `'declined'` after
|
|
203
|
+
* the server confirms the decline — there is no optimistic update.
|
|
204
|
+
*
|
|
205
|
+
* If the sender canceled the share before the decline reaches the server, the
|
|
206
|
+
* share status will transition to `'canceled'` (not `'error'`) and the
|
|
207
|
+
* mutation will throw a `MapShareCanceledError`.
|
|
193
208
|
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
209
|
+
* @throws {MapShareCanceledError} If the share is already known to be
|
|
210
|
+
* canceled, or if the server reports that the sender canceled the share
|
|
211
|
+
* while the decline was in flight. In both cases `share.status` will be
|
|
212
|
+
* `'canceled'`.
|
|
213
|
+
* @throws {InvalidStatusTransitionError} If the share is not in
|
|
214
|
+
* `status='pending'` (e.g. already downloading, completed, or declined).
|
|
197
215
|
*
|
|
198
216
|
* @example
|
|
199
217
|
* ```tsx
|
|
200
218
|
* import { DeclineReason } from '@comapeo/core-react'
|
|
201
219
|
* function DeclineButton({ shareId }: { shareId: string }) {
|
|
202
|
-
* const { mutate: decline } =
|
|
220
|
+
* const { mutate: decline } = useDeclineReceivedMapShare()
|
|
203
221
|
*
|
|
204
222
|
* return (
|
|
205
223
|
* <button onClick={() => decline({ shareId, reason: DeclineReason.user_rejected })}>
|
|
@@ -229,13 +247,15 @@ export declare function useDeclineReceivedMapShare(): Pick<import("@tanstack/rea
|
|
|
229
247
|
/**
|
|
230
248
|
* Abort an in-progress map share download.
|
|
231
249
|
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
250
|
+
* @throws {MapShareCanceledError} If the share is already known to be canceled.
|
|
251
|
+
* @throws {InvalidStatusTransitionError} If the share is not in
|
|
252
|
+
* `status='downloading'` (e.g. still pending, already completed, or
|
|
253
|
+
* declined).
|
|
234
254
|
*
|
|
235
255
|
* @example
|
|
236
256
|
* ```tsx
|
|
237
257
|
* function AbortButton({ shareId }: { shareId: string }) {
|
|
238
|
-
* const { mutate: abort } =
|
|
258
|
+
* const { mutate: abort } = useAbortReceivedMapShareDownload()
|
|
239
259
|
*
|
|
240
260
|
* return <button onClick={() => abort({ shareId })}>Cancel Download</button>
|
|
241
261
|
* }
|
|
@@ -264,8 +284,6 @@ export declare function useAbortReceivedMapShareDownload(): Pick<import("@tansta
|
|
|
264
284
|
* mutation resolves with the created map share object, including its ID, which
|
|
265
285
|
* can be used to track the share status with `useSingleSentMapShare`.
|
|
266
286
|
*
|
|
267
|
-
* @param opts.projectId Public ID of project for sending the map share: you can only send map shares to users on the same project
|
|
268
|
-
*
|
|
269
287
|
* @example
|
|
270
288
|
* ```tsx
|
|
271
289
|
* function SendMapButton({ projectId, deviceId }: { projectId: string; deviceId: string }) {
|
|
@@ -274,7 +292,7 @@ export declare function useAbortReceivedMapShareDownload(): Pick<import("@tansta
|
|
|
274
292
|
* return (
|
|
275
293
|
* <button
|
|
276
294
|
* onClick={() =>
|
|
277
|
-
* send({
|
|
295
|
+
* send({ receiverDeviceId: deviceId, mapId: 'custom' }, {
|
|
278
296
|
* onSuccess: (mapShare) => {
|
|
279
297
|
* console.log('Share sent with id', mapShare.shareId)
|
|
280
298
|
* }
|
|
@@ -182,15 +182,25 @@ function useSingleReceivedMapShare({ shareId }) {
|
|
|
182
182
|
/**
|
|
183
183
|
* Accept and download a map share that has been received. The mutate promise
|
|
184
184
|
* resolves once the map _starts_ downloading, before it finishes downloading.
|
|
185
|
-
* Use `
|
|
185
|
+
* Use `useManyReceivedMapShares` or `useSingleReceivedMapShare` to track
|
|
186
|
+
* download progress and final status.
|
|
186
187
|
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
188
|
+
* If the sender canceled the share before the receiver calls this, the
|
|
189
|
+
* mutation will still resolve (the download starts), but the share status will
|
|
190
|
+
* end up as `'canceled'` rather than `'completed'`. This is the only way the
|
|
191
|
+
* receiver discovers that a share has been canceled — check `share.status`
|
|
192
|
+
* after the download settles.
|
|
193
|
+
*
|
|
194
|
+
* @throws {MapShareCanceledError} If the share is already known to be canceled
|
|
195
|
+
* (i.e. `status` is `'canceled'` in the store, e.g. after a previous
|
|
196
|
+
* download attempt discovered the cancellation).
|
|
197
|
+
* @throws {InvalidStatusTransitionError} If the share is not in a valid state
|
|
198
|
+
* to start downloading (e.g. already downloading, completed, or declined).
|
|
189
199
|
*
|
|
190
200
|
* @example
|
|
191
201
|
* ```tsx
|
|
192
202
|
* function AcceptButton({ shareId }: { shareId: string }) {
|
|
193
|
-
* const { mutate: accept } =
|
|
203
|
+
* const { mutate: accept } = useDownloadReceivedMapShare()
|
|
194
204
|
*
|
|
195
205
|
* return <button onClick={() => accept({ shareId })}>Accept</button>
|
|
196
206
|
* }
|
|
@@ -207,17 +217,25 @@ function useDownloadReceivedMapShare() {
|
|
|
207
217
|
}
|
|
208
218
|
/**
|
|
209
219
|
* Decline a map share that has been received. Notifies the sender that the
|
|
210
|
-
* share was declined.
|
|
220
|
+
* share was declined. The share status is only updated to `'declined'` after
|
|
221
|
+
* the server confirms the decline — there is no optimistic update.
|
|
222
|
+
*
|
|
223
|
+
* If the sender canceled the share before the decline reaches the server, the
|
|
224
|
+
* share status will transition to `'canceled'` (not `'error'`) and the
|
|
225
|
+
* mutation will throw a `MapShareCanceledError`.
|
|
211
226
|
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
227
|
+
* @throws {MapShareCanceledError} If the share is already known to be
|
|
228
|
+
* canceled, or if the server reports that the sender canceled the share
|
|
229
|
+
* while the decline was in flight. In both cases `share.status` will be
|
|
230
|
+
* `'canceled'`.
|
|
231
|
+
* @throws {InvalidStatusTransitionError} If the share is not in
|
|
232
|
+
* `status='pending'` (e.g. already downloading, completed, or declined).
|
|
215
233
|
*
|
|
216
234
|
* @example
|
|
217
235
|
* ```tsx
|
|
218
236
|
* import { DeclineReason } from '@comapeo/core-react'
|
|
219
237
|
* function DeclineButton({ shareId }: { shareId: string }) {
|
|
220
|
-
* const { mutate: decline } =
|
|
238
|
+
* const { mutate: decline } = useDeclineReceivedMapShare()
|
|
221
239
|
*
|
|
222
240
|
* return (
|
|
223
241
|
* <button onClick={() => decline({ shareId, reason: DeclineReason.user_rejected })}>
|
|
@@ -239,13 +257,15 @@ function useDeclineReceivedMapShare() {
|
|
|
239
257
|
/**
|
|
240
258
|
* Abort an in-progress map share download.
|
|
241
259
|
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
260
|
+
* @throws {MapShareCanceledError} If the share is already known to be canceled.
|
|
261
|
+
* @throws {InvalidStatusTransitionError} If the share is not in
|
|
262
|
+
* `status='downloading'` (e.g. still pending, already completed, or
|
|
263
|
+
* declined).
|
|
244
264
|
*
|
|
245
265
|
* @example
|
|
246
266
|
* ```tsx
|
|
247
267
|
* function AbortButton({ shareId }: { shareId: string }) {
|
|
248
|
-
* const { mutate: abort } =
|
|
268
|
+
* const { mutate: abort } = useAbortReceivedMapShareDownload()
|
|
249
269
|
*
|
|
250
270
|
* return <button onClick={() => abort({ shareId })}>Cancel Download</button>
|
|
251
271
|
* }
|
|
@@ -269,8 +289,6 @@ function useAbortReceivedMapShareDownload() {
|
|
|
269
289
|
* mutation resolves with the created map share object, including its ID, which
|
|
270
290
|
* can be used to track the share status with `useSingleSentMapShare`.
|
|
271
291
|
*
|
|
272
|
-
* @param opts.projectId Public ID of project for sending the map share: you can only send map shares to users on the same project
|
|
273
|
-
*
|
|
274
292
|
* @example
|
|
275
293
|
* ```tsx
|
|
276
294
|
* function SendMapButton({ projectId, deviceId }: { projectId: string; deviceId: string }) {
|
|
@@ -279,7 +297,7 @@ function useAbortReceivedMapShareDownload() {
|
|
|
279
297
|
* return (
|
|
280
298
|
* <button
|
|
281
299
|
* onClick={() =>
|
|
282
|
-
* send({
|
|
300
|
+
* send({ receiverDeviceId: deviceId, mapId: 'custom' }, {
|
|
283
301
|
* onSuccess: (mapShare) => {
|
|
284
302
|
* console.log('Share sent with id', mapShare.shareId)
|
|
285
303
|
* }
|
package/dist/commonjs/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { useCreateDocument, useDeleteDocument, useManyDocs, usePresetsSelection,
|
|
|
4
4
|
export { useAcceptInvite, useManyInvites, useRejectInvite, useRequestCancelInvite, useSendInvite, useSingleInvite, } from './hooks/invites.js';
|
|
5
5
|
export { useMapStyleUrl, useImportCustomMapFile, useRemoveCustomMapFile, useGetCustomMapInfo, useManyReceivedMapShares, useSingleReceivedMapShare, useDeclineReceivedMapShare, useDownloadReceivedMapShare, useAbortReceivedMapShareDownload, useSendMapShare, useCancelSentMapShare, useSingleSentMapShare, } from './hooks/maps.js';
|
|
6
6
|
export type { SentMapShareState, ReceivedMapShareState, AbortMapShareOptions, CancelMapShareOptions, DeclineMapShareOptions, DownloadMapShareOptions, CreateAndSendMapShareOptions, } from './lib/map-shares-stores.js';
|
|
7
|
-
export { DeclineReason } from './lib/map-shares-stores.js';
|
|
7
|
+
export { DeclineReason, MapShareErrorCode, getErrorCode, MapShareCanceledError, InvalidStatusTransitionError, } 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
9
|
export type { SyncState } from './lib/sync.js';
|
|
10
10
|
export type { WriteableDocument, WriteableDocumentType, WriteableValue, } from './lib/types.js';
|
package/dist/commonjs/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.isHTTPError = exports.HTTPError = exports.useExportZipFile = exports.useExportGeoJSON = exports.useChangeMemberRole = exports.useUpdateProjectSettings = exports.useSyncState = exports.useStopSync = exports.useStartSync = exports.useSingleProject = exports.useSingleMember = exports.useSetAutostopDataSyncTimeout = exports.useRemoveMember = void 0;
|
|
3
|
+
exports.useManyProjects = exports.useManyMembers = exports.useLeaveProject = exports.useImportProjectConfig = exports.useImportProjectCategories = exports.useIconUrl = exports.useDocumentCreatedBy = exports.useDisconnectSyncServers = exports.useDataSyncProgress = exports.useCreateProject = exports.useCreateBlob = exports.useConnectSyncServers = exports.useAttachmentUrl = exports.useAddServerPeer = exports.InvalidStatusTransitionError = exports.MapShareCanceledError = exports.getErrorCode = exports.MapShareErrorCode = exports.DeclineReason = exports.useSingleSentMapShare = exports.useCancelSentMapShare = exports.useSendMapShare = exports.useAbortReceivedMapShareDownload = exports.useDownloadReceivedMapShare = exports.useDeclineReceivedMapShare = exports.useSingleReceivedMapShare = exports.useManyReceivedMapShares = exports.useGetCustomMapInfo = exports.useRemoveCustomMapFile = exports.useImportCustomMapFile = exports.useMapStyleUrl = exports.useSingleInvite = exports.useSendInvite = exports.useRequestCancelInvite = exports.useRejectInvite = exports.useManyInvites = exports.useAcceptInvite = exports.useUpdateDocument = exports.useSingleDocByVersionId = exports.useSingleDocByDocId = exports.usePresetsSelection = exports.useManyDocs = exports.useDeleteDocument = exports.useCreateDocument = exports.useSetOwnDeviceInfo = exports.useSetIsArchiveDevice = exports.useOwnDeviceInfo = exports.useIsArchiveDevice = exports.useClientApi = exports.ComapeoCoreProvider = void 0;
|
|
4
|
+
exports.isHTTPError = exports.HTTPError = exports.useExportZipFile = exports.useExportGeoJSON = exports.useChangeMemberRole = exports.useUpdateProjectSettings = exports.useSyncState = exports.useStopSync = exports.useStartSync = exports.useSingleProject = exports.useSingleMember = exports.useSetAutostopDataSyncTimeout = exports.useRemoveMember = exports.useRemoveServerPeer = exports.useProjectSettings = exports.useProjectOwnRoleChangeListener = exports.useOwnRoleInProject = void 0;
|
|
5
5
|
var ComapeoCore_js_1 = require("./contexts/ComapeoCore.js");
|
|
6
6
|
Object.defineProperty(exports, "ComapeoCoreProvider", { enumerable: true, get: function () { return ComapeoCore_js_1.ComapeoCoreProvider; } });
|
|
7
7
|
var client_js_1 = require("./hooks/client.js");
|
|
@@ -40,6 +40,10 @@ Object.defineProperty(exports, "useCancelSentMapShare", { enumerable: true, get:
|
|
|
40
40
|
Object.defineProperty(exports, "useSingleSentMapShare", { enumerable: true, get: function () { return maps_js_1.useSingleSentMapShare; } });
|
|
41
41
|
var map_shares_stores_js_1 = require("./lib/map-shares-stores.js");
|
|
42
42
|
Object.defineProperty(exports, "DeclineReason", { enumerable: true, get: function () { return map_shares_stores_js_1.DeclineReason; } });
|
|
43
|
+
Object.defineProperty(exports, "MapShareErrorCode", { enumerable: true, get: function () { return map_shares_stores_js_1.MapShareErrorCode; } });
|
|
44
|
+
Object.defineProperty(exports, "getErrorCode", { enumerable: true, get: function () { return map_shares_stores_js_1.getErrorCode; } });
|
|
45
|
+
Object.defineProperty(exports, "MapShareCanceledError", { enumerable: true, get: function () { return map_shares_stores_js_1.MapShareCanceledError; } });
|
|
46
|
+
Object.defineProperty(exports, "InvalidStatusTransitionError", { enumerable: true, get: function () { return map_shares_stores_js_1.InvalidStatusTransitionError; } });
|
|
43
47
|
var projects_js_1 = require("./hooks/projects.js");
|
|
44
48
|
Object.defineProperty(exports, "useAddServerPeer", { enumerable: true, get: function () { return projects_js_1.useAddServerPeer; } });
|
|
45
49
|
Object.defineProperty(exports, "useAttachmentUrl", { enumerable: true, get: function () { return projects_js_1.useAttachmentUrl; } });
|
|
@@ -9,6 +9,107 @@ export type ReceivedMapShareState = DistributedIntersection<Simplify<MapShare>,
|
|
|
9
9
|
export type SentMapShareState = ServerMapShareState;
|
|
10
10
|
export type ReceivedMapSharesStore = ReturnType<typeof createReceivedMapSharesStore>;
|
|
11
11
|
export type SentMapSharesStore = ReturnType<typeof createSentMapSharesStore>;
|
|
12
|
+
/**
|
|
13
|
+
* Error codes for map share operations. Use with {@link getErrorCode} to safely
|
|
14
|
+
* check the error code of an unknown error thrown by a map share mutation, or
|
|
15
|
+
* to check the `error.code` on a share in `status='error'`.
|
|
16
|
+
*
|
|
17
|
+
* ## Receiver errors
|
|
18
|
+
*
|
|
19
|
+
* **Mutation errors** (thrown by receiver hooks, check via
|
|
20
|
+
* `getErrorCode(mutation.error)`):
|
|
21
|
+
* - `MAP_SHARE_CANCELED` — the sender canceled the share
|
|
22
|
+
* - `INVALID_STATUS_TRANSITION` — the action is not valid for the share's
|
|
23
|
+
* current status (e.g. declining a share that is already downloading)
|
|
24
|
+
* - `MAP_SHARE_NOT_FOUND` — no share with the given `shareId` exists
|
|
25
|
+
* - `DOWNLOAD_NOT_FOUND` — abort was called but no download is tracked for
|
|
26
|
+
* this share
|
|
27
|
+
*
|
|
28
|
+
* **Share state errors** (on received `share.error.code` when
|
|
29
|
+
* `share.status === 'error'`):
|
|
30
|
+
* - `DOWNLOAD_ERROR` — the download failed (network, disk, or server error)
|
|
31
|
+
* - `DECLINE_CANNOT_CONNECT` — the decline was accepted locally but the sender
|
|
32
|
+
* could not be reached to notify them
|
|
33
|
+
*
|
|
34
|
+
* ## Sender errors
|
|
35
|
+
*
|
|
36
|
+
* **Mutation errors** (thrown by sender hooks, check via
|
|
37
|
+
* `getErrorCode(mutation.error)`):
|
|
38
|
+
* - `INVALID_STATUS_TRANSITION` — the action is not valid for the share's
|
|
39
|
+
* current status (e.g. canceling a share that is already canceled)
|
|
40
|
+
* - `MAP_SHARE_NOT_FOUND` — no share with the given `shareId` exists
|
|
41
|
+
*
|
|
42
|
+
* **Share state errors** (on sent `share.error.code` when
|
|
43
|
+
* `share.status === 'error'`):
|
|
44
|
+
* - `CANCEL_SHARE_NOT_CANCELABLE` — the cancel request reached the server but
|
|
45
|
+
* the share is already in a final state (e.g. completed or declined)
|
|
46
|
+
*
|
|
47
|
+
* ## Common
|
|
48
|
+
*
|
|
49
|
+
* - `UNKNOWN_ERROR` — fallback when the original error has no specific code.
|
|
50
|
+
* Can appear as both a mutation error and a share state error for either
|
|
51
|
+
* sender or receiver.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```tsx
|
|
55
|
+
* import { getErrorCode, MapShareErrorCode } from '@comapeo/core-react'
|
|
56
|
+
*
|
|
57
|
+
* // Receiver: checking a mutation error
|
|
58
|
+
* const decline = useDeclineReceivedMapShare()
|
|
59
|
+
* // ... after mutation fails:
|
|
60
|
+
* if (getErrorCode(decline.error) === MapShareErrorCode.MAP_SHARE_CANCELED) {
|
|
61
|
+
* // Show "this share was canceled by the sender"
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```tsx
|
|
67
|
+
* // Receiver: checking a share state error
|
|
68
|
+
* const share = useSingleReceivedMapShare({ shareId })
|
|
69
|
+
* if (share.status === 'error') {
|
|
70
|
+
* if (share.error.code === MapShareErrorCode.DOWNLOAD_ERROR) {
|
|
71
|
+
* // Show "download failed, try again?"
|
|
72
|
+
* }
|
|
73
|
+
* }
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export declare const MapShareErrorCode: {
|
|
77
|
+
/** Receiver: the sender canceled the share before the action could complete */
|
|
78
|
+
readonly MAP_SHARE_CANCELED: "MAP_SHARE_CANCELED";
|
|
79
|
+
/** Receiver/Sender: the action is not valid for the share's current status */
|
|
80
|
+
readonly INVALID_STATUS_TRANSITION: "INVALID_STATUS_TRANSITION";
|
|
81
|
+
/** Receiver/Sender: no map share with the given `shareId` exists in the store */
|
|
82
|
+
readonly MAP_SHARE_NOT_FOUND: "MAP_SHARE_NOT_FOUND";
|
|
83
|
+
/** Receiver: abort was called but no download is tracked for this share */
|
|
84
|
+
readonly DOWNLOAD_NOT_FOUND: "DOWNLOAD_NOT_FOUND";
|
|
85
|
+
/** Receiver: the download failed due to a network, disk, or server error */
|
|
86
|
+
readonly DOWNLOAD_ERROR: "DOWNLOAD_ERROR";
|
|
87
|
+
/** Receiver: could not connect to the sender to notify them of the decline */
|
|
88
|
+
readonly DECLINE_CANNOT_CONNECT: "DECLINE_CANNOT_CONNECT";
|
|
89
|
+
/** Sender: cancel failed because the share is already in a final state on the server */
|
|
90
|
+
readonly CANCEL_SHARE_NOT_CANCELABLE: "CANCEL_SHARE_NOT_CANCELABLE";
|
|
91
|
+
/** Receiver/Sender: fallback code when the original error has no specific code */
|
|
92
|
+
readonly UNKNOWN_ERROR: "UNKNOWN_ERROR";
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Safely extract the `code` property from an unknown error. Returns `undefined`
|
|
96
|
+
* if the value is not an Error or has no string `code` property.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```tsx
|
|
100
|
+
* import { getErrorCode, MapShareErrorCode } from '@comapeo/core-react'
|
|
101
|
+
*
|
|
102
|
+
* try {
|
|
103
|
+
* await decline.mutateAsync({ shareId, reason: 'user_rejected' })
|
|
104
|
+
* } catch (e) {
|
|
105
|
+
* const code = getErrorCode(e)
|
|
106
|
+
* if (code === MapShareErrorCode.MAP_SHARE_CANCELED) {
|
|
107
|
+
* // handle cancellation
|
|
108
|
+
* }
|
|
109
|
+
* }
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
export declare function getErrorCode(error: unknown): string | undefined;
|
|
12
113
|
/** Known reasons for declining a map share */
|
|
13
114
|
export declare const DeclineReason: {
|
|
14
115
|
/** User explicitly rejected the map share */
|
|
@@ -35,8 +136,6 @@ export type AbortMapShareOptions = {
|
|
|
35
136
|
};
|
|
36
137
|
/** Options for creating and sending a map share */
|
|
37
138
|
export type CreateAndSendMapShareOptions = {
|
|
38
|
-
/** Public ID of the project to send the share on behalf of */
|
|
39
|
-
projectId: string;
|
|
40
139
|
/** Device ID of the recipient */
|
|
41
140
|
receiverDeviceId: string;
|
|
42
141
|
/** ID of the map to share - not needed until we support multiple maps */
|
|
@@ -47,6 +146,24 @@ export type CancelMapShareOptions = {
|
|
|
47
146
|
/** ID of the map share to cancel */
|
|
48
147
|
shareId: string;
|
|
49
148
|
};
|
|
149
|
+
/**
|
|
150
|
+
* Thrown when a receiver action (download, decline, or abort) is attempted on a
|
|
151
|
+
* map share that has been canceled by the sender. Has `code: 'MAP_SHARE_CANCELED'`.
|
|
152
|
+
*/
|
|
153
|
+
export declare class MapShareCanceledError extends Error {
|
|
154
|
+
code: "MAP_SHARE_CANCELED";
|
|
155
|
+
constructor(shareId: string);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Thrown when an action is attempted on a map share whose current status does
|
|
159
|
+
* not allow the requested transition (e.g. declining a share that is already
|
|
160
|
+
* downloading, or aborting a share that is still pending).
|
|
161
|
+
* Has `code: 'INVALID_STATUS_TRANSITION'`.
|
|
162
|
+
*/
|
|
163
|
+
export declare class InvalidStatusTransitionError extends Error {
|
|
164
|
+
code: "INVALID_STATUS_TRANSITION";
|
|
165
|
+
constructor(current: string, next: string);
|
|
166
|
+
}
|
|
50
167
|
/**
|
|
51
168
|
* Store and actions for received map shares.
|
|
52
169
|
*/
|
|
@@ -73,7 +190,7 @@ export declare function createSentMapSharesStore({ clientApi, mapServerApi, }: {
|
|
|
73
190
|
subscribe: (listener: () => void) => () => boolean;
|
|
74
191
|
getSnapshot: () => ServerMapShareState[];
|
|
75
192
|
actions: {
|
|
76
|
-
createAndSend({
|
|
193
|
+
createAndSend({ receiverDeviceId, mapId, }: CreateAndSendMapShareOptions): Promise<ServerMapShareState>;
|
|
77
194
|
cancel({ shareId }: CancelMapShareOptions): Promise<void>;
|
|
78
195
|
};
|
|
79
196
|
};
|
|
@@ -3,7 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.DeclineReason = void 0;
|
|
6
|
+
exports.InvalidStatusTransitionError = exports.MapShareCanceledError = exports.DeclineReason = exports.MapShareErrorCode = void 0;
|
|
7
|
+
exports.getErrorCode = getErrorCode;
|
|
7
8
|
exports.createReceivedMapSharesStore = createReceivedMapSharesStore;
|
|
8
9
|
exports.createSentMapSharesStore = createSentMapSharesStore;
|
|
9
10
|
const constants_js_1 = require("@comapeo/map-server/constants.js");
|
|
@@ -16,6 +17,118 @@ const react_query_js_1 = require("./react-query.js");
|
|
|
16
17
|
// functions - if the documentation comments are added inline for the store
|
|
17
18
|
// actions, they do not show for the mutate() function in hooks.
|
|
18
19
|
// ============================================
|
|
20
|
+
/**
|
|
21
|
+
* Error codes for map share operations. Use with {@link getErrorCode} to safely
|
|
22
|
+
* check the error code of an unknown error thrown by a map share mutation, or
|
|
23
|
+
* to check the `error.code` on a share in `status='error'`.
|
|
24
|
+
*
|
|
25
|
+
* ## Receiver errors
|
|
26
|
+
*
|
|
27
|
+
* **Mutation errors** (thrown by receiver hooks, check via
|
|
28
|
+
* `getErrorCode(mutation.error)`):
|
|
29
|
+
* - `MAP_SHARE_CANCELED` — the sender canceled the share
|
|
30
|
+
* - `INVALID_STATUS_TRANSITION` — the action is not valid for the share's
|
|
31
|
+
* current status (e.g. declining a share that is already downloading)
|
|
32
|
+
* - `MAP_SHARE_NOT_FOUND` — no share with the given `shareId` exists
|
|
33
|
+
* - `DOWNLOAD_NOT_FOUND` — abort was called but no download is tracked for
|
|
34
|
+
* this share
|
|
35
|
+
*
|
|
36
|
+
* **Share state errors** (on received `share.error.code` when
|
|
37
|
+
* `share.status === 'error'`):
|
|
38
|
+
* - `DOWNLOAD_ERROR` — the download failed (network, disk, or server error)
|
|
39
|
+
* - `DECLINE_CANNOT_CONNECT` — the decline was accepted locally but the sender
|
|
40
|
+
* could not be reached to notify them
|
|
41
|
+
*
|
|
42
|
+
* ## Sender errors
|
|
43
|
+
*
|
|
44
|
+
* **Mutation errors** (thrown by sender hooks, check via
|
|
45
|
+
* `getErrorCode(mutation.error)`):
|
|
46
|
+
* - `INVALID_STATUS_TRANSITION` — the action is not valid for the share's
|
|
47
|
+
* current status (e.g. canceling a share that is already canceled)
|
|
48
|
+
* - `MAP_SHARE_NOT_FOUND` — no share with the given `shareId` exists
|
|
49
|
+
*
|
|
50
|
+
* **Share state errors** (on sent `share.error.code` when
|
|
51
|
+
* `share.status === 'error'`):
|
|
52
|
+
* - `CANCEL_SHARE_NOT_CANCELABLE` — the cancel request reached the server but
|
|
53
|
+
* the share is already in a final state (e.g. completed or declined)
|
|
54
|
+
*
|
|
55
|
+
* ## Common
|
|
56
|
+
*
|
|
57
|
+
* - `UNKNOWN_ERROR` — fallback when the original error has no specific code.
|
|
58
|
+
* Can appear as both a mutation error and a share state error for either
|
|
59
|
+
* sender or receiver.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```tsx
|
|
63
|
+
* import { getErrorCode, MapShareErrorCode } from '@comapeo/core-react'
|
|
64
|
+
*
|
|
65
|
+
* // Receiver: checking a mutation error
|
|
66
|
+
* const decline = useDeclineReceivedMapShare()
|
|
67
|
+
* // ... after mutation fails:
|
|
68
|
+
* if (getErrorCode(decline.error) === MapShareErrorCode.MAP_SHARE_CANCELED) {
|
|
69
|
+
* // Show "this share was canceled by the sender"
|
|
70
|
+
* }
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```tsx
|
|
75
|
+
* // Receiver: checking a share state error
|
|
76
|
+
* const share = useSingleReceivedMapShare({ shareId })
|
|
77
|
+
* if (share.status === 'error') {
|
|
78
|
+
* if (share.error.code === MapShareErrorCode.DOWNLOAD_ERROR) {
|
|
79
|
+
* // Show "download failed, try again?"
|
|
80
|
+
* }
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
exports.MapShareErrorCode = {
|
|
85
|
+
// --- Receiver mutation errors (thrown by receiver actions) ---
|
|
86
|
+
/** Receiver: the sender canceled the share before the action could complete */
|
|
87
|
+
MAP_SHARE_CANCELED: 'MAP_SHARE_CANCELED',
|
|
88
|
+
/** Receiver/Sender: the action is not valid for the share's current status */
|
|
89
|
+
INVALID_STATUS_TRANSITION: 'INVALID_STATUS_TRANSITION',
|
|
90
|
+
/** Receiver/Sender: no map share with the given `shareId` exists in the store */
|
|
91
|
+
MAP_SHARE_NOT_FOUND: 'MAP_SHARE_NOT_FOUND',
|
|
92
|
+
/** Receiver: abort was called but no download is tracked for this share */
|
|
93
|
+
DOWNLOAD_NOT_FOUND: 'DOWNLOAD_NOT_FOUND',
|
|
94
|
+
// --- Receiver share state errors (in share.error.code) ---
|
|
95
|
+
/** Receiver: the download failed due to a network, disk, or server error */
|
|
96
|
+
DOWNLOAD_ERROR: 'DOWNLOAD_ERROR',
|
|
97
|
+
/** Receiver: could not connect to the sender to notify them of the decline */
|
|
98
|
+
DECLINE_CANNOT_CONNECT: 'DECLINE_CANNOT_CONNECT',
|
|
99
|
+
// --- Sender share state errors (in share.error.code) ---
|
|
100
|
+
/** Sender: cancel failed because the share is already in a final state on the server */
|
|
101
|
+
CANCEL_SHARE_NOT_CANCELABLE: 'CANCEL_SHARE_NOT_CANCELABLE',
|
|
102
|
+
// --- Common ---
|
|
103
|
+
/** Receiver/Sender: fallback code when the original error has no specific code */
|
|
104
|
+
UNKNOWN_ERROR: 'UNKNOWN_ERROR',
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Safely extract the `code` property from an unknown error. Returns `undefined`
|
|
108
|
+
* if the value is not an Error or has no string `code` property.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```tsx
|
|
112
|
+
* import { getErrorCode, MapShareErrorCode } from '@comapeo/core-react'
|
|
113
|
+
*
|
|
114
|
+
* try {
|
|
115
|
+
* await decline.mutateAsync({ shareId, reason: 'user_rejected' })
|
|
116
|
+
* } catch (e) {
|
|
117
|
+
* const code = getErrorCode(e)
|
|
118
|
+
* if (code === MapShareErrorCode.MAP_SHARE_CANCELED) {
|
|
119
|
+
* // handle cancellation
|
|
120
|
+
* }
|
|
121
|
+
* }
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
function getErrorCode(error) {
|
|
125
|
+
if (error instanceof Error &&
|
|
126
|
+
'code' in error &&
|
|
127
|
+
typeof error.code === 'string') {
|
|
128
|
+
return error.code;
|
|
129
|
+
}
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
19
132
|
/** Known reasons for declining a map share */
|
|
20
133
|
exports.DeclineReason = {
|
|
21
134
|
/** User explicitly rejected the map share */
|
|
@@ -23,6 +136,32 @@ exports.DeclineReason = {
|
|
|
23
136
|
/** Device storage is full */
|
|
24
137
|
storage_full: 'storage_full',
|
|
25
138
|
};
|
|
139
|
+
/**
|
|
140
|
+
* Thrown when a receiver action (download, decline, or abort) is attempted on a
|
|
141
|
+
* map share that has been canceled by the sender. Has `code: 'MAP_SHARE_CANCELED'`.
|
|
142
|
+
*/
|
|
143
|
+
class MapShareCanceledError extends Error {
|
|
144
|
+
code = 'MAP_SHARE_CANCELED';
|
|
145
|
+
constructor(shareId) {
|
|
146
|
+
super(`Map share ${shareId} has been canceled by the sender`);
|
|
147
|
+
this.name = 'MapShareCanceledError';
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
exports.MapShareCanceledError = MapShareCanceledError;
|
|
151
|
+
/**
|
|
152
|
+
* Thrown when an action is attempted on a map share whose current status does
|
|
153
|
+
* not allow the requested transition (e.g. declining a share that is already
|
|
154
|
+
* downloading, or aborting a share that is still pending).
|
|
155
|
+
* Has `code: 'INVALID_STATUS_TRANSITION'`.
|
|
156
|
+
*/
|
|
157
|
+
class InvalidStatusTransitionError extends Error {
|
|
158
|
+
code = 'INVALID_STATUS_TRANSITION';
|
|
159
|
+
constructor(current, next) {
|
|
160
|
+
super(`Invalid status transition from ${current} to ${next}`);
|
|
161
|
+
this.name = 'InvalidStatusTransitionError';
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
exports.InvalidStatusTransitionError = InvalidStatusTransitionError;
|
|
26
165
|
/**
|
|
27
166
|
* This is like a mini zustand store. Keeping the map shares in an external
|
|
28
167
|
* store avoids unnecessary re-renders of the entire app when map shares are
|
|
@@ -143,6 +282,13 @@ function createReceivedMapSharesStore({ clientApi, mapServerApi, queryClient, })
|
|
|
143
282
|
const actions = {
|
|
144
283
|
async download({ shareId }) {
|
|
145
284
|
const mapShare = get(shareId);
|
|
285
|
+
// This path should be be impossible, because the map share only receives
|
|
286
|
+
// status updates from the receiver after the download starts, but adding
|
|
287
|
+
// for completeness, and the edge-case of the receiving trying to
|
|
288
|
+
// download() a second time.
|
|
289
|
+
if (mapShare.status === 'canceled') {
|
|
290
|
+
throw new MapShareCanceledError(shareId);
|
|
291
|
+
}
|
|
146
292
|
update(shareId, { status: 'downloading', bytesDownloaded: 0 });
|
|
147
293
|
try {
|
|
148
294
|
const downloadIdPromise = mapServerApi
|
|
@@ -184,22 +330,39 @@ function createReceivedMapSharesStore({ clientApi, mapServerApi, queryClient, })
|
|
|
184
330
|
},
|
|
185
331
|
async decline({ shareId, reason }) {
|
|
186
332
|
const mapShare = get(shareId);
|
|
187
|
-
|
|
333
|
+
if (mapShare.status === 'canceled') {
|
|
334
|
+
throw new MapShareCanceledError(shareId);
|
|
335
|
+
}
|
|
336
|
+
if (mapShare.status !== 'pending') {
|
|
337
|
+
throw new InvalidStatusTransitionError(mapShare.status, 'declined');
|
|
338
|
+
}
|
|
188
339
|
try {
|
|
189
|
-
await mapServerApi
|
|
340
|
+
await mapServerApi
|
|
341
|
+
.post(`mapShares/${shareId}/decline`, {
|
|
190
342
|
json: {
|
|
191
343
|
senderDeviceId: mapShare.senderDeviceId,
|
|
192
344
|
mapShareUrls: mapShare.mapShareUrls,
|
|
193
345
|
reason,
|
|
194
346
|
},
|
|
195
|
-
})
|
|
347
|
+
})
|
|
348
|
+
.json();
|
|
349
|
+
update(shareId, { status: 'declined', reason });
|
|
196
350
|
}
|
|
197
351
|
catch (e) {
|
|
352
|
+
const error = (0, ensure_error_1.default)(e);
|
|
353
|
+
if ('code' in error && error.code === 'MAP_SHARE_CANCELED') {
|
|
354
|
+
update(shareId, { status: 'canceled' });
|
|
355
|
+
throw new MapShareCanceledError(shareId);
|
|
356
|
+
}
|
|
198
357
|
handleError(shareId, e);
|
|
199
358
|
throw e;
|
|
200
359
|
}
|
|
201
360
|
},
|
|
202
361
|
async abort({ shareId }) {
|
|
362
|
+
const mapShare = get(shareId);
|
|
363
|
+
if (mapShare.status === 'canceled') {
|
|
364
|
+
throw new MapShareCanceledError(shareId);
|
|
365
|
+
}
|
|
203
366
|
update(shareId, { status: 'aborted' });
|
|
204
367
|
try {
|
|
205
368
|
const downloadId = await downloads.get(shareId);
|
|
@@ -229,15 +392,14 @@ function createReceivedMapSharesStore({ clientApi, mapServerApi, queryClient, })
|
|
|
229
392
|
function createSentMapSharesStore({ clientApi, mapServerApi, }) {
|
|
230
393
|
const { subscribe, getSnapshot, update, add, handleError, monitor } = createMapSharesStore({ mapServerApi });
|
|
231
394
|
const actions = {
|
|
232
|
-
async createAndSend({
|
|
395
|
+
async createAndSend({ receiverDeviceId, mapId = constants_js_1.CUSTOM_MAP_ID, }) {
|
|
233
396
|
const mapShare = await mapServerApi
|
|
234
397
|
.post('mapShares', {
|
|
235
398
|
json: { receiverDeviceId, mapId },
|
|
236
399
|
})
|
|
237
400
|
.json();
|
|
238
401
|
try {
|
|
239
|
-
|
|
240
|
-
await project.$sendMapShare(mapShare);
|
|
402
|
+
await clientApi.sendMapShare(mapShare);
|
|
241
403
|
}
|
|
242
404
|
catch (e) {
|
|
243
405
|
await mapServerApi.post(`mapShares/${mapShare.shareId}/cancel`);
|
|
@@ -279,7 +441,7 @@ const allowedStatusTransitions = {
|
|
|
279
441
|
*/
|
|
280
442
|
function assertValidStatusTransition(current, next) {
|
|
281
443
|
if (!allowedStatusTransitions[current].includes(next)) {
|
|
282
|
-
throw new
|
|
444
|
+
throw new InvalidStatusTransitionError(current, next);
|
|
283
445
|
}
|
|
284
446
|
}
|
|
285
447
|
const finalStatuses = [
|