ImageViewer resolveExportSrc — resolve the download address before the default export (WEB-1541)
Version: 0.10.0 · Type: ✨ Feature
Problem
ImageViewer's export had exactly two granularities: the built-in default download (fetch to a blob → same-origin objectURL → <a download>, with cache: 'reload' to bypass an HTTP cache poisoned by a no-cors <img>, fallback to the raw address when the fetch fails, and delayed objectURL revocation), or a full onExport takeover that skips all of it.
The downstream requirement in ANT-1620 is narrower than either: on a web4 recording detail page left open past the presigned URL's TTL, downloading a highlight screenshot still uses the locally cached, now-expired URL and S3 answers AccessDenied / Request has expired. The <img> keeps showing the image — it loaded long ago and never re-requests — so the viewer-level self-heal never fires. Only the address needs to change; the blob / cache / cross-origin / filename behavior should stay exactly as-is.
With only onExport available, a consumer had to take over and re-implement the export orchestration (resolve address → download → fall back) outside the component. That is a second copy of logic the component already owns: if the default path later grows analytics, a failure toast, or multi-image support, the consumer's branch silently stops matching.
Changed Files
packages/design/src/components/ImageViewer/ImageViewer.tsxpackages/design/src/components/ImageViewer/__tests__/ImageViewer.test.tsxpackages/design-site/docs/components/image-viewer.mdx(and its zh-CN copy)
Changes
- Add an optional
resolveExportSrc?: (image, index) => string | null | undefined | Promise<string | null | undefined>prop that answers only "which address to download". A returned address is used; an empty value or a thrown error falls back toimage.src, matching the existing blob-fetch failure fallback so nothing fails silently. - Extract the default export path into
runDefaultExport(image, index):await resolveExportSrcinside atry/catch, then hand the resulting address to the internal download. The download implementation (downloadImageBySrc) stays module-private and is not part of the public API. - Define the precedence explicitly: passing
onExportis a full takeover, soresolveExportSrcis never invoked in that case. Documented in both the prop JSDoc and the component docs. - Add four tests: download the resolved address, fall back to
image.srcon an empty resolve, fall back toimage.srcon a rejected resolve, and confirmonExportbypassesresolveExportSrcentirely. - Document the third export mode in
image-viewer.mdx(both locales): a new Props-table row plus an expanded "export download mechanics" section.
Impact
Purely additive. Omitting resolveExportSrc leaves both the default download path and the onExport takeover path unchanged, hence minor rather than a breaking change. Note that the export button shows no loading state while the address resolves — the same wait window as today's fetch before the download starts.
The first consumers are web4's HighlightImageAttachment (re-signs via picture_link only when the URL is expired) and ImageFilePage (re-signs via useSourceUrlSelfHeal.resolveFreshUrl). Both now keep only the genuinely business-specific decision — whether to re-sign — and leave the fallback semantics to the component's documented contract.