@ethora/chat-component-rn 26.10.4 → 26.10.5
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/VideoCalls/ActiveCallBar.js +61 -16
- package/lib/commonjs/components/VideoCalls/ActiveCallBar.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/CallIcons.js +41 -16
- package/lib/commonjs/components/VideoCalls/CallIcons.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js +183 -9
- package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/VideoCallSession.js +40 -15
- package/lib/commonjs/components/VideoCalls/VideoCallSession.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/callSessionController.js +49 -26
- package/lib/commonjs/components/VideoCalls/callSessionController.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/callUiStore.js +33 -4
- package/lib/commonjs/components/VideoCalls/callUiStore.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/livekitRuntime.js +9 -1
- package/lib/commonjs/components/VideoCalls/livekitRuntime.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/useCallKeep.js +197 -28
- package/lib/commonjs/components/VideoCalls/useCallKeep.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/useOngoingCallNotification.js +88 -0
- package/lib/commonjs/components/VideoCalls/useOngoingCallNotification.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/usePipTile.js +232 -0
- package/lib/commonjs/components/VideoCalls/usePipTile.js.map +1 -0
- package/lib/commonjs/helpers/updateMessagesTillLast.js +3 -1
- package/lib/commonjs/helpers/updateMessagesTillLast.js.map +1 -1
- package/lib/commonjs/i18n/strings.js +48 -0
- package/lib/commonjs/i18n/strings.js.map +1 -1
- package/lib/commonjs/main.js.map +1 -1
- package/lib/commonjs/version.js +1 -1
- package/lib/module/components/VideoCalls/ActiveCallBar.js +63 -18
- package/lib/module/components/VideoCalls/ActiveCallBar.js.map +1 -1
- package/lib/module/components/VideoCalls/CallIcons.js +38 -15
- package/lib/module/components/VideoCalls/CallIcons.js.map +1 -1
- package/lib/module/components/VideoCalls/VideoCallOverlay.js +185 -11
- package/lib/module/components/VideoCalls/VideoCallOverlay.js.map +1 -1
- package/lib/module/components/VideoCalls/VideoCallSession.js +41 -16
- package/lib/module/components/VideoCalls/VideoCallSession.js.map +1 -1
- package/lib/module/components/VideoCalls/callSessionController.js +46 -24
- package/lib/module/components/VideoCalls/callSessionController.js.map +1 -1
- package/lib/module/components/VideoCalls/callUiStore.js +30 -3
- package/lib/module/components/VideoCalls/callUiStore.js.map +1 -1
- package/lib/module/components/VideoCalls/livekitRuntime.js +9 -1
- package/lib/module/components/VideoCalls/livekitRuntime.js.map +1 -1
- package/lib/module/components/VideoCalls/useCallKeep.js +198 -29
- package/lib/module/components/VideoCalls/useCallKeep.js.map +1 -1
- package/lib/module/components/VideoCalls/useOngoingCallNotification.js +84 -0
- package/lib/module/components/VideoCalls/useOngoingCallNotification.js.map +1 -0
- package/lib/module/components/VideoCalls/usePipTile.js +228 -0
- package/lib/module/components/VideoCalls/usePipTile.js.map +1 -0
- package/lib/module/helpers/updateMessagesTillLast.js +3 -1
- package/lib/module/helpers/updateMessagesTillLast.js.map +1 -1
- package/lib/module/i18n/strings.js +48 -0
- package/lib/module/i18n/strings.js.map +1 -1
- package/lib/module/main.js.map +1 -1
- package/lib/module/version.js +1 -1
- package/lib/typescript/components/VideoCalls/ActiveCallBar.d.ts +9 -3
- package/lib/typescript/components/VideoCalls/ActiveCallBar.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/CallIcons.d.ts +9 -0
- package/lib/typescript/components/VideoCalls/CallIcons.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/VideoCallOverlay.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/VideoCallSession.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/callSessionController.d.ts +7 -0
- package/lib/typescript/components/VideoCalls/callSessionController.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/callUiStore.d.ts +13 -0
- package/lib/typescript/components/VideoCalls/callUiStore.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/livekitRuntime.d.ts +5 -0
- package/lib/typescript/components/VideoCalls/livekitRuntime.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/useCallKeep.d.ts +12 -0
- package/lib/typescript/components/VideoCalls/useCallKeep.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/useOngoingCallNotification.d.ts +10 -0
- package/lib/typescript/components/VideoCalls/useOngoingCallNotification.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/usePipTile.d.ts +46 -0
- package/lib/typescript/components/VideoCalls/usePipTile.d.ts.map +1 -0
- package/lib/typescript/helpers/updateMessagesTillLast.d.ts.map +1 -1
- package/lib/typescript/i18n/strings.d.ts.map +1 -1
- package/lib/typescript/main.d.ts +1 -1
- package/lib/typescript/main.d.ts.map +1 -1
- package/lib/typescript/types/types.d.ts +40 -0
- package/lib/typescript/types/types.d.ts.map +1 -1
- package/lib/typescript/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/VideoCalls/ActiveCallBar.tsx +56 -10
- package/src/components/VideoCalls/CallIcons.tsx +36 -12
- package/src/components/VideoCalls/VideoCallOverlay.tsx +205 -8
- package/src/components/VideoCalls/VideoCallSession.tsx +45 -13
- package/src/components/VideoCalls/callSessionController.ts +51 -20
- package/src/components/VideoCalls/callUiStore.ts +33 -3
- package/src/components/VideoCalls/livekitRuntime.ts +13 -0
- package/src/components/VideoCalls/useCallKeep.ts +204 -33
- package/src/components/VideoCalls/useOngoingCallNotification.ts +113 -0
- package/src/components/VideoCalls/usePipTile.ts +214 -0
- package/src/helpers/updateMessagesTillLast.tsx +2 -1
- package/src/i18n/strings.ts +48 -0
- package/src/main.ts +3 -0
- package/src/types/types.ts +48 -0
- package/src/version.ts +1 -1
- package/lib/commonjs/components/VideoCalls/useDraggable.js +0 -59
- package/lib/commonjs/components/VideoCalls/useDraggable.js.map +0 -1
- package/lib/module/components/VideoCalls/useDraggable.js +0 -55
- package/lib/module/components/VideoCalls/useDraggable.js.map +0 -1
- package/lib/typescript/components/VideoCalls/useDraggable.d.ts +0 -19
- package/lib/typescript/components/VideoCalls/useDraggable.d.ts.map +0 -1
- package/src/components/VideoCalls/useDraggable.ts +0 -46
|
@@ -14,6 +14,8 @@ import { ProfileImagePlaceholder } from '../MainComponents/ProfileImagePlacehold
|
|
|
14
14
|
import {
|
|
15
15
|
CameraOffIcon,
|
|
16
16
|
CameraOnIcon,
|
|
17
|
+
FillVideoIcon,
|
|
18
|
+
FitVideoIcon,
|
|
17
19
|
HangUpIcon,
|
|
18
20
|
MicOffIcon,
|
|
19
21
|
MicOnIcon,
|
|
@@ -31,7 +33,7 @@ import {
|
|
|
31
33
|
toggleCallSpeaker,
|
|
32
34
|
useCallSession,
|
|
33
35
|
} from './callSessionController';
|
|
34
|
-
import {
|
|
36
|
+
import { usePipTile } from './usePipTile';
|
|
35
37
|
import { useT } from '../../i18n/useT';
|
|
36
38
|
|
|
37
39
|
const DEFAULT_PRIMARY = '#0052CD';
|
|
@@ -99,10 +101,14 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
|
|
|
99
101
|
const insets = useSafeAreaInsets();
|
|
100
102
|
const runtime = loadLiveKit();
|
|
101
103
|
const session = useCallSession();
|
|
102
|
-
const
|
|
104
|
+
const pipTile = usePipTile({ topInset: insets.top });
|
|
103
105
|
const elapsed = useSharedCallElapsed(session.anchorAt);
|
|
104
106
|
// Telegram-style: tap the small tile to swap it with the big one.
|
|
105
107
|
const [swapped, setSwapped] = useState(false);
|
|
108
|
+
// The peer's video is shown whole by default: a web camera is landscape,
|
|
109
|
+
// and filling a portrait phone crops most of it away. The button next to
|
|
110
|
+
// minimize stretches it to fill (the old behaviour) and back.
|
|
111
|
+
const [fillRemote, setFillRemote] = useState(false);
|
|
106
112
|
|
|
107
113
|
if (!runtime) {
|
|
108
114
|
return (
|
|
@@ -124,6 +130,7 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
|
|
|
124
130
|
deviceError,
|
|
125
131
|
remoteMicMuted,
|
|
126
132
|
facingMode,
|
|
133
|
+
localVideoEpoch,
|
|
127
134
|
} = session;
|
|
128
135
|
// The call slice's name (the chat title) first, as web does: LiveKit's
|
|
129
136
|
// participant name is often just the opaque xmpp identity.
|
|
@@ -137,8 +144,14 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
|
|
|
137
144
|
const isSwapped = swapped && canSwap;
|
|
138
145
|
const mainVideo = isSwapped ? localVideo : showRemoteVideo ? remoteVideo : null;
|
|
139
146
|
const pipVideo = isSwapped ? remoteVideo : localLive ? localVideo : null;
|
|
147
|
+
const mainIsRemote = !!mainVideo && mainVideo === remoteVideo;
|
|
148
|
+
const mainFit = mainIsRemote && !fillRemote ? 'contain' : 'cover';
|
|
140
149
|
// Only the front camera is mirrored, as in every camera app.
|
|
141
150
|
const selfMirror = facingMode === 'user';
|
|
151
|
+
// The local view remounts when the camera is re-acquired (switching
|
|
152
|
+
// front/back), so it never keeps rendering the stopped stream.
|
|
153
|
+
const videoKey = (ref: any) =>
|
|
154
|
+
ref === localVideo ? `local-${localVideoEpoch}` : 'remote';
|
|
142
155
|
|
|
143
156
|
const statusText =
|
|
144
157
|
elapsed !== null
|
|
@@ -154,12 +167,13 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
|
|
|
154
167
|
return (
|
|
155
168
|
<View style={styles.canvas}>
|
|
156
169
|
{/* Stage */}
|
|
157
|
-
<View style={styles.stage}>
|
|
170
|
+
<View style={styles.stage} onLayout={pipTile.onStageLayout}>
|
|
158
171
|
{mainVideo ? (
|
|
159
172
|
<VideoTrack
|
|
173
|
+
key={videoKey(mainVideo)}
|
|
160
174
|
trackRef={mainVideo}
|
|
161
175
|
style={styles.remoteVideo}
|
|
162
|
-
objectFit=
|
|
176
|
+
objectFit={mainFit}
|
|
163
177
|
mirror={isSwapped && selfMirror}
|
|
164
178
|
zOrder={0}
|
|
165
179
|
/>
|
|
@@ -204,12 +218,12 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
|
|
|
204
218
|
</View>
|
|
205
219
|
)}
|
|
206
220
|
|
|
207
|
-
{/* Picture-in-picture:
|
|
208
|
-
it with the main video. */}
|
|
221
|
+
{/* Picture-in-picture: drag it and it snaps to a corner, pinch it
|
|
222
|
+
for the large size, tap it to swap it with the main video. */}
|
|
209
223
|
{pipVideo && (
|
|
210
224
|
<Animated.View
|
|
211
|
-
{...
|
|
212
|
-
style={[styles.pip,
|
|
225
|
+
{...pipTile.panHandlers}
|
|
226
|
+
style={[styles.pip, pipTile.style]}
|
|
213
227
|
>
|
|
214
228
|
<Pressable
|
|
215
229
|
style={styles.pipVideo}
|
|
@@ -219,6 +233,7 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
|
|
|
219
233
|
accessibilityLabel={t('call.action.swapVideo')}
|
|
220
234
|
>
|
|
221
235
|
<VideoTrack
|
|
236
|
+
key={videoKey(pipVideo)}
|
|
222
237
|
trackRef={pipVideo}
|
|
223
238
|
style={styles.pipVideo}
|
|
224
239
|
objectFit="cover"
|
|
@@ -240,6 +255,23 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
|
|
|
240
255
|
</TouchableOpacity>
|
|
241
256
|
)}
|
|
242
257
|
|
|
258
|
+
{mainIsRemote && (
|
|
259
|
+
<TouchableOpacity
|
|
260
|
+
accessibilityRole="button"
|
|
261
|
+
accessibilityLabel={
|
|
262
|
+
fillRemote ? t('call.action.fitVideo') : t('call.action.fillVideo')
|
|
263
|
+
}
|
|
264
|
+
onPress={() => setFillRemote((value) => !value)}
|
|
265
|
+
style={[styles.minimizeButton, styles.fitButton, { top: 16 + insets.top }]}
|
|
266
|
+
>
|
|
267
|
+
{fillRemote ? (
|
|
268
|
+
<FitVideoIcon color={TEXT_ON_DARK} size={20} />
|
|
269
|
+
) : (
|
|
270
|
+
<FillVideoIcon color={TEXT_ON_DARK} size={20} />
|
|
271
|
+
)}
|
|
272
|
+
</TouchableOpacity>
|
|
273
|
+
)}
|
|
274
|
+
|
|
243
275
|
{!!deviceError && (
|
|
244
276
|
<TouchableOpacity
|
|
245
277
|
style={[styles.deviceErrorBanner, { top: 64 + insets.top }]}
|
|
@@ -354,7 +386,7 @@ const styles = StyleSheet.create({
|
|
|
354
386
|
top: 0,
|
|
355
387
|
left: 0,
|
|
356
388
|
right: 0,
|
|
357
|
-
paddingHorizontal:
|
|
389
|
+
paddingHorizontal: 108,
|
|
358
390
|
paddingBottom: 14,
|
|
359
391
|
alignItems: 'center',
|
|
360
392
|
backgroundColor: 'rgba(0,0,0,0.35)',
|
|
@@ -397,10 +429,6 @@ const styles = StyleSheet.create({
|
|
|
397
429
|
fontSize: 12,
|
|
398
430
|
},
|
|
399
431
|
pip: {
|
|
400
|
-
position: 'absolute',
|
|
401
|
-
right: 16,
|
|
402
|
-
width: 104,
|
|
403
|
-
height: 148,
|
|
404
432
|
borderRadius: 12,
|
|
405
433
|
overflow: 'hidden',
|
|
406
434
|
backgroundColor: '#000',
|
|
@@ -422,6 +450,10 @@ const styles = StyleSheet.create({
|
|
|
422
450
|
backgroundColor: 'rgba(0,0,0,0.45)',
|
|
423
451
|
zIndex: 6,
|
|
424
452
|
},
|
|
453
|
+
// Next to minimize, top-left: the top-right corner belongs to the tile.
|
|
454
|
+
fitButton: {
|
|
455
|
+
left: 60,
|
|
456
|
+
},
|
|
425
457
|
controls: {
|
|
426
458
|
flexDirection: 'row',
|
|
427
459
|
alignItems: 'center',
|
|
@@ -33,6 +33,11 @@ export interface CallSessionSnapshot {
|
|
|
33
33
|
speakerOn: boolean;
|
|
34
34
|
/** Which camera the local video comes from; only the front one is mirrored. */
|
|
35
35
|
facingMode: 'user' | 'environment';
|
|
36
|
+
/**
|
|
37
|
+
* Bumped whenever the local camera track is re-acquired, so the self view
|
|
38
|
+
* can remount onto the new stream instead of holding the stopped one.
|
|
39
|
+
*/
|
|
40
|
+
localVideoEpoch: number;
|
|
36
41
|
/** `{ participant, publication, source }` for `<VideoTrack />`, live and unmuted only. */
|
|
37
42
|
remoteVideo: any;
|
|
38
43
|
localVideo: any;
|
|
@@ -56,6 +61,7 @@ const IDLE: CallSessionSnapshot = {
|
|
|
56
61
|
cameraOn: false,
|
|
57
62
|
speakerOn: false,
|
|
58
63
|
facingMode: 'user',
|
|
64
|
+
localVideoEpoch: 0,
|
|
59
65
|
remoteVideo: null,
|
|
60
66
|
localVideo: null,
|
|
61
67
|
remoteName: null,
|
|
@@ -299,7 +305,14 @@ export const startCallSession = ({
|
|
|
299
305
|
// Claims audio focus and sets the right native audio mode. Must
|
|
300
306
|
// start BEFORE connecting or the first seconds route to the earpiece
|
|
301
307
|
// on Android even for a video call.
|
|
302
|
-
|
|
308
|
+
try {
|
|
309
|
+
await AudioSession.startAudioSession();
|
|
310
|
+
} catch {
|
|
311
|
+
// A call answered from the CallKit banner: CallKit owns the iOS
|
|
312
|
+
// audio session and activates it itself (useCallKeep forwards that
|
|
313
|
+
// to WebRTC), so our own activation is refused. Not a reason to
|
|
314
|
+
// fail the call.
|
|
315
|
+
}
|
|
303
316
|
await room.connect(livekitUrl, token);
|
|
304
317
|
} catch (error: any) {
|
|
305
318
|
if (current.disposed) {return;}
|
|
@@ -350,10 +363,12 @@ export const startCallSession = ({
|
|
|
350
363
|
|
|
351
364
|
// ---- controls -----------------------------------------------------------
|
|
352
365
|
|
|
353
|
-
export const toggleCallMic =
|
|
366
|
+
export const toggleCallMic = (): Promise<void> => setCallMicEnabled(!snapshot.micOn);
|
|
367
|
+
|
|
368
|
+
/** Set the mic to a given state (system call UI reports absolute values). */
|
|
369
|
+
export const setCallMicEnabled = async (next: boolean): Promise<void> => {
|
|
354
370
|
const current = session;
|
|
355
|
-
if (!current) {return;}
|
|
356
|
-
const next = !snapshot.micOn;
|
|
371
|
+
if (!current || snapshot.micOn === next) {return;}
|
|
357
372
|
emit({ micOn: next });
|
|
358
373
|
try {
|
|
359
374
|
await current.room.localParticipant.setMicrophoneEnabled(next);
|
|
@@ -379,29 +394,45 @@ export const toggleCallCamera = async (): Promise<void> => {
|
|
|
379
394
|
}
|
|
380
395
|
};
|
|
381
396
|
|
|
397
|
+
let switchingCamera = false;
|
|
398
|
+
|
|
382
399
|
export const switchCallCamera = async (): Promise<void> => {
|
|
383
400
|
const current = session;
|
|
384
401
|
const runtime = loadLiveKit();
|
|
385
|
-
if (!current || !runtime) {return;}
|
|
386
|
-
const
|
|
402
|
+
if (!current || !runtime || switchingCamera) {return;}
|
|
403
|
+
const track = current.room.localParticipant.getTrackPublication?.(
|
|
387
404
|
runtime.Track.Source.Camera
|
|
388
|
-
)?.track
|
|
389
|
-
if (!
|
|
405
|
+
)?.track;
|
|
406
|
+
if (!track?.restartTrack) {return;}
|
|
390
407
|
const next = snapshot.facingMode === 'user' ? 'environment' : 'user';
|
|
408
|
+
switchingCamera = true;
|
|
391
409
|
try {
|
|
392
|
-
//
|
|
393
|
-
//
|
|
394
|
-
//
|
|
395
|
-
//
|
|
396
|
-
//
|
|
397
|
-
//
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
410
|
+
// A real device change needs a new capture: react-native-webrtc's
|
|
411
|
+
// native applyConstraints only applies width/height/frameRate and
|
|
412
|
+
// ignores facingMode on both platforms, so the old approach kept the
|
|
413
|
+
// front camera and merely flipped our mirror flag (the picture looked
|
|
414
|
+
// swapped left to right). restartTrack re-acquires the camera with
|
|
415
|
+
// the new facing mode and swaps it into the published sender; the
|
|
416
|
+
// <VideoTrack /> view follows it through TrackEvent.Restarted.
|
|
417
|
+
await track.restartTrack({
|
|
418
|
+
facingMode: next,
|
|
419
|
+
...(runtime.VideoPresets
|
|
420
|
+
? { resolution: runtime.VideoPresets.h720.resolution }
|
|
421
|
+
: {}),
|
|
422
|
+
});
|
|
423
|
+
if (current.disposed) {return;}
|
|
424
|
+
// Trust what the camera reports: a device with a single camera stays
|
|
425
|
+
// on it, and the mirror must follow the real camera, not the request.
|
|
426
|
+
const actual = track.mediaStreamTrack?.getSettings?.()?.facingMode;
|
|
427
|
+
emit({
|
|
428
|
+
facingMode: actual === 'user' || actual === 'environment' ? actual : next,
|
|
429
|
+
localVideoEpoch: snapshot.localVideoEpoch + 1,
|
|
430
|
+
});
|
|
402
431
|
resync();
|
|
403
|
-
} catch {
|
|
404
|
-
|
|
432
|
+
} catch (error) {
|
|
433
|
+
if (!current.disposed) {emit({ deviceError: humanizeDeviceError(error) });}
|
|
434
|
+
} finally {
|
|
435
|
+
switchingCamera = false;
|
|
405
436
|
}
|
|
406
437
|
};
|
|
407
438
|
|
|
@@ -16,11 +16,26 @@ import { store } from '../../roomStore';
|
|
|
16
16
|
|
|
17
17
|
interface CallUiState {
|
|
18
18
|
minimized: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* iOS shows this incoming call in its own CallKit UI (banner / full
|
|
21
|
+
* screen), so the in-app ring card stays hidden instead of doubling it.
|
|
22
|
+
*/
|
|
23
|
+
systemRinging: boolean;
|
|
19
24
|
/** Id of the header that shows the strip, or null when none is mounted. */
|
|
20
25
|
barHost: number | null;
|
|
26
|
+
/**
|
|
27
|
+
* Window y of the top of the strip slot under the hosting header, so the
|
|
28
|
+
* collapse animation can land exactly on the strip. Null: no header.
|
|
29
|
+
*/
|
|
30
|
+
barAnchorY: number | null;
|
|
21
31
|
}
|
|
22
32
|
|
|
23
|
-
let state: CallUiState = {
|
|
33
|
+
let state: CallUiState = {
|
|
34
|
+
minimized: false,
|
|
35
|
+
systemRinging: false,
|
|
36
|
+
barHost: null,
|
|
37
|
+
barAnchorY: null,
|
|
38
|
+
};
|
|
24
39
|
const hosts: number[] = [];
|
|
25
40
|
let nextHostId = 1;
|
|
26
41
|
const listeners = new Set<() => void>();
|
|
@@ -35,9 +50,13 @@ const emit = (patch: Partial<CallUiState>) => {
|
|
|
35
50
|
const ensureStoreWatcher = () => {
|
|
36
51
|
if (storeUnsubscribe) {return;}
|
|
37
52
|
storeUnsubscribe = store.subscribe(() => {
|
|
38
|
-
|
|
53
|
+
const phase = store.getState().call.phase;
|
|
54
|
+
if (state.minimized && phase === 'idle') {
|
|
39
55
|
emit({ minimized: false });
|
|
40
56
|
}
|
|
57
|
+
if (state.systemRinging && phase !== 'ringing-incoming') {
|
|
58
|
+
emit({ systemRinging: false });
|
|
59
|
+
}
|
|
41
60
|
});
|
|
42
61
|
};
|
|
43
62
|
|
|
@@ -61,6 +80,10 @@ export const setCallMinimized = (minimized: boolean): void => {
|
|
|
61
80
|
if (state.minimized !== minimized) {emit({ minimized });}
|
|
62
81
|
};
|
|
63
82
|
|
|
83
|
+
export const setCallSystemRinging = (systemRinging: boolean): void => {
|
|
84
|
+
if (state.systemRinging !== systemRinging) {emit({ systemRinging });}
|
|
85
|
+
};
|
|
86
|
+
|
|
64
87
|
export const registerCallBarHost = (
|
|
65
88
|
onId?: (id: number) => void
|
|
66
89
|
): (() => void) => {
|
|
@@ -71,6 +94,13 @@ export const registerCallBarHost = (
|
|
|
71
94
|
return () => {
|
|
72
95
|
const index = hosts.indexOf(id);
|
|
73
96
|
if (index >= 0) {hosts.splice(index, 1);}
|
|
74
|
-
|
|
97
|
+
const next = hosts.length ? hosts[hosts.length - 1] : null;
|
|
98
|
+
emit({ barHost: next, barAnchorY: next === null ? null : state.barAnchorY });
|
|
75
99
|
};
|
|
76
100
|
};
|
|
101
|
+
|
|
102
|
+
/** The hosting header reports where its strip slot is on screen. */
|
|
103
|
+
export const setCallBarAnchor = (hostId: number, y: number): void => {
|
|
104
|
+
if (state.barHost !== hostId || state.barAnchorY === y) {return;}
|
|
105
|
+
emit({ barAnchorY: y });
|
|
106
|
+
};
|
|
@@ -34,6 +34,11 @@ export interface LiveKitRuntime {
|
|
|
34
34
|
AudioSession: any;
|
|
35
35
|
/** Installs the WebRTC globals livekit-client expects. */
|
|
36
36
|
registerGlobals: () => void;
|
|
37
|
+
/**
|
|
38
|
+
* `@livekit/react-native-webrtc` RTCAudioSession: tells WebRTC when
|
|
39
|
+
* CallKit activates/deactivates the iOS audio session.
|
|
40
|
+
*/
|
|
41
|
+
RTCAudioSession: any;
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
let cached: LiveKitRuntime | null | undefined;
|
|
@@ -59,6 +64,13 @@ export const loadLiveKit = (): LiveKitRuntime | null => {
|
|
|
59
64
|
// moving subset across versions, the core package does not.
|
|
60
65
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
61
66
|
const core = require('livekit-client');
|
|
67
|
+
let webrtc: any = null;
|
|
68
|
+
try {
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
70
|
+
webrtc = require('@livekit/react-native-webrtc');
|
|
71
|
+
} catch {
|
|
72
|
+
// Only needed for CallKit; calls work without it.
|
|
73
|
+
}
|
|
62
74
|
|
|
63
75
|
cached = {
|
|
64
76
|
Room: core.Room,
|
|
@@ -69,6 +81,7 @@ export const loadLiveKit = (): LiveKitRuntime | null => {
|
|
|
69
81
|
VideoTrack: rn.VideoTrack,
|
|
70
82
|
AudioSession: rn.AudioSession,
|
|
71
83
|
registerGlobals: rn.registerGlobals,
|
|
84
|
+
RTCAudioSession: webrtc?.RTCAudioSession ?? null,
|
|
72
85
|
};
|
|
73
86
|
} catch {
|
|
74
87
|
cached = null;
|