mirror of
https://github.com/suitenumerique/meet.git
synced 2026-09-04 06:35:38 +00:00
♻️(refactor) inject side panel store via context for PiP
Keep pipLayoutStore separate from layoutStore; shared components use context.
This commit is contained in:
@@ -3,7 +3,10 @@ import { supportsScreenSharing } from '@livekit/components-core'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { styled } from '@/styled-system/jsx'
|
import { styled } from '@/styled-system/jsx'
|
||||||
import { SidePanel } from '@/features/rooms/livekit/components/SidePanel'
|
import { SidePanel } from '@/features/rooms/livekit/components/SidePanel'
|
||||||
import { useSidePanel } from '@/features/rooms/livekit/hooks/useSidePanel'
|
import {
|
||||||
|
SidePanelStoreProvider,
|
||||||
|
useSidePanel,
|
||||||
|
} from '@/features/rooms/livekit/hooks/useSidePanel'
|
||||||
import { pipLayoutStore } from '../stores/pipLayoutStore'
|
import { pipLayoutStore } from '../stores/pipLayoutStore'
|
||||||
import { useEscapeDismiss } from '../hooks/useEscapeDismiss'
|
import { useEscapeDismiss } from '../hooks/useEscapeDismiss'
|
||||||
import { usePipKeyboardShortcuts } from '../hooks/usePipKeyboardShortcuts'
|
import { usePipKeyboardShortcuts } from '../hooks/usePipKeyboardShortcuts'
|
||||||
@@ -17,12 +20,20 @@ import { PipConnectionStateToast } from './notifications/PipConnectionStateToast
|
|||||||
import { PipReactionPortals } from './PipReactionPortals'
|
import { PipReactionPortals } from './PipReactionPortals'
|
||||||
|
|
||||||
export const PipView = () => {
|
export const PipView = () => {
|
||||||
|
return (
|
||||||
|
<SidePanelStoreProvider value={pipLayoutStore}>
|
||||||
|
<PipViewContent />
|
||||||
|
</SidePanelStoreProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const PipViewContent = () => {
|
||||||
const browserSupportsScreenSharing = supportsScreenSharing()
|
const browserSupportsScreenSharing = supportsScreenSharing()
|
||||||
const { t } = useTranslation('rooms', {
|
const { t } = useTranslation('rooms', {
|
||||||
keyPrefix: 'options.items.pictureInPicture',
|
keyPrefix: 'options.items.pictureInPicture',
|
||||||
})
|
})
|
||||||
const containerRef = useRef<HTMLDivElement>(null)
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
const { isSidePanelOpen, closePanel } = useSidePanel(pipLayoutStore)
|
const { isSidePanelOpen, closePanel } = useSidePanel()
|
||||||
|
|
||||||
// Escape closes the side panel instead of the whole PiP window.
|
// Escape closes the side panel instead of the whole PiP window.
|
||||||
useEscapeDismiss(containerRef, isSidePanelOpen, closePanel)
|
useEscapeDismiss(containerRef, isSidePanelOpen, closePanel)
|
||||||
@@ -53,7 +64,7 @@ export const PipView = () => {
|
|||||||
<PipStage />
|
<PipStage />
|
||||||
<PipReactionsToolbar />
|
<PipReactionsToolbar />
|
||||||
<PipControlBar showScreenShare={browserSupportsScreenSharing} />
|
<PipControlBar showScreenShare={browserSupportsScreenSharing} />
|
||||||
<SidePanel store={pipLayoutStore} />
|
<SidePanel />
|
||||||
<PipReactionPortals />
|
<PipReactionPortals />
|
||||||
<OverlayStack>
|
<OverlayStack>
|
||||||
<PipConnectionStateToast />
|
<PipConnectionStateToast />
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { FeedbackMenuItem } from '@/features/rooms/livekit/components/controls/O
|
|||||||
import { EffectsMenuItem } from '@/features/rooms/livekit/components/controls/Options/EffectsMenuItem'
|
import { EffectsMenuItem } from '@/features/rooms/livekit/components/controls/Options/EffectsMenuItem'
|
||||||
import { SupportMenuItem } from '@/features/rooms/livekit/components/controls/Options/SupportMenuItem'
|
import { SupportMenuItem } from '@/features/rooms/livekit/components/controls/Options/SupportMenuItem'
|
||||||
import { PictureInPictureMenuItem } from '@/features/rooms/livekit/components/controls/Options/PictureInPictureMenuItem'
|
import { PictureInPictureMenuItem } from '@/features/rooms/livekit/components/controls/Options/PictureInPictureMenuItem'
|
||||||
import { pipLayoutStore } from '@/features/pip/stores/pipLayoutStore'
|
|
||||||
import { PipOverflowItems } from './PipOverflowItems'
|
import { PipOverflowItems } from './PipOverflowItems'
|
||||||
import type { CollapsibleControl } from '../PipControlBar'
|
import type { CollapsibleControl } from '../PipControlBar'
|
||||||
|
|
||||||
@@ -34,7 +33,7 @@ export const PipOptionsMenuItems = ({
|
|||||||
)}
|
)}
|
||||||
<MenuSection>
|
<MenuSection>
|
||||||
<PictureInPictureMenuItem />
|
<PictureInPictureMenuItem />
|
||||||
<EffectsMenuItem store={pipLayoutStore} />
|
<EffectsMenuItem />
|
||||||
</MenuSection>
|
</MenuSection>
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuSection>
|
<MenuSection>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { memo } from 'react'
|
|||||||
import type { TrackReferenceOrPlaceholder } from '@livekit/components-core'
|
import type { TrackReferenceOrPlaceholder } from '@livekit/components-core'
|
||||||
import { styled } from '@/styled-system/jsx'
|
import { styled } from '@/styled-system/jsx'
|
||||||
import { ParticipantTile } from '@/features/rooms/livekit/components/ParticipantTile'
|
import { ParticipantTile } from '@/features/rooms/livekit/components/ParticipantTile'
|
||||||
import { pipLayoutStore } from '../../stores/pipLayoutStore'
|
|
||||||
import { getTrackKey } from '../../utils/pipTrackSelection'
|
import { getTrackKey } from '../../utils/pipTrackSelection'
|
||||||
|
|
||||||
type PipFocusLayoutProps = {
|
type PipFocusLayoutProps = {
|
||||||
@@ -26,7 +25,6 @@ export const PipFocusLayout = memo(
|
|||||||
<ParticipantTile
|
<ParticipantTile
|
||||||
key={getTrackKey(mainTrack)}
|
key={getTrackKey(mainTrack)}
|
||||||
trackRef={mainTrack}
|
trackRef={mainTrack}
|
||||||
sidePanelStore={pipLayoutStore}
|
|
||||||
/>
|
/>
|
||||||
</MainSlot>
|
</MainSlot>
|
||||||
{thumbnailTrack && (
|
{thumbnailTrack && (
|
||||||
@@ -34,7 +32,6 @@ export const PipFocusLayout = memo(
|
|||||||
<ParticipantTile
|
<ParticipantTile
|
||||||
key={getTrackKey(thumbnailTrack)}
|
key={getTrackKey(thumbnailTrack)}
|
||||||
trackRef={thumbnailTrack}
|
trackRef={thumbnailTrack}
|
||||||
sidePanelStore={pipLayoutStore}
|
|
||||||
/>
|
/>
|
||||||
</Thumbnail>
|
</Thumbnail>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { memo, useMemo, useRef } from 'react'
|
|||||||
import type { TrackReferenceOrPlaceholder } from '@livekit/components-core'
|
import type { TrackReferenceOrPlaceholder } from '@livekit/components-core'
|
||||||
import { styled } from '@/styled-system/jsx'
|
import { styled } from '@/styled-system/jsx'
|
||||||
import { ParticipantTile } from '@/features/rooms/livekit/components/ParticipantTile'
|
import { ParticipantTile } from '@/features/rooms/livekit/components/ParticipantTile'
|
||||||
import { pipLayoutStore } from '../../stores/pipLayoutStore'
|
|
||||||
import { usePipElementSize } from '../../hooks/usePipElementSize'
|
import { usePipElementSize } from '../../hooks/usePipElementSize'
|
||||||
import { usePipFlipAnimations } from '../../hooks/usePipFlipAnimations'
|
import { usePipFlipAnimations } from '../../hooks/usePipFlipAnimations'
|
||||||
import { computePipGridLayout } from '../../utils/pipGrid'
|
import { computePipGridLayout } from '../../utils/pipGrid'
|
||||||
@@ -47,7 +46,7 @@ export const PipGridLayout = memo(({ tracks }: PipGridLayoutProps) => {
|
|||||||
<GridContainer ref={containerRef} style={gridStyle}>
|
<GridContainer ref={containerRef} style={gridStyle}>
|
||||||
{tracks.map((track, index) => (
|
{tracks.map((track, index) => (
|
||||||
<GridCell key={tileKeys[index]} style={placements[index]}>
|
<GridCell key={tileKeys[index]} style={placements[index]}>
|
||||||
<ParticipantTile trackRef={track} sidePanelStore={pipLayoutStore} />
|
<ParticipantTile trackRef={track} />
|
||||||
</GridCell>
|
</GridCell>
|
||||||
))}
|
))}
|
||||||
</GridContainer>
|
</GridContainer>
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import { HStack } from '@/styled-system/jsx'
|
|||||||
import { MutedMicIndicator } from './MutedMicIndicator'
|
import { MutedMicIndicator } from './MutedMicIndicator'
|
||||||
import { ParticipantPlaceholder } from './ParticipantPlaceholder'
|
import { ParticipantPlaceholder } from './ParticipantPlaceholder'
|
||||||
import { ParticipantTileFocus } from './ParticipantTileFocus'
|
import { ParticipantTileFocus } from './ParticipantTileFocus'
|
||||||
import type { SidePanelStore } from '../hooks/useSidePanel'
|
|
||||||
import { FullScreenShareWarning } from './FullScreenShareWarning'
|
import { FullScreenShareWarning } from './FullScreenShareWarning'
|
||||||
import { ParticipantName } from './ParticipantName'
|
import { ParticipantName } from './ParticipantName'
|
||||||
import { getParticipantName } from '@/features/rooms/utils/getParticipantName'
|
import { getParticipantName } from '@/features/rooms/utils/getParticipantName'
|
||||||
@@ -53,7 +52,6 @@ export function TrackRefContextIfNeeded(
|
|||||||
|
|
||||||
interface ParticipantTileExtendedProps extends ParticipantTileProps {
|
interface ParticipantTileExtendedProps extends ParticipantTileProps {
|
||||||
disableMetadata?: boolean
|
disableMetadata?: boolean
|
||||||
sidePanelStore?: SidePanelStore
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ParticipantTile: (
|
export const ParticipantTile: (
|
||||||
@@ -68,7 +66,6 @@ export const ParticipantTile: (
|
|||||||
onParticipantClick,
|
onParticipantClick,
|
||||||
disableSpeakingIndicator,
|
disableSpeakingIndicator,
|
||||||
disableMetadata,
|
disableMetadata,
|
||||||
sidePanelStore,
|
|
||||||
...htmlProps
|
...htmlProps
|
||||||
}: ParticipantTileExtendedProps,
|
}: ParticipantTileExtendedProps,
|
||||||
ref
|
ref
|
||||||
@@ -238,7 +235,6 @@ export const ParticipantTile: (
|
|||||||
<ParticipantTileFocus
|
<ParticipantTileFocus
|
||||||
trackRef={trackReference}
|
trackRef={trackReference}
|
||||||
hasKeyboardFocus={hasKeyboardFocus}
|
hasKeyboardFocus={hasKeyboardFocus}
|
||||||
sidePanelStore={sidePanelStore}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</ParticipantContextIfNeeded>
|
</ParticipantContextIfNeeded>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { TrackReferenceOrPlaceholder } from '@livekit/components-core'
|
import { TrackReferenceOrPlaceholder } from '@livekit/components-core'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { type SidePanelStore, useSidePanel } from '../hooks/useSidePanel'
|
import { useSidePanel } from '../hooks/useSidePanel'
|
||||||
import { useFullScreen } from '../hooks/useFullScreen'
|
import { useFullScreen } from '../hooks/useFullScreen'
|
||||||
import { Participant, Track } from 'livekit-client'
|
import { Participant, Track } from 'livekit-client'
|
||||||
import { MuteAlertDialog } from './MuteAlertDialog'
|
import { MuteAlertDialog } from './MuteAlertDialog'
|
||||||
@@ -74,9 +74,9 @@ const FocusButton = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const EffectsButton = ({ store }: { store?: SidePanelStore }) => {
|
const EffectsButton = () => {
|
||||||
const { t } = useTranslation('rooms', { keyPrefix: 'participantTileFocus' })
|
const { t } = useTranslation('rooms', { keyPrefix: 'participantTileFocus' })
|
||||||
const { isEffectsOpen, toggleEffects } = useSidePanel(store)
|
const { isEffectsOpen, toggleEffects } = useSidePanel()
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
@@ -132,11 +132,9 @@ const MOUSE_IDLE_TIME = 3000
|
|||||||
export const ParticipantTileFocus = ({
|
export const ParticipantTileFocus = ({
|
||||||
trackRef,
|
trackRef,
|
||||||
hasKeyboardFocus,
|
hasKeyboardFocus,
|
||||||
sidePanelStore,
|
|
||||||
}: {
|
}: {
|
||||||
trackRef: TrackReferenceOrPlaceholder
|
trackRef: TrackReferenceOrPlaceholder
|
||||||
hasKeyboardFocus: boolean
|
hasKeyboardFocus: boolean
|
||||||
sidePanelStore?: SidePanelStore
|
|
||||||
}) => {
|
}) => {
|
||||||
const [hovered, setHovered] = useState(false)
|
const [hovered, setHovered] = useState(false)
|
||||||
const [opacity, setOpacity] = useState(0)
|
const [opacity, setOpacity] = useState(0)
|
||||||
@@ -218,7 +216,7 @@ export const ParticipantTileFocus = ({
|
|||||||
{!isScreenShare ? (
|
{!isScreenShare ? (
|
||||||
<>
|
<>
|
||||||
{participant.isLocal ? (
|
{participant.isLocal ? (
|
||||||
<EffectsButton store={sidePanelStore} />
|
<EffectsButton />
|
||||||
) : (
|
) : (
|
||||||
canMute && <MuteButton participant={participant} />
|
canMute && <MuteButton participant={participant} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Button, Div } from '@/primitives'
|
|||||||
import { RiArrowLeftLine, RiCloseLine } from '@remixicon/react'
|
import { RiArrowLeftLine, RiCloseLine } from '@remixicon/react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { ParticipantsList } from './controls/Participants/ParticipantsList'
|
import { ParticipantsList } from './controls/Participants/ParticipantsList'
|
||||||
import { type SidePanelStore, useSidePanel } from '../hooks/useSidePanel'
|
import { useSidePanel } from '../hooks/useSidePanel'
|
||||||
import { ReactNode } from 'react'
|
import { ReactNode } from 'react'
|
||||||
import { Chat } from '../prefabs/Chat'
|
import { Chat } from '../prefabs/Chat'
|
||||||
import { Effects } from './effects/Effects'
|
import { Effects } from './effects/Effects'
|
||||||
@@ -143,7 +143,7 @@ const Panel = ({ isOpen, keepAlive = false, children }: PanelProps) => (
|
|||||||
{keepAlive || isOpen ? children : null}
|
{keepAlive || isOpen ? children : null}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
export const SidePanel = ({ store }: { store?: SidePanelStore }) => {
|
export const SidePanel = () => {
|
||||||
const {
|
const {
|
||||||
activePanelId,
|
activePanelId,
|
||||||
isParticipantsOpen,
|
isParticipantsOpen,
|
||||||
@@ -157,7 +157,7 @@ export const SidePanel = ({ store }: { store?: SidePanelStore }) => {
|
|||||||
activeSubPanelId,
|
activeSubPanelId,
|
||||||
closePanel,
|
closePanel,
|
||||||
goBack,
|
goBack,
|
||||||
} = useSidePanel(store)
|
} = useSidePanel()
|
||||||
const { t } = useTranslation('rooms', { keyPrefix: 'sidePanel' })
|
const { t } = useTranslation('rooms', { keyPrefix: 'sidePanel' })
|
||||||
const title = t(`heading.${activeSubPanelId || activePanelId}`)
|
const title = t(`heading.${activeSubPanelId || activePanelId}`)
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -2,11 +2,11 @@ import { RiImageCircleAiFill } from '@remixicon/react'
|
|||||||
import { MenuItem } from 'react-aria-components'
|
import { MenuItem } from 'react-aria-components'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { menuRecipe } from '@/primitives/menuRecipe'
|
import { menuRecipe } from '@/primitives/menuRecipe'
|
||||||
import { type SidePanelStore, useSidePanel } from '../../../hooks/useSidePanel'
|
import { useSidePanel } from '../../../hooks/useSidePanel'
|
||||||
|
|
||||||
export const EffectsMenuItem = ({ store }: { store?: SidePanelStore }) => {
|
export const EffectsMenuItem = () => {
|
||||||
const { t } = useTranslation('rooms', { keyPrefix: 'options.items' })
|
const { t } = useTranslation('rooms', { keyPrefix: 'options.items' })
|
||||||
const { toggleEffects } = useSidePanel(store)
|
const { toggleEffects } = useSidePanel()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { createContext, useContext } from 'react'
|
||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
import { layoutStore } from '@/stores/layout'
|
import { layoutStore } from '@/stores/layout'
|
||||||
import { PanelId, SubPanelId } from '../types/panel'
|
import { PanelId, SubPanelId } from '../types/panel'
|
||||||
@@ -9,7 +10,13 @@ export type SidePanelStore = {
|
|||||||
activeSubPanelId: SubPanelId | null
|
activeSubPanelId: SubPanelId | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useSidePanel = (store: SidePanelStore = layoutStore) => {
|
const SidePanelStoreContext = createContext<SidePanelStore>(layoutStore)
|
||||||
|
|
||||||
|
export const SidePanelStoreProvider = SidePanelStoreContext.Provider
|
||||||
|
|
||||||
|
export const useSidePanel = (store?: SidePanelStore) => {
|
||||||
|
const currentStore = useContext(SidePanelStoreContext)
|
||||||
|
store ??= currentStore
|
||||||
const layoutSnap = useSnapshot(store)
|
const layoutSnap = useSnapshot(store)
|
||||||
const activePanelId = layoutSnap.activePanelId
|
const activePanelId = layoutSnap.activePanelId
|
||||||
const activeSubPanelId = layoutSnap.activeSubPanelId
|
const activeSubPanelId = layoutSnap.activeSubPanelId
|
||||||
|
|||||||
Reference in New Issue
Block a user