Clean up unused things
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {
|
import {
|
||||||
$Typed,
|
type $Typed,
|
||||||
AppBskyFeedDefs,
|
type AppBskyFeedDefs,
|
||||||
AppBskyFeedPost,
|
AppBskyFeedPost,
|
||||||
AtUri,
|
AtUri,
|
||||||
moderatePost,
|
moderatePost,
|
||||||
@@ -17,7 +17,6 @@ import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
|||||||
import {unstableCacheProfileView} from '#/state/queries/profile'
|
import {unstableCacheProfileView} from '#/state/queries/profile'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {Link} from '#/view/com/util/Link'
|
import {Link} from '#/view/com/util/Link'
|
||||||
import {} from '#/view/com/util/post-embeds/types'
|
|
||||||
import {PostMeta} from '#/view/com/util/PostMeta'
|
import {PostMeta} from '#/view/com/util/PostMeta'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {ContentHider} from '#/components/moderation/ContentHider'
|
import {ContentHider} from '#/components/moderation/ContentHider'
|
||||||
@@ -26,15 +25,15 @@ import {RichText} from '#/components/RichText'
|
|||||||
import {Embed as StarterPackCard} from '#/components/StarterPack/StarterPackCard'
|
import {Embed as StarterPackCard} from '#/components/StarterPack/StarterPackCard'
|
||||||
import {SubtleWebHover} from '#/components/SubtleWebHover'
|
import {SubtleWebHover} from '#/components/SubtleWebHover'
|
||||||
import * as bsky from '#/types/bsky'
|
import * as bsky from '#/types/bsky'
|
||||||
import {Embed as TEmbed, EmbedType, parseEmbed} from '#/types/bsky/post'
|
import {type Embed as TEmbed, type EmbedType, parseEmbed} from '#/types/bsky/post'
|
||||||
import {ExternalEmbed} from './ExternalEmbed'
|
import {ExternalEmbed} from './ExternalEmbed'
|
||||||
import {ModeratedFeedEmbed} from './FeedEmbed'
|
import {ModeratedFeedEmbed} from './FeedEmbed'
|
||||||
import {ImageEmbed} from './ImageEmbed'
|
import {ImageEmbed} from './ImageEmbed'
|
||||||
import {ModeratedListEmbed} from './ListEmbed'
|
import {ModeratedListEmbed} from './ListEmbed'
|
||||||
import {PostPlaceholder as PostPlaceholderText} from './PostPlaceholder'
|
import {PostPlaceholder as PostPlaceholderText} from './PostPlaceholder'
|
||||||
import {
|
import {
|
||||||
CommonProps,
|
type CommonProps,
|
||||||
EmbedProps,
|
type EmbedProps,
|
||||||
PostEmbedViewContext,
|
PostEmbedViewContext,
|
||||||
QuoteEmbedViewContext,
|
QuoteEmbedViewContext,
|
||||||
} from './types'
|
} from './types'
|
||||||
|
|||||||
@@ -1,337 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import {
|
|
||||||
StyleProp,
|
|
||||||
StyleSheet,
|
|
||||||
TouchableOpacity,
|
|
||||||
View,
|
|
||||||
ViewStyle,
|
|
||||||
} from 'react-native'
|
|
||||||
import {
|
|
||||||
AppBskyEmbedExternal,
|
|
||||||
AppBskyEmbedImages,
|
|
||||||
AppBskyEmbedRecord,
|
|
||||||
AppBskyEmbedRecordWithMedia,
|
|
||||||
AppBskyEmbedVideo,
|
|
||||||
AppBskyFeedDefs,
|
|
||||||
AppBskyFeedPost,
|
|
||||||
moderatePost,
|
|
||||||
ModerationDecision,
|
|
||||||
RichText as RichTextAPI,
|
|
||||||
} from '@atproto/api'
|
|
||||||
import {AtUri} from '@atproto/api'
|
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
|
||||||
import {msg, Trans} from '@lingui/macro'
|
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {useQueryClient} from '@tanstack/react-query'
|
|
||||||
|
|
||||||
import {HITSLOP_20} from '#/lib/constants'
|
|
||||||
import {usePalette} from '#/lib/hooks/usePalette'
|
|
||||||
import {InfoCircleIcon} from '#/lib/icons'
|
|
||||||
import {makeProfileLink} from '#/lib/routes/links'
|
|
||||||
import {s} from '#/lib/styles'
|
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
|
||||||
import {precacheProfile} from '#/state/queries/profile'
|
|
||||||
import {useResolveLinkQuery} from '#/state/queries/resolve-link'
|
|
||||||
import {useSession} from '#/state/session'
|
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
|
||||||
import {RichText} from '#/components/RichText'
|
|
||||||
import {SubtleWebHover} from '#/components/SubtleWebHover'
|
|
||||||
import * as bsky from '#/types/bsky'
|
|
||||||
import {ContentHider} from '../../../../components/moderation/ContentHider'
|
|
||||||
import {PostAlerts} from '../../../../components/moderation/PostAlerts'
|
|
||||||
import {Link} from '../Link'
|
|
||||||
import {PostMeta} from '../PostMeta'
|
|
||||||
import {Text} from '../text/Text'
|
|
||||||
import {PostEmbeds} from '.'
|
|
||||||
import {QuoteEmbedViewContext} from './types'
|
|
||||||
|
|
||||||
export function MaybeQuoteEmbed({
|
|
||||||
embed,
|
|
||||||
onOpen,
|
|
||||||
style,
|
|
||||||
allowNestedQuotes,
|
|
||||||
viewContext,
|
|
||||||
}: {
|
|
||||||
embed: AppBskyEmbedRecord.View
|
|
||||||
onOpen?: () => void
|
|
||||||
style?: StyleProp<ViewStyle>
|
|
||||||
allowNestedQuotes?: boolean
|
|
||||||
viewContext?: QuoteEmbedViewContext
|
|
||||||
}) {
|
|
||||||
const t = useTheme()
|
|
||||||
const pal = usePalette('default')
|
|
||||||
const {currentAccount} = useSession()
|
|
||||||
if (
|
|
||||||
AppBskyEmbedRecord.isViewRecord(embed.record) &&
|
|
||||||
AppBskyFeedPost.isRecord(embed.record.value) &&
|
|
||||||
AppBskyFeedPost.validateRecord(embed.record.value).success
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<QuoteEmbedModerated
|
|
||||||
viewRecord={embed.record}
|
|
||||||
onOpen={onOpen}
|
|
||||||
style={style}
|
|
||||||
allowNestedQuotes={allowNestedQuotes}
|
|
||||||
viewContext={viewContext}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
} else if (AppBskyEmbedRecord.isViewBlocked(embed.record)) {
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
style={[styles.errorContainer, a.border, t.atoms.border_contrast_low]}>
|
|
||||||
<InfoCircleIcon size={18} style={pal.text} />
|
|
||||||
<Text type="lg" style={pal.text}>
|
|
||||||
<Trans>Blocked</Trans>
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
} else if (AppBskyEmbedRecord.isViewNotFound(embed.record)) {
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
style={[styles.errorContainer, a.border, t.atoms.border_contrast_low]}>
|
|
||||||
<InfoCircleIcon size={18} style={pal.text} />
|
|
||||||
<Text type="lg" style={pal.text}>
|
|
||||||
<Trans>Deleted</Trans>
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
} else if (AppBskyEmbedRecord.isViewDetached(embed.record)) {
|
|
||||||
const isViewerOwner = currentAccount?.did
|
|
||||||
? embed.record.uri.includes(currentAccount.did)
|
|
||||||
: false
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
style={[styles.errorContainer, a.border, t.atoms.border_contrast_low]}>
|
|
||||||
<InfoCircleIcon size={18} style={pal.text} />
|
|
||||||
<Text type="lg" style={pal.text}>
|
|
||||||
{isViewerOwner ? (
|
|
||||||
<Trans>Removed by you</Trans>
|
|
||||||
) : (
|
|
||||||
<Trans>Removed by author</Trans>
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
function QuoteEmbedModerated({
|
|
||||||
viewRecord,
|
|
||||||
onOpen,
|
|
||||||
style,
|
|
||||||
allowNestedQuotes,
|
|
||||||
viewContext,
|
|
||||||
}: {
|
|
||||||
viewRecord: AppBskyEmbedRecord.ViewRecord
|
|
||||||
onOpen?: () => void
|
|
||||||
style?: StyleProp<ViewStyle>
|
|
||||||
allowNestedQuotes?: boolean
|
|
||||||
viewContext?: QuoteEmbedViewContext
|
|
||||||
}) {
|
|
||||||
const moderationOpts = useModerationOpts()
|
|
||||||
const postView = React.useMemo(
|
|
||||||
() => viewRecordToPostView(viewRecord),
|
|
||||||
[viewRecord],
|
|
||||||
)
|
|
||||||
const moderation = React.useMemo(() => {
|
|
||||||
return moderationOpts ? moderatePost(postView, moderationOpts) : undefined
|
|
||||||
}, [postView, moderationOpts])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<QuoteEmbed
|
|
||||||
quote={postView}
|
|
||||||
moderation={moderation}
|
|
||||||
onOpen={onOpen}
|
|
||||||
style={style}
|
|
||||||
allowNestedQuotes={allowNestedQuotes}
|
|
||||||
viewContext={viewContext}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function QuoteEmbed({
|
|
||||||
quote,
|
|
||||||
moderation,
|
|
||||||
onOpen,
|
|
||||||
style,
|
|
||||||
allowNestedQuotes,
|
|
||||||
}: {
|
|
||||||
quote: AppBskyFeedDefs.PostView
|
|
||||||
moderation?: ModerationDecision
|
|
||||||
onOpen?: () => void
|
|
||||||
style?: StyleProp<ViewStyle>
|
|
||||||
allowNestedQuotes?: boolean
|
|
||||||
viewContext?: QuoteEmbedViewContext
|
|
||||||
}) {
|
|
||||||
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<AppBskyFeedPost.Record>(
|
|
||||||
quote.record,
|
|
||||||
AppBskyFeedPost.isRecord,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return undefined
|
|
||||||
const {text, facets} = quote.record
|
|
||||||
return text.trim()
|
|
||||||
? new RichTextAPI({text: text, facets: facets})
|
|
||||||
: undefined
|
|
||||||
}, [quote.record])
|
|
||||||
|
|
||||||
const embed = React.useMemo(() => {
|
|
||||||
const e = quote.embed
|
|
||||||
|
|
||||||
if (allowNestedQuotes) {
|
|
||||||
return e
|
|
||||||
} else {
|
|
||||||
if (
|
|
||||||
AppBskyEmbedImages.isView(e) ||
|
|
||||||
AppBskyEmbedExternal.isView(e) ||
|
|
||||||
AppBskyEmbedVideo.isView(e)
|
|
||||||
) {
|
|
||||||
return e
|
|
||||||
} else if (
|
|
||||||
AppBskyEmbedRecordWithMedia.isView(e) &&
|
|
||||||
(AppBskyEmbedImages.isView(e.media) ||
|
|
||||||
AppBskyEmbedExternal.isView(e.media) ||
|
|
||||||
AppBskyEmbedVideo.isView(e.media))
|
|
||||||
) {
|
|
||||||
return e.media
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [quote.embed, allowNestedQuotes])
|
|
||||||
|
|
||||||
const onBeforePress = React.useCallback(() => {
|
|
||||||
precacheProfile(queryClient, quote.author)
|
|
||||||
onOpen?.()
|
|
||||||
}, [queryClient, quote.author, onOpen])
|
|
||||||
|
|
||||||
const [hover, setHover] = React.useState(false)
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
onPointerEnter={() => {
|
|
||||||
setHover(true)
|
|
||||||
}}
|
|
||||||
onPointerLeave={() => {
|
|
||||||
setHover(false)
|
|
||||||
}}>
|
|
||||||
<ContentHider
|
|
||||||
modui={moderation?.ui('contentList')}
|
|
||||||
style={[
|
|
||||||
a.rounded_md,
|
|
||||||
a.p_md,
|
|
||||||
a.mt_sm,
|
|
||||||
a.border,
|
|
||||||
t.atoms.border_contrast_low,
|
|
||||||
style,
|
|
||||||
]}
|
|
||||||
childContainerStyle={[a.pt_sm]}>
|
|
||||||
<SubtleWebHover hover={hover} />
|
|
||||||
<Link
|
|
||||||
hoverStyle={{borderColor: pal.colors.borderLinkHover}}
|
|
||||||
href={itemHref}
|
|
||||||
title={itemTitle}
|
|
||||||
onBeforePress={onBeforePress}>
|
|
||||||
<View pointerEvents="none">
|
|
||||||
<PostMeta
|
|
||||||
author={quote.author}
|
|
||||||
moderation={moderation}
|
|
||||||
showAvatar
|
|
||||||
postHref={itemHref}
|
|
||||||
timestamp={quote.indexedAt}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
{moderation ? (
|
|
||||||
<PostAlerts
|
|
||||||
modui={moderation.ui('contentView')}
|
|
||||||
style={[a.py_xs]}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
{richText ? (
|
|
||||||
<RichText
|
|
||||||
value={richText}
|
|
||||||
style={a.text_md}
|
|
||||||
numberOfLines={20}
|
|
||||||
disableLinks
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
{embed && <PostEmbeds embed={embed} moderation={moderation} />}
|
|
||||||
</Link>
|
|
||||||
</ContentHider>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function QuoteX({onRemove}: {onRemove: () => void}) {
|
|
||||||
const {_} = useLingui()
|
|
||||||
return (
|
|
||||||
<TouchableOpacity
|
|
||||||
style={[
|
|
||||||
a.absolute,
|
|
||||||
a.p_xs,
|
|
||||||
a.rounded_full,
|
|
||||||
a.align_center,
|
|
||||||
a.justify_center,
|
|
||||||
{
|
|
||||||
top: 16,
|
|
||||||
right: 10,
|
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.75)',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
onPress={onRemove}
|
|
||||||
accessibilityRole="button"
|
|
||||||
accessibilityLabel={_(msg`Remove quote`)}
|
|
||||||
accessibilityHint={_(msg`Removes quoted post`)}
|
|
||||||
onAccessibilityEscape={onRemove}
|
|
||||||
hitSlop={HITSLOP_20}>
|
|
||||||
<FontAwesomeIcon size={12} icon="xmark" style={s.white} />
|
|
||||||
</TouchableOpacity>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function LazyQuoteEmbed({uri}: {uri: string}) {
|
|
||||||
const {data} = useResolveLinkQuery(uri)
|
|
||||||
const moderationOpts = useModerationOpts()
|
|
||||||
if (!data || data.type !== 'record' || data.kind !== 'post') {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const moderation = moderationOpts
|
|
||||||
? moderatePost(data.view, moderationOpts)
|
|
||||||
: undefined
|
|
||||||
return <QuoteEmbed quote={data.view} moderation={moderation} />
|
|
||||||
}
|
|
||||||
|
|
||||||
function viewRecordToPostView(
|
|
||||||
viewRecord: AppBskyEmbedRecord.ViewRecord,
|
|
||||||
): AppBskyFeedDefs.PostView {
|
|
||||||
const {value, embeds, ...rest} = viewRecord
|
|
||||||
return {
|
|
||||||
...rest,
|
|
||||||
$type: 'app.bsky.feed.defs#postView',
|
|
||||||
record: value,
|
|
||||||
embed: embeds?.[0],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
errorContainer: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: 4,
|
|
||||||
borderRadius: 8,
|
|
||||||
marginTop: 8,
|
|
||||||
paddingVertical: 14,
|
|
||||||
paddingHorizontal: 14,
|
|
||||||
borderWidth: StyleSheet.hairlineWidth,
|
|
||||||
},
|
|
||||||
alert: {
|
|
||||||
marginBottom: 6,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -1,258 +1,18 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import {StyleSheet, View} from 'react-native'
|
||||||
import {
|
import {
|
||||||
InteractionManager,
|
type AppBskyFeedDefs,
|
||||||
type StyleProp,
|
type AppBskyGraphDefs,
|
||||||
StyleSheet,
|
|
||||||
View,
|
|
||||||
type ViewStyle,
|
|
||||||
} from 'react-native'
|
|
||||||
import {
|
|
||||||
type MeasuredDimensions,
|
|
||||||
runOnJS,
|
|
||||||
runOnUI,
|
|
||||||
} from 'react-native-reanimated'
|
|
||||||
import {Image} from 'expo-image'
|
|
||||||
import {
|
|
||||||
AppBskyEmbedExternal,
|
|
||||||
AppBskyEmbedImages,
|
|
||||||
AppBskyEmbedRecord,
|
|
||||||
AppBskyEmbedRecordWithMedia,
|
|
||||||
AppBskyEmbedVideo,
|
|
||||||
AppBskyFeedDefs,
|
|
||||||
AppBskyGraphDefs,
|
|
||||||
moderateFeedGenerator,
|
moderateFeedGenerator,
|
||||||
moderateUserList,
|
moderateUserList,
|
||||||
type ModerationDecision,
|
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
|
|
||||||
import {type HandleRef, measureHandle} from '#/lib/hooks/useHandleRef'
|
|
||||||
import {usePalette} from '#/lib/hooks/usePalette'
|
import {usePalette} from '#/lib/hooks/usePalette'
|
||||||
import {useLightboxControls} from '#/state/lightbox'
|
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
import {FeedSourceCard} from '#/view/com/feeds/FeedSourceCard'
|
import {FeedSourceCard} from '#/view/com/feeds/FeedSourceCard'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import * as ListCard from '#/components/ListCard'
|
import * as ListCard from '#/components/ListCard'
|
||||||
import {ExternalEmbed} from '#/components/Post/Embed/ExternalEmbed'
|
|
||||||
import {VideoEmbed} from '#/components/Post/Embed/VideoEmbed'
|
|
||||||
import {Embed as StarterPackCard} from '#/components/StarterPack/StarterPackCard'
|
|
||||||
import {ContentHider} from '../../../../components/moderation/ContentHider'
|
import {ContentHider} from '../../../../components/moderation/ContentHider'
|
||||||
import {type Dimensions} from '../../lightbox/ImageViewing/@types'
|
|
||||||
import {AutoSizedImage} from '../images/AutoSizedImage'
|
|
||||||
import {ImageLayoutGrid} from '../images/ImageLayoutGrid'
|
|
||||||
import {MaybeQuoteEmbed} from './QuoteEmbed'
|
|
||||||
import {PostEmbedViewContext, QuoteEmbedViewContext} from './types'
|
|
||||||
|
|
||||||
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<ViewStyle>
|
|
||||||
allowNestedQuotes?: boolean
|
|
||||||
viewContext?: PostEmbedViewContext
|
|
||||||
}) {
|
|
||||||
const {openLightbox} = useLightboxControls()
|
|
||||||
|
|
||||||
// quote post with media
|
|
||||||
// =
|
|
||||||
if (AppBskyEmbedRecordWithMedia.isView(embed)) {
|
|
||||||
return (
|
|
||||||
<View style={style}>
|
|
||||||
<PostEmbeds
|
|
||||||
embed={embed.media}
|
|
||||||
moderation={moderation}
|
|
||||||
onOpen={onOpen}
|
|
||||||
viewContext={viewContext}
|
|
||||||
/>
|
|
||||||
<MaybeQuoteEmbed
|
|
||||||
embed={embed.record}
|
|
||||||
onOpen={onOpen}
|
|
||||||
viewContext={
|
|
||||||
viewContext === PostEmbedViewContext.Feed
|
|
||||||
? QuoteEmbedViewContext.FeedEmbedRecordWithMedia
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AppBskyEmbedRecord.isView(embed)) {
|
|
||||||
// custom feed embed (i.e. generator view)
|
|
||||||
if (AppBskyFeedDefs.isGeneratorView(embed.record)) {
|
|
||||||
return (
|
|
||||||
<View style={a.mt_sm}>
|
|
||||||
<MaybeFeedCard view={embed.record} />
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// list embed
|
|
||||||
if (AppBskyGraphDefs.isListView(embed.record)) {
|
|
||||||
return (
|
|
||||||
<View style={a.mt_sm}>
|
|
||||||
<MaybeListCard view={embed.record} />
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// starter pack embed
|
|
||||||
if (AppBskyGraphDefs.isStarterPackViewBasic(embed.record)) {
|
|
||||||
return (
|
|
||||||
<View style={a.mt_sm}>
|
|
||||||
<StarterPackCard starterPack={embed.record} />
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// quote post
|
|
||||||
// =
|
|
||||||
return (
|
|
||||||
<MaybeQuoteEmbed
|
|
||||||
embed={embed}
|
|
||||||
style={style}
|
|
||||||
onOpen={onOpen}
|
|
||||||
allowNestedQuotes={allowNestedQuotes}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 (
|
|
||||||
<ContentHider modui={moderation?.ui('contentMedia')}>
|
|
||||||
<View style={[a.mt_sm, style]}>
|
|
||||||
<AutoSizedImage
|
|
||||||
crop={
|
|
||||||
viewContext === PostEmbedViewContext.ThreadHighlighted
|
|
||||||
? 'none'
|
|
||||||
: viewContext ===
|
|
||||||
PostEmbedViewContext.FeedEmbedRecordWithMedia
|
|
||||||
? 'square'
|
|
||||||
: 'constrained'
|
|
||||||
}
|
|
||||||
image={image}
|
|
||||||
onPress={(containerRef, dims) =>
|
|
||||||
onPress(0, [containerRef], [dims])
|
|
||||||
}
|
|
||||||
onPressIn={() => onPressIn(0)}
|
|
||||||
hideBadge={
|
|
||||||
viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</ContentHider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ContentHider modui={moderation?.ui('contentMedia')}>
|
|
||||||
<View style={[a.mt_sm, style]}>
|
|
||||||
<ImageLayoutGrid
|
|
||||||
images={embed.images}
|
|
||||||
onPress={onPress}
|
|
||||||
onPressIn={onPressIn}
|
|
||||||
viewContext={viewContext}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</ContentHider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// external link embed
|
|
||||||
// =
|
|
||||||
if (AppBskyEmbedExternal.isView(embed)) {
|
|
||||||
const link = embed.external
|
|
||||||
return (
|
|
||||||
<ContentHider modui={moderation?.ui('contentMedia')}>
|
|
||||||
<ExternalEmbed link={link} onOpen={onOpen} style={[a.mt_sm, style]} />
|
|
||||||
</ContentHider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// video embed
|
|
||||||
// =
|
|
||||||
if (AppBskyEmbedVideo.isView(embed)) {
|
|
||||||
return (
|
|
||||||
<ContentHider modui={moderation?.ui('contentMedia')}>
|
|
||||||
<VideoEmbed
|
|
||||||
embed={embed}
|
|
||||||
crop={
|
|
||||||
viewContext === PostEmbedViewContext.ThreadHighlighted
|
|
||||||
? 'none'
|
|
||||||
: viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia
|
|
||||||
? 'square'
|
|
||||||
: 'constrained'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ContentHider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return <View />
|
|
||||||
}
|
|
||||||
|
|
||||||
export function MaybeFeedCard({view}: {view: AppBskyFeedDefs.GeneratorView}) {
|
export function MaybeFeedCard({view}: {view: AppBskyFeedDefs.GeneratorView}) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
export enum PostEmbedViewContext {
|
|
||||||
ThreadHighlighted = 'ThreadHighlighted',
|
|
||||||
Feed = 'Feed',
|
|
||||||
FeedEmbedRecordWithMedia = 'FeedEmbedRecordWithMedia',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum QuoteEmbedViewContext {
|
|
||||||
FeedEmbedRecordWithMedia = PostEmbedViewContext.FeedEmbedRecordWithMedia,
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user