@ermis-network/ermis-chat-react 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2501 -1249
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +1231 -134
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +306 -2
- package/dist/index.d.ts +306 -2
- package/dist/index.mjs +2427 -1181
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/components/ChannelHeader.tsx +50 -9
- package/src/components/ChannelInfo/AddMemberModal.tsx +48 -174
- package/src/components/ChannelList.tsx +9 -3
- package/src/components/CreateChannelModal.tsx +274 -0
- package/src/components/ErmisCallProvider.tsx +279 -0
- package/src/components/ErmisCallUI.tsx +634 -0
- package/src/components/MessageRenderers.tsx +37 -10
- package/src/components/Modal.tsx +2 -1
- package/src/components/UserPicker.tsx +377 -0
- package/src/context/ChatProvider.tsx +49 -1
- package/src/context/ErmisCallContext.tsx +37 -0
- package/src/hooks/useCallContext.ts +10 -0
- package/src/index.ts +27 -0
- package/src/styles/_add-member-modal.css +12 -29
- package/src/styles/_call-ui.css +743 -0
- package/src/styles/_channel-info.css +34 -34
- package/src/styles/_channel-list.css +7 -7
- package/src/styles/_create-channel-modal.css +183 -0
- package/src/styles/_message-bubble.css +108 -16
- package/src/styles/_message-input.css +4 -4
- package/src/styles/_message-list.css +11 -11
- package/src/styles/_modal.css +23 -36
- package/src/styles/_panel.css +1 -1
- package/src/styles/_search-panel.css +9 -9
- package/src/styles/_tokens.css +42 -0
- package/src/styles/_typing-indicator.css +15 -2
- package/src/styles/_user-picker.css +268 -0
- package/src/styles/index.css +3 -0
- package/src/types.ts +293 -1
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FormatMessageResponse, MessageLabel, Attachment, Channel, ChannelFilters, ChannelSort, ChannelQueryOptions } from '@ermis-network/ermis-chat-sdk';
|
|
1
|
+
import type { FormatMessageResponse, MessageLabel, Attachment, Channel, ChannelFilters, ChannelSort, ChannelQueryOptions, UserCallInfo } from '@ermis-network/ermis-chat-sdk';
|
|
2
2
|
import type { ErmisChat } from '@ermis-network/ermis-chat-sdk';
|
|
3
3
|
|
|
4
4
|
/* ----------------------------------------------------------
|
|
@@ -43,6 +43,8 @@ export type ChatContextValue = {
|
|
|
43
43
|
/** Message ID to jump/scroll to (set by search, cleared after scroll) */
|
|
44
44
|
jumpToMessageId: string | null;
|
|
45
45
|
setJumpToMessageId: (id: string | null) => void;
|
|
46
|
+
/** Indicates whether the direct call feature is enabled */
|
|
47
|
+
enableCall?: boolean;
|
|
46
48
|
};
|
|
47
49
|
|
|
48
50
|
export type ChatProviderProps = {
|
|
@@ -50,6 +52,185 @@ export type ChatProviderProps = {
|
|
|
50
52
|
children: React.ReactNode;
|
|
51
53
|
/** Initial theme, defaults to 'dark' */
|
|
52
54
|
initialTheme?: Theme;
|
|
55
|
+
/** Enable direct call feature (Audio/Video). If enabled, configures internal CallProvider */
|
|
56
|
+
enableCall?: boolean;
|
|
57
|
+
/** Provide session ID to be used for call nodes */
|
|
58
|
+
callSessionId?: string;
|
|
59
|
+
/** Override the WebAssembly module path for Call Nodes */
|
|
60
|
+
callWasmPath?: string;
|
|
61
|
+
/** Override the relay URL for Call Nodes */
|
|
62
|
+
callRelayUrl?: string;
|
|
63
|
+
/** Custom Component to completely replace the default Call UI */
|
|
64
|
+
CallUIComponent?: React.ComponentType;
|
|
65
|
+
/** Path to the mp3 file for incoming call ringing */
|
|
66
|
+
incomingCallAudioPath?: string;
|
|
67
|
+
/** Path to the mp3 file for outgoing call ringing */
|
|
68
|
+
outgoingCallAudioPath?: string;
|
|
69
|
+
/** Called when a call is initiated by the local user */
|
|
70
|
+
onCallStart?: (callType: 'audio' | 'video', cid: string) => void;
|
|
71
|
+
/** Called when a call ends (includes duration in seconds) */
|
|
72
|
+
onCallEnd?: (duration: number) => void;
|
|
73
|
+
/** Called when a call error occurs */
|
|
74
|
+
onCallError?: (error: string) => void;
|
|
75
|
+
/** Called when an incoming call is received */
|
|
76
|
+
onIncomingCall?: (callerInfo: UserCallInfo) => void;
|
|
77
|
+
/** Called when the local user accepts an incoming call */
|
|
78
|
+
onCallAccepted?: () => void;
|
|
79
|
+
/** Called when the local user rejects an incoming call */
|
|
80
|
+
onCallRejected?: () => void;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/* ----------------------------------------------------------
|
|
84
|
+
Call Provider types
|
|
85
|
+
---------------------------------------------------------- */
|
|
86
|
+
export interface ErmisCallProviderProps {
|
|
87
|
+
children: React.ReactNode;
|
|
88
|
+
client: ErmisChat;
|
|
89
|
+
sessionId: string;
|
|
90
|
+
wasmPath?: string;
|
|
91
|
+
relayUrl?: string;
|
|
92
|
+
/** Called when a call is initiated by the local user */
|
|
93
|
+
onCallStart?: (callType: 'audio' | 'video', cid: string) => void;
|
|
94
|
+
/** Called when a call ends (includes duration in seconds) */
|
|
95
|
+
onCallEnd?: (duration: number) => void;
|
|
96
|
+
/** Called when a call error occurs */
|
|
97
|
+
onCallError?: (error: string) => void;
|
|
98
|
+
/** Called when an incoming call is received */
|
|
99
|
+
onIncomingCall?: (callerInfo: UserCallInfo) => void;
|
|
100
|
+
/** Called when the local user accepts an incoming call */
|
|
101
|
+
onCallAccepted?: () => void;
|
|
102
|
+
/** Called when the local user rejects an incoming call */
|
|
103
|
+
onCallRejected?: () => void;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* ----------------------------------------------------------
|
|
107
|
+
Call UI types
|
|
108
|
+
---------------------------------------------------------- */
|
|
109
|
+
export type ErmisCallUIProps = {
|
|
110
|
+
/** Additional CSS class name */
|
|
111
|
+
className?: string;
|
|
112
|
+
incomingCallTitle?: (callType: string) => string;
|
|
113
|
+
outgoingCallTitle?: (callType: string) => string;
|
|
114
|
+
ongoingCallTitle?: (callType: string) => string;
|
|
115
|
+
isCallingYouLabel?: string;
|
|
116
|
+
ringingLabel?: string;
|
|
117
|
+
rejectCallLabel?: string;
|
|
118
|
+
acceptCallLabel?: string;
|
|
119
|
+
endCallLabel?: string;
|
|
120
|
+
cancelLabel?: string;
|
|
121
|
+
toggleMicTitle?: string;
|
|
122
|
+
toggleVideoTitle?: string;
|
|
123
|
+
shareScreenTitle?: string;
|
|
124
|
+
stopScreenShareTitle?: string;
|
|
125
|
+
/** Label shown during an active call (default: "Connected") */
|
|
126
|
+
connectedLabel?: string;
|
|
127
|
+
/** Label for the audio call type badge (default: "Audio Call") */
|
|
128
|
+
audioCallBadgeLabel?: string;
|
|
129
|
+
/** Label for the video call type badge (default: "Video Call") */
|
|
130
|
+
videoCallBadgeLabel?: string;
|
|
131
|
+
/** Tooltip for the fullscreen button (default: "Fullscreen") */
|
|
132
|
+
fullscreenTitle?: string;
|
|
133
|
+
/** Tooltip for the exit fullscreen button (default: "Exit Fullscreen") */
|
|
134
|
+
exitFullscreenTitle?: string;
|
|
135
|
+
/** Tooltip for the upgrade call button (default: "Request Video Upgrade") */
|
|
136
|
+
upgradeCallTitle?: string;
|
|
137
|
+
/** If true, suppress incoming call UI — useful for "Do Not Disturb" mode */
|
|
138
|
+
suppressIncomingCalls?: boolean;
|
|
139
|
+
/** Called on each second tick of the call duration timer */
|
|
140
|
+
onCallDurationChange?: (seconds: number) => void;
|
|
141
|
+
AvatarComponent?: React.ComponentType<AvatarProps>;
|
|
142
|
+
MicIcon?: React.ComponentType;
|
|
143
|
+
MicOffIcon?: React.ComponentType;
|
|
144
|
+
VideoIcon?: React.ComponentType;
|
|
145
|
+
VideoOffIcon?: React.ComponentType;
|
|
146
|
+
PhoneIcon?: React.ComponentType;
|
|
147
|
+
ScreenShareIcon?: React.ComponentType;
|
|
148
|
+
ScreenShareOffIcon?: React.ComponentType;
|
|
149
|
+
FullscreenIcon?: React.ComponentType;
|
|
150
|
+
ExitFullscreenIcon?: React.ComponentType;
|
|
151
|
+
/** Custom icon for the upgrade call button (audio → video) */
|
|
152
|
+
UpgradeCallIcon?: React.ComponentType;
|
|
153
|
+
incomingCallAudioPath?: string;
|
|
154
|
+
outgoingCallAudioPath?: string;
|
|
155
|
+
/** Replace the entire Ringing state view */
|
|
156
|
+
RingingComponent?: React.ComponentType<ErmisCallRingingProps>;
|
|
157
|
+
/** Replace the entire Connected Audio state view */
|
|
158
|
+
ConnectedAudioComponent?: React.ComponentType<ErmisCallConnectedAudioProps>;
|
|
159
|
+
/** Replace the entire Connected Video state view */
|
|
160
|
+
ConnectedVideoComponent?: React.ComponentType<ErmisCallConnectedVideoProps>;
|
|
161
|
+
/** Replace the entire Error state view */
|
|
162
|
+
ErrorComponent?: React.ComponentType<ErmisCallErrorProps>;
|
|
163
|
+
/** Replace the controls bar */
|
|
164
|
+
ControlsBarComponent?: React.ComponentType<ErmisCallControlsBarProps>;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/* ----------------------------------------------------------
|
|
168
|
+
Call sub-component prop types (for component slots)
|
|
169
|
+
---------------------------------------------------------- */
|
|
170
|
+
|
|
171
|
+
/** Props for the Ringing state view */
|
|
172
|
+
export type ErmisCallRingingProps = {
|
|
173
|
+
peerInfo?: UserCallInfo;
|
|
174
|
+
callType: string;
|
|
175
|
+
isIncoming: boolean;
|
|
176
|
+
acceptCall: () => Promise<void>;
|
|
177
|
+
rejectCall: () => Promise<void>;
|
|
178
|
+
endCall: () => Promise<void>;
|
|
179
|
+
AvatarComponent: React.ComponentType<AvatarProps>;
|
|
180
|
+
isCallingYouLabel: string;
|
|
181
|
+
ringingLabel: string;
|
|
182
|
+
rejectCallLabel: string;
|
|
183
|
+
acceptCallLabel: string;
|
|
184
|
+
endCallLabel: string;
|
|
185
|
+
audioCallBadgeLabel: string;
|
|
186
|
+
videoCallBadgeLabel: string;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/** Props for the Connected Audio state view */
|
|
190
|
+
export type ErmisCallConnectedAudioProps = {
|
|
191
|
+
peerInfo?: UserCallInfo;
|
|
192
|
+
callDuration: number;
|
|
193
|
+
isRemoteMicMuted: boolean;
|
|
194
|
+
AvatarComponent: React.ComponentType<AvatarProps>;
|
|
195
|
+
connectedLabel: string;
|
|
196
|
+
renderControls: () => React.ReactNode;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/** Props for the Connected Video state view */
|
|
200
|
+
export type ErmisCallConnectedVideoProps = {
|
|
201
|
+
localVideoRef: React.RefObject<HTMLVideoElement | null>;
|
|
202
|
+
remoteVideoRef: React.RefObject<HTMLVideoElement | null>;
|
|
203
|
+
isRemoteMicMuted: boolean;
|
|
204
|
+
renderControls: () => React.ReactNode;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
/** Props for the Error state view */
|
|
208
|
+
export type ErmisCallErrorProps = {
|
|
209
|
+
errorMessage: string;
|
|
210
|
+
clearError: () => void;
|
|
211
|
+
cancelLabel: string;
|
|
212
|
+
PhoneIcon: React.ComponentType;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
/** Props for the Controls bar */
|
|
216
|
+
export type ErmisCallControlsBarProps = {
|
|
217
|
+
callType: string;
|
|
218
|
+
toggleMic: () => void;
|
|
219
|
+
toggleVideo: () => void;
|
|
220
|
+
toggleScreenShare: () => Promise<void>;
|
|
221
|
+
toggleFullscreen: () => void;
|
|
222
|
+
upgradeCall: () => Promise<void>;
|
|
223
|
+
endCall: () => Promise<void>;
|
|
224
|
+
isMicMuted: boolean;
|
|
225
|
+
isVideoMuted: boolean;
|
|
226
|
+
isScreenSharing: boolean;
|
|
227
|
+
isFullscreen: boolean;
|
|
228
|
+
audioDevices: MediaDeviceInfo[];
|
|
229
|
+
videoDevices: MediaDeviceInfo[];
|
|
230
|
+
selectedAudioDeviceId: string;
|
|
231
|
+
selectedVideoDeviceId: string;
|
|
232
|
+
switchAudioDevice: (id: string) => Promise<void>;
|
|
233
|
+
switchVideoDevice: (id: string) => Promise<void>;
|
|
53
234
|
};
|
|
54
235
|
|
|
55
236
|
/* ----------------------------------------------------------
|
|
@@ -96,6 +277,16 @@ export type ChannelHeaderProps = {
|
|
|
96
277
|
renderRight?: (channel: Channel, actionDisabled?: boolean) => React.ReactNode;
|
|
97
278
|
/** Override default title rendering */
|
|
98
279
|
renderTitle?: (channel: Channel) => React.ReactNode;
|
|
280
|
+
/** Custom renderer for Audio Call button */
|
|
281
|
+
renderAudioCallButton?: (onClick: () => void, disabled: boolean) => React.ReactNode;
|
|
282
|
+
/** Custom renderer for Video Call button */
|
|
283
|
+
renderVideoCallButton?: (onClick: () => void, disabled: boolean) => React.ReactNode;
|
|
284
|
+
/** I18n label for the audio call button tooltip (default: "Audio Call") */
|
|
285
|
+
audioCallTitle?: string;
|
|
286
|
+
/** I18n label for the video call button tooltip (default: "Video Call") */
|
|
287
|
+
videoCallTitle?: string;
|
|
288
|
+
/** Custom component to show when a call is active (e.g. "Call in progress" badge) */
|
|
289
|
+
CallBadgeComponent?: React.ComponentType<{ callType: string }>;
|
|
99
290
|
};
|
|
100
291
|
|
|
101
292
|
/** Data passed to a fully custom HeaderComponent */
|
|
@@ -591,6 +782,7 @@ export interface ModalProps {
|
|
|
591
782
|
footer?: React.ReactNode;
|
|
592
783
|
maxWidth?: string;
|
|
593
784
|
hideCloseButton?: boolean;
|
|
785
|
+
closeOnOutsideClick?: boolean;
|
|
594
786
|
}
|
|
595
787
|
|
|
596
788
|
/* ----------------------------------------------------------
|
|
@@ -953,3 +1145,103 @@ export type ChannelSettingsPanelProps = {
|
|
|
953
1145
|
/** Custom slow mode options */
|
|
954
1146
|
slowModeOptions?: { label: string; value: number }[];
|
|
955
1147
|
};
|
|
1148
|
+
|
|
1149
|
+
/* ----------------------------------------------------------
|
|
1150
|
+
UserPicker types
|
|
1151
|
+
---------------------------------------------------------- */
|
|
1152
|
+
|
|
1153
|
+
/** Individual user item in UserPicker */
|
|
1154
|
+
export type UserPickerUser = {
|
|
1155
|
+
id: string;
|
|
1156
|
+
name?: string;
|
|
1157
|
+
email?: string;
|
|
1158
|
+
phone?: string;
|
|
1159
|
+
avatar?: string;
|
|
1160
|
+
[key: string]: any;
|
|
1161
|
+
};
|
|
1162
|
+
|
|
1163
|
+
/** Props for each user row in UserPicker */
|
|
1164
|
+
export type UserPickerItemProps = {
|
|
1165
|
+
user: UserPickerUser;
|
|
1166
|
+
selected: boolean;
|
|
1167
|
+
disabled: boolean;
|
|
1168
|
+
mode: 'radio' | 'checkbox';
|
|
1169
|
+
onToggle: (user: UserPickerUser) => void;
|
|
1170
|
+
AvatarComponent: React.ComponentType<AvatarProps>;
|
|
1171
|
+
};
|
|
1172
|
+
|
|
1173
|
+
/** Props for the selected users chip box */
|
|
1174
|
+
export type UserPickerSelectedBoxProps = {
|
|
1175
|
+
users: UserPickerUser[];
|
|
1176
|
+
onRemove: (userId: string) => void;
|
|
1177
|
+
AvatarComponent: React.ComponentType<AvatarProps>;
|
|
1178
|
+
/** Label when no users selected */
|
|
1179
|
+
emptyLabel?: string;
|
|
1180
|
+
};
|
|
1181
|
+
|
|
1182
|
+
/** Main UserPicker props */
|
|
1183
|
+
export type UserPickerProps = {
|
|
1184
|
+
/** Selection mode: 'radio' for single, 'checkbox' for multi */
|
|
1185
|
+
mode: 'radio' | 'checkbox';
|
|
1186
|
+
/** Called whenever selection changes */
|
|
1187
|
+
onSelectionChange?: (users: UserPickerUser[]) => void;
|
|
1188
|
+
/** User IDs to exclude from the list (e.g. existing members) */
|
|
1189
|
+
excludeUserIds?: string[];
|
|
1190
|
+
/** Users that are pre-selected on mount */
|
|
1191
|
+
initialSelectedUsers?: UserPickerUser[];
|
|
1192
|
+
/** Page size for queryUsers (default: 30) */
|
|
1193
|
+
pageSize?: number;
|
|
1194
|
+
/** Custom avatar component */
|
|
1195
|
+
AvatarComponent?: React.ComponentType<AvatarProps>;
|
|
1196
|
+
/** Custom user item component (replaces the default row) */
|
|
1197
|
+
UserItemComponent?: React.ComponentType<UserPickerItemProps>;
|
|
1198
|
+
/** Custom selected box component (checkbox mode only) */
|
|
1199
|
+
SelectedBoxComponent?: React.ComponentType<UserPickerSelectedBoxProps>;
|
|
1200
|
+
/** Custom search input component */
|
|
1201
|
+
SearchInputComponent?: React.ComponentType<{
|
|
1202
|
+
value: string;
|
|
1203
|
+
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
1204
|
+
placeholder: string;
|
|
1205
|
+
}>;
|
|
1206
|
+
|
|
1207
|
+
/** I18n labels */
|
|
1208
|
+
searchPlaceholder?: string;
|
|
1209
|
+
loadingText?: string;
|
|
1210
|
+
emptyText?: string;
|
|
1211
|
+
loadingMoreText?: string;
|
|
1212
|
+
selectedEmptyLabel?: string;
|
|
1213
|
+
};
|
|
1214
|
+
|
|
1215
|
+
/* ----------------------------------------------------------
|
|
1216
|
+
Create Channel Modal Props
|
|
1217
|
+
---------------------------------------------------------- */
|
|
1218
|
+
|
|
1219
|
+
export type CreateChannelModalProps = {
|
|
1220
|
+
isOpen: boolean;
|
|
1221
|
+
onClose: () => void;
|
|
1222
|
+
onSuccess?: (channel: any) => void; // Uses 'any' or 'Channel' based on context
|
|
1223
|
+
|
|
1224
|
+
/** Override visual components */
|
|
1225
|
+
AvatarComponent?: React.ComponentType<AvatarProps>;
|
|
1226
|
+
UserItemComponent?: React.ComponentType<UserPickerItemProps>;
|
|
1227
|
+
|
|
1228
|
+
/** i18n labels */
|
|
1229
|
+
title?: string;
|
|
1230
|
+
directTabLabel?: string;
|
|
1231
|
+
groupTabLabel?: string;
|
|
1232
|
+
groupNameLabel?: string;
|
|
1233
|
+
groupNamePlaceholder?: string;
|
|
1234
|
+
groupDescriptionLabel?: string;
|
|
1235
|
+
groupDescriptionPlaceholder?: string;
|
|
1236
|
+
groupPublicLabel?: string;
|
|
1237
|
+
groupAvatarLabel?: string;
|
|
1238
|
+
userSearchPlaceholder?: string;
|
|
1239
|
+
cancelButtonLabel?: string;
|
|
1240
|
+
createButtonLabel?: string;
|
|
1241
|
+
creatingButtonLabel?: string;
|
|
1242
|
+
|
|
1243
|
+
/** File upload configuration for group channel images */
|
|
1244
|
+
imageAccept?: string;
|
|
1245
|
+
maxImageSize?: number; // bytes
|
|
1246
|
+
maxImageSizeError?: string;
|
|
1247
|
+
};
|