mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-21 22:31:46 +00:00
bcd2c8c959
Repos gated reads on raw navigator.onLine and the online branch had no try/catch, so a captive portal or connected-but-no-internet (navigator.onLine lying "true") threw a network error instead of serving the good cached copy — blanking the trip even though Dexie held it. - new onlineThenCache(onlineFn, cacheFn) helper: reads the cache when offline, and on a network-level failure (Axios error with no HTTP response). A genuine HTTP error (4xx/5xx — the server responded) is rethrown so callers still set error state / navigate, not masked by a stale cache. - gates only on navigator.onLine, NOT the connectivity probe: the probe is a coarse global flag and one failed health check would otherwise divert every read to the (possibly empty) cache even when the request would succeed. - every repo list/get read path routed through it (reads only — writes still go through the mutation queue so failures surface) - tests: captive-portal fallback, HTTP-error rethrow, non-Axios rethrow
96 lines
3.2 KiB
TypeScript
96 lines
3.2 KiB
TypeScript
import { packingApi } from '../api/client'
|
|
import { offlineDb, upsertPackingItems } from '../db/offlineDb'
|
|
import { mutationQueue, generateUUID, nextTempId } from '../sync/mutationQueue'
|
|
import { onlineThenCache } from './withOfflineFallback'
|
|
import type { PackingItem } from '../types'
|
|
|
|
export const packingRepo = {
|
|
async list(tripId: number | string): Promise<{ items: PackingItem[] }> {
|
|
return onlineThenCache(
|
|
async () => {
|
|
const result = await packingApi.list(tripId)
|
|
upsertPackingItems(result.items)
|
|
return result
|
|
},
|
|
async () => ({
|
|
items: await offlineDb.packingItems
|
|
.where('trip_id').equals(Number(tripId)).toArray(),
|
|
}),
|
|
)
|
|
},
|
|
|
|
async create(tripId: number | string, data: Record<string, unknown> & { name: string }): Promise<{ item: PackingItem }> {
|
|
if (!navigator.onLine) {
|
|
const tempId = nextTempId()
|
|
const tempItem: PackingItem = {
|
|
...(data as Partial<PackingItem>),
|
|
id: tempId,
|
|
trip_id: Number(tripId),
|
|
name: (data.name as string) ?? 'New item',
|
|
checked: 0,
|
|
} as PackingItem
|
|
await offlineDb.packingItems.put(tempItem)
|
|
const id = generateUUID()
|
|
await mutationQueue.enqueue({
|
|
id,
|
|
tripId: Number(tripId),
|
|
method: 'POST',
|
|
url: `/trips/${tripId}/packing`,
|
|
body: data,
|
|
resource: 'packingItems',
|
|
tempId,
|
|
})
|
|
return { item: tempItem }
|
|
}
|
|
const result = await packingApi.create(tripId, data)
|
|
offlineDb.packingItems.put(result.item)
|
|
return result
|
|
},
|
|
|
|
async update(tripId: number | string, id: number, data: Record<string, unknown>): Promise<{ item: PackingItem }> {
|
|
if (!navigator.onLine) {
|
|
const existing = await offlineDb.packingItems.get(id)
|
|
const optimistic: PackingItem = { ...(existing ?? {} as PackingItem), ...(data as Partial<PackingItem>), id }
|
|
await offlineDb.packingItems.put(optimistic)
|
|
const mutId = generateUUID()
|
|
const isTemp = id < 0
|
|
await mutationQueue.enqueue({
|
|
id: mutId,
|
|
tripId: Number(tripId),
|
|
method: 'PUT',
|
|
url: isTemp ? `/trips/${tripId}/packing/{id}` : `/trips/${tripId}/packing/${id}`,
|
|
body: data,
|
|
resource: 'packingItems',
|
|
entityId: id,
|
|
...(isTemp ? { tempEntityId: id } : {}),
|
|
})
|
|
return { item: optimistic }
|
|
}
|
|
const result = await packingApi.update(tripId, id, data)
|
|
offlineDb.packingItems.put(result.item)
|
|
return result
|
|
},
|
|
|
|
async delete(tripId: number | string, id: number): Promise<unknown> {
|
|
if (!navigator.onLine) {
|
|
await offlineDb.packingItems.delete(id)
|
|
const mutId = generateUUID()
|
|
const isTemp = id < 0
|
|
await mutationQueue.enqueue({
|
|
id: mutId,
|
|
tripId: Number(tripId),
|
|
method: 'DELETE',
|
|
url: isTemp ? `/trips/${tripId}/packing/{id}` : `/trips/${tripId}/packing/${id}`,
|
|
body: undefined,
|
|
resource: 'packingItems',
|
|
entityId: id,
|
|
...(isTemp ? { tempEntityId: id } : {}),
|
|
})
|
|
return { success: true }
|
|
}
|
|
const result = await packingApi.delete(tripId, id)
|
|
offlineDb.packingItems.delete(id)
|
|
return result
|
|
},
|
|
}
|