UNPKG

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