@liveblocks/react 2.10.0 → 2.10.1-react19rc
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/_private.d.mts +1 -1
- package/dist/_private.d.ts +1 -1
- package/dist/_private.js +4 -6
- package/dist/_private.js.map +1 -1
- package/dist/_private.mjs +1 -3
- package/dist/_private.mjs.map +1 -1
- package/dist/{chunk-WJLAYICR.js → chunk-3MM4G6XB.js} +729 -694
- package/dist/chunk-3MM4G6XB.js.map +1 -0
- package/dist/{chunk-PJN3AKDH.js → chunk-6Z2BEGKA.js} +2 -2
- package/dist/{chunk-LMENFXCS.mjs → chunk-A7GJNN4L.mjs} +723 -688
- package/dist/chunk-A7GJNN4L.mjs.map +1 -0
- package/dist/{chunk-PXKGBTHT.mjs → chunk-ZGTP66ZG.mjs} +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4 -4
- package/dist/index.mjs.map +1 -1
- package/dist/{liveblocks-X3qYz1Vg.d.mts → liveblocks-SAVcXwMX.d.mts} +105 -60
- package/dist/{liveblocks-X3qYz1Vg.d.ts → liveblocks-SAVcXwMX.d.ts} +105 -60
- package/dist/{suspense-pF_E-0CC.d.mts → suspense-W7Cp9ygn.d.ts} +18 -13
- package/dist/{suspense-qQ0vzMgn.d.ts → suspense-vGJE9Mgq.d.mts} +18 -13
- package/dist/suspense.d.mts +2 -2
- package/dist/suspense.d.ts +2 -2
- package/dist/suspense.js +4 -4
- package/dist/suspense.js.map +1 -1
- package/dist/suspense.mjs +4 -4
- package/dist/suspense.mjs.map +1 -1
- package/package.json +4 -4
- package/dist/chunk-LMENFXCS.mjs.map +0 -1
- package/dist/chunk-WJLAYICR.js.map +0 -1
- /package/dist/{chunk-PJN3AKDH.js.map → chunk-6Z2BEGKA.js.map} +0 -0
- /package/dist/{chunk-PXKGBTHT.mjs.map → chunk-ZGTP66ZG.mjs.map} +0 -0
|
@@ -9,18 +9,125 @@ function useIsInsideRoom() {
|
|
|
9
9
|
return room !== null;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
// src/liveblocks.tsx
|
|
13
|
+
import {
|
|
14
|
+
assert,
|
|
15
|
+
createClient,
|
|
16
|
+
kInternal as kInternal2,
|
|
17
|
+
makePoller,
|
|
18
|
+
raise,
|
|
19
|
+
shallow as shallow3
|
|
20
|
+
} from "@liveblocks/core";
|
|
21
|
+
import React2, {
|
|
22
|
+
createContext as createContext2,
|
|
23
|
+
useCallback as useCallback2,
|
|
24
|
+
useContext as useContext2,
|
|
25
|
+
useEffect as useEffect2,
|
|
26
|
+
useMemo
|
|
27
|
+
} from "react";
|
|
28
|
+
import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
|
|
29
|
+
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector.js";
|
|
30
|
+
|
|
31
|
+
// src/config.ts
|
|
32
|
+
var SECONDS = 1e3;
|
|
33
|
+
var MINUTES = 60 * SECONDS;
|
|
34
|
+
var config = {
|
|
35
|
+
NOTIFICATIONS_POLL_INTERVAL: 1 * MINUTES,
|
|
36
|
+
NOTIFICATIONS_MAX_STALE_TIME: 5 * SECONDS,
|
|
37
|
+
ROOM_THREADS_POLL_INTERVAL: 5 * MINUTES,
|
|
38
|
+
ROOM_THREADS_MAX_STALE_TIME: 5 * SECONDS,
|
|
39
|
+
USER_THREADS_POLL_INTERVAL: 1 * MINUTES,
|
|
40
|
+
USER_THREADS_MAX_STALE_TIME: 5 * SECONDS,
|
|
41
|
+
HISTORY_VERSIONS_POLL_INTERVAL: 1 * MINUTES,
|
|
42
|
+
HISTORY_VERSIONS_MAX_STALE_TIME: 5 * SECONDS,
|
|
43
|
+
NOTIFICATION_SETTINGS_POLL_INTERVAL: 1 * MINUTES,
|
|
44
|
+
NOTIFICATION_SETTINGS_MAX_STALE_TIME: 5 * SECONDS
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// src/lib/shallow2.ts
|
|
48
|
+
import { isPlainObject, shallow } from "@liveblocks/core";
|
|
49
|
+
function shallow2(a, b) {
|
|
50
|
+
if (!isPlainObject(a) || !isPlainObject(b)) {
|
|
51
|
+
return shallow(a, b);
|
|
52
|
+
}
|
|
53
|
+
const keysA = Object.keys(a);
|
|
54
|
+
if (keysA.length !== Object.keys(b).length) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return keysA.every(
|
|
58
|
+
(key) => Object.prototype.hasOwnProperty.call(b, key) && shallow(a[key], b[key])
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/lib/use-initial.ts
|
|
63
|
+
import { useCallback, useReducer } from "react";
|
|
64
|
+
|
|
65
|
+
// src/lib/use-latest.ts
|
|
66
|
+
import { useEffect, useRef } from "react";
|
|
67
|
+
function useLatest(value) {
|
|
68
|
+
const ref = useRef(value);
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
ref.current = value;
|
|
71
|
+
}, [value]);
|
|
72
|
+
return ref;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/lib/use-initial.ts
|
|
76
|
+
var noop = (state) => state;
|
|
77
|
+
function useInitial(value) {
|
|
78
|
+
return useReducer(noop, value)[0];
|
|
79
|
+
}
|
|
80
|
+
function useInitialUnlessFunction(latestValue) {
|
|
81
|
+
const frozenValue = useInitial(latestValue);
|
|
82
|
+
if (typeof frozenValue === "function") {
|
|
83
|
+
const ref = useLatest(latestValue);
|
|
84
|
+
return useCallback((...args) => ref.current(...args), [
|
|
85
|
+
ref
|
|
86
|
+
]);
|
|
87
|
+
} else {
|
|
88
|
+
return frozenValue;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/lib/use-polyfill.ts
|
|
93
|
+
var use = (
|
|
94
|
+
// React.use ||
|
|
95
|
+
(promise) => {
|
|
96
|
+
if (promise.status === "pending") {
|
|
97
|
+
throw promise;
|
|
98
|
+
} else if (promise.status === "fulfilled") {
|
|
99
|
+
return promise.value;
|
|
100
|
+
} else if (promise.status === "rejected") {
|
|
101
|
+
throw promise.reason;
|
|
102
|
+
} else {
|
|
103
|
+
promise.status = "pending";
|
|
104
|
+
promise.then(
|
|
105
|
+
(v) => {
|
|
106
|
+
promise.status = "fulfilled";
|
|
107
|
+
promise.value = v;
|
|
108
|
+
},
|
|
109
|
+
(e) => {
|
|
110
|
+
promise.status = "rejected";
|
|
111
|
+
promise.reason = e;
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
throw promise;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
|
|
12
119
|
// src/umbrella-store.ts
|
|
13
120
|
import {
|
|
14
121
|
autoRetry,
|
|
15
122
|
compactObject,
|
|
16
123
|
console as console2,
|
|
17
124
|
createStore,
|
|
125
|
+
HttpError,
|
|
18
126
|
kInternal,
|
|
19
127
|
makeEventSource,
|
|
20
128
|
mapValues,
|
|
21
129
|
nanoid,
|
|
22
130
|
nn,
|
|
23
|
-
StopRetrying,
|
|
24
131
|
stringify
|
|
25
132
|
} from "@liveblocks/core";
|
|
26
133
|
|
|
@@ -41,21 +148,13 @@ function autobind(self) {
|
|
|
41
148
|
} while ((obj = Reflect.getPrototypeOf(obj)) && obj !== Object.prototype);
|
|
42
149
|
}
|
|
43
150
|
|
|
44
|
-
// src/
|
|
45
|
-
|
|
46
|
-
return a.createdAt.getTime() - b.createdAt.getTime();
|
|
47
|
-
}
|
|
48
|
-
function isMoreRecentlyUpdated(a, b) {
|
|
49
|
-
return byMostRecentlyUpdated(a, b) < 0;
|
|
50
|
-
}
|
|
51
|
-
function byMostRecentlyUpdated(a, b) {
|
|
52
|
-
return b.updatedAt.getTime() - a.updatedAt.getTime();
|
|
53
|
-
}
|
|
151
|
+
// src/ThreadDB.ts
|
|
152
|
+
import { SortedList } from "@liveblocks/core";
|
|
54
153
|
|
|
55
154
|
// src/lib/guards.ts
|
|
56
|
-
import { isPlainObject } from "@liveblocks/core";
|
|
155
|
+
import { isPlainObject as isPlainObject2 } from "@liveblocks/core";
|
|
57
156
|
function isStartsWith(blob) {
|
|
58
|
-
return
|
|
157
|
+
return isPlainObject2(blob) && isString(blob.startsWith);
|
|
59
158
|
}
|
|
60
159
|
function isString(value) {
|
|
61
160
|
return typeof value === "string";
|
|
@@ -88,8 +187,124 @@ function matchesOperator(value, op) {
|
|
|
88
187
|
}
|
|
89
188
|
}
|
|
90
189
|
|
|
190
|
+
// src/ThreadDB.ts
|
|
191
|
+
function sanitizeThread(thread) {
|
|
192
|
+
if (thread.deletedAt) {
|
|
193
|
+
if (thread.comments.length > 0) {
|
|
194
|
+
return { ...thread, comments: [] };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const hasComment = thread.comments.some((c) => !c.deletedAt);
|
|
198
|
+
if (!hasComment) {
|
|
199
|
+
return { ...thread, deletedAt: /* @__PURE__ */ new Date(), comments: [] };
|
|
200
|
+
}
|
|
201
|
+
return thread;
|
|
202
|
+
}
|
|
203
|
+
var ThreadDB = class _ThreadDB {
|
|
204
|
+
// The version is auto-incremented on every mutation and can be used as a reliable indicator to tell if the contents of the thread pool has changed
|
|
205
|
+
constructor() {
|
|
206
|
+
this._asc = SortedList.from([], (t1, t2) => {
|
|
207
|
+
const d1 = t1.createdAt;
|
|
208
|
+
const d2 = t2.createdAt;
|
|
209
|
+
return d1 < d2 ? true : d1 === d2 ? t1.id < t2.id : false;
|
|
210
|
+
});
|
|
211
|
+
this._desc = SortedList.from([], (t1, t2) => {
|
|
212
|
+
const d2 = t2.updatedAt;
|
|
213
|
+
const d1 = t1.updatedAt;
|
|
214
|
+
return d2 < d1 ? true : d2 === d1 ? t2.id < t1.id : false;
|
|
215
|
+
});
|
|
216
|
+
this._byId = /* @__PURE__ */ new Map();
|
|
217
|
+
this._version = 0;
|
|
218
|
+
}
|
|
219
|
+
//
|
|
220
|
+
// Public APIs
|
|
221
|
+
//
|
|
222
|
+
clone() {
|
|
223
|
+
const newPool = new _ThreadDB();
|
|
224
|
+
newPool._byId = new Map(this._byId);
|
|
225
|
+
newPool._asc = this._asc.clone();
|
|
226
|
+
newPool._desc = this._desc.clone();
|
|
227
|
+
newPool._version = this._version;
|
|
228
|
+
return newPool;
|
|
229
|
+
}
|
|
230
|
+
/** Gets the transaction count for this DB. Increments any time the DB is modified. */
|
|
231
|
+
get version() {
|
|
232
|
+
return this._version;
|
|
233
|
+
}
|
|
234
|
+
/** Returns an existing thread by ID. Will never return a deleted thread. */
|
|
235
|
+
get(threadId) {
|
|
236
|
+
const thread = this.getEvenIfDeleted(threadId);
|
|
237
|
+
return thread?.deletedAt ? void 0 : thread;
|
|
238
|
+
}
|
|
239
|
+
/** Returns the (possibly deleted) thread by ID. */
|
|
240
|
+
getEvenIfDeleted(threadId) {
|
|
241
|
+
return this._byId.get(threadId);
|
|
242
|
+
}
|
|
243
|
+
/** Adds or updates a thread in the DB. If the newly given thread is a deleted one, it will get deleted. */
|
|
244
|
+
upsert(thread) {
|
|
245
|
+
thread = sanitizeThread(thread);
|
|
246
|
+
const id = thread.id;
|
|
247
|
+
const toRemove = this._byId.get(id);
|
|
248
|
+
if (toRemove) {
|
|
249
|
+
if (toRemove.deletedAt) return;
|
|
250
|
+
this._asc.remove(toRemove);
|
|
251
|
+
this._desc.remove(toRemove);
|
|
252
|
+
}
|
|
253
|
+
if (!thread.deletedAt) {
|
|
254
|
+
this._asc.add(thread);
|
|
255
|
+
this._desc.add(thread);
|
|
256
|
+
}
|
|
257
|
+
this._byId.set(id, thread);
|
|
258
|
+
this.touch();
|
|
259
|
+
}
|
|
260
|
+
/** Like .upsert(), except it won't update if a thread by this ID already exists. */
|
|
261
|
+
// TODO Consider renaming this to just .upsert(). I'm not sure if we really
|
|
262
|
+
// TODO need the raw .upsert(). Would be nice if this behavior was the default.
|
|
263
|
+
upsertIfNewer(thread) {
|
|
264
|
+
const existing = this.get(thread.id);
|
|
265
|
+
if (!existing || thread.updatedAt >= existing.updatedAt) {
|
|
266
|
+
this.upsert(thread);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Marks a thread as deleted. It will no longer pop up in .findMany()
|
|
271
|
+
* queries, but it can still be accessed via `.getEvenIfDeleted()`.
|
|
272
|
+
*/
|
|
273
|
+
delete(threadId, deletedAt) {
|
|
274
|
+
const existing = this._byId.get(threadId);
|
|
275
|
+
if (existing && !existing.deletedAt) {
|
|
276
|
+
this.upsert({ ...existing, deletedAt, updatedAt: deletedAt });
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Returns all threads matching a given roomId and query. If roomId is not
|
|
281
|
+
* specified, it will return all threads matching the query, across all
|
|
282
|
+
* rooms.
|
|
283
|
+
*
|
|
284
|
+
* Returns the results in the requested order. Please note:
|
|
285
|
+
* 'asc' means by createdAt ASC
|
|
286
|
+
* 'desc' means by updatedAt DESC
|
|
287
|
+
*
|
|
288
|
+
* Will never return deleted threads in the result.
|
|
289
|
+
*/
|
|
290
|
+
findMany(roomId, query, direction) {
|
|
291
|
+
const index = direction === "desc" ? this._desc : this._asc;
|
|
292
|
+
const crit = [];
|
|
293
|
+
if (roomId !== void 0) {
|
|
294
|
+
crit.push((t) => t.roomId === roomId);
|
|
295
|
+
}
|
|
296
|
+
crit.push(makeThreadsFilter(query));
|
|
297
|
+
return Array.from(index.filter((t) => crit.every((pred) => pred(t))));
|
|
298
|
+
}
|
|
299
|
+
//
|
|
300
|
+
// Private APIs
|
|
301
|
+
//
|
|
302
|
+
touch() {
|
|
303
|
+
++this._version;
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
|
|
91
307
|
// src/umbrella-store.ts
|
|
92
|
-
var ASYNC_OK = Object.freeze({ isLoading: false, data: void 0 });
|
|
93
308
|
function makeRoomThreadsQueryKey(roomId, query) {
|
|
94
309
|
return `${roomId}-${stringify(query ?? {})}`;
|
|
95
310
|
}
|
|
@@ -102,19 +317,6 @@ function makeNotificationSettingsQueryKey(roomId) {
|
|
|
102
317
|
function makeVersionsQueryKey(roomId) {
|
|
103
318
|
return `${roomId}-VERSIONS`;
|
|
104
319
|
}
|
|
105
|
-
function selectThreads(state, options) {
|
|
106
|
-
let threads = state.threads;
|
|
107
|
-
if (options.roomId !== null) {
|
|
108
|
-
threads = threads.filter((thread) => thread.roomId === options.roomId);
|
|
109
|
-
}
|
|
110
|
-
const query = options.query;
|
|
111
|
-
if (query) {
|
|
112
|
-
threads = threads.filter(makeThreadsFilter(query));
|
|
113
|
-
}
|
|
114
|
-
return threads.sort(
|
|
115
|
-
options.orderBy === "last-update" ? byMostRecentlyUpdated : byFirstCreated
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
320
|
function usify(promise) {
|
|
119
321
|
if ("status" in promise) {
|
|
120
322
|
return promise;
|
|
@@ -133,7 +335,7 @@ function usify(promise) {
|
|
|
133
335
|
);
|
|
134
336
|
return usable;
|
|
135
337
|
}
|
|
136
|
-
var
|
|
338
|
+
var noop2 = Promise.resolve();
|
|
137
339
|
var ASYNC_LOADING = Object.freeze({ isLoading: true });
|
|
138
340
|
var PaginatedResource = class {
|
|
139
341
|
constructor(fetchPage) {
|
|
@@ -174,7 +376,7 @@ var PaginatedResource = class {
|
|
|
174
376
|
fetchMore() {
|
|
175
377
|
const state = this._paginationState;
|
|
176
378
|
if (state?.cursor === null) {
|
|
177
|
-
return
|
|
379
|
+
return noop2;
|
|
178
380
|
}
|
|
179
381
|
if (!this._pendingFetchMore) {
|
|
180
382
|
this._pendingFetchMore = this._fetchMore().finally(() => {
|
|
@@ -237,8 +439,54 @@ var PaginatedResource = class {
|
|
|
237
439
|
return promise;
|
|
238
440
|
}
|
|
239
441
|
};
|
|
442
|
+
var SinglePageResource = class {
|
|
443
|
+
constructor(fetchPage) {
|
|
444
|
+
this._cachedPromise = null;
|
|
445
|
+
this._fetchPage = fetchPage;
|
|
446
|
+
this._eventSource = makeEventSource();
|
|
447
|
+
this.observable = this._eventSource.observable;
|
|
448
|
+
autobind(this);
|
|
449
|
+
}
|
|
450
|
+
get() {
|
|
451
|
+
const usable = this._cachedPromise;
|
|
452
|
+
if (usable === null || usable.status === "pending") {
|
|
453
|
+
return ASYNC_LOADING;
|
|
454
|
+
}
|
|
455
|
+
if (usable.status === "rejected") {
|
|
456
|
+
return { isLoading: false, error: usable.reason };
|
|
457
|
+
}
|
|
458
|
+
return {
|
|
459
|
+
isLoading: false,
|
|
460
|
+
data: void 0
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
waitUntilLoaded() {
|
|
464
|
+
if (this._cachedPromise) {
|
|
465
|
+
return this._cachedPromise;
|
|
466
|
+
}
|
|
467
|
+
const initialFetcher = autoRetry(
|
|
468
|
+
() => this._fetchPage(),
|
|
469
|
+
5,
|
|
470
|
+
[5e3, 5e3, 1e4, 15e3]
|
|
471
|
+
);
|
|
472
|
+
const promise = usify(initialFetcher);
|
|
473
|
+
promise.then(
|
|
474
|
+
() => this._eventSource.notify(),
|
|
475
|
+
() => {
|
|
476
|
+
this._eventSource.notify();
|
|
477
|
+
setTimeout(() => {
|
|
478
|
+
this._cachedPromise = null;
|
|
479
|
+
this._eventSource.notify();
|
|
480
|
+
}, 5e3);
|
|
481
|
+
}
|
|
482
|
+
);
|
|
483
|
+
this._cachedPromise = promise;
|
|
484
|
+
return promise;
|
|
485
|
+
}
|
|
486
|
+
};
|
|
240
487
|
var UmbrellaStore = class {
|
|
241
488
|
constructor(client) {
|
|
489
|
+
this._prevVersion = -1;
|
|
242
490
|
this._prevState = null;
|
|
243
491
|
this._stateCached = null;
|
|
244
492
|
// Notifications
|
|
@@ -249,16 +497,16 @@ var UmbrellaStore = class {
|
|
|
249
497
|
// User Threads
|
|
250
498
|
this._userThreadsLastRequestedAt = null;
|
|
251
499
|
this._userThreads = /* @__PURE__ */ new Map();
|
|
500
|
+
// Room versions
|
|
501
|
+
this._roomVersions = /* @__PURE__ */ new Map();
|
|
502
|
+
this._roomVersionsLastRequestedAtByRoom = /* @__PURE__ */ new Map();
|
|
503
|
+
// Room notification settings
|
|
504
|
+
this._roomNotificationSettings = /* @__PURE__ */ new Map();
|
|
505
|
+
this._client = client[kInternal].as();
|
|
252
506
|
const inboxFetcher = async (cursor) => {
|
|
253
|
-
|
|
254
|
-
throw new StopRetrying(
|
|
255
|
-
"Client is required in order to load threads for the room"
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
const result = await client.getInboxNotifications({ cursor });
|
|
507
|
+
const result = await this._client.getInboxNotifications({ cursor });
|
|
259
508
|
this.updateThreadsAndNotifications(
|
|
260
509
|
result.threads,
|
|
261
|
-
// TODO: Figure out how to remove this casting
|
|
262
510
|
result.inboxNotifications
|
|
263
511
|
);
|
|
264
512
|
if (this._notificationsLastRequestedAt === null) {
|
|
@@ -267,7 +515,6 @@ var UmbrellaStore = class {
|
|
|
267
515
|
const nextCursor = result.nextCursor;
|
|
268
516
|
return nextCursor;
|
|
269
517
|
};
|
|
270
|
-
this._client = client;
|
|
271
518
|
this._notifications = new PaginatedResource(inboxFetcher);
|
|
272
519
|
this._notifications.observable.subscribe(
|
|
273
520
|
() => (
|
|
@@ -276,10 +523,8 @@ var UmbrellaStore = class {
|
|
|
276
523
|
this._store.set((store) => ({ ...store }))
|
|
277
524
|
)
|
|
278
525
|
);
|
|
526
|
+
this._rawThreadsDB = new ThreadDB();
|
|
279
527
|
this._store = createStore({
|
|
280
|
-
rawThreadsById: {},
|
|
281
|
-
queries3: {},
|
|
282
|
-
queries4: {},
|
|
283
528
|
optimisticUpdates: [],
|
|
284
529
|
notificationsById: {},
|
|
285
530
|
settingsByRoomId: {},
|
|
@@ -289,9 +534,11 @@ var UmbrellaStore = class {
|
|
|
289
534
|
}
|
|
290
535
|
get() {
|
|
291
536
|
const rawState = this._store.get();
|
|
292
|
-
if (this.
|
|
537
|
+
if (this._prevVersion !== this._rawThreadsDB.version || // Note: Version check is only needed temporarily, until we can get rid of the Zustand-like update model
|
|
538
|
+
this._prevState !== rawState || this._stateCached === null) {
|
|
539
|
+
this._stateCached = internalToExternalState(rawState, this._rawThreadsDB);
|
|
293
540
|
this._prevState = rawState;
|
|
294
|
-
this.
|
|
541
|
+
this._prevVersion = this._rawThreadsDB.version;
|
|
295
542
|
}
|
|
296
543
|
return this._stateCached;
|
|
297
544
|
}
|
|
@@ -306,8 +553,7 @@ var UmbrellaStore = class {
|
|
|
306
553
|
* then it will return the threads that match that provided query and room id.
|
|
307
554
|
*
|
|
308
555
|
*/
|
|
309
|
-
|
|
310
|
-
getRoomThreadsAsync(roomId, query) {
|
|
556
|
+
getRoomThreadsLoadingState(roomId, query) {
|
|
311
557
|
const queryKey = makeRoomThreadsQueryKey(roomId, query);
|
|
312
558
|
const paginatedResource = this._roomThreads.get(queryKey);
|
|
313
559
|
if (paginatedResource === void 0) {
|
|
@@ -317,11 +563,11 @@ var UmbrellaStore = class {
|
|
|
317
563
|
if (asyncResult.isLoading || asyncResult.error) {
|
|
318
564
|
return asyncResult;
|
|
319
565
|
}
|
|
320
|
-
const threads =
|
|
566
|
+
const threads = this.getFullState().threadsDB.findMany(
|
|
321
567
|
roomId,
|
|
322
|
-
query,
|
|
323
|
-
|
|
324
|
-
|
|
568
|
+
query ?? {},
|
|
569
|
+
"asc"
|
|
570
|
+
);
|
|
325
571
|
const page = asyncResult.data;
|
|
326
572
|
return {
|
|
327
573
|
isLoading: false,
|
|
@@ -332,8 +578,7 @@ var UmbrellaStore = class {
|
|
|
332
578
|
fetchMore: page.fetchMore
|
|
333
579
|
};
|
|
334
580
|
}
|
|
335
|
-
|
|
336
|
-
getUserThreadsAsync(query) {
|
|
581
|
+
getUserThreadsLoadingState(query) {
|
|
337
582
|
const queryKey = makeUserThreadsQueryKey(query);
|
|
338
583
|
const paginatedResource = this._userThreads.get(queryKey);
|
|
339
584
|
if (paginatedResource === void 0) {
|
|
@@ -343,12 +588,12 @@ var UmbrellaStore = class {
|
|
|
343
588
|
if (asyncResult.isLoading || asyncResult.error) {
|
|
344
589
|
return asyncResult;
|
|
345
590
|
}
|
|
346
|
-
const threads =
|
|
347
|
-
|
|
591
|
+
const threads = this.getFullState().threadsDB.findMany(
|
|
592
|
+
void 0,
|
|
348
593
|
// Do _not_ filter by roomId
|
|
349
|
-
query,
|
|
350
|
-
|
|
351
|
-
|
|
594
|
+
query ?? {},
|
|
595
|
+
"desc"
|
|
596
|
+
);
|
|
352
597
|
const page = asyncResult.data;
|
|
353
598
|
return {
|
|
354
599
|
isLoading: false,
|
|
@@ -360,8 +605,7 @@ var UmbrellaStore = class {
|
|
|
360
605
|
};
|
|
361
606
|
}
|
|
362
607
|
// NOTE: This will read the async result, but WILL NOT start loading at the moment!
|
|
363
|
-
|
|
364
|
-
getInboxNotificationsAsync() {
|
|
608
|
+
getInboxNotificationsLoadingState() {
|
|
365
609
|
const asyncResult = this._notifications.get();
|
|
366
610
|
if (asyncResult.isLoading || asyncResult.error) {
|
|
367
611
|
return asyncResult;
|
|
@@ -369,7 +613,7 @@ var UmbrellaStore = class {
|
|
|
369
613
|
const page = asyncResult.data;
|
|
370
614
|
return {
|
|
371
615
|
isLoading: false,
|
|
372
|
-
inboxNotifications: this.getFullState().
|
|
616
|
+
inboxNotifications: this.getFullState().cleanedNotifications,
|
|
373
617
|
hasFetchedAll: page.hasFetchedAll,
|
|
374
618
|
isFetchingMore: page.isFetchingMore,
|
|
375
619
|
fetchMoreError: page.fetchMoreError,
|
|
@@ -377,32 +621,34 @@ var UmbrellaStore = class {
|
|
|
377
621
|
};
|
|
378
622
|
}
|
|
379
623
|
// NOTE: This will read the async result, but WILL NOT start loading at the moment!
|
|
380
|
-
|
|
381
|
-
const
|
|
382
|
-
const
|
|
383
|
-
if (
|
|
624
|
+
getNotificationSettingsLoadingState(roomId) {
|
|
625
|
+
const queryKey = makeNotificationSettingsQueryKey(roomId);
|
|
626
|
+
const resource = this._roomNotificationSettings.get(queryKey);
|
|
627
|
+
if (resource === void 0) {
|
|
384
628
|
return ASYNC_LOADING;
|
|
385
629
|
}
|
|
386
|
-
|
|
387
|
-
|
|
630
|
+
const asyncResult = resource.get();
|
|
631
|
+
if (asyncResult.isLoading || asyncResult.error) {
|
|
632
|
+
return asyncResult;
|
|
388
633
|
}
|
|
389
634
|
return {
|
|
390
635
|
isLoading: false,
|
|
391
|
-
settings: nn(
|
|
636
|
+
settings: nn(this.get().settingsByRoomId[roomId])
|
|
392
637
|
};
|
|
393
638
|
}
|
|
394
|
-
|
|
395
|
-
const
|
|
396
|
-
const
|
|
397
|
-
if (
|
|
639
|
+
getRoomVersionsLoadingState(roomId) {
|
|
640
|
+
const queryKey = makeVersionsQueryKey(roomId);
|
|
641
|
+
const resource = this._roomVersions.get(queryKey);
|
|
642
|
+
if (resource === void 0) {
|
|
398
643
|
return ASYNC_LOADING;
|
|
399
644
|
}
|
|
400
|
-
|
|
401
|
-
|
|
645
|
+
const asyncResult = resource.get();
|
|
646
|
+
if (asyncResult.isLoading || asyncResult.error) {
|
|
647
|
+
return asyncResult;
|
|
402
648
|
}
|
|
403
649
|
return {
|
|
404
650
|
isLoading: false,
|
|
405
|
-
versions:
|
|
651
|
+
versions: Object.values(this.get().versionsByRoomId[roomId] ?? {})
|
|
406
652
|
};
|
|
407
653
|
}
|
|
408
654
|
/**
|
|
@@ -414,33 +660,14 @@ var UmbrellaStore = class {
|
|
|
414
660
|
subscribe(callback) {
|
|
415
661
|
return this._store.subscribe(callback);
|
|
416
662
|
}
|
|
417
|
-
/**
|
|
418
|
-
* @private Only used by the E2E test suite.
|
|
419
|
-
*/
|
|
420
|
-
_subscribeOptimisticUpdates(callback) {
|
|
421
|
-
return this.subscribe(callback);
|
|
422
|
-
}
|
|
423
|
-
subscribeThreads(callback) {
|
|
424
|
-
return this.subscribe(callback);
|
|
425
|
-
}
|
|
426
|
-
subscribeUserThreads(callback) {
|
|
427
|
-
return this.subscribe(callback);
|
|
428
|
-
}
|
|
429
|
-
subscribeThreadsOrInboxNotifications(callback) {
|
|
430
|
-
return this.subscribe(callback);
|
|
431
|
-
}
|
|
432
|
-
subscribeNotificationSettings(callback) {
|
|
433
|
-
return this.subscribe(callback);
|
|
434
|
-
}
|
|
435
|
-
subscribeVersions(callback) {
|
|
436
|
-
return this.subscribe(callback);
|
|
437
|
-
}
|
|
438
663
|
// Direct low-level cache mutations ------------------------------------------------- {{{
|
|
439
|
-
|
|
440
|
-
this.
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
664
|
+
mutateThreadsDB(mutate) {
|
|
665
|
+
const db = this._rawThreadsDB;
|
|
666
|
+
const old = db.version;
|
|
667
|
+
mutate(db);
|
|
668
|
+
if (old !== db.version) {
|
|
669
|
+
this._store.set((state) => ({ ...state }));
|
|
670
|
+
}
|
|
444
671
|
}
|
|
445
672
|
updateInboxNotificationsCache(mapFn) {
|
|
446
673
|
this._store.set((state) => {
|
|
@@ -457,32 +684,23 @@ var UmbrellaStore = class {
|
|
|
457
684
|
}
|
|
458
685
|
}));
|
|
459
686
|
}
|
|
460
|
-
|
|
461
|
-
this._store.set((state) =>
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
}
|
|
476
|
-
})
|
|
477
|
-
}
|
|
478
|
-
setQuery4State(queryKey, queryState) {
|
|
479
|
-
this._store.set((state) => ({
|
|
480
|
-
...state,
|
|
481
|
-
queries4: {
|
|
482
|
-
...state.queries4,
|
|
483
|
-
[queryKey]: queryState
|
|
484
|
-
}
|
|
485
|
-
}));
|
|
687
|
+
updateRoomVersions(roomId, versions) {
|
|
688
|
+
this._store.set((state) => {
|
|
689
|
+
const versionsById = Object.fromEntries(
|
|
690
|
+
versions.map((version2) => [version2.id, version2])
|
|
691
|
+
);
|
|
692
|
+
return {
|
|
693
|
+
...state,
|
|
694
|
+
versionsByRoomId: {
|
|
695
|
+
...state.versionsByRoomId,
|
|
696
|
+
[roomId]: {
|
|
697
|
+
// Merge with existing versions for the room, or start with an empty object
|
|
698
|
+
...state.versionsByRoomId[roomId] ?? {},
|
|
699
|
+
...versionsById
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
});
|
|
486
704
|
}
|
|
487
705
|
updateOptimisticUpdatesCache(mapFn) {
|
|
488
706
|
this._store.set((state) => ({
|
|
@@ -557,7 +775,7 @@ var UmbrellaStore = class {
|
|
|
557
775
|
createThread(optimisticUpdateId, thread) {
|
|
558
776
|
this._store.batch(() => {
|
|
559
777
|
this.removeOptimisticUpdate(optimisticUpdateId);
|
|
560
|
-
this.
|
|
778
|
+
this.mutateThreadsDB((db) => db.upsert(thread));
|
|
561
779
|
});
|
|
562
780
|
}
|
|
563
781
|
/**
|
|
@@ -575,18 +793,11 @@ var UmbrellaStore = class {
|
|
|
575
793
|
if (optimisticUpdateId !== null) {
|
|
576
794
|
this.removeOptimisticUpdate(optimisticUpdateId);
|
|
577
795
|
}
|
|
578
|
-
this.
|
|
579
|
-
const existing =
|
|
580
|
-
if (!existing)
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
if (existing.deletedAt !== void 0) {
|
|
584
|
-
return cache;
|
|
585
|
-
}
|
|
586
|
-
if (!!updatedAt && existing.updatedAt > updatedAt) {
|
|
587
|
-
return cache;
|
|
588
|
-
}
|
|
589
|
-
return { ...cache, [threadId]: callback(existing) };
|
|
796
|
+
this.mutateThreadsDB((db) => {
|
|
797
|
+
const existing = db.get(threadId);
|
|
798
|
+
if (!existing) return;
|
|
799
|
+
if (!!updatedAt && existing.updatedAt > updatedAt) return;
|
|
800
|
+
db.upsert(callback(existing));
|
|
590
801
|
});
|
|
591
802
|
});
|
|
592
803
|
}
|
|
@@ -637,14 +848,13 @@ var UmbrellaStore = class {
|
|
|
637
848
|
createComment(newComment, optimisticUpdateId) {
|
|
638
849
|
this._store.batch(() => {
|
|
639
850
|
this.removeOptimisticUpdate(optimisticUpdateId);
|
|
640
|
-
const existingThread = this.
|
|
851
|
+
const existingThread = this._rawThreadsDB.get(newComment.threadId);
|
|
641
852
|
if (!existingThread) {
|
|
642
853
|
return;
|
|
643
854
|
}
|
|
644
|
-
this.
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
}));
|
|
855
|
+
this.mutateThreadsDB(
|
|
856
|
+
(db) => db.upsert(applyUpsertComment(existingThread, newComment))
|
|
857
|
+
);
|
|
648
858
|
this.updateInboxNotificationsCache((cache) => {
|
|
649
859
|
const existingNotification = Object.values(cache).find(
|
|
650
860
|
(notification) => notification.kind === "thread" && notification.threadId === newComment.threadId
|
|
@@ -680,10 +890,7 @@ var UmbrellaStore = class {
|
|
|
680
890
|
}
|
|
681
891
|
updateThreadAndNotification(thread, inboxNotification) {
|
|
682
892
|
this._store.batch(() => {
|
|
683
|
-
this.
|
|
684
|
-
const existingThread = cache[thread.id];
|
|
685
|
-
return existingThread === void 0 || isMoreRecentlyUpdated(thread, existingThread) ? { ...cache, [thread.id]: thread } : cache;
|
|
686
|
-
});
|
|
893
|
+
this.mutateThreadsDB((db) => db.upsertIfNewer(thread));
|
|
687
894
|
if (inboxNotification !== void 0) {
|
|
688
895
|
this.updateInboxNotificationsCache((cache) => ({
|
|
689
896
|
...cache,
|
|
@@ -694,11 +901,8 @@ var UmbrellaStore = class {
|
|
|
694
901
|
}
|
|
695
902
|
updateThreadsAndNotifications(threads, inboxNotifications, deletedThreads = [], deletedInboxNotifications = []) {
|
|
696
903
|
this._store.batch(() => {
|
|
697
|
-
this.
|
|
698
|
-
(
|
|
699
|
-
newThreads: threads,
|
|
700
|
-
deletedThreads
|
|
701
|
-
})
|
|
904
|
+
this.mutateThreadsDB(
|
|
905
|
+
(db) => applyThreadDeltaUpdates(db, { newThreads: threads, deletedThreads })
|
|
702
906
|
);
|
|
703
907
|
this.updateInboxNotificationsCache(
|
|
704
908
|
(cache) => applyNotificationsUpdates(cache, {
|
|
@@ -712,28 +916,12 @@ var UmbrellaStore = class {
|
|
|
712
916
|
* Updates existing notification setting for a room with a new value,
|
|
713
917
|
* replacing the corresponding optimistic update.
|
|
714
918
|
*/
|
|
715
|
-
|
|
716
|
-
updateRoomInboxNotificationSettings2(roomId, optimisticUpdateId, settings) {
|
|
919
|
+
updateRoomNotificationSettings_confirmOptimisticUpdate(roomId, optimisticUpdateId, settings) {
|
|
717
920
|
this._store.batch(() => {
|
|
718
921
|
this.removeOptimisticUpdate(optimisticUpdateId);
|
|
719
922
|
this.setNotificationSettings(roomId, settings);
|
|
720
923
|
});
|
|
721
924
|
}
|
|
722
|
-
// XXXX Rename this helper method
|
|
723
|
-
updateRoomInboxNotificationSettings(roomId, settings, queryKey) {
|
|
724
|
-
this._store.batch(() => {
|
|
725
|
-
this.setQuery3OK(queryKey);
|
|
726
|
-
this.setNotificationSettings(roomId, settings);
|
|
727
|
-
});
|
|
728
|
-
}
|
|
729
|
-
updateRoomVersions(roomId, versions, queryKey) {
|
|
730
|
-
this._store.batch(() => {
|
|
731
|
-
this.setVersions(roomId, versions);
|
|
732
|
-
if (queryKey !== void 0) {
|
|
733
|
-
this.setQuery4OK(queryKey);
|
|
734
|
-
}
|
|
735
|
-
});
|
|
736
|
-
}
|
|
737
925
|
addOptimisticUpdate(optimisticUpdate) {
|
|
738
926
|
const id = nanoid();
|
|
739
927
|
const newUpdate = { ...optimisticUpdate, id };
|
|
@@ -745,36 +933,15 @@ var UmbrellaStore = class {
|
|
|
745
933
|
(cache) => cache.filter((ou) => ou.id !== optimisticUpdateId)
|
|
746
934
|
);
|
|
747
935
|
}
|
|
748
|
-
|
|
749
|
-
setQuery3Loading(queryKey) {
|
|
750
|
-
this.setQuery3State(queryKey, ASYNC_LOADING);
|
|
751
|
-
}
|
|
752
|
-
setQuery3OK(queryKey) {
|
|
753
|
-
this.setQuery3State(queryKey, ASYNC_OK);
|
|
754
|
-
}
|
|
755
|
-
setQuery3Error(queryKey, error) {
|
|
756
|
-
this.setQuery3State(queryKey, { isLoading: false, error });
|
|
757
|
-
}
|
|
758
|
-
// Query 4
|
|
759
|
-
setQuery4Loading(queryKey) {
|
|
760
|
-
this.setQuery4State(queryKey, ASYNC_LOADING);
|
|
761
|
-
}
|
|
762
|
-
setQuery4OK(queryKey) {
|
|
763
|
-
this.setQuery4State(queryKey, ASYNC_OK);
|
|
764
|
-
}
|
|
765
|
-
setQuery4Error(queryKey, error) {
|
|
766
|
-
this.setQuery4State(queryKey, { isLoading: false, error });
|
|
767
|
-
}
|
|
768
|
-
async fetchNotificationsDeltaUpdate() {
|
|
936
|
+
async fetchNotificationsDeltaUpdate(signal) {
|
|
769
937
|
const lastRequestedAt = this._notificationsLastRequestedAt;
|
|
770
938
|
if (lastRequestedAt === null) {
|
|
771
939
|
return;
|
|
772
940
|
}
|
|
773
|
-
const
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
);
|
|
777
|
-
const result = await client.getInboxNotificationsSince(lastRequestedAt);
|
|
941
|
+
const result = await this._client.getInboxNotificationsSince({
|
|
942
|
+
since: lastRequestedAt,
|
|
943
|
+
signal
|
|
944
|
+
});
|
|
778
945
|
if (lastRequestedAt < result.requestedAt) {
|
|
779
946
|
this._notificationsLastRequestedAt = result.requestedAt;
|
|
780
947
|
}
|
|
@@ -790,21 +957,13 @@ var UmbrellaStore = class {
|
|
|
790
957
|
}
|
|
791
958
|
waitUntilRoomThreadsLoaded(roomId, query) {
|
|
792
959
|
const threadsFetcher = async (cursor) => {
|
|
793
|
-
if (this._client === void 0) {
|
|
794
|
-
throw new StopRetrying(
|
|
795
|
-
"Client is required in order to load threads for the room"
|
|
796
|
-
);
|
|
797
|
-
}
|
|
798
960
|
const room = this._client.getRoom(roomId);
|
|
799
961
|
if (room === null) {
|
|
800
|
-
throw new
|
|
801
|
-
`Room with id ${roomId} is not available on client`
|
|
802
|
-
);
|
|
962
|
+
throw new HttpError(`Room '${roomId}' is not available on client`, 479);
|
|
803
963
|
}
|
|
804
964
|
const result = await room.getThreads({ cursor, query });
|
|
805
965
|
this.updateThreadsAndNotifications(
|
|
806
966
|
result.threads,
|
|
807
|
-
// TODO: Figure out how to remove this casting
|
|
808
967
|
result.inboxNotifications
|
|
809
968
|
);
|
|
810
969
|
const lastRequestedAt = this._roomThreadsLastRequestedAtByRoom.get(roomId);
|
|
@@ -828,21 +987,18 @@ var UmbrellaStore = class {
|
|
|
828
987
|
this._roomThreads.set(queryKey, paginatedResource);
|
|
829
988
|
return paginatedResource.waitUntilLoaded();
|
|
830
989
|
}
|
|
831
|
-
async fetchRoomThreadsDeltaUpdate(roomId) {
|
|
990
|
+
async fetchRoomThreadsDeltaUpdate(roomId, signal) {
|
|
832
991
|
const lastRequestedAt = this._roomThreadsLastRequestedAtByRoom.get(roomId);
|
|
833
992
|
if (lastRequestedAt === void 0) {
|
|
834
993
|
return;
|
|
835
994
|
}
|
|
836
|
-
const client = nn(
|
|
837
|
-
this._client,
|
|
838
|
-
"Client is required in order to load notifications for the room"
|
|
839
|
-
);
|
|
840
995
|
const room = nn(
|
|
841
|
-
|
|
996
|
+
this._client.getRoom(roomId),
|
|
842
997
|
`Room with id ${roomId} is not available on client`
|
|
843
998
|
);
|
|
844
999
|
const updates = await room.getThreadsSince({
|
|
845
|
-
since: lastRequestedAt
|
|
1000
|
+
since: lastRequestedAt,
|
|
1001
|
+
signal
|
|
846
1002
|
});
|
|
847
1003
|
this.updateThreadsAndNotifications(
|
|
848
1004
|
updates.threads.updated,
|
|
@@ -857,18 +1013,12 @@ var UmbrellaStore = class {
|
|
|
857
1013
|
waitUntilUserThreadsLoaded(query) {
|
|
858
1014
|
const queryKey = makeUserThreadsQueryKey(query);
|
|
859
1015
|
const threadsFetcher = async (cursor) => {
|
|
860
|
-
if (this._client === void 0) {
|
|
861
|
-
throw new StopRetrying(
|
|
862
|
-
"Client is required in order to load threads for the room"
|
|
863
|
-
);
|
|
864
|
-
}
|
|
865
1016
|
const result = await this._client[kInternal].getUserThreads_experimental({
|
|
866
1017
|
cursor,
|
|
867
1018
|
query
|
|
868
1019
|
});
|
|
869
1020
|
this.updateThreadsAndNotifications(
|
|
870
1021
|
result.threads,
|
|
871
|
-
// TODO: Figure out how to remove this casting
|
|
872
1022
|
result.inboxNotifications
|
|
873
1023
|
);
|
|
874
1024
|
if (this._userThreadsLastRequestedAt === null) {
|
|
@@ -890,17 +1040,14 @@ var UmbrellaStore = class {
|
|
|
890
1040
|
this._userThreads.set(queryKey, paginatedResource);
|
|
891
1041
|
return paginatedResource.waitUntilLoaded();
|
|
892
1042
|
}
|
|
893
|
-
async fetchUserThreadsDeltaUpdate() {
|
|
1043
|
+
async fetchUserThreadsDeltaUpdate(signal) {
|
|
894
1044
|
const lastRequestedAt = this._userThreadsLastRequestedAt;
|
|
895
1045
|
if (lastRequestedAt === null) {
|
|
896
1046
|
return;
|
|
897
1047
|
}
|
|
898
|
-
const
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
);
|
|
902
|
-
const result = await client[kInternal].getUserThreadsSince_experimental({
|
|
903
|
-
since: lastRequestedAt
|
|
1048
|
+
const result = await this._client[kInternal].getUserThreadsSince_experimental({
|
|
1049
|
+
since: lastRequestedAt,
|
|
1050
|
+
signal
|
|
904
1051
|
});
|
|
905
1052
|
if (lastRequestedAt < result.requestedAt) {
|
|
906
1053
|
this._notificationsLastRequestedAt = result.requestedAt;
|
|
@@ -912,77 +1059,138 @@ var UmbrellaStore = class {
|
|
|
912
1059
|
result.inboxNotifications.deleted
|
|
913
1060
|
);
|
|
914
1061
|
}
|
|
1062
|
+
waitUntilRoomVersionsLoaded(roomId) {
|
|
1063
|
+
const queryKey = makeVersionsQueryKey(roomId);
|
|
1064
|
+
let resource = this._roomVersions.get(queryKey);
|
|
1065
|
+
if (resource === void 0) {
|
|
1066
|
+
const versionsFetcher = async () => {
|
|
1067
|
+
const room = this._client.getRoom(roomId);
|
|
1068
|
+
if (room === null) {
|
|
1069
|
+
throw new HttpError(
|
|
1070
|
+
`Room '${roomId}' is not available on client`,
|
|
1071
|
+
479
|
|
1072
|
+
);
|
|
1073
|
+
}
|
|
1074
|
+
const result = await room[kInternal].listTextVersions();
|
|
1075
|
+
this.updateRoomVersions(roomId, result.versions);
|
|
1076
|
+
const lastRequestedAt = this._roomVersionsLastRequestedAtByRoom.get(roomId);
|
|
1077
|
+
if (lastRequestedAt === void 0 || lastRequestedAt > result.requestedAt) {
|
|
1078
|
+
this._roomVersionsLastRequestedAtByRoom.set(
|
|
1079
|
+
roomId,
|
|
1080
|
+
result.requestedAt
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
resource = new SinglePageResource(versionsFetcher);
|
|
1085
|
+
}
|
|
1086
|
+
resource.observable.subscribe(
|
|
1087
|
+
() => (
|
|
1088
|
+
// Note that the store itself does not change, but it's only vehicle at
|
|
1089
|
+
// the moment to trigger a re-render, so we'll do a no-op update here.
|
|
1090
|
+
this._store.set((store) => ({ ...store }))
|
|
1091
|
+
)
|
|
1092
|
+
);
|
|
1093
|
+
this._roomVersions.set(queryKey, resource);
|
|
1094
|
+
return resource.waitUntilLoaded();
|
|
1095
|
+
}
|
|
1096
|
+
async fetchRoomVersionsDeltaUpdate(roomId, signal) {
|
|
1097
|
+
const lastRequestedAt = this._roomVersionsLastRequestedAtByRoom.get(roomId);
|
|
1098
|
+
if (lastRequestedAt === void 0) {
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
const room = nn(
|
|
1102
|
+
this._client.getRoom(roomId),
|
|
1103
|
+
`Room with id ${roomId} is not available on client`
|
|
1104
|
+
);
|
|
1105
|
+
const updates = await room[kInternal].listTextVersionsSince({
|
|
1106
|
+
since: lastRequestedAt,
|
|
1107
|
+
signal
|
|
1108
|
+
});
|
|
1109
|
+
this.updateRoomVersions(roomId, updates.versions);
|
|
1110
|
+
if (lastRequestedAt < updates.requestedAt) {
|
|
1111
|
+
this._roomVersionsLastRequestedAtByRoom.set(roomId, updates.requestedAt);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
waitUntilRoomNotificationSettingsLoaded(roomId) {
|
|
1115
|
+
const queryKey = makeNotificationSettingsQueryKey(roomId);
|
|
1116
|
+
let resource = this._roomNotificationSettings.get(queryKey);
|
|
1117
|
+
if (resource === void 0) {
|
|
1118
|
+
const notificationSettingsFetcher = async () => {
|
|
1119
|
+
const room = this._client.getRoom(roomId);
|
|
1120
|
+
if (room === null) {
|
|
1121
|
+
throw new HttpError(
|
|
1122
|
+
`Room '${roomId}' is not available on client`,
|
|
1123
|
+
479
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
const result = await room.getNotificationSettings();
|
|
1127
|
+
this.setNotificationSettings(roomId, result);
|
|
1128
|
+
};
|
|
1129
|
+
resource = new SinglePageResource(notificationSettingsFetcher);
|
|
1130
|
+
}
|
|
1131
|
+
resource.observable.subscribe(
|
|
1132
|
+
() => (
|
|
1133
|
+
// Note that the store itself does not change, but it's only vehicle at
|
|
1134
|
+
// the moment to trigger a re-render, so we'll do a no-op update here.
|
|
1135
|
+
this._store.set((store) => ({ ...store }))
|
|
1136
|
+
)
|
|
1137
|
+
);
|
|
1138
|
+
this._roomNotificationSettings.set(queryKey, resource);
|
|
1139
|
+
return resource.waitUntilLoaded();
|
|
1140
|
+
}
|
|
1141
|
+
async refreshRoomNotificationSettings(roomId, signal) {
|
|
1142
|
+
const room = nn(
|
|
1143
|
+
this._client.getRoom(roomId),
|
|
1144
|
+
`Room with id ${roomId} is not available on client`
|
|
1145
|
+
);
|
|
1146
|
+
const result = await room.getNotificationSettings({ signal });
|
|
1147
|
+
this.setNotificationSettings(roomId, result);
|
|
1148
|
+
}
|
|
915
1149
|
};
|
|
916
|
-
function internalToExternalState(state) {
|
|
1150
|
+
function internalToExternalState(state, rawThreadsDB) {
|
|
1151
|
+
const threadsDB = rawThreadsDB.clone();
|
|
917
1152
|
const computed = {
|
|
918
|
-
threadsById: { ...state.rawThreadsById },
|
|
919
1153
|
notificationsById: { ...state.notificationsById },
|
|
920
1154
|
settingsByRoomId: { ...state.settingsByRoomId }
|
|
921
1155
|
};
|
|
922
1156
|
for (const optimisticUpdate of state.optimisticUpdates) {
|
|
923
1157
|
switch (optimisticUpdate.type) {
|
|
924
1158
|
case "create-thread": {
|
|
925
|
-
|
|
1159
|
+
threadsDB.upsert(optimisticUpdate.thread);
|
|
926
1160
|
break;
|
|
927
1161
|
}
|
|
928
1162
|
case "edit-thread-metadata": {
|
|
929
|
-
const thread =
|
|
930
|
-
if (thread === void 0)
|
|
931
|
-
break;
|
|
932
|
-
}
|
|
933
|
-
if (thread.deletedAt !== void 0) {
|
|
934
|
-
break;
|
|
935
|
-
}
|
|
1163
|
+
const thread = threadsDB.get(optimisticUpdate.threadId);
|
|
1164
|
+
if (thread === void 0) break;
|
|
936
1165
|
if (thread.updatedAt > optimisticUpdate.updatedAt) {
|
|
937
1166
|
break;
|
|
938
1167
|
}
|
|
939
|
-
|
|
1168
|
+
threadsDB.upsert({
|
|
940
1169
|
...thread,
|
|
941
1170
|
updatedAt: optimisticUpdate.updatedAt,
|
|
942
1171
|
metadata: {
|
|
943
1172
|
...thread.metadata,
|
|
944
1173
|
...optimisticUpdate.metadata
|
|
945
1174
|
}
|
|
946
|
-
};
|
|
1175
|
+
});
|
|
947
1176
|
break;
|
|
948
1177
|
}
|
|
949
1178
|
case "mark-thread-as-resolved": {
|
|
950
|
-
const thread =
|
|
951
|
-
if (thread === void 0)
|
|
952
|
-
|
|
953
|
-
}
|
|
954
|
-
if (thread.deletedAt !== void 0) {
|
|
955
|
-
break;
|
|
956
|
-
}
|
|
957
|
-
computed.threadsById[thread.id] = {
|
|
958
|
-
...thread,
|
|
959
|
-
resolved: true
|
|
960
|
-
};
|
|
1179
|
+
const thread = threadsDB.get(optimisticUpdate.threadId);
|
|
1180
|
+
if (thread === void 0) break;
|
|
1181
|
+
threadsDB.upsert({ ...thread, resolved: true });
|
|
961
1182
|
break;
|
|
962
|
-
}
|
|
963
|
-
case "mark-thread-as-unresolved": {
|
|
964
|
-
const thread =
|
|
965
|
-
if (thread === void 0)
|
|
966
|
-
|
|
967
|
-
}
|
|
968
|
-
if (thread.deletedAt !== void 0) {
|
|
969
|
-
break;
|
|
970
|
-
}
|
|
971
|
-
computed.threadsById[thread.id] = {
|
|
972
|
-
...thread,
|
|
973
|
-
resolved: false
|
|
974
|
-
};
|
|
1183
|
+
}
|
|
1184
|
+
case "mark-thread-as-unresolved": {
|
|
1185
|
+
const thread = threadsDB.get(optimisticUpdate.threadId);
|
|
1186
|
+
if (thread === void 0) break;
|
|
1187
|
+
threadsDB.upsert({ ...thread, resolved: false });
|
|
975
1188
|
break;
|
|
976
1189
|
}
|
|
977
1190
|
case "create-comment": {
|
|
978
|
-
const thread =
|
|
979
|
-
if (thread === void 0)
|
|
980
|
-
|
|
981
|
-
}
|
|
982
|
-
computed.threadsById[thread.id] = applyUpsertComment(
|
|
983
|
-
thread,
|
|
984
|
-
optimisticUpdate.comment
|
|
985
|
-
);
|
|
1191
|
+
const thread = threadsDB.get(optimisticUpdate.comment.threadId);
|
|
1192
|
+
if (thread === void 0) break;
|
|
1193
|
+
threadsDB.upsert(applyUpsertComment(thread, optimisticUpdate.comment));
|
|
986
1194
|
const inboxNotification = Object.values(
|
|
987
1195
|
computed.notificationsById
|
|
988
1196
|
).find(
|
|
@@ -999,64 +1207,57 @@ function internalToExternalState(state) {
|
|
|
999
1207
|
break;
|
|
1000
1208
|
}
|
|
1001
1209
|
case "edit-comment": {
|
|
1002
|
-
const thread =
|
|
1003
|
-
if (thread === void 0)
|
|
1004
|
-
|
|
1005
|
-
}
|
|
1006
|
-
computed.threadsById[thread.id] = applyUpsertComment(
|
|
1007
|
-
thread,
|
|
1008
|
-
optimisticUpdate.comment
|
|
1009
|
-
);
|
|
1210
|
+
const thread = threadsDB.get(optimisticUpdate.comment.threadId);
|
|
1211
|
+
if (thread === void 0) break;
|
|
1212
|
+
threadsDB.upsert(applyUpsertComment(thread, optimisticUpdate.comment));
|
|
1010
1213
|
break;
|
|
1011
1214
|
}
|
|
1012
1215
|
case "delete-comment": {
|
|
1013
|
-
const thread =
|
|
1014
|
-
if (thread === void 0)
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1216
|
+
const thread = threadsDB.get(optimisticUpdate.threadId);
|
|
1217
|
+
if (thread === void 0) break;
|
|
1218
|
+
threadsDB.upsert(
|
|
1219
|
+
applyDeleteComment(
|
|
1220
|
+
thread,
|
|
1221
|
+
optimisticUpdate.commentId,
|
|
1222
|
+
optimisticUpdate.deletedAt
|
|
1223
|
+
)
|
|
1021
1224
|
);
|
|
1022
1225
|
break;
|
|
1023
1226
|
}
|
|
1024
1227
|
case "delete-thread": {
|
|
1025
|
-
const thread =
|
|
1026
|
-
if (thread === void 0)
|
|
1027
|
-
|
|
1028
|
-
}
|
|
1029
|
-
computed.threadsById[optimisticUpdate.threadId] = {
|
|
1228
|
+
const thread = threadsDB.get(optimisticUpdate.threadId);
|
|
1229
|
+
if (thread === void 0) break;
|
|
1230
|
+
threadsDB.upsert({
|
|
1030
1231
|
...thread,
|
|
1031
1232
|
deletedAt: optimisticUpdate.deletedAt,
|
|
1032
1233
|
updatedAt: optimisticUpdate.deletedAt,
|
|
1033
1234
|
comments: []
|
|
1034
|
-
};
|
|
1235
|
+
});
|
|
1035
1236
|
break;
|
|
1036
1237
|
}
|
|
1037
1238
|
case "add-reaction": {
|
|
1038
|
-
const thread =
|
|
1039
|
-
if (thread === void 0)
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1239
|
+
const thread = threadsDB.get(optimisticUpdate.threadId);
|
|
1240
|
+
if (thread === void 0) break;
|
|
1241
|
+
threadsDB.upsert(
|
|
1242
|
+
applyAddReaction(
|
|
1243
|
+
thread,
|
|
1244
|
+
optimisticUpdate.commentId,
|
|
1245
|
+
optimisticUpdate.reaction
|
|
1246
|
+
)
|
|
1046
1247
|
);
|
|
1047
1248
|
break;
|
|
1048
1249
|
}
|
|
1049
1250
|
case "remove-reaction": {
|
|
1050
|
-
const thread =
|
|
1051
|
-
if (thread === void 0)
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1251
|
+
const thread = threadsDB.get(optimisticUpdate.threadId);
|
|
1252
|
+
if (thread === void 0) break;
|
|
1253
|
+
threadsDB.upsert(
|
|
1254
|
+
applyRemoveReaction(
|
|
1255
|
+
thread,
|
|
1256
|
+
optimisticUpdate.commentId,
|
|
1257
|
+
optimisticUpdate.emoji,
|
|
1258
|
+
optimisticUpdate.userId,
|
|
1259
|
+
optimisticUpdate.removedAt
|
|
1260
|
+
)
|
|
1060
1261
|
);
|
|
1061
1262
|
break;
|
|
1062
1263
|
}
|
|
@@ -1104,51 +1305,27 @@ function internalToExternalState(state) {
|
|
|
1104
1305
|
}
|
|
1105
1306
|
}
|
|
1106
1307
|
}
|
|
1107
|
-
const cleanedThreads = (
|
|
1108
|
-
// Don't expose any soft-deleted threads
|
|
1109
|
-
Object.values(computed.threadsById).filter((thread) => !thread.deletedAt).filter(
|
|
1110
|
-
(thread) => (
|
|
1111
|
-
// Only keep a thread if there is at least one non-deleted comment
|
|
1112
|
-
thread.comments.some((c) => c.deletedAt === void 0)
|
|
1113
|
-
)
|
|
1114
|
-
)
|
|
1115
|
-
);
|
|
1116
1308
|
const cleanedNotifications = (
|
|
1117
1309
|
// Sort so that the most recent notifications are first
|
|
1118
1310
|
Object.values(computed.notificationsById).filter(
|
|
1119
|
-
(ibn) => ibn.kind === "thread" ?
|
|
1311
|
+
(ibn) => ibn.kind === "thread" ? threadsDB.get(ibn.threadId) !== void 0 : true
|
|
1120
1312
|
).sort((a, b) => b.notifiedAt.getTime() - a.notifiedAt.getTime())
|
|
1121
1313
|
);
|
|
1122
1314
|
return {
|
|
1123
|
-
|
|
1315
|
+
cleanedNotifications,
|
|
1124
1316
|
notificationsById: computed.notificationsById,
|
|
1125
1317
|
settingsByRoomId: computed.settingsByRoomId,
|
|
1126
|
-
|
|
1127
|
-
queries4: state.queries4,
|
|
1128
|
-
threads: cleanedThreads,
|
|
1129
|
-
threadsById: computed.threadsById,
|
|
1318
|
+
threadsDB,
|
|
1130
1319
|
versionsByRoomId: state.versionsByRoomId
|
|
1131
1320
|
};
|
|
1132
1321
|
}
|
|
1133
|
-
function
|
|
1134
|
-
|
|
1135
|
-
updates.newThreads.forEach((thread) => {
|
|
1136
|
-
const existingThread = updatedThreads[thread.id];
|
|
1137
|
-
if (existingThread) {
|
|
1138
|
-
if (isMoreRecentlyUpdated(existingThread, thread)) {
|
|
1139
|
-
return;
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
updatedThreads[thread.id] = thread;
|
|
1143
|
-
});
|
|
1322
|
+
function applyThreadDeltaUpdates(db, updates) {
|
|
1323
|
+
updates.newThreads.forEach((thread) => db.upsertIfNewer(thread));
|
|
1144
1324
|
updates.deletedThreads.forEach(({ id, deletedAt }) => {
|
|
1145
|
-
const
|
|
1146
|
-
if (
|
|
1147
|
-
|
|
1148
|
-
existingThread.updatedAt = deletedAt;
|
|
1149
|
-
existingThread.comments = [];
|
|
1325
|
+
const existing = db.getEvenIfDeleted(id);
|
|
1326
|
+
if (!existing) return;
|
|
1327
|
+
db.delete(id, deletedAt);
|
|
1150
1328
|
});
|
|
1151
|
-
return updatedThreads;
|
|
1152
1329
|
}
|
|
1153
1330
|
function applyNotificationsUpdates(existingInboxNotifications, updates) {
|
|
1154
1331
|
const updatedInboxNotifications = { ...existingInboxNotifications };
|
|
@@ -1346,97 +1523,6 @@ function upsertReaction(reactions, reaction) {
|
|
|
1346
1523
|
return reactions;
|
|
1347
1524
|
}
|
|
1348
1525
|
|
|
1349
|
-
// src/liveblocks.tsx
|
|
1350
|
-
import {
|
|
1351
|
-
assert,
|
|
1352
|
-
createClient,
|
|
1353
|
-
kInternal as kInternal2,
|
|
1354
|
-
makePoller,
|
|
1355
|
-
raise,
|
|
1356
|
-
shallow as shallow3
|
|
1357
|
-
} from "@liveblocks/core";
|
|
1358
|
-
import React2, {
|
|
1359
|
-
createContext as createContext2,
|
|
1360
|
-
useCallback as useCallback2,
|
|
1361
|
-
useContext as useContext2,
|
|
1362
|
-
useEffect as useEffect2,
|
|
1363
|
-
useMemo
|
|
1364
|
-
} from "react";
|
|
1365
|
-
import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
|
|
1366
|
-
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector.js";
|
|
1367
|
-
|
|
1368
|
-
// src/lib/shallow2.ts
|
|
1369
|
-
import { isPlainObject as isPlainObject2, shallow } from "@liveblocks/core";
|
|
1370
|
-
function shallow2(a, b) {
|
|
1371
|
-
if (!isPlainObject2(a) || !isPlainObject2(b)) {
|
|
1372
|
-
return shallow(a, b);
|
|
1373
|
-
}
|
|
1374
|
-
const keysA = Object.keys(a);
|
|
1375
|
-
if (keysA.length !== Object.keys(b).length) {
|
|
1376
|
-
return false;
|
|
1377
|
-
}
|
|
1378
|
-
return keysA.every(
|
|
1379
|
-
(key) => Object.prototype.hasOwnProperty.call(b, key) && shallow(a[key], b[key])
|
|
1380
|
-
);
|
|
1381
|
-
}
|
|
1382
|
-
|
|
1383
|
-
// src/lib/use-initial.ts
|
|
1384
|
-
import { useCallback, useReducer } from "react";
|
|
1385
|
-
|
|
1386
|
-
// src/lib/use-latest.ts
|
|
1387
|
-
import { useEffect, useRef } from "react";
|
|
1388
|
-
function useLatest(value) {
|
|
1389
|
-
const ref = useRef(value);
|
|
1390
|
-
useEffect(() => {
|
|
1391
|
-
ref.current = value;
|
|
1392
|
-
}, [value]);
|
|
1393
|
-
return ref;
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
|
-
// src/lib/use-initial.ts
|
|
1397
|
-
var noop2 = (state) => state;
|
|
1398
|
-
function useInitial(value) {
|
|
1399
|
-
return useReducer(noop2, value)[0];
|
|
1400
|
-
}
|
|
1401
|
-
function useInitialUnlessFunction(latestValue) {
|
|
1402
|
-
const frozenValue = useInitial(latestValue);
|
|
1403
|
-
if (typeof frozenValue === "function") {
|
|
1404
|
-
const ref = useLatest(latestValue);
|
|
1405
|
-
return useCallback((...args) => ref.current(...args), [
|
|
1406
|
-
ref
|
|
1407
|
-
]);
|
|
1408
|
-
} else {
|
|
1409
|
-
return frozenValue;
|
|
1410
|
-
}
|
|
1411
|
-
}
|
|
1412
|
-
|
|
1413
|
-
// src/lib/use-polyfill.ts
|
|
1414
|
-
var use = (
|
|
1415
|
-
// React.use ||
|
|
1416
|
-
(promise) => {
|
|
1417
|
-
if (promise.status === "pending") {
|
|
1418
|
-
throw promise;
|
|
1419
|
-
} else if (promise.status === "fulfilled") {
|
|
1420
|
-
return promise.value;
|
|
1421
|
-
} else if (promise.status === "rejected") {
|
|
1422
|
-
throw promise.reason;
|
|
1423
|
-
} else {
|
|
1424
|
-
promise.status = "pending";
|
|
1425
|
-
promise.then(
|
|
1426
|
-
(v) => {
|
|
1427
|
-
promise.status = "fulfilled";
|
|
1428
|
-
promise.value = v;
|
|
1429
|
-
},
|
|
1430
|
-
(e) => {
|
|
1431
|
-
promise.status = "rejected";
|
|
1432
|
-
promise.reason = e;
|
|
1433
|
-
}
|
|
1434
|
-
);
|
|
1435
|
-
throw promise;
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
);
|
|
1439
|
-
|
|
1440
1526
|
// src/liveblocks.tsx
|
|
1441
1527
|
var ClientContext = createContext2(null);
|
|
1442
1528
|
function missingUserError(userId) {
|
|
@@ -1453,7 +1539,6 @@ function identity(x) {
|
|
|
1453
1539
|
var _umbrellaStores = /* @__PURE__ */ new WeakMap();
|
|
1454
1540
|
var _extras = /* @__PURE__ */ new WeakMap();
|
|
1455
1541
|
var _bundles = /* @__PURE__ */ new WeakMap();
|
|
1456
|
-
var POLLING_INTERVAL = 60 * 1e3;
|
|
1457
1542
|
function selectUnreadInboxNotificationsCount(inboxNotifications) {
|
|
1458
1543
|
let count = 0;
|
|
1459
1544
|
for (const notification of inboxNotifications) {
|
|
@@ -1532,61 +1617,36 @@ function getLiveblocksExtrasForClient(client) {
|
|
|
1532
1617
|
}
|
|
1533
1618
|
return extras;
|
|
1534
1619
|
}
|
|
1535
|
-
function makeDeltaPoller_Notifications(store) {
|
|
1536
|
-
const poller = makePoller(async () => {
|
|
1537
|
-
try {
|
|
1538
|
-
await store.waitUntilNotificationsLoaded();
|
|
1539
|
-
await store.fetchNotificationsDeltaUpdate();
|
|
1540
|
-
} catch (err) {
|
|
1541
|
-
console.warn(`Polling new inbox notifications failed: ${String(err)}`);
|
|
1542
|
-
}
|
|
1543
|
-
}, POLLING_INTERVAL);
|
|
1544
|
-
let pollerSubscribers = 0;
|
|
1545
|
-
return () => {
|
|
1546
|
-
pollerSubscribers++;
|
|
1547
|
-
poller.enable(pollerSubscribers > 0);
|
|
1548
|
-
return () => {
|
|
1549
|
-
pollerSubscribers--;
|
|
1550
|
-
poller.enable(pollerSubscribers > 0);
|
|
1551
|
-
};
|
|
1552
|
-
};
|
|
1553
|
-
}
|
|
1554
|
-
function makeDeltaPoller_UserThreads(store) {
|
|
1555
|
-
const poller = makePoller(async () => {
|
|
1556
|
-
try {
|
|
1557
|
-
await store.fetchUserThreadsDeltaUpdate();
|
|
1558
|
-
} catch (err) {
|
|
1559
|
-
console.warn(`Polling new user threads failed: ${String(err)}`);
|
|
1560
|
-
}
|
|
1561
|
-
}, POLLING_INTERVAL);
|
|
1562
|
-
let pollerSubscribers = 0;
|
|
1563
|
-
return () => {
|
|
1564
|
-
pollerSubscribers++;
|
|
1565
|
-
poller.enable(pollerSubscribers > 0);
|
|
1566
|
-
return () => {
|
|
1567
|
-
pollerSubscribers--;
|
|
1568
|
-
poller.enable(pollerSubscribers > 0);
|
|
1569
|
-
};
|
|
1570
|
-
};
|
|
1571
|
-
}
|
|
1572
1620
|
function makeLiveblocksExtrasForClient(client) {
|
|
1573
1621
|
const store = getUmbrellaStoreForClient(client);
|
|
1622
|
+
const notificationsPoller = makePoller(
|
|
1623
|
+
async (signal) => {
|
|
1624
|
+
try {
|
|
1625
|
+
return await store.fetchNotificationsDeltaUpdate(signal);
|
|
1626
|
+
} catch (err) {
|
|
1627
|
+
console.warn(`Polling new inbox notifications failed: ${String(err)}`);
|
|
1628
|
+
throw err;
|
|
1629
|
+
}
|
|
1630
|
+
},
|
|
1631
|
+
config.NOTIFICATIONS_POLL_INTERVAL,
|
|
1632
|
+
{ maxStaleTimeMs: config.NOTIFICATIONS_MAX_STALE_TIME }
|
|
1633
|
+
);
|
|
1634
|
+
const userThreadsPoller = makePoller(
|
|
1635
|
+
async (signal) => {
|
|
1636
|
+
try {
|
|
1637
|
+
return await store.fetchUserThreadsDeltaUpdate(signal);
|
|
1638
|
+
} catch (err) {
|
|
1639
|
+
console.warn(`Polling new user threads failed: ${String(err)}`);
|
|
1640
|
+
throw err;
|
|
1641
|
+
}
|
|
1642
|
+
},
|
|
1643
|
+
config.USER_THREADS_POLL_INTERVAL,
|
|
1644
|
+
{ maxStaleTimeMs: config.USER_THREADS_MAX_STALE_TIME }
|
|
1645
|
+
);
|
|
1574
1646
|
return {
|
|
1575
1647
|
store,
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
* notifications through a stream of delta updates. Call the unsub function
|
|
1579
|
-
* returned to stop this subscription when unmounting. Currently
|
|
1580
|
-
* implemented by a periodic poller.
|
|
1581
|
-
*/
|
|
1582
|
-
subscribeToNotificationsDeltaUpdates: makeDeltaPoller_Notifications(store),
|
|
1583
|
-
/**
|
|
1584
|
-
* Sub/unsub pair to start the process of watching for new user threads
|
|
1585
|
-
* through a stream of delta updates. Call the unsub function returned to
|
|
1586
|
-
* stop this subscription when unmounting. Currently implemented by
|
|
1587
|
-
* a periodic poller.
|
|
1588
|
-
*/
|
|
1589
|
-
subscribeToUserThreadsDeltaUpdates: makeDeltaPoller_UserThreads(store)
|
|
1648
|
+
notificationsPoller,
|
|
1649
|
+
userThreadsPoller
|
|
1590
1650
|
};
|
|
1591
1651
|
}
|
|
1592
1652
|
function makeLiveblocksContextBundle(client) {
|
|
@@ -1627,19 +1687,21 @@ function makeLiveblocksContextBundle(client) {
|
|
|
1627
1687
|
return bundle;
|
|
1628
1688
|
}
|
|
1629
1689
|
function useInboxNotifications_withClient(client, selector, isEqual) {
|
|
1630
|
-
const {
|
|
1631
|
-
store,
|
|
1632
|
-
subscribeToNotificationsDeltaUpdates: subscribeToDeltaUpdates
|
|
1633
|
-
} = getLiveblocksExtrasForClient(client);
|
|
1690
|
+
const { store, notificationsPoller: poller } = getLiveblocksExtrasForClient(client);
|
|
1634
1691
|
useEffect2(() => {
|
|
1635
|
-
void store.waitUntilNotificationsLoaded()
|
|
1636
|
-
});
|
|
1692
|
+
void store.waitUntilNotificationsLoaded();
|
|
1637
1693
|
});
|
|
1638
|
-
useEffect2(
|
|
1694
|
+
useEffect2(() => {
|
|
1695
|
+
poller.inc();
|
|
1696
|
+
poller.pollNowIfStale();
|
|
1697
|
+
return () => {
|
|
1698
|
+
poller.dec();
|
|
1699
|
+
};
|
|
1700
|
+
}, [poller]);
|
|
1639
1701
|
return useSyncExternalStoreWithSelector(
|
|
1640
|
-
store.
|
|
1641
|
-
store.
|
|
1642
|
-
store.
|
|
1702
|
+
store.subscribe,
|
|
1703
|
+
store.getInboxNotificationsLoadingState,
|
|
1704
|
+
store.getInboxNotificationsLoadingState,
|
|
1643
1705
|
selector,
|
|
1644
1706
|
isEqual
|
|
1645
1707
|
);
|
|
@@ -1768,7 +1830,7 @@ function useInboxNotificationThread_withClient(client, inboxNotificationId) {
|
|
|
1768
1830
|
`Inbox notification with ID "${inboxNotificationId}" is not of kind "thread"`
|
|
1769
1831
|
);
|
|
1770
1832
|
}
|
|
1771
|
-
const thread = state.
|
|
1833
|
+
const thread = state.threadsDB.get(inboxNotification.threadId) ?? raise(
|
|
1772
1834
|
`Thread with ID "${inboxNotification.threadId}" not found, this inbox notification might not be of kind "thread"`
|
|
1773
1835
|
);
|
|
1774
1836
|
return thread;
|
|
@@ -1776,7 +1838,7 @@ function useInboxNotificationThread_withClient(client, inboxNotificationId) {
|
|
|
1776
1838
|
[inboxNotificationId]
|
|
1777
1839
|
);
|
|
1778
1840
|
return useSyncExternalStoreWithSelector(
|
|
1779
|
-
store.
|
|
1841
|
+
store.subscribe,
|
|
1780
1842
|
// Re-evaluate if we need to update any time the notification changes over time
|
|
1781
1843
|
getter,
|
|
1782
1844
|
getter,
|
|
@@ -1960,11 +2022,10 @@ function useUserThreads_experimental(options = {
|
|
|
1960
2022
|
}
|
|
1961
2023
|
}) {
|
|
1962
2024
|
const client = useClient();
|
|
1963
|
-
const { store,
|
|
2025
|
+
const { store, userThreadsPoller: poller } = getLiveblocksExtrasForClient(client);
|
|
1964
2026
|
useEffect2(
|
|
1965
2027
|
() => {
|
|
1966
|
-
void store.waitUntilUserThreadsLoaded(options.query)
|
|
1967
|
-
});
|
|
2028
|
+
void store.waitUntilUserThreadsLoaded(options.query);
|
|
1968
2029
|
}
|
|
1969
2030
|
// NOTE: Deliberately *not* using a dependency array here!
|
|
1970
2031
|
//
|
|
@@ -1975,13 +2036,19 @@ function useUserThreads_experimental(options = {
|
|
|
1975
2036
|
// 3. If ever the promise would fail, then after 5 seconds it would reset, and on the very
|
|
1976
2037
|
// *next* render after that, a *new* fetch/promise will get created.
|
|
1977
2038
|
);
|
|
1978
|
-
useEffect2(
|
|
2039
|
+
useEffect2(() => {
|
|
2040
|
+
poller.inc();
|
|
2041
|
+
poller.pollNowIfStale();
|
|
2042
|
+
return () => {
|
|
2043
|
+
poller.dec();
|
|
2044
|
+
};
|
|
2045
|
+
}, [poller]);
|
|
1979
2046
|
const getter = useCallback2(
|
|
1980
|
-
() => store.
|
|
2047
|
+
() => store.getUserThreadsLoadingState(options.query),
|
|
1981
2048
|
[store, options.query]
|
|
1982
2049
|
);
|
|
1983
2050
|
return useSyncExternalStoreWithSelector(
|
|
1984
|
-
store.
|
|
2051
|
+
store.subscribe,
|
|
1985
2052
|
getter,
|
|
1986
2053
|
getter,
|
|
1987
2054
|
identity,
|
|
@@ -2154,32 +2221,20 @@ var UpdateNotificationSettingsError = class extends Error {
|
|
|
2154
2221
|
import { shallow as shallow4 } from "@liveblocks/client";
|
|
2155
2222
|
import {
|
|
2156
2223
|
assert as assert2,
|
|
2157
|
-
CommentsApiError,
|
|
2158
2224
|
console as console3,
|
|
2159
2225
|
createCommentId,
|
|
2160
2226
|
createThreadId,
|
|
2161
2227
|
deprecateIf,
|
|
2162
2228
|
errorIf,
|
|
2229
|
+
HttpError as HttpError2,
|
|
2163
2230
|
kInternal as kInternal3,
|
|
2164
2231
|
makeEventSource as makeEventSource2,
|
|
2165
2232
|
makePoller as makePoller2,
|
|
2166
|
-
NotificationsApiError,
|
|
2167
2233
|
ServerMsgCode
|
|
2168
2234
|
} from "@liveblocks/core";
|
|
2169
2235
|
import * as React4 from "react";
|
|
2170
2236
|
import { useSyncExternalStoreWithSelector as useSyncExternalStoreWithSelector2 } from "use-sync-external-store/shim/with-selector.js";
|
|
2171
2237
|
|
|
2172
|
-
// src/lib/retry-error.ts
|
|
2173
|
-
var MAX_ERROR_RETRY_COUNT = 5;
|
|
2174
|
-
var ERROR_RETRY_INTERVAL = 5e3;
|
|
2175
|
-
function retryError(action, retryCount) {
|
|
2176
|
-
if (retryCount >= MAX_ERROR_RETRY_COUNT) return;
|
|
2177
|
-
const timeout = Math.pow(2, retryCount) * ERROR_RETRY_INTERVAL;
|
|
2178
|
-
setTimeout(() => {
|
|
2179
|
-
void action();
|
|
2180
|
-
}, timeout);
|
|
2181
|
-
}
|
|
2182
|
-
|
|
2183
2238
|
// src/use-scroll-to-comment-on-load-effect.ts
|
|
2184
2239
|
import * as React3 from "react";
|
|
2185
2240
|
function handleScrollToCommentOnLoad(shouldScrollOnLoad, state) {
|
|
@@ -2230,7 +2285,6 @@ function useSyncExternalStore2(s, gs, gss) {
|
|
|
2230
2285
|
return useSyncExternalStoreWithSelector2(s, gs, gss, identity2);
|
|
2231
2286
|
}
|
|
2232
2287
|
var STABLE_EMPTY_LIST = Object.freeze([]);
|
|
2233
|
-
var POLLING_INTERVAL2 = 5 * 60 * 1e3;
|
|
2234
2288
|
function alwaysEmptyList() {
|
|
2235
2289
|
return STABLE_EMPTY_LIST;
|
|
2236
2290
|
}
|
|
@@ -2285,28 +2339,6 @@ function handleApiError(err) {
|
|
|
2285
2339
|
}
|
|
2286
2340
|
return new Error(message);
|
|
2287
2341
|
}
|
|
2288
|
-
function makeDeltaPoller_RoomThreads(client) {
|
|
2289
|
-
const store = getUmbrellaStoreForClient(client);
|
|
2290
|
-
const poller = makePoller2(async () => {
|
|
2291
|
-
const roomIds = client[kInternal3].getRoomIds();
|
|
2292
|
-
await Promise.allSettled(
|
|
2293
|
-
roomIds.map((roomId) => {
|
|
2294
|
-
const room = client.getRoom(roomId);
|
|
2295
|
-
if (room === null) return;
|
|
2296
|
-
return store.fetchRoomThreadsDeltaUpdate(room.id);
|
|
2297
|
-
})
|
|
2298
|
-
);
|
|
2299
|
-
}, POLLING_INTERVAL2);
|
|
2300
|
-
let pollerSubscribers = 0;
|
|
2301
|
-
return () => {
|
|
2302
|
-
pollerSubscribers++;
|
|
2303
|
-
poller.enable(pollerSubscribers > 0);
|
|
2304
|
-
return () => {
|
|
2305
|
-
pollerSubscribers--;
|
|
2306
|
-
poller.enable(pollerSubscribers > 0);
|
|
2307
|
-
};
|
|
2308
|
-
};
|
|
2309
|
-
}
|
|
2310
2342
|
var _extras2 = /* @__PURE__ */ new WeakMap();
|
|
2311
2343
|
var _bundles2 = /* @__PURE__ */ new WeakMap();
|
|
2312
2344
|
function getOrCreateRoomContextBundle(client) {
|
|
@@ -2327,78 +2359,87 @@ function getRoomExtrasForClient(client) {
|
|
|
2327
2359
|
}
|
|
2328
2360
|
function makeRoomExtrasForClient(client) {
|
|
2329
2361
|
const store = getUmbrellaStoreForClient(client);
|
|
2330
|
-
const requestsByQuery = /* @__PURE__ */ new Map();
|
|
2331
|
-
async function getRoomVersions(room, { retryCount } = { retryCount: 0 }) {
|
|
2332
|
-
const queryKey = makeVersionsQueryKey(room.id);
|
|
2333
|
-
const existingRequest = requestsByQuery.get(queryKey);
|
|
2334
|
-
if (existingRequest !== void 0) return existingRequest;
|
|
2335
|
-
const request = room[kInternal3].listTextVersions();
|
|
2336
|
-
requestsByQuery.set(queryKey, request);
|
|
2337
|
-
store.setQuery4Loading(queryKey);
|
|
2338
|
-
try {
|
|
2339
|
-
const result = await request;
|
|
2340
|
-
const data = await result.json();
|
|
2341
|
-
const versions = data.versions.map(({ createdAt, ...version2 }) => {
|
|
2342
|
-
return {
|
|
2343
|
-
createdAt: new Date(createdAt),
|
|
2344
|
-
...version2
|
|
2345
|
-
};
|
|
2346
|
-
});
|
|
2347
|
-
store.updateRoomVersions(room.id, versions, queryKey);
|
|
2348
|
-
requestsByQuery.delete(queryKey);
|
|
2349
|
-
} catch (err) {
|
|
2350
|
-
requestsByQuery.delete(queryKey);
|
|
2351
|
-
retryError(() => {
|
|
2352
|
-
void getRoomVersions(room, {
|
|
2353
|
-
retryCount: retryCount + 1
|
|
2354
|
-
});
|
|
2355
|
-
}, retryCount);
|
|
2356
|
-
store.setQuery4Error(queryKey, err);
|
|
2357
|
-
}
|
|
2358
|
-
return;
|
|
2359
|
-
}
|
|
2360
|
-
async function getInboxNotificationSettings(room, { retryCount } = { retryCount: 0 }) {
|
|
2361
|
-
const queryKey = makeNotificationSettingsQueryKey(room.id);
|
|
2362
|
-
const existingRequest = requestsByQuery.get(queryKey);
|
|
2363
|
-
if (existingRequest !== void 0) return existingRequest;
|
|
2364
|
-
try {
|
|
2365
|
-
const request = room.getNotificationSettings();
|
|
2366
|
-
requestsByQuery.set(queryKey, request);
|
|
2367
|
-
store.setQuery3Loading(queryKey);
|
|
2368
|
-
const settings = await request;
|
|
2369
|
-
store.updateRoomInboxNotificationSettings(room.id, settings, queryKey);
|
|
2370
|
-
} catch (err) {
|
|
2371
|
-
requestsByQuery.delete(queryKey);
|
|
2372
|
-
retryError(() => {
|
|
2373
|
-
void getInboxNotificationSettings(room, {
|
|
2374
|
-
retryCount: retryCount + 1
|
|
2375
|
-
});
|
|
2376
|
-
}, retryCount);
|
|
2377
|
-
store.setQuery3Error(queryKey, err);
|
|
2378
|
-
}
|
|
2379
|
-
return;
|
|
2380
|
-
}
|
|
2381
2362
|
const commentsErrorEventSource = makeEventSource2();
|
|
2382
2363
|
function onMutationFailure(innerError, optimisticUpdateId, createPublicError) {
|
|
2383
2364
|
store.removeOptimisticUpdate(optimisticUpdateId);
|
|
2384
|
-
if (innerError instanceof
|
|
2365
|
+
if (innerError instanceof HttpError2) {
|
|
2385
2366
|
const error = handleApiError(innerError);
|
|
2386
2367
|
commentsErrorEventSource.notify(createPublicError(error));
|
|
2387
2368
|
return;
|
|
2388
2369
|
}
|
|
2389
|
-
if (innerError instanceof
|
|
2370
|
+
if (innerError instanceof HttpError2) {
|
|
2390
2371
|
handleApiError(innerError);
|
|
2391
2372
|
return;
|
|
2392
2373
|
}
|
|
2393
2374
|
throw innerError;
|
|
2394
2375
|
}
|
|
2376
|
+
const threadsPollersByRoomId = /* @__PURE__ */ new Map();
|
|
2377
|
+
const versionsPollersByRoomId = /* @__PURE__ */ new Map();
|
|
2378
|
+
const roomNotificationSettingsPollersByRoomId = /* @__PURE__ */ new Map();
|
|
2379
|
+
function getOrCreateThreadsPollerForRoomId(roomId) {
|
|
2380
|
+
let poller = threadsPollersByRoomId.get(roomId);
|
|
2381
|
+
if (!poller) {
|
|
2382
|
+
poller = makePoller2(
|
|
2383
|
+
async (signal) => {
|
|
2384
|
+
try {
|
|
2385
|
+
return await store.fetchRoomThreadsDeltaUpdate(roomId, signal);
|
|
2386
|
+
} catch (err) {
|
|
2387
|
+
console3.warn(`Polling new threads for '${roomId}' failed: ${String(err)}`);
|
|
2388
|
+
throw err;
|
|
2389
|
+
}
|
|
2390
|
+
},
|
|
2391
|
+
config.ROOM_THREADS_POLL_INTERVAL,
|
|
2392
|
+
{ maxStaleTimeMs: config.ROOM_THREADS_MAX_STALE_TIME }
|
|
2393
|
+
);
|
|
2394
|
+
threadsPollersByRoomId.set(roomId, poller);
|
|
2395
|
+
}
|
|
2396
|
+
return poller;
|
|
2397
|
+
}
|
|
2398
|
+
function getOrCreateVersionsPollerForRoomId(roomId) {
|
|
2399
|
+
let poller = versionsPollersByRoomId.get(roomId);
|
|
2400
|
+
if (!poller) {
|
|
2401
|
+
poller = makePoller2(
|
|
2402
|
+
async (signal) => {
|
|
2403
|
+
try {
|
|
2404
|
+
return await store.fetchRoomVersionsDeltaUpdate(roomId, signal);
|
|
2405
|
+
} catch (err) {
|
|
2406
|
+
console3.warn(`Polling new history versions for '${roomId}' failed: ${String(err)}`);
|
|
2407
|
+
throw err;
|
|
2408
|
+
}
|
|
2409
|
+
},
|
|
2410
|
+
config.HISTORY_VERSIONS_POLL_INTERVAL,
|
|
2411
|
+
{ maxStaleTimeMs: config.HISTORY_VERSIONS_MAX_STALE_TIME }
|
|
2412
|
+
);
|
|
2413
|
+
versionsPollersByRoomId.set(roomId, poller);
|
|
2414
|
+
}
|
|
2415
|
+
return poller;
|
|
2416
|
+
}
|
|
2417
|
+
function getOrCreateNotificationsSettingsPollerForRoomId(roomId) {
|
|
2418
|
+
let poller = roomNotificationSettingsPollersByRoomId.get(roomId);
|
|
2419
|
+
if (!poller) {
|
|
2420
|
+
poller = makePoller2(
|
|
2421
|
+
async (signal) => {
|
|
2422
|
+
try {
|
|
2423
|
+
return await store.refreshRoomNotificationSettings(roomId, signal);
|
|
2424
|
+
} catch (err) {
|
|
2425
|
+
console3.warn(`Polling notification settings for '${roomId}' failed: ${String(err)}`);
|
|
2426
|
+
throw err;
|
|
2427
|
+
}
|
|
2428
|
+
},
|
|
2429
|
+
config.NOTIFICATION_SETTINGS_POLL_INTERVAL,
|
|
2430
|
+
{ maxStaleTimeMs: config.NOTIFICATION_SETTINGS_MAX_STALE_TIME }
|
|
2431
|
+
);
|
|
2432
|
+
roomNotificationSettingsPollersByRoomId.set(roomId, poller);
|
|
2433
|
+
}
|
|
2434
|
+
return poller;
|
|
2435
|
+
}
|
|
2395
2436
|
return {
|
|
2396
2437
|
store,
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2438
|
+
commentsErrorEventSource: commentsErrorEventSource.observable,
|
|
2439
|
+
onMutationFailure,
|
|
2440
|
+
getOrCreateThreadsPollerForRoomId,
|
|
2441
|
+
getOrCreateVersionsPollerForRoomId,
|
|
2442
|
+
getOrCreateNotificationsSettingsPollerForRoomId
|
|
2402
2443
|
};
|
|
2403
2444
|
}
|
|
2404
2445
|
function makeRoomContextBundle(client) {
|
|
@@ -2582,7 +2623,7 @@ function RoomProviderInner(props) {
|
|
|
2582
2623
|
return;
|
|
2583
2624
|
}
|
|
2584
2625
|
const { thread, inboxNotification } = info;
|
|
2585
|
-
const existingThread = store.getFullState().
|
|
2626
|
+
const existingThread = store.getFullState().threadsDB.getEvenIfDeleted(message.threadId);
|
|
2586
2627
|
switch (message.type) {
|
|
2587
2628
|
case ServerMsgCode.COMMENT_EDITED:
|
|
2588
2629
|
case ServerMsgCode.THREAD_METADATA_UPDATED:
|
|
@@ -2604,22 +2645,6 @@ function RoomProviderInner(props) {
|
|
|
2604
2645
|
(message) => void handleCommentEvent(message)
|
|
2605
2646
|
);
|
|
2606
2647
|
}, [client, room]);
|
|
2607
|
-
React4.useEffect(() => {
|
|
2608
|
-
const store = getRoomExtrasForClient(client).store;
|
|
2609
|
-
void store.fetchRoomThreadsDeltaUpdate(room.id).catch(() => {
|
|
2610
|
-
});
|
|
2611
|
-
}, [client, room.id]);
|
|
2612
|
-
React4.useEffect(() => {
|
|
2613
|
-
function handleIsOnline() {
|
|
2614
|
-
const store = getRoomExtrasForClient(client).store;
|
|
2615
|
-
void store.fetchRoomThreadsDeltaUpdate(room.id).catch(() => {
|
|
2616
|
-
});
|
|
2617
|
-
}
|
|
2618
|
-
window.addEventListener("online", handleIsOnline);
|
|
2619
|
-
return () => {
|
|
2620
|
-
window.removeEventListener("online", handleIsOnline);
|
|
2621
|
-
};
|
|
2622
|
-
}, [client, room.id]);
|
|
2623
2648
|
React4.useEffect(() => {
|
|
2624
2649
|
const pair = stableEnterRoom(roomId, frozenProps);
|
|
2625
2650
|
setRoomLeavePair(pair);
|
|
@@ -2907,11 +2932,11 @@ function useThreads(options = {
|
|
|
2907
2932
|
const { scrollOnLoad = true } = options;
|
|
2908
2933
|
const client = useClient();
|
|
2909
2934
|
const room = useRoom();
|
|
2910
|
-
const { store,
|
|
2935
|
+
const { store, getOrCreateThreadsPollerForRoomId } = getRoomExtrasForClient(client);
|
|
2936
|
+
const poller = getOrCreateThreadsPollerForRoomId(room.id);
|
|
2911
2937
|
React4.useEffect(
|
|
2912
2938
|
() => {
|
|
2913
|
-
void store.waitUntilRoomThreadsLoaded(room.id, options.query)
|
|
2914
|
-
});
|
|
2939
|
+
void store.waitUntilRoomThreadsLoaded(room.id, options.query);
|
|
2915
2940
|
}
|
|
2916
2941
|
// NOTE: Deliberately *not* using a dependency array here!
|
|
2917
2942
|
//
|
|
@@ -2922,13 +2947,17 @@ function useThreads(options = {
|
|
|
2922
2947
|
// 3. If ever the promise would fail, then after 5 seconds it would reset, and on the very
|
|
2923
2948
|
// *next* render after that, a *new* fetch/promise will get created.
|
|
2924
2949
|
);
|
|
2925
|
-
React4.useEffect(
|
|
2950
|
+
React4.useEffect(() => {
|
|
2951
|
+
poller.inc();
|
|
2952
|
+
poller.pollNowIfStale();
|
|
2953
|
+
return () => poller.dec();
|
|
2954
|
+
}, [poller]);
|
|
2926
2955
|
const getter = React4.useCallback(
|
|
2927
|
-
() => store.
|
|
2956
|
+
() => store.getRoomThreadsLoadingState(room.id, options.query),
|
|
2928
2957
|
[store, room.id, options.query]
|
|
2929
2958
|
);
|
|
2930
2959
|
const state = useSyncExternalStoreWithSelector2(
|
|
2931
|
-
store.
|
|
2960
|
+
store.subscribe,
|
|
2932
2961
|
getter,
|
|
2933
2962
|
getter,
|
|
2934
2963
|
identity2,
|
|
@@ -3012,9 +3041,9 @@ function useDeleteThread() {
|
|
|
3012
3041
|
return React4.useCallback(
|
|
3013
3042
|
(threadId) => {
|
|
3014
3043
|
const { store, onMutationFailure } = getRoomExtrasForClient(client);
|
|
3015
|
-
const thread = store.getFullState().threadsById[threadId];
|
|
3016
3044
|
const userId = getCurrentUserId(room);
|
|
3017
|
-
|
|
3045
|
+
const existing = store.getFullState().threadsDB.get(threadId);
|
|
3046
|
+
if (existing?.comments?.[0]?.userId !== userId) {
|
|
3018
3047
|
throw new Error("Only the thread creator can delete the thread");
|
|
3019
3048
|
}
|
|
3020
3049
|
const optimisticUpdateId = store.addOptimisticUpdate({
|
|
@@ -3130,14 +3159,14 @@ function useEditComment() {
|
|
|
3130
3159
|
({ threadId, commentId, body, attachments }) => {
|
|
3131
3160
|
const editedAt = /* @__PURE__ */ new Date();
|
|
3132
3161
|
const { store, onMutationFailure } = getRoomExtrasForClient(client);
|
|
3133
|
-
const
|
|
3134
|
-
if (
|
|
3162
|
+
const existing = store.getFullState().threadsDB.getEvenIfDeleted(threadId);
|
|
3163
|
+
if (existing === void 0) {
|
|
3135
3164
|
console3.warn(
|
|
3136
3165
|
`Internal unexpected behavior. Cannot edit comment in thread "${threadId}" because the thread does not exist in the cache.`
|
|
3137
3166
|
);
|
|
3138
3167
|
return;
|
|
3139
3168
|
}
|
|
3140
|
-
const comment =
|
|
3169
|
+
const comment = existing.comments.find(
|
|
3141
3170
|
(comment2) => comment2.id === commentId
|
|
3142
3171
|
);
|
|
3143
3172
|
if (comment === void 0 || comment.deletedAt !== void 0) {
|
|
@@ -3411,24 +3440,22 @@ function useThreadSubscription(threadId) {
|
|
|
3411
3440
|
const { store } = getRoomExtrasForClient(client);
|
|
3412
3441
|
const selector = React4.useCallback(
|
|
3413
3442
|
(state) => {
|
|
3414
|
-
const
|
|
3415
|
-
(
|
|
3443
|
+
const notification = state.cleanedNotifications.find(
|
|
3444
|
+
(inboxNotification) => inboxNotification.kind === "thread" && inboxNotification.threadId === threadId
|
|
3416
3445
|
);
|
|
3417
|
-
const thread = state.
|
|
3418
|
-
if (
|
|
3419
|
-
return {
|
|
3420
|
-
status: "not-subscribed"
|
|
3421
|
-
};
|
|
3446
|
+
const thread = state.threadsDB.get(threadId);
|
|
3447
|
+
if (notification === void 0 || thread === void 0) {
|
|
3448
|
+
return { status: "not-subscribed" };
|
|
3422
3449
|
}
|
|
3423
3450
|
return {
|
|
3424
3451
|
status: "subscribed",
|
|
3425
|
-
unreadSince:
|
|
3452
|
+
unreadSince: notification.readAt
|
|
3426
3453
|
};
|
|
3427
3454
|
},
|
|
3428
3455
|
[threadId]
|
|
3429
3456
|
);
|
|
3430
3457
|
return useSyncExternalStoreWithSelector2(
|
|
3431
|
-
store.
|
|
3458
|
+
store.subscribe,
|
|
3432
3459
|
store.getFullState,
|
|
3433
3460
|
store.getFullState,
|
|
3434
3461
|
selector
|
|
@@ -3438,48 +3465,51 @@ function useRoomNotificationSettings() {
|
|
|
3438
3465
|
const updateRoomNotificationSettings = useUpdateRoomNotificationSettings();
|
|
3439
3466
|
const client = useClient();
|
|
3440
3467
|
const room = useRoom();
|
|
3441
|
-
const { store } = getRoomExtrasForClient(client);
|
|
3468
|
+
const { store, getOrCreateNotificationsSettingsPollerForRoomId } = getRoomExtrasForClient(client);
|
|
3469
|
+
const poller = getOrCreateNotificationsSettingsPollerForRoomId(room.id);
|
|
3470
|
+
React4.useEffect(
|
|
3471
|
+
() => {
|
|
3472
|
+
void store.waitUntilRoomNotificationSettingsLoaded(room.id);
|
|
3473
|
+
}
|
|
3474
|
+
// NOTE: Deliberately *not* using a dependency array here!
|
|
3475
|
+
//
|
|
3476
|
+
// It is important to call waitUntil on *every* render.
|
|
3477
|
+
// This is harmless though, on most renders, except:
|
|
3478
|
+
// 1. The very first render, in which case we'll want to trigger the initial page fetch.
|
|
3479
|
+
// 2. All other subsequent renders now "just" return the same promise (a quick operation).
|
|
3480
|
+
// 3. If ever the promise would fail, then after 5 seconds it would reset, and on the very
|
|
3481
|
+
// *next* render after that, a *new* fetch/promise will get created.
|
|
3482
|
+
);
|
|
3483
|
+
React4.useEffect(() => {
|
|
3484
|
+
poller.inc();
|
|
3485
|
+
poller.pollNowIfStale();
|
|
3486
|
+
return () => {
|
|
3487
|
+
poller.dec();
|
|
3488
|
+
};
|
|
3489
|
+
}, [poller]);
|
|
3442
3490
|
const getter = React4.useCallback(
|
|
3443
|
-
() => store.
|
|
3491
|
+
() => store.getNotificationSettingsLoadingState(room.id),
|
|
3444
3492
|
[store, room.id]
|
|
3445
3493
|
);
|
|
3446
|
-
React4.useEffect(() => {
|
|
3447
|
-
const { getInboxNotificationSettings } = getRoomExtrasForClient(client);
|
|
3448
|
-
void getInboxNotificationSettings(room);
|
|
3449
|
-
}, [client, room]);
|
|
3450
3494
|
const settings = useSyncExternalStoreWithSelector2(
|
|
3451
|
-
store.
|
|
3495
|
+
store.subscribe,
|
|
3452
3496
|
getter,
|
|
3453
3497
|
getter,
|
|
3454
3498
|
identity2,
|
|
3455
|
-
|
|
3499
|
+
shallow2
|
|
3456
3500
|
);
|
|
3457
3501
|
return React4.useMemo(() => {
|
|
3458
3502
|
return [settings, updateRoomNotificationSettings];
|
|
3459
3503
|
}, [settings, updateRoomNotificationSettings]);
|
|
3460
3504
|
}
|
|
3461
3505
|
function useRoomNotificationSettingsSuspense() {
|
|
3462
|
-
const updateRoomNotificationSettings = useUpdateRoomNotificationSettings();
|
|
3463
3506
|
const client = useClient();
|
|
3507
|
+
const store = getRoomExtrasForClient(client).store;
|
|
3464
3508
|
const room = useRoom();
|
|
3465
|
-
|
|
3466
|
-
const
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
);
|
|
3470
|
-
const settings = useSyncExternalStoreWithSelector2(
|
|
3471
|
-
store.subscribeNotificationSettings,
|
|
3472
|
-
getter,
|
|
3473
|
-
getter,
|
|
3474
|
-
identity2,
|
|
3475
|
-
shallow4
|
|
3476
|
-
);
|
|
3477
|
-
if (settings.isLoading) {
|
|
3478
|
-
const { getInboxNotificationSettings } = getRoomExtrasForClient(client);
|
|
3479
|
-
throw getInboxNotificationSettings(room);
|
|
3480
|
-
} else if (settings.error) {
|
|
3481
|
-
throw settings.error;
|
|
3482
|
-
}
|
|
3509
|
+
use(store.waitUntilRoomNotificationSettingsLoaded(room.id));
|
|
3510
|
+
const [settings, updateRoomNotificationSettings] = useRoomNotificationSettings();
|
|
3511
|
+
assert2(!settings.error, "Did not expect error");
|
|
3512
|
+
assert2(!settings.isLoading, "Did not expect loading");
|
|
3483
3513
|
return React4.useMemo(() => {
|
|
3484
3514
|
return [settings, updateRoomNotificationSettings];
|
|
3485
3515
|
}, [settings, updateRoomNotificationSettings]);
|
|
@@ -3516,45 +3546,48 @@ function useHistoryVersionData(versionId) {
|
|
|
3516
3546
|
function useHistoryVersions() {
|
|
3517
3547
|
const client = useClient();
|
|
3518
3548
|
const room = useRoom();
|
|
3519
|
-
const { store,
|
|
3549
|
+
const { store, getOrCreateVersionsPollerForRoomId } = getRoomExtrasForClient(client);
|
|
3550
|
+
const poller = getOrCreateVersionsPollerForRoomId(room.id);
|
|
3551
|
+
React4.useEffect(() => {
|
|
3552
|
+
poller.inc();
|
|
3553
|
+
poller.pollNowIfStale();
|
|
3554
|
+
return () => poller.dec();
|
|
3555
|
+
}, [poller]);
|
|
3520
3556
|
const getter = React4.useCallback(
|
|
3521
|
-
() => store.
|
|
3557
|
+
() => store.getRoomVersionsLoadingState(room.id),
|
|
3522
3558
|
[store, room.id]
|
|
3523
3559
|
);
|
|
3524
|
-
React4.useEffect(
|
|
3525
|
-
|
|
3526
|
-
|
|
3560
|
+
React4.useEffect(
|
|
3561
|
+
() => {
|
|
3562
|
+
void store.waitUntilRoomVersionsLoaded(room.id);
|
|
3563
|
+
}
|
|
3564
|
+
// NOTE: Deliberately *not* using a dependency array here!
|
|
3565
|
+
//
|
|
3566
|
+
// It is important to call waitUntil on *every* render.
|
|
3567
|
+
// This is harmless though, on most renders, except:
|
|
3568
|
+
// 1. The very first render, in which case we'll want to trigger the initial page fetch.
|
|
3569
|
+
// 2. All other subsequent renders now "just" return the same promise (a quick operation).
|
|
3570
|
+
// 3. If ever the promise would fail, then after 5 seconds it would reset, and on the very
|
|
3571
|
+
// *next* render after that, a *new* fetch/promise will get created.
|
|
3572
|
+
);
|
|
3527
3573
|
const state = useSyncExternalStoreWithSelector2(
|
|
3528
|
-
store.
|
|
3574
|
+
store.subscribe,
|
|
3529
3575
|
getter,
|
|
3530
3576
|
getter,
|
|
3531
3577
|
identity2,
|
|
3532
|
-
|
|
3578
|
+
shallow2
|
|
3533
3579
|
);
|
|
3534
3580
|
return state;
|
|
3535
3581
|
}
|
|
3536
3582
|
function useHistoryVersionsSuspense() {
|
|
3537
3583
|
const client = useClient();
|
|
3538
3584
|
const room = useRoom();
|
|
3539
|
-
const
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
);
|
|
3544
|
-
|
|
3545
|
-
store.subscribeVersions,
|
|
3546
|
-
getter,
|
|
3547
|
-
getter,
|
|
3548
|
-
identity2,
|
|
3549
|
-
shallow4
|
|
3550
|
-
);
|
|
3551
|
-
if (state.isLoading) {
|
|
3552
|
-
const { getRoomVersions } = getRoomExtrasForClient(client);
|
|
3553
|
-
throw getRoomVersions(room);
|
|
3554
|
-
} else if (state.error) {
|
|
3555
|
-
throw state.error;
|
|
3556
|
-
}
|
|
3557
|
-
return state;
|
|
3585
|
+
const store = getRoomExtrasForClient(client).store;
|
|
3586
|
+
use(store.waitUntilRoomVersionsLoaded(room.id));
|
|
3587
|
+
const result = useHistoryVersions();
|
|
3588
|
+
assert2(!result.error, "Did not expect error");
|
|
3589
|
+
assert2(!result.isLoading, "Did not expect loading");
|
|
3590
|
+
return result;
|
|
3558
3591
|
}
|
|
3559
3592
|
function useUpdateRoomNotificationSettings() {
|
|
3560
3593
|
const client = useClient();
|
|
@@ -3569,7 +3602,7 @@ function useUpdateRoomNotificationSettings() {
|
|
|
3569
3602
|
});
|
|
3570
3603
|
room.updateNotificationSettings(settings).then(
|
|
3571
3604
|
(settings2) => {
|
|
3572
|
-
store.
|
|
3605
|
+
store.updateRoomNotificationSettings_confirmOptimisticUpdate(
|
|
3573
3606
|
room.id,
|
|
3574
3607
|
optimisticUpdateId,
|
|
3575
3608
|
settings2
|
|
@@ -3731,6 +3764,8 @@ var _useOthersMapped = useOthersMapped;
|
|
|
3731
3764
|
var _useOthersMappedSuspense = useOthersMappedSuspense;
|
|
3732
3765
|
var _useThreads = useThreads;
|
|
3733
3766
|
var _useThreadsSuspense = useThreadsSuspense;
|
|
3767
|
+
var _useRoomNotificationSettings = useRoomNotificationSettings;
|
|
3768
|
+
var _useRoomNotificationSettingsSuspense = useRoomNotificationSettingsSuspense;
|
|
3734
3769
|
var _useHistoryVersions = useHistoryVersions;
|
|
3735
3770
|
var _useHistoryVersionsSuspense = useHistoryVersionsSuspense;
|
|
3736
3771
|
var _useOther = useOther;
|
|
@@ -3754,7 +3789,6 @@ var _useUpdateMyPresence = useUpdateMyPresence;
|
|
|
3754
3789
|
|
|
3755
3790
|
export {
|
|
3756
3791
|
RoomContext,
|
|
3757
|
-
selectThreads,
|
|
3758
3792
|
ClientContext,
|
|
3759
3793
|
getUmbrellaStoreForClient,
|
|
3760
3794
|
useClient,
|
|
@@ -3796,7 +3830,6 @@ export {
|
|
|
3796
3830
|
useMarkThreadAsResolved,
|
|
3797
3831
|
useMarkThreadAsUnresolved,
|
|
3798
3832
|
useThreadSubscription,
|
|
3799
|
-
useRoomNotificationSettings,
|
|
3800
3833
|
useHistoryVersionData,
|
|
3801
3834
|
useUpdateRoomNotificationSettings,
|
|
3802
3835
|
useOthersConnectionIdsSuspense,
|
|
@@ -3820,6 +3853,8 @@ export {
|
|
|
3820
3853
|
_useOthersMappedSuspense,
|
|
3821
3854
|
_useThreads,
|
|
3822
3855
|
_useThreadsSuspense,
|
|
3856
|
+
_useRoomNotificationSettings,
|
|
3857
|
+
_useRoomNotificationSettingsSuspense,
|
|
3823
3858
|
_useHistoryVersions,
|
|
3824
3859
|
_useHistoryVersionsSuspense,
|
|
3825
3860
|
_useOther,
|
|
@@ -3833,4 +3868,4 @@ export {
|
|
|
3833
3868
|
_useStorageRoot,
|
|
3834
3869
|
_useUpdateMyPresence
|
|
3835
3870
|
};
|
|
3836
|
-
//# sourceMappingURL=chunk-
|
|
3871
|
+
//# sourceMappingURL=chunk-A7GJNN4L.mjs.map
|