communication-react-19
Version:
React library for building modern communication user experiences utilizing Azure Communication Services (React 19 compatible fork)
139 lines • 8.67 kB
JavaScript
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { Layer, Stack } from '@fluentui/react';
import React, { useMemo } from 'react';
import { useCallback } from 'react';
import { _DrawerMenu } from './Drawer';
import { StreamMedia } from './StreamMedia';
import { drawerMenuWrapperStyles, remoteVideoTileWrapperStyle } from './VideoGallery/styles/RemoteVideoTile.styles';
import { useRemoteVideoStreamLifecycleMaintainer } from './VideoGallery/useVideoStreamLifecycleMaintainer';
import { useVideoTileContextualMenuProps } from './VideoGallery/useVideoTileContextualMenuProps';
import { VideoTile } from './VideoTile';
import { _formatString } from "../../../acs-ui-common/src";
import { MeetingReactionOverlay } from './MeetingReactionOverlay';
/**
* A memoized version of VideoTile for rendering remote participants. React.memo is used for a performance
* boost by memoizing the same rendered component to avoid rerendering a VideoTile when its position in the
* array changes causing a rerender in the parent component. https://reactjs.org/docs/react-api.html#reactmemo
*
* @internal
*/
export const _RemoteVideoTile = React.memo((props) => {
var _a, _b;
const { isAvailable, isReceiving = true, // default to true to prevent any breaking change
isScreenSharingOn, onCreateRemoteStreamView, onDisposeRemoteStreamView, remoteVideoViewOptions, renderElement, userId, onRenderAvatar, showMuteIndicator, remoteParticipant, participantState, menuKind, isPinned, onPinParticipant, onUnpinParticipant, spotlightedParticipantUserIds, isSpotlighted, onStartSpotlight, onStopSpotlight, maxParticipantsToSpotlight, onMuteParticipant, onUpdateScalingMode, disablePinMenuItem, toggleAnnouncerString, strings, reactionResources, streamId, onForbidAudio, onPermitAudio, onForbidVideo, onPermitVideo } = props;
const remoteVideoStreamProps = useMemo(() => ({
isMirrored: remoteVideoViewOptions === null || remoteVideoViewOptions === void 0 ? void 0 : remoteVideoViewOptions.isMirrored,
isScreenSharingOn,
isStreamAvailable: isAvailable,
isStreamReceiving: isReceiving,
onCreateRemoteStreamView,
onDisposeRemoteStreamView,
remoteParticipantId: userId,
renderElementExists: !!renderElement,
scalingMode: remoteVideoViewOptions === null || remoteVideoViewOptions === void 0 ? void 0 : remoteVideoViewOptions.scalingMode,
streamId,
isVideoPermitted: remoteParticipant.mediaAccess ? remoteParticipant.mediaAccess.isVideoPermitted : true
}), [
isAvailable,
isReceiving,
isScreenSharingOn,
onCreateRemoteStreamView,
onDisposeRemoteStreamView,
remoteVideoViewOptions === null || remoteVideoViewOptions === void 0 ? void 0 : remoteVideoViewOptions.isMirrored,
remoteVideoViewOptions === null || remoteVideoViewOptions === void 0 ? void 0 : remoteVideoViewOptions.scalingMode,
renderElement,
userId,
streamId,
remoteParticipant.mediaAccess
]);
// Handle creating, destroying and updating the video stream as necessary
const createVideoStreamResult = useRemoteVideoStreamLifecycleMaintainer(remoteVideoStreamProps);
const contextualMenuProps = useVideoTileContextualMenuProps({
participant: remoteParticipant,
view: createVideoStreamResult === null || createVideoStreamResult === void 0 ? void 0 : createVideoStreamResult.view,
strings: Object.assign({}, props.strings),
isPinned,
onPinParticipant,
onUnpinParticipant,
onUpdateScalingMode,
disablePinMenuItem,
toggleAnnouncerString,
spotlightedParticipantUserIds,
isSpotlighted,
onStartSpotlight,
onStopSpotlight,
maxParticipantsToSpotlight,
onMuteParticipant,
onForbidAudio,
onPermitAudio,
onForbidVideo,
onPermitVideo
});
const videoTileContextualMenuProps = useMemo(() => {
if (menuKind !== 'contextual' || !contextualMenuProps) {
return {};
}
return {
contextualMenu: contextualMenuProps
};
}, [contextualMenuProps, menuKind]);
const showLoadingIndicator = isAvailable && isReceiving === false && participantState !== 'Disconnected';
const isReconnecting = participantState === 'Reconnecting';
const [drawerMenuItemProps, setDrawerMenuItemProps] = React.useState([]);
const renderVideoStreamElement = useMemo(() => {
// Checking if renderElement is well defined or not as calling SDK has a number of video streams limitation which
// implies that, after their threshold, all streams have no child (blank video)
if ((!renderElement || !renderElement.childElementCount) && !isReconnecting) {
// Returning `undefined` results in the placeholder with avatar being shown
return undefined;
}
return (React.createElement(StreamMedia, { videoStreamElement: renderElement !== null && renderElement !== void 0 ? renderElement : null, loadingState: showLoadingIndicator ? 'loading' : isReconnecting ? 'reconnecting' : 'none' }));
}, [isReconnecting, renderElement, showLoadingIndicator]);
const onKeyDown = useCallback((e) => {
if (e.key === 'Enter') {
setDrawerMenuItemProps(convertContextualMenuItemsToDrawerMenuItemProps(contextualMenuProps, () => setDrawerMenuItemProps([])));
}
}, [setDrawerMenuItemProps, contextualMenuProps]);
const attendeeRoleString = (_a = props.strings) === null || _a === void 0 ? void 0 : _a.attendeeRole;
const formatDisplayName = (displayName, role) => {
if (displayName && role) {
return _formatString(displayName, { AttendeeRole: role });
}
return displayName;
};
const displayName = formatDisplayName(remoteParticipant.displayName ? remoteParticipant.displayName : strings.displayNamePlaceholder, attendeeRoleString);
const formatInitialsName = () => {
if (remoteParticipant.displayName && attendeeRoleString) {
return _formatString(remoteParticipant.displayName, { AttendeeRole: attendeeRoleString });
}
return remoteParticipant.displayName;
};
const reactionOverlay = reactionResources && (React.createElement(MeetingReactionOverlay, { overlayMode: "grid-tiles", reaction: remoteParticipant.reaction, reactionResources: reactionResources }));
return (React.createElement(Stack, { tabIndex: menuKind === 'drawer' ? 0 : undefined, onKeyDown: menuKind === 'drawer' ? onKeyDown : undefined, style: remoteVideoTileWrapperStyle },
React.createElement(VideoTile, Object.assign({ key: userId, userId: userId, initialsName: (_b = formatInitialsName()) !== null && _b !== void 0 ? _b : '', renderElement: renderVideoStreamElement, displayName: displayName, onRenderPlaceholder: onRenderAvatar, isMuted: remoteParticipant.isMuted, raisedHand: remoteParticipant.raisedHand, isSpeaking: remoteParticipant.isSpeaking, showMuteIndicator: showMuteIndicator, personaMinSize: props.personaMinSize, showLabel: props.showLabel, alwaysShowLabelBackground: props.alwaysShowLabelBackground, participantState: participantState }, videoTileContextualMenuProps, { isPinned: props.isPinned, onLongTouch: props.onLongTouch
? props.onLongTouch
: () => setDrawerMenuItemProps(convertContextualMenuItemsToDrawerMenuItemProps(contextualMenuProps, () => setDrawerMenuItemProps([]))), isSpotlighted: isSpotlighted, overlay: reactionOverlay, mediaAccess: remoteParticipant.mediaAccess })),
drawerMenuItemProps.length > 0 && (React.createElement(Layer, { hostId: props.drawerMenuHostId },
React.createElement(Stack, { styles: drawerMenuWrapperStyles },
React.createElement(_DrawerMenu, { onLightDismiss: () => setDrawerMenuItemProps([]), items: drawerMenuItemProps, heading: displayName }))))));
});
const convertContextualMenuItemsToDrawerMenuItemProps = (contextualMenuProps, onLightDismiss) => {
if (!contextualMenuProps) {
return [];
}
return contextualMenuProps.items.map((item) => {
return {
itemKey: item.key,
text: item.text,
iconProps: item.iconProps,
disabled: item.disabled,
onItemClick: () => {
var _a;
(_a = item.onClick) === null || _a === void 0 ? void 0 : _a.call(item);
onLightDismiss === null || onLightDismiss === void 0 ? void 0 : onLightDismiss();
}
};
});
};
//# sourceMappingURL=RemoteVideoTile.js.map