- Added comprehensive error handling for PGLite IndexedDB initialization - Implemented iOS Safari detection and in-memory database fallback - Added timeout handling to prevent infinite loading states - Improved error boundaries with user-friendly error messages - Enhanced migration robustness with detailed error logging - Provides 'Retry' functionality for transient failures Fixes iPhone app showing only spinner and TabBar without content loading
124 lines
3.6 KiB
TypeScript
124 lines
3.6 KiB
TypeScript
import { STORAGE_KEYS } from "@/shared/lib/constants"
|
|
import type { PGlite } from "@electric-sql/pglite"
|
|
|
|
/** One-time migration: read existing localStorage data, insert into PGlite, remove localStorage keys. */
|
|
export async function migrateFromLocalStorage(db: PGlite): Promise<void> {
|
|
console.log("Starting localStorage migration...")
|
|
|
|
try {
|
|
// device ID
|
|
const deviceId = localStorage.getItem(STORAGE_KEYS.deviceId)
|
|
if (deviceId) {
|
|
try {
|
|
await db.query(
|
|
"INSERT INTO device (id) VALUES ($1) ON CONFLICT DO NOTHING",
|
|
[deviceId],
|
|
)
|
|
localStorage.removeItem(STORAGE_KEYS.deviceId)
|
|
console.log("Migrated device ID")
|
|
} catch (error) {
|
|
console.warn("Failed to migrate device ID:", error)
|
|
}
|
|
}
|
|
|
|
// follows
|
|
const followsRaw = localStorage.getItem(STORAGE_KEYS.follows)
|
|
if (followsRaw) {
|
|
try {
|
|
const follows = JSON.parse(followsRaw) as Array<{
|
|
type: string
|
|
entity_id: number
|
|
label: string
|
|
}>
|
|
for (const f of follows) {
|
|
try {
|
|
await db.query(
|
|
"INSERT INTO follows (type, entity_id, label) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING",
|
|
[f.type, f.entity_id, f.label],
|
|
)
|
|
} catch (error) {
|
|
console.warn("Failed to migrate follow item:", f, error)
|
|
}
|
|
}
|
|
localStorage.removeItem(STORAGE_KEYS.follows)
|
|
console.log("Migrated follows")
|
|
} catch (error) {
|
|
console.warn("Failed to migrate follows:", error)
|
|
}
|
|
}
|
|
|
|
// feed cache
|
|
const feedRaw = localStorage.getItem(STORAGE_KEYS.feedCache)
|
|
if (feedRaw) {
|
|
try {
|
|
const parsed = JSON.parse(feedRaw) as {
|
|
items: unknown[]
|
|
updatedAt: number
|
|
}
|
|
if (Array.isArray(parsed.items)) {
|
|
await db.query(
|
|
`INSERT INTO feed_cache (id, data, updated_at) VALUES ('feed_items', $1, to_timestamp($2 / 1000.0))
|
|
ON CONFLICT (id) DO UPDATE SET data = $1, updated_at = to_timestamp($2 / 1000.0)`,
|
|
[JSON.stringify({ items: parsed.items }), parsed.updatedAt],
|
|
)
|
|
}
|
|
localStorage.removeItem(STORAGE_KEYS.feedCache)
|
|
console.log("Migrated feed cache")
|
|
} catch (error) {
|
|
console.warn("Failed to migrate feed cache:", error)
|
|
}
|
|
}
|
|
|
|
// geo cache
|
|
const geoRaw = localStorage.getItem(STORAGE_KEYS.geoCache)
|
|
if (geoRaw) {
|
|
try {
|
|
const cache = JSON.parse(geoRaw) as Record<
|
|
string,
|
|
{ timestamp: number; result: unknown }
|
|
>
|
|
for (const [bundesland, entry] of Object.entries(cache)) {
|
|
try {
|
|
await db.query(
|
|
`INSERT INTO geo_cache (bundesland, data, cached_at) VALUES ($1, $2, to_timestamp($3 / 1000.0))
|
|
ON CONFLICT (bundesland) DO UPDATE SET data = $2, cached_at = to_timestamp($3 / 1000.0)`,
|
|
[bundesland, JSON.stringify(entry.result), entry.timestamp],
|
|
)
|
|
} catch (error) {
|
|
console.warn(
|
|
"Failed to migrate geo cache entry:",
|
|
bundesland,
|
|
error,
|
|
)
|
|
}
|
|
}
|
|
localStorage.removeItem(STORAGE_KEYS.geoCache)
|
|
console.log("Migrated geo cache")
|
|
} catch (error) {
|
|
console.warn("Failed to migrate geo cache:", error)
|
|
}
|
|
}
|
|
|
|
// push enabled
|
|
const pushEnabled = localStorage.getItem(STORAGE_KEYS.pushEnabled)
|
|
if (pushEnabled) {
|
|
try {
|
|
await db.query(
|
|
`INSERT INTO push_state (key, value) VALUES ('enabled', $1)
|
|
ON CONFLICT (key) DO UPDATE SET value = $1`,
|
|
[pushEnabled],
|
|
)
|
|
localStorage.removeItem(STORAGE_KEYS.pushEnabled)
|
|
console.log("Migrated push enabled state")
|
|
} catch (error) {
|
|
console.warn("Failed to migrate push enabled state:", error)
|
|
}
|
|
}
|
|
|
|
console.log("localStorage migration completed")
|
|
} catch (error) {
|
|
console.error("Migration failed with critical error:", error)
|
|
// Don't throw - let the app continue with empty database
|
|
}
|
|
}
|