mirror of
https://github.com/suitenumerique/meet.git
synced 2026-08-30 12:09:08 +00:00
✨(feat) connect PiP to room and options menu
connecting pip and room
This commit is contained in:
+2
@@ -7,6 +7,7 @@ import { EffectsMenuItem } from './EffectsMenuItem'
|
|||||||
import { SupportMenuItem } from './SupportMenuItem'
|
import { SupportMenuItem } from './SupportMenuItem'
|
||||||
import { TranscriptMenuItem } from './TranscriptMenuItem'
|
import { TranscriptMenuItem } from './TranscriptMenuItem'
|
||||||
import { ScreenRecordingMenuItem } from './ScreenRecordingMenuItem'
|
import { ScreenRecordingMenuItem } from './ScreenRecordingMenuItem'
|
||||||
|
import { PictureInPictureMenuItem } from './PictureInPictureMenuItem'
|
||||||
|
|
||||||
// @todo try refactoring it to use MenuList component
|
// @todo try refactoring it to use MenuList component
|
||||||
export const OptionsMenuItems = () => {
|
export const OptionsMenuItems = () => {
|
||||||
@@ -21,6 +22,7 @@ export const OptionsMenuItems = () => {
|
|||||||
<TranscriptMenuItem />
|
<TranscriptMenuItem />
|
||||||
<ScreenRecordingMenuItem />
|
<ScreenRecordingMenuItem />
|
||||||
<FullScreenMenuItem />
|
<FullScreenMenuItem />
|
||||||
|
<PictureInPictureMenuItem />
|
||||||
<EffectsMenuItem />
|
<EffectsMenuItem />
|
||||||
</MenuSection>
|
</MenuSection>
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|||||||
+24
@@ -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 { useVideoResolutionSubscription } from '../hooks/useVideoResolutionSubscription'
|
||||||
import { SettingsDialogProvider } from '@/features/settings/components/SettingsDialogProvider'
|
import { SettingsDialogProvider } from '@/features/settings/components/SettingsDialogProvider'
|
||||||
import { IsIdleDisconnectModal } from '../components/IsIdleDisconnectModal'
|
import { IsIdleDisconnectModal } from '../components/IsIdleDisconnectModal'
|
||||||
|
import { RoomPiP } from '../components/RoomPiP'
|
||||||
|
import { RoomPiPProvider } from '../hooks/RoomPiPProvider'
|
||||||
import { getParticipantName } from '@/features/rooms/utils/getParticipantName'
|
import { getParticipantName } from '@/features/rooms/utils/getParticipantName'
|
||||||
import { useScreenReaderAnnounce } from '@/hooks/useScreenReaderAnnounce'
|
import { useScreenReaderAnnounce } from '@/hooks/useScreenReaderAnnounce'
|
||||||
import { ReactionPortals } from '@/features/reactions/components/ReactionPortals'
|
import { ReactionPortals } from '@/features/reactions/components/ReactionPortals'
|
||||||
@@ -238,10 +240,11 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isWeb() && (
|
{isWeb() && (
|
||||||
<LayoutContextProvider
|
<RoomPiPProvider>
|
||||||
value={layoutContext}
|
<LayoutContextProvider
|
||||||
// onPinChange={handleFocusStateChange}
|
value={layoutContext}
|
||||||
>
|
// onPinChange={handleFocusStateChange}
|
||||||
|
>
|
||||||
<ScreenShareErrorModal
|
<ScreenShareErrorModal
|
||||||
isOpen={isShareErrorVisible}
|
isOpen={isShareErrorVisible}
|
||||||
onClose={() => setIsShareErrorVisible(false)}
|
onClose={() => setIsShareErrorVisible(false)}
|
||||||
@@ -289,7 +292,9 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<SidePanel />
|
<SidePanel />
|
||||||
</LayoutContextProvider>
|
<RoomPiP />
|
||||||
|
</LayoutContextProvider>
|
||||||
|
</RoomPiPProvider>
|
||||||
)}
|
)}
|
||||||
<RoomAudioRenderer />
|
<RoomAudioRenderer />
|
||||||
<ConnectionStateToast />
|
<ConnectionStateToast />
|
||||||
|
|||||||
Reference in New Issue
Block a user