import AsyncStorage from "@react-native-async-storage/async-storage"; import type { HostConfig, SavedComputer } from "./types"; const HOST_KEY = "pi-mobile:last-host"; const COMPUTERS_KEY = "pi-mobile:computers"; const PROJECTS_PREFIX = "pi-mobile:projects:"; function generateId(): string { return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; } function computerKey(host: string, port: string): string { return `${host.trim()}:${port.trim() || "8787"}`; } function sortByRecent(computers: SavedComputer[]): SavedComputer[] { return [...computers].sort((a, b) => b.lastUsedAt - a.lastUsedAt); } function projectsStorageKey(host: string, port: string): string { return `${PROJECTS_PREFIX}${host.trim()}:${port.trim() || "8787"}`; } export function buildBaseUrl(host: string, port: string): string { const trimmedHost = host.trim(); const trimmedPort = port.trim() || "8787"; const withProtocol = trimmedHost.startsWith("http") ? trimmedHost : `http://${trimmedHost}`; const url = new URL(withProtocol); url.port = trimmedPort; return url.origin; } export async function loadHostConfig(): Promise { const raw = await AsyncStorage.getItem(HOST_KEY); if (!raw) { return null; } try { return JSON.parse(raw) as HostConfig; } catch { return null; } } export async function saveHostConfig(config: HostConfig): Promise { await AsyncStorage.setItem(HOST_KEY, JSON.stringify(config)); } export async function loadSavedComputers(): Promise { const raw = await AsyncStorage.getItem(COMPUTERS_KEY); if (raw) { try { const parsed = JSON.parse(raw) as unknown; if (Array.isArray(parsed)) { return sortByRecent( parsed.filter( (entry): entry is SavedComputer => typeof entry === "object" && entry !== null && typeof (entry as SavedComputer).id === "string" && typeof (entry as SavedComputer).name === "string" && typeof (entry as SavedComputer).host === "string" && typeof (entry as SavedComputer).port === "string" && typeof (entry as SavedComputer).lastUsedAt === "number", ), ); } } catch { // fall through to migration } } const legacy = await loadHostConfig(); if (!legacy?.host.trim()) { return []; } const migrated: SavedComputer = { id: generateId(), name: legacy.host.trim(), host: legacy.host.trim(), port: legacy.port.trim() || "8787", lastUsedAt: Date.now(), }; await saveSavedComputers([migrated]); return [migrated]; } export async function saveSavedComputers(computers: SavedComputer[]): Promise { await AsyncStorage.setItem(COMPUTERS_KEY, JSON.stringify(sortByRecent(computers))); } export async function upsertSavedComputer( computers: SavedComputer[], host: string, port: string, name?: string, ): Promise { const trimmedHost = host.trim(); const trimmedPort = port.trim() || "8787"; if (!trimmedHost) { return computers; } const key = computerKey(trimmedHost, trimmedPort); const existing = computers.find((entry) => computerKey(entry.host, entry.port) === key); const trimmedName = name?.trim(); if (existing) { const next = computers.map((entry) => entry.id === existing.id ? { ...entry, name: trimmedName || entry.name, host: trimmedHost, port: trimmedPort, lastUsedAt: Date.now(), } : entry, ); await saveSavedComputers(next); return sortByRecent(next); } const next = [ { id: generateId(), name: trimmedName || trimmedHost, host: trimmedHost, port: trimmedPort, lastUsedAt: Date.now(), }, ...computers, ]; await saveSavedComputers(next); return sortByRecent(next); } export async function removeSavedComputer( computers: SavedComputer[], id: string, ): Promise { const next = computers.filter((entry) => entry.id !== id); await saveSavedComputers(next); return next; } export async function loadSavedProjects(host: string, port: string): Promise { const raw = await AsyncStorage.getItem(projectsStorageKey(host, port)); if (!raw) { return []; } try { const parsed = JSON.parse(raw) as unknown; if (!Array.isArray(parsed)) { return []; } return parsed.filter((entry): entry is string => typeof entry === "string" && entry.length > 0); } catch { return []; } } export async function saveSavedProjects( host: string, port: string, projects: string[], ): Promise { await AsyncStorage.setItem(projectsStorageKey(host, port), JSON.stringify(projects)); } export async function checkHealth(baseUrl: string): Promise { const response = await fetch(`${baseUrl}/health`); if (!response.ok) { return false; } const data = (await response.json()) as { ok?: boolean }; return data.ok === true; } export async function listFolders(baseUrl: string, path = "~") { const response = await fetch(`${baseUrl}/fs/list?path=${encodeURIComponent(path)}`); if (!response.ok) { const body = (await response.json().catch(() => ({}))) as { error?: string }; throw new Error(body.error ?? "Failed to list folders"); } return response.json(); } export async function createFolder( baseUrl: string, path: string, name: string, ): Promise<{ name: string; path: string }> { const response = await fetch(`${baseUrl}/fs/mkdir`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ path, name }), }); if (!response.ok) { const body = (await response.json().catch(() => ({}))) as { error?: string }; throw new Error(body.error ?? "Failed to create folder"); } return response.json(); }