From 843b9b540f695665ea05d9c9be5eec4a153e1015 Mon Sep 17 00:00:00 2001 From: vineyardbovines Date: Wed, 3 Jun 2026 19:29:09 -0400 Subject: [PATCH] Add frontend support for up to 10 images Frontend plumbing for the Photos v2 increase from 4 to 10 images. - Bump MAX_IMAGES from 4 to 10 in the composer; downstream consumers read the constant dynamically, so selection limits and copy follow automatically. - In-feed display rule per the Photos v2 spec: 1 image renders the single view, 2-4 render the existing grid, >4 render the carousel. This is gated behind the existing PostGalleryEmbedEnable flag, which now acts as a carousel kill-switch. - Fix the reply-to thumbnail to show a 2x2 preview of the first 4 images for any post with >=4 images (previously it rendered blank for >4). Note: actually publishing >4 images additionally requires the app.bsky.embed.images lexicon maxLength to be raised in @atproto/api (PR in flight). Until that lands, posts with >4 images are rejected by applyWrites({validate: true}). This change is UI-only and safe behind the flag. APP-2197 --- src/components/Post/Embed/ImageEmbed.tsx | 20 +++++++++++++++++++- src/view/com/composer/ComposerReplyTo.tsx | 5 ++++- src/view/com/composer/SelectMediaButton.tsx | 4 ++-- src/view/com/composer/state/composer.ts | 2 +- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/components/Post/Embed/ImageEmbed.tsx b/src/components/Post/Embed/ImageEmbed.tsx index 2c6bb6c5de..539930481a 100644 --- a/src/components/Post/Embed/ImageEmbed.tsx +++ b/src/components/Post/Embed/ImageEmbed.tsx @@ -15,6 +15,14 @@ import {useAnalytics} from '#/analytics' import {type EmbedType} from '#/types/bsky/post' import {type CommonProps} from './types' +/** + * Posts with more than this many images render the swipeable carousel + * (Gallery) instead of the static grid (ImageLayoutGrid). Per the Photos v2 + * spec: show the grid for up to 4 photos, show the carousel for more than 4. + * The grid only supports up to 4 images, so this is also the grid's max. + */ +const GALLERY_IMAGE_THRESHOLD = 4 + export function ImageEmbed({ embed, ...rest @@ -24,7 +32,17 @@ export function ImageEmbed({ const ax = useAnalytics() const {openLightbox} = useLightboxControls() const {images} = embed.view + // PostGalleryEmbedEnable is kept as a kill-switch for the carousel. When the + // flag is ON (the intended state), the grid-vs-carousel choice follows + // GALLERY_IMAGE_THRESHOLD: 2-4 images use the existing grid, >4 use the + // carousel. The 2-4 grid is intentionally preserved (decoupled from the 4->10 + // launch) per the Photos v2 spec. When the flag is OFF, the carousel is + // suppressed entirely and we fall back to the pre-carousel behavior: the grid + // renders the first 4 images. NOTE: with the flag OFF, images beyond the first + // 4 are not shown - this is the same limitation the grid always had, and only + // matters as a deliberate kill-switch state. See OPEN QUESTIONS in the PR. const galleryEnabled = ax.features.enabled(ax.features.PostGalleryEmbedEnable) + const useGallery = galleryEnabled && images.length > GALLERY_IMAGE_THRESHOLD // Captured from AutoSizedImage so the peek-commit handler can reuse the same // ref + dims that a tap would — keeps the lightbox's return animation intact. @@ -109,7 +127,7 @@ export function ImageEmbed({ ) } - if (galleryEnabled) { + if (useGallery) { return ( )) || - (images.length === 4 && ( + (images.length >= 4 && ( + // Posts can now have up to 10 images, but this is a small 64x64 + // thumbnail of the post being replied to, so we cap it at a 2x2 + // preview of the first 4 images for anything >=4.