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>
68 lines
1.9 KiB
TypeScript
68 lines
1.9 KiB
TypeScript
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
|
|
const ACCOUNTS_KEY = "beenvoice:accounts";
|
|
const ACTIVE_ACCOUNT_KEY = "beenvoice:active-account-id";
|
|
const DRAFT_INSTANCE_URL_KEY = "beenvoice:draft-instance-url";
|
|
|
|
export type SavedAccount = {
|
|
id: string;
|
|
instanceUrl: string;
|
|
userId: string;
|
|
email: string;
|
|
name: string;
|
|
lastUsedAt: number;
|
|
};
|
|
|
|
export function buildAccountId(instanceUrl: string, userId: string) {
|
|
const host = instanceUrl.replace(/^https?:\/\//, "").replace(/\/$/, "");
|
|
return `${host}::${userId}`;
|
|
}
|
|
|
|
export function authStoragePrefix(accountId: string) {
|
|
return `beenvoice:auth:${accountId}`;
|
|
}
|
|
|
|
export async function loadAccounts(): Promise<SavedAccount[]> {
|
|
const raw = await AsyncStorage.getItem(ACCOUNTS_KEY);
|
|
if (!raw) return [];
|
|
try {
|
|
const parsed = JSON.parse(raw) as SavedAccount[];
|
|
return Array.isArray(parsed) ? parsed : [];
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
export async function saveAccounts(accounts: SavedAccount[]) {
|
|
await AsyncStorage.setItem(ACCOUNTS_KEY, JSON.stringify(accounts));
|
|
}
|
|
|
|
export async function loadActiveAccountId(): Promise<string | null> {
|
|
return AsyncStorage.getItem(ACTIVE_ACCOUNT_KEY);
|
|
}
|
|
|
|
export async function saveActiveAccountId(accountId: string | null) {
|
|
if (accountId) {
|
|
await AsyncStorage.setItem(ACTIVE_ACCOUNT_KEY, accountId);
|
|
} else {
|
|
await AsyncStorage.removeItem(ACTIVE_ACCOUNT_KEY);
|
|
}
|
|
}
|
|
|
|
export async function loadDraftInstanceUrl(): Promise<string | null> {
|
|
return AsyncStorage.getItem(DRAFT_INSTANCE_URL_KEY);
|
|
}
|
|
|
|
export async function saveDraftInstanceUrl(url: string | null) {
|
|
if (url) {
|
|
await AsyncStorage.setItem(DRAFT_INSTANCE_URL_KEY, url);
|
|
} else {
|
|
await AsyncStorage.removeItem(DRAFT_INSTANCE_URL_KEY);
|
|
}
|
|
}
|
|
|
|
export async function hasConfiguredInstanceUrl(): Promise<boolean> {
|
|
const [accounts, draft] = await Promise.all([loadAccounts(), loadDraftInstanceUrl()]);
|
|
return accounts.length > 0 || Boolean(draft);
|
|
}
|