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 = ( ) => {
26- setPopupOpen ( ( open ) => ! open ) ;
27-
28- if ( currentError ) {
29- handleDismissError ( ) ;
27+ if ( isAnimating . current ) {
28+ // prevent re-opening before room has disconnected
29+ return ;
3030 }
31+
32+ setError ( null ) ;
33+ setPopupOpen ( ( open ) => ! open ) ;
3134 } ;
3235
3336 const handleDismissError = ( ) => {
3437 room . disconnect ( ) ;
35- setCurrentError ( null ) ;
38+ setError ( null ) ;
39+ } ;
40+
41+ const handlePanelAnimationStart = ( ) => {
42+ isAnimating . current = true ;
43+ } ;
44+
45+ const handlePanelAnimationComplete = ( ) => {
46+ isAnimating . current = false ;
47+ if ( ! popupOpen && room . state !== 'disconnected' ) {
48+ room . disconnect ( ) ;
49+ }
3650 } ;
3751
3852 useEffect ( ( ) => {
@@ -41,7 +55,7 @@ function EmbedFixedAgentClient({ appConfig }: EmbedFixedAgentClientProps) {
4155 refreshConnectionDetails ( ) ;
4256 } ;
4357 const onMediaDevicesError = ( error : Error ) => {
44- setCurrentError ( {
58+ setError ( {
4559 title : 'Encountered an error with your media devices' ,
4660 description : `${ error . name } : ${ error . message } ` ,
4761 } ) ;
@@ -58,10 +72,14 @@ function EmbedFixedAgentClient({ appConfig }: EmbedFixedAgentClientProps) {
5872 if ( ! popupOpen ) {
5973 return ;
6074 }
61- if ( room . state !== 'disconnected' ) {
75+ if ( ! connectionDetails ) {
76+ setError ( {
77+ title : 'Error fetching connection details' ,
78+ description : 'Please try again later' ,
79+ } ) ;
6280 return ;
6381 }
64- if ( ! connectionDetails ) {
82+ if ( room . state !== 'disconnected' ) {
6583 return ;
6684 }
6785
@@ -74,26 +92,23 @@ function EmbedFixedAgentClient({ appConfig }: EmbedFixedAgentClientProps) {
7492 } catch ( error : unknown ) {
7593 if ( error instanceof Error ) {
7694 console . error ( 'Error connecting to agent:' , error ) ;
77- setCurrentError ( {
95+ setError ( {
7896 title : 'There was an error connecting to the agent' ,
7997 description : `${ error . name } : ${ error . message } ` ,
8098 } ) ;
8199 }
82100 }
83101 } ;
84- connect ( ) ;
85102
86- return ( ) => {
87- room . disconnect ( ) ;
88- } ;
103+ connect ( ) ;
89104 } , [ room , popupOpen , connectionDetails , appConfig . isPreConnectBufferEnabled ] ) ;
90105
91106 return (
92107 < RoomContext . Provider value = { room } >
93108 < RoomAudioRenderer />
94109 < StartAudio label = "Start Audio" />
95110
96- < Trigger error = { ! ! currentError } popupOpen = { popupOpen } onToggle = { handleTogglePopup } />
111+ < Trigger error = { error } popupOpen = { popupOpen } onToggle = { handleTogglePopup } />
97112
98113 < motion . div
99114 inert = { ! popupOpen }
@@ -107,58 +122,35 @@ function EmbedFixedAgentClient({ appConfig }: EmbedFixedAgentClientProps) {
107122 } }
108123 transition = { {
109124 type : 'spring' ,
110- duration : 1 ,
111125 bounce : 0 ,
126+ duration : popupOpen ? 1 : 0.2 ,
112127 } }
128+ onAnimationStart = { handlePanelAnimationStart }
129+ onAnimationComplete = { handlePanelAnimationComplete }
113130 className = "fixed right-0 bottom-20 z-50 w-full px-4"
114131 >
115132 < 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]" >
116133 < 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
134+ < ErrorMessage error = { error } />
135+ { ! error && (
136+ < PopupViewMotion
137+ initial = { { opacity : 1 } }
138+ animate = { { opacity : error === null ? 1 : 0 } }
139+ transition = { {
140+ type : 'linear' ,
141+ duration : 0.2 ,
142+ } }
152143 disabled = { ! popupOpen }
153144 sessionStarted = { popupOpen }
154- onDisplayError = { setCurrentError }
145+ onEmbedError = { setError }
146+ className = "absolute inset-0"
155147 />
156- </ div >
148+ ) }
157149 </ div >
158150 </ div >
159151 </ motion . div >
160152 </ RoomContext . Provider >
161153 ) ;
162154}
163155
164- export default EmbedFixedAgentClient ;
156+ export default AgentClient ;
0 commit comments