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