// IndexedDB storage wrapper for persistent data storage // This solves iOS Safari's 7-day localStorage expiration issue const DB_NAME = 'SourdoughChronicleDB'; const DB_VERSION = 1; const STORE_NAME = 'breadData'; class SourdoughStorage { constructor() { this.db = null; this.initPromise = this.initDB(); } async initDB() { return new Promise((resolve, reject) => { const request = indexedDB.open(DB_NAME, DB_VERSION); request.onerror = () => { console.error('IndexedDB failed to open:', request.error); reject(request.error); }; request.onsuccess = () => { this.db = request.result; console.log('IndexedDB opened successfully'); resolve(this.db); }; request.onupgradeneeded = (event) => { const db = event.target.result; // Create object store if it doesn't exist if (!db.objectStoreNames.contains(STORE_NAME)) { const objectStore = db.createObjectStore(STORE_NAME, { keyPath: 'id' }); objectStore.createIndex('date', 'date', { unique: false }); console.log('Object store created'); } }; }); } async ensureDB() { if (!this.db) { await this.initPromise; } return this.db; } // Get all bread data async getAllBread() { try { const db = await this.ensureDB(); return new Promise((resolve, reject) => { const transaction = db.transaction([STORE_NAME], 'readonly'); const objectStore = transaction.objectStore(STORE_NAME); const request = objectStore.getAll(); request.onsuccess = () => { resolve(request.result || []); }; request.onerror = () => { console.error('Error getting all bread:', request.error); reject(request.error); }; }); } catch (error) { console.error('Error in getAllBread:', error); return []; } } // Save a single bread entry async saveBread(bread) { try { const db = await this.ensureDB(); return new Promise((resolve, reject) => { const transaction = db.transaction([STORE_NAME], 'readwrite'); const objectStore = transaction.objectStore(STORE_NAME); const request = objectStore.put(bread); request.onsuccess = () => { resolve(request.result); }; request.onerror = () => { console.error('Error saving bread:', request.error); reject(request.error); }; }); } catch (error) { console.error('Error in saveBread:', error); throw error; } } // Save all bread data (bulk operation) async saveAllBread(breadArray) { try { const db = await this.ensureDB(); return new Promise((resolve, reject) => { const transaction = db.transaction([STORE_NAME], 'readwrite'); const objectStore = transaction.objectStore(STORE_NAME); // Clear existing data first objectStore.clear(); // Add all bread entries breadArray.forEach(bread => { objectStore.put(bread); }); transaction.oncomplete = () => { resolve(); }; transaction.onerror = () => { console.error('Error saving all bread:', transaction.error); reject(transaction.error); }; }); } catch (error) { console.error('Error in saveAllBread:', error); throw error; } } // Delete a bread entry async deleteBread(breadId) { try { const db = await this.ensureDB(); return new Promise((resolve, reject) => { const transaction = db.transaction([STORE_NAME], 'readwrite'); const objectStore = transaction.objectStore(STORE_NAME); const request = objectStore.delete(breadId); request.onsuccess = () => { resolve(); }; request.onerror = () => { console.error('Error deleting bread:', request.error); reject(request.error); }; }); } catch (error) { console.error('Error in deleteBread:', error); throw error; } } // Get a specific bread entry async getBread(breadId) { try { const db = await this.ensureDB(); return new Promise((resolve, reject) => { const transaction = db.transaction([STORE_NAME], 'readonly'); const objectStore = transaction.objectStore(STORE_NAME); const request = objectStore.get(breadId); request.onsuccess = () => { resolve(request.result); }; request.onerror = () => { console.error('Error getting bread:', request.error); reject(request.error); }; }); } catch (error) { console.error('Error in getBread:', error); return null; } } // Migrate data from localStorage to IndexedDB async migrateFromLocalStorage() { try { const localData = localStorage.getItem('breadData'); if (localData) { const breadArray = JSON.parse(localData); console.log('Migrating', breadArray.length, 'entries from localStorage to IndexedDB'); await this.saveAllBread(breadArray); // Keep localStorage as backup for now console.log('Migration complete'); return true; } return false; } catch (error) { console.error('Error migrating from localStorage:', error); return false; } } // Request persistent storage permission async requestPersistentStorage() { if (navigator.storage && navigator.storage.persist) { try { const isPersisted = await navigator.storage.persist(); console.log('Persistent storage granted:', isPersisted); return isPersisted; } catch (error) { console.error('Error requesting persistent storage:', error); return false; } } else { console.log('Persistent storage API not available'); return false; } } // Check if persistent storage is already granted async isPersisted() { if (navigator.storage && navigator.storage.persisted) { try { return await navigator.storage.persisted(); } catch (error) { console.error('Error checking persistence:', error); return false; } } return false; } } // Create a singleton instance const storage = new SourdoughStorage();