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

Expose onRenderAvatar api to all the composites #5662

Open
wants to merge 7 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": "minor",
"area": "feature",
"workstream": "",
"comment": "Expose onRenderAvatar api to all the composites",
"packageName": "@azure/communication-react",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "minor",
"area": "feature",
"workstream": "",
"comment": "Expose onRenderAvatar api to all the composites",
"packageName": "@azure/communication-react",
"email": "[email protected]",
"dependentChangeType": "patch"
}
4 changes: 3 additions & 1 deletion common/config/babel/features.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ module.exports = {
// Feature for RTT
"rtt",
// Feature for together mode
"together-mode"
"together-mode",
// Feature for exposing the onRenderAvatar API into the composites
"composite-onRenderAvatar-API"
],
stable: [
// Demo feature. Used in live-documentation of conditional compilation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ export interface BaseCompositeProps<TIcons extends Record<string, JSX.Element>>
locale?: CompositeLocale;
onFetchAvatarPersonaData?: AvatarPersonaDataCallback;
onFetchParticipantMenuItems?: ParticipantMenuItemsCallback;
onRenderAvatar?: OnRenderAvatarCallback;
rtl?: boolean;
}

Expand Down Expand Up @@ -2863,7 +2864,10 @@ export type CreateViewResult = {
export type CustomAvatarOptions = {
coinSize?: number;
hidePersonaDetails?: boolean;
initialsColor?: PersonaInitialsColor | string;
initialsTextColor?: string;
imageUrl?: string;
imageInitials?: string;
noVideoAvailableAriaLabel?: string;
presence?: PersonaPresence;
size?: PersonaSize;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { ChatMessageComponentWrapperProps } from '../ChatMessageComponentWrapper
import { BlockedMessage } from '../../../types/ChatMessage';
import { ChatMessage } from '../../../types/ChatMessage';
import { ChatMessageComponentAsMessageBubble } from './ChatMessageComponentAsMessageBubble';
import { CustomAvatarOptions } from '../../../types';

/**
* Props for {@link FluentChatMessageComponentWrapper}
Expand Down Expand Up @@ -119,17 +120,28 @@ export const FluentChatMessageComponent = (props: FluentChatMessageComponentWrap
return { className: mergeClasses(chatMessageRenderStyles.rootMessage, chatMessageRenderStyles.rootCommon) };
}, [chatMessageRenderStyles.rootCommon, chatMessageRenderStyles.rootMessage]);

const avatar = useMemo(() => {
const chatAvatarStyle = shouldShowAvatar ? gutterWithAvatar : gutterWithHiddenAvatar;
const personaOptions: IPersona = {
const personaOptions: IPersona = useMemo(
() => ({
hidePersonaDetails: true,
size: PersonaSize.size32,
text: message.senderDisplayName,
showOverflowTooltip: false
};
}),
[message.senderDisplayName]
);

const defaultOnRenderAvatar = useCallback(
(props: CustomAvatarOptions) => {
return <Persona {...{ ...personaOptions, ...props }} />;
},
[personaOptions]
);

const avatar = useMemo(() => {
const chatAvatarStyle = shouldShowAvatar ? gutterWithAvatar : gutterWithHiddenAvatar;
let renderedAvatar;
if (onRenderAvatar) {
const avatarComponent = onRenderAvatar?.(message.senderId, personaOptions);
const avatarComponent = onRenderAvatar?.(message.senderId, personaOptions, defaultOnRenderAvatar);
if (!avatarComponent) {
return undefined;
} else {
Expand All @@ -138,10 +150,10 @@ export const FluentChatMessageComponent = (props: FluentChatMessageComponentWrap
}
return (
<div className={mergeStyles(chatAvatarStyle)}>
{renderedAvatar ? renderedAvatar : <Persona {...personaOptions} />}
{renderedAvatar ? renderedAvatar : defaultOnRenderAvatar(personaOptions)}
</div>
);
}, [message.senderDisplayName, message.senderId, onRenderAvatar, shouldShowAvatar]);
}, [defaultOnRenderAvatar, message.senderId, onRenderAvatar, personaOptions, shouldShowAvatar]);

const setMessageContainerRef = useCallback((node: HTMLDivElement | null) => {
removeFluentUIKeyboardNavigationStyles(node);
Expand Down
19 changes: 19 additions & 0 deletions packages/react-components/src/types/OnRender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
PersonaPresence,
PersonaSize
} from '@fluentui/react';
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
import { PersonaInitialsColor } from '@fluentui/react';
import { ParticipantState } from '.';

/**
Expand All @@ -21,8 +23,25 @@ export type CustomAvatarOptions = {
coinSize?: number;
/** Only show Coin and Initials */
hidePersonaDetails?: boolean;
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
/**
* The background color when the user's initials are displayed.
* @defaultvalue Derived from `text`
*/
initialsColor?: PersonaInitialsColor | string;
/** Text color of initials inside the coin */
initialsTextColor?: string;
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
/**
* Image URL to use, should be a square aspect ratio and big enough to fit in the image area.
*/
imageUrl?: string;
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
/**
* The user's initials to display in the image area when there is no image.
* @defaultvalue Derived from `text`
*/
imageInitials?: string;
/** Optional property to set the aria label of the video tile if there is no available stream. */
noVideoAvailableAriaLabel?: string;
/** User status */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import {
mainScreenContainerStyleMobile
} from './styles/CallComposite.styles';
import { CallControlOptions } from './types/CallControlOptions';
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
import { OnRenderAvatarCallback } from '@internal/react-components';

import { LayerHost, mergeStyles } from '@fluentui/react';
import { modalLayerHostStyle } from '../common/styles/ModalLocalAndRemotePIP.styles';
Expand Down Expand Up @@ -356,6 +358,8 @@ type MainScreenProps = {
modalLayerHostId: string;
callInvitationUrl?: string;
onFetchAvatarPersonaData?: AvatarPersonaDataCallback;
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
onRenderAvatar?: OnRenderAvatarCallback;
onFetchParticipantMenuItems?: ParticipantMenuItemsCallback;
options?: CallCompositeOptions;
overrideSidePane?: InjectedSidePaneProps;
Expand Down Expand Up @@ -408,7 +412,12 @@ const MainScreen = (props: MainScreenProps): JSX.Element => {
micHasPermission
]);

const { callInvitationUrl, onFetchAvatarPersonaData, onFetchParticipantMenuItems } = props;
const {
callInvitationUrl,
onFetchAvatarPersonaData,
/* @conditional-compile-remove(composite-onRenderAvatar-API) */ onRenderAvatar,
onFetchParticipantMenuItems
} = props;
const page = useSelector(getPage);
const endedCall = useSelector(getEndedCall);

Expand Down Expand Up @@ -692,6 +701,8 @@ const MainScreen = (props: MainScreenProps): JSX.Element => {
updateSidePaneRenderer={setSidePaneRenderer}
mobileChatTabHeader={props.mobileChatTabHeader}
onFetchAvatarPersonaData={onFetchAvatarPersonaData}
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
onRenderAvatar={onRenderAvatar}
latestErrors={latestInCallErrors}
latestNotifications={latestNotifications}
onDismissError={onDismissError}
Expand All @@ -705,6 +716,8 @@ const MainScreen = (props: MainScreenProps): JSX.Element => {
<CallPage
callInvitationURL={callInvitationUrl}
onFetchAvatarPersonaData={onFetchAvatarPersonaData}
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
onRenderAvatar={onRenderAvatar}
onFetchParticipantMenuItems={onFetchParticipantMenuItems}
mobileView={props.mobileView}
modalLayerHostId={props.modalLayerHostId}
Expand Down Expand Up @@ -756,6 +769,8 @@ const MainScreen = (props: MainScreenProps): JSX.Element => {
<CallPage
callInvitationURL={callInvitationUrl}
onFetchAvatarPersonaData={onFetchAvatarPersonaData}
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
onRenderAvatar={onRenderAvatar}
onFetchParticipantMenuItems={onFetchParticipantMenuItems}
mobileView={props.mobileView}
modalLayerHostId={props.modalLayerHostId}
Expand Down Expand Up @@ -841,6 +856,8 @@ export const CallCompositeInner = (props: CallCompositeProps & InternalCallCompo
adapter,
callInvitationUrl,
onFetchAvatarPersonaData,
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
onRenderAvatar,
onFetchParticipantMenuItems,
options,
formFactor = 'desktop'
Expand All @@ -860,6 +877,8 @@ export const CallCompositeInner = (props: CallCompositeProps & InternalCallCompo
<MainScreen
callInvitationUrl={callInvitationUrl}
onFetchAvatarPersonaData={onFetchAvatarPersonaData}
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
onRenderAvatar={onRenderAvatar}
onFetchParticipantMenuItems={onFetchParticipantMenuItems}
mobileView={mobileView}
modalLayerHostId={modalLayerHostId}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ import { useCompositeStringsForNotificationStackStrings } from '../hooks/useComp
/* @conditional-compile-remove(breakout-rooms) */
import { BreakoutRoomsBanner } from './BreakoutRoomsBanner';
import { DtmfDialPadOptions } from '../CallComposite';
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
import { OnRenderAvatarCallback } from '@internal/react-components';

/**
* @private
Expand All @@ -110,6 +112,8 @@ export interface CallArrangementProps {
mobileView: boolean;
modalLayerHostId: string;
onFetchAvatarPersonaData?: AvatarPersonaDataCallback;
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
onRenderAvatar?: OnRenderAvatarCallback;
updateSidePaneRenderer: (renderer: SidePaneRenderer | undefined) => void;
mobileChatTabHeader?: MobileChatSidePaneTabHeaderProps;
latestErrors: ActiveErrorMessage[] | ActiveNotification[];
Expand Down Expand Up @@ -197,6 +201,8 @@ export const CallArrangement = (props: CallArrangementProps): JSX.Element => {
setDrawerMenuItems,
inviteLink: props.callControlProps.callInvitationURL,
onFetchAvatarPersonaData: props.onFetchAvatarPersonaData,
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
onRenderAvatar: props.onRenderAvatar,
onFetchParticipantMenuItems: props.callControlProps?.onFetchParticipantMenuItems,
mobileView: props.mobileView,
peopleButtonRef,
Expand All @@ -209,10 +215,9 @@ export const CallArrangement = (props: CallArrangementProps): JSX.Element => {
props.callControlProps.callInvitationURL,
props.callControlProps?.onFetchParticipantMenuItems,
props.onFetchAvatarPersonaData,
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
props.onRenderAvatar,
props.mobileView,
peopleButtonRef,
setParticipantActioned,
sidePaneDismissButtonRef,
props.onCloseChatPane
]
);
Expand Down Expand Up @@ -582,6 +587,8 @@ export const CallArrangement = (props: CallArrangementProps): JSX.Element => {
captionsOptions={props.captionsOptions}
isMobile={props.mobileView}
onFetchAvatarPersonaData={props.onFetchAvatarPersonaData}
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
onRenderAvatar={props.onRenderAvatar}
useTeamsCaptions={useTeamsCaptions}
/* @conditional-compile-remove(rtt) */
isRealTimeTextOn={openRealTimeText}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { PromptProps } from './Prompt';
import { useLocalSpotlightCallbacksWithPrompt, useRemoteSpotlightCallbacksWithPrompt } from '../utils/spotlightUtils';
import { VideoTilesOptions } from '@internal/react-components';
import { getCapabilites, getIsRoomsCall, getReactionResources, getRole } from '../selectors/baseSelectors';
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
import { OnRenderAvatarCallback } from '@internal/react-components';

const VideoGalleryStyles = {
root: {
Expand All @@ -54,6 +56,8 @@ export interface MediaGalleryProps {
isMicrophoneChecked?: boolean;
onStartLocalVideo: () => Promise<void>;
onFetchAvatarPersonaData?: AvatarPersonaDataCallback;
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
onRenderAvatar?: OnRenderAvatarCallback;
isMobile?: boolean;
drawerMenuHostId?: string;
remoteVideoTileMenuOptions?: RemoteVideoTileMenuOptions;
Expand Down Expand Up @@ -82,7 +86,10 @@ export const MediaGallery = (props: MediaGalleryProps): JSX.Element => {
setPromptProps,
hideSpotlightButtons,
videoTilesOptions,
captionsOptions
captionsOptions,
onFetchAvatarPersonaData,
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
onRenderAvatar
} = props;

const videoGalleryProps = usePropsFor(VideoGallery);
Expand Down Expand Up @@ -110,19 +117,32 @@ export const MediaGallery = (props: MediaGalleryProps): JSX.Element => {
};
}, [cameraSwitcherCallback, cameraSwitcherCameras]);

const onRenderAvatar = useCallback(
const defaultOnRenderAvatar = useCallback(
(userId?: string, options?: CustomAvatarOptions) => {
return (
<Stack className={mergeStyles({ position: 'absolute', height: '100%', width: '100%' })}>
<Stack styles={{ root: { margin: 'auto', maxHeight: '100%' } }}>
{options?.coinSize && (
<AvatarPersona userId={userId} {...options} dataProvider={props.onFetchAvatarPersonaData} />
<AvatarPersona userId={userId} {...options} dataProvider={onFetchAvatarPersonaData} />
)}
</Stack>
</Stack>
);
},
[props.onFetchAvatarPersonaData]
[onFetchAvatarPersonaData]
);

const onRenderAvatarCallback = useCallback(
(userId?: string, options?: CustomAvatarOptions) => {
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
if (onRenderAvatar) {
const defaultOnRenderAvatarWrapper = (props: CustomAvatarOptions): JSX.Element =>
defaultOnRenderAvatar(userId, props);
return onRenderAvatar(userId, options, defaultOnRenderAvatarWrapper);
}
return defaultOnRenderAvatar(userId, options);
},
[defaultOnRenderAvatar, /* @conditional-compile-remove(composite-onRenderAvatar-API) */ onRenderAvatar]
);

const remoteVideoTileMenuOptions: false | VideoTileContextualMenuProps | VideoTileDrawerMenuProps = useMemo(() => {
Expand Down Expand Up @@ -201,7 +221,7 @@ export const MediaGallery = (props: MediaGalleryProps): JSX.Element => {
layout={layoutBasedOnUserSelection()}
showCameraSwitcherInLocalPreview={props.isMobile}
localVideoCameraCycleButtonProps={cameraSwitcherProps}
onRenderAvatar={onRenderAvatar}
onRenderAvatar={onRenderAvatarCallback}
remoteVideoTileMenu={remoteVideoTileMenuOptions}
overflowGalleryPosition={overflowGalleryPosition}
localVideoTileSize={
Expand Down Expand Up @@ -234,7 +254,7 @@ export const MediaGallery = (props: MediaGalleryProps): JSX.Element => {
props.localVideoTileOptions,
props.userSetGalleryLayout,
cameraSwitcherProps,
onRenderAvatar,
onRenderAvatarCallback,
remoteVideoTileMenuOptions,
overflowGalleryPosition,
userRole,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { SidePaneHeader } from '../../../common/SidePaneHeader';
import { PeoplePaneContent } from '../../../common/PeoplePaneContent';
import { useLocale } from '../../../localization';
import { ParticipantMenuItemsCallback, _DrawerMenuItemProps, MediaAccess } from '@internal/react-components';
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
import { OnRenderAvatarCallback } from '@internal/react-components';
import { AvatarPersonaDataCallback } from '../../../common/AvatarPersona';
import { IButton, IContextualMenuProps, IContextualMenuItem } from '@fluentui/react';
import { useSelector } from '../../hooks/useSelector';
Expand All @@ -21,6 +23,8 @@ export const usePeoplePane = (props: {
setDrawerMenuItems: (items: _DrawerMenuItemProps[]) => void;
inviteLink?: string;
onFetchAvatarPersonaData?: AvatarPersonaDataCallback;
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
onRenderAvatar?: OnRenderAvatarCallback;
onFetchParticipantMenuItems?: ParticipantMenuItemsCallback;
mobileView?: boolean;
peopleButtonRef?: RefObject<IButton>;
Expand Down Expand Up @@ -58,6 +62,8 @@ export const usePeoplePane = (props: {
updateSidePaneRenderer,
inviteLink,
onFetchAvatarPersonaData,
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
onRenderAvatar,
onFetchParticipantMenuItems,
setDrawerMenuItems,
mobileView,
Expand Down Expand Up @@ -623,6 +629,8 @@ export const usePeoplePane = (props: {
<PeoplePaneContent
inviteLink={inviteLink}
onFetchAvatarPersonaData={onFetchAvatarPersonaData}
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
onRenderAvatar={onRenderAvatar}
onFetchParticipantMenuItems={onFetchParticipantMenuItemsForCallComposite}
setDrawerMenuItems={setDrawerMenuItems}
mobileView={mobileView}
Expand Down Expand Up @@ -659,6 +667,8 @@ export const usePeoplePane = (props: {
showPermitOthersVideoPrompt,
inviteLink,
onFetchAvatarPersonaData,
/* @conditional-compile-remove(composite-onRenderAvatar-API) */
onRenderAvatar,
onFetchParticipantMenuItemsForCallComposite,
setDrawerMenuItems,
mobileView,
Expand Down
Loading