@ethora/chat-component-rn 26.10.1 → 26.10.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/lib/commonjs/components/MainComponents/ChatHeader.js +11 -10
- 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/CallButtons.js +3 -1
- package/lib/commonjs/components/VideoCalls/CallButtons.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js +123 -111
- package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/VideoCallSession.js +207 -346
- 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 +431 -0
- package/lib/commonjs/components/VideoCalls/callSessionController.js.map +1 -0
- 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/hooks/useLogout.js +15 -10
- package/lib/commonjs/hooks/useLogout.js.map +1 -1
- package/lib/commonjs/i18n/strings.js +18 -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/MainComponents/ChatHeader.js +13 -12
- 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/CallButtons.js +3 -1
- package/lib/module/components/VideoCalls/CallButtons.js.map +1 -1
- package/lib/module/components/VideoCalls/VideoCallOverlay.js +125 -113
- package/lib/module/components/VideoCalls/VideoCallOverlay.js.map +1 -1
- package/lib/module/components/VideoCalls/VideoCallSession.js +208 -348
- 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 +420 -0
- package/lib/module/components/VideoCalls/callSessionController.js.map +1 -0
- 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/hooks/useLogout.js +15 -10
- package/lib/module/hooks/useLogout.js.map +1 -1
- package/lib/module/i18n/strings.js +18 -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/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/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 +9 -20
- 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 +61 -0
- package/lib/typescript/components/VideoCalls/callSessionController.d.ts.map +1 -0
- 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/hooks/useLogout.d.ts.map +1 -1
- 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/MainComponents/ChatHeader.tsx +10 -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/CallButtons.tsx +5 -1
- package/src/components/VideoCalls/VideoCallOverlay.tsx +156 -163
- package/src/components/VideoCalls/VideoCallSession.tsx +207 -378
- package/src/components/VideoCalls/callActions.ts +63 -0
- package/src/components/VideoCalls/callSessionController.ts +421 -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/hooks/useLogout.tsx +15 -10
- package/src/i18n/strings.ts +18 -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
|
@@ -71,7 +71,11 @@ export const CallButtons: FC = () => {
|
|
|
71
71
|
// Don't show "deleted" / "Deleted User" / "Unknown" as the dial
|
|
72
72
|
// screen title when the chat title is one of the server sentinels.
|
|
73
73
|
// Prefer the peer's first+last name, falling back to the bare chat name.
|
|
74
|
-
|
|
74
|
+
// `title` is what the header shows ("John Pauliller"); on new-arch
|
|
75
|
+
// private rooms `name` is the bare room id.
|
|
76
|
+
const rawTitle = String(
|
|
77
|
+
(currentRoom as any).title || currentRoom.name || ''
|
|
78
|
+
).trim();
|
|
75
79
|
const isBadTitle =
|
|
76
80
|
!rawTitle ||
|
|
77
81
|
['deleted', 'deleted user', 'unknown', 'null'].includes(
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import React, {
|
|
2
|
+
Component,
|
|
2
3
|
FC,
|
|
4
|
+
ReactNode,
|
|
3
5
|
useCallback,
|
|
4
6
|
useEffect,
|
|
5
7
|
useMemo,
|
|
@@ -19,23 +21,20 @@ import {
|
|
|
19
21
|
View,
|
|
20
22
|
} from 'react-native';
|
|
21
23
|
import { useDispatch, useSelector } from 'react-redux';
|
|
22
|
-
import { RootState
|
|
24
|
+
import { RootState } from '../../roomStore';
|
|
23
25
|
import {
|
|
24
26
|
acceptIncomingCall,
|
|
25
|
-
declineIncomingCall,
|
|
26
|
-
endCall,
|
|
27
27
|
resetCall,
|
|
28
28
|
setCallError,
|
|
29
|
-
setCallPhase,
|
|
30
29
|
} from '../../roomStore/callSlice';
|
|
31
|
-
import { addRoomMessage } from '../../roomStore/roomsSlice';
|
|
32
|
-
import { buildLocalCallLogMessage } from '../../helpers/callLogMessage';
|
|
33
30
|
import { useChatSettingState } from '../../hooks/useChatSettingState';
|
|
34
|
-
import {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
} from '../../networking/callTokenStanza';
|
|
31
|
+
import { sendCallStateSignal } from '../../networking/callTokenStanza';
|
|
32
|
+
import { useTheme } from '../../hooks/useTheme';
|
|
33
|
+
import type { ChatTheme } from '../../theme/theme';
|
|
38
34
|
import { VideoCallSession } from './VideoCallSession';
|
|
35
|
+
import { startCallSession } from './callSessionController';
|
|
36
|
+
import { declineActiveCall, terminateActiveCall } from './callActions';
|
|
37
|
+
import { useDraggable } from './useDraggable';
|
|
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,58 @@ 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 panel (two
|
|
291
|
+
* different trees here) 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();
|
|
299
|
+
const panel = useDraggable();
|
|
265
300
|
|
|
266
301
|
const videoCallsConfig = config?.videoCalls;
|
|
267
302
|
const enabled = videoCallsConfig?.enabled === true;
|
|
268
303
|
const livekitUrl = (videoCallsConfig?.livekitUrl || '').trim();
|
|
269
|
-
const primaryColor = config?.colors?.primary || DEFAULT_PRIMARY;
|
|
304
|
+
const primaryColor = theme.primary || config?.colors?.primary || DEFAULT_PRIMARY;
|
|
270
305
|
|
|
271
306
|
const isOpen = enabled && call.phase !== 'idle';
|
|
272
307
|
|
|
@@ -283,18 +318,35 @@ export const VideoCallOverlay: FC = () => {
|
|
|
283
318
|
!!livekitUrl;
|
|
284
319
|
const isMinimized = minimized && canRenderSession;
|
|
285
320
|
|
|
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.
|
|
323
|
+
useEffect(() => {
|
|
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
|
+
|
|
286
335
|
// Reset the minimized flag whenever the call ends, so the next call
|
|
287
336
|
// opens full-screen.
|
|
288
337
|
useEffect(() => {
|
|
289
338
|
if (call.phase === 'idle') {setMinimized(false);}
|
|
290
339
|
}, [call.phase]);
|
|
291
340
|
|
|
292
|
-
// Give up on an unanswered outgoing dial.
|
|
341
|
+
// Give up on an unanswered outgoing dial. Unlike web (which only shows
|
|
342
|
+
// the error), also tell the callee, who would otherwise keep ringing:
|
|
343
|
+
// there is no incoming ring timeout on either platform.
|
|
293
344
|
useEffect(() => {
|
|
294
345
|
if (call.phase !== 'requesting' || call.direction !== 'outgoing') {
|
|
295
346
|
return;
|
|
296
347
|
}
|
|
297
348
|
const timer = setTimeout(() => {
|
|
349
|
+
sendCallStateSignal('cancelled');
|
|
298
350
|
dispatch(setCallError(t('call.error.noAnswerTimeout')));
|
|
299
351
|
}, OUTGOING_CALL_TIMEOUT_MS);
|
|
300
352
|
return () => clearTimeout(timer);
|
|
@@ -311,53 +363,15 @@ export const VideoCallOverlay: FC = () => {
|
|
|
311
363
|
return () => Vibration.cancel();
|
|
312
364
|
}, [call.direction, call.phase]);
|
|
313
365
|
|
|
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
366
|
const hangupCurrent = useCallback(() => {
|
|
353
367
|
if (call.direction === 'incoming' && call.phase === 'ringing-incoming') {
|
|
354
|
-
|
|
368
|
+
declineActiveCall();
|
|
355
369
|
} else if (call.phase === 'error') {
|
|
356
370
|
dispatch(resetCall());
|
|
357
371
|
} else {
|
|
358
|
-
|
|
372
|
+
terminateActiveCall(call.phase === 'requesting' ? 'cancelled' : 'ended');
|
|
359
373
|
}
|
|
360
|
-
}, [call.direction, call.phase,
|
|
374
|
+
}, [call.direction, call.phase, dispatch]);
|
|
361
375
|
|
|
362
376
|
// Android hardware back = hang up, the RN equivalent of the web Esc
|
|
363
377
|
// handler. A minimized panel shouldn't swallow back: the user is using
|
|
@@ -402,29 +416,30 @@ export const VideoCallOverlay: FC = () => {
|
|
|
402
416
|
const showIncomingDecision =
|
|
403
417
|
call.direction === 'incoming' && call.phase === 'ringing-incoming';
|
|
404
418
|
|
|
419
|
+
const session = (
|
|
420
|
+
<VideoCallSession
|
|
421
|
+
kind={call.kind}
|
|
422
|
+
peerName={call.roomName}
|
|
423
|
+
primaryColor={primaryColor}
|
|
424
|
+
icons={videoCallsConfig?.icons}
|
|
425
|
+
minimized={isMinimized}
|
|
426
|
+
onToggleMinimize={() => setMinimized((m) => !m)}
|
|
427
|
+
onHangup={() => terminateActiveCall('ended')}
|
|
428
|
+
/>
|
|
429
|
+
);
|
|
430
|
+
|
|
405
431
|
// The minimized session is NOT a Modal: a modal is by definition on top
|
|
406
432
|
// of everything and swallows touches, which is the opposite of what
|
|
407
433
|
// minimize is for (keep talking, keep using the chat). It renders as a
|
|
408
|
-
// floating
|
|
409
|
-
if (
|
|
434
|
+
// floating bar the user can drag out of the way, like web's panel.
|
|
435
|
+
if (isMinimized) {
|
|
410
436
|
return (
|
|
411
|
-
<View
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
primaryColor={primaryColor}
|
|
418
|
-
icons={videoCallsConfig?.icons}
|
|
419
|
-
startWithCameraOn={videoCallsConfig?.startWithCameraOn}
|
|
420
|
-
startWithMicOn={videoCallsConfig?.startWithMicOn}
|
|
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>
|
|
437
|
+
<Animated.View
|
|
438
|
+
{...panel.panHandlers}
|
|
439
|
+
style={[styles.floatingPanel, panel.style]}
|
|
440
|
+
>
|
|
441
|
+
{session}
|
|
442
|
+
</Animated.View>
|
|
428
443
|
);
|
|
429
444
|
}
|
|
430
445
|
|
|
@@ -434,101 +449,83 @@ export const VideoCallOverlay: FC = () => {
|
|
|
434
449
|
transparent
|
|
435
450
|
animationType="fade"
|
|
436
451
|
statusBarTranslucent
|
|
452
|
+
supportedOrientations={['portrait', 'landscape']}
|
|
437
453
|
onRequestClose={hangupCurrent}
|
|
438
454
|
>
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
<
|
|
447
|
-
|
|
448
|
-
|
|
455
|
+
{canRenderSession ? (
|
|
456
|
+
// Full-bleed: on a phone the call owns the screen, like the native
|
|
457
|
+
// dialer. The audio call keeps the same dark canvas as video.
|
|
458
|
+
<View style={styles.sessionFull}>{session}</View>
|
|
459
|
+
) : (
|
|
460
|
+
<View style={[styles.backdrop, { backgroundColor: theme.overlay }]}>
|
|
461
|
+
{showIncomingDecision ? (
|
|
462
|
+
<RingingCard
|
|
463
|
+
title={ringingHeader}
|
|
464
|
+
subtitle={ringingSubtitle}
|
|
465
|
+
roomName={call.roomName}
|
|
449
466
|
kind={call.kind}
|
|
450
|
-
|
|
467
|
+
variant="incoming"
|
|
451
468
|
primaryColor={primaryColor}
|
|
452
469
|
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')}
|
|
470
|
+
theme={theme}
|
|
471
|
+
onAccept={() => dispatch(acceptIncomingCall())}
|
|
472
|
+
onDecline={declineActiveCall}
|
|
460
473
|
/>
|
|
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>
|
|
474
|
+
) : call.phase === 'error' ? (
|
|
475
|
+
<RingingCard
|
|
476
|
+
title={ringingHeader}
|
|
477
|
+
subtitle={ringingSubtitle}
|
|
478
|
+
roomName={call.roomName}
|
|
479
|
+
kind={call.kind}
|
|
480
|
+
variant="error"
|
|
481
|
+
primaryColor={primaryColor}
|
|
482
|
+
theme={theme}
|
|
483
|
+
errorMessage={
|
|
484
|
+
call.error ||
|
|
485
|
+
(!livekitUrl ? t('call.error.missingLivekitUrl') : null)
|
|
486
|
+
}
|
|
487
|
+
onDismiss={() => dispatch(resetCall())}
|
|
488
|
+
/>
|
|
489
|
+
) : (
|
|
490
|
+
<RingingCard
|
|
491
|
+
title={ringingHeader}
|
|
492
|
+
subtitle={ringingSubtitle}
|
|
493
|
+
roomName={call.roomName}
|
|
494
|
+
kind={call.kind}
|
|
495
|
+
variant="outgoing"
|
|
496
|
+
primaryColor={primaryColor}
|
|
497
|
+
icons={videoCallsConfig?.icons}
|
|
498
|
+
theme={theme}
|
|
499
|
+
onHangup={() => terminateActiveCall('cancelled')}
|
|
500
|
+
/>
|
|
501
|
+
)}
|
|
502
|
+
</View>
|
|
503
|
+
)}
|
|
501
504
|
</Modal>
|
|
502
505
|
);
|
|
503
506
|
};
|
|
504
507
|
|
|
508
|
+
export const VideoCallOverlay: FC = () => {
|
|
509
|
+
const callId = useSelector(
|
|
510
|
+
(state: RootState) => state.call.callId || String(state.call.startedAt || '')
|
|
511
|
+
);
|
|
512
|
+
return (
|
|
513
|
+
<CallErrorBoundary resetKey={callId}>
|
|
514
|
+
<VideoCallOverlayInner />
|
|
515
|
+
</CallErrorBoundary>
|
|
516
|
+
);
|
|
517
|
+
};
|
|
518
|
+
|
|
505
519
|
const styles = StyleSheet.create({
|
|
506
520
|
backdrop: {
|
|
507
521
|
flex: 1,
|
|
508
|
-
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
509
522
|
alignItems: 'center',
|
|
510
523
|
justifyContent: 'center',
|
|
511
524
|
padding: 16,
|
|
512
525
|
},
|
|
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',
|
|
526
|
+
sessionFull: {
|
|
527
|
+
flex: 1,
|
|
528
|
+
backgroundColor: '#0B1220',
|
|
532
529
|
},
|
|
533
530
|
floatingPanel: {
|
|
534
531
|
position: 'absolute',
|
|
@@ -543,13 +540,11 @@ const styles = StyleSheet.create({
|
|
|
543
540
|
maxWidth: 420,
|
|
544
541
|
padding: 32,
|
|
545
542
|
borderRadius: 24,
|
|
546
|
-
backgroundColor: '#fff',
|
|
547
543
|
alignItems: 'center',
|
|
548
544
|
gap: 24,
|
|
549
545
|
},
|
|
550
546
|
ringingTitle: {
|
|
551
547
|
fontSize: 14,
|
|
552
|
-
color: TEXT_MUTED,
|
|
553
548
|
letterSpacing: 0.2,
|
|
554
549
|
},
|
|
555
550
|
avatarWrap: {
|
|
@@ -571,7 +566,6 @@ const styles = StyleSheet.create({
|
|
|
571
566
|
},
|
|
572
567
|
ringingName: {
|
|
573
568
|
fontSize: 24,
|
|
574
|
-
color: TEXT_PRIMARY,
|
|
575
569
|
textAlign: 'center',
|
|
576
570
|
},
|
|
577
571
|
ringingSubtitleRow: {
|
|
@@ -580,7 +574,6 @@ const styles = StyleSheet.create({
|
|
|
580
574
|
gap: 6,
|
|
581
575
|
},
|
|
582
576
|
ringingSubtitle: {
|
|
583
|
-
color: TEXT_MUTED,
|
|
584
577
|
fontSize: 14,
|
|
585
578
|
},
|
|
586
579
|
errorBox: {
|