✨(frontend) introduce dedicated recording download page
Create initial version of dedicated page for recording downloads, linked directly from email notifications sent to users. Implementation is basic but functional, serving as temporary solution until files can be stored in drive storage. Enables recipients to access recordings through direct links.
This commit is contained in:
committed by
aleb_the_flash
parent
06462a55b0
commit
e5af74685e
@@ -13,3 +13,6 @@ export { RecordingMode } from './types'
|
||||
export { RecordingStateToast } from './components/RecordingStateToast'
|
||||
export { TranscriptSidePanel } from './components/TranscriptSidePanel'
|
||||
export { ScreenRecordingSidePanel } from './components/ScreenRecordingSidePanel'
|
||||
|
||||
// routes
|
||||
export { RecordingDownload as RecordingDownloadRoute } from './routes/RecordingDownload'
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { useParams } from 'wouter'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { Center, VStack } from '@/styled-system/jsx'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import fourthSlide from '@/assets/intro-slider/4_record.png'
|
||||
import { mediaUrl } from '@/api/mediaUrl'
|
||||
import { UserAware, useUser } from '@/features/auth'
|
||||
import { Screen } from '@/layout/Screen'
|
||||
import { H, LinkButton, Text } from '@/primitives'
|
||||
import { formatDate } from '@/utils/formatDate'
|
||||
import { ErrorScreen } from '@/components/ErrorScreen'
|
||||
import { LoadingScreen } from '@/components/LoadingScreen'
|
||||
import { fetchRecording } from '../api/fetchRecording'
|
||||
|
||||
export const RecordingDownload = () => {
|
||||
const { t } = useTranslation('recording')
|
||||
const { recordingId } = useParams()
|
||||
const { isLoggedIn } = useUser()
|
||||
|
||||
const { data, isLoading, isError } = useQuery({
|
||||
queryKey: ['recording', recordingId],
|
||||
queryFn: () => fetchRecording({ recordingId }),
|
||||
retry: false,
|
||||
enabled: !!recordingId,
|
||||
})
|
||||
|
||||
if (isLoading || !data) {
|
||||
return <LoadingScreen />
|
||||
}
|
||||
|
||||
if (!isLoggedIn) {
|
||||
return (
|
||||
<ErrorScreen
|
||||
title={t('authentication.title')}
|
||||
body={t('authentication.body')}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
return <ErrorScreen title={t('error.title')} body={t('error.body')} />
|
||||
}
|
||||
|
||||
return (
|
||||
<UserAware>
|
||||
<Screen layout="centered" footer={false}>
|
||||
<Center>
|
||||
<VStack>
|
||||
<img
|
||||
src={fourthSlide}
|
||||
alt={''}
|
||||
className={css({
|
||||
maxHeight: '309px',
|
||||
})}
|
||||
/>
|
||||
<H lvl={1} centered>
|
||||
{t('success.title')}
|
||||
</H>
|
||||
<Text centered margin="md" wrap={'balance'}>
|
||||
<span
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: t('success.body', {
|
||||
room: data.room.name,
|
||||
created_at: formatDate(data.created_at, 'YYYY-MM-DD HH:mm'),
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
</Text>
|
||||
<LinkButton
|
||||
href={mediaUrl(data.key)}
|
||||
download={`${data.room.name}-${formatDate(data.created_at)}`}
|
||||
>
|
||||
{t('success.button')}
|
||||
</LinkButton>
|
||||
</VStack>
|
||||
</Center>
|
||||
</Screen>
|
||||
</UserAware>
|
||||
)
|
||||
}
|
||||
15
src/frontend/src/locales/de/recording.json
Normal file
15
src/frontend/src/locales/de/recording.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"error": {
|
||||
"title": "",
|
||||
"body": ""
|
||||
},
|
||||
"authentication": {
|
||||
"title": "",
|
||||
"body": ""
|
||||
},
|
||||
"success": {
|
||||
"title": "",
|
||||
"body": "",
|
||||
"button": ""
|
||||
}
|
||||
}
|
||||
15
src/frontend/src/locales/en/recording.json
Normal file
15
src/frontend/src/locales/en/recording.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"error": {
|
||||
"title": "Recording unavailable",
|
||||
"body": "This recording could not be found or was deleted after its 7-day validity period."
|
||||
},
|
||||
"authentication": {
|
||||
"title": "Authentication required",
|
||||
"body": "Please log in to access this recording."
|
||||
},
|
||||
"success": {
|
||||
"title": "Your recording is ready!",
|
||||
"body": "Recording of the meeting <b>{{room}}</b> from {{created_at}}.",
|
||||
"button": "Download"
|
||||
}
|
||||
}
|
||||
15
src/frontend/src/locales/fr/recording.json
Normal file
15
src/frontend/src/locales/fr/recording.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"error": {
|
||||
"title": "Enregistrement indisponible",
|
||||
"body": "Cet enregistrement est introuvable ou a été supprimé après sa période de validité de 7 jours."
|
||||
},
|
||||
"authentication": {
|
||||
"title": "Authentification requise",
|
||||
"body": "Veuillez vous connecter pour accéder à cet enregistrement."
|
||||
},
|
||||
"success": {
|
||||
"title": "Votre enregistrement est prêt !",
|
||||
"body": "Enregistrement de la réunion <b>{{room}}</b> du {{created_at}}.",
|
||||
"button": "Télécharger"
|
||||
}
|
||||
}
|
||||
15
src/frontend/src/locales/nl/recording.json
Normal file
15
src/frontend/src/locales/nl/recording.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"error": {
|
||||
"title": "Opname niet beschikbaar",
|
||||
"body": "Deze opname is niet gevonden of is verwijderd na de geldigheidsperiode van 7 dagen."
|
||||
},
|
||||
"authentication": {
|
||||
"title": "Authenticatie vereist",
|
||||
"body": "Log in om toegang te krijgen tot deze opname."
|
||||
},
|
||||
"success": {
|
||||
"title": "Je opname is klaar!",
|
||||
"body": "Opname van de vergadering <b>{{room}}</b> op {{created_at}}.",
|
||||
"button": "Downloaden"
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { AccessibilityRoute } from '@/features/legalsTerms/Accessibility'
|
||||
import { TermsOfServiceRoute } from '@/features/legalsTerms/TermsOfService'
|
||||
import { CreatePopup } from '@/features/sdk/routes/CreatePopup'
|
||||
import { CreateMeetingButton } from '@/features/sdk/routes/CreateMeetingButton'
|
||||
import { RecordingDownloadRoute } from '@/features/recording'
|
||||
|
||||
export const routes: Record<
|
||||
| 'home'
|
||||
@@ -14,7 +15,8 @@ export const routes: Record<
|
||||
| 'accessibility'
|
||||
| 'termsOfService'
|
||||
| 'sdkCreatePopup'
|
||||
| 'sdkCreateButton',
|
||||
| 'sdkCreateButton'
|
||||
| 'recordingDownload',
|
||||
{
|
||||
name: RouteName
|
||||
path: RegExp | string
|
||||
@@ -64,6 +66,14 @@ export const routes: Record<
|
||||
path: '/sdk/create-button',
|
||||
Component: CreateMeetingButton,
|
||||
},
|
||||
recordingDownload: {
|
||||
name: 'recordingDownload',
|
||||
path: new RegExp(
|
||||
`^[/]recording[/](?<recordingId>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$`
|
||||
),
|
||||
to: (recordingId: string) => `/recording/${recordingId.trim()}`,
|
||||
Component: RecordingDownloadRoute,
|
||||
},
|
||||
}
|
||||
|
||||
export type RouteName = keyof typeof routes
|
||||
|
||||
Reference in New Issue
Block a user