diff options
author | Eric Bailey <git@esb.lol> | 2025-06-13 12:05:41 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-06-13 12:05:41 -0500 |
commit | 45f0f7eefecae1922c2f30d4e7760d2b93b1ae56 (patch) | |
tree | a2fd6917867f18fe334b54dd3289775c2930bc85 /src/view/com/util/post-embeds/QuoteEmbed.tsx | |
parent | ba0f5a9bdef5bd0447ded23cab1af222b65511cc (diff) | |
download | voidsky-45f0f7eefecae1922c2f30d4e7760d2b93b1ae56.tar.zst |
Port post embeds to new arch (#7408)
* Direct port of embeds to new arch (cherry picked from commit cc3fa1f6cea396dd9222486c633a508bfee1ecd6) * Re-org * Split out ListEmbed and FeedEmbed * Split out ImageEmbed * DRY up a bit * Port over ExternalLinkEmbed * Port over Player and Gif embeds * Migrate ComposerReplyTo * Replace other usages of old post-embeds * Migrate view contexts * Copy pasta VideoEmbed * Copy pasta GifEmbed * Swap in new file location * Clean up * Fix up native * Add back in correct moderation on List and Feed embeds * Format * Prettier * delete old video utils * move bandwidth-estimate.ts * Remove log * Add LazyQuoteEmbed for composer use * Clean up unused things * Remove remaining items * Prettier * Fix imports * Handle nested quotes same as prod * Add back silenced error handling * Fix lint --------- Co-authored-by: Samuel Newman <mozzius@protonmail.com>
Diffstat (limited to 'src/view/com/util/post-embeds/QuoteEmbed.tsx')
-rw-r--r-- | src/view/com/util/post-embeds/QuoteEmbed.tsx | 337 |
1 files changed, 0 insertions, 337 deletions
diff --git a/src/view/com/util/post-embeds/QuoteEmbed.tsx b/src/view/com/util/post-embeds/QuoteEmbed.tsx deleted file mode 100644 index f788af1f8..000000000 --- a/src/view/com/util/post-embeds/QuoteEmbed.tsx +++ /dev/null @@ -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, - }, -}) |