11'use client' ;
22
3- import { useEffect , useMemo , useState } from 'react' ;
3+ import { useEffect , useMemo , useRef , useState } from 'react' ;
44import { Room , RoomEvent } from 'livekit-client' ;
55import { motion } from 'motion/react' ;
66import { RoomAudioRenderer , RoomContext , StartAudio } from '@livekit/components-react' ;
7- import { XIcon } from '@phosphor-icons/react ' ;
7+ import { ErrorMessage } from '@/components/embed-popup/error-message ' ;
88import { PopupView } from '@/components/embed-popup/popup-view' ;
99import { Trigger } from '@/components/embed-popup/trigger' ;
10- import { Button } from '@/components/ui/button' ;
1110import useConnectionDetails from '@/hooks/use-connection-details' ;
1211import { type AppConfig , EmbedErrorDetails } from '@/lib/types' ;
13- import { cn } from '@/lib/utils' ;
12+
13+ const PopupViewMotion = motion . create ( PopupView ) ;
1414
1515export type EmbedFixedAgentClientProps = {
1616 appConfig : AppConfig ;
1717} ;
1818
19- function EmbedFixedAgentClient ( { appConfig } : EmbedFixedAgentClientProps ) {
19+ function AgentClient ( { appConfig } : EmbedFixedAgentClientProps ) {
20+ const isAnimating = useRef ( false ) ;
2021 const room = useMemo ( ( ) => new Room ( ) , [ ] ) ;
2122 const [ popupOpen , setPopupOpen ] = useState ( false ) ;
22- const [ currentError , setCurrentError ] = useState < EmbedErrorDetails | null > ( null ) ;
23+ const [ error , setError ] = useState < EmbedErrorDetails | null > ( null ) ;
2324 const { connectionDetails, refreshConnectionDetails } = useConnectionDetails ( ) ;
2425
2526 const handleTogglePopup = ( ) => {
27+ if ( isAnimating . current ) {
28+ // prevent re-opening before room has disconnected
29+ return ;
30+ }
31+
32+ setError ( null ) ;
2633 setPopupOpen ( ( open ) => ! open ) ;
34+ } ;
2735
28- if ( currentError ) {
29- handleDismissError ( ) ;
30- }
36+ const handlePanelAnimationStart = ( ) => {
37+ isAnimating . current = true ;
3138 } ;
3239
33- const handleDismissError = ( ) => {
34- room . disconnect ( ) ;
35- setCurrentError ( null ) ;
40+ const handlePanelAnimationComplete = ( ) => {
41+ isAnimating . current = false ;
42+ if ( ! popupOpen && room . state !== 'disconnected' ) {
43+ room . disconnect ( ) ;
44+ }
3645 } ;
3746
3847 useEffect ( ( ) => {
@@ -41,7 +50,7 @@ function EmbedFixedAgentClient({ appConfig }: EmbedFixedAgentClientProps) {
4150 refreshConnectionDetails ( ) ;
4251 } ;
4352 const onMediaDevicesError = ( error : Error ) => {
44- setCurrentError ( {
53+ setError ( {
4554 title : 'Encountered an error with your media devices' ,
4655 description : `${ error . name } : ${ error . message } ` ,
4756 } ) ;
@@ -58,10 +67,14 @@ function EmbedFixedAgentClient({ appConfig }: EmbedFixedAgentClientProps) {
5867 if ( ! popupOpen ) {
5968 return ;
6069 }
61- if ( room . state !== 'disconnected' ) {
70+ if ( ! connectionDetails ) {
71+ setError ( {
72+ title : 'Error fetching connection details' ,
73+ description : 'Please try again later' ,
74+ } ) ;
6275 return ;
6376 }
64- if ( ! connectionDetails ) {
77+ if ( room . state !== 'disconnected' ) {
6578 return ;
6679 }
6780
@@ -74,26 +87,23 @@ function EmbedFixedAgentClient({ appConfig }: EmbedFixedAgentClientProps) {
7487 } catch ( error : unknown ) {
7588 if ( error instanceof Error ) {
7689 console . error ( 'Error connecting to agent:' , error ) ;
77- setCurrentError ( {
90+ setError ( {
7891 title : 'There was an error connecting to the agent' ,
7992 description : `${ error . name } : ${ error . message } ` ,
8093 } ) ;
8194 }
8295 }
8396 } ;
84- connect ( ) ;
8597
86- return ( ) => {
87- room . disconnect ( ) ;
88- } ;
98+ connect ( ) ;
8999 } , [ room , popupOpen , connectionDetails , appConfig . isPreConnectBufferEnabled ] ) ;
90100
91101 return (
92102 < RoomContext . Provider value = { room } >
93103 < RoomAudioRenderer />
94104 < StartAudio label = "Start Audio" />
95105
96- < Trigger error = { ! ! currentError } popupOpen = { popupOpen } onToggle = { handleTogglePopup } />
106+ < Trigger error = { error } popupOpen = { popupOpen } onToggle = { handleTogglePopup } />
97107
98108 < motion . div
99109 inert = { ! popupOpen }
@@ -107,58 +117,35 @@ function EmbedFixedAgentClient({ appConfig }: EmbedFixedAgentClientProps) {
107117 } }
108118 transition = { {
109119 type : 'spring' ,
110- duration : 1 ,
111120 bounce : 0 ,
121+ duration : popupOpen ? 1 : 0.2 ,
112122 } }
123+ onAnimationStart = { handlePanelAnimationStart }
124+ onAnimationComplete = { handlePanelAnimationComplete }
113125 className = "fixed right-0 bottom-20 z-50 w-full px-4"
114126 >
115127 < div className = "bg-bg2 dark:bg-bg1 border-separator1 ml-auto h-[480px] w-full rounded-[28px] border drop-shadow-md md:max-w-[360px]" >
116128 < div className = "relative h-full w-full" >
117- < div
118- inert = { currentError === null }
119- className = { cn (
120- 'absolute inset-0 flex h-full w-full flex-col items-center justify-center gap-5 transition-opacity' ,
121- currentError === null ? 'opacity-0' : 'opacity-100'
122- ) }
123- >
124- < div className = "pl-3" >
125- { /* eslint-disable-next-line @next/next/no-img-element */ }
126- < img src = "/lk-logo.svg" alt = "LiveKit Logo" className = "block size-6 dark:hidden" />
127- { /* eslint-disable-next-line @next/next/no-img-element */ }
128- < img
129- src = "/lk-logo-dark.svg"
130- alt = "LiveKit Logo"
131- className = "hidden size-6 dark:block"
132- />
133- </ div >
134-
135- < div className = "flex w-full flex-col justify-center gap-1 overflow-auto px-4 text-center" >
136- < span className = "text-sm font-medium" > { currentError ?. title } </ span >
137- < span className = "text-xs" > { currentError ?. description } </ span >
138- </ div >
139-
140- < Button variant = "secondary" onClick = { handleDismissError } >
141- < XIcon /> Dismiss
142- </ Button >
143- </ div >
144- < div
145- inert = { currentError !== null }
146- className = { cn (
147- 'absolute inset-0 transition-opacity' ,
148- currentError === null ? 'opacity-100' : 'opacity-0'
149- ) }
150- >
151- < PopupView
129+ < ErrorMessage error = { error } />
130+ { ! error && (
131+ < PopupViewMotion
132+ initial = { { opacity : 1 } }
133+ animate = { { opacity : error === null ? 1 : 0 } }
134+ transition = { {
135+ type : 'linear' ,
136+ duration : 0.2 ,
137+ } }
152138 disabled = { ! popupOpen }
153139 sessionStarted = { popupOpen }
154- onDisplayError = { setCurrentError }
140+ onEmbedError = { setError }
141+ className = "absolute inset-0"
155142 />
156- </ div >
143+ ) }
157144 </ div >
158145 </ div >
159146 </ motion . div >
160147 </ RoomContext . Provider >
161148 ) ;
162149}
163150
164- export default EmbedFixedAgentClient ;
151+ export default AgentClient ;
0 commit comments