Fix createComposerState count-blind init, tighten gallery offset guard
B1: `createComposerState` always typed the initial media as `ImagesMedia`
regardless of `initImageUris.length`. With the picker cap raised to 10 in
ComposerPrompt + the URL intent handler not capping at all, the FAB and
deep-link flows could land the composer with 5-10 images in an `images`
embed, which `app.bsky.embed.images` rejects (`maxLength: 4`) on submit.
Extract `imagesToMediaVariant` and reuse from `createComposerState`,
`embed_add_images`, and `embed_remove_image` so the variant decision
("<=4 = images, >4 = gallery, cap at 10") lives in one place. The
remove-image demote branch collapses into the helper too — same effect,
less code.
W1: `maybeApplyGalleryOffsetStyles` only short-circuited single-image
galleries, applying offset for 0-item galleries when nothing renders
below. Loosened to `<= 1` (covers both 0 and 1 with the same intent).
This commit is contained in:
@@ -69,8 +69,8 @@ export function maybeApplyGalleryOffsetStyles(
|
|||||||
hasImages = true
|
hasImages = true
|
||||||
}
|
}
|
||||||
if (isGalleryEmbed) {
|
if (isGalleryEmbed) {
|
||||||
// one image, not a gallery
|
// single (or empty) gallery — no offset needed
|
||||||
if (embed.items.length === 1) return
|
if (embed.items.length <= 1) return
|
||||||
hasImages = true
|
hasImages = true
|
||||||
}
|
}
|
||||||
if (isRecordWithMedia) {
|
if (isRecordWithMedia) {
|
||||||
@@ -89,8 +89,8 @@ export function maybeApplyGalleryOffsetStyles(
|
|||||||
AppBskyEmbedGallery.isMain,
|
AppBskyEmbedGallery.isMain,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
// one image, not a gallery
|
// single (or empty) gallery — no offset needed
|
||||||
if (embed.media.items.length === 1) return
|
if (embed.media.items.length <= 1) return
|
||||||
}
|
}
|
||||||
hasImages = true
|
hasImages = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,6 +162,20 @@ export type ComposerAction =
|
|||||||
export const MAX_IMAGES = 4
|
export const MAX_IMAGES = 4
|
||||||
export const MAX_GALLERY_IMAGES = 10
|
export const MAX_GALLERY_IMAGES = 10
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Picks the embed variant for a set of images. ≤4 lands in the legacy
|
||||||
|
* `app.bsky.embed.images` shape; >4 promotes to `app.bsky.embed.gallery`.
|
||||||
|
* Anything beyond the gallery cap is silently dropped — callers should
|
||||||
|
* already have enforced the cap upstream (picker, paste, etc).
|
||||||
|
*/
|
||||||
|
function imagesToMediaVariant(
|
||||||
|
images: ComposerImage[],
|
||||||
|
): ImagesMedia | GalleryMedia {
|
||||||
|
return images.length <= MAX_IMAGES
|
||||||
|
? {type: 'images', images: images.slice(0, MAX_IMAGES)}
|
||||||
|
: {type: 'gallery', images: images.slice(0, MAX_GALLERY_IMAGES)}
|
||||||
|
}
|
||||||
|
|
||||||
export function composerReducer(
|
export function composerReducer(
|
||||||
state: ComposerState,
|
state: ComposerState,
|
||||||
action: ComposerAction,
|
action: ComposerAction,
|
||||||
@@ -344,39 +358,12 @@ function postReducer(state: PostDraft, action: PostAction): PostDraft {
|
|||||||
const prevMedia = state.embed.media
|
const prevMedia = state.embed.media
|
||||||
let nextMedia = prevMedia
|
let nextMedia = prevMedia
|
||||||
if (!prevMedia) {
|
if (!prevMedia) {
|
||||||
// First selection: pick the variant based on count. ImagesMedia caps
|
nextMedia = imagesToMediaVariant(action.images)
|
||||||
// at 4 (legacy `app.bsky.embed.images`); above that promotes to the
|
} else if (prevMedia.type === 'images' || prevMedia.type === 'gallery') {
|
||||||
// new `app.bsky.embed.gallery` (capped at 10).
|
nextMedia = imagesToMediaVariant([
|
||||||
if (action.images.length <= MAX_IMAGES) {
|
...prevMedia.images,
|
||||||
nextMedia = {
|
...action.images,
|
||||||
type: 'images',
|
])
|
||||||
images: action.images.slice(0, MAX_IMAGES),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nextMedia = {
|
|
||||||
type: 'gallery',
|
|
||||||
images: action.images.slice(0, MAX_GALLERY_IMAGES),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (prevMedia.type === 'images') {
|
|
||||||
const combined = [...prevMedia.images, ...action.images]
|
|
||||||
if (combined.length <= MAX_IMAGES) {
|
|
||||||
nextMedia = {...prevMedia, images: combined}
|
|
||||||
} else {
|
|
||||||
// Adding more than 4 promotes the existing images into a gallery.
|
|
||||||
nextMedia = {
|
|
||||||
type: 'gallery',
|
|
||||||
images: combined.slice(0, MAX_GALLERY_IMAGES),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (prevMedia.type === 'gallery') {
|
|
||||||
nextMedia = {
|
|
||||||
...prevMedia,
|
|
||||||
images: [...prevMedia.images, ...action.images].slice(
|
|
||||||
0,
|
|
||||||
MAX_GALLERY_IMAGES,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@@ -423,15 +410,11 @@ function postReducer(state: PostDraft, action: PostAction): PostDraft {
|
|||||||
if (!state.embed.link) {
|
if (!state.embed.link) {
|
||||||
nextLabels = []
|
nextLabels = []
|
||||||
}
|
}
|
||||||
} else if (
|
|
||||||
prevMedia.type === 'gallery' &&
|
|
||||||
remainingImages.length <= MAX_IMAGES
|
|
||||||
) {
|
|
||||||
// Drop back to the legacy `app.bsky.embed.images` shape when a
|
|
||||||
// gallery shrinks to <=4 items, so old clients still see it.
|
|
||||||
nextMedia = {type: 'images', images: remainingImages}
|
|
||||||
} else {
|
} else {
|
||||||
nextMedia = {...prevMedia, images: remainingImages}
|
// Re-pick the variant so a gallery that shrinks to <=4 demotes
|
||||||
|
// back to the legacy `app.bsky.embed.images` shape — keeps old
|
||||||
|
// clients rendering it when possible.
|
||||||
|
nextMedia = imagesToMediaVariant(remainingImages)
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@@ -618,12 +601,9 @@ export function createComposerState({
|
|||||||
| AppBskyActorDefs.PostInteractionSettingsPref
|
| AppBskyActorDefs.PostInteractionSettingsPref
|
||||||
| undefined
|
| undefined
|
||||||
}): ComposerState {
|
}): ComposerState {
|
||||||
let media: ImagesMedia | undefined
|
let media: ImagesMedia | GalleryMedia | undefined
|
||||||
if (initImageUris?.length) {
|
if (initImageUris?.length) {
|
||||||
media = {
|
media = imagesToMediaVariant(createInitialImages(initImageUris))
|
||||||
type: 'images',
|
|
||||||
images: createInitialImages(initImageUris),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let quote: Link | undefined
|
let quote: Link | undefined
|
||||||
if (initQuoteUri) {
|
if (initQuoteUri) {
|
||||||
|
|||||||
Reference in New Issue
Block a user