/** * Persistent storage for keys and app settings. * On mobile: expo-secure-store for key material, AsyncStorage for settings. * On web: falls back to localStorage (dev only). */ import * as SecureStore from 'expo-secure-store'; import AsyncStorage from '@react-native-async-storage/async-storage'; import type { KeyFile, Contact, NodeSettings } from './types'; // ─── Keys ───────────────────────────────────────────────────────────────────── const KEYFILE_KEY = 'dchain_keyfile'; const CONTACTS_KEY = 'dchain_contacts'; const SETTINGS_KEY = 'dchain_settings'; const CHATS_KEY = 'dchain_chats'; /** Save the key file in secure storage (encrypted on device). */ export async function saveKeyFile(kf: KeyFile): Promise { await SecureStore.setItemAsync(KEYFILE_KEY, JSON.stringify(kf)); } /** Load key file. Returns null if not set. */ export async function loadKeyFile(): Promise { const raw = await SecureStore.getItemAsync(KEYFILE_KEY); if (!raw) return null; return JSON.parse(raw) as KeyFile; } /** Delete key file (logout / factory reset). */ export async function deleteKeyFile(): Promise { await SecureStore.deleteItemAsync(KEYFILE_KEY); } // ─── Node settings ───────────────────────────────────────────────────────────── const DEFAULT_SETTINGS: NodeSettings = { nodeUrl: 'http://localhost:8081', contractId: '', }; export async function loadSettings(): Promise { const raw = await AsyncStorage.getItem(SETTINGS_KEY); if (!raw) return DEFAULT_SETTINGS; return { ...DEFAULT_SETTINGS, ...JSON.parse(raw) }; } export async function saveSettings(s: Partial): Promise { const current = await loadSettings(); await AsyncStorage.setItem(SETTINGS_KEY, JSON.stringify({ ...current, ...s })); } // ─── Contacts ───────────────────────────────────────────────────────────────── export async function loadContacts(): Promise { const raw = await AsyncStorage.getItem(CONTACTS_KEY); if (!raw) return []; return JSON.parse(raw) as Contact[]; } export async function saveContact(c: Contact): Promise { const contacts = await loadContacts(); const idx = contacts.findIndex(x => x.address === c.address); if (idx >= 0) contacts[idx] = c; else contacts.push(c); await AsyncStorage.setItem(CONTACTS_KEY, JSON.stringify(contacts)); } export async function deleteContact(address: string): Promise { const contacts = await loadContacts(); await AsyncStorage.setItem( CONTACTS_KEY, JSON.stringify(contacts.filter(c => c.address !== address)), ); } // ─── Message cache (per-chat local store) ──────────────────────────────────── export interface CachedMessage { id: string; from: string; text: string; timestamp: number; mine: boolean; } export async function loadMessages(chatId: string): Promise { const raw = await AsyncStorage.getItem(`${CHATS_KEY}_${chatId}`); if (!raw) return []; return JSON.parse(raw) as CachedMessage[]; } export async function appendMessage(chatId: string, msg: CachedMessage): Promise { const msgs = await loadMessages(chatId); // Deduplicate by id if (msgs.find(m => m.id === msg.id)) return; msgs.push(msg); // Keep last 500 messages per chat const trimmed = msgs.slice(-500); await AsyncStorage.setItem(`${CHATS_KEY}_${chatId}`, JSON.stringify(trimmed)); }