From c9a91bbdadd4cc7df4645f82dccff04532298dd7 Mon Sep 17 00:00:00 2001 From: Maurice Date: Mon, 9 Nov 2026 09:16:58 +0100 Subject: [PATCH] fix(native): the native question reads no ref while rendering, and its tests type and lint clean --- .../screens/trip/plan/useMPlanDragReorder.test.ts | 13 +++++++------ client/src/native/keyboardFields.test.ts | 2 +- client/src/native/nativeDialogs.test.ts | 6 +++--- client/src/native/useNativeQuestion.ts | 11 +++++++++-- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/client/src/mobile/screens/trip/plan/useMPlanDragReorder.test.ts b/client/src/mobile/screens/trip/plan/useMPlanDragReorder.test.ts index 789777ce3..27073a77f 100644 --- a/client/src/mobile/screens/trip/plan/useMPlanDragReorder.test.ts +++ b/client/src/mobile/screens/trip/plan/useMPlanDragReorder.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest' import { renderHook, act } from '@testing-library/react' -import { useMPlanDragReorder } from './useMPlanDragReorder' +import { useMPlanDragReorder, type DragRowProps } from './useMPlanDragReorder' import { pressHaptic, selectionHaptic, tickHaptic } from '../../../../native/haptics' import type { MergedItem } from '../../../../utils/dayMerge' import type { PlanRow } from './planTimelineModel' @@ -122,14 +122,15 @@ describe('useMPlanDragReorder', () => { vi.mocked(selectionHaptic).mockClear() vi.mocked(tickHaptic).mockClear() const { result, onMove } = setup() - act(() => { result.current.dragPropsFor(rowFor(0))!.onDragStart(dragEvent()) }) + const rowProps = (i: number) => result.current.dragPropsFor(rowFor(i)) as DragRowProps + act(() => { rowProps(0).onDragStart(dragEvent()) }) expect(pressHaptic).toHaveBeenCalledTimes(1) - act(() => { result.current.dragPropsFor(rowFor(0))!.onDragOver(dragEvent()) }) + act(() => { rowProps(0).onDragOver(dragEvent()) }) expect(selectionHaptic).not.toHaveBeenCalled() - act(() => { result.current.dragPropsFor(rowFor(2))!.onDragOver(dragEvent()) }) - act(() => { result.current.dragPropsFor(rowFor(2))!.onDragOver(dragEvent()) }) + act(() => { rowProps(2).onDragOver(dragEvent()) }) + act(() => { rowProps(2).onDragOver(dragEvent()) }) expect(selectionHaptic).toHaveBeenCalledTimes(1) - act(() => { result.current.dragPropsFor(rowFor(2))!.onDrop(dragEvent()) }) + act(() => { rowProps(2).onDrop(dragEvent()) }) expect(tickHaptic).toHaveBeenCalledTimes(1) expect(onMove).toHaveBeenCalledTimes(1) }) diff --git a/client/src/native/keyboardFields.test.ts b/client/src/native/keyboardFields.test.ts index e7efef78f..a66006451 100644 --- a/client/src/native/keyboardFields.test.ts +++ b/client/src/native/keyboardFields.test.ts @@ -3,7 +3,7 @@ import { prepareField, prepareKeyboardFields } from './keyboardFields'; function field(html: string): HTMLInputElement { document.body.innerHTML = html; - return document.body.querySelector('input')!; + return document.body.querySelector('input') as HTMLInputElement; } afterEach(() => { diff --git a/client/src/native/nativeDialogs.test.ts b/client/src/native/nativeDialogs.test.ts index f94368eae..bfc7ed881 100644 --- a/client/src/native/nativeDialogs.test.ts +++ b/client/src/native/nativeDialogs.test.ts @@ -34,7 +34,7 @@ describe('canAskNatively', () => { describe('askNatively', () => { it('asks a destroying question on a sheet with the action in red, yes only for that action', async () => { - vi.mocked(ActionSheet.showActions).mockResolvedValue({ index: 0 }); + vi.mocked(ActionSheet.showActions).mockResolvedValue({ index: 0, canceled: false }); expect(await askNatively({ ...question, danger: true })).toBe(true); expect(ActionSheet.showActions).toHaveBeenCalledWith({ title: 'Delete place?', @@ -44,9 +44,9 @@ describe('askNatively', () => { { title: 'Cancel', style: 'CANCEL' }, ], }); - vi.mocked(ActionSheet.showActions).mockResolvedValue({ index: 1 }); + vi.mocked(ActionSheet.showActions).mockResolvedValue({ index: 1, canceled: false }); expect(await askNatively({ ...question, danger: true })).toBe(false); - vi.mocked(ActionSheet.showActions).mockResolvedValue({ index: -1 }); + vi.mocked(ActionSheet.showActions).mockResolvedValue({ index: -1, canceled: true }); expect(await askNatively({ ...question, danger: true })).toBe(false); }); diff --git a/client/src/native/useNativeQuestion.ts b/client/src/native/useNativeQuestion.ts index 706d47bb1..bd2e21f47 100644 --- a/client/src/native/useNativeQuestion.ts +++ b/client/src/native/useNativeQuestion.ts @@ -15,15 +15,22 @@ export function useNativeQuestion( ): boolean { const native = question !== null && canAskNatively(); const [failed, setFailed] = useState(false); + // A closed question forgets that the system failed it, so the next one is asked natively again. + const [wasOpen, setWasOpen] = useState(open); + if (open !== wasOpen) { + setWasOpen(open); + if (!open) setFailed(false); + } // Asked once per opening, also when StrictMode runs the effect twice. const asked = useRef(false); const latest = useRef({ question, answer }); - latest.current = { question, answer }; + useEffect(() => { + latest.current = { question, answer }; + }); useEffect(() => { if (!open) { asked.current = false; - setFailed(false); return; } if (!native || asked.current || !latest.current.question) return;