From 5e1705d25937eb1b91b02bb4b1709f49f005bdea Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Wed, 31 Dec 2025 18:24:24 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=B8(frontend)=20align=20screen=20recor?= =?UTF-8?q?ding=20side=20panel=20ux?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the screen recording side panel to align with the transcription UX, ensuring a more consistent and homogeneous user experience. This commit also introduces a checkbox allowing users to request transcription of the screen recording, which is one of the most requested features. The side panel will be enriched with more information soon, especially once Fichier is integrated for storing recordings, so the destination can be made explicit. More recording settings (layout, quality, etc.) will be introduced in upcoming commits. --- .../components/ScreenRecordingSidePanel.tsx | 282 ++++++++++-------- .../hooks/useIsRecordingTransitioning.ts | 15 - src/frontend/src/features/recording/index.ts | 1 - src/frontend/src/locales/de/rooms.json | 28 +- src/frontend/src/locales/en/rooms.json | 30 +- src/frontend/src/locales/fr/rooms.json | 28 +- src/frontend/src/locales/nl/rooms.json | 32 +- 7 files changed, 213 insertions(+), 203 deletions(-) delete mode 100644 src/frontend/src/features/recording/hooks/useIsRecordingTransitioning.ts diff --git a/src/frontend/src/features/recording/components/ScreenRecordingSidePanel.tsx b/src/frontend/src/features/recording/components/ScreenRecordingSidePanel.tsx index b91b1d52..524bc5c7 100644 --- a/src/frontend/src/features/recording/components/ScreenRecordingSidePanel.tsx +++ b/src/frontend/src/features/recording/components/ScreenRecordingSidePanel.tsx @@ -6,14 +6,17 @@ import { useRoomContext } from '@livekit/components-react' import { RecordingMode, useHasFeatureWithoutAdminRights, - useIsRecordingTransitioning, useStartRecording, useStopRecording, } from '@/features/recording' import { useEffect, useMemo, useState } from 'react' import { ConnectionState, RoomEvent } from 'livekit-client' import { useTranslation } from 'react-i18next' -import { RecordingStatus, recordingStore } from '@/stores/recording' +import { + RecordingLanguage, + RecordingStatus, + recordingStore, +} from '@/stores/recording' import { NotificationType, @@ -28,6 +31,8 @@ import humanizeDuration from 'humanize-duration' import i18n from 'i18next' import { FeatureFlags } from '@/features/analytics/enums' import { NoAccessView } from './NoAccessView' +import { HStack, VStack } from '@/styled-system/jsx' +import { Checkbox } from '@/primitives/Checkbox' export const ScreenRecordingSidePanel = () => { const { data } = useConfig() @@ -37,6 +42,8 @@ export const ScreenRecordingSidePanel = () => { const [isErrorDialogOpen, setIsErrorDialogOpen] = useState('') + const [includeTranscript, setIncludeTranscript] = useState(false) + const hasFeatureWithoutAdminRights = useHasFeatureWithoutAdminRights( RecordingMode.ScreenRecording, FeatureFlags.ScreenRecording @@ -70,7 +77,6 @@ export const ScreenRecordingSidePanel = () => { const room = useRoomContext() const isRoomConnected = room.state == ConnectionState.Connected - const isRecordingTransitioning = useIsRecordingTransitioning() useEffect(() => { const handleRecordingStatusChanged = () => { @@ -90,6 +96,7 @@ export const ScreenRecordingSidePanel = () => { try { setIsLoading(true) if (room.isRecording) { + setIncludeTranscript(false) await stopRecordingRoom({ id: roomId }) recordingStore.status = RecordingStatus.SCREEN_RECORDING_STOPPING await notifyParticipants({ @@ -100,9 +107,17 @@ export const ScreenRecordingSidePanel = () => { room.localParticipant ) } else { + const recordingOptions = { + ...(recordingSnap.language != RecordingLanguage.AUTOMATIC && { + language: recordingSnap.language, + }), + ...(includeTranscript && { transcribe: true }), + } + await startRecordingRoom({ id: roomId, mode: RecordingMode.ScreenRecording, + options: recordingOptions, }) recordingStore.status = RecordingStatus.SCREEN_RECORDING_STARTING await notifyParticipants({ @@ -116,15 +131,6 @@ export const ScreenRecordingSidePanel = () => { } } - const isDisabled = useMemo( - () => - isLoading || - isRecordingTransitioning || - statuses.isAnotherModeStarted || - !isRoomConnected, - [isLoading, isRecordingTransitioning, statuses, isRoomConnected] - ) - if (hasFeatureWithoutAdminRights) { return ( { > {''} - - {statuses.isStarted ? ( - <> - - {t('stop.heading')} - - + + {t('heading')} + + + {data?.recording?.max_duration + ? t('body', { + max_duration: humanizeDuration(data?.recording?.max_duration, { + language: i18n.language, + }), + }) + : t('bodyWithoutMaxDuration')}{' '} + {data?.support?.help_article_recording && ( + + {t('linkMore')} + + )} + + + +
+
- {t('stop.body')} - -
+
- {t('stop.button')} - - - ) : ( - <> - {statuses.isStopping || isPendingToStop ? ( - <> - - {t('stopping.heading')} - - - {t('stopping.body')} - - - - ) : ( - <> - - {t('start.heading')} - - - {t('start.body', { - duration_message: data?.recording?.max_duration - ? t('durationMessage', { - max_duration: humanizeDuration( - data?.recording?.max_duration, - { - language: i18n.language, - } - ), - }) - : '', - })}{' '} - {data?.support?.help_article_recording && ( - - {t('start.linkMore')} - - )} - + {t('details.destination')} +
+
+
+
+ mail +
+
+ {t('details.receiver')} +
+
+ +
+ +
+ + {t('details.transcription')} + +
+ +
+ {statuses.isStopping || isPendingToStop ? ( + + + {t('button.saving')} + + ) : ( + <> + {statuses.isStarted || statuses.isStarting || room.isRecording ? ( - - )} - - )} + ) : ( + + )} + + )} +
{ - const recordingSnap = useSnapshot(recordingStore) - - const transitionalStates = [ - RecordingStatus.TRANSCRIPT_STARTING, - RecordingStatus.TRANSCRIPT_STOPPING, - RecordingStatus.SCREEN_RECORDING_STARTING, - RecordingStatus.SCREEN_RECORDING_STOPPING, - ] - - return transitionalStates.includes(recordingSnap.status) -} diff --git a/src/frontend/src/features/recording/index.ts b/src/frontend/src/features/recording/index.ts index 757ecd96..af1cfa09 100644 --- a/src/frontend/src/features/recording/index.ts +++ b/src/frontend/src/features/recording/index.ts @@ -1,6 +1,5 @@ // hooks export { useIsRecordingModeEnabled } from './hooks/useIsRecordingModeEnabled' -export { useIsRecordingTransitioning } from './hooks/useIsRecordingTransitioning' export { useHasRecordingAccess } from './hooks/useHasRecordingAccess' export { useIsRecordingActive } from './hooks/useIsRecordingActive' export { useHasFeatureWithoutAdminRights } from './hooks/useHasFeatureWithoutAdminRights' diff --git a/src/frontend/src/locales/de/rooms.json b/src/frontend/src/locales/de/rooms.json index 2f03f06b..60c705cd 100644 --- a/src/frontend/src/locales/de/rooms.json +++ b/src/frontend/src/locales/de/rooms.json @@ -354,12 +354,21 @@ } }, "screenRecording": { - "start": { - "heading": "Dieses Gespräch aufzeichnen", - "body": "Zeichne dieses Gespräch auf, um es später anzusehen {{duration_message}}. Du erhältst die Videoaufnahme per E-Mail.", - "button": "Aufzeichnung starten", + "heading": "Diesen Anruf für später aufzeichnen", + "body": "Zeichnen Sie bis zu {{max_duration}} des Meetings auf.", + "bodyWithoutMaxDuration": "Zeichnen Sie Ihr Meeting unbegrenzt auf.", + "linkMore": "Mehr erfahren", + "details": { + "receiver": "Die Aufzeichnung wird an den Organisator und die Mitorganisatoren gesendet.", + "destination": "Diese Aufzeichnung wird vorübergehend auf unseren Servern gespeichert", "loading": "Aufzeichnung wird gestartet", - "linkMore": "Mehr erfahren" + "linkMore": "Mehr erfahren", + "transcription": "Diese Aufzeichnung transkribieren" + }, + "button": { + "start": "Meeting-Aufzeichnung starten", + "stop": "Meeting-Aufzeichnung beenden", + "saving": "Wird gespeichert…" }, "notAdminOrOwner": { "heading": "Zugriff eingeschränkt", @@ -370,15 +379,6 @@ "body": "Nur der Ersteller der Besprechung oder ein Administrator kann die Aufzeichnung starten. Melden Sie sich an, um Ihre Berechtigungen zu überprüfen." } }, - "stopping": { - "heading": "Daten werden gespeichert…", - "body": "Sie können das Meeting verlassen, wenn Sie möchten; die Aufzeichnung wird automatisch beendet." - }, - "stop": { - "heading": "Aufzeichnung läuft…", - "body": "Du erhältst das Ergebnis per E-Mail, sobald die Aufzeichnung abgeschlossen ist.", - "button": "Aufzeichnung stoppen" - }, "alert": { "title": "Aufzeichnung fehlgeschlagen", "body": { diff --git a/src/frontend/src/locales/en/rooms.json b/src/frontend/src/locales/en/rooms.json index f7544691..ecc1daf9 100644 --- a/src/frontend/src/locales/en/rooms.json +++ b/src/frontend/src/locales/en/rooms.json @@ -354,12 +354,21 @@ } }, "screenRecording": { - "start": { - "heading": "Record this call", - "body": "Record this call to watch it later {{duration_message}} and receive the video recording by email.", - "button": "Start recording", - "loading": "Recording starting", - "linkMore": "Learn more" + "heading": "Record this call for later", + "body": "Record up to {{max_duration}} of meeting.", + "bodyWithoutMaxDuration": "Record your meeting without limit.", + "linkMore": "Learn more", + "details": { + "receiver": "The recording will be sent to the host and co-hosts.", + "destination": "This recording will be temporarily stored on our servers", + "loading": "Starting recording", + "linkMore": "Learn more", + "transcription": "Transcribe this recording" + }, + "button": { + "start": "Start recording the meeting", + "stop": "Stop recording the meeting", + "saving": "Saving…" }, "notAdminOrOwner": { "heading": "Restricted Access", @@ -370,15 +379,6 @@ "body": "Only the meeting creator or an admin can start screen recording. Log in to verify your permissions." } }, - "stopping": { - "heading": "Saving your data…", - "body": "You can leave the meeting if you wish; the recording will finish automatically." - }, - "stop": { - "heading": "Recording in progress…", - "body": "You will receive the result by email once the recording is complete.", - "button": "Stop recording" - }, "alert": { "title": "Recording Failed", "body": { diff --git a/src/frontend/src/locales/fr/rooms.json b/src/frontend/src/locales/fr/rooms.json index 85b0e823..566df1a9 100644 --- a/src/frontend/src/locales/fr/rooms.json +++ b/src/frontend/src/locales/fr/rooms.json @@ -354,12 +354,21 @@ } }, "screenRecording": { - "start": { - "heading": "Enregistrer cet appel", - "body": "Enregistrez cet appel pour plus tard {{duration_message}} et recevez l'enregistrement vidéo par mail.", - "button": "Démarrer l'enregistrement", + "heading": "Enregistrez cet appel pour plus tard", + "body": "Enregistrez jusqu'à {{max_duration}} de réunion.", + "bodyWithoutMaxDuration": "Enregistrez votre réunion sans limite.", + "linkMore": "En savoir plus", + "details": { + "receiver": "L'enregistrement sera envoyé à l'organisateur et aux coorganisateurs.", + "destination": "Cet enregistrement sera conservé temporairement sur nos serveurs", "loading": "Démarrage de l'enregistrement", - "linkMore": "En savoir plus" + "linkMore": "En savoir plus", + "transcription": "Transcrire cet enregistrement" + }, + "button": { + "start": "Commencer à enregistrer la réunion", + "stop": "Arrêter d'enregistrer la réunion", + "saving": "Sauvegarde…" }, "notAdminOrOwner": { "heading": "Accès restreint", @@ -370,15 +379,6 @@ "body": "Seul le créateur de la réunion ou un administrateur peut démarrer l'enregistrement. Connectez-vous pour vérifier vos autorisations." } }, - "stopping": { - "heading": "Sauvegarde de vos données…", - "body": "Vous pouvez quitter la réunion si vous le souhaitez, la sauvegarde se terminera automatiquement." - }, - "stop": { - "heading": "Enregistrement en cours …", - "body": "Vous recevrez le resultat par email une fois l'enregistrement terminé.", - "button": "Arrêter l'enregistrement" - }, "alert": { "title": "Échec de l'enregistrement", "body": { diff --git a/src/frontend/src/locales/nl/rooms.json b/src/frontend/src/locales/nl/rooms.json index e83aadc8..02f3dd07 100644 --- a/src/frontend/src/locales/nl/rooms.json +++ b/src/frontend/src/locales/nl/rooms.json @@ -314,7 +314,7 @@ "body": "Transcribeer tot {{max_duration}} aan vergadertijd.", "bodyWithoutMaxDuration": "Transcribeer uw vergadering zonder limiet.", "details": { - "receiver": "Het transcript wordt verzonden naar de organisator en medeorganisatoren.", + "receiver": "Het transcript wordt verzonden naar de host en de co-host.", "destination": "Er wordt een nieuw document aangemaakt op", "destinationUnknown": "Een nieuw document wordt aangemaakt", "language": "Vergadertalen:", @@ -354,12 +354,21 @@ } }, "screenRecording": { - "start": { - "heading": "Dit gesprek opnemen", - "body": "Neem dit gesprek op om het later terug te kijken {{duration_message}}. Je ontvangt de video-opname per e-mail.", - "button": "Opname starten", - "loading": "Opname gestarten", - "linkMore": "Meer informatie" + "heading": "Neem dit gesprek op voor later", + "body": "Neem tot {{max_duration}} van de vergadering op.", + "bodyWithoutMaxDuration": "Neem je vergadering onbeperkt op.", + "linkMore": "Meer informatie", + "details": { + "receiver": "De opname wordt verzonden naar de organisator en co-organisatoren.", + "destination": "Deze opname wordt tijdelijk op onze servers bewaard", + "loading": "Opname wordt gestart", + "linkMore": "Meer informatie", + "transcription": "Transcribeer deze opname" + }, + "button": { + "start": "Start met opnemen van de vergadering", + "stop": "Stop met opnemen van de vergadering", + "saving": "Bezig met opslaan…" }, "notAdminOrOwner": { "heading": "Toegang beperkt", @@ -370,15 +379,6 @@ "body": "Alleen de maker van de vergadering of een beheerder kan de opname starten. Log in om uw machtigingen te controleren." } }, - "stopping": { - "heading": "Uw gegevens worden opgeslagen…", - "body": "U kunt de vergadering verlaten als u dat wilt; de opname wordt automatisch voltooid." - }, - "stop": { - "heading": "Opname bezig …", - "body": "Je ontvangt het resultaat per e-mail zodra de opname is voltooid.", - "button": "Opname stoppen" - }, "alert": { "title": "Opname mislukt", "body": {