communication-react-19
Version:
React library for building modern communication user experiences utilizing Azure Communication Services (React 19 compatible fork)
178 lines • 10.7 kB
JavaScript
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { useRef } from 'react';
import { smartDominantSpeakerParticipants } from '../../../gallery';
const DEFAULT_MAX_OVERFLOW_GALLERY_DOMINANT_SPEAKERS = 6;
const DEFAULT_MAX_VIDEO_SREAMS = 4;
/**
* @private
*/
export const MAX_GRID_PARTICIPANTS_NOT_LARGE_GALLERY = 9;
const getOrganizedParticipants = (props) => {
const { remoteParticipants = [], dominantSpeakers = [], maxGridParticipants = DEFAULT_MAX_VIDEO_SREAMS, maxOverflowGalleryDominantSpeakers = DEFAULT_MAX_OVERFLOW_GALLERY_DOMINANT_SPEAKERS, layout, previousGridParticipants = [], previousOverflowParticipants = [] } = props;
const callingParticipants = remoteParticipants.filter((p) => p.state === 'Connecting' || p.state === 'Ringing');
const callingParticipantsSet = new Set(callingParticipants.map((p) => p.userId));
const connectedParticipants = remoteParticipants.filter((p) => !callingParticipantsSet.has(p.userId));
const remoteParticipantsOrdered = putVideoParticipantsFirst(connectedParticipants);
const videoParticipants = remoteParticipants.filter((p) => { var _a; return (_a = p.videoStream) === null || _a === void 0 ? void 0 : _a.isAvailable; });
const participantsForGrid = layout === 'floatingLocalVideo' && videoParticipants.length > 0 ? videoParticipants : remoteParticipantsOrdered;
let newGridParticipants = smartDominantSpeakerParticipants({
participants: participantsForGrid,
dominantSpeakers,
currentParticipants: previousGridParticipants,
maxDominantSpeakers: maxGridParticipants
}).slice(0, maxGridParticipants);
if (layout === 'speaker') {
if (dominantSpeakers === null || dominantSpeakers === void 0 ? void 0 : dominantSpeakers[0]) {
newGridParticipants = newGridParticipants.filter((p) => p.userId === dominantSpeakers[0]);
}
else {
newGridParticipants = newGridParticipants.slice(1);
}
}
const gridParticipantSet = new Set(newGridParticipants.map((p) => p.userId));
const newOverflowGalleryParticipants = smartDominantSpeakerParticipants({
participants: remoteParticipantsOrdered.filter((p) => !gridParticipantSet.has(p.userId)),
dominantSpeakers: dominantSpeakers,
currentParticipants: previousOverflowParticipants,
maxDominantSpeakers: maxOverflowGalleryDominantSpeakers
});
let gridParticipants = newGridParticipants;
let overflowGalleryParticipants = newOverflowGalleryParticipants;
// Add the participants being called into the call.
// If there are already overflow participants, add these to the array of overflow participants
// Otherwise, add the maximum number to the main grid, then put the rest in the overflow
if (overflowGalleryParticipants.length === 0) {
const numberOfCallingParticipantsInGrid = maxGridParticipants - gridParticipants.length;
const gridCallingParticipants = callingParticipants.slice(0, numberOfCallingParticipantsInGrid);
const overflowGalleryCallingParticipants = callingParticipants.slice(numberOfCallingParticipantsInGrid);
gridParticipants = gridParticipants.concat(gridCallingParticipants);
overflowGalleryParticipants = overflowGalleryCallingParticipants;
}
else {
overflowGalleryParticipants = overflowGalleryParticipants.concat(callingParticipants);
}
return { gridParticipants, overflowGalleryParticipants };
};
/**
* Hook to determine which participants should be in grid and overflow gallery and their order respectively
* @private
*/
export const useOrganizedParticipants = (props) => {
var _a, _b;
const spotlightedParticipantUserIds = (_a = props.spotlightedParticipantUserIds) !== null && _a !== void 0 ? _a : [];
const pinnedParticipantUserIds = (_b = props.pinnedParticipantUserIds) !== null && _b !== void 0 ? _b : [];
// Focussed participants are the participants that are either spotlighted or pinned. Ordered by spotlighted first and then pinned.
// A set is used to dedupe participants.
const focusedParticipantUserIdSet = new Set(spotlightedParticipantUserIds.concat(pinnedParticipantUserIds));
const focusedParticipants = [...focusedParticipantUserIdSet]
.map((userId) => props.remoteParticipants.find((p) => p.userId === userId))
.filter((p) => p !== undefined);
// Unfocused participants are the rest of the participants
const unfocusedParticipants = props.remoteParticipants.filter((p) => !focusedParticipantUserIdSet.has(p.userId));
const currentGridParticipants = useRef([]);
const currentOverflowGalleryParticipants = useRef([]);
const organizedParticipantsArgs = Object.assign(Object.assign({}, props), {
// if there are focused participants then leave no room in the grid by setting maxGridParticipants to 0
maxGridParticipants: focusedParticipants.length > 0 || props.isScreenShareActive ? 0 : props.maxGridParticipants, remoteParticipants: unfocusedParticipants, previousGridParticipants: currentGridParticipants.current, previousOverflowParticipants: currentOverflowGalleryParticipants.current });
const organizedParticipants = getOrganizedParticipants(organizedParticipantsArgs);
currentGridParticipants.current = organizedParticipants.gridParticipants;
currentOverflowGalleryParticipants.current = organizedParticipants.overflowGalleryParticipants;
return focusedParticipants.length > 0
? {
gridParticipants: props.isScreenShareActive ? [] : focusedParticipants,
overflowGalleryParticipants: props.isScreenShareActive
? focusedParticipants.concat(organizedParticipants.overflowGalleryParticipants)
: organizedParticipants.overflowGalleryParticipants
}
: organizedParticipants;
};
const putVideoParticipantsFirst = (remoteParticipants) => {
const videoParticipants = [];
const audioParticipants = [];
remoteParticipants.forEach((p) => {
var _a;
if ((_a = p.videoStream) === null || _a === void 0 ? void 0 : _a.isAvailable) {
videoParticipants.push(p);
}
else {
audioParticipants.push(p);
}
});
const remoteParticipantSortedByVideo = videoParticipants.concat(audioParticipants);
return remoteParticipantSortedByVideo;
};
/**
* @private
*/
export const renderTiles = (gridParticipants, onRenderRemoteParticipant, maxRemoteVideoStreams, indexesToRender, overflowGalleryParticipants, dominantSpeakers) => {
const _dominantSpeakers = dominantSpeakers !== null && dominantSpeakers !== void 0 ? dominantSpeakers : [];
let streamsLeftToRender = maxRemoteVideoStreams;
// Render the grid participants
const participantWithStreamsToRenderInGrid = gridParticipants.filter((p) => { var _a; return (_a = p === null || p === void 0 ? void 0 : p.videoStream) === null || _a === void 0 ? void 0 : _a.isAvailable; });
const dominantSpeakerWithStreamsToRenderInGrid = _dominantSpeakers
.filter((userId) => participantWithStreamsToRenderInGrid.find((p) => (p === null || p === void 0 ? void 0 : p.userId) === userId))
.slice(0, streamsLeftToRender);
streamsLeftToRender = streamsLeftToRender - dominantSpeakerWithStreamsToRenderInGrid.length;
const gridTiles = gridParticipants.map((p) => {
var _a;
return onRenderRemoteParticipant(p, dominantSpeakerWithStreamsToRenderInGrid.includes(p.userId) ||
(((_a = p.videoStream) === null || _a === void 0 ? void 0 : _a.isAvailable) && streamsLeftToRender-- > 0));
});
// Render the overflow participants
const participantWithStreamsToRenderInOverflow = indexesToRender
.map((i) => {
return overflowGalleryParticipants.at(i);
})
.filter((p) => { var _a; return (_a = p === null || p === void 0 ? void 0 : p.videoStream) === null || _a === void 0 ? void 0 : _a.isAvailable; });
const dominantSpeakerWithStreamsToRenderInOverflow = _dominantSpeakers
.filter((userId) => participantWithStreamsToRenderInOverflow.find((p) => (p === null || p === void 0 ? void 0 : p.userId) === userId))
.slice(0, streamsLeftToRender);
streamsLeftToRender = streamsLeftToRender - dominantSpeakerWithStreamsToRenderInOverflow.length;
const overflowGalleryTiles = overflowGalleryParticipants.map((p) => {
var _a;
return onRenderRemoteParticipant(p, dominantSpeakerWithStreamsToRenderInOverflow.includes(p.userId) ||
(((_a = p.videoStream) === null || _a === void 0 ? void 0 : _a.isAvailable) && streamsLeftToRender-- > 0));
});
return { gridTiles, overflowGalleryTiles };
};
/**
* @private
*/
export const getEmojiResource = (reactionName, reactionResources) => {
var _a, _b, _c, _d, _e;
switch (reactionName) {
case 'like':
return (_a = reactionResources.likeReaction) === null || _a === void 0 ? void 0 : _a.url;
case 'heart':
return (_b = reactionResources.heartReaction) === null || _b === void 0 ? void 0 : _b.url;
case 'laugh':
return (_c = reactionResources.laughReaction) === null || _c === void 0 ? void 0 : _c.url;
case 'applause':
return (_d = reactionResources.applauseReaction) === null || _d === void 0 ? void 0 : _d.url;
case 'surprised':
return (_e = reactionResources.surprisedReaction) === null || _e === void 0 ? void 0 : _e.url;
}
return undefined;
};
/**
* @private
*/
export const getEmojiFrameCount = (reactionName, reactionResources) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
switch (reactionName) {
case 'like':
return (_b = (_a = reactionResources.likeReaction) === null || _a === void 0 ? void 0 : _a.frameCount) !== null && _b !== void 0 ? _b : 0;
case 'heart':
return (_d = (_c = reactionResources.heartReaction) === null || _c === void 0 ? void 0 : _c.frameCount) !== null && _d !== void 0 ? _d : 0;
case 'laugh':
return (_f = (_e = reactionResources.laughReaction) === null || _e === void 0 ? void 0 : _e.frameCount) !== null && _f !== void 0 ? _f : 0;
case 'applause':
return (_h = (_g = reactionResources.applauseReaction) === null || _g === void 0 ? void 0 : _g.frameCount) !== null && _h !== void 0 ? _h : 0;
case 'surprised':
return (_k = (_j = reactionResources.surprisedReaction) === null || _j === void 0 ? void 0 : _j.frameCount) !== null && _k !== void 0 ? _k : 0;
default:
return 0;
}
};
//# sourceMappingURL=videoGalleryLayoutUtils.js.map