import {useCallback, useMemo, useState} from 'react'
import {LayoutAnimation, Pressable, View} from 'react-native'
import {Image} from 'expo-image'
import {
AppBskyEmbedGallery,
AppBskyEmbedImages,
AppBskyEmbedRecord,
AppBskyEmbedRecordWithMedia,
AppBskyFeedPost,
} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {sanitizeHandle} from '#/lib/strings/handles'
import {type ComposerOptsPostRef} from '#/state/shell/composer'
import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, useTheme, utils, web} from '#/alf'
import {QuoteEmbed} from '#/components/Post/Embed'
import {ProfileBadges} from '#/components/ProfileBadges'
import {Text} from '#/components/Typography'
import {parseEmbed} from '#/types/bsky/post'
export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) {
const t = useTheme()
const {_} = useLingui()
const {embed} = replyTo
const [showFull, setShowFull] = useState(false)
const onPress = useCallback(() => {
setShowFull(prev => !prev)
LayoutAnimation.configureNext({
duration: 350,
update: {type: 'spring', springDamping: 0.7},
})
}, [])
const quoteEmbed = useMemo(() => {
if (
AppBskyEmbedRecord.isView(embed) &&
AppBskyEmbedRecord.isViewRecord(embed.record) &&
AppBskyFeedPost.isRecord(embed.record.value)
) {
return embed
} else if (
AppBskyEmbedRecordWithMedia.isView(embed) &&
AppBskyEmbedRecord.isViewRecord(embed.record.record) &&
AppBskyFeedPost.isRecord(embed.record.record.value)
) {
return embed.record
}
return null
}, [embed])
const parsedQuoteEmbed = quoteEmbed
? parseEmbed({
$type: 'app.bsky.embed.record#view',
...quoteEmbed,
})
: null
const {images, totalNumber} = useMemo(() => {
if (AppBskyEmbedImages.isView(embed)) {
return {images: embed.images, totalNumber: embed.images.length}
} else if (AppBskyEmbedGallery.isView(embed)) {
return {
images: galleryItemsToImages(embed.items),
totalNumber: embed.items.length,
}
} else if (AppBskyEmbedRecordWithMedia.isView(embed)) {
if (AppBskyEmbedImages.isView(embed.media)) {
return {
images: embed.media.images,
totalNumber: embed.media.images.length,
}
} else if (AppBskyEmbedGallery.isView(embed.media)) {
return {
images: galleryItemsToImages(embed.media.items),
totalNumber: embed.media.items.length,
}
}
}
return {images: [], totalNumber: 0}
}, [embed])
return (
{sanitizeDisplayName(
replyTo.author.displayName ||
sanitizeHandle(replyTo.author.handle),
)}
{replyTo.text}
{images && !replyTo.moderation?.ui('contentMedia').blur && (
)}
{showFull && parsedQuoteEmbed && parsedQuoteEmbed.type === 'post' && (
)}
)
}
function galleryItemsToImages(
items: AppBskyEmbedGallery.View['items'],
): AppBskyEmbedImages.ViewImage[] {
// The reply-to thumbnail only renders up to 4 tiles; slicing here keeps
// the existing layout switch valid for galleries up to 10 items.
return items
.filter(AppBskyEmbedGallery.isViewImage)
.slice(0, 4)
.map(item => ({
thumb: item.thumbnail,
fullsize: item.fullsize,
alt: item.alt,
aspectRatio: item.aspectRatio,
}))
}
function ComposerReplyToImages({
images,
totalNumber,
}: {
images: AppBskyEmbedImages.ViewImage[]
totalNumber: number
}) {
const t = useTheme()
return (
{(images.length === 1 && (
)) ||
(images.length === 2 && (
)) ||
(images.length === 3 && (
)) ||
(images.length === 4 && (
{totalNumber > 4 && (
+{totalNumber - 3}
)}
))}
)
}