9aea4362d5
Wires the new gallery embed (atproto PR #4827) through the display and compose pipelines while the lexicon is unpublished. Gallery posts render via the existing carousel for >4 items and grid for <=4. Compose now allows up to 10 images and auto-promotes images -> gallery above 4 (demotes back when count drops to <=4 so old clients still see legacy embeds when possible). Drafts persist as the new `embedGallery` field. Local shim types in `src/lib/api/gallery-embed-shim*.ts` mirror the lexicon shape; delete both files once @atproto/api ships the generated types.
124 lines
2.9 KiB
TypeScript
124 lines
2.9 KiB
TypeScript
import {
|
|
AppBskyEmbedImages,
|
|
AppBskyEmbedRecordWithMedia,
|
|
type AppBskyFeedDefs,
|
|
AppBskyFeedPost,
|
|
type ModerationCause,
|
|
type ModerationUI,
|
|
} from '@atproto/api'
|
|
|
|
import {AppBskyEmbedGallery} from '#/lib/api/gallery-embed-shim'
|
|
import {unique} from '#/lib/moderation'
|
|
import {type AppModerationCause} from '#/components/Pills'
|
|
import {Features, features} from '#/analytics/features'
|
|
import * as bsky from '#/types/bsky'
|
|
|
|
export const POST_META_NO_CONTENT_OFFSET = {paddingTop: 10}
|
|
export const POST_EMBED_NO_CONTENT_OFFSET = {paddingTop: 6}
|
|
|
|
export function maybeApplyGalleryOffsetStyles(
|
|
placement: 'meta' | 'embed',
|
|
{
|
|
post,
|
|
modui,
|
|
additionalCauses,
|
|
}: {
|
|
post: AppBskyFeedDefs.PostView
|
|
modui: ModerationUI
|
|
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,
|
|
AppBskyFeedPost.isRecord,
|
|
)
|
|
) {
|
|
return
|
|
}
|
|
|
|
/*
|
|
* First check if we even have images
|
|
*/
|
|
const embed = post.record.embed
|
|
const isImageEmbed =
|
|
embed &&
|
|
bsky.dangerousIsType<AppBskyEmbedImages.Main>(
|
|
embed,
|
|
AppBskyEmbedImages.isMain,
|
|
)
|
|
const isGalleryEmbed =
|
|
embed &&
|
|
bsky.dangerousIsType<AppBskyEmbedGallery.Main>(
|
|
embed,
|
|
AppBskyEmbedGallery.isMain,
|
|
)
|
|
const isRecordWithMedia =
|
|
embed &&
|
|
bsky.dangerousIsType<AppBskyEmbedRecordWithMedia.Main>(
|
|
embed,
|
|
AppBskyEmbedRecordWithMedia.isMain,
|
|
)
|
|
let hasImages = false
|
|
if (isImageEmbed) {
|
|
// one image, not a gallery
|
|
if (embed.images.length === 1) return
|
|
hasImages = true
|
|
}
|
|
if (isGalleryEmbed) {
|
|
// one image, not a gallery
|
|
if (embed.items.length === 1) return
|
|
hasImages = true
|
|
}
|
|
if (isRecordWithMedia) {
|
|
if (
|
|
bsky.dangerousIsType<AppBskyEmbedImages.Main>(
|
|
embed.media,
|
|
AppBskyEmbedImages.isMain,
|
|
)
|
|
) {
|
|
// one image, not a gallery
|
|
if (embed.media.images.length === 1) return
|
|
}
|
|
if (
|
|
bsky.dangerousIsType<AppBskyEmbedGallery.Main>(
|
|
embed.media,
|
|
AppBskyEmbedGallery.isMain,
|
|
)
|
|
) {
|
|
// one image, not a gallery
|
|
if (embed.media.items.length === 1) return
|
|
}
|
|
hasImages = true
|
|
}
|
|
if (!hasImages) return
|
|
|
|
/*
|
|
* Then check if we have any text
|
|
*/
|
|
let hasLabels = false
|
|
if (modui.alert) {
|
|
hasLabels = modui.alerts.filter(unique).length > 0
|
|
}
|
|
if (modui.inform) {
|
|
hasLabels = hasLabels || modui.informs.filter(unique).length > 0
|
|
}
|
|
if (additionalCauses?.length) {
|
|
hasLabels = true
|
|
}
|
|
|
|
/*
|
|
* If no text or labels, then we need a lil bump
|
|
*/
|
|
const shouldApplyOffset = !post.record.text && !hasLabels
|
|
|
|
return shouldApplyOffset
|
|
? placement === 'meta'
|
|
? POST_META_NO_CONTENT_OFFSET
|
|
: POST_EMBED_NO_CONTENT_OFFSET
|
|
: {}
|
|
}
|