♻️(frontend) create a ParticipantListItem component
Main update: create a component, which has the responsability of rendering a Participant in the list. I tried gettig closer to a cleaner codebase, with small and well-scoped components. WIP. Formatting participants before rendering the list was way to over complicated, iterating twice on the list was kinda useless. Fixed! Few updates: - Removed capitalizing Participant's name to be consistent with the avatar. This choices mimics GMeet UX. - Duplicated isLocal utils function from LiveKit, which is not exported by their package.
This commit is contained in:
committed by
aleb_the_flash
parent
4ecb7202ec
commit
1e140a01b5
@@ -0,0 +1,63 @@
|
|||||||
|
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'
|
||||||
|
|
||||||
|
type ParticipantListItemProps = {
|
||||||
|
participant: Participant
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ParticipantListItem = ({
|
||||||
|
participant,
|
||||||
|
}: ParticipantListItemProps) => {
|
||||||
|
const { t } = useTranslation('rooms')
|
||||||
|
const name = participant.name || participant.identity
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HStack
|
||||||
|
role="listitem"
|
||||||
|
key={participant.identity}
|
||||||
|
id={participant.identity}
|
||||||
|
className={css({
|
||||||
|
padding: '0.25rem 0',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Avatar name={name} bgColor={getParticipantColor(participant)} />
|
||||||
|
<Text
|
||||||
|
variant={'sm'}
|
||||||
|
className={css({
|
||||||
|
userSelect: 'none',
|
||||||
|
cursor: 'default',
|
||||||
|
display: 'flex',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={css({
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
maxWidth: '120px',
|
||||||
|
display: 'block',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</span>
|
||||||
|
{isLocal(participant) && (
|
||||||
|
<span
|
||||||
|
className={css({
|
||||||
|
marginLeft: '.25rem',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
({t('participants.you')})
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -3,15 +3,13 @@ import { useParticipants } from '@livekit/components-react'
|
|||||||
|
|
||||||
import { Heading } from 'react-aria-components'
|
import { Heading } from 'react-aria-components'
|
||||||
import { Box, Button, Div } from '@/primitives'
|
import { Box, Button, Div } from '@/primitives'
|
||||||
import { HStack, VStack } from '@/styled-system/jsx'
|
import { text } from '@/primitives/Text'
|
||||||
import { Text, text } from '@/primitives/Text'
|
|
||||||
import { RiCloseLine } from '@remixicon/react'
|
import { RiCloseLine } from '@remixicon/react'
|
||||||
import { capitalize } from '@/utils/capitalize'
|
|
||||||
import { participantsStore } from '@/stores/participants'
|
import { participantsStore } from '@/stores/participants'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { allParticipantRoomEvents } from '@/features/rooms/livekit/constants/events'
|
import { allParticipantRoomEvents } from '@/features/rooms/livekit/constants/events'
|
||||||
import { Avatar } from '@/components/Avatar'
|
import { ParticipantListItem } from '@/features/rooms/livekit/components/controls/Participants/ParticipantListItem'
|
||||||
import { getParticipantColor } from '@/features/rooms/utils/getParticipantColor'
|
import { VStack } from '@/styled-system/jsx'
|
||||||
|
|
||||||
// TODO: Optimize rendering performance, especially for longer participant lists, even though they are generally short.
|
// TODO: Optimize rendering performance, especially for longer participant lists, even though they are generally short.
|
||||||
export const ParticipantsList = () => {
|
export const ParticipantsList = () => {
|
||||||
@@ -24,18 +22,16 @@ export const ParticipantsList = () => {
|
|||||||
updateOnlyOn: allParticipantRoomEvents,
|
updateOnlyOn: allParticipantRoomEvents,
|
||||||
})
|
})
|
||||||
|
|
||||||
const formattedParticipants = participants.map((participant) => ({
|
const sortedRemoteParticipants = participants
|
||||||
name: participant.name || participant.identity,
|
|
||||||
id: participant.identity,
|
|
||||||
color: getParticipantColor(participant),
|
|
||||||
}))
|
|
||||||
|
|
||||||
const sortedRemoteParticipants = formattedParticipants
|
|
||||||
.slice(1)
|
.slice(1)
|
||||||
.sort((a, b) => a.name.localeCompare(b.name))
|
.sort((participantA, participantB) => {
|
||||||
|
const nameA = participantA.name || participantA.identity
|
||||||
|
const nameB = participantB.name || participantB.identity
|
||||||
|
return nameA.localeCompare(nameB)
|
||||||
|
})
|
||||||
|
|
||||||
const allParticipants = [
|
const sortedParticipants = [
|
||||||
formattedParticipants[0], // first participant returned by the hook, is always the local one
|
participants[0], // first participant returned by the hook, is always the local one
|
||||||
...sortedRemoteParticipants,
|
...sortedRemoteParticipants,
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -74,7 +70,7 @@ export const ParticipantsList = () => {
|
|||||||
<RiCloseLine />
|
<RiCloseLine />
|
||||||
</Button>
|
</Button>
|
||||||
</Div>
|
</Div>
|
||||||
{participants?.length > 0 && (
|
{sortedParticipants?.length > 0 && (
|
||||||
<VStack
|
<VStack
|
||||||
role="list"
|
role="list"
|
||||||
className={css({
|
className={css({
|
||||||
@@ -87,47 +83,8 @@ export const ParticipantsList = () => {
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{allParticipants.map((participant, index) => (
|
{sortedParticipants.map((participant) => (
|
||||||
<HStack
|
<ParticipantListItem participant={participant} />
|
||||||
role="listitem"
|
|
||||||
key={participant.id}
|
|
||||||
id={participant.id}
|
|
||||||
className={css({
|
|
||||||
padding: '0.25rem 0',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<Avatar name={participant.name} bgColor={participant.color} />
|
|
||||||
<Text
|
|
||||||
variant={'sm'}
|
|
||||||
className={css({
|
|
||||||
userSelect: 'none',
|
|
||||||
cursor: 'default',
|
|
||||||
display: 'flex',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={css({
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
overflow: 'hidden',
|
|
||||||
textOverflow: 'ellipsis',
|
|
||||||
maxWidth: '120px',
|
|
||||||
display: 'block',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{capitalize(participant.name)}
|
|
||||||
</span>
|
|
||||||
{index === 0 && (
|
|
||||||
<span
|
|
||||||
className={css({
|
|
||||||
marginLeft: '.25rem',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
({t('participants.you')})
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
</HStack>
|
|
||||||
))}
|
))}
|
||||||
</VStack>
|
</VStack>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
import { getBrowser, LogLevel, setLogLevel } from 'livekit-client'
|
import {
|
||||||
|
getBrowser,
|
||||||
|
LocalParticipant,
|
||||||
|
LogLevel,
|
||||||
|
Participant,
|
||||||
|
setLogLevel,
|
||||||
|
} from 'livekit-client'
|
||||||
|
|
||||||
export const silenceLiveKitLogs = (shouldSilenceLogs: boolean) => {
|
export const silenceLiveKitLogs = (shouldSilenceLogs: boolean) => {
|
||||||
setLogLevel(shouldSilenceLogs ? LogLevel.silent : LogLevel.debug)
|
setLogLevel(shouldSilenceLogs ? LogLevel.silent : LogLevel.debug)
|
||||||
@@ -15,3 +21,7 @@ export function isChromiumBased(): boolean {
|
|||||||
export function isSafari(): boolean {
|
export function isSafari(): boolean {
|
||||||
return getBrowser()?.name === 'Safari'
|
return getBrowser()?.name === 'Safari'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isLocal(p: Participant) {
|
||||||
|
return p instanceof LocalParticipant
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user