UNPKG

communication-react-19

Version:

React library for building modern communication user experiences utilizing Azure Communication Services (React 19 compatible fork)

185 lines 13.9 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. import { Icon, merge, mergeStyles, PersonaPresence, Stack } from '@fluentui/react'; import { Text } from '@fluentui/react'; import { useTheme } from '../theming'; import { RaisedHandIcon } from './assets/RaisedHandIcon'; import React, { useCallback, useMemo } from 'react'; import { useIdentifiers } from '../identifiers'; import { useLocale } from '../localization'; import { formatParticipantStateString, ParticipantItem } from './ParticipantItem'; import { iconStyles, participantListItemStyle, participantListStyle } from './styles/ParticipantList.styles'; import { _formatString } from "../../../acs-ui-common/src"; const onRenderParticipantDefault = (participant, strings, myUserId, onRenderAvatar, createParticipantMenuItems, styles, onParticipantClick, showParticipantOverflowTooltip, participantAriaLabelledBy, theme, pinnedParticipants) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p; const callingParticipant = participant; let presence = undefined; if (callingParticipant) { presence = PersonaPresence.none; } const menuItems = createParticipantMenuItems && createParticipantMenuItems(participant); const formatDisplayName = (displayName) => { if (displayName && strings.attendeeRole) { return _formatString(displayName, { AttendeeRole: strings.attendeeRole }); } return displayName; }; const displayName = formatDisplayName(participant.displayName); const callingPalette = theme.callingPalette; const isPinned = pinnedParticipants && (pinnedParticipants === null || pinnedParticipants === void 0 ? void 0 : pinnedParticipants.includes(participant.userId)); const isScreenSharing = callingParticipant === null || callingParticipant === void 0 ? void 0 : callingParticipant.isScreenSharing; const isMuted = callingParticipant === null || callingParticipant === void 0 ? void 0 : callingParticipant.isMuted; const hasRaisedHand = callingParticipant === null || callingParticipant === void 0 ? void 0 : callingParticipant.raisedHand; const isAudioPermitted = (callingParticipant === null || callingParticipant === void 0 ? void 0 : callingParticipant.mediaAccess) ? callingParticipant.mediaAccess.isAudioPermitted : true; const isVideoPermitted = (callingParticipant === null || callingParticipant === void 0 ? void 0 : callingParticipant.mediaAccess) ? callingParticipant.mediaAccess.isVideoPermitted : true; const shouldRenderParticipantIcon = isScreenSharing || isMuted || hasRaisedHand || isPinned || !isAudioPermitted || !isVideoPermitted; const onRenderIcon = shouldRenderParticipantIcon ? () => { var _a, _b; return (React.createElement(Stack, { horizontal: true, tokens: { childrenGap: '0.5rem' } }, callingParticipant.raisedHand && (React.createElement(Stack, { horizontal: true, tokens: { childrenGap: '0.2rem' }, style: { alignItems: 'center', padding: '0.1rem 0.2rem', backgroundColor: theme === null || theme === void 0 ? void 0 : theme.palette.neutralLighter, borderRadius: '0.3rem' } }, callingParticipant.raisedHand.raisedHandOrderPosition && (React.createElement(Stack.Item, null, React.createElement(Text, null, (_a = callingParticipant.raisedHand) === null || _a === void 0 ? void 0 : _a.raisedHandOrderPosition))), React.createElement(Stack.Item, null, React.createElement(RaisedHandIcon, null)))), callingParticipant.isScreenSharing && (React.createElement(Icon, { iconName: "ParticipantItemScreenShareStart", className: iconStyles, ariaLabel: strings.sharingIconLabel })), callingParticipant.spotlight && React.createElement(Icon, { iconName: "ParticipantItemSpotlighted", className: iconStyles }), isPinned && React.createElement(Icon, { iconName: "ParticipantItemPinned", className: iconStyles }), callingParticipant.mediaAccess && !callingParticipant.mediaAccess.isVideoPermitted ? (React.createElement(Icon, { iconName: "ControlButtonCameraProhibited", className: iconStyles, ariaLabel: strings.mutedIconLabel })) : undefined, callingParticipant.mediaAccess && !((_b = callingParticipant.mediaAccess) === null || _b === void 0 ? void 0 : _b.isAudioPermitted) ? (React.createElement(Icon, { iconName: "ControlButtonMicProhibited", className: iconStyles, ariaLabel: strings.mutedIconLabel })) : undefined, (callingParticipant.mediaAccess ? callingParticipant.mediaAccess.isAudioPermitted : true) && callingParticipant.isMuted ? (React.createElement(Icon, { iconName: "ParticipantItemMicOff", className: iconStyles, ariaLabel: strings.mutedIconLabel })) : undefined)); } : () => null; const onRenderAvatarWithRaiseHand = (callingParticipant === null || callingParticipant === void 0 ? void 0 : callingParticipant.raisedHand) && onRenderAvatar ? (userId, options, defaultOnRender) => onRenderAvatar(userId, Object.assign(Object.assign({}, options), { styles: { root: { border: callingPalette.raiseHandGold } } }), defaultOnRender) : onRenderAvatar; const ariaLabelTemplate = (_a = (menuItems && menuItems.length > 0 ? strings === null || strings === void 0 ? void 0 : strings.participantItemWithMoreOptionsAriaLabel : undefined)) !== null && _a !== void 0 ? _a : strings === null || strings === void 0 ? void 0 : strings.participantItemAriaLabel; const ariaLabel = _formatString(ariaLabelTemplate !== null && ariaLabelTemplate !== void 0 ? ariaLabelTemplate : '', { displayName: displayName !== null && displayName !== void 0 ? displayName : '', connectionState: (_b = formatParticipantStateString(callingParticipant, strings)) !== null && _b !== void 0 ? _b : '', mutedState: (_c = (callingParticipant.isMuted ? strings === null || strings === void 0 ? void 0 : strings.mutedIconLabel : undefined)) !== null && _c !== void 0 ? _c : '', micDisabledState: (_e = (((_d = callingParticipant.mediaAccess) === null || _d === void 0 ? void 0 : _d.isAudioPermitted) === false ? strings === null || strings === void 0 ? void 0 : strings.micDisabledIconLabel : undefined)) !== null && _e !== void 0 ? _e : '', cameraDisabledState: (_g = (((_f = callingParticipant.mediaAccess) === null || _f === void 0 ? void 0 : _f.isVideoPermitted) === false ? strings === null || strings === void 0 ? void 0 : strings.cameraDisabledIconLabel : undefined)) !== null && _g !== void 0 ? _g : '', sharingState: (_h = (callingParticipant.isScreenSharing ? strings === null || strings === void 0 ? void 0 : strings.sharingIconLabel : undefined)) !== null && _h !== void 0 ? _h : '', handRaisedState: (_p = (((_j = callingParticipant.raisedHand) === null || _j === void 0 ? void 0 : _j.raisedHandOrderPosition) ? _formatString((_k = strings === null || strings === void 0 ? void 0 : strings.handRaisedIconLabel) !== null && _k !== void 0 ? _k : '', { position: (_o = (_m = (_l = callingParticipant.raisedHand) === null || _l === void 0 ? void 0 : _l.raisedHandOrderPosition) === null || _m === void 0 ? void 0 : _m.toString()) !== null && _o !== void 0 ? _o : '' }) : undefined)) !== null && _p !== void 0 ? _p : '' }); return (React.createElement(ParticipantItem, { styles: styles, key: participant.userId, userId: participant.userId, displayName: displayName, me: myUserId ? participant.userId === myUserId : false, menuItems: menuItems, presence: presence, onRenderIcon: onRenderIcon, onRenderAvatar: onRenderAvatarWithRaiseHand, onClick: onParticipantClick ? () => onParticipantClick === null || onParticipantClick === void 0 ? void 0 : onParticipantClick(participant) : undefined, showParticipantOverflowTooltip: showParticipantOverflowTooltip, participantState: callingParticipant.state, ariaLabelledBy: participantAriaLabelledBy, strings: { participantItemAriaLabel: ariaLabel } })); }; /** * Sort participants by raised hand order position */ const sortParticipants = (participants) => { const isParticipantListCallParticipant = function (participant) { return 'raisedHand' in participant; }; participants.sort((a, b) => { if (!isParticipantListCallParticipant(a) || !isParticipantListCallParticipant(b)) { return 0; } const callA = a; const callB = b; if (callA.raisedHand && callB.raisedHand) { return callA.raisedHand.raisedHandOrderPosition - callB.raisedHand.raisedHandOrderPosition; } else if (callA.raisedHand) { return -1; } else if (callB.raisedHand) { return 1; } return 0; }); return participants; }; const getParticipantsForDefaultRender = (participants, excludeMe, myUserId) => { if (!excludeMe || !myUserId) { return [...participants]; } const userIndex = participants.map((p) => p.userId).indexOf(myUserId); if (userIndex === -1) { return [...participants]; } const remoteParticipants = [...participants]; remoteParticipants.splice(userIndex, 1); return remoteParticipants; }; /** * Component to render all calling or chat participants. * * By default, each participant is rendered with {@link ParticipantItem}. See {@link ParticipantListProps.onRenderParticipant} to override. * * @public */ export const ParticipantList = (props) => { var _a, _b, _c, _d, _e; const { excludeMe = false, myUserId, participants, onRemoveParticipant, onRenderAvatar, onRenderParticipant, onFetchParticipantMenuItems, showParticipantOverflowTooltip, /* @conditional-compile-remove(total-participant-count) */ totalParticipantCount, /* @conditional-compile-remove(total-participant-count) */ strings, participantAriaLabelledBy, pinnedParticipants } = props; const theme = useTheme(); const ids = useIdentifiers(); const participantItemStrings = useLocale().strings.participantItem; /* @conditional-compile-remove(total-participant-count) */ const participantListStrings = useLocale().strings.ParticipantList; const displayedParticipants = useMemo(() => { return onRenderParticipant ? participants : getParticipantsForDefaultRender(participants, excludeMe, myUserId); }, [participants, excludeMe, myUserId, onRenderParticipant]); sortParticipants(displayedParticipants); const createParticipantMenuItems = useCallback((participant) => { var _a, _b; let menuItems = []; const participantIsRemovable = participant.isRemovable; if (participant.userId !== myUserId && onRemoveParticipant && participantIsRemovable) { menuItems.push({ key: 'remove', text: participantItemStrings.removeButtonLabel, onClick: () => onRemoveParticipant(participant.userId), itemProps: { styles: (_b = (_a = props.styles) === null || _a === void 0 ? void 0 : _a.participantItemStyles) === null || _b === void 0 ? void 0 : _b.participantSubMenuItemsStyles }, iconProps: { iconName: 'ContextMenuRemoveParticipant', styles: { root: { lineHeight: 0 } } }, 'data-ui-id': ids.participantListRemoveParticipantButton }); } if (onFetchParticipantMenuItems) { menuItems = onFetchParticipantMenuItems(participant.userId, myUserId, menuItems); } return menuItems; }, [ ids.participantListRemoveParticipantButton, myUserId, onFetchParticipantMenuItems, onRemoveParticipant, (_b = (_a = props.styles) === null || _a === void 0 ? void 0 : _a.participantItemStyles) === null || _b === void 0 ? void 0 : _b.participantSubMenuItemsStyles, participantItemStrings.removeButtonLabel ]); const participantItemStyles = useMemo(() => { var _a; return merge(participantListItemStyle, (_a = props.styles) === null || _a === void 0 ? void 0 : _a.participantItemStyles); }, [(_c = props.styles) === null || _c === void 0 ? void 0 : _c.participantItemStyles]); /* @conditional-compile-remove(total-participant-count) */ const overflowParticipantCountString = (_d = strings === null || strings === void 0 ? void 0 : strings.overflowParticipantCount) !== null && _d !== void 0 ? _d : participantListStrings === null || participantListStrings === void 0 ? void 0 : participantListStrings.overflowParticipantCount; return (React.createElement(Stack, { "data-ui-id": ids.participantList, className: mergeStyles(participantListStyle, (_e = props.styles) === null || _e === void 0 ? void 0 : _e.root), role: 'menu' }, displayedParticipants.map((participant) => onRenderParticipant ? onRenderParticipant(participant) : onRenderParticipantDefault(participant, participantItemStrings, myUserId, onRenderAvatar, createParticipantMenuItems, participantItemStyles, props.onParticipantClick, showParticipantOverflowTooltip, participantAriaLabelledBy, theme, pinnedParticipants)), /* @conditional-compile-remove(total-participant-count) */ overflowParticipantCountString && totalParticipantCount && totalParticipantCount > displayedParticipants.length && (React.createElement(Text, { style: { fontWeight: 400, margin: '0.5rem' } }, _formatString(overflowParticipantCountString, { overflowCount: `${totalParticipantCount - displayedParticipants.length}` }))))); }; //# sourceMappingURL=ParticipantList.js.map