import React from 'react' import { InteractionManager, StyleProp, StyleSheet, View, ViewStyle, } from 'react-native' import {MeasuredDimensions, runOnJS, runOnUI} from 'react-native-reanimated' import {Image} from 'expo-image' import { AppBskyEmbedExternal, AppBskyEmbedImages, AppBskyEmbedRecord, AppBskyEmbedRecordWithMedia, AppBskyEmbedVideo, AppBskyFeedDefs, AppBskyGraphDefs, moderateFeedGenerator, moderateUserList, ModerationDecision, } from '@atproto/api' import {HandleRef, measureHandle} from '#/lib/hooks/useHandleRef' import {usePalette} from '#/lib/hooks/usePalette' import {useLightboxControls} from '#/state/lightbox' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {FeedSourceCard} from '#/view/com/feeds/FeedSourceCard' import {atoms as a, useTheme} from '#/alf' import * as ListCard from '#/components/ListCard' import {Embed as StarterPackCard} from '#/components/StarterPack/StarterPackCard' import {ContentHider} from '../../../../components/moderation/ContentHider' import {Dimensions} from '../../lightbox/ImageViewing/@types' import {AutoSizedImage} from '../images/AutoSizedImage' import {ImageLayoutGrid} from '../images/ImageLayoutGrid' import {ExternalLinkEmbed} from './ExternalLinkEmbed' import {MaybeQuoteEmbed} from './QuoteEmbed' import {PostEmbedViewContext, QuoteEmbedViewContext} from './types' import {VideoEmbed} from './VideoEmbed' export * from './types' type Embed = | AppBskyEmbedRecord.View | AppBskyEmbedImages.View | AppBskyEmbedVideo.View | AppBskyEmbedExternal.View | AppBskyEmbedRecordWithMedia.View | {$type: string; [k: string]: unknown} export function PostEmbeds({ embed, moderation, onOpen, style, allowNestedQuotes, viewContext, }: { embed?: Embed moderation?: ModerationDecision onOpen?: () => void style?: StyleProp allowNestedQuotes?: boolean viewContext?: PostEmbedViewContext }) { const {openLightbox} = useLightboxControls() // quote post with media // = if (AppBskyEmbedRecordWithMedia.isView(embed)) { return ( ) } if (AppBskyEmbedRecord.isView(embed)) { // custom feed embed (i.e. generator view) if (AppBskyFeedDefs.isGeneratorView(embed.record)) { return ( ) } // list embed if (AppBskyGraphDefs.isListView(embed.record)) { return ( ) } // starter pack embed if (AppBskyGraphDefs.isStarterPackViewBasic(embed.record)) { return ( ) } // quote post // = return ( ) } // image embed // = if (AppBskyEmbedImages.isView(embed)) { const {images} = embed if (images.length > 0) { const items = embed.images.map(img => ({ uri: img.fullsize, thumbUri: img.thumb, alt: img.alt, dimensions: img.aspectRatio ?? null, })) const _openLightbox = ( index: number, thumbRects: (MeasuredDimensions | null)[], fetchedDims: (Dimensions | null)[], ) => { openLightbox({ images: items.map((item, i) => ({ ...item, thumbRect: thumbRects[i] ?? null, thumbDimensions: fetchedDims[i] ?? null, type: 'image', })), index, }) } const onPress = ( index: number, refs: HandleRef[], fetchedDims: (Dimensions | null)[], ) => { const handles = refs.map(r => r.current) runOnUI(() => { 'worklet' const rects = handles.map(measureHandle) runOnJS(_openLightbox)(index, rects, fetchedDims) })() } const onPressIn = (_: number) => { InteractionManager.runAfterInteractions(() => { Image.prefetch(items.map(i => i.uri)) }) } if (images.length === 1) { const image = images[0] return ( onPress(0, [containerRef], [dims]) } onPressIn={() => onPressIn(0)} hideBadge={ viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia } /> ) } return ( ) } } // external link embed // = if (AppBskyEmbedExternal.isView(embed)) { const link = embed.external return ( ) } // video embed // = if (AppBskyEmbedVideo.isView(embed)) { return ( ) } return } export function MaybeFeedCard({view}: {view: AppBskyFeedDefs.GeneratorView}) { const pal = usePalette('default') const moderationOpts = useModerationOpts() const moderation = React.useMemo(() => { return moderationOpts ? moderateFeedGenerator(view, moderationOpts) : undefined }, [view, moderationOpts]) return ( ) } export function MaybeListCard({view}: {view: AppBskyGraphDefs.ListView}) { const moderationOpts = useModerationOpts() const moderation = React.useMemo(() => { return moderationOpts ? moderateUserList(view, moderationOpts) : undefined }, [view, moderationOpts]) const t = useTheme() return ( ) } const styles = StyleSheet.create({ altContainer: { backgroundColor: 'rgba(0, 0, 0, 0.75)', borderRadius: 6, paddingHorizontal: 6, paddingVertical: 3, position: 'absolute', right: 6, bottom: 6, }, alt: { color: 'white', fontSize: 7, fontWeight: '600', }, customFeedOuter: { borderWidth: StyleSheet.hairlineWidth, borderRadius: 8, paddingHorizontal: 12, paddingVertical: 12, }, })