From fbf6cd2840835ef01a40bb4120e36acb49843c4f Mon Sep 17 00:00:00 2001 From: Maurice Date: Wed, 21 Oct 2026 15:18:01 +0200 Subject: [PATCH] fix(journey): read a day kept meanwhile again to offer replace, date the phone row in the app language --- .../Journey/useJourneyRecordingDay.test.ts | 21 ++++++++++++++++++- .../Journey/useJourneyRecordingDay.ts | 6 +++++- .../mobile/screens/journey/MJourneyDetail.tsx | 4 ++-- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/client/src/components/Journey/useJourneyRecordingDay.test.ts b/client/src/components/Journey/useJourneyRecordingDay.test.ts index 246475952..858d105db 100644 --- a/client/src/components/Journey/useJourneyRecordingDay.test.ts +++ b/client/src/components/Journey/useJourneyRecordingDay.test.ts @@ -1,4 +1,4 @@ -// FE-JRN-RECORDING-DAY-001 to FE-JRN-RECORDING-DAY-007 +// FE-JRN-RECORDING-DAY-001 to FE-JRN-RECORDING-DAY-008 // // JRN-01: taking one day's recorded route over, for the desktop dialog and the // phone sheet alike. Nothing is kept before the button, what is kept is the @@ -123,6 +123,25 @@ describe('useJourneyRecordingDay', () => { expect(toast.error).not.toHaveBeenCalled(); }); + it('FE-JRN-RECORDING-DAY-008: a day someone else kept meanwhile is read again and offers Replace, not a Keep that fails', async () => { + repo.preview.mockResolvedValue(buildRecordingPreview()); + repo.save.mockRejectedValue(httpError('recording_exists')); + const { result, onKept, onClose } = setup(); + await waitFor(() => expect(result.current.action).toBe('keep')); + + repo.preview.mockResolvedValue( + buildRecordingPreview({ stored: buildRecordedDay({ contentHash: 'b'.repeat(64) }) }) + ); + await act(() => result.current.save()); + + expect(toast.error).toHaveBeenCalledWith('This journey already keeps another recording of that day.'); + await waitFor(() => expect(result.current.action).toBe('replace')); + expect(repo.preview).toHaveBeenCalledTimes(2); + expect(result.current.changedSincePreview).toBe(false); + expect(onKept).not.toHaveBeenCalled(); + expect(onClose).not.toHaveBeenCalled(); + }); + it('FE-JRN-RECORDING-DAY-006: says why the day could not be read, and what was recorded nothing of has nothing to keep', async () => { repo.preview.mockRejectedValueOnce(httpError('not_connected')); const { result } = setup(); diff --git a/client/src/components/Journey/useJourneyRecordingDay.ts b/client/src/components/Journey/useJourneyRecordingDay.ts index 02d56d1e3..caa22a5ee 100644 --- a/client/src/components/Journey/useJourneyRecordingDay.ts +++ b/client/src/components/Journey/useJourneyRecordingDay.ts @@ -106,11 +106,15 @@ export function useJourneyRecordingDay({ toast.success(t(action === 'replace' ? 'journey.recording.replacedToast' : 'journey.recording.keptToast')); onClose(); } catch (err) { - if (recordingErrorCode(err) === 'recording_changed') { + const code = recordingErrorCode(err); + if (code === 'recording_changed') { setChangedSincePreview(true); retry(); } else { toast.error(recordingErrorText(err, t)); + // Someone kept the day meanwhile: read it again so the dialog shows + // their version and offers Replace instead of a Keep that cannot work. + if (code === 'recording_exists') retry(); } } finally { setBusy(null); diff --git a/client/src/mobile/screens/journey/MJourneyDetail.tsx b/client/src/mobile/screens/journey/MJourneyDetail.tsx index 2cbc9f32e..2f0849518 100644 --- a/client/src/mobile/screens/journey/MJourneyDetail.tsx +++ b/client/src/mobile/screens/journey/MJourneyDetail.tsx @@ -40,7 +40,7 @@ import MJourneyRecordingSheet from './MJourneyRecordingSheet' */ export default function MJourneyDetail({ journeyId, embedded = false }: JourneyDetailOptions = {}) { const { - id, navigate, toast, t, + id, navigate, toast, t, locale, current, loading, canEditEntries, canEditJourney, view, setView, @@ -515,7 +515,7 @@ export default function MJourneyDetail({ journeyId, embedded = false }: JourneyD { setShowActionMenu(false); recordings.open(entries[activeIndex].entry_date) }} />