Files
TREK/client/src/repo/fileRepo.ts
T
jubnl 852f0085d1 feat: complete offline write support with mutation queue + runtime SW cache config
- Add offline CRUD to todoRepo, budgetRepo, reservationRepo, accommodationRepo,
  dayRepo, tripRepo, fileRepo with optimistic Dexie writes and mutation queue
- Wire all store slices (todo, budget, reservations, files, dayNotes, assignments,
  tripStore) through repos for offline-aware writes
- Cover archive/unarchive, file toggleStar/update/delete, assignment create/delete,
  day title/notes update offline paths
- Migrate service worker from generateSW to injectManifest (custom sw.ts) with
  runtime-configurable api-data (7d/500) and map-tiles (30d/1000) cache policies
- Add Settings → Offline cache configuration UI with save/reset and live SW postMessage
- Extend mutationQueue flush to cover all writable Dexie tables
2026-05-04 21:36:44 +02:00

77 lines
2.4 KiB
TypeScript

import { filesApi } from '../api/client'
import { offlineDb, upsertTripFiles } from '../db/offlineDb'
import { mutationQueue, generateUUID } from '../sync/mutationQueue'
import type { TripFile } from '../types'
export const fileRepo = {
async list(tripId: number | string): Promise<{ files: TripFile[] }> {
if (!navigator.onLine) {
const cached = await offlineDb.tripFiles
.where('trip_id')
.equals(Number(tripId))
.toArray()
return { files: cached }
}
const result = await filesApi.list(tripId)
upsertTripFiles(result.files)
return result
},
async update(tripId: number | string, id: number, data: Record<string, unknown>): Promise<unknown> {
if (!navigator.onLine) {
const existing = await offlineDb.tripFiles.get(id)
if (existing) await offlineDb.tripFiles.put({ ...existing, ...(data as Partial<TripFile>) })
await mutationQueue.enqueue({
id: generateUUID(),
tripId: Number(tripId),
method: 'PUT',
url: `/trips/${tripId}/files/${id}`,
body: data,
resource: 'tripFiles',
})
return { success: true }
}
const result = await filesApi.update(tripId, id, data)
const file = (result as { file?: TripFile }).file
if (file) offlineDb.tripFiles.put(file)
return result
},
async toggleStar(tripId: number | string, id: number): Promise<unknown> {
if (!navigator.onLine) {
const existing = await offlineDb.tripFiles.get(id)
if (existing) {
await offlineDb.tripFiles.put({ ...existing, starred: existing.starred ? 0 : 1 })
}
await mutationQueue.enqueue({
id: generateUUID(),
tripId: Number(tripId),
method: 'PATCH',
url: `/trips/${tripId}/files/${id}/star`,
body: undefined,
})
return { success: true }
}
return filesApi.toggleStar(tripId, id)
},
async delete(tripId: number | string, id: number): Promise<unknown> {
if (!navigator.onLine) {
await offlineDb.tripFiles.delete(id)
await mutationQueue.enqueue({
id: generateUUID(),
tripId: Number(tripId),
method: 'DELETE',
url: `/trips/${tripId}/files/${id}`,
body: undefined,
resource: 'tripFiles',
entityId: id,
})
return { success: true }
}
const result = await filesApi.delete(tripId, id)
offlineDb.tripFiles.delete(id)
return result
},
}