@ethora/chat-component 26.9.5 → 26.9.8

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 (81) hide show
  1. package/README.md +45 -1
  2. package/dist/{ChatProfileModal-Pr4--RdW.js → ChatProfileModal-3UH0H5Ec.js} +725 -714
  3. package/dist/{FilePreviewModal-CYHlKzRc.js → FilePreviewModal-Cgj1iHry.js} +161 -161
  4. package/dist/{ManageDataModal-BE1eGlYh.js → ManageDataModal-BblTuE9g.js} +14 -14
  5. package/dist/{MarkdownBody-G0_J0NL6.js → MarkdownBody-RMEx8yW2.js} +1 -1
  6. package/dist/MessageSearchModal-CrYR2EH8.js +381 -0
  7. package/dist/PublicChatsModal-Bi5r4mrV.js +202 -0
  8. package/dist/{SideDrawer-BG2m7Bcm.js → SideDrawer-CDAoobvX.js} +34 -34
  9. package/dist/{UserProfileModal-B2Hp5bAF.js → UserProfileModal-4Frgvam-.js} +3 -3
  10. package/dist/{UserSettingsModal-D871FDAT.js → UserSettingsModal-C-mb-9cb.js} +2 -2
  11. package/dist/{VideoCallOverlay-Br0ji0nZ.js → VideoCallOverlay-92dFLb9U.js} +1 -1
  12. package/dist/{VisibilityModal-DdFBV1_g.js → VisibilityModal-zIAV6dAP.js} +9 -9
  13. package/dist/assets/icons.d.ts +9 -0
  14. package/dist/components/InputComponents/MentionDropdown.d.ts +2 -0
  15. package/dist/components/InputComponents/Search.d.ts +4 -0
  16. package/dist/components/MainComponents/ArchivedMessageCard.d.ts +11 -0
  17. package/dist/components/MainComponents/scrollAnchor.d.ts +19 -0
  18. package/dist/components/MainComponents/useJumpToMessage.d.ts +94 -0
  19. package/dist/components/MainComponents/useScrollAnchor.d.ts +29 -0
  20. package/dist/components/MainComponents/useStickToBottom.d.ts +27 -0
  21. package/dist/components/Modals/MessageSearchModal/MessageHitResults.d.ts +27 -0
  22. package/dist/components/Modals/MessageSearchModal/MessageSearchModal.d.ts +6 -0
  23. package/dist/components/Modals/MessageSearchModal/resolveSender.d.ts +33 -0
  24. package/dist/components/Modals/MessageSearchModal/searchFilters.d.ts +34 -0
  25. package/dist/components/Modals/MessageSearchModal/snippet.d.ts +14 -0
  26. package/dist/components/Modals/MessageSearchModal/useMessageSearch.d.ts +22 -0
  27. package/dist/components/Modals/PublicChatsModal/PublicChatsModal.d.ts +13 -0
  28. package/dist/components/Modals/styledModalComponents.d.ts +0 -8
  29. package/dist/components/styled/RoomListComponents/index.d.ts +3 -0
  30. package/dist/context/ToastContext.d.ts +7 -0
  31. package/dist/firebase-messaging-sw.js +21 -3
  32. package/dist/{handleCopyClick-DPjxMoJn.js → handleCopyClick-BtXos0AJ.js} +1 -1
  33. package/dist/helpers/checkUniqueUsers.d.ts +2 -2
  34. package/dist/helpers/constants/MODAL_TYPES.d.ts +2 -0
  35. package/dist/helpers/dedupeMembers.d.ts +10 -0
  36. package/dist/helpers/historyPreloadConfig.d.ts +25 -0
  37. package/dist/helpers/historyPreloadScheduler.d.ts +3 -0
  38. package/dist/helpers/insertMessageWithDelimiter.d.ts +1 -0
  39. package/dist/helpers/isMessageSearchEnabled.d.ts +9 -0
  40. package/dist/helpers/jumpThread.d.ts +56 -0
  41. package/dist/helpers/jumpWindow.d.ts +73 -0
  42. package/dist/helpers/nanoToMs.d.ts +1 -1
  43. package/dist/helpers/openRoomAtMessage.d.ts +17 -0
  44. package/dist/helpers/openThreadForJump.d.ts +26 -0
  45. package/dist/helpers/pdf/loadPdfjs.d.ts +8 -1
  46. package/dist/helpers/roomUserCount.d.ts +19 -0
  47. package/dist/helpers/userResolver.d.ts +57 -0
  48. package/dist/helpers/xmppIdShape.d.ts +1 -1
  49. package/dist/hooks/useChatWrapperInit.d.ts +6 -0
  50. package/dist/hooks/useMessageSearchShortcut.d.ts +14 -0
  51. package/dist/hooks/usePdfThumbnail.d.ts +2 -1
  52. package/dist/hooks/useRoomDirectory.d.ts +11 -0
  53. package/dist/hooks/useRoomState.d.ts +1 -0
  54. package/dist/{main-BRIZ98KV.js → main-BnLtessp.js} +24764 -21859
  55. package/dist/main.css +1 -1
  56. package/dist/main.js +16 -16
  57. package/dist/networking/api-requests/messageSearch.api.d.ts +56 -0
  58. package/dist/networking/api-requests/publicChats.api.d.ts +23 -0
  59. package/dist/networking/api-requests/roomMembers.api.d.ts +18 -0
  60. package/dist/networking/api-requests/xmppToken.api.d.ts +1 -0
  61. package/dist/networking/ethoraEvents.d.ts +44 -0
  62. package/dist/networking/stanzaHandlers.d.ts +2 -2
  63. package/dist/networking/xmpp/allRoomPresences.xmpp.d.ts +18 -1
  64. package/dist/networking/xmpp/presenceInRoom.xmpp.d.ts +1 -1
  65. package/dist/networking/xmppClient.d.ts +58 -2
  66. package/dist/roomStore/Middleware/jumpThreadMiddleware.d.ts +2 -0
  67. package/dist/roomStore/chatSettingsSlice.d.ts +1 -1
  68. package/dist/roomStore/index.d.ts +12 -0
  69. package/dist/roomStore/roomsSlice.d.ts +70 -1
  70. package/dist/roomStore/safeKey.d.ts +12 -0
  71. package/dist/roomStore/usersSetCap.d.ts +15 -0
  72. package/dist/styles/classNames.d.ts +2 -0
  73. package/dist/types/models/config.model.d.ts +78 -0
  74. package/dist/types/models/room.model.d.ts +14 -0
  75. package/dist/types/models/xmpp.model.d.ts +21 -1
  76. package/dist/utils/notificationUtils.d.ts +12 -0
  77. package/dist/version.d.ts +1 -1
  78. package/package.json +2 -1
  79. package/public/firebase-messaging-sw.js +21 -3
  80. package/dist/components/Modals/NewChatModal/UpdNewChatModal.d.ts +0 -3
  81. package/dist/components/SortDropDown/index.d.ts +0 -8
@@ -1,6 +1,6 @@
1
1
  import { jsxs as i, jsx as o } from "react/jsx-runtime";
2
2
  import { useId as f, useRef as v } from "react";
3
- import { p as u, y as g, aI as m, aJ as w, B as b, aK as y, x as e, aL as D, aM as k } from "./main-BRIZ98KV.js";
3
+ import { p as u, y as g, aO as m, aV as w, B as b, aW as y, x as e, aX as D, aY as k } from "./main-BnLtessp.js";
4
4
  const c = 767, R = `${c}px`, z = e.div`
5
5
  position: relative;
6
6
  pointer-events: auto;
@@ -58,11 +58,11 @@ const c = 767, R = `${c}px`, z = e.div`
58
58
  padding-left: var(--ethora-space-4, 16px);
59
59
  padding-right: var(--ethora-space-4, 16px);
60
60
  }
61
- `, $ = e.section`
61
+ `, O = e.section`
62
62
  display: flex;
63
63
  flex-direction: column;
64
64
  gap: var(--ethora-space-2, 8px);
65
- `, K = e.h3`
65
+ `, W = e.h3`
66
66
  margin: 0;
67
67
  padding: 0 var(--ethora-space-1, 4px);
68
68
  font-size: var(--ethora-font-size-xs, 12px);
@@ -70,7 +70,7 @@ const c = 767, R = `${c}px`, z = e.div`
70
70
  letter-spacing: 0.06em;
71
71
  text-transform: uppercase;
72
72
  color: var(--ethora-color-text-muted, #8c8c8c);
73
- `, O = e.div`
73
+ `, $ = e.div`
74
74
  display: flex;
75
75
  flex-direction: column;
76
76
  box-sizing: border-box;
@@ -79,17 +79,17 @@ const c = 767, R = `${c}px`, z = e.div`
79
79
  border-radius: var(--ethora-radius-md, 12px);
80
80
  background-color: var(--ethora-color-bg, #fff);
81
81
  overflow: hidden;
82
- `, W = e.div`
82
+ `, _ = e.div`
83
83
  display: flex;
84
84
  flex-direction: column;
85
85
  gap: var(--ethora-space-1, 4px);
86
86
  padding: var(--ethora-space-3, 12px) var(--ethora-space-4, 16px);
87
- `, _ = e.div`
87
+ `, K = e.div`
88
88
  display: flex;
89
89
  align-items: baseline;
90
90
  justify-content: space-between;
91
91
  gap: var(--ethora-space-2, 8px);
92
- `, F = e.button`
92
+ `, V = e.button`
93
93
  border: none;
94
94
  background: transparent;
95
95
  padding: var(--ethora-space-2, 8px) 0;
@@ -109,11 +109,11 @@ const c = 767, R = `${c}px`, z = e.div`
109
109
  outline-offset: 2px;
110
110
  border-radius: var(--ethora-radius-sm, 8px);
111
111
  }
112
- `, J = e.div`
112
+ `, X = e.div`
113
113
  height: 1px;
114
114
  margin: 0 var(--ethora-space-4, 16px);
115
115
  background-color: var(--ethora-color-border, #e6e8ec);
116
- `, V = e.div`
116
+ `, F = e.div`
117
117
  display: flex;
118
118
  align-items: center;
119
119
  gap: var(--ethora-space-2, 8px);
@@ -135,7 +135,7 @@ const c = 767, R = `${c}px`, z = e.div`
135
135
  & > svg {
136
136
  flex: 0 0 auto;
137
137
  }
138
- `, X = e.input`
138
+ `, Y = e.input`
139
139
  flex: 1 1 auto;
140
140
  min-width: 0;
141
141
  border: none;
@@ -152,18 +152,18 @@ const c = 767, R = `${c}px`, z = e.div`
152
152
  display: flex;
153
153
  flex-direction: column;
154
154
  width: 100%;
155
- `, L = e.div`
155
+ `, T = e.div`
156
156
  font-size: var(--ethora-font-size-sm, 14px);
157
157
  font-weight: var(--ethora-font-weight-medium, 500);
158
158
  color: var(--ethora-color-text, #141414);
159
159
  text-align: start;
160
- `, T = e.div`
160
+ `, C = e.div`
161
161
  font-size: var(--ethora-font-size-xs, 12px);
162
162
  font-weight: var(--ethora-font-weight-regular, 400);
163
163
  color: var(--ethora-color-text-muted, #8c8c8c);
164
164
  text-align: start;
165
165
  line-height: 1.4;
166
- `, C = e.button`
166
+ `, E = e.button`
167
167
  display: flex;
168
168
  align-items: center;
169
169
  gap: var(--ethora-space-3, 12px);
@@ -186,17 +186,17 @@ const c = 767, R = `${c}px`, z = e.div`
186
186
  outline: 2px solid var(--ethora-color-primary, #0052cd);
187
187
  outline-offset: -2px;
188
188
  }
189
- `, E = e.span`
189
+ `, L = e.span`
190
190
  display: flex;
191
191
  flex-direction: column;
192
192
  gap: var(--ethora-space-1, 4px);
193
193
  flex: 1 1 auto;
194
194
  min-width: 0;
195
- `, M = e.span`
195
+ `, j = e.span`
196
196
  flex: 0 0 auto;
197
197
  display: inline-flex;
198
198
  color: var(--ethora-color-text-muted, #8c8c8c);
199
- `, j = () => /* @__PURE__ */ o("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ o(
199
+ `, M = () => /* @__PURE__ */ o("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ o(
200
200
  "path",
201
201
  {
202
202
  d: "M6 3.5L10.5 8L6 12.5",
@@ -209,13 +209,13 @@ const c = 767, R = `${c}px`, z = e.div`
209
209
  label: r,
210
210
  hint: a,
211
211
  onClick: t
212
- }) => /* @__PURE__ */ i(C, { type: "button", onClick: t, children: [
213
- /* @__PURE__ */ i(E, { children: [
214
- /* @__PURE__ */ o(L, { children: r }),
215
- a && /* @__PURE__ */ o(T, { children: a })
212
+ }) => /* @__PURE__ */ i(E, { type: "button", onClick: t, children: [
213
+ /* @__PURE__ */ i(L, { children: [
214
+ /* @__PURE__ */ o(T, { children: r }),
215
+ a && /* @__PURE__ */ o(C, { children: a })
216
216
  ] }),
217
- /* @__PURE__ */ o(M, { "aria-hidden": "true", children: /* @__PURE__ */ o(j, {}) })
218
- ] }), Q = ({
217
+ /* @__PURE__ */ o(j, { "aria-hidden": "true", children: /* @__PURE__ */ o(M, {}) })
218
+ ] }), J = ({
219
219
  title: r,
220
220
  onClose: a,
221
221
  headerActions: t,
@@ -253,18 +253,18 @@ const c = 767, R = `${c}px`, z = e.div`
253
253
  );
254
254
  };
255
255
  export {
256
- $ as D,
257
- Q as S,
258
- K as a,
259
- O as b,
260
- J as c,
256
+ O as D,
257
+ J as S,
258
+ W as a,
259
+ $ as b,
260
+ X as c,
261
261
  G as d,
262
- W as e,
263
- T as f,
264
- L as g,
265
- _ as h,
266
- V as i,
267
- X as j,
262
+ _ as e,
263
+ C as f,
264
+ T as g,
265
+ K as h,
266
+ F as i,
267
+ Y as j,
268
268
  q as k,
269
- F as l
269
+ V as l
270
270
  };
@@ -1,8 +1,8 @@
1
1
  import { jsxs as u, Fragment as $, jsx as o } from "react/jsx-runtime";
2
2
  import { forwardRef as ge, useState as I, useCallback as y, useImperativeHandle as ve, useRef as ae, useMemo as M } from "react";
3
- import { a as be, p as J, t as ye, v as xe, w as we, P as re, I as H, x as D, y as Ce, z as Se, C as Ie, D as te, E as De, F as Ne, G as ke, J as Ue, u as Q, K as Fe, L as Pe, N as Ae, O as Re, Q as Y, q as Z, R as ze, S as je, T as Be, s as $e, B as X, U as Ee, W as Oe, X as Te, Y as ee, Z as R, _ as Le, $ as Me, V as He, A as Xe, a0 as We, a1 as Je, a2 as Ke, a3 as Ve } from "./main-BRIZ98KV.js";
4
- import { D as E, a as W, b as Ge, e as qe, f as _e, S as Qe } from "./SideDrawer-BG2m7Bcm.js";
5
- import { P as ne, a as Ye, D as Ze, b as eo, h as oo } from "./handleCopyClick-DPjxMoJn.js";
3
+ import { a as be, p as J, t as ye, v as xe, w as we, P as re, I as H, x as D, y as Ce, z as Se, C as Ie, D as te, E as De, F as Ne, G as ke, J as Ue, u as Q, K as Fe, L as Pe, N as Ae, O as Re, Q as Y, q as Z, R as ze, S as je, T as Be, s as $e, B as X, U as Ee, W as Oe, X as Te, Y as ee, Z as R, _ as Le, $ as Me, V as He, A as Xe, a0 as We, a1 as Je, a2 as Ke, a3 as Ve } from "./main-BnLtessp.js";
4
+ import { D as E, a as W, b as Ge, e as qe, f as _e, S as Qe } from "./SideDrawer-CDAoobvX.js";
5
+ import { P as ne, a as Ye, D as Ze, b as eo, h as oo } from "./handleCopyClick-BtXos0AJ.js";
6
6
  const ao = (c, i) => {
7
7
  const l = atob(c.split(",")[1]), g = new ArrayBuffer(l.length), e = new Uint8Array(g);
8
8
  for (let r = 0; r < l.length; r++)
@@ -1,7 +1,7 @@
1
1
  import { jsx as i, jsxs as r } from "react/jsx-runtime";
2
2
  import m, { useCallback as D, useMemo as h } from "react";
3
- import { p, a as g, q as w, M as l } from "./main-BRIZ98KV.js";
4
- import { S as b, D as u, a as k, b as y, c as S, d as f } from "./SideDrawer-BG2m7Bcm.js";
3
+ import { p, a as g, q as w, M as l } from "./main-BnLtessp.js";
4
+ import { S as b, D as u, a as k, b as y, c as S, d as f } from "./SideDrawer-CDAoobvX.js";
5
5
  const T = ({
6
6
  handleCloseModal: n
7
7
  }) => {
@@ -1,7 +1,7 @@
1
1
  import { jsxs as K, jsx as x, Fragment as Hr } from "react/jsx-runtime";
2
2
  import * as F from "react";
3
3
  import { useState as Yt, useRef as ke, useEffect as be, useMemo as Wt, useCallback as mi } from "react";
4
- import { u as Xi, P as Kn, H as xi, a as Hd, b as zd, c as Kd, d as Gd, s as po, e as Jd, f as Yd, g as fo, h as $d, i as Xd, j as mo, k as Qd, l as Zd, r as go, m as eu, n as tu, A as vo, V as bo, B as nu } from "./main-BRIZ98KV.js";
4
+ import { u as Xi, P as Kn, H as xi, a as Hd, b as zd, c as Kd, d as Gd, s as po, e as Jd, f as Yd, g as fo, h as $d, i as Xd, j as mo, k as Qd, l as Zd, r as go, m as eu, n as tu, A as vo, V as bo, B as nu } from "./main-BnLtessp.js";
5
5
  function iu(n, e) {
6
6
  return e.forEach(function(t) {
7
7
  t && typeof t != "string" && !Array.isArray(t) && Object.keys(t).forEach(function(i) {
@@ -1,7 +1,7 @@
1
1
  import { jsxs as r, jsx as s } from "react/jsx-runtime";
2
2
  import { useState as b, useEffect as x } from "react";
3
- import { x as o, a as L, p as P, u as z, aG as D, aH as I } from "./main-BRIZ98KV.js";
4
- import { S as N, D as C, a as $, b as k, e as w, f as p } from "./SideDrawer-BG2m7Bcm.js";
3
+ import { x as o, a as L, p as N, u as P, aM as D, aN as z } from "./main-BnLtessp.js";
4
+ import { S as I, D as C, a as $, b as k, e as w, f as p } from "./SideDrawer-CDAoobvX.js";
5
5
  o.div``;
6
6
  o.div`
7
7
  font-size: 16px;
@@ -25,7 +25,7 @@ const O = o.div`
25
25
  `, u = o.input`
26
26
  accent-color: ${({ $radioColor: i }) => i || "var(--ethora-color-primary, #0052CD)"};
27
27
  margin: 0px;
28
- `, R = o.div`
28
+ `, M = o.div`
29
29
  padding: 10px 15px;
30
30
  margin-top: 16px;
31
31
  color: ${({ type: i }) => i === "success" ? "#10b981" : "#ef4444"};
@@ -39,12 +39,12 @@ const O = o.div`
39
39
  background: ${({ type: i }) => i === "success" ? "rgba(16, 185, 129, 0.15)" : "rgba(242, 118, 107, 0.15)"};
40
40
  border-color: ${({ type: i }) => i === "success" ? "var(--ethora-color-success, #10b981)" : "var(--ethora-color-danger, #ef4444)"};
41
41
  }
42
- `, H = ({
42
+ `, B = ({
43
43
  handleCloseModal: i
44
44
  }) => {
45
- const S = L(), e = P(), { user: n, config: l } = z(
45
+ const S = L(), e = N(), { user: n, config: l } = P(
46
46
  (t) => t.chatSettingStore
47
- ), g = (t) => S(I(t)), [a, m] = b(n?.isProfileOpen), [c, v] = b(n?.isAssetsOpen), [f, y] = b(null), d = (t, A) => {
47
+ ), g = (t) => S(z(t)), [a, m] = b(n?.isProfileOpen), [c, v] = b(n?.isAssetsOpen), [f, y] = b(null), d = (t, A) => {
48
48
  y({ message: t, type: A }), setTimeout(() => y(null), 3e3);
49
49
  };
50
50
  return x(() => {
@@ -56,7 +56,7 @@ const O = o.div`
56
56
  g(t.user), d(e("notification.saved"), "success");
57
57
  }).catch(() => d(e("toast.error"), "error"));
58
58
  }, [c]), /* @__PURE__ */ r(
59
- N,
59
+ I,
60
60
  {
61
61
  title: e("settings.visibility.title"),
62
62
  onClose: i,
@@ -128,11 +128,11 @@ const O = o.div`
128
128
  /* @__PURE__ */ s(p, { children: e("settings.visibility.individualDescription") })
129
129
  ] }) }) })
130
130
  ] }),
131
- f && /* @__PURE__ */ s(R, { type: f.type, children: f.message })
131
+ f && /* @__PURE__ */ s(M, { type: f.type, children: f.message })
132
132
  ]
133
133
  }
134
134
  );
135
135
  };
136
136
  export {
137
- H as default
137
+ B as default
138
138
  };
@@ -1,3 +1,12 @@
1
+ /**
2
+ * A compass: "discover". Deliberately not the globe, which is the language
3
+ * selector's icon in the chat header, so the two buttons read as different
4
+ * things when they sit next to each other.
5
+ */
6
+ export declare const DiscoverIcon: ({ color, ...props }: {
7
+ [x: string]: any;
8
+ color?: string;
9
+ }) => import("react/jsx-runtime").JSX.Element;
1
10
  export declare const GlobeIcon: ({ color, ...props }: {
2
11
  [x: string]: any;
3
12
  color?: string;
@@ -8,6 +8,8 @@ interface MentionDropdownProps {
8
8
  onSelect: (candidate: MentionCandidate) => void;
9
9
  onShowAll: () => void;
10
10
  onHoverIndex: (index: number) => void;
11
+ /** More people are still being loaded in the background. */
12
+ loading?: boolean;
11
13
  }
12
14
  export declare const MentionDropdown: React.FC<MentionDropdownProps>;
13
15
  export default MentionDropdown;
@@ -4,6 +4,10 @@ interface SearchInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
4
4
  animated?: boolean;
5
5
  direction?: 'left' | 'right';
6
6
  colorBg?: string;
7
+ /** When set, a clear button shows while the input has text. */
8
+ onClear?: () => void;
9
+ /** Accessible label for the clear button (pass a translated string). */
10
+ clearLabel?: string;
7
11
  }
8
12
  declare const SearchInput: React.FC<SearchInputProps>;
9
13
  export { SearchInput };
@@ -0,0 +1,11 @@
1
+ import { default as React } from 'react';
2
+ /**
3
+ * The message a search hit points at, shown when the transcript cannot reach
4
+ * it (the search archive is deeper than the chat's own history). It sits over
5
+ * the conversation of the room it came from, so it needs no navigation and
6
+ * says what the message is instead of reporting that it was not found.
7
+ */
8
+ declare const ArchivedMessageCard: React.FC<{
9
+ roomJID: string;
10
+ }>;
11
+ export default ArchivedMessageCard;
@@ -0,0 +1,19 @@
1
+ export interface AnchorRowRect {
2
+ id: string;
3
+ top: number;
4
+ bottom: number;
5
+ }
6
+ export interface ScrollAnchor {
7
+ id: string;
8
+ /** Row top relative to the scroller's top edge. */
9
+ offset: number;
10
+ }
11
+ /**
12
+ * The row to hold still while content changes around it: the first row that
13
+ * is fully inside the viewport, or, when none is (a row taller than the
14
+ * viewport, or the viewport cuts every row), the first one that intersects it.
15
+ * `rows` must be in document order.
16
+ */
17
+ export declare function pickAnchorRow(viewTop: number, viewBottom: number, rows: AnchorRowRect[]): ScrollAnchor | null;
18
+ /** How far scrollTop must move for the anchor row to sit at its old offset. */
19
+ export declare function anchorScrollDelta(anchor: ScrollAnchor, rowTop: number, viewTop: number): number;
@@ -0,0 +1,94 @@
1
+ import { MutableRefObject, RefObject } from 'react';
2
+ import { PendingJump } from '../../roomStore/roomsSlice';
3
+ import { IMessage } from '../../types/types';
4
+ import { findMessageIndex } from '../../helpers/jumpThread';
5
+ export { findMessageIndex };
6
+ export declare const NEAR_LIVE_MESSAGES = 300;
7
+ /**
8
+ * How the attempt to open a window around the target went:
9
+ * - found: the window is in the store; the list now renders it;
10
+ * - missing: the server answered and the target is not in its archive;
11
+ * - unavailable: could not ask (no archive id, request failed): page instead.
12
+ */
13
+ export type WindowFetchResult = 'found' | 'missing' | 'unavailable';
14
+ /** What the server said about one page of older history. */
15
+ export interface OlderPage {
16
+ /** False when the request failed or timed out. */
17
+ ok: boolean;
18
+ /**
19
+ * The RSM `<first>` of the page: the id (microseconds) of the oldest ROW the
20
+ * server returned. The next page continues from here. It is NOT the oldest
21
+ * message that got displayed: a page can be made entirely of reactions,
22
+ * system rows or deleted messages, display nothing, and still be followed
23
+ * by plenty of history.
24
+ */
25
+ cursor?: number | null;
26
+ /** The server's `<fin complete>`: there is nothing older. */
27
+ complete?: boolean;
28
+ }
29
+ interface Options {
30
+ roomJID: string;
31
+ /** The room's messages, in display order. */
32
+ messages: IMessage[];
33
+ /** How many of them are currently mounted (the newest N). */
34
+ visibleCount: number;
35
+ setRenderWindow: (size: number) => void;
36
+ /**
37
+ * Fetches the next page of older history by MAM and resolves once that page
38
+ * is in the store, with the server's own cursor for what it returned.
39
+ */
40
+ fetchOlderPage: (roomJID: string, before: number, max: number) => Promise<OlderPage>;
41
+ containerRef: RefObject<HTMLElement>;
42
+ historyComplete?: boolean;
43
+ /**
44
+ * MessageList's "reader is away from the bottom" flag. Set for the length of
45
+ * a jump: paging older history grows the message count, and with the flag
46
+ * false the list reads that as new messages arriving and scrolls to the
47
+ * bottom, undoing the jump the moment the target loads.
48
+ */
49
+ isUserScrolledUpRef: MutableRefObject<boolean>;
50
+ /** `messages` is a jump window around an earlier target, not the live list. */
51
+ jumpWindowActive?: boolean;
52
+ /**
53
+ * Opens a window around a far target (and puts it in the store). Omitted
54
+ * where windows do not apply (threads); the paging path is used then.
55
+ */
56
+ fetchWindow?: (jump: PendingJump) => Promise<WindowFetchResult>;
57
+ /**
58
+ * Which list this instance is. A jump is fulfilled by exactly one list: the
59
+ * main list, or the thread its target is a reply in. The other instances
60
+ * stay inert for it, so one of them cannot give up (and show the archived
61
+ * card) while the owner is still working.
62
+ */
63
+ scope?: 'main' | {
64
+ threadId: string;
65
+ };
66
+ /**
67
+ * The room's messages BEFORE the main list's filter (which hides thread
68
+ * replies). Lets the main list recognise a target that is a thread reply.
69
+ */
70
+ allMessages?: IMessage[];
71
+ /**
72
+ * Opens the thread a reply target belongs to (main list only). Resolves true
73
+ * when the thread is open and owns the jump from now on.
74
+ */
75
+ resolveReply?: (jump: PendingJump, reply: IMessage) => Promise<boolean>;
76
+ /**
77
+ * The room is still opening (joining, loading its first history). While it
78
+ * is, a request that cannot be answered yet is waited out, not counted as a
79
+ * failure: the card is for "the server answered and the message is absent".
80
+ */
81
+ roomOpening?: boolean;
82
+ }
83
+ /**
84
+ * Fulfils a pending "scroll to this message" request for this room.
85
+ *
86
+ * The target can be in one of three places, tried in this order:
87
+ * 1. mounted: scroll to it and flash it;
88
+ * 2. in the store but older than the render window: widen the window to
89
+ * include it, then scroll;
90
+ * 3. not loaded at all: page older history from the server until it shows
91
+ * up, bounded by MAX_HISTORY_PAGES and by the end of the archive.
92
+ * If it cannot be reached, say so rather than leaving the tap looking dead.
93
+ */
94
+ export declare function useJumpToMessage({ roomJID, messages, visibleCount, setRenderWindow, fetchOlderPage, containerRef, historyComplete, isUserScrolledUpRef, jumpWindowActive, fetchWindow, scope, allMessages, resolveReply, roomOpening, }: Options): void;
@@ -0,0 +1,29 @@
1
+ import { RefObject } from 'react';
2
+ /** The reader counts as still after this long without scrolling. */
3
+ export declare const ANCHOR_IDLE_MS = 100;
4
+ interface Options {
5
+ containerRef: RefObject<HTMLElement>;
6
+ /** The element whose height changes as rows grow. */
7
+ flowRef: RefObject<HTMLElement>;
8
+ /** Identity changes whenever the mounted rows can change. */
9
+ rows: unknown;
10
+ /** True when the reader follows the bottom: nothing is anchored then. */
11
+ isStuckToBottom: () => boolean;
12
+ }
13
+ /**
14
+ * Keeps the rows the reader is looking at perfectly still while content is
15
+ * added or resized above or below them (older history prepended, a jump
16
+ * window paged either way, the render window widened, rows trimmed).
17
+ *
18
+ * - Just before a commit that can change the rows, the anchor is captured from
19
+ * the live DOM (the render phase still sees the previous layout).
20
+ * - The layout effect puts it back before paint, by the anchor ROW's own
21
+ * movement, so a row that gained a date divider or header is absorbed too.
22
+ * - Rows keep changing height after a commit (images, embeds, waveforms).
23
+ * Whenever the content resizes and the reader has been still for
24
+ * ANCHOR_IDLE_MS, the anchor is re-applied by a ResizeObserver. The anchor
25
+ * is re-taken ANCHOR_IDLE_MS after any scroll this hook did not make (the
26
+ * reader, a jump) and dropped while that scroll is going on.
27
+ */
28
+ export declare function useScrollAnchor({ containerRef, flowRef, rows, isStuckToBottom, }: Options): void;
29
+ export {};
@@ -0,0 +1,27 @@
1
+ import { MutableRefObject, RefObject } from 'react';
2
+ interface Options {
3
+ /** The scrolling element. */
4
+ containerRef: RefObject<HTMLElement>;
5
+ /** The element that grows as messages, images and history arrive. */
6
+ contentRef: RefObject<HTMLElement>;
7
+ /** MessageList's "the reader is away from the bottom" flag. */
8
+ isUserScrolledUpRef: MutableRefObject<boolean>;
9
+ /** An unread delimiter means the list opens ON it, not at the bottom. */
10
+ hasUnreadDelimiter: boolean;
11
+ }
12
+ /**
13
+ * Keeps a transcript pinned to the bottom while it is still filling in.
14
+ *
15
+ * Opening a room scrolls to the bottom once, and then the room keeps
16
+ * growing: history pages arrive (prepended above), images and embeds finish
17
+ * sizing. The scrollTop stays where it was, so the reader is left hundreds of
18
+ * pixels above the newest message (measured 1477px, and it stayed there),
19
+ * looking at the middle of the conversation for no visible reason.
20
+ *
21
+ * So: whenever the content's height changes and the reader has not scrolled
22
+ * away, go back to the bottom. A reader who has scrolled up (which also
23
+ * covers a jump to a message, and paging older history by scrolling up) is
24
+ * left exactly where they are.
25
+ */
26
+ export declare function useStickToBottom({ containerRef, contentRef, isUserScrolledUpRef, hasUnreadDelimiter, }: Options): void;
27
+ export {};
@@ -0,0 +1,27 @@
1
+ import { default as React } from 'react';
2
+ import { MessageSearchHit } from '../../../networking/api-requests/messageSearch.api';
3
+ /**
4
+ * Secondary pill button used for "Show more" and "Retry" under search
5
+ * results, in the search panel and in the room list.
6
+ */
7
+ export declare const SecondaryPillButton: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never>> & string;
8
+ /**
9
+ * What a tap on a search hit does, shared by the search panel and the room
10
+ * list's search box: switch to the hit's room and ask it to scroll to, load
11
+ * and flash the message.
12
+ */
13
+ export declare function useMessageHitActions(): {
14
+ rooms: any;
15
+ senderName: (hit: MessageSearchHit) => string;
16
+ open: (hit: MessageSearchHit) => boolean;
17
+ isReachable: (hit: MessageSearchHit) => boolean;
18
+ };
19
+ interface MessageHitListProps {
20
+ hits: MessageSearchHit[];
21
+ query: string;
22
+ /** Prefix each row with its chat's title (search across chats). */
23
+ showRoom: boolean;
24
+ onOpen: (hit: MessageSearchHit) => void;
25
+ }
26
+ export declare const MessageHitList: React.FC<MessageHitListProps>;
27
+ export {};
@@ -0,0 +1,6 @@
1
+ import { default as React } from 'react';
2
+ interface MessageSearchModalProps {
3
+ handleCloseModal: () => void;
4
+ }
5
+ declare const MessageSearchModal: React.FC<MessageSearchModalProps>;
6
+ export default MessageSearchModal;
@@ -0,0 +1,33 @@
1
+ interface PersonLike {
2
+ _id?: string;
3
+ id?: string;
4
+ firstName?: string;
5
+ lastName?: string;
6
+ name?: string;
7
+ xmppUsername?: string;
8
+ }
9
+ /** `appId_userId@xmpp.host` (or without the host) -> `appId_userId`. */
10
+ export declare const localPart: (jid?: string) => string;
11
+ interface Sources {
12
+ /** rooms.usersSet: keyed by `appId_userId`. */
13
+ usersSet?: Record<string, PersonLike>;
14
+ /** The hit's room members, which carry the user id search reports. */
15
+ members?: PersonLike[];
16
+ myXmppUsername?: string;
17
+ }
18
+ /**
19
+ * Who sent a search hit.
20
+ *
21
+ * `from` arrives as a full JID with the XMPP host on it, while usersSet is
22
+ * keyed by the local part, so looking it up as-is found nobody and every hit
23
+ * was attributed to "Someone". The user id (`fromUserId`) is the other way
24
+ * in: room members carry it as `_id`.
25
+ */
26
+ export declare function resolveSender(hit: {
27
+ from: string;
28
+ fromUserId: string;
29
+ }, { usersSet, members, myXmppUsername }: Sources): {
30
+ name: string;
31
+ isSelf: boolean;
32
+ };
33
+ export {};
@@ -0,0 +1,34 @@
1
+ export interface SearchFilters {
2
+ /** Sender's user id (a room member's `_id`). */
3
+ fromUserId?: string;
4
+ /** ISO 8601 instant: start of the chosen day, in the reader's time zone. */
5
+ since?: string;
6
+ /** ISO 8601 instant: end of the chosen day, in the reader's time zone. */
7
+ until?: string;
8
+ }
9
+ /** `2026-07-14` -> that day's first instant in the reader's time zone. */
10
+ export declare function dayStartISO(day: string): string | undefined;
11
+ /**
12
+ * The day's LAST instant, not just the date. The server reads a bare date in
13
+ * `until` as midnight at the START of that day, so passing "2026-07-14"
14
+ * silently excluded the whole day the reader picked (measured on QA: 329
15
+ * hits with the bare date against 340 with the end of the day).
16
+ */
17
+ export declare function dayEndISO(day: string): string | undefined;
18
+ /** True when both days are set and the range runs backwards. */
19
+ export declare const isBackwardsRange: (from: string, to: string) => boolean;
20
+ export interface Person {
21
+ _id?: string;
22
+ firstName?: string;
23
+ lastName?: string;
24
+ name?: string;
25
+ }
26
+ /**
27
+ * Up to `limit` people whose name contains `needle`, best matches first
28
+ * (a name that STARTS with it ranks above one that merely contains it).
29
+ * Rooms here have thousands of members, so this never renders them all.
30
+ */
31
+ export declare function matchPeople(people: Iterable<Person>, needle: string, limit?: number): {
32
+ id: string;
33
+ name: string;
34
+ }[];
@@ -0,0 +1,14 @@
1
+ export interface SnippetPart {
2
+ text: string;
3
+ match: boolean;
4
+ }
5
+ /**
6
+ * The part of a message worth showing for a hit: a window around the first
7
+ * match, cut on word boundaries with ellipses, split into matching and
8
+ * non-matching runs so the caller can emphasise the match.
9
+ *
10
+ * Plain substring, case-insensitive, like the server's default mode. The
11
+ * query is never used as a regular expression, so `.` or `(` in it are just
12
+ * characters.
13
+ */
14
+ export declare function buildSnippet(body: string, query: string): SnippetPart[];
@@ -0,0 +1,22 @@
1
+ import { SearchFilters } from './searchFilters';
2
+ import { MessageSearchHit } from '../../../networking/api-requests/messageSearch.api';
3
+ export declare const MIN_QUERY_LENGTH = 2;
4
+ export type SearchScope = 'chat' | 'all';
5
+ /**
6
+ * Debounced, cancellable message search with paging.
7
+ *
8
+ * A newer query (or scope) aborts the request in flight, and a response that
9
+ * arrives after it was superseded is dropped, so a slow answer to "he" can
10
+ * never overwrite the results for "hello".
11
+ */
12
+ export declare function useMessageSearch(query: string, scope: SearchScope, roomName?: string, filters?: SearchFilters): {
13
+ searchable: boolean;
14
+ hasMore: boolean;
15
+ loadMore: () => void;
16
+ retry: () => any;
17
+ status: "idle" | "loading" | "loadingMore" | "done" | "error";
18
+ items: MessageSearchHit[];
19
+ total: number;
20
+ /** Server rows consumed so far: the offset of the next page. */
21
+ nextOffset: number;
22
+ };
@@ -0,0 +1,13 @@
1
+ import { default as React } from 'react';
2
+ interface PublicChatsModalProps {
3
+ handleCloseModal: () => void;
4
+ }
5
+ /**
6
+ * A directory of the app's public chats (GET /v1/chats/public), so finding a
7
+ * chat to join no longer needs someone to hand over a link or a QR code.
8
+ *
9
+ * Joining reuses what a shared link already does: selecting a room you are
10
+ * not in makes the room initialiser join it and refresh the room list.
11
+ */
12
+ declare const PublicChatsModal: React.FC<PublicChatsModalProps>;
13
+ export default PublicChatsModal;