good lord
This commit is contained in:
@@ -1,6 +1,3 @@
|
|||||||
export const ITEM_GAP = 8 // tokens.space.sm
|
export const ITEM_GAP = 8 // tokens.space.sm
|
||||||
export const MIN_ASPECT_RATIO = 2 / 3 // portrait limit
|
export const MIN_ASPECT_RATIO = 2 / 3 // portrait limit
|
||||||
export const MAX_ASPECT_RATIO = 3 / 2 // landscape 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}
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import {useAnalytics} from '#/analytics'
|
|||||||
import {IS_WEB} from '#/env'
|
import {IS_WEB} from '#/env'
|
||||||
|
|
||||||
export * from './const'
|
export * from './const'
|
||||||
|
export * from './maybeApplyGalleryOffsetStyles'
|
||||||
|
|
||||||
interface GalleryProps {
|
interface GalleryProps {
|
||||||
images: AppBskyEmbedImages.ViewImage[]
|
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 {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash'
|
||||||
import {
|
import {
|
||||||
GalleryBleed,
|
GalleryBleed,
|
||||||
POST_EMBED_NO_CONTENT_OFFSET,
|
maybeApplyGalleryOffsetStyles,
|
||||||
POST_META_NO_CONTENT_OFFSET,
|
|
||||||
} from '#/components/images/Gallery'
|
} from '#/components/images/Gallery'
|
||||||
import {LabelsOnMyPost} from '#/components/moderation/LabelsOnMe'
|
import {LabelsOnMyPost} from '#/components/moderation/LabelsOnMe'
|
||||||
import {PostAlerts} from '#/components/moderation/PostAlerts'
|
import {PostAlerts} from '#/components/moderation/PostAlerts'
|
||||||
@@ -304,7 +303,11 @@ const ThreadItemPostInner = memo(function ThreadItemPostInner({
|
|||||||
postHref={postHref}
|
postHref={postHref}
|
||||||
style={[
|
style={[
|
||||||
a.pb_xs,
|
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]} />
|
<LabelsOnMyPost post={post} style={[a.pb_xs]} />
|
||||||
@@ -335,7 +338,11 @@ const ThreadItemPostInner = memo(function ThreadItemPostInner({
|
|||||||
{post.embed && (
|
{post.embed && (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
!richText?.text ? POST_EMBED_NO_CONTENT_OFFSET : [],
|
maybeApplyGalleryOffsetStyles('embed', {
|
||||||
|
post,
|
||||||
|
modui: moderation.ui('contentList'),
|
||||||
|
additionalCauses: additionalPostAlerts,
|
||||||
|
}),
|
||||||
a.pb_xs,
|
a.pb_xs,
|
||||||
]}>
|
]}>
|
||||||
<Embed
|
<Embed
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
|
|||||||
import {atoms as a} from '#/alf'
|
import {atoms as a} from '#/alf'
|
||||||
import {
|
import {
|
||||||
GalleryBleed,
|
GalleryBleed,
|
||||||
POST_EMBED_NO_CONTENT_OFFSET,
|
maybeApplyGalleryOffsetStyles,
|
||||||
POST_META_NO_CONTENT_OFFSET,
|
|
||||||
} from '#/components/images/Gallery'
|
} from '#/components/images/Gallery'
|
||||||
import {ContentHider} from '#/components/moderation/ContentHider'
|
import {ContentHider} from '#/components/moderation/ContentHider'
|
||||||
import {LabelsOnMyPost} from '#/components/moderation/LabelsOnMe'
|
import {LabelsOnMyPost} from '#/components/moderation/LabelsOnMe'
|
||||||
@@ -190,7 +189,11 @@ function PostInner({
|
|||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
styles.layoutContent,
|
styles.layoutContent,
|
||||||
!richText.text && POST_META_NO_CONTENT_OFFSET,
|
maybeApplyGalleryOffsetStyles('meta', {
|
||||||
|
post,
|
||||||
|
modui: moderation.ui('contentList'),
|
||||||
|
additionalCauses: [],
|
||||||
|
}),
|
||||||
]}>
|
]}>
|
||||||
<PostMeta
|
<PostMeta
|
||||||
author={post.author}
|
author={post.author}
|
||||||
@@ -232,9 +235,11 @@ function PostInner({
|
|||||||
<TranslatedPost hideTranslateLink post={post} />
|
<TranslatedPost hideTranslateLink post={post} />
|
||||||
{post.embed ? (
|
{post.embed ? (
|
||||||
<View
|
<View
|
||||||
style={
|
style={maybeApplyGalleryOffsetStyles('embed', {
|
||||||
!richText.text ? POST_EMBED_NO_CONTENT_OFFSET : undefined
|
post,
|
||||||
}>
|
modui: moderation.ui('contentList'),
|
||||||
|
additionalCauses: [],
|
||||||
|
})}>
|
||||||
<Embed
|
<Embed
|
||||||
embed={post.embed}
|
embed={post.embed}
|
||||||
moderation={moderation}
|
moderation={moderation}
|
||||||
|
|||||||
@@ -36,8 +36,7 @@ import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
|
|||||||
import {atoms as a} from '#/alf'
|
import {atoms as a} from '#/alf'
|
||||||
import {
|
import {
|
||||||
GalleryBleed,
|
GalleryBleed,
|
||||||
POST_EMBED_NO_CONTENT_OFFSET,
|
maybeApplyGalleryOffsetStyles,
|
||||||
POST_META_NO_CONTENT_OFFSET,
|
|
||||||
} from '#/components/images/Gallery'
|
} from '#/components/images/Gallery'
|
||||||
import {ContentHider} from '#/components/moderation/ContentHider'
|
import {ContentHider} from '#/components/moderation/ContentHider'
|
||||||
import {LabelsOnMyPost} from '#/components/moderation/LabelsOnMe'
|
import {LabelsOnMyPost} from '#/components/moderation/LabelsOnMe'
|
||||||
@@ -168,6 +167,7 @@ let FeedItemInner = ({
|
|||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const {openComposer} = useOpenComposer()
|
const {openComposer} = useOpenComposer()
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
|
const {currentAccount} = useSession()
|
||||||
|
|
||||||
const [hover, setHover] = useState(false)
|
const [hover, setHover] = useState(false)
|
||||||
|
|
||||||
@@ -298,6 +298,30 @@ let FeedItemInner = ({
|
|||||||
}
|
}
|
||||||
}, [reason])
|
}, [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 (
|
return (
|
||||||
<GalleryBleed>
|
<GalleryBleed>
|
||||||
<Link
|
<Link
|
||||||
@@ -368,7 +392,11 @@ let FeedItemInner = ({
|
|||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
styles.layoutContent,
|
styles.layoutContent,
|
||||||
!richText.text && POST_META_NO_CONTENT_OFFSET,
|
maybeApplyGalleryOffsetStyles('meta', {
|
||||||
|
post,
|
||||||
|
modui: moderation.ui('contentList'),
|
||||||
|
additionalCauses: additionalPostAlerts,
|
||||||
|
}),
|
||||||
]}>
|
]}>
|
||||||
<PostMeta
|
<PostMeta
|
||||||
author={post.author}
|
author={post.author}
|
||||||
@@ -393,7 +421,7 @@ let FeedItemInner = ({
|
|||||||
postAuthor={post.author}
|
postAuthor={post.author}
|
||||||
onOpenEmbed={onOpenEmbed}
|
onOpenEmbed={onOpenEmbed}
|
||||||
post={post}
|
post={post}
|
||||||
threadgateRecord={threadgateRecord}
|
additionalPostAlerts={additionalPostAlerts}
|
||||||
/>
|
/>
|
||||||
<PostControls
|
<PostControls
|
||||||
post={post}
|
post={post}
|
||||||
@@ -424,7 +452,7 @@ let PostContent = ({
|
|||||||
postEmbed,
|
postEmbed,
|
||||||
postAuthor,
|
postAuthor,
|
||||||
onOpenEmbed,
|
onOpenEmbed,
|
||||||
threadgateRecord,
|
additionalPostAlerts,
|
||||||
}: {
|
}: {
|
||||||
moderation: ModerationDecision
|
moderation: ModerationDecision
|
||||||
richText: RichTextAPI
|
richText: RichTextAPI
|
||||||
@@ -432,35 +460,11 @@ let PostContent = ({
|
|||||||
postAuthor: AppBskyFeedDefs.PostView['author']
|
postAuthor: AppBskyFeedDefs.PostView['author']
|
||||||
onOpenEmbed: () => void
|
onOpenEmbed: () => void
|
||||||
post: AppBskyFeedDefs.PostView
|
post: AppBskyFeedDefs.PostView
|
||||||
threadgateRecord?: AppBskyFeedThreadgate.Record
|
additionalPostAlerts?: AppModerationCause[]
|
||||||
}): React.ReactNode => {
|
}): React.ReactNode => {
|
||||||
const {currentAccount} = useSession()
|
|
||||||
const [limitLines, setLimitLines] = useState(
|
const [limitLines, setLimitLines] = useState(
|
||||||
() => countLines(richText.text) >= MAX_POST_LINES,
|
() => 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>(
|
const record = useMemo<AppBskyFeedPost.Record | undefined>(
|
||||||
() =>
|
() =>
|
||||||
@@ -503,7 +507,15 @@ let PostContent = ({
|
|||||||
) : undefined}
|
) : undefined}
|
||||||
{record && <TranslatedPost hideTranslateLink post={post} />}
|
{record && <TranslatedPost hideTranslateLink post={post} />}
|
||||||
{postEmbed ? (
|
{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
|
||||||
embed={postEmbed}
|
embed={postEmbed}
|
||||||
moderation={moderation}
|
moderation={moderation}
|
||||||
|
|||||||
Reference in New Issue
Block a user