@@ -14,7 +14,7 @@ import {
14
14
type RoomOptions ,
15
15
Track ,
16
16
} from "livekit-client" ;
17
- import { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
17
+ import { useEffect , useRef } from "react" ;
18
18
import E2EEWorker from "livekit-client/e2ee-worker?worker" ;
19
19
import { logger } from "matrix-js-sdk/lib/logger" ;
20
20
import { type MatrixRTCSession } from "matrix-js-sdk/lib/matrixrtc" ;
@@ -24,11 +24,7 @@ import { map } from "rxjs";
24
24
import { defaultLiveKitOptions } from "./options" ;
25
25
import { type SFUConfig } from "./openIDSFU" ;
26
26
import { type MuteStates } from "../room/MuteStates" ;
27
- import {
28
- type MediaDeviceHandle ,
29
- type MediaDevices ,
30
- useMediaDevices ,
31
- } from "./MediaDevicesContext" ;
27
+ import { type MediaDeviceHandle , useMediaDevices } from "./MediaDevicesContext" ;
32
28
import {
33
29
type ECConnectionState ,
34
30
useECConnectionState ,
@@ -42,6 +38,7 @@ import {
42
38
} from "./TrackProcessorContext" ;
43
39
import { observeTrackReference$ } from "../state/MediaViewModel" ;
44
40
import { useUrlParams } from "../UrlParams" ;
41
+ import { useInitial } from "../useInitial" ;
45
42
46
43
interface UseLivekitResult {
47
44
livekitRoom ?: Room ;
@@ -56,9 +53,10 @@ export function useLivekit(
56
53
) : UseLivekitResult {
57
54
const { controlledAudioDevices } = useUrlParams ( ) ;
58
55
59
- const initialMuteStates = useRef < MuteStates > ( muteStates ) ;
56
+ const initialMuteStates = useInitial ( ( ) => muteStates ) ;
57
+
60
58
const devices = useMediaDevices ( ) ;
61
- const initialDevices = useRef < MediaDevices > ( devices ) ;
59
+ const initialDevices = useInitial ( ( ) => devices ) ;
62
60
63
61
// Store if audio/video are currently updating. If to prohibit unnecessary calls
64
62
// to setMicrophoneEnabled/setCameraEnabled
@@ -67,63 +65,59 @@ export function useLivekit(
67
65
// Store the current button mute state that gets passed to this hook via props.
68
66
// We need to store it for awaited code that relies on the current value.
69
67
const buttonEnabled = useRef ( {
70
- audio : initialMuteStates . current . audio . enabled ,
71
- video : initialMuteStates . current . video . enabled ,
68
+ audio : initialMuteStates . audio . enabled ,
69
+ video : initialMuteStates . video . enabled ,
72
70
} ) ;
73
71
74
72
const { processor } = useTrackProcessor ( ) ;
75
73
76
- const createRoom = ( opt : RoomOptions , e2ee : EncryptionSystem ) : Room => {
77
- logger . info ( "[LivekitRoom] Create LiveKit room with options" , opt ) ;
78
- // We have to create the room manually here due to a bug inside
79
- // @livekit /components-react. JSON.stringify() is used in deps of a
80
- // useEffect() with an argument that references itself, if E2EE is enabled
81
- let newE2eeOptions : E2EEManagerOptions | undefined ;
82
- if ( e2ee . kind === E2eeType . PER_PARTICIPANT ) {
74
+ // Only ever create the room once via useInitial.
75
+ const room = useInitial ( ( ) => {
76
+ logger . info ( "[LivekitRoom] Create LiveKit room" ) ;
77
+
78
+ let e2ee : E2EEManagerOptions | undefined ;
79
+ if ( e2eeSystem . kind === E2eeType . PER_PARTICIPANT ) {
83
80
logger . info ( "Created MatrixKeyProvider (per participant)" ) ;
84
- newE2eeOptions = {
81
+ e2ee = {
85
82
keyProvider : new MatrixKeyProvider ( ) ,
86
83
worker : new E2EEWorker ( ) ,
87
84
} ;
88
- } else if ( e2ee . kind === E2eeType . SHARED_KEY && e2ee . secret ) {
85
+ } else if ( e2eeSystem . kind === E2eeType . SHARED_KEY && e2eeSystem . secret ) {
89
86
logger . info ( "Created ExternalE2EEKeyProvider (shared key)" ) ;
90
- newE2eeOptions = {
87
+ e2ee = {
91
88
keyProvider : new ExternalE2EEKeyProvider ( ) ,
92
89
worker : new E2EEWorker ( ) ,
93
90
} ;
94
91
}
95
- const r = new Room ( { ...opt , e2ee : newE2eeOptions } ) ;
96
- r . setE2EEEnabled ( e2ee . kind !== E2eeType . NONE ) . catch ( ( e ) => {
97
- logger . error ( "Failed to set E2EE enabled on room" , e ) ;
98
- } ) ;
99
92
100
- return r ;
101
- } ;
102
-
103
- // Track the current room options in case we need to recreate the room if the encryption system changes
104
- // Only needed because we allow swapping the room in case the e2ee system changes.
105
- // otherwise this could become part of: `createRoom`
106
- const roomOptions = useMemo (
107
- ( ) : RoomOptions => ( {
93
+ const roomOptions : RoomOptions = {
108
94
...defaultLiveKitOptions ,
109
95
videoCaptureDefaults : {
110
96
...defaultLiveKitOptions . videoCaptureDefaults ,
111
- deviceId : devices . videoInput . selectedId ,
112
- processor : processor ,
97
+ deviceId : initialDevices . videoInput . selectedId ,
98
+ processor,
113
99
} ,
114
100
audioCaptureDefaults : {
115
101
...defaultLiveKitOptions . audioCaptureDefaults ,
116
- deviceId : devices . audioInput . selectedId ,
102
+ deviceId : initialDevices . audioInput . selectedId ,
117
103
} ,
118
104
audioOutput : {
119
- deviceId : devices . audioOutput . selectedId ,
105
+ deviceId : initialDevices . audioOutput . selectedId ,
120
106
} ,
121
- } ) ,
122
- [ processor , devices ] ,
123
- ) ;
124
- const [ room , setRoom ] = useState ( ( ) => createRoom ( roomOptions , e2eeSystem ) ) ;
107
+ e2ee,
108
+ } ;
109
+ // We have to create the room manually here due to a bug inside
110
+ // @livekit /components-react. JSON.stringify() is used in deps of a
111
+ // useEffect() with an argument that references itself, if E2EE is enabled
112
+ const room = new Room ( roomOptions ) ;
113
+ room . setE2EEEnabled ( e2eeSystem . kind !== E2eeType . NONE ) . catch ( ( e ) => {
114
+ logger . error ( "Failed to set E2EE enabled on room" , e ) ;
115
+ } ) ;
125
116
126
- // Setup and update the already existing keyProvider
117
+ return room ;
118
+ } ) ;
119
+
120
+ // Setup and update the keyProvider which was create by `createRoom`
127
121
useEffect ( ( ) => {
128
122
const e2eeOptions = room . options . e2ee ;
129
123
if (
@@ -143,41 +137,6 @@ export function useLivekit(
143
137
}
144
138
} , [ room . options . e2ee , e2eeSystem , rtcSession ] ) ;
145
139
146
- // Do we really allow hot swapping the e2ee system?
147
- // Will we ever reach this code?
148
- useEffect ( ( ) => {
149
- const e2eeOptions = room . options . e2ee ;
150
- // Only do sth if our e2eeSystem has changed.
151
- if (
152
- // from non to sth else.
153
- ( e2eeSystem . kind === E2eeType . NONE && e2eeOptions !== undefined ) ||
154
- // from MatrixKeyProvider to sth else
155
- ( e2eeSystem . kind === E2eeType . PER_PARTICIPANT &&
156
- ! (
157
- e2eeOptions &&
158
- "keyProvider" in e2eeOptions &&
159
- e2eeOptions . keyProvider instanceof MatrixKeyProvider
160
- ) ) ||
161
- // from ExternalE2EEKeyProvider to sth else
162
- ( e2eeSystem . kind === E2eeType . SHARED_KEY &&
163
- ! (
164
- e2eeOptions &&
165
- "keyProvider" in e2eeOptions &&
166
- e2eeOptions . keyProvider instanceof ExternalE2EEKeyProvider
167
- ) )
168
- ) {
169
- logger . warn (
170
- "[LivekitRoom] we cannot change the key provider after the room has been created, disconnecting and creating a new room" ,
171
- ) ;
172
- const resetRoom = async ( ) : Promise < void > => {
173
- await room . disconnect ( ) ;
174
- const newRoom = createRoom ( roomOptions , e2eeSystem ) ;
175
- setRoom ( newRoom ) ;
176
- } ;
177
- void resetRoom ( ) ;
178
- }
179
- } , [ room , e2eeSystem , roomOptions , createRoom ] ) ;
180
-
181
140
// Sync the requested track processors with LiveKit
182
141
useTrackProcessorSync (
183
142
useObservableEagerState (
@@ -198,8 +157,8 @@ export function useLivekit(
198
157
) ;
199
158
200
159
const connectionState = useECConnectionState (
201
- initialDevices . current . audioInput . selectedId ,
202
- initialMuteStates . current . audio . enabled ,
160
+ initialDevices . audioInput . selectedId ,
161
+ initialMuteStates . audio . enabled ,
203
162
room ,
204
163
sfuConfig ,
205
164
) ;
0 commit comments