From 1b2e0ad4311d597a807c746ff4aefd7dd681fd90 Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Mon, 2 Sep 2024 18:14:42 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=B8(frontend)=20introduce=20sections?= =?UTF-8?q?=20in=20participants=20menu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Why? This layout is extensible. This menu will have two sections, to separate in-room participants from the waiting room ones. It's copied from Gmeet User Experience. In-room participants will be divided in sub-lists based on their states (ex: hand raised, …). This User Experience is extensible if in the future with support subroom in a room or whatever. --- .../ParticipantsCollapsableList.tsx | 106 ++++++++++++++++++ .../Participants/ParticipantsList.tsx | 66 +++++------ src/frontend/src/locales/de/rooms.json | 6 + src/frontend/src/locales/en/rooms.json | 6 + src/frontend/src/locales/fr/rooms.json | 6 + 5 files changed, 158 insertions(+), 32 deletions(-) create mode 100644 src/frontend/src/features/rooms/livekit/components/controls/Participants/ParticipantsCollapsableList.tsx diff --git a/src/frontend/src/features/rooms/livekit/components/controls/Participants/ParticipantsCollapsableList.tsx b/src/frontend/src/features/rooms/livekit/components/controls/Participants/ParticipantsCollapsableList.tsx new file mode 100644 index 00000000..66373772 --- /dev/null +++ b/src/frontend/src/features/rooms/livekit/components/controls/Participants/ParticipantsCollapsableList.tsx @@ -0,0 +1,106 @@ +import { useState } from 'react' +import { css } from '@/styled-system/css' +import { ToggleButton } from 'react-aria-components' +import { HStack, styled, VStack } from '@/styled-system/jsx' +import { RiArrowUpSLine } from '@remixicon/react' +import { Participant } from 'livekit-client' +import { useTranslation } from 'react-i18next' + +const ToggleHeader = styled(ToggleButton, { + base: { + minHeight: '40px', //fixme hardcoded value + paddingRight: '.5rem', + cursor: 'pointer', + display: 'flex', + justifyContent: 'space-between', + width: '100%', + alignItems: 'center', + transition: 'background 200ms', + borderTopRadius: '7px', + '&[data-hovered]': { + backgroundColor: '#f5f5f5', + }, + }, +}) + +const Container = styled('div', { + base: { + border: '1px solid #dadce0', + borderRadius: '8px', + margin: '0 .625rem', + }, +}) + +const ListContainer = styled(VStack, { + base: { + borderTop: '1px solid #dadce0', + alignItems: 'start', + overflowY: 'scroll', + overflowX: 'hidden', + minHeight: 0, + flexGrow: 1, + display: 'flex', + paddingY: '0.5rem', + paddingX: '1rem', + gap: 0, + }, +}) + +type ParticipantsCollapsableListProps = { + heading: string + participants: Array + renderParticipant: (participant: Participant) => JSX.Element +} + +export const ParticipantsCollapsableList = ({ + heading, + participants, + renderParticipant, +}: ParticipantsCollapsableListProps) => { + const { t } = useTranslation('rooms') + const [isOpen, setIsOpen] = useState(true) + const label = t(`participants.collapsable.${isOpen ? 'close' : 'open'}`, { + name: heading, + }) + return ( + + setIsOpen(!isOpen)} + style={{ + borderRadius: !isOpen ? '7px' : undefined, + }} + > + +
+ {heading} +
+
{participants?.length || 0}
+
+ +
+ {isOpen && ( + + {participants.map((participant) => renderParticipant(participant))} + + )} +
+ ) +} 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 dd82ba5c..cbcba0c4 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 @@ -2,14 +2,14 @@ import { css } from '@/styled-system/css' import { useParticipants } from '@livekit/components-react' import { Heading } from 'react-aria-components' -import { Box, Button, Div } from '@/primitives' +import { Box, Button, Div, H } from '@/primitives' import { text } from '@/primitives/Text' import { RiCloseLine } from '@remixicon/react' import { participantsStore } from '@/stores/participants' import { useTranslation } from 'react-i18next' import { allParticipantRoomEvents } from '@/features/rooms/livekit/constants/events' import { ParticipantListItem } from '@/features/rooms/livekit/components/controls/Participants/ParticipantListItem' -import { VStack } from '@/styled-system/jsx' +import { ParticipantsCollapsableList } from '@/features/rooms/livekit/components/controls/Participants/ParticipantsCollapsableList' // TODO: Optimize rendering performance, especially for longer participant lists, even though they are generally short. export const ParticipantsList = () => { @@ -39,25 +39,26 @@ export const ParticipantsList = () => { return ( - - {t('participants.heading')}{' '} - - {participants?.length} - + + {t('participants.heading')}
- {sortedParticipants?.length > 0 && ( - - {sortedParticipants.map((participant) => ( - - ))} - - )} + + {t('participants.subheading').toUpperCase()} + + ( + + )} + />
) } diff --git a/src/frontend/src/locales/de/rooms.json b/src/frontend/src/locales/de/rooms.json index 40a49cfc..da63ccd1 100644 --- a/src/frontend/src/locales/de/rooms.json +++ b/src/frontend/src/locales/de/rooms.json @@ -53,7 +53,13 @@ }, "participants": { "heading": "", + "subheading": "", "closeButton": "", + "contributors": "", + "collapsable": { + "open": "", + "close": "" + }, "you": "", "muteParticipant": "", "muteParticipantAlert": { diff --git a/src/frontend/src/locales/en/rooms.json b/src/frontend/src/locales/en/rooms.json index ab668143..b31a7938 100644 --- a/src/frontend/src/locales/en/rooms.json +++ b/src/frontend/src/locales/en/rooms.json @@ -53,8 +53,14 @@ }, "participants": { "heading": "Participants", + "subheading": "In room", "closeButton": "Hide participants", "you": "You", + "contributors": "Contributors", + "collapsable": { + "open": "Open {{name}} list", + "close": "Close {{name}} list" + }, "muteParticipant": "Close the mic of {{name}}", "muteParticipantAlert": { "description": "Mute {{name}} for all participants? {{name}} is the only person who can unmute themselves.", diff --git a/src/frontend/src/locales/fr/rooms.json b/src/frontend/src/locales/fr/rooms.json index 0e719b68..64186e8a 100644 --- a/src/frontend/src/locales/fr/rooms.json +++ b/src/frontend/src/locales/fr/rooms.json @@ -53,8 +53,14 @@ }, "participants": { "heading": "Participants", + "subheading": "Dans la réunion", "closeButton": "Masquer les participants", "you": "Vous", + "contributors": "Contributeurs", + "collapsable": { + "open": "Ouvrir la liste {{name}}", + "close": "Fermer la liste {{name}}" + }, "muteParticipant": "Couper le micro de {{name}}", "muteParticipantAlert": { "description": "Couper le micro de {{name}} pour tous les participants ? {{name}} est la seule personne habilitée à réactiver son micro",