communication-react-19
Version:
React library for building modern communication user experiences utilizing Azure Communication Services (React 19 compatible fork)
374 lines • 16.1 kB
JavaScript
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { MessageBarType } from '@fluentui/react';
import { _formatString } from "../../../acs-ui-common/src";
/**
* @private
*
* @param fileName
* @param length
* @returns string
*/
export const truncatedFileName = (fileName, length) => {
return fileName.substring(0, length).trimEnd() + (fileName.length > length ? '... ' : '');
};
/**
* @private
*
* @param fileName
* @returns string
*/
export const extension = (fileName) => fileName.split('.').pop() || '';
/**
* @private
* @param dismissedErrors
* @param toDismiss
* @returns DismissedError[]
* Always returns a new Array so that the state variable is updated, trigerring a render.
*/
export const dismissError = (dismissedErrors, toDismiss) => {
var _a;
const now = new Date(Date.now());
for (const error of dismissedErrors) {
if (error.type === toDismiss.type) {
// Bump the timestamp for latest dismissal of this error to now.
error.dismissedAt = now;
error.activeSince = toDismiss.timestamp;
return Array.from(dismissedErrors);
}
}
const toDismissTimestamp = (_a = toDismiss.timestamp) !== null && _a !== void 0 ? _a : now;
// Record that this error was dismissed for the first time right now.
return [
...dismissedErrors,
{
type: toDismiss.type,
// the error time could be sometimes later than the button click time, which cause the dismiss not working
// so we set the dismiss time to the later one
dismissedAt: now > toDismissTimestamp ? now : toDismissTimestamp,
activeSince: toDismiss.timestamp
}
];
};
/**
* @private
* @param activeErrorMessages
* @param dismissedErrors
* @returns DismissedError[]
* Returns a new Array if and only if contents change, to avoid re-rendering when nothing was dropped.
*/
export const dropDismissalsForInactiveErrors = (activeErrorMessages, dismissedErrors) => {
const active = new Map();
for (const message of activeErrorMessages) {
active.set(message.type, message);
}
// For an error such that:
// * It was previously active, and dismissed.
// * It did not have a timestamp associated with it.
// * It is no longer active.
//
// We remove it from dismissals. When it becomes active again next time, it will be shown again on the UI.
const shouldDeleteDismissal = (dismissed) => dismissed.activeSince === undefined && active.get(dismissed.type) === undefined;
if (dismissedErrors.some((dismissed) => shouldDeleteDismissal(dismissed))) {
return dismissedErrors.filter((dismissed) => !shouldDeleteDismissal(dismissed));
}
return dismissedErrors;
};
/**
* @private
* @param dismissedNotifications
* @param toDismiss
* @returns DismissedNotification[]
* Always returns a new Array so that the state variable is updated, trigerring a render.
*/
export const dismissNotification = (dismissedNotifications, toDismiss) => {
var _a;
const now = new Date(Date.now());
for (const notification of dismissedNotifications) {
if (notification.type === toDismiss.type) {
// Bump the timestamp for latest dismissal of this error to now.
notification.dismissedAt = now;
notification.activeSince = toDismiss.timestamp;
return Array.from(dismissedNotifications);
}
}
const toDismissTimestamp = (_a = toDismiss.timestamp) !== null && _a !== void 0 ? _a : now;
// Record that this error was dismissed for the first time right now.
return [
...dismissedNotifications,
{
type: toDismiss.type,
// the error time could be sometimes later than the button click time, which cause the dismiss not working
// so we set the dismiss time to the later one
dismissedAt: now > toDismissTimestamp ? now : toDismissTimestamp,
activeSince: toDismiss.timestamp
}
];
};
/**
* @private
* @param activeNotifications
* @param dismissedNotifications
* @returns DismissedError[]
* Returns a new Array if and only if contents change, to avoid re-rendering when nothing was dropped.
*/
export const dropDismissalsForInactiveNotifications = (activeNotifications, dismissedNotifications) => {
const active = new Map();
for (const message of activeNotifications) {
active.set(message.type, message);
}
// For an error such that:
// * It was previously active, and dismissed.
// * It did not have a timestamp associated with it.
// * It is no longer active.
//
// We remove it from dismissals. When it becomes active again next time, it will be shown again on the UI.
const shouldDeleteDismissal = (dismissed) => dismissed.activeSince === undefined && active.get(dismissed.type) === undefined;
if (dismissedNotifications.some((dismissed) => shouldDeleteDismissal(dismissed))) {
return dismissedNotifications.filter((dismissed) => !shouldDeleteDismissal(dismissed));
}
return dismissedNotifications;
};
/**
* @private
* @param activeErrorMessages
* @param dismissedErrors
* @returns ActiveErrorMessage[]
*/
export const errorsToShow = (activeErrorMessages, dismissedErrors, mountTimestamp) => {
const dismissed = new Map();
for (const error of dismissedErrors) {
dismissed.set(error.type, error);
}
return activeErrorMessages.filter((error) => {
if (mountTimestamp && error.timestamp && mountTimestamp > error.timestamp) {
// Error has a timestamp and it is older than when the component was mounted.
return false;
}
const dismissal = dismissed.get(error.type);
if (!dismissal) {
// This error was never dismissed.
return true;
}
if (!error.timestamp) {
// No timestamp associated with the error. In this case, the existence of a dismissal is enough to suppress the error.
return false;
}
// Error has an associated timestamp, so compare with last dismissal.
return error.timestamp > dismissal.dismissedAt;
});
};
/**
* @private
* @param activeNotifications
* @param dismissedNotifications
* @returns ActiveNotification[]
*/
export const notificationsToShow = (activeNotifications, dismissedNotifications, mountTimestamp) => {
const dismissed = new Map();
for (const notification of dismissedNotifications) {
dismissed.set(notification.type, notification);
}
return activeNotifications.filter((notification) => {
if (mountTimestamp && notification.timestamp && mountTimestamp > notification.timestamp) {
// Notification has a timestamp and it is older than when the component was mounted.
return false;
}
const dismissal = dismissed.get(notification.type);
if (!dismissal) {
// This error was never dismissed.
return true;
}
if (!notification.timestamp) {
// No timestamp associated with the error. In this case, the existence of a dismissal is enough to suppress the error.
return false;
}
// Error has an associated timestamp, so compare with last dismissal.
return notification.timestamp > dismissal.dismissedAt;
});
};
/**
* @private
* @param errorType
* @returns MessageBarType
*/
export const messageBarType = (errorType) => {
switch (errorType) {
case 'callNetworkQualityLow':
case 'callNoSpeakerFound':
case 'callNoMicrophoneFound':
case 'callMicrophoneAccessDenied':
case 'callMicrophoneAccessDeniedSafari':
case 'callMicrophoneMutedBySystem':
case 'callMicrophoneUnmutedBySystem':
case 'callMacOsMicrophoneAccessDenied':
case 'callLocalVideoFreeze':
case 'callCameraAccessDenied':
case 'callCameraAccessDeniedSafari':
case 'callCameraAlreadyInUse':
case 'callVideoStoppedBySystem':
case 'callVideoRecoveredBySystem':
case 'callMacOsCameraAccessDenied':
case 'callMacOsScreenShareAccessDenied':
case 'startScreenShareGeneric':
case 'cameraFrozenForRemoteParticipants':
return MessageBarType.warning;
default:
return MessageBarType.error;
}
};
/**
* @private
* @param errorType
* @returns IIconProps | undefined
*/
export const messageBarIconProps = (errorType) => {
const iconName = customIconName[errorType];
return iconName ? { iconName } : undefined;
};
/**
* @private
*/
export const customIconName = {
callNetworkQualityLow: 'ErrorBarCallNetworkQualityLow',
callNoSpeakerFound: 'ErrorBarCallNoSpeakerFound',
callNoMicrophoneFound: 'ErrorBarCallNoMicrophoneFound',
callMicrophoneAccessDenied: 'ErrorBarCallMicrophoneAccessDenied',
callMicrophoneAccessDeniedSafari: 'ErrorBarCallMicrophoneAccessDenied',
callMicrophoneMutedBySystem: 'ErrorBarCallMicrophoneMutedBySystem',
callMicrophoneUnmutedBySystem: 'ErrorBarCallMicrophoneUnmutedBySystem',
callMacOsMicrophoneAccessDenied: 'ErrorBarCallMacOsMicrophoneAccessDenied',
callLocalVideoFreeze: 'ErrorBarCallLocalVideoFreeze',
callCameraAccessDenied: 'ErrorBarCallCameraAccessDenied',
callCameraAccessDeniedSafari: 'ErrorBarCallCameraAccessDenied',
callCameraAlreadyInUse: 'ErrorBarCallCameraAlreadyInUse',
callVideoStoppedBySystem: 'ErrorBarCallVideoStoppedBySystem',
callVideoRecoveredBySystem: 'ErrorBarCallVideoRecoveredBySystem',
callMacOsCameraAccessDenied: 'ErrorBarCallMacOsCameraAccessDenied'
};
/**
* @private
* @param NotificationType
* @returns IIconProps | undefined
*/
export const NotificationIconProps = (notificationType) => {
const iconName = customNotificationIconName[notificationType];
return iconName ? { iconName } : undefined;
};
/**
* @private
*/
export const customNotificationIconName = {
callNetworkQualityLow: 'ErrorBarCallNetworkQualityLow',
teamsMeetingCallNetworkQualityLow: 'ErrorBarCallNetworkQualityLow',
callNoSpeakerFound: 'ErrorBarCallNoSpeakerFound',
callNoMicrophoneFound: 'ErrorBarCallNoMicrophoneFound',
callMicrophoneAccessDenied: 'ErrorBarCallMicrophoneAccessDenied',
callMicrophoneAccessDeniedSafari: 'ErrorBarCallMicrophoneAccessDenied',
callMicrophoneMutedBySystem: 'ErrorBarCallMicrophoneMutedBySystem',
callMicrophoneUnmutedBySystem: 'ErrorBarCallMicrophoneUnmutedBySystem',
callMacOsMicrophoneAccessDenied: 'ErrorBarCallMacOsMicrophoneAccessDenied',
callLocalVideoFreeze: 'ErrorBarCallLocalVideoFreeze',
callCameraAccessDenied: 'ErrorBarCallCameraAccessDenied',
callCameraAccessDeniedSafari: 'ErrorBarCallCameraAccessDenied',
callCameraAlreadyInUse: 'ErrorBarCallCameraAlreadyInUse',
callVideoStoppedBySystem: 'ErrorBarCallVideoStoppedBySystem',
callVideoRecoveredBySystem: 'ErrorBarCallVideoRecoveredBySystem',
callMacOsCameraAccessDenied: 'ErrorBarCallMacOsCameraAccessDenied',
mutedByRemoteParticipant: 'ErrorBarMutedByRemoteParticipant',
speakingWhileMuted: 'ErrorBarCallMicrophoneMutedBySystem',
recordingStarted: 'NotificationBarRecording',
transcriptionStarted: 'NotificationBarRecording',
recordingStopped: 'NotificationBarRecording',
transcriptionStopped: 'NotificationBarRecording',
recordingAndTranscriptionStarted: 'NotificationBarRecording',
recordingAndTranscriptionStopped: 'NotificationBarRecording',
recordingStoppedStillTranscribing: 'NotificationBarRecording',
transcriptionStoppedStillRecording: 'NotificationBarRecording',
assignedBreakoutRoomOpened: 'NotificationBarBreakoutRoomOpened',
assignedBreakoutRoomOpenedPromptJoin: 'NotificationBarBreakoutRoomPromptJoin',
assignedBreakoutRoomChanged: 'NotificationBarBreakoutRoomChanged',
assignedBreakoutRoomClosed: 'NotificationBarBreakoutRoomClosed',
breakoutRoomJoined: 'NotificationBarBreakoutRoomJoined',
breakoutRoomClosingSoon: 'NotificationBarBreakoutRoomClosingSoon',
capabilityTurnVideoOnPresent: 'ControlButtonCameraOff',
capabilityTurnVideoOnAbsent: 'ControlButtonCameraProhibited',
capabilityUnmuteMicPresent: 'ControlButtonMicOff',
capabilityUnmuteMicAbsent: 'ControlButtonMicProhibited',
togetherModeStarted: 'NotificationBarTogetherModeIcon',
togetherModeEnded: 'NotificationBarTogetherModeIcon',
transcriptionError: 'NotificationBarTranscriptionError',
transcriptionStartedByYou: 'NotificationBarTranscriptionStartedByYou'
};
/**
* @private
*/
export const isValidString = (string) => {
return !!string && string.length > 0;
};
/**
* Chunk an array into rows of a given size.
* @private
*/
export function chunk(options, itemsPerRow) {
const rows = [];
let currentRow = [];
for (const option of options) {
currentRow.push(option);
if (currentRow.length === itemsPerRow) {
rows.push(currentRow);
currentRow = [];
}
}
if (currentRow.length > 0) {
rows.push(currentRow);
}
return rows;
}
/**
* @private
*/
export const defaultSpokenLanguage = 'en-us';
/**
* @private
*/
const SAFARI_COMPOSITION_KEYCODE = 229;
/**
* Determine if the press of the enter key is from a composition session or not (Safari only)
*
* @private
*/
export const isEnterKeyEventFromCompositionSession = (e) => {
// Uses KeyCode 229 and which code 229 to determine if the press of the enter key is from a composition session or not (the code check is needed for Safari only, for everything else e.isComposing check is enough)
const isComposing = e.isComposing || e.keyCode === SAFARI_COMPOSITION_KEYCODE || e.which === SAFARI_COMPOSITION_KEYCODE;
return isComposing && e.key === 'Enter';
};
/**
* @private
*/
export const nullToUndefined = (value) => (value === null ? undefined : value);
/**
* @private
*/
export const formatMoreButtonAriaDescription = (displayName, isMuted, isHandRaised, state, isSpeaking, strings, isMicDisabled, isCameraDisabled) => {
const mutedState = isMuted
? strings === null || strings === void 0 ? void 0 : strings.moreOptionsParticipantMutedStateMutedAriaLabel
: strings === null || strings === void 0 ? void 0 : strings.moreOptionsParticipantMutedStateUnmutedAriaLabel;
const handRaisedState = isHandRaised ? strings === null || strings === void 0 ? void 0 : strings.moreOptionsParticipantHandRaisedAriaLabel : undefined;
const isSpeakingState = isSpeaking ? strings === null || strings === void 0 ? void 0 : strings.moreOptionsParticipantIsSpeakingAriaLabel : undefined;
const micDisabledState = isMicDisabled ? strings === null || strings === void 0 ? void 0 : strings.moreOptionsParticipantMicDisabledAriaLabel : undefined;
const cameraDisabledState = isCameraDisabled ? strings === null || strings === void 0 ? void 0 : strings.moreOptionsParticipantCameraDisabledAriaLabel : undefined;
const description = (strings === null || strings === void 0 ? void 0 : strings.moreOptionsButtonAriaLabel)
? _formatString(strings === null || strings === void 0 ? void 0 : strings.moreOptionsButtonAriaLabel, {
displayName: displayName !== null && displayName !== void 0 ? displayName : ' ',
isMuted: mutedState !== null && mutedState !== void 0 ? mutedState : ' ',
isHandRaised: handRaisedState !== null && handRaisedState !== void 0 ? handRaisedState : ' ',
state: state !== null && state !== void 0 ? state : ' ',
isSpeaking: isSpeakingState !== null && isSpeakingState !== void 0 ? isSpeakingState : ' ',
micDisabledState: micDisabledState !== null && micDisabledState !== void 0 ? micDisabledState : ' ',
cameraDisabledState: cameraDisabledState !== null && cameraDisabledState !== void 0 ? cameraDisabledState : ' '
})
: '';
return description;
};
//# sourceMappingURL=utils.js.map