fix(journey): keep page mounted during in-place journey refetch

loadJourney previously set loading=true unconditionally, causing the
JourneyDetailPage guard (if loading || !current) to unmount the entire
page tree on every background refetch — entry saves, settings saves,
trip link/unlink, contributor invite, delete, and WS realtime events
all triggered the full-page spinner flash.

Now loading is only toggled on cold loads (current?.id !== id).
Warm refreshes replace current silently so the hero, sidebar, map,
and timeline stay mounted throughout. Closes #673.
This commit is contained in:
jubnl
2026-04-16 15:27:13 +02:00
parent 0c4de72356
commit 75d23eb6aa
2 changed files with 44 additions and 2 deletions
+41
View File
@@ -314,6 +314,47 @@ describe('journeyStore', () => {
expect(storedEntry?.photos[0].id).toBe(201);
});
// ── loadJourney silent refresh ───────────────────────────────────────────
it('FE-STORE-JOURNEY-016: loadJourney does not set loading when refreshing same journey', async () => {
const existing = buildJourneyDetail({ id: 5, title: 'Old' });
useJourneyStore.setState({ current: existing, loading: false });
const loadingValues: boolean[] = [];
const unsub = useJourneyStore.subscribe(s => loadingValues.push(s.loading));
const refreshed = buildJourneyDetail({ id: 5, title: 'Refreshed' });
server.use(
http.get('/api/journeys/5', () => HttpResponse.json(refreshed))
);
await useJourneyStore.getState().loadJourney(5);
unsub();
expect(loadingValues.every(v => v === false)).toBe(true);
expect(useJourneyStore.getState().current?.title).toBe('Refreshed');
});
it('FE-STORE-JOURNEY-017: loadJourney sets loading on cold load (different journey)', async () => {
const existing = buildJourneyDetail({ id: 5 });
useJourneyStore.setState({ current: existing, loading: false });
const loadingValues: boolean[] = [];
const unsub = useJourneyStore.subscribe(s => loadingValues.push(s.loading));
const other = buildJourneyDetail({ id: 99 });
server.use(
http.get('/api/journeys/99', () => HttpResponse.json(other))
);
await useJourneyStore.getState().loadJourney(99);
unsub();
expect(loadingValues).toContain(true);
expect(useJourneyStore.getState().current?.id).toBe(99);
expect(useJourneyStore.getState().loading).toBe(false);
});
// ── clear ────────────────────────────────────────────────────────────────
it('FE-STORE-JOURNEY-015: clear resets state', () => {
+3 -2
View File
@@ -124,7 +124,8 @@ export const useJourneyStore = create<JourneyState>((set, get) => ({
},
loadJourney: async (id) => {
set({ loading: true, notFound: false })
const cold = get().current?.id !== id
if (cold) set({ loading: true, notFound: false })
try {
const data = await journeyApi.get(id)
set({ current: data })
@@ -134,7 +135,7 @@ export const useJourneyStore = create<JourneyState>((set, get) => ({
}
throw err
} finally {
set({ loading: false })
if (cold) set({ loading: false })
}
},