@dittolive/ditto-chat-core 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +377 -0
- package/dist/index.es.js +1764 -0
- package/dist/index.umd.js +5 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/slices/useChatUser.d.ts +22 -0
- package/dist/src/slices/useMessages.d.ts +41 -0
- package/dist/src/slices/useRBAC.d.ts +8 -0
- package/dist/src/slices/useRooms.d.ts +31 -0
- package/dist/src/types/ChatUser.d.ts +11 -0
- package/dist/src/types/Message.d.ts +30 -0
- package/dist/src/types/MessageWithUser.d.ts +9 -0
- package/dist/src/types/RBAC.d.ts +11 -0
- package/dist/src/types/Room.d.ts +13 -0
- package/dist/src/useChat.d.ts +37 -0
- package/dist/tests/setup.d.ts +14 -0
- package/dist/tests/useChat.edge-cases.test.d.ts +1 -0
- package/dist/tests/useChat.error.test.d.ts +1 -0
- package/dist/tests/useChat.test.d.ts +1 -0
- package/dist/tests/useChatUser.test.d.ts +1 -0
- package/dist/tests/useMessages.test.d.ts +1 -0
- package/dist/tests/useRBAC.test.d.ts +1 -0
- package/dist/tests/useRooms.test.d.ts +1 -0
- package/package.json +67 -17
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -14
package/dist/index.es.js
ADDED
|
@@ -0,0 +1,1764 @@
|
|
|
1
|
+
import k, { useMemo as Fe } from "react";
|
|
2
|
+
const ze = {
|
|
3
|
+
canCreateRoom: !0,
|
|
4
|
+
canEditOwnMessage: !0,
|
|
5
|
+
canDeleteOwnMessage: !0,
|
|
6
|
+
canAddReaction: !0,
|
|
7
|
+
canRemoveOwnReaction: !0,
|
|
8
|
+
canMentionUsers: !0,
|
|
9
|
+
canSubscribeToRoom: !0
|
|
10
|
+
}, He = (e, t, { rbacConfig: r = {} }) => ({
|
|
11
|
+
rbacConfig: r,
|
|
12
|
+
/**
|
|
13
|
+
* Check if a specific permission action is allowed.
|
|
14
|
+
*
|
|
15
|
+
* This method evaluates the RBAC configuration to determine
|
|
16
|
+
* if an action is permitted. Uses a permissive-by-default approach.
|
|
17
|
+
*
|
|
18
|
+
* Workflow:
|
|
19
|
+
* 1. Gets current RBAC config from state
|
|
20
|
+
* 2. Checks if permission is explicitly set
|
|
21
|
+
* 3. Returns explicit value or default (true)
|
|
22
|
+
*
|
|
23
|
+
* @param action - Permission key to check (e.g., 'canCreateRoom')
|
|
24
|
+
* @returns boolean indicating if action is allowed
|
|
25
|
+
*/
|
|
26
|
+
canPerformAction: (s) => {
|
|
27
|
+
const a = t().rbacConfig[s];
|
|
28
|
+
return a !== void 0 ? a : ze[s];
|
|
29
|
+
},
|
|
30
|
+
/**
|
|
31
|
+
* Update the RBAC permission configuration.
|
|
32
|
+
*
|
|
33
|
+
* This method merges new permissions with the existing config,
|
|
34
|
+
* allowing partial updates without overwriting unrelated permissions.
|
|
35
|
+
*
|
|
36
|
+
* Workflow:
|
|
37
|
+
* 1. Merges provided config with existing rbacConfig
|
|
38
|
+
* 2. Updates state with combined configuration
|
|
39
|
+
*
|
|
40
|
+
* @param config - Partial RBAC config with permissions to update
|
|
41
|
+
*/
|
|
42
|
+
updateRBACConfig: (s) => {
|
|
43
|
+
e((o) => ({
|
|
44
|
+
...o,
|
|
45
|
+
rbacConfig: {
|
|
46
|
+
...o.rbacConfig,
|
|
47
|
+
...s
|
|
48
|
+
}
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
var Ae = /* @__PURE__ */ Symbol.for("immer-nothing"), ye = /* @__PURE__ */ Symbol.for("immer-draftable"), O = /* @__PURE__ */ Symbol.for("immer-state"), Le = process.env.NODE_ENV !== "production" ? [
|
|
53
|
+
// All error codes, starting by 0:
|
|
54
|
+
function(e) {
|
|
55
|
+
return `The plugin for '${e}' has not been loaded into Immer. To enable the plugin, import and call \`enable${e}()\` when initializing your application.`;
|
|
56
|
+
},
|
|
57
|
+
function(e) {
|
|
58
|
+
return `produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'. Got '${e}'`;
|
|
59
|
+
},
|
|
60
|
+
"This object has been frozen and should not be mutated",
|
|
61
|
+
function(e) {
|
|
62
|
+
return "Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? " + e;
|
|
63
|
+
},
|
|
64
|
+
"An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.",
|
|
65
|
+
"Immer forbids circular references",
|
|
66
|
+
"The first or second argument to `produce` must be a function",
|
|
67
|
+
"The third argument to `produce` must be a function or undefined",
|
|
68
|
+
"First argument to `createDraft` must be a plain object, an array, or an immerable object",
|
|
69
|
+
"First argument to `finishDraft` must be a draft returned by `createDraft`",
|
|
70
|
+
function(e) {
|
|
71
|
+
return `'current' expects a draft, got: ${e}`;
|
|
72
|
+
},
|
|
73
|
+
"Object.defineProperty() cannot be used on an Immer draft",
|
|
74
|
+
"Object.setPrototypeOf() cannot be used on an Immer draft",
|
|
75
|
+
"Immer only supports deleting array indices",
|
|
76
|
+
"Immer only supports setting array indices and the 'length' property",
|
|
77
|
+
function(e) {
|
|
78
|
+
return `'original' expects a draft, got: ${e}`;
|
|
79
|
+
}
|
|
80
|
+
// Note: if more errors are added, the errorOffset in Patches.ts should be increased
|
|
81
|
+
// See Patches.ts for additional errors
|
|
82
|
+
] : [];
|
|
83
|
+
function T(e, ...t) {
|
|
84
|
+
if (process.env.NODE_ENV !== "production") {
|
|
85
|
+
const r = Le[e], n = typeof r == "function" ? r.apply(null, t) : r;
|
|
86
|
+
throw new Error(`[Immer] ${n}`);
|
|
87
|
+
}
|
|
88
|
+
throw new Error(
|
|
89
|
+
`[Immer] minified error nr: ${e}. Full error at: https://bit.ly/3cXEKWf`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
var B = Object.getPrototypeOf;
|
|
93
|
+
function N(e) {
|
|
94
|
+
return !!e && !!e[O];
|
|
95
|
+
}
|
|
96
|
+
function U(e) {
|
|
97
|
+
return e ? ve(e) || Array.isArray(e) || !!e[ye] || !!e.constructor?.[ye] || H(e) || X(e) : !1;
|
|
98
|
+
}
|
|
99
|
+
var $e = Object.prototype.constructor.toString(), ge = /* @__PURE__ */ new WeakMap();
|
|
100
|
+
function ve(e) {
|
|
101
|
+
if (!e || typeof e != "object")
|
|
102
|
+
return !1;
|
|
103
|
+
const t = Object.getPrototypeOf(e);
|
|
104
|
+
if (t === null || t === Object.prototype)
|
|
105
|
+
return !0;
|
|
106
|
+
const r = Object.hasOwnProperty.call(t, "constructor") && t.constructor;
|
|
107
|
+
if (r === Object)
|
|
108
|
+
return !0;
|
|
109
|
+
if (typeof r != "function")
|
|
110
|
+
return !1;
|
|
111
|
+
let n = ge.get(r);
|
|
112
|
+
return n === void 0 && (n = Function.toString.call(r), ge.set(r, n)), n === $e;
|
|
113
|
+
}
|
|
114
|
+
function q(e, t, r = !0) {
|
|
115
|
+
V(e) === 0 ? (r ? Reflect.ownKeys(e) : Object.keys(e)).forEach((s) => {
|
|
116
|
+
t(s, e[s], e);
|
|
117
|
+
}) : e.forEach((n, s) => t(s, n, e));
|
|
118
|
+
}
|
|
119
|
+
function V(e) {
|
|
120
|
+
const t = e[O];
|
|
121
|
+
return t ? t.type_ : Array.isArray(e) ? 1 : H(e) ? 2 : X(e) ? 3 : 0;
|
|
122
|
+
}
|
|
123
|
+
function oe(e, t) {
|
|
124
|
+
return V(e) === 2 ? e.has(t) : Object.prototype.hasOwnProperty.call(e, t);
|
|
125
|
+
}
|
|
126
|
+
function Me(e, t, r) {
|
|
127
|
+
const n = V(e);
|
|
128
|
+
n === 2 ? e.set(t, r) : n === 3 ? e.add(r) : e[t] = r;
|
|
129
|
+
}
|
|
130
|
+
function je(e, t) {
|
|
131
|
+
return e === t ? e !== 0 || 1 / e === 1 / t : e !== e && t !== t;
|
|
132
|
+
}
|
|
133
|
+
function H(e) {
|
|
134
|
+
return e instanceof Map;
|
|
135
|
+
}
|
|
136
|
+
function X(e) {
|
|
137
|
+
return e instanceof Set;
|
|
138
|
+
}
|
|
139
|
+
function M(e) {
|
|
140
|
+
return e.copy_ || e.base_;
|
|
141
|
+
}
|
|
142
|
+
function ie(e, t) {
|
|
143
|
+
if (H(e))
|
|
144
|
+
return new Map(e);
|
|
145
|
+
if (X(e))
|
|
146
|
+
return new Set(e);
|
|
147
|
+
if (Array.isArray(e))
|
|
148
|
+
return Array.prototype.slice.call(e);
|
|
149
|
+
const r = ve(e);
|
|
150
|
+
if (t === !0 || t === "class_only" && !r) {
|
|
151
|
+
const n = Object.getOwnPropertyDescriptors(e);
|
|
152
|
+
delete n[O];
|
|
153
|
+
let s = Reflect.ownKeys(n);
|
|
154
|
+
for (let o = 0; o < s.length; o++) {
|
|
155
|
+
const a = s[o], c = n[a];
|
|
156
|
+
c.writable === !1 && (c.writable = !0, c.configurable = !0), (c.get || c.set) && (n[a] = {
|
|
157
|
+
configurable: !0,
|
|
158
|
+
writable: !0,
|
|
159
|
+
// could live with !!desc.set as well here...
|
|
160
|
+
enumerable: c.enumerable,
|
|
161
|
+
value: e[a]
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return Object.create(B(e), n);
|
|
165
|
+
} else {
|
|
166
|
+
const n = B(e);
|
|
167
|
+
if (n !== null && r)
|
|
168
|
+
return { ...e };
|
|
169
|
+
const s = Object.create(n);
|
|
170
|
+
return Object.assign(s, e);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function fe(e, t = !1) {
|
|
174
|
+
return Z(e) || N(e) || !U(e) || (V(e) > 1 && Object.defineProperties(e, {
|
|
175
|
+
set: W,
|
|
176
|
+
add: W,
|
|
177
|
+
clear: W,
|
|
178
|
+
delete: W
|
|
179
|
+
}), Object.freeze(e), t && Object.values(e).forEach((r) => fe(r, !0))), e;
|
|
180
|
+
}
|
|
181
|
+
function We() {
|
|
182
|
+
T(2);
|
|
183
|
+
}
|
|
184
|
+
var W = {
|
|
185
|
+
value: We
|
|
186
|
+
};
|
|
187
|
+
function Z(e) {
|
|
188
|
+
return e === null || typeof e != "object" ? !0 : Object.isFrozen(e);
|
|
189
|
+
}
|
|
190
|
+
var Ge = {};
|
|
191
|
+
function P(e) {
|
|
192
|
+
const t = Ge[e];
|
|
193
|
+
return t || T(0, e), t;
|
|
194
|
+
}
|
|
195
|
+
var F;
|
|
196
|
+
function Ce() {
|
|
197
|
+
return F;
|
|
198
|
+
}
|
|
199
|
+
function qe(e, t) {
|
|
200
|
+
return {
|
|
201
|
+
drafts_: [],
|
|
202
|
+
parent_: e,
|
|
203
|
+
immer_: t,
|
|
204
|
+
// Whenever the modified draft contains a draft from another scope, we
|
|
205
|
+
// need to prevent auto-freezing so the unowned draft can be finalized.
|
|
206
|
+
canAutoFreeze_: !0,
|
|
207
|
+
unfinalizedDrafts_: 0
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function he(e, t) {
|
|
211
|
+
t && (P("Patches"), e.patches_ = [], e.inversePatches_ = [], e.patchListener_ = t);
|
|
212
|
+
}
|
|
213
|
+
function ae(e) {
|
|
214
|
+
ce(e), e.drafts_.forEach(Qe), e.drafts_ = null;
|
|
215
|
+
}
|
|
216
|
+
function ce(e) {
|
|
217
|
+
e === F && (F = e.parent_);
|
|
218
|
+
}
|
|
219
|
+
function be(e) {
|
|
220
|
+
return F = qe(F, e);
|
|
221
|
+
}
|
|
222
|
+
function Qe(e) {
|
|
223
|
+
const t = e[O];
|
|
224
|
+
t.type_ === 0 || t.type_ === 1 ? t.revoke_() : t.revoked_ = !0;
|
|
225
|
+
}
|
|
226
|
+
function _e(e, t) {
|
|
227
|
+
t.unfinalizedDrafts_ = t.drafts_.length;
|
|
228
|
+
const r = t.drafts_[0];
|
|
229
|
+
return e !== void 0 && e !== r ? (r[O].modified_ && (ae(t), T(4)), U(e) && (e = Q(t, e), t.parent_ || Y(t, e)), t.patches_ && P("Patches").generateReplacementPatches_(
|
|
230
|
+
r[O].base_,
|
|
231
|
+
e,
|
|
232
|
+
t.patches_,
|
|
233
|
+
t.inversePatches_
|
|
234
|
+
)) : e = Q(t, r, []), ae(t), t.patches_ && t.patchListener_(t.patches_, t.inversePatches_), e !== Ae ? e : void 0;
|
|
235
|
+
}
|
|
236
|
+
function Q(e, t, r) {
|
|
237
|
+
if (Z(t))
|
|
238
|
+
return t;
|
|
239
|
+
const n = e.immer_.shouldUseStrictIteration(), s = t[O];
|
|
240
|
+
if (!s)
|
|
241
|
+
return q(
|
|
242
|
+
t,
|
|
243
|
+
(o, a) => we(e, s, t, o, a, r),
|
|
244
|
+
n
|
|
245
|
+
), t;
|
|
246
|
+
if (s.scope_ !== e)
|
|
247
|
+
return t;
|
|
248
|
+
if (!s.modified_)
|
|
249
|
+
return Y(e, s.base_, !0), s.base_;
|
|
250
|
+
if (!s.finalized_) {
|
|
251
|
+
s.finalized_ = !0, s.scope_.unfinalizedDrafts_--;
|
|
252
|
+
const o = s.copy_;
|
|
253
|
+
let a = o, c = !1;
|
|
254
|
+
s.type_ === 3 && (a = new Set(o), o.clear(), c = !0), q(
|
|
255
|
+
a,
|
|
256
|
+
(d, y) => we(
|
|
257
|
+
e,
|
|
258
|
+
s,
|
|
259
|
+
o,
|
|
260
|
+
d,
|
|
261
|
+
y,
|
|
262
|
+
r,
|
|
263
|
+
c
|
|
264
|
+
),
|
|
265
|
+
n
|
|
266
|
+
), Y(e, o, !1), r && e.patches_ && P("Patches").generatePatches_(
|
|
267
|
+
s,
|
|
268
|
+
r,
|
|
269
|
+
e.patches_,
|
|
270
|
+
e.inversePatches_
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
return s.copy_;
|
|
274
|
+
}
|
|
275
|
+
function we(e, t, r, n, s, o, a) {
|
|
276
|
+
if (s == null || typeof s != "object" && !a)
|
|
277
|
+
return;
|
|
278
|
+
const c = Z(s);
|
|
279
|
+
if (!(c && !a)) {
|
|
280
|
+
if (process.env.NODE_ENV !== "production" && s === r && T(5), N(s)) {
|
|
281
|
+
const d = o && t && t.type_ !== 3 && // Set objects are atomic since they have no keys.
|
|
282
|
+
!oe(t.assigned_, n) ? o.concat(n) : void 0, y = Q(e, s, d);
|
|
283
|
+
if (Me(r, n, y), N(y))
|
|
284
|
+
e.canAutoFreeze_ = !1;
|
|
285
|
+
else
|
|
286
|
+
return;
|
|
287
|
+
} else a && r.add(s);
|
|
288
|
+
if (U(s) && !c) {
|
|
289
|
+
if (!e.immer_.autoFreeze_ && e.unfinalizedDrafts_ < 1 || t && t.base_ && t.base_[n] === s && c)
|
|
290
|
+
return;
|
|
291
|
+
Q(e, s), (!t || !t.scope_.parent_) && typeof n != "symbol" && (H(r) ? r.has(n) : Object.prototype.propertyIsEnumerable.call(r, n)) && Y(e, s);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function Y(e, t, r = !1) {
|
|
296
|
+
!e.parent_ && e.immer_.autoFreeze_ && e.canAutoFreeze_ && fe(t, r);
|
|
297
|
+
}
|
|
298
|
+
function Ye(e, t) {
|
|
299
|
+
const r = Array.isArray(e), n = {
|
|
300
|
+
type_: r ? 1 : 0,
|
|
301
|
+
// Track which produce call this is associated with.
|
|
302
|
+
scope_: t ? t.scope_ : Ce(),
|
|
303
|
+
// True for both shallow and deep changes.
|
|
304
|
+
modified_: !1,
|
|
305
|
+
// Used during finalization.
|
|
306
|
+
finalized_: !1,
|
|
307
|
+
// Track which properties have been assigned (true) or deleted (false).
|
|
308
|
+
assigned_: {},
|
|
309
|
+
// The parent draft state.
|
|
310
|
+
parent_: t,
|
|
311
|
+
// The base state.
|
|
312
|
+
base_: e,
|
|
313
|
+
// The base proxy.
|
|
314
|
+
draft_: null,
|
|
315
|
+
// set below
|
|
316
|
+
// The base copy with any updated values.
|
|
317
|
+
copy_: null,
|
|
318
|
+
// Called by the `produce` function.
|
|
319
|
+
revoke_: null,
|
|
320
|
+
isManual_: !1
|
|
321
|
+
};
|
|
322
|
+
let s = n, o = me;
|
|
323
|
+
r && (s = [n], o = z);
|
|
324
|
+
const { revoke: a, proxy: c } = Proxy.revocable(s, o);
|
|
325
|
+
return n.draft_ = c, n.revoke_ = a, c;
|
|
326
|
+
}
|
|
327
|
+
var me = {
|
|
328
|
+
get(e, t) {
|
|
329
|
+
if (t === O)
|
|
330
|
+
return e;
|
|
331
|
+
const r = M(e);
|
|
332
|
+
if (!oe(r, t))
|
|
333
|
+
return Ve(e, r, t);
|
|
334
|
+
const n = r[t];
|
|
335
|
+
return e.finalized_ || !U(n) ? n : n === ee(e.base_, t) ? (te(e), e.copy_[t] = le(n, e)) : n;
|
|
336
|
+
},
|
|
337
|
+
has(e, t) {
|
|
338
|
+
return t in M(e);
|
|
339
|
+
},
|
|
340
|
+
ownKeys(e) {
|
|
341
|
+
return Reflect.ownKeys(M(e));
|
|
342
|
+
},
|
|
343
|
+
set(e, t, r) {
|
|
344
|
+
const n = Ue(M(e), t);
|
|
345
|
+
if (n?.set)
|
|
346
|
+
return n.set.call(e.draft_, r), !0;
|
|
347
|
+
if (!e.modified_) {
|
|
348
|
+
const s = ee(M(e), t), o = s?.[O];
|
|
349
|
+
if (o && o.base_ === r)
|
|
350
|
+
return e.copy_[t] = r, e.assigned_[t] = !1, !0;
|
|
351
|
+
if (je(r, s) && (r !== void 0 || oe(e.base_, t)))
|
|
352
|
+
return !0;
|
|
353
|
+
te(e), ue(e);
|
|
354
|
+
}
|
|
355
|
+
return e.copy_[t] === r && // special case: handle new props with value 'undefined'
|
|
356
|
+
(r !== void 0 || t in e.copy_) || // special case: NaN
|
|
357
|
+
Number.isNaN(r) && Number.isNaN(e.copy_[t]) || (e.copy_[t] = r, e.assigned_[t] = !0), !0;
|
|
358
|
+
},
|
|
359
|
+
deleteProperty(e, t) {
|
|
360
|
+
return ee(e.base_, t) !== void 0 || t in e.base_ ? (e.assigned_[t] = !1, te(e), ue(e)) : delete e.assigned_[t], e.copy_ && delete e.copy_[t], !0;
|
|
361
|
+
},
|
|
362
|
+
// Note: We never coerce `desc.value` into an Immer draft, because we can't make
|
|
363
|
+
// the same guarantee in ES5 mode.
|
|
364
|
+
getOwnPropertyDescriptor(e, t) {
|
|
365
|
+
const r = M(e), n = Reflect.getOwnPropertyDescriptor(r, t);
|
|
366
|
+
return n && {
|
|
367
|
+
writable: !0,
|
|
368
|
+
configurable: e.type_ !== 1 || t !== "length",
|
|
369
|
+
enumerable: n.enumerable,
|
|
370
|
+
value: r[t]
|
|
371
|
+
};
|
|
372
|
+
},
|
|
373
|
+
defineProperty() {
|
|
374
|
+
T(11);
|
|
375
|
+
},
|
|
376
|
+
getPrototypeOf(e) {
|
|
377
|
+
return B(e.base_);
|
|
378
|
+
},
|
|
379
|
+
setPrototypeOf() {
|
|
380
|
+
T(12);
|
|
381
|
+
}
|
|
382
|
+
}, z = {};
|
|
383
|
+
q(me, (e, t) => {
|
|
384
|
+
z[e] = function() {
|
|
385
|
+
return arguments[0] = arguments[0][0], t.apply(this, arguments);
|
|
386
|
+
};
|
|
387
|
+
});
|
|
388
|
+
z.deleteProperty = function(e, t) {
|
|
389
|
+
return process.env.NODE_ENV !== "production" && isNaN(parseInt(t)) && T(13), z.set.call(this, e, t, void 0);
|
|
390
|
+
};
|
|
391
|
+
z.set = function(e, t, r) {
|
|
392
|
+
return process.env.NODE_ENV !== "production" && t !== "length" && isNaN(parseInt(t)) && T(14), me.set.call(this, e[0], t, r, e[0]);
|
|
393
|
+
};
|
|
394
|
+
function ee(e, t) {
|
|
395
|
+
const r = e[O];
|
|
396
|
+
return (r ? M(r) : e)[t];
|
|
397
|
+
}
|
|
398
|
+
function Ve(e, t, r) {
|
|
399
|
+
const n = Ue(t, r);
|
|
400
|
+
return n ? "value" in n ? n.value : (
|
|
401
|
+
// This is a very special case, if the prop is a getter defined by the
|
|
402
|
+
// prototype, we should invoke it with the draft as context!
|
|
403
|
+
n.get?.call(e.draft_)
|
|
404
|
+
) : void 0;
|
|
405
|
+
}
|
|
406
|
+
function Ue(e, t) {
|
|
407
|
+
if (!(t in e))
|
|
408
|
+
return;
|
|
409
|
+
let r = B(e);
|
|
410
|
+
for (; r; ) {
|
|
411
|
+
const n = Object.getOwnPropertyDescriptor(r, t);
|
|
412
|
+
if (n)
|
|
413
|
+
return n;
|
|
414
|
+
r = B(r);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
function ue(e) {
|
|
418
|
+
e.modified_ || (e.modified_ = !0, e.parent_ && ue(e.parent_));
|
|
419
|
+
}
|
|
420
|
+
function te(e) {
|
|
421
|
+
e.copy_ || (e.copy_ = ie(
|
|
422
|
+
e.base_,
|
|
423
|
+
e.scope_.immer_.useStrictShallowCopy_
|
|
424
|
+
));
|
|
425
|
+
}
|
|
426
|
+
var Xe = class {
|
|
427
|
+
constructor(e) {
|
|
428
|
+
this.autoFreeze_ = !0, this.useStrictShallowCopy_ = !1, this.useStrictIteration_ = !0, this.produce = (t, r, n) => {
|
|
429
|
+
if (typeof t == "function" && typeof r != "function") {
|
|
430
|
+
const o = r;
|
|
431
|
+
r = t;
|
|
432
|
+
const a = this;
|
|
433
|
+
return function(d = o, ...y) {
|
|
434
|
+
return a.produce(d, (p) => r.call(this, p, ...y));
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
typeof r != "function" && T(6), n !== void 0 && typeof n != "function" && T(7);
|
|
438
|
+
let s;
|
|
439
|
+
if (U(t)) {
|
|
440
|
+
const o = be(this), a = le(t, void 0);
|
|
441
|
+
let c = !0;
|
|
442
|
+
try {
|
|
443
|
+
s = r(a), c = !1;
|
|
444
|
+
} finally {
|
|
445
|
+
c ? ae(o) : ce(o);
|
|
446
|
+
}
|
|
447
|
+
return he(o, n), _e(s, o);
|
|
448
|
+
} else if (!t || typeof t != "object") {
|
|
449
|
+
if (s = r(t), s === void 0 && (s = t), s === Ae && (s = void 0), this.autoFreeze_ && fe(s, !0), n) {
|
|
450
|
+
const o = [], a = [];
|
|
451
|
+
P("Patches").generateReplacementPatches_(t, s, o, a), n(o, a);
|
|
452
|
+
}
|
|
453
|
+
return s;
|
|
454
|
+
} else
|
|
455
|
+
T(1, t);
|
|
456
|
+
}, this.produceWithPatches = (t, r) => {
|
|
457
|
+
if (typeof t == "function")
|
|
458
|
+
return (a, ...c) => this.produceWithPatches(a, (d) => t(d, ...c));
|
|
459
|
+
let n, s;
|
|
460
|
+
return [this.produce(t, r, (a, c) => {
|
|
461
|
+
n = a, s = c;
|
|
462
|
+
}), n, s];
|
|
463
|
+
}, typeof e?.autoFreeze == "boolean" && this.setAutoFreeze(e.autoFreeze), typeof e?.useStrictShallowCopy == "boolean" && this.setUseStrictShallowCopy(e.useStrictShallowCopy), typeof e?.useStrictIteration == "boolean" && this.setUseStrictIteration(e.useStrictIteration);
|
|
464
|
+
}
|
|
465
|
+
createDraft(e) {
|
|
466
|
+
U(e) || T(8), N(e) && (e = Ze(e));
|
|
467
|
+
const t = be(this), r = le(e, void 0);
|
|
468
|
+
return r[O].isManual_ = !0, ce(t), r;
|
|
469
|
+
}
|
|
470
|
+
finishDraft(e, t) {
|
|
471
|
+
const r = e && e[O];
|
|
472
|
+
(!r || !r.isManual_) && T(9);
|
|
473
|
+
const { scope_: n } = r;
|
|
474
|
+
return he(n, t), _e(void 0, n);
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Pass true to automatically freeze all copies created by Immer.
|
|
478
|
+
*
|
|
479
|
+
* By default, auto-freezing is enabled.
|
|
480
|
+
*/
|
|
481
|
+
setAutoFreeze(e) {
|
|
482
|
+
this.autoFreeze_ = e;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Pass true to enable strict shallow copy.
|
|
486
|
+
*
|
|
487
|
+
* By default, immer does not copy the object descriptors such as getter, setter and non-enumrable properties.
|
|
488
|
+
*/
|
|
489
|
+
setUseStrictShallowCopy(e) {
|
|
490
|
+
this.useStrictShallowCopy_ = e;
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Pass false to use faster iteration that skips non-enumerable properties
|
|
494
|
+
* but still handles symbols for compatibility.
|
|
495
|
+
*
|
|
496
|
+
* By default, strict iteration is enabled (includes all own properties).
|
|
497
|
+
*/
|
|
498
|
+
setUseStrictIteration(e) {
|
|
499
|
+
this.useStrictIteration_ = e;
|
|
500
|
+
}
|
|
501
|
+
shouldUseStrictIteration() {
|
|
502
|
+
return this.useStrictIteration_;
|
|
503
|
+
}
|
|
504
|
+
applyPatches(e, t) {
|
|
505
|
+
let r;
|
|
506
|
+
for (r = t.length - 1; r >= 0; r--) {
|
|
507
|
+
const s = t[r];
|
|
508
|
+
if (s.path.length === 0 && s.op === "replace") {
|
|
509
|
+
e = s.value;
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
r > -1 && (t = t.slice(r + 1));
|
|
514
|
+
const n = P("Patches").applyPatches_;
|
|
515
|
+
return N(e) ? n(e, t) : this.produce(
|
|
516
|
+
e,
|
|
517
|
+
(s) => n(s, t)
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
function le(e, t) {
|
|
522
|
+
const r = H(e) ? P("MapSet").proxyMap_(e, t) : X(e) ? P("MapSet").proxySet_(e, t) : Ye(e, t);
|
|
523
|
+
return (t ? t.scope_ : Ce()).drafts_.push(r), r;
|
|
524
|
+
}
|
|
525
|
+
function Ze(e) {
|
|
526
|
+
return N(e) || T(10, e), Pe(e);
|
|
527
|
+
}
|
|
528
|
+
function Pe(e) {
|
|
529
|
+
if (!U(e) || Z(e))
|
|
530
|
+
return e;
|
|
531
|
+
const t = e[O];
|
|
532
|
+
let r, n = !0;
|
|
533
|
+
if (t) {
|
|
534
|
+
if (!t.modified_)
|
|
535
|
+
return t.base_;
|
|
536
|
+
t.finalized_ = !0, r = ie(e, t.scope_.immer_.useStrictShallowCopy_), n = t.scope_.immer_.shouldUseStrictIteration();
|
|
537
|
+
} else
|
|
538
|
+
r = ie(e, !0);
|
|
539
|
+
return q(
|
|
540
|
+
r,
|
|
541
|
+
(s, o) => {
|
|
542
|
+
Me(r, s, Pe(o));
|
|
543
|
+
},
|
|
544
|
+
n
|
|
545
|
+
), t && (t.finalized_ = !1), r;
|
|
546
|
+
}
|
|
547
|
+
var Je = new Xe(), C = Je.produce;
|
|
548
|
+
const E = [];
|
|
549
|
+
for (let e = 0; e < 256; ++e)
|
|
550
|
+
E.push((e + 256).toString(16).slice(1));
|
|
551
|
+
function Ke(e, t = 0) {
|
|
552
|
+
return (E[e[t + 0]] + E[e[t + 1]] + E[e[t + 2]] + E[e[t + 3]] + "-" + E[e[t + 4]] + E[e[t + 5]] + "-" + E[e[t + 6]] + E[e[t + 7]] + "-" + E[e[t + 8]] + E[e[t + 9]] + "-" + E[e[t + 10]] + E[e[t + 11]] + E[e[t + 12]] + E[e[t + 13]] + E[e[t + 14]] + E[e[t + 15]]).toLowerCase();
|
|
553
|
+
}
|
|
554
|
+
let re;
|
|
555
|
+
const et = new Uint8Array(16);
|
|
556
|
+
function tt() {
|
|
557
|
+
if (!re) {
|
|
558
|
+
if (typeof crypto > "u" || !crypto.getRandomValues)
|
|
559
|
+
throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
|
|
560
|
+
re = crypto.getRandomValues.bind(crypto);
|
|
561
|
+
}
|
|
562
|
+
return re(et);
|
|
563
|
+
}
|
|
564
|
+
const rt = typeof crypto < "u" && crypto.randomUUID && crypto.randomUUID.bind(crypto), pe = { randomUUID: rt };
|
|
565
|
+
function nt(e, t, r) {
|
|
566
|
+
e = e || {};
|
|
567
|
+
const n = e.random ?? e.rng?.() ?? tt();
|
|
568
|
+
if (n.length < 16)
|
|
569
|
+
throw new Error("Random bytes length must be >= 16");
|
|
570
|
+
return n[6] = n[6] & 15 | 64, n[8] = n[8] & 63 | 128, Ke(n);
|
|
571
|
+
}
|
|
572
|
+
function Ne(e, t, r) {
|
|
573
|
+
return pe.randomUUID && !e ? pe.randomUUID() : nt(e);
|
|
574
|
+
}
|
|
575
|
+
function Ee(e, t, r) {
|
|
576
|
+
if (r.items.length === 0)
|
|
577
|
+
return;
|
|
578
|
+
const n = r.items.map((s) => {
|
|
579
|
+
const o = t().messagesPublisher;
|
|
580
|
+
return o(s.value), s.value;
|
|
581
|
+
});
|
|
582
|
+
e((s) => C(s, (o) => {
|
|
583
|
+
const a = s.rooms.filter(
|
|
584
|
+
(c) => c.collectionId !== n[0].collectionId
|
|
585
|
+
);
|
|
586
|
+
return o.rooms = a.concat(n), o.roomsLoading = !1, o;
|
|
587
|
+
}));
|
|
588
|
+
}
|
|
589
|
+
async function ne({
|
|
590
|
+
ditto: e,
|
|
591
|
+
currentUserId: t,
|
|
592
|
+
name: r,
|
|
593
|
+
collectionId: n,
|
|
594
|
+
messagesId: s,
|
|
595
|
+
participants: o = [],
|
|
596
|
+
retentionDays: a,
|
|
597
|
+
isGenerated: c = !1,
|
|
598
|
+
id: d
|
|
599
|
+
}) {
|
|
600
|
+
if (e)
|
|
601
|
+
try {
|
|
602
|
+
const p = {
|
|
603
|
+
_id: d || Ne(),
|
|
604
|
+
name: r,
|
|
605
|
+
messagesId: s,
|
|
606
|
+
collectionId: n,
|
|
607
|
+
isGenerated: c,
|
|
608
|
+
createdBy: t,
|
|
609
|
+
createdOn: (/* @__PURE__ */ new Date()).toISOString(),
|
|
610
|
+
participants: o || void 0,
|
|
611
|
+
...a !== void 0 && { retentionDays: a }
|
|
612
|
+
}, S = `INSERT INTO \`${n}\` DOCUMENTS (:newDoc) ON ID CONFLICT DO UPDATE`;
|
|
613
|
+
return await e.store.execute(S, { newDoc: p }), p;
|
|
614
|
+
} catch (y) {
|
|
615
|
+
console.error(`Error creating ${n}:`, y);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
const st = (e, t, { ditto: r, userId: n }) => {
|
|
619
|
+
const s = {
|
|
620
|
+
rooms: [],
|
|
621
|
+
dmRooms: [],
|
|
622
|
+
generatedRooms: [],
|
|
623
|
+
roomsLoading: !0,
|
|
624
|
+
roomsObserver: null,
|
|
625
|
+
roomsSubscription: null,
|
|
626
|
+
dmRoomsObserver: null,
|
|
627
|
+
dmRoomsSubscription: null,
|
|
628
|
+
/**
|
|
629
|
+
* Creates a new public room with RBAC permission checks.
|
|
630
|
+
*
|
|
631
|
+
* This function creates a new regular (non-DM) room in the chat application.
|
|
632
|
+
* The creation process includes:
|
|
633
|
+
*
|
|
634
|
+
* 1. **Permission Check**:
|
|
635
|
+
* - Verifies the user has "canCreateRoom" permission via RBAC
|
|
636
|
+
* - Returns undefined immediately if permission is denied
|
|
637
|
+
* - Logs a warning for debugging permission issues
|
|
638
|
+
*
|
|
639
|
+
* 2. **User Context**:
|
|
640
|
+
* - Retrieves the current user from state
|
|
641
|
+
* - Falls back to the userId parameter if currentUser is not available
|
|
642
|
+
* - Ensures proper creator attribution
|
|
643
|
+
*
|
|
644
|
+
* 3. **Room Creation**:
|
|
645
|
+
* - Delegates to createRoomBase with appropriate parameters
|
|
646
|
+
* - Sets collectionId to "rooms" for regular rooms
|
|
647
|
+
* - Sets messagesId to "messages" collection
|
|
648
|
+
* - Passes through optional retentionDays parameter
|
|
649
|
+
*
|
|
650
|
+
* This approach ensures:
|
|
651
|
+
* - RBAC integration prevents unauthorized room creation
|
|
652
|
+
* - Consistent room creation through shared base function
|
|
653
|
+
* - Optional custom retention policies per room
|
|
654
|
+
*
|
|
655
|
+
* @param name - Display name for the new room
|
|
656
|
+
* @param options - Optional configuration for the room
|
|
657
|
+
* @param options.retentionDays - Optional custom message retention period (overrides global default)
|
|
658
|
+
* @param options.isGenerated - Optional flag to mark room as generated (hidden from main list)
|
|
659
|
+
* @returns Promise resolving to the created Room object, or undefined if permission denied
|
|
660
|
+
*/
|
|
661
|
+
createRoom(o, a) {
|
|
662
|
+
if (!t().canPerformAction("canCreateRoom"))
|
|
663
|
+
return console.warn("Permission denied: canCreateRoom is false"), Promise.resolve(void 0);
|
|
664
|
+
const c = t().currentUser;
|
|
665
|
+
return ne({
|
|
666
|
+
ditto: r,
|
|
667
|
+
currentUserId: c?._id || n,
|
|
668
|
+
name: o,
|
|
669
|
+
collectionId: "rooms",
|
|
670
|
+
messagesId: "messages",
|
|
671
|
+
retentionDays: a?.retentionDays,
|
|
672
|
+
isGenerated: a?.isGenerated ?? !1
|
|
673
|
+
});
|
|
674
|
+
},
|
|
675
|
+
/**
|
|
676
|
+
* Creates a new direct message (DM) room between two users.
|
|
677
|
+
*
|
|
678
|
+
* This function creates a private one-on-one chat room between the current
|
|
679
|
+
* user and another user. The creation process includes:
|
|
680
|
+
*
|
|
681
|
+
* 1. **Validation**:
|
|
682
|
+
* - Ensures both current user and DM user have valid IDs
|
|
683
|
+
* - Throws an error if either user ID is missing
|
|
684
|
+
* - Prevents creation of invalid DM rooms
|
|
685
|
+
*
|
|
686
|
+
* 2. **Room Naming**:
|
|
687
|
+
* - Generates a room name combining both users' names
|
|
688
|
+
* - Format: "CurrentUser & OtherUser"
|
|
689
|
+
* - Provides clear identification of DM participants
|
|
690
|
+
*
|
|
691
|
+
* 3. **DM Room Creation**:
|
|
692
|
+
* - Delegates to createRoomBase with DM-specific parameters
|
|
693
|
+
* - Sets collectionId to "dm_rooms" for DM storage
|
|
694
|
+
* - Sets messagesId to "dm_messages" collection
|
|
695
|
+
* - Includes both user IDs in participants array
|
|
696
|
+
*
|
|
697
|
+
* This approach ensures:
|
|
698
|
+
* - Proper participant tracking for DM rooms
|
|
699
|
+
* - Separation of DM rooms from regular rooms in storage
|
|
700
|
+
* - User-friendly room naming for DM conversations
|
|
701
|
+
*
|
|
702
|
+
* @param dmUser - The ChatUser to create a DM room with
|
|
703
|
+
* @returns Promise resolving to the created DM Room object
|
|
704
|
+
* @throws Error if either user ID is invalid or missing
|
|
705
|
+
*/
|
|
706
|
+
createDMRoom(o) {
|
|
707
|
+
const a = t().currentUser;
|
|
708
|
+
if (!a?._id || !o?._id)
|
|
709
|
+
throw Error("Invalid users");
|
|
710
|
+
return ne({
|
|
711
|
+
ditto: r,
|
|
712
|
+
currentUserId: a?._id || n,
|
|
713
|
+
name: `${a?.name} & ${o.name}`,
|
|
714
|
+
collectionId: "dm_rooms",
|
|
715
|
+
messagesId: "dm_messages",
|
|
716
|
+
participants: [a?._id, o._id]
|
|
717
|
+
});
|
|
718
|
+
},
|
|
719
|
+
/**
|
|
720
|
+
* Creates a generated room (comment room) with a specific ID.
|
|
721
|
+
*
|
|
722
|
+
* Generated rooms are used for programmatic use cases like comment threads
|
|
723
|
+
* on products, articles, or other entities. They are hidden from the main
|
|
724
|
+
* chat list but can be accessed directly when needed.
|
|
725
|
+
*
|
|
726
|
+
* @param id - Custom ID for the room (e.g., "comments-product-123")
|
|
727
|
+
* @param name - Display name for the room
|
|
728
|
+
* @returns Promise resolving to the created Room object
|
|
729
|
+
*/
|
|
730
|
+
async createGeneratedRoom(o, a) {
|
|
731
|
+
const c = t().currentUser, d = await ne({
|
|
732
|
+
ditto: r,
|
|
733
|
+
currentUserId: c?._id || n,
|
|
734
|
+
name: a,
|
|
735
|
+
collectionId: "rooms",
|
|
736
|
+
messagesId: "messages",
|
|
737
|
+
isGenerated: !0,
|
|
738
|
+
id: o
|
|
739
|
+
});
|
|
740
|
+
return d && e((y) => y.generatedRooms.some((S) => S._id === d._id) ? y : {
|
|
741
|
+
...y,
|
|
742
|
+
generatedRooms: [...y.generatedRooms, d]
|
|
743
|
+
}), d;
|
|
744
|
+
},
|
|
745
|
+
/**
|
|
746
|
+
* Returns all public rooms, filtering out generated rooms.
|
|
747
|
+
*
|
|
748
|
+
* This selector provides the list of rooms that should be displayed
|
|
749
|
+
* in the main chat list, excluding generated/comment rooms.
|
|
750
|
+
*
|
|
751
|
+
* @returns Array of non-generated rooms
|
|
752
|
+
*/
|
|
753
|
+
getPublicRooms() {
|
|
754
|
+
return t().rooms.filter((o) => !o.isGenerated);
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
if (r) {
|
|
758
|
+
const o = "SELECT * from rooms WHERE isGenerated = false OR isGenerated IS NULL", a = "SELECT * from dm_rooms where (array_contains(participants, :userId))";
|
|
759
|
+
s.roomsSubscription = r.sync.registerSubscription(o), s.dmRoomsSubscription = r.sync.registerSubscription(a, {
|
|
760
|
+
userId: n
|
|
761
|
+
}), s.roomsObserver = r.store.registerObserver(
|
|
762
|
+
o,
|
|
763
|
+
(c) => {
|
|
764
|
+
Ee(e, t, c);
|
|
765
|
+
}
|
|
766
|
+
), s.dmRoomsObserver = r.store.registerObserver(
|
|
767
|
+
a,
|
|
768
|
+
(c) => {
|
|
769
|
+
Ee(e, t, c);
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
userId: n
|
|
773
|
+
}
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
return s;
|
|
777
|
+
}, Te = (e) => {
|
|
778
|
+
let t;
|
|
779
|
+
const r = /* @__PURE__ */ new Set(), n = (y, p) => {
|
|
780
|
+
const S = typeof y == "function" ? y(t) : y;
|
|
781
|
+
if (!Object.is(S, t)) {
|
|
782
|
+
const x = t;
|
|
783
|
+
t = p ?? (typeof S != "object" || S === null) ? S : Object.assign({}, t, S), r.forEach((L) => L(t, x));
|
|
784
|
+
}
|
|
785
|
+
}, s = () => t, c = { setState: n, getState: s, getInitialState: () => d, subscribe: (y) => (r.add(y), () => r.delete(y)) }, d = t = e(n, s, c);
|
|
786
|
+
return c;
|
|
787
|
+
}, ot = ((e) => e ? Te(e) : Te), it = (e) => e;
|
|
788
|
+
function xe(e, t = it) {
|
|
789
|
+
const r = k.useSyncExternalStore(
|
|
790
|
+
e.subscribe,
|
|
791
|
+
k.useCallback(() => t(e.getState()), [e, t]),
|
|
792
|
+
k.useCallback(() => t(e.getInitialState()), [e, t])
|
|
793
|
+
);
|
|
794
|
+
return k.useDebugValue(r), r;
|
|
795
|
+
}
|
|
796
|
+
const Se = (e) => Symbol.iterator in e, Oe = (e) => (
|
|
797
|
+
// HACK: avoid checking entries type
|
|
798
|
+
"entries" in e
|
|
799
|
+
), Re = (e, t) => {
|
|
800
|
+
const r = e instanceof Map ? e : new Map(e.entries()), n = t instanceof Map ? t : new Map(t.entries());
|
|
801
|
+
if (r.size !== n.size)
|
|
802
|
+
return !1;
|
|
803
|
+
for (const [s, o] of r)
|
|
804
|
+
if (!n.has(s) || !Object.is(o, n.get(s)))
|
|
805
|
+
return !1;
|
|
806
|
+
return !0;
|
|
807
|
+
}, at = (e, t) => {
|
|
808
|
+
const r = e[Symbol.iterator](), n = t[Symbol.iterator]();
|
|
809
|
+
let s = r.next(), o = n.next();
|
|
810
|
+
for (; !s.done && !o.done; ) {
|
|
811
|
+
if (!Object.is(s.value, o.value))
|
|
812
|
+
return !1;
|
|
813
|
+
s = r.next(), o = n.next();
|
|
814
|
+
}
|
|
815
|
+
return !!s.done && !!o.done;
|
|
816
|
+
};
|
|
817
|
+
function ct(e, t) {
|
|
818
|
+
return Object.is(e, t) ? !0 : typeof e != "object" || e === null || typeof t != "object" || t === null || Object.getPrototypeOf(e) !== Object.getPrototypeOf(t) ? !1 : Se(e) && Se(t) ? Oe(e) && Oe(t) ? Re(e, t) : at(e, t) : Re(
|
|
819
|
+
{ entries: () => Object.entries(e) },
|
|
820
|
+
{ entries: () => Object.entries(t) }
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
function ut(e) {
|
|
824
|
+
const t = k.useRef(void 0);
|
|
825
|
+
return (r) => {
|
|
826
|
+
const n = e(r);
|
|
827
|
+
return ct(t.current, n) ? t.current : t.current = n;
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
const lt = (e, t, { ditto: r, userId: n, userCollectionKey: s }) => {
|
|
831
|
+
const o = {
|
|
832
|
+
usersLoading: !0,
|
|
833
|
+
currentUser: null,
|
|
834
|
+
userObserver: null,
|
|
835
|
+
userSubscription: null,
|
|
836
|
+
allUsers: [],
|
|
837
|
+
allUsersObserver: null,
|
|
838
|
+
allUsersSubscription: null,
|
|
839
|
+
/**
|
|
840
|
+
* Add a new user to the users collection.
|
|
841
|
+
*
|
|
842
|
+
* This method creates or updates a user document in the Ditto store.
|
|
843
|
+
* Uses upsert behavior to handle both new and existing users.
|
|
844
|
+
*
|
|
845
|
+
* Workflow:
|
|
846
|
+
* 1. Validates Ditto is initialized
|
|
847
|
+
* 2. Executes INSERT with ON ID CONFLICT DO UPDATE
|
|
848
|
+
* 3. Creates new user or updates existing if ID matches
|
|
849
|
+
*
|
|
850
|
+
* @param user - User object (with optional _id for upsert)
|
|
851
|
+
*/
|
|
852
|
+
async addUser(a) {
|
|
853
|
+
if (r)
|
|
854
|
+
try {
|
|
855
|
+
await r.store.execute(
|
|
856
|
+
`INSERT INTO ${s} DOCUMENTS (:newUser) ON ID CONFLICT DO UPDATE`,
|
|
857
|
+
{ newUser: a }
|
|
858
|
+
);
|
|
859
|
+
} catch (c) {
|
|
860
|
+
console.error("Error in addUser:", c);
|
|
861
|
+
}
|
|
862
|
+
},
|
|
863
|
+
/**
|
|
864
|
+
* Update an existing user's fields.
|
|
865
|
+
*
|
|
866
|
+
* This method merges patch data with existing user data and persists.
|
|
867
|
+
* Only the specified fields are updated; others remain unchanged.
|
|
868
|
+
*
|
|
869
|
+
* Workflow:
|
|
870
|
+
* 1. Validates Ditto and user ID
|
|
871
|
+
* 2. Fetches current user data
|
|
872
|
+
* 3. Merges patch with existing user
|
|
873
|
+
* 4. Persists updated user document
|
|
874
|
+
*
|
|
875
|
+
* @param params - Object with _id and fields to update
|
|
876
|
+
*/
|
|
877
|
+
async updateUser({ _id: a, ...c }) {
|
|
878
|
+
if (r && a)
|
|
879
|
+
try {
|
|
880
|
+
const d = await t().findUserById(a);
|
|
881
|
+
if (!d)
|
|
882
|
+
return;
|
|
883
|
+
const y = { ...d, ...c };
|
|
884
|
+
await r.store.execute(
|
|
885
|
+
`INSERT INTO ${s} DOCUMENTS (:newUser) ON ID CONFLICT DO UPDATE`,
|
|
886
|
+
{ newUser: y }
|
|
887
|
+
);
|
|
888
|
+
} catch (d) {
|
|
889
|
+
console.error("Error in updateUser:", d);
|
|
890
|
+
}
|
|
891
|
+
},
|
|
892
|
+
/**
|
|
893
|
+
* Find a user by their unique ID.
|
|
894
|
+
*
|
|
895
|
+
* This method queries the users collection for a specific user.
|
|
896
|
+
*
|
|
897
|
+
* Workflow:
|
|
898
|
+
* 1. Validates Ditto is initialized
|
|
899
|
+
* 2. Executes SELECT query with user ID
|
|
900
|
+
* 3. Returns user object or null if not found
|
|
901
|
+
*
|
|
902
|
+
* @param userId - The unique identifier of the user
|
|
903
|
+
* @returns Promise resolving to ChatUser or null
|
|
904
|
+
*/
|
|
905
|
+
async findUserById(a) {
|
|
906
|
+
if (!r)
|
|
907
|
+
return null;
|
|
908
|
+
try {
|
|
909
|
+
return (await r.store.execute(
|
|
910
|
+
`SELECT * FROM ${s} WHERE _id = :id`,
|
|
911
|
+
{ id: a }
|
|
912
|
+
)).items?.[0]?.value;
|
|
913
|
+
} catch (c) {
|
|
914
|
+
return console.error("Error in findUserById:", c), null;
|
|
915
|
+
}
|
|
916
|
+
},
|
|
917
|
+
/**
|
|
918
|
+
* Mark a room's messages as read for the current user.
|
|
919
|
+
*
|
|
920
|
+
* This method updates the user's subscription timestamp and clears
|
|
921
|
+
* any pending mentions for the specified room.
|
|
922
|
+
*
|
|
923
|
+
* Workflow:
|
|
924
|
+
* 1. Validates Ditto and user ID
|
|
925
|
+
* 2. Fetches current user data
|
|
926
|
+
* 3. Updates subscription timestamp if subscribed
|
|
927
|
+
* 4. Clears mentions array for the room
|
|
928
|
+
* 5. Persists updated user document
|
|
929
|
+
*
|
|
930
|
+
* @param roomId - ID of the room to mark as read
|
|
931
|
+
*/
|
|
932
|
+
async markRoomAsRead(a) {
|
|
933
|
+
if (!(!r || !n))
|
|
934
|
+
try {
|
|
935
|
+
let c = !1;
|
|
936
|
+
const d = await t().findUserById(n);
|
|
937
|
+
if (!d)
|
|
938
|
+
return;
|
|
939
|
+
const y = d.subscriptions || {}, p = d.mentions || {};
|
|
940
|
+
if (y[a]) {
|
|
941
|
+
const S = (/* @__PURE__ */ new Date()).toISOString();
|
|
942
|
+
y[a] = S, c = !0;
|
|
943
|
+
}
|
|
944
|
+
p[a] && (p[a] = [], c = !0), c && await t().updateUser({ _id: n, subscriptions: y, mentions: p });
|
|
945
|
+
} catch (c) {
|
|
946
|
+
console.error("Error in markRoomAsRead:", c);
|
|
947
|
+
}
|
|
948
|
+
},
|
|
949
|
+
/**
|
|
950
|
+
* Toggle subscription status for a room.
|
|
951
|
+
*
|
|
952
|
+
* This method subscribes to or unsubscribes from a room's notifications.
|
|
953
|
+
* When subscribed, user receives notifications for new messages.
|
|
954
|
+
*
|
|
955
|
+
* Workflow:
|
|
956
|
+
* 1. Checks canSubscribeToRoom permission
|
|
957
|
+
* 2. Fetches current user data
|
|
958
|
+
* 3. Toggles subscription (null = unsubscribed, timestamp = subscribed)
|
|
959
|
+
* 4. Persists updated subscriptions map
|
|
960
|
+
*
|
|
961
|
+
* @param roomId - ID of the room to toggle subscription for
|
|
962
|
+
*/
|
|
963
|
+
async toggleRoomSubscription(a) {
|
|
964
|
+
if (!(!r || !n)) {
|
|
965
|
+
if (!t().canPerformAction("canSubscribeToRoom")) {
|
|
966
|
+
console.warn("Permission denied: canSubscribeToRoom is false");
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
try {
|
|
970
|
+
const c = await t().findUserById(n);
|
|
971
|
+
if (!c)
|
|
972
|
+
return;
|
|
973
|
+
const d = { ...c.subscriptions };
|
|
974
|
+
if (a in d && d[a] !== null)
|
|
975
|
+
d[a] = null;
|
|
976
|
+
else {
|
|
977
|
+
const y = (/* @__PURE__ */ new Date()).toISOString();
|
|
978
|
+
d[a] = y;
|
|
979
|
+
}
|
|
980
|
+
await t().updateUser({
|
|
981
|
+
_id: n,
|
|
982
|
+
subscriptions: d
|
|
983
|
+
});
|
|
984
|
+
} catch (c) {
|
|
985
|
+
console.error("Error in toggleRoomSubscription:", c);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
};
|
|
990
|
+
if (r) {
|
|
991
|
+
const a = `SELECT * FROM ${s} WHERE _id = :id`, c = `SELECT * FROM ${s}`, d = { id: n };
|
|
992
|
+
o.userSubscription = r.sync.registerSubscription(
|
|
993
|
+
a,
|
|
994
|
+
d
|
|
995
|
+
), o.userObserver = r.store.registerObserver(
|
|
996
|
+
a,
|
|
997
|
+
(y) => {
|
|
998
|
+
e({ currentUser: y.items?.[0]?.value });
|
|
999
|
+
},
|
|
1000
|
+
d
|
|
1001
|
+
), o.allUsersSubscription = r.sync.registerSubscription(c), o.allUsersObserver = r.store.registerObserver(
|
|
1002
|
+
c,
|
|
1003
|
+
(y) => {
|
|
1004
|
+
e({
|
|
1005
|
+
allUsers: y.items.map((p) => p.value),
|
|
1006
|
+
usersLoading: !1
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
);
|
|
1010
|
+
}
|
|
1011
|
+
return o;
|
|
1012
|
+
}, Ie = 30, ft = 1e4, G = 282, mt = (e, t, {
|
|
1013
|
+
ditto: r,
|
|
1014
|
+
userId: n,
|
|
1015
|
+
userCollectionKey: s,
|
|
1016
|
+
retentionDays: o,
|
|
1017
|
+
notificationHandler: a
|
|
1018
|
+
}) => {
|
|
1019
|
+
const c = async (i) => {
|
|
1020
|
+
if (!r)
|
|
1021
|
+
throw new Error("Ditto not initialized");
|
|
1022
|
+
const u = await r.store.execute(
|
|
1023
|
+
`SELECT * FROM ${i.collectionId || "rooms"} WHERE _id = :id`,
|
|
1024
|
+
{ id: i._id }
|
|
1025
|
+
);
|
|
1026
|
+
if (u.items.length === 0)
|
|
1027
|
+
throw new Error("Room not found");
|
|
1028
|
+
return u.items[0].value;
|
|
1029
|
+
}, d = async () => {
|
|
1030
|
+
if (!r || !n)
|
|
1031
|
+
throw new Error("Ditto not initialized or user not found");
|
|
1032
|
+
const u = (await r.store.execute(
|
|
1033
|
+
`SELECT * FROM ${s} WHERE _id = :id`,
|
|
1034
|
+
{ id: n }
|
|
1035
|
+
)).items?.[0]?.value;
|
|
1036
|
+
return {
|
|
1037
|
+
id: n,
|
|
1038
|
+
name: u?.name ?? n
|
|
1039
|
+
};
|
|
1040
|
+
}, y = () => {
|
|
1041
|
+
e((i) => C(i, (u) => {
|
|
1042
|
+
u.messagesLoading = !u.rooms.every(
|
|
1043
|
+
(l) => l._id in u.messagesByRoom
|
|
1044
|
+
);
|
|
1045
|
+
}));
|
|
1046
|
+
}, p = (i, u, l, f) => {
|
|
1047
|
+
const m = {
|
|
1048
|
+
message: l,
|
|
1049
|
+
user: f,
|
|
1050
|
+
id: l._id
|
|
1051
|
+
}, g = {
|
|
1052
|
+
...m,
|
|
1053
|
+
message: m.message,
|
|
1054
|
+
user: m.user
|
|
1055
|
+
}, b = i.findIndex((h) => h.id === l._id);
|
|
1056
|
+
if (b === -1 && !l.archivedMessage && S(l, u) && a && t().activeRoomId !== u._id) {
|
|
1057
|
+
const _ = f?.name || "Unknown User", w = u.name, D = u.collectionId === "dm_rooms" ? `New message from ${_}` : `#${w}: ${_}`, A = l.text ? l.text.substring(0, 30) + (l.text.length > 30 ? "..." : "") : "Sent an attachment";
|
|
1058
|
+
a(D, A);
|
|
1059
|
+
}
|
|
1060
|
+
if (l.archivedMessage) {
|
|
1061
|
+
const h = i.findIndex(
|
|
1062
|
+
(_) => _.id === l.archivedMessage
|
|
1063
|
+
);
|
|
1064
|
+
h !== -1 ? i[h] = g : b === -1 ? i.push(g) : i[b] = g;
|
|
1065
|
+
} else
|
|
1066
|
+
b === -1 ? i.push(g) : i[b] = g;
|
|
1067
|
+
}, S = (i, u) => {
|
|
1068
|
+
const f = t().currentUser, m = i.userId === f?._id, g = new Date(i.createdOn).getTime() > Date.now() - ft, b = f?.subscriptions?.[u._id], h = i.mentions?.some(
|
|
1069
|
+
(w) => w.userId === f?._id
|
|
1070
|
+
), _ = u.collectionId === "dm_rooms";
|
|
1071
|
+
return !m && g && (b || _ || h);
|
|
1072
|
+
}, x = async (i, u, l = "") => {
|
|
1073
|
+
if (!r || !n)
|
|
1074
|
+
throw new Error("Ditto not initialized or user not found");
|
|
1075
|
+
const f = await c(i), m = u._id || Ne(), g = {
|
|
1076
|
+
_id: m,
|
|
1077
|
+
roomId: i._id,
|
|
1078
|
+
userId: n,
|
|
1079
|
+
createdOn: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1080
|
+
isArchived: !1,
|
|
1081
|
+
archivedMessage: null,
|
|
1082
|
+
largeImageToken: null,
|
|
1083
|
+
thumbnailImageToken: null,
|
|
1084
|
+
fileAttachmentToken: null,
|
|
1085
|
+
isEdited: !1,
|
|
1086
|
+
isDeleted: !1,
|
|
1087
|
+
...u,
|
|
1088
|
+
mentions: u.mentions?.map((h) => ({ ...h })) || [],
|
|
1089
|
+
reactions: u.reactions?.map((h) => ({ ...h })) || []
|
|
1090
|
+
}, b = l ? `COLLECTION ${f.messagesId} ${l}` : f.messagesId;
|
|
1091
|
+
if (await r.store.execute(
|
|
1092
|
+
`INSERT INTO ${b} DOCUMENTS (:newDoc) ON ID CONFLICT DO UPDATE`,
|
|
1093
|
+
{ newDoc: g }
|
|
1094
|
+
), u.mentions && u.mentions.length > 0) {
|
|
1095
|
+
const h = t().allUsers;
|
|
1096
|
+
await Promise.all(
|
|
1097
|
+
u.mentions.map((_) => {
|
|
1098
|
+
const w = h.find((D) => D._id === _.userId);
|
|
1099
|
+
if (!w)
|
|
1100
|
+
return;
|
|
1101
|
+
const R = {
|
|
1102
|
+
...w.mentions || {},
|
|
1103
|
+
[i._id]: [...w.mentions?.[i._id] || [], m]
|
|
1104
|
+
};
|
|
1105
|
+
return r.store.execute(
|
|
1106
|
+
"UPDATE users SET mentions = :mentions WHERE _id = :id",
|
|
1107
|
+
{ id: w._id, mentions: R }
|
|
1108
|
+
);
|
|
1109
|
+
})
|
|
1110
|
+
);
|
|
1111
|
+
}
|
|
1112
|
+
return g;
|
|
1113
|
+
}, L = async (i, u, l) => {
|
|
1114
|
+
if (!r)
|
|
1115
|
+
throw new Error("Ditto not initialized");
|
|
1116
|
+
return await r.store.execute(
|
|
1117
|
+
`UPDATE ${u.messagesId} SET isArchived = :isArchived WHERE _id = :id`,
|
|
1118
|
+
{ id: i._id, isArchived: !0 }
|
|
1119
|
+
), x(u, {
|
|
1120
|
+
text: "",
|
|
1121
|
+
createdOn: i.createdOn,
|
|
1122
|
+
archivedMessage: i._id,
|
|
1123
|
+
...l
|
|
1124
|
+
});
|
|
1125
|
+
}, de = async (i, u, l, f) => {
|
|
1126
|
+
if (!r || !n)
|
|
1127
|
+
throw new Error("Ditto not initialized or user not found");
|
|
1128
|
+
const m = await d(), g = {
|
|
1129
|
+
thumbnailImageToken: null,
|
|
1130
|
+
largeImageToken: null,
|
|
1131
|
+
fileAttachmentToken: null
|
|
1132
|
+
};
|
|
1133
|
+
if (f === "image") {
|
|
1134
|
+
const h = await dt(u), _ = await De(h), w = new Uint8Array(await _.arrayBuffer());
|
|
1135
|
+
g.thumbnailImageToken = await r.store.newAttachment(
|
|
1136
|
+
w,
|
|
1137
|
+
se(m.id, m.name, "thumbnail", u)
|
|
1138
|
+
);
|
|
1139
|
+
const R = await De(await ke(u)), D = new Uint8Array(await R.arrayBuffer());
|
|
1140
|
+
g.largeImageToken = await r.store.newAttachment(
|
|
1141
|
+
D,
|
|
1142
|
+
se(m.id, m.name, "large", u)
|
|
1143
|
+
);
|
|
1144
|
+
} else {
|
|
1145
|
+
const h = new Uint8Array(await u.arrayBuffer());
|
|
1146
|
+
g.fileAttachmentToken = await r.store.newAttachment(
|
|
1147
|
+
h,
|
|
1148
|
+
se(m.id, m.name, "file", u)
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1151
|
+
const b = f === "image" ? "(thumbnailImageToken ATTACHMENT, largeImageToken ATTACHMENT)" : "(fileAttachmentToken ATTACHMENT)";
|
|
1152
|
+
await x(
|
|
1153
|
+
i,
|
|
1154
|
+
{
|
|
1155
|
+
text: l || (f === "file" ? u.name : ""),
|
|
1156
|
+
...g
|
|
1157
|
+
},
|
|
1158
|
+
b
|
|
1159
|
+
);
|
|
1160
|
+
};
|
|
1161
|
+
return {
|
|
1162
|
+
messagesLoading: !0,
|
|
1163
|
+
messagesByRoom: {},
|
|
1164
|
+
messageObserversByRoom: {},
|
|
1165
|
+
messageSubscriptionsByRoom: {},
|
|
1166
|
+
notificationHandler: null,
|
|
1167
|
+
/**
|
|
1168
|
+
* Register a notification handler for new messages.
|
|
1169
|
+
*
|
|
1170
|
+
* This method sets up a callback to be invoked when new messages
|
|
1171
|
+
* arrive and meet notification criteria (subscribed rooms, mentions, DMs).
|
|
1172
|
+
*
|
|
1173
|
+
* Use case: Displaying toast notifications or browser notifications
|
|
1174
|
+
*
|
|
1175
|
+
* @param handler - Callback function receiving (title, preview) parameters
|
|
1176
|
+
*/
|
|
1177
|
+
registerNotificationHandler(i) {
|
|
1178
|
+
e({ notificationHandler: i });
|
|
1179
|
+
},
|
|
1180
|
+
/**
|
|
1181
|
+
* Subscribe to and observe messages for a room.
|
|
1182
|
+
*
|
|
1183
|
+
* This method sets up real-time sync and observation for messages in a room.
|
|
1184
|
+
* It's automatically called for each room when the chat store initializes.
|
|
1185
|
+
*
|
|
1186
|
+
* Workflow:
|
|
1187
|
+
* 1. Validates Ditto is initialized and no existing subscription
|
|
1188
|
+
* 2. Calculates retention date based on room/global settings
|
|
1189
|
+
* 3. Creates sync subscription for message documents
|
|
1190
|
+
* 4. Sets up store observer to update state on changes
|
|
1191
|
+
* 5. Triggers notifications for new messages from other users
|
|
1192
|
+
*
|
|
1193
|
+
* @param room - Room to subscribe to messages for
|
|
1194
|
+
* @param retentionDays - Optional override for message retention period
|
|
1195
|
+
*/
|
|
1196
|
+
async messagesPublisher(i, u) {
|
|
1197
|
+
if (!r)
|
|
1198
|
+
return;
|
|
1199
|
+
const l = i._id;
|
|
1200
|
+
if (t().messageSubscriptionsByRoom[l])
|
|
1201
|
+
return;
|
|
1202
|
+
const f = u ?? i.retentionDays ?? o ?? Ie, m = new Date(
|
|
1203
|
+
Date.now() - f * 24 * 60 * 60 * 1e3
|
|
1204
|
+
), g = `SELECT * FROM COLLECTION ${i.messagesId} (thumbnailImageToken ATTACHMENT, largeImageToken ATTACHMENT, fileAttachmentToken ATTACHMENT)
|
|
1205
|
+
WHERE roomId = :roomId AND createdOn >= :date AND isArchived = false
|
|
1206
|
+
ORDER BY createdOn ASC`, b = {
|
|
1207
|
+
roomId: i._id,
|
|
1208
|
+
date: m.toISOString()
|
|
1209
|
+
};
|
|
1210
|
+
try {
|
|
1211
|
+
const h = r.sync.registerSubscription(g, b), _ = t().allUsers, w = r.store.registerObserver(
|
|
1212
|
+
g,
|
|
1213
|
+
async (R) => {
|
|
1214
|
+
e((D) => C(D, (A) => {
|
|
1215
|
+
if (A.messagesByRoom[i._id] || (A.messagesByRoom[i._id] = []), R.items.length === 0)
|
|
1216
|
+
return A;
|
|
1217
|
+
for (const v of R.items) {
|
|
1218
|
+
const $ = v.value, j = A.messagesByRoom[i._id], J = _.find((K) => K._id === $.userId);
|
|
1219
|
+
p(j, i, $, J);
|
|
1220
|
+
}
|
|
1221
|
+
return A;
|
|
1222
|
+
})), y();
|
|
1223
|
+
},
|
|
1224
|
+
b
|
|
1225
|
+
);
|
|
1226
|
+
e({
|
|
1227
|
+
messageSubscriptionsByRoom: {
|
|
1228
|
+
...t().messageSubscriptionsByRoom,
|
|
1229
|
+
[l]: h
|
|
1230
|
+
},
|
|
1231
|
+
messageObserversByRoom: {
|
|
1232
|
+
...t().messageObserversByRoom,
|
|
1233
|
+
[l]: w
|
|
1234
|
+
}
|
|
1235
|
+
});
|
|
1236
|
+
} catch (h) {
|
|
1237
|
+
console.error("Error in messagesPublisher:", h);
|
|
1238
|
+
}
|
|
1239
|
+
},
|
|
1240
|
+
/**
|
|
1241
|
+
* Subscribe to messages for a specific room on-demand.
|
|
1242
|
+
*
|
|
1243
|
+
* This method enables dynamic subscription management for generated rooms.
|
|
1244
|
+
* Unlike messagesPublisher which is called automatically for all rooms,
|
|
1245
|
+
* this method is explicitly called when a component mounts to view a room.
|
|
1246
|
+
*
|
|
1247
|
+
* Workflow:
|
|
1248
|
+
* 1. Validates Ditto is initialized
|
|
1249
|
+
* 2. Checks for existing subscription (prevents duplicates)
|
|
1250
|
+
* 3. Creates subscription and observer using same logic as messagesPublisher
|
|
1251
|
+
* 4. Stores subscription/observer references in state
|
|
1252
|
+
*
|
|
1253
|
+
* Use case: ChatView component mounting to display a generated room
|
|
1254
|
+
*
|
|
1255
|
+
* @param roomId - Room ID to subscribe to
|
|
1256
|
+
* @param messagesId - Collection ID for messages ("messages" or "dm_messages")
|
|
1257
|
+
* @param retentionDays - Optional message retention override
|
|
1258
|
+
*/
|
|
1259
|
+
async subscribeToRoomMessages(i, u, l) {
|
|
1260
|
+
if (!r || t().messageSubscriptionsByRoom[i])
|
|
1261
|
+
return;
|
|
1262
|
+
const f = l ?? o ?? Ie, m = new Date(
|
|
1263
|
+
Date.now() - f * 24 * 60 * 60 * 1e3
|
|
1264
|
+
), g = `SELECT * FROM COLLECTION ${u} (thumbnailImageToken ATTACHMENT, largeImageToken ATTACHMENT, fileAttachmentToken ATTACHMENT)
|
|
1265
|
+
WHERE roomId = :roomId AND createdOn >= :date AND isArchived = false
|
|
1266
|
+
ORDER BY createdOn ASC`, b = {
|
|
1267
|
+
roomId: i,
|
|
1268
|
+
date: m.toISOString()
|
|
1269
|
+
};
|
|
1270
|
+
try {
|
|
1271
|
+
const h = r.sync.registerSubscription(g, b), _ = t().allUsers, w = r.store.registerObserver(
|
|
1272
|
+
g,
|
|
1273
|
+
async (R) => {
|
|
1274
|
+
const D = {
|
|
1275
|
+
_id: i,
|
|
1276
|
+
name: "",
|
|
1277
|
+
messagesId: u,
|
|
1278
|
+
collectionId: "rooms",
|
|
1279
|
+
createdBy: "",
|
|
1280
|
+
createdOn: "",
|
|
1281
|
+
isGenerated: !0
|
|
1282
|
+
};
|
|
1283
|
+
e((A) => C(A, (v) => {
|
|
1284
|
+
if (v.messagesByRoom[i] || (v.messagesByRoom[i] = []), R.items.length === 0)
|
|
1285
|
+
return v;
|
|
1286
|
+
for (const $ of R.items) {
|
|
1287
|
+
const j = $.value, J = v.messagesByRoom[i], K = _.find((Be) => Be._id === j.userId);
|
|
1288
|
+
p(J, D, j, K);
|
|
1289
|
+
}
|
|
1290
|
+
return v;
|
|
1291
|
+
})), y();
|
|
1292
|
+
},
|
|
1293
|
+
b
|
|
1294
|
+
);
|
|
1295
|
+
e({
|
|
1296
|
+
messageSubscriptionsByRoom: {
|
|
1297
|
+
...t().messageSubscriptionsByRoom,
|
|
1298
|
+
[i]: h
|
|
1299
|
+
},
|
|
1300
|
+
messageObserversByRoom: {
|
|
1301
|
+
...t().messageObserversByRoom,
|
|
1302
|
+
[i]: w
|
|
1303
|
+
}
|
|
1304
|
+
});
|
|
1305
|
+
} catch (h) {
|
|
1306
|
+
console.error("Error in subscribeToRoomMessages:", h);
|
|
1307
|
+
}
|
|
1308
|
+
},
|
|
1309
|
+
/**
|
|
1310
|
+
* Unsubscribe from messages for a specific room.
|
|
1311
|
+
*
|
|
1312
|
+
* This method cleans up subscriptions when a component unmounts.
|
|
1313
|
+
* Essential for preventing memory leaks with dynamic subscriptions.
|
|
1314
|
+
*
|
|
1315
|
+
* Workflow:
|
|
1316
|
+
* 1. Retrieves subscription and observer from state
|
|
1317
|
+
* 2. Cancels subscription (stops sync)
|
|
1318
|
+
* 3. Cancels observer (stops state updates)
|
|
1319
|
+
* 4. Removes messages from state
|
|
1320
|
+
* 5. Removes subscription/observer references from state
|
|
1321
|
+
*
|
|
1322
|
+
* Use case: ChatView component unmounting after viewing a generated room
|
|
1323
|
+
*
|
|
1324
|
+
* @param roomId - Room ID to unsubscribe from
|
|
1325
|
+
*/
|
|
1326
|
+
unsubscribeFromRoomMessages(i) {
|
|
1327
|
+
const u = t().messageSubscriptionsByRoom[i], l = t().messageObserversByRoom[i];
|
|
1328
|
+
u && !u.isCancelled && u.cancel(), l && !l.isCancelled && l.cancel(), e((f) => C(f, (m) => (delete m.messagesByRoom[i], delete m.messageSubscriptionsByRoom[i], delete m.messageObserversByRoom[i], m)));
|
|
1329
|
+
},
|
|
1330
|
+
/**
|
|
1331
|
+
* Create a new text message in a room.
|
|
1332
|
+
*
|
|
1333
|
+
* This method creates and persists a new message document with optional mentions.
|
|
1334
|
+
* Mention permission is checked before including mentions in the message.
|
|
1335
|
+
*
|
|
1336
|
+
* Workflow:
|
|
1337
|
+
* 1. Validates Ditto connection and user
|
|
1338
|
+
* 2. Checks mention permission if mentions provided
|
|
1339
|
+
* 3. Creates message document with text and filtered mentions
|
|
1340
|
+
* 4. Updates mentioned users' mention records
|
|
1341
|
+
*
|
|
1342
|
+
* @param room - Room to create message in
|
|
1343
|
+
* @param text - Message text content
|
|
1344
|
+
* @param mentions - Optional array of user mentions
|
|
1345
|
+
*/
|
|
1346
|
+
async createMessage(i, u, l = []) {
|
|
1347
|
+
if (!r || !n)
|
|
1348
|
+
return;
|
|
1349
|
+
const f = t().canPerformAction("canMentionUsers"), m = l.length > 0 && !f ? [] : l;
|
|
1350
|
+
l.length > 0 && !f && console.warn("Permission denied: canMentionUsers is false");
|
|
1351
|
+
try {
|
|
1352
|
+
await x(i, {
|
|
1353
|
+
text: u,
|
|
1354
|
+
mentions: m
|
|
1355
|
+
});
|
|
1356
|
+
} catch (g) {
|
|
1357
|
+
console.error("Error in createMessage:", g);
|
|
1358
|
+
}
|
|
1359
|
+
},
|
|
1360
|
+
/**
|
|
1361
|
+
* Save edits to an existing message.
|
|
1362
|
+
*
|
|
1363
|
+
* This method archives the original message and creates a new version
|
|
1364
|
+
* with updated content while preserving attachments and reactions.
|
|
1365
|
+
*
|
|
1366
|
+
* Workflow:
|
|
1367
|
+
* 1. Checks canEditOwnMessage permission
|
|
1368
|
+
* 2. Archives original message (sets isArchived = true)
|
|
1369
|
+
* 3. Creates new message with archivedMessage reference
|
|
1370
|
+
* 4. Preserves attachments, mentions, and reactions
|
|
1371
|
+
*
|
|
1372
|
+
* @param message - Message with updated text content
|
|
1373
|
+
* @param room - Room containing the message
|
|
1374
|
+
*/
|
|
1375
|
+
async saveEditedTextMessage(i, u) {
|
|
1376
|
+
if (!t().canPerformAction("canEditOwnMessage")) {
|
|
1377
|
+
console.warn("Permission denied: canEditOwnMessage is false");
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
try {
|
|
1381
|
+
await L(i, u, {
|
|
1382
|
+
text: i.text,
|
|
1383
|
+
largeImageToken: i.largeImageToken || null,
|
|
1384
|
+
thumbnailImageToken: i.thumbnailImageToken || null,
|
|
1385
|
+
fileAttachmentToken: i.fileAttachmentToken || null,
|
|
1386
|
+
isEdited: !0,
|
|
1387
|
+
isDeleted: !1,
|
|
1388
|
+
mentions: i.mentions,
|
|
1389
|
+
reactions: i.reactions
|
|
1390
|
+
});
|
|
1391
|
+
} catch (l) {
|
|
1392
|
+
console.error("Error in saveEditedTextMessage:", l);
|
|
1393
|
+
}
|
|
1394
|
+
},
|
|
1395
|
+
/**
|
|
1396
|
+
* Soft-delete a message by replacing its content.
|
|
1397
|
+
*
|
|
1398
|
+
* This method archives the original message and creates a placeholder
|
|
1399
|
+
* indicating deletion. Attachments are removed and content is replaced.
|
|
1400
|
+
*
|
|
1401
|
+
* Workflow:
|
|
1402
|
+
* 1. Checks canDeleteOwnMessage permission
|
|
1403
|
+
* 2. Archives original message
|
|
1404
|
+
* 3. Creates replacement with "[deleted ...]" text
|
|
1405
|
+
* 4. Removes all attachments and mentions
|
|
1406
|
+
*
|
|
1407
|
+
* @param message - Message to delete
|
|
1408
|
+
* @param room - Room containing the message
|
|
1409
|
+
* @param type - Type of content: 'text', 'image', or 'file'
|
|
1410
|
+
*/
|
|
1411
|
+
async saveDeletedMessage(i, u, l = "text") {
|
|
1412
|
+
if (!t().canPerformAction("canDeleteOwnMessage")) {
|
|
1413
|
+
console.warn("Permission denied: canDeleteOwnMessage is false");
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
const f = {
|
|
1417
|
+
text: "[deleted message]",
|
|
1418
|
+
image: "[deleted image]",
|
|
1419
|
+
file: "[deleted file]"
|
|
1420
|
+
}[l];
|
|
1421
|
+
try {
|
|
1422
|
+
await L(i, u, {
|
|
1423
|
+
text: f,
|
|
1424
|
+
createdOn: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1425
|
+
largeImageToken: null,
|
|
1426
|
+
thumbnailImageToken: null,
|
|
1427
|
+
fileAttachmentToken: null,
|
|
1428
|
+
isEdited: !1,
|
|
1429
|
+
isDeleted: !0,
|
|
1430
|
+
mentions: []
|
|
1431
|
+
});
|
|
1432
|
+
} catch (m) {
|
|
1433
|
+
console.error("Error in saveDeletedMessage:", m);
|
|
1434
|
+
}
|
|
1435
|
+
},
|
|
1436
|
+
/**
|
|
1437
|
+
* Create a message with an image attachment.
|
|
1438
|
+
*
|
|
1439
|
+
* This method creates a message with both thumbnail and full-size
|
|
1440
|
+
* image attachments for efficient loading in the UI.
|
|
1441
|
+
*
|
|
1442
|
+
* Workflow:
|
|
1443
|
+
* 1. Creates thumbnail version of image
|
|
1444
|
+
* 2. Uploads both thumbnail and large image as attachments
|
|
1445
|
+
* 3. Creates message document with attachment tokens
|
|
1446
|
+
*
|
|
1447
|
+
* @param room - Room to create message in
|
|
1448
|
+
* @param imageFile - Image file to attach
|
|
1449
|
+
* @param text - Optional caption text
|
|
1450
|
+
*/
|
|
1451
|
+
async createImageMessage(i, u, l) {
|
|
1452
|
+
try {
|
|
1453
|
+
await de(i, u, l, "image");
|
|
1454
|
+
} catch (f) {
|
|
1455
|
+
console.error("Error in createImageMessage:", f);
|
|
1456
|
+
}
|
|
1457
|
+
},
|
|
1458
|
+
/**
|
|
1459
|
+
* Create a message with a file attachment.
|
|
1460
|
+
*
|
|
1461
|
+
* This method uploads a file and creates a message with the
|
|
1462
|
+
* file attachment token. The filename is used as default text.
|
|
1463
|
+
*
|
|
1464
|
+
* Workflow:
|
|
1465
|
+
* 1. Uploads file as attachment with metadata
|
|
1466
|
+
* 2. Creates message document with file attachment token
|
|
1467
|
+
* 3. Uses filename as message text if not provided
|
|
1468
|
+
*
|
|
1469
|
+
* @param room - Room to create message in
|
|
1470
|
+
* @param file - File to attach
|
|
1471
|
+
* @param text - Optional text (defaults to filename)
|
|
1472
|
+
*/
|
|
1473
|
+
async createFileMessage(i, u, l) {
|
|
1474
|
+
try {
|
|
1475
|
+
await de(i, u, l, "file");
|
|
1476
|
+
} catch (f) {
|
|
1477
|
+
console.error("Error in createFileMessage:", f);
|
|
1478
|
+
}
|
|
1479
|
+
},
|
|
1480
|
+
/**
|
|
1481
|
+
* Fetch attachment data with progress tracking.
|
|
1482
|
+
*
|
|
1483
|
+
* This method downloads attachment data from Ditto's attachment store,
|
|
1484
|
+
* providing progress updates during download and handling various states.
|
|
1485
|
+
*
|
|
1486
|
+
* Workflow:
|
|
1487
|
+
* 1. Validates Ditto and token are available
|
|
1488
|
+
* 2. Initiates attachment fetch with event handler
|
|
1489
|
+
* 3. Reports progress via onProgress callback
|
|
1490
|
+
* 4. Returns data and metadata on completion
|
|
1491
|
+
* 5. Handles deletion and error states
|
|
1492
|
+
*
|
|
1493
|
+
* @param token - Attachment token from message
|
|
1494
|
+
* @param onProgress - Callback receiving progress (0-1)
|
|
1495
|
+
* @param onComplete - Callback receiving AttachmentResult
|
|
1496
|
+
*/
|
|
1497
|
+
fetchAttachment(i, u, l) {
|
|
1498
|
+
if (!r) {
|
|
1499
|
+
l({
|
|
1500
|
+
success: !1,
|
|
1501
|
+
error: new Error("Ditto not initialized")
|
|
1502
|
+
});
|
|
1503
|
+
return;
|
|
1504
|
+
}
|
|
1505
|
+
if (!i) {
|
|
1506
|
+
l({
|
|
1507
|
+
success: !1,
|
|
1508
|
+
error: new Error("No attachment token provided")
|
|
1509
|
+
});
|
|
1510
|
+
return;
|
|
1511
|
+
}
|
|
1512
|
+
try {
|
|
1513
|
+
r.store.fetchAttachment(i, async (f) => {
|
|
1514
|
+
switch (f.type) {
|
|
1515
|
+
case "Progress": {
|
|
1516
|
+
const m = Number(f.downloadedBytes) / (Number(f.totalBytes) || 1);
|
|
1517
|
+
u(m);
|
|
1518
|
+
break;
|
|
1519
|
+
}
|
|
1520
|
+
case "Completed":
|
|
1521
|
+
try {
|
|
1522
|
+
const m = f.attachment.getData(), g = m instanceof Promise ? await m : m;
|
|
1523
|
+
l({
|
|
1524
|
+
success: !0,
|
|
1525
|
+
data: g,
|
|
1526
|
+
metadata: f.attachment.metadata || {}
|
|
1527
|
+
});
|
|
1528
|
+
} catch (m) {
|
|
1529
|
+
l({
|
|
1530
|
+
success: !1,
|
|
1531
|
+
error: m instanceof Error ? m : new Error("Unknown error getting attachment data")
|
|
1532
|
+
});
|
|
1533
|
+
}
|
|
1534
|
+
break;
|
|
1535
|
+
case "Deleted":
|
|
1536
|
+
l({
|
|
1537
|
+
success: !1,
|
|
1538
|
+
error: new Error("Attachment was deleted")
|
|
1539
|
+
});
|
|
1540
|
+
break;
|
|
1541
|
+
}
|
|
1542
|
+
});
|
|
1543
|
+
} catch (f) {
|
|
1544
|
+
l({
|
|
1545
|
+
success: !1,
|
|
1546
|
+
error: f instanceof Error ? f : new Error("Failed to fetch attachment")
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
},
|
|
1550
|
+
/**
|
|
1551
|
+
* Updates message reactions using an optimistic update pattern.
|
|
1552
|
+
*
|
|
1553
|
+
* This function implements optimistic UI updates to provide instant feedback
|
|
1554
|
+
* to users without waiting for database operations to complete. The pattern
|
|
1555
|
+
* works in two phases:
|
|
1556
|
+
*
|
|
1557
|
+
* 1. **Optimistic Update (Immediate)**:
|
|
1558
|
+
* - Updates the local state immediately with the new reactions
|
|
1559
|
+
* - Provides instant UI feedback for a responsive user experience
|
|
1560
|
+
* - Users see their reaction changes without any delay
|
|
1561
|
+
*
|
|
1562
|
+
* 2. **Database Persistence (Async)**:
|
|
1563
|
+
* - Asynchronously persists the changes to the Ditto database
|
|
1564
|
+
* - If the database update fails, automatically rolls back the UI changes
|
|
1565
|
+
* - Restores the previous reactions state to maintain data consistency
|
|
1566
|
+
*
|
|
1567
|
+
* This approach ensures:
|
|
1568
|
+
* - Quick UI interactions without perceived latency
|
|
1569
|
+
* - Data consistency through automatic rollback on errors
|
|
1570
|
+
* - Better user experience compared to waiting for DB operations
|
|
1571
|
+
*
|
|
1572
|
+
* @param message - The message whose reactions are being updated
|
|
1573
|
+
* @param room - The room containing the message
|
|
1574
|
+
* @param reactions - The new array of reactions to apply
|
|
1575
|
+
*/
|
|
1576
|
+
async updateMessageReactions(i, u, l) {
|
|
1577
|
+
if (!r || !n)
|
|
1578
|
+
return;
|
|
1579
|
+
const f = u._id, m = t().messagesByRoom[f] || [], g = m.findIndex((_) => _.id === i._id);
|
|
1580
|
+
if (g === -1)
|
|
1581
|
+
throw new Error("Message not found");
|
|
1582
|
+
const b = m[g].message, h = b.reactions || [];
|
|
1583
|
+
e(
|
|
1584
|
+
(_) => C(_, (w) => {
|
|
1585
|
+
w.messagesByRoom[f][g].message.reactions = l;
|
|
1586
|
+
})
|
|
1587
|
+
), setTimeout(async () => {
|
|
1588
|
+
try {
|
|
1589
|
+
await r.store.execute(
|
|
1590
|
+
`UPDATE ${u.messagesId} SET reactions = :reactions WHERE _id = :id`,
|
|
1591
|
+
{ id: b._id, reactions: l }
|
|
1592
|
+
);
|
|
1593
|
+
} catch (_) {
|
|
1594
|
+
console.error("Error updating reactions, rolling back:", _), e(
|
|
1595
|
+
(w) => C(w, (R) => {
|
|
1596
|
+
R.messagesByRoom[f][g].message.reactions = h;
|
|
1597
|
+
})
|
|
1598
|
+
);
|
|
1599
|
+
}
|
|
1600
|
+
}, 0);
|
|
1601
|
+
},
|
|
1602
|
+
/**
|
|
1603
|
+
* Add a reaction to a message.
|
|
1604
|
+
*
|
|
1605
|
+
* This method adds a new reaction to a message's reactions array
|
|
1606
|
+
* using optimistic updates for instant UI feedback.
|
|
1607
|
+
*
|
|
1608
|
+
* Workflow:
|
|
1609
|
+
* 1. Checks canAddReaction permission
|
|
1610
|
+
* 2. Finds message in state
|
|
1611
|
+
* 3. Appends reaction to existing reactions
|
|
1612
|
+
* 4. Calls updateMessageReactions for optimistic update
|
|
1613
|
+
*
|
|
1614
|
+
* @param message - Message to add reaction to
|
|
1615
|
+
* @param room - Room containing the message
|
|
1616
|
+
* @param reaction - Reaction object with emoji and userId
|
|
1617
|
+
*/
|
|
1618
|
+
async addReactionToMessage(i, u, l) {
|
|
1619
|
+
if (!r || !n)
|
|
1620
|
+
return;
|
|
1621
|
+
if (!t().canPerformAction("canAddReaction")) {
|
|
1622
|
+
console.warn("Permission denied: canAddReaction is false");
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
const f = t().messagesByRoom[u._id], m = f.findIndex((h) => h.id === i._id);
|
|
1626
|
+
if (m === -1)
|
|
1627
|
+
throw new Error("Message not found");
|
|
1628
|
+
const g = f[m].message, b = [...g.reactions || [], l];
|
|
1629
|
+
await t().updateMessageReactions(g, u, b);
|
|
1630
|
+
},
|
|
1631
|
+
/**
|
|
1632
|
+
* Remove a reaction from a message.
|
|
1633
|
+
*
|
|
1634
|
+
* This method removes a user's reaction from a message using
|
|
1635
|
+
* optimistic updates for instant UI feedback.
|
|
1636
|
+
*
|
|
1637
|
+
* Workflow:
|
|
1638
|
+
* 1. Checks canRemoveOwnReaction permission
|
|
1639
|
+
* 2. Finds message in state
|
|
1640
|
+
* 3. Filters out matching reaction (emoji + userId)
|
|
1641
|
+
* 4. Calls updateMessageReactions for optimistic update
|
|
1642
|
+
*
|
|
1643
|
+
* @param message - Message to remove reaction from
|
|
1644
|
+
* @param room - Room containing the message
|
|
1645
|
+
* @param reaction - Reaction to remove (matched by emoji and userId)
|
|
1646
|
+
*/
|
|
1647
|
+
async removeReactionFromMessage(i, u, l) {
|
|
1648
|
+
if (!r || !n)
|
|
1649
|
+
return;
|
|
1650
|
+
if (!t().canPerformAction("canRemoveOwnReaction")) {
|
|
1651
|
+
console.warn("Permission denied: canRemoveOwnReaction is false");
|
|
1652
|
+
return;
|
|
1653
|
+
}
|
|
1654
|
+
const f = t().messagesByRoom[u._id], m = f.findIndex((h) => h.id === i._id);
|
|
1655
|
+
if (m === -1)
|
|
1656
|
+
throw new Error("Message not found");
|
|
1657
|
+
const g = f[m].message, b = (g.reactions || []).filter(
|
|
1658
|
+
(h) => !(h.emoji === l.emoji && h.userId === l.userId)
|
|
1659
|
+
);
|
|
1660
|
+
await t().updateMessageReactions(g, u, b);
|
|
1661
|
+
}
|
|
1662
|
+
};
|
|
1663
|
+
};
|
|
1664
|
+
async function dt(e) {
|
|
1665
|
+
const t = await ke(e), r = document.createElement("canvas"), n = r.getContext("2d");
|
|
1666
|
+
if (!n)
|
|
1667
|
+
throw new Error("Failed to get 2D context");
|
|
1668
|
+
let { width: s, height: o } = t;
|
|
1669
|
+
return s > o ? (o = G / s * o, s = G) : (s = G / o * s, o = G), r.width = s, r.height = o, n.drawImage(t, 0, 0, s, o), r;
|
|
1670
|
+
}
|
|
1671
|
+
function ke(e) {
|
|
1672
|
+
return new Promise((t, r) => {
|
|
1673
|
+
const n = new FileReader();
|
|
1674
|
+
n.onload = (s) => {
|
|
1675
|
+
const o = new Image();
|
|
1676
|
+
o.onload = () => t(o), o.onerror = r, o.src = String(s.target?.result);
|
|
1677
|
+
}, n.onerror = r, n.readAsDataURL(e);
|
|
1678
|
+
});
|
|
1679
|
+
}
|
|
1680
|
+
async function De(e) {
|
|
1681
|
+
return new Promise((t, r) => {
|
|
1682
|
+
const n = (s) => {
|
|
1683
|
+
s.toBlob(
|
|
1684
|
+
(o) => o ? t(o) : r(new Error("Failed to convert to blob")),
|
|
1685
|
+
"image/jpeg",
|
|
1686
|
+
1
|
|
1687
|
+
);
|
|
1688
|
+
};
|
|
1689
|
+
if (e instanceof HTMLCanvasElement)
|
|
1690
|
+
n(e);
|
|
1691
|
+
else {
|
|
1692
|
+
const s = document.createElement("canvas");
|
|
1693
|
+
s.width = e.width, s.height = e.height;
|
|
1694
|
+
const o = s.getContext("2d");
|
|
1695
|
+
if (!o) {
|
|
1696
|
+
r(new Error("Failed to get 2D context"));
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1699
|
+
o.drawImage(e, 0, 0), n(s);
|
|
1700
|
+
}
|
|
1701
|
+
});
|
|
1702
|
+
}
|
|
1703
|
+
function se(e, t, r, n) {
|
|
1704
|
+
const s = (/* @__PURE__ */ new Date()).toISOString(), o = t.replace(/\s/g, "-"), a = s.replace(/:/g, "-"), c = r === "file" ? n.name.split(".").pop() || "bin" : "jpg";
|
|
1705
|
+
return {
|
|
1706
|
+
filename: `${o}_${r}_${a}.${c}`,
|
|
1707
|
+
userId: e,
|
|
1708
|
+
username: t,
|
|
1709
|
+
fileformat: `.${c}`,
|
|
1710
|
+
filesize: n.size.toString(),
|
|
1711
|
+
timestamp: s,
|
|
1712
|
+
originalName: n.name
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
function I(e) {
|
|
1716
|
+
e && !e.isCancelled && e.cancel();
|
|
1717
|
+
}
|
|
1718
|
+
function ht(e) {
|
|
1719
|
+
const t = Fe(() => (globalThis.__DITTO_CHAT_STORE__ ? console.log("Reusing EXISTING global chatStore instance") : (console.log("Creating NEW global chatStore instance"), globalThis.__DITTO_CHAT_STORE__ = ot()(
|
|
1720
|
+
(r, n) => ({
|
|
1721
|
+
...st(r, n, e),
|
|
1722
|
+
...lt(r, n, e),
|
|
1723
|
+
...mt(r, n, e),
|
|
1724
|
+
...He(r, n, e),
|
|
1725
|
+
activeRoomId: null,
|
|
1726
|
+
setActiveRoomId: (s) => r({ activeRoomId: s }),
|
|
1727
|
+
chatLogout: () => {
|
|
1728
|
+
const s = n();
|
|
1729
|
+
I(s.roomsSubscription), I(s.roomsObserver), I(s.dmRoomsSubscription), I(s.dmRoomsObserver), Object.values(s.messageSubscriptionsByRoom || {}).map(
|
|
1730
|
+
(o) => I(o)
|
|
1731
|
+
), Object.values(s.messageObserversByRoom || {}).map(
|
|
1732
|
+
(o) => I(o)
|
|
1733
|
+
), I(s.userObserver), I(s.userSubscription), I(s.allUsersObserver), I(s.allUsersSubscription);
|
|
1734
|
+
}
|
|
1735
|
+
})
|
|
1736
|
+
)), globalThis.__DITTO_CHAT_STORE__), [e]);
|
|
1737
|
+
return xe(t);
|
|
1738
|
+
}
|
|
1739
|
+
function bt(e) {
|
|
1740
|
+
if (!globalThis.__DITTO_CHAT_STORE__)
|
|
1741
|
+
throw new Error(
|
|
1742
|
+
"chatStore must be initialized before useDittoChatStore. use useDittoChat for initialization"
|
|
1743
|
+
);
|
|
1744
|
+
const t = ut(
|
|
1745
|
+
e || ((n) => n)
|
|
1746
|
+
);
|
|
1747
|
+
return xe(globalThis.__DITTO_CHAT_STORE__, t);
|
|
1748
|
+
}
|
|
1749
|
+
function _t() {
|
|
1750
|
+
return globalThis.__DITTO_CHAT_STORE__;
|
|
1751
|
+
}
|
|
1752
|
+
function wt() {
|
|
1753
|
+
globalThis.__DITTO_CHAT_STORE__ = void 0;
|
|
1754
|
+
}
|
|
1755
|
+
export {
|
|
1756
|
+
ze as DEFAULT_PERMISSIONS,
|
|
1757
|
+
I as cancelSubscriptionOrObserver,
|
|
1758
|
+
He as createRBACSlice,
|
|
1759
|
+
st as createRoomSlice,
|
|
1760
|
+
_t as getChatStore,
|
|
1761
|
+
wt as resetChatStore,
|
|
1762
|
+
ht as useDittoChat,
|
|
1763
|
+
bt as useDittoChatStore
|
|
1764
|
+
};
|