Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stablize Caption Banner component #5460

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "prerelease",
"area": "feature",
"workstream": "Captions",
"comment": "Stablize Captions Banner Component",
"packageName": "@azure/communication-react",
"email": "[email protected]",
"dependentChangeType": "patch"
}
12 changes: 6 additions & 6 deletions packages/calling-component-bindings/src/captionsSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
} from './baseSelectors';
import * as reselect from 'reselect';
import { toFlatCommunicationIdentifier } from '@internal/acs-ui-common';
import { _CaptionsInfo, _SupportedCaptionLanguage, _SupportedSpokenLanguage } from '@internal/react-components';
import { CaptionsInformation, _SupportedCaptionLanguage, _SupportedSpokenLanguage } from '@internal/react-components';

/**
* Selector type for the {@link StartCaptionsButton} component.
Expand Down Expand Up @@ -96,22 +96,22 @@ export const _captionSettingsSelector: _CaptionSettingsSelector = reselect.creat
);
/**
* Selector type for the {@link CaptionsBanner} component.
* @internal
* @public
*/
export type _CaptionsBannerSelector = (
export type CaptionsBannerSelector = (
state: CallClientState,
props: CallingBaseSelectorProps
) => {
captions: _CaptionsInfo[];
captions: CaptionsInformation[];
isCaptionsOn: boolean;
};

/**
* Selector for {@link CaptionsBanner} component.
*
* @internal
* @public
*/
export const _captionsBannerSelector: _CaptionsBannerSelector = reselect.createSelector(
export const captionsBannerSelector: CaptionsBannerSelector = reselect.createSelector(
[getCaptions, getCaptionsStatus, getStartCaptionsInProgress, getRemoteParticipants, getDisplayName, getIdentifier],
(captions, isCaptionsFeatureActive, startCaptionsInProgress, remoteParticipants, displayName, identifier) => {
const captionsInfo = captions?.map((c, index) => {
Expand Down
10 changes: 8 additions & 2 deletions packages/calling-component-bindings/src/hooks/usePropsFor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
DevicesButton,
ParticipantList,
ScreenShareButton,
VideoGallery
VideoGallery,
CaptionsBanner
} from '@internal/react-components';
import { IncomingCallStack } from '@internal/react-components';

Expand Down Expand Up @@ -45,6 +46,7 @@ import { ReactionButton } from '@internal/react-components';
import { _ComponentCallingHandlers } from '../handlers/createHandlers';
import { notificationStackSelector, NotificationStackSelector } from '../notificationStackSelector';
import { incomingCallStackSelector, IncomingCallStackSelector } from '../incomingCallStackSelector';
import { captionsBannerSelector } from '../captionsSelector';

/**
* Primary hook to get all hooks necessary for a calling Component.
Expand Down Expand Up @@ -126,7 +128,9 @@ export type GetSelector<Component extends (props: any) => JSX.Element | undefine
? RaiseHandButtonSelector
: AreEqual<Component, typeof RaiseHandButton> extends true
? EmptySelector
: undefined;
: AreEqual<Component, typeof CaptionsBanner> extends true
? EmptySelector
: undefined;

/**
* Get the selector for a specified component.
Expand Down Expand Up @@ -178,6 +182,8 @@ const findSelector = (component: (props: any) => JSX.Element | undefined): any =
return holdButtonSelector;
case IncomingCallStack:
return incomingCallStackSelector;
case CaptionsBanner:
return captionsBannerSelector;
}
return undefined;
};
Expand Down
4 changes: 2 additions & 2 deletions packages/calling-component-bindings/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ export { incomingCallStackSelector } from './incomingCallStackSelector';
export type {
_StartCaptionsButtonSelector,
_CaptionSettingsSelector,
_CaptionsBannerSelector
CaptionsBannerSelector
} from './captionsSelector';

export { _captionsBannerSelector, _startCaptionsButtonSelector, _captionSettingsSelector } from './captionsSelector';
export { captionsBannerSelector, _startCaptionsButtonSelector, _captionSettingsSelector } from './captionsSelector';

export type { CallingHandlers, CreateDefaultCallingHandlers } from './handlers/createHandlers';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1814,6 +1814,27 @@ export interface CaptionLanguageStrings {
vi: string;
}

// @public
export const CaptionsBanner: (props: CaptionsBannerProps) => JSX.Element;

// @public
export interface CaptionsBannerProps {
captions: CaptionsInformation[];
captionsOptions?: {
height: 'full' | 'default';
};
formFactor?: 'default' | 'compact';
isCaptionsOn?: boolean;
onRenderAvatar?: OnRenderAvatarCallback;
startCaptionsInProgress?: boolean;
strings?: CaptionsBannerStrings;
}

// @public
export interface CaptionsBannerStrings {
captionsBannerSpinnerText?: string;
}

// @public (undocumented)
export interface CaptionsCallFeatureState {
captions: CaptionsInfo[];
Expand All @@ -1837,6 +1858,14 @@ export interface CaptionsInfo {
timestamp: Date;
}

// @public
export type CaptionsInformation = {
id: string;
displayName: string;
captionText: string;
userId?: string;
};

// @public
export type CaptionsOptions = {
spokenLanguage: string;
Expand Down Expand Up @@ -2430,6 +2459,7 @@ export interface ComponentStrings {
CameraSitePermissionsDenied: SitePermissionsStrings;
CameraSitePermissionsDeniedSafari: SitePermissionsStrings;
CameraSitePermissionsRequest: SitePermissionsStrings;
captionsBanner: CaptionsBannerStrings;
devicesButton: DevicesButtonStrings;
dialpad: DialpadStrings;
endCallButton: EndCallButtonStrings;
Expand Down Expand Up @@ -3287,7 +3317,7 @@ export interface FluentThemeProviderProps {
export const fromFlatCommunicationIdentifier: (id: string) => CommunicationIdentifier;

// @public
export type GetCallingSelector<Component extends (props: any) => JSX.Element | undefined> = AreEqual<Component, typeof VideoGallery> extends true ? VideoGallerySelector : AreEqual<Component, typeof DevicesButton> extends true ? DevicesButtonSelector : AreEqual<Component, typeof MicrophoneButton> extends true ? MicrophoneButtonSelector : AreEqual<Component, typeof CameraButton> extends true ? CameraButtonSelector : AreEqual<Component, typeof ScreenShareButton> extends true ? ScreenShareButtonSelector : AreEqual<Component, typeof ParticipantList> extends true ? ParticipantListSelector : AreEqual<Component, typeof ParticipantsButton> extends true ? ParticipantsButtonSelector : AreEqual<Component, typeof EndCallButton> extends true ? EmptySelector : AreEqual<Component, typeof ErrorBar> extends true ? CallErrorBarSelector : AreEqual<Component, typeof Dialpad> extends true ? EmptySelector : AreEqual<Component, typeof HoldButton> extends true ? HoldButtonSelector : AreEqual<Component, typeof NotificationStack> extends true ? NotificationStackSelector : AreEqual<Component, typeof IncomingCallStack> extends true ? IncomingCallStackSelector : AreEqual<Component, typeof ReactionButton> extends true ? RaiseHandButtonSelector : AreEqual<Component, typeof RaiseHandButton> extends true ? EmptySelector : undefined;
export type GetCallingSelector<Component extends (props: any) => JSX.Element | undefined> = AreEqual<Component, typeof VideoGallery> extends true ? VideoGallerySelector : AreEqual<Component, typeof DevicesButton> extends true ? DevicesButtonSelector : AreEqual<Component, typeof MicrophoneButton> extends true ? MicrophoneButtonSelector : AreEqual<Component, typeof CameraButton> extends true ? CameraButtonSelector : AreEqual<Component, typeof ScreenShareButton> extends true ? ScreenShareButtonSelector : AreEqual<Component, typeof ParticipantList> extends true ? ParticipantListSelector : AreEqual<Component, typeof ParticipantsButton> extends true ? ParticipantsButtonSelector : AreEqual<Component, typeof EndCallButton> extends true ? EmptySelector : AreEqual<Component, typeof ErrorBar> extends true ? CallErrorBarSelector : AreEqual<Component, typeof Dialpad> extends true ? EmptySelector : AreEqual<Component, typeof HoldButton> extends true ? HoldButtonSelector : AreEqual<Component, typeof NotificationStack> extends true ? NotificationStackSelector : AreEqual<Component, typeof IncomingCallStack> extends true ? IncomingCallStackSelector : AreEqual<Component, typeof ReactionButton> extends true ? RaiseHandButtonSelector : AreEqual<Component, typeof RaiseHandButton> extends true ? EmptySelector : AreEqual<Component, typeof CaptionsBanner> extends true ? EmptySelector : undefined;

// @public
export const getCallingSelector: <Component extends (props: any) => JSX.Element | undefined>(component: Component) => GetCallingSelector<Component>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1524,6 +1524,27 @@ export interface CaptionLanguageStrings {
vi: string;
}

// @public
export const CaptionsBanner: (props: CaptionsBannerProps) => JSX.Element;

// @public
export interface CaptionsBannerProps {
captions: CaptionsInformation[];
captionsOptions?: {
height: 'full' | 'default';
};
formFactor?: 'default' | 'compact';
isCaptionsOn?: boolean;
onRenderAvatar?: OnRenderAvatarCallback;
startCaptionsInProgress?: boolean;
strings?: CaptionsBannerStrings;
}

// @public
export interface CaptionsBannerStrings {
captionsBannerSpinnerText?: string;
}

// @public (undocumented)
export interface CaptionsCallFeatureState {
captions: CaptionsInfo[];
Expand All @@ -1547,6 +1568,14 @@ export interface CaptionsInfo {
timestamp: Date;
}

// @public
export type CaptionsInformation = {
id: string;
displayName: string;
captionText: string;
userId?: string;
};

// @public
export type CaptionsOptions = {
spokenLanguage: string;
Expand Down Expand Up @@ -2073,6 +2102,7 @@ export type ComponentSlotStyle = Omit<IRawStyle, 'animation'>;
// @public
export interface ComponentStrings {
cameraButton: CameraButtonStrings;
captionsBanner: CaptionsBannerStrings;
devicesButton: DevicesButtonStrings;
dialpad: DialpadStrings;
endCallButton: EndCallButtonStrings;
Expand Down Expand Up @@ -2846,7 +2876,7 @@ export interface FluentThemeProviderProps {
export const fromFlatCommunicationIdentifier: (id: string) => CommunicationIdentifier;

// @public
export type GetCallingSelector<Component extends (props: any) => JSX.Element | undefined> = AreEqual<Component, typeof VideoGallery> extends true ? VideoGallerySelector : AreEqual<Component, typeof DevicesButton> extends true ? DevicesButtonSelector : AreEqual<Component, typeof MicrophoneButton> extends true ? MicrophoneButtonSelector : AreEqual<Component, typeof CameraButton> extends true ? CameraButtonSelector : AreEqual<Component, typeof ScreenShareButton> extends true ? ScreenShareButtonSelector : AreEqual<Component, typeof ParticipantList> extends true ? ParticipantListSelector : AreEqual<Component, typeof ParticipantsButton> extends true ? ParticipantsButtonSelector : AreEqual<Component, typeof EndCallButton> extends true ? EmptySelector : AreEqual<Component, typeof ErrorBar> extends true ? CallErrorBarSelector : AreEqual<Component, typeof Dialpad> extends true ? EmptySelector : AreEqual<Component, typeof HoldButton> extends true ? HoldButtonSelector : AreEqual<Component, typeof NotificationStack> extends true ? NotificationStackSelector : AreEqual<Component, typeof IncomingCallStack> extends true ? IncomingCallStackSelector : AreEqual<Component, typeof ReactionButton> extends true ? RaiseHandButtonSelector : AreEqual<Component, typeof RaiseHandButton> extends true ? EmptySelector : undefined;
export type GetCallingSelector<Component extends (props: any) => JSX.Element | undefined> = AreEqual<Component, typeof VideoGallery> extends true ? VideoGallerySelector : AreEqual<Component, typeof DevicesButton> extends true ? DevicesButtonSelector : AreEqual<Component, typeof MicrophoneButton> extends true ? MicrophoneButtonSelector : AreEqual<Component, typeof CameraButton> extends true ? CameraButtonSelector : AreEqual<Component, typeof ScreenShareButton> extends true ? ScreenShareButtonSelector : AreEqual<Component, typeof ParticipantList> extends true ? ParticipantListSelector : AreEqual<Component, typeof ParticipantsButton> extends true ? ParticipantsButtonSelector : AreEqual<Component, typeof EndCallButton> extends true ? EmptySelector : AreEqual<Component, typeof ErrorBar> extends true ? CallErrorBarSelector : AreEqual<Component, typeof Dialpad> extends true ? EmptySelector : AreEqual<Component, typeof HoldButton> extends true ? HoldButtonSelector : AreEqual<Component, typeof NotificationStack> extends true ? NotificationStackSelector : AreEqual<Component, typeof IncomingCallStack> extends true ? IncomingCallStackSelector : AreEqual<Component, typeof ReactionButton> extends true ? RaiseHandButtonSelector : AreEqual<Component, typeof RaiseHandButton> extends true ? EmptySelector : AreEqual<Component, typeof CaptionsBanner> extends true ? EmptySelector : undefined;

// @public
export const getCallingSelector: <Component extends (props: any) => JSX.Element | undefined>(component: Component) => GetCallingSelector<Component>;
Expand Down
6 changes: 6 additions & 0 deletions packages/communication-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,3 +460,9 @@ export type { RealTimeTextProps, RealTimeTextStrings } from '../../react-compone
export { RealTimeText } from '../../react-components/src/components/RealTimeText';
/* @conditional-compile-remove(media-access) */
export type { MediaAccess } from '../../react-components/src';
export type {
CaptionsBannerProps,
CaptionsInformation,
CaptionsBannerStrings
} from '../../react-components/src/components/CaptionsBanner';
export { CaptionsBanner } from '../../react-components/src/components/CaptionsBanner';
4 changes: 2 additions & 2 deletions packages/react-components/src/components/Caption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import {
displayNameContainerClassName,
iconClassName
} from './styles/Captions.style';
import { _CaptionsInfo } from './CaptionsBanner';
import { CaptionsInformation } from './CaptionsBanner';

/**
* @internal
* Props for a single line of caption.
*/
export interface _CaptionProps extends _CaptionsInfo {
export interface _CaptionProps extends CaptionsInformation {
/**
* Optional callback to override render of the avatar.
*
Expand Down
57 changes: 45 additions & 12 deletions packages/react-components/src/components/CaptionsBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,46 +12,78 @@ import {
loadingBannerStyles
} from './styles/Captions.style';
import { OnRenderAvatarCallback } from '../types';
import { useLocale } from '../localization';

/**
* @internal
* @public
* information required for each line of caption
*/
export type _CaptionsInfo = {
export type CaptionsInformation = {
/**
* unique id for each caption
*/
id: string;
/**
* speaker's display name
*/
displayName: string;
/**
* content of the caption
*/
captionText: string;
/**
* id of the speaker
*/
userId?: string;
};

/**
* @internal
* @public
* strings for captions banner
*/
export interface _CaptionsBannerStrings {
export interface CaptionsBannerStrings {
/**
* Spinner text for captions banner
*/
captionsBannerSpinnerText?: string;
}

/**
* @internal
* _CaptionsBanner Component Props.
* @public
* CaptionsBanner Component Props.
*/
export interface _CaptionsBannerProps {
captions: _CaptionsInfo[];
export interface CaptionsBannerProps {
/**
* Array of captions to be displayed
*/
captions: CaptionsInformation[];
/**
* Flag to indicate if captions are on
*/
isCaptionsOn?: boolean;
/**
* Flag to indicate if captions are being started
* This is used to show spinner while captions are being started
*/
startCaptionsInProgress?: boolean;
/**
* Optional callback to override render of the avatar.
*
* @param userId - user Id
*/
onRenderAvatar?: OnRenderAvatarCallback;
strings?: _CaptionsBannerStrings;
/**
* Optional strings for the component
*/
strings?: CaptionsBannerStrings;
/**
* Optional form factor for the component.
* @defaultValue 'default'
*/
formFactor?: 'default' | 'compact';
/**
* Optional options for the component.
*/
captionsOptions?: {
height: 'full' | 'default';
};
Expand All @@ -60,19 +92,20 @@ export interface _CaptionsBannerProps {
const SCROLL_OFFSET_ALLOWANCE = 20;

/**
* @internal
* @public
* A component for displaying a CaptionsBanner with user icon, displayName and captions text.
*/
export const _CaptionsBanner = (props: _CaptionsBannerProps): JSX.Element => {
export const CaptionsBanner = (props: CaptionsBannerProps): JSX.Element => {
const {
captions,
isCaptionsOn,
startCaptionsInProgress,
onRenderAvatar,
strings,
formFactor = 'default',
captionsOptions
} = props;
const localeStrings = useLocale().strings.captionsBanner;
const strings = { ...localeStrings, ...props.strings };
const captionsScrollDivRef = useRef<HTMLUListElement>(null);
const [isAtBottomOfScroll, setIsAtBottomOfScroll] = useState<boolean>(true);
const theme = useTheme();
Expand Down
2 changes: 0 additions & 2 deletions packages/react-components/src/components/RealTimeText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import {
rttContainerClassName
} from './styles/Captions.style';
/* @conditional-compile-remove(rtt) */
import { _CaptionsInfo } from './CaptionsBanner';
/* @conditional-compile-remove(rtt) */
import { useLocale } from '../localization';

/* @conditional-compile-remove(rtt) */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import {
ScreenShareButtonStrings,
SendBoxStrings,
TypingIndicatorStrings,
VideoGalleryStrings
VideoGalleryStrings,
CaptionsBannerStrings
} from '../components';
import { NotificationStackStrings } from '../components';
import { RaiseHandButtonStrings } from '../components';
Expand Down Expand Up @@ -194,6 +195,8 @@ export interface ComponentStrings {
/* @conditional-compile-remove(rtt) */
/** Strings for RealTimeText */
rtt: RealTimeTextStrings;
/** Strings for CaptionsBanner */
captionsBanner: CaptionsBannerStrings;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@
"rttCancelButtonLabel": "Cancel",
"rttCloseModalButtonAriaLabel": "Close RTT Modal"
},
"captionsBanner": {
"captionsBannerSpinnerText": "Starting captions..."
},
"mentionPopover": {
"mentionPopoverHeader": "Suggestions"
},
Expand Down
Loading