fix: load draft preview images and get dimensions

Fix draft preview images not showing on web and add proper aspect
ratio support:

1. Try to load all images regardless of the exists cache flag, which
   may be stale due to async cache population timing
2. Use Image.loadAsync() from expo-image to get image dimensions
3. Pass dimensions to viewImages for proper aspect ratio in previews

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-01-16 11:30:46 +02:00
parent 0992c9376e
commit 7c129c0aec
+22 -17
View File
@@ -5,12 +5,12 @@ import {type AppBskyEmbedImages} from '@atproto/api'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {isNative} from '#/platform/detection'
import { import {
type DraftPostDisplay, type DraftPostDisplay,
type DraftSummary, type DraftSummary,
type LocalMediaDisplay, type LocalMediaDisplay,
} from '#/state/drafts' } from '#/state/drafts'
import * as storage from '#/state/drafts/storage'
import {useCurrentAccountProfile} from '#/state/queries/useCurrentAccountProfile' import {useCurrentAccountProfile} from '#/state/queries/useCurrentAccountProfile'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {TimeElapsed} from '#/view/com/util/TimeElapsed' import {TimeElapsed} from '#/view/com/util/TimeElapsed'
@@ -23,11 +23,6 @@ import {ImageLayoutGrid} from '#/components/images/ImageLayoutGrid'
import * as Prompt from '#/components/Prompt' import * as Prompt from '#/components/Prompt'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
// Platform-specific storage import
const storage = isNative
? require('#/state/drafts/storage')
: require('#/state/drafts/storage.web')
export function DraftItem({ export function DraftItem({
draft, draft,
onSelect, onSelect,
@@ -215,6 +210,8 @@ function DraftPostRow({
type LoadedImage = { type LoadedImage = {
url: string url: string
meta: LocalMediaDisplay meta: LocalMediaDisplay
width?: number
height?: number
} }
function DraftMediaPreview({post}: {post: DraftPostDisplay}) { function DraftMediaPreview({post}: {post: DraftPostDisplay}) {
@@ -223,17 +220,25 @@ function DraftMediaPreview({post}: {post: DraftPostDisplay}) {
useEffect(() => { useEffect(() => {
async function loadMedia() { async function loadMedia() {
// Load images that exist locally // Try to load all images - the exists flag may be stale due to async cache
if (post.images && post.images.length > 0) { if (post.images && post.images.length > 0) {
const loaded: LoadedImage[] = [] const loaded: LoadedImage[] = []
for (const image of post.images) { for (const image of post.images) {
if (image.exists) { try {
const url = await storage.loadMediaFromLocal(image.localPath)
// Get dimensions using expo-image's loadAsync
let width: number | undefined
let height: number | undefined
try { try {
const url = await storage.loadMediaFromLocal(image.localPath) const imageRef = await Image.loadAsync(url)
loaded.push({url, meta: image}) width = imageRef.width
} catch (e) { height = imageRef.height
console.warn('Failed to load draft image', e) } catch {
// Dimensions unavailable, will use default aspect ratio
} }
loaded.push({url, meta: {...image, exists: true}, width, height})
} catch (e) {
// Image doesn't exist locally, skip it
} }
} }
setLoadedImages(loaded) setLoadedImages(loaded)
@@ -245,16 +250,16 @@ function DraftMediaPreview({post}: {post: DraftPostDisplay}) {
// Convert loaded images to ViewImage format for the embed components // Convert loaded images to ViewImage format for the embed components
const viewImages = useMemo<AppBskyEmbedImages.ViewImage[]>(() => { const viewImages = useMemo<AppBskyEmbedImages.ViewImage[]>(() => {
return loadedImages.map(({url}) => ({ return loadedImages.map(({url, width, height, meta}) => ({
thumb: url, thumb: url,
fullsize: url, fullsize: url,
alt: '', alt: meta.altText || '',
aspectRatio: undefined, // No dimensions stored in new schema aspectRatio: width && height ? {width, height} : {width: 1, height: 1},
})) }))
}, [loadedImages]) }, [loadedImages])
// Count missing images // Count missing images (images we tried to load but couldn't)
const missingImageCount = post.images?.filter(img => !img.exists).length ?? 0 const missingImageCount = (post.images?.length ?? 0) - loadedImages.length
// Nothing to show // Nothing to show
if (viewImages.length === 0 && !post.gif && !post.video) { if (viewImages.length === 0 && !post.gif && !post.video) {