🚸(frontend) notify all participants when recording/transcription stops

Broadcast limit-reached notifications to all room participants, not just
owner, to ensure everyone knows recording has stopped due to duration
limits.
This commit is contained in:
lebaudantoine
2025-07-16 12:11:20 +02:00
committed by aleb_the_flash
parent 7c631bb76f
commit d075d60d19
12 changed files with 47 additions and 21 deletions

View File

@@ -72,29 +72,33 @@ export const MainNotificationToast = () => {
) => {
const notification = decodeNotificationDataReceived(payload)
if (!participant || !notification) return
if (!notification) return
switch (notification.type) {
case NotificationType.ParticipantMuted:
toastQueue.add(
{
participant,
type: NotificationType.ParticipantMuted,
},
{ timeout: NotificationDuration.ALERT }
)
if (participant) {
toastQueue.add(
{
participant,
type: NotificationType.ParticipantMuted,
},
{ timeout: NotificationDuration.ALERT }
)
}
break
case NotificationType.ReactionReceived:
if (notification.data?.emoji)
if (notification.data?.emoji && participant)
handleEmoji(notification.data.emoji, participant)
break
case NotificationType.TranscriptionStarted:
case NotificationType.TranscriptionStopped:
case NotificationType.ScreenRecordingStarted:
case NotificationType.ScreenRecordingStopped:
case NotificationType.TranscriptionLimitReached:
case NotificationType.ScreenRecordingLimitReached:
toastQueue.add(
{
participant,
type: notification.type,
},
{ timeout: NotificationDuration.ALERT }

View File

@@ -19,10 +19,14 @@ export function ToastAnyRecording({ state, ...props }: ToastProps) {
return 'transcript.started'
case NotificationType.TranscriptionStopped:
return 'transcript.stopped'
case NotificationType.TranscriptionLimitReached:
return 'transcript.limitReached'
case NotificationType.ScreenRecordingStarted:
return 'screenRecording.started'
case NotificationType.ScreenRecordingStopped:
return 'screenRecording.stopped'
case NotificationType.ScreenRecordingLimitReached:
return 'screenRecording.limitReached'
default:
return
}
@@ -40,7 +44,7 @@ export function ToastAnyRecording({ state, ...props }: ToastProps) {
gap={0}
>
{t(key, {
name: participant.name,
name: participant?.name,
})}
</HStack>
</StyledToastContainer>

View File

@@ -29,6 +29,9 @@ export function ToastJoined({ state, ...props }: ToastProps) {
)
const layoutContext = useMaybeLayoutContext()
const participant = props.toast.content.participant
if (!participant) return
const trackReference = {
participant,
publication: participant.getTrackPublication(Source.Camera),

View File

@@ -27,7 +27,7 @@ export function ToastMessageReceived({ state, ...props }: ToastProps) {
}
}, [isChatOpen, toast, state])
if (isChatOpen) return null
if (isChatOpen || !participant) return null
return (
<StyledToastContainer {...toastProps} ref={ref}>

View File

@@ -10,6 +10,9 @@ export function ToastMuted({ state, ...props }: ToastProps) {
const ref = useRef(null)
const { toastProps, contentProps } = useToast(props, state, ref)
const participant = props.toast.content.participant
if (!participant) return
return (
<StyledToastContainer {...toastProps} ref={ref}>
<HStack

View File

@@ -5,7 +5,7 @@ import { Participant } from 'livekit-client'
import { NotificationType } from '../NotificationType'
export interface ToastData {
participant: Participant
participant?: Participant
type: NotificationType
message?: string
// eslint-disable-next-line @typescript-eslint/no-explicit-any

View File

@@ -20,6 +20,8 @@ export function ToastRaised({ state, ...props }: ToastProps) {
const participant = props.toast.content.participant
const { isParticipantsOpen, toggleParticipants } = useSidePanel()
if (!participant) return
return (
<StyledToastContainer {...toastProps} ref={ref}>
<HStack

View File

@@ -40,8 +40,10 @@ const renderToast = (
case NotificationType.TranscriptionStarted:
case NotificationType.TranscriptionStopped:
case NotificationType.TranscriptionLimitReached:
case NotificationType.ScreenRecordingStarted:
case NotificationType.ScreenRecordingStopped:
case NotificationType.ScreenRecordingLimitReached:
return <ToastAnyRecording key={toast.key} toast={toast} state={state} />
case NotificationType.RecordingSaving:

View File

@@ -24,11 +24,13 @@
},
"transcript": {
"started": "{{name}} hat die Transkription des Treffens gestartet.",
"stopped": "{{name}} hat die Transkription des Treffens gestoppt."
"stopped": "{{name}} hat die Transkription des Treffens gestoppt.",
"limitReached": "Die Transkription hat die maximal zulässige Dauer überschritten und wird automatisch gespeichert."
},
"screenRecording": {
"started": "{{name}} hat die Aufzeichnung des Treffens gestartet.",
"stopped": "{{name}} hat die Aufzeichnung des Treffens gestoppt."
"stopped": "{{name}} hat die Aufzeichnung des Treffens gestoppt.",
"limitReached": "Die Aufzeichnung hat die maximal zulässige Dauer überschritten und wird automatisch gespeichert."
},
"recordingSave": {
"transcript": {

View File

@@ -24,11 +24,13 @@
},
"transcript": {
"started": "{{name}} started the meeting transcription.",
"stopped": "{{name}} stopped the meeting transcription."
"stopped": "{{name}} stopped the meeting transcription.",
"limitReached": "The transcription has exceeded the maximum allowed duration and will be automatically saved."
},
"screenRecording": {
"started": "{{name}} started the meeting recording.",
"stopped": "{{name}} stopped the meeting recording."
"stopped": "{{name}} stopped the meeting recording.",
"limitReached": "The recording has exceeded the maximum allowed duration and will be automatically saved."
},
"recordingSave": {
"transcript": {

View File

@@ -24,11 +24,13 @@
},
"transcript": {
"started": "{{name}} a démarré la transcription de la réunion.",
"stopped": "{{name}} a arrêté la transcription de la réunion."
"stopped": "{{name}} a arrêté la transcription de la réunion.",
"limitReached": "La transcription a dépassé la durée maximale autorisée, elle va être automatiquement sauvegardée."
},
"screenRecording": {
"started": "{{name}} a démarré l'enregistrement de la réunion.",
"stopped": "{{name}} a arrêté l'enregistrement de la réunion."
"stopped": "{{name}} a arrêté l'enregistrement de la réunion.",
"limitReached": "L'enregistrement a dépassé la durée maximale autorisée, il va être automatiquement sauvegardé."
},
"recordingSave": {
"transcript": {

View File

@@ -24,11 +24,13 @@
},
"transcript": {
"started": "{{name}} is de transcriptie van de vergadering gestart.",
"stopped": "{{name}} heeft de transcriptie van de vergadering gestopt."
"stopped": "{{name}} heeft de transcriptie van de vergadering gestopt.",
"limitReached": "De transcriptie heeft de maximaal toegestane duur overschreden en wordt automatisch opgeslagen."
},
"screenRecording": {
"started": "{{name}} is begonnen met het opnemen van de vergadering.",
"stopped": "{{name}} is gestopt met het opnemen van de vergadering."
"stopped": "{{name}} is gestopt met het opnemen van de vergadering.",
"limitReached": "De opname heeft de maximaal toegestane duur overschreden en wordt automatisch opgeslagen."
},
"recordingSave": {
"transcript": {