🚸(frontend) add notification with recording handling details
Display notification clarifying recording is processing and show which email will receive completion notification. Reduces user mental load per @sampaccoud's feedback.
This commit is contained in:
committed by
aleb_the_flash
parent
29a46a413e
commit
8d1f01645a
@@ -11,5 +11,6 @@ export const NotificationDuration = {
|
|||||||
PARTICIPANT_JOINED: ToastDuration.LONG,
|
PARTICIPANT_JOINED: ToastDuration.LONG,
|
||||||
HAND_RAISED: ToastDuration.LONG,
|
HAND_RAISED: ToastDuration.LONG,
|
||||||
LOWER_HAND: ToastDuration.EXTRA_LONG,
|
LOWER_HAND: ToastDuration.EXTRA_LONG,
|
||||||
|
RECORDING_SAVING: ToastDuration.EXTRA_LONG,
|
||||||
REACTION_RECEIVED: ToastDuration.SHORT,
|
REACTION_RECEIVED: ToastDuration.SHORT,
|
||||||
} as const
|
} as const
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ export enum NotificationType {
|
|||||||
TranscriptionStopped = 'transcriptionStopped',
|
TranscriptionStopped = 'transcriptionStopped',
|
||||||
ScreenRecordingStarted = 'screenRecordingStarted',
|
ScreenRecordingStarted = 'screenRecordingStarted',
|
||||||
ScreenRecordingStopped = 'screenRecordingStopped',
|
ScreenRecordingStopped = 'screenRecordingStopped',
|
||||||
|
RecordingSaving = 'recordingSaving',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ export interface ToastData {
|
|||||||
participant: Participant
|
participant: Participant
|
||||||
type: NotificationType
|
type: NotificationType
|
||||||
message?: string
|
message?: string
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
// Using a global queue for toasts allows for centralized management and queuing of notifications
|
// Using a global queue for toasts allows for centralized management and queuing of notifications
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { useToast } from '@react-aria/toast'
|
||||||
|
import { useMemo, useRef } from 'react'
|
||||||
|
import { Text } from '@/primitives'
|
||||||
|
|
||||||
|
import { StyledToastContainer, ToastProps } from './Toast'
|
||||||
|
import { HStack } from '@/styled-system/jsx'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useUser } from '@/features/auth'
|
||||||
|
import { css } from '@/styled-system/css'
|
||||||
|
import { RecordingMode } from '@/features/recording'
|
||||||
|
|
||||||
|
export function ToastRecordingSaving({ state, ...props }: ToastProps) {
|
||||||
|
const { t } = useTranslation('notifications', { keyPrefix: 'recordingSave' })
|
||||||
|
const ref = useRef(null)
|
||||||
|
const { toastProps, contentProps } = useToast(props, state, ref)
|
||||||
|
|
||||||
|
const { user } = useUser()
|
||||||
|
|
||||||
|
const modeLabel = useMemo(() => {
|
||||||
|
const mode = props.toast.content.mode as RecordingMode
|
||||||
|
switch (mode) {
|
||||||
|
case RecordingMode.Transcript:
|
||||||
|
return 'transcript'
|
||||||
|
case RecordingMode.ScreenRecording:
|
||||||
|
return 'screenRecording'
|
||||||
|
}
|
||||||
|
}, [props.toast.content])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledToastContainer {...toastProps} ref={ref}>
|
||||||
|
<HStack
|
||||||
|
justify="center"
|
||||||
|
alignItems="center"
|
||||||
|
{...contentProps}
|
||||||
|
padding={14}
|
||||||
|
gap={1}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
margin={false}
|
||||||
|
className={css({
|
||||||
|
maxWidth: '22rem',
|
||||||
|
wordBreak: 'break-word',
|
||||||
|
overflowWrap: 'break-word',
|
||||||
|
whiteSpace: 'normal',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{user?.email ? (
|
||||||
|
<span
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: t(`${modeLabel}.message`, {
|
||||||
|
email: user.email,
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
t(`${modeLabel}.default`)
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
</StyledToastContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import { ToastMuted } from './ToastMuted'
|
|||||||
import { ToastMessageReceived } from './ToastMessageReceived'
|
import { ToastMessageReceived } from './ToastMessageReceived'
|
||||||
import { ToastLowerHand } from './ToastLowerHand'
|
import { ToastLowerHand } from './ToastLowerHand'
|
||||||
import { ToastAnyRecording } from './ToastAnyRecording'
|
import { ToastAnyRecording } from './ToastAnyRecording'
|
||||||
|
import { ToastRecordingSaving } from './ToastRecordingSaving'
|
||||||
|
|
||||||
interface ToastRegionProps extends AriaToastRegionProps {
|
interface ToastRegionProps extends AriaToastRegionProps {
|
||||||
state: ToastState<ToastData>
|
state: ToastState<ToastData>
|
||||||
@@ -43,6 +44,11 @@ const renderToast = (
|
|||||||
case NotificationType.ScreenRecordingStopped:
|
case NotificationType.ScreenRecordingStopped:
|
||||||
return <ToastAnyRecording key={toast.key} toast={toast} state={state} />
|
return <ToastAnyRecording key={toast.key} toast={toast} state={state} />
|
||||||
|
|
||||||
|
case NotificationType.RecordingSaving:
|
||||||
|
return (
|
||||||
|
<ToastRecordingSaving key={toast.key} toast={toast} state={state} />
|
||||||
|
)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return <Toast key={toast.key} toast={toast} state={state} />
|
return <Toast key={toast.key} toast={toast} state={state} />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export { useNotifyParticipants } from './hooks/useNotifyParticipants'
|
export { useNotifyParticipants } from './hooks/useNotifyParticipants'
|
||||||
export { NotificationType } from './NotificationType'
|
export { NotificationType } from './NotificationType'
|
||||||
|
export { notifyRecordingSaveInProgress } from './utils'
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { NotificationType } from './NotificationType'
|
|||||||
import { NotificationDuration } from './NotificationDuration'
|
import { NotificationDuration } from './NotificationDuration'
|
||||||
import { Participant } from 'livekit-client'
|
import { Participant } from 'livekit-client'
|
||||||
import { NotificationPayload } from './NotificationPayload'
|
import { NotificationPayload } from './NotificationPayload'
|
||||||
|
import { RecordingMode } from '@/features/recording'
|
||||||
|
|
||||||
export const showLowerHandToast = (
|
export const showLowerHandToast = (
|
||||||
participant: Participant,
|
participant: Participant,
|
||||||
@@ -49,3 +50,17 @@ export const decodeNotificationDataReceived = (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const notifyRecordingSaveInProgress = (
|
||||||
|
mode: RecordingMode,
|
||||||
|
participant: Participant
|
||||||
|
) => {
|
||||||
|
toastQueue.add(
|
||||||
|
{
|
||||||
|
participant,
|
||||||
|
mode,
|
||||||
|
type: NotificationType.RecordingSaving,
|
||||||
|
},
|
||||||
|
{ timeout: NotificationDuration.RECORDING_SAVING }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -106,14 +106,10 @@ export const RecordingStateToast = () => {
|
|||||||
switch (recordingSnap.status) {
|
switch (recordingSnap.status) {
|
||||||
case RecordingStatus.TRANSCRIPT_STARTED:
|
case RecordingStatus.TRANSCRIPT_STARTED:
|
||||||
return 'transcript.started'
|
return 'transcript.started'
|
||||||
case RecordingStatus.TRANSCRIPT_STOPPING:
|
|
||||||
return 'transcript.stopping'
|
|
||||||
case RecordingStatus.TRANSCRIPT_STARTING:
|
case RecordingStatus.TRANSCRIPT_STARTING:
|
||||||
return 'transcript.starting'
|
return 'transcript.starting'
|
||||||
case RecordingStatus.SCREEN_RECORDING_STARTED:
|
case RecordingStatus.SCREEN_RECORDING_STARTED:
|
||||||
return 'screenRecording.started'
|
return 'screenRecording.started'
|
||||||
case RecordingStatus.SCREEN_RECORDING_STOPPING:
|
|
||||||
return 'screenRecording.stopping'
|
|
||||||
case RecordingStatus.SCREEN_RECORDING_STARTING:
|
case RecordingStatus.SCREEN_RECORDING_STARTING:
|
||||||
return 'screenRecording.starting'
|
return 'screenRecording.starting'
|
||||||
case RecordingStatus.ANY_STARTED:
|
case RecordingStatus.ANY_STARTED:
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { CRISP_HELP_ARTICLE_RECORDING } from '@/utils/constants'
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
NotificationType,
|
NotificationType,
|
||||||
|
notifyRecordingSaveInProgress,
|
||||||
useNotifyParticipants,
|
useNotifyParticipants,
|
||||||
} from '@/features/notifications'
|
} from '@/features/notifications'
|
||||||
import posthog from 'posthog-js'
|
import posthog from 'posthog-js'
|
||||||
@@ -84,6 +85,10 @@ export const ScreenRecordingSidePanel = () => {
|
|||||||
await notifyParticipants({
|
await notifyParticipants({
|
||||||
type: NotificationType.ScreenRecordingStopped,
|
type: NotificationType.ScreenRecordingStopped,
|
||||||
})
|
})
|
||||||
|
notifyRecordingSaveInProgress(
|
||||||
|
RecordingMode.ScreenRecording,
|
||||||
|
room.localParticipant
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
await startRecordingRoom({
|
await startRecordingRoom({
|
||||||
id: roomId,
|
id: roomId,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { FeatureFlags } from '@/features/analytics/enums'
|
|||||||
import {
|
import {
|
||||||
NotificationType,
|
NotificationType,
|
||||||
useNotifyParticipants,
|
useNotifyParticipants,
|
||||||
|
notifyRecordingSaveInProgress,
|
||||||
} from '@/features/notifications'
|
} from '@/features/notifications'
|
||||||
import posthog from 'posthog-js'
|
import posthog from 'posthog-js'
|
||||||
import { useSnapshot } from 'valtio/index'
|
import { useSnapshot } from 'valtio/index'
|
||||||
@@ -99,6 +100,10 @@ export const TranscriptSidePanel = () => {
|
|||||||
await notifyParticipants({
|
await notifyParticipants({
|
||||||
type: NotificationType.TranscriptionStopped,
|
type: NotificationType.TranscriptionStopped,
|
||||||
})
|
})
|
||||||
|
notifyRecordingSaveInProgress(
|
||||||
|
RecordingMode.Transcript,
|
||||||
|
room.localParticipant
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
await startRecordingRoom({ id: roomId, mode: RecordingMode.Transcript })
|
await startRecordingRoom({ id: roomId, mode: RecordingMode.Transcript })
|
||||||
recordingStore.status = RecordingStatus.TRANSCRIPT_STARTING
|
recordingStore.status = RecordingStatus.TRANSCRIPT_STARTING
|
||||||
|
|||||||
@@ -29,5 +29,15 @@
|
|||||||
"screenRecording": {
|
"screenRecording": {
|
||||||
"started": "{{name}} hat die Aufzeichnung des Treffens gestartet.",
|
"started": "{{name}} hat die Aufzeichnung des Treffens gestartet.",
|
||||||
"stopped": "{{name}} hat die Aufzeichnung des Treffens gestoppt."
|
"stopped": "{{name}} hat die Aufzeichnung des Treffens gestoppt."
|
||||||
|
},
|
||||||
|
"recordingSave": {
|
||||||
|
"transcript": {
|
||||||
|
"message": "Wir finalisieren Ihre Aufnahme! Sie erhalten eine E-Mail an <strong>{{email}}</strong>, sobald die Transkription fertig ist.",
|
||||||
|
"default": "Wir finalisieren Ihre Aufnahme! Sie erhalten eine E-Mail, sobald die Transkription fertig ist."
|
||||||
|
},
|
||||||
|
"screenRecording": {
|
||||||
|
"message": "Wir finalisieren Ihre Aufnahme! Sie erhalten eine E-Mail an <strong>{{email}}</strong>, sobald sie fertig ist.",
|
||||||
|
"default": "Wir finalisieren Ihre Aufnahme! Sie erhalten eine E-Mail, sobald sie fertig ist."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,5 +29,15 @@
|
|||||||
"screenRecording": {
|
"screenRecording": {
|
||||||
"started": "{{name}} started the meeting recording.",
|
"started": "{{name}} started the meeting recording.",
|
||||||
"stopped": "{{name}} stopped the meeting recording."
|
"stopped": "{{name}} stopped the meeting recording."
|
||||||
|
},
|
||||||
|
"recordingSave": {
|
||||||
|
"transcript": {
|
||||||
|
"message": "Your recording is being saved! We’ll send the transcript to <strong>{{email}}</strong> as soon as it’s ready.",
|
||||||
|
"default": "Your recording is being saved! We’ll send the transcript to your email as soon as it’s ready."
|
||||||
|
},
|
||||||
|
"screenRecording": {
|
||||||
|
"message": "Your recording is being saved! We’ll send a notification to <strong>{{email}}</strong> as soon as it’s ready.",
|
||||||
|
"default": "Your recording is being saved! We’ll send a notification to your email as soon as it’s ready."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,5 +29,15 @@
|
|||||||
"screenRecording": {
|
"screenRecording": {
|
||||||
"started": "{{name}} a démarré l'enregistrement de la réunion.",
|
"started": "{{name}} a démarré l'enregistrement de la réunion.",
|
||||||
"stopped": "{{name}} a arrêté l'enregistrement de la réunion."
|
"stopped": "{{name}} a arrêté l'enregistrement de la réunion."
|
||||||
|
},
|
||||||
|
"recordingSave": {
|
||||||
|
"transcript": {
|
||||||
|
"message": "Nous finalisons votre enregistrement ! Vous recevrez un e-mail à <strong>{{email}}</strong> dès que la transcription sera prête.",
|
||||||
|
"default": "Nous finalisons votre enregistrement ! Vous recevrez un e-mail dès que la transcription sera prête."
|
||||||
|
},
|
||||||
|
"screenRecording": {
|
||||||
|
"message": "Nous finalisons votre enregistrement ! Vous recevrez un e-mail à <strong>{{email}}</strong> dès qu’il sera prêt.",
|
||||||
|
"default": "Nous finalisons votre enregistrement ! Vous recevrez un e-mail dès qu’il sera prêt."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,5 +29,15 @@
|
|||||||
"screenRecording": {
|
"screenRecording": {
|
||||||
"started": "{{name}} is begonnen met het opnemen van de vergadering.",
|
"started": "{{name}} is begonnen met het opnemen van de vergadering.",
|
||||||
"stopped": "{{name}} is gestopt met het opnemen van de vergadering."
|
"stopped": "{{name}} is gestopt met het opnemen van de vergadering."
|
||||||
|
},
|
||||||
|
"recordingSave": {
|
||||||
|
"transcript": {
|
||||||
|
"message": "We zijn uw opname aan het voltooien! U ontvangt een e-mail op <strong>{{email}}</strong> zodra de transcriptie klaar is.",
|
||||||
|
"default": "We zijn uw opname aan het voltooien! U ontvangt een e-mail zodra de transcriptie klaar is."
|
||||||
|
},
|
||||||
|
"screenRecording": {
|
||||||
|
"message": "We zijn uw opname aan het voltooien! U ontvangt een e-mail op <strong>{{email}}</strong> zodra deze klaar is.",
|
||||||
|
"default": "We zijn uw opname aan het voltooien! U ontvangt een e-mail zodra deze klaar is."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user