(frontend) draft a Mic indicator in participants list

First raw iteration.

Tried replicating Gmeet UX and layout. However, in their ux, even if
action button are disabled, some tooltips explain to the user why they
cannot perform the action. With the default Trigger, it's not easily
feasible. As soon as the button is disabled, there is no way to trigger
the tooltip in an uncontrolled manner.

In upcoming commits I'll arrange layout, and call the remote LiveKit
ServerApi, to perform the mute action.
This commit is contained in:
lebaudantoine
2024-08-28 18:51:44 +02:00
committed by aleb_the_flash
parent 1e140a01b5
commit 790d37569c
4 changed files with 64 additions and 4 deletions

View File

@@ -5,8 +5,64 @@ 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 { Participant, Track } from 'livekit-client'
import { isLocal } from '@/utils/livekit'
import { Button } from 'react-aria-components'
import { ActiveSpeaker } from '@/features/rooms/components/ActiveSpeaker'
import {
useIsSpeaking,
useTrackMutedIndicator,
} from '@livekit/components-react'
import Source = Track.Source
import { RiMicOffLine } from '@remixicon/react'
import { TooltipWrapper } from '@/primitives/TooltipWrapper.tsx'
type MicIndicatorProps = {
participant: Participant
}
const MicIndicator = ({ participant }: MicIndicatorProps) => {
const { t } = useTranslation('rooms')
const { isMuted } = useTrackMutedIndicator({
participant: participant,
source: Source.Microphone,
})
const isSpeaking = useIsSpeaking(participant)
const isDisabled = isLocal(participant) || (!isLocal(participant) && isMuted)
return (
<TooltipWrapper
tooltip={t('participants.muteParticipant', {
name: participant.name || participant.identity,
})}
tooltipType="instant"
>
<Button
isDisabled={isDisabled}
className={css({
padding: '10px',
minWidth: '24px',
minHeight: '24px',
borderRadius: '50%',
backgroundColor: 'transparent',
transition: 'background 200ms',
'&[data-hovered]': {
backgroundColor: '#f5f5f5',
},
'&[data-focused]': {
backgroundColor: '#f5f5f5',
},
})}
onPress={() => !isMuted && console.log(`mute ${participant.identity}`)}
>
{isMuted ? (
<RiMicOffLine color="gray" />
) : (
<ActiveSpeaker isSpeaking={isSpeaking} />
)}
</Button>
</TooltipWrapper>
)
}
type ParticipantListItemProps = {
participant: Participant
@@ -58,6 +114,7 @@ export const ParticipantListItem = ({
</span>
)}
</Text>
<MicIndicator participant={participant} />
</HStack>
)
}

View File

@@ -50,6 +50,7 @@
"participants": {
"heading": "",
"closeButton": "",
"you": ""
"you": "",
"muteParticipant": ""
}
}

View File

@@ -50,6 +50,7 @@
"participants": {
"heading": "Participants",
"closeButton": "Hide participants",
"you": "You"
"you": "You",
"muteParticipant": "Close the mic of {{name}}"
}
}

View File

@@ -50,6 +50,7 @@
"participants": {
"heading": "Participants",
"closeButton": "Masquer les participants",
"you": "Vous"
"you": "Vous",
"muteParticipant": "Couper le micro de {{name}}"
}
}