@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.
Files changed (151) hide show
  1. package/README.md +72 -16
  2. package/dist/ChatProfileModal-hxsWI8cx.js +2139 -0
  3. package/dist/FilePreviewModal-CwWnHsB1.js +736 -0
  4. package/dist/ManageDataModal-CDPXcC93.js +159 -0
  5. package/dist/MarkdownBody-DBJGraWM.js +16874 -0
  6. package/dist/SideDrawer-Dr_lkaOW.js +226 -0
  7. package/dist/UserProfileModal-Q_jhXpsX.js +568 -0
  8. package/dist/UserSettingsModal-OcAJao-d.js +50 -0
  9. package/dist/{VideoCallOverlay-C4M2I9oV.js → VideoCallOverlay-CC4sShdK.js} +9 -10
  10. package/dist/VisibilityModal-DkMhx_2k.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/HeaderLogo.d.ts +18 -0
  25. package/dist/components/MainComponents/MediaMessage.d.ts +5 -0
  26. package/dist/components/MainComponents/MessageContainer.d.ts +2 -2
  27. package/dist/components/MainComponents/ReduxWrapper.d.ts +1 -2
  28. package/dist/components/MainComponents/SystemMessage.d.ts +1 -2
  29. package/dist/components/MenuRoom/MenuRoom.d.ts +1 -0
  30. package/dist/components/MessageBubble/CustomDivider.d.ts +3 -3
  31. package/dist/components/MessageBubble/Message.d.ts +2 -2
  32. package/dist/components/MessageBubble/MessageTranslate.d.ts +28 -10
  33. package/dist/components/MessageBubble/QuickReplies.d.ts +6 -5
  34. package/dist/components/Modals/ChatProfileModal/ChatProfileMemberRow.d.ts +18 -0
  35. package/dist/components/Modals/FilePreviewModal/FilePreviewModal.d.ts +17 -0
  36. package/dist/components/Modals/FilePreviewModal/ImageLightbox.d.ts +35 -0
  37. package/dist/components/Modals/FilePreviewModal/PdfView.d.ts +14 -3
  38. package/dist/components/Modals/NewChatModal/styledNewChatComponents.d.ts +12 -0
  39. package/dist/components/Modals/SettingsModals/Visibility/StyledComponents.d.ts +1 -1
  40. package/dist/components/Modals/SideDrawer/DrawerProfileParts.d.ts +13 -0
  41. package/dist/components/Modals/SideDrawer/SideDrawer.d.ts +44 -0
  42. package/dist/components/Modals/modalComponents.d.ts +6 -0
  43. package/dist/components/Modals/styledModalComponents.d.ts +14 -3
  44. package/dist/components/RoomComponents/ChatRoomItem.d.ts +0 -1
  45. package/dist/components/RoomComponents/OnlineUsersPopover.d.ts +0 -1
  46. package/dist/components/UsersList/StyledComponents.d.ts +5 -1
  47. package/dist/components/UsersList/UsersList.d.ts +23 -0
  48. package/dist/components/styled/Button.d.ts +11 -1
  49. package/dist/components/styled/ChatWrapperBox.d.ts +3 -1
  50. package/dist/components/styled/PdfMessage.d.ts +13 -0
  51. package/dist/components/styled/RoomListComponents/index.d.ts +22 -1
  52. package/dist/components/styled/SendInput.d.ts +19 -5
  53. package/dist/components/styled/StyledComponents.d.ts +1 -1
  54. package/dist/components/styled/StyledInputComponents/MediaComponents.d.ts +18 -0
  55. package/dist/components/styled/StyledInputComponents/StyledInputComponents.d.ts +21 -0
  56. package/dist/components/styled/TreadLabel.d.ts +2 -2
  57. package/dist/components/styled/UnsupportedType.d.ts +1 -1
  58. package/dist/examples/quiz/QuizDemo.d.ts +3 -0
  59. package/dist/examples/quiz/quizScript.d.ts +38 -0
  60. package/dist/examples/quiz/useDemoSession.d.ts +42 -0
  61. package/dist/examples/quiz/useQuizFlow.d.ts +47 -0
  62. package/dist/firebase-config.d.ts +2 -4
  63. package/dist/handleCopyClick-CpmPa6VI.js +32 -0
  64. package/dist/helpers/MarkdownBody.d.ts +7 -0
  65. package/dist/helpers/attachments.d.ts +33 -0
  66. package/dist/helpers/botMarkup.d.ts +30 -0
  67. package/dist/helpers/constants/MODAL_TYPES.d.ts +11 -7
  68. package/dist/helpers/ethoraLogger.d.ts +8 -0
  69. package/dist/helpers/fileKind.d.ts +17 -0
  70. package/dist/helpers/hiddenRooms.d.ts +1 -1
  71. package/dist/helpers/historyPreloadScheduler.d.ts +1 -0
  72. package/dist/helpers/lazyEmoji.d.ts +3 -0
  73. package/dist/helpers/localStorageUser.d.ts +1 -1
  74. package/dist/helpers/markdownSanitizeSchema.d.ts +3 -0
  75. package/dist/helpers/mentions.d.ts +63 -0
  76. package/dist/helpers/parseMessageBody.d.ts +12 -2
  77. package/dist/helpers/pdf/loadPdfjs.d.ts +64 -0
  78. package/dist/helpers/remarkLineBreaks.d.ts +42 -0
  79. package/dist/helpers/resolveMessageTranslation.d.ts +34 -0
  80. package/dist/helpers/roomImageGallery.d.ts +53 -0
  81. package/dist/helpers/secureFileUrl.d.ts +8 -0
  82. package/dist/helpers/sendFailureWatchdog.d.ts +62 -0
  83. package/dist/helpers/transformTranslatations.d.ts +1 -2
  84. package/dist/hooks/pushSubscriptionSweep.d.ts +41 -0
  85. package/dist/hooks/useChatWrapperInit.d.ts +1 -0
  86. package/dist/hooks/useFilesEndpointSupport.d.ts +9 -0
  87. package/dist/hooks/useInView.d.ts +17 -0
  88. package/dist/hooks/useMentionComposer.d.ts +45 -0
  89. package/dist/hooks/useMessageHeapState.d.ts +1 -1
  90. package/dist/hooks/useMessageTranslation.d.ts +4 -3
  91. package/dist/hooks/useModalDismiss.d.ts +27 -0
  92. package/dist/hooks/useMyFiles.d.ts +16 -0
  93. package/dist/hooks/usePdfThumbnail.d.ts +22 -0
  94. package/dist/hooks/useRoomMute.d.ts +19 -0
  95. package/dist/hooks/useRoomState.d.ts +1 -1
  96. package/dist/hooks/useSendMessage.d.ts +4 -3
  97. package/dist/hooks/useTypography.d.ts +1 -1
  98. package/dist/hooks/useUnreadMessagesCounter.d.ts +11 -2
  99. package/dist/index.esm-B-3fyDHY.js +28 -0
  100. package/dist/index.esm-CRM4VCRa.js +943 -0
  101. package/dist/index.esm-msbp26WN.js +3352 -0
  102. package/dist/index.esm2017-CL0mvq6y.js +1578 -0
  103. package/dist/main-B-8IhwaP.js +31837 -0
  104. package/dist/main.css +1 -1
  105. package/dist/main.d.ts +14 -0
  106. package/dist/main.js +21 -21
  107. package/dist/module-DGXkb0R_.js +2960 -0
  108. package/dist/native-CDi4YD_s.js +13 -0
  109. package/dist/networking/api-requests/files.api.d.ts +19 -0
  110. package/dist/networking/api-requests/rooms.api.d.ts +10 -2
  111. package/dist/networking/api-requests/translate.api.d.ts +15 -0
  112. package/dist/networking/api-requests/user.api.d.ts +2 -0
  113. package/dist/networking/sharedRequest.d.ts +4 -0
  114. package/dist/networking/xmpp/allRoomPresences.xmpp.d.ts +3 -1
  115. package/dist/networking/xmpp/createTimeoutPromise.xmpp.d.ts +14 -0
  116. package/dist/networking/xmpp/sendTextMessage.xmpp.d.ts +2 -1
  117. package/dist/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.d.ts +2 -1
  118. package/dist/networking/xmppClient.d.ts +12 -3
  119. package/dist/pdf-B5b7qHu6.js +17130 -0
  120. package/dist/pdf.worker.min-COYn-gId.js +24385 -0
  121. package/dist/roomStore/index.d.ts +20 -0
  122. package/dist/roomStore/persistEncryption.d.ts +51 -0
  123. package/dist/roomStore/roomsSlice.d.ts +49 -6
  124. package/dist/styles/classNames.d.ts +35 -0
  125. package/dist/styles/motion.d.ts +44 -0
  126. package/dist/styles/tokens.d.ts +27 -0
  127. package/dist/testIds.d.ts +1 -1
  128. package/dist/types/models/attachment.model.d.ts +33 -0
  129. package/dist/types/models/config.model.d.ts +160 -19
  130. package/dist/types/models/file.model.d.ts +20 -0
  131. package/dist/types/models/message.model.d.ts +15 -6
  132. package/dist/types/models/room.model.d.ts +12 -2
  133. package/dist/types/types.d.ts +3 -1
  134. package/dist/utils/resendMessage.d.ts +22 -1
  135. package/dist/utils/translateModePolicy.d.ts +1 -0
  136. package/dist/wavesurfer.esm-D_a19qBW.js +952 -0
  137. package/package.json +22 -2
  138. package/dist/VideoCallOverlay-C4M2I9oV.js.map +0 -1
  139. package/dist/components/MainComponents/VirtualList.d.ts +0 -10
  140. package/dist/components/Modals/SettingsModals/BlockedUsers/BlockedUsersModal.d.ts +0 -6
  141. package/dist/components/Modals/SettingsModals/DocumentShares/DocumentSharesModal.d.ts +0 -6
  142. package/dist/components/Modals/SettingsModals/ManageDataModal/StyledComponents.d.ts +0 -0
  143. package/dist/components/Modals/SettingsModals/ProfileShares/ProfileShares.d.ts +0 -6
  144. package/dist/components/Modals/SettingsModals/Referrals/Referrals.d.ts +0 -6
  145. package/dist/hooks/useMessageQueue.d.ts +0 -4
  146. package/dist/main-DeiuFYsl.js +0 -55812
  147. package/dist/main-DeiuFYsl.js.map +0 -1
  148. package/dist/main.js.map +0 -1
  149. package/dist/networking/xmpp/getHistory.xmpp.d.ts +0 -3
  150. package/dist/networking/xmpp/listenerManager.d.ts +0 -17
  151. package/dist/utils/xmppMemoryMonitor.d.ts +0 -16
@@ -3,6 +3,26 @@ import { IMessage, IRoom } from '../types/types';
3
3
  export declare const sanitizeRoomsMap: (roomsMap: Record<string, any>) => Record<string, IRoom>;
4
4
  export declare const MAX_MESSAGES_PER_ROOM = 100;
5
5
  export declare const MAX_PERSISTED_ROOMS = 100;
6
+ /**
7
+ * Composer drafts are a NEW top-level key of the rooms slice, so they are
8
+ * persisted deliberately rather than by accident: they are the one piece of
9
+ * live composer state worth surviving a reload, and unlike `members` (see
10
+ * REFETCHED_ROOM_FIELDS below) nothing on the server can rebuild them.
11
+ *
12
+ * They are also kept OFF the room objects on purpose. The rooms map is what
13
+ * the char budget fights over, and its last-resort eviction can only drop
14
+ * `messages` - a draft riding on a room would compete with the message
15
+ * cache for that budget and could not be evicted independently. As its own
16
+ * key it is bounded on its own terms by the caps in roomsSlice:
17
+ * MAX_PERSISTED_DRAFTS (30, least recently typed dropped first, using the
18
+ * map's insertion order as recency) x MAX_DRAFT_LENGTH (2,000 chars) =
19
+ * 60,000 chars, i.e. 6% of PERSISTED_ROOMS_CHAR_BUDGET even at the worst
20
+ * case, and typically a few hundred bytes.
21
+ *
22
+ * Applied on the way out AND on the way back in (sanitizeRoomsSliceKey), so
23
+ * an older or hand-edited blob cannot rehydrate an unbounded map either.
24
+ */
25
+ export declare const compactDraftsForPersist: (drafts: unknown) => Record<string, string>;
6
26
  export declare const PERSISTED_ROOMS_CHAR_BUDGET = 1000000;
7
27
  /** ~1.4x: AES + base64 overhead the encrypt transform adds on top. */
8
28
  export declare const ENCRYPTION_INFLATION_FACTOR = 1.4;
@@ -0,0 +1,51 @@
1
+ import { User } from '../types/types';
2
+ /**
3
+ * WHICH CLAIMS, AND WHY NOT THE WHOLE TOKEN.
4
+ *
5
+ * The obvious input, `user.token`, is the wrong one: it rotates.
6
+ * `authRefresh.ts` runs refresh-token rotation with reuse detection, so
7
+ * the access token string changes several times an hour. Key the
8
+ * encryption on the whole token and every rotation orphans the blob
9
+ * that the previous token wrote: the user would lose their entire chat
10
+ * cache on a schedule, silently, and each reload would start from an
11
+ * empty transcript until MAM caught up.
12
+ *
13
+ * So we derive from the parts of the token that are stable for the life
14
+ * of the ACCOUNT rather than the life of the credential: the `userId`
15
+ * and `appId` claims out of the JWT payload (Ethora issues
16
+ * `{ data: { userId, appId }, iat, exp }`). `iat` / `exp` / the
17
+ * signature are deliberately excluded - they are exactly the parts that
18
+ * change on refresh.
19
+ *
20
+ * `appToken` joins them as a build-time constant so two different apps
21
+ * embedding the SDK on one origin never derive the same key. It adds no
22
+ * secrecy on its own (it is in the bundle), which is why it is a
23
+ * component of the key and not the key.
24
+ */
25
+ export interface PersistKeyIdentity {
26
+ userId: string;
27
+ appId: string;
28
+ }
29
+ /** The key used when there is no session: SSR, logged out, first paint. */
30
+ export declare const ANONYMOUS_PERSIST_SECRET_KEY: string;
31
+ /**
32
+ * Stable account identity for the key, read from the JWT payload first
33
+ * and from the stored user record second. The fallback matters for
34
+ * hosts that hand the SDK a user object without an Ethora access token
35
+ * (they run their own auth and supply `refreshTokens.refreshFunction`).
36
+ */
37
+ export declare const readStableIdentity: (user?: Partial<User> | null) => PersistKeyIdentity | null;
38
+ export declare const derivePersistSecretKey: (identity?: PersistKeyIdentity | null) => string;
39
+ export declare const getPersistSecretKey: () => string;
40
+ /** Test seam, and the hook logout could use if it ever needs to. */
41
+ export declare const resetPersistSecretKeyCache: () => void;
42
+ /**
43
+ * Drop-in replacement for the old fixed-key `encryptTransform`.
44
+ *
45
+ * It has to resolve the key lazily, per call: `encryptTransform`
46
+ * captures `secretKey` when it is constructed, and this module is
47
+ * imported at store-creation time, long before a `<Chat>` consumer has
48
+ * logged anybody in. Resolving on each in/out means the first write
49
+ * after login already uses that user's key.
50
+ */
51
+ export declare const sessionEncryptTransform: import('redux-persist').Transform<any, any, any, any>;
@@ -1,5 +1,21 @@
1
- import { AddRoomMessageAction, ApiRoom, EditAction, IMessage, IRoom, ReactionAction, RoomMember } from '../types/types';
1
+ import { AddRoomMessageAction, ApiRoom, EditAction, IMessage, IRoom, ReactionAction, RoomMember, Translation } from '../types/types';
2
2
  import { default as XmppClient } from '../networking/xmppClient';
3
+ /**
4
+ * Draft caps. Drafts ride along in the persisted rooms slice, so they have
5
+ * to be bounded before they are written, not after:
6
+ *
7
+ * 30 rooms x 2,000 chars = 60,000 chars worst case, ~84,000 after the
8
+ * encrypt transform's ~1.4x inflation (~168 KB of the origin's ~5 MB).
9
+ *
10
+ * That is small next to PERSISTED_ROOMS_CHAR_BUDGET (1,000,000 chars) and,
11
+ * because drafts are their own top-level slice key, it can never squeeze
12
+ * the message cache the way the member roster once did. 2,000 chars is far
13
+ * longer than any message anyone actually leaves half-typed; anything past
14
+ * it is truncated rather than dropped, so the user still gets most of it
15
+ * back.
16
+ */
17
+ export declare const MAX_DRAFT_LENGTH = 2000;
18
+ export declare const MAX_PERSISTED_DRAFTS = 30;
3
19
  interface RoomMessagesState {
4
20
  rooms: {
5
21
  [jid: string]: IRoom;
@@ -16,12 +32,13 @@ interface RoomMessagesState {
16
32
  subscribedRooms: string[];
17
33
  pushSubscriptionStatus: Record<string, 'pending' | 'subscribed' | 'error' | 'blocked'>;
18
34
  loadingText?: string;
35
+ drafts: Record<string, string>;
19
36
  }
20
37
  interface PreloadRoomUpdate {
21
38
  jid: string;
22
39
  messages?: IMessage[];
23
40
  unreadCapped?: boolean;
24
- historyPreloadState?: 'idle' | 'loading' | 'done' | 'error';
41
+ historyPreloadState?: 'idle' | 'loading' | 'partial' | 'done' | 'error';
25
42
  }
26
43
  export declare const addRoomViaApi: import('@reduxjs/toolkit').AsyncThunk<void, {
27
44
  room: IRoom;
@@ -48,11 +65,26 @@ export declare const addRoom: import('@reduxjs/toolkit').ActionCreatorWithOption
48
65
  }, "roomMessages/replaceRoomMessages">, addRoomMessage: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<AddRoomMessageAction, "roomMessages/addRoomMessage">, deleteRoomMessage: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
49
66
  roomJID: string;
50
67
  messageId: string;
51
- }, "roomMessages/deleteRoomMessage">, setEditAction: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<EditAction, "roomMessages/setEditAction">, editRoomMessage: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
68
+ }, "roomMessages/deleteRoomMessage">, removeRoomMessage: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
69
+ roomJID: string;
70
+ messageId: string;
71
+ }, "roomMessages/removeRoomMessage">, setEditAction: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<EditAction, "roomMessages/setEditAction">, setMessageSendFailed: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
72
+ roomJID: string;
73
+ messageId: string;
74
+ }, "roomMessages/setMessageSendFailed">, setMessageSendRetrying: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
75
+ roomJID: string;
76
+ messageId: string;
77
+ }, "roomMessages/setMessageSendRetrying">, editRoomMessage: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
52
78
  roomJID: string;
53
79
  messageId: string;
54
80
  text: string;
55
- }, "roomMessages/editRoomMessage">, setComposing: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
81
+ isEdited?: boolean;
82
+ }, "roomMessages/editRoomMessage">, setMessageTranslation: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
83
+ roomJID: string;
84
+ messageId: string;
85
+ locale: string;
86
+ entry: Translation;
87
+ }, "roomMessages/setMessageTranslation">, setComposing: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
56
88
  chatJID: string;
57
89
  composing: boolean;
58
90
  composingList?: string[];
@@ -87,7 +119,10 @@ export declare const addRoom: import('@reduxjs/toolkit').ActionCreatorWithOption
87
119
  }, "roomMessages/deleteRoom">, updateRoom: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
88
120
  jid: string;
89
121
  updates: Partial<IRoom>;
90
- }, "roomMessages/updateRoom">, updateUsersSet: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
122
+ }, "roomMessages/updateRoom">, updateRooms: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
123
+ jid: string;
124
+ updates: Partial<IRoom>;
125
+ }[], "roomMessages/updateRooms">, updateUsersSet: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
91
126
  rooms: ApiRoom[];
92
127
  }, "roomMessages/updateUsersSet">, setOpenReportModal: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
93
128
  isOpen: boolean;
@@ -98,6 +133,14 @@ export declare const addRoom: import('@reduxjs/toolkit').ActionCreatorWithOption
98
133
  status: "pending" | "subscribed" | "error" | "blocked";
99
134
  }, "roomMessages/setPushSubscriptionStatus">, clearPushSubscriptions: import('@reduxjs/toolkit').ActionCreatorWithoutPayload<"roomMessages/clearPushSubscriptions">, applyRoomsPreloadBatch: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
100
135
  rooms: PreloadRoomUpdate[];
101
- }, "roomMessages/applyRoomsPreloadBatch">;
136
+ }, "roomMessages/applyRoomsPreloadBatch">, setRoomDraft: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
137
+ jid: string;
138
+ text: string;
139
+ }, "roomMessages/setRoomDraft">, clearRoomDraft: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
140
+ jid: string;
141
+ }, "roomMessages/clearRoomDraft">, setRoomMuted: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
142
+ jid: string;
143
+ muted: boolean | undefined;
144
+ }, "roomMessages/setRoomMuted">;
102
145
  declare const _default: import('redux').Reducer<RoomMessagesState>;
103
146
  export default _default;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Global CSS class names owned by this SDK.
3
+ *
4
+ * This package is embedded into host applications, so every class name it
5
+ * writes into the DOM shares a namespace with the host's own stylesheet.
6
+ * Anything declared in `src/index.css` must therefore be prefixed with
7
+ * `ethora-` (so it cannot collide with a host class of the same name) and,
8
+ * where possible, nested under {@link CHAT_ROOT_CLASS} (so it cannot leak
9
+ * styling onto host markup).
10
+ *
11
+ * Keep these constants as the single source of truth: `index.css` and every
12
+ * component that adds/removes one of these classes must agree, and a plain
13
+ * string literal in a `classList.add(...)` call is easy to drift.
14
+ */
15
+ /**
16
+ * Marks a subtree as "owned by the chat".
17
+ *
18
+ * Applied to:
19
+ * - the invisible (`display: contents`) wrapper `<Chat>` renders around its
20
+ * whole tree, so every chat node is a descendant, and
21
+ * - UI the SDK renders OUTSIDE that tree (the in-app notification container,
22
+ * the portalled language modal), which would otherwise lose the chat's
23
+ * scrollbar/typography defaults.
24
+ *
25
+ * Everything in `index.css` that is not itself `ethora-`-prefixed (scrollbars,
26
+ * base font) is scoped to this class, which is what keeps the host page's own
27
+ * scrollbars and body typography untouched.
28
+ */
29
+ export declare const CHAT_ROOT_CLASS = "ethora-chat-root";
30
+ /** Transient highlight flashed on a message when a notification is opened. */
31
+ export declare const MESSAGE_HIGHLIGHT_CLASS = "ethora-message-highlight";
32
+ /** Fixed-position container holding the in-app notification toasts. */
33
+ export declare const NOTIFICATION_CONTAINER_CLASS = "ethora-message-notification-container";
34
+ /** Forces the colour-emoji font on nodes that render a bare emoji glyph. */
35
+ export declare const APPLE_EMOJI_CLASS = "ethora-apple-emoji";
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Shared motion primitives for the chat UI.
3
+ *
4
+ * Keep durations/easing in sync with the tokens published by
5
+ * `styles/tokens.ts` (`--ethora-motion-fast` = 150ms, `--ethora-motion-base`
6
+ * = 220ms, `--ethora-motion-ease`). The literal values are duplicated here
7
+ * (rather than reading the CSS variables inside `@keyframes`, which most
8
+ * browsers don't support) but must stay numerically identical.
9
+ */
10
+ export declare const MOTION_FAST = "150ms";
11
+ export declare const MOTION_BASE = "220ms";
12
+ export declare const MOTION_EASE = "cubic-bezier(.2,.8,.2,1)";
13
+ export declare const fadeIn: import('styled-components/dist/models/Keyframes').default;
14
+ export declare const fadeInUp: import('styled-components/dist/models/Keyframes').default;
15
+ export declare const scaleIn: import('styled-components/dist/models/Keyframes').default;
16
+ export declare const shimmer: import('styled-components/dist/models/Keyframes').default;
17
+ /**
18
+ * Right-hand side drawer entrance. Travels further than `fadeInUp` (a panel
19
+ * sliding in from the screen edge reads as motion only if it actually
20
+ * covers some distance) but keeps the same duration/easing as everything
21
+ * else, so a drawer does not feel like a different app.
22
+ */
23
+ export declare const slideInRight: import('styled-components/dist/models/Keyframes').default;
24
+ /**
25
+ * Disables all animations/transitions when the user prefers reduced motion.
26
+ * Spread this LAST in a styled-components template so it wins the cascade,
27
+ * e.g.:
28
+ *
29
+ * const Row = styled.div`
30
+ * animation: ${fadeInUp} ${MOTION_BASE} ${MOTION_EASE};
31
+ * ${reducedMotion}
32
+ * `;
33
+ */
34
+ export declare const reducedMotion: import('styled-components').RuleSet<object>;
35
+ /** Convenience: fade+rise-in animation shorthand, respecting reduced motion. */
36
+ export declare const fadeInUpAnimation: import('styled-components').RuleSet<object>;
37
+ /** Convenience: cross-fade animation shorthand, respecting reduced motion. */
38
+ export declare const fadeInAnimation: import('styled-components').RuleSet<object>;
39
+ /** Convenience: scale-in animation shorthand, respecting reduced motion. */
40
+ export declare const scaleInAnimation: import('styled-components').RuleSet<object>;
41
+ /** Convenience: side-drawer slide-in shorthand, respecting reduced motion. */
42
+ export declare const slideInRightAnimation: import('styled-components').RuleSet<object>;
43
+ /** Shimmering skeleton background, respecting reduced motion (stays static). */
44
+ export declare const shimmerBackground: import('styled-components').RuleSet<object>;
@@ -0,0 +1,27 @@
1
+ import { CSSProperties } from 'react';
2
+ import { IConfig, TypographyConfig } from '../types/types';
3
+ /** Mix `hex` toward white by `amount` (0-1). Used for soft tints. */
4
+ export declare const tintColor: (hex: string, amount: number) => string;
5
+ /** Mix `hex` toward black by `amount` (0-1). Used for hover/pressed shades. */
6
+ export declare const shadeColor: (hex: string, amount: number) => string;
7
+ export type ThemeTokenMap = Record<string, string>;
8
+ /**
9
+ * Build the full `--ethora-*` CSS variable map from the host's config.
10
+ * Pure function - safe to call outside the browser (SSR, tests).
11
+ */
12
+ export declare function buildThemeTokens(colors?: IConfig['colors'], typography?: TypographyConfig): ThemeTokenMap;
13
+ /**
14
+ * The class the chat's root element carries; useful for scoping global CSS.
15
+ * Re-exported from `styles/classNames`, which owns every class name this
16
+ * SDK writes into the DOM, so the literal exists in exactly one place.
17
+ */
18
+ export { CHAT_ROOT_CLASS } from './classNames';
19
+ /**
20
+ * Memoized React.CSSProperties built from the token map - spread this onto
21
+ * the chat root element's inline `style` so the variables are scoped to the
22
+ * chat subtree wherever the root can carry them.
23
+ */
24
+ export declare function useThemeTokenStyle(colors?: IConfig['colors'], typography?: TypographyConfig): CSSProperties;
25
+ export declare function ThemeTokens({ config, }: {
26
+ config?: Pick<IConfig, 'colors' | 'typography'>;
27
+ }): null;
package/dist/testIds.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * Kotlin objects (in `chat-ui/src/main/.../components/*Kt`) and iOS's
7
7
  * `*AccessibilityID` Swift enums (in `XMPPChatUI/AccessibilityIdentifiers.swift`),
8
8
  * so a single Maestro YAML flow exercises the same intent on either
9
- * mobile platform — and Playwright tests in host apps using this
9
+ * mobile platform - and Playwright tests in host apps using this
10
10
  * component (e.g. `ethora-app-reactjs`) can resolve the same nodes
11
11
  * via `data-testid`.
12
12
  *
@@ -0,0 +1,33 @@
1
+ /**
2
+ * One uploaded file carried by a media message.
3
+ *
4
+ * Historically a media message described exactly one file through flat
5
+ * `IMessage` fields (`location`, `mimetype`, `originalName`, ...). Those
6
+ * fields are still written (they are what every already-shipped client
7
+ * reads), and they now double as attachment #0 of `IMessage.attachments`.
8
+ * Anything past #0 only exists in the `attachments` payload, so old clients
9
+ * degrade to "shows the first file" instead of breaking.
10
+ */
11
+ export interface IAttachment {
12
+ /** `_id` returned by POST /files/ - stable per upload, used as a render key. */
13
+ attachmentId?: string;
14
+ /** Canonical file URL. */
15
+ location: string;
16
+ /** Server-side generated preview (images always, other types sometimes). */
17
+ locationPreview?: string;
18
+ mimetype?: string;
19
+ /** Name the user picked. */
20
+ originalName?: string;
21
+ /** Name on the storage side. */
22
+ fileName?: string;
23
+ /** Bytes, as a string - that is how it travels on the wire. */
24
+ size?: string;
25
+ duration?: string;
26
+ ownerKey?: string;
27
+ userId?: string;
28
+ createdAt?: string;
29
+ updatedAt?: string;
30
+ expiresAt?: string;
31
+ isVisible?: boolean;
32
+ isPrivate?: boolean;
33
+ }
@@ -1,7 +1,7 @@
1
1
  import { User } from './user.model';
2
2
  import { xmppSettingsInterface } from './xmpp.model';
3
- import { PartialRoomWithMandatoryKeys, ConfigRoom } from './room.model';
4
- import { MessageBubble, MessageProps, IMessage } from './message.model';
3
+ import { ConfigRoom } from './room.model';
4
+ import { MessageProps, IMessage } from './message.model';
5
5
  import { Iso639_1Codes } from './language.model';
6
6
  import { QuickReply } from '../../helpers/quickReplies';
7
7
  import { default as React } from 'react';
@@ -56,7 +56,7 @@ export interface FBConfig {
56
56
  }
57
57
  /**
58
58
  * A single self-hosted font file to register via an injected `@font-face`
59
- * rule. Use this for fonts that are not on Google Fonts — e.g. the Ukrainian
59
+ * rule. Use this for fonts that are not on Google Fonts - e.g. the Ukrainian
60
60
  * government "e-Ukraine" family distributed from thedigital.gov.ua. Host the
61
61
  * `.woff2`/`.ttf` somewhere reachable and point `src` at it.
62
62
  */
@@ -114,10 +114,54 @@ export interface TypographyConfig {
114
114
  bold?: number;
115
115
  };
116
116
  }
117
+ /** Which part of the SDK an uncaught render error came from. */
118
+ export type ChatErrorScope = 'chat' | 'call-overlay';
119
+ /**
120
+ * What the SDK reports when its error boundary catches an uncaught render
121
+ * error: the error itself, React's component stack (when the runtime gives
122
+ * one) and which subtree crashed.
123
+ */
124
+ export interface ChatErrorInfo {
125
+ error: Error;
126
+ componentStack?: string;
127
+ scope: ChatErrorScope;
128
+ }
129
+ /**
130
+ * Render-prop form of `fallbackScreens.error`. `reset()` clears the boundary
131
+ * and re-mounts the chat subtree, so a host can build its own "try again".
132
+ */
133
+ export type ChatErrorFallbackRenderer = (context: ChatErrorInfo & {
134
+ reset: () => void;
135
+ }) => React.ReactNode;
117
136
  export interface IConfig {
118
137
  appId?: string;
119
138
  disableHeader?: boolean;
120
139
  disableMedia?: boolean;
140
+ /** Composer attachment limits. Applies to the built-in SendInput. */
141
+ attachments?: {
142
+ /**
143
+ * How many files one message may carry. Defaults to 5 and is hard-capped
144
+ * at 10 by the stanza payload (MAX_ATTACHMENTS_PER_MESSAGE).
145
+ */
146
+ maxFiles?: number;
147
+ /** Per-file ceiling in MB. Omit to skip the client-side size check. */
148
+ maxFileSizeMb?: number;
149
+ /** `accept` attribute handed to the file picker. Omit to allow anything. */
150
+ accept?: string;
151
+ };
152
+ /** First-page PDF thumbnails inside message bubbles. */
153
+ pdfPreview?: {
154
+ /** Set false to always show the static document card. Default true. */
155
+ enabled?: boolean;
156
+ /**
157
+ * URL of a pdf.js worker script. Omit and pdf.js runs on the main
158
+ * thread, which needs no bundler-specific asset wiring - fine for
159
+ * thumbnails, worth overriding if your users open large documents.
160
+ */
161
+ workerSrc?: string;
162
+ /** Documents above this are not auto-rendered. Default 25. */
163
+ maxFileSizeMb?: number;
164
+ };
121
165
  colors?: {
122
166
  primary: string;
123
167
  secondary: string;
@@ -157,6 +201,25 @@ export interface IConfig {
157
201
  noConnection?: React.ReactNode;
158
202
  /** Shown when the user has no chat room to display. */
159
203
  noRoom?: React.ReactNode;
204
+ /**
205
+ * Shown when an uncaught render error inside the chat is caught by the
206
+ * SDK's error boundary (instead of white-screening the host app).
207
+ *
208
+ * Same shape as the screens above (string = centered text, React node =
209
+ * rendered as-is) plus a render-function form, which is what you want if
210
+ * the replacement screen needs its own retry control or wants to show
211
+ * something about the error:
212
+ *
213
+ * fallbackScreens: {
214
+ * error: ({ error, reset }) => (
215
+ * <MyErrorPanel message={error.message} onRetry={reset} />
216
+ * ),
217
+ * }
218
+ *
219
+ * Omit it to keep the built-in calm, themed, translated screen with its
220
+ * own "try again" button.
221
+ */
222
+ error?: React.ReactNode | ChatErrorFallbackRenderer;
160
223
  };
161
224
  /**
162
225
  * Hide specific rooms from the room list and unread counters without
@@ -193,13 +256,35 @@ export interface IConfig {
193
256
  defaultLogin?: boolean;
194
257
  disableInteractions?: boolean;
195
258
  chatHeaderBurgerMenu?: boolean;
196
- forceSetRoom?: boolean;
197
259
  roomListStyles?: React.CSSProperties;
198
260
  chatRoomStyles?: React.CSSProperties;
199
261
  noMessagesPlaceholder?: React.ComponentType;
200
262
  setRoomJidInPath?: boolean;
201
263
  disableRoomMenu?: boolean;
264
+ /**
265
+ * Hide the "Mute/Unmute notifications" toggle from the chat header menu
266
+ * and the room profile drawer. The toggle is only ever offered when the
267
+ * backend has reported a `muted` boolean for the room in the first place
268
+ * (see IRoom.muted) - this flag is for a host that wants to hide it
269
+ * outright even then, e.g. because it renders its own mute control.
270
+ */
271
+ disableRoomMute?: boolean;
202
272
  defaultRooms?: ConfigRoom[];
273
+ /**
274
+ * The "Files" tab shown in the sidebar alongside "Chats". Backed by
275
+ * `GET /v2/files`, which not every backend has yet, so this defaults to
276
+ * an auto-detecting mode rather than always-on:
277
+ * - `enabled: true` always shows the tab - use this once you know your
278
+ * backend has /v2/files.
279
+ * - `enabled: false` always hides it.
280
+ * - omitted (default): the tab shows optimistically and only hides
281
+ * itself if the first files list request (when the user opens the tab)
282
+ * comes back 404/501, meaning this backend doesn't have the endpoint.
283
+ * No extra probe request is made just to decide this up front.
284
+ */
285
+ filesTab?: {
286
+ enabled?: boolean;
287
+ };
203
288
  refreshTokens?: {
204
289
  enabled: boolean;
205
290
  /**
@@ -227,15 +312,27 @@ export interface IConfig {
227
312
  color?: string;
228
313
  image?: string | File;
229
314
  };
230
- bubleMessage?: MessageBubble;
315
+ /**
316
+ * Host logo rendered at the left of the chat header, before the back
317
+ * button / burger menu. A plain string is treated as an image URL and
318
+ * rendered as an `<img>` (32px tall, aspect preserved); any other React
319
+ * element is rendered as-is. Same string-or-node duality as
320
+ * `fallbackScreens`. Omit to render no logo (the default).
321
+ */
231
322
  headerLogo?: string | React.ReactElement;
323
+ /**
324
+ * Takes over the room-list burger menu. When set, the built-in
325
+ * Profile/Settings/Logout dropdown is not rendered at all: the same burger
326
+ * button just calls this handler, so the host can open its own account
327
+ * menu or drawer.
328
+ */
232
329
  headerMenu?: () => void;
330
+ /**
331
+ * Same idea for the chat header's room menu (the "more" button carrying
332
+ * Report and Leave). When set, that dropdown is not rendered and the
333
+ * button calls this handler instead.
334
+ */
233
335
  headerChatMenu?: () => void;
234
- customRooms?: {
235
- rooms: PartialRoomWithMandatoryKeys[];
236
- disableGetRooms?: boolean;
237
- singleRoom: boolean;
238
- };
239
336
  translates?: {
240
337
  enabled: boolean;
241
338
  translations?: Iso639_1Codes;
@@ -244,11 +341,17 @@ export interface IConfig {
244
341
  * "Translate" link the reader clicks (LinkedIn-style), then renders the
245
342
  * result inline with a "Show original" toggle. Default 'auto'.
246
343
  *
247
- * Neither mode calls any translation service - both only ever display
344
+ * 'auto' is purely a display choice: it only ever reads
248
345
  * `message.translations`, whatever arrived attached to the stanza (see
249
- * `readerLocale` below for how a message gets translated in the first
250
- * place). This is purely a display choice: show it immediately, or let
251
- * the reader ask for it.
346
+ * `readerLocale` below), and never calls a translation service itself.
347
+ * 'manual' shows exactly the same thing on click, via the same lookup -
348
+ * no host function and no `endpoint` means no network request, just the
349
+ * translation already attached (if any). 'manual' does one thing more:
350
+ * on a click, if nothing is attached and the host hasn't supplied
351
+ * `onTranslate`, it fetches a translation from `endpoint` ONLY when the
352
+ * host has set that explicitly (see below) and caches the result onto
353
+ * `message.translations` so a second click - and 'auto' mode, if the
354
+ * reader switches to it - see it for free.
252
355
  */
253
356
  mode?: 'auto' | 'manual';
254
357
  /**
@@ -295,8 +398,9 @@ export interface IConfig {
295
398
  /**
296
399
  * Host-provided translation function. When set, the manual Translate
297
400
  * action calls this - wire it to your own service - instead of reading
298
- * `message.translations`. Optional; omit to use only what already
299
- * arrived over XMPP.
401
+ * `message.translations` or falling back to the built-in translate
402
+ * service. Optional; omit to use only what already arrived over XMPP
403
+ * (and, in manual mode, the fetch described on `endpoint` below).
300
404
  */
301
405
  onTranslate?: (text: string, ctx: {
302
406
  sourceLocale?: string;
@@ -306,9 +410,30 @@ export interface IConfig {
306
410
  /**
307
411
  * Host predicate deciding whether to show the Translate action for a given
308
412
  * message. When omitted, the component compares base languages (message
309
- * source vs reader, region ignored) and shows the action when they differ.
413
+ * source vs reader, region ignored) and shows the action when they differ,
414
+ * AND only when something could actually answer the click (a translation
415
+ * is already attached, `onTranslate` is set, or `endpoint` is set below) -
416
+ * otherwise no Translate link is shown at all, rather than one that could
417
+ * only ever end in "Could not translate".
310
418
  */
311
419
  showTranslateForMessage?: (message: IMessage) => boolean;
420
+ /**
421
+ * Explicit opt-in override for the built-in translate service's URL (e.g.
422
+ * "https://translate.api.example.com/translate"), used by manual mode's
423
+ * on-demand fetch (see `mode` above) when nothing is attached to the
424
+ * message and the host hasn't supplied `onTranslate`.
425
+ *
426
+ * This is strictly opt-in: the built-in HTTP call is never made unless
427
+ * this is set explicitly - there is no default derived from
428
+ * `config.baseUrl`. Leave it unset to rely only on `onTranslate` and
429
+ * whatever translation already arrived over XMPP. The call is made
430
+ * directly from the browser, so a self-hosted endpoint must send
431
+ * `Access-Control-Allow-Origin` for this origin or the fetch is blocked
432
+ * with no error shown beyond the "Could not translate" retry link; if you
433
+ * can't set that on the endpoint, use `onTranslate` and proxy the call
434
+ * through your own backend instead.
435
+ */
436
+ endpoint?: string;
312
437
  };
313
438
  /**
314
439
  * Static UI i18n (interface captions like "Search...", "Type message").
@@ -322,10 +447,20 @@ export interface IConfig {
322
447
  locale?: string;
323
448
  strings?: Record<string, string>;
324
449
  };
450
+ /**
451
+ * Hide every control that mutates a room's configuration: the room avatar
452
+ * upload/remove overlay, the "Delete chat" entry in the chat-details menu,
453
+ * the add-members action and the per-member moderator menu (appoint as
454
+ * admin / remove member). Read-only room details stay visible.
455
+ */
325
456
  disableRoomConfig?: boolean;
326
457
  disableProfilesInteractions?: boolean;
458
+ /**
459
+ * Hide the member-count subtitle in the chat header ("N users", plus the
460
+ * online-users popover that hangs off it). The 1:1 online/offline line is
461
+ * a presence state, not a count, so it is unaffected.
462
+ */
327
463
  disableUserCount?: boolean;
328
- clearStoreBeforeInit?: boolean;
329
464
  disableSentLogic?: boolean;
330
465
  initBeforeLoad?: boolean;
331
466
  initBeforeLoadAuth?: {
@@ -351,7 +486,6 @@ export interface IConfig {
351
486
  enabled: boolean;
352
487
  element: any;
353
488
  };
354
- botMessageAutoScroll?: boolean;
355
489
  messageTextFilter?: {
356
490
  enabled: boolean;
357
491
  filterFunction: (text: string) => string;
@@ -394,6 +528,13 @@ export interface IConfig {
394
528
  /** `reply.questionId`, or the button's index when the bot sent none. */
395
529
  questionId: string;
396
530
  }) => void | Promise<void>;
531
+ /**
532
+ * Called when the SDK's error boundary catches an uncaught render error,
533
+ * i.e. the crash that would otherwise have taken the host app down with
534
+ * it. Wire it to your own crash reporter (Sentry, Datadog, ...). A throw
535
+ * inside this handler is caught and logged, never rethrown.
536
+ */
537
+ onError?: (event: ChatErrorInfo) => void;
397
538
  };
398
539
  disableTypingIndicator?: boolean;
399
540
  blockMessageSendingWhenProcessing?: boolean | {
@@ -0,0 +1,20 @@
1
+ export type ApiFile = {
2
+ _id: string;
3
+ userId: string;
4
+ ownerKey?: string;
5
+ roomName?: string;
6
+ location: string;
7
+ locationPreview?: string;
8
+ originalname: string;
9
+ filename?: string;
10
+ mimetype?: string;
11
+ size?: number;
12
+ expiresAt?: number;
13
+ isPrivate?: boolean;
14
+ duration?: string;
15
+ encrypted?: boolean;
16
+ key?: string;
17
+ iv?: string;
18
+ createdAt?: string;
19
+ updatedAt?: string;
20
+ };