🚸(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 { ParticipantTile } from '../components/ParticipantTile'
|
||||||
import { SidePanel } from '../components/SidePanel'
|
import { SidePanel } from '../components/SidePanel'
|
||||||
import { useSidePanel } from '../hooks/useSidePanel'
|
import { useSidePanel } from '../hooks/useSidePanel'
|
||||||
import { RecordingStateToast } from '../components/RecordingStateToast'
|
import { TranscriptStateToast } from '../components/TranscriptStateToast'
|
||||||
import { ScreenShareErrorModal } from '../components/ScreenShareErrorModal'
|
import { ScreenShareErrorModal } from '../components/ScreenShareErrorModal'
|
||||||
|
|
||||||
const LayoutWrapper = styled(
|
const LayoutWrapper = styled(
|
||||||
@@ -231,7 +231,7 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
|
|||||||
)}
|
)}
|
||||||
<RoomAudioRenderer />
|
<RoomAudioRenderer />
|
||||||
<ConnectionStateToast />
|
<ConnectionStateToast />
|
||||||
<RecordingStateToast />
|
<TranscriptStateToast />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -282,7 +282,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"recording": {
|
"recording": {
|
||||||
"label": ""
|
"transcript": {
|
||||||
|
"started": "",
|
||||||
|
"starting": "",
|
||||||
|
"stopping": ""
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"participantTileFocus": {
|
"participantTileFocus": {
|
||||||
"pin": {
|
"pin": {
|
||||||
|
|||||||
@@ -281,7 +281,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"recording": {
|
"recording": {
|
||||||
"label": "Recording"
|
"transcript": {
|
||||||
|
"started": "Transcribing",
|
||||||
|
"starting": "Transcription starting",
|
||||||
|
"stopping": "Transcription stopping"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"participantTileFocus": {
|
"participantTileFocus": {
|
||||||
"pin": {
|
"pin": {
|
||||||
|
|||||||
@@ -281,7 +281,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"recording": {
|
"recording": {
|
||||||
"label": "Enregistrement"
|
"transcript": {
|
||||||
|
"started": "Transcription en cours",
|
||||||
|
"starting": "Démarrage de la transcription",
|
||||||
|
"stopping": "Arrêt de la transcription"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"participantTileFocus": {
|
"participantTileFocus": {
|
||||||
"pin": {
|
"pin": {
|
||||||
|
|||||||
@@ -281,7 +281,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"recording": {
|
"recording": {
|
||||||
"label": "Opnemen"
|
"transcript": {
|
||||||
|
"started": "Transcriptie bezig",
|
||||||
|
"starting": "Transcriptie begint",
|
||||||
|
"stopping": "Transcriptie stopt"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"participantTileFocus": {
|
"participantTileFocus": {
|
||||||
"pin": {
|
"pin": {
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
import { ProgressBar } from 'react-aria-components'
|
import { ProgressBar } from 'react-aria-components'
|
||||||
import { css } from '@/styled-system/css'
|
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 center = 14
|
||||||
const strokeWidth = 3
|
const strokeWidth = 3
|
||||||
const r = 14 - strokeWidth
|
const r = 14 - strokeWidth
|
||||||
@@ -25,7 +31,7 @@ export const Spinner = ({ size = 56 }: { size?: number }) => {
|
|||||||
strokeDashoffset={0}
|
strokeDashoffset={0}
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
className={css({
|
className={css({
|
||||||
stroke: 'primary.100',
|
stroke: variant == 'light' ? 'primary.100' : 'primaryDark.100',
|
||||||
})}
|
})}
|
||||||
style={{}}
|
style={{}}
|
||||||
/>
|
/>
|
||||||
@@ -37,7 +43,7 @@ export const Spinner = ({ size = 56 }: { size?: number }) => {
|
|||||||
strokeDashoffset={percentage && c - (percentage / 100) * c}
|
strokeDashoffset={percentage && c - (percentage / 100) * c}
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
className={css({
|
className={css({
|
||||||
stroke: 'primary.800',
|
stroke: variant == 'light' ? 'primary.800' : 'white',
|
||||||
})}
|
})}
|
||||||
style={{
|
style={{
|
||||||
animation: `rotate 1s ease-in-out infinite`,
|
animation: `rotate 1s ease-in-out infinite`,
|
||||||
|
|||||||
Reference in New Issue
Block a user