🚸(frontend) enhance transcript copywritting

Simplify the instructions. Also offer a way to user to discover
the feature and register as beta users.
This commit is contained in:
lebaudantoine
2025-04-04 18:16:47 +02:00
committed by aleb_the_flash
parent 255da4bf60
commit 37fe23c0f7
6 changed files with 171 additions and 54 deletions

View File

@@ -1,10 +1,7 @@
import { Button, Div, H, Text } from '@/primitives' import { A, Button, Div, LinkButton, Text } from '@/primitives'
import thirdSlide from '@/assets/intro-slider/3_resume.png' import thirdSlide from '@/assets/intro-slider/3_resume.png'
import { css } from '@/styled-system/css' import { css } from '@/styled-system/css'
import { useHasTranscriptAccess } from '../hooks/useHasTranscriptAccess'
import { RiRecordCircleLine, RiStopCircleLine } from '@remixicon/react'
import { useRoomId } from '@/features/rooms/livekit/hooks/useRoomId' import { useRoomId } from '@/features/rooms/livekit/hooks/useRoomId'
import { useRoomContext } from '@livekit/components-react' import { useRoomContext } from '@livekit/components-react'
import { import {
@@ -12,22 +9,34 @@ import {
useStartRecording, useStartRecording,
} from '@/features/rooms/api/startRecording' } from '@/features/rooms/api/startRecording'
import { useStopRecording } from '@/features/rooms/api/stopRecording' import { useStopRecording } from '@/features/rooms/api/stopRecording'
import { useEffect, useState } from 'react' import { useEffect, useMemo, useState } from 'react'
import { RoomEvent } from 'livekit-client' import { RoomEvent } from 'livekit-client'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { NotificationPayload } from '@/features/notifications/NotificationPayload'
import { NotificationType } from '@/features/notifications/NotificationType'
import { useSnapshot } from 'valtio/index'
import {
TranscriptionStatus,
transcriptionStore,
} from '@/stores/transcription.ts'
import { useHasTranscriptAccess } from '../hooks/useHasTranscriptAccess'
import {
BETA_USERS_FORM_URL,
CRISP_HELP_ARTICLE_TRANSCRIPT,
} from '@/utils/constants'
export const Transcript = () => { export const Transcript = () => {
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const { t } = useTranslation('rooms', { keyPrefix: 'transcript' }) const { t } = useTranslation('rooms', { keyPrefix: 'transcript' })
const hasTranscriptAccess = useHasTranscriptAccess() const hasTranscriptAccess = useHasTranscriptAccess()
const roomId = useRoomId() const roomId = useRoomId()
const { mutateAsync: startRecordingRoom } = useStartRecording() const { mutateAsync: startRecordingRoom } = useStartRecording()
const { mutateAsync: stopRecordingRoom } = useStopRecording() const { mutateAsync: stopRecordingRoom } = useStopRecording()
const transcriptionSnap = useSnapshot(transcriptionStore)
const room = useRoomContext() const room = useRoomContext()
useEffect(() => { useEffect(() => {
@@ -40,6 +49,17 @@ export const Transcript = () => {
} }
}, [room]) }, [room])
const notifyParticipant = async (status: NotificationType) => {
const encoder = new TextEncoder()
const payload: NotificationPayload = {
type: status,
}
const data = encoder.encode(JSON.stringify(payload))
await room.localParticipant.publishData(data, {
reliable: true,
})
}
const handleTranscript = async () => { const handleTranscript = async () => {
if (!roomId) { if (!roomId) {
console.warn('No room ID found') console.warn('No room ID found')
@@ -49,8 +69,12 @@ export const Transcript = () => {
setIsLoading(true) setIsLoading(true)
if (room.isRecording) { if (room.isRecording) {
await stopRecordingRoom({ id: roomId }) await stopRecordingRoom({ id: roomId })
await notifyParticipant(NotificationType.TranscriptionStopped)
transcriptionStore.status = TranscriptionStatus.STOPPING
} else { } else {
await startRecordingRoom({ id: roomId, mode: RecordingMode.Transcript }) await startRecordingRoom({ id: roomId, mode: RecordingMode.Transcript })
await notifyParticipant(NotificationType.TranscriptionStarted)
transcriptionStore.status = TranscriptionStatus.STARTING
} }
} catch (error) { } catch (error) {
console.error('Failed to handle transcript:', error) console.error('Failed to handle transcript:', error)
@@ -58,7 +82,13 @@ export const Transcript = () => {
} }
} }
if (!hasTranscriptAccess) return const isDisabled = useMemo(
() =>
isLoading ||
transcriptionSnap.status === TranscriptionStatus.STARTING ||
transcriptionSnap.status === TranscriptionStatus.STOPPING,
[isLoading, transcriptionSnap]
)
return ( return (
<Div <Div
@@ -69,38 +99,98 @@ export const Transcript = () => {
flexDirection="column" flexDirection="column"
alignItems="center" alignItems="center"
> >
<img src={thirdSlide} alt={'wip'} /> <img
{room.isRecording ? ( src={thirdSlide}
alt={''}
className={css({
minHeight: '309px',
marginBottom: '1rem',
})}
/>
{!hasTranscriptAccess ? (
<> <>
<H lvl={2}>{t('stop.heading')}</H> <Text>{t('beta.heading')}</Text>
<Text variant="sm" centered wrap="balance"> <Text
{t('stop.body')} variant="note"
</Text> wrap={'pretty'}
<div className={css({ height: '2rem' })} /> centered
<Button className={css({
isDisabled={isLoading} textStyle: 'sm',
onPress={() => handleTranscript()} marginBottom: '2.5rem',
data-attr="stop-transcript" marginTop: '0.25rem',
})}
> >
<RiStopCircleLine style={{ marginRight: '0.5rem' }} />{' '} {t('beta.body')}{' '}
{t('stop.button')} <A href={CRISP_HELP_ARTICLE_TRANSCRIPT} target="_blank">
</Button> {t('start.linkMore')}
</A>
</Text>
<LinkButton
size="sm"
variant="tertiary"
href={BETA_USERS_FORM_URL}
target="_blank"
>
{t('beta.button')}
</LinkButton>
</> </>
) : ( ) : (
<> <>
<H lvl={2}>{t('start.heading')}</H> {room.isRecording ? (
<Text variant="sm" centered wrap="balance"> <>
{t('start.body')} <Text>{t('stop.heading')}</Text>
</Text> <Text
<div className={css({ height: '2rem' })} /> variant="note"
<Button wrap={'pretty'}
isDisabled={isLoading} centered
onPress={() => handleTranscript()} className={css({
data-attr="start-transcript" textStyle: 'sm',
> marginBottom: '2.5rem',
<RiRecordCircleLine style={{ marginRight: '0.5rem' }} />{' '} marginTop: '0.25rem',
{t('start.button')} })}
</Button> >
{t('stop.body')}
</Text>
<Button
isDisabled={isDisabled}
onPress={() => handleTranscript()}
data-attr="stop-transcript"
size="sm"
variant="tertiary"
>
{t('stop.button')}
</Button>
</>
) : (
<>
<Text>{t('start.heading')}</Text>
<Text
variant="note"
wrap={'pretty'}
centered
className={css({
textStyle: 'sm',
maxWidth: '90%',
marginBottom: '2.5rem',
marginTop: '0.25rem',
})}
>
{t('start.body')} <br />{' '}
<A href={CRISP_HELP_ARTICLE_TRANSCRIPT} target="_blank">
{t('start.linkMore')}
</A>
</Text>
<Button
isDisabled={isDisabled}
onPress={() => handleTranscript()}
data-attr="start-transcript"
size="sm"
variant="tertiary"
>
{t('start.button')}
</Button>
</>
)}
</> </>
)} )}
</Div> </Div>

View File

@@ -199,12 +199,18 @@
"start": { "start": {
"heading": "", "heading": "",
"body": "", "body": "",
"button": "" "button": "",
"linkMore": ""
}, },
"stop": { "stop": {
"heading": "", "heading": "",
"body": "", "body": "",
"button": "" "button": ""
},
"beta": {
"heading": "",
"body": "",
"button": ""
} }
}, },
"admin": { "admin": {

View File

@@ -196,14 +196,20 @@
}, },
"transcript": { "transcript": {
"start": { "start": {
"heading": "Start the Assistant!", "heading": "Transcribe this call",
"body": "The assistant automatically starts recording your meeting audio (limited to 1 hour). At the end, you'll receive a clear and concise summary of the discussion directly via email.", "body": "Automatically transcribe this call and receive the summary in Docs.",
"button": "Start" "button": "Start transcription",
"linkMore": "Learn more"
}, },
"stop": { "stop": {
"heading": "Recording in Progress...", "heading": "Transcription in progress...",
"body": "Your meeting is currently being recorded. You will receive a summary via email once the meeting ends.", "body": "The transcription of your meeting is in progress. You will receive the result by email once the meeting is finished.",
"button": "Stop Recording" "button": "Stop transcription"
},
"beta": {
"heading": "Become a beta tester",
"body": "Record your meeting for later. You will receive a summary by email once the meeting is finished.",
"button": "Sign up"
} }
}, },
"admin": { "admin": {

View File

@@ -196,14 +196,20 @@
}, },
"transcript": { "transcript": {
"start": { "start": {
"heading": "Démarrer l'assistant !", "heading": "Transcrire cet appel",
"body": "L'assistant démarre automatiquement l'enregistrement sonore de votre réunion (limité à 1h). À la fin, vous recevrez un résumé clair et concis des échanges directement par e-mail.", "body": "Transcrivez cet appel automatiquement et recevez le compte rendu dans Docs.",
"button": "Démarrer" "button": "Démarrer la transcription",
"linkMore": "En savoir plus"
}, },
"stop": { "stop": {
"heading": "Enregistrement en cours …", "heading": "Transcription en cours …",
"body": "L'enregistrement de votre réunion est en cours. Vous recevrez un compte-rendu par email une fois la réunion terminée.", "body": "La transcription de votre réunion est en cours. Vous recevrez le resultat par email une fois la réunion terminée.",
"button": "Arrêter l'enregistrement" "button": "Arrêter la transcription"
},
"beta": {
"heading": "Devenez beta testeur",
"body": "Enregistrer votre réunion pour plus tard. Vous recevrez un compte-rendu par email une fois la réunion terminée.",
"button": "Inscrivez-vous"
} }
}, },
"admin": { "admin": {

View File

@@ -196,14 +196,20 @@
}, },
"transcript": { "transcript": {
"start": { "start": {
"heading": "Start de assistent!", "heading": "Transcribeer dit gesprek",
"body": "De assistent begint automatisch de audio van uw vergadering op te nemen (beperkt tot 1 uur). Na afloop krijgt u direct een heldere en beknopte samenvatting van de discussies in uw e-mail.", "body": "Transcribeer dit gesprek automatisch en ontvang het verslag in Docs.",
"button": "Start" "button": "Transcriptie starten",
"linkMore": "Meer informatie"
}, },
"stop": { "stop": {
"heading": "Opname loopt ...", "heading": "Transcriptie bezig...",
"body": "Uw vergadering wordt momenteel opgenomen. U ontvangt een samenvatting via e-mail, zo gauw de vergarding gesloten wordt.", "body": "De transcriptie van uw vergadering is bezig. U ontvangt het resultaat per e-mail zodra de vergadering is afgelopen.",
"button": "Stop met opname" "button": "Transcriptie stoppen"
},
"beta": {
"heading": "Word betatester",
"body": "Neem uw vergadering op voor later. U ontvangt een samenvatting per e-mail zodra de vergadering is afgelopen.",
"button": "Aanmelden"
} }
}, },
"admin": { "admin": {

View File

@@ -6,3 +6,6 @@ export const BETA_USERS_FORM_URL =
export const CRISP_HELP_ARTICLE_MORE_TOOLS = export const CRISP_HELP_ARTICLE_MORE_TOOLS =
'https://lasuite.crisp.help/fr/article/visio-tools-bvxj23' as const 'https://lasuite.crisp.help/fr/article/visio-tools-bvxj23' as const
export const CRISP_HELP_ARTICLE_TRANSCRIPT =
'https://lasuite.crisp.help/fr/article/visio-transcript-1sjq43x' as const