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