From 584be7e65b7bbd9d8f92a0656c261676a010419b Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Tue, 3 Sep 2024 09:59:21 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(frontend)=20add=20raised=20hands=20wa?= =?UTF-8?q?iting=20room?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Show the raised hand waiting list, allow any participant to lower other participants' hands. Inspired from Gmeet. I found it quite clear to have a dedicated waiting list for raised hands. --- .../rooms/livekit/api/lowerHandParticipant.ts | 33 ++++++++ .../Participants/HandRaisedListItem.tsx | 78 +++++++++++++++++++ .../Participants/ParticipantsList.tsx | 21 +++++ src/frontend/src/locales/de/rooms.json | 4 +- src/frontend/src/locales/en/rooms.json | 4 +- src/frontend/src/locales/fr/rooms.json | 4 +- 6 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 src/frontend/src/features/rooms/livekit/api/lowerHandParticipant.ts create mode 100644 src/frontend/src/features/rooms/livekit/components/controls/Participants/HandRaisedListItem.tsx diff --git a/src/frontend/src/features/rooms/livekit/api/lowerHandParticipant.ts b/src/frontend/src/features/rooms/livekit/api/lowerHandParticipant.ts new file mode 100644 index 00000000..e9d91fe4 --- /dev/null +++ b/src/frontend/src/features/rooms/livekit/api/lowerHandParticipant.ts @@ -0,0 +1,33 @@ +import { Participant } from 'livekit-client' +import { fetchServerApi } from './fetchServerApi' +import { buildServerApiUrl } from './buildServerApiUrl' +import { useRoomData } from '../hooks/useRoomData' + +export const useLowerHandParticipant = () => { + const data = useRoomData() + + const lowerHandParticipant = (participant: Participant) => { + if (!data || !data?.livekit) { + throw new Error('Room data is not available') + } + const newMetadata = JSON.parse(participant.metadata || '{}') + newMetadata.raised = !newMetadata.raised + return fetchServerApi( + buildServerApiUrl( + data.livekit.url, + 'twirp/livekit.RoomService/UpdateParticipant' + ), + data.livekit.token, + { + method: 'POST', + body: JSON.stringify({ + room: data.livekit.room, + identity: participant.identity, + metadata: JSON.stringify(newMetadata), + permission: participant.permissions, + }), + } + ) + } + return { lowerHandParticipant } +} diff --git a/src/frontend/src/features/rooms/livekit/components/controls/Participants/HandRaisedListItem.tsx b/src/frontend/src/features/rooms/livekit/components/controls/Participants/HandRaisedListItem.tsx new file mode 100644 index 00000000..0571d066 --- /dev/null +++ b/src/frontend/src/features/rooms/livekit/components/controls/Participants/HandRaisedListItem.tsx @@ -0,0 +1,78 @@ +import { css } from '@/styled-system/css' + +import { HStack } from '@/styled-system/jsx' +import { Text } from '@/primitives/Text' +import { useTranslation } from 'react-i18next' +import { Avatar } from '@/components/Avatar' +import { getParticipantColor } from '@/features/rooms/utils/getParticipantColor' +import { Participant } from 'livekit-client' +import { isLocal } from '@/utils/livekit' +import { RiHand } from '@remixicon/react' +import { ListItemActionButton } from '@/features/rooms/livekit/components/controls/Participants/ListItemActionButton' +import { useLowerHandParticipant } from '@/features/rooms/livekit/api/lowerHandParticipant.ts' + +type HandRaisedListItemProps = { + participant: Participant +} + +export const HandRaisedListItem = ({ + participant, +}: HandRaisedListItemProps) => { + const { t } = useTranslation('rooms') + const name = participant.name || participant.identity + + const { lowerHandParticipant } = useLowerHandParticipant() + + return ( + + + + + + {name} + + {isLocal(participant) && ( + + ({t('participants.you')}) + + )} + + + lowerHandParticipant(participant)} + tooltip={t('participants.lowerParticipantHand', { name })} + > + + + + ) +} 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 cbcba0c4..16ca082b 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 @@ -10,6 +10,7 @@ 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' // TODO: Optimize rendering performance, especially for longer participant lists, even though they are generally short. export const ParticipantsList = () => { @@ -35,6 +36,11 @@ export const ParticipantsList = () => { ...sortedRemoteParticipants, ] + const raisedHandParticipants = participants.filter((participant) => { + const data = JSON.parse(participant.metadata || '{}') + return data.raised + }) + // TODO - extract inline styling in a centralized styling file, and avoid magic numbers return ( { > {t('participants.subheading').toUpperCase()} + {raisedHandParticipants.length > 0 && ( +
+ ( + + )} + /> +
+ )}