diff --git a/src/frontend/src/features/pip/components/PipControlBar.tsx b/src/frontend/src/features/pip/components/PipControlBar.tsx index ff9756f1..40cdbfc2 100644 --- a/src/frontend/src/features/pip/components/PipControlBar.tsx +++ b/src/frontend/src/features/pip/components/PipControlBar.tsx @@ -14,17 +14,21 @@ import { usePipElementSize } from '../hooks/usePipElementSize' import { PipOptionsMenu } from './controls/PipOptionsMenu' import { PipReactionsToggle } from './PipReactionsToggle' +export const CollapsibleControls = { + HAND: 'hand', + SUBTITLES: 'subtitles', + SCREEN_SHARE: 'screenShare', + REACTIONS: 'reactions', +} as const + export type CollapsibleControl = - | 'hand' - | 'subtitles' - | 'screenShare' - | 'reactions' + (typeof CollapsibleControls)[keyof typeof CollapsibleControls] const COLLAPSE_ORDER: CollapsibleControl[] = [ - 'hand', - 'subtitles', - 'screenShare', - 'reactions', + CollapsibleControls.HAND, + CollapsibleControls.SUBTITLES, + CollapsibleControls.SCREEN_SHARE, + CollapsibleControls.REACTIONS, ] const BUTTON_SLOT = 50 @@ -39,7 +43,7 @@ const getHiddenControls = ( const collapsible = showScreenShare ? COLLAPSE_ORDER - : COLLAPSE_ORDER.filter((c) => c !== 'screenShare') + : COLLAPSE_ORDER.filter((c) => c !== CollapsibleControls.SCREEN_SHARE) const available = containerWidth - ESSENTIAL_WIDTH const maxVisible = Math.max(0, Math.floor(available / BUTTON_SLOT)) @@ -84,10 +88,10 @@ export const PipControlBar = ({ - {!hidden.has('reactions') && } - {showScreenShare && !hidden.has('screenShare') && } - {!hidden.has('subtitles') && } - {!hidden.has('hand') && } + {!hidden.has(CollapsibleControls.REACTIONS) && } + {showScreenShare && !hidden.has(CollapsibleControls.SCREEN_SHARE) && } + {!hidden.has(CollapsibleControls.SUBTITLES) && } + {!hidden.has(CollapsibleControls.HAND) && } diff --git a/src/frontend/src/features/pip/components/controls/PipOptionsMenuItems.tsx b/src/frontend/src/features/pip/components/controls/PipOptionsMenuItems.tsx index 3d41e2bf..d27dcb47 100644 --- a/src/frontend/src/features/pip/components/controls/PipOptionsMenuItems.tsx +++ b/src/frontend/src/features/pip/components/controls/PipOptionsMenuItems.tsx @@ -1,5 +1,4 @@ import { Menu as RACMenu, MenuSection } from 'react-aria-components' -import { useTranslation } from 'react-i18next' import { Separator } from '@/primitives/Separator' import { FeedbackMenuItem } from '@/features/rooms/livekit/components/controls/Options/FeedbackMenuItem' import { EffectsMenuItem } from '@/features/rooms/livekit/components/controls/Options/EffectsMenuItem' @@ -16,7 +15,6 @@ type PipOptionsMenuItemsProps = { export const PipOptionsMenuItems = ({ overflowControls, }: PipOptionsMenuItemsProps) => { - const { t } = useTranslation('rooms') const hasOverflow = overflowControls && overflowControls.size > 0 return ( @@ -29,7 +27,7 @@ export const PipOptionsMenuItems = ({ {hasOverflow && ( <> - + diff --git a/src/frontend/src/features/pip/components/controls/PipOverflowItems.tsx b/src/frontend/src/features/pip/components/controls/PipOverflowItems.tsx index 40f16dcb..f7875fb6 100644 --- a/src/frontend/src/features/pip/components/controls/PipOverflowItems.tsx +++ b/src/frontend/src/features/pip/components/controls/PipOverflowItems.tsx @@ -1,3 +1,4 @@ +import React from 'react' import { MenuItem } from 'react-aria-components' import { RiHand, @@ -5,57 +6,60 @@ import { RiArrowUpLine, RiEmotionLine, } from '@remixicon/react' -import { TFunction } from 'i18next' +import { useTranslation } from 'react-i18next' +import { Track } from 'livekit-client' import { pipLayoutStore } from '@/features/pip/stores/pipLayoutStore' import { menuRecipe } from '@/primitives/menuRecipe' -import { useRoomContext } from '@livekit/components-react' +import { useRoomContext, useTrackToggle } from '@livekit/components-react' import { useRaisedHand } from '@/features/rooms/livekit/hooks/useRaisedHand' import { useSubtitles } from '@/features/subtitle/hooks/useSubtitles' import { useAreSubtitlesAvailable } from '@/features/subtitle/hooks/useAreSubtitlesAvailable' import { useSnapshot } from 'valtio' -import type { CollapsibleControl } from '../PipControlBar' +import { CollapsibleControls, type CollapsibleControl } from '../PipControlBar' type PipOverflowItemsProps = { overflowControls: Set - t: TFunction<'rooms'> } export const PipOverflowItems = ({ overflowControls, - t, }: PipOverflowItemsProps) => { + const { t } = useTranslation('rooms') const room = useRoomContext() const { isHandRaised, toggleRaisedHand } = useRaisedHand({ participant: room.localParticipant, }) const { areSubtitlesOpen, toggleSubtitles } = useSubtitles() const areSubtitlesAvailable = useAreSubtitlesAvailable() - const pipSnap = useSnapshot(pipLayoutStore) + const { buttonProps: screenShareProps, enabled: isScreenSharing } = + useTrackToggle({ + source: Track.Source.ScreenShare, + captureOptions: { audio: true, selfBrowserSurface: 'include' }, + }) + const pipLayoutSnap = useSnapshot(pipLayoutStore) const toggleReactions = () => { - pipLayoutStore.showReactionsToolbar = !pipSnap.showReactionsToolbar + pipLayoutStore.showReactionsToolbar = !pipLayoutSnap.showReactionsToolbar } const itemClass = menuRecipe({ icon: true, variant: 'dark' }).item return ( <> - {overflowControls.has('reactions') && ( + {overflowControls.has(CollapsibleControls.REACTIONS) && ( {t('controls.reactions.button')} )} - {overflowControls.has('screenShare') && ( + {overflowControls.has(CollapsibleControls.SCREEN_SHARE) && ( { - /* screen share requires track toggle, handled externally */ - }} + onAction={() => screenShareProps.onClick?.({} as React.MouseEvent)} className={itemClass} > - {t('controls.screenShare.start')} + {t(isScreenSharing ? 'controls.screenShare.stop' : 'controls.screenShare.start')} )} - {overflowControls.has('subtitles') && areSubtitlesAvailable && ( + {overflowControls.has(CollapsibleControls.SUBTITLES) && areSubtitlesAvailable && ( {areSubtitlesOpen @@ -63,7 +67,7 @@ export const PipOverflowItems = ({ : t('controls.subtitles.closed')} )} - {overflowControls.has('hand') && ( + {overflowControls.has(CollapsibleControls.HAND) && ( {isHandRaised ? t('controls.hand.lower') : t('controls.hand.raise')}