Skip to content

Commit

Permalink
Real Time Text Composite Changes (#5522)
Browse files Browse the repository at this point in the history
* rtt

* rtt

* RTt update with captions

* update

* test

* update

* add missing cc

* RTT disclosure banner

* Change files

* rename

* Change files

* Update

* Adapter event

* update

* Revert "update"

This reverts commit c60f37d.

* update

* cc

* update

* fix build

* Update

* update

* update

* cc bug

* update

* update

* udpate api

* update

* cc

* update

* strings

* Accessibility bug fix

* Remove redundant type assertion in captionsSelector

* Change files

* Update packages/react-composites CallWithChatComposite browser test snapshots

* Update packages/react-composites CallComposite browser test snapshots

---------

Signed-off-by: carocao-msft <96077406+carocao-msft@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
carocao-msft and github-actions[bot] authored Jan 29, 2025
1 parent 46692aa commit 5dcd4c3
Showing 53 changed files with 200 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "prerelease",
"area": "feature",
"workstream": "RTT",
"comment": "Add RTT to composite experience",
"packageName": "@azure/communication-react",
"email": "96077406+carocao-msft@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

/* @conditional-compile-remove(rtt) */
import { RealTimeTextFeature, RealTimeTextInfo, RealTimeTextReceivedEventHandler } from '@azure/communication-calling';
/* @conditional-compile-remove(rtt) */
Original file line number Diff line number Diff line change
@@ -58,6 +58,10 @@ export interface _DrawerMenuItemProps {
* Custom JSX item injection for custom mobile view button on drawers
*/
onRendererContent?: () => JSX.Element;
/**
* Aria label for the menu item
*/
ariaLabel?: string;
}

/**
@@ -92,6 +96,7 @@ export const DrawerMenuItem = (props: _DrawerMenuItemProps): JSX.Element => {
onClick={props.disabled ? undefined : onClick}
tokens={menuItemChildrenGap}
id={props.id}
aria-label={props.ariaLabel}
>
{props.iconProps && (
<Stack.Item
Original file line number Diff line number Diff line change
@@ -537,6 +537,12 @@ export const CallArrangement = (props: CallArrangementProps): JSX.Element => {
: latestNotifications;

const reactionResources = useSelector(getReactionResources);
/* @conditional-compile-remove(rtt) */
const [openRealTimeText, setOpenRealTimeText] = useState<boolean>(false);
/* @conditional-compile-remove(rtt) */
const onStartRealTimeText = useCallback(() => {
setOpenRealTimeText(true);
}, []);

return (
<div ref={containerRef} className={mergeStyles(containerDivStyles)} id={props.id}>
@@ -578,6 +584,12 @@ export const CallArrangement = (props: CallArrangementProps): JSX.Element => {
onPeopleButtonClicked={togglePeoplePane}
onMoreButtonClicked={onMoreButtonClicked}
isCaptionsSupported={(useTeamsCaptions && hasJoinedCall) || hasJoinedCall}
/* @conditional-compile-remove(rtt) */
isRealTimeTextSupported={hasJoinedCall}
/* @conditional-compile-remove(rtt) */
onStartRealTimeText={onStartRealTimeText}
/* @conditional-compile-remove(rtt) */
startRealTimeTextButtonChecked={openRealTimeText}
useTeamsCaptions={useTeamsCaptions}
isCaptionsOn={isCaptionsOn}
onClickVideoEffects={onResolveVideoEffectDependency ? openVideoEffectsPane : undefined}
@@ -607,6 +619,12 @@ export const CallArrangement = (props: CallArrangementProps): JSX.Element => {
onPeopleButtonClicked={onMoreDrawerPeopleClicked}
disableButtonsForHoldScreen={isInLocalHold}
isCaptionsSupported={(useTeamsCaptions && hasJoinedCall) || hasJoinedCall}
/* @conditional-compile-remove(rtt) */
isRealTimeTextSupported={hasJoinedCall}
/* @conditional-compile-remove(rtt) */
onStartRealTimeText={onStartRealTimeText}
/* @conditional-compile-remove(rtt) */
startRealTimeTextButtonChecked={openRealTimeText}
useTeamsCaptions={useTeamsCaptions}
onUserSetGalleryLayout={props.onUserSetGalleryLayoutChange}
userSetGalleryLayout={props.userSetGalleryLayout}
@@ -663,6 +681,8 @@ export const CallArrangement = (props: CallArrangementProps): JSX.Element => {
onFetchAvatarPersonaData={props.onFetchAvatarPersonaData}
useTeamsCaptions={useTeamsCaptions}
returnFocusRef={controlBarRef}
/* @conditional-compile-remove(rtt) */
isRealTimeTextOn={openRealTimeText}
/>
)}
</Stack>
Original file line number Diff line number Diff line change
@@ -25,6 +25,8 @@ export const CallingCaptionsBanner = (props: {
};
/** Element to return focus to when the Captions Banner is closed */
returnFocusRef?: React.RefObject<FocusableElement>;
/* @conditional-compile-remove(rtt) */
isRealTimeTextOn?: boolean;
}): JSX.Element => {
const captionsBannerProps = usePropsFor(CaptionsBanner);
const [isCaptionsSettingsOpen, setIsCaptionsSettingsOpen] = useState<boolean>(false);
@@ -120,6 +122,8 @@ export const CallingCaptionsBanner = (props: {
formFactor={props.isMobile ? 'compact' : 'default'}
strings={captionsBannerStrings}
{...captionsBannerProps}
/* @conditional-compile-remove(rtt) */
isRealTimeTextOn={props.isRealTimeTextOn || captionsBannerProps.isRealTimeTextOn}
/>
</Stack.Item>
</Stack>
Original file line number Diff line number Diff line change
@@ -66,6 +66,8 @@ import { MeetingConferencePhoneInfoModal } from '@internal/react-components';
/* @conditional-compile-remove(breakout-rooms) */
import { Timer } from './Timer';
import { FocusableElement } from '../types/FocusableElement';
/* @conditional-compile-remove(rtt) */
import { CallingRealTimeTextModal } from '../CallingRealTimeTextModal';

/**
* @private
@@ -82,6 +84,7 @@ export interface CommonCallControlBarProps {
onClickShowDialpad?: () => void;
onClickVideoEffects?: (showVideoEffects: boolean) => void;
isCaptionsSupported?: boolean;
isRealTimeTextSupported?: boolean;
isCaptionsOn?: boolean;
displayVertical?: boolean;
onUserSetOverflowGalleryPositionChange?: (position: 'Responsive' | 'horizontalTop') => void;
@@ -97,6 +100,10 @@ export interface CommonCallControlBarProps {
onToggleTeamsMeetingConferenceModal?: () => void;
teamsMeetingConferenceModalPresent?: boolean;
sidePaneDismissButtonRef?: RefObject<IButton>;
/* @conditional-compile-remove(rtt) */
onStartRealTimeText?: () => void;
/* @conditional-compile-remove(rtt) */
startRealTimeTextButtonChecked?: boolean;
}

const inferCommonCallControlOptions = (
@@ -153,6 +160,8 @@ export const CommonCallControlBar = forwardRef<FocusableElement, CommonCallContr
const options = inferCommonCallControlOptions(props.mobileView, props.callControls);

const [showCaptionsSettingsModal, setShowCaptionsSettingsModal] = useState(false);
/* @conditional-compile-remove(rtt) */
const [showRealTimeTextModal, setShowRealTimeTextModal] = useState(false);

// If the hangup capability is not present, we default to true
const isHangUpForEveryoneAllowed =
@@ -200,6 +209,14 @@ export const CommonCallControlBar = forwardRef<FocusableElement, CommonCallContr
const openCaptionsSettingsModal = useCallback((): void => {
setShowCaptionsSettingsModal(true);
}, []);
/* @conditional-compile-remove(rtt) */
const openRealTimeTextModal = useCallback((): void => {
setShowRealTimeTextModal(true);
}, []);
/* @conditional-compile-remove(rtt) */
const onDismissRealTimeTextModal = useCallback((): void => {
setShowRealTimeTextModal(false);
}, []);

const onDismissCaptionsSettings = useCallback((): void => {
setShowCaptionsSettingsModal(false);
@@ -350,12 +367,18 @@ export const CommonCallControlBar = forwardRef<FocusableElement, CommonCallContr
const showExitSpotlightButton = options?.exitSpotlightButton !== false;

const showCaptionsButton = props.isCaptionsSupported && isEnabled(options.captionsButton);
/* @conditional-compile-remove(rtt) */
const showRealTimeTextButton = props.isRealTimeTextSupported;

const showTeamsMeetingPhoneCallButton = isEnabled(options?.teamsMeetingPhoneCallButton);

const showDesktopMoreButton =
isEnabled(options?.moreButton) &&
(false || isEnabled(options?.holdButton) || showCaptionsButton || props.onUserSetGalleryLayout);
(false ||
isEnabled(options?.holdButton) ||
showCaptionsButton ||
props.onUserSetGalleryLayout ||
/* @conditional-compile-remove(rtt) */ showRealTimeTextButton);

const role = props.callAdapter.getState().call?.role;
const hideRaiseHandButtonInRoomsCall =
@@ -372,6 +395,15 @@ export const CommonCallControlBar = forwardRef<FocusableElement, CommonCallContr
changeCaptionLanguage={props.isCaptionsOn && props.useTeamsCaptions}
/>
)}
{
/* @conditional-compile-remove(rtt) */ showRealTimeTextModal && (
<CallingRealTimeTextModal
showModal={showRealTimeTextModal}
onDismissModal={onDismissRealTimeTextModal}
onStartRealTimeText={props.onStartRealTimeText}
/>
)
}
{props.teamsMeetingConferenceModalPresent && (
<MeetingConferencePhoneInfoModal
conferencePhoneInfoList={props.callAdapter.getState().call?.meetingConference?.conferencePhones ?? []}
@@ -528,7 +560,13 @@ export const CommonCallControlBar = forwardRef<FocusableElement, CommonCallContr
onClickShowDialpad={props.onClickShowDialpad}
callControls={props.callControls}
isCaptionsSupported={showCaptionsButton}
/* @conditional-compile-remove(rtt) */
isRealTimeTextSupported={showRealTimeTextButton}
onCaptionsSettingsClick={openCaptionsSettingsModal}
/* @conditional-compile-remove(rtt) */
onStartRealTimeTextClick={openRealTimeTextModal}
/* @conditional-compile-remove(rtt) */
startRealTimeTextButtonChecked={props.startRealTimeTextButtonChecked}
onUserSetOverflowGalleryPositionChange={props.onUserSetOverflowGalleryPositionChange}
onUserSetGalleryLayout={props.onUserSetGalleryLayout}
userSetGalleryLayout={props.userSetGalleryLayout}
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@

import { IContextualMenuItem } from '@fluentui/react';
import { ControlBarButtonProps } from '@internal/react-components';
/* @conditional-compile-remove(rtt) */
import { CaptionsBanner } from '@internal/react-components';
import { VideoGalleryLayout } from '@internal/react-components';
import { HoldButton } from '@internal/react-components';
import { StartCaptionsButton } from '@internal/react-components';
@@ -31,8 +33,14 @@ export interface DesktopMoreButtonProps extends ControlBarButtonProps {
disableButtonsForHoldScreen?: boolean;
onClickShowDialpad?: () => void;
isCaptionsSupported?: boolean;
/* @conditional-compile-remove(rtt) */
isRealTimeTextSupported?: boolean;
callControls?: boolean | CommonCallControlOptions;
onCaptionsSettingsClick?: () => void;
/* @conditional-compile-remove(rtt) */
onStartRealTimeTextClick?: () => void;
/* @conditional-compile-remove(rtt) */
startRealTimeTextButtonChecked?: boolean;
onUserSetOverflowGalleryPositionChange?: (position: 'Responsive' | 'horizontalTop') => void;
onUserSetGalleryLayout?: (layout: VideoGalleryLayout) => void;
userSetGalleryLayout?: VideoGalleryLayout;
@@ -50,6 +58,8 @@ export const DesktopMoreButton = (props: DesktopMoreButtonProps): JSX.Element =>
const localeStrings = useLocale();
const holdButtonProps = usePropsFor(HoldButton);
const startCaptionsButtonProps = usePropsFor(StartCaptionsButton);
/* @conditional-compile-remove(rtt) */
const realTimeTextProps = usePropsFor(CaptionsBanner);
const startCaptions = useCallback(async () => {
await startCaptionsButtonProps.onStartCaptions({
spokenLanguage: startCaptionsButtonProps.currentSpokenLanguage
@@ -171,6 +181,54 @@ export const DesktopMoreButton = (props: DesktopMoreButtonProps): JSX.Element =>
}
}

//RTT
/* @conditional-compile-remove(rtt) */
if (props.isRealTimeTextSupported) {
const realTimeTextContextualMenuItems: IContextualMenuItem[] = [];
const rttDisabled =
props.disableButtonsForHoldScreen || realTimeTextProps.isRealTimeTextOn || props.startRealTimeTextButtonChecked;

moreButtonContextualMenuItems.push({
key: 'realTimeTextKey',
id: 'common-call-composite-captions-button',
text: localeStrings.strings.call.realTimeTextLabel,
iconProps: { iconName: 'RealTimeTextIcon', styles: { root: { lineHeight: 0 } } },
itemProps: {
styles: buttonFlyoutIncreasedSizeStyles
},
disabled: props.disableButtonsForHoldScreen,
subMenuProps: {
id: 'rtt-contextual-menu',
items: realTimeTextContextualMenuItems,
calloutProps: {
preventDismissOnEvent: _preventDismissOnEvent
}
},
submenuIconProps: {
iconName: 'HorizontalGalleryRightButton',
styles: menuSubIconStyleSet
}
});

realTimeTextContextualMenuItems.push({
key: 'StartRealTimeTextKey',
id: 'common-call-composite-rtt-start-button',
text: localeStrings.strings.call.startRealTimeTextLabel,
ariaLabel: rttDisabled
? localeStrings.strings.call.disabledStartRealTimeTextLabel
: localeStrings.strings.call.startRealTimeTextLabel,
onClick: props.onStartRealTimeTextClick,
iconProps: {
iconName: 'RealTimeTextIcon',
styles: { root: { lineHeight: 0 } }
},
itemProps: {
styles: buttonFlyoutIncreasedSizeStyles
},
disabled: rttDisabled
});
}

const dtmfDialerScreenOption = {
key: 'dtmfDialerScreenKey',
itemProps: {
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ import { _pxToRem } from '@internal/acs-ui-common';
/**
* @private
*/
export const themedToggleButtonStyle = (theme: Theme, checked: boolean): Partial<IToggleStyles> => {
export const themedToggleButtonStyle = (theme: Theme, checked?: boolean): Partial<IToggleStyles> => {
if (checked) {
return {
root: {
Original file line number Diff line number Diff line change
@@ -13,6 +13,8 @@ import {
CaptionLanguageStrings,
CaptionsSettingsModal
} from '@internal/react-components';
/* @conditional-compile-remove(rtt) */
import { CaptionsBanner } from '@internal/react-components';
import { _ReactionDrawerMenuItem } from '@internal/react-components';
import { ReactionResources } from '@internal/react-components';
import { VideoGalleryLayout } from '@internal/react-components';
@@ -149,6 +151,8 @@ export interface MoreDrawerProps extends MoreDrawerDevicesMenuProps {
callControls?: boolean | CommonCallControlOptions;
onClickShowDialpad?: () => void;
isCaptionsSupported?: boolean;
/* @conditional-compile-remove(rtt) */
isRealTimeTextSupported?: boolean;
strings: MoreDrawerStrings;
disableButtonsForHoldScreen?: boolean;
useTeamsCaptions?: boolean;
@@ -164,6 +168,10 @@ export interface MoreDrawerProps extends MoreDrawerDevicesMenuProps {
onForbidOthersVideo?: () => void;
/* @conditional-compile-remove(media-access) */
onPermitOthersVideo?: () => void;
/* @conditional-compile-remove(rtt) */
onStartRealTimeText?: () => void;
/* @conditional-compile-remove(rtt) */
startRealTimeTextButtonChecked?: boolean;
}

const inferCallWithChatControlOptions = (
@@ -186,6 +194,8 @@ export const MoreDrawer = (props: MoreDrawerProps): JSX.Element => {

const localeStrings = useLocale();
const holdButtonProps = usePropsFor(HoldButton);
/* @conditional-compile-remove(rtt) */
const realTimeTextProps = usePropsFor(CaptionsBanner);

const callees = useSelector(getTargetCallees);
const participants = useSelector(getRemoteParticipantsConnectedSelector);
@@ -214,6 +224,8 @@ export const MoreDrawer = (props: MoreDrawerProps): JSX.Element => {

const showCaptionsButton =
props.isCaptionsSupported && drawerSelectionOptions !== false && isEnabled(drawerSelectionOptions.captionsButton);
/* @conditional-compile-remove(rtt) */
const showRealTimeTextButton = props.isRealTimeTextSupported;

if (props.reactionResources !== undefined) {
drawerMenuItems.push({
@@ -577,6 +589,51 @@ export const MoreDrawer = (props: MoreDrawerProps): JSX.Element => {
}
}

/* @conditional-compile-remove(rtt) */
const rttDisabled =
props.disableButtonsForHoldScreen || realTimeTextProps.isRealTimeTextOn || props.startRealTimeTextButtonChecked;
// rtt
/* @conditional-compile-remove(rtt) */
if (showRealTimeTextButton) {
const realTimeTextDrawerItems: DrawerMenuItemProps[] = [];

drawerMenuItems.push({
itemKey: 'realTimeText',
id: 'common-call-composite-rtt-button',
disabled: props.disableButtonsForHoldScreen,
text: localeStrings.strings.call.realTimeTextLabel,
iconProps: { iconName: 'RealTimeTextIcon' },
subMenuProps: realTimeTextDrawerItems
});

realTimeTextDrawerItems.push({
itemKey: 'ToggleRTTKey',
text: localeStrings.strings.call.startRealTimeTextLabel,
ariaLabel: rttDisabled
? localeStrings.strings.call.disabledStartRealTimeTextLabel
: localeStrings.strings.call.startRealTimeTextLabel,
iconProps: {
iconName: 'RealTimeTextIcon',
styles: { root: { lineHeight: 0 } }
},
onItemClick: props.onStartRealTimeText,
disabled: rttDisabled,
secondaryComponent: (
<Stack verticalFill verticalAlign="center">
<Toggle
id="common-call-composite-rtt-toggle-button"
checked={realTimeTextProps.isRealTimeTextOn || props.startRealTimeTextButtonChecked}
styles={themedToggleButtonStyle(
theme,
realTimeTextProps.isRealTimeTextOn || props.startRealTimeTextButtonChecked
)}
onChange={props.onStartRealTimeText}
/>
</Stack>
)
});
}

const customDrawerButtons = useMemo(
() =>
generateCustomCallDrawerButtons(
Loading

0 comments on commit 5dcd4c3

Please sign in to comment.