fix(vision): remote camera connection and session management
- Fix race condition in useRemoteCameraDesktop where session ID wasn't saved before socket connection check, preventing auto-reconnect - Same fix in useRemoteCameraPhone for phone-side connection - Fix "new session" button in RemoteCameraQRCode - properly clears old session and creates new one using prevRef to detect state changes - Show full QR code UI with copyable URL (removed compact mode) - Redesign AbacusVisionBridge UI: camera feed as hero, toolbar on feed, collapsible crop settings, source selector as tabs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,8 @@ export interface RemoteCameraQRCodeProps {
|
||||
size?: number
|
||||
/** Existing session ID to reuse (for reconnection scenarios) */
|
||||
existingSessionId?: string | null
|
||||
/** Compact mode - just the QR code, no instructions or URL */
|
||||
compact?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,14 +30,18 @@ export function RemoteCameraQRCode({
|
||||
onSessionCreated,
|
||||
size = 200,
|
||||
existingSessionId,
|
||||
compact = false,
|
||||
}: RemoteCameraQRCodeProps) {
|
||||
const { session, isCreating, error, createSession, setExistingSession, getPhoneUrl } =
|
||||
const { session, isCreating, error, createSession, setExistingSession, clearSession, getPhoneUrl } =
|
||||
useRemoteCameraSession()
|
||||
|
||||
// Ref to track if we've already initiated session creation
|
||||
// This prevents React 18 Strict Mode from creating duplicate sessions
|
||||
const creationInitiatedRef = useRef(false)
|
||||
|
||||
// Track previous existingSessionId to detect when it changes TO null
|
||||
const prevExistingSessionIdRef = useRef<string | null | undefined>(existingSessionId)
|
||||
|
||||
// If we have an existing session ID, use it instead of creating a new one
|
||||
useEffect(() => {
|
||||
if (existingSessionId && !session) {
|
||||
@@ -43,6 +49,19 @@ export function RemoteCameraQRCode({
|
||||
}
|
||||
}, [existingSessionId, session, setExistingSession])
|
||||
|
||||
// Reset when existingSessionId CHANGES from truthy to null (user wants fresh session)
|
||||
// This prevents clearing sessions that we just created ourselves
|
||||
useEffect(() => {
|
||||
const prevId = prevExistingSessionIdRef.current
|
||||
prevExistingSessionIdRef.current = existingSessionId
|
||||
|
||||
// Only clear if existingSessionId changed FROM something TO null
|
||||
if (prevId && !existingSessionId) {
|
||||
clearSession()
|
||||
creationInitiatedRef.current = false
|
||||
}
|
||||
}, [existingSessionId, clearSession])
|
||||
|
||||
// Create session on mount only if no existing session
|
||||
// Use ref to prevent duplicate creation in React 18 Strict Mode
|
||||
useEffect(() => {
|
||||
@@ -143,6 +162,22 @@ export function RemoteCameraQRCode({
|
||||
return null
|
||||
}
|
||||
|
||||
// Compact mode - just the QR code in a minimal container
|
||||
if (compact) {
|
||||
return (
|
||||
<div
|
||||
className={css({
|
||||
bg: 'white',
|
||||
p: 2,
|
||||
borderRadius: 'lg',
|
||||
})}
|
||||
data-component="remote-camera-qr-compact"
|
||||
>
|
||||
<AbacusQRCode value={phoneUrl} size={size} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={css({
|
||||
|
||||
@@ -254,16 +254,19 @@ export function useRemoteCameraDesktop(): UseRemoteCameraDesktopReturn {
|
||||
'connected:',
|
||||
isConnected
|
||||
)
|
||||
if (!socket || !isConnected) {
|
||||
console.error('[RemoteCameraDesktop] Socket not connected!')
|
||||
setError('Socket not connected')
|
||||
return
|
||||
}
|
||||
|
||||
// Save session ID FIRST, so auto-connect handler can use it
|
||||
// even if socket isn't connected yet
|
||||
currentSessionIdRef.current = sessionId
|
||||
setCurrentSessionId(sessionId)
|
||||
persistSessionId(sessionId)
|
||||
setError(null)
|
||||
|
||||
if (!socket || !isConnected) {
|
||||
console.log('[RemoteCameraDesktop] Socket not connected yet, will subscribe on connect')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('[RemoteCameraDesktop] Emitting remote-camera:subscribe')
|
||||
socket.emit('remote-camera:subscribe', { sessionId })
|
||||
},
|
||||
|
||||
@@ -354,15 +354,17 @@ export function useRemoteCameraPhone(
|
||||
'connected:',
|
||||
isSocketConnected
|
||||
)
|
||||
if (!socket || !isSocketConnected) {
|
||||
console.error('[RemoteCameraPhone] Socket not connected!')
|
||||
setError('Socket not connected')
|
||||
return
|
||||
}
|
||||
|
||||
// Save session ID FIRST, so auto-connect handler can use it
|
||||
// even if socket isn't connected yet
|
||||
sessionIdRef.current = sessionId
|
||||
setError(null)
|
||||
|
||||
if (!socket || !isSocketConnected) {
|
||||
console.log('[RemoteCameraPhone] Socket not connected yet, will join on connect')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('[RemoteCameraPhone] Emitting remote-camera:join')
|
||||
socket.emit('remote-camera:join', { sessionId })
|
||||
setIsConnected(true)
|
||||
|
||||
Reference in New Issue
Block a user