diff --git a/src/frontend/src/features/rooms/livekit/components/SidePanel.tsx b/src/frontend/src/features/rooms/livekit/components/SidePanel.tsx new file mode 100644 index 00000000..febf6489 --- /dev/null +++ b/src/frontend/src/features/rooms/livekit/components/SidePanel.tsx @@ -0,0 +1,86 @@ +import { useSnapshot } from 'valtio' +import { layoutStore } from '@/stores/layout' +import { css } from '@/styled-system/css' +import { Heading } from 'react-aria-components' +import { text } from '@/primitives/Text' +import { Box, Button, Div } from '@/primitives' +import { RiCloseLine } from '@remixicon/react' +import { useTranslation } from 'react-i18next' +import { ParticipantsList } from './controls/Participants/ParticipantsList' +import { useWidgetInteraction } from '../hooks/useWidgetInteraction' +import { ReactNode } from 'react' + +type StyledSidePanelProps = { + title: string + children: ReactNode + onClose: () => void + closeButtonTooltip: string +} + +const StyledSidePanel = ({ + title, + children, + onClose, + closeButtonTooltip, +}: StyledSidePanelProps) => ( + + + {title} + +
+ +
+
{children}
+
+) + +export const SidePanel = () => { + const layoutSnap = useSnapshot(layoutStore) + const sidePanel = layoutSnap.sidePanel + + const { isParticipantsOpen } = useWidgetInteraction() + const { t } = useTranslation('rooms', { keyPrefix: 'sidePanel' }) + + if (!sidePanel) { + return + } + + return ( + (layoutStore.sidePanel = null)} + closeButtonTooltip={t('closeButton', { + content: t(`content.${sidePanel}`), + })} + > + {isParticipantsOpen && } + + ) +} diff --git a/src/frontend/src/features/rooms/livekit/components/controls/Participants/ParticipantsList.tsx b/src/frontend/src/features/rooms/livekit/components/controls/Participants/ParticipantsList.tsx index 1144db1b..6c79ee9c 100644 --- a/src/frontend/src/features/rooms/livekit/components/controls/Participants/ParticipantsList.tsx +++ b/src/frontend/src/features/rooms/livekit/components/controls/Participants/ParticipantsList.tsx @@ -1,17 +1,13 @@ import { css } from '@/styled-system/css' import { useParticipants } from '@livekit/components-react' -import { Heading } from 'react-aria-components' -import { Box, Button, Div, H } from '@/primitives' -import { text } from '@/primitives/Text' -import { RiCloseLine } from '@remixicon/react' -import { participantsStore } from '@/stores/participants' +import { Div, H } from '@/primitives' import { useTranslation } from 'react-i18next' import { allParticipantRoomEvents } from '@/features/rooms/livekit/constants/events' -import { ParticipantListItem } from '@/features/rooms/livekit/components/controls/Participants/ParticipantListItem' -import { ParticipantsCollapsableList } from '@/features/rooms/livekit/components/controls/Participants/ParticipantsCollapsableList' -import { HandRaisedListItem } from '@/features/rooms/livekit/components/controls/Participants/HandRaisedListItem' -import { LowerAllHandsButton } from '@/features/rooms/livekit/components/controls/Participants/LowerAllHandsButton' +import { ParticipantListItem } from '../../controls/Participants/ParticipantListItem' +import { ParticipantsCollapsableList } from '../../controls/Participants/ParticipantsCollapsableList' +import { HandRaisedListItem } from '../../controls/Participants/HandRaisedListItem' +import { LowerAllHandsButton } from '../../controls/Participants/LowerAllHandsButton' // TODO: Optimize rendering performance, especially for longer participant lists, even though they are generally short. export const ParticipantsList = () => { @@ -44,76 +40,40 @@ export const ParticipantsList = () => { // TODO - extract inline styling in a centralized styling file, and avoid magic numbers return ( - - + - {t('heading')} - -
- -
-
- - {t('subheading').toUpperCase()} - - {raisedHandParticipants.length > 0 && ( -
- ( - - )} - action={() => ( - - )} - /> -
+ {t('subheading').toUpperCase()} + + {raisedHandParticipants.length > 0 && ( +
+ ( + + )} + action={() => ( + + )} + /> +
+ )} + ( + )} - ( - - )} - /> -
-
+ /> + ) } diff --git a/src/frontend/src/features/rooms/livekit/hooks/useWidgetInteraction.ts b/src/frontend/src/features/rooms/livekit/hooks/useWidgetInteraction.ts index 09f92cab..1c158709 100644 --- a/src/frontend/src/features/rooms/livekit/hooks/useWidgetInteraction.ts +++ b/src/frontend/src/features/rooms/livekit/hooks/useWidgetInteraction.ts @@ -1,23 +1,25 @@ import { useLayoutContext } from '@livekit/components-react' import { useSnapshot } from 'valtio' -import { participantsStore } from '@/stores/participants.ts' +import { layoutStore } from '@/stores/layout' export const useWidgetInteraction = () => { const { dispatch, state } = useLayoutContext().widget - const participantsSnap = useSnapshot(participantsStore) - const isParticipantsOpen = participantsSnap.showParticipants + const layoutSnap = useSnapshot(layoutStore) + const sidePanel = layoutSnap.sidePanel + + const isParticipantsOpen = sidePanel == 'participants' const toggleParticipants = () => { if (dispatch && state?.showChat) { dispatch({ msg: 'toggle_chat' }) } - participantsStore.showParticipants = !isParticipantsOpen + layoutStore.sidePanel = isParticipantsOpen ? null : 'participants' } const toggleChat = () => { if (isParticipantsOpen) { - participantsStore.showParticipants = false + layoutStore.sidePanel = null } if (dispatch) { dispatch({ msg: 'toggle_chat' }) diff --git a/src/frontend/src/features/rooms/livekit/prefabs/VideoConference.tsx b/src/frontend/src/features/rooms/livekit/prefabs/VideoConference.tsx index 5504a370..c78a5632 100644 --- a/src/frontend/src/features/rooms/livekit/prefabs/VideoConference.tsx +++ b/src/frontend/src/features/rooms/livekit/prefabs/VideoConference.tsx @@ -30,11 +30,11 @@ import { import { ControlBar } from './ControlBar' import { styled } from '@/styled-system/jsx' import { cva } from '@/styled-system/css' -import { ParticipantsList } from '@/features/rooms/livekit/components/controls/Participants/ParticipantsList' import { useSnapshot } from 'valtio' -import { participantsStore } from '@/stores/participants' +import { layoutStore } from '@/stores/layout' import { FocusLayout } from '../components/FocusLayout' import { ParticipantTile } from '../components/ParticipantTile' +import { SidePanel } from '../components/SidePanel' import { MainNotificationToast } from '@/features/notifications/MainNotificationToast' const LayoutWrapper = styled( @@ -172,8 +172,8 @@ export function VideoConference({ ]) /* eslint-enable react-hooks/exhaustive-deps */ - const participantsSnap = useSnapshot(participantsStore) - const showParticipants = participantsSnap.showParticipants + const layoutSnap = useSnapshot(layoutStore) + const sidePanel = layoutSnap.sidePanel return (
@@ -223,7 +223,7 @@ export function VideoConference({ messageEncoder={chatMessageEncoder} messageDecoder={chatMessageDecoder} /> - {showParticipants && } + {sidePanel && }
diff --git a/src/frontend/src/locales/de/rooms.json b/src/frontend/src/locales/de/rooms.json index ab9779e0..c06c160f 100644 --- a/src/frontend/src/locales/de/rooms.json +++ b/src/frontend/src/locales/de/rooms.json @@ -73,10 +73,17 @@ "effects": "" } }, + "sidePanel": { + "heading": { + "participants": "" + }, + "content": { + "participants": "" + }, + "closeButton": "" + }, "participants": { - "heading": "", "subheading": "", - "closeButton": "", "contributors": "", "collapsable": { "open": "", diff --git a/src/frontend/src/locales/en/rooms.json b/src/frontend/src/locales/en/rooms.json index 1a4baa71..294d2806 100644 --- a/src/frontend/src/locales/en/rooms.json +++ b/src/frontend/src/locales/en/rooms.json @@ -71,10 +71,17 @@ "effects": "Apply effects" } }, + "sidePanel": { + "heading": { + "participants": "Participants" + }, + "content": { + "participants": "participants" + }, + "closeButton": "Hide {{content}}" + }, "participants": { - "heading": "Participants", "subheading": "In room", - "closeButton": "Hide participants", "you": "You", "contributors": "Contributors", "collapsable": { diff --git a/src/frontend/src/locales/fr/rooms.json b/src/frontend/src/locales/fr/rooms.json index 587164b0..6f2379f3 100644 --- a/src/frontend/src/locales/fr/rooms.json +++ b/src/frontend/src/locales/fr/rooms.json @@ -71,10 +71,17 @@ "effects": "Appliquer des effets" } }, + "sidePanel": { + "heading": { + "participants": "Participants" + }, + "content": { + "participants": "les participants" + }, + "closeButton": "Masquer {{content}}" + }, "participants": { - "heading": "Participants", "subheading": "Dans la réunion", - "closeButton": "Masquer les participants", "you": "Vous", "contributors": "Contributeurs", "collapsable": { diff --git a/src/frontend/src/stores/layout.ts b/src/frontend/src/stores/layout.ts index bee6baed..70cb4e8d 100644 --- a/src/frontend/src/stores/layout.ts +++ b/src/frontend/src/stores/layout.ts @@ -2,8 +2,10 @@ import { proxy } from 'valtio' type State = { showHeader: boolean + sidePanel: 'participants' | null } export const layoutStore = proxy({ showHeader: false, + sidePanel: null, }) diff --git a/src/frontend/src/stores/participants.ts b/src/frontend/src/stores/participants.ts deleted file mode 100644 index 0fdc7ed9..00000000 --- a/src/frontend/src/stores/participants.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { proxy } from 'valtio' - -type State = { - showParticipants: boolean -} - -export const participantsStore = proxy({ - showParticipants: false, -})