@liveblocks/react 1.9.7 → 1.10.0-beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +318 -767
- package/dist/index.d.ts +318 -767
- package/dist/index.js +1598 -1371
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1616 -1389
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -6
package/dist/index.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { detectDupes } from "@liveblocks/core";
|
|
|
5
5
|
|
|
6
6
|
// src/version.ts
|
|
7
7
|
var PKG_NAME = "@liveblocks/react";
|
|
8
|
-
var PKG_VERSION = "1.
|
|
8
|
+
var PKG_VERSION = "1.10.0-beta1";
|
|
9
9
|
var PKG_FORMAT = "esm";
|
|
10
10
|
|
|
11
11
|
// src/ClientSideSuspense.tsx
|
|
@@ -18,29 +18,47 @@ function ClientSideSuspense(props) {
|
|
|
18
18
|
return /* @__PURE__ */ React.createElement(React.Suspense, { fallback: props.fallback }, mounted ? props.children() : props.fallback);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
// src/
|
|
21
|
+
// src/liveblocks.tsx
|
|
22
|
+
import { kInternal as kInternal3, makePoller as makePoller2 } from "@liveblocks/core";
|
|
23
|
+
import { nanoid as nanoid3 } from "nanoid";
|
|
24
|
+
import React3, {
|
|
25
|
+
createContext as createContext2,
|
|
26
|
+
useCallback as useCallback3,
|
|
27
|
+
useContext as useContext3,
|
|
28
|
+
useEffect as useEffect5
|
|
29
|
+
} from "react";
|
|
30
|
+
import { useSyncExternalStore as useSyncExternalStore3 } from "use-sync-external-store/shim/index.js";
|
|
31
|
+
import { useSyncExternalStoreWithSelector as useSyncExternalStoreWithSelector2 } from "use-sync-external-store/shim/with-selector.js";
|
|
32
|
+
|
|
33
|
+
// src/comments/lib/selected-inbox-notifications.ts
|
|
34
|
+
import { applyOptimisticUpdates } from "@liveblocks/core";
|
|
35
|
+
function selectedInboxNotifications(state) {
|
|
36
|
+
const result = applyOptimisticUpdates(state);
|
|
37
|
+
return Object.values(result.inboxNotifications);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/shared.ts
|
|
41
|
+
import { kInternal as kInternal2 } from "@liveblocks/core";
|
|
42
|
+
import { useCallback as useCallback2, useContext as useContext2, useEffect as useEffect4 } from "react";
|
|
43
|
+
import { useSyncExternalStore as useSyncExternalStore2 } from "use-sync-external-store/shim/index.js";
|
|
44
|
+
|
|
45
|
+
// src/room.tsx
|
|
22
46
|
import { shallow } from "@liveblocks/client";
|
|
23
47
|
import {
|
|
24
|
-
|
|
48
|
+
CommentsApiError,
|
|
49
|
+
console as console2,
|
|
25
50
|
deprecateIf,
|
|
26
51
|
errorIf,
|
|
27
52
|
isLiveNode,
|
|
28
|
-
|
|
29
|
-
|
|
53
|
+
kInternal,
|
|
54
|
+
makeEventSource,
|
|
55
|
+
makePoller,
|
|
56
|
+
NotificationsApiError,
|
|
57
|
+
ServerMsgCode,
|
|
58
|
+
stringify
|
|
30
59
|
} from "@liveblocks/core";
|
|
31
|
-
import
|
|
32
|
-
import
|
|
33
|
-
|
|
34
|
-
// src/comments/CommentsRoom.tsx
|
|
35
|
-
import { CommentsApiError, makeEventSource, stringify } from "@liveblocks/core";
|
|
36
|
-
import { nanoid } from "nanoid";
|
|
37
|
-
import React2, {
|
|
38
|
-
createContext,
|
|
39
|
-
useCallback as useCallback3,
|
|
40
|
-
useContext,
|
|
41
|
-
useEffect as useEffect3,
|
|
42
|
-
useMemo
|
|
43
|
-
} from "react";
|
|
60
|
+
import { nanoid as nanoid2 } from "nanoid";
|
|
61
|
+
import * as React2 from "react";
|
|
44
62
|
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector.js";
|
|
45
63
|
|
|
46
64
|
// src/comments/errors.ts
|
|
@@ -100,1298 +118,322 @@ var RemoveReactionError = class extends Error {
|
|
|
100
118
|
this.name = "RemoveReactionError";
|
|
101
119
|
}
|
|
102
120
|
};
|
|
121
|
+
var MarkInboxNotificationAsReadError = class extends Error {
|
|
122
|
+
constructor(cause, context) {
|
|
123
|
+
super("Mark inbox notification as read failed.");
|
|
124
|
+
this.cause = cause;
|
|
125
|
+
this.context = context;
|
|
126
|
+
this.name = "MarkInboxNotificationAsReadError";
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
var UpdateNotificationSettingsError = class extends Error {
|
|
130
|
+
constructor(cause, context) {
|
|
131
|
+
super("Update notification settings failed.");
|
|
132
|
+
this.cause = cause;
|
|
133
|
+
this.context = context;
|
|
134
|
+
this.name = "UpdateNotificationSettingsError";
|
|
135
|
+
}
|
|
136
|
+
};
|
|
103
137
|
|
|
104
|
-
// src/comments/lib/
|
|
105
|
-
import {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const isVisible = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
111
|
-
return isVisible;
|
|
138
|
+
// src/comments/lib/createIds.ts
|
|
139
|
+
import { nanoid } from "nanoid";
|
|
140
|
+
var THREAD_ID_PREFIX = "th";
|
|
141
|
+
var COMMENT_ID_PREFIX = "cm";
|
|
142
|
+
function createOptimisticId(prefix) {
|
|
143
|
+
return `${prefix}_${nanoid()}`;
|
|
112
144
|
}
|
|
113
|
-
function
|
|
114
|
-
|
|
115
|
-
return () => {
|
|
116
|
-
document.removeEventListener("visibilitychange", onStoreChange);
|
|
117
|
-
};
|
|
145
|
+
function createThreadId() {
|
|
146
|
+
return createOptimisticId(THREAD_ID_PREFIX);
|
|
118
147
|
}
|
|
119
|
-
function
|
|
120
|
-
|
|
121
|
-
return isDocumentDefined ? document.visibilityState === "visible" : true;
|
|
148
|
+
function createCommentId() {
|
|
149
|
+
return createOptimisticId(COMMENT_ID_PREFIX);
|
|
122
150
|
}
|
|
123
151
|
|
|
124
|
-
// src/comments/lib/
|
|
125
|
-
import {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
isOnlineRef.current = true;
|
|
132
|
-
onStoreChange();
|
|
133
|
-
}
|
|
134
|
-
function handleIsOffline() {
|
|
135
|
-
isOnlineRef.current = false;
|
|
136
|
-
onStoreChange();
|
|
137
|
-
}
|
|
138
|
-
window.addEventListener("online", handleIsOnline);
|
|
139
|
-
window.addEventListener("offline", handleIsOffline);
|
|
140
|
-
return () => {
|
|
141
|
-
window.removeEventListener("online", handleIsOnline);
|
|
142
|
-
window.removeEventListener("offline", handleIsOffline);
|
|
143
|
-
};
|
|
144
|
-
}, []);
|
|
145
|
-
const getSnapshot2 = useCallback(() => {
|
|
146
|
-
return isOnlineRef.current;
|
|
147
|
-
}, []);
|
|
148
|
-
const isOnline = useSyncExternalStore2(subscribe2, getSnapshot2, getSnapshot2);
|
|
149
|
-
return isOnline;
|
|
152
|
+
// src/comments/lib/select-notification-settings.ts
|
|
153
|
+
import {
|
|
154
|
+
applyOptimisticUpdates as applyOptimisticUpdates2
|
|
155
|
+
} from "@liveblocks/core";
|
|
156
|
+
function selectNotificationSettings(roomId, state) {
|
|
157
|
+
const { notificationSettings } = applyOptimisticUpdates2(state);
|
|
158
|
+
return notificationSettings[roomId];
|
|
150
159
|
}
|
|
151
160
|
|
|
152
|
-
// src/comments/lib/
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
retryCount = 0
|
|
168
|
-
}) => {
|
|
169
|
-
let startAt;
|
|
170
|
-
const shouldStartRequest = !manager.getRequest() || !shouldDedupe;
|
|
171
|
-
function deleteActiveRequest() {
|
|
172
|
-
const activeRequest = manager.getRequest();
|
|
173
|
-
if (!activeRequest)
|
|
174
|
-
return;
|
|
175
|
-
if (activeRequest.timestamp !== startAt)
|
|
176
|
-
return;
|
|
177
|
-
manager.setRequest(void 0);
|
|
178
|
-
}
|
|
179
|
-
function handleError() {
|
|
180
|
-
const timeout = ~~((Math.random() + 0.5) * (1 << (retryCount < 8 ? retryCount : 8))) * errorRetryInterval;
|
|
181
|
-
if (retryCount > errorRetryCount)
|
|
182
|
-
return;
|
|
183
|
-
setTimeout(() => {
|
|
184
|
-
void _revalidateCache({
|
|
185
|
-
shouldDedupe: false,
|
|
186
|
-
retryCount: retryCount + 1
|
|
187
|
-
});
|
|
188
|
-
}, timeout);
|
|
189
|
-
}
|
|
190
|
-
if (shouldStartRequest) {
|
|
191
|
-
manager.setRequest({
|
|
192
|
-
fetcher: fetcher(),
|
|
193
|
-
timestamp: ++timestamp
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
try {
|
|
197
|
-
let activeRequest = manager.getRequest();
|
|
198
|
-
if (!activeRequest)
|
|
199
|
-
return;
|
|
200
|
-
startAt = activeRequest.timestamp;
|
|
201
|
-
const newData = await activeRequest.fetcher;
|
|
202
|
-
if (shouldStartRequest) {
|
|
203
|
-
setTimeout(deleteActiveRequest, dedupingInterval);
|
|
204
|
-
}
|
|
205
|
-
activeRequest = manager.getRequest();
|
|
206
|
-
if (!activeRequest || activeRequest.timestamp !== startAt)
|
|
207
|
-
return;
|
|
208
|
-
const activeMutation = manager.getMutation();
|
|
209
|
-
if (activeMutation && (activeMutation.startTime > startAt || activeMutation.endTime > startAt || activeMutation.endTime === 0)) {
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
manager.setCache(newData);
|
|
213
|
-
} catch (err) {
|
|
214
|
-
deleteActiveRequest();
|
|
215
|
-
const isVisible = document.visibilityState === "visible";
|
|
216
|
-
const isOnline = isOnlineRef.current;
|
|
217
|
-
if (shouldStartRequest && isVisible && isOnline)
|
|
218
|
-
handleError();
|
|
219
|
-
manager.setError(err);
|
|
161
|
+
// src/comments/lib/selected-threads.ts
|
|
162
|
+
import {
|
|
163
|
+
applyOptimisticUpdates as applyOptimisticUpdates3
|
|
164
|
+
} from "@liveblocks/core";
|
|
165
|
+
function selectedThreads(roomId, state, options) {
|
|
166
|
+
const result = applyOptimisticUpdates3(state);
|
|
167
|
+
return Object.values(result.threads).filter((thread) => {
|
|
168
|
+
if (thread.roomId !== roomId)
|
|
169
|
+
return false;
|
|
170
|
+
const query = options.query;
|
|
171
|
+
if (!query)
|
|
172
|
+
return true;
|
|
173
|
+
for (const key in query.metadata) {
|
|
174
|
+
if (thread.metadata[key] !== query.metadata[key]) {
|
|
175
|
+
return false;
|
|
220
176
|
}
|
|
221
|
-
return;
|
|
222
|
-
},
|
|
223
|
-
[manager, fetcher, dedupingInterval, errorRetryInterval, errorRetryCount]
|
|
224
|
-
);
|
|
225
|
-
useEffect2(() => {
|
|
226
|
-
function handleIsOnline() {
|
|
227
|
-
isOnlineRef.current = true;
|
|
228
177
|
}
|
|
229
|
-
|
|
230
|
-
|
|
178
|
+
return true;
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/comments/lib/upsert-comment.ts
|
|
183
|
+
function upsertComment(threads, newComment) {
|
|
184
|
+
const thread = threads[newComment.threadId];
|
|
185
|
+
if (thread === void 0) {
|
|
186
|
+
return threads;
|
|
187
|
+
}
|
|
188
|
+
const newComments = [];
|
|
189
|
+
let updated = false;
|
|
190
|
+
for (const comment of thread.comments) {
|
|
191
|
+
if (comment.id === newComment.id) {
|
|
192
|
+
updated = true;
|
|
193
|
+
newComments.push(newComment);
|
|
194
|
+
} else {
|
|
195
|
+
newComments.push(comment);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (!updated) {
|
|
199
|
+
newComments.push(newComment);
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
...threads,
|
|
203
|
+
[thread.id]: {
|
|
204
|
+
...thread,
|
|
205
|
+
comments: newComments
|
|
231
206
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/lib/use-initial.ts
|
|
211
|
+
import { useState as useState2 } from "react";
|
|
212
|
+
function useInitial(value) {
|
|
213
|
+
return useState2(value)[0];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// src/lib/use-latest.ts
|
|
217
|
+
import { useEffect as useEffect2, useRef } from "react";
|
|
218
|
+
function useLatest(value) {
|
|
219
|
+
const ref = useRef(value);
|
|
220
|
+
useEffect2(() => {
|
|
221
|
+
ref.current = value;
|
|
222
|
+
}, [value]);
|
|
223
|
+
return ref;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// src/lib/use-rerender.ts
|
|
227
|
+
import { useReducer } from "react";
|
|
228
|
+
function useRerender() {
|
|
229
|
+
const [, update] = useReducer(
|
|
230
|
+
// This implementation works by incrementing a hidden counter value that is
|
|
231
|
+
// never consumed. Simply incrementing the counter changes the component's
|
|
232
|
+
// state and, thus, trigger a re-render.
|
|
233
|
+
(x) => x + 1,
|
|
234
|
+
0
|
|
244
235
|
);
|
|
245
|
-
return
|
|
236
|
+
return update;
|
|
246
237
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
238
|
+
|
|
239
|
+
// src/room.tsx
|
|
240
|
+
var noop = () => {
|
|
241
|
+
};
|
|
242
|
+
var identity = (x) => x;
|
|
243
|
+
var missing_unstable_batchedUpdates = (reactVersion, roomId) => `We noticed you\u2019re using React ${reactVersion}. Please pass unstable_batchedUpdates at the RoomProvider level until you\u2019re ready to upgrade to React 18:
|
|
244
|
+
|
|
245
|
+
import { unstable_batchedUpdates } from "react-dom"; // or "react-native"
|
|
246
|
+
|
|
247
|
+
<RoomProvider id=${JSON.stringify(
|
|
248
|
+
roomId
|
|
249
|
+
)} ... unstable_batchedUpdates={unstable_batchedUpdates}>
|
|
250
|
+
...
|
|
251
|
+
</RoomProvider>
|
|
252
|
+
|
|
253
|
+
Why? Please see https://liveblocks.io/docs/platform/troubleshooting#stale-props-zombie-child for more information`;
|
|
254
|
+
var superfluous_unstable_batchedUpdates = "You don\u2019t need to pass unstable_batchedUpdates to RoomProvider anymore, since you\u2019re on React 18+ already.";
|
|
255
|
+
function useSyncExternalStore(s, gs, gss) {
|
|
256
|
+
return useSyncExternalStoreWithSelector(s, gs, gss, identity);
|
|
257
|
+
}
|
|
258
|
+
var STABLE_EMPTY_LIST = Object.freeze([]);
|
|
259
|
+
var POLLING_INTERVAL = 5 * 60 * 1e3;
|
|
260
|
+
var MENTION_SUGGESTIONS_DEBOUNCE = 500;
|
|
261
|
+
function alwaysEmptyList() {
|
|
262
|
+
return STABLE_EMPTY_LIST;
|
|
263
|
+
}
|
|
264
|
+
function alwaysNull() {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
function makeMutationContext(room) {
|
|
268
|
+
const errmsg = "This mutation cannot be used until connected to the Liveblocks room";
|
|
269
|
+
return {
|
|
270
|
+
get storage() {
|
|
271
|
+
const mutableRoot = room.getStorageSnapshot();
|
|
272
|
+
if (mutableRoot === null) {
|
|
273
|
+
throw new Error(errmsg);
|
|
262
274
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
275
|
+
return mutableRoot;
|
|
276
|
+
},
|
|
277
|
+
get self() {
|
|
278
|
+
const self = room.getSelf();
|
|
279
|
+
if (self === null) {
|
|
280
|
+
throw new Error(errmsg);
|
|
268
281
|
}
|
|
269
|
-
|
|
270
|
-
|
|
282
|
+
return self;
|
|
283
|
+
},
|
|
284
|
+
get others() {
|
|
285
|
+
const others = room.getOthers();
|
|
286
|
+
if (room.getSelf() === null) {
|
|
287
|
+
throw new Error(errmsg);
|
|
271
288
|
}
|
|
272
|
-
|
|
273
|
-
startTime: beforeMutationTimestamp,
|
|
274
|
-
endTime: ++timestamp
|
|
275
|
-
});
|
|
276
|
-
manager.setRequest(void 0);
|
|
277
|
-
void revalidateCache({ shouldDedupe: false });
|
|
278
|
-
if (error)
|
|
279
|
-
throw error;
|
|
289
|
+
return others;
|
|
280
290
|
},
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
return mutate;
|
|
291
|
+
setMyPresence: room.updatePresence
|
|
292
|
+
};
|
|
284
293
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
var POLLING_INTERVAL = 5e3;
|
|
291
|
-
function createCommentsRoom(errorEventSource) {
|
|
292
|
-
const store = createClientCacheStore();
|
|
293
|
-
const FetcherContext = createContext(null);
|
|
294
|
-
const RoomManagerContext = createContext(null);
|
|
295
|
-
function getThreads(manager) {
|
|
296
|
-
const threads = manager.getCache();
|
|
297
|
-
if (!threads) {
|
|
298
|
-
throw new Error(
|
|
299
|
-
"Cannot update threads or comments before they are loaded."
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
return threads;
|
|
294
|
+
var ContextBundle = React2.createContext(null);
|
|
295
|
+
function useRoomContextBundle() {
|
|
296
|
+
const bundle = React2.useContext(ContextBundle);
|
|
297
|
+
if (bundle === null) {
|
|
298
|
+
throw new Error("RoomProvider is missing from the React tree.");
|
|
303
299
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
return
|
|
319
|
-
|
|
300
|
+
return bundle;
|
|
301
|
+
}
|
|
302
|
+
function createRoomContext(client) {
|
|
303
|
+
const RoomContext = React2.createContext(null);
|
|
304
|
+
const commentsErrorEventSource = makeEventSource();
|
|
305
|
+
const shared = createSharedContext(client);
|
|
306
|
+
function RoomProviderOuter(props) {
|
|
307
|
+
const [cache] = React2.useState(
|
|
308
|
+
() => /* @__PURE__ */ new Map()
|
|
309
|
+
);
|
|
310
|
+
const stableEnterRoom = React2.useCallback(
|
|
311
|
+
(roomId, options) => {
|
|
312
|
+
const cached = cache.get(roomId);
|
|
313
|
+
if (cached)
|
|
314
|
+
return cached;
|
|
315
|
+
const rv = client.enterRoom(
|
|
316
|
+
roomId,
|
|
317
|
+
options
|
|
318
|
+
);
|
|
319
|
+
const origLeave = rv.leave;
|
|
320
|
+
rv.leave = () => {
|
|
321
|
+
origLeave();
|
|
322
|
+
cache.delete(roomId);
|
|
323
|
+
};
|
|
324
|
+
cache.set(roomId, rv);
|
|
325
|
+
return rv;
|
|
326
|
+
},
|
|
327
|
+
[cache]
|
|
328
|
+
);
|
|
329
|
+
return /* @__PURE__ */ React2.createElement(RoomProviderInner, { ...props, stableEnterRoom });
|
|
330
|
+
}
|
|
331
|
+
function RoomProviderInner(props) {
|
|
332
|
+
const { id: roomId, stableEnterRoom } = props;
|
|
333
|
+
if (process.env.NODE_ENV !== "production") {
|
|
334
|
+
if (!roomId) {
|
|
335
|
+
throw new Error(
|
|
336
|
+
"RoomProvider id property is required. For more information: https://liveblocks.io/docs/errors/liveblocks-react/RoomProvider-id-property-is-required"
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
if (typeof roomId !== "string") {
|
|
340
|
+
throw new Error("RoomProvider id property should be a string.");
|
|
341
|
+
}
|
|
342
|
+
const majorReactVersion = parseInt(React2.version) || 1;
|
|
343
|
+
const oldReactVersion = majorReactVersion < 18;
|
|
344
|
+
errorIf(
|
|
345
|
+
oldReactVersion && props.unstable_batchedUpdates === void 0,
|
|
346
|
+
missing_unstable_batchedUpdates(majorReactVersion, roomId)
|
|
320
347
|
);
|
|
321
|
-
|
|
322
|
-
|
|
348
|
+
deprecateIf(
|
|
349
|
+
!oldReactVersion && props.unstable_batchedUpdates !== void 0,
|
|
350
|
+
superfluous_unstable_batchedUpdates
|
|
323
351
|
);
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
status === "connected"
|
|
352
|
+
}
|
|
353
|
+
const frozenProps = useInitial({
|
|
354
|
+
initialPresence: props.initialPresence,
|
|
355
|
+
initialStorage: props.initialStorage,
|
|
356
|
+
unstable_batchedUpdates: props.unstable_batchedUpdates,
|
|
357
|
+
autoConnect: props.autoConnect ?? props.shouldInitiallyConnect ?? typeof window !== "undefined"
|
|
358
|
+
});
|
|
359
|
+
const [{ room }, setRoomLeavePair] = React2.useState(
|
|
360
|
+
() => stableEnterRoom(roomId, {
|
|
361
|
+
...frozenProps,
|
|
362
|
+
autoConnect: false
|
|
363
|
+
// Deliberately using false here on the first render, see below
|
|
364
|
+
})
|
|
338
365
|
);
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
if (
|
|
366
|
+
React2.useEffect(() => {
|
|
367
|
+
async function handleCommentEvent(message) {
|
|
368
|
+
const info = await room.getThread({ threadId: message.threadId });
|
|
369
|
+
if (!info) {
|
|
370
|
+
store.deleteThread(message.threadId);
|
|
343
371
|
return;
|
|
344
|
-
revalidationTimerId = window.setTimeout(() => {
|
|
345
|
-
if (isOnline && isDocumentVisible && !manager.getError() && manager.getTotalReferenceCount() > 0) {
|
|
346
|
-
void revalidateCache({ shouldDedupe: true }).then(
|
|
347
|
-
scheduleRevalidation
|
|
348
|
-
);
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
scheduleRevalidation();
|
|
352
|
-
}, refreshInterval);
|
|
353
|
-
}
|
|
354
|
-
scheduleRevalidation();
|
|
355
|
-
return () => {
|
|
356
|
-
window.clearTimeout(revalidationTimerId);
|
|
357
|
-
};
|
|
358
|
-
}, [
|
|
359
|
-
revalidateCache,
|
|
360
|
-
refreshInterval,
|
|
361
|
-
isOnline,
|
|
362
|
-
isDocumentVisible,
|
|
363
|
-
manager
|
|
364
|
-
]);
|
|
365
|
-
useEffect3(() => {
|
|
366
|
-
function handleIsOnline() {
|
|
367
|
-
if (isDocumentVisible) {
|
|
368
|
-
void revalidateCache({ shouldDedupe: true });
|
|
369
372
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
373
|
+
const { thread, inboxNotification } = info;
|
|
374
|
+
const existingThread = store.get().threads[message.threadId];
|
|
375
|
+
switch (message.type) {
|
|
376
|
+
case ServerMsgCode.COMMENT_EDITED:
|
|
377
|
+
case ServerMsgCode.THREAD_METADATA_UPDATED:
|
|
378
|
+
case ServerMsgCode.COMMENT_REACTION_ADDED:
|
|
379
|
+
case ServerMsgCode.COMMENT_REACTION_REMOVED:
|
|
380
|
+
case ServerMsgCode.COMMENT_DELETED:
|
|
381
|
+
if (!existingThread)
|
|
382
|
+
break;
|
|
383
|
+
store.updateThreadAndNotification(thread, inboxNotification);
|
|
384
|
+
break;
|
|
385
|
+
case ServerMsgCode.COMMENT_CREATED:
|
|
386
|
+
store.updateThreadAndNotification(thread, inboxNotification);
|
|
387
|
+
break;
|
|
388
|
+
default:
|
|
389
|
+
break;
|
|
381
390
|
}
|
|
382
391
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
});
|
|
392
|
+
return room.events.comments.subscribe(
|
|
393
|
+
(message) => void handleCommentEvent(message)
|
|
394
|
+
);
|
|
395
|
+
}, [room]);
|
|
396
|
+
React2.useEffect(() => {
|
|
397
|
+
const pair = stableEnterRoom(roomId, frozenProps);
|
|
398
|
+
setRoomLeavePair(pair);
|
|
399
|
+
const { room: room2, leave } = pair;
|
|
400
|
+
if (frozenProps.autoConnect) {
|
|
401
|
+
room2.connect();
|
|
402
|
+
}
|
|
395
403
|
return () => {
|
|
396
|
-
|
|
404
|
+
leave();
|
|
397
405
|
};
|
|
398
|
-
}, [
|
|
399
|
-
return /* @__PURE__ */ React2.createElement(
|
|
406
|
+
}, [roomId, frozenProps, stableEnterRoom]);
|
|
407
|
+
return /* @__PURE__ */ React2.createElement(RoomContext.Provider, { value: room }, /* @__PURE__ */ React2.createElement(
|
|
408
|
+
ContextBundle.Provider,
|
|
409
|
+
{
|
|
410
|
+
value: bundle
|
|
411
|
+
},
|
|
412
|
+
props.children
|
|
413
|
+
));
|
|
400
414
|
}
|
|
401
|
-
function
|
|
402
|
-
|
|
403
|
-
if (manager === null) {
|
|
404
|
-
throw new Error("CommentsRoomProvider is missing from the React tree.");
|
|
405
|
-
}
|
|
406
|
-
return manager;
|
|
407
|
-
}
|
|
408
|
-
function getUseThreadsRevalidationManager(options, roomManager) {
|
|
409
|
-
const key = stringify(options);
|
|
410
|
-
const revalidationManager = roomManager.getRevalidationManager(key);
|
|
411
|
-
if (!revalidationManager) {
|
|
412
|
-
const useThreadsRevalidationManager = createUseThreadsRevalidationManager(
|
|
413
|
-
options,
|
|
414
|
-
roomManager
|
|
415
|
-
);
|
|
416
|
-
roomManager.setRevalidationmanager(key, useThreadsRevalidationManager);
|
|
417
|
-
return useThreadsRevalidationManager;
|
|
418
|
-
}
|
|
419
|
-
return revalidationManager;
|
|
415
|
+
function connectionIdSelector(others) {
|
|
416
|
+
return others.map((user) => user.connectionId);
|
|
420
417
|
}
|
|
421
|
-
function
|
|
422
|
-
const
|
|
423
|
-
if (
|
|
424
|
-
throw new Error("
|
|
425
|
-
}
|
|
426
|
-
return fetcher;
|
|
427
|
-
}
|
|
428
|
-
function useThreads(room, options = { query: { metadata: {} } }) {
|
|
429
|
-
const key = useMemo(() => stringify(options), [options]);
|
|
430
|
-
const manager = useRoomManager();
|
|
431
|
-
const useThreadsRevalidationManager = getUseThreadsRevalidationManager(
|
|
432
|
-
options,
|
|
433
|
-
manager
|
|
434
|
-
);
|
|
435
|
-
const fetcher = React2.useCallback(
|
|
436
|
-
() => {
|
|
437
|
-
return room.getThreads(options);
|
|
438
|
-
},
|
|
439
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps -- The missing dependency is `options` but `key` and `normalized` are analogous, so we only include `key` as dependency. This helps minimize the number of re-renders as `options` can change on each render
|
|
440
|
-
[key, room]
|
|
441
|
-
);
|
|
442
|
-
const revalidateCache = useRevalidateCache(
|
|
443
|
-
useThreadsRevalidationManager,
|
|
444
|
-
fetcher
|
|
445
|
-
);
|
|
446
|
-
useEffect3(() => {
|
|
447
|
-
void revalidateCache({ shouldDedupe: true });
|
|
448
|
-
}, [revalidateCache]);
|
|
449
|
-
useEffect3(() => {
|
|
450
|
-
manager.incrementReferenceCount(key);
|
|
451
|
-
return () => {
|
|
452
|
-
manager.decrementReferenceCount(key);
|
|
453
|
-
};
|
|
454
|
-
}, [manager, key]);
|
|
455
|
-
const cache = _useThreads(room, options);
|
|
456
|
-
return cache;
|
|
457
|
-
}
|
|
458
|
-
function useThreadsSuspense(room, options = { query: { metadata: {} } }) {
|
|
459
|
-
const key = useMemo(() => stringify(options), [options]);
|
|
460
|
-
const manager = useRoomManager();
|
|
461
|
-
const useThreadsRevalidationManager = getUseThreadsRevalidationManager(
|
|
462
|
-
options,
|
|
463
|
-
manager
|
|
464
|
-
);
|
|
465
|
-
const fetcher = React2.useCallback(
|
|
466
|
-
() => {
|
|
467
|
-
return room.getThreads(options);
|
|
468
|
-
},
|
|
469
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps -- The missing dependency is `options` but `key` and `normalized` are analogous, so we only include `key` as dependency. This helps minimize the number of re-renders as `options` can change on each render
|
|
470
|
-
[key, room]
|
|
471
|
-
);
|
|
472
|
-
const revalidateCache = useRevalidateCache(
|
|
473
|
-
useThreadsRevalidationManager,
|
|
474
|
-
fetcher
|
|
475
|
-
);
|
|
476
|
-
useEffect3(() => {
|
|
477
|
-
void revalidateCache({ shouldDedupe: true });
|
|
478
|
-
}, [revalidateCache]);
|
|
479
|
-
useEffect3(() => {
|
|
480
|
-
manager.incrementReferenceCount(key);
|
|
481
|
-
return () => {
|
|
482
|
-
manager.decrementReferenceCount(key);
|
|
483
|
-
};
|
|
484
|
-
}, [manager, key]);
|
|
485
|
-
const cache = _useThreads(room, options);
|
|
486
|
-
if (cache.error) {
|
|
487
|
-
throw cache.error;
|
|
488
|
-
}
|
|
489
|
-
if (cache.isLoading || !cache.threads) {
|
|
490
|
-
throw revalidateCache({
|
|
491
|
-
shouldDedupe: true
|
|
492
|
-
});
|
|
493
|
-
}
|
|
494
|
-
return {
|
|
495
|
-
isLoading: false,
|
|
496
|
-
threads: cache.threads,
|
|
497
|
-
error: cache.error
|
|
498
|
-
};
|
|
499
|
-
}
|
|
500
|
-
function _useThreads(room, options) {
|
|
501
|
-
const manager = useRoomManager();
|
|
502
|
-
const useThreadsRevalidationManager = getUseThreadsRevalidationManager(
|
|
503
|
-
options,
|
|
504
|
-
manager
|
|
505
|
-
);
|
|
506
|
-
return useSyncExternalStoreWithSelector(
|
|
507
|
-
store.subscribe,
|
|
508
|
-
() => store.getThreads(),
|
|
509
|
-
() => store.getThreads(),
|
|
510
|
-
(state) => {
|
|
511
|
-
const isLoading = useThreadsRevalidationManager.getIsLoading();
|
|
512
|
-
if (isLoading) {
|
|
513
|
-
return {
|
|
514
|
-
isLoading: true
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
const options2 = useThreadsRevalidationManager.getOptions();
|
|
518
|
-
const error = useThreadsRevalidationManager.getError();
|
|
519
|
-
const filtered = state.filter((thread) => {
|
|
520
|
-
if (thread.roomId !== room.id)
|
|
521
|
-
return false;
|
|
522
|
-
const query = options2.query ?? {};
|
|
523
|
-
for (const key in query.metadata) {
|
|
524
|
-
if (thread.metadata[key] !== query.metadata[key]) {
|
|
525
|
-
return false;
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
return true;
|
|
529
|
-
});
|
|
530
|
-
return {
|
|
531
|
-
isLoading: false,
|
|
532
|
-
threads: filtered,
|
|
533
|
-
error
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
);
|
|
537
|
-
}
|
|
538
|
-
function useEditThreadMetadata(room) {
|
|
539
|
-
const manager = useRoomManager();
|
|
540
|
-
const fetcher = useThreadsFetcher();
|
|
541
|
-
const revalidate = useRevalidateCache(manager, fetcher);
|
|
542
|
-
const mutate = useMutate(manager, revalidate);
|
|
543
|
-
const editThreadMetadata = useCallback3(
|
|
544
|
-
(options) => {
|
|
545
|
-
const threadId = options.threadId;
|
|
546
|
-
const metadata = "metadata" in options ? options.metadata : {};
|
|
547
|
-
const threads = getThreads(manager);
|
|
548
|
-
const optimisticData = threads.map(
|
|
549
|
-
(thread) => thread.id === threadId ? {
|
|
550
|
-
...thread,
|
|
551
|
-
metadata: {
|
|
552
|
-
...thread.metadata,
|
|
553
|
-
...metadata
|
|
554
|
-
}
|
|
555
|
-
} : thread
|
|
556
|
-
);
|
|
557
|
-
mutate(room.editThreadMetadata({ metadata, threadId }), {
|
|
558
|
-
optimisticData
|
|
559
|
-
}).catch((err) => {
|
|
560
|
-
if (!(err instanceof CommentsApiError)) {
|
|
561
|
-
throw err;
|
|
562
|
-
}
|
|
563
|
-
const error = handleCommentsApiError(err);
|
|
564
|
-
errorEventSource.notify(
|
|
565
|
-
new EditThreadMetadataError(error, {
|
|
566
|
-
roomId: room.id,
|
|
567
|
-
threadId,
|
|
568
|
-
metadata
|
|
569
|
-
})
|
|
570
|
-
);
|
|
571
|
-
});
|
|
572
|
-
},
|
|
573
|
-
[room, mutate, manager]
|
|
574
|
-
);
|
|
575
|
-
return editThreadMetadata;
|
|
576
|
-
}
|
|
577
|
-
function useCreateThread(room) {
|
|
578
|
-
const manager = useRoomManager();
|
|
579
|
-
const fetcher = useThreadsFetcher();
|
|
580
|
-
const revalidate = useRevalidateCache(manager, fetcher);
|
|
581
|
-
const mutate = useMutate(manager, revalidate);
|
|
582
|
-
const createThread = useCallback3(
|
|
583
|
-
(options) => {
|
|
584
|
-
const body = options.body;
|
|
585
|
-
const metadata = "metadata" in options ? options.metadata : {};
|
|
586
|
-
const threads = getThreads(manager);
|
|
587
|
-
const threadId = createThreadId();
|
|
588
|
-
const commentId = createCommentId();
|
|
589
|
-
const now = /* @__PURE__ */ new Date();
|
|
590
|
-
const newComment = {
|
|
591
|
-
id: commentId,
|
|
592
|
-
threadId,
|
|
593
|
-
roomId: room.id,
|
|
594
|
-
createdAt: now,
|
|
595
|
-
type: "comment",
|
|
596
|
-
userId: getCurrentUserId(room),
|
|
597
|
-
body,
|
|
598
|
-
reactions: []
|
|
599
|
-
};
|
|
600
|
-
const newThread = {
|
|
601
|
-
id: threadId,
|
|
602
|
-
type: "thread",
|
|
603
|
-
createdAt: now,
|
|
604
|
-
roomId: room.id,
|
|
605
|
-
metadata,
|
|
606
|
-
comments: [newComment]
|
|
607
|
-
};
|
|
608
|
-
mutate(room.createThread({ threadId, commentId, body, metadata }), {
|
|
609
|
-
optimisticData: [...threads, newThread]
|
|
610
|
-
}).catch((err) => {
|
|
611
|
-
if (!(err instanceof CommentsApiError)) {
|
|
612
|
-
throw err;
|
|
613
|
-
}
|
|
614
|
-
const error = handleCommentsApiError(err);
|
|
615
|
-
errorEventSource.notify(
|
|
616
|
-
new CreateThreadError(error, {
|
|
617
|
-
roomId: room.id,
|
|
618
|
-
threadId,
|
|
619
|
-
commentId,
|
|
620
|
-
body,
|
|
621
|
-
metadata
|
|
622
|
-
})
|
|
623
|
-
);
|
|
624
|
-
});
|
|
625
|
-
return newThread;
|
|
626
|
-
},
|
|
627
|
-
[room, mutate, manager]
|
|
628
|
-
);
|
|
629
|
-
return createThread;
|
|
630
|
-
}
|
|
631
|
-
function useCreateComment(room) {
|
|
632
|
-
const manager = useRoomManager();
|
|
633
|
-
const fetcher = useThreadsFetcher();
|
|
634
|
-
const revalidate = useRevalidateCache(manager, fetcher);
|
|
635
|
-
const mutate = useMutate(manager, revalidate);
|
|
636
|
-
const createComment = useCallback3(
|
|
637
|
-
({ threadId, body }) => {
|
|
638
|
-
const threads = getThreads(manager);
|
|
639
|
-
const commentId = createCommentId();
|
|
640
|
-
const now = /* @__PURE__ */ new Date();
|
|
641
|
-
const comment = {
|
|
642
|
-
id: commentId,
|
|
643
|
-
threadId,
|
|
644
|
-
roomId: room.id,
|
|
645
|
-
type: "comment",
|
|
646
|
-
createdAt: now,
|
|
647
|
-
userId: getCurrentUserId(room),
|
|
648
|
-
body,
|
|
649
|
-
reactions: []
|
|
650
|
-
};
|
|
651
|
-
const optimisticData = threads.map(
|
|
652
|
-
(thread) => thread.id === threadId ? {
|
|
653
|
-
...thread,
|
|
654
|
-
comments: [...thread.comments, comment]
|
|
655
|
-
} : thread
|
|
656
|
-
);
|
|
657
|
-
mutate(room.createComment({ threadId, commentId, body }), {
|
|
658
|
-
optimisticData
|
|
659
|
-
}).catch((err) => {
|
|
660
|
-
if (!(err instanceof CommentsApiError)) {
|
|
661
|
-
throw err;
|
|
662
|
-
}
|
|
663
|
-
const error = handleCommentsApiError(err);
|
|
664
|
-
errorEventSource.notify(
|
|
665
|
-
new CreateCommentError(error, {
|
|
666
|
-
roomId: room.id,
|
|
667
|
-
threadId,
|
|
668
|
-
commentId,
|
|
669
|
-
body
|
|
670
|
-
})
|
|
671
|
-
);
|
|
672
|
-
});
|
|
673
|
-
return comment;
|
|
674
|
-
},
|
|
675
|
-
[room, mutate, manager]
|
|
676
|
-
);
|
|
677
|
-
return createComment;
|
|
678
|
-
}
|
|
679
|
-
function useEditComment(room) {
|
|
680
|
-
const manager = useRoomManager();
|
|
681
|
-
const fetcher = useThreadsFetcher();
|
|
682
|
-
const revalidate = useRevalidateCache(manager, fetcher);
|
|
683
|
-
const mutate = useMutate(manager, revalidate);
|
|
684
|
-
const editComment = useCallback3(
|
|
685
|
-
({ threadId, commentId, body }) => {
|
|
686
|
-
const threads = getThreads(manager);
|
|
687
|
-
const now = /* @__PURE__ */ new Date();
|
|
688
|
-
const optimisticData = threads.map(
|
|
689
|
-
(thread) => thread.id === threadId ? {
|
|
690
|
-
...thread,
|
|
691
|
-
comments: thread.comments.map(
|
|
692
|
-
(comment) => comment.id === commentId ? {
|
|
693
|
-
...comment,
|
|
694
|
-
editedAt: now,
|
|
695
|
-
body
|
|
696
|
-
} : comment
|
|
697
|
-
)
|
|
698
|
-
} : thread
|
|
699
|
-
);
|
|
700
|
-
mutate(room.editComment({ threadId, commentId, body }), {
|
|
701
|
-
optimisticData
|
|
702
|
-
}).catch((err) => {
|
|
703
|
-
if (!(err instanceof CommentsApiError)) {
|
|
704
|
-
throw err;
|
|
705
|
-
}
|
|
706
|
-
const error = handleCommentsApiError(err);
|
|
707
|
-
errorEventSource.notify(
|
|
708
|
-
new EditCommentError(error, {
|
|
709
|
-
roomId: room.id,
|
|
710
|
-
threadId,
|
|
711
|
-
commentId,
|
|
712
|
-
body
|
|
713
|
-
})
|
|
714
|
-
);
|
|
715
|
-
});
|
|
716
|
-
},
|
|
717
|
-
[room, mutate, manager]
|
|
718
|
-
);
|
|
719
|
-
return editComment;
|
|
720
|
-
}
|
|
721
|
-
function useDeleteComment(room) {
|
|
722
|
-
const manager = useRoomManager();
|
|
723
|
-
const fetcher = useThreadsFetcher();
|
|
724
|
-
const revalidate = useRevalidateCache(manager, fetcher);
|
|
725
|
-
const mutate = useMutate(manager, revalidate);
|
|
726
|
-
const deleteComment = useCallback3(
|
|
727
|
-
({ threadId, commentId }) => {
|
|
728
|
-
const threads = getThreads(manager);
|
|
729
|
-
const now = /* @__PURE__ */ new Date();
|
|
730
|
-
const newThreads = [];
|
|
731
|
-
for (const thread of threads) {
|
|
732
|
-
if (thread.id === threadId) {
|
|
733
|
-
const newThread = {
|
|
734
|
-
...thread,
|
|
735
|
-
comments: thread.comments.map(
|
|
736
|
-
(comment) => comment.id === commentId ? {
|
|
737
|
-
...comment,
|
|
738
|
-
deletedAt: now,
|
|
739
|
-
body: void 0
|
|
740
|
-
} : comment
|
|
741
|
-
)
|
|
742
|
-
};
|
|
743
|
-
if (newThread.comments.some(
|
|
744
|
-
(comment) => comment.deletedAt === void 0
|
|
745
|
-
)) {
|
|
746
|
-
newThreads.push(newThread);
|
|
747
|
-
}
|
|
748
|
-
} else {
|
|
749
|
-
newThreads.push(thread);
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
mutate(room.deleteComment({ threadId, commentId }), {
|
|
753
|
-
optimisticData: newThreads
|
|
754
|
-
}).catch((err) => {
|
|
755
|
-
if (!(err instanceof CommentsApiError)) {
|
|
756
|
-
throw err;
|
|
757
|
-
}
|
|
758
|
-
const error = handleCommentsApiError(err);
|
|
759
|
-
errorEventSource.notify(
|
|
760
|
-
new DeleteCommentError(error, {
|
|
761
|
-
roomId: room.id,
|
|
762
|
-
threadId,
|
|
763
|
-
commentId
|
|
764
|
-
})
|
|
765
|
-
);
|
|
766
|
-
});
|
|
767
|
-
},
|
|
768
|
-
[room, mutate, manager]
|
|
769
|
-
);
|
|
770
|
-
return deleteComment;
|
|
771
|
-
}
|
|
772
|
-
function useAddReaction(room) {
|
|
773
|
-
const manager = useRoomManager();
|
|
774
|
-
const fetcher = useThreadsFetcher();
|
|
775
|
-
const revalidate = useRevalidateCache(manager, fetcher);
|
|
776
|
-
const mutate = useMutate(manager, revalidate);
|
|
777
|
-
const createComment = useCallback3(
|
|
778
|
-
({ threadId, commentId, emoji }) => {
|
|
779
|
-
const threads = getThreads(manager);
|
|
780
|
-
const now = /* @__PURE__ */ new Date();
|
|
781
|
-
const userId = getCurrentUserId(room);
|
|
782
|
-
const optimisticData = threads.map(
|
|
783
|
-
(thread) => thread.id === threadId ? {
|
|
784
|
-
...thread,
|
|
785
|
-
comments: thread.comments.map((comment) => {
|
|
786
|
-
if (comment.id !== commentId) {
|
|
787
|
-
return comment;
|
|
788
|
-
}
|
|
789
|
-
let reactions;
|
|
790
|
-
if (comment.reactions.some(
|
|
791
|
-
(reaction) => reaction.emoji === emoji
|
|
792
|
-
)) {
|
|
793
|
-
reactions = comment.reactions.map(
|
|
794
|
-
(reaction) => reaction.emoji === emoji ? {
|
|
795
|
-
...reaction,
|
|
796
|
-
users: [...reaction.users, { id: userId }]
|
|
797
|
-
} : reaction
|
|
798
|
-
);
|
|
799
|
-
} else {
|
|
800
|
-
reactions = [
|
|
801
|
-
...comment.reactions,
|
|
802
|
-
{
|
|
803
|
-
emoji,
|
|
804
|
-
createdAt: now,
|
|
805
|
-
users: [{ id: userId }]
|
|
806
|
-
}
|
|
807
|
-
];
|
|
808
|
-
}
|
|
809
|
-
return {
|
|
810
|
-
...comment,
|
|
811
|
-
reactions
|
|
812
|
-
};
|
|
813
|
-
})
|
|
814
|
-
} : thread
|
|
815
|
-
);
|
|
816
|
-
mutate(room.addReaction({ threadId, commentId, emoji }), {
|
|
817
|
-
optimisticData
|
|
818
|
-
}).catch((err) => {
|
|
819
|
-
if (!(err instanceof CommentsApiError)) {
|
|
820
|
-
throw err;
|
|
821
|
-
}
|
|
822
|
-
const error = handleCommentsApiError(err);
|
|
823
|
-
errorEventSource.notify(
|
|
824
|
-
new AddReactionError(error, {
|
|
825
|
-
roomId: room.id,
|
|
826
|
-
threadId,
|
|
827
|
-
commentId,
|
|
828
|
-
emoji
|
|
829
|
-
})
|
|
830
|
-
);
|
|
831
|
-
});
|
|
832
|
-
},
|
|
833
|
-
[room, mutate, manager]
|
|
834
|
-
);
|
|
835
|
-
return createComment;
|
|
836
|
-
}
|
|
837
|
-
function useRemoveReaction(room) {
|
|
838
|
-
const manager = useRoomManager();
|
|
839
|
-
const fetcher = useThreadsFetcher();
|
|
840
|
-
const revalidate = useRevalidateCache(manager, fetcher);
|
|
841
|
-
const mutate = useMutate(manager, revalidate);
|
|
842
|
-
const createComment = useCallback3(
|
|
843
|
-
({ threadId, commentId, emoji }) => {
|
|
844
|
-
const threads = getThreads(manager);
|
|
845
|
-
const userId = getCurrentUserId(room);
|
|
846
|
-
const optimisticData = threads.map(
|
|
847
|
-
(thread) => thread.id === threadId ? {
|
|
848
|
-
...thread,
|
|
849
|
-
comments: thread.comments.map((comment) => {
|
|
850
|
-
if (comment.id !== commentId) {
|
|
851
|
-
return comment;
|
|
852
|
-
}
|
|
853
|
-
const reactionIndex = comment.reactions.findIndex(
|
|
854
|
-
(reaction) => reaction.emoji === emoji
|
|
855
|
-
);
|
|
856
|
-
let reactions = comment.reactions;
|
|
857
|
-
if (reactionIndex >= 0 && comment.reactions[reactionIndex].users.some(
|
|
858
|
-
(user) => user.id === userId
|
|
859
|
-
)) {
|
|
860
|
-
if (comment.reactions[reactionIndex].users.length <= 1) {
|
|
861
|
-
reactions = [...comment.reactions];
|
|
862
|
-
reactions.splice(reactionIndex, 1);
|
|
863
|
-
} else {
|
|
864
|
-
reactions[reactionIndex] = {
|
|
865
|
-
...reactions[reactionIndex],
|
|
866
|
-
users: reactions[reactionIndex].users.filter(
|
|
867
|
-
(user) => user.id !== userId
|
|
868
|
-
)
|
|
869
|
-
};
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
return {
|
|
873
|
-
...comment,
|
|
874
|
-
reactions
|
|
875
|
-
};
|
|
876
|
-
})
|
|
877
|
-
} : thread
|
|
878
|
-
);
|
|
879
|
-
mutate(room.removeReaction({ threadId, commentId, emoji }), {
|
|
880
|
-
optimisticData
|
|
881
|
-
}).catch((err) => {
|
|
882
|
-
if (!(err instanceof CommentsApiError)) {
|
|
883
|
-
throw err;
|
|
884
|
-
}
|
|
885
|
-
const error = handleCommentsApiError(err);
|
|
886
|
-
errorEventSource.notify(
|
|
887
|
-
new RemoveReactionError(error, {
|
|
888
|
-
roomId: room.id,
|
|
889
|
-
threadId,
|
|
890
|
-
commentId,
|
|
891
|
-
emoji
|
|
892
|
-
})
|
|
893
|
-
);
|
|
894
|
-
});
|
|
895
|
-
},
|
|
896
|
-
[room, mutate, manager]
|
|
897
|
-
);
|
|
898
|
-
return createComment;
|
|
899
|
-
}
|
|
900
|
-
return {
|
|
901
|
-
CommentsRoomProvider,
|
|
902
|
-
useThreads,
|
|
903
|
-
useThreadsSuspense,
|
|
904
|
-
useEditThreadMetadata,
|
|
905
|
-
useCreateThread,
|
|
906
|
-
useCreateComment,
|
|
907
|
-
useEditComment,
|
|
908
|
-
useDeleteComment,
|
|
909
|
-
useAddReaction,
|
|
910
|
-
useRemoveReaction
|
|
911
|
-
};
|
|
912
|
-
}
|
|
913
|
-
function createOptimisticId(prefix) {
|
|
914
|
-
return `${prefix}_${nanoid()}`;
|
|
915
|
-
}
|
|
916
|
-
function createThreadId() {
|
|
917
|
-
return createOptimisticId(THREAD_ID_PREFIX);
|
|
918
|
-
}
|
|
919
|
-
function createCommentId() {
|
|
920
|
-
return createOptimisticId(COMMENT_ID_PREFIX);
|
|
921
|
-
}
|
|
922
|
-
function getCurrentUserId(room) {
|
|
923
|
-
const self = room.getSelf();
|
|
924
|
-
if (self === null || self.id === void 0) {
|
|
925
|
-
return "anonymous";
|
|
926
|
-
} else {
|
|
927
|
-
return self.id;
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
function handleCommentsApiError(err) {
|
|
931
|
-
const message = `Request failed with status ${err.status}: ${err.message}`;
|
|
932
|
-
if (err.details?.error === "FORBIDDEN") {
|
|
933
|
-
const detailedMessage = [message, err.details.suggestion, err.details.docs].filter(Boolean).join("\n");
|
|
934
|
-
console.error(detailedMessage);
|
|
935
|
-
}
|
|
936
|
-
return new Error(message);
|
|
937
|
-
}
|
|
938
|
-
function createRoomRevalidationManager(roomId, {
|
|
939
|
-
getCache,
|
|
940
|
-
setCache
|
|
941
|
-
}) {
|
|
942
|
-
let request;
|
|
943
|
-
let error;
|
|
944
|
-
let mutation;
|
|
945
|
-
const revalidationManagerByOptions = /* @__PURE__ */ new Map();
|
|
946
|
-
const referenceCountByOptions = /* @__PURE__ */ new Map();
|
|
947
|
-
return {
|
|
948
|
-
// Cache
|
|
949
|
-
getCache() {
|
|
950
|
-
const threads = getCache();
|
|
951
|
-
const filtered = threads.filter((thread) => thread.roomId === roomId);
|
|
952
|
-
return filtered;
|
|
953
|
-
},
|
|
954
|
-
setCache(value) {
|
|
955
|
-
for (const key of revalidationManagerByOptions.keys()) {
|
|
956
|
-
if (referenceCountByOptions.get(key) === 0) {
|
|
957
|
-
revalidationManagerByOptions.delete(key);
|
|
958
|
-
referenceCountByOptions.delete(key);
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
const sorted = value.sort(
|
|
962
|
-
(a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
|
|
963
|
-
);
|
|
964
|
-
const threads = getCache();
|
|
965
|
-
const newThreads = threads.filter((thread) => thread.roomId !== roomId).concat(sorted);
|
|
966
|
-
setCache(newThreads);
|
|
967
|
-
},
|
|
968
|
-
// Request
|
|
969
|
-
getRequest() {
|
|
970
|
-
return request;
|
|
971
|
-
},
|
|
972
|
-
setRequest(value) {
|
|
973
|
-
request = value;
|
|
974
|
-
},
|
|
975
|
-
// Error
|
|
976
|
-
getError() {
|
|
977
|
-
return error;
|
|
978
|
-
},
|
|
979
|
-
setError(err) {
|
|
980
|
-
error = err;
|
|
981
|
-
},
|
|
982
|
-
// Mutation
|
|
983
|
-
getMutation() {
|
|
984
|
-
return mutation;
|
|
985
|
-
},
|
|
986
|
-
setMutation(info) {
|
|
987
|
-
mutation = info;
|
|
988
|
-
},
|
|
989
|
-
getRevalidationManagers() {
|
|
990
|
-
return Array.from(revalidationManagerByOptions.entries());
|
|
991
|
-
},
|
|
992
|
-
getRevalidationManager(key) {
|
|
993
|
-
return revalidationManagerByOptions.get(key);
|
|
994
|
-
},
|
|
995
|
-
setRevalidationmanager(key, manager) {
|
|
996
|
-
revalidationManagerByOptions.set(key, manager);
|
|
997
|
-
},
|
|
998
|
-
getTotalReferenceCount() {
|
|
999
|
-
return Array.from(referenceCountByOptions.values()).reduce(
|
|
1000
|
-
(acc, count) => acc + count,
|
|
1001
|
-
0
|
|
1002
|
-
);
|
|
1003
|
-
},
|
|
1004
|
-
incrementReferenceCount(key) {
|
|
1005
|
-
const count = referenceCountByOptions.get(key) ?? 0;
|
|
1006
|
-
referenceCountByOptions.set(key, count + 1);
|
|
1007
|
-
},
|
|
1008
|
-
decrementReferenceCount(key) {
|
|
1009
|
-
const count = referenceCountByOptions.get(key) ?? 0;
|
|
1010
|
-
referenceCountByOptions.set(key, count - 1);
|
|
1011
|
-
},
|
|
1012
|
-
getReferenceCount(key) {
|
|
1013
|
-
return referenceCountByOptions.get(key) ?? 0;
|
|
1014
|
-
}
|
|
1015
|
-
};
|
|
1016
|
-
}
|
|
1017
|
-
function createClientCacheStore() {
|
|
1018
|
-
let threads = [];
|
|
1019
|
-
const threadsEventSource = makeEventSource();
|
|
1020
|
-
return {
|
|
1021
|
-
getThreads() {
|
|
1022
|
-
return threads;
|
|
1023
|
-
},
|
|
1024
|
-
setThreads(value) {
|
|
1025
|
-
threads = value;
|
|
1026
|
-
threadsEventSource.notify(threads);
|
|
1027
|
-
},
|
|
1028
|
-
subscribe(callback) {
|
|
1029
|
-
return threadsEventSource.subscribe(callback);
|
|
1030
|
-
}
|
|
1031
|
-
};
|
|
1032
|
-
}
|
|
1033
|
-
function createUseThreadsRevalidationManager(options, manager) {
|
|
1034
|
-
let isLoading = true;
|
|
1035
|
-
let request;
|
|
1036
|
-
let error;
|
|
1037
|
-
return {
|
|
1038
|
-
// Cache
|
|
1039
|
-
getCache() {
|
|
1040
|
-
return void 0;
|
|
1041
|
-
},
|
|
1042
|
-
setCache(value) {
|
|
1043
|
-
const cache = new Map(
|
|
1044
|
-
(manager.getCache() ?? []).map((thread) => [thread.id, thread])
|
|
1045
|
-
);
|
|
1046
|
-
for (const thread of value) {
|
|
1047
|
-
cache.set(thread.id, thread);
|
|
1048
|
-
}
|
|
1049
|
-
manager.setCache(Array.from(cache.values()));
|
|
1050
|
-
isLoading = false;
|
|
1051
|
-
},
|
|
1052
|
-
// Request
|
|
1053
|
-
getRequest() {
|
|
1054
|
-
return request;
|
|
1055
|
-
},
|
|
1056
|
-
setRequest(value) {
|
|
1057
|
-
request = value;
|
|
1058
|
-
},
|
|
1059
|
-
// Error
|
|
1060
|
-
getError() {
|
|
1061
|
-
return error;
|
|
1062
|
-
},
|
|
1063
|
-
setError(err) {
|
|
1064
|
-
error = err;
|
|
1065
|
-
isLoading = false;
|
|
1066
|
-
const cache = manager.getCache();
|
|
1067
|
-
manager.setCache(cache);
|
|
1068
|
-
},
|
|
1069
|
-
// Mutation
|
|
1070
|
-
getMutation() {
|
|
1071
|
-
return void 0;
|
|
1072
|
-
},
|
|
1073
|
-
setMutation(_) {
|
|
1074
|
-
return;
|
|
1075
|
-
},
|
|
1076
|
-
getOptions() {
|
|
1077
|
-
return options;
|
|
1078
|
-
},
|
|
1079
|
-
getIsLoading() {
|
|
1080
|
-
return isLoading;
|
|
1081
|
-
},
|
|
1082
|
-
setIsLoading(value) {
|
|
1083
|
-
isLoading = value;
|
|
1084
|
-
}
|
|
1085
|
-
};
|
|
1086
|
-
}
|
|
1087
|
-
function getPollingInterval(isBrowserOnline, isDocumentVisible, isRoomConnected) {
|
|
1088
|
-
if (!isBrowserOnline || !isDocumentVisible)
|
|
1089
|
-
return;
|
|
1090
|
-
if (isRoomConnected)
|
|
1091
|
-
return POLLING_INTERVAL_REALTIME;
|
|
1092
|
-
return POLLING_INTERVAL;
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
// src/comments/lib/use-debounce.ts
|
|
1096
|
-
import { useEffect as useEffect4, useRef as useRef3, useState as useState2 } from "react";
|
|
1097
|
-
var DEFAULT_DELAY = 500;
|
|
1098
|
-
function useDebounce(value, delay = DEFAULT_DELAY) {
|
|
1099
|
-
const timeout = useRef3();
|
|
1100
|
-
const [debouncedValue, setDebouncedValue] = useState2(value);
|
|
1101
|
-
useEffect4(() => {
|
|
1102
|
-
if (delay === false) {
|
|
1103
|
-
return;
|
|
1104
|
-
}
|
|
1105
|
-
if (timeout.current === void 0) {
|
|
1106
|
-
setDebouncedValue(value);
|
|
1107
|
-
}
|
|
1108
|
-
timeout.current = window.setTimeout(() => {
|
|
1109
|
-
setDebouncedValue(value);
|
|
1110
|
-
timeout.current = void 0;
|
|
1111
|
-
}, delay);
|
|
1112
|
-
return () => {
|
|
1113
|
-
window.clearTimeout(timeout.current);
|
|
1114
|
-
};
|
|
1115
|
-
}, [value, delay]);
|
|
1116
|
-
return debouncedValue;
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
// src/lib/use-async-cache.ts
|
|
1120
|
-
import { useCallback as useCallback4, useEffect as useEffect5, useMemo as useMemo2, useRef as useRef4 } from "react";
|
|
1121
|
-
import { useSyncExternalStore as useSyncExternalStore4 } from "use-sync-external-store/shim/index.js";
|
|
1122
|
-
|
|
1123
|
-
// src/lib/use-initial.ts
|
|
1124
|
-
import { useState as useState3 } from "react";
|
|
1125
|
-
function useInitial(value) {
|
|
1126
|
-
return useState3(value)[0];
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
// src/lib/use-async-cache.ts
|
|
1130
|
-
var INITIAL_ASYNC_STATE = {
|
|
1131
|
-
isLoading: false,
|
|
1132
|
-
data: void 0,
|
|
1133
|
-
error: void 0
|
|
1134
|
-
};
|
|
1135
|
-
var noop = () => {
|
|
1136
|
-
};
|
|
1137
|
-
function useAsyncCache(cache, key, options) {
|
|
1138
|
-
const frozenOptions = useInitial(options);
|
|
1139
|
-
const cacheItem = useMemo2(() => {
|
|
1140
|
-
if (key === null || !cache) {
|
|
1141
|
-
return null;
|
|
1142
|
-
}
|
|
1143
|
-
const cacheItem2 = cache.create(key);
|
|
1144
|
-
void cacheItem2.get();
|
|
1145
|
-
return cacheItem2;
|
|
1146
|
-
}, [cache, key]);
|
|
1147
|
-
const subscribe2 = useCallback4(
|
|
1148
|
-
(callback) => cacheItem?.subscribe(callback) ?? noop,
|
|
1149
|
-
[cacheItem]
|
|
1150
|
-
);
|
|
1151
|
-
const getState = useCallback4(
|
|
1152
|
-
() => cacheItem?.getState() ?? INITIAL_ASYNC_STATE,
|
|
1153
|
-
[cacheItem]
|
|
1154
|
-
);
|
|
1155
|
-
const revalidate = useCallback4(() => cacheItem?.revalidate(), [cacheItem]);
|
|
1156
|
-
const state = useSyncExternalStore4(subscribe2, getState, getState);
|
|
1157
|
-
const previousData = useRef4();
|
|
1158
|
-
let data = state.data;
|
|
1159
|
-
useEffect5(() => {
|
|
1160
|
-
previousData.current = { key, data: state.data };
|
|
1161
|
-
}, [key, state.data]);
|
|
1162
|
-
if (!cacheItem) {
|
|
1163
|
-
return {
|
|
1164
|
-
isLoading: false,
|
|
1165
|
-
data: void 0,
|
|
1166
|
-
error: void 0,
|
|
1167
|
-
getState,
|
|
1168
|
-
revalidate
|
|
1169
|
-
};
|
|
1170
|
-
}
|
|
1171
|
-
if (frozenOptions?.suspense) {
|
|
1172
|
-
const error = getState().error;
|
|
1173
|
-
if (error) {
|
|
1174
|
-
throw error;
|
|
1175
|
-
} else if (getState().isLoading) {
|
|
1176
|
-
throw new Promise((resolve) => {
|
|
1177
|
-
cacheItem.subscribeOnce((state2) => {
|
|
1178
|
-
if (!state2.isLoading) {
|
|
1179
|
-
resolve();
|
|
1180
|
-
}
|
|
1181
|
-
});
|
|
1182
|
-
});
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
|
-
if (state.isLoading && frozenOptions?.keepPreviousDataWhileLoading && typeof state.data === "undefined" && previousData.current?.key !== key && typeof previousData.current?.data !== "undefined") {
|
|
1186
|
-
data = previousData.current.data;
|
|
1187
|
-
}
|
|
1188
|
-
return {
|
|
1189
|
-
isLoading: state.isLoading,
|
|
1190
|
-
data,
|
|
1191
|
-
error: state.error,
|
|
1192
|
-
getState,
|
|
1193
|
-
revalidate
|
|
1194
|
-
};
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
// src/lib/use-latest.ts
|
|
1198
|
-
import { useEffect as useEffect6, useRef as useRef5 } from "react";
|
|
1199
|
-
function useLatest(value) {
|
|
1200
|
-
const ref = useRef5(value);
|
|
1201
|
-
useEffect6(() => {
|
|
1202
|
-
ref.current = value;
|
|
1203
|
-
}, [value]);
|
|
1204
|
-
return ref;
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
// src/lib/use-rerender.ts
|
|
1208
|
-
import { useReducer } from "react";
|
|
1209
|
-
function useRerender() {
|
|
1210
|
-
const [, update] = useReducer(
|
|
1211
|
-
// This implementation works by incrementing a hidden counter value that is
|
|
1212
|
-
// never consumed. Simply incrementing the counter changes the component's
|
|
1213
|
-
// state and, thus, trigger a re-render.
|
|
1214
|
-
(x) => x + 1,
|
|
1215
|
-
0
|
|
1216
|
-
);
|
|
1217
|
-
return update;
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
// src/factory.tsx
|
|
1221
|
-
var noop2 = () => {
|
|
1222
|
-
};
|
|
1223
|
-
var identity = (x) => x;
|
|
1224
|
-
var missing_unstable_batchedUpdates = (reactVersion, roomId) => `We noticed you\u2019re using React ${reactVersion}. Please pass unstable_batchedUpdates at the RoomProvider level until you\u2019re ready to upgrade to React 18:
|
|
1225
|
-
|
|
1226
|
-
import { unstable_batchedUpdates } from "react-dom"; // or "react-native"
|
|
1227
|
-
|
|
1228
|
-
<RoomProvider id=${JSON.stringify(
|
|
1229
|
-
roomId
|
|
1230
|
-
)} ... unstable_batchedUpdates={unstable_batchedUpdates}>
|
|
1231
|
-
...
|
|
1232
|
-
</RoomProvider>
|
|
1233
|
-
|
|
1234
|
-
Why? Please see https://liveblocks.io/docs/platform/troubleshooting#stale-props-zombie-child for more information`;
|
|
1235
|
-
var superfluous_unstable_batchedUpdates = "You don\u2019t need to pass unstable_batchedUpdates to RoomProvider anymore, since you\u2019re on React 18+ already.";
|
|
1236
|
-
function useSyncExternalStore3(s, gs, gss) {
|
|
1237
|
-
return useSyncExternalStoreWithSelector2(s, gs, gss, identity);
|
|
1238
|
-
}
|
|
1239
|
-
var STABLE_EMPTY_LIST = Object.freeze([]);
|
|
1240
|
-
function alwaysEmptyList() {
|
|
1241
|
-
return STABLE_EMPTY_LIST;
|
|
1242
|
-
}
|
|
1243
|
-
function alwaysNull() {
|
|
1244
|
-
return null;
|
|
1245
|
-
}
|
|
1246
|
-
function makeMutationContext(room) {
|
|
1247
|
-
const errmsg = "This mutation cannot be used until connected to the Liveblocks room";
|
|
1248
|
-
return {
|
|
1249
|
-
get storage() {
|
|
1250
|
-
const mutableRoot = room.getStorageSnapshot();
|
|
1251
|
-
if (mutableRoot === null) {
|
|
1252
|
-
throw new Error(errmsg);
|
|
1253
|
-
}
|
|
1254
|
-
return mutableRoot;
|
|
1255
|
-
},
|
|
1256
|
-
get self() {
|
|
1257
|
-
const self = room.getSelf();
|
|
1258
|
-
if (self === null) {
|
|
1259
|
-
throw new Error(errmsg);
|
|
1260
|
-
}
|
|
1261
|
-
return self;
|
|
1262
|
-
},
|
|
1263
|
-
get others() {
|
|
1264
|
-
const others = room.getOthers();
|
|
1265
|
-
if (room.getSelf() === null) {
|
|
1266
|
-
throw new Error(errmsg);
|
|
1267
|
-
}
|
|
1268
|
-
return others;
|
|
1269
|
-
},
|
|
1270
|
-
setMyPresence: room.updatePresence
|
|
1271
|
-
};
|
|
1272
|
-
}
|
|
1273
|
-
var hasWarnedIfNoResolveUsers = false;
|
|
1274
|
-
function warnIfNoResolveUsers(usersCache) {
|
|
1275
|
-
if (!hasWarnedIfNoResolveUsers && !usersCache && process.env.NODE_ENV !== "production") {
|
|
1276
|
-
console.warn(
|
|
1277
|
-
"Set the resolveUsers option in createRoomContext to specify user info."
|
|
1278
|
-
);
|
|
1279
|
-
hasWarnedIfNoResolveUsers = true;
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
var ContextBundle = React3.createContext(null);
|
|
1283
|
-
function useRoomContextBundle() {
|
|
1284
|
-
const bundle = React3.useContext(ContextBundle);
|
|
1285
|
-
if (bundle === null) {
|
|
1286
|
-
throw new Error("RoomProvider is missing from the React tree.");
|
|
1287
|
-
}
|
|
1288
|
-
return bundle;
|
|
1289
|
-
}
|
|
1290
|
-
function createRoomContext(client, options) {
|
|
1291
|
-
const RoomContext = React3.createContext(null);
|
|
1292
|
-
const commentsErrorEventSource = makeEventSource2();
|
|
1293
|
-
const { CommentsRoomProvider, ...commentsRoom } = createCommentsRoom(commentsErrorEventSource);
|
|
1294
|
-
function RoomProviderOuter(props) {
|
|
1295
|
-
const [cache] = React3.useState(
|
|
1296
|
-
() => /* @__PURE__ */ new Map()
|
|
1297
|
-
);
|
|
1298
|
-
const stableEnterRoom = React3.useCallback(
|
|
1299
|
-
(roomId, options2) => {
|
|
1300
|
-
const cached = cache.get(roomId);
|
|
1301
|
-
if (cached)
|
|
1302
|
-
return cached;
|
|
1303
|
-
const rv = client.enterRoom(
|
|
1304
|
-
roomId,
|
|
1305
|
-
options2
|
|
1306
|
-
);
|
|
1307
|
-
const origLeave = rv.leave;
|
|
1308
|
-
rv.leave = () => {
|
|
1309
|
-
origLeave();
|
|
1310
|
-
cache.delete(roomId);
|
|
1311
|
-
};
|
|
1312
|
-
cache.set(roomId, rv);
|
|
1313
|
-
return rv;
|
|
1314
|
-
},
|
|
1315
|
-
[cache]
|
|
1316
|
-
);
|
|
1317
|
-
return /* @__PURE__ */ React3.createElement(RoomProviderInner, { ...props, stableEnterRoom });
|
|
1318
|
-
}
|
|
1319
|
-
function RoomProviderInner(props) {
|
|
1320
|
-
const { id: roomId, stableEnterRoom } = props;
|
|
1321
|
-
if (process.env.NODE_ENV !== "production") {
|
|
1322
|
-
if (!roomId) {
|
|
1323
|
-
throw new Error(
|
|
1324
|
-
"RoomProvider id property is required. For more information: https://liveblocks.io/docs/errors/liveblocks-react/RoomProvider-id-property-is-required"
|
|
1325
|
-
);
|
|
1326
|
-
}
|
|
1327
|
-
if (typeof roomId !== "string") {
|
|
1328
|
-
throw new Error("RoomProvider id property should be a string.");
|
|
1329
|
-
}
|
|
1330
|
-
const majorReactVersion = parseInt(React3.version) || 1;
|
|
1331
|
-
const oldReactVersion = majorReactVersion < 18;
|
|
1332
|
-
errorIf(
|
|
1333
|
-
oldReactVersion && props.unstable_batchedUpdates === void 0,
|
|
1334
|
-
missing_unstable_batchedUpdates(majorReactVersion, roomId)
|
|
1335
|
-
);
|
|
1336
|
-
deprecateIf(
|
|
1337
|
-
!oldReactVersion && props.unstable_batchedUpdates !== void 0,
|
|
1338
|
-
superfluous_unstable_batchedUpdates
|
|
1339
|
-
);
|
|
1340
|
-
}
|
|
1341
|
-
const frozenProps = useInitial({
|
|
1342
|
-
initialPresence: props.initialPresence,
|
|
1343
|
-
initialStorage: props.initialStorage,
|
|
1344
|
-
unstable_batchedUpdates: props.unstable_batchedUpdates,
|
|
1345
|
-
autoConnect: props.autoConnect ?? props.shouldInitiallyConnect ?? typeof window !== "undefined"
|
|
1346
|
-
});
|
|
1347
|
-
const [{ room }, setRoomLeavePair] = React3.useState(
|
|
1348
|
-
() => stableEnterRoom(roomId, {
|
|
1349
|
-
...frozenProps,
|
|
1350
|
-
autoConnect: false
|
|
1351
|
-
// Deliberately using false here on the first render, see below
|
|
1352
|
-
})
|
|
1353
|
-
);
|
|
1354
|
-
React3.useEffect(() => {
|
|
1355
|
-
const pair = stableEnterRoom(roomId, frozenProps);
|
|
1356
|
-
setRoomLeavePair(pair);
|
|
1357
|
-
const { room: room2, leave } = pair;
|
|
1358
|
-
if (frozenProps.autoConnect) {
|
|
1359
|
-
room2.connect();
|
|
1360
|
-
}
|
|
1361
|
-
return () => {
|
|
1362
|
-
leave();
|
|
1363
|
-
};
|
|
1364
|
-
}, [roomId, frozenProps, stableEnterRoom]);
|
|
1365
|
-
return /* @__PURE__ */ React3.createElement(RoomContext.Provider, { value: room }, /* @__PURE__ */ React3.createElement(CommentsRoomProvider, { room }, /* @__PURE__ */ React3.createElement(
|
|
1366
|
-
ContextBundle.Provider,
|
|
1367
|
-
{
|
|
1368
|
-
value: internalBundle
|
|
1369
|
-
},
|
|
1370
|
-
props.children
|
|
1371
|
-
)));
|
|
1372
|
-
}
|
|
1373
|
-
function connectionIdSelector(others) {
|
|
1374
|
-
return others.map((user) => user.connectionId);
|
|
1375
|
-
}
|
|
1376
|
-
function useRoom() {
|
|
1377
|
-
const room = React3.useContext(RoomContext);
|
|
1378
|
-
if (room === null) {
|
|
1379
|
-
throw new Error("RoomProvider is missing from the React tree.");
|
|
418
|
+
function useRoom() {
|
|
419
|
+
const room = React2.useContext(RoomContext);
|
|
420
|
+
if (room === null) {
|
|
421
|
+
throw new Error("RoomProvider is missing from the React tree.");
|
|
1380
422
|
}
|
|
1381
423
|
return room;
|
|
1382
424
|
}
|
|
1383
425
|
function useStatus() {
|
|
1384
426
|
const room = useRoom();
|
|
1385
|
-
const
|
|
1386
|
-
const
|
|
427
|
+
const subscribe = room.events.status.subscribe;
|
|
428
|
+
const getSnapshot = room.getStatus;
|
|
1387
429
|
const getServerSnapshot = room.getStatus;
|
|
1388
|
-
return
|
|
430
|
+
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
1389
431
|
}
|
|
1390
432
|
function useMyPresence() {
|
|
1391
433
|
const room = useRoom();
|
|
1392
|
-
const
|
|
1393
|
-
const
|
|
1394
|
-
const presence =
|
|
434
|
+
const subscribe = room.events.myPresence.subscribe;
|
|
435
|
+
const getSnapshot = room.getPresence;
|
|
436
|
+
const presence = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
1395
437
|
const setPresence = room.updatePresence;
|
|
1396
438
|
return [presence, setPresence];
|
|
1397
439
|
}
|
|
@@ -1400,12 +442,12 @@ function createRoomContext(client, options) {
|
|
|
1400
442
|
}
|
|
1401
443
|
function useOthers(selector, isEqual) {
|
|
1402
444
|
const room = useRoom();
|
|
1403
|
-
const
|
|
1404
|
-
const
|
|
445
|
+
const subscribe = room.events.others.subscribe;
|
|
446
|
+
const getSnapshot = room.getOthers;
|
|
1405
447
|
const getServerSnapshot = alwaysEmptyList;
|
|
1406
|
-
return
|
|
1407
|
-
|
|
1408
|
-
|
|
448
|
+
return useSyncExternalStoreWithSelector(
|
|
449
|
+
subscribe,
|
|
450
|
+
getSnapshot,
|
|
1409
451
|
getServerSnapshot,
|
|
1410
452
|
selector ?? identity,
|
|
1411
453
|
isEqual
|
|
@@ -1415,13 +457,13 @@ function createRoomContext(client, options) {
|
|
|
1415
457
|
return useOthers(connectionIdSelector, shallow);
|
|
1416
458
|
}
|
|
1417
459
|
function useOthersMapped(itemSelector, itemIsEqual) {
|
|
1418
|
-
const wrappedSelector =
|
|
460
|
+
const wrappedSelector = React2.useCallback(
|
|
1419
461
|
(others) => others.map(
|
|
1420
462
|
(other) => [other.connectionId, itemSelector(other)]
|
|
1421
463
|
),
|
|
1422
464
|
[itemSelector]
|
|
1423
465
|
);
|
|
1424
|
-
const wrappedIsEqual =
|
|
466
|
+
const wrappedIsEqual = React2.useCallback(
|
|
1425
467
|
(a, b) => {
|
|
1426
468
|
const eq = itemIsEqual ?? Object.is;
|
|
1427
469
|
return a.length === b.length && a.every((atuple, index) => {
|
|
@@ -1435,7 +477,7 @@ function createRoomContext(client, options) {
|
|
|
1435
477
|
}
|
|
1436
478
|
const NOT_FOUND = Symbol();
|
|
1437
479
|
function useOther(connectionId, selector, isEqual) {
|
|
1438
|
-
const wrappedSelector =
|
|
480
|
+
const wrappedSelector = React2.useCallback(
|
|
1439
481
|
(others) => {
|
|
1440
482
|
const other2 = others.find(
|
|
1441
483
|
(other3) => other3.connectionId === connectionId
|
|
@@ -1444,7 +486,7 @@ function createRoomContext(client, options) {
|
|
|
1444
486
|
},
|
|
1445
487
|
[connectionId, selector]
|
|
1446
488
|
);
|
|
1447
|
-
const wrappedIsEqual =
|
|
489
|
+
const wrappedIsEqual = React2.useCallback(
|
|
1448
490
|
(prev, curr) => {
|
|
1449
491
|
if (prev === NOT_FOUND || curr === NOT_FOUND) {
|
|
1450
492
|
return prev === curr;
|
|
@@ -1464,9 +506,9 @@ function createRoomContext(client, options) {
|
|
|
1464
506
|
}
|
|
1465
507
|
function useBroadcastEvent() {
|
|
1466
508
|
const room = useRoom();
|
|
1467
|
-
return
|
|
1468
|
-
(event,
|
|
1469
|
-
room.broadcastEvent(event,
|
|
509
|
+
return React2.useCallback(
|
|
510
|
+
(event, options = { shouldQueueEventIfNotReady: false }) => {
|
|
511
|
+
room.broadcastEvent(event, options);
|
|
1470
512
|
},
|
|
1471
513
|
[room]
|
|
1472
514
|
);
|
|
@@ -1474,7 +516,7 @@ function createRoomContext(client, options) {
|
|
|
1474
516
|
function useOthersListener(callback) {
|
|
1475
517
|
const room = useRoom();
|
|
1476
518
|
const savedCallback = useLatest(callback);
|
|
1477
|
-
|
|
519
|
+
React2.useEffect(
|
|
1478
520
|
() => room.events.others.subscribe((event) => savedCallback.current(event)),
|
|
1479
521
|
[room, savedCallback]
|
|
1480
522
|
);
|
|
@@ -1482,7 +524,7 @@ function createRoomContext(client, options) {
|
|
|
1482
524
|
function useLostConnectionListener(callback) {
|
|
1483
525
|
const room = useRoom();
|
|
1484
526
|
const savedCallback = useLatest(callback);
|
|
1485
|
-
|
|
527
|
+
React2.useEffect(
|
|
1486
528
|
() => room.events.lostConnection.subscribe(
|
|
1487
529
|
(event) => savedCallback.current(event)
|
|
1488
530
|
),
|
|
@@ -1492,7 +534,7 @@ function createRoomContext(client, options) {
|
|
|
1492
534
|
function useErrorListener(callback) {
|
|
1493
535
|
const room = useRoom();
|
|
1494
536
|
const savedCallback = useLatest(callback);
|
|
1495
|
-
|
|
537
|
+
React2.useEffect(
|
|
1496
538
|
() => room.events.error.subscribe((e) => savedCallback.current(e)),
|
|
1497
539
|
[room, savedCallback]
|
|
1498
540
|
);
|
|
@@ -1500,7 +542,7 @@ function createRoomContext(client, options) {
|
|
|
1500
542
|
function useEventListener(callback) {
|
|
1501
543
|
const room = useRoom();
|
|
1502
544
|
const savedCallback = useLatest(callback);
|
|
1503
|
-
|
|
545
|
+
React2.useEffect(() => {
|
|
1504
546
|
const listener = (eventData) => {
|
|
1505
547
|
savedCallback.current(eventData);
|
|
1506
548
|
};
|
|
@@ -1509,17 +551,17 @@ function createRoomContext(client, options) {
|
|
|
1509
551
|
}
|
|
1510
552
|
function useSelf(maybeSelector, isEqual) {
|
|
1511
553
|
const room = useRoom();
|
|
1512
|
-
const
|
|
1513
|
-
const
|
|
554
|
+
const subscribe = room.events.self.subscribe;
|
|
555
|
+
const getSnapshot = room.getSelf;
|
|
1514
556
|
const selector = maybeSelector ?? identity;
|
|
1515
|
-
const wrappedSelector =
|
|
557
|
+
const wrappedSelector = React2.useCallback(
|
|
1516
558
|
(me) => me !== null ? selector(me) : null,
|
|
1517
559
|
[selector]
|
|
1518
560
|
);
|
|
1519
561
|
const getServerSnapshot = alwaysNull;
|
|
1520
|
-
return
|
|
1521
|
-
|
|
1522
|
-
|
|
562
|
+
return useSyncExternalStoreWithSelector(
|
|
563
|
+
subscribe,
|
|
564
|
+
getSnapshot,
|
|
1523
565
|
getServerSnapshot,
|
|
1524
566
|
wrappedSelector,
|
|
1525
567
|
isEqual
|
|
@@ -1527,10 +569,10 @@ function createRoomContext(client, options) {
|
|
|
1527
569
|
}
|
|
1528
570
|
function useMutableStorageRoot() {
|
|
1529
571
|
const room = useRoom();
|
|
1530
|
-
const
|
|
1531
|
-
const
|
|
572
|
+
const subscribe = room.events.storageDidLoad.subscribeOnce;
|
|
573
|
+
const getSnapshot = room.getStorageSnapshot;
|
|
1532
574
|
const getServerSnapshot = alwaysNull;
|
|
1533
|
-
return
|
|
575
|
+
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
1534
576
|
}
|
|
1535
577
|
function useStorageRoot() {
|
|
1536
578
|
return [useMutableStorageRoot()];
|
|
@@ -1546,15 +588,15 @@ function createRoomContext(client, options) {
|
|
|
1546
588
|
}
|
|
1547
589
|
function useCanUndo() {
|
|
1548
590
|
const room = useRoom();
|
|
1549
|
-
const
|
|
591
|
+
const subscribe = room.events.history.subscribe;
|
|
1550
592
|
const canUndo = room.history.canUndo;
|
|
1551
|
-
return
|
|
593
|
+
return useSyncExternalStore(subscribe, canUndo, canUndo);
|
|
1552
594
|
}
|
|
1553
595
|
function useCanRedo() {
|
|
1554
596
|
const room = useRoom();
|
|
1555
|
-
const
|
|
597
|
+
const subscribe = room.events.history.subscribe;
|
|
1556
598
|
const canRedo = room.history.canRedo;
|
|
1557
|
-
return
|
|
599
|
+
return useSyncExternalStore(subscribe, canRedo, canRedo);
|
|
1558
600
|
}
|
|
1559
601
|
function useBatch() {
|
|
1560
602
|
return useRoom().batch;
|
|
@@ -1563,7 +605,7 @@ function createRoomContext(client, options) {
|
|
|
1563
605
|
const room = useRoom();
|
|
1564
606
|
const rootOrNull = useMutableStorageRoot();
|
|
1565
607
|
const rerender = useRerender();
|
|
1566
|
-
|
|
608
|
+
React2.useEffect(() => {
|
|
1567
609
|
if (rootOrNull === null) {
|
|
1568
610
|
return;
|
|
1569
611
|
}
|
|
@@ -1599,15 +641,15 @@ function createRoomContext(client, options) {
|
|
|
1599
641
|
function useStorage(selector, isEqual) {
|
|
1600
642
|
const room = useRoom();
|
|
1601
643
|
const rootOrNull = useMutableStorageRoot();
|
|
1602
|
-
const wrappedSelector =
|
|
644
|
+
const wrappedSelector = React2.useCallback(
|
|
1603
645
|
(rootOrNull2) => rootOrNull2 !== null ? selector(rootOrNull2) : null,
|
|
1604
646
|
[selector]
|
|
1605
647
|
);
|
|
1606
|
-
const
|
|
1607
|
-
(onStoreChange) => rootOrNull !== null ? room.subscribe(rootOrNull, onStoreChange, { isDeep: true }) :
|
|
648
|
+
const subscribe = React2.useCallback(
|
|
649
|
+
(onStoreChange) => rootOrNull !== null ? room.subscribe(rootOrNull, onStoreChange, { isDeep: true }) : noop,
|
|
1608
650
|
[room, rootOrNull]
|
|
1609
651
|
);
|
|
1610
|
-
const
|
|
652
|
+
const getSnapshot = React2.useCallback(() => {
|
|
1611
653
|
if (rootOrNull === null) {
|
|
1612
654
|
return null;
|
|
1613
655
|
} else {
|
|
@@ -1617,9 +659,9 @@ function createRoomContext(client, options) {
|
|
|
1617
659
|
}
|
|
1618
660
|
}, [rootOrNull]);
|
|
1619
661
|
const getServerSnapshot = alwaysNull;
|
|
1620
|
-
return
|
|
1621
|
-
|
|
1622
|
-
|
|
662
|
+
return useSyncExternalStoreWithSelector(
|
|
663
|
+
subscribe,
|
|
664
|
+
getSnapshot,
|
|
1623
665
|
getServerSnapshot,
|
|
1624
666
|
wrappedSelector,
|
|
1625
667
|
isEqual
|
|
@@ -1655,7 +697,7 @@ function createRoomContext(client, options) {
|
|
|
1655
697
|
}
|
|
1656
698
|
function useMutation(callback, deps) {
|
|
1657
699
|
const room = useRoom();
|
|
1658
|
-
return
|
|
700
|
+
return React2.useMemo(
|
|
1659
701
|
() => {
|
|
1660
702
|
return (...args) => (
|
|
1661
703
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
@@ -1711,102 +753,831 @@ function createRoomContext(client, options) {
|
|
|
1711
753
|
useSuspendUntilStorageLoaded();
|
|
1712
754
|
return useLegacyKey(key);
|
|
1713
755
|
}
|
|
1714
|
-
|
|
756
|
+
const store = client[kInternal].cacheStore;
|
|
757
|
+
function onMutationFailure(innerError, optimisticUpdateId, createPublicError) {
|
|
758
|
+
store.set((state) => ({
|
|
759
|
+
...state,
|
|
760
|
+
optimisticUpdates: state.optimisticUpdates.filter(
|
|
761
|
+
(update) => update.id !== optimisticUpdateId
|
|
762
|
+
)
|
|
763
|
+
}));
|
|
764
|
+
if (innerError instanceof CommentsApiError) {
|
|
765
|
+
const error = handleApiError(innerError);
|
|
766
|
+
commentsErrorEventSource.notify(createPublicError(error));
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
if (innerError instanceof NotificationsApiError) {
|
|
770
|
+
handleApiError(innerError);
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
throw innerError;
|
|
774
|
+
}
|
|
775
|
+
const requestsCache = /* @__PURE__ */ new Map();
|
|
776
|
+
const poller = makePoller(refreshThreadsAndNotifications);
|
|
777
|
+
async function refreshThreadsAndNotifications() {
|
|
778
|
+
await Promise.allSettled(
|
|
779
|
+
Array.from(requestsCache.entries()).filter(([_, requestCache]) => requestCache.subscribers > 0).map(async ([_, requestCache]) => {
|
|
780
|
+
return requestCache.requestFactory().then(
|
|
781
|
+
(result) => requestCache.onSuccess(result),
|
|
782
|
+
() => {
|
|
783
|
+
}
|
|
784
|
+
);
|
|
785
|
+
})
|
|
786
|
+
);
|
|
787
|
+
}
|
|
788
|
+
function incrementQuerySubscribers(queryKey) {
|
|
789
|
+
const requestCache = requestsCache.get(queryKey);
|
|
790
|
+
if (requestCache === void 0) {
|
|
791
|
+
console2.warn(
|
|
792
|
+
`Internal unexpected behavior. Cannot increase subscriber count for query "${queryKey}"`
|
|
793
|
+
);
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
requestCache.subscribers++;
|
|
797
|
+
poller.start(POLLING_INTERVAL);
|
|
798
|
+
}
|
|
799
|
+
function decrementQuerySubscribers(queryKey) {
|
|
800
|
+
const requestCache = requestsCache.get(queryKey);
|
|
801
|
+
if (requestCache === void 0 || requestCache.subscribers <= 0) {
|
|
802
|
+
console2.warn(
|
|
803
|
+
`Internal unexpected behavior. Cannot decrease subscriber count for query "${queryKey}"`
|
|
804
|
+
);
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
requestCache.subscribers--;
|
|
808
|
+
let totalSubscribers = 0;
|
|
809
|
+
for (const requestCache2 of requestsCache.values()) {
|
|
810
|
+
totalSubscribers += requestCache2.subscribers;
|
|
811
|
+
}
|
|
812
|
+
if (totalSubscribers <= 0) {
|
|
813
|
+
poller.stop();
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
async function getOrInitRequest(queryKey, requestFactory, onSuccess) {
|
|
817
|
+
const requestInfo = requestsCache.get(queryKey);
|
|
818
|
+
if (requestInfo !== void 0) {
|
|
819
|
+
return requestInfo.promise;
|
|
820
|
+
}
|
|
821
|
+
const promise = requestFactory();
|
|
822
|
+
requestsCache.set(queryKey, {
|
|
823
|
+
promise,
|
|
824
|
+
requestFactory,
|
|
825
|
+
onSuccess,
|
|
826
|
+
subscribers: 0
|
|
827
|
+
});
|
|
828
|
+
store.setQueryState(queryKey, {
|
|
829
|
+
isLoading: true
|
|
830
|
+
});
|
|
831
|
+
try {
|
|
832
|
+
const result = await promise;
|
|
833
|
+
onSuccess(result);
|
|
834
|
+
} catch (er) {
|
|
835
|
+
store.setQueryState(queryKey, {
|
|
836
|
+
isLoading: false,
|
|
837
|
+
error: er
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
poller.start(POLLING_INTERVAL);
|
|
841
|
+
}
|
|
842
|
+
async function getThreadsAndInboxNotifications(room, queryKey, options) {
|
|
843
|
+
const roomId = room.id;
|
|
844
|
+
return getOrInitRequest(
|
|
845
|
+
queryKey,
|
|
846
|
+
async () => {
|
|
847
|
+
const room2 = client.getRoom(roomId);
|
|
848
|
+
if (room2 === null) {
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
return room2.getThreads(options);
|
|
852
|
+
},
|
|
853
|
+
(result) => {
|
|
854
|
+
if (result !== void 0) {
|
|
855
|
+
store.updateThreadsAndNotifications(
|
|
856
|
+
result.threads,
|
|
857
|
+
result.inboxNotifications,
|
|
858
|
+
queryKey
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
function useThreads(options = { query: { metadata: {} } }) {
|
|
1715
865
|
const room = useRoom();
|
|
1716
|
-
|
|
866
|
+
const queryKey = React2.useMemo(
|
|
867
|
+
() => generateQueryKey(room.id, options.query),
|
|
868
|
+
[room, options]
|
|
869
|
+
);
|
|
870
|
+
React2.useEffect(() => {
|
|
871
|
+
void getThreadsAndInboxNotifications(room, queryKey, options);
|
|
872
|
+
incrementQuerySubscribers(queryKey);
|
|
873
|
+
return () => decrementQuerySubscribers(queryKey);
|
|
874
|
+
}, [room, queryKey]);
|
|
875
|
+
return useSyncExternalStoreWithSelector(
|
|
876
|
+
store.subscribe,
|
|
877
|
+
store.get,
|
|
878
|
+
store.get,
|
|
879
|
+
(state) => {
|
|
880
|
+
if (state.queries[queryKey] === void 0 || state.queries[queryKey].isLoading) {
|
|
881
|
+
return {
|
|
882
|
+
isLoading: true
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
return {
|
|
886
|
+
threads: selectedThreads(room.id, state, options),
|
|
887
|
+
isLoading: false,
|
|
888
|
+
error: state.queries[queryKey].error
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
);
|
|
1717
892
|
}
|
|
1718
|
-
function useThreadsSuspense(
|
|
893
|
+
function useThreadsSuspense(options = { query: { metadata: {} } }) {
|
|
1719
894
|
const room = useRoom();
|
|
1720
|
-
|
|
895
|
+
const queryKey = React2.useMemo(
|
|
896
|
+
() => generateQueryKey(room.id, options?.query),
|
|
897
|
+
[room, options]
|
|
898
|
+
);
|
|
899
|
+
const query = store.get().queries[queryKey];
|
|
900
|
+
if (query === void 0 || query.isLoading) {
|
|
901
|
+
throw getThreadsAndInboxNotifications(room, queryKey, options);
|
|
902
|
+
}
|
|
903
|
+
if (query.error) {
|
|
904
|
+
throw query.error;
|
|
905
|
+
}
|
|
906
|
+
React2.useEffect(() => {
|
|
907
|
+
incrementQuerySubscribers(queryKey);
|
|
908
|
+
return () => {
|
|
909
|
+
decrementQuerySubscribers(queryKey);
|
|
910
|
+
};
|
|
911
|
+
}, [room, queryKey]);
|
|
912
|
+
return useSyncExternalStoreWithSelector(
|
|
913
|
+
store.subscribe,
|
|
914
|
+
store.get,
|
|
915
|
+
store.get,
|
|
916
|
+
(state) => {
|
|
917
|
+
return {
|
|
918
|
+
threads: selectedThreads(room.id, state, options),
|
|
919
|
+
isLoading: false
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
);
|
|
1721
923
|
}
|
|
1722
924
|
function useCreateThread() {
|
|
1723
925
|
const room = useRoom();
|
|
1724
|
-
return
|
|
926
|
+
return React2.useCallback(
|
|
927
|
+
(options) => {
|
|
928
|
+
const body = options.body;
|
|
929
|
+
const metadata = "metadata" in options ? options.metadata : {};
|
|
930
|
+
const threadId = createThreadId();
|
|
931
|
+
const commentId = createCommentId();
|
|
932
|
+
const now = /* @__PURE__ */ new Date();
|
|
933
|
+
const newComment = {
|
|
934
|
+
id: commentId,
|
|
935
|
+
threadId,
|
|
936
|
+
roomId: room.id,
|
|
937
|
+
createdAt: now,
|
|
938
|
+
type: "comment",
|
|
939
|
+
userId: getCurrentUserId(room),
|
|
940
|
+
body,
|
|
941
|
+
reactions: []
|
|
942
|
+
};
|
|
943
|
+
const newThread = {
|
|
944
|
+
id: threadId,
|
|
945
|
+
type: "thread",
|
|
946
|
+
createdAt: now,
|
|
947
|
+
roomId: room.id,
|
|
948
|
+
metadata,
|
|
949
|
+
comments: [newComment]
|
|
950
|
+
};
|
|
951
|
+
const optimisticUpdateId = nanoid2();
|
|
952
|
+
store.pushOptimisticUpdate({
|
|
953
|
+
type: "create-thread",
|
|
954
|
+
thread: newThread,
|
|
955
|
+
id: optimisticUpdateId
|
|
956
|
+
});
|
|
957
|
+
room.createThread({ threadId, commentId, body, metadata }).then(
|
|
958
|
+
(thread) => {
|
|
959
|
+
store.set((state) => ({
|
|
960
|
+
...state,
|
|
961
|
+
threads: {
|
|
962
|
+
...state.threads,
|
|
963
|
+
[threadId]: thread
|
|
964
|
+
},
|
|
965
|
+
optimisticUpdates: state.optimisticUpdates.filter(
|
|
966
|
+
(update) => update.id !== optimisticUpdateId
|
|
967
|
+
)
|
|
968
|
+
}));
|
|
969
|
+
},
|
|
970
|
+
(err) => onMutationFailure(
|
|
971
|
+
err,
|
|
972
|
+
optimisticUpdateId,
|
|
973
|
+
(err2) => new CreateThreadError(err2, {
|
|
974
|
+
roomId: room.id,
|
|
975
|
+
threadId,
|
|
976
|
+
commentId,
|
|
977
|
+
body,
|
|
978
|
+
metadata
|
|
979
|
+
})
|
|
980
|
+
)
|
|
981
|
+
);
|
|
982
|
+
return newThread;
|
|
983
|
+
},
|
|
984
|
+
[room]
|
|
985
|
+
);
|
|
1725
986
|
}
|
|
1726
987
|
function useEditThreadMetadata() {
|
|
1727
988
|
const room = useRoom();
|
|
1728
|
-
return
|
|
989
|
+
return React2.useCallback(
|
|
990
|
+
(options) => {
|
|
991
|
+
if (!("metadata" in options)) {
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
const threadId = options.threadId;
|
|
995
|
+
const metadata = options.metadata;
|
|
996
|
+
const optimisticUpdateId = nanoid2();
|
|
997
|
+
store.pushOptimisticUpdate({
|
|
998
|
+
type: "edit-thread-metadata",
|
|
999
|
+
metadata,
|
|
1000
|
+
id: optimisticUpdateId,
|
|
1001
|
+
threadId
|
|
1002
|
+
});
|
|
1003
|
+
room.editThreadMetadata({ metadata, threadId }).then(
|
|
1004
|
+
(metadata2) => {
|
|
1005
|
+
store.set((state) => ({
|
|
1006
|
+
...state,
|
|
1007
|
+
threads: {
|
|
1008
|
+
...state.threads,
|
|
1009
|
+
[threadId]: {
|
|
1010
|
+
...state.threads[threadId],
|
|
1011
|
+
metadata: {
|
|
1012
|
+
...state.threads[threadId].metadata,
|
|
1013
|
+
...metadata2
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
},
|
|
1017
|
+
optimisticUpdates: state.optimisticUpdates.filter(
|
|
1018
|
+
(update) => update.id !== optimisticUpdateId
|
|
1019
|
+
)
|
|
1020
|
+
}));
|
|
1021
|
+
},
|
|
1022
|
+
(err) => onMutationFailure(
|
|
1023
|
+
err,
|
|
1024
|
+
optimisticUpdateId,
|
|
1025
|
+
(error) => new EditThreadMetadataError(error, {
|
|
1026
|
+
roomId: room.id,
|
|
1027
|
+
threadId,
|
|
1028
|
+
metadata
|
|
1029
|
+
})
|
|
1030
|
+
)
|
|
1031
|
+
);
|
|
1032
|
+
},
|
|
1033
|
+
[room]
|
|
1034
|
+
);
|
|
1729
1035
|
}
|
|
1730
1036
|
function useAddReaction() {
|
|
1731
1037
|
const room = useRoom();
|
|
1732
|
-
return
|
|
1038
|
+
return React2.useCallback(
|
|
1039
|
+
({ threadId, commentId, emoji }) => {
|
|
1040
|
+
const now = /* @__PURE__ */ new Date();
|
|
1041
|
+
const userId = getCurrentUserId(room);
|
|
1042
|
+
const optimisticUpdateId = nanoid2();
|
|
1043
|
+
store.pushOptimisticUpdate({
|
|
1044
|
+
type: "add-reaction",
|
|
1045
|
+
threadId,
|
|
1046
|
+
commentId,
|
|
1047
|
+
emoji,
|
|
1048
|
+
userId,
|
|
1049
|
+
createdAt: now,
|
|
1050
|
+
id: optimisticUpdateId
|
|
1051
|
+
});
|
|
1052
|
+
room.addReaction({ threadId, commentId, emoji }).then(
|
|
1053
|
+
(addedReaction) => {
|
|
1054
|
+
store.set((state) => ({
|
|
1055
|
+
...state,
|
|
1056
|
+
threads: {
|
|
1057
|
+
...state.threads,
|
|
1058
|
+
[threadId]: {
|
|
1059
|
+
...state.threads[threadId],
|
|
1060
|
+
comments: state.threads[threadId].comments.map(
|
|
1061
|
+
(comment) => comment.id === commentId ? {
|
|
1062
|
+
...comment,
|
|
1063
|
+
reactions: comment.reactions.some(
|
|
1064
|
+
(reaction) => reaction.emoji === addedReaction.emoji
|
|
1065
|
+
) ? comment.reactions.map(
|
|
1066
|
+
(reaction) => reaction.emoji === addedReaction.emoji ? {
|
|
1067
|
+
...reaction,
|
|
1068
|
+
users: [
|
|
1069
|
+
...reaction.users,
|
|
1070
|
+
{ id: addedReaction.userId }
|
|
1071
|
+
]
|
|
1072
|
+
} : reaction
|
|
1073
|
+
) : [
|
|
1074
|
+
...comment.reactions,
|
|
1075
|
+
{
|
|
1076
|
+
emoji: addedReaction.emoji,
|
|
1077
|
+
createdAt: addedReaction.createdAt,
|
|
1078
|
+
users: [{ id: addedReaction.userId }]
|
|
1079
|
+
}
|
|
1080
|
+
]
|
|
1081
|
+
} : comment
|
|
1082
|
+
)
|
|
1083
|
+
}
|
|
1084
|
+
},
|
|
1085
|
+
optimisticUpdates: state.optimisticUpdates.filter(
|
|
1086
|
+
(update) => update.id !== optimisticUpdateId
|
|
1087
|
+
)
|
|
1088
|
+
}));
|
|
1089
|
+
},
|
|
1090
|
+
(err) => onMutationFailure(
|
|
1091
|
+
err,
|
|
1092
|
+
optimisticUpdateId,
|
|
1093
|
+
(error) => new AddReactionError(error, {
|
|
1094
|
+
roomId: room.id,
|
|
1095
|
+
threadId,
|
|
1096
|
+
commentId,
|
|
1097
|
+
emoji
|
|
1098
|
+
})
|
|
1099
|
+
)
|
|
1100
|
+
);
|
|
1101
|
+
},
|
|
1102
|
+
[room]
|
|
1103
|
+
);
|
|
1104
|
+
}
|
|
1105
|
+
function useRemoveReaction() {
|
|
1106
|
+
const room = useRoom();
|
|
1107
|
+
return React2.useCallback(
|
|
1108
|
+
({ threadId, commentId, emoji }) => {
|
|
1109
|
+
const userId = getCurrentUserId(room);
|
|
1110
|
+
const optimisticUpdateId = nanoid2();
|
|
1111
|
+
store.pushOptimisticUpdate({
|
|
1112
|
+
type: "remove-reaction",
|
|
1113
|
+
threadId,
|
|
1114
|
+
commentId,
|
|
1115
|
+
emoji,
|
|
1116
|
+
userId,
|
|
1117
|
+
id: optimisticUpdateId
|
|
1118
|
+
});
|
|
1119
|
+
room.removeReaction({ threadId, commentId, emoji }).then(
|
|
1120
|
+
() => {
|
|
1121
|
+
store.set((state) => ({
|
|
1122
|
+
...state,
|
|
1123
|
+
threads: {
|
|
1124
|
+
...state.threads,
|
|
1125
|
+
[threadId]: {
|
|
1126
|
+
...state.threads[threadId],
|
|
1127
|
+
comments: state.threads[threadId].comments.map((comment) => {
|
|
1128
|
+
if (comment.id !== commentId) {
|
|
1129
|
+
return comment;
|
|
1130
|
+
}
|
|
1131
|
+
const reactionIndex = comment.reactions.findIndex(
|
|
1132
|
+
(reaction) => reaction.emoji === emoji
|
|
1133
|
+
);
|
|
1134
|
+
let reactions = comment.reactions;
|
|
1135
|
+
if (reactionIndex >= 0 && comment.reactions[reactionIndex].users.some(
|
|
1136
|
+
(user) => user.id === userId
|
|
1137
|
+
)) {
|
|
1138
|
+
if (comment.reactions[reactionIndex].users.length <= 1) {
|
|
1139
|
+
reactions = [...comment.reactions];
|
|
1140
|
+
reactions.splice(reactionIndex, 1);
|
|
1141
|
+
} else {
|
|
1142
|
+
reactions[reactionIndex] = {
|
|
1143
|
+
...reactions[reactionIndex],
|
|
1144
|
+
users: reactions[reactionIndex].users.filter(
|
|
1145
|
+
(user) => user.id !== userId
|
|
1146
|
+
)
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
return {
|
|
1151
|
+
...comment,
|
|
1152
|
+
reactions
|
|
1153
|
+
};
|
|
1154
|
+
})
|
|
1155
|
+
}
|
|
1156
|
+
},
|
|
1157
|
+
optimisticUpdates: state.optimisticUpdates.filter(
|
|
1158
|
+
(update) => update.id !== optimisticUpdateId
|
|
1159
|
+
)
|
|
1160
|
+
}));
|
|
1161
|
+
},
|
|
1162
|
+
(err) => onMutationFailure(
|
|
1163
|
+
err,
|
|
1164
|
+
optimisticUpdateId,
|
|
1165
|
+
(error) => new RemoveReactionError(error, {
|
|
1166
|
+
roomId: room.id,
|
|
1167
|
+
threadId,
|
|
1168
|
+
commentId,
|
|
1169
|
+
emoji
|
|
1170
|
+
})
|
|
1171
|
+
)
|
|
1172
|
+
);
|
|
1173
|
+
},
|
|
1174
|
+
[room]
|
|
1175
|
+
);
|
|
1176
|
+
}
|
|
1177
|
+
function useCreateComment() {
|
|
1178
|
+
const room = useRoom();
|
|
1179
|
+
return React2.useCallback(
|
|
1180
|
+
({ threadId, body }) => {
|
|
1181
|
+
const commentId = createCommentId();
|
|
1182
|
+
const now = /* @__PURE__ */ new Date();
|
|
1183
|
+
const comment = {
|
|
1184
|
+
id: commentId,
|
|
1185
|
+
threadId,
|
|
1186
|
+
roomId: room.id,
|
|
1187
|
+
type: "comment",
|
|
1188
|
+
createdAt: now,
|
|
1189
|
+
userId: getCurrentUserId(room),
|
|
1190
|
+
body,
|
|
1191
|
+
reactions: []
|
|
1192
|
+
};
|
|
1193
|
+
const optimisticUpdateId = nanoid2();
|
|
1194
|
+
const inboxNotification = Object.values(
|
|
1195
|
+
store.get().inboxNotifications
|
|
1196
|
+
).find((inboxNotification2) => inboxNotification2.threadId === threadId);
|
|
1197
|
+
store.pushOptimisticUpdate({
|
|
1198
|
+
type: "create-comment",
|
|
1199
|
+
comment,
|
|
1200
|
+
id: optimisticUpdateId,
|
|
1201
|
+
inboxNotificationId: inboxNotification?.id
|
|
1202
|
+
});
|
|
1203
|
+
room.createComment({ threadId, commentId, body }).then(
|
|
1204
|
+
(newComment) => {
|
|
1205
|
+
store.set((state) => ({
|
|
1206
|
+
...state,
|
|
1207
|
+
threads: upsertComment(state.threads, newComment),
|
|
1208
|
+
inboxNotifications: inboxNotification ? {
|
|
1209
|
+
...state.inboxNotifications,
|
|
1210
|
+
[inboxNotification.id]: {
|
|
1211
|
+
...inboxNotification,
|
|
1212
|
+
notifiedAt: newComment.createdAt,
|
|
1213
|
+
readAt: newComment.createdAt
|
|
1214
|
+
}
|
|
1215
|
+
} : state.inboxNotifications,
|
|
1216
|
+
optimisticUpdates: state.optimisticUpdates.filter(
|
|
1217
|
+
(update) => update.id !== optimisticUpdateId
|
|
1218
|
+
)
|
|
1219
|
+
}));
|
|
1220
|
+
},
|
|
1221
|
+
(err) => onMutationFailure(
|
|
1222
|
+
err,
|
|
1223
|
+
optimisticUpdateId,
|
|
1224
|
+
(err2) => new CreateCommentError(err2, {
|
|
1225
|
+
roomId: room.id,
|
|
1226
|
+
threadId,
|
|
1227
|
+
commentId,
|
|
1228
|
+
body
|
|
1229
|
+
})
|
|
1230
|
+
)
|
|
1231
|
+
);
|
|
1232
|
+
return comment;
|
|
1233
|
+
},
|
|
1234
|
+
[room]
|
|
1235
|
+
);
|
|
1236
|
+
}
|
|
1237
|
+
function useEditComment() {
|
|
1238
|
+
const room = useRoom();
|
|
1239
|
+
return React2.useCallback(
|
|
1240
|
+
({ threadId, commentId, body }) => {
|
|
1241
|
+
const now = /* @__PURE__ */ new Date();
|
|
1242
|
+
const optimisticUpdateId = nanoid2();
|
|
1243
|
+
store.pushOptimisticUpdate({
|
|
1244
|
+
type: "edit-comment",
|
|
1245
|
+
threadId,
|
|
1246
|
+
commentId,
|
|
1247
|
+
body,
|
|
1248
|
+
editedAt: now,
|
|
1249
|
+
id: optimisticUpdateId
|
|
1250
|
+
});
|
|
1251
|
+
room.editComment({ threadId, commentId, body }).then(
|
|
1252
|
+
(editedComment) => {
|
|
1253
|
+
store.set((state) => ({
|
|
1254
|
+
...state,
|
|
1255
|
+
threads: upsertComment(state.threads, editedComment),
|
|
1256
|
+
optimisticUpdates: state.optimisticUpdates.filter(
|
|
1257
|
+
(update) => update.id !== optimisticUpdateId
|
|
1258
|
+
)
|
|
1259
|
+
}));
|
|
1260
|
+
},
|
|
1261
|
+
(err) => onMutationFailure(
|
|
1262
|
+
err,
|
|
1263
|
+
optimisticUpdateId,
|
|
1264
|
+
(error) => new EditCommentError(error, {
|
|
1265
|
+
roomId: room.id,
|
|
1266
|
+
threadId,
|
|
1267
|
+
commentId,
|
|
1268
|
+
body
|
|
1269
|
+
})
|
|
1270
|
+
)
|
|
1271
|
+
);
|
|
1272
|
+
},
|
|
1273
|
+
[room]
|
|
1274
|
+
);
|
|
1275
|
+
}
|
|
1276
|
+
function useDeleteComment() {
|
|
1277
|
+
const room = useRoom();
|
|
1278
|
+
return React2.useCallback(
|
|
1279
|
+
({ threadId, commentId }) => {
|
|
1280
|
+
const now = /* @__PURE__ */ new Date();
|
|
1281
|
+
const optimisticUpdateId = nanoid2();
|
|
1282
|
+
store.pushOptimisticUpdate({
|
|
1283
|
+
type: "delete-comment",
|
|
1284
|
+
threadId,
|
|
1285
|
+
commentId,
|
|
1286
|
+
deletedAt: now,
|
|
1287
|
+
id: optimisticUpdateId
|
|
1288
|
+
});
|
|
1289
|
+
room.deleteComment({ threadId, commentId }).then(
|
|
1290
|
+
() => {
|
|
1291
|
+
const newThreads = { ...store.get().threads };
|
|
1292
|
+
const thread = newThreads[threadId];
|
|
1293
|
+
if (thread === void 0)
|
|
1294
|
+
return;
|
|
1295
|
+
newThreads[thread.id] = {
|
|
1296
|
+
...thread,
|
|
1297
|
+
comments: thread.comments.map(
|
|
1298
|
+
(comment) => comment.id === commentId ? {
|
|
1299
|
+
...comment,
|
|
1300
|
+
deletedAt: now,
|
|
1301
|
+
body: void 0
|
|
1302
|
+
} : comment
|
|
1303
|
+
)
|
|
1304
|
+
};
|
|
1305
|
+
if (!newThreads[thread.id].comments.some(
|
|
1306
|
+
(comment) => comment.deletedAt === void 0
|
|
1307
|
+
)) {
|
|
1308
|
+
delete newThreads[thread.id];
|
|
1309
|
+
}
|
|
1310
|
+
store.set((state) => ({
|
|
1311
|
+
...state,
|
|
1312
|
+
threads: newThreads,
|
|
1313
|
+
optimisticUpdates: state.optimisticUpdates.filter(
|
|
1314
|
+
(update) => update.id !== optimisticUpdateId
|
|
1315
|
+
)
|
|
1316
|
+
}));
|
|
1317
|
+
},
|
|
1318
|
+
(err) => onMutationFailure(
|
|
1319
|
+
err,
|
|
1320
|
+
optimisticUpdateId,
|
|
1321
|
+
(error) => new DeleteCommentError(error, {
|
|
1322
|
+
roomId: room.id,
|
|
1323
|
+
threadId,
|
|
1324
|
+
commentId
|
|
1325
|
+
})
|
|
1326
|
+
)
|
|
1327
|
+
);
|
|
1328
|
+
},
|
|
1329
|
+
[room]
|
|
1330
|
+
);
|
|
1733
1331
|
}
|
|
1734
|
-
|
|
1332
|
+
const resolveMentionSuggestions = client[kInternal].resolveMentionSuggestions;
|
|
1333
|
+
const mentionSuggestionsCache = /* @__PURE__ */ new Map();
|
|
1334
|
+
function useMentionSuggestions(search) {
|
|
1735
1335
|
const room = useRoom();
|
|
1736
|
-
|
|
1336
|
+
const [mentionSuggestions, setMentionSuggestions] = React2.useState();
|
|
1337
|
+
const lastInvokedAt = React2.useRef();
|
|
1338
|
+
React2.useEffect(() => {
|
|
1339
|
+
if (search === void 0 || !resolveMentionSuggestions) {
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
const resolveMentionSuggestionsArgs = { text: search, roomId: room.id };
|
|
1343
|
+
const mentionSuggestionsCacheKey = stringify(
|
|
1344
|
+
resolveMentionSuggestionsArgs
|
|
1345
|
+
);
|
|
1346
|
+
let debounceTimeout;
|
|
1347
|
+
let isCanceled = false;
|
|
1348
|
+
const getMentionSuggestions = async () => {
|
|
1349
|
+
try {
|
|
1350
|
+
lastInvokedAt.current = performance.now();
|
|
1351
|
+
const mentionSuggestions2 = await resolveMentionSuggestions(
|
|
1352
|
+
resolveMentionSuggestionsArgs
|
|
1353
|
+
);
|
|
1354
|
+
if (!isCanceled) {
|
|
1355
|
+
setMentionSuggestions(mentionSuggestions2);
|
|
1356
|
+
mentionSuggestionsCache.set(
|
|
1357
|
+
mentionSuggestionsCacheKey,
|
|
1358
|
+
mentionSuggestions2
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
} catch (error) {
|
|
1362
|
+
console2.error(error?.message);
|
|
1363
|
+
}
|
|
1364
|
+
};
|
|
1365
|
+
if (mentionSuggestionsCache.has(mentionSuggestionsCacheKey)) {
|
|
1366
|
+
setMentionSuggestions(
|
|
1367
|
+
mentionSuggestionsCache.get(mentionSuggestionsCacheKey)
|
|
1368
|
+
);
|
|
1369
|
+
} else if (!lastInvokedAt.current || Math.abs(performance.now() - lastInvokedAt.current) > MENTION_SUGGESTIONS_DEBOUNCE) {
|
|
1370
|
+
void getMentionSuggestions();
|
|
1371
|
+
} else {
|
|
1372
|
+
debounceTimeout = window.setTimeout(() => {
|
|
1373
|
+
void getMentionSuggestions();
|
|
1374
|
+
}, MENTION_SUGGESTIONS_DEBOUNCE);
|
|
1375
|
+
}
|
|
1376
|
+
return () => {
|
|
1377
|
+
isCanceled = true;
|
|
1378
|
+
window.clearTimeout(debounceTimeout);
|
|
1379
|
+
};
|
|
1380
|
+
}, [room.id, search]);
|
|
1381
|
+
return mentionSuggestions;
|
|
1737
1382
|
}
|
|
1738
|
-
function
|
|
1739
|
-
|
|
1740
|
-
|
|
1383
|
+
function useThreadSubscription(threadId) {
|
|
1384
|
+
return useSyncExternalStoreWithSelector(
|
|
1385
|
+
store.subscribe,
|
|
1386
|
+
store.get,
|
|
1387
|
+
store.get,
|
|
1388
|
+
(state) => {
|
|
1389
|
+
const inboxNotification = selectedInboxNotifications(state).find(
|
|
1390
|
+
(inboxNotification2) => inboxNotification2.threadId === threadId
|
|
1391
|
+
);
|
|
1392
|
+
const thread = state.threads[threadId];
|
|
1393
|
+
if (inboxNotification === void 0 || thread === void 0) {
|
|
1394
|
+
return {
|
|
1395
|
+
status: "not-subscribed"
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
return {
|
|
1399
|
+
status: "subscribed",
|
|
1400
|
+
unreadSince: inboxNotification.readAt
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
);
|
|
1741
1404
|
}
|
|
1742
|
-
function
|
|
1743
|
-
|
|
1744
|
-
|
|
1405
|
+
function useMarkThreadAsRead() {
|
|
1406
|
+
return React2.useCallback((threadId) => {
|
|
1407
|
+
const inboxNotification = Object.values(
|
|
1408
|
+
store.get().inboxNotifications
|
|
1409
|
+
).find((inboxNotification2) => inboxNotification2.threadId === threadId);
|
|
1410
|
+
if (!inboxNotification)
|
|
1411
|
+
return;
|
|
1412
|
+
const optimisticUpdateId = nanoid2();
|
|
1413
|
+
const now = /* @__PURE__ */ new Date();
|
|
1414
|
+
store.pushOptimisticUpdate({
|
|
1415
|
+
type: "mark-inbox-notification-as-read",
|
|
1416
|
+
id: optimisticUpdateId,
|
|
1417
|
+
inboxNotificationId: inboxNotification.id,
|
|
1418
|
+
readAt: now
|
|
1419
|
+
});
|
|
1420
|
+
client.markInboxNotificationAsRead(inboxNotification.id).then(
|
|
1421
|
+
() => {
|
|
1422
|
+
store.set((state) => ({
|
|
1423
|
+
...state,
|
|
1424
|
+
inboxNotifications: {
|
|
1425
|
+
...state.inboxNotifications,
|
|
1426
|
+
[inboxNotification.id]: {
|
|
1427
|
+
...inboxNotification,
|
|
1428
|
+
readAt: now
|
|
1429
|
+
}
|
|
1430
|
+
},
|
|
1431
|
+
optimisticUpdates: state.optimisticUpdates.filter(
|
|
1432
|
+
(update) => update.id !== optimisticUpdateId
|
|
1433
|
+
)
|
|
1434
|
+
}));
|
|
1435
|
+
},
|
|
1436
|
+
(err) => {
|
|
1437
|
+
onMutationFailure(
|
|
1438
|
+
err,
|
|
1439
|
+
optimisticUpdateId,
|
|
1440
|
+
(error) => new MarkInboxNotificationAsReadError(error, {
|
|
1441
|
+
inboxNotificationId: inboxNotification.id
|
|
1442
|
+
})
|
|
1443
|
+
);
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
);
|
|
1447
|
+
}, []);
|
|
1448
|
+
}
|
|
1449
|
+
function makeNotificationSettingsQueryKey(roomId) {
|
|
1450
|
+
return `${roomId}:NOTIFICATION_SETTINGS`;
|
|
1451
|
+
}
|
|
1452
|
+
async function getInboxNotificationSettings(room, queryKey) {
|
|
1453
|
+
const roomId = room.id;
|
|
1454
|
+
return getOrInitRequest(
|
|
1455
|
+
queryKey,
|
|
1456
|
+
async () => {
|
|
1457
|
+
const room2 = client.getRoom(roomId);
|
|
1458
|
+
if (room2 === null) {
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
return room2.getRoomNotificationSettings();
|
|
1462
|
+
},
|
|
1463
|
+
(settings) => {
|
|
1464
|
+
if (settings !== void 0) {
|
|
1465
|
+
store.set((state) => ({
|
|
1466
|
+
...state,
|
|
1467
|
+
notificationSettings: {
|
|
1468
|
+
...state.notificationSettings,
|
|
1469
|
+
[room.id]: settings
|
|
1470
|
+
},
|
|
1471
|
+
queries: {
|
|
1472
|
+
...state.queries,
|
|
1473
|
+
[queryKey]: {
|
|
1474
|
+
isLoading: false
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
}));
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
);
|
|
1745
1481
|
}
|
|
1746
|
-
function
|
|
1482
|
+
function useRoomNotificationSettings() {
|
|
1747
1483
|
const room = useRoom();
|
|
1748
|
-
|
|
1484
|
+
React2.useEffect(() => {
|
|
1485
|
+
const queryKey = makeNotificationSettingsQueryKey(room.id);
|
|
1486
|
+
void getInboxNotificationSettings(room, queryKey);
|
|
1487
|
+
incrementQuerySubscribers(queryKey);
|
|
1488
|
+
return () => decrementQuerySubscribers(queryKey);
|
|
1489
|
+
}, [room]);
|
|
1490
|
+
const updateRoomNotificationSettings = useUpdateRoomNotificationSettings();
|
|
1491
|
+
return [
|
|
1492
|
+
useSyncExternalStoreWithSelector(
|
|
1493
|
+
store.subscribe,
|
|
1494
|
+
store.get,
|
|
1495
|
+
store.get,
|
|
1496
|
+
(state) => {
|
|
1497
|
+
const query = state.queries[makeNotificationSettingsQueryKey(room.id)];
|
|
1498
|
+
if (query === void 0 || query.isLoading) {
|
|
1499
|
+
return { isLoading: true };
|
|
1500
|
+
}
|
|
1501
|
+
if (query.error !== void 0) {
|
|
1502
|
+
return { isLoading: false, error: query.error };
|
|
1503
|
+
}
|
|
1504
|
+
return {
|
|
1505
|
+
isLoading: false,
|
|
1506
|
+
settings: selectNotificationSettings(room.id, state)
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
),
|
|
1510
|
+
updateRoomNotificationSettings
|
|
1511
|
+
];
|
|
1749
1512
|
}
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
const users = await resolveUsers(
|
|
1753
|
-
JSON.parse(stringifiedOptions)
|
|
1754
|
-
);
|
|
1755
|
-
return users?.[0];
|
|
1756
|
-
}) : void 0;
|
|
1757
|
-
function useUser(userId) {
|
|
1513
|
+
function useRoomNotificationSettingsSuspense() {
|
|
1514
|
+
const updateRoomNotificationSettings = useUpdateRoomNotificationSettings();
|
|
1758
1515
|
const room = useRoom();
|
|
1759
|
-
const
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
return {
|
|
1767
|
-
isLoading: true
|
|
1768
|
-
};
|
|
1769
|
-
} else {
|
|
1770
|
-
return {
|
|
1771
|
-
user: state.data,
|
|
1772
|
-
error: state.error,
|
|
1773
|
-
isLoading: false
|
|
1774
|
-
};
|
|
1516
|
+
const queryKey = makeNotificationSettingsQueryKey(room.id);
|
|
1517
|
+
const query = store.get().queries[queryKey];
|
|
1518
|
+
if (query === void 0 || query.isLoading) {
|
|
1519
|
+
throw getInboxNotificationSettings(room, queryKey);
|
|
1520
|
+
}
|
|
1521
|
+
if (query.error) {
|
|
1522
|
+
throw query.error;
|
|
1775
1523
|
}
|
|
1524
|
+
React2.useEffect(() => {
|
|
1525
|
+
const queryKey2 = makeNotificationSettingsQueryKey(room.id);
|
|
1526
|
+
incrementQuerySubscribers(queryKey2);
|
|
1527
|
+
return () => decrementQuerySubscribers(queryKey2);
|
|
1528
|
+
}, [room]);
|
|
1529
|
+
return [
|
|
1530
|
+
useSyncExternalStoreWithSelector(
|
|
1531
|
+
store.subscribe,
|
|
1532
|
+
store.get,
|
|
1533
|
+
store.get,
|
|
1534
|
+
(state) => {
|
|
1535
|
+
return {
|
|
1536
|
+
isLoading: false,
|
|
1537
|
+
settings: selectNotificationSettings(room.id, state)
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
),
|
|
1541
|
+
updateRoomNotificationSettings
|
|
1542
|
+
];
|
|
1776
1543
|
}
|
|
1777
|
-
function
|
|
1544
|
+
function useUpdateRoomNotificationSettings() {
|
|
1778
1545
|
const room = useRoom();
|
|
1779
|
-
|
|
1780
|
-
() =>
|
|
1781
|
-
|
|
1546
|
+
return React2.useCallback(
|
|
1547
|
+
(settings) => {
|
|
1548
|
+
const optimisticUpdateId = nanoid2();
|
|
1549
|
+
store.pushOptimisticUpdate({
|
|
1550
|
+
id: optimisticUpdateId,
|
|
1551
|
+
type: "update-notification-settings",
|
|
1552
|
+
roomId: room.id,
|
|
1553
|
+
settings
|
|
1554
|
+
});
|
|
1555
|
+
room.updateRoomNotificationSettings(settings).then(
|
|
1556
|
+
(settings2) => {
|
|
1557
|
+
store.set((state) => ({
|
|
1558
|
+
...state,
|
|
1559
|
+
notificationSettings: {
|
|
1560
|
+
[room.id]: settings2
|
|
1561
|
+
},
|
|
1562
|
+
optimisticUpdates: state.optimisticUpdates.filter(
|
|
1563
|
+
(update) => update.id !== optimisticUpdateId
|
|
1564
|
+
)
|
|
1565
|
+
}));
|
|
1566
|
+
},
|
|
1567
|
+
(err) => onMutationFailure(
|
|
1568
|
+
err,
|
|
1569
|
+
optimisticUpdateId,
|
|
1570
|
+
(error) => new UpdateNotificationSettingsError(error, {
|
|
1571
|
+
roomId: room.id
|
|
1572
|
+
})
|
|
1573
|
+
)
|
|
1574
|
+
);
|
|
1575
|
+
},
|
|
1576
|
+
[room]
|
|
1782
1577
|
);
|
|
1783
|
-
const state = useAsyncCache(usersCache, resolverKey, {
|
|
1784
|
-
suspense: true
|
|
1785
|
-
});
|
|
1786
|
-
React3.useEffect(() => warnIfNoResolveUsers(usersCache), []);
|
|
1787
|
-
return {
|
|
1788
|
-
user: state.data,
|
|
1789
|
-
isLoading: false
|
|
1790
|
-
};
|
|
1791
1578
|
}
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
return resolveMentionSuggestions(
|
|
1795
|
-
JSON.parse(stringifiedOptions)
|
|
1796
|
-
);
|
|
1797
|
-
} : () => Promise.resolve([])
|
|
1798
|
-
);
|
|
1799
|
-
function useMentionSuggestions(search) {
|
|
1800
|
-
const room = useRoom();
|
|
1801
|
-
const debouncedSearch = useDebounce(search, 500);
|
|
1802
|
-
const resolverKey = React3.useMemo(
|
|
1803
|
-
() => debouncedSearch !== void 0 ? stringify2({ text: debouncedSearch, roomId: room.id }) : null,
|
|
1804
|
-
[debouncedSearch, room.id]
|
|
1805
|
-
);
|
|
1806
|
-
const { data } = useAsyncCache(mentionSuggestionsCache, resolverKey, {
|
|
1807
|
-
keepPreviousDataWhileLoading: true
|
|
1808
|
-
});
|
|
1809
|
-
return data;
|
|
1579
|
+
function useCurrentUserId() {
|
|
1580
|
+
return useSelf((user) => typeof user.id === "string" ? user.id : null);
|
|
1810
1581
|
}
|
|
1811
1582
|
const bundle = {
|
|
1812
1583
|
RoomContext,
|
|
@@ -1839,7 +1610,6 @@ function createRoomContext(client, options) {
|
|
|
1839
1610
|
useOther,
|
|
1840
1611
|
useMutation,
|
|
1841
1612
|
useThreads,
|
|
1842
|
-
useUser,
|
|
1843
1613
|
useCreateThread,
|
|
1844
1614
|
useEditThreadMetadata,
|
|
1845
1615
|
useCreateComment,
|
|
@@ -1847,6 +1617,11 @@ function createRoomContext(client, options) {
|
|
|
1847
1617
|
useDeleteComment,
|
|
1848
1618
|
useAddReaction,
|
|
1849
1619
|
useRemoveReaction,
|
|
1620
|
+
useMarkThreadAsRead,
|
|
1621
|
+
useThreadSubscription,
|
|
1622
|
+
useRoomNotificationSettings,
|
|
1623
|
+
useUpdateRoomNotificationSettings,
|
|
1624
|
+
...shared,
|
|
1850
1625
|
suspense: {
|
|
1851
1626
|
RoomContext,
|
|
1852
1627
|
RoomProvider: RoomProviderOuter,
|
|
@@ -1878,31 +1653,483 @@ function createRoomContext(client, options) {
|
|
|
1878
1653
|
useOther: useOtherSuspense,
|
|
1879
1654
|
useMutation,
|
|
1880
1655
|
useThreads: useThreadsSuspense,
|
|
1881
|
-
useUser: useUserSuspense,
|
|
1882
1656
|
useCreateThread,
|
|
1883
1657
|
useEditThreadMetadata,
|
|
1884
1658
|
useCreateComment,
|
|
1885
1659
|
useEditComment,
|
|
1886
1660
|
useDeleteComment,
|
|
1887
1661
|
useAddReaction,
|
|
1888
|
-
useRemoveReaction
|
|
1662
|
+
useRemoveReaction,
|
|
1663
|
+
useMarkThreadAsRead,
|
|
1664
|
+
useThreadSubscription,
|
|
1665
|
+
useRoomNotificationSettings: useRoomNotificationSettingsSuspense,
|
|
1666
|
+
useUpdateRoomNotificationSettings,
|
|
1667
|
+
...shared.suspense
|
|
1668
|
+
},
|
|
1669
|
+
[kInternal]: {
|
|
1670
|
+
useCurrentUserId,
|
|
1671
|
+
hasResolveMentionSuggestions: resolveMentionSuggestions !== void 0,
|
|
1672
|
+
useMentionSuggestions
|
|
1889
1673
|
}
|
|
1890
1674
|
};
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1675
|
+
return Object.defineProperty(bundle, kInternal, {
|
|
1676
|
+
enumerable: false
|
|
1677
|
+
});
|
|
1678
|
+
}
|
|
1679
|
+
function getCurrentUserId(room) {
|
|
1680
|
+
const self = room.getSelf();
|
|
1681
|
+
if (self === null || self.id === void 0) {
|
|
1682
|
+
return "anonymous";
|
|
1683
|
+
} else {
|
|
1684
|
+
return self.id;
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
function handleApiError(err) {
|
|
1688
|
+
const message = `Request failed with status ${err.status}: ${err.message}`;
|
|
1689
|
+
if (err.details?.error === "FORBIDDEN") {
|
|
1690
|
+
const detailedMessage = [message, err.details.suggestion, err.details.docs].filter(Boolean).join("\n");
|
|
1691
|
+
console2.error(detailedMessage);
|
|
1692
|
+
}
|
|
1693
|
+
return new Error(message);
|
|
1694
|
+
}
|
|
1695
|
+
function generateQueryKey(roomId, options) {
|
|
1696
|
+
return `${roomId}-${stringify(options ?? {})}`;
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
// src/shared.ts
|
|
1700
|
+
function useSharedContextBundle() {
|
|
1701
|
+
const roomContextBundle = useContext2(ContextBundle);
|
|
1702
|
+
const liveblocksContextBundle = useContext2(ContextBundle2);
|
|
1703
|
+
if (roomContextBundle !== null) {
|
|
1704
|
+
return roomContextBundle;
|
|
1705
|
+
} else if (liveblocksContextBundle !== null) {
|
|
1706
|
+
return liveblocksContextBundle;
|
|
1707
|
+
} else {
|
|
1708
|
+
throw new Error(
|
|
1709
|
+
"LiveblocksProvider or RoomProvider are missing from the React tree."
|
|
1710
|
+
);
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
function createSharedContext(client) {
|
|
1714
|
+
const usersStore = client[kInternal2].usersStore;
|
|
1715
|
+
const roomsInfoStore = client[kInternal2].roomsInfoStore;
|
|
1716
|
+
function useUser(userId) {
|
|
1717
|
+
const getUserState = useCallback2(
|
|
1718
|
+
() => usersStore.getState(userId),
|
|
1719
|
+
[userId]
|
|
1720
|
+
);
|
|
1721
|
+
useEffect4(() => {
|
|
1722
|
+
void usersStore.get(userId);
|
|
1723
|
+
}, [userId]);
|
|
1724
|
+
const state = useSyncExternalStore2(
|
|
1725
|
+
usersStore.subscribe,
|
|
1726
|
+
getUserState,
|
|
1727
|
+
getUserState
|
|
1728
|
+
);
|
|
1729
|
+
return state ? {
|
|
1730
|
+
...state,
|
|
1731
|
+
user: state.data
|
|
1732
|
+
} : { isLoading: true };
|
|
1733
|
+
}
|
|
1734
|
+
function useUserSuspense(userId) {
|
|
1735
|
+
const getUserState = useCallback2(
|
|
1736
|
+
() => usersStore.getState(userId),
|
|
1737
|
+
[userId]
|
|
1738
|
+
);
|
|
1739
|
+
const userState = getUserState();
|
|
1740
|
+
if (!userState || userState.isLoading) {
|
|
1741
|
+
throw usersStore.get(userId);
|
|
1742
|
+
}
|
|
1743
|
+
if (userState.error) {
|
|
1744
|
+
throw userState.error;
|
|
1745
|
+
}
|
|
1746
|
+
const state = useSyncExternalStore2(
|
|
1747
|
+
usersStore.subscribe,
|
|
1748
|
+
getUserState,
|
|
1749
|
+
getUserState
|
|
1750
|
+
);
|
|
1751
|
+
return {
|
|
1752
|
+
...state,
|
|
1753
|
+
user: state?.data
|
|
1754
|
+
};
|
|
1755
|
+
}
|
|
1756
|
+
function useRoomInfo(roomId) {
|
|
1757
|
+
const getRoomInfoState = useCallback2(
|
|
1758
|
+
() => roomsInfoStore.getState(roomId),
|
|
1759
|
+
[roomId]
|
|
1760
|
+
);
|
|
1761
|
+
useEffect4(() => {
|
|
1762
|
+
void roomsInfoStore.get(roomId);
|
|
1763
|
+
}, [roomId]);
|
|
1764
|
+
const state = useSyncExternalStore2(
|
|
1765
|
+
roomsInfoStore.subscribe,
|
|
1766
|
+
getRoomInfoState,
|
|
1767
|
+
getRoomInfoState
|
|
1768
|
+
);
|
|
1769
|
+
return state ? {
|
|
1770
|
+
...state,
|
|
1771
|
+
info: state.data
|
|
1772
|
+
} : { isLoading: true };
|
|
1773
|
+
}
|
|
1774
|
+
function useRoomInfoSuspense(roomId) {
|
|
1775
|
+
const getRoomInfoState = useCallback2(
|
|
1776
|
+
() => roomsInfoStore.getState(roomId),
|
|
1777
|
+
[roomId]
|
|
1778
|
+
);
|
|
1779
|
+
const roomInfoState = getRoomInfoState();
|
|
1780
|
+
if (!roomInfoState || roomInfoState.isLoading) {
|
|
1781
|
+
throw usersStore.get(roomId);
|
|
1782
|
+
}
|
|
1783
|
+
if (roomInfoState.error) {
|
|
1784
|
+
throw roomInfoState.error;
|
|
1785
|
+
}
|
|
1786
|
+
const state = useSyncExternalStore2(
|
|
1787
|
+
usersStore.subscribe,
|
|
1788
|
+
getRoomInfoState,
|
|
1789
|
+
getRoomInfoState
|
|
1790
|
+
);
|
|
1791
|
+
return {
|
|
1792
|
+
...state,
|
|
1793
|
+
info: state?.data
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
const bundle = {
|
|
1797
|
+
useUser,
|
|
1798
|
+
useRoomInfo,
|
|
1799
|
+
suspense: {
|
|
1800
|
+
useUser: useUserSuspense,
|
|
1801
|
+
useRoomInfo: useRoomInfoSuspense
|
|
1802
|
+
}
|
|
1895
1803
|
};
|
|
1896
1804
|
return bundle;
|
|
1897
1805
|
}
|
|
1898
1806
|
|
|
1807
|
+
// src/liveblocks.tsx
|
|
1808
|
+
var ContextBundle2 = createContext2(null);
|
|
1809
|
+
function useLiveblocksContextBundle() {
|
|
1810
|
+
const bundle = useContext3(ContextBundle2);
|
|
1811
|
+
if (bundle === null) {
|
|
1812
|
+
throw new Error("LiveblocksProvider is missing from the React tree.");
|
|
1813
|
+
}
|
|
1814
|
+
return bundle;
|
|
1815
|
+
}
|
|
1816
|
+
function createLiveblocksContext(client) {
|
|
1817
|
+
const shared = createSharedContext(client);
|
|
1818
|
+
const store = client[kInternal3].cacheStore;
|
|
1819
|
+
function LiveblocksProvider(props) {
|
|
1820
|
+
return /* @__PURE__ */ React3.createElement(
|
|
1821
|
+
ContextBundle2.Provider,
|
|
1822
|
+
{
|
|
1823
|
+
value: bundle
|
|
1824
|
+
},
|
|
1825
|
+
props.children
|
|
1826
|
+
);
|
|
1827
|
+
}
|
|
1828
|
+
let fetchInboxNotificationsRequest = null;
|
|
1829
|
+
let inboxNotificationsSubscribers = 0;
|
|
1830
|
+
const INBOX_NOTIFICATIONS_QUERY = "INBOX_NOTIFICATIONS";
|
|
1831
|
+
const POLLING_INTERVAL2 = 60 * 1e3;
|
|
1832
|
+
const poller = makePoller2(refreshThreadsAndNotifications);
|
|
1833
|
+
function refreshThreadsAndNotifications() {
|
|
1834
|
+
return client.getInboxNotifications().then(
|
|
1835
|
+
({ threads, inboxNotifications }) => {
|
|
1836
|
+
store.updateThreadsAndNotifications(
|
|
1837
|
+
threads,
|
|
1838
|
+
inboxNotifications,
|
|
1839
|
+
INBOX_NOTIFICATIONS_QUERY
|
|
1840
|
+
);
|
|
1841
|
+
},
|
|
1842
|
+
() => {
|
|
1843
|
+
}
|
|
1844
|
+
);
|
|
1845
|
+
}
|
|
1846
|
+
function incrementInboxNotificationsSubscribers() {
|
|
1847
|
+
inboxNotificationsSubscribers++;
|
|
1848
|
+
poller.start(POLLING_INTERVAL2);
|
|
1849
|
+
}
|
|
1850
|
+
function decrementInboxNotificationsSubscribers() {
|
|
1851
|
+
if (inboxNotificationsSubscribers <= 0) {
|
|
1852
|
+
console.warn(
|
|
1853
|
+
`Internal unexpected behavior. Cannot decrease subscriber count for query "${INBOX_NOTIFICATIONS_QUERY}"`
|
|
1854
|
+
);
|
|
1855
|
+
return;
|
|
1856
|
+
}
|
|
1857
|
+
inboxNotificationsSubscribers--;
|
|
1858
|
+
if (inboxNotificationsSubscribers <= 0) {
|
|
1859
|
+
poller.stop();
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
async function fetchInboxNotifications() {
|
|
1863
|
+
if (fetchInboxNotificationsRequest) {
|
|
1864
|
+
return fetchInboxNotificationsRequest;
|
|
1865
|
+
}
|
|
1866
|
+
store.setQueryState(INBOX_NOTIFICATIONS_QUERY, {
|
|
1867
|
+
isLoading: true
|
|
1868
|
+
});
|
|
1869
|
+
try {
|
|
1870
|
+
fetchInboxNotificationsRequest = client.getInboxNotifications();
|
|
1871
|
+
const { inboxNotifications, threads } = await fetchInboxNotificationsRequest;
|
|
1872
|
+
store.updateThreadsAndNotifications(
|
|
1873
|
+
threads,
|
|
1874
|
+
inboxNotifications,
|
|
1875
|
+
INBOX_NOTIFICATIONS_QUERY
|
|
1876
|
+
);
|
|
1877
|
+
} catch (er) {
|
|
1878
|
+
store.setQueryState(INBOX_NOTIFICATIONS_QUERY, {
|
|
1879
|
+
isLoading: false,
|
|
1880
|
+
error: er
|
|
1881
|
+
});
|
|
1882
|
+
}
|
|
1883
|
+
return;
|
|
1884
|
+
}
|
|
1885
|
+
function useInboxNotifications() {
|
|
1886
|
+
useEffect5(() => {
|
|
1887
|
+
void fetchInboxNotifications();
|
|
1888
|
+
incrementInboxNotificationsSubscribers();
|
|
1889
|
+
return () => decrementInboxNotificationsSubscribers();
|
|
1890
|
+
});
|
|
1891
|
+
const result = useSyncExternalStoreWithSelector2(
|
|
1892
|
+
store.subscribe,
|
|
1893
|
+
store.get,
|
|
1894
|
+
store.get,
|
|
1895
|
+
(state) => {
|
|
1896
|
+
const query = state.queries[INBOX_NOTIFICATIONS_QUERY];
|
|
1897
|
+
if (query === void 0 || query.isLoading) {
|
|
1898
|
+
return {
|
|
1899
|
+
isLoading: true
|
|
1900
|
+
};
|
|
1901
|
+
}
|
|
1902
|
+
if (query.error !== void 0) {
|
|
1903
|
+
return {
|
|
1904
|
+
error: query.error,
|
|
1905
|
+
isLoading: false
|
|
1906
|
+
};
|
|
1907
|
+
}
|
|
1908
|
+
return {
|
|
1909
|
+
inboxNotifications: selectedInboxNotifications(state),
|
|
1910
|
+
isLoading: false
|
|
1911
|
+
};
|
|
1912
|
+
}
|
|
1913
|
+
);
|
|
1914
|
+
return result;
|
|
1915
|
+
}
|
|
1916
|
+
function useInboxNotificationsSuspense() {
|
|
1917
|
+
if (store.get().queries[INBOX_NOTIFICATIONS_QUERY] === void 0 || store.get().queries[INBOX_NOTIFICATIONS_QUERY].isLoading) {
|
|
1918
|
+
throw fetchInboxNotifications();
|
|
1919
|
+
}
|
|
1920
|
+
React3.useEffect(() => {
|
|
1921
|
+
incrementInboxNotificationsSubscribers();
|
|
1922
|
+
return () => {
|
|
1923
|
+
decrementInboxNotificationsSubscribers();
|
|
1924
|
+
};
|
|
1925
|
+
}, []);
|
|
1926
|
+
return useSyncExternalStoreWithSelector2(
|
|
1927
|
+
store.subscribe,
|
|
1928
|
+
store.get,
|
|
1929
|
+
store.get,
|
|
1930
|
+
(state) => {
|
|
1931
|
+
return {
|
|
1932
|
+
inboxNotifications: selectedInboxNotifications(state),
|
|
1933
|
+
isLoading: false
|
|
1934
|
+
};
|
|
1935
|
+
}
|
|
1936
|
+
);
|
|
1937
|
+
}
|
|
1938
|
+
function selectUnreadInboxNotificationsCount(state) {
|
|
1939
|
+
let count = 0;
|
|
1940
|
+
for (const notification of selectedInboxNotifications(state)) {
|
|
1941
|
+
if (notification.readAt === null || notification.readAt < notification.notifiedAt) {
|
|
1942
|
+
count++;
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
return count;
|
|
1946
|
+
}
|
|
1947
|
+
function useUnreadInboxNotificationsCount() {
|
|
1948
|
+
useEffect5(() => {
|
|
1949
|
+
void fetchInboxNotifications();
|
|
1950
|
+
incrementInboxNotificationsSubscribers();
|
|
1951
|
+
return () => decrementInboxNotificationsSubscribers();
|
|
1952
|
+
});
|
|
1953
|
+
return useSyncExternalStoreWithSelector2(
|
|
1954
|
+
store.subscribe,
|
|
1955
|
+
store.get,
|
|
1956
|
+
store.get,
|
|
1957
|
+
(state) => {
|
|
1958
|
+
const query = store.get().queries[INBOX_NOTIFICATIONS_QUERY];
|
|
1959
|
+
if (query === void 0 || query.isLoading) {
|
|
1960
|
+
return {
|
|
1961
|
+
isLoading: true
|
|
1962
|
+
};
|
|
1963
|
+
}
|
|
1964
|
+
if (query.error !== void 0) {
|
|
1965
|
+
return {
|
|
1966
|
+
error: query.error,
|
|
1967
|
+
isLoading: false
|
|
1968
|
+
};
|
|
1969
|
+
}
|
|
1970
|
+
return {
|
|
1971
|
+
isLoading: false,
|
|
1972
|
+
count: selectUnreadInboxNotificationsCount(state)
|
|
1973
|
+
};
|
|
1974
|
+
}
|
|
1975
|
+
);
|
|
1976
|
+
}
|
|
1977
|
+
function useUnreadInboxNotificationsCountSuspense() {
|
|
1978
|
+
if (store.get().queries[INBOX_NOTIFICATIONS_QUERY] === void 0 || store.get().queries[INBOX_NOTIFICATIONS_QUERY].isLoading) {
|
|
1979
|
+
throw fetchInboxNotifications();
|
|
1980
|
+
}
|
|
1981
|
+
React3.useEffect(() => {
|
|
1982
|
+
incrementInboxNotificationsSubscribers();
|
|
1983
|
+
return () => {
|
|
1984
|
+
decrementInboxNotificationsSubscribers();
|
|
1985
|
+
};
|
|
1986
|
+
}, []);
|
|
1987
|
+
return useSyncExternalStoreWithSelector2(
|
|
1988
|
+
store.subscribe,
|
|
1989
|
+
store.get,
|
|
1990
|
+
store.get,
|
|
1991
|
+
(state) => {
|
|
1992
|
+
return {
|
|
1993
|
+
isLoading: false,
|
|
1994
|
+
count: selectUnreadInboxNotificationsCount(state)
|
|
1995
|
+
};
|
|
1996
|
+
}
|
|
1997
|
+
);
|
|
1998
|
+
}
|
|
1999
|
+
function useMarkInboxNotificationAsRead() {
|
|
2000
|
+
return useCallback3((inboxNotificationId) => {
|
|
2001
|
+
const optimisticUpdateId = nanoid3();
|
|
2002
|
+
const readAt = /* @__PURE__ */ new Date();
|
|
2003
|
+
store.pushOptimisticUpdate({
|
|
2004
|
+
type: "mark-inbox-notification-as-read",
|
|
2005
|
+
id: optimisticUpdateId,
|
|
2006
|
+
inboxNotificationId,
|
|
2007
|
+
readAt
|
|
2008
|
+
});
|
|
2009
|
+
client.markInboxNotificationAsRead(inboxNotificationId).then(
|
|
2010
|
+
() => {
|
|
2011
|
+
store.set((state) => ({
|
|
2012
|
+
...state,
|
|
2013
|
+
inboxNotifications: {
|
|
2014
|
+
...state.inboxNotifications,
|
|
2015
|
+
[inboxNotificationId]: {
|
|
2016
|
+
// TODO: Handle potential deleted inbox notification
|
|
2017
|
+
...state.inboxNotifications[inboxNotificationId],
|
|
2018
|
+
readAt
|
|
2019
|
+
}
|
|
2020
|
+
},
|
|
2021
|
+
optimisticUpdates: state.optimisticUpdates.filter(
|
|
2022
|
+
(update) => update.id !== optimisticUpdateId
|
|
2023
|
+
)
|
|
2024
|
+
}));
|
|
2025
|
+
},
|
|
2026
|
+
() => {
|
|
2027
|
+
store.set((state) => ({
|
|
2028
|
+
...state,
|
|
2029
|
+
optimisticUpdates: state.optimisticUpdates.filter(
|
|
2030
|
+
(update) => update.id !== optimisticUpdateId
|
|
2031
|
+
)
|
|
2032
|
+
}));
|
|
2033
|
+
}
|
|
2034
|
+
);
|
|
2035
|
+
}, []);
|
|
2036
|
+
}
|
|
2037
|
+
function useMarkAllInboxNotificationsAsRead() {
|
|
2038
|
+
return useCallback3(() => {
|
|
2039
|
+
const optimisticUpdateId = nanoid3();
|
|
2040
|
+
const readAt = /* @__PURE__ */ new Date();
|
|
2041
|
+
store.pushOptimisticUpdate({
|
|
2042
|
+
type: "mark-inbox-notifications-as-read",
|
|
2043
|
+
id: optimisticUpdateId,
|
|
2044
|
+
readAt
|
|
2045
|
+
});
|
|
2046
|
+
client.markAllInboxNotificationsAsRead().then(
|
|
2047
|
+
() => {
|
|
2048
|
+
store.set((state) => ({
|
|
2049
|
+
...state,
|
|
2050
|
+
inboxNotifications: Object.fromEntries(
|
|
2051
|
+
Array.from(Object.entries(state.inboxNotifications)).map(
|
|
2052
|
+
([id, inboxNotification]) => [
|
|
2053
|
+
id,
|
|
2054
|
+
{ ...inboxNotification, readAt }
|
|
2055
|
+
]
|
|
2056
|
+
)
|
|
2057
|
+
),
|
|
2058
|
+
optimisticUpdates: state.optimisticUpdates.filter(
|
|
2059
|
+
(update) => update.id !== optimisticUpdateId
|
|
2060
|
+
)
|
|
2061
|
+
}));
|
|
2062
|
+
},
|
|
2063
|
+
() => {
|
|
2064
|
+
store.set((state) => ({
|
|
2065
|
+
...state,
|
|
2066
|
+
optimisticUpdates: state.optimisticUpdates.filter(
|
|
2067
|
+
(update) => update.id !== optimisticUpdateId
|
|
2068
|
+
)
|
|
2069
|
+
}));
|
|
2070
|
+
}
|
|
2071
|
+
);
|
|
2072
|
+
}, []);
|
|
2073
|
+
}
|
|
2074
|
+
function useThreadFromCache(threadId) {
|
|
2075
|
+
return useSyncExternalStoreWithSelector2(
|
|
2076
|
+
store.subscribe,
|
|
2077
|
+
store.get,
|
|
2078
|
+
store.get,
|
|
2079
|
+
(state) => {
|
|
2080
|
+
const thread = state.threads[threadId];
|
|
2081
|
+
if (thread === void 0) {
|
|
2082
|
+
throw new Error(
|
|
2083
|
+
`Internal error: thread with id "${threadId}" not found in cache`
|
|
2084
|
+
);
|
|
2085
|
+
}
|
|
2086
|
+
return thread;
|
|
2087
|
+
}
|
|
2088
|
+
);
|
|
2089
|
+
}
|
|
2090
|
+
const currentUserIdStore = client[kInternal3].currentUserIdStore;
|
|
2091
|
+
function useCurrentUserId() {
|
|
2092
|
+
return useSyncExternalStore3(
|
|
2093
|
+
currentUserIdStore.subscribe,
|
|
2094
|
+
currentUserIdStore.get,
|
|
2095
|
+
currentUserIdStore.get
|
|
2096
|
+
);
|
|
2097
|
+
}
|
|
2098
|
+
const bundle = {
|
|
2099
|
+
LiveblocksProvider,
|
|
2100
|
+
useInboxNotifications,
|
|
2101
|
+
useUnreadInboxNotificationsCount,
|
|
2102
|
+
useMarkInboxNotificationAsRead,
|
|
2103
|
+
useMarkAllInboxNotificationsAsRead,
|
|
2104
|
+
...shared,
|
|
2105
|
+
suspense: {
|
|
2106
|
+
LiveblocksProvider,
|
|
2107
|
+
useInboxNotifications: useInboxNotificationsSuspense,
|
|
2108
|
+
useUnreadInboxNotificationsCount: useUnreadInboxNotificationsCountSuspense,
|
|
2109
|
+
useMarkInboxNotificationAsRead,
|
|
2110
|
+
useMarkAllInboxNotificationsAsRead,
|
|
2111
|
+
...shared.suspense
|
|
2112
|
+
},
|
|
2113
|
+
[kInternal3]: {
|
|
2114
|
+
useThreadFromCache,
|
|
2115
|
+
useCurrentUserId
|
|
2116
|
+
}
|
|
2117
|
+
};
|
|
2118
|
+
return Object.defineProperty(bundle, kInternal3, {
|
|
2119
|
+
enumerable: false
|
|
2120
|
+
});
|
|
2121
|
+
}
|
|
2122
|
+
|
|
1899
2123
|
// src/index.ts
|
|
1900
2124
|
import { shallow as shallow2 } from "@liveblocks/client";
|
|
1901
2125
|
detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);
|
|
1902
2126
|
export {
|
|
1903
2127
|
ClientSideSuspense,
|
|
2128
|
+
createLiveblocksContext,
|
|
1904
2129
|
createRoomContext,
|
|
1905
2130
|
shallow2 as shallow,
|
|
1906
|
-
|
|
2131
|
+
useLiveblocksContextBundle,
|
|
2132
|
+
useRoomContextBundle,
|
|
2133
|
+
useSharedContextBundle
|
|
1907
2134
|
};
|
|
1908
2135
|
//# sourceMappingURL=index.mjs.map
|