@@ -117,6 +117,7 @@ export const LocalDeviceSettings = (props: LocalDeviceSettingsType): JSX.Element
117117
118118 const cameraPermissionGranted = props . cameraPermissionGranted ;
119119 const micPermissionGranted = props . microphonePermissionGranted ;
120+
120121 let roleCanUseCamera = true ;
121122 let roleCanUseMic = true ;
122123
@@ -145,6 +146,9 @@ export const LocalDeviceSettings = (props: LocalDeviceSettingsType): JSX.Element
145146 const hasCameras = props . cameras . length > 0 ;
146147 const hasMicrophones = props . microphones . length > 0 ;
147148 const hasSpeakers = props . speakers . length > 0 ;
149+ /* @conditional -compile-remove(unsupported-browser) */
150+ const isSafariWithNoSpeakers =
151+ adapter . getState ( ) . environmentInfo ?. environment . browser . toLowerCase ( ) === 'safari' && ! hasSpeakers ;
148152
149153 const cameraGrantedDropdown = (
150154 < Dropdown
@@ -209,6 +213,33 @@ export const LocalDeviceSettings = (props: LocalDeviceSettingsType): JSX.Element
209213 </ >
210214 ) ;
211215
216+ const speakerDropdown = (
217+ < Dropdown
218+ aria-labelledby = { 'call-composite-local-sound-settings-label' }
219+ placeholder = { hasSpeakers ? defaultPlaceHolder : noSpeakersLabel }
220+ styles = { dropDownStyles ( theme ) }
221+ disabled = { props . speakers . length === 0 }
222+ options = { getDropDownList ( props . speakers ) }
223+ defaultSelectedKey = { props . selectedSpeaker ? props . selectedSpeaker . id : defaultDeviceId ( props . speakers ) }
224+ onChange = { (
225+ event : React . FormEvent < HTMLDivElement > ,
226+ option ?: IDropdownOption | undefined ,
227+ index ?: number | undefined
228+ ) => {
229+ props . onSelectSpeaker ( props . speakers [ index ?? 0 ] ) ;
230+ } }
231+ onRenderTitle = { ( props ?: IDropdownOption [ ] ) => onRenderTitle ( 'Speaker' , props ) }
232+ />
233+ ) ;
234+
235+ const SafariBrowserSpeakerDropdownTrampoline = ( ) : JSX . Element => {
236+ /* @conditional -compile-remove(unsupported-browser) */
237+ if ( isSafariWithNoSpeakers ) {
238+ return < > </ > ;
239+ }
240+ return speakerDropdown ;
241+ } ;
242+
212243 return (
213244 < Stack data-ui-id = "call-composite-device-settings" tokens = { mainStackTokens } >
214245 { roleCanUseCamera && (
@@ -262,22 +293,7 @@ export const LocalDeviceSettings = (props: LocalDeviceSettingsType): JSX.Element
262293 /* @conditional -compile-remove(call-readiness) */
263294 onClickEnableDevicePermission = { props . onClickEnableDevicePermission }
264295 />
265- < Dropdown
266- aria-labelledby = { 'call-composite-local-sound-settings-label' }
267- placeholder = { hasSpeakers ? defaultPlaceHolder : noSpeakersLabel }
268- styles = { dropDownStyles ( theme ) }
269- disabled = { props . speakers . length === 0 }
270- options = { getDropDownList ( props . speakers ) }
271- defaultSelectedKey = { props . selectedSpeaker ? props . selectedSpeaker . id : defaultDeviceId ( props . speakers ) }
272- onChange = { (
273- event : React . FormEvent < HTMLDivElement > ,
274- option ?: IDropdownOption | undefined ,
275- index ?: number | undefined
276- ) => {
277- props . onSelectSpeaker ( props . speakers [ index ?? 0 ] ) ;
278- } }
279- onRenderTitle = { ( props ?: IDropdownOption [ ] ) => onRenderTitle ( 'Speaker' , props ) }
280- />
296+ < SafariBrowserSpeakerDropdownTrampoline />
281297 </ Stack >
282298 </ Stack >
283299 </ Stack >
0 commit comments