diff --git a/apps/mobile/src/features/threads/ThreadMarkdownImage.test.tsx b/apps/mobile/src/features/threads/ThreadMarkdownImage.test.tsx index 14d595950e223..e3927ffba376d 100644 --- a/apps/mobile/src/features/threads/ThreadMarkdownImage.test.tsx +++ b/apps/mobile/src/features/threads/ThreadMarkdownImage.test.tsx @@ -118,3 +118,45 @@ it("fetches a failed SVG again on the next mount but keeps a parsed one", async registry.dispose(); } }); + +it("does not parse an obsolete SVG response after its last reader unmounts", async () => { + (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; + const registry = AtomRegistry.make({ + scheduleTask: (task) => { + queueMicrotask(task); + return () => {}; + }, + }); + const root = createRoot(document.createElement("div")); + const fetchSvg = vi.mocked(fetch); + const parseSvg = vi.mocked(parse); + fetchSvg.mockReset(); + parseSvg.mockClear(); + const body = Promise.withResolvers(); + fetchSvg.mockResolvedValue({ ok: true, status: 200, text: () => body.promise } as never); + try { + await act(async () => + root.render( + + {}} + /> + , + ), + ); + expect(fetchSvg).toHaveBeenCalledOnce(); + const signal = fetchSvg.mock.calls[0]![1]!.signal!; + await act(async () => root.render(null)); + expect(signal.aborted).toBe(true); + await act(async () => body.resolve("")); + expect(parseSvg).not.toHaveBeenCalled(); + } finally { + await act(async () => root.unmount()); + registry.dispose(); + } +}); diff --git a/apps/mobile/src/features/threads/ThreadMarkdownImage.tsx b/apps/mobile/src/features/threads/ThreadMarkdownImage.tsx index bad77ec4edd8b..8fe6fc1930121 100644 --- a/apps/mobile/src/features/threads/ThreadMarkdownImage.tsx +++ b/apps/mobile/src/features/threads/ThreadMarkdownImage.tsx @@ -70,7 +70,9 @@ const svgDocumentAtom = Atom.family((uri: string) => if (cached !== null) return Effect.succeed(cached); return Effect.tryPromise({ try: async (signal): Promise => { - const ast = parse(await readSvgXml(uri, signal)); + const xml = await readSvgXml(uri, signal); + signal.throwIfAborted(); + const ast = parse(xml); if (!ast) throw new Error("SVG has no root element"); return { ast, size: resolveSvgImageSize(ast.props) }; },