@ethora/chat-component-rn 26.10.2 → 26.10.4
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/lib/commonjs/components/Header/HeaderRoomList.js +72 -69
- package/lib/commonjs/components/Header/HeaderRoomList.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatHeader.js +13 -11
- package/lib/commonjs/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/commonjs/components/MainComponents/MessageContainer.js +10 -2
- package/lib/commonjs/components/MainComponents/MessageContainer.js.map +1 -1
- package/lib/commonjs/components/MainComponents/RoomList.js +8 -18
- package/lib/commonjs/components/MainComponents/RoomList.js.map +1 -1
- package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js +17 -14
- package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
- package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js +131 -17
- package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/ChatRoomItem.js +6 -1
- package/lib/commonjs/components/RoomComponents/ChatRoomItem.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/ActiveCallBar.js +203 -0
- package/lib/commonjs/components/VideoCalls/ActiveCallBar.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/CallButtons.js +3 -1
- package/lib/commonjs/components/VideoCalls/CallButtons.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js +123 -129
- package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/VideoCallSession.js +212 -414
- package/lib/commonjs/components/VideoCalls/VideoCallSession.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/callActions.js +61 -0
- package/lib/commonjs/components/VideoCalls/callActions.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/callSessionController.js +461 -0
- package/lib/commonjs/components/VideoCalls/callSessionController.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/callUiStore.js +91 -0
- package/lib/commonjs/components/VideoCalls/callUiStore.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/livekitRuntime.js +1 -0
- package/lib/commonjs/components/VideoCalls/livekitRuntime.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/useDraggable.js +59 -0
- package/lib/commonjs/components/VideoCalls/useDraggable.js.map +1 -0
- package/lib/commonjs/helpers/createRoomFromApi.js +4 -0
- package/lib/commonjs/helpers/createRoomFromApi.js.map +1 -1
- package/lib/commonjs/helpers/incomingCallFromPush.js +1 -1
- package/lib/commonjs/helpers/incomingCallFromPush.js.map +1 -1
- package/lib/commonjs/helpers/roomUserCount.js +7 -1
- package/lib/commonjs/helpers/roomUserCount.js.map +1 -1
- package/lib/commonjs/helpers/uniqueMembers.js +36 -0
- package/lib/commonjs/helpers/uniqueMembers.js.map +1 -0
- package/lib/commonjs/hooks/useLeaveRoom.js +52 -0
- package/lib/commonjs/hooks/useLeaveRoom.js.map +1 -0
- package/lib/commonjs/i18n/strings.js +24 -0
- package/lib/commonjs/i18n/strings.js.map +1 -1
- package/lib/commonjs/networking/api-requests/rooms.api.js +36 -1
- package/lib/commonjs/networking/api-requests/rooms.api.js.map +1 -1
- package/lib/commonjs/networking/callTokenStanza.js +47 -3
- package/lib/commonjs/networking/callTokenStanza.js.map +1 -1
- package/lib/commonjs/networking/xmppClient.js +9 -2
- package/lib/commonjs/networking/xmppClient.js.map +1 -1
- package/lib/commonjs/version.js +1 -1
- package/lib/module/components/Header/HeaderRoomList.js +73 -70
- package/lib/module/components/Header/HeaderRoomList.js.map +1 -1
- package/lib/module/components/MainComponents/ChatHeader.js +15 -13
- package/lib/module/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/module/components/MainComponents/MessageContainer.js +10 -2
- package/lib/module/components/MainComponents/MessageContainer.js.map +1 -1
- package/lib/module/components/MainComponents/RoomList.js +8 -18
- package/lib/module/components/MainComponents/RoomList.js.map +1 -1
- package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js +18 -15
- package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
- package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js +132 -18
- package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
- package/lib/module/components/RoomComponents/ChatRoomItem.js +6 -1
- package/lib/module/components/RoomComponents/ChatRoomItem.js.map +1 -1
- package/lib/module/components/VideoCalls/ActiveCallBar.js +195 -0
- package/lib/module/components/VideoCalls/ActiveCallBar.js.map +1 -0
- package/lib/module/components/VideoCalls/CallButtons.js +3 -1
- package/lib/module/components/VideoCalls/CallButtons.js.map +1 -1
- package/lib/module/components/VideoCalls/VideoCallOverlay.js +125 -131
- package/lib/module/components/VideoCalls/VideoCallOverlay.js.map +1 -1
- package/lib/module/components/VideoCalls/VideoCallSession.js +213 -417
- package/lib/module/components/VideoCalls/VideoCallSession.js.map +1 -1
- package/lib/module/components/VideoCalls/callActions.js +55 -0
- package/lib/module/components/VideoCalls/callActions.js.map +1 -0
- package/lib/module/components/VideoCalls/callSessionController.js +450 -0
- package/lib/module/components/VideoCalls/callSessionController.js.map +1 -0
- package/lib/module/components/VideoCalls/callUiStore.js +84 -0
- package/lib/module/components/VideoCalls/callUiStore.js.map +1 -0
- package/lib/module/components/VideoCalls/livekitRuntime.js +1 -0
- package/lib/module/components/VideoCalls/livekitRuntime.js.map +1 -1
- package/lib/module/components/VideoCalls/useDraggable.js +55 -0
- package/lib/module/components/VideoCalls/useDraggable.js.map +1 -0
- package/lib/module/helpers/createRoomFromApi.js +4 -0
- package/lib/module/helpers/createRoomFromApi.js.map +1 -1
- package/lib/module/helpers/incomingCallFromPush.js +1 -1
- package/lib/module/helpers/incomingCallFromPush.js.map +1 -1
- package/lib/module/helpers/roomUserCount.js +7 -1
- package/lib/module/helpers/roomUserCount.js.map +1 -1
- package/lib/module/helpers/uniqueMembers.js +31 -0
- package/lib/module/helpers/uniqueMembers.js.map +1 -0
- package/lib/module/hooks/useLeaveRoom.js +48 -0
- package/lib/module/hooks/useLeaveRoom.js.map +1 -0
- package/lib/module/i18n/strings.js +24 -0
- package/lib/module/i18n/strings.js.map +1 -1
- package/lib/module/networking/api-requests/rooms.api.js +35 -1
- package/lib/module/networking/api-requests/rooms.api.js.map +1 -1
- package/lib/module/networking/callTokenStanza.js +47 -3
- package/lib/module/networking/callTokenStanza.js.map +1 -1
- package/lib/module/networking/xmppClient.js +8 -1
- package/lib/module/networking/xmppClient.js.map +1 -1
- package/lib/module/version.js +1 -1
- package/lib/typescript/components/Header/HeaderRoomList.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/ChatHeader.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/MessageContainer.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/RoomList.d.ts.map +1 -1
- package/lib/typescript/components/Modals/ChatProfileModal/ChatProfileModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/UserProfileModal/UserProfileModal.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/ChatRoomItem.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/ActiveCallBar.d.ts +21 -0
- package/lib/typescript/components/VideoCalls/ActiveCallBar.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/CallButtons.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/VideoCallOverlay.d.ts +0 -6
- package/lib/typescript/components/VideoCalls/VideoCallOverlay.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/VideoCallSession.d.ts +15 -22
- package/lib/typescript/components/VideoCalls/VideoCallSession.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/callActions.d.ts +15 -0
- package/lib/typescript/components/VideoCalls/callActions.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/callSessionController.d.ts +63 -0
- package/lib/typescript/components/VideoCalls/callSessionController.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/callUiStore.d.ts +24 -0
- package/lib/typescript/components/VideoCalls/callUiStore.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/livekitRuntime.d.ts +2 -0
- package/lib/typescript/components/VideoCalls/livekitRuntime.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/useDraggable.d.ts +19 -0
- package/lib/typescript/components/VideoCalls/useDraggable.d.ts.map +1 -0
- package/lib/typescript/helpers/createRoomFromApi.d.ts.map +1 -1
- package/lib/typescript/helpers/roomUserCount.d.ts +1 -0
- package/lib/typescript/helpers/roomUserCount.d.ts.map +1 -1
- package/lib/typescript/helpers/uniqueMembers.d.ts +11 -0
- package/lib/typescript/helpers/uniqueMembers.d.ts.map +1 -0
- package/lib/typescript/hooks/useLeaveRoom.d.ts +9 -0
- package/lib/typescript/hooks/useLeaveRoom.d.ts.map +1 -0
- package/lib/typescript/i18n/strings.d.ts.map +1 -1
- package/lib/typescript/networking/api-requests/rooms.api.d.ts +8 -0
- package/lib/typescript/networking/api-requests/rooms.api.d.ts.map +1 -1
- package/lib/typescript/networking/callTokenStanza.d.ts.map +1 -1
- package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
- package/lib/typescript/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/Header/HeaderRoomList.tsx +66 -61
- package/src/components/MainComponents/ChatHeader.tsx +13 -8
- package/src/components/MainComponents/MessageContainer.tsx +14 -1
- package/src/components/MainComponents/RoomList.tsx +8 -14
- package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +16 -13
- package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +167 -36
- package/src/components/RoomComponents/ChatRoomItem.tsx +8 -1
- package/src/components/VideoCalls/ActiveCallBar.tsx +176 -0
- package/src/components/VideoCalls/CallButtons.tsx +5 -1
- package/src/components/VideoCalls/VideoCallOverlay.tsx +153 -183
- package/src/components/VideoCalls/VideoCallSession.tsx +227 -439
- package/src/components/VideoCalls/callActions.ts +63 -0
- package/src/components/VideoCalls/callSessionController.ts +439 -0
- package/src/components/VideoCalls/callUiStore.ts +76 -0
- package/src/components/VideoCalls/livekitRuntime.ts +3 -0
- package/src/components/VideoCalls/useDraggable.ts +46 -0
- package/src/helpers/createRoomFromApi.tsx +2 -0
- package/src/helpers/incomingCallFromPush.ts +2 -2
- package/src/helpers/roomUserCount.ts +6 -2
- package/src/helpers/uniqueMembers.ts +26 -0
- package/src/hooks/useLeaveRoom.ts +42 -0
- package/src/i18n/strings.ts +24 -0
- package/src/networking/api-requests/rooms.api.ts +28 -1
- package/src/networking/callTokenStanza.ts +46 -6
- package/src/networking/xmppClient.ts +10 -1
- package/src/version.ts +1 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React, {
|
|
2
|
+
Component,
|
|
2
3
|
FC,
|
|
4
|
+
ReactNode,
|
|
3
5
|
useCallback,
|
|
4
6
|
useEffect,
|
|
5
7
|
useMemo,
|
|
6
8
|
useRef,
|
|
7
|
-
useState,
|
|
8
9
|
} from 'react';
|
|
9
10
|
import {
|
|
10
11
|
Animated,
|
|
@@ -19,23 +20,21 @@ import {
|
|
|
19
20
|
View,
|
|
20
21
|
} from 'react-native';
|
|
21
22
|
import { useDispatch, useSelector } from 'react-redux';
|
|
22
|
-
import { RootState
|
|
23
|
+
import { RootState } from '../../roomStore';
|
|
23
24
|
import {
|
|
24
25
|
acceptIncomingCall,
|
|
25
|
-
declineIncomingCall,
|
|
26
|
-
endCall,
|
|
27
26
|
resetCall,
|
|
28
27
|
setCallError,
|
|
29
|
-
setCallPhase,
|
|
30
28
|
} from '../../roomStore/callSlice';
|
|
31
|
-
import { addRoomMessage } from '../../roomStore/roomsSlice';
|
|
32
|
-
import { buildLocalCallLogMessage } from '../../helpers/callLogMessage';
|
|
33
29
|
import { useChatSettingState } from '../../hooks/useChatSettingState';
|
|
34
|
-
import {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
} from '../../networking/callTokenStanza';
|
|
30
|
+
import { sendCallStateSignal } from '../../networking/callTokenStanza';
|
|
31
|
+
import { useTheme } from '../../hooks/useTheme';
|
|
32
|
+
import type { ChatTheme } from '../../theme/theme';
|
|
38
33
|
import { VideoCallSession } from './VideoCallSession';
|
|
34
|
+
import { startCallSession } from './callSessionController';
|
|
35
|
+
import { declineActiveCall, terminateActiveCall } from './callActions';
|
|
36
|
+
import { DockedCallBar } from './ActiveCallBar';
|
|
37
|
+
import { setCallMinimized, useCallUi } from './callUiStore';
|
|
39
38
|
import { ProfileImagePlaceholder } from '../MainComponents/ProfileImagePlaceholder';
|
|
40
39
|
import {
|
|
41
40
|
AudioCallIcon,
|
|
@@ -47,8 +46,6 @@ import { useT } from '../../i18n/useT';
|
|
|
47
46
|
import { useCallKeep } from './useCallKeep';
|
|
48
47
|
|
|
49
48
|
const DEFAULT_PRIMARY = '#0052CD';
|
|
50
|
-
const TEXT_PRIMARY = '#141414';
|
|
51
|
-
const TEXT_MUTED = '#8c8c8c';
|
|
52
49
|
const DANGER = '#E53935';
|
|
53
50
|
const ACCEPT = '#10B981';
|
|
54
51
|
|
|
@@ -146,6 +143,7 @@ interface RingingCardProps {
|
|
|
146
143
|
onDecline?: () => void;
|
|
147
144
|
onHangup?: () => void;
|
|
148
145
|
onDismiss?: () => void;
|
|
146
|
+
theme: ChatTheme;
|
|
149
147
|
}
|
|
150
148
|
|
|
151
149
|
const RingingCard: FC<RingingCardProps> = ({
|
|
@@ -161,12 +159,13 @@ const RingingCard: FC<RingingCardProps> = ({
|
|
|
161
159
|
onDecline,
|
|
162
160
|
onHangup,
|
|
163
161
|
onDismiss,
|
|
162
|
+
theme,
|
|
164
163
|
}) => {
|
|
165
164
|
const t = useT();
|
|
166
165
|
|
|
167
166
|
return (
|
|
168
|
-
<View style={styles.ringingCard}>
|
|
169
|
-
<Text style={styles.ringingTitle}>{title}</Text>
|
|
167
|
+
<View style={[styles.ringingCard, { backgroundColor: theme.surface }]}>
|
|
168
|
+
<Text style={[styles.ringingTitle, { color: theme.textSecondary }]}>{title}</Text>
|
|
170
169
|
|
|
171
170
|
<View style={styles.avatarWrap}>
|
|
172
171
|
{variant !== 'error' && (
|
|
@@ -179,17 +178,17 @@ const RingingCard: FC<RingingCardProps> = ({
|
|
|
179
178
|
</View>
|
|
180
179
|
|
|
181
180
|
<View style={styles.ringingIdentity}>
|
|
182
|
-
<Text style={styles.ringingName}>
|
|
181
|
+
<Text style={[styles.ringingName, { color: theme.text }]}>
|
|
183
182
|
{roomName ||
|
|
184
183
|
(variant === 'incoming' ? t('call.unknownCaller') : t('call.title'))}
|
|
185
184
|
</Text>
|
|
186
185
|
<View style={styles.ringingSubtitleRow}>
|
|
187
186
|
{kind === 'audio' ? (
|
|
188
|
-
<AudioCallIcon color={
|
|
187
|
+
<AudioCallIcon color={theme.textSecondary} size={16} />
|
|
189
188
|
) : (
|
|
190
|
-
<VideoCallIcon color={
|
|
189
|
+
<VideoCallIcon color={theme.textSecondary} size={16} />
|
|
191
190
|
)}
|
|
192
|
-
<Text style={styles.ringingSubtitle}>{subtitle}</Text>
|
|
191
|
+
<Text style={[styles.ringingSubtitle, { color: theme.textSecondary }]}>{subtitle}</Text>
|
|
193
192
|
</View>
|
|
194
193
|
</View>
|
|
195
194
|
|
|
@@ -251,22 +250,57 @@ const RingingCard: FC<RingingCardProps> = ({
|
|
|
251
250
|
);
|
|
252
251
|
};
|
|
253
252
|
|
|
253
|
+
/**
|
|
254
|
+
* A crash in the call UI must not take the chat down with it (web wraps
|
|
255
|
+
* the overlay in a silent ChatErrorBoundary). Renders nothing on error and
|
|
256
|
+
* clears itself on the next call.
|
|
257
|
+
*/
|
|
258
|
+
class CallErrorBoundary extends Component<
|
|
259
|
+
{ resetKey: string; children: ReactNode },
|
|
260
|
+
{ failed: boolean }
|
|
261
|
+
> {
|
|
262
|
+
state = { failed: false };
|
|
263
|
+
|
|
264
|
+
static getDerivedStateFromError() {
|
|
265
|
+
return { failed: true };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
componentDidUpdate(prev: { resetKey: string }) {
|
|
269
|
+
if (prev.resetKey !== this.props.resetKey && this.state.failed) {
|
|
270
|
+
this.setState({ failed: false });
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
componentDidCatch(error: unknown) {
|
|
275
|
+
console.warn('[calls] call UI crashed', error);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
render() {
|
|
279
|
+
return this.state.failed ? null : this.props.children;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
254
283
|
/**
|
|
255
284
|
* Call orchestration surface. Mounted once, above the chat, by XmppProvider,
|
|
256
285
|
* so an incoming call rings on whatever screen the user happens to be on
|
|
257
286
|
* rather than only inside <Chat>. Renders nothing while `call.phase` is
|
|
258
287
|
* 'idle'.
|
|
288
|
+
*
|
|
289
|
+
* The LiveKit connection itself lives in callSessionController.ts, so
|
|
290
|
+
* switching between the full-screen Modal and the minimized header strip
|
|
291
|
+
* (two different trees) no longer drops the call.
|
|
259
292
|
*/
|
|
260
|
-
|
|
293
|
+
const VideoCallOverlayInner: FC = () => {
|
|
261
294
|
const dispatch = useDispatch();
|
|
262
295
|
const call = useSelector((state: RootState) => state.call);
|
|
263
296
|
const { config } = useChatSettingState();
|
|
297
|
+
const theme = useTheme();
|
|
264
298
|
const t = useT();
|
|
265
299
|
|
|
266
300
|
const videoCallsConfig = config?.videoCalls;
|
|
267
301
|
const enabled = videoCallsConfig?.enabled === true;
|
|
268
302
|
const livekitUrl = (videoCallsConfig?.livekitUrl || '').trim();
|
|
269
|
-
const primaryColor = config?.colors?.primary || DEFAULT_PRIMARY;
|
|
303
|
+
const primaryColor = theme.primary || config?.colors?.primary || DEFAULT_PRIMARY;
|
|
270
304
|
|
|
271
305
|
const isOpen = enabled && call.phase !== 'idle';
|
|
272
306
|
|
|
@@ -275,7 +309,8 @@ export const VideoCallOverlay: FC = () => {
|
|
|
275
309
|
// fallback and always renders.
|
|
276
310
|
useCallKeep();
|
|
277
311
|
|
|
278
|
-
|
|
312
|
+
// Shared with the header strip (ActiveCallBar), which expands it again.
|
|
313
|
+
const { minimized } = useCallUi();
|
|
279
314
|
|
|
280
315
|
const canRenderSession =
|
|
281
316
|
!!call.token &&
|
|
@@ -283,18 +318,29 @@ export const VideoCallOverlay: FC = () => {
|
|
|
283
318
|
!!livekitUrl;
|
|
284
319
|
const isMinimized = minimized && canRenderSession;
|
|
285
320
|
|
|
286
|
-
//
|
|
287
|
-
//
|
|
321
|
+
// Join the LiveKit room once the call has a token. Idempotent, and the
|
|
322
|
+
// controller tears itself down when the call slice leaves these phases.
|
|
288
323
|
useEffect(() => {
|
|
289
|
-
if (call.
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
324
|
+
if (!enabled || !canRenderSession || !call.token) {return;}
|
|
325
|
+
startCallSession({
|
|
326
|
+
token: call.token,
|
|
327
|
+
livekitUrl,
|
|
328
|
+
kind: call.kind,
|
|
329
|
+
startWithMicOn: videoCallsConfig?.startWithMicOn,
|
|
330
|
+
startWithCameraOn: videoCallsConfig?.startWithCameraOn,
|
|
331
|
+
});
|
|
332
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
333
|
+
}, [enabled, canRenderSession, call.token, livekitUrl, call.kind]);
|
|
334
|
+
|
|
335
|
+
// Give up on an unanswered outgoing dial. Unlike web (which only shows
|
|
336
|
+
// the error), also tell the callee, who would otherwise keep ringing:
|
|
337
|
+
// there is no incoming ring timeout on either platform.
|
|
293
338
|
useEffect(() => {
|
|
294
339
|
if (call.phase !== 'requesting' || call.direction !== 'outgoing') {
|
|
295
340
|
return;
|
|
296
341
|
}
|
|
297
342
|
const timer = setTimeout(() => {
|
|
343
|
+
sendCallStateSignal('cancelled');
|
|
298
344
|
dispatch(setCallError(t('call.error.noAnswerTimeout')));
|
|
299
345
|
}, OUTGOING_CALL_TIMEOUT_MS);
|
|
300
346
|
return () => clearTimeout(timer);
|
|
@@ -311,53 +357,15 @@ export const VideoCallOverlay: FC = () => {
|
|
|
311
357
|
return () => Vibration.cancel();
|
|
312
358
|
}, [call.direction, call.phase]);
|
|
313
359
|
|
|
314
|
-
const terminateCall = useCallback(
|
|
315
|
-
(state: CallSignalState) => {
|
|
316
|
-
// Write a local "call ended" log entry BEFORE endCall() wipes the
|
|
317
|
-
// call slice. The authoritative entry is a server call-state
|
|
318
|
-
// broadcast, but that isn't guaranteed to arrive, so we synthesize
|
|
319
|
-
// our own. It's deduped against any server copy by callId in
|
|
320
|
-
// addRoomMessage.
|
|
321
|
-
const snap = store.getState().call;
|
|
322
|
-
if (snap.roomJid && (snap.callId || snap.connectedAt)) {
|
|
323
|
-
const durationMs = snap.connectedAt
|
|
324
|
-
? Math.max(0, Date.now() - snap.connectedAt)
|
|
325
|
-
: 0;
|
|
326
|
-
dispatch(
|
|
327
|
-
addRoomMessage({
|
|
328
|
-
roomJID: snap.roomJid,
|
|
329
|
-
message: buildLocalCallLogMessage({
|
|
330
|
-
callId: snap.callId || '',
|
|
331
|
-
direction:
|
|
332
|
-
snap.direction === 'incoming' ? 'incoming' : 'outgoing',
|
|
333
|
-
durationMs,
|
|
334
|
-
kind: snap.kind === 'audio' ? 'audio' : 'video',
|
|
335
|
-
selfXmppUsername:
|
|
336
|
-
store.getState().chatSettingStore.user?.xmppUsername || '',
|
|
337
|
-
}),
|
|
338
|
-
})
|
|
339
|
-
);
|
|
340
|
-
}
|
|
341
|
-
sendCallStateSignal(state);
|
|
342
|
-
dispatch(endCall());
|
|
343
|
-
},
|
|
344
|
-
[dispatch]
|
|
345
|
-
);
|
|
346
|
-
|
|
347
|
-
const declineCall = useCallback(() => {
|
|
348
|
-
sendCallStateSignal('declined');
|
|
349
|
-
dispatch(declineIncomingCall());
|
|
350
|
-
}, [dispatch]);
|
|
351
|
-
|
|
352
360
|
const hangupCurrent = useCallback(() => {
|
|
353
361
|
if (call.direction === 'incoming' && call.phase === 'ringing-incoming') {
|
|
354
|
-
|
|
362
|
+
declineActiveCall();
|
|
355
363
|
} else if (call.phase === 'error') {
|
|
356
364
|
dispatch(resetCall());
|
|
357
365
|
} else {
|
|
358
|
-
|
|
366
|
+
terminateActiveCall(call.phase === 'requesting' ? 'cancelled' : 'ended');
|
|
359
367
|
}
|
|
360
|
-
}, [call.direction, call.phase,
|
|
368
|
+
}, [call.direction, call.phase, dispatch]);
|
|
361
369
|
|
|
362
370
|
// Android hardware back = hang up, the RN equivalent of the web Esc
|
|
363
371
|
// handler. A minimized panel shouldn't swallow back: the user is using
|
|
@@ -402,30 +410,22 @@ export const VideoCallOverlay: FC = () => {
|
|
|
402
410
|
const showIncomingDecision =
|
|
403
411
|
call.direction === 'incoming' && call.phase === 'ringing-incoming';
|
|
404
412
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
minimized
|
|
422
|
-
onToggleMinimize={() => setMinimized((m) => !m)}
|
|
423
|
-
onConnected={() => dispatch(setCallPhase('in-call'))}
|
|
424
|
-
onError={(message) => dispatch(setCallError(message))}
|
|
425
|
-
onHangup={() => terminateCall('ended')}
|
|
426
|
-
/>
|
|
427
|
-
</View>
|
|
428
|
-
);
|
|
413
|
+
const session = (
|
|
414
|
+
<VideoCallSession
|
|
415
|
+
kind={call.kind}
|
|
416
|
+
peerName={call.roomName}
|
|
417
|
+
primaryColor={primaryColor}
|
|
418
|
+
icons={videoCallsConfig?.icons}
|
|
419
|
+
onToggleMinimize={() => setCallMinimized(!minimized)}
|
|
420
|
+
onHangup={() => terminateActiveCall('ended')}
|
|
421
|
+
/>
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
// Minimized, the call is a strip under the visible SDK header
|
|
425
|
+
// (HeaderCallBar), Telegram-style, so the chat stays fully usable. With
|
|
426
|
+
// no SDK header on screen the same strip docks to the top instead.
|
|
427
|
+
if (isMinimized) {
|
|
428
|
+
return <DockedCallBar />;
|
|
429
429
|
}
|
|
430
430
|
|
|
431
431
|
return (
|
|
@@ -434,122 +434,94 @@ export const VideoCallOverlay: FC = () => {
|
|
|
434
434
|
transparent
|
|
435
435
|
animationType="fade"
|
|
436
436
|
statusBarTranslucent
|
|
437
|
+
supportedOrientations={['portrait', 'landscape']}
|
|
437
438
|
onRequestClose={hangupCurrent}
|
|
438
439
|
>
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
<
|
|
447
|
-
|
|
448
|
-
|
|
440
|
+
{canRenderSession ? (
|
|
441
|
+
// Full-bleed: on a phone the call owns the screen, like the native
|
|
442
|
+
// dialer. The audio call keeps the same dark canvas as video.
|
|
443
|
+
<View style={styles.sessionFull}>{session}</View>
|
|
444
|
+
) : (
|
|
445
|
+
<View style={[styles.backdrop, { backgroundColor: theme.overlay }]}>
|
|
446
|
+
{showIncomingDecision ? (
|
|
447
|
+
<RingingCard
|
|
448
|
+
title={ringingHeader}
|
|
449
|
+
subtitle={ringingSubtitle}
|
|
450
|
+
roomName={call.roomName}
|
|
449
451
|
kind={call.kind}
|
|
450
|
-
|
|
452
|
+
variant="incoming"
|
|
451
453
|
primaryColor={primaryColor}
|
|
452
454
|
icons={videoCallsConfig?.icons}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
onToggleMinimize={() => setMinimized((m) => !m)}
|
|
457
|
-
onConnected={() => dispatch(setCallPhase('in-call'))}
|
|
458
|
-
onError={(message) => dispatch(setCallError(message))}
|
|
459
|
-
onHangup={() => terminateCall('ended')}
|
|
455
|
+
theme={theme}
|
|
456
|
+
onAccept={() => dispatch(acceptIncomingCall())}
|
|
457
|
+
onDecline={declineActiveCall}
|
|
460
458
|
/>
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
subtitle={ringingSubtitle}
|
|
492
|
-
roomName={call.roomName}
|
|
493
|
-
kind={call.kind}
|
|
494
|
-
variant="outgoing"
|
|
495
|
-
primaryColor={primaryColor}
|
|
496
|
-
icons={videoCallsConfig?.icons}
|
|
497
|
-
onHangup={() => terminateCall('cancelled')}
|
|
498
|
-
/>
|
|
499
|
-
)}
|
|
500
|
-
</View>
|
|
459
|
+
) : call.phase === 'error' ? (
|
|
460
|
+
<RingingCard
|
|
461
|
+
title={ringingHeader}
|
|
462
|
+
subtitle={ringingSubtitle}
|
|
463
|
+
roomName={call.roomName}
|
|
464
|
+
kind={call.kind}
|
|
465
|
+
variant="error"
|
|
466
|
+
primaryColor={primaryColor}
|
|
467
|
+
theme={theme}
|
|
468
|
+
errorMessage={
|
|
469
|
+
call.error ||
|
|
470
|
+
(!livekitUrl ? t('call.error.missingLivekitUrl') : null)
|
|
471
|
+
}
|
|
472
|
+
onDismiss={() => dispatch(resetCall())}
|
|
473
|
+
/>
|
|
474
|
+
) : (
|
|
475
|
+
<RingingCard
|
|
476
|
+
title={ringingHeader}
|
|
477
|
+
subtitle={ringingSubtitle}
|
|
478
|
+
roomName={call.roomName}
|
|
479
|
+
kind={call.kind}
|
|
480
|
+
variant="outgoing"
|
|
481
|
+
primaryColor={primaryColor}
|
|
482
|
+
icons={videoCallsConfig?.icons}
|
|
483
|
+
theme={theme}
|
|
484
|
+
onHangup={() => terminateActiveCall('cancelled')}
|
|
485
|
+
/>
|
|
486
|
+
)}
|
|
487
|
+
</View>
|
|
488
|
+
)}
|
|
501
489
|
</Modal>
|
|
502
490
|
);
|
|
503
491
|
};
|
|
504
492
|
|
|
493
|
+
export const VideoCallOverlay: FC = () => {
|
|
494
|
+
const callId = useSelector(
|
|
495
|
+
(state: RootState) => state.call.callId || String(state.call.startedAt || '')
|
|
496
|
+
);
|
|
497
|
+
return (
|
|
498
|
+
<CallErrorBoundary resetKey={callId}>
|
|
499
|
+
<VideoCallOverlayInner />
|
|
500
|
+
</CallErrorBoundary>
|
|
501
|
+
);
|
|
502
|
+
};
|
|
503
|
+
|
|
505
504
|
const styles = StyleSheet.create({
|
|
506
505
|
backdrop: {
|
|
507
506
|
flex: 1,
|
|
508
|
-
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
509
507
|
alignItems: 'center',
|
|
510
508
|
justifyContent: 'center',
|
|
511
509
|
padding: 16,
|
|
512
510
|
},
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
borderRadius: 24,
|
|
517
|
-
backgroundColor: '#fff',
|
|
518
|
-
overflow: 'hidden',
|
|
519
|
-
},
|
|
520
|
-
// Audio calls don't need the full video canvas: a fixed tall card leaves
|
|
521
|
-
// a dead gap around the avatar and strands the controls far from
|
|
522
|
-
// anything else, so this one shrink-wraps its content.
|
|
523
|
-
audioSessionCard: {
|
|
524
|
-
width: '100%',
|
|
525
|
-
maxHeight: 600,
|
|
526
|
-
flexGrow: 0,
|
|
527
|
-
flexShrink: 1,
|
|
528
|
-
minHeight: 420,
|
|
529
|
-
borderRadius: 24,
|
|
530
|
-
backgroundColor: '#fff',
|
|
531
|
-
overflow: 'hidden',
|
|
532
|
-
},
|
|
533
|
-
floatingPanel: {
|
|
534
|
-
position: 'absolute',
|
|
535
|
-
right: 16,
|
|
536
|
-
bottom: 24,
|
|
537
|
-
left: 16,
|
|
538
|
-
zIndex: 1001,
|
|
539
|
-
elevation: 12,
|
|
511
|
+
sessionFull: {
|
|
512
|
+
flex: 1,
|
|
513
|
+
backgroundColor: '#0B1220',
|
|
540
514
|
},
|
|
541
515
|
ringingCard: {
|
|
542
516
|
width: '100%',
|
|
543
517
|
maxWidth: 420,
|
|
544
518
|
padding: 32,
|
|
545
519
|
borderRadius: 24,
|
|
546
|
-
backgroundColor: '#fff',
|
|
547
520
|
alignItems: 'center',
|
|
548
521
|
gap: 24,
|
|
549
522
|
},
|
|
550
523
|
ringingTitle: {
|
|
551
524
|
fontSize: 14,
|
|
552
|
-
color: TEXT_MUTED,
|
|
553
525
|
letterSpacing: 0.2,
|
|
554
526
|
},
|
|
555
527
|
avatarWrap: {
|
|
@@ -571,7 +543,6 @@ const styles = StyleSheet.create({
|
|
|
571
543
|
},
|
|
572
544
|
ringingName: {
|
|
573
545
|
fontSize: 24,
|
|
574
|
-
color: TEXT_PRIMARY,
|
|
575
546
|
textAlign: 'center',
|
|
576
547
|
},
|
|
577
548
|
ringingSubtitleRow: {
|
|
@@ -580,7 +551,6 @@ const styles = StyleSheet.create({
|
|
|
580
551
|
gap: 6,
|
|
581
552
|
},
|
|
582
553
|
ringingSubtitle: {
|
|
583
|
-
color: TEXT_MUTED,
|
|
584
554
|
fontSize: 14,
|
|
585
555
|
},
|
|
586
556
|
errorBox: {
|