in apps/meeting/src/providers/VideoTileGridProvider/state.tsx [157:397]
export function reducer(state: State, { type, payload }: Action): State {
const { gridState, attendeeStates, videoSourceState, priorityBasedPolicy } = state;
switch (type) {
case VideoTileGridAction.UpdateAttendeeStates: {
const { roster } = payload;
// Remove attendee that left the meeting
for (const attendeeId in attendeeStates) {
if (!isContentShare(attendeeId) && !(attendeeId in roster)) {
delete attendeeStates[attendeeId];
}
}
// Add attendee that joined the meeting
for (const attendeeId in roster) {
const name = roster[attendeeId]?.name || '';
if (attendeeId in attendeeStates) {
attendeeStates[attendeeId].name = name;
} else {
attendeeStates[attendeeId] = {
attendeeId,
name,
videoEnabled: false,
bandwidthConstrained: false,
} as AttendeeState;
}
}
// Ensure the state of `videoEnabled` in the racing condition of UpdateAttendeeStates and UpdateVideoSources
for (const attendeeId of videoSourceState.cameraSources) {
if (attendeeId in attendeeStates) {
attendeeStates[attendeeId].videoEnabled = true;
}
}
return {
...state,
attendeeStates,
};
}
case VideoTileGridAction.UpdateVideoSources: {
const { videoSources, localAttendeeId } = payload as { videoSources: VideoSource[]; localAttendeeId: string | null };
const cameraSources: string[] = [];
const videoSourceIdSet = new Set(
videoSources.map(videoSource => videoSource.attendee.attendeeId)
);
for (const attendeeId in attendeeStates) {
// Reset the `videoEnabled` for all remote attendees
if (!localAttendeeId || attendeeId !== localAttendeeId) {
attendeeStates[attendeeId].videoEnabled = false;
}
// Remove content share from attendeeStates,
// if content share is not in video sources
if (
isContentShare(attendeeId) &&
!videoSourceIdSet.has(attendeeId)
) {
delete attendeeStates[attendeeId];
}
}
// Update the `videoEnabled` according to `videoSources`
for (const attendeeId of videoSourceIdSet) {
if (!(attendeeId in attendeeStates)) {
if (isContentShare(attendeeId)) {
attendeeStates[attendeeId] = {
attendeeId,
name: 'content share',
bandwidthConstrained: false,
} as AttendeeState;
} else {
attendeeStates[attendeeId] = {
attendeeId,
name: '',
bandwidthConstrained: false,
} as AttendeeState;
}
}
attendeeStates[attendeeId].videoEnabled = true;
}
// Populate the `cameraSources` based on the order of `attendeeStates`
for (const attendeeId in attendeeStates) {
const attendee = attendeeStates[attendeeId];
if (
attendee.videoEnabled &&
!isContentShare(attendee.attendeeId) &&
attendee.attendeeId !== localAttendeeId
) {
cameraSources.push(attendee.attendeeId);
}
}
// Remove the attendee from `activeSpeakersWithCameraSource`,
// if this attendee close the camera while still active
for (const attendeeId in videoSourceState.activeSpeakersWithCameraSource) {
if (!attendeeStates[attendeeId]?.videoEnabled) {
videoSourceState.activeSpeakersWithCameraSource.filter(id => id !== attendeeId);
}
}
videoSourceState.cameraSources = cameraSources;
updateDownlinkPreferences(gridState, videoSourceState, attendeeStates, priorityBasedPolicy);
return {
...state,
attendeeStates,
videoSourceState,
};
}
case VideoTileGridAction.UpdateActiveSpeakers: {
const { activeSpeakers } = payload;
const activeSpeakersWithCameraSource = [];
const { cameraSources } = videoSourceState;
for (const attendeeId of activeSpeakers) {
if (
attendeeStates[attendeeId]?.videoEnabled &&
cameraSources.includes(attendeeId)
) {
activeSpeakersWithCameraSource.push(attendeeId);
}
}
videoSourceState.activeSpeakersWithCameraSource = activeSpeakersWithCameraSource;
updateDownlinkPreferences(gridState, videoSourceState, attendeeStates, priorityBasedPolicy);
return {
...state,
videoSourceState,
};
}
case VideoTileGridAction.UpdateLocalSourceState: {
const {
isVideoEnabled,
localAttendeeId,
isLocalUserSharing,
sharingAttendeeId,
} = payload;
videoSourceState.hasLocalVideo = isVideoEnabled;
videoSourceState.hasLocalContentSharing = isLocalUserSharing;
videoSourceState.contentShareId = sharingAttendeeId;
if (localAttendeeId && localAttendeeId in attendeeStates) {
attendeeStates[localAttendeeId].videoEnabled = isVideoEnabled;
}
updateDownlinkPreferences(gridState, videoSourceState, attendeeStates, priorityBasedPolicy);
return {
...state,
attendeeStates,
videoSourceState,
};
}
case VideoTileGridAction.UpdateLayout: {
const { layout } = payload;
gridState.layout = layout;
updateDownlinkPreferences(gridState, videoSourceState, attendeeStates, priorityBasedPolicy);
return {
...state,
gridState,
};
}
case VideoTileGridAction.PauseVideoTile: {
const { attendeeId } = payload;
if (attendeeId in attendeeStates) {
attendeeStates[attendeeId].bandwidthConstrained = true;
}
return {
...state,
attendeeStates,
};
}
case VideoTileGridAction.UnpauseVideoTile: {
const { attendeeId } = payload;
if (attendeeId in attendeeStates) {
attendeeStates[attendeeId].bandwidthConstrained = false;
}
return {
...state,
attendeeStates,
};
}
case VideoTileGridAction.ZoomIn: {
const { threshold } = gridState;
const { cameraSources, hasLocalVideo } = videoSourceState;
const numberOfTiles = cameraSources.length + (hasLocalVideo ? 1 : 0);
if (numberOfTiles > threshold) {
gridState.isZoomed = true;
updateDownlinkPreferences(gridState, videoSourceState, attendeeStates, priorityBasedPolicy);
}
return {
...state,
gridState,
};
}
case VideoTileGridAction.ZoomOut: {
if (gridState.isZoomed) {
gridState.isZoomed = false;
updateDownlinkPreferences(gridState, videoSourceState, attendeeStates, priorityBasedPolicy);
}
return {
...state,
};
}
case VideoTileGridAction.SetPriorityBasedPolicy: {
const { policy } = payload as { policy: VideoPriorityBasedPolicy | undefined};
return {
...state,
...{ priorityBasedPolicy: policy },
};
}
default:
throw new Error('Incorrect type in VideoTileGridProvider');
}
}