good lord

This commit is contained in:
Eric Bailey
2026-04-15 17:39:33 -05:00
parent ce3a97a9ff
commit 5d3483b703
6 changed files with 168 additions and 44 deletions
-3
View File
@@ -1,6 +1,3 @@
export const ITEM_GAP = 8 // tokens.space.sm
export const MIN_ASPECT_RATIO = 2 / 3 // portrait limit
export const MAX_ASPECT_RATIO = 3 / 2 // landscape limit
export const POST_META_NO_CONTENT_OFFSET = {paddingTop: 10}
export const POST_EMBED_NO_CONTENT_OFFSET = {paddingTop: 6}
+1
View File
@@ -42,6 +42,7 @@ import {useAnalytics} from '#/analytics'
import {IS_WEB} from '#/env'
export * from './const'
export * from './maybeApplyGalleryOffsetStyles'
interface GalleryProps {
images: AppBskyEmbedImages.ViewImage[]
@@ -0,0 +1,102 @@
import {
AppBskyEmbedImages,
AppBskyEmbedRecordWithMedia,
type AppBskyFeedDefs,
AppBskyFeedPost,
type ModerationCause,
type ModerationUI,
} from '@atproto/api'
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 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 (isRecordWithMedia) {
if (
bsky.dangerousIsType<AppBskyEmbedImages.Main>(
embed.media,
AppBskyEmbedImages.isMain,
)
) {
// one image, not a gallery
if (embed.media.images.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
: {}
}
@@ -34,8 +34,7 @@ import {useInteractionState} from '#/components/hooks/useInteractionState'
import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash'
import {
GalleryBleed,
POST_EMBED_NO_CONTENT_OFFSET,
POST_META_NO_CONTENT_OFFSET,
maybeApplyGalleryOffsetStyles,
} from '#/components/images/Gallery'
import {LabelsOnMyPost} from '#/components/moderation/LabelsOnMe'
import {PostAlerts} from '#/components/moderation/PostAlerts'
@@ -304,7 +303,11 @@ const ThreadItemPostInner = memo(function ThreadItemPostInner({
postHref={postHref}
style={[
a.pb_xs,
!richText?.text && POST_META_NO_CONTENT_OFFSET,
maybeApplyGalleryOffsetStyles('meta', {
post,
modui: moderation.ui('contentList'),
additionalCauses: additionalPostAlerts,
}),
]}
/>
<LabelsOnMyPost post={post} style={[a.pb_xs]} />
@@ -335,7 +338,11 @@ const ThreadItemPostInner = memo(function ThreadItemPostInner({
{post.embed && (
<View
style={[
!richText?.text ? POST_EMBED_NO_CONTENT_OFFSET : [],
maybeApplyGalleryOffsetStyles('embed', {
post,
modui: moderation.ui('contentList'),
additionalCauses: additionalPostAlerts,
}),
a.pb_xs,
]}>
<Embed
+11 -6
View File
@@ -29,8 +29,7 @@ import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a} from '#/alf'
import {
GalleryBleed,
POST_EMBED_NO_CONTENT_OFFSET,
POST_META_NO_CONTENT_OFFSET,
maybeApplyGalleryOffsetStyles,
} from '#/components/images/Gallery'
import {ContentHider} from '#/components/moderation/ContentHider'
import {LabelsOnMyPost} from '#/components/moderation/LabelsOnMe'
@@ -190,7 +189,11 @@ function PostInner({
<View
style={[
styles.layoutContent,
!richText.text && POST_META_NO_CONTENT_OFFSET,
maybeApplyGalleryOffsetStyles('meta', {
post,
modui: moderation.ui('contentList'),
additionalCauses: [],
}),
]}>
<PostMeta
author={post.author}
@@ -232,9 +235,11 @@ function PostInner({
<TranslatedPost hideTranslateLink post={post} />
{post.embed ? (
<View
style={
!richText.text ? POST_EMBED_NO_CONTENT_OFFSET : undefined
}>
style={maybeApplyGalleryOffsetStyles('embed', {
post,
modui: moderation.ui('contentList'),
additionalCauses: [],
})}>
<Embed
embed={post.embed}
moderation={moderation}
+43 -31
View File
@@ -36,8 +36,7 @@ import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a} from '#/alf'
import {
GalleryBleed,
POST_EMBED_NO_CONTENT_OFFSET,
POST_META_NO_CONTENT_OFFSET,
maybeApplyGalleryOffsetStyles,
} from '#/components/images/Gallery'
import {ContentHider} from '#/components/moderation/ContentHider'
import {LabelsOnMyPost} from '#/components/moderation/LabelsOnMe'
@@ -168,6 +167,7 @@ let FeedItemInner = ({
const queryClient = useQueryClient()
const {openComposer} = useOpenComposer()
const pal = usePalette('default')
const {currentAccount} = useSession()
const [hover, setHover] = useState(false)
@@ -298,6 +298,30 @@ let FeedItemInner = ({
}
}, [reason])
const threadgateHiddenReplies = useMergedThreadgateHiddenReplies({
threadgateRecord,
})
const additionalPostAlerts: AppModerationCause[] = useMemo(() => {
const isPostHiddenByThreadgate = threadgateHiddenReplies.has(post.uri)
const rootPostUri = bsky.dangerousIsType<AppBskyFeedPost.Record>(
post.record,
AppBskyFeedPost.isRecord,
)
? post.record?.reply?.root?.uri || post.uri
: undefined
const isControlledByViewer =
rootPostUri && new AtUri(rootPostUri).host === currentAccount?.did
return isControlledByViewer && isPostHiddenByThreadgate
? [
{
type: 'reply-hidden',
source: {type: 'user', did: currentAccount?.did},
priority: 6,
},
]
: []
}, [post, currentAccount?.did, threadgateHiddenReplies])
return (
<GalleryBleed>
<Link
@@ -368,7 +392,11 @@ let FeedItemInner = ({
<View
style={[
styles.layoutContent,
!richText.text && POST_META_NO_CONTENT_OFFSET,
maybeApplyGalleryOffsetStyles('meta', {
post,
modui: moderation.ui('contentList'),
additionalCauses: additionalPostAlerts,
}),
]}>
<PostMeta
author={post.author}
@@ -393,7 +421,7 @@ let FeedItemInner = ({
postAuthor={post.author}
onOpenEmbed={onOpenEmbed}
post={post}
threadgateRecord={threadgateRecord}
additionalPostAlerts={additionalPostAlerts}
/>
<PostControls
post={post}
@@ -424,7 +452,7 @@ let PostContent = ({
postEmbed,
postAuthor,
onOpenEmbed,
threadgateRecord,
additionalPostAlerts,
}: {
moderation: ModerationDecision
richText: RichTextAPI
@@ -432,35 +460,11 @@ let PostContent = ({
postAuthor: AppBskyFeedDefs.PostView['author']
onOpenEmbed: () => void
post: AppBskyFeedDefs.PostView
threadgateRecord?: AppBskyFeedThreadgate.Record
additionalPostAlerts?: AppModerationCause[]
}): React.ReactNode => {
const {currentAccount} = useSession()
const [limitLines, setLimitLines] = useState(
() => countLines(richText.text) >= MAX_POST_LINES,
)
const threadgateHiddenReplies = useMergedThreadgateHiddenReplies({
threadgateRecord,
})
const additionalPostAlerts: AppModerationCause[] = useMemo(() => {
const isPostHiddenByThreadgate = threadgateHiddenReplies.has(post.uri)
const rootPostUri = bsky.dangerousIsType<AppBskyFeedPost.Record>(
post.record,
AppBskyFeedPost.isRecord,
)
? post.record?.reply?.root?.uri || post.uri
: undefined
const isControlledByViewer =
rootPostUri && new AtUri(rootPostUri).host === currentAccount?.did
return isControlledByViewer && isPostHiddenByThreadgate
? [
{
type: 'reply-hidden',
source: {type: 'user', did: currentAccount?.did},
priority: 6,
},
]
: []
}, [post, currentAccount?.did, threadgateHiddenReplies])
const record = useMemo<AppBskyFeedPost.Record | undefined>(
() =>
@@ -503,7 +507,15 @@ let PostContent = ({
) : undefined}
{record && <TranslatedPost hideTranslateLink post={post} />}
{postEmbed ? (
<View style={[a.pb_xs, !richText.text && POST_EMBED_NO_CONTENT_OFFSET]}>
<View
style={[
a.pb_xs,
maybeApplyGalleryOffsetStyles('embed', {
post,
modui: moderation.ui('contentList'),
additionalCauses: additionalPostAlerts,
}),
]}>
<Embed
embed={postEmbed}
moderation={moderation}