import React, { createContext, useCallback, useContext, useEffect, useRef, useState, } from "react"; import * as SecureStore from "expo-secure-store"; import { ConnectionStatus, ServerConfig, WsIncoming, WsOutgoing, } from "../types"; import { wsClient } from "../services/websocket"; const SECURE_STORE_KEY = "pailot_server_config"; interface ConnectionContextValue { serverConfig: ServerConfig | null; status: ConnectionStatus; connect: (config?: ServerConfig) => void; disconnect: () => void; sendTextMessage: (text: string) => boolean; sendVoiceMessage: (audioBase64: string, transcript?: string) => boolean; sendCommand: (command: string, args?: Record) => boolean; saveServerConfig: (config: ServerConfig) => Promise; onMessageReceived: React.MutableRefObject< ((data: WsIncoming) => void) | null >; } const ConnectionContext = createContext(null); export function ConnectionProvider({ children, }: { children: React.ReactNode; }) { const [serverConfig, setServerConfig] = useState(null); const [status, setStatus] = useState("disconnected"); const onMessageReceived = useRef<((data: WsIncoming) => void) | null>(null); useEffect(() => { loadConfig(); }, []); useEffect(() => { wsClient.setCallbacks({ onOpen: () => setStatus("connected"), onClose: () => setStatus("disconnected"), onError: () => setStatus("disconnected"), onMessage: (data) => { onMessageReceived.current?.(data as WsIncoming); }, }); }, []); async function loadConfig() { try { const stored = await SecureStore.getItemAsync(SECURE_STORE_KEY); if (stored) { const config = JSON.parse(stored) as ServerConfig; setServerConfig(config); connectToServer(config); } } catch { // No stored config } } function connectToServer(config: ServerConfig) { setStatus("connecting"); const url = `ws://${config.host}:${config.port}`; wsClient.connect(url); } const connect = useCallback( (config?: ServerConfig) => { const target = config ?? serverConfig; if (!target) return; connectToServer(target); }, [serverConfig] ); const disconnect = useCallback(() => { wsClient.disconnect(); setStatus("disconnected"); }, []); const saveServerConfig = useCallback(async (config: ServerConfig) => { await SecureStore.setItemAsync(SECURE_STORE_KEY, JSON.stringify(config)); setServerConfig(config); }, []); const sendTextMessage = useCallback((text: string): boolean => { return wsClient.send({ type: "text", content: text }); }, []); const sendVoiceMessage = useCallback( (audioBase64: string, transcript: string = ""): boolean => { return wsClient.send({ type: "voice", content: transcript, audioBase64, }); }, [] ); const sendCommand = useCallback( (command: string, args?: Record): boolean => { const msg: WsOutgoing = { type: "command", command, args }; return wsClient.send(msg as any); }, [] ); return ( {children} ); } export function useConnection() { const ctx = useContext(ConnectionContext); if (!ctx) throw new Error("useConnection must be used within ConnectionProvider"); return ctx; }