@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,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React, {
|
|
4
|
-
import { ActivityIndicator,
|
|
3
|
+
import React, { useEffect, useState } from 'react';
|
|
4
|
+
import { ActivityIndicator, Animated, Pressable, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
|
5
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
5
6
|
import { ProfileImagePlaceholder } from '../MainComponents/ProfileImagePlaceholder';
|
|
6
|
-
import { CameraOffIcon, CameraOnIcon, HangUpIcon, MicOffIcon, MicOnIcon, MinimizeIcon,
|
|
7
|
-
import {
|
|
7
|
+
import { CameraOffIcon, CameraOnIcon, HangUpIcon, MicOffIcon, MicOnIcon, MinimizeIcon, SpeakerIcon, SpeakerOffIcon, SwitchCameraIcon } from './CallIcons';
|
|
8
|
+
import { loadLiveKit } from './livekitRuntime';
|
|
9
|
+
import { dismissCallDeviceError, switchCallCamera, toggleCallCamera, toggleCallMic, toggleCallSpeaker, useCallSession } from './callSessionController';
|
|
10
|
+
import { useDraggable } from './useDraggable';
|
|
8
11
|
import { useT } from '../../i18n/useT';
|
|
9
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
13
|
const DEFAULT_PRIMARY = '#0052CD';
|
|
@@ -13,329 +16,60 @@ const TEXT_ON_DARK = '#FFFFFF';
|
|
|
13
16
|
const TEXT_MUTED = '#9AA4B2';
|
|
14
17
|
const DANGER = '#E53935';
|
|
15
18
|
const CONTROL_IDLE = 'rgba(255, 255, 255, 0.16)';
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (name === 'NotReadableError' || text.includes('in use')) {
|
|
33
|
-
return t('call.error.deviceInUse');
|
|
19
|
+
/**
|
|
20
|
+
* Seconds since the call truly connected, counted from the server-set
|
|
21
|
+
* `joinedAt` anchor so both sides show the same time (web's
|
|
22
|
+
* useSharedCallElapsed). Null until the peer is in.
|
|
23
|
+
*/
|
|
24
|
+
export const useSharedCallElapsed = anchorAt => {
|
|
25
|
+
const [, tick] = useState(0);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (anchorAt === null) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const id = setInterval(() => tick(n => n + 1), 1000);
|
|
31
|
+
return () => clearInterval(id);
|
|
32
|
+
}, [anchorAt]);
|
|
33
|
+
if (anchorAt === null) {
|
|
34
|
+
return null;
|
|
34
35
|
}
|
|
35
|
-
return
|
|
36
|
+
return Math.max(0, Math.floor((Date.now() - anchorAt) / 1000));
|
|
36
37
|
};
|
|
38
|
+
export const formatCallDuration = totalSeconds => {
|
|
39
|
+
const safe = Math.max(0, Math.floor(totalSeconds));
|
|
40
|
+
const hours = Math.floor(safe / 3600);
|
|
41
|
+
const minutes = Math.floor(safe % 3600 / 60);
|
|
42
|
+
const seconds = safe % 60;
|
|
43
|
+
const mm = String(minutes).padStart(2, '0');
|
|
44
|
+
const ss = String(seconds).padStart(2, '0');
|
|
45
|
+
return hours > 0 ? `${hours}:${mm}:${ss}` : `${mm}:${ss}`;
|
|
46
|
+
};
|
|
47
|
+
|
|
37
48
|
/**
|
|
38
|
-
* The
|
|
49
|
+
* The call screen, React Native edition. Pure view over the LiveKit session
|
|
50
|
+
* owned by callSessionController.ts: it can unmount and remount (minimize,
|
|
51
|
+
* leaving the chat screen) without touching the connection.
|
|
39
52
|
*
|
|
40
|
-
* The web SDK builds this on `@livekit/components-react` hooks
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* connection state) in local React state, re-syncing on RoomEvent. Only the
|
|
44
|
-
* actual video surface comes from `@livekit/react-native` (`<VideoTrack />`),
|
|
45
|
-
* which wraps the native renderer.
|
|
46
|
-
*
|
|
47
|
-
* Requires `@livekit/react-native` >= 2.7 for the `trackRef` prop shape used
|
|
48
|
-
* below. Both LiveKit packages are optional peer deps, see livekitRuntime.ts.
|
|
53
|
+
* The web SDK builds this on `@livekit/components-react` hooks, which
|
|
54
|
+
* assume a DOM. Only the video surface comes from `@livekit/react-native`
|
|
55
|
+
* (`<VideoTrack />`, `trackRef` prop, >= 2.7).
|
|
49
56
|
*/
|
|
50
57
|
export const VideoCallSession = ({
|
|
51
|
-
token,
|
|
52
|
-
livekitUrl,
|
|
53
58
|
kind = 'video',
|
|
54
59
|
peerName,
|
|
55
60
|
primaryColor = DEFAULT_PRIMARY,
|
|
56
61
|
icons,
|
|
57
|
-
startWithCameraOn = true,
|
|
58
|
-
startWithMicOn = true,
|
|
59
|
-
minimized = false,
|
|
60
62
|
onToggleMinimize,
|
|
61
|
-
onConnected,
|
|
62
|
-
onError,
|
|
63
63
|
onHangup
|
|
64
64
|
}) => {
|
|
65
65
|
const t = useT();
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
const [
|
|
73
|
-
const [remoteVideo, setRemoteVideo] = useState(null);
|
|
74
|
-
const [localVideo, setLocalVideo] = useState(null);
|
|
75
|
-
const [remoteName, setRemoteName] = useState(null);
|
|
76
|
-
const [remoteJoined, setRemoteJoined] = useState(false);
|
|
77
|
-
|
|
78
|
-
// Callbacks are read through a ref inside the connect effect so that a
|
|
79
|
-
// parent re-render (new inline arrow props) never re-runs connect and
|
|
80
|
-
// tears down a live call. Same guard the web session uses.
|
|
81
|
-
const callbacksRef = useRef({
|
|
82
|
-
onConnected,
|
|
83
|
-
onError,
|
|
84
|
-
onHangup
|
|
85
|
-
});
|
|
86
|
-
useEffect(() => {
|
|
87
|
-
callbacksRef.current = {
|
|
88
|
-
onConnected,
|
|
89
|
-
onError,
|
|
90
|
-
onHangup
|
|
91
|
-
};
|
|
92
|
-
}, [onConnected, onError, onHangup]);
|
|
93
|
-
|
|
94
|
-
// ---- track plumbing -------------------------------------------------
|
|
95
|
-
|
|
96
|
-
// Build the { participant, publication, source } shape `<VideoTrack />`
|
|
97
|
-
// expects, from whatever camera publication a participant currently has.
|
|
98
|
-
const toTrackRef = useCallback((participant, Track) => {
|
|
99
|
-
if (!participant) {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
const publication = participant.getTrackPublication?.(Track.Source.Camera);
|
|
103
|
-
if (!publication?.track) {
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
return {
|
|
107
|
-
participant,
|
|
108
|
-
publication,
|
|
109
|
-
source: Track.Source.Camera
|
|
110
|
-
};
|
|
111
|
-
}, []);
|
|
112
|
-
const resyncTracks = useCallback(() => {
|
|
113
|
-
const room = roomRef.current;
|
|
114
|
-
if (!room || !runtime) {
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
const {
|
|
118
|
-
Track
|
|
119
|
-
} = runtime;
|
|
120
|
-
const remote = Array.from(room.remoteParticipants?.values?.() || [])[0];
|
|
121
|
-
setRemoteJoined(!!remote);
|
|
122
|
-
setRemoteName(remote?.name || remote?.identity || null);
|
|
123
|
-
setRemoteVideo(toTrackRef(remote, Track));
|
|
124
|
-
setLocalVideo(toTrackRef(room.localParticipant, Track));
|
|
125
|
-
}, [runtime, toTrackRef]);
|
|
126
|
-
|
|
127
|
-
// ---- connect / teardown ---------------------------------------------
|
|
128
|
-
|
|
129
|
-
useEffect(() => {
|
|
130
|
-
if (!runtime) {
|
|
131
|
-
callbacksRef.current.onError?.(t('call.error.sdkMissing'));
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
if (!token || !livekitUrl) {
|
|
135
|
-
callbacksRef.current.onError?.(t('call.error.missingConfig'));
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
ensureLiveKitGlobals(runtime);
|
|
139
|
-
const {
|
|
140
|
-
Room,
|
|
141
|
-
RoomEvent,
|
|
142
|
-
AudioSession
|
|
143
|
-
} = runtime;
|
|
144
|
-
let disposed = false;
|
|
145
|
-
|
|
146
|
-
// adaptiveStream + dynacast keep mobile data and battery sane: the SDK
|
|
147
|
-
// drops resolution for a small tile and stops publishing layers nobody
|
|
148
|
-
// subscribes to. Both are safe defaults for 1:1.
|
|
149
|
-
const room = new Room({
|
|
150
|
-
adaptiveStream: true,
|
|
151
|
-
dynacast: true
|
|
152
|
-
});
|
|
153
|
-
roomRef.current = room;
|
|
154
|
-
const onStateChange = () => {
|
|
155
|
-
if (disposed) {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
resyncTracks();
|
|
159
|
-
};
|
|
160
|
-
room.on(RoomEvent.TrackSubscribed, onStateChange).on(RoomEvent.TrackUnsubscribed, onStateChange).on(RoomEvent.LocalTrackPublished, onStateChange).on(RoomEvent.LocalTrackUnpublished, onStateChange).on(RoomEvent.TrackMuted, onStateChange).on(RoomEvent.TrackUnmuted, onStateChange).on(RoomEvent.ParticipantConnected, onStateChange).on(RoomEvent.MediaDevicesError, error => {
|
|
161
|
-
if (disposed) {
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
setDeviceError(humanizeDeviceError(error, t));
|
|
165
|
-
}).on(RoomEvent.ParticipantDisconnected, () => {
|
|
166
|
-
if (disposed) {
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
resyncTracks();
|
|
170
|
-
// 1:1 semantics: the other side leaving IS the end of the call.
|
|
171
|
-
// Without this the local user sits on a black canvas until they
|
|
172
|
-
// hang up manually.
|
|
173
|
-
callbacksRef.current.onHangup?.();
|
|
174
|
-
}).on(RoomEvent.Disconnected, () => {
|
|
175
|
-
if (disposed) {
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
setConnected(false);
|
|
179
|
-
callbacksRef.current.onHangup?.();
|
|
180
|
-
});
|
|
181
|
-
(async () => {
|
|
182
|
-
// Joining the LiveKit room is the only step whose failure should fail
|
|
183
|
-
// the call (bad token, network, server down). Everything after this
|
|
184
|
-
// is best-effort device setup — see the separate try/catches below.
|
|
185
|
-
try {
|
|
186
|
-
// Claims audio focus and sets the right native audio mode. Must
|
|
187
|
-
// start BEFORE connecting or the first seconds route to the
|
|
188
|
-
// earpiece on Android even for a video call.
|
|
189
|
-
await AudioSession.startAudioSession();
|
|
190
|
-
await room.connect(livekitUrl, token);
|
|
191
|
-
} catch (error) {
|
|
192
|
-
if (disposed) {
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
callbacksRef.current.onError?.(String(error?.message || '') || t('call.error.connectFailed'));
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
if (disposed) {
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Devices are best-effort: if the mic/camera prompt is blocked, or —
|
|
203
|
-
// notably — this is the iOS Simulator (no real camera hardware at
|
|
204
|
-
// all), the call still connects. The user can hear/be heard on
|
|
205
|
-
// audio and re-enable the camera later; failing the whole call here
|
|
206
|
-
// (as an earlier version did, sharing one try/catch with `connect`)
|
|
207
|
-
// meant every video call died before ever reaching the ring-answered
|
|
208
|
-
// screen — indistinguishable from "Accept doesn't work".
|
|
209
|
-
try {
|
|
210
|
-
await room.localParticipant.setMicrophoneEnabled(startWithMicOn);
|
|
211
|
-
} catch (error) {
|
|
212
|
-
if (!disposed) {
|
|
213
|
-
setDeviceError(humanizeDeviceError(error, t));
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
if (kind === 'video') {
|
|
217
|
-
try {
|
|
218
|
-
await room.localParticipant.setCameraEnabled(startWithCameraOn);
|
|
219
|
-
} catch (error) {
|
|
220
|
-
if (!disposed) {
|
|
221
|
-
setDeviceError(humanizeDeviceError(error, t));
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
if (disposed) {
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Video calls belong on the loudspeaker, audio calls on the
|
|
230
|
-
// earpiece, matching what every native dialer does.
|
|
231
|
-
try {
|
|
232
|
-
await AudioSession.setAppleAudioConfiguration?.({
|
|
233
|
-
audioCategory: 'playAndRecord',
|
|
234
|
-
audioMode: kind === 'video' ? 'videoChat' : 'voiceChat'
|
|
235
|
-
});
|
|
236
|
-
} catch {
|
|
237
|
-
// iOS-only helper, absent on Android builds of the SDK.
|
|
238
|
-
}
|
|
239
|
-
setConnected(true);
|
|
240
|
-
resyncTracks();
|
|
241
|
-
callbacksRef.current.onConnected?.();
|
|
242
|
-
})();
|
|
243
|
-
return () => {
|
|
244
|
-
disposed = true;
|
|
245
|
-
try {
|
|
246
|
-
room.removeAllListeners?.();
|
|
247
|
-
void room.disconnect();
|
|
248
|
-
} catch {
|
|
249
|
-
// Already gone.
|
|
250
|
-
}
|
|
251
|
-
try {
|
|
252
|
-
void AudioSession.stopAudioSession();
|
|
253
|
-
} catch {
|
|
254
|
-
// Never let teardown throw, it runs during unmount.
|
|
255
|
-
}
|
|
256
|
-
roomRef.current = null;
|
|
257
|
-
};
|
|
258
|
-
// Connect exactly once per (url, token, kind). Everything else is read
|
|
259
|
-
// through refs, see callbacksRef above.
|
|
260
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
261
|
-
}, [runtime, livekitUrl, token, kind]);
|
|
262
|
-
|
|
263
|
-
// ---- controls --------------------------------------------------------
|
|
264
|
-
|
|
265
|
-
const toggleMic = useCallback(async () => {
|
|
266
|
-
const room = roomRef.current;
|
|
267
|
-
if (!room) {
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
const next = !micOn;
|
|
271
|
-
setMicOn(next);
|
|
272
|
-
try {
|
|
273
|
-
await room.localParticipant.setMicrophoneEnabled(next);
|
|
274
|
-
} catch {
|
|
275
|
-
setMicOn(!next);
|
|
276
|
-
}
|
|
277
|
-
}, [micOn]);
|
|
278
|
-
const toggleCamera = useCallback(async () => {
|
|
279
|
-
const room = roomRef.current;
|
|
280
|
-
if (!room) {
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
const next = !cameraOn;
|
|
284
|
-
setCameraOn(next);
|
|
285
|
-
try {
|
|
286
|
-
await room.localParticipant.setCameraEnabled(next);
|
|
287
|
-
resyncTracks();
|
|
288
|
-
} catch {
|
|
289
|
-
setCameraOn(!next);
|
|
290
|
-
}
|
|
291
|
-
}, [cameraOn, resyncTracks]);
|
|
292
|
-
const switchCamera = useCallback(async () => {
|
|
293
|
-
const room = roomRef.current;
|
|
294
|
-
if (!room || !runtime) {
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
try {
|
|
298
|
-
const publication = room.localParticipant.getTrackPublication?.(runtime.Track.Source.Camera);
|
|
299
|
-
const track = publication?.track;
|
|
300
|
-
// `restartTrack` with the flipped facingMode is the portable way to
|
|
301
|
-
// switch cameras; the RN SDK's `_switchCamera` helper is internal and
|
|
302
|
-
// has moved between versions.
|
|
303
|
-
await track?.restartTrack?.({
|
|
304
|
-
facingMode: track?.mediaStreamTrack?.getSettings?.().facingMode === 'environment' ? 'user' : 'environment'
|
|
305
|
-
});
|
|
306
|
-
resyncTracks();
|
|
307
|
-
} catch {
|
|
308
|
-
// Device has a single camera, or the track is mid-restart.
|
|
309
|
-
}
|
|
310
|
-
}, [runtime, resyncTracks]);
|
|
311
|
-
const toggleSpeaker = useCallback(async () => {
|
|
312
|
-
const next = !speakerOn;
|
|
313
|
-
setSpeakerOn(next);
|
|
314
|
-
if (!runtime) {
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
try {
|
|
318
|
-
if (Platform.OS === 'android') {
|
|
319
|
-
// NB: not `setAndroidAudioConfiguration` — that method has never
|
|
320
|
-
// existed on AudioSession (the old `?.` call silently no-oped, so
|
|
321
|
-
// the speaker icon toggled while audio stayed on the earpiece).
|
|
322
|
-
// `selectAudioOutput` routes directly; on Android its device ids
|
|
323
|
-
// are the literal strings 'speaker' / 'earpiece'.
|
|
324
|
-
await runtime.AudioSession.selectAudioOutput(next ? 'speaker' : 'earpiece');
|
|
325
|
-
} else {
|
|
326
|
-
await runtime.AudioSession.setAppleAudioConfiguration?.({
|
|
327
|
-
audioCategory: 'playAndRecord',
|
|
328
|
-
audioCategoryOptions: next ? ['defaultToSpeaker'] : [],
|
|
329
|
-
audioMode: next ? 'videoChat' : 'voiceChat'
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
} catch {
|
|
333
|
-
setSpeakerOn(!next);
|
|
334
|
-
}
|
|
335
|
-
}, [runtime, speakerOn]);
|
|
336
|
-
|
|
337
|
-
// ---- render ----------------------------------------------------------
|
|
338
|
-
|
|
66
|
+
const insets = useSafeAreaInsets();
|
|
67
|
+
const runtime = loadLiveKit();
|
|
68
|
+
const session = useCallSession();
|
|
69
|
+
const selfView = useDraggable();
|
|
70
|
+
const elapsed = useSharedCallElapsed(session.anchorAt);
|
|
71
|
+
// Telegram-style: tap the small tile to swap it with the big one.
|
|
72
|
+
const [swapped, setSwapped] = useState(false);
|
|
339
73
|
if (!runtime) {
|
|
340
74
|
return /*#__PURE__*/_jsx(View, {
|
|
341
75
|
style: [styles.canvas, styles.centered],
|
|
@@ -348,64 +82,46 @@ export const VideoCallSession = ({
|
|
|
348
82
|
const {
|
|
349
83
|
VideoTrack
|
|
350
84
|
} = runtime;
|
|
351
|
-
const
|
|
85
|
+
const {
|
|
86
|
+
connected,
|
|
87
|
+
remoteJoined,
|
|
88
|
+
remoteVideo,
|
|
89
|
+
localVideo,
|
|
90
|
+
micOn,
|
|
91
|
+
cameraOn,
|
|
92
|
+
speakerOn,
|
|
93
|
+
deviceError,
|
|
94
|
+
remoteMicMuted,
|
|
95
|
+
facingMode
|
|
96
|
+
} = session;
|
|
97
|
+
// The call slice's name (the chat title) first, as web does: LiveKit's
|
|
98
|
+
// participant name is often just the opaque xmpp identity.
|
|
99
|
+
const displayName = peerName || session.remoteName || t('call.unknownCaller');
|
|
352
100
|
const isVideo = kind === 'video';
|
|
353
101
|
const showRemoteVideo = isVideo && !!remoteVideo;
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
368
|
-
accessibilityRole: "button",
|
|
369
|
-
accessibilityLabel: t('call.action.expand'),
|
|
370
|
-
onPress: onToggleMinimize,
|
|
371
|
-
style: [styles.miniButton, {
|
|
372
|
-
backgroundColor: CONTROL_IDLE
|
|
373
|
-
}],
|
|
374
|
-
children: /*#__PURE__*/_jsx(ExpandIcon, {
|
|
375
|
-
color: TEXT_ON_DARK,
|
|
376
|
-
size: 20
|
|
377
|
-
})
|
|
378
|
-
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
379
|
-
accessibilityRole: "button",
|
|
380
|
-
accessibilityLabel: t('call.action.hangUp'),
|
|
381
|
-
onPress: onHangup,
|
|
382
|
-
style: [styles.miniButton, {
|
|
383
|
-
backgroundColor: DANGER
|
|
384
|
-
}],
|
|
385
|
-
children: icons?.hangup ?? /*#__PURE__*/_jsx(HangUpIcon, {
|
|
386
|
-
color: TEXT_ON_DARK,
|
|
387
|
-
size: 20
|
|
388
|
-
})
|
|
389
|
-
})]
|
|
390
|
-
});
|
|
391
|
-
}
|
|
102
|
+
const localLive = isVideo && cameraOn && !!localVideo;
|
|
103
|
+
// Swapping only means something while both sides have a picture; if
|
|
104
|
+
// either goes away, fall back to the default layout on its own.
|
|
105
|
+
const canSwap = showRemoteVideo && localLive;
|
|
106
|
+
const isSwapped = swapped && canSwap;
|
|
107
|
+
const mainVideo = isSwapped ? localVideo : showRemoteVideo ? remoteVideo : null;
|
|
108
|
+
const pipVideo = isSwapped ? remoteVideo : localLive ? localVideo : null;
|
|
109
|
+
// Only the front camera is mirrored, as in every camera app.
|
|
110
|
+
const selfMirror = facingMode === 'user';
|
|
111
|
+
const statusText = elapsed !== null ? formatCallDuration(elapsed) : !connected ? t('call.status.connecting') : !remoteJoined ? t('call.status.waitingForPeer') : t('call.status.inCall');
|
|
112
|
+
const peerMutedLabel = t('call.peerMuted', {
|
|
113
|
+
name: displayName
|
|
114
|
+
});
|
|
392
115
|
return /*#__PURE__*/_jsxs(View, {
|
|
393
116
|
style: styles.canvas,
|
|
394
|
-
children: [
|
|
395
|
-
style: styles.deviceErrorBanner,
|
|
396
|
-
onPress: () => setDeviceError(null),
|
|
397
|
-
accessibilityRole: "button",
|
|
398
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
399
|
-
style: styles.deviceErrorText,
|
|
400
|
-
numberOfLines: 2,
|
|
401
|
-
children: deviceError
|
|
402
|
-
})
|
|
403
|
-
}), /*#__PURE__*/_jsxs(View, {
|
|
117
|
+
children: [/*#__PURE__*/_jsxs(View, {
|
|
404
118
|
style: styles.stage,
|
|
405
|
-
children: [
|
|
406
|
-
trackRef:
|
|
119
|
+
children: [mainVideo ? /*#__PURE__*/_jsx(VideoTrack, {
|
|
120
|
+
trackRef: mainVideo,
|
|
407
121
|
style: styles.remoteVideo,
|
|
408
|
-
objectFit: "cover"
|
|
122
|
+
objectFit: "cover",
|
|
123
|
+
mirror: isSwapped && selfMirror,
|
|
124
|
+
zOrder: 0
|
|
409
125
|
}) : /*#__PURE__*/_jsxs(View, {
|
|
410
126
|
style: styles.centered,
|
|
411
127
|
children: [/*#__PURE__*/_jsx(ProfileImagePlaceholder, {
|
|
@@ -416,36 +132,95 @@ export const VideoCallSession = ({
|
|
|
416
132
|
children: displayName
|
|
417
133
|
}), /*#__PURE__*/_jsx(Text, {
|
|
418
134
|
style: styles.stageStatus,
|
|
419
|
-
children:
|
|
135
|
+
children: isVideo && remoteJoined && elapsed !== null ? t('call.status.cameraOff') : statusText
|
|
420
136
|
}), !connected && /*#__PURE__*/_jsx(ActivityIndicator, {
|
|
421
137
|
color: primaryColor,
|
|
422
138
|
style: styles.spinner,
|
|
423
139
|
size: "small"
|
|
140
|
+
}), !micOn && /*#__PURE__*/_jsx(View, {
|
|
141
|
+
style: [styles.pill, styles.pillDanger],
|
|
142
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
143
|
+
style: styles.pillDangerText,
|
|
144
|
+
children: t('call.selfMuted')
|
|
145
|
+
})
|
|
146
|
+
}), remoteMicMuted && !showRemoteVideo && /*#__PURE__*/_jsx(View, {
|
|
147
|
+
style: styles.pill,
|
|
148
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
149
|
+
style: styles.pillText,
|
|
150
|
+
children: peerMutedLabel
|
|
151
|
+
})
|
|
152
|
+
})]
|
|
153
|
+
}), mainVideo && /*#__PURE__*/_jsxs(View, {
|
|
154
|
+
style: [styles.header, {
|
|
155
|
+
paddingTop: 14 + insets.top
|
|
156
|
+
}],
|
|
157
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
158
|
+
style: styles.headerName,
|
|
159
|
+
numberOfLines: 1,
|
|
160
|
+
children: displayName
|
|
161
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
162
|
+
style: styles.headerStatus,
|
|
163
|
+
children: statusText
|
|
164
|
+
}), remoteMicMuted && /*#__PURE__*/_jsxs(View, {
|
|
165
|
+
style: [styles.pill, styles.headerPill],
|
|
166
|
+
children: [/*#__PURE__*/_jsx(MicOffIcon, {
|
|
167
|
+
color: TEXT_ON_DARK,
|
|
168
|
+
size: 14
|
|
169
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
170
|
+
style: styles.pillText,
|
|
171
|
+
children: peerMutedLabel
|
|
172
|
+
})]
|
|
424
173
|
})]
|
|
425
|
-
}),
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
174
|
+
}), pipVideo && /*#__PURE__*/_jsx(Animated.View, {
|
|
175
|
+
...selfView.panHandlers,
|
|
176
|
+
style: [styles.pip, {
|
|
177
|
+
top: 16 + insets.top
|
|
178
|
+
}, selfView.style],
|
|
179
|
+
children: /*#__PURE__*/_jsx(Pressable, {
|
|
429
180
|
style: styles.pipVideo,
|
|
430
|
-
|
|
431
|
-
|
|
181
|
+
disabled: !canSwap,
|
|
182
|
+
onPress: () => setSwapped(value => !value),
|
|
183
|
+
accessibilityRole: "button",
|
|
184
|
+
accessibilityLabel: t('call.action.swapVideo'),
|
|
185
|
+
children: /*#__PURE__*/_jsx(VideoTrack, {
|
|
186
|
+
trackRef: pipVideo,
|
|
187
|
+
style: styles.pipVideo,
|
|
188
|
+
objectFit: "cover",
|
|
189
|
+
mirror: !isSwapped && selfMirror,
|
|
190
|
+
zOrder: 1
|
|
191
|
+
})
|
|
432
192
|
})
|
|
433
193
|
}), onToggleMinimize && /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
434
194
|
accessibilityRole: "button",
|
|
435
195
|
accessibilityLabel: t('call.action.minimize'),
|
|
436
196
|
onPress: onToggleMinimize,
|
|
437
|
-
style: styles.minimizeButton,
|
|
197
|
+
style: [styles.minimizeButton, {
|
|
198
|
+
top: 16 + insets.top
|
|
199
|
+
}],
|
|
438
200
|
children: /*#__PURE__*/_jsx(MinimizeIcon, {
|
|
439
201
|
color: TEXT_ON_DARK,
|
|
440
202
|
size: 20
|
|
441
203
|
})
|
|
204
|
+
}), !!deviceError && /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
205
|
+
style: [styles.deviceErrorBanner, {
|
|
206
|
+
top: 64 + insets.top
|
|
207
|
+
}],
|
|
208
|
+
onPress: dismissCallDeviceError,
|
|
209
|
+
accessibilityRole: "button",
|
|
210
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
211
|
+
style: styles.deviceErrorText,
|
|
212
|
+
numberOfLines: 2,
|
|
213
|
+
children: deviceError
|
|
214
|
+
})
|
|
442
215
|
})]
|
|
443
216
|
}), /*#__PURE__*/_jsxs(View, {
|
|
444
|
-
style: styles.controls,
|
|
217
|
+
style: [styles.controls, {
|
|
218
|
+
paddingBottom: 20 + insets.bottom
|
|
219
|
+
}],
|
|
445
220
|
children: [/*#__PURE__*/_jsx(TouchableOpacity, {
|
|
446
221
|
accessibilityRole: "button",
|
|
447
222
|
accessibilityLabel: micOn ? t('call.action.muteMic') : t('call.action.unmuteMic'),
|
|
448
|
-
onPress:
|
|
223
|
+
onPress: toggleCallMic,
|
|
449
224
|
style: [styles.controlButton, {
|
|
450
225
|
backgroundColor: micOn ? CONTROL_IDLE : DANGER
|
|
451
226
|
}],
|
|
@@ -457,7 +232,7 @@ export const VideoCallSession = ({
|
|
|
457
232
|
}), isVideo && /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
458
233
|
accessibilityRole: "button",
|
|
459
234
|
accessibilityLabel: cameraOn ? t('call.action.stopCamera') : t('call.action.startCamera'),
|
|
460
|
-
onPress:
|
|
235
|
+
onPress: toggleCallCamera,
|
|
461
236
|
style: [styles.controlButton, {
|
|
462
237
|
backgroundColor: cameraOn ? CONTROL_IDLE : DANGER
|
|
463
238
|
}],
|
|
@@ -469,7 +244,7 @@ export const VideoCallSession = ({
|
|
|
469
244
|
}), isVideo && cameraOn && /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
470
245
|
accessibilityRole: "button",
|
|
471
246
|
accessibilityLabel: t('call.action.switchCamera'),
|
|
472
|
-
onPress:
|
|
247
|
+
onPress: switchCallCamera,
|
|
473
248
|
style: [styles.controlButton, {
|
|
474
249
|
backgroundColor: CONTROL_IDLE
|
|
475
250
|
}],
|
|
@@ -479,7 +254,7 @@ export const VideoCallSession = ({
|
|
|
479
254
|
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
480
255
|
accessibilityRole: "button",
|
|
481
256
|
accessibilityLabel: speakerOn ? t('call.action.speakerOff') : t('call.action.speakerOn'),
|
|
482
|
-
onPress:
|
|
257
|
+
onPress: toggleCallSpeaker,
|
|
483
258
|
style: [styles.controlButton, {
|
|
484
259
|
backgroundColor: CONTROL_IDLE
|
|
485
260
|
}],
|
|
@@ -509,7 +284,8 @@ const styles = StyleSheet.create({
|
|
|
509
284
|
flex: 1,
|
|
510
285
|
alignItems: 'center',
|
|
511
286
|
justifyContent: 'center',
|
|
512
|
-
gap: 12
|
|
287
|
+
gap: 12,
|
|
288
|
+
paddingHorizontal: 24
|
|
513
289
|
},
|
|
514
290
|
stage: {
|
|
515
291
|
flex: 1,
|
|
@@ -523,26 +299,75 @@ const styles = StyleSheet.create({
|
|
|
523
299
|
stageName: {
|
|
524
300
|
color: TEXT_ON_DARK,
|
|
525
301
|
fontSize: 22,
|
|
526
|
-
marginTop: 16
|
|
302
|
+
marginTop: 16,
|
|
303
|
+
textAlign: 'center'
|
|
527
304
|
},
|
|
528
305
|
stageStatus: {
|
|
529
306
|
color: TEXT_MUTED,
|
|
530
|
-
fontSize: 14
|
|
307
|
+
fontSize: 14,
|
|
308
|
+
fontVariant: ['tabular-nums']
|
|
531
309
|
},
|
|
532
310
|
spinner: {
|
|
533
311
|
marginTop: 8
|
|
534
312
|
},
|
|
313
|
+
header: {
|
|
314
|
+
position: 'absolute',
|
|
315
|
+
top: 0,
|
|
316
|
+
left: 0,
|
|
317
|
+
right: 0,
|
|
318
|
+
paddingHorizontal: 64,
|
|
319
|
+
paddingBottom: 14,
|
|
320
|
+
alignItems: 'center',
|
|
321
|
+
backgroundColor: 'rgba(0,0,0,0.35)',
|
|
322
|
+
gap: 2
|
|
323
|
+
},
|
|
324
|
+
headerName: {
|
|
325
|
+
color: TEXT_ON_DARK,
|
|
326
|
+
fontSize: 15,
|
|
327
|
+
fontWeight: '600'
|
|
328
|
+
},
|
|
329
|
+
headerStatus: {
|
|
330
|
+
color: 'rgba(255,255,255,0.7)',
|
|
331
|
+
fontSize: 13,
|
|
332
|
+
fontVariant: ['tabular-nums']
|
|
333
|
+
},
|
|
334
|
+
headerPill: {
|
|
335
|
+
marginTop: 4,
|
|
336
|
+
backgroundColor: 'rgba(0,0,0,0.45)'
|
|
337
|
+
},
|
|
338
|
+
pill: {
|
|
339
|
+
flexDirection: 'row',
|
|
340
|
+
alignItems: 'center',
|
|
341
|
+
gap: 6,
|
|
342
|
+
paddingVertical: 4,
|
|
343
|
+
paddingHorizontal: 10,
|
|
344
|
+
borderRadius: 999,
|
|
345
|
+
backgroundColor: CONTROL_IDLE
|
|
346
|
+
},
|
|
347
|
+
pillText: {
|
|
348
|
+
color: TEXT_ON_DARK,
|
|
349
|
+
fontSize: 12
|
|
350
|
+
},
|
|
351
|
+
pillDanger: {
|
|
352
|
+
backgroundColor: 'rgba(229, 57, 53, 0.18)',
|
|
353
|
+
borderWidth: 1,
|
|
354
|
+
borderColor: 'rgba(229, 57, 53, 0.45)'
|
|
355
|
+
},
|
|
356
|
+
pillDangerText: {
|
|
357
|
+
color: '#FF8A80',
|
|
358
|
+
fontSize: 12
|
|
359
|
+
},
|
|
535
360
|
pip: {
|
|
536
361
|
position: 'absolute',
|
|
537
362
|
right: 16,
|
|
538
|
-
top: 16,
|
|
539
363
|
width: 104,
|
|
540
364
|
height: 148,
|
|
541
365
|
borderRadius: 12,
|
|
542
366
|
overflow: 'hidden',
|
|
543
367
|
backgroundColor: '#000',
|
|
544
368
|
borderWidth: 1,
|
|
545
|
-
borderColor: 'rgba(255,255,255,0.18)'
|
|
369
|
+
borderColor: 'rgba(255,255,255,0.18)',
|
|
370
|
+
zIndex: 5
|
|
546
371
|
},
|
|
547
372
|
pipVideo: {
|
|
548
373
|
flex: 1
|
|
@@ -550,20 +375,20 @@ const styles = StyleSheet.create({
|
|
|
550
375
|
minimizeButton: {
|
|
551
376
|
position: 'absolute',
|
|
552
377
|
left: 16,
|
|
553
|
-
top: 16,
|
|
554
378
|
width: 36,
|
|
555
379
|
height: 36,
|
|
556
380
|
borderRadius: 18,
|
|
557
381
|
alignItems: 'center',
|
|
558
382
|
justifyContent: 'center',
|
|
559
|
-
backgroundColor: 'rgba(0,0,0,0.45)'
|
|
383
|
+
backgroundColor: 'rgba(0,0,0,0.45)',
|
|
384
|
+
zIndex: 6
|
|
560
385
|
},
|
|
561
386
|
controls: {
|
|
562
387
|
flexDirection: 'row',
|
|
563
388
|
alignItems: 'center',
|
|
564
389
|
justifyContent: 'center',
|
|
565
390
|
gap: 16,
|
|
566
|
-
|
|
391
|
+
paddingTop: 20,
|
|
567
392
|
paddingHorizontal: 16
|
|
568
393
|
},
|
|
569
394
|
controlButton: {
|
|
@@ -582,11 +407,10 @@ const styles = StyleSheet.create({
|
|
|
582
407
|
textAlign: 'center',
|
|
583
408
|
paddingHorizontal: 24
|
|
584
409
|
},
|
|
585
|
-
// Dismissible, non-blocking
|
|
586
|
-
//
|
|
410
|
+
// Dismissible, non-blocking: the call is still up underneath; this only
|
|
411
|
+
// flags that camera/mic didn't come up.
|
|
587
412
|
deviceErrorBanner: {
|
|
588
413
|
position: 'absolute',
|
|
589
|
-
top: 16,
|
|
590
414
|
left: 16,
|
|
591
415
|
right: 16,
|
|
592
416
|
zIndex: 10,
|
|
@@ -599,34 +423,6 @@ const styles = StyleSheet.create({
|
|
|
599
423
|
color: TEXT_ON_DARK,
|
|
600
424
|
fontSize: 13,
|
|
601
425
|
textAlign: 'center'
|
|
602
|
-
},
|
|
603
|
-
miniBar: {
|
|
604
|
-
flexDirection: 'row',
|
|
605
|
-
alignItems: 'center',
|
|
606
|
-
gap: 12,
|
|
607
|
-
paddingHorizontal: 16,
|
|
608
|
-
paddingVertical: 12,
|
|
609
|
-
borderRadius: 16,
|
|
610
|
-
backgroundColor: SURFACE_DARK
|
|
611
|
-
},
|
|
612
|
-
miniInfo: {
|
|
613
|
-
flex: 1,
|
|
614
|
-
minWidth: 0
|
|
615
|
-
},
|
|
616
|
-
miniName: {
|
|
617
|
-
color: TEXT_ON_DARK,
|
|
618
|
-
fontSize: 15
|
|
619
|
-
},
|
|
620
|
-
miniStatus: {
|
|
621
|
-
color: TEXT_MUTED,
|
|
622
|
-
fontSize: 12
|
|
623
|
-
},
|
|
624
|
-
miniButton: {
|
|
625
|
-
width: 40,
|
|
626
|
-
height: 40,
|
|
627
|
-
borderRadius: 20,
|
|
628
|
-
alignItems: 'center',
|
|
629
|
-
justifyContent: 'center'
|
|
630
426
|
}
|
|
631
427
|
});
|
|
632
428
|
export default VideoCallSession;
|