Files
meet/src/frontend/src/features/rooms/livekit/hooks/useRaisedHand.ts
T
lebaudantoine 48c0cb320e ♻️(frontend) encapsulate error tracking behind a telemetry module
Introduce a telemetry module that exposes a `reportError` helper.
Under the hood it forwards errors to PostHog, but the module is the
only place that knows about PostHog.

Replace `console.error` calls used for error reporting with
`reportError`, so the codebase now goes through a single, consistent
API for telemetry.

This normalizes how errors are reported and makes it straightforward
to swap PostHog for another backend later on, without touching every
call site.
2026-08-12 14:52:09 +02:00

91 lines
2.5 KiB
TypeScript

import { Participant, RoomEvent } from 'livekit-client'
import {
useRoomContext,
useParticipantAttribute,
useParticipantInfo,
useRemoteParticipants,
} from '@livekit/components-react'
import { isLocal } from '@/utils/livekit'
import { useMemo } from 'react'
import { useRaiseHand } from '@/features/rooms/api/updateRaiseHand'
import { reportError } from '@/features/analytics/telemetry'
type useRaisedHandProps = {
participant: Participant
}
export function useRaisedHandPosition({ participant }: useRaisedHandProps) {
const room = useRoomContext()
const { identity } = useParticipantInfo({ participant })
const localIdentity = room.localParticipant.identity
const localHandRaisedAt = useParticipantAttribute('handRaisedAt', {
participant: room.localParticipant,
})
const remoteParticipants = useRemoteParticipants({
updateOnlyOn: [RoomEvent.ParticipantAttributesChanged],
})
const raisedHands = useMemo(() => {
const byIdentity = new Map<string, number>()
const add = (id: string, value?: string) => {
const time = new Date(value ?? '').getTime()
if (Number.isNaN(time)) return
const existing = byIdentity.get(id)
if (existing === undefined || time < existing) byIdentity.set(id, time)
}
if (localIdentity) add(localIdentity, localHandRaisedAt)
remoteParticipants.forEach((p) =>
add(p.identity, p.attributes.handRaisedAt)
)
return byIdentity
}, [remoteParticipants, localIdentity, localHandRaisedAt])
const sortedHands = useMemo(
() =>
[...raisedHands.entries()]
.map(([identity, time]) => ({ identity, time }))
.sort(
(a, b) => a.time - b.time || a.identity.localeCompare(b.identity)
),
[raisedHands]
)
const positionInQueue = useMemo(() => {
const index = sortedHands.findIndex((h) => h.identity === identity)
return index === -1 ? undefined : index + 1
}, [sortedHands, identity])
return {
positionInQueue,
firstInQueue: positionInQueue === 1,
}
}
export function useRaisedHand({ participant }: useRaisedHandProps) {
const handRaisedAtAttribute = useParticipantAttribute('handRaisedAt', {
participant,
})
const { raiseHand } = useRaiseHand()
const isHandRaised = !!handRaisedAtAttribute
const toggleRaisedHand = async () => {
if (!isLocal(participant)) return
try {
await raiseHand(!isHandRaised)
} catch (e) {
reportError('generic_failure', e, {
context: 'toggle_raised_hand',
})
}
}
return { isHandRaised, toggleRaisedHand }
}