All create/update/delete repo methods now write to IndexedDB optimistically
and fire mutationQueue.flush() as fire-and-forget, returning immediately
without waiting for the network. This eliminates the 8-second UX freeze
previously seen when the API was unreachable but navigator.onLine was true.
- Repos rewritten: trip, day, place, packing, todo, budget, accommodation,
reservation, file — write methods never throw, always return optimistic data
- mutationQueue.flush() changed to iterative (one item per loop iteration)
so mutations enqueued mid-flush (e.g. bulk check-all) are picked up
- fileRepo.toggleStar skips the IDB put when the file is not cached locally
- DayDetailPanel passes place_name into accommodationRepo.create so the
optimistic accommodation renders the correct hotel label immediately
- Test suite updated throughout to reflect optimistic-first semantics:
no more rollback assertions, IDB cleared in component test beforeEach hooks,
FileManager tests switched from filesApi spy to MSW endpoint assertions
Add navigator.onLine guard to SWR refresh IIFEs so background
network calls don't fire in offline mode (prevents fake-IDB leakage
in tests via MSW default handlers).
Fix IDB isolation in affected test files by flushing pending macro
tasks then clearing IDB tables in beforeEach, so stale IDB writes
from previous tests' background IIFEs don't bleed into the next test.
Restore loadBudgetItems and refreshPlaces to apply background refresh
results to store state.
Move tags/categories API calls before the main Promise.all in
loadTrip so MSW handlers resolve during the await window.
navigator.onLine is unreliable on Android — returns true whenever any
network interface is up, regardless of actual reachability. This caused
all repo reads to take the API branch and either wait 5 s for the SW
NetworkFirst timeout (cache hit) or hang indefinitely (cache miss).
- All read repos (list/get) now return cached IndexedDB data instantly
and carry a background refresh promise that resolves to fresh data or
null on failure. Callers that opted in (loadTrip, loadTrips) apply
fresh data silently when it arrives.
- tripStore.loadTrip: Promise.all now reads all 7 resources from
IndexedDB (instant), fires network refreshes in background, sets
isLoading: false immediately, then applies fresh data via a second
Promise.all when ready. Tags/categories use upsertTags/upsertCategories.
- DashboardPage.loadTrips: same pattern — renders from cache instantly,
silently updates trip list on refresh.
- axios timeout set to 8 s so requests can never hang indefinitely.
- SW networkTimeoutSeconds lowered from 5 to 2 as defence in depth.