Add gallery embed type support (display + compose) (#10707)

This commit is contained in:
Spence Pope
2026-06-04 17:22:40 -04:00
committed by GitHub
parent de926d1838
commit 144ba30ea6
19 changed files with 494 additions and 143 deletions
@@ -1,4 +1,5 @@
import {
AppBskyEmbedGallery,
AppBskyEmbedImages,
AppBskyEmbedRecordWithMedia,
type AppBskyFeedDefs,
@@ -27,9 +28,6 @@ export function maybeApplyGalleryOffsetStyles(
additionalCauses?: ModerationCause[] | AppModerationCause[]
},
) {
// don't ever check gates like this, except this one time
if (!features.isOn(Features.PostGalleryEmbedEnable)) return
if (
!bsky.dangerousIsType<AppBskyFeedPost.Record>(
post.record,
@@ -39,6 +37,13 @@ export function maybeApplyGalleryOffsetStyles(
return
}
// The gate only controls whether legacy image embeds opt into the new
// expanded gallery layout. Gallery embeds always render expanded by item
// count, so their offset must apply regardless of the gate.
const isPostGalleryEmbedEnabled = features.isOn(
Features.PostGalleryEmbedEnable,
)
/*
* First check if we even have images
*/
@@ -49,6 +54,12 @@ export function maybeApplyGalleryOffsetStyles(
embed,
AppBskyEmbedImages.isMain,
)
const isGalleryEmbed =
embed &&
bsky.dangerousIsType<AppBskyEmbedGallery.Main>(
embed,
AppBskyEmbedGallery.isMain,
)
const isRecordWithMedia =
embed &&
bsky.dangerousIsType<AppBskyEmbedRecordWithMedia.Main>(
@@ -57,10 +68,16 @@ export function maybeApplyGalleryOffsetStyles(
)
let hasImages = false
if (isImageEmbed) {
if (!isPostGalleryEmbedEnabled) return
// one image, not a gallery
if (embed.images.length === 1) return
hasImages = true
}
if (isGalleryEmbed) {
// single (or empty) gallery - no offset needed
if (embed.items.length <= 1) return
hasImages = true
}
if (isRecordWithMedia) {
if (
bsky.dangerousIsType<AppBskyEmbedImages.Main>(
@@ -68,9 +85,19 @@ export function maybeApplyGalleryOffsetStyles(
AppBskyEmbedImages.isMain,
)
) {
if (!isPostGalleryEmbedEnabled) return
// one image, not a gallery
if (embed.media.images.length === 1) return
}
if (
bsky.dangerousIsType<AppBskyEmbedGallery.Main>(
embed.media,
AppBskyEmbedGallery.isMain,
)
) {
// single (or empty) gallery - no offset needed
if (embed.media.items.length <= 1) return
}
hasImages = true
}
if (!hasImages) return
+15 -7
View File
@@ -19,21 +19,28 @@ interface ImageLayoutGridProps {
onPressIn?: (index: number) => void
style?: StyleProp<ViewStyle>
viewContext?: PostEmbedViewContext
isWithinQuote?: boolean
}
export function ImageLayoutGrid({style, ...props}: ImageLayoutGridProps) {
export function ImageLayoutGrid({
style,
isWithinQuote: isWithinQuoteProp,
...props
}: ImageLayoutGridProps) {
const {gtMobile} = useBreakpoints()
const gap =
const isWithinQuote =
isWithinQuoteProp ??
props.viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia
? gtMobile
? a.gap_xs
: a.gap_2xs
: a.gap_xs
const gap = isWithinQuote ? (gtMobile ? a.gap_xs : a.gap_2xs) : a.gap_xs
return (
<View style={style}>
<View style={[gap, a.rounded_md, a.overflow_hidden]}>
<ImageLayoutGridInner {...props} gap={gap} />
<ImageLayoutGridInner
{...props}
gap={gap}
isWithinQuote={isWithinQuote}
/>
</View>
</View>
)
@@ -49,6 +56,7 @@ interface ImageLayoutGridInnerProps {
onLongPress?: (index: number) => void
onPressIn?: (index: number) => void
viewContext?: PostEmbedViewContext
isWithinQuote?: boolean
gap: {gap: number}
}
@@ -29,6 +29,7 @@ interface Props {
onPressIn?: EventFunction
imageStyle?: StyleProp<ImageStyle>
viewContext?: PostEmbedViewContext
isWithinQuote?: boolean
insetBorderStyle?: StyleProp<ViewStyle>
containerRefs: AnimatedRef<any>[]
thumbDimsRef: React.RefObject<(Dimensions | null)[]>
@@ -42,6 +43,7 @@ export function GalleryItem({
onPressIn,
onLongPress,
viewContext,
isWithinQuote,
insetBorderStyle,
containerRefs,
thumbDimsRef,
@@ -52,6 +54,7 @@ export function GalleryItem({
const image = images[index]
const hasAlt = !!image.alt
const hideBadges =
isWithinQuote ??
viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia
const aspect =