@ethora/chat-component 26.10.7 → 26.10.8

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 (74) hide show
  1. package/README.md +88 -3
  2. package/dist/ChatProfileModal-83t5BRkI.js +2298 -0
  3. package/dist/{FilePreviewModal-DlOjnYzv.js → FilePreviewModal-DxUGjSHe.js} +130 -130
  4. package/dist/{ManageDataModal-BZLGJZze.js → ManageDataModal-DFE_Wegj.js} +14 -14
  5. package/dist/{MarkdownBody-CtArwCDa.js → MarkdownBody-uFITrPjT.js} +1 -1
  6. package/dist/{MessageSearchModal-CcUnfZlU.js → MessageSearchModal-CsbTWgNm.js} +32 -32
  7. package/dist/{PublicChatsModal-BWn0NXVo.js → PublicChatsModal-bDOp3IL9.js} +19 -19
  8. package/dist/{SideDrawer-CqZl9sxK.js → SideDrawer-A_1ECqD2.js} +28 -28
  9. package/dist/{UserProfileModal-D0JZ3-uy.js → UserProfileModal-Ca5wHYvr.js} +124 -120
  10. package/dist/{UserSettingsModal-OvykYDsT.js → UserSettingsModal-CAyyQGV6.js} +2 -2
  11. package/dist/{VideoCallOverlay-DrvO2v_g.js → VideoCallOverlay-BMiQh8kI.js} +5908 -4168
  12. package/dist/{VisibilityModal-pOF9-UXR.js → VisibilityModal-BXyIy7_J.js} +7 -7
  13. package/dist/assets/icons.d.ts +4 -0
  14. package/dist/calls/callDisplay.d.ts +22 -0
  15. package/dist/calls/callMode.d.ts +105 -0
  16. package/dist/calls/openScheduledCall.d.ts +20 -0
  17. package/dist/calls/p2p/P2PCallController.d.ts +160 -0
  18. package/dist/calls/p2p/icePolicy.d.ts +36 -0
  19. package/dist/calls/p2p/negotiator.d.ts +56 -0
  20. package/dist/calls/p2p/p2pRuntime.d.ts +35 -0
  21. package/dist/calls/p2p/signalInbox.d.ts +34 -0
  22. package/dist/calls/p2p/signalStanza.d.ts +51 -0
  23. package/dist/calls/placeCall.d.ts +20 -0
  24. package/dist/calls/scheduledCall.d.ts +101 -0
  25. package/dist/calls/scheduledCallActions.d.ts +12 -0
  26. package/dist/calls/scheduledCallFormat.d.ts +13 -0
  27. package/dist/calls/scheduledCallsConfig.d.ts +27 -0
  28. package/dist/calls/senderTrust.d.ts +7 -0
  29. package/dist/calls/startCall.d.ts +16 -0
  30. package/dist/components/MainComponents/ChatWrapper.d.ts +1 -0
  31. package/dist/components/MainComponents/LoginWrapper.d.ts +1 -0
  32. package/dist/components/MainComponents/ProfileImagePlaceholder.d.ts +1 -0
  33. package/dist/components/MainComponents/ReduxWrapper.d.ts +6 -0
  34. package/dist/components/MenuRoom/MenuRoom.d.ts +1 -0
  35. package/dist/components/Modals/NewChatModal/styledNewChatComponents.d.ts +27 -0
  36. package/dist/components/Modals/styledModalComponents.d.ts +21 -0
  37. package/dist/components/ScheduledCalls/ScheduleCallModal.d.ts +3 -0
  38. package/dist/components/ScheduledCalls/ScheduledCallCancelConfirm.d.ts +8 -0
  39. package/dist/components/ScheduledCalls/ScheduledCallCard.d.ts +7 -0
  40. package/dist/components/ScheduledCalls/ScheduledCallLayer.d.ts +15 -0
  41. package/dist/components/ScheduledCalls/ScheduledCallPreJoin.d.ts +3 -0
  42. package/dist/components/VideoCalls/P2PCallRunner.d.ts +1 -0
  43. package/dist/components/VideoCalls/P2PCallSession.d.ts +14 -0
  44. package/dist/components/VideoCalls/VideoCallSession.d.ts +37 -0
  45. package/dist/{handleCopyClick-BUbVvd2G.js → handleCopyClick-BX1fcFGN.js} +1 -1
  46. package/dist/helpers/callPush.d.ts +2 -0
  47. package/dist/helpers/canLeaveRoom.d.ts +3 -0
  48. package/dist/helpers/chatComponentConfigEvents.d.ts +16 -0
  49. package/dist/helpers/liveConfig.d.ts +46 -0
  50. package/dist/helpers/reportRoom.d.ts +18 -0
  51. package/dist/helpers/scheduledCallMessage.d.ts +23 -0
  52. package/dist/helpers/storeConsole.d.ts +1 -0
  53. package/dist/hooks/useNow.d.ts +6 -0
  54. package/dist/hooks/useRemoteAppearance.d.ts +21 -0
  55. package/dist/hooks/useTypography.d.ts +4 -0
  56. package/dist/{main-CpHMnydn.js → main-B25iFKAC.js} +15794 -13324
  57. package/dist/main.d.ts +3 -1
  58. package/dist/main.js +28 -26
  59. package/dist/networking/api-requests/callState.api.d.ts +24 -0
  60. package/dist/networking/api-requests/remoteAppearance.api.d.ts +20 -0
  61. package/dist/networking/api-requests/rooms.api.d.ts +2 -1
  62. package/dist/networking/api-requests/scheduledCalls.api.d.ts +19 -0
  63. package/dist/networking/ethoraEvents.d.ts +14 -1
  64. package/dist/networking/xmpp/notifyXmppEvent.d.ts +6 -0
  65. package/dist/roomStore/callSlice.d.ts +23 -1
  66. package/dist/roomStore/chatSettingsSlice.d.ts +9 -1
  67. package/dist/roomStore/index.d.ts +4 -0
  68. package/dist/roomStore/scheduledCallSlice.d.ts +47 -0
  69. package/dist/types/models/config.model.d.ts +71 -1
  70. package/dist/types/models/message.model.d.ts +2 -0
  71. package/dist/types/models/room.model.d.ts +2 -0
  72. package/dist/types/models/scheduledCall.model.d.ts +116 -0
  73. package/package.json +1 -1
  74. package/dist/ChatProfileModal-BDZvd0rm.js +0 -2168
package/dist/main.d.ts CHANGED
@@ -14,6 +14,7 @@ export { useRoomMute } from './hooks/useRoomMute';
14
14
  export type { UseRoomMuteResult } from './hooks/useRoomMute';
15
15
  export { parseQuickReplies } from './helpers/quickReplies';
16
16
  export type { QuickReply } from './helpers/quickReplies';
17
+ export { openScheduledCall, parseScheduledCallLink } from './calls/openScheduledCall';
17
18
  export { applyTypography, clearTypography } from './helpers/applyTypography';
18
19
  export { useTypography } from './hooks/useTypography';
19
20
  export type { TypographyConfig, FontFaceSource, } from './types/models/config.model';
@@ -22,7 +23,8 @@ export { ChatInputTestIds, MessageBubbleTestIds, RoomListTestIds, AuthTestIds, }
22
23
  export { refreshAuthTokens, refreshAuthTokensQuietly, RefreshFatalError, isRefreshFatalError, } from './networking/authRefresh';
23
24
  export type { RefreshErrorCode, RefreshResult, RefreshOptions, } from './networking/authRefresh';
24
25
  export type { ChatWrapperProps as ChatProps } from './components/MainComponents/ReduxWrapper';
25
- export type { IConfig } from './types/models/config.model';
26
+ export type { IConfig, XmppMessageEvent } from './types/models/config.model';
27
+ export type { ScheduledCallsConfig, ScheduledCallOpenError, ScheduledCall, ScheduledCallKind, } from './types/models/scheduledCall.model';
26
28
  export type { VideoCallsConfig, VideoCallIcons, FBConfig, } from './types/models/config.model';
27
29
  export type { User, IUser, ConfigUser, StorageUser, } from './types/models/user.model';
28
30
  export type { IMessage, IReply, LastMessage, MessageProps, MediaMessageType, ReactionMessage, } from './types/models/message.model';
package/dist/main.js CHANGED
@@ -1,30 +1,32 @@
1
- import { bG as e, bH as r, bI as o, bJ as b, bK as t, bL as i, bM as u, bN as p, bO as h, bP as n, bQ as d, i as l, bR as T, a3 as I, bS as R, bT as f, bU as m, bV as y, bW as c, bX as g, bY as C, bZ as E, b_ as Q, aE as A, aF as F, b$ as M, c0 as P } from "./main-CpHMnydn.js";
1
+ import { bS as a, bT as r, bU as o, bV as t, bW as c, bX as u, bY as p, bZ as h, b_ as i, b$ as l, c0 as n, v as d, c1 as b, af as T, c2 as f, c3 as m, c4 as y, c5 as I, c6 as R, c7 as g, c8 as C, c9 as Q, ca as k, cb as A, cc as E, aP as P, aQ as S, cd as v, ce as F } from "./main-B25iFKAC.js";
2
2
  export {
3
- e as AuthTestIds,
3
+ a as AuthTestIds,
4
4
  r as Chat,
5
5
  o as ChatInputTestIds,
6
- b as MessageBubbleTestIds,
7
- t as RefreshFatalError,
8
- i as RoomListTestIds,
9
- u as XmppProvider,
10
- p as applyTypography,
11
- h as clearTypography,
12
- n as formatFingerprint,
13
- d as handleQRChatId,
14
- l as isE2eeEnabled,
15
- T as isRefreshFatalError,
16
- I as logoutService,
17
- R as omemo,
18
- f as parseQuickReplies,
19
- m as refreshAuthTokens,
20
- y as refreshAuthTokensQuietly,
21
- c as resendMessage,
22
- g as useInAppNotifications,
23
- C as useIsUserOnline,
24
- E as usePushNotifications,
25
- Q as useQRCodeChat,
26
- A as useRoomMute,
27
- F as useRoomPresence,
28
- M as useTypography,
29
- P as useUnread
6
+ t as MessageBubbleTestIds,
7
+ c as RefreshFatalError,
8
+ u as RoomListTestIds,
9
+ p as XmppProvider,
10
+ h as applyTypography,
11
+ i as clearTypography,
12
+ l as formatFingerprint,
13
+ n as handleQRChatId,
14
+ d as isE2eeEnabled,
15
+ b as isRefreshFatalError,
16
+ T as logoutService,
17
+ f as omemo,
18
+ m as openScheduledCall,
19
+ y as parseQuickReplies,
20
+ I as parseScheduledCallLink,
21
+ R as refreshAuthTokens,
22
+ g as refreshAuthTokensQuietly,
23
+ C as resendMessage,
24
+ Q as useInAppNotifications,
25
+ k as useIsUserOnline,
26
+ A as usePushNotifications,
27
+ E as useQRCodeChat,
28
+ P as useRoomMute,
29
+ S as useRoomPresence,
30
+ v as useTypography,
31
+ F as useUnread
30
32
  };
@@ -0,0 +1,24 @@
1
+ import { IceServerConfig } from '../../calls/callMode';
2
+ /** Callee: the incoming UI is on screen. */
3
+ export declare const reportCallRinging: (callId: string | null) => void;
4
+ /** Callee: the user rejected the call. The server relays it to the caller. */
5
+ export declare const reportCallDeclined: (callId: string | null) => void;
6
+ /** Media is flowing (ICE connected). Starts the server-side call timer. */
7
+ export declare const reportCallConnected: (callId: string | null) => void;
8
+ /**
9
+ * A ringing or connected call ended for any reason. Idempotent on the server
10
+ * (a second report returns `ended:false`), so both peers may send it.
11
+ */
12
+ export declare const reportCallEnded: (callId: string | null) => void;
13
+ export interface IceServersResponse {
14
+ enabled: boolean;
15
+ iceServers: IceServerConfig[];
16
+ ttlSeconds: number | null;
17
+ }
18
+ /**
19
+ * GET /v1/calls/ice-servers: fresh STUN/TURN credentials. Used by a callee
20
+ * whose call-token carried no `ice` list, and to refresh the (ephemeral)
21
+ * credentials before an ICE restart. Returns null on any failure so callers
22
+ * can carry on with the servers they already have.
23
+ */
24
+ export declare function fetchIceServers(): Promise<IceServersResponse | null>;
@@ -0,0 +1,20 @@
1
+ import { RemoteConfigRecord } from '../../helpers/liveConfig';
2
+ export interface RemoteAppearanceSnapshot {
3
+ record: RemoteConfigRecord;
4
+ /** `'0'` when the App never saved anything. */
5
+ version: string;
6
+ }
7
+ export declare const isValidRemoteAppId: (appId: unknown) => appId is string;
8
+ export declare const remoteAppearanceUrl: (baseUrl: string | undefined, appId: string, version?: string) => string;
9
+ /**
10
+ * Reads the App's public chat component config. Unauthenticated on purpose
11
+ * (no token, no cookies, no refresh logic): it is the same public endpoint
12
+ * the web app reads at load. Pass the `version` the server announced to get
13
+ * past the endpoint's 30 s cache. Throws on a network or HTTP failure; the
14
+ * caller keeps what it has.
15
+ */
16
+ export declare function fetchRemoteAppearance(opts: {
17
+ baseUrl?: string;
18
+ appId: string;
19
+ version?: string;
20
+ }, signal?: AbortSignal): Promise<RemoteAppearanceSnapshot>;
@@ -1,4 +1,5 @@
1
1
  import { ApiRoom, DeleteRoomMember, PostAddRoomMember, PostReportRoom, PostRoom, RoomMember } from '../../types/types';
2
+ import { CallCreateResult } from '../../calls/callMode';
2
3
  export interface BlockedUserEntry {
3
4
  _id: string;
4
5
  firstName?: string;
@@ -58,4 +59,4 @@ export declare function deleteRoom(name: string): Promise<any>;
58
59
  export declare function declineChatCall(callId: string): Promise<boolean>;
59
60
  export declare function createChatCall(chatName: string, options?: {
60
61
  kind?: 'audio' | 'video';
61
- }): Promise<void>;
62
+ }): Promise<CallCreateResult>;
@@ -0,0 +1,19 @@
1
+ import { CreateScheduledCallInput, ScheduledCall, ScheduledCallDetails, ScheduledCallOpenError, ScheduledCallWithLinks, UpdateScheduledCallInput } from '../../types/models/scheduledCall.model';
2
+ /** What went wrong, in the terms the UI needs. */
3
+ export declare class ScheduledCallApiError extends Error {
4
+ readonly status: number | null;
5
+ readonly reason: ScheduledCallOpenError;
6
+ /** The server's own `code` (or `error`) string, when it sent one. */
7
+ readonly code: string | null;
8
+ constructor(status: number | null, reason: ScheduledCallOpenError, code: string | null);
9
+ }
10
+ export declare function createScheduledCall(input: CreateScheduledCallInput): Promise<ScheduledCallWithLinks>;
11
+ export declare function listScheduledCalls(params: {
12
+ chatName: string;
13
+ from?: string;
14
+ to?: string;
15
+ }): Promise<ScheduledCallWithLinks[]>;
16
+ /** GET one call by slug. 404 -> `not-found`, 403 -> `not-a-member`. */
17
+ export declare function getScheduledCall(slug: string): Promise<ScheduledCallDetails>;
18
+ export declare function updateScheduledCall(slug: string, patch: UpdateScheduledCallInput): Promise<ScheduledCallWithLinks>;
19
+ export declare function cancelScheduledCall(slug: string): Promise<ScheduledCall>;
@@ -5,6 +5,13 @@ import { Element } from 'ltx';
5
5
  *
6
6
  * user-profile-updated xmppUsername="<appId>_<userId>" uuid appId
7
7
  * chat-meta-updated chatName="<appId>_<roomId>" appId
8
+ * chat-component-config-updated appId version
9
+ *
10
+ * The last one is how a change in the admin's Chat component tab reaches
11
+ * open chats. The server sends it to every online session of its XMPP host
12
+ * in one go (ejabberd `announce/online`), so it arrives FROM the bare server
13
+ * domain and for any App on the host: it is only acted on when `appId` is
14
+ * this session's own App.
8
15
  *
9
16
  * The payload is a pointer only. The client re-reads the user or the room
10
17
  * through the normal API, so nothing in the stanza is ever shown or stored.
@@ -13,6 +20,7 @@ import { Element } from 'ltx';
13
20
  export declare const ETHORA_EVENT_XMLNS = "urn:ethora:events:1";
14
21
  export declare const ETHORA_EVENT_USER_PROFILE = "user-profile-updated";
15
22
  export declare const ETHORA_EVENT_CHAT_META = "chat-meta-updated";
23
+ export declare const ETHORA_EVENT_CHAT_COMPONENT_CONFIG = "chat-component-config-updated";
16
24
  type HostLike = {
17
25
  host?: string;
18
26
  client?: {
@@ -30,8 +38,13 @@ type HostLike = {
30
38
  * - when config.trustedEventSenders is set, the sender must be listed
31
39
  * The server's admin local part is deployment config (XMPP_ADMIN), so it
32
40
  * cannot be hard-coded here.
41
+ *
42
+ * `allowServerDomain` also accepts the bare server domain itself as the
43
+ * sender (what ejabberd stamps on a host-wide announcement). A client cannot
44
+ * send as the domain: the server fixes `from` on every client stanza. A
45
+ * pinned `trustedEventSenders` list then has to name the domain.
33
46
  */
34
- export declare const isTrustedEventSender: (stanza: Element, xmppWs: HostLike | undefined) => boolean;
47
+ export declare const isTrustedEventSender: (stanza: Element, xmppWs: HostLike | undefined, allowServerDomain?: boolean) => boolean;
35
48
  /**
36
49
  * Handles `<ethora-event>` headlines. Returns true when the stanza carried
37
50
  * one (trusted or not), so the caller can stop routing it. Unknown types,
@@ -0,0 +1,6 @@
1
+ import { Element } from 'ltx';
2
+ /**
3
+ * Notify the host about every inbound XMPP message, including messages that
4
+ * are consumed by protocol handlers or routed to MAM instead of the chat UI.
5
+ */
6
+ export declare function notifyXmppEvent(stanza: Element): void;
@@ -1,3 +1,4 @@
1
+ import { CallMode, IceServerConfig } from '../calls/callMode';
1
2
  export type CallPhase = 'idle' | 'requesting' | 'ringing-incoming' | 'connecting' | 'in-call' | 'error';
2
3
  export type CallDirection = 'outgoing' | 'incoming' | null;
3
4
  export type CallKind = 'audio' | 'video';
@@ -16,6 +17,12 @@ export interface CallState {
16
17
  error: string | null;
17
18
  startedAt: number | null;
18
19
  connectedAt: number | null;
20
+ mode: CallMode;
21
+ isGroup: boolean;
22
+ livekitUrl: string | null;
23
+ iceServers: IceServerConfig[] | null;
24
+ callerName: string | null;
25
+ callerXmppUsername: string | null;
19
26
  }
20
27
  export declare const startOutgoingCall: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
21
28
  roomJid: string;
@@ -23,6 +30,7 @@ export declare const startOutgoingCall: import('@reduxjs/toolkit').ActionCreator
23
30
  roomBareName?: string | null;
24
31
  kind?: CallKind;
25
32
  peerXmppUsername?: string | null;
33
+ isGroup?: boolean;
26
34
  }, "call/startOutgoingCall">, setIncomingCallToken: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
27
35
  roomJid: string;
28
36
  roomName?: string | null;
@@ -31,11 +39,25 @@ export declare const startOutgoingCall: import('@reduxjs/toolkit').ActionCreator
31
39
  kind?: CallKind;
32
40
  callId?: string | null;
33
41
  peerXmppUsername?: string | null;
42
+ mode?: CallMode;
43
+ isGroup?: boolean;
44
+ iceServers?: IceServerConfig[] | null;
45
+ livekitUrl?: string | null;
46
+ callerName?: string | null;
47
+ callerXmppUsername?: string | null;
34
48
  }, "call/setIncomingCallToken">, setOutgoingCallToken: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
35
49
  roomJid: string;
36
50
  token: string;
37
51
  callId?: string | null;
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<{
52
+ livekitUrl?: string | null;
53
+ }, "call/setOutgoingCallToken">, setOutgoingCallRoute: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
54
+ startedAt: number | null;
55
+ callId: string | null;
56
+ mode: CallMode;
57
+ group?: boolean | null;
58
+ livekitUrl?: string | null;
59
+ iceServers?: IceServerConfig[] | null;
60
+ }, "call/setOutgoingCallRoute">, 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
61
  key: string;
40
62
  callId: string | null;
41
63
  }, "call/setCallE2eeKey">, resetCall: import('@reduxjs/toolkit').ActionCreatorWithoutPayload<"call/resetCall">;
@@ -1,7 +1,15 @@
1
1
  import { DeleteModal, IConfig, Iso639_1Codes, IUser, ModalFile, ModalType, User } from '../types/types';
2
+ import { RemoteConfigRecord } from '../helpers/liveConfig';
2
3
  interface ChatState {
3
4
  user: User;
4
5
  config?: IConfig;
6
+ /**
7
+ * The operator-saved look read for `config.remoteAppearance` (validated,
8
+ * live keys only). Kept next to `config` so that whoever writes `config`
9
+ * (the chat, the provider, the init hook) gets it laid over their value,
10
+ * and one writer cannot undo what another applied. Not persisted.
11
+ */
12
+ remoteConfig?: RemoteConfigRecord | null;
5
13
  appId?: string;
6
14
  activeModal?: ModalType;
7
15
  deleteModal?: DeleteModal;
@@ -34,7 +42,7 @@ interface ChatState {
34
42
  translateMode?: 'auto' | 'manual';
35
43
  blockedUserIds: string[];
36
44
  }
37
- export declare const setUser: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<User, "chatSettingStore/setUser">, setConfig: import('@reduxjs/toolkit').ActionCreatorWithNonInferrablePayload<"chatSettingStore/setConfig">, refreshTokens: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
45
+ export declare const setUser: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<User, "chatSettingStore/setUser">, setConfig: import('@reduxjs/toolkit').ActionCreatorWithNonInferrablePayload<"chatSettingStore/setConfig">, setRemoteConfig: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<RemoteConfigRecord, "chatSettingStore/setRemoteConfig">, refreshTokens: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
38
46
  token: string;
39
47
  refreshToken: string;
40
48
  fileToken?: string;
@@ -62,11 +62,13 @@ declare const rootReducer: Reducer<{
62
62
  rooms: Record<string, any> & import('redux-persist/es/persistReducer').PersistPartial;
63
63
  roomHeapSlice: import('./roomHeapSlice').roomHeapSliceState;
64
64
  call: import('immer').WritableDraft<import('./callSlice').CallState>;
65
+ scheduledCall: import('immer').WritableDraft<import('./scheduledCallSlice').ScheduledCallState>;
65
66
  }, import('redux').UnknownAction, Partial<{
66
67
  chatSettingStore: Record<string, any> & import('redux-persist/es/persistReducer').PersistPartial;
67
68
  rooms: Record<string, any> & import('redux-persist/es/persistReducer').PersistPartial;
68
69
  roomHeapSlice: import('./roomHeapSlice').roomHeapSliceState;
69
70
  call: import('immer').WritableDraft<import('./callSlice').CallState>;
71
+ scheduledCall: import('immer').WritableDraft<import('./scheduledCallSlice').ScheduledCallState>;
70
72
  }>>;
71
73
  export type RootState = ReturnType<typeof rootReducer>;
72
74
  export declare const getActiveRoom: (state: RootState) => IRoom | null;
@@ -75,12 +77,14 @@ export declare const store: import('@reduxjs/toolkit').EnhancedStore<{
75
77
  rooms: Record<string, any> & import('redux-persist/es/persistReducer').PersistPartial;
76
78
  roomHeapSlice: import('./roomHeapSlice').roomHeapSliceState;
77
79
  call: import('immer').WritableDraft<import('./callSlice').CallState>;
80
+ scheduledCall: import('immer').WritableDraft<import('./scheduledCallSlice').ScheduledCallState>;
78
81
  }, AnyAction, import('@reduxjs/toolkit').Tuple<[import('redux').StoreEnhancer<{
79
82
  dispatch: import('redux-thunk').ThunkDispatch<{
80
83
  chatSettingStore: Record<string, any> & import('redux-persist/es/persistReducer').PersistPartial;
81
84
  rooms: Record<string, any> & import('redux-persist/es/persistReducer').PersistPartial;
82
85
  roomHeapSlice: import('./roomHeapSlice').roomHeapSliceState;
83
86
  call: import('immer').WritableDraft<import('./callSlice').CallState>;
87
+ scheduledCall: import('immer').WritableDraft<import('./scheduledCallSlice').ScheduledCallState>;
84
88
  }, undefined, import('redux').UnknownAction>;
85
89
  }>, import('redux').StoreEnhancer]>>;
86
90
  export type AppDispatch = typeof store.dispatch;
@@ -0,0 +1,47 @@
1
+ import { ScheduledCallDetails, ScheduledCallOpenError } from '../types/models/scheduledCall.model';
2
+ export interface ScheduledCallDetailsEntry {
3
+ status: 'loading' | 'ready' | 'error';
4
+ data?: ScheduledCallDetails;
5
+ error?: ScheduledCallOpenError;
6
+ }
7
+ export interface ScheduledCallState {
8
+ /** A slug somebody asked to open (host prop or openScheduledCall). */
9
+ openRequest: {
10
+ slug: string;
11
+ requestedAt: number;
12
+ } | null;
13
+ /** The call whose pre-join panel is on screen. */
14
+ preJoin: ScheduledCallDetails | null;
15
+ /** The schedule form; `editing` set means edit mode. */
16
+ scheduleModal: {
17
+ roomJid: string;
18
+ editing: ScheduledCallDetails | null;
19
+ } | null;
20
+ /** The "cancel this call?" confirmation (mounted by the layer, outside the message list). */
21
+ cancelModal: {
22
+ slug: string;
23
+ title: string;
24
+ } | null;
25
+ /** Server copy of the calls the cards show, by slug. */
26
+ details: Record<string, ScheduledCallDetailsEntry>;
27
+ /** The last open request that failed, for the in-chat error notice. */
28
+ openError: {
29
+ slug: string;
30
+ reason: ScheduledCallOpenError;
31
+ } | null;
32
+ }
33
+ export declare const requestOpenScheduledCall: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<string, "scheduledCall/requestOpenScheduledCall">, clearOpenRequest: import('@reduxjs/toolkit').ActionCreatorWithoutPayload<"scheduledCall/clearOpenRequest">, setOpenError: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
34
+ slug: string;
35
+ reason: ScheduledCallOpenError;
36
+ }, "scheduledCall/setOpenError">, openPreJoin: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<ScheduledCallDetails, "scheduledCall/openPreJoin">, closePreJoin: import('@reduxjs/toolkit').ActionCreatorWithoutPayload<"scheduledCall/closePreJoin">, openScheduleModal: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
37
+ roomJid: string;
38
+ editing?: ScheduledCallDetails | null;
39
+ }, "scheduledCall/openScheduleModal">, closeScheduleModal: import('@reduxjs/toolkit').ActionCreatorWithoutPayload<"scheduledCall/closeScheduleModal">, openCancelModal: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
40
+ slug: string;
41
+ title: string;
42
+ }, "scheduledCall/openCancelModal">, closeCancelModal: import('@reduxjs/toolkit').ActionCreatorWithoutPayload<"scheduledCall/closeCancelModal">, detailsLoading: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<string, "scheduledCall/detailsLoading">, detailsLoaded: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<ScheduledCallDetails, "scheduledCall/detailsLoaded">, detailsFailed: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
43
+ slug: string;
44
+ reason: ScheduledCallOpenError;
45
+ }, "scheduledCall/detailsFailed">, forgetDetails: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<string, "scheduledCall/forgetDetails">;
46
+ declare const _default: import('redux').Reducer<import('immer').WritableDraft<ScheduledCallState>>;
47
+ export default _default;
@@ -3,6 +3,7 @@ import { xmppSettingsInterface } from './xmpp.model';
3
3
  import { ConfigRoom } from './room.model';
4
4
  import { MessageProps, IMessage } from './message.model';
5
5
  import { Iso639_1Codes } from './language.model';
6
+ import { ScheduledCallsConfig } from './scheduledCall.model';
6
7
  import { QuickReply } from '../../helpers/quickReplies';
7
8
  import { default as React } from 'react';
8
9
  import { MessageNotificationToastProps } from '../../components/MessageNotification/MessageNotificationToast';
@@ -27,7 +28,15 @@ export interface VideoCallIcons {
27
28
  export interface VideoCallsConfig {
28
29
  enabled: boolean;
29
30
  livekitUrl: string;
30
- allowedRoomTypes?: Array<'private'>;
31
+ /**
32
+ * Room types that can start and receive calls. Defaults to
33
+ * `['private', 'group']`: 1:1 chats plus members-only group chats (group
34
+ * calls always run over LiveKit and need a backend that accepts them; on an
35
+ * older backend the call fails with an error). Pass `['private']` to keep
36
+ * calls 1:1 only. The 1:1 transport (own WebRTC peer to peer, or LiveKit) is
37
+ * chosen by the server per call, not by this config.
38
+ */
39
+ allowedRoomTypes?: Array<'private' | 'group'>;
31
40
  /**
32
41
  * Show the audio-only call button next to the video-call button. Off by
33
42
  * default: it's a separate opt-in feature. An audio call uses the same call
@@ -152,6 +161,15 @@ export interface ChatErrorInfo {
152
161
  componentStack?: string;
153
162
  scope: ChatErrorScope;
154
163
  }
164
+ /** An incoming XMPP `<message/>` stanza received by the SDK. */
165
+ export interface XmppMessageEvent {
166
+ /** The complete stanza as XML, including its message payload. */
167
+ stanza: string;
168
+ from?: string;
169
+ to?: string;
170
+ id?: string;
171
+ type?: string;
172
+ }
155
173
  /**
156
174
  * Render-prop form of `fallbackScreens.error`. `reset()` clears the boundary
157
175
  * and re-mounts the chat subtree, so a host can build its own "try again".
@@ -646,6 +664,13 @@ export interface IConfig {
646
664
  filterFunction: (text: string) => string;
647
665
  };
648
666
  eventHandlers?: {
667
+ /**
668
+ * Called once for every incoming XMPP `<message/>` stanza, before the
669
+ * SDK routes it to chat, archive, call, or protocol handlers. `stanza`
670
+ * contains the raw XML; on end-to-end encrypted messages it may still be
671
+ * encrypted. A thrown error or rejected promise is caught and logged.
672
+ */
673
+ onXmppEvent?: (event: XmppMessageEvent) => void | Promise<void>;
649
674
  onMessageSent?: (event: {
650
675
  message: string;
651
676
  roomJID: string;
@@ -690,6 +715,23 @@ export interface IConfig {
690
715
  * inside this handler is caught and logged, never rethrown.
691
716
  */
692
717
  onError?: (event: ChatErrorInfo) => void;
718
+ /**
719
+ * Called when the server announces that the App's chat component config
720
+ * (the admin's Chat component tab) changed: an `ethora-event` headline of
721
+ * type `chat-component-config-updated` from the server. The event carries
722
+ * no values, only the App id and an opaque `version`; read the config
723
+ * again (`GET /v2/chat-component/config?appId=<appId>&v=<version>`, the
724
+ * `v` steps around the endpoint's 30 s cache) and pass the result in
725
+ * `config`, or set `config.remoteAppearance` and let the chat do both.
726
+ *
727
+ * Fires once per version, for the signed-in session's own App only
728
+ * (`config.appId`, or `remoteAppearance.appId`), a few hundred ms after
729
+ * the last of a burst. A throw inside it is caught and logged.
730
+ */
731
+ onChatComponentConfigUpdated?: (event: {
732
+ appId: string;
733
+ version: string;
734
+ }) => void;
693
735
  };
694
736
  disableTypingIndicator?: boolean;
695
737
  blockMessageSendingWhenProcessing?: boolean | {
@@ -743,6 +785,28 @@ export interface IConfig {
743
785
  * (`admin`).
744
786
  */
745
787
  trustedEventSenders?: string[];
788
+ /**
789
+ * Let the chat read the App's look from the server and keep it current:
790
+ * the colours, fonts, layout switches and AI display options the admin
791
+ * saved in the Chat component tab. Opt in by naming the App.
792
+ *
793
+ * The chat reads `GET /v2/chat-component/config?appId=` once at start and
794
+ * holds its first paint for it (at most `initialWaitMs`), lays the saved
795
+ * values over `config` (the server wins for the keys it holds), and reads
796
+ * again whenever the server announces a new version, applying the result to
797
+ * the open chat without reconnecting or resetting anything. Only the keys
798
+ * the chat can change live are accepted from the server (see the README
799
+ * table), each validated; nothing else in `config` is ever touched. A
800
+ * failed read keeps the look you already have.
801
+ */
802
+ remoteAppearance?: {
803
+ /** The App whose config to read. Usually the same as `config.appId`. */
804
+ appId: string;
805
+ /** API origin. Default: `config.baseUrl`, then the SDK's own default. */
806
+ baseUrl?: string;
807
+ /** Longest the first paint waits for the first read, in ms. Default 2500. */
808
+ initialWaitMs?: number;
809
+ };
746
810
  /**
747
811
  * Background history preload after connect. Default `{ mode: 'staged',
748
812
  * topRooms: 8, concurrency: 3 }`.
@@ -886,4 +950,10 @@ export interface IConfig {
886
950
  }) => void | Promise<void>;
887
951
  };
888
952
  videoCalls?: VideoCallsConfig;
953
+ /**
954
+ * Scheduled calls (booking): a "Schedule call" action next to the call
955
+ * buttons, and invite cards with Add to calendar and Join in the room. On by
956
+ * default whenever `videoCalls.enabled` is on. See ScheduledCallsConfig.
957
+ */
958
+ scheduledCalls?: ScheduledCallsConfig;
889
959
  }
@@ -2,6 +2,7 @@ import { IUser } from './user.model';
2
2
  import { TranslationObject } from '../types';
3
3
  import { Iso639_1Codes } from './language.model';
4
4
  import { IAttachment } from './attachment.model';
5
+ import { ScheduledCallMeta } from './scheduledCall.model';
5
6
  export interface IMessage {
6
7
  id: string;
7
8
  user: IUser;
@@ -81,6 +82,7 @@ export interface IMessage {
81
82
  missed: boolean;
82
83
  kind: 'audio' | 'video';
83
84
  };
85
+ scheduledCall?: ScheduledCallMeta;
84
86
  }
85
87
  export interface ReactionMessage {
86
88
  emoji: string[];
@@ -178,6 +178,8 @@ export interface IRoomCompressed extends Pick<IRoom, 'jid'> {
178
178
  export interface ConfigRoom {
179
179
  jid: string;
180
180
  pinned: boolean;
181
+ /** Whether users can leave this app-level room. Defaults to false for configured rooms. */
182
+ removable?: boolean;
181
183
  _id?: string;
182
184
  }
183
185
  export type ChatAccessOption = {
@@ -0,0 +1,116 @@
1
+ export type ScheduledCallKind = 'video' | 'audio';
2
+ /** Lifecycle stored on the server row. */
3
+ export type ScheduledCallStatus = 'scheduled' | 'cancelled';
4
+ /**
5
+ * Status carried by the room message. `updated` means the call was edited
6
+ * after the first invite; the card treats it like `scheduled`.
7
+ */
8
+ export type ScheduledCallMessageStatus = 'scheduled' | 'updated' | 'cancelled';
9
+ export interface ScheduledCall {
10
+ id?: string;
11
+ appId?: string;
12
+ chatName: string;
13
+ chatType?: 'private' | 'group' | 'public';
14
+ createdBy?: string;
15
+ title: string;
16
+ description?: string;
17
+ /** ISO 8601, UTC. */
18
+ startAt: string;
19
+ durationMinutes: number;
20
+ timezone?: string;
21
+ kind: ScheduledCallKind;
22
+ slug: string;
23
+ status: ScheduledCallStatus;
24
+ sequence?: number;
25
+ reminderSentAt?: string | null;
26
+ createdAt?: string;
27
+ updatedAt?: string;
28
+ }
29
+ /** Links are built by the backend only; clients never compose calendar URLs. */
30
+ export interface ScheduledCallLinks {
31
+ join: string;
32
+ ics: string;
33
+ google: string;
34
+ outlook: string;
35
+ office365?: string;
36
+ }
37
+ export interface ScheduledCallWithLinks {
38
+ scheduledCall: ScheduledCall;
39
+ links: ScheduledCallLinks;
40
+ }
41
+ export interface ScheduledCallDetails extends ScheduledCallWithLinks {
42
+ chat: {
43
+ name: string;
44
+ title?: string;
45
+ type?: string;
46
+ jid: string;
47
+ };
48
+ }
49
+ export interface CreateScheduledCallInput {
50
+ chatName: string;
51
+ title?: string;
52
+ description?: string;
53
+ startAt: string;
54
+ durationMinutes?: number;
55
+ timezone?: string;
56
+ kind?: ScheduledCallKind;
57
+ }
58
+ export interface UpdateScheduledCallInput {
59
+ title?: string;
60
+ description?: string;
61
+ startAt?: string;
62
+ durationMinutes?: number;
63
+ kind?: ScheduledCallKind;
64
+ }
65
+ /**
66
+ * What a `<data type="scheduledCall">` message boils down to. Persisted on the
67
+ * message (like `callLog`) so a reloaded transcript repaints the card before
68
+ * the archive re-delivers the stanza.
69
+ */
70
+ export interface ScheduledCallMeta {
71
+ slug: string;
72
+ title: string;
73
+ /** ISO 8601, UTC. */
74
+ startAt: string;
75
+ durationMinutes: number;
76
+ kind: ScheduledCallKind;
77
+ status: ScheduledCallMessageStatus;
78
+ /** Creator's user id (`scheduledCallCreatedBy`), decides who sees Edit and Cancel. */
79
+ createdBy?: string;
80
+ joinUrl?: string;
81
+ icsUrl?: string;
82
+ googleUrl?: string;
83
+ outlookUrl?: string;
84
+ }
85
+ /** Why a scheduled call could not be opened. */
86
+ export type ScheduledCallOpenError = 'not-found' | 'not-a-member' | 'cancelled' | 'network';
87
+ export interface ScheduledCallsConfig {
88
+ /**
89
+ * Scheduling and invite cards. Defaults to on whenever video calls are on
90
+ * (`config.videoCalls.enabled`). `false` hides the "Schedule call" action;
91
+ * invite cards still render so a room that already holds one stays readable,
92
+ * but without the Join button.
93
+ */
94
+ enabled?: boolean;
95
+ /**
96
+ * Called when `scheduledCallSlug` (or `openScheduledCall(slug)`) could not be
97
+ * opened. A host that wants its own error screen resolves the slug itself
98
+ * first; this is for the in-chat fallback.
99
+ */
100
+ onOpenError?: (error: {
101
+ slug: string;
102
+ reason: ScheduledCallOpenError;
103
+ }) => void;
104
+ /**
105
+ * Hosts whose `https://<host>/call/<slug>` links `parseScheduledCallLink`
106
+ * accepts on top of `app.chat.ethora.com` and the page's own host. Same option
107
+ * name and defaults as the React Native SDK.
108
+ */
109
+ hosts?: string[];
110
+ /**
111
+ * Custom URL schemes (without `://`) that `parseScheduledCallLink` accepts
112
+ * for `<scheme>://call/<slug>`. Unset accepts any scheme. Same option name
113
+ * as the React Native SDK.
114
+ */
115
+ schemes?: string[];
116
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ethora/chat-component",
3
- "version": "26.10.7",
3
+ "version": "26.10.8",
4
4
  "type": "module",
5
5
  "main": "dist/main.js",
6
6
  "types": "dist/main.d.ts",