✨(frontend) display host status to meeting participants
Add host identification display for participants using boolean flag from LiveKit token attributes. Currently passes simple boolean but will be refactored to distinguish owner/admin/member roles and host/co-host with different privileges. Security note: attributes are not fully secure as participants can update their own metadata, potentially faking admin status. However, consequences are limited to user confusion without destructive capabilities. Metadata updates currently needed for name changes and hand raising functionality. Plan to remove canUpdateOwnMetadata permission to strengthen security while preserving essential user interaction capabilities.
This commit is contained in:
committed by
aleb_the_flash
parent
1268346405
commit
4793f2fa8f
@@ -1,10 +1,11 @@
|
|||||||
import { css } from '@/styled-system/css'
|
import { css } from '@/styled-system/css'
|
||||||
|
|
||||||
import { HStack } from '@/styled-system/jsx'
|
import { HStack, VStack } from '@/styled-system/jsx'
|
||||||
import { Text } from '@/primitives/Text'
|
import { Text } from '@/primitives/Text'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Avatar } from '@/components/Avatar'
|
import { Avatar } from '@/components/Avatar'
|
||||||
import { getParticipantColor } from '@/features/rooms/utils/getParticipantColor'
|
import { getParticipantColor } from '@/features/rooms/utils/getParticipantColor'
|
||||||
|
import { getParticipantIsRoomAdmin } from '@/features/rooms/utils/getParticipantIsRoomAdmin'
|
||||||
import { Participant, Track } from 'livekit-client'
|
import { Participant, Track } from 'livekit-client'
|
||||||
import { isLocal } from '@/utils/livekit'
|
import { isLocal } from '@/utils/livekit'
|
||||||
import {
|
import {
|
||||||
@@ -102,36 +103,41 @@ export const ParticipantListItem = ({
|
|||||||
>
|
>
|
||||||
<HStack>
|
<HStack>
|
||||||
<Avatar name={name} bgColor={getParticipantColor(participant)} />
|
<Avatar name={name} bgColor={getParticipantColor(participant)} />
|
||||||
<Text
|
<VStack gap={0} alignItems="start">
|
||||||
variant={'sm'}
|
<Text
|
||||||
className={css({
|
sm
|
||||||
userSelect: 'none',
|
|
||||||
cursor: 'default',
|
|
||||||
display: 'flex',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={css({
|
className={css({
|
||||||
whiteSpace: 'nowrap',
|
userSelect: 'none',
|
||||||
overflow: 'hidden',
|
cursor: 'default',
|
||||||
textOverflow: 'ellipsis',
|
display: 'flex',
|
||||||
maxWidth: '120px',
|
|
||||||
display: 'block',
|
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{name}
|
|
||||||
</span>
|
|
||||||
{isLocal(participant) && (
|
|
||||||
<span
|
<span
|
||||||
className={css({
|
className={css({
|
||||||
marginLeft: '.25rem',
|
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
maxWidth: '120px',
|
||||||
|
display: 'block',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
({t('participants.you')})
|
{name}
|
||||||
</span>
|
</span>
|
||||||
|
{isLocal(participant) && (
|
||||||
|
<span
|
||||||
|
className={css({
|
||||||
|
marginLeft: '.25rem',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
({t('participants.you')})
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
{getParticipantIsRoomAdmin(participant) && (
|
||||||
|
<Text variant="xsNote">{t('participants.host')}</Text>
|
||||||
)}
|
)}
|
||||||
</Text>
|
</VStack>
|
||||||
</HStack>
|
</HStack>
|
||||||
<HStack>
|
<HStack>
|
||||||
<MicIndicator participant={participant} />
|
<MicIndicator participant={participant} />
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { Participant } from 'livekit-client'
|
||||||
|
|
||||||
|
export const getParticipantIsRoomAdmin = (
|
||||||
|
participant: Participant
|
||||||
|
): boolean => {
|
||||||
|
const attributes = participant.attributes
|
||||||
|
|
||||||
|
if (!attributes) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return attributes?.room_admin === 'true'
|
||||||
|
}
|
||||||
@@ -426,6 +426,7 @@
|
|||||||
"participants": {
|
"participants": {
|
||||||
"subheading": "Im Raum",
|
"subheading": "Im Raum",
|
||||||
"you": "Du",
|
"you": "Du",
|
||||||
|
"host": "Host",
|
||||||
"contributors": "Mitwirkende",
|
"contributors": "Mitwirkende",
|
||||||
"collapsable": {
|
"collapsable": {
|
||||||
"open": "{{name}}-Liste öffnen",
|
"open": "{{name}}-Liste öffnen",
|
||||||
|
|||||||
@@ -426,6 +426,7 @@
|
|||||||
"participants": {
|
"participants": {
|
||||||
"subheading": "In room",
|
"subheading": "In room",
|
||||||
"you": "You",
|
"you": "You",
|
||||||
|
"host": "Host",
|
||||||
"contributors": "Contributors",
|
"contributors": "Contributors",
|
||||||
"collapsable": {
|
"collapsable": {
|
||||||
"open": "Open {{name}} list",
|
"open": "Open {{name}} list",
|
||||||
|
|||||||
@@ -427,6 +427,7 @@
|
|||||||
"subheading": "Dans la réunion",
|
"subheading": "Dans la réunion",
|
||||||
"you": "Vous",
|
"you": "Vous",
|
||||||
"contributors": "Contributeurs",
|
"contributors": "Contributeurs",
|
||||||
|
"host": "Organisateur de la réunion",
|
||||||
"collapsable": {
|
"collapsable": {
|
||||||
"open": "Ouvrir la liste {{name}}",
|
"open": "Ouvrir la liste {{name}}",
|
||||||
"close": "Fermer la liste {{name}}"
|
"close": "Fermer la liste {{name}}"
|
||||||
|
|||||||
@@ -426,6 +426,7 @@
|
|||||||
"participants": {
|
"participants": {
|
||||||
"subheading": "In de ruimte",
|
"subheading": "In de ruimte",
|
||||||
"you": "U",
|
"you": "U",
|
||||||
|
"host": "Host",
|
||||||
"contributors": "Deelnemers",
|
"contributors": "Deelnemers",
|
||||||
"collapsable": {
|
"collapsable": {
|
||||||
"open": "Open {{name}} lijst",
|
"open": "Open {{name}} lijst",
|
||||||
|
|||||||
Reference in New Issue
Block a user