From 9734df9d5dfcb034bc266133dc56bad18f9527ec Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Mon, 7 Apr 2025 11:44:17 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F(frontend)=20generalize=20rec?= =?UTF-8?q?ording=20state=20badge=20for=20all=20recording=20types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend recording state badge component to work with all types of recordings instead of just transcripts. Create flexible implementation that supports screen recordings and future recording formats while maintaining consistent visual indicators. --- ...StateToast.tsx => RecordingStateBadge.tsx} | 66 +++++++++++++------ .../rooms/livekit/components/Transcript.tsx | 17 ++--- .../rooms/livekit/prefabs/VideoConference.tsx | 4 +- src/frontend/src/locales/de/rooms.json | 8 +++ src/frontend/src/locales/en/rooms.json | 8 +++ src/frontend/src/locales/fr/rooms.json | 8 +++ src/frontend/src/locales/nl/rooms.json | 8 +++ src/frontend/src/stores/recording.ts | 20 ++++++ src/frontend/src/stores/transcription.ts | 16 ----- 9 files changed, 106 insertions(+), 49 deletions(-) rename src/frontend/src/features/rooms/livekit/components/{TranscriptStateToast.tsx => RecordingStateBadge.tsx} (52%) create mode 100644 src/frontend/src/stores/recording.ts delete mode 100644 src/frontend/src/stores/transcription.ts diff --git a/src/frontend/src/features/rooms/livekit/components/TranscriptStateToast.tsx b/src/frontend/src/features/rooms/livekit/components/RecordingStateBadge.tsx similarity index 52% rename from src/frontend/src/features/rooms/livekit/components/TranscriptStateToast.tsx rename to src/frontend/src/features/rooms/livekit/components/RecordingStateBadge.tsx index 23784f8a..138a1d51 100644 --- a/src/frontend/src/features/rooms/livekit/components/TranscriptStateToast.tsx +++ b/src/frontend/src/features/rooms/livekit/components/RecordingStateBadge.tsx @@ -8,22 +8,22 @@ import { Text } from '@/primitives' import { RemoteParticipant, RoomEvent } from 'livekit-client' import { decodeNotificationDataReceived } from '@/features/notifications/utils' import { NotificationType } from '@/features/notifications/NotificationType' -import { TranscriptionStatus, transcriptionStore } from '@/stores/transcription' +import { RecordingStatus, recordingStore } from '@/stores/recording' -export const TranscriptStateToast = () => { +export const RecordingStateBadge = () => { const { t } = useTranslation('rooms', { - keyPrefix: 'recordingBadge.transcript', + keyPrefix: 'recordingBadge', }) const room = useRoomContext() - const transcriptionSnap = useSnapshot(transcriptionStore) + const recordingSnap = useSnapshot(recordingStore) useEffect(() => { - if (room.isRecording) { - transcriptionStore.status = TranscriptionStatus.STARTED + if (room.isRecording && recordingSnap.status == RecordingStatus.STOPPED) { + recordingStore.status = RecordingStatus.ANY_STARTED } // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) + }, [room.isRecording]) useEffect(() => { const handleDataReceived = ( @@ -36,10 +36,16 @@ export const TranscriptStateToast = () => { switch (notification.type) { case NotificationType.TranscriptionStarted: - transcriptionStore.status = TranscriptionStatus.STARTING + recordingStore.status = RecordingStatus.TRANSCRIPT_STARTING break case NotificationType.TranscriptionStopped: - transcriptionStore.status = TranscriptionStatus.STOPPING + recordingStore.status = RecordingStatus.TRANSCRIPT_STOPPING + break + case NotificationType.ScreenRecordingStarted: + recordingStore.status = RecordingStatus.SCREEN_RECORDING_STARTING + break + case NotificationType.ScreenRecordingStopped: + recordingStore.status = RecordingStatus.SCREEN_RECORDING_STOPPING break default: return @@ -47,9 +53,17 @@ export const TranscriptStateToast = () => { } const handleRecordingStatusChanged = (status: boolean) => { - transcriptionStore.status = status - ? TranscriptionStatus.STARTED - : TranscriptionStatus.STOPPED + if (!status) { + recordingStore.status = RecordingStatus.STOPPED + } else if (recordingSnap.status == RecordingStatus.TRANSCRIPT_STARTING) { + recordingStore.status = RecordingStatus.TRANSCRIPT_STARTED + } else if ( + recordingSnap.status == RecordingStatus.SCREEN_RECORDING_STARTING + ) { + recordingStore.status = RecordingStatus.SCREEN_RECORDING_STARTED + } else { + recordingStore.status = RecordingStatus.ANY_STARTED + } } room.on(RoomEvent.DataReceived, handleDataReceived) @@ -59,20 +73,30 @@ export const TranscriptStateToast = () => { room.off(RoomEvent.DataReceived, handleDataReceived) room.off(RoomEvent.RecordingStatusChanged, handleRecordingStatusChanged) } - }, [room]) + }, [room, recordingSnap]) const key = useMemo(() => { - switch (transcriptionSnap.status) { - case TranscriptionStatus.STOPPING: - return 'stopping' - case TranscriptionStatus.STARTING: - return 'starting' + switch (recordingSnap.status) { + case RecordingStatus.TRANSCRIPT_STARTED: + return 'transcript.started' + case RecordingStatus.TRANSCRIPT_STOPPING: + return 'transcript.stopping' + case RecordingStatus.TRANSCRIPT_STARTING: + return 'transcript.starting' + case RecordingStatus.SCREEN_RECORDING_STARTED: + return 'screenRecording.started' + case RecordingStatus.SCREEN_RECORDING_STOPPING: + return 'screenRecording.stopping' + case RecordingStatus.SCREEN_RECORDING_STARTING: + return 'screenRecording.starting' + case RecordingStatus.ANY_STARTED: + return 'any.started' default: - return 'started' + return } - }, [transcriptionSnap]) + }, [recordingSnap]) - if (transcriptionSnap.status == TranscriptionStatus.STOPPED) return + if (!key) return return (
{ const { mutateAsync: startRecordingRoom } = useStartRecording() const { mutateAsync: stopRecordingRoom } = useStopRecording() - const transcriptionSnap = useSnapshot(transcriptionStore) + const recordingSnap = useSnapshot(recordingStore) const room = useRoomContext() @@ -70,11 +67,11 @@ export const Transcript = () => { if (room.isRecording) { await stopRecordingRoom({ id: roomId }) await notifyParticipant(NotificationType.TranscriptionStopped) - transcriptionStore.status = TranscriptionStatus.STOPPING + recordingStore.status = RecordingStatus.TRANSCRIPT_STOPPING } else { await startRecordingRoom({ id: roomId, mode: RecordingMode.Transcript }) await notifyParticipant(NotificationType.TranscriptionStarted) - transcriptionStore.status = TranscriptionStatus.STARTING + recordingStore.status = RecordingStatus.TRANSCRIPT_STARTING } } catch (error) { console.error('Failed to handle transcript:', error) @@ -85,9 +82,9 @@ export const Transcript = () => { const isDisabled = useMemo( () => isLoading || - transcriptionSnap.status === TranscriptionStatus.STARTING || - transcriptionSnap.status === TranscriptionStatus.STOPPING, - [isLoading, transcriptionSnap] + recordingSnap.status === RecordingStatus.TRANSCRIPT_STARTING || + recordingSnap.status === RecordingStatus.TRANSCRIPT_STOPPING, + [isLoading, recordingSnap] ) return ( diff --git a/src/frontend/src/features/rooms/livekit/prefabs/VideoConference.tsx b/src/frontend/src/features/rooms/livekit/prefabs/VideoConference.tsx index af94bce4..9b540aed 100644 --- a/src/frontend/src/features/rooms/livekit/prefabs/VideoConference.tsx +++ b/src/frontend/src/features/rooms/livekit/prefabs/VideoConference.tsx @@ -28,7 +28,7 @@ import { FocusLayout } from '../components/FocusLayout' import { ParticipantTile } from '../components/ParticipantTile' import { SidePanel } from '../components/SidePanel' import { useSidePanel } from '../hooks/useSidePanel' -import { TranscriptStateToast } from '../components/TranscriptStateToast' +import { RecordingStateBadge } from '../components/RecordingStateBadge' import { ScreenShareErrorModal } from '../components/ScreenShareErrorModal' const LayoutWrapper = styled( @@ -231,7 +231,7 @@ export function VideoConference({ ...props }: VideoConferenceProps) { )} - +
) } diff --git a/src/frontend/src/locales/de/rooms.json b/src/frontend/src/locales/de/rooms.json index aecede86..2401c271 100644 --- a/src/frontend/src/locales/de/rooms.json +++ b/src/frontend/src/locales/de/rooms.json @@ -297,6 +297,14 @@ "started": "", "starting": "", "stopping": "" + }, + "screenRecording": { + "started": "", + "starting": "", + "stopping": "" + }, + "any": { + "started": "" } }, "participantTileFocus": { diff --git a/src/frontend/src/locales/en/rooms.json b/src/frontend/src/locales/en/rooms.json index 9d711d41..7681d5e8 100644 --- a/src/frontend/src/locales/en/rooms.json +++ b/src/frontend/src/locales/en/rooms.json @@ -296,6 +296,14 @@ "started": "Transcribing", "starting": "Transcription starting", "stopping": "Transcription stopping" + }, + "screenRecording": { + "started": "Recording in progress", + "starting": "Starting recording", + "stopping": "Stopping recording" + }, + "any": { + "started": "Recording in progress" } }, "participantTileFocus": { diff --git a/src/frontend/src/locales/fr/rooms.json b/src/frontend/src/locales/fr/rooms.json index ad474d00..6f5e0f45 100644 --- a/src/frontend/src/locales/fr/rooms.json +++ b/src/frontend/src/locales/fr/rooms.json @@ -296,6 +296,14 @@ "started": "Transcription en cours", "starting": "Démarrage de la transcription", "stopping": "Arrêt de la transcription" + }, + "screenRecording": { + "started": "Enregistrement en cours", + "starting": "Démarrage de l'enregistrement", + "stopping": "Arrêt de l'enregistrement" + }, + "any": { + "started": "Enregistrement en cours" } }, "participantTileFocus": { diff --git a/src/frontend/src/locales/nl/rooms.json b/src/frontend/src/locales/nl/rooms.json index 4d7791cd..76e04210 100644 --- a/src/frontend/src/locales/nl/rooms.json +++ b/src/frontend/src/locales/nl/rooms.json @@ -296,6 +296,14 @@ "started": "Transcriptie bezig", "starting": "Transcriptie begint", "stopping": "Transcriptie stopt" + }, + "screenRecording": { + "started": "Opname bezig", + "starting": "Opname starten", + "stopping": "Opname stoppen" + }, + "any": { + "started": "Opname bezig" } }, "participantTileFocus": { diff --git a/src/frontend/src/stores/recording.ts b/src/frontend/src/stores/recording.ts new file mode 100644 index 00000000..8ce95600 --- /dev/null +++ b/src/frontend/src/stores/recording.ts @@ -0,0 +1,20 @@ +import { proxy } from 'valtio' + +export enum RecordingStatus { + TRANSCRIPT_STARTING, + TRANSCRIPT_STARTED, + TRANSCRIPT_STOPPING, + STOPPED, + SCREEN_RECORDING_STARTING, + SCREEN_RECORDING_STARTED, + SCREEN_RECORDING_STOPPING, + ANY_STARTED, +} + +type State = { + status: RecordingStatus +} + +export const recordingStore = proxy({ + status: RecordingStatus.STOPPED, +}) diff --git a/src/frontend/src/stores/transcription.ts b/src/frontend/src/stores/transcription.ts deleted file mode 100644 index 1b4b8caf..00000000 --- a/src/frontend/src/stores/transcription.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { proxy } from 'valtio' - -export enum TranscriptionStatus { - STARTING, - STARTED, - STOPPING, - STOPPED, -} - -type State = { - status: TranscriptionStatus -} - -export const transcriptionStore = proxy({ - status: TranscriptionStatus.STOPPED, -})