🚸(frontend) share more information about transcription state
Previous toast state was too naive. Enhance message deliver to participants.
This commit is contained in:
committed by
aleb_the_flash
parent
ac9ba0df62
commit
d202a025e7
@@ -1,43 +0,0 @@
|
||||
import { css } from '@/styled-system/css'
|
||||
import { RiRecordCircleLine } from '@remixicon/react'
|
||||
import { Text } from '@/primitives'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useRoomContext } from '@livekit/components-react'
|
||||
|
||||
export const RecordingStateToast = () => {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'recording' })
|
||||
|
||||
const room = useRoomContext()
|
||||
|
||||
if (!room?.isRecording) return
|
||||
|
||||
return (
|
||||
<div
|
||||
className={css({
|
||||
display: 'flex',
|
||||
position: 'fixed',
|
||||
top: '10px',
|
||||
left: '10px',
|
||||
paddingY: '0.25rem',
|
||||
paddingX: '0.25rem 0.35rem',
|
||||
backgroundColor: 'primaryDark.200',
|
||||
borderColor: 'primaryDark.400',
|
||||
border: '1px solid',
|
||||
color: 'white',
|
||||
borderRadius: '4px',
|
||||
gap: '0.5rem',
|
||||
})}
|
||||
>
|
||||
<RiRecordCircleLine
|
||||
size={20}
|
||||
className={css({
|
||||
color: 'white',
|
||||
backgroundColor: 'danger.700',
|
||||
padding: '3px',
|
||||
borderRadius: '3px',
|
||||
})}
|
||||
/>
|
||||
<Text variant={'sm'}>{t('label')}</Text>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import { css } from '@/styled-system/css'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useSnapshot } from 'valtio/index'
|
||||
import { useRoomContext } from '@livekit/components-react'
|
||||
import { Spinner } from '@/primitives/Spinner'
|
||||
import { useEffect, useMemo } from 'react'
|
||||
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'
|
||||
|
||||
export const TranscriptStateToast = () => {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'recording.transcript' })
|
||||
const room = useRoomContext()
|
||||
|
||||
const transcriptionSnap = useSnapshot(transcriptionStore)
|
||||
|
||||
useEffect(() => {
|
||||
if (room.isRecording) {
|
||||
transcriptionStore.status = TranscriptionStatus.STARTED
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const handleDataReceived = (
|
||||
payload: Uint8Array,
|
||||
participant?: RemoteParticipant
|
||||
) => {
|
||||
const notification = decodeNotificationDataReceived(payload)
|
||||
|
||||
if (!participant || !notification) return
|
||||
|
||||
switch (notification.type) {
|
||||
case NotificationType.TranscriptionStarted:
|
||||
transcriptionStore.status = TranscriptionStatus.STARTING
|
||||
break
|
||||
case NotificationType.TranscriptionStopped:
|
||||
transcriptionStore.status = TranscriptionStatus.STOPPING
|
||||
break
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const handleRecordingStatusChanged = (status: boolean) => {
|
||||
transcriptionStore.status = status
|
||||
? TranscriptionStatus.STARTED
|
||||
: TranscriptionStatus.STOPPED
|
||||
}
|
||||
|
||||
room.on(RoomEvent.DataReceived, handleDataReceived)
|
||||
room.on(RoomEvent.RecordingStatusChanged, handleRecordingStatusChanged)
|
||||
|
||||
return () => {
|
||||
room.off(RoomEvent.DataReceived, handleDataReceived)
|
||||
room.off(RoomEvent.RecordingStatusChanged, handleRecordingStatusChanged)
|
||||
}
|
||||
}, [room])
|
||||
|
||||
const key = useMemo(() => {
|
||||
switch (transcriptionSnap.status) {
|
||||
case TranscriptionStatus.STOPPING:
|
||||
return 'stopping'
|
||||
case TranscriptionStatus.STARTING:
|
||||
return 'starting'
|
||||
default:
|
||||
return 'started'
|
||||
}
|
||||
}, [transcriptionSnap])
|
||||
|
||||
if (transcriptionSnap.status == TranscriptionStatus.STOPPED) return
|
||||
|
||||
return (
|
||||
<div
|
||||
className={css({
|
||||
display: 'flex',
|
||||
position: 'fixed',
|
||||
top: '10px',
|
||||
left: '10px',
|
||||
paddingY: '0.25rem',
|
||||
paddingX: '0.75rem 0.75rem',
|
||||
backgroundColor: 'primaryDark.100',
|
||||
borderColor: 'white',
|
||||
border: '1px solid',
|
||||
color: 'white',
|
||||
borderRadius: '4px',
|
||||
gap: '0.5rem',
|
||||
})}
|
||||
>
|
||||
<Spinner size={20} variant="dark" />
|
||||
<Text
|
||||
variant={'sm'}
|
||||
className={css({
|
||||
fontWeight: '500 !important',
|
||||
})}
|
||||
>
|
||||
{t(key)}
|
||||
</Text>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -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 { RecordingStateToast } from '../components/RecordingStateToast'
|
||||
import { TranscriptStateToast } from '../components/TranscriptStateToast'
|
||||
import { ScreenShareErrorModal } from '../components/ScreenShareErrorModal'
|
||||
|
||||
const LayoutWrapper = styled(
|
||||
@@ -231,7 +231,7 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
|
||||
)}
|
||||
<RoomAudioRenderer />
|
||||
<ConnectionStateToast />
|
||||
<RecordingStateToast />
|
||||
<TranscriptStateToast />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -282,7 +282,11 @@
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
"label": ""
|
||||
"transcript": {
|
||||
"started": "",
|
||||
"starting": "",
|
||||
"stopping": ""
|
||||
}
|
||||
},
|
||||
"participantTileFocus": {
|
||||
"pin": {
|
||||
|
||||
@@ -281,7 +281,11 @@
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
"label": "Recording"
|
||||
"transcript": {
|
||||
"started": "Transcribing",
|
||||
"starting": "Transcription starting",
|
||||
"stopping": "Transcription stopping"
|
||||
}
|
||||
},
|
||||
"participantTileFocus": {
|
||||
"pin": {
|
||||
|
||||
@@ -281,7 +281,11 @@
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
"label": "Enregistrement"
|
||||
"transcript": {
|
||||
"started": "Transcription en cours",
|
||||
"starting": "Démarrage de la transcription",
|
||||
"stopping": "Arrêt de la transcription"
|
||||
}
|
||||
},
|
||||
"participantTileFocus": {
|
||||
"pin": {
|
||||
|
||||
@@ -281,7 +281,11 @@
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
"label": "Opnemen"
|
||||
"transcript": {
|
||||
"started": "Transcriptie bezig",
|
||||
"starting": "Transcriptie begint",
|
||||
"stopping": "Transcriptie stopt"
|
||||
}
|
||||
},
|
||||
"participantTileFocus": {
|
||||
"pin": {
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import { ProgressBar } from 'react-aria-components'
|
||||
import { css } from '@/styled-system/css'
|
||||
|
||||
export const Spinner = ({ size = 56 }: { size?: number }) => {
|
||||
export const Spinner = ({
|
||||
size = 56,
|
||||
variant = 'light',
|
||||
}: {
|
||||
size?: number
|
||||
variant?: 'light' | 'dark'
|
||||
}) => {
|
||||
const center = 14
|
||||
const strokeWidth = 3
|
||||
const r = 14 - strokeWidth
|
||||
@@ -25,7 +31,7 @@ export const Spinner = ({ size = 56 }: { size?: number }) => {
|
||||
strokeDashoffset={0}
|
||||
strokeLinecap="round"
|
||||
className={css({
|
||||
stroke: 'primary.100',
|
||||
stroke: variant == 'light' ? 'primary.100' : 'primaryDark.100',
|
||||
})}
|
||||
style={{}}
|
||||
/>
|
||||
@@ -37,7 +43,7 @@ export const Spinner = ({ size = 56 }: { size?: number }) => {
|
||||
strokeDashoffset={percentage && c - (percentage / 100) * c}
|
||||
strokeLinecap="round"
|
||||
className={css({
|
||||
stroke: 'primary.800',
|
||||
stroke: variant == 'light' ? 'primary.800' : 'white',
|
||||
})}
|
||||
style={{
|
||||
animation: `rotate 1s ease-in-out infinite`,
|
||||
|
||||
Reference in New Issue
Block a user