14c880123c
Expo app with dashboard, time clock, invoices, and settings — native tabs, glass UI, theme-aware components, and iOS Live Activities. Co-authored-by: Cursor <cursoragent@cursor.com>
43 lines
1.3 KiB
TypeScript
43 lines
1.3 KiB
TypeScript
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
|
|
const STORAGE_KEY = "beenvoice:instance-url";
|
|
|
|
export function normalizeInstanceUrl(input: string): string | null {
|
|
const trimmed = input.trim().replace(/\/$/, "");
|
|
if (!trimmed) return null;
|
|
|
|
let url = trimmed;
|
|
if (!/^https?:\/\//i.test(url)) {
|
|
const isLocal =
|
|
/^(localhost|127\.|192\.168\.|10\.|172\.(1[6-9]|2\d|3[01])\.)/i.test(url);
|
|
url = `${isLocal ? "http" : "https"}://${url}`;
|
|
}
|
|
|
|
try {
|
|
const parsed = new URL(url);
|
|
if (!parsed.hostname) return null;
|
|
return `${parsed.protocol}//${parsed.host}`;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export async function loadStoredInstanceUrl(): Promise<string | null> {
|
|
const stored = await AsyncStorage.getItem(STORAGE_KEY);
|
|
if (!stored) return null;
|
|
return normalizeInstanceUrl(stored) ?? stored.replace(/\/$/, "");
|
|
}
|
|
|
|
export async function saveStoredInstanceUrl(url: string): Promise<string> {
|
|
const normalized = normalizeInstanceUrl(url);
|
|
if (!normalized) {
|
|
throw new Error("Enter a valid server URL (e.g. beenvoice.app or localhost:3000)");
|
|
}
|
|
await AsyncStorage.setItem(STORAGE_KEY, normalized);
|
|
return normalized;
|
|
}
|
|
|
|
export async function clearStoredInstanceUrl(): Promise<void> {
|
|
await AsyncStorage.removeItem(STORAGE_KEY);
|
|
}
|