import React from 'react'
import {View} from 'react-native'
import {
type $Typed,
type AppBskyFeedDefs,
AppBskyFeedPost,
AtUri,
moderatePost,
RichText as RichTextAPI,
} from '@atproto/api'
import {Trans} from '@lingui/macro'
import {useQueryClient} from '@tanstack/react-query'
import {usePalette} from '#/lib/hooks/usePalette'
import {makeProfileLink} from '#/lib/routes/links'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {unstableCacheProfileView} from '#/state/queries/profile'
import {useSession} from '#/state/session'
import {Link} from '#/view/com/util/Link'
import {PostMeta} from '#/view/com/util/PostMeta'
import {atoms as a, useTheme} from '#/alf'
import {ContentHider} from '#/components/moderation/ContentHider'
import {PostAlerts} from '#/components/moderation/PostAlerts'
import {RichText} from '#/components/RichText'
import {Embed as StarterPackCard} from '#/components/StarterPack/StarterPackCard'
import {SubtleWebHover} from '#/components/SubtleWebHover'
import * as bsky from '#/types/bsky'
import {
type Embed as TEmbed,
type EmbedType,
parseEmbed,
} from '#/types/bsky/post'
import {ExternalEmbed} from './ExternalEmbed'
import {ModeratedFeedEmbed} from './FeedEmbed'
import {ImageEmbed} from './ImageEmbed'
import {ModeratedListEmbed} from './ListEmbed'
import {PostPlaceholder as PostPlaceholderText} from './PostPlaceholder'
import {
type CommonProps,
type EmbedProps,
PostEmbedViewContext,
QuoteEmbedViewContext,
} from './types'
import {VideoEmbed} from './VideoEmbed'
export {PostEmbedViewContext, QuoteEmbedViewContext} from './types'
export function Embed({embed: rawEmbed, ...rest}: EmbedProps) {
const embed = parseEmbed(rawEmbed)
switch (embed.type) {
case 'images':
case 'link':
case 'video': {
return
}
case 'feed':
case 'list':
case 'starter_pack':
case 'labeler':
case 'post':
case 'post_not_found':
case 'post_blocked':
case 'post_detached': {
return
}
case 'post_with_media': {
return (
)
}
default: {
return null
}
}
}
function MediaEmbed({
embed,
...rest
}: CommonProps & {
embed: TEmbed
}) {
switch (embed.type) {
case 'images': {
return (
)
}
case 'link': {
return (
)
}
case 'video': {
return (
)
}
default: {
return null
}
}
}
function RecordEmbed({
embed,
...rest
}: CommonProps & {
embed: TEmbed
}) {
switch (embed.type) {
case 'feed': {
return (
)
}
case 'list': {
return (
)
}
case 'starter_pack': {
return (
)
}
case 'labeler': {
// not implemented
return null
}
case 'post': {
if (rest.isWithinQuote && !rest.allowNestedQuotes) {
return null
}
return (
)
}
case 'post_not_found': {
return (
Deleted
)
}
case 'post_blocked': {
return (
Blocked
)
}
case 'post_detached': {
return
}
default: {
return null
}
}
}
export function PostDetachedEmbed({
embed,
}: {
embed: EmbedType<'post_detached'>
}) {
const {currentAccount} = useSession()
const isViewerOwner = currentAccount?.did
? embed.view.uri.includes(currentAccount.did)
: false
return (
{isViewerOwner ? (
Removed by you
) : (
Removed by author
)}
)
}
/*
* Nests parent `Embed` component and therefore must live in this file to avoid
* circular imports.
*/
export function QuoteEmbed({
embed,
onOpen,
style,
isWithinQuote: parentIsWithinQuote,
allowNestedQuotes: parentAllowNestedQuotes,
}: Omit & {
embed: EmbedType<'post'>
viewContext?: QuoteEmbedViewContext
}) {
const moderationOpts = useModerationOpts()
const quote = React.useMemo<$Typed>(
() => ({
...embed.view,
$type: 'app.bsky.feed.defs#postView',
record: embed.view.value,
embed: embed.view.embeds?.[0],
}),
[embed],
)
const moderation = React.useMemo(() => {
return moderationOpts ? moderatePost(quote, moderationOpts) : undefined
}, [quote, moderationOpts])
const t = useTheme()
const queryClient = useQueryClient()
const pal = usePalette('default')
const itemUrip = new AtUri(quote.uri)
const itemHref = makeProfileLink(quote.author, 'post', itemUrip.rkey)
const itemTitle = `Post by ${quote.author.handle}`
const richText = React.useMemo(() => {
if (
!bsky.dangerousIsType(
quote.record,
AppBskyFeedPost.isRecord,
)
)
return undefined
const {text, facets} = quote.record
return text.trim()
? new RichTextAPI({text: text, facets: facets})
: undefined
}, [quote.record])
const onBeforePress = React.useCallback(() => {
unstableCacheProfileView(queryClient, quote.author)
onOpen?.()
}, [queryClient, quote.author, onOpen])
const [hover, setHover] = React.useState(false)
return (
{
setHover(true)
}}
onPointerLeave={() => {
setHover(false)
}}>
{moderation ? (
) : null}
{richText ? (
) : null}
{quote.embed && (
)}
)
}