Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9333d72538 | |||
| 5ecb05ba57 | |||
| f7d07f77a7 | |||
| 7a22831cb9 | |||
| 1a409da941 | |||
| db8626adcc | |||
| eaf2c4d021 | |||
| bad5d1de3f | |||
| 5e31ca727a | |||
| 5f42fcd159 | |||
| 985b621e14 | |||
| d2d66fb8b6 |
@@ -116,10 +116,10 @@ const config: Config = {
|
|||||||
'80%': { transform: 'rotate(20deg)' },
|
'80%': { transform: 'rotate(20deg)' },
|
||||||
'100%': { transform: 'rotate(0)' },
|
'100%': { transform: 'rotate(0)' },
|
||||||
},
|
},
|
||||||
pulse_mic: {
|
pulse_background: {
|
||||||
'0%': { color: 'primary', opacity: '1' },
|
'0%': { opacity: '1' },
|
||||||
'50%': { color: 'primary', opacity: '0.8' },
|
'50%': { opacity: '0.65' },
|
||||||
'100%': { color: 'primary', opacity: '1' },
|
'100%': { opacity: '1' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tokens: defineTokens({
|
tokens: defineTokens({
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { useConfig } from '@/api/useConfig.ts'
|
||||||
|
|
||||||
|
export const useIsAnalyticsEnabled = () => {
|
||||||
|
const { data } = useConfig()
|
||||||
|
return !!data?.analytics?.id
|
||||||
|
}
|
||||||
@@ -121,7 +121,13 @@ const OpenFeedback = ({
|
|||||||
>
|
>
|
||||||
{t('submit')}
|
{t('submit')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button invisible size="sm" fullWidth onPress={onNext}>
|
<Button
|
||||||
|
invisible
|
||||||
|
variant="secondary"
|
||||||
|
size="sm"
|
||||||
|
fullWidth
|
||||||
|
onPress={onNext}
|
||||||
|
>
|
||||||
{t('skip')}
|
{t('skip')}
|
||||||
</Button>
|
</Button>
|
||||||
</VStack>
|
</VStack>
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import { useSidePanel } from '../hooks/useSidePanel'
|
|||||||
import { ReactNode } from 'react'
|
import { ReactNode } from 'react'
|
||||||
import { Effects } from './Effects'
|
import { Effects } from './Effects'
|
||||||
import { Chat } from '../prefabs/Chat'
|
import { Chat } from '../prefabs/Chat'
|
||||||
|
import { Transcript } from './Transcript'
|
||||||
|
|
||||||
type StyledSidePanelProps = {
|
type StyledSidePanelProps = {
|
||||||
title: string
|
title: string
|
||||||
@@ -106,6 +107,7 @@ export const SidePanel = () => {
|
|||||||
isEffectsOpen,
|
isEffectsOpen,
|
||||||
isChatOpen,
|
isChatOpen,
|
||||||
isSidePanelOpen,
|
isSidePanelOpen,
|
||||||
|
isTranscriptOpen,
|
||||||
} = useSidePanel()
|
} = useSidePanel()
|
||||||
const { t } = useTranslation('rooms', { keyPrefix: 'sidePanel' })
|
const { t } = useTranslation('rooms', { keyPrefix: 'sidePanel' })
|
||||||
|
|
||||||
@@ -127,6 +129,9 @@ export const SidePanel = () => {
|
|||||||
<Panel isOpen={isChatOpen}>
|
<Panel isOpen={isChatOpen}>
|
||||||
<Chat />
|
<Chat />
|
||||||
</Panel>
|
</Panel>
|
||||||
|
<Panel isOpen={isTranscriptOpen}>
|
||||||
|
<Transcript />
|
||||||
|
</Panel>
|
||||||
</StyledSidePanel>
|
</StyledSidePanel>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
import { Button, Div, H, Text } from '@/primitives'
|
||||||
|
|
||||||
|
import thirdSlide from '@/assets/intro-slider/3_resume.png'
|
||||||
|
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 { useRoomContext } from '@livekit/components-react'
|
||||||
|
import {
|
||||||
|
RecordingMode,
|
||||||
|
useStartRecording,
|
||||||
|
} from '@/features/rooms/api/startRecording'
|
||||||
|
import { useStopRecording } from '@/features/rooms/api/stopRecording'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { RoomEvent } from 'livekit-client'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
export const Transcript = () => {
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
|
||||||
|
const { t } = useTranslation('rooms', { keyPrefix: 'transcript' })
|
||||||
|
|
||||||
|
const hasTranscriptAccess = useHasTranscriptAccess()
|
||||||
|
|
||||||
|
const roomId = useRoomId()
|
||||||
|
|
||||||
|
const { mutateAsync: startRecordingRoom } = useStartRecording()
|
||||||
|
const { mutateAsync: stopRecordingRoom } = useStopRecording()
|
||||||
|
|
||||||
|
const room = useRoomContext()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleRecordingStatusChanged = () => {
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
room.on(RoomEvent.RecordingStatusChanged, handleRecordingStatusChanged)
|
||||||
|
return () => {
|
||||||
|
room.off(RoomEvent.RecordingStatusChanged, handleRecordingStatusChanged)
|
||||||
|
}
|
||||||
|
}, [room])
|
||||||
|
|
||||||
|
const handleTranscript = async () => {
|
||||||
|
if (!roomId) {
|
||||||
|
console.warn('No room ID found')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
setIsLoading(true)
|
||||||
|
if (room.isRecording) {
|
||||||
|
await stopRecordingRoom({ id: roomId })
|
||||||
|
} else {
|
||||||
|
await startRecordingRoom({ id: roomId, mode: RecordingMode.Transcript })
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to handle transcript:', error)
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasTranscriptAccess) return
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Div
|
||||||
|
display="flex"
|
||||||
|
overflowY="scroll"
|
||||||
|
padding="0 1.5rem"
|
||||||
|
flexGrow={1}
|
||||||
|
flexDirection="column"
|
||||||
|
alignItems="center"
|
||||||
|
>
|
||||||
|
<img src={thirdSlide} alt={'wip'} />
|
||||||
|
{room.isRecording ? (
|
||||||
|
<>
|
||||||
|
<H lvl={2}>{t('stop.heading')}</H>
|
||||||
|
<Text variant="sm" centered wrap="balance">
|
||||||
|
{t('stop.body')}
|
||||||
|
</Text>
|
||||||
|
<div className={css({ height: '2rem' })} />
|
||||||
|
<Button isDisabled={isLoading} onPress={() => handleTranscript()}>
|
||||||
|
<RiStopCircleLine style={{ marginRight: '0.5rem' }} />{' '}
|
||||||
|
{t('stop.button')}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<H lvl={2}>{t('start.heading')}</H>
|
||||||
|
<Text variant="sm" centered wrap="balance">
|
||||||
|
{t('start.body')}
|
||||||
|
</Text>
|
||||||
|
<div className={css({ height: '2rem' })} />
|
||||||
|
<Button isDisabled={isLoading} onPress={() => handleTranscript()}>
|
||||||
|
<RiRecordCircleLine style={{ marginRight: '0.5rem' }} />{' '}
|
||||||
|
{t('start.button')}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Div>
|
||||||
|
)
|
||||||
|
}
|
||||||
-2
@@ -10,7 +10,6 @@ import { DialogState } from './OptionsButton'
|
|||||||
import { Separator } from '@/primitives/Separator'
|
import { Separator } from '@/primitives/Separator'
|
||||||
import { useSidePanel } from '../../../hooks/useSidePanel'
|
import { useSidePanel } from '../../../hooks/useSidePanel'
|
||||||
import { menuRecipe } from '@/primitives/menuRecipe.ts'
|
import { menuRecipe } from '@/primitives/menuRecipe.ts'
|
||||||
import { TranscriptMenuItem } from './TranscriptMenuItem'
|
|
||||||
|
|
||||||
// @todo try refactoring it to use MenuList component
|
// @todo try refactoring it to use MenuList component
|
||||||
export const OptionsMenuItems = ({
|
export const OptionsMenuItems = ({
|
||||||
@@ -35,7 +34,6 @@ export const OptionsMenuItems = ({
|
|||||||
<RiAccountBoxLine size={20} />
|
<RiAccountBoxLine size={20} />
|
||||||
{t('effects')}
|
{t('effects')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<TranscriptMenuItem />
|
|
||||||
</Section>
|
</Section>
|
||||||
<Separator />
|
<Separator />
|
||||||
<Section>
|
<Section>
|
||||||
|
|||||||
-71
@@ -1,71 +0,0 @@
|
|||||||
import { RiRecordCircleLine, RiStopCircleLine } from '@remixicon/react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import { menuRecipe } from '@/primitives/menuRecipe'
|
|
||||||
import { MenuItem } from 'react-aria-components'
|
|
||||||
import {
|
|
||||||
RecordingMode,
|
|
||||||
useStartRecording,
|
|
||||||
} from '@/features/rooms/api/startRecording'
|
|
||||||
import { useStopRecording } from '@/features/rooms/api/stopRecording'
|
|
||||||
import { useRoomContext } from '@livekit/components-react'
|
|
||||||
import { useRoomData } from '@/features/rooms/livekit/hooks/useRoomData'
|
|
||||||
import { useConfig } from '@/api/useConfig'
|
|
||||||
|
|
||||||
export const TranscriptMenuItem = () => {
|
|
||||||
const { t } = useTranslation('rooms', { keyPrefix: 'options.items' })
|
|
||||||
|
|
||||||
const apiRoomData = useRoomData()
|
|
||||||
|
|
||||||
const { mutateAsync: startRecordingRoom } = useStartRecording()
|
|
||||||
const { mutateAsync: stopRecordingRoom } = useStopRecording()
|
|
||||||
|
|
||||||
const { data } = useConfig()
|
|
||||||
|
|
||||||
const room = useRoomContext()
|
|
||||||
|
|
||||||
const handleTranscript = async () => {
|
|
||||||
const roomId = apiRoomData?.livekit?.room
|
|
||||||
|
|
||||||
if (!roomId) {
|
|
||||||
console.warn('No room ID found')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (room.isRecording) {
|
|
||||||
await stopRecordingRoom({ id: roomId })
|
|
||||||
} else {
|
|
||||||
await startRecordingRoom({ id: roomId, mode: RecordingMode.Transcript })
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to handle transcript:', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!data?.recording?.is_enabled ||
|
|
||||||
!data?.recording?.available_modes?.includes(RecordingMode.Transcript) ||
|
|
||||||
!apiRoomData?.is_administrable
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MenuItem
|
|
||||||
className={menuRecipe({ icon: true }).item}
|
|
||||||
onAction={async () => await handleTranscript()}
|
|
||||||
>
|
|
||||||
{room.isRecording ? (
|
|
||||||
<>
|
|
||||||
<RiRecordCircleLine size={20} />
|
|
||||||
{t('transcript.stop')}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<RiStopCircleLine size={20} />
|
|
||||||
{t('transcript.start')}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</MenuItem>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
+6
-3
@@ -84,9 +84,12 @@ const MicIndicator = ({ participant }: MicIndicatorProps) => {
|
|||||||
<RiMicOffFill color={'gray'} />
|
<RiMicOffFill color={'gray'} />
|
||||||
) : (
|
) : (
|
||||||
<RiMicFill
|
<RiMicFill
|
||||||
style={{
|
className={css({
|
||||||
animation: isSpeaking ? 'pulse_mic 800ms infinite' : undefined,
|
color: isSpeaking ? 'primaryDark.300' : 'primaryDark.50',
|
||||||
}}
|
animation: isSpeaking
|
||||||
|
? 'pulse_background 800ms infinite'
|
||||||
|
: undefined,
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { ToggleButton } from '@/primitives'
|
||||||
|
import { RiBardLine } from '@remixicon/react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useSidePanel } from '../../hooks/useSidePanel'
|
||||||
|
import { useHasTranscriptAccess } from '../../hooks/useHasTranscriptAccess'
|
||||||
|
import { css } from '@/styled-system/css'
|
||||||
|
|
||||||
|
export const TranscriptToggle = () => {
|
||||||
|
const { t } = useTranslation('rooms', { keyPrefix: 'controls.transcript' })
|
||||||
|
|
||||||
|
const { isTranscriptOpen, toggleTranscript } = useSidePanel()
|
||||||
|
const tooltipLabel = isTranscriptOpen ? 'open' : 'closed'
|
||||||
|
|
||||||
|
const hasTranscriptAccess = useHasTranscriptAccess()
|
||||||
|
|
||||||
|
if (!hasTranscriptAccess) return
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={css({
|
||||||
|
position: 'relative',
|
||||||
|
display: 'inline-block',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<ToggleButton
|
||||||
|
square
|
||||||
|
variant="primaryTextDark"
|
||||||
|
aria-label={t(tooltipLabel)}
|
||||||
|
tooltip={t(tooltipLabel)}
|
||||||
|
isSelected={isTranscriptOpen}
|
||||||
|
onPress={() => toggleTranscript()}
|
||||||
|
>
|
||||||
|
<RiBardLine />
|
||||||
|
</ToggleButton>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { useFeatureFlagEnabled } from 'posthog-js/react'
|
||||||
|
import { useIsAnalyticsEnabled } from '@/features/analytics/hooks/useIsAnalyticsEnabled'
|
||||||
|
import { useIsTranscriptEnabled } from './useIsTranscriptEnabled'
|
||||||
|
import { useIsAdminOrOwner } from './useIsAdminOrOwner'
|
||||||
|
|
||||||
|
export const useHasTranscriptAccess = () => {
|
||||||
|
const featureEnabled = useFeatureFlagEnabled('transcription-summary')
|
||||||
|
const isAnalyticsEnabled = useIsAnalyticsEnabled()
|
||||||
|
const isTranscriptEnabled = useIsTranscriptEnabled()
|
||||||
|
const isAdminOrOwner = useIsAdminOrOwner()
|
||||||
|
|
||||||
|
return (
|
||||||
|
(featureEnabled || !isAnalyticsEnabled) &&
|
||||||
|
isAdminOrOwner &&
|
||||||
|
isTranscriptEnabled
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { useRoomData } from './useRoomData'
|
||||||
|
|
||||||
|
export const useIsAdminOrOwner = () => {
|
||||||
|
const apiRoomData = useRoomData()
|
||||||
|
return apiRoomData?.is_administrable
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { RecordingMode } from '@/features/rooms/api/startRecording'
|
||||||
|
import { useConfig } from '@/api/useConfig'
|
||||||
|
|
||||||
|
export const useIsTranscriptEnabled = () => {
|
||||||
|
const { data } = useConfig()
|
||||||
|
|
||||||
|
return (
|
||||||
|
data?.recording?.is_enabled &&
|
||||||
|
data?.recording?.available_modes?.includes(RecordingMode.Transcript)
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { useRoomData } from './useRoomData'
|
||||||
|
|
||||||
|
export const useRoomId = () => {
|
||||||
|
const apiRoomData = useRoomData()
|
||||||
|
return apiRoomData?.livekit?.room
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ export enum PanelId {
|
|||||||
PARTICIPANTS = 'participants',
|
PARTICIPANTS = 'participants',
|
||||||
EFFECTS = 'effects',
|
EFFECTS = 'effects',
|
||||||
CHAT = 'chat',
|
CHAT = 'chat',
|
||||||
|
TRANSCRIPT = 'transcript',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useSidePanel = () => {
|
export const useSidePanel = () => {
|
||||||
@@ -14,6 +15,7 @@ export const useSidePanel = () => {
|
|||||||
const isParticipantsOpen = activePanelId == PanelId.PARTICIPANTS
|
const isParticipantsOpen = activePanelId == PanelId.PARTICIPANTS
|
||||||
const isEffectsOpen = activePanelId == PanelId.EFFECTS
|
const isEffectsOpen = activePanelId == PanelId.EFFECTS
|
||||||
const isChatOpen = activePanelId == PanelId.CHAT
|
const isChatOpen = activePanelId == PanelId.CHAT
|
||||||
|
const isTranscriptOpen = activePanelId == PanelId.TRANSCRIPT
|
||||||
const isSidePanelOpen = !!activePanelId
|
const isSidePanelOpen = !!activePanelId
|
||||||
|
|
||||||
const toggleParticipants = () => {
|
const toggleParticipants = () => {
|
||||||
@@ -28,14 +30,20 @@ export const useSidePanel = () => {
|
|||||||
layoutStore.activePanelId = isEffectsOpen ? null : PanelId.EFFECTS
|
layoutStore.activePanelId = isEffectsOpen ? null : PanelId.EFFECTS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const toggleTranscript = () => {
|
||||||
|
layoutStore.activePanelId = isTranscriptOpen ? null : PanelId.TRANSCRIPT
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activePanelId,
|
activePanelId,
|
||||||
toggleParticipants,
|
toggleParticipants,
|
||||||
toggleChat,
|
toggleChat,
|
||||||
toggleEffects,
|
toggleEffects,
|
||||||
|
toggleTranscript,
|
||||||
isChatOpen,
|
isChatOpen,
|
||||||
isParticipantsOpen,
|
isParticipantsOpen,
|
||||||
isEffectsOpen,
|
isEffectsOpen,
|
||||||
isSidePanelOpen,
|
isSidePanelOpen,
|
||||||
|
isTranscriptOpen,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,10 @@ import { HandToggle } from '../components/controls/HandToggle'
|
|||||||
import { SelectToggleDevice } from '../components/controls/SelectToggleDevice'
|
import { SelectToggleDevice } from '../components/controls/SelectToggleDevice'
|
||||||
import { LeaveButton } from '../components/controls/LeaveButton'
|
import { LeaveButton } from '../components/controls/LeaveButton'
|
||||||
import { ScreenShareToggle } from '../components/controls/ScreenShareToggle'
|
import { ScreenShareToggle } from '../components/controls/ScreenShareToggle'
|
||||||
|
import { SupportToggle } from '../components/controls/SupportToggle'
|
||||||
|
import { TranscriptToggle } from '../components/controls/TranscriptToggle'
|
||||||
|
|
||||||
import { css } from '@/styled-system/css'
|
import { css } from '@/styled-system/css'
|
||||||
import { SupportToggle } from '@/features/rooms/livekit/components/controls/SupportToggle.tsx'
|
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type ControlBarControls = {
|
export type ControlBarControls = {
|
||||||
@@ -156,6 +158,7 @@ export function ControlBar({
|
|||||||
>
|
>
|
||||||
<ChatToggle />
|
<ChatToggle />
|
||||||
<ParticipantsToggle />
|
<ParticipantsToggle />
|
||||||
|
<TranscriptToggle />
|
||||||
<SupportToggle />
|
<SupportToggle />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -28,6 +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'
|
||||||
|
|
||||||
const LayoutWrapper = styled(
|
const LayoutWrapper = styled(
|
||||||
'div',
|
'div',
|
||||||
@@ -212,6 +213,7 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
|
|||||||
)}
|
)}
|
||||||
<RoomAudioRenderer />
|
<RoomAudioRenderer />
|
||||||
<ConnectionStateToast />
|
<ConnectionStateToast />
|
||||||
|
<RecordingStateToast />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,10 @@
|
|||||||
"open": "",
|
"open": "",
|
||||||
"closed": ""
|
"closed": ""
|
||||||
},
|
},
|
||||||
|
"transcript": {
|
||||||
|
"open": "",
|
||||||
|
"closed": ""
|
||||||
|
},
|
||||||
"support": ""
|
"support": ""
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
@@ -79,11 +83,7 @@
|
|||||||
"feedbacks": "",
|
"feedbacks": "",
|
||||||
"settings": "",
|
"settings": "",
|
||||||
"username": "",
|
"username": "",
|
||||||
"effects": "",
|
"effects": ""
|
||||||
"transcript": {
|
|
||||||
"start": "",
|
|
||||||
"stop": ""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"effects": {
|
"effects": {
|
||||||
@@ -102,18 +102,32 @@
|
|||||||
"heading": {
|
"heading": {
|
||||||
"participants": "",
|
"participants": "",
|
||||||
"effects": "",
|
"effects": "",
|
||||||
"chat": ""
|
"chat": "",
|
||||||
|
"transcript": ""
|
||||||
},
|
},
|
||||||
"content": {
|
"content": {
|
||||||
"participants": "",
|
"participants": "",
|
||||||
"effects": "",
|
"effects": "",
|
||||||
"chat": ""
|
"chat": "",
|
||||||
|
"transcript": ""
|
||||||
},
|
},
|
||||||
"closeButton": ""
|
"closeButton": ""
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"disclaimer": ""
|
"disclaimer": ""
|
||||||
},
|
},
|
||||||
|
"transcript": {
|
||||||
|
"start": {
|
||||||
|
"heading": "",
|
||||||
|
"body": "",
|
||||||
|
"button": ""
|
||||||
|
},
|
||||||
|
"stop": {
|
||||||
|
"heading": "",
|
||||||
|
"body": "",
|
||||||
|
"button": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
"rating": {
|
"rating": {
|
||||||
"submit": "",
|
"submit": "",
|
||||||
"question": "",
|
"question": "",
|
||||||
@@ -150,5 +164,8 @@
|
|||||||
"raisedHands": "",
|
"raisedHands": "",
|
||||||
"lowerParticipantHand": "",
|
"lowerParticipantHand": "",
|
||||||
"lowerParticipantsHand": ""
|
"lowerParticipantsHand": ""
|
||||||
|
},
|
||||||
|
"recording": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,10 @@
|
|||||||
"open": "Hide everyone",
|
"open": "Hide everyone",
|
||||||
"closed": "See everyone"
|
"closed": "See everyone"
|
||||||
},
|
},
|
||||||
|
"transcript": {
|
||||||
|
"open": "Hide AI assistant",
|
||||||
|
"closed": "Show AI assistant"
|
||||||
|
},
|
||||||
"support": "Support"
|
"support": "Support"
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
@@ -78,11 +82,7 @@
|
|||||||
"feedbacks": "Give us feedbacks",
|
"feedbacks": "Give us feedbacks",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"username": "Update Your Name",
|
"username": "Update Your Name",
|
||||||
"effects": "Apply effects",
|
"effects": "Apply effects"
|
||||||
"transcript": {
|
|
||||||
"start": "Start meeting transcription",
|
|
||||||
"stop": "Stop ongoing transcription"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"effects": {
|
"effects": {
|
||||||
@@ -101,18 +101,32 @@
|
|||||||
"heading": {
|
"heading": {
|
||||||
"participants": "Participants",
|
"participants": "Participants",
|
||||||
"effects": "Effects",
|
"effects": "Effects",
|
||||||
"chat": "Messages in the chat"
|
"chat": "Messages in the chat",
|
||||||
|
"transcript": "AI Assistant"
|
||||||
},
|
},
|
||||||
"content": {
|
"content": {
|
||||||
"participants": "participants",
|
"participants": "participants",
|
||||||
"effects": "effects",
|
"effects": "effects",
|
||||||
"chat": "messages"
|
"chat": "messages",
|
||||||
|
"transcript": "AI assistant"
|
||||||
},
|
},
|
||||||
"closeButton": "Hide {{content}}"
|
"closeButton": "Hide {{content}}"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"disclaimer": "The messages are visible to participants only at the time they are sent. All messages are deleted at the end of the call."
|
"disclaimer": "The messages are visible to participants only at the time they are sent. All messages are deleted at the end of the call."
|
||||||
},
|
},
|
||||||
|
"transcript": {
|
||||||
|
"start": {
|
||||||
|
"heading": "Start the Assistant!",
|
||||||
|
"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.",
|
||||||
|
"button": "Start"
|
||||||
|
},
|
||||||
|
"stop": {
|
||||||
|
"heading": "Recording in Progress...",
|
||||||
|
"body": "Your meeting is currently being recorded. You will receive a summary via email once the meeting ends.",
|
||||||
|
"button": "Stop Recording"
|
||||||
|
}
|
||||||
|
},
|
||||||
"rating": {
|
"rating": {
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
"question": "What do you think about the quality of your call?",
|
"question": "What do you think about the quality of your call?",
|
||||||
@@ -149,5 +163,8 @@
|
|||||||
"raisedHands": "Raised hands",
|
"raisedHands": "Raised hands",
|
||||||
"lowerParticipantHand": "Lower {{name}}'s hand",
|
"lowerParticipantHand": "Lower {{name}}'s hand",
|
||||||
"lowerParticipantsHand": "Lower all hands"
|
"lowerParticipantsHand": "Lower all hands"
|
||||||
|
},
|
||||||
|
"recording": {
|
||||||
|
"label": "Recording"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,10 @@
|
|||||||
"open": "Masquer les participants",
|
"open": "Masquer les participants",
|
||||||
"closed": "Afficher les participants"
|
"closed": "Afficher les participants"
|
||||||
},
|
},
|
||||||
|
"transcript": {
|
||||||
|
"open": "Masquer l'assistant IA",
|
||||||
|
"closed": "Afficher l'assistant IA"
|
||||||
|
},
|
||||||
"support": "Support"
|
"support": "Support"
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
@@ -78,11 +82,7 @@
|
|||||||
"feedbacks": "Partager votre avis",
|
"feedbacks": "Partager votre avis",
|
||||||
"settings": "Paramètres",
|
"settings": "Paramètres",
|
||||||
"username": "Choisir votre nom",
|
"username": "Choisir votre nom",
|
||||||
"effects": "Appliquer des effets",
|
"effects": "Appliquer des effets"
|
||||||
"transcript": {
|
|
||||||
"start": "Démarrer la transcription",
|
|
||||||
"stop": "Arrêter la transcription en cours"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"effects": {
|
"effects": {
|
||||||
@@ -101,18 +101,32 @@
|
|||||||
"heading": {
|
"heading": {
|
||||||
"participants": "Participants",
|
"participants": "Participants",
|
||||||
"effects": "Effets",
|
"effects": "Effets",
|
||||||
"chat": "Messages dans l'appel"
|
"chat": "Messages dans l'appel",
|
||||||
|
"transcript": "Assistant IA"
|
||||||
},
|
},
|
||||||
"content": {
|
"content": {
|
||||||
"participants": "les participants",
|
"participants": "les participants",
|
||||||
"effects": "les effets",
|
"effects": "les effets",
|
||||||
"chat": "les messages"
|
"chat": "les messages",
|
||||||
|
"transcript": "l'assistant IA"
|
||||||
},
|
},
|
||||||
"closeButton": "Masquer {{content}}"
|
"closeButton": "Masquer {{content}}"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"disclaimer": "Les messages sont visibles par les participants uniquement au moment de\nleur envoi. Tous les messages sont supprimés à la fin de l'appel."
|
"disclaimer": "Les messages sont visibles par les participants uniquement au moment de\nleur envoi. Tous les messages sont supprimés à la fin de l'appel."
|
||||||
},
|
},
|
||||||
|
"transcript": {
|
||||||
|
"start": {
|
||||||
|
"heading": "Démarrer l'assistant !",
|
||||||
|
"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.",
|
||||||
|
"button": "Démarrer"
|
||||||
|
},
|
||||||
|
"stop": {
|
||||||
|
"heading": "Enregistrement 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.",
|
||||||
|
"button": "Arrêter l'enregistrement"
|
||||||
|
}
|
||||||
|
},
|
||||||
"rating": {
|
"rating": {
|
||||||
"submit": "Envoyer",
|
"submit": "Envoyer",
|
||||||
"question": "Que pensez-vous de la qualité de votre appel ?",
|
"question": "Que pensez-vous de la qualité de votre appel ?",
|
||||||
@@ -149,5 +163,8 @@
|
|||||||
"raisedHands": "Mains levées",
|
"raisedHands": "Mains levées",
|
||||||
"lowerParticipantHand": "Baisser la main de {{name}}",
|
"lowerParticipantHand": "Baisser la main de {{name}}",
|
||||||
"lowerParticipantsHand": "Baisser la main de tous les participants"
|
"lowerParticipantsHand": "Baisser la main de tous les participants"
|
||||||
|
},
|
||||||
|
"recording": {
|
||||||
|
"label": "Enregistrement"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,14 @@ export const text = cva({
|
|||||||
textAlign: 'inherit',
|
textAlign: 'inherit',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
wrap: {
|
||||||
|
balance: {
|
||||||
|
textWrap: 'balance',
|
||||||
|
},
|
||||||
|
pretty: {
|
||||||
|
textWrap: 'pretty',
|
||||||
|
},
|
||||||
|
},
|
||||||
bold: {
|
bold: {
|
||||||
true: {
|
true: {
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
|
|||||||
Reference in New Issue
Block a user