🚸(frontend) refactor participant tile while being hovered
Inspired by GMeet. Make central actions available on a participant tile when a user hover it. This new interactive zone will be extended with more actions and controls.
This commit is contained in:
committed by
aleb_the_flash
parent
0b74cf96f2
commit
17f8ec6319
@@ -1,7 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
AudioTrack,
|
AudioTrack,
|
||||||
ConnectionQualityIndicator,
|
ConnectionQualityIndicator,
|
||||||
FocusToggle,
|
|
||||||
LockLockedIcon,
|
LockLockedIcon,
|
||||||
ParticipantName,
|
ParticipantName,
|
||||||
ParticipantTileProps,
|
ParticipantTileProps,
|
||||||
@@ -23,11 +22,12 @@ import {
|
|||||||
TrackReferenceOrPlaceholder,
|
TrackReferenceOrPlaceholder,
|
||||||
} from '@livekit/components-core'
|
} from '@livekit/components-core'
|
||||||
import { Track } from 'livekit-client'
|
import { Track } from 'livekit-client'
|
||||||
import { ParticipantPlaceholder } from '@/features/rooms/livekit/components/ParticipantPlaceholder'
|
|
||||||
import { RiHand } from '@remixicon/react'
|
import { RiHand } from '@remixicon/react'
|
||||||
import { useRaisedHand } from '@/features/rooms/livekit/hooks/useRaisedHand'
|
import { useRaisedHand } from '../hooks/useRaisedHand'
|
||||||
import { HStack } from '@/styled-system/jsx'
|
import { HStack } from '@/styled-system/jsx'
|
||||||
import { MutedMicIndicator } from '@/features/rooms/livekit/components/MutedMicIndicator'
|
import { MutedMicIndicator } from './MutedMicIndicator'
|
||||||
|
import { ParticipantPlaceholder } from './ParticipantPlaceholder'
|
||||||
|
import { ParticipantTileFocus } from './ParticipantTileFocus'
|
||||||
|
|
||||||
export function TrackRefContextIfNeeded(
|
export function TrackRefContextIfNeeded(
|
||||||
props: React.PropsWithChildren<{
|
props: React.PropsWithChildren<{
|
||||||
@@ -173,7 +173,9 @@ export const ParticipantTile: (
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{!disableMetadata && <FocusToggle trackRef={trackReference} />}
|
{!disableMetadata && (
|
||||||
|
<ParticipantTileFocus trackRef={trackReference} />
|
||||||
|
)}
|
||||||
</ParticipantContextIfNeeded>
|
</ParticipantContextIfNeeded>
|
||||||
</TrackRefContextIfNeeded>
|
</TrackRefContextIfNeeded>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import { css } from '@/styled-system/css'
|
||||||
|
import { HStack } from '@/styled-system/jsx'
|
||||||
|
import { Button } from '@/primitives'
|
||||||
|
import { RiPushpin2Line, RiUnpinLine } from '@remixicon/react'
|
||||||
|
import { useFocusToggle } from '@livekit/components-react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { TrackReferenceOrPlaceholder } from '@livekit/components-core'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
const FocusButton = ({
|
||||||
|
trackRef,
|
||||||
|
}: {
|
||||||
|
trackRef: TrackReferenceOrPlaceholder
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation('rooms', { keyPrefix: 'participantTileFocus' })
|
||||||
|
const { mergedProps, inFocus } = useFocusToggle({
|
||||||
|
trackRef,
|
||||||
|
props: {},
|
||||||
|
})
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="primaryTextDark"
|
||||||
|
square
|
||||||
|
tooltip={inFocus ? t('pin.disable') : t('pin.enable')}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
onPress={(event) => mergedProps?.onClick?.(event as any)}
|
||||||
|
>
|
||||||
|
{inFocus ? <RiUnpinLine /> : <RiPushpin2Line />}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ParticipantTileFocus = ({
|
||||||
|
trackRef,
|
||||||
|
}: {
|
||||||
|
trackRef: TrackReferenceOrPlaceholder
|
||||||
|
}) => {
|
||||||
|
const [hovered, setHovered] = useState(false)
|
||||||
|
const [opacity, setOpacity] = useState(0)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (hovered) {
|
||||||
|
// Wait for next frame to ensure element is mounted
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
setOpacity(0.6)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setOpacity(0)
|
||||||
|
}
|
||||||
|
}, [hovered])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={css({
|
||||||
|
position: 'absolute',
|
||||||
|
left: '0',
|
||||||
|
top: '0',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
})}
|
||||||
|
onMouseEnter={() => setHovered(true)}
|
||||||
|
onMouseLeave={() => setHovered(false)}
|
||||||
|
>
|
||||||
|
{hovered && (
|
||||||
|
<div
|
||||||
|
className={css({
|
||||||
|
backgroundColor: 'primaryDark.50',
|
||||||
|
transition: 'opacity 200ms linear',
|
||||||
|
zIndex: 1,
|
||||||
|
borderRadius: '0.25rem',
|
||||||
|
display: 'flex',
|
||||||
|
_hover: {
|
||||||
|
opacity: '0.95 !important',
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
style={{ opacity }}
|
||||||
|
>
|
||||||
|
<HStack
|
||||||
|
gap={0.5}
|
||||||
|
className={css({
|
||||||
|
padding: '0.5rem',
|
||||||
|
_hover: {
|
||||||
|
opacity: '1 !important',
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<FocusButton trackRef={trackRef} />
|
||||||
|
</HStack>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -189,5 +189,11 @@
|
|||||||
},
|
},
|
||||||
"recording": {
|
"recording": {
|
||||||
"label": ""
|
"label": ""
|
||||||
|
},
|
||||||
|
"participantTileFocus": {
|
||||||
|
"pin": {
|
||||||
|
"enable": "",
|
||||||
|
"disable": ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,5 +188,11 @@
|
|||||||
},
|
},
|
||||||
"recording": {
|
"recording": {
|
||||||
"label": "Recording"
|
"label": "Recording"
|
||||||
|
},
|
||||||
|
"participantTileFocus": {
|
||||||
|
"pin": {
|
||||||
|
"enable": "Pin",
|
||||||
|
"disable": "Unpin"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,5 +188,11 @@
|
|||||||
},
|
},
|
||||||
"recording": {
|
"recording": {
|
||||||
"label": "Enregistrement"
|
"label": "Enregistrement"
|
||||||
|
},
|
||||||
|
"participantTileFocus": {
|
||||||
|
"pin": {
|
||||||
|
"enable": "Épingler",
|
||||||
|
"disable": "Annuler l'épinglage"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user