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 && ( )} ) }