import { signal, computed } from "@preact/signals"; import type { SphereData } from "@exosphere/core/types"; import { apiFetch } from "./api.ts"; import { isMultiSphere } from "./config.ts"; const LOADING_DELAY = 400; interface SphereState { pending: boolean; loading: boolean; data: SphereData | null; error: string | null; } export const sphereState = signal({ pending: true, loading: false, data: null, error: null, }); export const sphereHandle = computed(() => sphereState.value.data?.sphere.handle ?? null); /** * Load a sphere. In multi-sphere mode, pass the handle from the URL. * In self-hosted mode (no handle), loads `/api/spheres/current`. */ export async function loadSphere(handle?: string) { // In multi-sphere mode with no handle, there's no sphere to load (dashboard page) if (isMultiSphere && !handle) { sphereState.value = { pending: false, loading: false, data: null, error: null }; return; } sphereState.value = { pending: true, loading: false, data: null, error: null }; const timer = setTimeout(() => { sphereState.value = { ...sphereState.value, loading: true }; }, LOADING_DELAY); try { const url = handle ? `/api/spheres/${handle}` : "/api/spheres/current"; const data = await apiFetch(url); clearTimeout(timer); sphereState.value = { pending: false, loading: false, data, error: null }; } catch (err) { clearTimeout(timer); sphereState.value = { pending: false, loading: false, data: null, error: err instanceof Error ? err.message : "Failed to load sphere", }; } } /** Silent refresh — keeps existing data visible while fetching. */ export async function refreshSphere() { const handle = sphereHandle.value; // In multi-sphere mode with no current sphere, nothing to refresh if (isMultiSphere && !handle) return; try { const url = handle ? `/api/spheres/${handle}` : "/api/spheres/current"; const data = await apiFetch(url); sphereState.value = { pending: false, loading: false, data, error: null }; } catch (err) { sphereState.value = { ...sphereState.value, error: err instanceof Error ? err.message : "Failed to load sphere", }; } } /** Extract sphere handle from the current URL path in multi-sphere mode. */ export function getSphereHandleFromUrl(): string | null { const match = location.pathname.match(/^\/s\/([^/]+)/); return match?.[1] ?? null; }