import {useMemo} from 'react' import {View} from 'react-native' import {Image} from 'expo-image' import {LinearGradient} from 'expo-linear-gradient' import { AppBskyActorDefs, AppBskyEmbedVideo, AppBskyFeedDefs, AppBskyFeedPost, ModerationDecision, } from '@atproto/api' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {sanitizeHandle} from '#/lib/strings/handles' import {formatCount} from '#/view/com/util/numeric/format' import {UserAvatar} from '#/view/com/util/UserAvatar' import {VideoFeedSourceContext} from '#/screens/VideoFeed/types' import {atoms as a, useTheme} from '#/alf' import {BLUE_HUE} from '#/alf/util/colorGeneration' import {select} from '#/alf/util/themeSelector' import {useInteractionState} from '#/components/hooks/useInteractionState' import {EyeSlash_Stroke2_Corner0_Rounded as Eye} from '#/components/icons/EyeSlash' import {Heart2_Stroke2_Corner0_Rounded as Heart} from '#/components/icons/Heart2' import {Repost_Stroke2_Corner2_Rounded as Repost} from '#/components/icons/Repost' import {Link} from '#/components/Link' import {MediaInsetBorder} from '#/components/MediaInsetBorder' import * as Hider from '#/components/moderation/Hider' import {Text} from '#/components/Typography' import * as bsky from '#/types/bsky' function getBlackColor(t: ReturnType) { return select(t.name, { light: t.palette.black, dark: t.atoms.bg_contrast_25.backgroundColor, dim: `hsl(${BLUE_HUE}, 28%, 6%)`, }) } export function VideoPostCard({ post, sourceContext, moderation, onInteract, }: { post: AppBskyFeedDefs.PostView sourceContext: VideoFeedSourceContext moderation: ModerationDecision /** * Callback for metrics etc */ onInteract?: () => void }) { const t = useTheme() const {_, i18n} = useLingui() const embed = post.embed const { state: pressed, onIn: onPressIn, onOut: onPressOut, } = useInteractionState() const listModUi = moderation.ui('contentList') const mergedModui = useMemo(() => { const modui = moderation.ui('contentList') const mediaModui = moderation.ui('contentMedia') modui.alerts = [...modui.alerts, ...mediaModui.alerts] modui.blurs = [...modui.blurs, ...mediaModui.blurs] modui.filters = [...modui.filters, ...mediaModui.filters] modui.informs = [...modui.informs, ...mediaModui.informs] return modui }, [moderation]) /** * Filtering should be done at a higher level, such as `PostFeed` or * `PostFeedVideoGridRow`, but we need to protect here as well. */ if (!AppBskyEmbedVideo.isView(embed)) return null const author = post.author const text = bsky.dangerousIsType( post.record, AppBskyFeedPost.isRecord, ) ? post.record?.text : '' const likeCount = post?.likeCount ?? 0 const repostCount = post?.repostCount ?? 0 const {thumbnail} = embed const black = getBlackColor(t) const textAndAuthor = ( {text && ( {text} )} {sanitizeHandle(post.author.handle, '@')} ) return ( { onInteract?.() }} onPressIn={onPressIn} onPressOut={onPressOut} style={[ a.flex_col, { alignItems: undefined, justifyContent: undefined, }, ]}> {_(msg`Hidden`)} {listModUi.blur ? ( ) : ( textAndAuthor )} {likeCount > 0 && ( {formatCount(i18n, likeCount)} )} {repostCount > 0 && ( {formatCount(i18n, repostCount)} )} {textAndAuthor} ) } export function VideoPostCardPlaceholder() { const t = useTheme() const black = getBlackColor(t) return ( ) } export function VideoPostCardTextPlaceholder({ author, }: { author?: AppBskyActorDefs.ProfileViewBasic }) { const t = useTheme() return ( {author ? ( {sanitizeHandle(author.handle, '@')} ) : ( )} ) } export function CompactVideoPostCard({ post, sourceContext, moderation, onInteract, }: { post: AppBskyFeedDefs.PostView sourceContext: VideoFeedSourceContext moderation: ModerationDecision /** * Callback for metrics etc */ onInteract?: () => void }) { const t = useTheme() const {_, i18n} = useLingui() const embed = post.embed const { state: pressed, onIn: onPressIn, onOut: onPressOut, } = useInteractionState() const mergedModui = useMemo(() => { const modui = moderation.ui('contentList') const mediaModui = moderation.ui('contentMedia') modui.alerts = [...modui.alerts, ...mediaModui.alerts] modui.blurs = [...modui.blurs, ...mediaModui.blurs] modui.filters = [...modui.filters, ...mediaModui.filters] modui.informs = [...modui.informs, ...mediaModui.informs] return modui }, [moderation]) /** * Filtering should be done at a higher level, such as `PostFeed` or * `PostFeedVideoGridRow`, but we need to protect here as well. */ if (!AppBskyEmbedVideo.isView(embed)) return null const likeCount = post?.likeCount ?? 0 const showLikeCount = false const {thumbnail} = embed const black = getBlackColor(t) return ( { onInteract?.() }} onPressIn={onPressIn} onPressOut={onPressOut} style={[ a.flex_col, { alignItems: undefined, justifyContent: undefined, }, ]}> {_(msg`Hidden`)} {showLikeCount && ( {likeCount > 0 && ( {formatCount(i18n, likeCount)} )} )} ) } export function CompactVideoPostCardPlaceholder() { const t = useTheme() const black = getBlackColor(t) return ( ) }