️(frontend) size the Avatar with CSS instead of useSize

Stop using the useSize hook to compute the Avatar size in JS. Rely
on CSS to size the Avatar responsively instead.

This removes a set of unnecessary re-renders triggered by the
useSize subscription every time the container resized.
This commit is contained in:
lebaudantoine
2026-07-23 18:24:15 +02:00
parent 6047682fb1
commit df62b37046
2 changed files with 36 additions and 35 deletions
@@ -1,10 +1,7 @@
import type { Participant } from 'livekit-client'
import { styled } from '@/styled-system/jsx' import { styled } from '@/styled-system/jsx'
import { Avatar } from '@/components/Avatar' import { Avatar } from '@/components/Avatar'
import { useIsSpeaking } from '@livekit/components-react'
import { getParticipantBackgroundGradient } from '@/features/rooms/utils/getParticipantBackgroundGradient' import { getParticipantBackgroundGradient } from '@/features/rooms/utils/getParticipantBackgroundGradient'
import { getParticipantColor } from '@/features/rooms/utils/getParticipantColor' import React, { useMemo } from 'react'
import { useMemo } from 'react'
const StyledParticipantPlaceHolder = styled('div', { const StyledParticipantPlaceHolder = styled('div', {
base: { base: {
@@ -23,39 +20,36 @@ const StyledAvatarWrapper = styled('div', {
aspectRatio: '1 / 1', aspectRatio: '1 / 1',
width: 'min(90cqmin, 160px)', width: 'min(90cqmin, 160px)',
fontSize: 'min(27cqmin, 48px)', fontSize: 'min(27cqmin, 48px)',
'[data-lk-speaking="true"] &': {
animation: 'pulse 1s infinite',
},
}, },
}) })
type ParticipantPlaceholderProps = { type ParticipantPlaceholderProps = {
participant: Participant color: string
displayedNamed: string
} }
export const ParticipantPlaceholder = ({ export const ParticipantPlaceholder = React.memo(
participant, ({ color, displayedNamed }: ParticipantPlaceholderProps) => {
}: ParticipantPlaceholderProps) => { const backgroundGradient = useMemo(
const isSpeaking = useIsSpeaking(participant) () => getParticipantBackgroundGradient(color),
const participantColor = getParticipantColor(participant) [color]
const backgroundGradient = useMemo( )
() => getParticipantBackgroundGradient(participantColor), return (
[participantColor] <StyledParticipantPlaceHolder
) style={{
backgroundColor: color,
return ( backgroundImage: backgroundGradient,
<StyledParticipantPlaceHolder }}
style={{
backgroundColor: participantColor,
backgroundImage: backgroundGradient,
}}
>
<StyledAvatarWrapper
style={{ animation: isSpeaking ? 'pulse 1s infinite' : undefined }}
> >
<Avatar <StyledAvatarWrapper>
name={participant.name} <Avatar name={displayedNamed} bgColor={color} context="placeholder" />
bgColor={participantColor} </StyledAvatarWrapper>
context="placeholder" </StyledParticipantPlaceHolder>
/> )
</StyledAvatarWrapper> }
</StyledParticipantPlaceHolder> )
)
} ParticipantPlaceholder.displayName = 'ParticipantPlaceholder'
@@ -1,3 +1,4 @@
import React from 'react'
import { import {
AudioTrack, AudioTrack,
ParticipantTileProps, ParticipantTileProps,
@@ -9,7 +10,6 @@ import {
TrackRefContext, TrackRefContext,
ParticipantContextIfNeeded, ParticipantContextIfNeeded,
} from '@livekit/components-react' } from '@livekit/components-react'
import React from 'react'
import { import {
isEqualTrackRef, isEqualTrackRef,
isTrackReference, isTrackReference,
@@ -26,6 +26,7 @@ import { formatShortcutLabel } from '@/features/shortcuts/formatLabels'
import { KeyboardShortcutHint } from './KeyboardShortcutHint' import { KeyboardShortcutHint } from './KeyboardShortcutHint'
import { layoutStore, clearPinnedTrack } from '@/stores/layout' import { layoutStore, clearPinnedTrack } from '@/stores/layout'
import { ParticipantMetadata } from './ParticipantMetadata' import { ParticipantMetadata } from './ParticipantMetadata'
import { getParticipantColor } from '@/features/rooms/utils/getParticipantColor'
export function TrackRefContextIfNeeded( export function TrackRefContextIfNeeded(
props: React.PropsWithChildren<{ props: React.PropsWithChildren<{
@@ -90,6 +91,8 @@ export const ParticipantTile: (
const isScreenShare = trackReference.source != Track.Source.Camera const isScreenShare = trackReference.source != Track.Source.Camera
const [hasKeyboardFocus, setHasKeyboardFocus] = React.useState(false) const [hasKeyboardFocus, setHasKeyboardFocus] = React.useState(false)
const participantColor = getParticipantColor(trackReference.participant)
const participantName = getParticipantName(trackReference.participant) const participantName = getParticipantName(trackReference.participant)
const { t } = useTranslation('rooms', { keyPrefix: 'participantTileFocus' }) const { t } = useTranslation('rooms', { keyPrefix: 'participantTileFocus' })
@@ -141,7 +144,11 @@ export const ParticipantTile: (
)} )}
<div className="lk-participant-placeholder"> <div className="lk-participant-placeholder">
<ParticipantPlaceholder <ParticipantPlaceholder
participant={trackReference.participant} color={participantColor}
displayedNamed={
trackReference.participant.name ||
trackReference.participant.identity
}
/> />
</div> </div>
{!disableMetadata && ( {!disableMetadata && (