"use client"; import * as React from "react"; import type { ColineConnectionStatus } from "@colineapp/sdk/v2"; import { useColine } from "../provider"; // Every useConnection for a key shares what any of them learns: connecting // through a ConnectButton must update the page's own useConnection too. const subscribers = new Map void>>(); function publish(key: string, status: ColineConnectionStatus) { for (const listener of subscribers.get(key) ?? []) listener(status); } /** An account connection's status, with connect and disconnect actions. */ export function useConnection(key: string) { const coline = useColine(); const [status, setStatus] = React.useState(null); const [isLoading, setLoading] = React.useState(true); const [isConnecting, setConnecting] = React.useState(false); const [error, setError] = React.useState(null); React.useEffect(() => { const listener = (next: ColineConnectionStatus) => { setStatus(next); setError(null); setLoading(false); }; let set = subscribers.get(key); if (!set) subscribers.set(key, (set = new Set())); set.add(listener); return () => { set.delete(listener); if (set.size === 0) subscribers.delete(key); }; }, [key]); const refresh = React.useCallback(async () => { try { publish(key, await coline.connections.get(key)); } catch (cause) { setError(cause instanceof Error ? cause.message : String(cause)); } finally { setLoading(false); } }, [coline, key]); React.useEffect(() => { let cancelled = false; coline.connections .get(key) .then((next) => { if (cancelled) return; setStatus(next); setError(null); }) .catch((cause: unknown) => { if (!cancelled) setError(cause instanceof Error ? cause.message : String(cause)); }) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, [coline, key]); const connect = React.useCallback(async () => { setConnecting(true); try { const next = await coline.connections.connect(key); publish(key, next); return next.connected; } catch (cause) { setError(cause instanceof Error ? cause.message : String(cause)); return false; } finally { setConnecting(false); } }, [coline, key]); const disconnect = React.useCallback(async () => { await coline.connections.disconnect(key); await refresh(); }, [coline, key, refresh]); return { status, isLoading, isConnecting, error, connect, disconnect, refresh }; } /** A value that settles `delayMs` after it stops changing. */ export function useDebouncedValue(value: T, delayMs = 250): T { const [settled, setSettled] = React.useState(value); React.useEffect(() => { const timer = setTimeout(() => setSettled(value), delayMs); return () => clearTimeout(timer); }, [value, delayMs]); return settled; }