@comapeo/core-react 10.0.1 → 11.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.
- package/dist/commonjs/contexts/MapShares.d.ts +1 -1
- package/dist/commonjs/hooks/maps.d.ts +57 -18
- package/dist/commonjs/hooks/maps.js +58 -19
- 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/commonjs/lib/react-query.js +2 -2
- package/dist/esm/contexts/MapShares.d.ts +1 -1
- package/dist/esm/hooks/maps.d.ts +57 -18
- package/dist/esm/hooks/maps.js +58 -19
- 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/dist/esm/lib/react-query.js +1 -1
- package/docs/API.md +19 -24
- package/package.json +6 -6
|
@@ -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
|
};
|
|
@@ -8,6 +8,118 @@ import { invalidateMapQueries } from './react-query.js';
|
|
|
8
8
|
// functions - if the documentation comments are added inline for the store
|
|
9
9
|
// actions, they do not show for the mutate() function in hooks.
|
|
10
10
|
// ============================================
|
|
11
|
+
/**
|
|
12
|
+
* Error codes for map share operations. Use with {@link getErrorCode} to safely
|
|
13
|
+
* check the error code of an unknown error thrown by a map share mutation, or
|
|
14
|
+
* to check the `error.code` on a share in `status='error'`.
|
|
15
|
+
*
|
|
16
|
+
* ## Receiver errors
|
|
17
|
+
*
|
|
18
|
+
* **Mutation errors** (thrown by receiver hooks, check via
|
|
19
|
+
* `getErrorCode(mutation.error)`):
|
|
20
|
+
* - `MAP_SHARE_CANCELED` — the sender canceled the share
|
|
21
|
+
* - `INVALID_STATUS_TRANSITION` — the action is not valid for the share's
|
|
22
|
+
* current status (e.g. declining a share that is already downloading)
|
|
23
|
+
* - `MAP_SHARE_NOT_FOUND` — no share with the given `shareId` exists
|
|
24
|
+
* - `DOWNLOAD_NOT_FOUND` — abort was called but no download is tracked for
|
|
25
|
+
* this share
|
|
26
|
+
*
|
|
27
|
+
* **Share state errors** (on received `share.error.code` when
|
|
28
|
+
* `share.status === 'error'`):
|
|
29
|
+
* - `DOWNLOAD_ERROR` — the download failed (network, disk, or server error)
|
|
30
|
+
* - `DECLINE_CANNOT_CONNECT` — the decline was accepted locally but the sender
|
|
31
|
+
* could not be reached to notify them
|
|
32
|
+
*
|
|
33
|
+
* ## Sender errors
|
|
34
|
+
*
|
|
35
|
+
* **Mutation errors** (thrown by sender hooks, check via
|
|
36
|
+
* `getErrorCode(mutation.error)`):
|
|
37
|
+
* - `INVALID_STATUS_TRANSITION` — the action is not valid for the share's
|
|
38
|
+
* current status (e.g. canceling a share that is already canceled)
|
|
39
|
+
* - `MAP_SHARE_NOT_FOUND` — no share with the given `shareId` exists
|
|
40
|
+
*
|
|
41
|
+
* **Share state errors** (on sent `share.error.code` when
|
|
42
|
+
* `share.status === 'error'`):
|
|
43
|
+
* - `CANCEL_SHARE_NOT_CANCELABLE` — the cancel request reached the server but
|
|
44
|
+
* the share is already in a final state (e.g. completed or declined)
|
|
45
|
+
*
|
|
46
|
+
* ## Common
|
|
47
|
+
*
|
|
48
|
+
* - `UNKNOWN_ERROR` — fallback when the original error has no specific code.
|
|
49
|
+
* Can appear as both a mutation error and a share state error for either
|
|
50
|
+
* sender or receiver.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```tsx
|
|
54
|
+
* import { getErrorCode, MapShareErrorCode } from '@comapeo/core-react'
|
|
55
|
+
*
|
|
56
|
+
* // Receiver: checking a mutation error
|
|
57
|
+
* const decline = useDeclineReceivedMapShare()
|
|
58
|
+
* // ... after mutation fails:
|
|
59
|
+
* if (getErrorCode(decline.error) === MapShareErrorCode.MAP_SHARE_CANCELED) {
|
|
60
|
+
* // Show "this share was canceled by the sender"
|
|
61
|
+
* }
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```tsx
|
|
66
|
+
* // Receiver: checking a share state error
|
|
67
|
+
* const share = useSingleReceivedMapShare({ shareId })
|
|
68
|
+
* if (share.status === 'error') {
|
|
69
|
+
* if (share.error.code === MapShareErrorCode.DOWNLOAD_ERROR) {
|
|
70
|
+
* // Show "download failed, try again?"
|
|
71
|
+
* }
|
|
72
|
+
* }
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export const MapShareErrorCode = {
|
|
76
|
+
// --- Receiver mutation errors (thrown by receiver actions) ---
|
|
77
|
+
/** Receiver: the sender canceled the share before the action could complete */
|
|
78
|
+
MAP_SHARE_CANCELED: 'MAP_SHARE_CANCELED',
|
|
79
|
+
/** Receiver/Sender: the action is not valid for the share's current status */
|
|
80
|
+
INVALID_STATUS_TRANSITION: 'INVALID_STATUS_TRANSITION',
|
|
81
|
+
/** Receiver/Sender: no map share with the given `shareId` exists in the store */
|
|
82
|
+
MAP_SHARE_NOT_FOUND: 'MAP_SHARE_NOT_FOUND',
|
|
83
|
+
/** Receiver: abort was called but no download is tracked for this share */
|
|
84
|
+
DOWNLOAD_NOT_FOUND: 'DOWNLOAD_NOT_FOUND',
|
|
85
|
+
// --- Receiver share state errors (in share.error.code) ---
|
|
86
|
+
/** Receiver: the download failed due to a network, disk, or server error */
|
|
87
|
+
DOWNLOAD_ERROR: 'DOWNLOAD_ERROR',
|
|
88
|
+
/** Receiver: could not connect to the sender to notify them of the decline */
|
|
89
|
+
DECLINE_CANNOT_CONNECT: 'DECLINE_CANNOT_CONNECT',
|
|
90
|
+
// --- Sender share state errors (in share.error.code) ---
|
|
91
|
+
/** Sender: cancel failed because the share is already in a final state on the server */
|
|
92
|
+
CANCEL_SHARE_NOT_CANCELABLE: 'CANCEL_SHARE_NOT_CANCELABLE',
|
|
93
|
+
// --- Common ---
|
|
94
|
+
/** Receiver/Sender: fallback code when the original error has no specific code */
|
|
95
|
+
UNKNOWN_ERROR: 'UNKNOWN_ERROR',
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Safely extract the `code` property from an unknown error. Returns `undefined`
|
|
99
|
+
* if the value is not an Error or has no string `code` property.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```tsx
|
|
103
|
+
* import { getErrorCode, MapShareErrorCode } from '@comapeo/core-react'
|
|
104
|
+
*
|
|
105
|
+
* try {
|
|
106
|
+
* await decline.mutateAsync({ shareId, reason: 'user_rejected' })
|
|
107
|
+
* } catch (e) {
|
|
108
|
+
* const code = getErrorCode(e)
|
|
109
|
+
* if (code === MapShareErrorCode.MAP_SHARE_CANCELED) {
|
|
110
|
+
* // handle cancellation
|
|
111
|
+
* }
|
|
112
|
+
* }
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
export function getErrorCode(error) {
|
|
116
|
+
if (error instanceof Error &&
|
|
117
|
+
'code' in error &&
|
|
118
|
+
typeof error.code === 'string') {
|
|
119
|
+
return error.code;
|
|
120
|
+
}
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
11
123
|
/** Known reasons for declining a map share */
|
|
12
124
|
export const DeclineReason = {
|
|
13
125
|
/** User explicitly rejected the map share */
|
|
@@ -15,6 +127,30 @@ export const DeclineReason = {
|
|
|
15
127
|
/** Device storage is full */
|
|
16
128
|
storage_full: 'storage_full',
|
|
17
129
|
};
|
|
130
|
+
/**
|
|
131
|
+
* Thrown when a receiver action (download, decline, or abort) is attempted on a
|
|
132
|
+
* map share that has been canceled by the sender. Has `code: 'MAP_SHARE_CANCELED'`.
|
|
133
|
+
*/
|
|
134
|
+
export class MapShareCanceledError extends Error {
|
|
135
|
+
code = 'MAP_SHARE_CANCELED';
|
|
136
|
+
constructor(shareId) {
|
|
137
|
+
super(`Map share ${shareId} has been canceled by the sender`);
|
|
138
|
+
this.name = 'MapShareCanceledError';
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Thrown when an action is attempted on a map share whose current status does
|
|
143
|
+
* not allow the requested transition (e.g. declining a share that is already
|
|
144
|
+
* downloading, or aborting a share that is still pending).
|
|
145
|
+
* Has `code: 'INVALID_STATUS_TRANSITION'`.
|
|
146
|
+
*/
|
|
147
|
+
export class InvalidStatusTransitionError extends Error {
|
|
148
|
+
code = 'INVALID_STATUS_TRANSITION';
|
|
149
|
+
constructor(current, next) {
|
|
150
|
+
super(`Invalid status transition from ${current} to ${next}`);
|
|
151
|
+
this.name = 'InvalidStatusTransitionError';
|
|
152
|
+
}
|
|
153
|
+
}
|
|
18
154
|
/**
|
|
19
155
|
* This is like a mini zustand store. Keeping the map shares in an external
|
|
20
156
|
* store avoids unnecessary re-renders of the entire app when map shares are
|
|
@@ -135,6 +271,13 @@ export function createReceivedMapSharesStore({ clientApi, mapServerApi, queryCli
|
|
|
135
271
|
const actions = {
|
|
136
272
|
async download({ shareId }) {
|
|
137
273
|
const mapShare = get(shareId);
|
|
274
|
+
// This path should be be impossible, because the map share only receives
|
|
275
|
+
// status updates from the receiver after the download starts, but adding
|
|
276
|
+
// for completeness, and the edge-case of the receiving trying to
|
|
277
|
+
// download() a second time.
|
|
278
|
+
if (mapShare.status === 'canceled') {
|
|
279
|
+
throw new MapShareCanceledError(shareId);
|
|
280
|
+
}
|
|
138
281
|
update(shareId, { status: 'downloading', bytesDownloaded: 0 });
|
|
139
282
|
try {
|
|
140
283
|
const downloadIdPromise = mapServerApi
|
|
@@ -176,22 +319,39 @@ export function createReceivedMapSharesStore({ clientApi, mapServerApi, queryCli
|
|
|
176
319
|
},
|
|
177
320
|
async decline({ shareId, reason }) {
|
|
178
321
|
const mapShare = get(shareId);
|
|
179
|
-
|
|
322
|
+
if (mapShare.status === 'canceled') {
|
|
323
|
+
throw new MapShareCanceledError(shareId);
|
|
324
|
+
}
|
|
325
|
+
if (mapShare.status !== 'pending') {
|
|
326
|
+
throw new InvalidStatusTransitionError(mapShare.status, 'declined');
|
|
327
|
+
}
|
|
180
328
|
try {
|
|
181
|
-
await mapServerApi
|
|
329
|
+
await mapServerApi
|
|
330
|
+
.post(`mapShares/${shareId}/decline`, {
|
|
182
331
|
json: {
|
|
183
332
|
senderDeviceId: mapShare.senderDeviceId,
|
|
184
333
|
mapShareUrls: mapShare.mapShareUrls,
|
|
185
334
|
reason,
|
|
186
335
|
},
|
|
187
|
-
})
|
|
336
|
+
})
|
|
337
|
+
.json();
|
|
338
|
+
update(shareId, { status: 'declined', reason });
|
|
188
339
|
}
|
|
189
340
|
catch (e) {
|
|
341
|
+
const error = ensureError(e);
|
|
342
|
+
if ('code' in error && error.code === 'MAP_SHARE_CANCELED') {
|
|
343
|
+
update(shareId, { status: 'canceled' });
|
|
344
|
+
throw new MapShareCanceledError(shareId);
|
|
345
|
+
}
|
|
190
346
|
handleError(shareId, e);
|
|
191
347
|
throw e;
|
|
192
348
|
}
|
|
193
349
|
},
|
|
194
350
|
async abort({ shareId }) {
|
|
351
|
+
const mapShare = get(shareId);
|
|
352
|
+
if (mapShare.status === 'canceled') {
|
|
353
|
+
throw new MapShareCanceledError(shareId);
|
|
354
|
+
}
|
|
195
355
|
update(shareId, { status: 'aborted' });
|
|
196
356
|
try {
|
|
197
357
|
const downloadId = await downloads.get(shareId);
|
|
@@ -221,15 +381,14 @@ export function createReceivedMapSharesStore({ clientApi, mapServerApi, queryCli
|
|
|
221
381
|
export function createSentMapSharesStore({ clientApi, mapServerApi, }) {
|
|
222
382
|
const { subscribe, getSnapshot, update, add, handleError, monitor } = createMapSharesStore({ mapServerApi });
|
|
223
383
|
const actions = {
|
|
224
|
-
async createAndSend({
|
|
384
|
+
async createAndSend({ receiverDeviceId, mapId = CUSTOM_MAP_ID, }) {
|
|
225
385
|
const mapShare = await mapServerApi
|
|
226
386
|
.post('mapShares', {
|
|
227
387
|
json: { receiverDeviceId, mapId },
|
|
228
388
|
})
|
|
229
389
|
.json();
|
|
230
390
|
try {
|
|
231
|
-
|
|
232
|
-
await project.$sendMapShare(mapShare);
|
|
391
|
+
await clientApi.sendMapShare(mapShare);
|
|
233
392
|
}
|
|
234
393
|
catch (e) {
|
|
235
394
|
await mapServerApi.post(`mapShares/${mapShare.shareId}/cancel`);
|
|
@@ -271,7 +430,7 @@ const allowedStatusTransitions = {
|
|
|
271
430
|
*/
|
|
272
431
|
function assertValidStatusTransition(current, next) {
|
|
273
432
|
if (!allowedStatusTransitions[current].includes(next)) {
|
|
274
|
-
throw new
|
|
433
|
+
throw new InvalidStatusTransitionError(current, next);
|
|
275
434
|
}
|
|
276
435
|
}
|
|
277
436
|
const finalStatuses = [
|
package/docs/API.md
CHANGED
|
@@ -210,7 +210,7 @@ Set or unset the current device as an archive device.
|
|
|
210
210
|
|
|
211
211
|
| Function | Type |
|
|
212
212
|
| ---------- | ---------- |
|
|
213
|
-
| `useSentMapSharesActions` | `() => { createAndSend({
|
|
213
|
+
| `useSentMapSharesActions` | `() => { createAndSend({ receiverDeviceId, mapId, }: CreateAndSendMapShareOptions): Promise<ServerMapShareState>; cancel({ shareId }: CancelMapShareOptions): Promise<...>; }` |
|
|
214
214
|
|
|
215
215
|
### useSentMapSharesState
|
|
216
216
|
|
|
@@ -965,7 +965,7 @@ Update a document within a project.
|
|
|
965
965
|
|
|
966
966
|
| Function | Type |
|
|
967
967
|
| ---------- | ---------- |
|
|
968
|
-
| `useUpdateDocument` | `<D extends WriteableDocumentType>({ docType, projectId, }: { docType: D; projectId: string; }) => FilteredMutationResult<UseMutationResult<any, Error, { value: Omit<any, "schemaName">; }>>` |
|
|
968
|
+
| `useUpdateDocument` | `<D extends WriteableDocumentType>({ docType, projectId, }: { docType: D; projectId: string; }) => FilteredMutationResult<UseMutationResult<any, Error, { value: Omit<any, "schemaName">; versionId: string; }>>` |
|
|
969
969
|
|
|
970
970
|
Parameters:
|
|
971
971
|
|
|
@@ -1231,10 +1231,14 @@ function MapShareDetail({ shareId }: { shareId: string }) {
|
|
|
1231
1231
|
|
|
1232
1232
|
Accept and download a map share that has been received. The mutate promise
|
|
1233
1233
|
resolves once the map _starts_ downloading, before it finishes downloading.
|
|
1234
|
-
Use `
|
|
1234
|
+
Use `useManyReceivedMapShares` or `useSingleReceivedMapShare` to track
|
|
1235
|
+
download progress and final status.
|
|
1235
1236
|
|
|
1236
|
-
|
|
1237
|
-
|
|
1237
|
+
If the sender canceled the share before the receiver calls this, the
|
|
1238
|
+
mutation will still resolve (the download starts), but the share status will
|
|
1239
|
+
end up as `'canceled'` rather than `'completed'`. This is the only way the
|
|
1240
|
+
receiver discovers that a share has been canceled — check `share.status`
|
|
1241
|
+
after the download settles.
|
|
1238
1242
|
|
|
1239
1243
|
| Function | Type |
|
|
1240
1244
|
| ---------- | ---------- |
|
|
@@ -1244,7 +1248,7 @@ Examples:
|
|
|
1244
1248
|
|
|
1245
1249
|
```tsx
|
|
1246
1250
|
function AcceptButton({ shareId }: { shareId: string }) {
|
|
1247
|
-
const { mutate: accept } =
|
|
1251
|
+
const { mutate: accept } = useDownloadReceivedMapShare()
|
|
1248
1252
|
|
|
1249
1253
|
return <button onClick={() => accept({ shareId })}>Accept</button>
|
|
1250
1254
|
}
|
|
@@ -1254,11 +1258,12 @@ function AcceptButton({ shareId }: { shareId: string }) {
|
|
|
1254
1258
|
### useDeclineReceivedMapShare
|
|
1255
1259
|
|
|
1256
1260
|
Decline a map share that has been received. Notifies the sender that the
|
|
1257
|
-
share was declined.
|
|
1261
|
+
share was declined. The share status is only updated to `'declined'` after
|
|
1262
|
+
the server confirms the decline — there is no optimistic update.
|
|
1258
1263
|
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1264
|
+
If the sender canceled the share before the decline reaches the server, the
|
|
1265
|
+
share status will transition to `'canceled'` (not `'error'`) and the
|
|
1266
|
+
mutation will throw a `MapShareCanceledError`.
|
|
1262
1267
|
|
|
1263
1268
|
| Function | Type |
|
|
1264
1269
|
| ---------- | ---------- |
|
|
@@ -1269,7 +1274,7 @@ Examples:
|
|
|
1269
1274
|
```tsx
|
|
1270
1275
|
import { DeclineReason } from '@comapeo/core-react'
|
|
1271
1276
|
function DeclineButton({ shareId }: { shareId: string }) {
|
|
1272
|
-
const { mutate: decline } =
|
|
1277
|
+
const { mutate: decline } = useDeclineReceivedMapShare()
|
|
1273
1278
|
|
|
1274
1279
|
return (
|
|
1275
1280
|
<button onClick={() => decline({ shareId, reason: DeclineReason.user_rejected })}>
|
|
@@ -1284,9 +1289,6 @@ function DeclineButton({ shareId }: { shareId: string }) {
|
|
|
1284
1289
|
|
|
1285
1290
|
Abort an in-progress map share download.
|
|
1286
1291
|
|
|
1287
|
-
Throws if the share is not in `status="downloading"`
|
|
1288
|
-
Throws if shareId is invalid
|
|
1289
|
-
|
|
1290
1292
|
| Function | Type |
|
|
1291
1293
|
| ---------- | ---------- |
|
|
1292
1294
|
| `useAbortReceivedMapShareDownload` | `() => Pick<Override<MutationObserverIdleResult<void, Error, AbortMapShareOptions, unknown>, { mutate: UseMutateFunction<void, Error, AbortMapShareOptions, unknown>; }> and { ...; }, "error" or ... 3 more ... or "mutateAsync"> or Pick<...> or Pick<...> or Pick<...>` |
|
|
@@ -1295,7 +1297,7 @@ Examples:
|
|
|
1295
1297
|
|
|
1296
1298
|
```tsx
|
|
1297
1299
|
function AbortButton({ shareId }: { shareId: string }) {
|
|
1298
|
-
const { mutate: abort } =
|
|
1300
|
+
const { mutate: abort } = useAbortReceivedMapShareDownload()
|
|
1299
1301
|
|
|
1300
1302
|
return <button onClick={() => abort({ shareId })}>Cancel Download</button>
|
|
1301
1303
|
}
|
|
@@ -1313,11 +1315,6 @@ can be used to track the share status with `useSingleSentMapShare`.
|
|
|
1313
1315
|
| ---------- | ---------- |
|
|
1314
1316
|
| `useSendMapShare` | `() => Pick<Override<MutationObserverIdleResult<ServerMapShareState, Error, CreateAndSendMapShareOptions, unknown>, { mutate: UseMutateFunction<ServerMapShareState, Error, CreateAndSendMapShareOptions, unknown>; }> and { ...; }, "error" or ... 3 more ... or "mutateAsync"> or Pick<...> or Pick<...> or Pick<...>` |
|
|
1315
1317
|
|
|
1316
|
-
Parameters:
|
|
1317
|
-
|
|
1318
|
-
* `opts.projectId`: Public ID of project for sending the map share: you can only send map shares to users on the same project
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
1318
|
Examples:
|
|
1322
1319
|
|
|
1323
1320
|
```tsx
|
|
@@ -1327,7 +1324,7 @@ const { mutate: send } = useSendMapShare()
|
|
|
1327
1324
|
return (
|
|
1328
1325
|
<button
|
|
1329
1326
|
onClick={() =>
|
|
1330
|
-
send({
|
|
1327
|
+
send({ receiverDeviceId: deviceId, mapId: 'custom' }, {
|
|
1331
1328
|
onSuccess: (mapShare) => {
|
|
1332
1329
|
console.log('Share sent with id', mapShare.shareId)
|
|
1333
1330
|
}
|
|
@@ -1375,8 +1372,6 @@ Track the status and progress of a sent map share. Returns the current state
|
|
|
1375
1372
|
of the share, updated in real-time. When the recipient starts downloading, or
|
|
1376
1373
|
if they decline the share, then the returned share will update.
|
|
1377
1374
|
|
|
1378
|
-
Throws if no share with the specified ID is found.
|
|
1379
|
-
|
|
1380
1375
|
| Function | Type |
|
|
1381
1376
|
| ---------- | ---------- |
|
|
1382
1377
|
| `useSingleSentMapShare` | `({ shareId, }: { shareId: string; }) => ServerMapShareState` |
|
|
@@ -1427,7 +1422,7 @@ function SentShareStatus({ shareId }: { shareId: string }) {
|
|
|
1427
1422
|
|
|
1428
1423
|
| Constant | Type |
|
|
1429
1424
|
| ---------- | ---------- |
|
|
1430
|
-
| `SentMapSharesContext` | `Context<{ subscribe: (listener: () => void) => () => boolean; getSnapshot: () => ServerMapShareState[]; actions: { createAndSend({
|
|
1425
|
+
| `SentMapSharesContext` | `Context<{ subscribe: (listener: () => void) => () => boolean; getSnapshot: () => ServerMapShareState[]; actions: { createAndSend({ receiverDeviceId, mapId, }: CreateAndSendMapShareOptions): Promise<ServerMapShareState>; cancel({ shareId }: CancelMapShareOptions): Promise<...>; }; } or null>` |
|
|
1431
1426
|
|
|
1432
1427
|
### MapServerContext
|
|
1433
1428
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@comapeo/core-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "11.0.1",
|
|
4
4
|
"description": "React wrapper for working with @comapeo/core",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -64,20 +64,20 @@
|
|
|
64
64
|
"types": "tsc"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@comapeo/map-server": "1.0
|
|
67
|
+
"@comapeo/map-server": "^1.1.0",
|
|
68
68
|
"ensure-error": "5.0.0",
|
|
69
69
|
"eventsource-client": "1.2.0",
|
|
70
70
|
"type-fest": "5.4.4"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
|
-
"@comapeo/core": "^
|
|
74
|
-
"@comapeo/ipc": "^
|
|
73
|
+
"@comapeo/core": "^7.0.1",
|
|
74
|
+
"@comapeo/ipc": "^8.0.0",
|
|
75
75
|
"@tanstack/react-query": "^5",
|
|
76
76
|
"react": "^18 || ^19"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
|
-
"@comapeo/core": "
|
|
80
|
-
"@comapeo/ipc": "
|
|
79
|
+
"@comapeo/core": "7.0.1",
|
|
80
|
+
"@comapeo/ipc": "8.0.0",
|
|
81
81
|
"@eslint/compat": "2.0.3",
|
|
82
82
|
"@eslint/js": "9.39.4",
|
|
83
83
|
"@ianvs/prettier-plugin-sort-imports": "4.7.1",
|