@@ -184,41 +166,8 @@ export const TogetherModeOverlay = memo(
onMouseLeave={() => setHoveredParticipantID('')}
>
- {participantStatus.reaction?.reactionType && (
- // First div - Section that fixes the travel height and applies the movement animation
- // Second div - Responsible for ensuring the sprite emoji is always centered in the participant seat position
- // Third div - Play Animation as the other animation applies on the base play animation for the sprite
-
- )}
-
{participantStatus.showDisplayName && (
-
+
)}
+
+ {participantStatus.reaction?.reactionType && (
+ // First div - Section that fixes the travel height and applies the movement animation
+ // Second div - Responsible for ensuring the sprite emoji is always centered in the participant seat position
+ // Third div - Play Animation as the other animation applies on the base play animation for the sprite
+
+ )}
)
diff --git a/packages/react-components/src/components/VideoGallery.tsx b/packages/react-components/src/components/VideoGallery.tsx
index 951538f2837..4caa52575e3 100644
--- a/packages/react-components/src/components/VideoGallery.tsx
+++ b/packages/react-components/src/components/VideoGallery.tsx
@@ -798,7 +798,6 @@ export const VideoGallery = (props: VideoGalleryProps): JSX.Element => {
localParticipant={localParticipant}
remoteParticipants={remoteParticipants}
reactionResources={reactionResources}
- screenShareComponent={screenShareComponent}
containerWidth={containerWidth}
containerHeight={containerHeight}
/>
@@ -815,7 +814,6 @@ export const VideoGallery = (props: VideoGalleryProps): JSX.Element => {
localParticipant,
remoteParticipants,
reactionResources,
- screenShareComponent,
containerWidth,
containerHeight
]
@@ -842,9 +840,7 @@ export const VideoGallery = (props: VideoGalleryProps): JSX.Element => {
pinnedParticipantUserIds: pinnedParticipants,
overflowGalleryPosition,
localVideoTileSize,
- spotlightedParticipantUserIds: spotlightedParticipants,
- /* @conditional-compile-remove(together-mode) */
- togetherModeStreamComponent
+ spotlightedParticipantUserIds: spotlightedParticipants
}),
[
remoteParticipants,
@@ -862,9 +858,7 @@ export const VideoGallery = (props: VideoGalleryProps): JSX.Element => {
pinnedParticipants,
overflowGalleryPosition,
localVideoTileSize,
- spotlightedParticipants,
- /* @conditional-compile-remove(together-mode) */
- togetherModeStreamComponent
+ spotlightedParticipants
]
);
@@ -886,15 +880,17 @@ export const VideoGallery = (props: VideoGalleryProps): JSX.Element => {
/* @conditional-compile-remove(together-mode) */
// Teams users can switch to Together mode layout only if they have the capability,
// while ACS users can do so only if Together mode is enabled.
- if (layout === 'togetherMode' && canSwitchToTogetherModeLayout) {
- return
;
+ if (!screenShareComponent && layout === 'togetherMode' && canSwitchToTogetherModeLayout) {
+ return
;
}
return
;
}, [
/* @conditional-compile-remove(together-mode) */ canSwitchToTogetherModeLayout,
layout,
layoutProps,
- screenShareParticipant
+ screenShareComponent,
+ screenShareParticipant,
+ /* @conditional-compile-remove(together-mode) */ togetherModeStreamComponent
]);
return (
diff --git a/packages/react-components/src/components/VideoGallery/TogetherModeLayout.tsx b/packages/react-components/src/components/VideoGallery/TogetherModeLayout.tsx
index 17a4c6ac3ba..e2b2eb7b442 100644
--- a/packages/react-components/src/components/VideoGallery/TogetherModeLayout.tsx
+++ b/packages/react-components/src/components/VideoGallery/TogetherModeLayout.tsx
@@ -2,27 +2,11 @@
// Licensed under the MIT License.
/* @conditional-compile-remove(together-mode) */
-import React, { useMemo, useRef, useState } from 'react';
-/* @conditional-compile-remove(together-mode) */
-import { useId } from '@fluentui/react-hooks';
+import React from 'react';
/* @conditional-compile-remove(together-mode) */
import { _formatString } from '@internal/acs-ui-common';
/* @conditional-compile-remove(together-mode) */
-import { LayoutProps } from './Layout';
-/* @conditional-compile-remove(together-mode) */
-import { LayerHost, mergeStyles, Stack } from '@fluentui/react';
-/* @conditional-compile-remove(together-mode) */
-import { renderTiles, useOrganizedParticipants } from './utils/videoGalleryLayoutUtils';
-/* @conditional-compile-remove(together-mode) */
-import { OverflowGallery } from './OverflowGallery';
-/* @conditional-compile-remove(together-mode) */
-import { rootLayoutStyle } from './styles/DefaultLayout.styles';
-/* @conditional-compile-remove(together-mode) */
-import { isNarrowWidth, isShortHeight } from '../utils/responsive';
-/* @conditional-compile-remove(together-mode) */
-import { innerLayoutStyle, layerHostStyle } from './styles/FloatingLocalVideoLayout.styles';
-/* @conditional-compile-remove(together-mode) */
-import { videoGalleryLayoutGap } from './styles/Layout.styles';
+import { Stack } from '@fluentui/react';
/* @conditional-compile-remove(together-mode) */
/**
@@ -30,113 +14,7 @@ import { videoGalleryLayoutGap } from './styles/Layout.styles';
* boost by memoizing the same rendered component to avoid rerendering this when the parent component rerenders.
* https://reactjs.org/docs/react-api.html#reactmemo
*/
-export const TogetherModeLayout = (props: LayoutProps): JSX.Element => {
- const {
- remoteParticipants = [],
- dominantSpeakers,
- screenShareComponent,
- onRenderRemoteParticipant,
- styles,
- maxRemoteVideoStreams,
- parentWidth,
- parentHeight,
- overflowGalleryPosition = 'horizontalBottom',
- pinnedParticipantUserIds = [],
- togetherModeStreamComponent
- } = props;
- const isNarrow = parentWidth ? isNarrowWidth(parentWidth) : false;
-
- const isShort = parentHeight ? isShortHeight(parentHeight) : false;
-
- const [indexesToRender, setIndexesToRender] = useState
([]);
- const childrenPerPage = useRef(4);
-
- const { gridParticipants, overflowGalleryParticipants } = useOrganizedParticipants({
- remoteParticipants,
- dominantSpeakers,
- maxGridParticipants: maxRemoteVideoStreams,
- isScreenShareActive: !!screenShareComponent,
- maxOverflowGalleryDominantSpeakers: screenShareComponent
- ? childrenPerPage.current - (pinnedParticipantUserIds.length % childrenPerPage.current)
- : childrenPerPage.current,
- pinnedParticipantUserIds,
- layout: 'floatingLocalVideo'
- });
- const { gridTiles, overflowGalleryTiles } = renderTiles(
- gridParticipants,
- onRenderRemoteParticipant,
- maxRemoteVideoStreams,
- indexesToRender,
- overflowGalleryParticipants,
- dominantSpeakers
- );
-
- const layerHostId = useId('layerhost');
- const togetherModeOverFlowGalleryTiles = useMemo(() => {
- let newTiles = overflowGalleryTiles;
- if (togetherModeStreamComponent) {
- if (screenShareComponent) {
- newTiles = gridTiles.concat(overflowGalleryTiles);
- }
- }
- return newTiles;
- }, [gridTiles, overflowGalleryTiles, screenShareComponent, togetherModeStreamComponent]);
-
- const overflowGallery = useMemo(() => {
- if (overflowGalleryTiles.length === 0 && !props.screenShareComponent) {
- return null;
- }
- return (
- {
- childrenPerPage.current = n;
- }}
- parentWidth={parentWidth}
- />
- );
- }, [
- overflowGalleryTiles.length,
- props.screenShareComponent,
- isShort,
- isNarrow,
- togetherModeOverFlowGalleryTiles,
- styles?.horizontalGallery,
- styles?.verticalGallery,
- overflowGalleryPosition,
- parentWidth
- ]);
-
- return screenShareComponent ? (
-
-
-
- {props.overflowGalleryPosition === 'horizontalTop' ? overflowGallery : <>>}
- {screenShareComponent}
- {overflowGalleryTrampoline(overflowGallery, props.overflowGalleryPosition)}
-
-
- ) : (
- {props.togetherModeStreamComponent}
- );
-};
-
-/* @conditional-compile-remove(together-mode) */
-const overflowGalleryTrampoline = (
- gallery: JSX.Element | null,
- galleryPosition?: 'horizontalBottom' | 'verticalRight' | 'horizontalTop'
-): JSX.Element | null => {
- return galleryPosition !== 'horizontalTop' ? gallery : <>>;
- return gallery;
+export const TogetherModeLayout = (props: { togetherModeStreamComponent: JSX.Element }): JSX.Element => {
+ const { togetherModeStreamComponent } = props;
+ return {togetherModeStreamComponent};
};
diff --git a/packages/react-components/src/components/VideoGallery/TogetherModeStream.tsx b/packages/react-components/src/components/VideoGallery/TogetherModeStream.tsx
index 521a58aa2dc..d81639b1112 100644
--- a/packages/react-components/src/components/VideoGallery/TogetherModeStream.tsx
+++ b/packages/react-components/src/components/VideoGallery/TogetherModeStream.tsx
@@ -42,7 +42,6 @@ export const TogetherModeStream = memo(
reactionResources?: ReactionResources;
localParticipant?: VideoGalleryLocalParticipant;
remoteParticipants?: VideoGalleryRemoteParticipant[];
- screenShareComponent?: JSX.Element;
containerWidth?: number;
containerHeight?: number;
}): JSX.Element => {
diff --git a/packages/react-components/src/components/styles/TogetherMode.styles.ts b/packages/react-components/src/components/styles/TogetherMode.styles.ts
index b816b7a06b9..3bd8d260684 100644
--- a/packages/react-components/src/components/styles/TogetherMode.styles.ts
+++ b/packages/react-components/src/components/styles/TogetherMode.styles.ts
@@ -30,6 +30,12 @@ export const REACTION_TRAVEL_HEIGHT = 0.35 * REM_TO_PX_MULTIPLIER;
*/
export const REACTION_MAX_TRAVEL_HEIGHT = 0.5 * REM_TO_PX_MULTIPLIER;
+/* @conditional-compile-remove(together-mode) */
+/**
+ * The maximum width for displaying the participant's display name.
+ */
+export const MAX_DISPLAY_NAME_WIDTH = 150;
+
/* @conditional-compile-remove(together-mode) */
/**
* Interface for defining the coordinates of a seat in Together Mode.
@@ -198,16 +204,30 @@ export const togetherModeParticipantDisplayName = (
isParticipantHovered: boolean,
participantSeatingWidth: number,
color: string
-): CSSProperties => {
- const MIN_DISPLAY_NAME_WIDTH = 100;
+): React.CSSProperties => {
+ // expands the display name width when participant is hovered or clicked on else make it 70% of the participant seating width
+ const width =
+ isParticipantHovered || participantSeatingWidth * REM_TO_PX_MULTIPLIER > MAX_DISPLAY_NAME_WIDTH
+ ? 'fit-content'
+ : _pxToRem(0.7 * participantSeatingWidth * REM_TO_PX_MULTIPLIER);
+
+ // For smaller displays, the display name is hidden only participant is hovered or clicked on for mobile view
+ const showDisplayName =
+ isParticipantHovered || participantSeatingWidth * REM_TO_PX_MULTIPLIER > MAX_DISPLAY_NAME_WIDTH
+ ? 'inline-block'
+ : 'none';
+
return {
textOverflow: 'ellipsis',
- flexGrow: 1, // Allow text to grow within available space
- overflow: isParticipantHovered ? 'visible' : 'hidden',
whiteSpace: 'nowrap',
textAlign: 'center',
color,
- display: isParticipantHovered || participantSeatingWidth > MIN_DISPLAY_NAME_WIDTH ? 'inline-block' : 'none' // Completely remove the element when hidden
+ overflow: isParticipantHovered ? 'visible' : 'hidden',
+ width,
+ display: showDisplayName,
+ fontSize: `${_pxToRem(13)}`,
+ lineHeight: `${_pxToRem(20)}`,
+ maxWidth: isParticipantHovered ? 'fit-content' : _pxToRem(0.7 * participantSeatingWidth * REM_TO_PX_MULTIPLIER)
};
};
@@ -221,7 +241,7 @@ export const togetherModeParticipantEmojiSpriteStyle = (
participantSeatWidth: string
): CSSProperties => {
const participantSeatWidthInPixel = parseFloat(participantSeatWidth) * REM_TO_PX_MULTIPLIER;
- const emojiScaledSizeInPercent = (emojiScaledSize / participantSeatWidthInPixel) * 100;
+ const emojiScaledSizeInPercent = 100 - (emojiScaledSize / participantSeatWidthInPixel) * 100;
return {
width: `${emojiSize}`,
position: 'absolute',
@@ -229,3 +249,18 @@ export const togetherModeParticipantEmojiSpriteStyle = (
left: `${emojiScaledSizeInPercent / 2}%`
};
};
+
+/* @conditional-compile-remove(together-mode) */
+/**
+ * The style for the transition of the participant status container in Together Mode.
+ * @private
+ */
+export const participantStatusTransitionStyle: CSSProperties = {
+ position: 'absolute',
+ bottom: `${_pxToRem(2)}`,
+ width: 'fit-content',
+ textAlign: 'center',
+ transform: 'translate(-50%)',
+ transition: 'width 0.3s ease, transform 0.3s ease',
+ left: '50%'
+};
diff --git a/packages/react-components/src/index.ts b/packages/react-components/src/index.ts
index 45ade06249e..335e4b0ae25 100644
--- a/packages/react-components/src/index.ts
+++ b/packages/react-components/src/index.ts
@@ -64,6 +64,15 @@ export type {
ViewScalingMode
} from './types';
+/* @conditional-compile-remove(together-mode) */
+export type {
+ TogetherModeStreamViewResult,
+ VideoGalleryTogetherModeParticipantPosition,
+ VideoGalleryTogetherModeSeatingInfo,
+ VideoGalleryTogetherModeStreams,
+ TogetherModeStreamOptions
+} from './types';
+
export type { RaisedHand } from './types';
export type { Spotlight } from './types';
@@ -87,14 +96,5 @@ export type { SurveyIssuesHeadingStrings } from './types';
export type { CallSurveyImprovementSuggestions } from './types';
-/* @conditional-compile-remove(together-mode) */
-export type {
- TogetherModeStreamViewResult,
- VideoGalleryTogetherModeParticipantPosition,
- VideoGalleryTogetherModeSeatingInfo,
- VideoGalleryTogetherModeStreams,
- TogetherModeStreamOptions
-} from './types';
-
/* @conditional-compile-remove(media-access) */
export type { MediaAccess } from './types';
diff --git a/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts b/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts
index c0f80e64a9a..561db28f6e3 100644
--- a/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts
+++ b/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts
@@ -121,7 +121,7 @@ import { CallSurvey, CallSurveyResponse } from '@azure/communication-calling';
import { CallingSoundSubscriber } from './CallingSoundSubscriber';
import { CallingSounds } from './CallAdapter';
/* @conditional-compile-remove(together-mode) */
-import { TogetherModeStreamViewResult } from '@internal/react-components';
+import { TogetherModeStreamViewResult, TogetherModeStreamOptions } from '@internal/react-components';
type CallTypeOf = AgentType extends CallAgent ? Call : TeamsCall;
@@ -820,7 +820,7 @@ export class AzureCommunicationCallAdapter {
return await this.handlers.onCreateTogetherModeStreamView(options);
}
diff --git a/packages/react-composites/src/composites/CallComposite/selectors/baseSelectors.ts b/packages/react-composites/src/composites/CallComposite/selectors/baseSelectors.ts
index 44041c63c99..84a2c55bc14 100644
--- a/packages/react-composites/src/composites/CallComposite/selectors/baseSelectors.ts
+++ b/packages/react-composites/src/composites/CallComposite/selectors/baseSelectors.ts
@@ -44,6 +44,8 @@ import { CommunicationIdentifier } from '@azure/communication-common';
import { CaptionsKind } from '@azure/communication-calling';
import { ReactionResources } from '@internal/react-components';
+/* @conditional-compile-remove(together-mode) */
+import { CommunicationIdentifierKind } from '@azure/communication-common';
/* @conditional-compile-remove(media-access) */
import { MediaAccess } from '@internal/react-components';
@@ -332,6 +334,24 @@ export const getIsRoomsCall = (state: CallAdapterState): boolean => state.isRoom
export const getVideoBackgroundImages = (state: CallAdapterState): VideoBackgroundImage[] | undefined =>
state.videoBackgroundImages;
+/* @conditional-compile-remove(together-mode) */
+/**
+ * @private
+ * Gets the together mode streams state.
+ * @param state - The current state of the call adapter.
+ * @returns The together mode streams state or undefined.
+ */
+export const getIsTogetherModeActive = (state: CallAdapterState): boolean | undefined =>
+ state.call?.togetherMode.isActive;
+
+/* @conditional-compile-remove(together-mode) */
+/**
+ * @private
+ * Gets local participant's user id.
+ * @param state - The current state of the call adapter.
+ * @returns The local participant's user id or undefined.
+ */
+export const getLocalUserId = (state: CallAdapterState): CommunicationIdentifierKind | undefined => state.userId;
/* @conditional-compile-remove(media-access) */
/** @private */
export const getMediaAccessSetting = (state: CallAdapterState): MediaAccess | undefined =>
diff --git a/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts b/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts
index 86048758c13..c5fe973421f 100644
--- a/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts
+++ b/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts
@@ -27,7 +27,7 @@ import { MessageOptions } from '@internal/acs-ui-common';
/* @conditional-compile-remove(breakout-rooms) */
import { toFlatCommunicationIdentifier } from '@internal/acs-ui-common';
/* @conditional-compile-remove(together-mode) */
-import { TogetherModeStreamViewResult } from '@internal/react-components';
+import { TogetherModeStreamViewResult, TogetherModeStreamOptions } from '@internal/react-components';
import {
ParticipantsJoinedListener,
ParticipantsLeftListener,
@@ -525,7 +525,7 @@ export class AzureCommunicationCallWithChatAdapter implements CallWithChatAdapte
}
/* @conditional-compile-remove(together-mode) */
public async createTogetherModeStreamView(
- options?: VideoStreamOptions
+ options?: TogetherModeStreamOptions
): Promise {
return await this.callAdapter.createTogetherModeStreamView(options);
}
diff --git a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts
index d335f2ed8b4..58dc06b2709 100644
--- a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts
+++ b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts
@@ -7,7 +7,7 @@ import { CallAdapter, CallAdapterState } from '../../CallComposite';
import { VideoBackgroundImage, VideoBackgroundEffect } from '../../CallComposite';
import { CreateVideoStreamViewResult, VideoStreamOptions } from '@internal/react-components';
/* @conditional-compile-remove(together-mode) */
-import { TogetherModeStreamViewResult } from '@internal/react-components';
+import { TogetherModeStreamViewResult, TogetherModeStreamOptions } from '@internal/react-components';
import {
AudioDeviceInfo,
VideoDeviceInfo,
@@ -139,7 +139,7 @@ export class CallWithChatBackedCallAdapter implements CallAdapter {
await this.callWithChatAdapter.createStreamView(remoteUserId, options);
/* @conditional-compile-remove(together-mode) */
public createTogetherModeStreamView = async (
- options?: VideoStreamOptions
+ options?: TogetherModeStreamOptions
): Promise =>
await this.callWithChatAdapter.createTogetherModeStreamView(options);
/* @conditional-compile-remove(together-mode) */
diff --git a/packages/react-composites/src/composites/common/ControlBar/DesktopMoreButton.tsx b/packages/react-composites/src/composites/common/ControlBar/DesktopMoreButton.tsx
index 9c74d4944c3..8a7b347c8f2 100644
--- a/packages/react-composites/src/composites/common/ControlBar/DesktopMoreButton.tsx
+++ b/packages/react-composites/src/composites/common/ControlBar/DesktopMoreButton.tsx
@@ -25,6 +25,13 @@ import { _preventDismissOnEvent } from '@internal/acs-ui-common';
import { showDtmfDialer } from '../../CallComposite/utils/MediaGalleryUtils';
import { useSelector } from '../../CallComposite/hooks/useSelector';
import { getTargetCallees } from '../../CallComposite/selectors/baseSelectors';
+/* @conditional-compile-remove(together-mode) */
+import {
+ getIsTogetherModeActive,
+ getCapabilites,
+ getLocalUserId,
+ getIsTeamsCall
+} from '../../CallComposite/selectors/baseSelectors';
import { getTeamsMeetingCoordinates, getIsTeamsMeeting } from '../../CallComposite/selectors/baseSelectors';
import { CallControlOptions } from '../../CallComposite';
@@ -78,6 +85,14 @@ export const DesktopMoreButton = (props: DesktopMoreButtonProps): JSX.Element =>
const isTeamsMeeting = useSelector(getIsTeamsMeeting);
const teamsMeetingCoordinates = useSelector(getTeamsMeetingCoordinates);
+ /* @conditional-compile-remove(together-mode) */
+ const isTogetherModeActive = useSelector(getIsTogetherModeActive);
+ /* @conditional-compile-remove(together-mode) */
+ const participantCapability = useSelector(getCapabilites);
+ /* @conditional-compile-remove(together-mode) */
+ const participantId = useSelector(getLocalUserId);
+ /* @conditional-compile-remove(together-mode) */
+ const isTeamsCall = useSelector(getIsTeamsCall);
const [dtmfDialerChecked, setDtmfDialerChecked] = useState(props.dtmfDialerPresent ?? false);
@@ -394,6 +409,29 @@ export const DesktopMoreButton = (props: DesktopMoreButtonProps): JSX.Element =>
}
};
+ /* @conditional-compile-remove(together-mode) */
+ const togetherModeOption = {
+ key: 'togetherModeSelectionKey',
+ text: localeStrings.strings.call.moreButtonTogetherModeLayoutLabel,
+ canCheck: true,
+ itemProps: {
+ styles: buttonFlyoutIncreasedSizeStyles
+ },
+ isChecked: props.userSetGalleryLayout === 'togetherMode',
+ onClick: () => {
+ props.onUserSetGalleryLayout && props.onUserSetGalleryLayout('togetherMode');
+ setFocusedContentOn(false);
+ },
+ disabled: !(
+ (participantId?.kind === 'microsoftTeamsUser' && participantCapability?.startTogetherMode?.isPresent) ||
+ isTogetherModeActive
+ ),
+ iconProps: {
+ iconName: 'TogetherModeLayout',
+ styles: { root: { lineHeight: 0 } }
+ }
+ };
+
/* @conditional-compile-remove(overflow-top-composite) */
const overflowGalleryOption = {
key: 'topKey',
@@ -424,6 +462,10 @@ export const DesktopMoreButton = (props: DesktopMoreButtonProps): JSX.Element =>
galleryOptions.subMenuProps?.items?.push(galleryOption);
/* @conditional-compile-remove(overflow-top-composite) */
galleryOptions.subMenuProps?.items?.push(overflowGalleryOption);
+ /* @conditional-compile-remove(together-mode) */
+ if (isTeamsCall || isTeamsMeeting) {
+ galleryOptions.subMenuProps?.items?.push(togetherModeOption);
+ }
if (props.callControls === true || (props.callControls as CallControlOptions)?.galleryControlsButton !== false) {
moreButtonContextualMenuItems.push(galleryOptions);
}
diff --git a/packages/react-composites/src/composites/common/Drawer/MoreDrawer.tsx b/packages/react-composites/src/composites/common/Drawer/MoreDrawer.tsx
index 1668a5b62df..75431f45eba 100644
--- a/packages/react-composites/src/composites/common/Drawer/MoreDrawer.tsx
+++ b/packages/react-composites/src/composites/common/Drawer/MoreDrawer.tsx
@@ -45,6 +45,13 @@ import { showDtmfDialer } from '../../CallComposite/utils/MediaGalleryUtils';
import { SpokenLanguageSettingsDrawer } from './SpokenLanguageSettingsDrawer';
import { DtmfDialPadOptions } from '../../CallComposite';
import { getRemoteParticipantsConnectedSelector } from '../../CallComposite/selectors/mediaGallerySelector';
+/* @conditional-compile-remove(together-mode) */
+import {
+ getCapabilites,
+ getIsTogetherModeActive,
+ getLocalUserId,
+ getIsTeamsCall
+} from '../../CallComposite/selectors/baseSelectors';
/** @private */
export interface MoreDrawerStrings {
@@ -203,7 +210,16 @@ export const MoreDrawer = (props: MoreDrawerProps): JSX.Element => {
const [dtmfDialerChecked, setDtmfDialerChecked] = useState(props.dtmfDialerPresent ?? false);
const raiseHandButtonProps = usePropsFor(RaiseHandButton) as RaiseHandButtonProps;
+ /* @conditional-compile-remove(together-mode) */
+ const participantCapability = useSelector(getCapabilites);
+ /* @conditional-compile-remove(together-mode) */
+ const participantId = useSelector(getLocalUserId);
+ /* @conditional-compile-remove(together-mode) */
+ const isTogetherModeActive = useSelector(getIsTogetherModeActive);
+ /* @conditional-compile-remove(together-mode) */
+ const isTeamsCall = useSelector(getIsTeamsCall);
+ const isTeamsMeeting = getIsTeamsMeeting(callAdapter.getState());
const onSpeakerItemClick = useCallback(
(
_ev: React.MouseEvent | React.KeyboardEvent | undefined,
@@ -389,8 +405,31 @@ export const MoreDrawer = (props: MoreDrawerProps): JSX.Element => {
secondaryIconProps: props.userSetGalleryLayout === 'default' ? { iconName: 'Accept' } : undefined
};
+ /* @conditional-compile-remove(together-mode) */
+ const togetherModeOption = {
+ itemKey: 'togetherModeSelectionKey',
+ text: localeStrings.strings.call.moreButtonTogetherModeLayoutLabel,
+ onItemClick: () => {
+ props.onUserSetGalleryLayout && props.onUserSetGalleryLayout('togetherMode');
+ onLightDismiss();
+ },
+ iconProps: {
+ iconName: 'TogetherModeLayout',
+ styles: { root: { lineHeight: 0 } }
+ },
+ disabled: !(
+ (participantId?.kind === 'microsoftTeamsUser' && participantCapability?.startTogetherMode?.isPresent) ||
+ isTogetherModeActive
+ ),
+ secondaryIconProps: props.userSetGalleryLayout === 'default' ? { iconName: 'Accept' } : undefined
+ };
+
/* @conditional-compile-remove(gallery-layout-composite) */
galleryLayoutOptions.subMenuProps?.push(galleryOption);
+ /* @conditional-compile-remove(together-mode) */
+ if (isTeamsCall || isTeamsMeeting) {
+ galleryLayoutOptions.subMenuProps?.push(togetherModeOption);
+ }
if (drawerSelectionOptions !== false && isEnabled(drawerSelectionOptions?.galleryControlsButton)) {
drawerMenuItems.push(galleryLayoutOptions);
@@ -448,8 +487,6 @@ export const MoreDrawer = (props: MoreDrawerProps): JSX.Element => {
});
}
- const isTeamsMeeting = getIsTeamsMeeting(callAdapter.getState());
-
const teamsMeetingCoordinates = getTeamsMeetingCoordinates(callAdapter.getState());
if (
diff --git a/packages/react-composites/tests/browser/snapshots/beta/tests/browser/call/hermetic/VideoGallery.test.ts-snapshots/default-layout-Desktop-Chrome-linux.png b/packages/react-composites/tests/browser/snapshots/beta/tests/browser/call/hermetic/VideoGallery.test.ts-snapshots/default-layout-Desktop-Chrome-linux.png
index c6d4839f524..f3924505285 100644
Binary files a/packages/react-composites/tests/browser/snapshots/beta/tests/browser/call/hermetic/VideoGallery.test.ts-snapshots/default-layout-Desktop-Chrome-linux.png and b/packages/react-composites/tests/browser/snapshots/beta/tests/browser/call/hermetic/VideoGallery.test.ts-snapshots/default-layout-Desktop-Chrome-linux.png differ
diff --git a/packages/react-composites/tests/browser/snapshots/beta/tests/browser/call/hermetic/VideoGallery.test.ts-snapshots/gallery-options-mobile-Mobile-Android-Portrait-linux.png b/packages/react-composites/tests/browser/snapshots/beta/tests/browser/call/hermetic/VideoGallery.test.ts-snapshots/gallery-options-mobile-Mobile-Android-Portrait-linux.png
index c208d9f46c4..0897ddafc6f 100644
Binary files a/packages/react-composites/tests/browser/snapshots/beta/tests/browser/call/hermetic/VideoGallery.test.ts-snapshots/gallery-options-mobile-Mobile-Android-Portrait-linux.png and b/packages/react-composites/tests/browser/snapshots/beta/tests/browser/call/hermetic/VideoGallery.test.ts-snapshots/gallery-options-mobile-Mobile-Android-Portrait-linux.png differ