@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.
- package/README.md +72 -16
- package/dist/ChatProfileModal-DehQM4Bo.js +2135 -0
- package/dist/FilePreviewModal-BcZLtVAy.js +743 -0
- package/dist/ManageDataModal-DWabKhSC.js +178 -0
- package/dist/MarkdownBody-CSY3YKDh.js +16874 -0
- package/dist/SideDrawer-DewJ-iPn.js +270 -0
- package/dist/UserProfileModal-BHZ0TULc.js +574 -0
- package/dist/UserSettingsModal-BxwVWNKo.js +50 -0
- package/dist/{VideoCallOverlay-C4M2I9oV.js → VideoCallOverlay-D5SuimGP.js} +9 -10
- package/dist/VisibilityModal-Bd67b4EM.js +132 -0
- package/dist/assets/icons.d.ts +30 -0
- package/dist/components/ContextMenu/ContextMenuComponents.d.ts +1 -3
- package/dist/components/EmojiPicker/LazyEmojiPicker.d.ts +3 -0
- package/dist/components/ExampleComponents/CustomMessage.d.ts +3 -3
- package/dist/components/Files/FilesList.d.ts +12 -0
- package/dist/components/Files/FilesPanel.d.ts +3 -0
- package/dist/components/Files/fileCategory.d.ts +13 -0
- package/dist/components/InputComponents/AttachmentPreview.d.ts +17 -0
- package/dist/components/InputComponents/MentionDropdown.d.ts +13 -0
- package/dist/components/InputComponents/MentionPickerModal.d.ts +19 -0
- package/dist/components/MainComponents/AttachmentList.d.ts +7 -0
- package/dist/components/MainComponents/ChatErrorBoundary.d.ts +53 -0
- package/dist/components/MainComponents/ChatErrorScreen.d.ts +24 -0
- package/dist/components/MainComponents/ChatRoomOpeningPreview.d.ts +24 -0
- package/dist/components/MainComponents/ChatWrapper.d.ts +1 -0
- package/dist/components/MainComponents/ChooseChatMessage.d.ts +11 -1
- package/dist/components/MainComponents/HeaderLogo.d.ts +18 -0
- package/dist/components/MainComponents/MediaMessage.d.ts +5 -0
- package/dist/components/MainComponents/MessageContainer.d.ts +2 -2
- package/dist/components/MainComponents/ReduxWrapper.d.ts +1 -2
- package/dist/components/MainComponents/SystemMessage.d.ts +1 -2
- package/dist/components/MenuRoom/MenuRoom.d.ts +1 -0
- package/dist/components/MessageBubble/CustomDivider.d.ts +3 -3
- package/dist/components/MessageBubble/Message.d.ts +2 -2
- package/dist/components/MessageBubble/MessageTranslate.d.ts +28 -10
- package/dist/components/MessageBubble/QuickReplies.d.ts +6 -5
- package/dist/components/Modals/ChatProfileModal/ChatProfileMemberRow.d.ts +18 -0
- package/dist/components/Modals/FilePreviewModal/FilePreviewModal.d.ts +17 -0
- package/dist/components/Modals/FilePreviewModal/ImageLightbox.d.ts +35 -0
- package/dist/components/Modals/FilePreviewModal/PdfView.d.ts +14 -3
- package/dist/components/Modals/Modal/Modal.d.ts +10 -0
- package/dist/components/Modals/Modal/ModalContent.d.ts +21 -0
- package/dist/components/Modals/ModalBox/ModalBox.d.ts +9 -0
- package/dist/components/Modals/ModalReportChat/ModalReportChat.d.ts +6 -1
- package/dist/components/Modals/ModalWrapper/ModalWrapper.d.ts +8 -0
- package/dist/components/Modals/NewChatModal/styledNewChatComponents.d.ts +12 -0
- package/dist/components/Modals/SettingsModals/SharedStyledComponents.d.ts +12 -0
- package/dist/components/Modals/SettingsModals/Visibility/StyledComponents.d.ts +1 -1
- package/dist/components/Modals/SideDrawer/DrawerProfileParts.d.ts +42 -0
- package/dist/components/Modals/SideDrawer/SideDrawer.d.ts +64 -0
- package/dist/components/Modals/SidePanel/SidePanel.d.ts +31 -0
- package/dist/components/Modals/UserProfileModal/EditUserModal.d.ts +5 -1
- package/dist/components/Modals/modalComponents.d.ts +6 -0
- package/dist/components/Modals/motionVariants.d.ts +37 -0
- package/dist/components/Modals/styledModalComponents.d.ts +13 -3
- package/dist/components/RoomComponents/ChatRoomItem.d.ts +0 -1
- package/dist/components/RoomComponents/OnlineUsersPopover.d.ts +0 -1
- package/dist/components/UsersList/StyledComponents.d.ts +5 -1
- package/dist/components/UsersList/UsersList.d.ts +23 -0
- package/dist/components/styled/Button.d.ts +11 -1
- package/dist/components/styled/ChatWrapperBox.d.ts +3 -1
- package/dist/components/styled/PdfMessage.d.ts +13 -0
- package/dist/components/styled/RoomListComponents/index.d.ts +22 -1
- package/dist/components/styled/SendInput.d.ts +19 -5
- package/dist/components/styled/StyledComponents.d.ts +1 -1
- package/dist/components/styled/StyledInputComponents/MediaComponents.d.ts +18 -0
- package/dist/components/styled/StyledInputComponents/StyledInputComponents.d.ts +21 -0
- package/dist/components/styled/TreadLabel.d.ts +2 -2
- package/dist/components/styled/UnsupportedType.d.ts +1 -1
- package/dist/context/ModalTransitionContext.d.ts +2 -0
- package/dist/examples/quiz/QuizDemo.d.ts +3 -0
- package/dist/examples/quiz/quizScript.d.ts +38 -0
- package/dist/examples/quiz/useDemoSession.d.ts +42 -0
- package/dist/examples/quiz/useQuizFlow.d.ts +47 -0
- package/dist/firebase-config.d.ts +2 -4
- package/dist/handleCopyClick-BoZWTBLC.js +108 -0
- package/dist/helpers/MarkdownBody.d.ts +7 -0
- package/dist/helpers/attachments.d.ts +33 -0
- package/dist/helpers/botMarkup.d.ts +30 -0
- package/dist/helpers/buildChatShareLink.d.ts +20 -0
- package/dist/helpers/cacheScope.d.ts +15 -1
- package/dist/helpers/constants/MODAL_TYPES.d.ts +13 -7
- package/dist/helpers/createRoomFromApi.d.ts +1 -1
- package/dist/helpers/ethoraLogger.d.ts +8 -0
- package/dist/helpers/fileKind.d.ts +17 -0
- package/dist/helpers/formatNumberWithCommas.d.ts +1 -1
- package/dist/helpers/hiddenRooms.d.ts +1 -1
- package/dist/helpers/historyPreloadScheduler.d.ts +1 -0
- package/dist/helpers/lazyEmoji.d.ts +3 -0
- package/dist/helpers/localStorageUser.d.ts +1 -1
- package/dist/helpers/markdownSanitizeSchema.d.ts +3 -0
- package/dist/helpers/mentions.d.ts +63 -0
- package/dist/helpers/parseMessageBody.d.ts +12 -2
- package/dist/helpers/pdf/loadPdfjs.d.ts +64 -0
- package/dist/helpers/remarkLineBreaks.d.ts +42 -0
- package/dist/helpers/resolveMessageTranslation.d.ts +34 -0
- package/dist/helpers/roomImageGallery.d.ts +53 -0
- package/dist/helpers/secureFileUrl.d.ts +8 -0
- package/dist/helpers/sendFailureWatchdog.d.ts +62 -0
- package/dist/helpers/transformTranslatations.d.ts +1 -2
- package/dist/hooks/pushSubscriptionSweep.d.ts +41 -0
- package/dist/hooks/useChatOpenPhase.d.ts +21 -0
- package/dist/hooks/useChatWrapperInit.d.ts +1 -0
- package/dist/hooks/useDelayedAction.d.ts +22 -0
- package/dist/hooks/useExclusiveRightPane.d.ts +25 -0
- package/dist/hooks/useExitTransition.d.ts +24 -0
- package/dist/hooks/useFilesEndpointSupport.d.ts +9 -0
- package/dist/hooks/useGetNewArchRoom.d.ts +3 -1
- package/dist/hooks/useInView.d.ts +17 -0
- package/dist/hooks/useMentionComposer.d.ts +45 -0
- package/dist/hooks/useMessageHeapState.d.ts +1 -1
- package/dist/hooks/useMessageTranslation.d.ts +4 -3
- package/dist/hooks/useModalDismiss.d.ts +27 -0
- package/dist/hooks/useMyFiles.d.ts +16 -0
- package/dist/hooks/usePdfThumbnail.d.ts +22 -0
- package/dist/hooks/useQRCodeChatHandler.d.ts +29 -1
- package/dist/hooks/useRoomMute.d.ts +19 -0
- package/dist/hooks/useRoomState.d.ts +1 -1
- package/dist/hooks/useSendMessage.d.ts +4 -3
- package/dist/hooks/useTypography.d.ts +1 -1
- package/dist/hooks/useUnreadMessagesCounter.d.ts +11 -2
- package/dist/index.esm-B-3fyDHY.js +28 -0
- package/dist/index.esm-CRM4VCRa.js +943 -0
- package/dist/index.esm-msbp26WN.js +3352 -0
- package/dist/index.esm2017-CL0mvq6y.js +1578 -0
- package/dist/main-C2FKeV6y.js +32375 -0
- package/dist/main.css +1 -1
- package/dist/main.d.ts +14 -0
- package/dist/main.js +18 -18
- package/dist/module-DGXkb0R_.js +2960 -0
- package/dist/native-CDi4YD_s.js +13 -0
- package/dist/networking/api-requests/files.api.d.ts +19 -0
- package/dist/networking/api-requests/rooms.api.d.ts +10 -2
- package/dist/networking/api-requests/translate.api.d.ts +15 -0
- package/dist/networking/api-requests/user.api.d.ts +2 -0
- package/dist/networking/sharedRequest.d.ts +4 -0
- package/dist/networking/xmpp/allRoomPresences.xmpp.d.ts +3 -1
- package/dist/networking/xmpp/createTimeoutPromise.xmpp.d.ts +14 -0
- package/dist/networking/xmpp/sendTextMessage.xmpp.d.ts +2 -1
- package/dist/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.d.ts +2 -1
- package/dist/networking/xmppClient.d.ts +12 -3
- package/dist/pdf-B5b7qHu6.js +17130 -0
- package/dist/pdf.worker.min-COYn-gId.js +24385 -0
- package/dist/roomStore/index.d.ts +20 -0
- package/dist/roomStore/persistEncryption.d.ts +51 -0
- package/dist/roomStore/roomsSlice.d.ts +49 -6
- package/dist/styles/classNames.d.ts +35 -0
- package/dist/styles/motion.d.ts +71 -0
- package/dist/styles/tokens.d.ts +27 -0
- package/dist/testIds.d.ts +1 -1
- package/dist/types/models/attachment.model.d.ts +33 -0
- package/dist/types/models/config.model.d.ts +160 -19
- package/dist/types/models/file.model.d.ts +20 -0
- package/dist/types/models/message.model.d.ts +15 -6
- package/dist/types/models/room.model.d.ts +40 -2
- package/dist/types/types.d.ts +3 -1
- package/dist/utils/resendMessage.d.ts +22 -1
- package/dist/utils/translateModePolicy.d.ts +1 -0
- package/dist/wavesurfer.esm-D_a19qBW.js +952 -0
- package/package.json +22 -2
- package/dist/VideoCallOverlay-C4M2I9oV.js.map +0 -1
- package/dist/components/MainComponents/VirtualList.d.ts +0 -10
- package/dist/components/Modals/SettingsModals/BlockedUsers/BlockedUsersModal.d.ts +0 -6
- package/dist/components/Modals/SettingsModals/DocumentShares/DocumentSharesModal.d.ts +0 -6
- package/dist/components/Modals/SettingsModals/ManageDataModal/StyledComponents.d.ts +0 -0
- package/dist/components/Modals/SettingsModals/ProfileShares/ProfileShares.d.ts +0 -6
- package/dist/components/Modals/SettingsModals/Referrals/Referrals.d.ts +0 -6
- package/dist/hooks/useMessageQueue.d.ts +0 -4
- package/dist/main-DeiuFYsl.js +0 -55812
- package/dist/main-DeiuFYsl.js.map +0 -1
- package/dist/main.js.map +0 -1
- package/dist/networking/xmpp/getHistory.xmpp.d.ts +0 -3
- package/dist/networking/xmpp/listenerManager.d.ts +0 -17
- 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
|
-
|
|
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
|
-
|
|
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
|
|
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 {};
|
|
@@ -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
|
|
20
|
-
*
|
|
21
|
-
*
|
|
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;
|