Share expo-image cache; thumb placeholder; smoother dismiss

Two UX fixes for the peek preview:

1. Black flash on first peek — the preview VC now depends on SDWebImage
   directly (same version pinned by expo-image) and reads from
   `SDImageCache.shared`. On open it paints the fullsize synchronously if it's
   cached (common, since onPressIn prefetches), otherwise paints the thumb as a
   placeholder while the fullsize loads and swaps in. Also switches the image
   view to autoresizingMask so the dismiss animation interpolates cleanly
   without AutoLayout relayout mid-animation.

2. Two-stage dismiss animation on aspect-mismatched thumbnails — drop the
   explicit `previewForDismissingMenuWithConfiguration` override. With only
   the highlight provider implemented iOS reverses it end-to-end, which
   interpolates size and position together instead of snapping back to the
   preview's original size before translating.

https://claude.ai/code/session_015REmux3R9uuEMMJUHxTyQT
This commit is contained in:
Claude
2026-04-19 08:29:54 +00:00
committed by Samuel Newman
parent 2dc22c19b0
commit 1856c36221
9 changed files with 80 additions and 29 deletions
@@ -20,6 +20,7 @@ import * as ContextMenu from '../../../../modules/expo-bluesky-context-menu'
*/
export function ImageContextMenu({
fullsizeUri,
thumbUri,
aspectRatio,
borderRadius,
onPreviewPress,
@@ -27,6 +28,9 @@ export function ImageContextMenu({
children,
}: {
fullsizeUri: string
/** Thumbnail URL. Used as an instant placeholder in the native preview
* while the fullsize loads, so there's no black flash on first peek. */
thumbUri?: string
/** width / height; defaults to 1 if missing. */
aspectRatio: number | undefined
borderRadius?: number
@@ -54,6 +58,7 @@ export function ImageContextMenu({
preview={{
type: 'image',
uri: fullsizeUri,
thumbUri,
aspectRatio: aspectRatio && aspectRatio > 0 ? aspectRatio : 1,
}}
borderRadius={borderRadius}
+1
View File
@@ -80,6 +80,7 @@ export function ImageEmbed({
<View style={[a.mt_sm, rest.style]}>
<ImageContextMenu
fullsizeUri={image.fullsize}
thumbUri={image.thumb}
aspectRatio={aspect}
borderRadius={tokens.borderRadius.md}
onPreviewPress={() => onPreviewPress(0)}>
+1
View File
@@ -426,6 +426,7 @@ function GalleryImage({
aria-label={image.alt || l`Image ${index + 1} of ${imageCount}`}>
<ImageContextMenu
fullsizeUri={image.fullsize}
thumbUri={image.thumb}
aspectRatio={aspectRatio}
borderRadius={tokens.borderRadius.md}
onPreviewPress={onPreviewPress}>
@@ -66,6 +66,7 @@ export function GalleryItem({
<View style={a.flex_1} ref={containerRefs[index]} collapsable={false}>
<ImageContextMenu
fullsizeUri={image.fullsize}
thumbUri={image.thumb}
aspectRatio={aspect}
onPreviewPress={
onPreviewPress ? () => onPreviewPress(index) : undefined