Skip to content

Commit 8eefe6c

Browse files
Real Time Text Component Related Changes and Strings (#5525)
* RTT component changes * Change files * build * udpate * update based on comment * Update packages/react-composites CallWithChatComposite browser test snapshots * Update packages/react-composites CallWithChatComposite browser test snapshots * update style * Revert "Update packages/react-composites CallWithChatComposite browser test snapshots" This reverts commit 83f804f. * Revert "Update packages/react-composites CallWithChatComposite browser test snapshots" This reverts commit 00cd69d. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent af6323a commit 8eefe6c

File tree

15 files changed

+275
-14
lines changed

15 files changed

+275
-14
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "prerelease",
3+
"area": "feature",
4+
"workstream": "Real Time Text",
5+
"comment": "Components for real time text",
6+
"packageName": "@azure/communication-react",
7+
"email": "[email protected]",
8+
"dependentChangeType": "patch"
9+
}

packages/communication-react/review/beta/communication-react.api.md

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -980,6 +980,8 @@ export interface CallCompositeStrings {
980980
pinParticipantMenuItemAriaLabel: string;
981981
pinParticipantMenuLabel: string;
982982
privacyPolicy: string;
983+
realTimeTextInputBoxDefaultText?: string;
984+
realTimeTextLabel?: string;
983985
rejoinCallButtonLabel: string;
984986
removeBackgroundEffectButtonLabel?: string;
985987
removeBackgroundTooltip?: string;
@@ -1016,6 +1018,7 @@ export interface CallCompositeStrings {
10161018
startCaptionsButtonOnLabel?: string;
10171019
startCaptionsButtonTooltipOffContent?: string;
10181020
startCaptionsButtonTooltipOnContent?: string;
1021+
startRealTimeTextLabel?: string;
10191022
startSpotlightMenuLabel: string;
10201023
stopAllSpotlightMenuLabel: string;
10211024
stopSpotlightMenuLabel: string;
@@ -1871,7 +1874,10 @@ export interface CaptionsBannerProps {
18711874
};
18721875
formFactor?: 'default' | 'compact';
18731876
isCaptionsOn?: boolean;
1877+
isRealTimeTextOn?: boolean;
1878+
latestLocalRealTimeText?: CaptionsInformation;
18741879
onRenderAvatar?: OnRenderAvatarCallback;
1880+
onSendRealTimeText?: (text: string, finalized?: boolean) => Promise<void>;
18751881
startCaptionsInProgress?: boolean;
18761882
strings?: CaptionsBannerStrings;
18771883
}
@@ -1885,6 +1891,7 @@ export type CaptionsBannerSelector = (state: CallClientState, props: CallingBase
18851891
// @public
18861892
export interface CaptionsBannerStrings {
18871893
captionsBannerSpinnerText?: string;
1894+
realTimeTextInputBoxDefaultText?: string;
18881895
}
18891896

18901897
// @public (undocumented)
@@ -1925,6 +1932,9 @@ export type CaptionsInformation = {
19251932
displayName: string;
19261933
captionText: string;
19271934
userId?: string;
1935+
isRealTimeText?: boolean;
1936+
isPartial?: boolean;
1937+
isLocalUser?: boolean;
19281938
};
19291939

19301940
// @public
@@ -2580,6 +2590,7 @@ export interface ComponentStrings {
25802590
sendBox: SendBoxStrings;
25812591
spokenLanguages: SpokenLanguageStrings;
25822592
startCaptionsButton: StartCaptionsButtonStrings;
2593+
startRealTimeTextButton: StartRealTimeTextButtonStrings;
25832594
typingIndicator: TypingIndicatorStrings;
25842595
UnsupportedBrowser: UnsupportedBrowserStrings;
25852596
UnsupportedBrowserVersion: UnsupportedBrowserVersionStrings;
@@ -2998,8 +3009,8 @@ export const DEFAULT_COMPONENT_ICONS: {
29983009
IncomingCallNotificationRejectIcon: React_2.JSX.Element;
29993010
IncomingCallNotificationAcceptIcon: React_2.JSX.Element;
30003011
IncomingCallNotificationAcceptWithVideoIcon: React_2.JSX.Element;
3001-
RTTIcon: React_2.JSX.Element;
30023012
NotificationBarTogetherModeIcon: React_2.JSX.Element;
3013+
RealTimeTextIcon: React_2.JSX.Element;
30033014
};
30043015

30053016
// @public
@@ -3184,8 +3195,8 @@ export const DEFAULT_COMPOSITE_ICONS: {
31843195
IncomingCallNotificationRejectIcon: React_2.JSX.Element;
31853196
IncomingCallNotificationAcceptIcon: React_2.JSX.Element;
31863197
IncomingCallNotificationAcceptWithVideoIcon: React_2.JSX.Element;
3187-
RTTIcon: React_2.JSX.Element;
31883198
NotificationBarTogetherModeIcon: React_2.JSX.Element;
3199+
RealTimeTextIcon: React_2.JSX.Element;
31893200
};
31903201

31913202
// @beta
@@ -4557,6 +4568,7 @@ export interface RealTimeTextProps {
45574568
captionText: string;
45584569
displayName: string;
45594570
id: string;
4571+
isLocalUser?: boolean;
45604572
isTyping?: boolean;
45614573
onRenderAvatar?: OnRenderAvatarCallback;
45624574
strings?: RealTimeTextStrings;
@@ -5024,6 +5036,22 @@ export interface StartCaptionsButtonStrings {
50245036
tooltipOnContent: string;
50255037
}
50265038

5039+
// @beta
5040+
export const StartRealTimeTextButton: (props: StartRealTimeTextButtonProps) => JSX.Element;
5041+
5042+
// @beta
5043+
export interface StartRealTimeTextButtonProps extends ControlBarButtonProps {
5044+
isRealTimeTextOn: boolean;
5045+
onStartRealTimeText: () => Promise<void>;
5046+
strings?: StartRealTimeTextButtonStrings;
5047+
}
5048+
5049+
// @beta
5050+
export interface StartRealTimeTextButtonStrings {
5051+
onLabel: string;
5052+
tooltipOnContent: string;
5053+
}
5054+
50275055
// @beta
50285056
export type StartTeamsCallIdentifier = MicrosoftTeamsUserIdentifier | PhoneNumberIdentifier | MicrosoftTeamsAppIdentifier | UnknownIdentifier;
50295057

packages/communication-react/src/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ export type {
9393
CaptionsBannerSelector,
9494
StartCaptionsButtonSelector
9595
} from '../../calling-component-bindings/src';
96+
9697
export type { HoldButtonSelector } from '../../calling-component-bindings/src';
9798

9899
export type { RaiseHandButtonSelector } from '../../calling-component-bindings/src';
@@ -465,6 +466,13 @@ export { RTTDisclosureBanner } from '../../react-components/src';
465466
export type { RealTimeTextProps, RealTimeTextStrings } from '../../react-components/src/components/RealTimeText';
466467
/* @conditional-compile-remove(rtt) */
467468
export { RealTimeText } from '../../react-components/src/components/RealTimeText';
469+
/* @conditional-compile-remove(rtt) */
470+
export { StartRealTimeTextButton } from '../../react-components/src/components/StartRealTimeTextButton';
471+
/* @conditional-compile-remove(rtt) */
472+
export type {
473+
StartRealTimeTextButtonProps,
474+
StartRealTimeTextButtonStrings
475+
} from '../../react-components/src/components/StartRealTimeTextButton';
468476
/* @conditional-compile-remove(media-access) */
469477
export type { MediaAccess } from '../../react-components/src';
470478
export type { CaptionsSettingsModalStrings, CaptionsSettingsModalProps } from '../../react-components/src';

packages/react-components/src/components/CaptionsBanner.tsx

Lines changed: 93 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33
import { Stack, FocusZone, Spinner, useTheme } from '@fluentui/react';
4+
/* @conditional-compile-remove(rtt) */
5+
import { TextField } from '@fluentui/react';
46
import React, { useEffect, useRef, useState, useCallback } from 'react';
57
import { _Caption } from './Caption';
68
import {
@@ -13,6 +15,10 @@ import {
1315
} from './styles/Captions.style';
1416
import { OnRenderAvatarCallback } from '../types';
1517
import { useLocale } from '../localization';
18+
/* @conditional-compile-remove(rtt) */
19+
import { RealTimeText } from './RealTimeText';
20+
/* @conditional-compile-remove(rtt) */
21+
import { RTTDisclosureBanner } from './RTTDisclosureBanner';
1622

1723
/**
1824
* @public
@@ -35,6 +41,21 @@ export type CaptionsInformation = {
3541
* id of the speaker
3642
*/
3743
userId?: string;
44+
/* @conditional-compile-remove(rtt) */
45+
/**
46+
* if the caption received is real time text
47+
*/
48+
isRealTimeText?: boolean;
49+
/* @conditional-compile-remove(rtt) */
50+
/**
51+
* if the caption received is a non finalized caption
52+
*/
53+
isPartial?: boolean;
54+
/* @conditional-compile-remove(rtt) */
55+
/**
56+
* if the caption received is from the local user
57+
*/
58+
isLocalUser?: boolean;
3859
};
3960

4061
/**
@@ -46,6 +67,11 @@ export interface CaptionsBannerStrings {
4667
* Spinner text for captions banner
4768
*/
4869
captionsBannerSpinnerText?: string;
70+
/* @conditional-compile-remove(rtt) */
71+
/**
72+
* Default text for RTT input text box
73+
*/
74+
realTimeTextInputBoxDefaultText?: string;
4975
}
5076

5177
/**
@@ -61,6 +87,11 @@ export interface CaptionsBannerProps {
6187
* Flag to indicate if captions are on
6288
*/
6389
isCaptionsOn?: boolean;
90+
/* @conditional-compile-remove(rtt) */
91+
/**
92+
* Flag to indicate if real time text is on
93+
*/
94+
isRealTimeTextOn?: boolean;
6495
/**
6596
* Flag to indicate if captions are being started
6697
* This is used to show spinner while captions are being started
@@ -87,6 +118,16 @@ export interface CaptionsBannerProps {
87118
captionsOptions?: {
88119
height: 'full' | 'default';
89120
};
121+
/* @conditional-compile-remove(rtt) */
122+
/**
123+
* Optional callback to send real time text.
124+
*/
125+
onSendRealTimeText?: (text: string, finalized?: boolean) => Promise<void>;
126+
/* @conditional-compile-remove(rtt) */
127+
/**
128+
* Latest local real time text
129+
*/
130+
latestLocalRealTimeText?: CaptionsInformation;
90131
}
91132

92133
const SCROLL_OFFSET_ALLOWANCE = 20;
@@ -102,7 +143,13 @@ export const CaptionsBanner = (props: CaptionsBannerProps): JSX.Element => {
102143
startCaptionsInProgress,
103144
onRenderAvatar,
104145
formFactor = 'default',
105-
captionsOptions
146+
captionsOptions,
147+
/* @conditional-compile-remove(rtt) */
148+
isRealTimeTextOn,
149+
/* @conditional-compile-remove(rtt) */
150+
onSendRealTimeText,
151+
/* @conditional-compile-remove(rtt) */
152+
latestLocalRealTimeText
106153
} = props;
107154
const localeStrings = useLocale().strings.captionsBanner;
108155
const strings = { ...localeStrings, ...props.strings };
@@ -142,12 +189,33 @@ export const CaptionsBanner = (props: CaptionsBannerProps): JSX.Element => {
142189
scrollToBottom();
143190
}
144191
}, [captions, isAtBottomOfScroll]);
192+
/* @conditional-compile-remove(rtt) */
193+
const [textFieldValue, setTextFieldValue] = useState<string>('');
194+
/* @conditional-compile-remove(rtt) */
195+
useEffect(() => {
196+
// if the latest real time text sent by myself is final, clear the text field
197+
if (latestLocalRealTimeText && !latestLocalRealTimeText.isPartial) {
198+
setTextFieldValue('');
199+
}
200+
}, [latestLocalRealTimeText]);
201+
202+
/* @conditional-compile-remove(rtt) */
203+
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>): void => {
204+
if (event.key === 'Enter') {
205+
event.preventDefault();
206+
if (textFieldValue && onSendRealTimeText) {
207+
onSendRealTimeText(textFieldValue, true);
208+
setTextFieldValue('');
209+
}
210+
}
211+
};
145212

146213
return (
147214
<>
148-
{startCaptionsInProgress && (
215+
{(startCaptionsInProgress || /* @conditional-compile-remove(rtt) */ isRealTimeTextOn) && (
149216
<FocusZone shouldFocusOnMount className={captionsContainerClassName} data-ui-id="captions-banner">
150-
{isCaptionsOn && (
217+
{/* @conditional-compile-remove(rtt) */ isRealTimeTextOn && <RTTDisclosureBanner />}
218+
{(isCaptionsOn || /* @conditional-compile-remove(rtt) */ isRealTimeTextOn) && (
151219
<ul
152220
ref={captionsScrollDivRef}
153221
className={
@@ -158,6 +226,14 @@ export const CaptionsBanner = (props: CaptionsBannerProps): JSX.Element => {
158226
data-ui-id="captions-banner-inner"
159227
>
160228
{captions.map((caption) => {
229+
/* @conditional-compile-remove(rtt) */
230+
if (caption.isRealTimeText) {
231+
return (
232+
<li key={caption.id} className={captionContainerClassName} data-is-focusable={true}>
233+
<RealTimeText {...caption} isTyping={caption.isPartial} onRenderAvatar={onRenderAvatar} />
234+
</li>
235+
);
236+
}
161237
return (
162238
<li key={caption.id} className={captionContainerClassName} data-is-focusable={true}>
163239
<_Caption {...caption} onRenderAvatar={onRenderAvatar} />
@@ -166,7 +242,20 @@ export const CaptionsBanner = (props: CaptionsBannerProps): JSX.Element => {
166242
})}
167243
</ul>
168244
)}
169-
{!isCaptionsOn && (
245+
{
246+
/* @conditional-compile-remove(rtt) */ isRealTimeTextOn && onSendRealTimeText && (
247+
<TextField
248+
label={strings.realTimeTextInputBoxDefaultText}
249+
value={textFieldValue}
250+
onKeyDown={handleKeyDown}
251+
onChange={(_, newValue) => {
252+
setTextFieldValue(newValue || '');
253+
onSendRealTimeText(newValue || '');
254+
}}
255+
/>
256+
)
257+
}
258+
{!isCaptionsOn && /* @conditional-compile-remove(rtt) */ !isRealTimeTextOn && (
170259
<Stack
171260
verticalAlign="center"
172261
styles={

packages/react-components/src/components/RTTDisclosureBanner.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { Notification } from './Notification';
1010
/* @conditional-compile-remove(rtt) */
1111
import { useTheme } from '@fluentui/react';
1212
/* @conditional-compile-remove(rtt) */
13-
import { rttContainerStyles } from './styles/RTTDisclosureBanner.styles';
13+
import { rttContainerStyles, rttIconStyles } from './styles/RTTDisclosureBanner.styles';
1414

1515
/* @conditional-compile-remove(rtt) */
1616
/**
@@ -54,7 +54,10 @@ export const RTTDisclosureBanner = (props: RTTDisclosureBannerProps): JSX.Elemen
5454
message: strings.bannerContent,
5555
linkLabel: strings.bannerLinkLabel
5656
}}
57-
notificationIconProps={{ iconName: 'RTTIcon' }}
57+
notificationIconProps={{
58+
iconName: 'RealTimeTextIcon',
59+
styles: rttIconStyles()
60+
}}
5861
onClickLink={props.onClickLink}
5962
styles={{ root: rttContainerStyles(theme) }}
6063
/>

packages/react-components/src/components/RealTimeText.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ export interface RealTimeTextProps {
6363
* Boolean indicating whether the RealTimeText is still in progress
6464
*/
6565
isTyping?: boolean;
66+
/**
67+
* Boolean indicating whether the RealTimeText is from the local user
68+
*/
69+
isLocalUser?: boolean;
6670
/**
6771
* Strings for RealTimeText
6872
*/

0 commit comments

Comments
 (0)