Skip to content

Commit 45c73a7

Browse files
Component changes for Real Time Text (#5555)
* component * Change files * Update packages/react-composites CallWithChatComposite browser test snapshots * Update packages/react-composites CallWithChatComposite browser test snapshots * Update packages/react-composites CallComposite browser test snapshots * Update packages/react-composites CallComposite browser test snapshots * Remove padding from captions container * Add padding to captions container style * Remove extra newline in Captions.style.ts * Update packages/react-composites CallWithChatComposite browser test snapshots * Update packages/react-composites CallWithChatComposite browser test snapshots * update * Update packages/react-composites CallComposite browser test snapshots * Update packages/react-composites CallComposite browser test snapshots * Update packages/react-composites CallComposite browser test snapshots * Update packages/react-composites CallComposite browser test snapshots * Update packages/react-composites CallComposite browser test snapshots * Update packages/react-composites CallComposite browser test snapshots * Revert "Update packages/react-composites CallComposite browser test snapshots" This reverts commit f682225. * Revert "Update packages/react-composites CallComposite browser test snapshots" This reverts commit 03dde8e. * Revert "Update packages/react-composites CallComposite browser test snapshots" This reverts commit 309b65a. * Revert "Update packages/react-composites CallComposite browser test snapshots" This reverts commit b306fc8. * Revert "Update packages/react-composites CallComposite browser test snapshots" This reverts commit cecfdc2. * Revert "Update packages/react-composites CallComposite browser test snapshots" This reverts commit 66af545. * Revert "Update packages/react-composites CallWithChatComposite browser test snapshots" This reverts commit 9c58fba. * Revert "Update packages/react-composites CallWithChatComposite browser test snapshots" This reverts commit 74cc038. * Revert "Update packages/react-composites CallComposite browser test snapshots" This reverts commit a1ca9e1. * Revert "Update packages/react-composites CallComposite browser test snapshots" This reverts commit 4cdc745. * Revert "Update packages/react-composites CallWithChatComposite browser test snapshots" This reverts commit 652ee63. * Revert "Update packages/react-composites CallWithChatComposite browser test snapshots" This reverts commit e53cf12. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 2b3201a commit 45c73a7

18 files changed

+273
-66
lines changed
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": "Real Time Text component changes and strings",
6+
"packageName": "@azure/communication-react",
7+
"email": "[email protected]",
8+
"dependentChangeType": "patch"
9+
}

change/@azure-communication-react-e4791dc2-7f02-4b0d-9147-ca8b52f51701.json

-9
This file was deleted.

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

+30-10
Original file line numberDiff line numberDiff line change
@@ -983,8 +983,14 @@ export interface CallCompositeStrings {
983983
realTimeTextBannerContent?: string;
984984
realTimeTextBannerLinkLabel?: string;
985985
realTimeTextBannerTitle?: string;
986+
realTimeTextCancelButtonLabel: string;
987+
realTimeTextCloseModalButtonAriaLabel: string;
988+
realTimeTextConfirmButtonLabel: string;
986989
realTimeTextInputBoxDefaultText?: string;
987990
realTimeTextLabel?: string;
991+
realTimeTextModalAriaLabel: string;
992+
realTimeTextModalText: string;
993+
realTimeTextModalTitle: string;
988994
rejoinCallButtonLabel: string;
989995
removeBackgroundEffectButtonLabel?: string;
990996
removeBackgroundTooltip?: string;
@@ -1878,9 +1884,14 @@ export interface CaptionsBannerProps {
18781884
formFactor?: 'default' | 'compact';
18791885
isCaptionsOn?: boolean;
18801886
isRealTimeTextOn?: boolean;
1881-
latestLocalRealTimeText?: CaptionsInformation;
1887+
latestLocalRealTimeText?: RealTimeTextInformation;
18821888
onRenderAvatar?: OnRenderAvatarCallback;
1883-
onSendRealTimeText?: (text: string, finalized?: boolean) => Promise<void>;
1889+
onSendRealTimeText?: (text: string, finalized: boolean) => Promise<void>;
1890+
realTimeTexts?: {
1891+
completedMessages?: RealTimeTextInformation[];
1892+
currentInProgress?: RealTimeTextInformation[];
1893+
myInProgress?: RealTimeTextInformation;
1894+
};
18841895
startCaptionsInProgress?: boolean;
18851896
strings?: CaptionsBannerStrings;
18861897
}
@@ -1938,9 +1949,7 @@ export type CaptionsInformation = {
19381949
displayName: string;
19391950
captionText: string;
19401951
userId?: string;
1941-
isRealTimeText?: boolean;
1942-
isPartial?: boolean;
1943-
isLocalUser?: boolean;
1952+
createdTimeStamp?: Date;
19441953
};
19451954

19461955
// @public
@@ -4570,13 +4579,24 @@ export type ReadReceiptsBySenderId = {
45704579
// @beta
45714580
export const RealTimeText: (props: RealTimeTextProps) => JSX.Element;
45724581

4582+
// @beta
4583+
export type RealTimeTextInformation = {
4584+
id: number;
4585+
displayName: string;
4586+
userId?: string;
4587+
message: string;
4588+
isTyping: boolean;
4589+
isMe: boolean;
4590+
finalizedTimeStamp: Date;
4591+
};
4592+
45734593
// @beta
45744594
export const RealTimeTextModal: (props: RealTimeTextModalProps) => JSX.Element;
45754595

45764596
// @beta
45774597
export interface RealTimeTextModalProps {
45784598
onDismissModal?: () => void;
4579-
onStartRealTimeText?: () => Promise<void>;
4599+
onStartRealTimeText?: () => void;
45804600
showModal?: boolean;
45814601
strings?: RealTimeTextModalStrings;
45824602
}
@@ -4593,11 +4613,11 @@ export interface RealTimeTextModalStrings {
45934613

45944614
// @beta
45954615
export interface RealTimeTextProps {
4596-
captionText: string;
45974616
displayName: string;
4598-
id: string;
4599-
isLocalUser?: boolean;
4617+
id: number;
4618+
isMe?: boolean;
46004619
isTyping?: boolean;
4620+
message: string;
46014621
onRenderAvatar?: OnRenderAvatarCallback;
46024622
strings?: RealTimeTextStrings;
46034623
userId?: string;
@@ -5030,7 +5050,7 @@ export const StartRealTimeTextButton: (props: StartRealTimeTextButtonProps) => J
50305050
// @beta
50315051
export interface StartRealTimeTextButtonProps extends ControlBarButtonProps {
50325052
isRealTimeTextOn: boolean;
5033-
onStartRealTimeText: () => Promise<void>;
5053+
onStartRealTimeText: () => void;
50345054
strings?: StartRealTimeTextButtonStrings;
50355055
}
50365056

packages/communication-react/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -484,3 +484,5 @@ export type {
484484
StartCaptionsButtonProps,
485485
StartCaptionsButtonStrings
486486
} from '../../react-components/src/components/StartCaptionsButton';
487+
/* @conditional-compile-remove(rtt) */
488+
export type { RealTimeTextInformation } from '../../react-components/src/components/CaptionsBanner';

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

+110-29
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { Stack, FocusZone, Spinner, useTheme } from '@fluentui/react';
44
/* @conditional-compile-remove(rtt) */
55
import { TextField } from '@fluentui/react';
66
import React, { useEffect, useRef, useState, useCallback } from 'react';
7+
/* @conditional-compile-remove(rtt) */
8+
import { useMemo } from 'react';
79
import { _Caption } from './Caption';
810
import {
911
captionContainerClassName,
@@ -19,6 +21,8 @@ import { useLocale } from '../localization';
1921
import { RealTimeText } from './RealTimeText';
2022
/* @conditional-compile-remove(rtt) */
2123
import { _RTTDisclosureBanner } from './RTTDisclosureBanner';
24+
/* @conditional-compile-remove(rtt) */
25+
import { sortCaptionsAndRealTimeTexts } from './utils/sortCaptionsAndRealTimeTexts';
2226

2327
/**
2428
* @public
@@ -43,21 +47,48 @@ export type CaptionsInformation = {
4347
userId?: string;
4448
/* @conditional-compile-remove(rtt) */
4549
/**
46-
* if the caption received is real time text
50+
* timestamp when the caption was created
51+
* Please note that this value is essential for determining the order of captions and real time text messages
52+
* If you are using both captions and real time text, please ensure that the createdTimeStamp is populated
4753
*/
48-
isRealTimeText?: boolean;
49-
/* @conditional-compile-remove(rtt) */
54+
createdTimeStamp?: Date;
55+
};
56+
57+
/* @conditional-compile-remove(rtt) */
58+
/**
59+
* @beta
60+
* information required for each line of real time text
61+
*/
62+
export type RealTimeTextInformation = {
5063
/**
51-
* if the caption received is a non finalized caption
64+
* The sequence id of the real time text.
5265
*/
53-
isPartial?: boolean;
54-
/* @conditional-compile-remove(rtt) */
66+
id: number;
5567
/**
56-
* if the caption received is from the local user
68+
* sender's display name
5769
*/
58-
isLocalUser?: boolean;
70+
displayName: string;
71+
/**
72+
* id of the sender
73+
*/
74+
userId?: string;
75+
/**
76+
* The real time text message.
77+
*/
78+
message: string;
79+
/**
80+
* if the real time text received is partial
81+
*/
82+
isTyping: boolean;
83+
/**
84+
* If message originated from the local participant
85+
*/
86+
isMe: boolean;
87+
/**
88+
* timestamp when the real time text was finalized
89+
*/
90+
finalizedTimeStamp: Date;
5991
};
60-
6192
/**
6293
* @public
6394
* strings for captions banner
@@ -98,6 +129,15 @@ export interface CaptionsBannerProps {
98129
* Array of captions to be displayed
99130
*/
100131
captions: CaptionsInformation[];
132+
/* @conditional-compile-remove(rtt) */
133+
/**
134+
* Array of finalized and partial real time text messages
135+
*/
136+
realTimeTexts?: {
137+
completedMessages?: RealTimeTextInformation[];
138+
currentInProgress?: RealTimeTextInformation[];
139+
myInProgress?: RealTimeTextInformation;
140+
};
101141
/**
102142
* Flag to indicate if captions are on
103143
*/
@@ -137,12 +177,12 @@ export interface CaptionsBannerProps {
137177
/**
138178
* Optional callback to send real time text.
139179
*/
140-
onSendRealTimeText?: (text: string, finalized?: boolean) => Promise<void>;
180+
onSendRealTimeText?: (text: string, finalized: boolean) => Promise<void>;
141181
/* @conditional-compile-remove(rtt) */
142182
/**
143183
* Latest local real time text
144184
*/
145-
latestLocalRealTimeText?: CaptionsInformation;
185+
latestLocalRealTimeText?: RealTimeTextInformation;
146186
}
147187

148188
const SCROLL_OFFSET_ALLOWANCE = 20;
@@ -154,6 +194,8 @@ const SCROLL_OFFSET_ALLOWANCE = 20;
154194
export const CaptionsBanner = (props: CaptionsBannerProps): JSX.Element => {
155195
const {
156196
captions,
197+
/* @conditional-compile-remove(rtt) */
198+
realTimeTexts,
157199
isCaptionsOn,
158200
startCaptionsInProgress,
159201
onRenderAvatar,
@@ -171,6 +213,20 @@ export const CaptionsBanner = (props: CaptionsBannerProps): JSX.Element => {
171213
const captionsScrollDivRef = useRef<HTMLUListElement>(null);
172214
const [isAtBottomOfScroll, setIsAtBottomOfScroll] = useState<boolean>(true);
173215
const theme = useTheme();
216+
/* @conditional-compile-remove(rtt) */
217+
// merge realtimetexts and captions into one array based on timestamp
218+
// Combine captions and realTimeTexts into one list
219+
const combinedList: (CaptionsInformation | RealTimeTextInformation)[] = useMemo(() => {
220+
return sortCaptionsAndRealTimeTexts(captions, realTimeTexts?.completedMessages ?? []);
221+
}, [captions, realTimeTexts?.completedMessages]);
222+
223+
/* @conditional-compile-remove(rtt) */
224+
const mergedCaptions: (CaptionsInformation | RealTimeTextInformation)[] = useMemo(() => {
225+
return [...combinedList, ...(realTimeTexts?.currentInProgress ?? []), realTimeTexts?.myInProgress] as (
226+
| CaptionsInformation
227+
| RealTimeTextInformation
228+
)[];
229+
}, [combinedList, realTimeTexts]);
174230

175231
const scrollToBottom = (): void => {
176232
if (captionsScrollDivRef.current) {
@@ -209,7 +265,7 @@ export const CaptionsBanner = (props: CaptionsBannerProps): JSX.Element => {
209265
/* @conditional-compile-remove(rtt) */
210266
useEffect(() => {
211267
// if the latest real time text sent by myself is final, clear the text field
212-
if (latestLocalRealTimeText && !latestLocalRealTimeText.isPartial) {
268+
if (latestLocalRealTimeText && !latestLocalRealTimeText.isTyping) {
213269
setTextFieldValue('');
214270
}
215271
}, [latestLocalRealTimeText]);
@@ -232,13 +288,52 @@ export const CaptionsBanner = (props: CaptionsBannerProps): JSX.Element => {
232288
bannerLinkLabel: strings.realTimeTextBannerLinkLabel ?? ''
233289
};
234290

291+
const captionsTrampoline = (): JSX.Element => {
292+
/* @conditional-compile-remove(rtt) */
293+
return (
294+
<>
295+
{mergedCaptions.map((caption) => {
296+
if (caption) {
297+
if ('message' in caption) {
298+
return (
299+
<li key={`RealTimeText - ${caption.id}`} className={captionContainerClassName} data-is-focusable={true}>
300+
<RealTimeText {...(caption as RealTimeTextInformation)} />
301+
</li>
302+
);
303+
}
304+
return (
305+
<li key={`Captions - ${caption.id}`} className={captionContainerClassName} data-is-focusable={true}>
306+
<_Caption {...(caption as CaptionsInformation)} onRenderAvatar={onRenderAvatar} />
307+
</li>
308+
);
309+
}
310+
return <></>;
311+
})}
312+
</>
313+
);
314+
315+
return (
316+
<>
317+
{captions.map((caption) => {
318+
return (
319+
<li key={caption.id} className={captionContainerClassName} data-is-focusable={true}>
320+
<_Caption {...(caption as CaptionsInformation)} onRenderAvatar={onRenderAvatar} />
321+
</li>
322+
);
323+
})}
324+
</>
325+
);
326+
};
327+
235328
return (
236329
<>
237330
{(startCaptionsInProgress || /* @conditional-compile-remove(rtt) */ isRealTimeTextOn) && (
238331
<FocusZone shouldFocusOnMount className={captionsContainerClassName} data-ui-id="captions-banner">
239332
{
240333
/* @conditional-compile-remove(rtt) */ isRealTimeTextOn && (
241-
<_RTTDisclosureBanner strings={realTimeTextDisclosureBannerStrings} />
334+
<div style={{ paddingTop: '0.5rem' }}>
335+
<_RTTDisclosureBanner strings={realTimeTextDisclosureBannerStrings} />
336+
</div>
242337
)
243338
}
244339
{(isCaptionsOn || /* @conditional-compile-remove(rtt) */ isRealTimeTextOn) && (
@@ -251,21 +346,7 @@ export const CaptionsBanner = (props: CaptionsBannerProps): JSX.Element => {
251346
}
252347
data-ui-id="captions-banner-inner"
253348
>
254-
{captions.map((caption) => {
255-
/* @conditional-compile-remove(rtt) */
256-
if (caption.isRealTimeText) {
257-
return (
258-
<li key={caption.id} className={captionContainerClassName} data-is-focusable={true}>
259-
<RealTimeText {...caption} isTyping={caption.isPartial} onRenderAvatar={onRenderAvatar} />
260-
</li>
261-
);
262-
}
263-
return (
264-
<li key={caption.id} className={captionContainerClassName} data-is-focusable={true}>
265-
<_Caption {...caption} onRenderAvatar={onRenderAvatar} />
266-
</li>
267-
);
268-
})}
349+
{captionsTrampoline()}
269350
</ul>
270351
)}
271352
{
@@ -276,7 +357,7 @@ export const CaptionsBanner = (props: CaptionsBannerProps): JSX.Element => {
276357
onKeyDown={handleKeyDown}
277358
onChange={(_, newValue) => {
278359
setTextFieldValue(newValue || '');
279-
onSendRealTimeText(newValue || '');
360+
onSendRealTimeText(newValue || '', false);
280361
}}
281362
/>
282363
)

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

+5-5
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,15 @@ export interface RealTimeTextProps {
4040
/**
4141
* RealTimeText id
4242
*/
43-
id: string;
43+
id: number;
4444
/**
4545
* Display name of the user
4646
*/
4747
displayName: string;
4848
/**
4949
* RealTimeText content
5050
*/
51-
captionText: string;
51+
message: string;
5252
/**
5353
* user id of the user
5454
*/
@@ -66,7 +66,7 @@ export interface RealTimeTextProps {
6666
/**
6767
* Boolean indicating whether the RealTimeText is from the local user
6868
*/
69-
isLocalUser?: boolean;
69+
isMe?: boolean;
7070
/**
7171
* Strings for RealTimeText
7272
*/
@@ -79,7 +79,7 @@ export interface RealTimeTextProps {
7979
* A component for displaying a single line of RealTimeText
8080
*/
8181
export const RealTimeText = (props: RealTimeTextProps): JSX.Element => {
82-
const { displayName, userId, captionText, onRenderAvatar, isTyping } = props;
82+
const { displayName, userId, message, onRenderAvatar, isTyping } = props;
8383
const theme = useTheme();
8484
const localeStrings = useLocale().strings.realTimeText;
8585
const strings = { ...localeStrings, ...props.strings };
@@ -115,7 +115,7 @@ export const RealTimeText = (props: RealTimeTextProps): JSX.Element => {
115115
{isTyping && <Text className={isTypingClassName(theme)}>{strings?.isTypingText}</Text>}
116116
</Stack>
117117
<Stack.Item className={captionClassName} dir="auto">
118-
{captionText}
118+
{message}
119119
</Stack.Item>
120120
</Stack>
121121
</Stack>

0 commit comments

Comments
 (0)