@ethora/chat-component 26.9.8 → 26.10.1

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 (101) hide show
  1. package/README.md +1 -1
  2. package/dist/{ChatProfileModal-3UH0H5Ec.js → ChatProfileModal-Bf5jGPBZ.js} +44 -44
  3. package/dist/FilePreviewModal-Bwx0RZZw.js +1763 -0
  4. package/dist/{ManageDataModal-BblTuE9g.js → ManageDataModal-Dhkp0LWH.js} +17 -17
  5. package/dist/{MarkdownBody-RMEx8yW2.js → MarkdownBody-_sUhW2O8.js} +1 -1
  6. package/dist/{MessageSearchModal-CrYR2EH8.js → MessageSearchModal-rwwWrILl.js} +40 -40
  7. package/dist/{PublicChatsModal-Bi5r4mrV.js → PublicChatsModal-Byso6_vt.js} +19 -19
  8. package/dist/{SideDrawer-CDAoobvX.js → SideDrawer-CQPOf0ek.js} +35 -35
  9. package/dist/{UserProfileModal-4Frgvam-.js → UserProfileModal-Vj2kICvc.js} +10 -10
  10. package/dist/{UserSettingsModal-C-mb-9cb.js → UserSettingsModal-CioX4ZIV.js} +5 -5
  11. package/dist/{VideoCallOverlay-92dFLb9U.js → VideoCallOverlay-DodAkiam.js} +3824 -3708
  12. package/dist/{VisibilityModal-zIAV6dAP.js → VisibilityModal-CIl7rkBM.js} +11 -11
  13. package/dist/assets/icons.d.ts +2 -3
  14. package/dist/components/Files/fileCategory.d.ts +4 -2
  15. package/dist/components/MainComponents/AlbumGrid.d.ts +22 -0
  16. package/dist/components/MainComponents/AttachmentList.d.ts +6 -6
  17. package/dist/components/MainComponents/ImageUnavailable.d.ts +21 -0
  18. package/dist/components/MainComponents/MediaUploadOverlay.d.ts +31 -0
  19. package/dist/components/MainComponents/MessageContainer.d.ts +3 -0
  20. package/dist/components/MessageBubble/MediaCaption.d.ts +9 -0
  21. package/dist/components/MessageBubble/MessageAvatarPhoto.d.ts +17 -0
  22. package/dist/components/MessageBubble/MessageReaction.d.ts +6 -0
  23. package/dist/components/MessageBubble/QuickReplies.d.ts +2 -0
  24. package/dist/components/Modals/FilePreviewModal/FilePreviewModal.d.ts +8 -10
  25. package/dist/components/Modals/FilePreviewModal/ImageLightbox.d.ts +4 -0
  26. package/dist/components/Modals/FilePreviewModal/PdfView.d.ts +4 -0
  27. package/dist/components/Modals/FilePreviewModal/ViewerChrome.d.ts +64 -0
  28. package/dist/components/RoomComponents/LastMessageItem.d.ts +2 -0
  29. package/dist/components/RoomComponents/styled/LastMessageMedia.d.ts +19 -0
  30. package/dist/components/RoomComponents/styled/LastTextMessage.d.ts +2 -2
  31. package/dist/components/RoomComponents/styled/StyledRoomComponents.d.ts +10 -3
  32. package/dist/components/VideoCalls/VideoCallSession.d.ts +13 -0
  33. package/dist/components/styled/FileCard.d.ts +20 -0
  34. package/dist/components/styled/MediaSurface.d.ts +50 -0
  35. package/dist/components/styled/MessageImage.d.ts +9 -10
  36. package/dist/components/styled/SendInput.d.ts +12 -2
  37. package/dist/components/styled/StyledComponents.d.ts +69 -2
  38. package/dist/components/styled/StyledInputComponents/MediaComponents.d.ts +36 -24
  39. package/dist/components/styled/UnsupportedType.d.ts +1 -0
  40. package/dist/components/styled/VideoMessage.d.ts +11 -6
  41. package/dist/e2ee/callKey.d.ts +32 -0
  42. package/dist/e2ee/fileEnvelope.d.ts +16 -5
  43. package/dist/e2ee/omemo.d.ts +18 -0
  44. package/dist/firebase-messaging-sw.js +20 -3
  45. package/dist/{handleCopyClick-BtXos0AJ.js → handleCopyClick-DGaZhy_u.js} +1 -1
  46. package/dist/helpers/albumLayout.d.ts +53 -0
  47. package/dist/helpers/attachments.d.ts +22 -1
  48. package/dist/helpers/avatarImageCache.d.ts +9 -0
  49. package/dist/helpers/caption.d.ts +63 -0
  50. package/dist/helpers/composerFocus.d.ts +22 -0
  51. package/dist/helpers/createMainMessageForThread.d.ts +1 -1
  52. package/dist/helpers/downloadFile.d.ts +19 -0
  53. package/dist/helpers/fileKind.d.ts +17 -0
  54. package/dist/helpers/lastMessagePreview.d.ts +55 -0
  55. package/dist/helpers/mapWithConcurrency.d.ts +8 -0
  56. package/dist/helpers/mediaBox.d.ts +110 -0
  57. package/dist/helpers/mediaBubble.d.ts +39 -0
  58. package/dist/helpers/mediaLabel.d.ts +10 -0
  59. package/dist/helpers/mediaSendRetry.d.ts +25 -0
  60. package/dist/helpers/messageRuns.d.ts +10 -0
  61. package/dist/helpers/repairFilenameEncoding.d.ts +11 -0
  62. package/dist/helpers/roomImageGallery.d.ts +34 -0
  63. package/dist/helpers/safeUrl.d.ts +15 -0
  64. package/dist/helpers/secureFileUrl.d.ts +4 -1
  65. package/dist/helpers/viewerSender.d.ts +47 -0
  66. package/dist/hooks/useAvatarImage.d.ts +21 -0
  67. package/dist/hooks/useIsMobileViewport.d.ts +5 -0
  68. package/dist/hooks/useMediaProbe.d.ts +40 -0
  69. package/dist/hooks/usePaneWidthVar.d.ts +11 -0
  70. package/dist/hooks/usePdfThumbnail.d.ts +0 -1
  71. package/dist/hooks/usePrefersReducedMotion.d.ts +2 -0
  72. package/dist/hooks/useSendMessage.d.ts +4 -1
  73. package/dist/{main-BnLtessp.js → main-CUB_w8AZ.js} +17712 -14488
  74. package/dist/main.js +10 -10
  75. package/dist/networking/api-requests/auth.api.d.ts +2 -0
  76. package/dist/networking/callKeyStanza.d.ts +7 -0
  77. package/dist/networking/stanzaHandlers.d.ts +4 -2
  78. package/dist/networking/xmpp/sendDisplayedMarker.xmpp.d.ts +2 -0
  79. package/dist/networking/xmpp/sendMediaMessage.xmpp.d.ts +8 -0
  80. package/dist/networking/xmpp/sendReceivedReceipt.xmpp.d.ts +2 -0
  81. package/dist/networking/xmppClient.d.ts +5 -0
  82. package/dist/roomStore/callSlice.d.ts +6 -1
  83. package/dist/roomStore/index.d.ts +10 -0
  84. package/dist/roomStore/roomsSlice.d.ts +18 -3
  85. package/dist/styles/motion.d.ts +22 -2
  86. package/dist/types/models/attachment.model.d.ts +19 -0
  87. package/dist/types/models/config.model.d.ts +28 -2
  88. package/dist/types/models/customComponents.model.d.ts +2 -0
  89. package/dist/types/models/message.model.d.ts +9 -0
  90. package/dist/types/models/modal.model.d.ts +19 -0
  91. package/dist/types/models/room.model.d.ts +16 -0
  92. package/dist/types/models/xmpp.model.d.ts +3 -0
  93. package/dist/utils/messageNotificationContent.d.ts +34 -0
  94. package/dist/version.d.ts +1 -1
  95. package/package.json +1 -1
  96. package/public/firebase-messaging-sw.js +20 -3
  97. package/dist/FilePreviewModal-Cgj1iHry.js +0 -743
  98. package/dist/components/RoomComponents/styled/LastAudioMessage.d.ts +0 -6
  99. package/dist/components/RoomComponents/styled/LastMessageFile.d.ts +0 -6
  100. package/dist/components/RoomComponents/styled/LastMessagePhoto.d.ts +0 -6
  101. package/dist/components/RoomComponents/styled/LastMessageVideo.d.ts +0 -6
package/dist/main.js CHANGED
@@ -1,4 +1,4 @@
1
- import { bh as a, bi as r, bj as b, bk as o, bl as t, bm as u, bn as i, bo as p, bp as h, bq as n, br as l, bs as d, bt as m, L as y, bu as T, bv as f, bw as I, bx as R, by as g, bz as c, bA as C, bB as A, bC as E, al as k, am as Q, bD as v, bE as F } from "./main-BnLtessp.js";
1
+ import { br as a, bs as r, bt as b, bu as o, bv as t, bw as u, bx as i, by as p, bz as h, bA as n, bB as d, i as l, bC as y, S as I, bD as T, bE as f, bF as m, bG as R, bH as g, bI as c, bJ as C, bK as A, bL as E, aq as F, ar as M, bM as Q, bN as k } from "./main-CUB_w8AZ.js";
2
2
  export {
3
3
  a as AuthTestIds,
4
4
  r as Chat,
@@ -10,21 +10,21 @@ export {
10
10
  p as applyTypography,
11
11
  h as clearTypography,
12
12
  n as formatFingerprint,
13
- l as handleQRChatId,
14
- d as isE2eeEnabled,
15
- m as isRefreshFatalError,
16
- y as logoutService,
13
+ d as handleQRChatId,
14
+ l as isE2eeEnabled,
15
+ y as isRefreshFatalError,
16
+ I as logoutService,
17
17
  T as omemo,
18
18
  f as parseQuickReplies,
19
- I as refreshAuthTokens,
19
+ m as refreshAuthTokens,
20
20
  R as refreshAuthTokensQuietly,
21
21
  g as resendMessage,
22
22
  c as useInAppNotifications,
23
23
  C as useIsUserOnline,
24
24
  A as usePushNotifications,
25
25
  E as useQRCodeChat,
26
- k as useRoomMute,
27
- Q as useRoomPresence,
28
- v as useTypography,
29
- F as useUnread
26
+ F as useRoomMute,
27
+ M as useRoomPresence,
28
+ Q as useTypography,
29
+ k as useUnread
30
30
  };
@@ -27,6 +27,8 @@ export interface UploadFileOptions {
27
27
  * BEFORE the secure/legacy split so both routes carry it.
28
28
  */
29
29
  clientEncrypted?: boolean;
30
+ /** Upload progress for this request, 0 to 100. */
31
+ onProgress?: (percent: number) => void;
30
32
  }
31
33
  export declare function uploadFile(formData: FormData, activeRoomJID: string, options?: UploadFileOptions): Promise<import('axios').AxiosResponse<any, any, {}>>;
32
34
  export declare function ensureUserFromMy(user: User | null | undefined): Promise<User | null>;
@@ -0,0 +1,7 @@
1
+ import { Element } from 'ltx';
2
+ /**
3
+ * Claims a decrypted <call-key> signal, feeding the media key into the call
4
+ * slice. Returns true when it handled the stanza (so the caller stops
5
+ * dispatching it to the chat handlers).
6
+ */
7
+ export declare function onCallKeyStanza(stanza: Element): boolean;
@@ -4,9 +4,11 @@ import { default as XmppClient } from './xmppClient';
4
4
  declare const onRealtimeMessage: (stanza: Element, xmppClient?: XmppClient) => Promise<IMessage>;
5
5
  declare const onReactionMessage: (stanza: Element) => Promise<void>;
6
6
  declare const onDeleteMessage: (stanza: Element) => Promise<void>;
7
+ declare const onReceivedReceipt: (stanza: Element) => Promise<void>;
8
+ declare const onDisplayedMarker: (stanza: Element) => Promise<void>;
7
9
  declare const onEditMessage: (stanza: Element) => Promise<void>;
8
10
  declare const onReactionHistory: (stanza: any) => Promise<void>;
9
- declare const onMessageHistory: (stanza: any) => Promise<void>;
11
+ declare const onMessageHistory: (stanza: any, xmppClient?: XmppClient) => Promise<void>;
10
12
  declare const handleComposing: (stanza: Element, currentUser: string) => Promise<void>;
11
13
  declare const onPresenceInRoom: (stanza: Element | any) => void;
12
14
  declare const onChatInvite: (stanza: Element, client: XmppClient) => Promise<void>;
@@ -31,4 +33,4 @@ export type XMPPErrorInfo = {
31
33
  export declare const handleErrorMessageStanza: (stanza: Element) => XMPPErrorInfo | null;
32
34
  declare const onUserUpdate: (stanza: Element, xmppWs?: XmppClient) => Promise<void>;
33
35
  declare const onChatUpdate: (stanza: Element, xmppWs?: XmppClient) => Promise<void>;
34
- export { onRealtimeMessage, onMessageHistory, onPresenceInRoom, onReactionHistory, onGetLastMessageArchive, handleComposing, onGetChatRooms, onNewRoomCreated, onGetMembers, onGetRoomInfo, onDeleteMessage, onEditMessage, onReactionMessage, onChatInvite, onRoomKicked, onRoomMembershipChange, onMembersRefreshSignal, onMessageError, onUserUpdate, onChatUpdate, };
36
+ export { onRealtimeMessage, onMessageHistory, onPresenceInRoom, onReactionHistory, onGetLastMessageArchive, handleComposing, onGetChatRooms, onNewRoomCreated, onGetMembers, onGetRoomInfo, onDeleteMessage, onEditMessage, onReactionMessage, onReceivedReceipt, onDisplayedMarker, onChatInvite, onRoomKicked, onRoomMembershipChange, onMembersRefreshSignal, onMessageError, onUserUpdate, onChatUpdate, };
@@ -0,0 +1,2 @@
1
+ import { Client } from '@xmpp/client';
2
+ export declare function sendDisplayedMarker(client: Client, messageId: string, roomJid: string): void;
@@ -1,4 +1,5 @@
1
1
  import { Client } from '@xmpp/client';
2
+ import { IMentionSpan } from '../../types/types';
2
3
  /**
3
4
  * What <body> carries in an e2ee room: the key material for each sealed
4
5
  * attachment, in the same order as `attachments` / the flat fields on <data>.
@@ -9,5 +10,12 @@ import { Client } from '@xmpp/client';
9
10
  export interface E2eeMediaBody {
10
11
  v: 1;
11
12
  keys: string[];
13
+ /**
14
+ * The text typed with the files. It lives here, inside the envelope, and
15
+ * nowhere on <data>, so an e2ee caption is never readable in transit.
16
+ * Older readers only look at `keys` and ignore the rest.
17
+ */
18
+ caption?: string;
19
+ captionMentions?: IMentionSpan[];
12
20
  }
13
21
  export declare function sendMediaMessage(client: Client, roomJID: string, data: any, id: string): Promise<void>;
@@ -0,0 +1,2 @@
1
+ import { Client } from '@xmpp/client';
2
+ export declare function sendReceivedReceipt(client: Client, messageId: string, roomJid: string): void;
@@ -64,6 +64,8 @@ export declare class XmppClient implements XmppClientInterface {
64
64
  private historyQueueWorkerScheduled;
65
65
  private historyQueueWorkerTimer;
66
66
  private roomHistoryEpoch;
67
+ private historyReceiptAckedIds;
68
+ private readonly maxHistoryReceiptAckedIds;
67
69
  private criticalSendUntil;
68
70
  private activeRoomBoostUntil;
69
71
  private sendStartedAtById;
@@ -313,6 +315,9 @@ export declare class XmppClient implements XmppClientInterface {
313
315
  deleteMessageStanza(room: string, msgId: string): void;
314
316
  editMessageStanza(room: string, msgId: string, text: string): void;
315
317
  sendMessageReactionStanza(messageId: string, roomJid: string, reactionsList: string[], reactionSymbol?: any): void;
318
+ sendReceivedReceiptStanza(messageId: string, roomJid: string): void;
319
+ sendDisplayedMarkerStanza(messageId: string, roomJid: string): void;
320
+ sendHistoryReceivedReceiptStanza(roomJid: string, messageId: string): void;
316
321
  sendTypingRequestStanza(chatId: string, fullName: string, start: boolean): void;
317
322
  getChatsPrivateStoreRequestStanza: () => Promise<Record<string, string | number> | null>;
318
323
  actionSetTimestampToPrivateStoreStanza(chatId: string, timestamp: number, chats?: string[]): Promise<void>;
@@ -11,6 +11,8 @@ export interface CallState {
11
11
  callId: string | null;
12
12
  peerXmppUsername: string | null;
13
13
  token: string | null;
14
+ e2eeKey: string | null;
15
+ e2eeKeyCallId: string | null;
14
16
  error: string | null;
15
17
  startedAt: number | null;
16
18
  connectedAt: number | null;
@@ -33,6 +35,9 @@ export declare const startOutgoingCall: import('@reduxjs/toolkit').ActionCreator
33
35
  roomJid: string;
34
36
  token: string;
35
37
  callId?: string | null;
36
- }, "call/setOutgoingCallToken">, acceptIncomingCall: import('@reduxjs/toolkit').ActionCreatorWithoutPayload<"call/acceptIncomingCall">, declineIncomingCall: import('@reduxjs/toolkit').ActionCreatorWithoutPayload<"call/declineIncomingCall">, endCall: import('@reduxjs/toolkit').ActionCreatorWithoutPayload<"call/endCall">, setCallError: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<string, "call/setCallError">, setCallPhase: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<CallPhase, "call/setCallPhase">, setCallKind: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<CallKind, "call/setCallKind">, resetCall: import('@reduxjs/toolkit').ActionCreatorWithoutPayload<"call/resetCall">;
38
+ }, "call/setOutgoingCallToken">, acceptIncomingCall: import('@reduxjs/toolkit').ActionCreatorWithoutPayload<"call/acceptIncomingCall">, declineIncomingCall: import('@reduxjs/toolkit').ActionCreatorWithoutPayload<"call/declineIncomingCall">, endCall: import('@reduxjs/toolkit').ActionCreatorWithoutPayload<"call/endCall">, setCallError: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<string, "call/setCallError">, setCallPhase: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<CallPhase, "call/setCallPhase">, setCallKind: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<CallKind, "call/setCallKind">, setCallE2eeKey: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
39
+ key: string;
40
+ callId: string | null;
41
+ }, "call/setCallE2eeKey">, resetCall: import('@reduxjs/toolkit').ActionCreatorWithoutPayload<"call/resetCall">;
37
42
  declare const _default: import('redux').Reducer<import('immer').WritableDraft<CallState>>;
38
43
  export default _default;
@@ -44,6 +44,16 @@ export declare const restoreUsersSetForRehydrate: (usersSet: Record<string, any>
44
44
  * /chats/my response re-seeds it.
45
45
  */
46
46
  export declare const resetSessionRoomState: (roomsMap: Record<string, IRoom>) => Record<string, IRoom>;
47
+ /**
48
+ * A transcript cached by a build that did not persist reactions repaints every
49
+ * bubble without its reaction chips, and the archive will not re-deliver them
50
+ * for the cached middle (the latest page is merged onto the cache and the
51
+ * backfill resumes from the cache's oldest message). Such a cache is dropped
52
+ * once - the room shell stays, the transcript is refetched from MAM - exactly
53
+ * like the cache-budget eviction, except the paging cursors go with it: a
54
+ * stale first/last id would make the backfill start behind the dropped range.
55
+ */
56
+ export declare const dropReactionlessCaches: (roomsMap: Record<string, IRoom>) => Record<string, IRoom>;
47
57
  export declare const limitMessagesTransform: import('redux-persist').Transform<any, any, Record<string, any>, Record<string, any>>;
48
58
  export declare const scrubSensitiveChatStateTransform: import('redux-persist').Transform<any, any, Record<string, any>, Record<string, any>>;
49
59
  export declare const sanitizeRoomsStateTransform: import('redux-persist').Transform<any, any, Record<string, any>, Record<string, any>>;
@@ -1,4 +1,4 @@
1
- import { AddRoomMessageAction, ApiRoom, EditAction, IMessage, IRoom, ReactionAction, RoomMember, Translation } from '../types/types';
1
+ import { AddRoomMessageAction, ApiRoom, EditAction, IAttachment, IMessage, IRoom, ReactionAction, RoomMember, Translation } from '../types/types';
2
2
  import { default as XmppClient } from '../networking/xmppClient';
3
3
  /**
4
4
  * Draft caps. Drafts ride along in the persisted rooms slice, so they have
@@ -129,7 +129,13 @@ export declare const addRoom: import('@reduxjs/toolkit').ActionCreatorWithOption
129
129
  }, "roomMessages/setMessageSendFailed">, setMessageSendRetrying: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
130
130
  roomJID: string;
131
131
  messageId: string;
132
- }, "roomMessages/setMessageSendRetrying">, editRoomMessage: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
132
+ }, "roomMessages/setMessageSendRetrying">, setMediaUploadState: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
133
+ roomJID: string;
134
+ messageId: string;
135
+ attachments?: IAttachment[];
136
+ pending?: boolean;
137
+ failed?: boolean;
138
+ }, "roomMessages/setMediaUploadState">, editRoomMessage: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
133
139
  roomJID: string;
134
140
  messageId: string;
135
141
  text: string;
@@ -182,7 +188,16 @@ export declare const addRoom: import('@reduxjs/toolkit').ActionCreatorWithOption
182
188
  }, "roomMessages/setMemberOffline">, setChatUiVisible: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<boolean, "roomMessages/setChatUiVisible">, setRoomRole: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
183
189
  chatJID: string;
184
190
  role: string;
185
- }, "roomMessages/setRoomRole">, setReactions: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<ReactionAction, "roomMessages/setReactions">, setLogoutState: import('@reduxjs/toolkit').ActionCreatorWithoutPayload<"roomMessages/setLogoutState">, setActiveMessage: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
191
+ }, "roomMessages/setRoomRole">, setReactions: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<ReactionAction, "roomMessages/setReactions">, setReceivedReceipt: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
192
+ roomJID: string;
193
+ messageId: string;
194
+ fromXmppUsername: string;
195
+ timestamp: number;
196
+ }, "roomMessages/setReceivedReceipt">, setDisplayedMarker: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
197
+ roomJID: string;
198
+ xmppUsername: string;
199
+ messageId: string;
200
+ }, "roomMessages/setDisplayedMarker">, setLogoutState: import('@reduxjs/toolkit').ActionCreatorWithoutPayload<"roomMessages/setLogoutState">, setActiveMessage: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
186
201
  id: string;
187
202
  chatJID: string;
188
203
  }, "roomMessages/setActiveMessage">, setCloseActiveMessage: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
@@ -20,7 +20,17 @@ export declare const MOTION_BASE_MS = 220;
20
20
  export declare const fadeIn: import('styled-components/dist/models/Keyframes').default;
21
21
  export declare const fadeInUp: import('styled-components/dist/models/Keyframes').default;
22
22
  export declare const scaleIn: import('styled-components/dist/models/Keyframes').default;
23
- export declare const shimmer: import('styled-components/dist/models/Keyframes').default;
23
+ /** Skeleton timing: one sweep cycle, and the delay before anything shows. */
24
+ export declare const SHIMMER_SWEEP_MS = 2000;
25
+ export declare const SHIMMER_DELAY_MS = 120;
26
+ export declare const SHIMMER_FADE_IN_MS = 240;
27
+ /**
28
+ * The loading band drifts left to right (reading direction). Keyframes move
29
+ * the band's own transform, not background-position, so the browser can run
30
+ * it on the compositor. The band is 60% of the host wide, so -100% parks it
31
+ * just off the left edge and 166.67% just off the right edge.
32
+ */
33
+ export declare const shimmerSweep: import('styled-components/dist/models/Keyframes').default;
24
34
  /**
25
35
  * Right-hand side drawer entrance. Travels further than `fadeInUp` (a panel
26
36
  * sliding in from the screen edge reads as motion only if it actually
@@ -47,7 +57,17 @@ export declare const fadeInAnimation: import('styled-components').RuleSet<object
47
57
  export declare const scaleInAnimation: import('styled-components').RuleSet<object>;
48
58
  /** Convenience: side-drawer slide-in shorthand, respecting reduced motion. */
49
59
  export declare const slideInRightAnimation: import('styled-components').RuleSet<object>;
50
- /** Shimmering skeleton background, respecting reduced motion (stays static). */
60
+ /**
61
+ * Loading skeleton: a calm, slightly tinted base with one soft band sweeping
62
+ * left to right every 2s. The band fades in with its own transparent edges,
63
+ * so there is no hard leading or trailing line. Nothing shows for the first
64
+ * 120ms, so an image that arrives quickly never flashes a placeholder.
65
+ *
66
+ * The host must be positioned (`position: relative` or `absolute`, set by the
67
+ * host itself so this helper cannot override it); the band is an ::after
68
+ * clipped by the host's `overflow: hidden`. Reduced motion keeps only the
69
+ * static base.
70
+ */
51
71
  export declare const shimmerBackground: import('styled-components').RuleSet<object>;
52
72
  export declare const fadeOut: import('styled-components/dist/models/Keyframes').default;
53
73
  export declare const scaleOut: import('styled-components/dist/models/Keyframes').default;
@@ -23,6 +23,9 @@ export interface IAttachment {
23
23
  /** Bytes, as a string - that is how it travels on the wire. */
24
24
  size?: string;
25
25
  duration?: string;
26
+ /** Natural pixel size, when the storage recorded it. Sizes the solo preview box. */
27
+ width?: number;
28
+ height?: number;
26
29
  ownerKey?: string;
27
30
  userId?: string;
28
31
  createdAt?: string;
@@ -30,4 +33,20 @@ export interface IAttachment {
30
33
  expiresAt?: string;
31
34
  isVisible?: boolean;
32
35
  isPrivate?: boolean;
36
+ /**
37
+ * Local-only state of an outgoing attachment, set by the sender while its
38
+ * message is still being sent. Never written to the wire.
39
+ * uploading - the file is going up (or every file is up and the message
40
+ * is waiting on its siblings); `uploadProgress` is 0 to 100.
41
+ * failed - this file did not upload; the message is held until it
42
+ * is retried.
43
+ */
44
+ uploadStatus?: 'uploading' | 'failed';
45
+ uploadProgress?: number;
46
+ /**
47
+ * Local-only `blob:` URL of the sender's own picture, so an outgoing image
48
+ * shows at once and stays on screen while the server copy loads. Never
49
+ * written to the wire.
50
+ */
51
+ localUrl?: string;
33
52
  }
@@ -45,6 +45,32 @@ export interface VideoCallsConfig {
45
45
  showScreenShare?: boolean;
46
46
  /** Custom icons for the call control bar / ring screen. See VideoCallIcons. */
47
47
  icons?: VideoCallIcons;
48
+ /**
49
+ * End-to-end encrypt the call media (LiveKit insertable streams). The media
50
+ * key is exchanged over OMEMO - the same channel the chat uses - so the SFU
51
+ * and the server never see it, which makes the call private even from the
52
+ * operator. The trust model is OMEMO's (blind-trust-before-verification on
53
+ * device fingerprints).
54
+ *
55
+ * Requires the chat's own encryption to be on (`config.e2ee.enabled`) and a
56
+ * room the backend marked `e2ee`. A call that cannot set E2EE up - an
57
+ * unsupported browser, an insecure (non-HTTPS) context, or a peer with no
58
+ * published OMEMO device - falls back to a normal (transport-encrypted)
59
+ * call rather than failing.
60
+ */
61
+ e2ee?: {
62
+ enabled: boolean;
63
+ /**
64
+ * Supplies the Web Worker LiveKit needs for insertable-stream E2EE. The
65
+ * library stays bundler-agnostic, so the host app creates it, typically:
66
+ * () => new Worker(
67
+ * new URL('livekit-client/e2ee-worker', import.meta.url),
68
+ * { type: 'module' }
69
+ * )
70
+ * Without a factory, call E2EE stays off.
71
+ */
72
+ workerFactory?: () => Worker;
73
+ };
48
74
  }
49
75
  export interface FBConfig {
50
76
  apiKey: string;
@@ -155,8 +181,8 @@ export interface IConfig {
155
181
  /** Composer attachment limits. Applies to the built-in SendInput. */
156
182
  attachments?: {
157
183
  /**
158
- * How many files one message may carry. Defaults to 5 and is hard-capped
159
- * at 10 by the stanza payload (MAX_ATTACHMENTS_PER_MESSAGE).
184
+ * How many files one message may carry. Defaults to 10, which is also the
185
+ * hard cap set by the stanza payload (MAX_ATTACHMENTS_PER_MESSAGE).
160
186
  */
161
187
  maxFiles?: number;
162
188
  /** Per-file ceiling in MB. Omit to skip the client-side size check. */
@@ -11,6 +11,8 @@ export interface NewMessageLabelProps {
11
11
  export interface DecoratedMessage {
12
12
  message: IMessage;
13
13
  showDateLabel: boolean;
14
+ /** False when the next row is the same sender's message (a run continues). */
15
+ isLastInGroup?: boolean;
14
16
  }
15
17
  export interface ScrollControllerApi {
16
18
  scrollToBottom: () => void;
@@ -55,6 +55,7 @@ export interface IMessage {
55
55
  mainMessage?: string;
56
56
  reply?: IReply[];
57
57
  reaction?: Record<string, ReactionMessage>;
58
+ receivedBy?: Record<string, number>;
58
59
  fileName?: string;
59
60
  translations?: TranslationObject;
60
61
  langSource?: Iso639_1Codes;
@@ -71,6 +72,8 @@ export interface IMessage {
71
72
  xmppFrom?: string;
72
73
  quickReplies?: unknown;
73
74
  mentions?: IMentionSpan[];
75
+ caption?: string;
76
+ captionMentions?: IMentionSpan[];
74
77
  callLog?: {
75
78
  callId: string;
76
79
  direction: 'outgoing' | 'incoming';
@@ -95,6 +98,12 @@ export interface MessageProps {
95
98
  message: IMessage;
96
99
  isUser: boolean;
97
100
  isReply: boolean;
101
+ /**
102
+ * False when the next message comes from the same sender (a run). The last
103
+ * message of a run carries the bubble tail and the larger gap below it.
104
+ * Omitted means last.
105
+ */
106
+ isLastInGroup?: boolean;
98
107
  }
99
108
  export interface MediaMessageType {
100
109
  }
@@ -4,4 +4,23 @@ export interface ModalFile {
4
4
  fileName: string;
5
5
  fileURL: string;
6
6
  mimetype: string;
7
+ /** Optional low-resolution copy, used for viewer thumbnails. */
8
+ previewURL?: string;
9
+ /** Optional message timestamp (ISO) shown in the viewer header. */
10
+ date?: string;
11
+ /** Optional sender shown in the viewer header. */
12
+ sender?: {
13
+ id?: string;
14
+ username?: string;
15
+ name?: string;
16
+ firstName?: string;
17
+ lastName?: string;
18
+ photo?: string;
19
+ };
20
+ /**
21
+ * Optional sibling files (e.g. a whole album) the viewer can page through
22
+ * when the room's loaded messages do not already contain this file.
23
+ * Single-file callers leave it out.
24
+ */
25
+ siblings?: ModalFile[];
7
26
  }
@@ -62,6 +62,7 @@ export interface IRoom {
62
62
  composingList?: string[];
63
63
  lastViewedTimestamp?: number;
64
64
  unreadBaselineTimestamp?: number;
65
+ lastDisplayedByUser?: Record<string, string>;
65
66
  unreadMessages?: number;
66
67
  /**
67
68
  * Unread count the server reported for this room in `GET /v1/chats/my`
@@ -80,6 +81,8 @@ export interface IRoom {
80
81
  firstMessageTimestamp?: number;
81
82
  };
82
83
  historyComplete?: boolean;
84
+ /** Persisted-cache stamp: the saved transcript includes its reactions. */
85
+ reactionsCached?: boolean;
83
86
  historyPreloadState?: 'idle' | 'loading' | 'partial' | 'done' | 'error';
84
87
  unreadCapped?: boolean;
85
88
  /**
@@ -109,6 +112,19 @@ export interface ApiRoomLastMessage {
109
112
  isOwn?: boolean;
110
113
  senderFirstName?: string;
111
114
  senderLastName?: string;
115
+ /**
116
+ * Media fields, present when the last message is a file. Any subset may be
117
+ * sent: a thin payload can carry just `mimetype` and `fileName`.
118
+ */
119
+ isMediafile?: string | boolean;
120
+ mimetype?: string;
121
+ fileName?: string;
122
+ originalName?: string;
123
+ location?: string;
124
+ locationPreview?: string;
125
+ duration?: string | number;
126
+ attachments?: unknown;
127
+ clientEncrypted?: string | boolean;
112
128
  }
113
129
  export interface ApiRoom {
114
130
  name: string;
@@ -104,6 +104,9 @@ export interface XmppClientInterface {
104
104
  sendMessage(roomJID: string, firstName: string, lastName: string, photo: string, walletAddress: string, userMessage: string, notDisplayedValue?: string, isReply?: boolean, showInChannel?: boolean, mainMessage?: string, customId?: string): void;
105
105
  deleteMessageStanza(room: string, msgId: string): void;
106
106
  editMessageStanza(room: string, msgId: string, text: string): void;
107
+ sendReceivedReceiptStanza(messageId: string, roomJid: string): void;
108
+ sendDisplayedMarkerStanza(messageId: string, roomJid: string): void;
109
+ sendHistoryReceivedReceiptStanza(roomJid: string, messageId: string): void;
107
110
  sendTypingRequestStanza(chatId: string, fullName: string, start: boolean): void;
108
111
  getChatsPrivateStoreRequestStanza(): Promise<any>;
109
112
  actionSetTimestampToPrivateStoreStanza(chatId: string, timestamp: number, chats?: string[]): Promise<void>;
@@ -0,0 +1,34 @@
1
+ import { IMessage } from '../types/models/message.model';
2
+ import { IRoom, RoomMember } from '../types/models/room.model';
3
+ export declare const MAX_NOTIFICATION_BODY_LENGTH = 120;
4
+ export interface NotificationContent {
5
+ title: string;
6
+ body: string;
7
+ /** One per room, so a burst in the same room collapses into one entry. */
8
+ tag: string;
9
+ roomJID: string;
10
+ messageId: string;
11
+ }
12
+ export interface NotificationContentInput {
13
+ message: IMessage;
14
+ roomJID: string;
15
+ room?: IRoom | null;
16
+ usersSet?: Record<string, RoomMember>;
17
+ currentXmppUsername?: string;
18
+ activeRoomJID?: string | null;
19
+ tabVisible: boolean;
20
+ locale?: string | null;
21
+ strings?: Record<string, string> | null;
22
+ /** Names the caller already has (toast args); never trusted blindly. */
23
+ senderNameHint?: string;
24
+ roomNameHint?: string;
25
+ }
26
+ export declare const resolveNotificationSenderName: (message: IMessage, usersSet?: Record<string, RoomMember>, room?: IRoom | null, hint?: string) => string;
27
+ export declare const resolveNotificationRoomName: (roomJID: string, room?: IRoom | null, hint?: string) => string;
28
+ export declare const getNotificationTag: (roomJID: string) => string;
29
+ /**
30
+ * Returns what to show, or null when this message must not raise an alert:
31
+ * own messages, system/receipt/typing stanzas (nothing to say), muted rooms,
32
+ * and the open room while the tab is visible.
33
+ */
34
+ export declare const buildNotificationContent: (input: NotificationContentInput) => NotificationContent | null;
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const ETHORA_CHAT_COMPONENT_VERSION = "26.9.8";
1
+ export declare const ETHORA_CHAT_COMPONENT_VERSION = "26.10.1";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ethora/chat-component",
3
- "version": "26.9.8",
3
+ "version": "26.10.1",
4
4
  "type": "module",
5
5
  "main": "dist/main.js",
6
6
  "types": "dist/main.d.ts",
@@ -151,8 +151,25 @@ async function checkAssetAvailability(url) {
151
151
  }
152
152
  }
153
153
 
154
+ // `<appId>_<userId>` (24-hex ids, or an appId and a uuid) is a machine id,
155
+ // never something to put in a notification title.
156
+ const OPAQUE_ID_RE =
157
+ /^(?:[0-9a-f]{24}(?:_[0-9a-f]{24})?|[a-z0-9]+_(?:[0-9a-f]{24}|[0-9a-f-]{36})(?:_[0-9a-z-]+)*)$/i;
158
+
159
+ function isOpaqueId(value) {
160
+ const text = String(value || '').trim();
161
+ return !!text && OPAQUE_ID_RE.test(text.split('@')[0]);
162
+ }
163
+
154
164
  function pickTitle(raw = {}, data = {}, notification = {}) {
155
- return notification.title || raw.title || data.title || 'New Message';
165
+ const title = notification.title || raw.title || data.title || '';
166
+ return title && !isOpaqueId(title) ? title : 'New Message';
167
+ }
168
+
169
+ // One notification per chat: a newer message in the same room replaces it.
170
+ function roomTag(data = {}) {
171
+ const jid = String(data.jid || '').split('@')[0];
172
+ return jid ? `ethora-room-${jid}` : '';
156
173
  }
157
174
 
158
175
  function pickBody(raw = {}, data = {}, notification = {}) {
@@ -449,8 +466,8 @@ async function handlePush(raw = {}, source = 'push') {
449
466
  icon,
450
467
  badge,
451
468
  image,
452
- tag: isCall ? `call:${callId || dedupKey}` : dedupKey,
453
- renotify: isCall,
469
+ tag: isCall ? `call:${callId || dedupKey}` : roomTag(data) || dedupKey,
470
+ renotify: isCall || !!roomTag(data),
454
471
  requireInteraction: isCall,
455
472
  // Spread the ORIGINAL payload first so the click handler's isCallPush()
456
473
  // sees the full call fields (callId/callToken/kind/room); our routing