UNPKG

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
// 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