@nivaro/react 0.3.24 → 0.3.26

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.
Files changed (27) hide show
  1. package/dist/hooks/useSessionRecorder.js +1 -1
  2. package/dist/index.d.ts +45 -26
  3. package/dist/shared/components/FilePreviewLightbox.js +56 -35
  4. package/dist/shared/components/ItemEditForm.js +4309 -4263
  5. package/dist/shared/components/PageRenderer.js +63 -62
  6. package/dist/shared/components/RecordReadView.js +229 -229
  7. package/dist/shared/components/ReportView.js +123 -120
  8. package/dist/shared/components/ai/AiMarkdown.js +168 -148
  9. package/dist/shared/components/chat/RoomParts.js +120 -119
  10. package/dist/shared/components/chat/chat-core.js +228 -224
  11. package/dist/shared/components/item-edit/FieldRenderer.js +333 -333
  12. package/dist/shared/components/item-edit/FieldRow.js +413 -394
  13. package/dist/shared/components/item-edit/GroupSection.js +637 -635
  14. package/dist/shared/components/item-edit/InlineGridField.js +89 -90
  15. package/dist/shared/components/item-edit/M2MCombobox.js +117 -116
  16. package/dist/shared/components/item-edit/QuickPicker.js +9 -9
  17. package/dist/shared/components/item-edit/RawEditSheet.js +80 -79
  18. package/dist/shared/components/item-edit/RelationCombobox.js +173 -167
  19. package/dist/shared/components/item-edit/RowMatchPanel.js +1 -1
  20. package/dist/shared/components/item-edit/m2m-invalidate.js +19 -0
  21. package/dist/shared/components/item-edit/record-lookups.js +15 -0
  22. package/dist/shared/components/item-edit/render-stability.js +147 -0
  23. package/dist/shared/components/layout-editor/FormMapButton.js +5 -4
  24. package/dist/shared/lib/picker-rank.js +21 -0
  25. package/dist/shared/lib/safe-href.js +10 -0
  26. package/dist/shared/lib/socket-realtime.js +107 -78
  27. package/package.json +6 -6
@@ -1,28 +1,29 @@
1
1
  import { readItems as J } from "@nivaro/sdk";
2
2
  import { useQueryClient as v, useMutation as h, useQuery as y } from "@tanstack/react-query";
3
- import { useContext as Z, useState as L, useEffect as $, useMemo as k, useCallback as M, useRef as U, createContext as X } from "react";
3
+ import { useContext as Z, useState as x, useEffect as K, useMemo as k, useCallback as M, useRef as U, createContext as X } from "react";
4
4
  import { toast as ee } from "sonner";
5
5
  import { useNivaroClient as m } from "../../context.js";
6
- import { get as g, patch as C, post as K, del as A } from "../../lib/commands.js";
6
+ import { get as g, patch as R, post as O, del as A } from "../../lib/commands.js";
7
7
  import { isDndActive as te } from "../../lib/dnd.js";
8
- import { playNotificationSound as ne } from "../../lib/notification-sound.js";
9
- import { announceLocalPrefs as se } from "../../lib/user-sync.js";
10
- import { bindChatOutbox as re, enqueueChatOutbox as oe } from "./chat-outbox.js";
11
- import { discardOutbox as Ct, editOutbox as Rt, holdOutbox as wt, nudgeChatOutbox as St, retryOutbox as Tt, useOutbox as $t } from "./chat-outbox.js";
12
- import { huddleHolds as ae, holdNotification as ie } from "./huddle/huddle-hold.js";
13
- const Me = ["👍", "✅", "👀", "🎉", "❤️", "😂"], ce = X(null);
8
+ import { useLiveRefetchInterval as ne } from "../../lib/live-queries.js";
9
+ import { playNotificationSound as se } from "../../lib/notification-sound.js";
10
+ import { announceLocalPrefs as re } from "../../lib/user-sync.js";
11
+ import { bindChatOutbox as oe, enqueueChatOutbox as ae } from "./chat-outbox.js";
12
+ import { discardOutbox as wt, editOutbox as St, holdOutbox as Tt, nudgeChatOutbox as $t, retryOutbox as Kt, useOutbox as Ot } from "./chat-outbox.js";
13
+ import { huddleHolds as ie, holdNotification as ce } from "./huddle/huddle-hold.js";
14
+ const ke = ["👍", "✅", "👀", "🎉", "❤️", "😂"], ue = X(null);
14
15
  function b() {
15
- const e = Z(ce);
16
+ const e = Z(ue);
16
17
  if (!e) throw new Error("Chat components must be wrapped in <ChatProvider>");
17
18
  return e;
18
19
  }
19
- const Ie = 0, ke = {
20
+ const Le = 0, xe = {
20
21
  collections: { messages: "chat_messages", reads: "chat_last_read", presence: "user_presence" },
21
22
  globalRoom: "global",
22
23
  globalLabel: "General",
23
24
  entityPattern: String.raw`\b([A-Za-z]{2,4}\d{2}(?:INV)?-\d+)\b`
24
25
  };
25
- function xe(e, t) {
26
+ function Qe(e, t) {
26
27
  return `dm:${[e.toUpperCase(), t.toUpperCase()].sort().join(":")}`;
27
28
  }
28
29
  function B(e, t) {
@@ -32,7 +33,7 @@ function B(e, t) {
32
33
  }
33
34
  function F(e, t) {
34
35
  const s = b(), n = v(), o = (t ?? []).join("|");
35
- $(() => {
36
+ K(() => {
36
37
  const r = () => {
37
38
  for (const a of e) n.invalidateQueries({ queryKey: a });
38
39
  };
@@ -40,23 +41,23 @@ function F(e, t) {
40
41
  if (s.realtime) return s.realtime(s.collections.messages, r);
41
42
  }, [s.subscribeRooms, s.realtime, o, n]);
42
43
  }
43
- function ue(e, t) {
44
+ function le(e, t) {
44
45
  const s = b(), n = v();
45
- $(() => {
46
+ K(() => {
46
47
  if (s.realtime)
47
48
  return s.realtime(e, () => {
48
49
  for (const o of t) n.invalidateQueries({ queryKey: o });
49
50
  });
50
51
  }, [s.realtime, e, n]);
51
52
  }
52
- function Le(e, t = null) {
53
+ function Ue(e, t = null) {
53
54
  var D, N, E;
54
55
  const s = b(), n = m(), o = t ? "around" in t ? `around:${t.around}` : `date:${t.date}` : "latest", { data: r, isLoading: a } = y({
55
56
  queryKey: ["nvr-chat", e, o],
56
57
  queryFn: async () => {
57
- const _ = { room: e, limit: 80 };
58
- return t && "around" in t && (_.around = t.around), t && "date" in t && (_.date = t.date), await n.request(
59
- g("/chat/messages", _)
58
+ const C = { room: e, limit: 80 };
59
+ return t && "around" in t && (C.around = t.around), t && "date" in t && (C.date = t.date), await n.request(
60
+ g("/chat/messages", C)
60
61
  );
61
62
  },
62
63
  enabled: !!e,
@@ -64,41 +65,41 @@ function Le(e, t = null) {
64
65
  refetchInterval: s.realtime || s.subscribeRooms ? void 0 : 1e4
65
66
  });
66
67
  F([["nvr-chat"], ["nvr-chat-rooms"], ["nvr-chat-thread"]], e ? [e] : []);
67
- const [i, c] = L([]), [f, u] = L(null), [l, d] = L(!1);
68
- $(() => {
68
+ const [i, c] = x([]), [f, u] = x(null), [d, l] = x(!1);
69
+ K(() => {
69
70
  c([]), u(null);
70
71
  }, [e, o]);
71
72
  const q = (r == null ? void 0 : r.data) ?? [], p = k(() => {
72
73
  if (i.length === 0) return q;
73
- const _ = new Set(q.map((R) => R.id));
74
- return [...i.filter((R) => !_.has(R.id)), ...q];
75
- }, [i, q]), O = M(async () => {
76
- var R;
77
- if (!e || l) return;
78
- const _ = p[0];
79
- if (_) {
80
- d(!0);
74
+ const C = new Set(q.map((w) => w.id));
75
+ return [...i.filter((w) => !C.has(w.id)), ...q];
76
+ }, [i, q]), _ = M(async () => {
77
+ var w;
78
+ if (!e || d) return;
79
+ const C = p[0];
80
+ if (C) {
81
+ l(!0);
81
82
  try {
82
83
  const P = await n.request(
83
- g("/chat/messages", { room: e, limit: 60, before: _.id })
84
+ g("/chat/messages", { room: e, limit: 60, before: C.id })
84
85
  );
85
- c((Y) => [...P.data ?? [], ...Y]), u(!!((R = P.meta) != null && R.has_older));
86
+ c((Y) => [...P.data ?? [], ...Y]), u(!!((w = P.meta) != null && w.has_older));
86
87
  } finally {
87
- d(!1);
88
+ l(!1);
88
89
  }
89
90
  }
90
- }, [e, p, n, l]);
91
+ }, [e, p, n, d]);
91
92
  return {
92
93
  messages: p,
93
94
  loading: a,
94
95
  hasOlder: f ?? !!((D = r == null ? void 0 : r.meta) != null && D.has_older),
95
96
  hasNewer: !!((N = r == null ? void 0 : r.meta) != null && N.has_newer),
96
97
  anchorId: ((E = r == null ? void 0 : r.meta) == null ? void 0 : E.anchor) ?? null,
97
- loadOlder: O,
98
- loadingOlder: l
98
+ loadOlder: _,
99
+ loadingOlder: d
99
100
  };
100
101
  }
101
- function Qe(e, t) {
102
+ function Ae(e, t) {
102
103
  const s = b(), n = m(), { data: o, isLoading: r } = y({
103
104
  queryKey: ["nvr-chat-thread", e, t],
104
105
  queryFn: async () => (await n.request(
@@ -116,15 +117,15 @@ function Qe(e, t) {
116
117
  loading: r
117
118
  };
118
119
  }
119
- function Ue(e) {
120
+ function De(e) {
120
121
  var r, a, i;
121
122
  const t = m(), s = v(), n = b();
122
- return re(t, s, ((r = n.me) == null ? void 0 : r.id) ?? null), { mutate: M(
123
+ return oe(t, s, ((r = n.me) == null ? void 0 : r.id) ?? null), { mutate: M(
123
124
  (c) => {
124
- var u, l;
125
+ var u, d;
125
126
  const f = typeof c == "string" ? { text: c } : c;
126
- oe({
127
- client_id: le(),
127
+ ae({
128
+ client_id: de(),
128
129
  room: e,
129
130
  text: f.text,
130
131
  mentions: f.mentions ?? [],
@@ -134,13 +135,13 @@ function Ue(e) {
134
135
  urgent: !!f.urgent,
135
136
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
136
137
  sender: ((u = n.me) == null ? void 0 : u.id) ?? "",
137
- sender_name: ((l = n.me) == null ? void 0 : l.name) ?? null
138
+ sender_name: ((d = n.me) == null ? void 0 : d.name) ?? null
138
139
  });
139
140
  },
140
141
  [e, (a = n.me) == null ? void 0 : a.id, (i = n.me) == null ? void 0 : i.name]
141
142
  ), isPending: !1 };
142
143
  }
143
- function le() {
144
+ function de() {
144
145
  var t;
145
146
  const e = globalThis.crypto;
146
147
  return ((t = e == null ? void 0 : e.randomUUID) == null ? void 0 : t.call(e)) ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
@@ -148,15 +149,15 @@ function le() {
148
149
  function G(e, t, s) {
149
150
  const n = (t == null ? void 0 : t.toLowerCase()) ?? "";
150
151
  return e.map((r) => {
151
- var c, f, u, l, d;
152
+ var c, f, u, d, l;
152
153
  const a = r.kind === "unknown" ? "entity" : r.kind, i = a === "global" ? (
153
154
  // The server names General only when an admin renamed it.
154
155
  r.label ?? s.globalLabel
155
156
  ) : a === "dm" ? (
156
157
  // The server resolves the peer's name from the user table; the
157
158
  // message sender is only a fallback for hosts that don't send one.
158
- r.label ?? (((f = (c = r.last_message) == null ? void 0 : c.sender) == null ? void 0 : f.toLowerCase()) !== n ? ((u = r.last_message) == null ? void 0 : u.sender_name) ?? null : null) ?? `User ${((l = B(r.room, n)) == null ? void 0 : l.slice(0, 8)) ?? ""}`
159
- ) : r.label ?? ((d = s.roomLabel) == null ? void 0 : d.call(s, r.room)) ?? r.room.toUpperCase();
159
+ r.label ?? (((f = (c = r.last_message) == null ? void 0 : c.sender) == null ? void 0 : f.toLowerCase()) !== n ? ((u = r.last_message) == null ? void 0 : u.sender_name) ?? null : null) ?? `User ${((d = B(r.room, n)) == null ? void 0 : d.slice(0, 8)) ?? ""}`
160
+ ) : r.label ?? ((l = s.roomLabel) == null ? void 0 : l.call(s, r.room)) ?? r.room.toUpperCase();
160
161
  return {
161
162
  room: r.room,
162
163
  label: i,
@@ -179,7 +180,7 @@ function G(e, t, s) {
179
180
  return (a.lastMessage ? new Date(a.lastMessage.date_created).getTime() : 0) - i;
180
181
  });
181
182
  }
182
- function Ae() {
183
+ function Ne() {
183
184
  const e = b(), t = m(), s = e.me, n = y({
184
185
  queryKey: ["nvr-chat-rooms", s == null ? void 0 : s.id],
185
186
  queryFn: async () => (await t.request(g("/chat/rooms"))).data ?? [],
@@ -191,7 +192,7 @@ function Ae() {
191
192
  const o = k(
192
193
  () => G(n.data ?? [], s == null ? void 0 : s.id, e),
193
194
  [n.data, s == null ? void 0 : s.id, e]
194
- ), r = x().chat_badge_mode, a = o.reduce((i, c) => {
195
+ ), r = L().chat_badge_mode, a = o.reduce((i, c) => {
195
196
  var f;
196
197
  if (c.muted) return i;
197
198
  if (r === "conversations") {
@@ -202,7 +203,7 @@ function Ae() {
202
203
  }, 0);
203
204
  return { rooms: o, totalUnread: a, loading: n.isLoading };
204
205
  }
205
- function De(e) {
206
+ function Ee(e) {
206
207
  const t = b(), s = m(), n = t.me, o = y({
207
208
  queryKey: ["nvr-chat-rooms", n == null ? void 0 : n.id, "archived"],
208
209
  queryFn: async () => (await s.request(
@@ -213,7 +214,7 @@ function De(e) {
213
214
  });
214
215
  return { rooms: k(() => G(o.data ?? [], n == null ? void 0 : n.id, t), [o.data, n == null ? void 0 : n.id, t]), loading: o.isLoading };
215
216
  }
216
- function Ne(e) {
217
+ function Pe(e) {
217
218
  const t = m(), { data: s, isLoading: n } = y({
218
219
  queryKey: ["nvr-chat-directory", e],
219
220
  queryFn: async () => (await t.request(
@@ -223,38 +224,38 @@ function Ne(e) {
223
224
  });
224
225
  return { channels: s ?? [], loading: n };
225
226
  }
226
- function Ee() {
227
+ function je() {
227
228
  const e = m(), t = v(), s = () => {
228
229
  t.invalidateQueries({ queryKey: ["nvr-chat-rooms"] }), t.invalidateQueries({ queryKey: ["nvr-chat-directory"] });
229
230
  }, n = h({
230
- mutationFn: (u) => e.request(K(`/chat/rooms/${encodeURIComponent(u)}/join`)),
231
+ mutationFn: (u) => e.request(O(`/chat/rooms/${encodeURIComponent(u)}/join`)),
231
232
  onSuccess: s
232
233
  }), o = h({
233
234
  mutationFn: (u) => e.request(A(`/chat/rooms/${encodeURIComponent(u)}/join`)),
234
235
  onSuccess: s
235
236
  }), r = h({
236
- mutationFn: ({ room: u, muted: l }) => e.request(C(`/chat/rooms/${encodeURIComponent(u)}`, { muted: l })),
237
+ mutationFn: ({ room: u, muted: d }) => e.request(R(`/chat/rooms/${encodeURIComponent(u)}`, { muted: d })),
237
238
  onSuccess: s
238
239
  }), a = h({
239
- mutationFn: ({ room: u, mode: l }) => e.request(
240
- C(`/chat/rooms/${encodeURIComponent(u)}`, {
241
- notify_mode: l === "mentions" ? "mentions" : null
240
+ mutationFn: ({ room: u, mode: d }) => e.request(
241
+ R(`/chat/rooms/${encodeURIComponent(u)}`, {
242
+ notify_mode: d === "mentions" ? "mentions" : null
242
243
  })
243
244
  ),
244
245
  onSuccess: s
245
246
  }), i = h({
246
- mutationFn: ({ room: u, archived: l }) => e.request(C(`/chat/rooms/${encodeURIComponent(u)}`, { archived: l })),
247
+ mutationFn: ({ room: u, archived: d }) => e.request(R(`/chat/rooms/${encodeURIComponent(u)}`, { archived: d })),
247
248
  onSuccess: s
248
249
  }), c = h({
249
- mutationFn: ({ room: u, starred: l }) => e.request(C(`/chat/rooms/${encodeURIComponent(u)}`, { starred: l })),
250
+ mutationFn: ({ room: u, starred: d }) => e.request(R(`/chat/rooms/${encodeURIComponent(u)}`, { starred: d })),
250
251
  onSuccess: s
251
252
  }), f = h({
252
- mutationFn: (u) => e.request(C(`/chat/rooms/${encodeURIComponent(u)}`, { welcome_seen: !0 })),
253
+ mutationFn: (u) => e.request(R(`/chat/rooms/${encodeURIComponent(u)}`, { welcome_seen: !0 })),
253
254
  onSuccess: s
254
255
  });
255
256
  return { join: n, leave: o, setMuted: r, setNotifyMode: a, setArchived: i, setStarred: c, dismissWelcome: f };
256
257
  }
257
- function Pe(e) {
258
+ function ze(e) {
258
259
  const t = m(), { data: s, isLoading: n } = y({
259
260
  queryKey: ["nvr-chat-members", e],
260
261
  queryFn: async () => (await t.request(
@@ -265,14 +266,14 @@ function Pe(e) {
265
266
  });
266
267
  return { members: s ?? [], loading: n };
267
268
  }
268
- function je(e) {
269
+ function He(e) {
269
270
  const t = m(), s = v(), n = () => {
270
271
  s.invalidateQueries({ queryKey: ["nvr-chat-members", e] }), s.invalidateQueries({ queryKey: ["nvr-chat-rooms"] }), s.invalidateQueries({ queryKey: ["nvr-chat-directory"] });
271
272
  }, o = h({
272
- mutationFn: (i) => t.request(C(`/chat/channels/${e}`, i)),
273
+ mutationFn: (i) => t.request(R(`/chat/channels/${e}`, i)),
273
274
  onSuccess: n
274
275
  }), r = h({
275
- mutationFn: (i) => t.request(K(`/chat/channels/${e}/members`, { user_id: i })),
276
+ mutationFn: (i) => t.request(O(`/chat/channels/${e}/members`, { user_id: i })),
276
277
  onSuccess: n
277
278
  }), a = h({
278
279
  mutationFn: (i) => t.request(A(`/chat/channels/${e}/members/${i}`)),
@@ -280,7 +281,7 @@ function je(e) {
280
281
  });
281
282
  return { update: o, addMember: r, removeMember: a };
282
283
  }
283
- function ze() {
284
+ function Be() {
284
285
  const e = m(), { data: t } = y({
285
286
  queryKey: ["nvr-chat-roles"],
286
287
  queryFn: async () => (await e.request(
@@ -290,7 +291,7 @@ function ze() {
290
291
  });
291
292
  return t ?? [];
292
293
  }
293
- function He(e, t) {
294
+ function Ge(e, t) {
294
295
  const s = m(), { data: n, isLoading: o } = y({
295
296
  queryKey: ["nvr-chat-user-search", e],
296
297
  queryFn: async () => (await s.request(
@@ -301,10 +302,10 @@ function He(e, t) {
301
302
  });
302
303
  return { users: n ?? [], loading: o };
303
304
  }
304
- function Be() {
305
+ function Ve() {
305
306
  const e = m(), t = v();
306
307
  return h({
307
- mutationFn: (s) => e.request(K("/chat/channels", s)),
308
+ mutationFn: (s) => e.request(O("/chat/channels", s)),
308
309
  onSuccess: () => {
309
310
  t.invalidateQueries({ queryKey: ["nvr-chat-rooms"] }), t.invalidateQueries({ queryKey: ["nvr-chat-directory"] });
310
311
  }
@@ -316,27 +317,27 @@ function z(e) {
316
317
  var t;
317
318
  return e.kind === "dm" || e.kind === "channel" && !!((t = e.channel) != null && t.is_direct);
318
319
  }
319
- const w = /* @__PURE__ */ new Map();
320
- function Ge(e) {
321
- $(() => {
320
+ const S = /* @__PURE__ */ new Map();
321
+ function We(e) {
322
+ K(() => {
322
323
  if (e)
323
- return w.set(e, (w.get(e) ?? 0) + 1), () => {
324
- const t = (w.get(e) ?? 1) - 1;
325
- t <= 0 ? w.delete(e) : w.set(e, t);
324
+ return S.set(e, (S.get(e) ?? 0) + 1), () => {
325
+ const t = (S.get(e) ?? 1) - 1;
326
+ t <= 0 ? S.delete(e) : S.set(e, t);
326
327
  };
327
328
  }, [e]);
328
329
  }
329
- function de() {
330
+ function me() {
330
331
  return typeof document > "u" || document.visibilityState === "visible";
331
332
  }
332
333
  function H(e) {
333
- return w.has(e) && de();
334
+ return S.has(e) && me();
334
335
  }
335
- function me() {
336
- const e = x().notification_sound;
336
+ function fe() {
337
+ const e = L().notification_sound;
337
338
  return typeof e == "string" ? e : "off";
338
339
  }
339
- function x() {
340
+ function L() {
340
341
  const e = m(), { data: t } = y({
341
342
  queryKey: ["nvr-profile-prefs"],
342
343
  queryFn: () => e.request(g("/users/me")).then((s) => {
@@ -347,10 +348,10 @@ function x() {
347
348
  });
348
349
  return t ?? {};
349
350
  }
350
- function fe() {
351
+ function he() {
351
352
  const e = m(), t = v();
352
353
  return h({
353
- mutationFn: (s) => e.request(C("/users/me/preferences", s)),
354
+ mutationFn: (s) => e.request(R("/users/me/preferences", s)),
354
355
  onMutate: (s) => {
355
356
  const n = t.getQueryData(["nvr-profile-prefs"]);
356
357
  return t.setQueryData(["nvr-profile-prefs"], { ...n ?? {}, ...s }), { prev: n };
@@ -358,15 +359,15 @@ function fe() {
358
359
  onError: (s, n, o) => {
359
360
  o != null && o.prev && t.setQueryData(["nvr-profile-prefs"], o.prev);
360
361
  },
361
- onSuccess: (s, n) => se(Object.keys(n)),
362
+ onSuccess: (s, n) => re(Object.keys(n)),
362
363
  onSettled: () => {
363
364
  t.invalidateQueries({ queryKey: ["nvr-profile-prefs"] }), t.invalidateQueries({ queryKey: ["presence-online"] });
364
365
  }
365
366
  });
366
367
  }
367
- function Ve() {
368
- const e = x(), t = fe(), s = Object.keys(e).length > 0;
369
- $(() => {
368
+ function Ye() {
369
+ const e = L(), t = he(), s = Object.keys(e).length > 0;
370
+ K(() => {
370
371
  if (!s) return;
371
372
  let n = "";
372
373
  try {
@@ -384,53 +385,53 @@ function Ve() {
384
385
  }
385
386
  }, [s, e.browser_timezone]);
386
387
  }
387
- function We(e, t) {
388
- const s = b(), n = me(), o = x(), r = U(o);
389
- r.current = o, $(() => {
390
- const a = t ? t.filter((u) => z(u) && !H(u.room)).reduce((u, l) => u + l.unread, 0) : e, i = a > j, c = ae(r.current), f = te(r.current) || c;
391
- if (s.sound && i && !f && ne(n), s.sound && i && c && ie({ kind: "chat", title: "New message" }), t) {
392
- const u = Q.size > 0, l = w.size > 0;
393
- for (const d of t) {
394
- const q = Q.get(d.room) ?? 0;
395
- if (u && !f && !d.muted && z(d) && d.unread > q && !H(d.room)) {
396
- const p = d.lastMessage, O = p != null && p.sender_name ? String(p.sender_name).trim() : null;
388
+ function Je(e, t) {
389
+ const s = b(), n = fe(), o = L(), r = U(o);
390
+ r.current = o, K(() => {
391
+ const a = t ? t.filter((u) => z(u) && !H(u.room)).reduce((u, d) => u + d.unread, 0) : e, i = a > j, c = ie(r.current), f = te(r.current) || c;
392
+ if (s.sound && i && !f && se(n), s.sound && i && c && ce({ kind: "chat", title: "New message" }), t) {
393
+ const u = Q.size > 0, d = S.size > 0;
394
+ for (const l of t) {
395
+ const q = Q.get(l.room) ?? 0;
396
+ if (u && !f && !l.muted && z(l) && l.unread > q && !H(l.room)) {
397
+ const p = l.lastMessage, _ = p != null && p.sender_name ? String(p.sender_name).trim() : null;
397
398
  ee(
398
- d.kind === "dm" ? `New message from ${O || d.label}` : `New message in ${d.label}${O ? ` — ${O}` : ""}`,
399
+ l.kind === "dm" ? `New message from ${_ || l.label}` : `New message in ${l.label}${_ ? ` — ${_}` : ""}`,
399
400
  {
400
401
  description: p != null && p.message ? String(p.message).replace(/<[^>]*>/g, "").slice(0, 90) : void 0,
401
402
  duration: 4e3,
402
- ...l ? { position: "top-center" } : {},
403
+ ...d ? { position: "top-center" } : {},
403
404
  // Reading the message is the whole reason the toast exists, so
404
405
  // it opens the conversation — only when a host has somewhere to
405
406
  // open it, otherwise the click would do nothing.
406
- ...qe() ? {
407
+ ...be() ? {
407
408
  className: "cursor-pointer",
408
409
  onDismiss: void 0,
409
410
  action: {
410
411
  label: "Open",
411
- onClick: () => W(d.room, d.label)
412
+ onClick: () => W(l.room, l.label)
412
413
  }
413
414
  } : {}
414
415
  }
415
416
  );
416
417
  }
417
- Q.set(d.room, d.unread);
418
+ Q.set(l.room, l.unread);
418
419
  }
419
420
  }
420
421
  j = a;
421
422
  }, [e, s.sound, t, n]);
422
423
  }
423
- function Ye() {
424
+ function Ze() {
424
425
  const e = m(), t = v();
425
426
  return h({
426
- mutationFn: (s) => e.request(K(`/chat/rooms/${encodeURIComponent(s)}/read`)),
427
+ mutationFn: (s) => e.request(O(`/chat/rooms/${encodeURIComponent(s)}/read`)),
427
428
  onSuccess: () => void t.invalidateQueries({ queryKey: ["nvr-chat-rooms"] })
428
429
  });
429
430
  }
430
- const he = ["nvr-chat-room-types"];
431
+ const ye = ["nvr-chat-room-types"];
431
432
  function V(e) {
432
433
  return {
433
- queryKey: he,
434
+ queryKey: ye,
434
435
  queryFn: async () => {
435
436
  const t = await e.request(g("/chat/room-types"));
436
437
  return ((t == null ? void 0 : t.data) ?? []).filter((s) => s.is_active);
@@ -438,28 +439,28 @@ function V(e) {
438
439
  staleTime: 5 * 6e4
439
440
  };
440
441
  }
441
- function Je(e) {
442
- const t = b(), s = m(), n = (e == null ? void 0 : e.indexOf(":")) ?? -1, o = e && n > 0 ? e.slice(0, n) : null, r = e && n > 0 ? e.slice(n + 1) : null, a = !!e && !!o && !!r && e !== t.globalRoom && o !== "dm" && o !== "ch", { data: i } = y({ ...V(s), enabled: a }), c = a ? i == null ? void 0 : i.find((l) => l.is_active && l.prefix === o) : void 0, { data: f } = y({
442
+ function Xe(e) {
443
+ const t = b(), s = m(), n = (e == null ? void 0 : e.indexOf(":")) ?? -1, o = e && n > 0 ? e.slice(0, n) : null, r = e && n > 0 ? e.slice(n + 1) : null, a = !!e && !!o && !!r && e !== t.globalRoom && o !== "dm" && o !== "ch", { data: i } = y({ ...V(s), enabled: a }), c = a ? i == null ? void 0 : i.find((d) => d.is_active && d.prefix === o) : void 0, { data: f } = y({
443
444
  queryKey: ["nvr-chat-entity-record", c == null ? void 0 : c.collection, c == null ? void 0 : c.match_field, r],
444
445
  queryFn: async () => {
445
- var d, q;
446
- return c.match_field === "id" ? r : ((q = (d = (await s.request(
446
+ var l, q;
447
+ return c.match_field === "id" ? r : ((q = (l = (await s.request(
447
448
  g(`/items/${c.collection}`, {
448
449
  limit: 1,
449
450
  fields: "id",
450
451
  filter: JSON.stringify({ [c.match_field]: { _eq: r } })
451
452
  })
452
- )).data) == null ? void 0 : d[0]) == null ? void 0 : q.id) ?? null;
453
+ )).data) == null ? void 0 : l[0]) == null ? void 0 : q.id) ?? null;
453
454
  },
454
455
  enabled: !!c && !!r,
455
456
  staleTime: 5 * 6e4
456
457
  });
457
- return !c || f == null ? null : (t.recordUrl ?? ((l, d) => `/collections/${l}/${d}`))(c.collection, f);
458
+ return !c || f == null ? null : (t.recordUrl ?? ((d, l) => `/collections/${d}/${l}`))(c.collection, f);
458
459
  }
459
- const ye = /@\[([^\]]+)\](?:\([^)]*\))?/g;
460
- function Ze(e, t) {
460
+ const ge = /@\[([^\]]+)\](?:\([^)]*\))?/g;
461
+ function et(e, t) {
461
462
  const s = new RegExp(t, "gi"), n = [];
462
- for (const a of e.matchAll(ye))
463
+ for (const a of e.matchAll(ge))
463
464
  n.push({
464
465
  start: a.index,
465
466
  end: a.index + a[0].length,
@@ -478,48 +479,51 @@ function Ze(e, t) {
478
479
  a.start > r && o.push({ text: e.slice(r, a.start) }), o.push(a.token), r = a.end;
479
480
  return r < e.length && o.push({ text: e.slice(r) }), o;
480
481
  }
481
- function Xe(e, t) {
482
+ function tt(e, t) {
482
483
  const n = e.slice(0, t).match(/@([^@\n\r]*)$/);
483
484
  return n ? n[1] : null;
484
485
  }
485
- const ge = 3e3;
486
- function et(e) {
486
+ const pe = 3e3;
487
+ function nt(e) {
487
488
  const t = b(), s = m();
488
489
  v();
489
490
  const n = U(!1), o = U(null), r = t.me, a = M(() => {
490
491
  o.current && clearTimeout(o.current), o.current = null, n.current && r && t.setTypingRoom && (n.current = !1, t.setTypingRoom(r.id, null));
491
492
  }, [r, t]), i = M(() => {
492
- !r || !e || !t.setTypingRoom || (n.current || (n.current = !0, t.setTypingRoom(r.id, e)), o.current && clearTimeout(o.current), o.current = setTimeout(a, ge));
493
+ !r || !e || !t.setTypingRoom || (n.current || (n.current = !0, t.setTypingRoom(r.id, e)), o.current && clearTimeout(o.current), o.current = setTimeout(a, pe));
493
494
  }, [r, e, t, a]);
494
- $(() => a, [a]);
495
- const { data: c } = y({
495
+ K(() => a, [a]);
496
+ const c = ne(6e3, {
497
+ safetyMs: 3e4,
498
+ enabled: !!t.realtime
499
+ }), { data: f } = y({
496
500
  queryKey: ["nvr-chat-typing", e],
497
501
  queryFn: async () => {
498
- const u = new Date(Date.now() - 6e4).toISOString();
502
+ const d = new Date(Date.now() - 6e4).toISOString();
499
503
  return (await s.request(
500
504
  J(t.collections.presence, {
501
505
  limit: 10,
502
506
  fields: ["user_id", "display_name", "typing_room", "last_seen"],
503
- filter: { typing_room: { _eq: e }, last_seen: { _gte: u } }
507
+ filter: { typing_room: { _eq: e }, last_seen: { _gte: d } }
504
508
  })
505
509
  )).data ?? [];
506
510
  },
507
511
  enabled: !!e && !!t.setTypingRoom,
508
- refetchInterval: 6e3,
512
+ refetchInterval: c,
509
513
  staleTime: 2e3
510
514
  });
511
- ue(t.collections.presence, [["nvr-chat-typing"]]);
512
- const f = k(() => {
513
- var d;
514
- const u = (d = r == null ? void 0 : r.id) == null ? void 0 : d.toLowerCase(), l = (c ?? []).filter((q) => {
515
- var p;
516
- return ((p = q.user_id) == null ? void 0 : p.toLowerCase()) !== u;
517
- }).map((q) => q.display_name ?? "Someone");
515
+ le(t.collections.presence, [["nvr-chat-typing"]]);
516
+ const u = k(() => {
517
+ var q;
518
+ const d = (q = r == null ? void 0 : r.id) == null ? void 0 : q.toLowerCase(), l = (f ?? []).filter((p) => {
519
+ var _;
520
+ return ((_ = p.user_id) == null ? void 0 : _.toLowerCase()) !== d;
521
+ }).map((p) => p.display_name ?? "Someone");
518
522
  return l.length === 0 ? null : l.length === 1 ? `${l[0]} is typing…` : l.length === 2 ? `${l[0]} and ${l[1]} are typing…` : `${l.length} people are typing…`;
519
- }, [c, r == null ? void 0 : r.id]);
520
- return { onType: i, clearTyping: a, typingText: f };
523
+ }, [f, r == null ? void 0 : r.id]);
524
+ return { onType: i, clearTyping: a, typingText: u };
521
525
  }
522
- function tt(e) {
526
+ function st(e) {
523
527
  const t = b(), s = m(), n = t.me, o = e && n ? B(e, n.id) : null, { data: r } = y({
524
528
  queryKey: ["nvr-chat-peer-read", e],
525
529
  queryFn: async () => {
@@ -536,21 +540,21 @@ function tt(e) {
536
540
  });
537
541
  return F([["nvr-chat-peer-read"]], e ? [e] : []), r ?? null;
538
542
  }
539
- function nt(e) {
543
+ function rt(e) {
540
544
  const t = m(), s = v();
541
545
  return h({
542
- mutationFn: ({ messageId: n, emoji: o }) => t.request(K(`/chat/messages/${n}/reactions`, { emoji: o })),
546
+ mutationFn: ({ messageId: n, emoji: o }) => t.request(O(`/chat/messages/${n}/reactions`, { emoji: o })),
543
547
  onSuccess: () => void s.invalidateQueries({ queryKey: ["nvr-chat", e] })
544
548
  });
545
549
  }
546
- function st(e) {
550
+ function ot(e) {
547
551
  const t = m(), s = v();
548
552
  return h({
549
- mutationFn: ({ messageId: n, text: o }) => t.request(C(`/chat/messages/${n}`, { message: o })),
553
+ mutationFn: ({ messageId: n, text: o }) => t.request(R(`/chat/messages/${n}`, { message: o })),
550
554
  onSuccess: () => void s.invalidateQueries({ queryKey: ["nvr-chat", e] })
551
555
  });
552
556
  }
553
- function rt(e) {
557
+ function at(e) {
554
558
  const t = m(), s = v();
555
559
  return h({
556
560
  mutationFn: (n) => t.request(A(`/chat/messages/${n}`)),
@@ -559,7 +563,7 @@ function rt(e) {
559
563
  }
560
564
  });
561
565
  }
562
- function ot(e, t = {}) {
566
+ function it(e, t = {}) {
563
567
  const s = m(), n = { q: e };
564
568
  t.sender && (n.sender = t.sender), t.room && (n.room = t.room), t.from && (n.from = t.from), t.to && (n.to = t.to), t.has_attachment && (n.has_attachment = "1"), t.mentions_me && (n.mentions_me = "1");
565
569
  const o = Object.keys(n).length > 1, { data: r, isLoading: a } = y({
@@ -576,14 +580,14 @@ function ot(e, t = {}) {
576
580
  });
577
581
  return { hits: (r == null ? void 0 : r.hits) ?? [], meaning: (r == null ? void 0 : r.meaning) ?? null, loading: a };
578
582
  }
579
- function at() {
583
+ function ct() {
580
584
  const e = m(), t = v();
581
585
  return h({
582
- mutationFn: async (s) => (await e.request(K("/chat/group-dm", s))).data,
586
+ mutationFn: async (s) => (await e.request(O("/chat/group-dm", s))).data,
583
587
  onSuccess: () => void t.invalidateQueries({ queryKey: ["nvr-chat-rooms"] })
584
588
  });
585
589
  }
586
- function pe() {
590
+ function ve() {
587
591
  const e = m(), { data: t } = y({
588
592
  queryKey: ["nvr-chat-config"],
589
593
  queryFn: async () => (await e.request(g("/chat/config"))).data ?? { bot_name: null, bot_user_id: null },
@@ -591,10 +595,10 @@ function pe() {
591
595
  });
592
596
  return t ?? { bot_name: null, bot_user_id: null };
593
597
  }
594
- function it() {
595
- return pe().bot_name;
598
+ function ut() {
599
+ return ve().bot_name;
596
600
  }
597
- function ct(e) {
601
+ function lt(e) {
598
602
  const t = m(), { data: s } = y({
599
603
  queryKey: ["nvr-chat-pins", e],
600
604
  queryFn: async () => (await t.request(
@@ -605,10 +609,10 @@ function ct(e) {
605
609
  });
606
610
  return F([["nvr-chat-pins"]], e ? [e] : []), s ?? [];
607
611
  }
608
- function ut(e) {
612
+ function dt(e) {
609
613
  const t = m(), s = v();
610
614
  return h({
611
- mutationFn: (n) => t.request(K(`/chat/messages/${n}/pin`)),
615
+ mutationFn: (n) => t.request(O(`/chat/messages/${n}/pin`)),
612
616
  onSuccess: () => void s.invalidateQueries({ queryKey: ["nvr-chat-pins", e] })
613
617
  });
614
618
  }
@@ -622,58 +626,58 @@ const I = [
622
626
  "#99f6e4",
623
627
  "#00ceff"
624
628
  ];
625
- function lt(e) {
626
- return I[ve(e)];
629
+ function mt(e) {
630
+ return I[qe(e)];
627
631
  }
628
- function ve(e) {
632
+ function qe(e) {
629
633
  let t = 0;
630
634
  for (const s of e) t = t * 31 + s.charCodeAt(0) | 0;
631
635
  return Math.abs(t) % I.length;
632
636
  }
633
- function dt(e) {
637
+ function ft(e) {
634
638
  return Number.isInteger(e) && e >= 0 && e < I.length ? I[e] : null;
635
639
  }
636
- function mt(e) {
640
+ function ht(e) {
637
641
  return [...e].sort((t, s) => {
638
642
  const n = (t.display_name ?? "").trim(), o = (s.display_name ?? "").trim();
639
643
  return !n != !o ? n ? -1 : 1 : n.localeCompare(o, void 0, { sensitivity: "base" }) || String(t.user_id).localeCompare(String(s.user_id));
640
644
  });
641
645
  }
642
- function ft(e) {
646
+ function yt(e) {
643
647
  var s, n;
644
648
  if (!e) return "?";
645
649
  const t = e.trim().split(/\s+/);
646
650
  return ((((s = t[0]) == null ? void 0 : s[0]) ?? "") + (((n = t[1]) == null ? void 0 : n[0]) ?? "")).toUpperCase() || "?";
647
651
  }
648
- let S = null;
649
- function ht(e) {
650
- return S = e, () => {
651
- S === e && (S = null);
652
- };
653
- }
654
- function yt() {
655
- return S !== null;
656
- }
657
- function gt(e, t) {
658
- S == null || S(e, t);
659
- }
660
652
  let T = null;
661
- function pt(e) {
653
+ function gt(e) {
662
654
  return T = e, () => {
663
655
  T === e && (T = null);
664
656
  };
665
657
  }
666
- function qe() {
658
+ function pt() {
667
659
  return T !== null;
668
660
  }
669
- function W(e, t) {
661
+ function vt(e, t) {
670
662
  T == null || T(e, t);
671
663
  }
672
- function vt(e = !0) {
664
+ let $ = null;
665
+ function qt(e) {
666
+ return $ = e, () => {
667
+ $ === e && ($ = null);
668
+ };
669
+ }
670
+ function be() {
671
+ return $ !== null;
672
+ }
673
+ function W(e, t) {
674
+ $ == null || $(e, t);
675
+ }
676
+ function bt(e = !0) {
673
677
  const t = m(), { data: s } = y({ ...V(t), enabled: e });
674
678
  return s;
675
679
  }
676
- async function qt(e, t, s) {
680
+ async function _t(e, t, s) {
677
681
  var r;
678
682
  const n = await e.request(
679
683
  g("/chat/record-room", { collection: t, item: s })
@@ -683,65 +687,65 @@ async function qt(e, t, s) {
683
687
  return W(n.data.room, o), !0;
684
688
  }
685
689
  export {
686
- ke as CHAT_DEFAULTS,
687
- he as CHAT_ROOM_TYPES_KEY,
688
- ce as ChatConfigContext,
689
- Ie as GENERAL_CHANNEL_ID,
690
- Me as REACTION_EMOJI,
691
- qe as canOpenChatRoom,
692
- yt as canOpenDm,
693
- lt as chatAvatarColor,
694
- dt as chatAvatarColorAt,
695
- ve as chatAvatarColorIndex,
696
- ft as chatInitials,
690
+ xe as CHAT_DEFAULTS,
691
+ ye as CHAT_ROOM_TYPES_KEY,
692
+ ue as ChatConfigContext,
693
+ Le as GENERAL_CHANNEL_ID,
694
+ ke as REACTION_EMOJI,
695
+ be as canOpenChatRoom,
696
+ pt as canOpenDm,
697
+ mt as chatAvatarColor,
698
+ ft as chatAvatarColorAt,
699
+ qe as chatAvatarColorIndex,
700
+ yt as chatInitials,
697
701
  V as chatRoomTypesQuery,
698
- Ct as discardOutbox,
699
- qt as discussRecord,
702
+ wt as discardOutbox,
703
+ _t as discussRecord,
700
704
  B as dmPeer,
701
- xe as dmRoom,
702
- Rt as editOutbox,
703
- Xe as getMentionQuery,
704
- wt as holdOutbox,
705
- St as nudgeChatOutbox,
705
+ Qe as dmRoom,
706
+ St as editOutbox,
707
+ tt as getMentionQuery,
708
+ Tt as holdOutbox,
709
+ $t as nudgeChatOutbox,
706
710
  W as openChatRoom,
707
- gt as openDmWith,
708
- ht as registerDmOpener,
709
- pt as registerRoomOpener,
710
- Tt as retryOutbox,
711
- mt as sortOnlineUsers,
712
- Ze as splitMessageTokens,
713
- De as useArchivedRooms,
714
- je as useChannelAdmin,
715
- Ne as useChannelDirectory,
716
- Pe as useChannelMembers,
717
- pe as useChatBotInfo,
718
- it as useChatBotName,
711
+ vt as openDmWith,
712
+ gt as registerDmOpener,
713
+ qt as registerRoomOpener,
714
+ Kt as retryOutbox,
715
+ ht as sortOnlineUsers,
716
+ et as splitMessageTokens,
717
+ Ee as useArchivedRooms,
718
+ He as useChannelAdmin,
719
+ Pe as useChannelDirectory,
720
+ ze as useChannelMembers,
721
+ ve as useChatBotInfo,
722
+ ut as useChatBotName,
719
723
  b as useChatConfig,
720
- Le as useChatMessages,
721
- ze as useChatRoles,
722
- Ae as useChatRooms,
723
- ot as useChatSearch,
724
- Qe as useChatThread,
725
- Be as useCreateChannel,
726
- at as useCreateGroupDm,
727
- rt as useDeleteMessage,
728
- st as useEditMessage,
729
- Je as useEntityRoomLink,
730
- Ye as useMarkRoomRead,
731
- x as useMyPreferences,
732
- me as useNotificationSoundPreference,
733
- Ge as useOpenRoomRegistration,
734
- $t as useOutbox,
735
- tt as usePeerReadAt,
736
- Ve as useRecordBrowserTimezone,
737
- vt as useRecordRoomTypes,
738
- Ee as useRoomMembership,
739
- ct as useRoomPins,
740
- Ue as useSendChatMessage,
741
- fe as useSetMyPreferences,
742
- ut as useTogglePin,
743
- nt as useToggleReaction,
744
- et as useTypingIndicator,
745
- We as useUnreadChirp,
746
- He as useUserSearch
724
+ Ue as useChatMessages,
725
+ Be as useChatRoles,
726
+ Ne as useChatRooms,
727
+ it as useChatSearch,
728
+ Ae as useChatThread,
729
+ Ve as useCreateChannel,
730
+ ct as useCreateGroupDm,
731
+ at as useDeleteMessage,
732
+ ot as useEditMessage,
733
+ Xe as useEntityRoomLink,
734
+ Ze as useMarkRoomRead,
735
+ L as useMyPreferences,
736
+ fe as useNotificationSoundPreference,
737
+ We as useOpenRoomRegistration,
738
+ Ot as useOutbox,
739
+ st as usePeerReadAt,
740
+ Ye as useRecordBrowserTimezone,
741
+ bt as useRecordRoomTypes,
742
+ je as useRoomMembership,
743
+ lt as useRoomPins,
744
+ De as useSendChatMessage,
745
+ he as useSetMyPreferences,
746
+ dt as useTogglePin,
747
+ rt as useToggleReaction,
748
+ nt as useTypingIndicator,
749
+ Je as useUnreadChirp,
750
+ Ge as useUserSearch
747
751
  };