(feat) connect PiP to room and options menu

connecting pip and room
This commit is contained in:
Cyril
2026-01-20 13:50:17 +01:00
parent f0f6c4bf56
commit 857dccd00b
6 changed files with 98 additions and 5 deletions
@@ -7,6 +7,7 @@ import { EffectsMenuItem } from './EffectsMenuItem'
import { SupportMenuItem } from './SupportMenuItem'
import { TranscriptMenuItem } from './TranscriptMenuItem'
import { ScreenRecordingMenuItem } from './ScreenRecordingMenuItem'
import { PictureInPictureMenuItem } from './PictureInPictureMenuItem'
// @todo try refactoring it to use MenuList component
export const OptionsMenuItems = () => {
@@ -21,6 +22,7 @@ export const OptionsMenuItems = () => {
<TranscriptMenuItem />
<ScreenRecordingMenuItem />
<FullScreenMenuItem />
<PictureInPictureMenuItem />
<EffectsMenuItem />
</MenuSection>
<Separator />
@@ -0,0 +1,24 @@
import { MenuItem } from 'react-aria-components'
import { useTranslation } from 'react-i18next'
import { RiPictureInPicture2Line } from '@remixicon/react'
import { menuRecipe } from '@/primitives/menuRecipe'
import { useRoomPiP } from '../../../hooks/useRoomPiP'
export const PictureInPictureMenuItem = () => {
const { t } = useTranslation('rooms', { keyPrefix: 'options.items' })
const { isSupported, isOpen, toggle } = useRoomPiP()
// Hide the entry when the browser doesn't support Document PiP.
if (!isSupported) return null
return (
<MenuItem
onAction={toggle}
className={menuRecipe({ icon: true, variant: 'dark' }).item}
>
<RiPictureInPicture2Line size={20} />
{isOpen ? t('pictureInPicture.exit') : t('pictureInPicture.enter')}
</MenuItem>
)
}
@@ -0,0 +1,39 @@
import { useCallback, useMemo, useState } from 'react'
import { RoomPiPContext } from './roomPiPContext'
export const RoomPiPProvider = ({
children,
}: {
children: React.ReactNode
}) => {
const [isOpen, setIsOpen] = useState(false)
// Feature detection for Document Picture-in-Picture.
const isSupported =
typeof window !== 'undefined' && 'documentPictureInPicture' in window
const open = useCallback(() => setIsOpen(true), [])
const close = useCallback(() => setIsOpen(false), [])
const toggle = useCallback(
() => setIsOpen((current) => !current),
[]
)
const value = useMemo(
() => ({
isSupported,
isOpen,
open,
close,
toggle,
}),
[close, isOpen, isSupported, open, toggle]
)
return (
<RoomPiPContext.Provider value={value}>
{children}
</RoomPiPContext.Provider>
)
}
@@ -0,0 +1,12 @@
import { createContext } from 'react'
export type RoomPiPContextValue = {
isSupported: boolean
isOpen: boolean
open: () => void
close: () => void
toggle: () => void
}
export const RoomPiPContext = createContext<RoomPiPContextValue | null>(null)
@@ -0,0 +1,11 @@
import { useContext } from 'react'
import { RoomPiPContext } from './roomPiPContext'
export const useRoomPiP = () => {
const context = useContext(RoomPiPContext)
if (!context) {
throw new Error('useRoomPiP must be used within a RoomPiPProvider')
}
return context
}
@@ -34,6 +34,8 @@ import { SettingsDialogExtendedKey } from '@/features/settings/type'
import { useVideoResolutionSubscription } from '../hooks/useVideoResolutionSubscription'
import { SettingsDialogProvider } from '@/features/settings/components/SettingsDialogProvider'
import { IsIdleDisconnectModal } from '../components/IsIdleDisconnectModal'
import { RoomPiP } from '../components/RoomPiP'
import { RoomPiPProvider } from '../hooks/RoomPiPProvider'
import { getParticipantName } from '@/features/rooms/utils/getParticipantName'
import { useScreenReaderAnnounce } from '@/hooks/useScreenReaderAnnounce'
import { ReactionPortals } from '@/features/reactions/components/ReactionPortals'
@@ -238,10 +240,11 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
}}
>
{isWeb() && (
<LayoutContextProvider
value={layoutContext}
// onPinChange={handleFocusStateChange}
>
<RoomPiPProvider>
<LayoutContextProvider
value={layoutContext}
// onPinChange={handleFocusStateChange}
>
<ScreenShareErrorModal
isOpen={isShareErrorVisible}
onClose={() => setIsShareErrorVisible(false)}
@@ -289,7 +292,9 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
}}
/>
<SidePanel />
</LayoutContextProvider>
<RoomPiP />
</LayoutContextProvider>
</RoomPiPProvider>
)}
<RoomAudioRenderer />
<ConnectionStateToast />