Skip to content

Commit e322858

Browse files
Fix noUncheckedIndexedAccess for react-composites (#5297)
1 parent 4ff7eef commit e322858

38 files changed

+322
-203
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "patch",
3+
"area": "improvement",
4+
"workstream": "",
5+
"comment": "Fix noUncheckedIndexedAccess for react-composites",
6+
"packageName": "@azure/communication-react",
7+
"email": "[email protected]",
8+
"dependentChangeType": "patch"
9+
}

packages/react-composites/src/composites/CallComposite/CallComposite.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ const MainScreen = (props: MainScreenProps): JSX.Element => {
477477
'recordingAndTranscriptionStopped',
478478
'recordingStoppedStillTranscribing',
479479
'transcriptionStoppedStillRecording'
480-
].includes(activeNotifications[index].type)
480+
].includes(notification.type)
481481
) {
482482
activeNotifications.splice(index, 1);
483483
}

packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ const findLatestEndedCall = (calls: { [key: string]: CallState }): CallState | u
355355
}
356356
let latestCall = callStates[0];
357357
for (const call of callStates.slice(1)) {
358-
if ((call.endTime?.getTime() ?? 0) > (latestCall.endTime?.getTime() ?? 0)) {
358+
if ((call.endTime?.getTime() ?? 0) > (latestCall?.endTime?.getTime() ?? 0)) {
359359
latestCall = call;
360360
}
361361
}
@@ -371,7 +371,7 @@ const findLatestAcceptedTransfer = (acceptedTransfers: {
371371
}
372372
let latestAcceptedTransfer = acceptedTransferValues[0];
373373
for (const acceptedTransfer of acceptedTransferValues.slice(1)) {
374-
if ((acceptedTransfer.timestamp?.getTime() ?? 0) > (latestAcceptedTransfer.timestamp?.getTime() ?? 0)) {
374+
if ((acceptedTransfer.timestamp?.getTime() ?? 0) > (latestAcceptedTransfer?.timestamp?.getTime() ?? 0)) {
375375
latestAcceptedTransfer = acceptedTransfer;
376376
}
377377
}
@@ -523,36 +523,56 @@ export class AzureCommunicationCallAdapter<AgentType extends CallAgent | TeamsCa
523523

524524
if (this.callAgent.kind === 'CallAgent') {
525525
const onCallsUpdated = (args: { added: Call[]; removed: Call[] }): void => {
526-
if (this.call?.id) {
527-
const removedCall = args.removed.find((call) => call.id === this.call?.id);
528-
if (removedCall) {
529-
const removedCallState = this.callClient.getState().callsEnded[removedCall.id];
530-
const latestAcceptedTransfer = findLatestAcceptedTransfer(removedCallState.transfer.acceptedTransfers);
531-
const _callAgent = callAgent as CallAgent;
532-
const transferCall = _callAgent.calls.find((call: Call) => call.id === latestAcceptedTransfer?.callId);
533-
if (transferCall) {
534-
this.processNewCall(transferCall);
535-
}
536-
}
526+
if (!this.call?.id) {
527+
return;
537528
}
529+
530+
const removedCall = args.removed.find((call) => call.id === this.call?.id);
531+
if (!removedCall) {
532+
return;
533+
}
534+
535+
const removedCallState = this.callClient.getState().callsEnded[removedCall.id];
536+
if (!removedCallState) {
537+
return;
538+
}
539+
540+
const latestAcceptedTransfer = findLatestAcceptedTransfer(removedCallState.transfer.acceptedTransfers);
541+
const _callAgent = callAgent as CallAgent;
542+
const transferCall = _callAgent.calls.find((call: Call) => call.id === latestAcceptedTransfer?.callId);
543+
if (!transferCall) {
544+
return;
545+
}
546+
547+
this.processNewCall(transferCall);
538548
};
539549
(this.callAgent as CallAgent).on('callsUpdated', onCallsUpdated);
540550
}
541551
/* @conditional-compile-remove(teams-identity-support) */
542552
if (this.callAgent.kind === 'TeamsCallAgent') {
543553
const onTeamsCallsUpdated = (args: { added: TeamsCall[]; removed: TeamsCall[] }): void => {
544-
if (this.call?.id) {
545-
const removedCall = args.removed.find((call) => call.id === this.call?.id);
546-
if (removedCall) {
547-
const removedCallState = this.callClient.getState().callsEnded[removedCall.id];
548-
const latestAcceptedTransfer = findLatestAcceptedTransfer(removedCallState.transfer.acceptedTransfers);
549-
const _callAgent = callAgent as TeamsCallAgent;
550-
const transferCall = _callAgent.calls.find((call: TeamsCall) => call.id === latestAcceptedTransfer?.callId);
551-
if (transferCall) {
552-
this.processNewCall(transferCall);
553-
}
554-
}
554+
if (!this.call?.id) {
555+
return;
555556
}
557+
558+
const removedCall = args.removed.find((call) => call.id === this.call?.id);
559+
if (!removedCall) {
560+
return;
561+
}
562+
563+
const removedCallState = this.callClient.getState().callsEnded[removedCall.id];
564+
if (!removedCallState) {
565+
return;
566+
}
567+
568+
const latestAcceptedTransfer = findLatestAcceptedTransfer(removedCallState.transfer.acceptedTransfers);
569+
const _callAgent = callAgent as TeamsCallAgent;
570+
const transferCall = _callAgent.calls.find((call: TeamsCall) => call.id === latestAcceptedTransfer?.callId);
571+
if (!transferCall) {
572+
return;
573+
}
574+
575+
this.processNewCall(transferCall);
556576
};
557577
(this.callAgent as TeamsCallAgent).on('callsUpdated', onTeamsCallsUpdated);
558578
}
@@ -1443,7 +1463,7 @@ export class AzureCommunicationCallAdapter<AgentType extends CallAgent | TeamsCa
14431463
private hangupOtherBreakoutRoomCalls(currentBreakoutRoomCallId: string): void {
14441464
// Get origin call id of breakout room call
14451465
const breakoutRoomCallState = this.callClient.getState().calls[currentBreakoutRoomCallId];
1446-
const originCallId = breakoutRoomCallState.breakoutRooms?.breakoutRoomOriginCallId;
1466+
const originCallId = breakoutRoomCallState?.breakoutRooms?.breakoutRoomOriginCallId;
14471467

14481468
// Get other breakout room calls with the same origin call
14491469
const otherBreakoutRoomCallStates = Object.values(this.callClient.getState().calls).filter((callState) => {

packages/react-composites/src/composites/CallComposite/adapter/CallingSoundSubscriber.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ const CALL_TRANSFER_SUBCODE = 7015;
2323
export class CallingSoundSubscriber {
2424
private call: CallCommon;
2525
private soundsLoaded?: CallingSoundsLoaded;
26-
private callee: CommunicationIdentifier[] | undefined;
26+
private callees: CommunicationIdentifier[] | undefined;
2727
public playingSounds: boolean = false;
2828

29-
constructor(call: CallCommon, callee?: CommunicationIdentifier[], sounds?: CallingSounds) {
29+
constructor(call: CallCommon, callees?: CommunicationIdentifier[], sounds?: CallingSounds) {
3030
this.call = call;
31-
this.callee = callee;
31+
this.callees = callees;
3232
if (sounds) {
3333
this.soundsLoaded = this.loadSounds(sounds);
3434
this.subscribeCallSoundEvents();
@@ -37,12 +37,12 @@ export class CallingSoundSubscriber {
3737

3838
private onCallStateChanged = (): void => {
3939
this.call.on('stateChanged', () => {
40-
if (shouldPlayRinging(this.call, this.callee) && this.soundsLoaded?.callRingingSound) {
40+
if (shouldPlayRinging(this.call, this.callees) && this.soundsLoaded?.callRingingSound) {
4141
this.soundsLoaded.callRingingSound.loop = true;
4242
this.playSound(this.soundsLoaded.callRingingSound);
4343
this.playingSounds = true;
4444
}
45-
if (!shouldPlayRinging(this.call, this.callee) && this.soundsLoaded?.callRingingSound) {
45+
if (!shouldPlayRinging(this.call, this.callees) && this.soundsLoaded?.callRingingSound) {
4646
this.soundsLoaded.callRingingSound.loop = false;
4747
this.soundsLoaded.callRingingSound.pause();
4848
this.playingSounds = false;
@@ -123,16 +123,15 @@ export class CallingSoundSubscriber {
123123
* Helper function to allow the calling sound subscriber to determine when to play the ringing
124124
* sound when making an outbound call.
125125
*/
126-
const shouldPlayRinging = (call: CallCommon, callee?: CommunicationIdentifier[]): boolean => {
126+
const shouldPlayRinging = (call: CallCommon, callees?: CommunicationIdentifier[]): boolean => {
127127
if (
128-
callee &&
129-
callee.length >= 1 &&
130-
!isPhoneNumberIdentifier(callee[0]) &&
128+
callees &&
129+
callees[0] &&
130+
!isPhoneNumberIdentifier(callees[0]) &&
131131
(call.state === 'Ringing' || call.state === 'Connecting')
132132
) {
133133
return true;
134134
} else {
135135
return false;
136136
}
137-
return false;
138137
};

packages/react-composites/src/composites/CallComposite/adapter/OnFetchProfileCallback.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,27 @@ export const createProfileStateModifier = (
3838

3939
return (state: CallAdapterState) => {
4040
const originalParticipants = state.call?.remoteParticipants;
41+
4142
(async () => {
4243
let shouldNotifyUpdates = false;
43-
for (const key in originalParticipants) {
44+
if (!originalParticipants) {
45+
return;
46+
}
47+
48+
for (const [key, participant] of Object.entries(originalParticipants)) {
4449
if (cachedDisplayName[key]) {
4550
continue;
4651
}
47-
const profile = await onFetchProfile(key, { displayName: originalParticipants[key].displayName });
48-
if (profile?.displayName && originalParticipants[key].displayName !== profile?.displayName) {
52+
const profile = await onFetchProfile(key, { displayName: participant.displayName });
53+
if (profile?.displayName && participant.displayName !== profile?.displayName) {
4954
cachedDisplayName[key] = profile?.displayName;
5055
shouldNotifyUpdates = true;
5156
}
5257
}
5358
// notify update only when there is a change, which most likely will trigger modifier and setState again
54-
shouldNotifyUpdates && notifyUpdate();
59+
if (shouldNotifyUpdates) {
60+
notifyUpdate();
61+
}
5562
})();
5663

5764
const participantsModifier = createParticipantModifier(

packages/react-composites/src/composites/CallComposite/components/LocalDeviceSettings.tsx

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,10 @@ const getOptionIcon = (type: iconType): JSX.Element | undefined => {
6262
};
6363

6464
const onRenderTitle = (iconType: iconType, props?: IDropdownOption[]): JSX.Element => {
65-
const icon = props && getOptionIcon(iconType);
6665
return props ? (
6766
<div className={dropDownTitleIconStyles}>
68-
{icon}
69-
<span>{props[0].text}</span>
67+
{getOptionIcon(iconType)}
68+
<span>{props[0]?.text}</span>
7069
</div>
7170
) : (
7271
<></>
@@ -168,7 +167,12 @@ export const LocalDeviceSettings = (props: LocalDeviceSettingsType): JSX.Element
168167
: 'deniedOrUnknown'
169168
}
170169
onChange={(event, option, index) => {
171-
props.onSelectCamera(props.cameras[index ?? 0], localVideoViewOptions);
170+
const camera = props.cameras[index ?? 0];
171+
if (camera) {
172+
props.onSelectCamera(camera, localVideoViewOptions);
173+
} else {
174+
console.error('No cameras available');
175+
}
172176
}}
173177
onRenderTitle={(props?: IDropdownOption[]) => onRenderTitle('Camera', props)}
174178
/>
@@ -200,7 +204,12 @@ export const LocalDeviceSettings = (props: LocalDeviceSettingsType): JSX.Element
200204
option?: IDropdownOption | undefined,
201205
index?: number | undefined
202206
) => {
203-
props.onSelectMicrophone(props.microphones[index ?? 0]);
207+
const microphone = props.microphones[index ?? 0];
208+
if (microphone) {
209+
props.onSelectMicrophone(microphone);
210+
} else {
211+
console.error('No microphones available');
212+
}
204213
}}
205214
onRenderTitle={(props?: IDropdownOption[]) => onRenderTitle('Microphone', props)}
206215
/>
@@ -221,7 +230,12 @@ export const LocalDeviceSettings = (props: LocalDeviceSettingsType): JSX.Element
221230
option?: IDropdownOption | undefined,
222231
index?: number | undefined
223232
) => {
224-
props.onSelectSpeaker(props.speakers[index ?? 0]);
233+
const speaker = props.speakers[index ?? 0];
234+
if (speaker) {
235+
props.onSelectSpeaker(speaker);
236+
} else {
237+
console.error('No speakers available');
238+
}
225239
}}
226240
onRenderTitle={(props?: IDropdownOption[]) => onRenderTitle('Speaker', props)}
227241
/>
@@ -293,13 +307,13 @@ export const LocalDeviceSettings = (props: LocalDeviceSettingsType): JSX.Element
293307
);
294308
};
295309

296-
const defaultDeviceId = (devices: AudioDeviceInfo[]): string => {
310+
const defaultDeviceId = (devices: AudioDeviceInfo[]): string | undefined => {
297311
if (devices.length === 0) {
298-
return '';
312+
return undefined;
299313
}
300314
const defaultDevice = devices.find((device) => device.isSystemDefault);
301315
if (defaultDevice) {
302316
return defaultDevice.id;
303317
}
304-
return devices[0].id;
318+
return devices[0]?.id;
305319
};

packages/react-composites/src/composites/CallComposite/components/NetworkReconnectTile.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export const NetworkReconnectTile = (props: NetworkReconnectTileProps): JSX.Elem
8080
<Text className={mergeStyles(moreDetailsStyle(palette, isVideoReady))} aria-live={'assertive'}>
8181
{strings.networkReconnectMoreDetails}
8282
</Text>
83-
{isTeamsMeeting && meetingCoordinates && meetingCoordinates.length > 0 && (
83+
{isTeamsMeeting && meetingCoordinates && meetingCoordinates[0] && (
8484
<Stack>
8585
<Stack horizontal horizontalAlign="center" verticalAlign="center" className={titleContainerClassName}>
8686
<Text className={titleClassName}>{localeStrings.meetingConferencePhoneInfoModalTitle}</Text>

packages/react-composites/src/composites/CallComposite/components/SidePane/usePeoplePane.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ export const usePeoplePane = (props: {
180180
/* @conditional-compile-remove(soft-mute) */
181181
if (onMuteParticipant && !isMe && remoteParticipants && remoteParticipants[participantId]) {
182182
const participant = remoteParticipants[participantId];
183-
const isMuted = participant.isMuted;
183+
const isMuted = !!participant?.isMuted;
184184
_defaultMenuItems.push({
185185
key: 'mute',
186186
text: 'Mute',

packages/react-composites/src/composites/CallComposite/pages/TransferPage.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ const TransferTile = (props: TransferTileProps): JSX.Element => {
155155

156156
const observer = useRef(
157157
new ResizeObserver((entries): void => {
158+
if (!entries[0]) {
159+
return;
160+
}
158161
const { width, height } = entries[0].contentRect;
159162
const personaSize = Math.min(width, height) / 2;
160163
setPersonaSize(Math.max(Math.min(personaSize, 150), 32));

packages/react-composites/src/composites/CallComposite/selectors/dominantRemoteParticipantSelector.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const dominantRemoteParticipantSelector = reselect.createSelector(
2828
const findDominantRemoteParticipant = (
2929
remoteParticipants: { [keys: string]: RemoteParticipantState },
3030
dominantSpeakerIds: string[]
31-
): RemoteParticipantState => {
31+
): RemoteParticipantState | undefined => {
3232
let dominantRemoteParticipantId = dominantSpeakerIds[0];
3333

3434
// Fallback to using the first remote participant if there are no dominant speaker IDs
@@ -38,5 +38,5 @@ const findDominantRemoteParticipant = (
3838
dominantRemoteParticipantId = remoteParticipantIds[0];
3939
}
4040

41-
return remoteParticipants[dominantRemoteParticipantId];
41+
return dominantRemoteParticipantId ? remoteParticipants[dominantRemoteParticipantId] : undefined;
4242
};

0 commit comments

Comments
 (0)