Skip to content

Commit 9a7354e

Browse files
committed
♻️(frontend) introduce a recording mutation hook
Mutualize and factorize the recording API error modal in a single place, and extract all recording mutations into a dedicated hook exposing both start and stop actions. This hook is responsible for interacting with the API error dialog when needed. Previously, this logic was duplicated across each side panel; centralizing it clarifies responsibilities and reduces duplication.
1 parent 7ac4be9 commit 9a7354e

10 files changed

Lines changed: 101 additions & 122 deletions

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Button, Dialog, P } from '@/primitives'
2+
import { useTranslation } from 'react-i18next'
3+
import { useSnapshot } from 'valtio'
4+
import { recordingStore } from '@/stores/recording'
5+
6+
export const ErrorAlertDialog = () => {
7+
const recordingSnap = useSnapshot(recordingStore)
8+
const { t } = useTranslation('rooms', {
9+
keyPrefix: 'errorRecordingAlertDialog',
10+
})
11+
12+
return (
13+
<Dialog
14+
isOpen={!!recordingSnap.isErrorDialogOpen}
15+
role="alertdialog"
16+
title={t('title')}
17+
aria-label={t('title')}
18+
>
19+
<P>{t(`body.${recordingSnap.isErrorDialogOpen}`)}</P>
20+
<Button
21+
variant="text"
22+
size="sm"
23+
onPress={() => (recordingStore.isErrorDialogOpen = '')}
24+
>
25+
{t('button')}
26+
</Button>
27+
</Dialog>
28+
)
29+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { LimitReachedAlertDialog } from './LimitReachedAlertDialog'
22
import { RecordingStateToast } from './RecordingStateToast'
3+
import { ErrorAlertDialog } from './ErrorAlertDialog'
34

45
export const RecordingProvider = () => {
56
return (
67
<>
78
<RecordingStateToast />
89
<LimitReachedAlertDialog />
10+
<ErrorAlertDialog />
911
</>
1012
)
1113
}

src/frontend/src/features/recording/components/ScreenRecordingSidePanel.tsx

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
import { A, Button, Dialog, Div, H, P, Text } from '@/primitives'
1+
import { A, Div, H, Text } from '@/primitives'
22

33
import { css } from '@/styled-system/css'
44
import { useRoomId } from '@/features/rooms/livekit/hooks/useRoomId'
55
import { useRoomContext } from '@livekit/components-react'
66
import {
77
RecordingMode,
88
useHasFeatureWithoutAdminRights,
9-
useStartRecording,
10-
useStopRecording,
119
useHumanizeRecordingMaxDuration,
1210
useRecordingStatuses,
1311
} from '@/features/recording'
@@ -28,6 +26,7 @@ import { RowWrapper } from './RowWrapper'
2826
import { VStack } from '@/styled-system/jsx'
2927
import { Checkbox } from '@/primitives/Checkbox'
3028
import { useTranscriptionLanguage } from '@/features/settings'
29+
import { useMutateRecording } from '../hooks/useMutateRecording'
3130

3231
export const ScreenRecordingSidePanel = () => {
3332
const { data } = useConfig()
@@ -36,8 +35,6 @@ export const ScreenRecordingSidePanel = () => {
3635
const keyPrefix = 'screenRecording'
3736
const { t } = useTranslation('rooms', { keyPrefix })
3837

39-
const [isErrorDialogOpen, setIsErrorDialogOpen] = useState('')
40-
4138
const [includeTranscript, setIncludeTranscript] = useState(false)
4239

4340
const hasFeatureWithoutAdminRights = useHasFeatureWithoutAdminRights(
@@ -51,14 +48,8 @@ export const ScreenRecordingSidePanel = () => {
5148

5249
const roomId = useRoomId()
5350

54-
const { mutateAsync: startRecordingRoom, isPending: isPendingToStart } =
55-
useStartRecording({
56-
onError: () => setIsErrorDialogOpen('start'),
57-
})
58-
const { mutateAsync: stopRecordingRoom, isPending: isPendingToStop } =
59-
useStopRecording({
60-
onError: () => setIsErrorDialogOpen('stop'),
61-
})
51+
const { startRecording, isPendingToStart, stopRecording, isPendingToStop } =
52+
useMutateRecording()
6253

6354
const statuses = useRecordingStatuses(RecordingMode.ScreenRecording)
6455

@@ -72,7 +63,7 @@ export const ScreenRecordingSidePanel = () => {
7263
try {
7364
if (statuses.isStarted || statuses.isStarting) {
7465
setIncludeTranscript(false)
75-
await stopRecordingRoom({ id: roomId })
66+
await stopRecording({ id: roomId })
7667

7768
await notifyParticipants({
7869
type: NotificationType.ScreenRecordingStopped,
@@ -89,7 +80,7 @@ export const ScreenRecordingSidePanel = () => {
8980
...(includeTranscript && { transcribe: true }),
9081
}
9182

92-
await startRecordingRoom({
83+
await startRecording({
9384
id: roomId,
9485
mode: RecordingMode.ScreenRecording,
9586
options: recordingOptions,
@@ -194,20 +185,6 @@ export const ScreenRecordingSidePanel = () => {
194185
isPendingToStart={isPendingToStart}
195186
isPendingToStop={isPendingToStop}
196187
/>
197-
<Dialog
198-
isOpen={!!isErrorDialogOpen}
199-
role="alertdialog"
200-
aria-label={t('alert.title')}
201-
>
202-
<P>{t(`alert.body.${isErrorDialogOpen}`)}</P>
203-
<Button
204-
variant="text"
205-
size="sm"
206-
onPress={() => setIsErrorDialogOpen('')}
207-
>
208-
{t('alert.button')}
209-
</Button>
210-
</Dialog>
211188
</Div>
212189
)
213190
}

src/frontend/src/features/recording/components/TranscriptSidePanel.tsx

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
import { A, Button, Dialog, Div, H, P, Text } from '@/primitives'
1+
import { A, Button, Div, H, Text } from '@/primitives'
22

33
import { css } from '@/styled-system/css'
44
import { useRoomId } from '@/features/rooms/livekit/hooks/useRoomId'
55
import { useRoomContext } from '@livekit/components-react'
66
import {
77
RecordingMode,
88
useHasRecordingAccess,
9-
useStartRecording,
10-
useStopRecording,
119
useHasFeatureWithoutAdminRights,
1210
useHumanizeRecordingMaxDuration,
1311
useRecordingStatuses,
@@ -33,6 +31,7 @@ import {
3331
import { NoAccessView } from './NoAccessView'
3432
import { ControlsButton } from './ControlsButton'
3533
import { RowWrapper } from './RowWrapper'
34+
import { useMutateRecording } from '../hooks/useMutateRecording'
3635

3736
export const TranscriptSidePanel = () => {
3837
const { data } = useConfig()
@@ -41,7 +40,6 @@ export const TranscriptSidePanel = () => {
4140
const keyPrefix = 'transcript'
4241
const { t } = useTranslation('rooms', { keyPrefix })
4342

44-
const [isErrorDialogOpen, setIsErrorDialogOpen] = useState('')
4543
const [includeScreenRecording, setIncludeScreenRecording] = useState(false)
4644

4745
const { notifyParticipants } = useNotifyParticipants()
@@ -62,15 +60,8 @@ export const TranscriptSidePanel = () => {
6260

6361
const roomId = useRoomId()
6462

65-
const { mutateAsync: startRecordingRoom, isPending: isPendingToStart } =
66-
useStartRecording({
67-
onError: () => setIsErrorDialogOpen('start'),
68-
})
69-
70-
const { mutateAsync: stopRecordingRoom, isPending: isPendingToStop } =
71-
useStopRecording({
72-
onError: () => setIsErrorDialogOpen('stop'),
73-
})
63+
const { startRecording, isPendingToStart, stopRecording, isPendingToStop } =
64+
useMutateRecording()
7465

7566
const statuses = useRecordingStatuses(RecordingMode.Transcript)
7667

@@ -83,7 +74,7 @@ export const TranscriptSidePanel = () => {
8374
}
8475
try {
8576
if (statuses.isStarted || statuses.isStarting) {
86-
await stopRecordingRoom({ id: roomId })
77+
await stopRecording({ id: roomId })
8778
setIncludeScreenRecording(false)
8879

8980
await notifyParticipants({
@@ -108,7 +99,7 @@ export const TranscriptSidePanel = () => {
10899
}),
109100
}
110101

111-
await startRecordingRoom({
102+
await startRecording({
112103
id: roomId,
113104
mode: recordingMode,
114105
options: recordingOptions,
@@ -251,20 +242,6 @@ export const TranscriptSidePanel = () => {
251242
isPendingToStart={isPendingToStart}
252243
isPendingToStop={isPendingToStop}
253244
/>
254-
<Dialog
255-
isOpen={!!isErrorDialogOpen}
256-
role="alertdialog"
257-
aria-label={t('alert.title')}
258-
>
259-
<P>{t(`alert.body.${isErrorDialogOpen}`)}</P>
260-
<Button
261-
variant="text"
262-
size="sm"
263-
onPress={() => setIsErrorDialogOpen('')}
264-
>
265-
{t('alert.button')}
266-
</Button>
267-
</Dialog>
268245
</Div>
269246
)
270247
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { useStartRecording, useStopRecording } from '@/features/recording'
2+
import { recordingStore } from '@/stores/recording'
3+
4+
export const useMutateRecording = () => {
5+
const { mutateAsync: startRecording, isPending: isPendingToStart } =
6+
useStartRecording({
7+
onError: () => {
8+
recordingStore.isErrorDialogOpen = 'start'
9+
},
10+
})
11+
const { mutateAsync: stopRecording, isPending: isPendingToStop } =
12+
useStopRecording({
13+
onError: () => {
14+
recordingStore.isErrorDialogOpen = 'stop'
15+
},
16+
})
17+
18+
return {
19+
startRecording,
20+
isPendingToStart,
21+
stopRecording,
22+
isPendingToStop,
23+
}
24+
}

src/frontend/src/locales/de/rooms.json

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -344,14 +344,6 @@
344344
"heading": "Sie sind nicht eingeloggt!",
345345
"body": "Sie müssen eingeloggt sein, um diese Funktion zu nutzen. Bitte melden Sie sich an und versuchen Sie es erneut."
346346
}
347-
},
348-
"alert": {
349-
"title": "Transkription fehlgeschlagen",
350-
"body": {
351-
"stop": "Die Transkription konnte nicht gestoppt werden. Bitte versuche es in einem Moment erneut.",
352-
"start": "Die Transkription konnte nicht gestartet werden. Bitte versuche es in einem Moment erneut."
353-
},
354-
"button": "OK"
355347
}
356348
},
357349
"screenRecording": {
@@ -381,16 +373,16 @@
381373
"body": "Sie sind nicht eingeloggt. Bitte melden Sie sich an und versuchen Sie dann erneut, die Aufnahme zu aktivieren."
382374
}
383375
},
384-
"alert": {
385-
"title": "Aufzeichnung fehlgeschlagen",
386-
"body": {
387-
"stop": "Die Aufzeichnung konnte nicht gestoppt werden. Bitte versuche es in einem Moment erneut.",
388-
"start": "Die Aufzeichnung konnte nicht gestartet werden. Bitte versuche es in einem Moment erneut."
389-
},
390-
"button": "OK"
391-
},
392376
"durationMessage": "(begrenzt auf {{max_duration}})"
393377
},
378+
"errorRecordingAlertDialog": {
379+
"title": "Aufzeichnung fehlgeschlagen",
380+
"body": {
381+
"stop": "Die Aufzeichnung konnte nicht gestoppt werden. Bitte versuche es in einem Moment erneut.",
382+
"start": "Die Aufzeichnung konnte nicht gestartet werden. Bitte versuche es in einem Moment erneut."
383+
},
384+
"button": "OK"
385+
},
394386
"admin": {
395387
"description": "Diese Einstellungen für Organisatoren ermöglichen dir die Kontrolle über dein Meeting. Nur Organisatoren haben Zugriff auf diese Optionen.",
396388
"access": {

src/frontend/src/locales/en/rooms.json

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -344,14 +344,6 @@
344344
"heading": "You are not logged in!",
345345
"body": "You must be logged in to use this feature. Please log in, then try again."
346346
}
347-
},
348-
"alert": {
349-
"title": "Transcription Failed",
350-
"body": {
351-
"stop": "We were unable to stop the transcription. Please try again in a moment.",
352-
"start": "We were unable to start the transcription. Please try again in a moment."
353-
},
354-
"button": "OK"
355347
}
356348
},
357349
"screenRecording": {
@@ -381,16 +373,16 @@
381373
"body": "You are not logged in. Please log in and then try to enable recording again."
382374
}
383375
},
384-
"alert": {
385-
"title": "Recording Failed",
386-
"body": {
387-
"stop": "We were unable to stop the recording. Please try again in a moment.",
388-
"start": "We were unable to start the recording. Please try again in a moment."
389-
},
390-
"button": "OK"
391-
},
392376
"durationMessage": "(limited to {{max_duration}}) "
393377
},
378+
"errorRecordingAlertDialog": {
379+
"title": "Recording Failed",
380+
"body": {
381+
"stop": "We were unable to stop the recording. Please try again in a moment.",
382+
"start": "We were unable to start the recording. Please try again in a moment."
383+
},
384+
"button": "OK"
385+
},
394386
"admin": {
395387
"description": "These organizer settings allow you to maintain control of your meeting. Only organizers can access these controls.",
396388
"access": {

src/frontend/src/locales/fr/rooms.json

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -344,14 +344,6 @@
344344
"heading": "Vous n'êtes pas connecté !",
345345
"body": "Vous devez être connecté pour utiliser cette fonctionnalité. Connectez-vous, puis réessayez."
346346
}
347-
},
348-
"alert": {
349-
"title": "Échec de transcription",
350-
"body": {
351-
"stop": "Nous n'avons pas pu stopper la transcription. Veuillez réessayer dans quelques instants.",
352-
"start": "Nous n'avons pas pu démarrer la transcription. Veuillez réessayer dans quelques instants."
353-
},
354-
"button": "OK"
355347
}
356348
},
357349
"screenRecording": {
@@ -381,16 +373,16 @@
381373
"body": "Vous n’êtes pas connecté. Connectez-vous puis réessayez d'activer l'enregistrement."
382374
}
383375
},
384-
"alert": {
385-
"title": "Échec de l'enregistrement",
386-
"body": {
387-
"stop": "Nous n'avons pas pu stopper l'enregistrement. Veuillez réessayer dans quelques instants.",
388-
"start": "Nous n'avons pas pu démarrer l'enregistrement. Veuillez réessayer dans quelques instants."
389-
},
390-
"button": "OK"
391-
},
392376
"durationMessage": "(limité à {{max_duration}}) "
393377
},
378+
"errorRecordingAlertDialog": {
379+
"title": "Échec de l'enregistrement",
380+
"body": {
381+
"stop": "Nous n'avons pas pu stopper l'enregistrement. Veuillez réessayer dans quelques instants.",
382+
"start": "Nous n'avons pas pu démarrer l'enregistrement. Veuillez réessayer dans quelques instants."
383+
},
384+
"button": "OK"
385+
},
394386
"admin": {
395387
"description": "Ces paramètres organisateur vous permettent de garder le contrôle de votre réunion. Seuls les organisateurs peuvent accéder à ces commandes.",
396388
"access": {

0 commit comments

Comments
 (0)