From 158ea5adf2ec2ca49365283a8e37c8c1469a6fa4 Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Wed, 15 Jul 2026 16:18:54 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=EF=B8=8F(frontend)=20optimize=20parti?= =?UTF-8?q?cipant=20metadata=20rendering=20on=20raised=20hand?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Push state down into the participant metadata subtree so a raised hand change no longer re-renders the whole metadata block. Use the children-as-props pattern so only the item actually related to the raised hand re-renders when its state changes. This also better encapsulates the color-update logic tied to the raised-hand state. --- .../components/ParticipantMetadata.tsx | 51 +---------- .../components/RaisedHandMetadataWrapper.tsx | 91 +++++++++++++++++++ 2 files changed, 96 insertions(+), 46 deletions(-) create mode 100644 src/frontend/src/features/participantTile/components/RaisedHandMetadataWrapper.tsx diff --git a/src/frontend/src/features/participantTile/components/ParticipantMetadata.tsx b/src/frontend/src/features/participantTile/components/ParticipantMetadata.tsx index 44555dee..48365901 100644 --- a/src/frontend/src/features/participantTile/components/ParticipantMetadata.tsx +++ b/src/frontend/src/features/participantTile/components/ParticipantMetadata.tsx @@ -1,7 +1,3 @@ -import { - useRaisedHand, - useRaisedHandPosition, -} from '@/features/rooms/livekit/hooks/useRaisedHand' import { ConnectionQualityIndicator, LockLockedIcon, @@ -12,8 +8,8 @@ import { import { HStack } from '@/styled-system/jsx' import { Participant } from 'livekit-client' import { MutedMicIndicator } from './MutedMicIndicator' -import { RiHand } from '@remixicon/react' import { ParticipantName } from './ParticipantName' +import { RaisedHandMetadataWrapper } from './RaisedHandMetadataWrapper' export const ParticipantMetadata = ({ participant, @@ -24,52 +20,15 @@ export const ParticipantMetadata = ({ }) => { const { identity, name } = useParticipantInfo({ participant }) const isEncrypted = useIsEncrypted(participant) - const { isHandRaised } = useRaisedHand({ participant }) - const { positionInQueue, firstInQueue } = useRaisedHandPosition({ - participant, - }) return (
{!isScreenShare && } -
- {isHandRaised && !isScreenShare && ( - - {positionInQueue} - - - )} {isScreenShare && (
-
+ diff --git a/src/frontend/src/features/participantTile/components/RaisedHandMetadataWrapper.tsx b/src/frontend/src/features/participantTile/components/RaisedHandMetadataWrapper.tsx new file mode 100644 index 00000000..d1fc77a6 --- /dev/null +++ b/src/frontend/src/features/participantTile/components/RaisedHandMetadataWrapper.tsx @@ -0,0 +1,91 @@ +import React, { ReactNode, RefObject, useEffect, useRef } from 'react' +import { + useRaisedHand, + useRaisedHandPosition, +} from '@/features/rooms/livekit/hooks/useRaisedHand' +import { Participant } from 'livekit-client' +import { RiHand } from '@remixicon/react' + +const PositionInQueue = React.memo( + ({ positionInQueue }: { positionInQueue?: number }) => { + if (!positionInQueue) return + return ( + + {positionInQueue} + + + ) + } +) + +PositionInQueue.displayName = 'PositionInQueue' + +const RaisedHandActiveItem = ({ + participant, + targetRef, +}: { + participant: Participant + targetRef: RefObject +}) => { + const { positionInQueue, firstInQueue } = useRaisedHandPosition({ + participant, + }) + useEffect(() => { + const el = targetRef.current + if (!el) return + + el.style.backgroundColor = firstInQueue ? '#fde047' : 'white' + el.style.color = 'black' + + return () => { + el.style.backgroundColor = '' + el.style.color = '' + } + }, [targetRef, firstInQueue]) + + return +} + +export const RaisedHandMetadataWrapper = ({ + participant, + enabled = true, + children, +}: { + participant: Participant + enabled?: boolean + children: ReactNode +}) => { + const ref = useRef(null) + + const { isHandRaised } = useRaisedHand({ participant }) + const showHand = isHandRaised && enabled + + return ( +
+ {showHand && ( + + )} + {children} +
+ ) +}