@ethora/chat-component 26.7.5 → 26.7.6
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-hxsWI8cx.js +2139 -0
- package/dist/FilePreviewModal-CwWnHsB1.js +736 -0
- package/dist/ManageDataModal-CDPXcC93.js +159 -0
- package/dist/MarkdownBody-DBJGraWM.js +16874 -0
- package/dist/SideDrawer-Dr_lkaOW.js +226 -0
- package/dist/UserProfileModal-Q_jhXpsX.js +568 -0
- package/dist/UserSettingsModal-OcAJao-d.js +50 -0
- package/dist/{VideoCallOverlay-C4M2I9oV.js → VideoCallOverlay-CC4sShdK.js} +9 -10
- package/dist/VisibilityModal-DkMhx_2k.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/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/NewChatModal/styledNewChatComponents.d.ts +12 -0
- package/dist/components/Modals/SettingsModals/Visibility/StyledComponents.d.ts +1 -1
- package/dist/components/Modals/SideDrawer/DrawerProfileParts.d.ts +13 -0
- package/dist/components/Modals/SideDrawer/SideDrawer.d.ts +44 -0
- package/dist/components/Modals/modalComponents.d.ts +6 -0
- package/dist/components/Modals/styledModalComponents.d.ts +14 -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/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-CpmPa6VI.js +32 -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/constants/MODAL_TYPES.d.ts +11 -7
- package/dist/helpers/ethoraLogger.d.ts +8 -0
- package/dist/helpers/fileKind.d.ts +17 -0
- 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/useChatWrapperInit.d.ts +1 -0
- package/dist/hooks/useFilesEndpointSupport.d.ts +9 -0
- 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/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-B-8IhwaP.js +31837 -0
- package/dist/main.css +1 -1
- package/dist/main.d.ts +14 -0
- package/dist/main.js +21 -21
- 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 +44 -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 +12 -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,32 @@
|
|
|
1
|
+
import { x as e } from "./main-B-8IhwaP.js";
|
|
2
|
+
const a = e.div`
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
align-items: center;
|
|
6
|
+
gap: var(--ethora-space-3, 12px);
|
|
7
|
+
padding: var(--ethora-space-2, 8px) 0 var(--ethora-space-1, 4px);
|
|
8
|
+
text-align: center;
|
|
9
|
+
`, r = e.div`
|
|
10
|
+
font-size: var(--ethora-font-size-lg, 18px);
|
|
11
|
+
font-weight: var(--ethora-font-weight-semibold, 600);
|
|
12
|
+
color: var(--ethora-color-text, #141414);
|
|
13
|
+
overflow-wrap: anywhere;
|
|
14
|
+
`, i = e.div`
|
|
15
|
+
font-size: var(--ethora-font-size-sm, 14px);
|
|
16
|
+
font-weight: var(--ethora-font-weight-regular, 400);
|
|
17
|
+
color: var(--ethora-color-text-muted, #8c8c8c);
|
|
18
|
+
`, c = e.div`
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
gap: var(--ethora-space-2, 8px);
|
|
22
|
+
width: 100%;
|
|
23
|
+
`, l = (o) => {
|
|
24
|
+
navigator.clipboard.writeText(o);
|
|
25
|
+
};
|
|
26
|
+
export {
|
|
27
|
+
a as P,
|
|
28
|
+
r as a,
|
|
29
|
+
c as b,
|
|
30
|
+
i as c,
|
|
31
|
+
l as h
|
|
32
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MentionClickHandler } from './parseMessageBody';
|
|
2
|
+
interface MarkdownBodyProps {
|
|
3
|
+
text: string;
|
|
4
|
+
onMentionClick?: MentionClickHandler;
|
|
5
|
+
}
|
|
6
|
+
declare const MarkdownBody: ({ text, onMentionClick }: MarkdownBodyProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default MarkdownBody;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { IAttachment, IMessage } from '../types/types';
|
|
2
|
+
/**
|
|
3
|
+
* Hard ceiling on how many attachments we will serialise into a stanza.
|
|
4
|
+
* The composer caps the picker far below this; this is the parser-side
|
|
5
|
+
* guard so a malformed or hostile stanza cannot make us render an
|
|
6
|
+
* unbounded grid.
|
|
7
|
+
*/
|
|
8
|
+
export declare const MAX_ATTACHMENTS_PER_MESSAGE = 10;
|
|
9
|
+
/**
|
|
10
|
+
* Turns one raw wire/API object into an `IAttachment`, or null when there is
|
|
11
|
+
* nothing renderable in it. `location` is allowed to be empty: that is what
|
|
12
|
+
* an optimistic (still-uploading) attachment looks like.
|
|
13
|
+
*/
|
|
14
|
+
export declare const normalizeAttachment: (raw: unknown) => IAttachment | null;
|
|
15
|
+
/** Accepts the parsed array, the raw JSON string, or junk. Never throws. */
|
|
16
|
+
export declare const parseAttachments: (raw: unknown) => IAttachment[];
|
|
17
|
+
/**
|
|
18
|
+
* The wire form: a JSON string on the `<data attachments="...">` attribute.
|
|
19
|
+
* Only the fields a receiver renders are sent - the stanza is already wide
|
|
20
|
+
* and every byte here is duplicated per attachment.
|
|
21
|
+
*/
|
|
22
|
+
export declare const serializeAttachments: (attachments: IAttachment[]) => string;
|
|
23
|
+
/**
|
|
24
|
+
* The only supported way to read a media message's files.
|
|
25
|
+
*
|
|
26
|
+
* Order of truth: the multi-attach payload when present, otherwise the flat
|
|
27
|
+
* legacy fields synthesised into a single attachment. Every message that
|
|
28
|
+
* predates this feature - and every message from a client that does not
|
|
29
|
+
* speak it - takes the second path.
|
|
30
|
+
*/
|
|
31
|
+
export declare const getMessageAttachments: (message?: Pick<IMessage, "attachments" | "location" | "locationPreview" | "mimetype" | "originalName" | "fileName" | "size"> | null) => IAttachment[];
|
|
32
|
+
/** Name to show for an attachment, with the URL tail as the last resort. */
|
|
33
|
+
export declare const getAttachmentName: (attachment: IAttachment) => string;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { QuickReply } from './quickReplies';
|
|
2
|
+
/**
|
|
3
|
+
* Buttons an AI agent writes straight into its reply text.
|
|
4
|
+
*
|
|
5
|
+
* Bots built on the SDK attach buttons as the `quickReplies` attribute of the
|
|
6
|
+
* stanza's <data> element. An LLM agent cannot do that: all it controls is the
|
|
7
|
+
* text of its answer. So its system prompt teaches it an in-band form of the
|
|
8
|
+
* same thing, part of the Ethora chat protocol:
|
|
9
|
+
*
|
|
10
|
+
* <xml>
|
|
11
|
+
* <bot-data type="buttons">[Accept],[Reject]</bot-data>
|
|
12
|
+
* <body>Choose one please</body>
|
|
13
|
+
* </xml>
|
|
14
|
+
*
|
|
15
|
+
* This module turns that text into the same QuickReply buttons the attribute
|
|
16
|
+
* produces, plus the text that should actually be shown. It is written for
|
|
17
|
+
* model output, so it is deliberately forgiving about the envelope (the
|
|
18
|
+
* <xml>/<body> wrappers are optional, code fences are unwrapped, an
|
|
19
|
+
* HTML-escaped copy is accepted) and deliberately strict about what it
|
|
20
|
+
* hides: only a `type="buttons"` block is consumed, and nothing is stripped
|
|
21
|
+
* from a message that does not contain one.
|
|
22
|
+
*/
|
|
23
|
+
export interface BotMarkup {
|
|
24
|
+
/** Text to display, with the machine-readable markup removed. */
|
|
25
|
+
text: string;
|
|
26
|
+
buttons: QuickReply[];
|
|
27
|
+
}
|
|
28
|
+
export declare const parseBotMarkup: (body: unknown) => BotMarkup;
|
|
29
|
+
/** The displayable text of a body, for previews and copy. */
|
|
30
|
+
export declare const stripBotMarkup: (body: unknown) => string;
|
|
@@ -4,12 +4,16 @@ export declare const MODAL_TYPES: {
|
|
|
4
4
|
CHAT_PROFILE: string;
|
|
5
5
|
MANAGE_DATA: string;
|
|
6
6
|
VISIBILITY: string;
|
|
7
|
-
PROFILE_SHARES: string;
|
|
8
|
-
DOCUMENT_SHARES: string;
|
|
9
|
-
BLOCKED_USERS: string;
|
|
10
|
-
REFERRALS: string;
|
|
11
7
|
FILE_PREVIEW: string;
|
|
12
8
|
};
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Panels reached from inside Settings. Their back button (and Escape) steps
|
|
11
|
+
* back to the Settings list rather than closing the drawer outright.
|
|
12
|
+
*/
|
|
13
|
+
export declare const SETTINGS_SUB_MODAL_TYPES: string[];
|
|
14
|
+
/**
|
|
15
|
+
* Panels that open as a right-hand side drawer instead of a centred card:
|
|
16
|
+
* the profile/settings family. Everything else (file preview) keeps the
|
|
17
|
+
* centred-dialog presentation.
|
|
18
|
+
*/
|
|
19
|
+
export declare const DRAWER_MODAL_TYPES: string[];
|
|
@@ -4,4 +4,12 @@ export declare const ethoraLogger: {
|
|
|
4
4
|
debug: (...args: unknown[]) => void;
|
|
5
5
|
error: (...args: unknown[]) => void;
|
|
6
6
|
always: (...args: unknown[]) => void;
|
|
7
|
+
/**
|
|
8
|
+
* For failures that must never be silent, whatever the verbose-console
|
|
9
|
+
* setting is: today, an uncaught render error caught by the SDK's error
|
|
10
|
+
* boundary. `error` above stays gated so routine debug noise remains
|
|
11
|
+
* opt-in; this one does not, because a swallowed crash leaves the host
|
|
12
|
+
* with a replaced chat pane and no explanation anywhere.
|
|
13
|
+
*/
|
|
14
|
+
criticalError: (...args: unknown[]) => void;
|
|
7
15
|
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One place that answers "what do we render for this file?".
|
|
3
|
+
*
|
|
4
|
+
* Before this, every surface (bubble, room list, notification toast, the
|
|
5
|
+
* composer) re-derived the answer from `mimetype` alone with a slightly
|
|
6
|
+
* different set of `startsWith` checks, and none of them looked at the file
|
|
7
|
+
* name. That is why a PDF uploaded as `application/octet-stream` (which our
|
|
8
|
+
* storage does for anything it cannot sniff) ended up in the audio branch.
|
|
9
|
+
*/
|
|
10
|
+
export type FileKind = 'image' | 'video' | 'audio' | 'pdf' | 'file';
|
|
11
|
+
/** Lowercase extension without the dot; '' when the name carries none. */
|
|
12
|
+
export declare const getFileExtension: (name?: string) => string;
|
|
13
|
+
export declare const isPdfFile: (mimetype?: string, name?: string) => boolean;
|
|
14
|
+
export declare const getFileKind: (mimetype?: string, name?: string) => FileKind;
|
|
15
|
+
export declare const formatFileSize: (sizeInBytes?: string | number) => string;
|
|
16
|
+
/** Middle-truncates the base name, always keeping the extension visible. */
|
|
17
|
+
export declare const formatFileName: (name: string, maxLength: number) => string;
|
|
@@ -2,7 +2,7 @@ import { IConfig, IRoom } from '../types/types';
|
|
|
2
2
|
type RoomLike = Pick<IRoom, 'jid' | 'title' | 'name'>;
|
|
3
3
|
/**
|
|
4
4
|
* True when the room is listed in `config.hiddenRooms` (by JID or by
|
|
5
|
-
* case-insensitive title). Hidden rooms stay joined
|
|
5
|
+
* case-insensitive title). Hidden rooms stay joined - they are only
|
|
6
6
|
* suppressed from the room list and the unread counters.
|
|
7
7
|
*/
|
|
8
8
|
export declare const isRoomHidden: (room: RoomLike | null | undefined, config?: Pick<IConfig, "hiddenRooms"> | null) => boolean;
|
|
@@ -9,6 +9,7 @@ interface HistoryPreloadSchedulerOptions {
|
|
|
9
9
|
selectedRoomJid?: string | null;
|
|
10
10
|
defaultRoomJids?: string[];
|
|
11
11
|
forceReload?: boolean;
|
|
12
|
+
completionState?: 'done' | 'partial';
|
|
12
13
|
}
|
|
13
14
|
export declare const runHistoryPreloadScheduler: (options: HistoryPreloadSchedulerOptions) => Promise<void>;
|
|
14
15
|
export default runHistoryPreloadScheduler;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const getLocalStorageUser: () => import('../
|
|
1
|
+
export declare const getLocalStorageUser: () => import('../main').User;
|
|
@@ -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>;
|
|
@@ -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,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;
|
|
@@ -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 {};
|