@ethora/chat-component 26.7.5 → 26.7.7

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 (174) hide show
  1. package/README.md +72 -16
  2. package/dist/ChatProfileModal-DehQM4Bo.js +2135 -0
  3. package/dist/FilePreviewModal-BcZLtVAy.js +743 -0
  4. package/dist/ManageDataModal-DWabKhSC.js +178 -0
  5. package/dist/MarkdownBody-CSY3YKDh.js +16874 -0
  6. package/dist/SideDrawer-DewJ-iPn.js +270 -0
  7. package/dist/UserProfileModal-BHZ0TULc.js +574 -0
  8. package/dist/UserSettingsModal-BxwVWNKo.js +50 -0
  9. package/dist/{VideoCallOverlay-C4M2I9oV.js → VideoCallOverlay-D5SuimGP.js} +9 -10
  10. package/dist/VisibilityModal-Bd67b4EM.js +132 -0
  11. package/dist/assets/icons.d.ts +30 -0
  12. package/dist/components/ContextMenu/ContextMenuComponents.d.ts +1 -3
  13. package/dist/components/EmojiPicker/LazyEmojiPicker.d.ts +3 -0
  14. package/dist/components/ExampleComponents/CustomMessage.d.ts +3 -3
  15. package/dist/components/Files/FilesList.d.ts +12 -0
  16. package/dist/components/Files/FilesPanel.d.ts +3 -0
  17. package/dist/components/Files/fileCategory.d.ts +13 -0
  18. package/dist/components/InputComponents/AttachmentPreview.d.ts +17 -0
  19. package/dist/components/InputComponents/MentionDropdown.d.ts +13 -0
  20. package/dist/components/InputComponents/MentionPickerModal.d.ts +19 -0
  21. package/dist/components/MainComponents/AttachmentList.d.ts +7 -0
  22. package/dist/components/MainComponents/ChatErrorBoundary.d.ts +53 -0
  23. package/dist/components/MainComponents/ChatErrorScreen.d.ts +24 -0
  24. package/dist/components/MainComponents/ChatRoomOpeningPreview.d.ts +24 -0
  25. package/dist/components/MainComponents/ChatWrapper.d.ts +1 -0
  26. package/dist/components/MainComponents/ChooseChatMessage.d.ts +11 -1
  27. package/dist/components/MainComponents/HeaderLogo.d.ts +18 -0
  28. package/dist/components/MainComponents/MediaMessage.d.ts +5 -0
  29. package/dist/components/MainComponents/MessageContainer.d.ts +2 -2
  30. package/dist/components/MainComponents/ReduxWrapper.d.ts +1 -2
  31. package/dist/components/MainComponents/SystemMessage.d.ts +1 -2
  32. package/dist/components/MenuRoom/MenuRoom.d.ts +1 -0
  33. package/dist/components/MessageBubble/CustomDivider.d.ts +3 -3
  34. package/dist/components/MessageBubble/Message.d.ts +2 -2
  35. package/dist/components/MessageBubble/MessageTranslate.d.ts +28 -10
  36. package/dist/components/MessageBubble/QuickReplies.d.ts +6 -5
  37. package/dist/components/Modals/ChatProfileModal/ChatProfileMemberRow.d.ts +18 -0
  38. package/dist/components/Modals/FilePreviewModal/FilePreviewModal.d.ts +17 -0
  39. package/dist/components/Modals/FilePreviewModal/ImageLightbox.d.ts +35 -0
  40. package/dist/components/Modals/FilePreviewModal/PdfView.d.ts +14 -3
  41. package/dist/components/Modals/Modal/Modal.d.ts +10 -0
  42. package/dist/components/Modals/Modal/ModalContent.d.ts +21 -0
  43. package/dist/components/Modals/ModalBox/ModalBox.d.ts +9 -0
  44. package/dist/components/Modals/ModalReportChat/ModalReportChat.d.ts +6 -1
  45. package/dist/components/Modals/ModalWrapper/ModalWrapper.d.ts +8 -0
  46. package/dist/components/Modals/NewChatModal/styledNewChatComponents.d.ts +12 -0
  47. package/dist/components/Modals/SettingsModals/SharedStyledComponents.d.ts +12 -0
  48. package/dist/components/Modals/SettingsModals/Visibility/StyledComponents.d.ts +1 -1
  49. package/dist/components/Modals/SideDrawer/DrawerProfileParts.d.ts +42 -0
  50. package/dist/components/Modals/SideDrawer/SideDrawer.d.ts +64 -0
  51. package/dist/components/Modals/SidePanel/SidePanel.d.ts +31 -0
  52. package/dist/components/Modals/UserProfileModal/EditUserModal.d.ts +5 -1
  53. package/dist/components/Modals/modalComponents.d.ts +6 -0
  54. package/dist/components/Modals/motionVariants.d.ts +37 -0
  55. package/dist/components/Modals/styledModalComponents.d.ts +13 -3
  56. package/dist/components/RoomComponents/ChatRoomItem.d.ts +0 -1
  57. package/dist/components/RoomComponents/OnlineUsersPopover.d.ts +0 -1
  58. package/dist/components/UsersList/StyledComponents.d.ts +5 -1
  59. package/dist/components/UsersList/UsersList.d.ts +23 -0
  60. package/dist/components/styled/Button.d.ts +11 -1
  61. package/dist/components/styled/ChatWrapperBox.d.ts +3 -1
  62. package/dist/components/styled/PdfMessage.d.ts +13 -0
  63. package/dist/components/styled/RoomListComponents/index.d.ts +22 -1
  64. package/dist/components/styled/SendInput.d.ts +19 -5
  65. package/dist/components/styled/StyledComponents.d.ts +1 -1
  66. package/dist/components/styled/StyledInputComponents/MediaComponents.d.ts +18 -0
  67. package/dist/components/styled/StyledInputComponents/StyledInputComponents.d.ts +21 -0
  68. package/dist/components/styled/TreadLabel.d.ts +2 -2
  69. package/dist/components/styled/UnsupportedType.d.ts +1 -1
  70. package/dist/context/ModalTransitionContext.d.ts +2 -0
  71. package/dist/examples/quiz/QuizDemo.d.ts +3 -0
  72. package/dist/examples/quiz/quizScript.d.ts +38 -0
  73. package/dist/examples/quiz/useDemoSession.d.ts +42 -0
  74. package/dist/examples/quiz/useQuizFlow.d.ts +47 -0
  75. package/dist/firebase-config.d.ts +2 -4
  76. package/dist/handleCopyClick-BoZWTBLC.js +108 -0
  77. package/dist/helpers/MarkdownBody.d.ts +7 -0
  78. package/dist/helpers/attachments.d.ts +33 -0
  79. package/dist/helpers/botMarkup.d.ts +30 -0
  80. package/dist/helpers/buildChatShareLink.d.ts +20 -0
  81. package/dist/helpers/cacheScope.d.ts +15 -1
  82. package/dist/helpers/constants/MODAL_TYPES.d.ts +13 -7
  83. package/dist/helpers/createRoomFromApi.d.ts +1 -1
  84. package/dist/helpers/ethoraLogger.d.ts +8 -0
  85. package/dist/helpers/fileKind.d.ts +17 -0
  86. package/dist/helpers/formatNumberWithCommas.d.ts +1 -1
  87. package/dist/helpers/hiddenRooms.d.ts +1 -1
  88. package/dist/helpers/historyPreloadScheduler.d.ts +1 -0
  89. package/dist/helpers/lazyEmoji.d.ts +3 -0
  90. package/dist/helpers/localStorageUser.d.ts +1 -1
  91. package/dist/helpers/markdownSanitizeSchema.d.ts +3 -0
  92. package/dist/helpers/mentions.d.ts +63 -0
  93. package/dist/helpers/parseMessageBody.d.ts +12 -2
  94. package/dist/helpers/pdf/loadPdfjs.d.ts +64 -0
  95. package/dist/helpers/remarkLineBreaks.d.ts +42 -0
  96. package/dist/helpers/resolveMessageTranslation.d.ts +34 -0
  97. package/dist/helpers/roomImageGallery.d.ts +53 -0
  98. package/dist/helpers/secureFileUrl.d.ts +8 -0
  99. package/dist/helpers/sendFailureWatchdog.d.ts +62 -0
  100. package/dist/helpers/transformTranslatations.d.ts +1 -2
  101. package/dist/hooks/pushSubscriptionSweep.d.ts +41 -0
  102. package/dist/hooks/useChatOpenPhase.d.ts +21 -0
  103. package/dist/hooks/useChatWrapperInit.d.ts +1 -0
  104. package/dist/hooks/useDelayedAction.d.ts +22 -0
  105. package/dist/hooks/useExclusiveRightPane.d.ts +25 -0
  106. package/dist/hooks/useExitTransition.d.ts +24 -0
  107. package/dist/hooks/useFilesEndpointSupport.d.ts +9 -0
  108. package/dist/hooks/useGetNewArchRoom.d.ts +3 -1
  109. package/dist/hooks/useInView.d.ts +17 -0
  110. package/dist/hooks/useMentionComposer.d.ts +45 -0
  111. package/dist/hooks/useMessageHeapState.d.ts +1 -1
  112. package/dist/hooks/useMessageTranslation.d.ts +4 -3
  113. package/dist/hooks/useModalDismiss.d.ts +27 -0
  114. package/dist/hooks/useMyFiles.d.ts +16 -0
  115. package/dist/hooks/usePdfThumbnail.d.ts +22 -0
  116. package/dist/hooks/useQRCodeChatHandler.d.ts +29 -1
  117. package/dist/hooks/useRoomMute.d.ts +19 -0
  118. package/dist/hooks/useRoomState.d.ts +1 -1
  119. package/dist/hooks/useSendMessage.d.ts +4 -3
  120. package/dist/hooks/useTypography.d.ts +1 -1
  121. package/dist/hooks/useUnreadMessagesCounter.d.ts +11 -2
  122. package/dist/index.esm-B-3fyDHY.js +28 -0
  123. package/dist/index.esm-CRM4VCRa.js +943 -0
  124. package/dist/index.esm-msbp26WN.js +3352 -0
  125. package/dist/index.esm2017-CL0mvq6y.js +1578 -0
  126. package/dist/main-C2FKeV6y.js +32375 -0
  127. package/dist/main.css +1 -1
  128. package/dist/main.d.ts +14 -0
  129. package/dist/main.js +18 -18
  130. package/dist/module-DGXkb0R_.js +2960 -0
  131. package/dist/native-CDi4YD_s.js +13 -0
  132. package/dist/networking/api-requests/files.api.d.ts +19 -0
  133. package/dist/networking/api-requests/rooms.api.d.ts +10 -2
  134. package/dist/networking/api-requests/translate.api.d.ts +15 -0
  135. package/dist/networking/api-requests/user.api.d.ts +2 -0
  136. package/dist/networking/sharedRequest.d.ts +4 -0
  137. package/dist/networking/xmpp/allRoomPresences.xmpp.d.ts +3 -1
  138. package/dist/networking/xmpp/createTimeoutPromise.xmpp.d.ts +14 -0
  139. package/dist/networking/xmpp/sendTextMessage.xmpp.d.ts +2 -1
  140. package/dist/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.d.ts +2 -1
  141. package/dist/networking/xmppClient.d.ts +12 -3
  142. package/dist/pdf-B5b7qHu6.js +17130 -0
  143. package/dist/pdf.worker.min-COYn-gId.js +24385 -0
  144. package/dist/roomStore/index.d.ts +20 -0
  145. package/dist/roomStore/persistEncryption.d.ts +51 -0
  146. package/dist/roomStore/roomsSlice.d.ts +49 -6
  147. package/dist/styles/classNames.d.ts +35 -0
  148. package/dist/styles/motion.d.ts +71 -0
  149. package/dist/styles/tokens.d.ts +27 -0
  150. package/dist/testIds.d.ts +1 -1
  151. package/dist/types/models/attachment.model.d.ts +33 -0
  152. package/dist/types/models/config.model.d.ts +160 -19
  153. package/dist/types/models/file.model.d.ts +20 -0
  154. package/dist/types/models/message.model.d.ts +15 -6
  155. package/dist/types/models/room.model.d.ts +40 -2
  156. package/dist/types/types.d.ts +3 -1
  157. package/dist/utils/resendMessage.d.ts +22 -1
  158. package/dist/utils/translateModePolicy.d.ts +1 -0
  159. package/dist/wavesurfer.esm-D_a19qBW.js +952 -0
  160. package/package.json +22 -2
  161. package/dist/VideoCallOverlay-C4M2I9oV.js.map +0 -1
  162. package/dist/components/MainComponents/VirtualList.d.ts +0 -10
  163. package/dist/components/Modals/SettingsModals/BlockedUsers/BlockedUsersModal.d.ts +0 -6
  164. package/dist/components/Modals/SettingsModals/DocumentShares/DocumentSharesModal.d.ts +0 -6
  165. package/dist/components/Modals/SettingsModals/ManageDataModal/StyledComponents.d.ts +0 -0
  166. package/dist/components/Modals/SettingsModals/ProfileShares/ProfileShares.d.ts +0 -6
  167. package/dist/components/Modals/SettingsModals/Referrals/Referrals.d.ts +0 -6
  168. package/dist/hooks/useMessageQueue.d.ts +0 -4
  169. package/dist/main-DeiuFYsl.js +0 -55812
  170. package/dist/main-DeiuFYsl.js.map +0 -1
  171. package/dist/main.js.map +0 -1
  172. package/dist/networking/xmpp/getHistory.xmpp.d.ts +0 -3
  173. package/dist/networking/xmpp/listenerManager.d.ts +0 -17
  174. package/dist/utils/xmppMemoryMonitor.d.ts +0 -16
@@ -0,0 +1,63 @@
1
+ import { IMentionSpan, RoomMember } from '../types/types';
2
+ export interface MentionCandidate {
3
+ jid: string;
4
+ name: string;
5
+ }
6
+ export declare const memberJid: (member: RoomMember) => string;
7
+ export declare const memberFullName: (member: RoomMember) => string;
8
+ /**
9
+ * Filters + ranks a room's members against a typed `@query` for the mention
10
+ * autocomplete dropdown. Prefix matches outrank mid-string substring
11
+ * matches; ties break alphabetically. The current user is always excluded
12
+ * (no self-mention suggestion).
13
+ */
14
+ export declare const rankMentionCandidates: (query: string, members: RoomMember[], selfId?: string) => MentionCandidate[];
15
+ /**
16
+ * Detects an in-progress "@query" the caret is currently inside, so the
17
+ * autocomplete dropdown knows whether to be open and what to filter by.
18
+ * Only triggers when the `@` starts a "word" (start of input, or preceded
19
+ * by whitespace) so it doesn't fire inside an email-looking string or an
20
+ * existing word.
21
+ */
22
+ export declare const findActiveMentionQuery: (text: string, caret: number) => {
23
+ start: number;
24
+ query: string;
25
+ } | null;
26
+ /**
27
+ * Re-anchors mention spans after a text edit. `editStart`/`editEnd` are the
28
+ * [start, end) range of the OLD text that was replaced, `insertedLength` is
29
+ * how long the replacement text is. Spans entirely before the edit are left
30
+ * alone; spans entirely after are shifted by the net length delta; a span
31
+ * that overlaps the edited range is dropped (its underlying text is no
32
+ * longer intact, so it's no longer a valid mention).
33
+ */
34
+ export declare const shiftMentionSpans: (spans: IMentionSpan[], editStart: number, editEnd: number, insertedLength: number) => IMentionSpan[];
35
+ /**
36
+ * Drops any span whose text no longer reads "@Name" in the current body
37
+ * (e.g. a multi-character selection delete clipped part of it). Safety net
38
+ * on top of shiftMentionSpans for edits that aren't tracked precisely.
39
+ */
40
+ export declare const validateMentionSpans: (text: string, spans: IMentionSpan[]) => IMentionSpan[];
41
+ /** Finds a mention span whose token ends exactly at `caret` (i.e. the caret
42
+ * sits right after a complete "@Name" token) - used to make Backspace
43
+ * delete the whole token atomically instead of one character. */
44
+ export declare const findMentionEndingAt: (spans: IMentionSpan[], caret: number) => IMentionSpan | undefined;
45
+ /**
46
+ * Diffs two versions of the composer text (before/after a single onChange)
47
+ * down to the edited range, via longest common prefix/suffix. Good enough
48
+ * for the single-edit-at-a-time case a controlled <input>/<textarea>
49
+ * onChange always is (typing, pasting, cutting, IME commit): everything
50
+ * outside the diffed range is provably unchanged, which is what
51
+ * shiftMentionSpans needs to re-anchor mention offsets correctly.
52
+ */
53
+ export declare const computeEditRange: (oldText: string, newText: string) => {
54
+ start: number;
55
+ end: number;
56
+ insertedLength: number;
57
+ };
58
+ /**
59
+ * Splices mention spans into `text` as raw `<mention>` tags for the
60
+ * markdown/rehype-raw pipeline to pick up (see parseMessageBody.tsx). Spans
61
+ * are applied back-to-front so earlier offsets stay valid while splicing.
62
+ */
63
+ export declare const spliceMentionMarkup: (text: string, spans: IMentionSpan[] | undefined) => string;
@@ -1,4 +1,14 @@
1
- export declare const parseMessageBody: ({ text }: {
1
+ import { IMentionSpan } from '../types/types';
2
+ export interface MentionClickHandler {
3
+ (mention: {
4
+ jid: string;
5
+ name: string;
6
+ }): void;
7
+ }
8
+ interface ParseMessageBodyArgs {
2
9
  text: string;
3
- }) => import("react/jsx-runtime").JSX.Element;
10
+ mentions?: IMentionSpan[];
11
+ onMentionClick?: MentionClickHandler;
12
+ }
13
+ export declare const parseMessageBody: ({ text, mentions, onMentionClick, }: ParseMessageBodyArgs) => import("react/jsx-runtime").JSX.Element;
4
14
  export default parseMessageBody;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Lazy, failure-tolerant access to pdf.js.
3
+ *
4
+ * Two things drive the shape of this module:
5
+ *
6
+ * 1. pdf.js is ~1.5 MB. It must never land in the chunk that a host pays for
7
+ * on first paint, so it is only ever reached through `import()` from
8
+ * here, and this module is itself only imported dynamically.
9
+ *
10
+ * 2. Wiring `GlobalWorkerOptions.workerSrc` from inside a library is a
11
+ * portability trap: the URL has to survive our Vite lib build AND the
12
+ * host's bundler (Vite, webpack, Next, CRA), and every published recipe
13
+ * for it is bundler-specific. So by default we do not use a worker file
14
+ * at all - importing the worker module and parking it on
15
+ * `globalThis.pdfjsWorker` makes pdf.js run its message handler on the
16
+ * main thread (see PDFWorker's `#mainThreadWorkerMessageHandler`). For a
17
+ * one-page thumbnail that is a few milliseconds of main-thread work, and
18
+ * it is bundler-agnostic. Hosts that render large PDFs can hand us a real
19
+ * `workerSrc` through `config.pdfPreview.workerSrc` and get the worker
20
+ * path back.
21
+ *
22
+ * Any failure (module missing, CSP blocking eval, unsupported browser)
23
+ * resolves to `null`; callers fall back to the static document card.
24
+ */
25
+ export interface PdfRenderTask {
26
+ promise: Promise<void>;
27
+ cancel: () => void;
28
+ }
29
+ export interface PdfViewport {
30
+ width: number;
31
+ height: number;
32
+ }
33
+ export interface PdfPageProxy {
34
+ getViewport: (params: {
35
+ scale: number;
36
+ }) => PdfViewport;
37
+ render: (params: {
38
+ canvasContext: CanvasRenderingContext2D;
39
+ viewport: PdfViewport;
40
+ }) => PdfRenderTask;
41
+ cleanup: () => void;
42
+ }
43
+ export interface PdfDocumentProxy {
44
+ numPages: number;
45
+ getPage: (pageNumber: number) => Promise<PdfPageProxy>;
46
+ destroy: () => Promise<void>;
47
+ }
48
+ export interface PdfDocumentLoadingTask {
49
+ promise: Promise<PdfDocumentProxy>;
50
+ destroy: () => Promise<void>;
51
+ }
52
+ export interface PdfjsLib {
53
+ getDocument: (params: {
54
+ url: string;
55
+ withCredentials?: boolean;
56
+ isEvalSupported?: boolean;
57
+ }) => PdfDocumentLoadingTask;
58
+ GlobalWorkerOptions: {
59
+ workerSrc: string;
60
+ };
61
+ }
62
+ /** Test seam - the module-level cache would otherwise leak between cases. */
63
+ export declare const resetPdfjsCache: () => void;
64
+ export declare const loadPdfjs: (workerSrc?: string) => Promise<PdfjsLib | null>;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * A tiny local stand-in for `remark-breaks`.
3
+ *
4
+ * In CommonMark a single "\n" inside a paragraph is a "soft break": it is
5
+ * kept as a literal newline character inside the surrounding mdast `text`
6
+ * node, and every renderer (ours included) turns that into a plain space.
7
+ * That is what made a message typed with real line breaks collapse into
8
+ * one line once it went through react-markdown.
9
+ *
10
+ * `remark-breaks` fixes this by rewriting each soft break into a real
11
+ * mdast `break` node (which renders as `<br />`). We do the same thing by
12
+ * hand instead of adding that package (or `unist-util-visit`) as a new
13
+ * dependency: this is a published SDK, and the tree walk below is a dozen
14
+ * lines.
15
+ *
16
+ * Only `text` nodes carry this problem. Fenced/indented code, inline code
17
+ * spans and raw HTML passthrough all keep their literal newlines as part
18
+ * of their own syntax (a `code`/`inlineCode` node's content lives in
19
+ * `value`, not in child `text` nodes, and an `html` node is a leaf too),
20
+ * so the walk below never touches them - it only ever rewrites `text`
21
+ * children, and only descends into nodes that actually have children.
22
+ *
23
+ * Existing hard breaks (two trailing spaces, or a trailing backslash,
24
+ * before a newline) are already parsed into their own `break` node by the
25
+ * time a remark plugin sees the tree - the newline itself is consumed by
26
+ * that syntax and never ends up inside a `text` node's value. So there is
27
+ * nothing left for this plugin to find there, and it cannot double them.
28
+ */
29
+ export interface MarkdownAstNode {
30
+ type: string;
31
+ value?: string;
32
+ children?: MarkdownAstNode[];
33
+ [key: string]: unknown;
34
+ }
35
+ /**
36
+ * Remark plugin: turns every soft line break in the document into a real
37
+ * `break` node. Usage mirrors any other remark plugin:
38
+ *
39
+ * remarkPlugins={[remarkGfm, remarkLineBreaks]}
40
+ */
41
+ export declare const remarkLineBreaks: () => (tree: MarkdownAstNode) => void;
42
+ export default remarkLineBreaks;
@@ -0,0 +1,34 @@
1
+ import { IMessage } from '../types/types';
2
+ export interface ResolvedMessageTranslation {
3
+ /**
4
+ * Whether this message would need translating for this reader at all: a
5
+ * known source language whose base differs from the reader's, and actual
6
+ * translatable text. False means there is nothing to reveal, no matter
7
+ * what a host `onTranslate` or a translate service might otherwise find -
8
+ * a bare number, an emoji-only reaction, an untagged message, or a
9
+ * message already in the reader's language.
10
+ */
11
+ needsTranslation: boolean;
12
+ /**
13
+ * True when a real, non-identical translation is already attached to the
14
+ * message (`message.translations`) for this reader - safe to render with
15
+ * no network call at all.
16
+ */
17
+ hasTranslation: boolean;
18
+ originalText: string;
19
+ /** hasTranslation ? the translation : originalText - always safe to render directly. */
20
+ displayText: string;
21
+ }
22
+ /**
23
+ * Resolve "what translation do we have for this message, for this reader" -
24
+ * pure, synchronous, no HTTP. This is the ONE place that reads
25
+ * `message.translations` and decides whether what it finds counts as a real
26
+ * translation.
27
+ *
28
+ * Auto mode (`useMessageTranslation`) and manual mode (`MessageTranslate`)
29
+ * both call this instead of keeping their own copies of the lookup, so they
30
+ * can never drift into showing different things for the same message: manual
31
+ * mode's click-to-reveal shows exactly what auto mode would have rendered
32
+ * automatically, because both ask this same function the same question.
33
+ */
34
+ export declare const resolveMessageTranslation: (message: Pick<IMessage, "body" | "langSource" | "translations">, readerLocale?: string) => ResolvedMessageTranslation;
@@ -0,0 +1,53 @@
1
+ import { IMessage, ModalFile } from '../types/types';
2
+ /**
3
+ * One navigable entry in the media viewer's gallery.
4
+ *
5
+ * Shaped as a `ModalFile` (plus a render key) on purpose: navigating is just
6
+ * `dispatch(setActiveFile(entry))`, so every downstream consumer - the
7
+ * download action, the secure `?ft=` token treatment, the alt text - keeps
8
+ * reading exactly the fields it read when a single file was opened by hand.
9
+ */
10
+ export interface GalleryImage extends ModalFile {
11
+ /** Stable per message+slot, so two identical URLs are still two entries. */
12
+ key: string;
13
+ }
14
+ /**
15
+ * The gallery the lightbox pages through.
16
+ *
17
+ * Scope, deliberately narrow and honest: **image attachments carried by the
18
+ * messages currently in the store for this room, in chronological order**.
19
+ * That is not "every image ever posted here":
20
+ *
21
+ * - History is paged from MAM. Only what has been loaded so far is here, so
22
+ * the gallery grows as the user (or the background preloader) pulls older
23
+ * history in. The counter re-renders with it.
24
+ * - MessageList's render window (120 initial / 60 per step) does NOT bound
25
+ * this: the window controls how many messages are *mounted*, while the
26
+ * store keeps every loaded message. So the gallery routinely contains
27
+ * images that are not currently in the DOM, which is what makes paging
28
+ * past the top of the viewport work at all.
29
+ * - `useMyFiles` (/v2/files) was the other candidate source and is the wrong
30
+ * one: that endpoint lists the *caller's own* uploads with no server-side
31
+ * room filter, so it would show the viewer their own files from other
32
+ * rooms while hiding every image anybody else posted in this one.
33
+ *
34
+ * Non-images (video, PDF, audio, arbitrary files) are excluded, so opening a
35
+ * PDF simply produces an empty gallery and no navigation chrome.
36
+ */
37
+ export declare const collectRoomImages: (messages?: Partial<IMessage>[] | null) => GalleryImage[];
38
+ /**
39
+ * Position of the open file inside the gallery, or -1 when it is not part of
40
+ * it (a PDF, a video, a file opened from the Files panel of another room).
41
+ *
42
+ * Matching is by URL with the secure-file `?ft=` token stripped from both
43
+ * sides: it is the one field every entry point fills in, but the URL that
44
+ * reached the store when the bubble was clicked can already carry a token
45
+ * (they are minted per viewer and expire in about an hour) while the copy
46
+ * read back from the message does not. Comparing the raw strings therefore
47
+ * failed for exactly the secure rooms this feature has to work in.
48
+ *
49
+ * Two posts of the same URL collapse onto the first: a duplicate image is
50
+ * the same picture, so paging to it twice would only look like the arrows
51
+ * were stuck.
52
+ */
53
+ export declare const findGalleryIndex: (images: GalleryImage[], activeFile?: ModalFile | null) => number;
@@ -1,4 +1,12 @@
1
1
  export declare const isSecureFileUrl: (url?: string | null) => boolean;
2
2
  export declare const appendFileToken: (url: string | null | undefined, fileToken: string | null | undefined) => string;
3
+ /**
4
+ * The same URL with any `?ft=` token removed, for comparing two references
5
+ * to the same file. Tokens are per-viewer and short-lived, so the URL a
6
+ * message bubble handed the store can carry one while the copy read back
7
+ * from the message's attachments does not (or carries an older one). Only
8
+ * the token-free form is a stable identity.
9
+ */
10
+ export declare const withoutFileToken: (url?: string | null) => string;
3
11
  export declare const withFileToken: (url?: string | null) => string;
4
12
  export declare const requestFileTokenRecovery: () => Promise<boolean>;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * How long a send may stay unacknowledged before we show the sender a
3
+ * "not delivered" state with a retry control.
4
+ *
5
+ * Why 12s, and not something snappier:
6
+ *
7
+ * - The only acknowledgement this protocol gives us is the MUC reflecting
8
+ * our own message back (`addRoomMessage` clears `pending` on it). There
9
+ * is no delivery receipt, so "no echo yet" is the ONLY failure signal we
10
+ * have, and it is a weak one - it is equally consistent with a healthy
11
+ * but slow send.
12
+ * - A healthy echo lands in ~100-300ms. xmppClient's own SLA warning fires
13
+ * at a p95 click-to-echo of 1500ms (`sendSlaWarningThresholdMs`), i.e.
14
+ * the client already considers 1.5s the outer edge of normal. 12s is 8x
15
+ * that outer edge.
16
+ * - The catch-up probe (`scheduleAckCatchup`, 2500ms) re-asserts presence
17
+ * and pulls MAM when the echo is missing. That probe gets a full ~9.5s
18
+ * to reconcile the message before this watchdog is allowed to call it
19
+ * failed, so the common "our presence silently dropped" case is repaired
20
+ * without the user ever seeing a failure.
21
+ * - Bursts and long messages are covered by WHERE the timer is armed, not
22
+ * by its length: it starts when the optimistic bubble appears and is
23
+ * re-armed on retry, while the send queue is serialised. A message stuck
24
+ * behind a burst still has its own 12s window measured from the moment
25
+ * the user could first see it.
26
+ *
27
+ * Erring long is deliberate. React Native defect #31 (26.7.1) shipped the
28
+ * opposite trade: a trigger-happy timeout showed accepted messages as
29
+ * failed and the resulting retries delivered them twice. A late "failed" is
30
+ * a cosmetic annoyance; an early one produces duplicates for the recipient.
31
+ */
32
+ export declare const SEND_FAILURE_TIMEOUT_MS = 12000;
33
+ /**
34
+ * Sanity check, not a runtime knob: the watchdog must never be able to fire
35
+ * before the catch-up probe has had a chance to repair the send.
36
+ */
37
+ export declare const SEND_FAILURE_MIN_TIMEOUT_MS: number;
38
+ /** True while a manual retry for this message id is on the wire. */
39
+ export declare const isSendRetryInFlight: (messageId: string) => boolean;
40
+ /**
41
+ * Claims the retry slot for a message id. Returns false when another retry
42
+ * for the same id already holds it, which is what makes a double-tap (or a
43
+ * second component instance rendering the same message) send exactly once.
44
+ */
45
+ export declare const beginSendRetry: (messageId: string) => boolean;
46
+ export declare const endSendRetry: (messageId: string) => void;
47
+ export declare const clearSendFailureWatchdog: (messageId?: string) => void;
48
+ export declare const clearAllSendFailureWatchdogs: () => void;
49
+ /** Whether the store currently shows this message as failed-to-send. */
50
+ export declare const isMessageMarkedFailed: (roomJID: string, messageId: string) => boolean;
51
+ /**
52
+ * Arms the "this send was never acknowledged" timer for one message.
53
+ *
54
+ * Re-arming for the same id replaces the previous timer rather than adding
55
+ * a second one, so a retry cannot leave two watchdogs racing on one message.
56
+ */
57
+ export declare const armSendFailureWatchdog: (params: {
58
+ roomJID: string;
59
+ messageId: string;
60
+ body: string;
61
+ timeoutMs?: number;
62
+ }) => void;
@@ -1,4 +1,4 @@
1
- interface Translation {
1
+ export interface Translation {
2
2
  translatedText: string;
3
3
  language: string;
4
4
  languageName: string;
@@ -7,4 +7,3 @@ export interface TranslationObject {
7
7
  [language: string]: Translation;
8
8
  }
9
9
  export declare const transformArrayToObject: (array: Translation[]) => TranslationObject;
10
- export {};
@@ -0,0 +1,41 @@
1
+ /**
2
+ * pushSubscriptionSweep.ts
3
+ *
4
+ * Pure, hook-free worker pool for usePushNotifications' per-room push
5
+ * subscription sweep. Kept in its own module (no React, no Firebase, no
6
+ * Redux imports) so it can be unit tested without pulling in the heavy
7
+ * side effects those modules carry at import time.
8
+ */
9
+ export type PushSubscribeResult = {
10
+ ok: boolean;
11
+ reason?: string;
12
+ message?: string;
13
+ };
14
+ export type PushSweepGuards = {
15
+ getStatus: (jid: string) => string | undefined;
16
+ getRetryAt: (jid: string) => number | undefined;
17
+ isInFlight: (jid: string) => boolean;
18
+ markInFlight: (jid: string) => void;
19
+ clearInFlight: (jid: string) => void;
20
+ setStatus: (jid: string, status: string) => void;
21
+ subscribe: (jid: string) => Promise<PushSubscribeResult>;
22
+ onSubscribed?: (jid: string) => void;
23
+ onFailed?: (jid: string, message?: string) => void;
24
+ };
25
+ /** Number of rooms subscribed to push in parallel during a sweep. */
26
+ export declare const PUSH_SWEEP_CONCURRENCY = 4;
27
+ /**
28
+ * Sweeps `roomJIDs` for push subscription, previously a strictly serial
29
+ * for-loop with a fixed 100ms sleep between every room (so N rooms took at
30
+ * least N*100ms even though subscribing does not depend on room order).
31
+ * This runs a small worker pool instead, bounded by `concurrency`, while
32
+ * keeping every existing guard intact: status checks (subscribed / pending
33
+ * / blocked / error are skipped), the roomRetryAtRef backoff, and the
34
+ * roomSubscribeInFlightRef in-flight lock. The caller owns the
35
+ * roomProcessRunningRef / lastRoomsHashRef gates around the whole sweep.
36
+ *
37
+ * `roomJIDs` are processed by a fixed-size pool of workers pulling from a
38
+ * shared index, so at most `concurrency` subscribe calls are in flight at
39
+ * once regardless of how many rooms need syncing.
40
+ */
41
+ export declare const sweepPushSubscriptions: (roomJIDs: string[], guards: PushSweepGuards, concurrency?: number) => Promise<void>;
@@ -0,0 +1,21 @@
1
+ export type ChatOpenPhase = 'opening' | 'settled';
2
+ /**
3
+ * Collapses "is this room's history still loading" into one phase that only
4
+ * ever moves forward, once, for a given room key: 'opening' -> 'settled'.
5
+ * It never reports 'opening' again for the same key once it has settled -
6
+ * ChatRoom relies on that to stop the Loader/placeholder pane from
7
+ * oscillating while a room opens (see the flicker this hook exists to
8
+ * fix). A settled-but-empty room can still legitimately start rendering
9
+ * messages later (a live message arrives) - that is not this hook's
10
+ * concern; callers derive that from their own `hasMessages`, this hook only
11
+ * owns whether the opening UI is still allowed to show.
12
+ *
13
+ * `roomKey` resets the latch: pass the active room's JID (or null). The
14
+ * reset happens synchronously during render (React's "adjust state while
15
+ * rendering" pattern), so switching rooms - even quickly, back and forth -
16
+ * never paints a frame of the previous room's settled state under the new
17
+ * room's key, and never leaves a stale debounce timer running against the
18
+ * new key (the effect below is keyed on `roomKey` too, so its cleanup
19
+ * clears the old timer before the new one can be scheduled).
20
+ */
21
+ export declare const useChatOpenPhase: (roomKey: string | null, isLoadingSignal: boolean) => ChatOpenPhase;
@@ -17,5 +17,6 @@ interface useChatWrapperInitResult {
17
17
  }
18
18
  export declare const resolveLegacyTranslatesLangSource: (translatesConfig?: IConfig["translates"]) => Iso639_1Codes | undefined;
19
19
  export declare const resolveExternalReaderLocaleLangSource: (readerLocale?: string) => Iso639_1Codes | undefined;
20
+ export declare const isStatusConnectionLost: (status?: string | null) => boolean;
20
21
  declare const useChatWrapperInit: ({ roomJID, wasAutoSelected, config, }: useChatWrapperInitProps) => useChatWrapperInitResult;
21
22
  export default useChatWrapperInit;
@@ -0,0 +1,22 @@
1
+ export interface DelayedActionState {
2
+ /** Call instead of the real close/dismiss handler. */
3
+ requestAction: () => void;
4
+ /** True from the moment `requestAction` is called until `action` actually
5
+ * runs - apply the exit animation while this is true. */
6
+ isPending: boolean;
7
+ }
8
+ /**
9
+ * For a component that closes ITSELF (a modal that owns `isOpen` locally,
10
+ * or one that only gets an `onClose` callback with no boolean to watch):
11
+ * wraps `action` so calling `requestAction()` plays out the exit animation
12
+ * first and only invokes the real `action` once it has finished, instead of
13
+ * unmounting on the same tick the user clicked close.
14
+ *
15
+ * This works because the component stays mounted for exactly as long as its
16
+ * parent's render condition stays true, and that condition usually depends
17
+ * on the very state `action` is about to change - delaying the call delays
18
+ * the unmount for free, no separate "still rendering while closing" flag
19
+ * needed at the parent. See `useExitTransition` for the opposite case: the
20
+ * parent owns `isOpen` and the child has no closing callback to intercept.
21
+ */
22
+ export declare function useDelayedAction(action: () => void, durationMs: number): DelayedActionState;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Keeps the chat to at most three columns.
3
+ *
4
+ * Two different things want to be the right-hand column: the thread view
5
+ * (`Thread/ThreadWrapper`) and the profile/settings panel
6
+ * (`Modals/SidePanel`). Letting both take space at once gives four columns
7
+ * and squeezes the conversation into nothing, so the rule is the one most
8
+ * products use: THE ONE OPENED LAST WINS, and the other closes. Opening a
9
+ * profile while reading a thread puts the profile there and drops back to the
10
+ * room's messages; opening a thread from a message while a profile is open
11
+ * closes the profile. Either way, dismissing the survivor leaves the plain
12
+ * two-column chat, never a half-state.
13
+ *
14
+ * The tie-break for "both already open on the first render" (a remount with
15
+ * persisted state, not something a click can produce) is to close the panel:
16
+ * the thread is the reading context the user was in, the panel is chrome.
17
+ */
18
+ interface UseExclusiveRightPaneOptions {
19
+ threadOpen: boolean;
20
+ panelOpen: boolean;
21
+ closeThread: () => void;
22
+ closePanel: () => void;
23
+ }
24
+ export declare const useExclusiveRightPane: ({ threadOpen, panelOpen, closeThread, closePanel, }: UseExclusiveRightPaneOptions) => void;
25
+ export default useExclusiveRightPane;
@@ -0,0 +1,24 @@
1
+ export interface ExitTransitionState {
2
+ /** Whether the caller should still render the element at all. */
3
+ shouldRender: boolean;
4
+ /** True for the window between `isOpen` going false and the element
5
+ * actually leaving the DOM - apply the exit animation while this is true. */
6
+ isExiting: boolean;
7
+ }
8
+ /**
9
+ * Keeps an element mounted for `durationMs` after `isOpen` flips to false, so
10
+ * a CSS exit animation has time to play instead of the element vanishing on
11
+ * the same frame the state changes.
12
+ *
13
+ * This is the "someone else decides when I close" half of the presence
14
+ * story - the caller owns `isOpen` (Redux state, a prop, ...) and this hook
15
+ * just tells it how long to keep rendering. For a component that closes
16
+ * itself (a modal with its own local open flag, reacting to its own close
17
+ * button), prefer `useDelayedAction` instead: it lets the component play the
18
+ * animation and only THEN flip its own state, which is simpler when there is
19
+ * no external boolean to watch.
20
+ *
21
+ * Respects `prefers-reduced-motion`: when set, the element is removed on the
22
+ * same tick `isOpen` goes false, same as if `durationMs` were 0.
23
+ */
24
+ export declare function useExitTransition(isOpen: boolean, durationMs: number): ExitTransitionState;
@@ -0,0 +1,9 @@
1
+ import { FilesEndpointSupport } from '../networking/api-requests/files.api';
2
+ /**
3
+ * Whether the current user's backend actually has /v2/files. Starts
4
+ * 'unknown' and flips to 'supported'/'unsupported' the first time a real
5
+ * files list request resolves (see files.api.ts) - there's no separate probe
6
+ * request, this just observes the outcome of whatever the Files panel itself
7
+ * already does.
8
+ */
9
+ export declare function useFilesEndpointSupport(): FilesEndpointSupport;
@@ -1,3 +1,5 @@
1
1
  import { ApiRoom } from '../types/types';
2
- declare const useGetNewArchRoom: () => (client: any, config: any) => Promise<ApiRoom[]>;
2
+ declare const useGetNewArchRoom: () => (client: any, config: any, options?: {
3
+ force?: boolean;
4
+ }) => Promise<ApiRoom[]>;
3
5
  export default useGetNewArchRoom;
@@ -0,0 +1,17 @@
1
+ interface UseInViewOptions {
2
+ /** Start work slightly before the element scrolls into view. */
3
+ rootMargin?: string;
4
+ /** Once true, stay true - re-rendering a thumbnail on every scroll pass is waste. */
5
+ once?: boolean;
6
+ }
7
+ /**
8
+ * Viewport gate for expensive per-message work (PDF rendering today).
9
+ * Environments without IntersectionObserver - jsdom, older Safari - report
10
+ * "in view" immediately, which degrades to the previous eager behaviour
11
+ * rather than to a blank card.
12
+ */
13
+ export declare const useInView: <T extends Element>({ rootMargin, once, }?: UseInViewOptions) => {
14
+ ref: import('react').MutableRefObject<T>;
15
+ inView: boolean;
16
+ };
17
+ export {};
@@ -0,0 +1,45 @@
1
+ import { IMentionSpan, RoomMember } from '../types/types';
2
+ import { MentionCandidate } from '../helpers/mentions';
3
+ export declare const MENTION_INLINE_LIMIT = 5;
4
+ interface UseMentionComposerParams {
5
+ roomMembers: RoomMember[];
6
+ selfId?: string;
7
+ }
8
+ interface DropdownState {
9
+ start: number;
10
+ query: string;
11
+ }
12
+ /**
13
+ * Tracks @-mention spans and the inline autocomplete dropdown for a plain
14
+ * <input>/<textarea> based message composer (see SendInput.tsx). Text
15
+ * editing and caret placement stay owned by the caller (a controlled React
16
+ * input) - this hook only computes the *derived* state: which mention spans
17
+ * still hold after an edit, and what the dropdown should show.
18
+ */
19
+ export declare const useMentionComposer: ({ roomMembers, selfId, }: UseMentionComposerParams) => {
20
+ mentionSpans: IMentionSpan[];
21
+ setMentionSpans: import('react').Dispatch<import('react').SetStateAction<IMentionSpan[]>>;
22
+ dropdown: DropdownState;
23
+ isDropdownOpen: boolean;
24
+ candidates: MentionCandidate[];
25
+ visibleCandidates: MentionCandidate[];
26
+ hasOverflow: boolean;
27
+ highlightedIndex: number;
28
+ setHighlightedIndex: import('react').Dispatch<import('react').SetStateAction<number>>;
29
+ moveHighlight: (delta: number) => void;
30
+ overflowOpen: boolean;
31
+ setOverflowOpen: import('react').Dispatch<import('react').SetStateAction<boolean>>;
32
+ resetMentions: () => void;
33
+ closeDropdown: () => void;
34
+ handleTextChange: (oldText: string, newText: string, caret: number) => void;
35
+ selectCandidate: (candidate: MentionCandidate, text: string, caretAtSelection: number) => {
36
+ text: string;
37
+ caret: number;
38
+ } | null;
39
+ handleBackspace: (text: string, selectionStart: number, selectionEnd: number) => {
40
+ text: string;
41
+ caret: number;
42
+ } | null;
43
+ };
44
+ export type UseMentionComposerReturn = ReturnType<typeof useMentionComposer>;
45
+ export {};
@@ -1,4 +1,4 @@
1
1
  export declare const useMessageHeapState: () => {
2
- queue: import('../types/types').IMessage[];
2
+ queue: import('../main').IMessage[];
3
3
  idSet: Set<string>;
4
4
  };
@@ -16,9 +16,10 @@ export interface MessageTranslationState {
16
16
  * `<translate source="xx"/>` (see sendTextMessageWithTranslateTag) and
17
17
  * whatever process attaches actual translations does so server-side,
18
18
  * landing on `message.translations` when the stanza is parsed (see
19
- * getDataFromXml). This function only ever reads that - it never calls a
20
- * translation service itself, so there's nothing to fail when one isn't
21
- * reachable, and nothing to await.
19
+ * getDataFromXml). This hook is a thin wrapper around
20
+ * `resolveMessageTranslation` (the shared lookup manual mode also uses) - it
21
+ * never calls a translation service itself, so there's nothing to fail when
22
+ * one isn't reachable, and nothing to await.
22
23
  *
23
24
  * A message sent before the sender had translate-sending turned on simply
24
25
  * never got tagged, so it has no translation to show - not an error, just
@@ -0,0 +1,27 @@
1
+ type DismissLayerKind = 'modal' | 'menu';
2
+ export declare const FOCUSABLE_SELECTOR = "a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])";
3
+ export interface UseModalDismissOptions {
4
+ onClose?: () => void;
5
+ enabled?: boolean;
6
+ /** Ref to the dialog/menu container; used for initial focus. */
7
+ containerRef?: React.RefObject<HTMLElement | null>;
8
+ /**
9
+ * 'modal' (default) stacks; 'menu' is exclusive, see the note above.
10
+ */
11
+ kind?: DismissLayerKind;
12
+ /**
13
+ * Close when a press lands outside the layer. Off by default: modals own
14
+ * a full-screen overlay that already handles this.
15
+ */
16
+ closeOnOutsidePress?: boolean;
17
+ /**
18
+ * Extra elements counted as "inside" for outside-press dismissal, on top
19
+ * of `containerRef`. A menu passes its trigger here: the trigger's own
20
+ * click is the toggle's business, and letting the document listener close
21
+ * the menu first would make the trigger look like it never closes (the
22
+ * toggle would immediately re-open what the listener just shut).
23
+ */
24
+ insideRefs?: React.RefObject<HTMLElement | null>[];
25
+ }
26
+ export declare const useModalDismiss: ({ onClose, enabled, containerRef, kind, closeOnOutsidePress, insideRefs, }: UseModalDismissOptions) => void;
27
+ export default useModalDismiss;