about summary refs log tree commit diff
path: root/src/view/com/util/post-embeds/QuoteEmbed.tsx
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2025-06-13 12:05:41 -0500
committerGitHub <noreply@github.com>2025-06-13 12:05:41 -0500
commit45f0f7eefecae1922c2f30d4e7760d2b93b1ae56 (patch)
treea2fd6917867f18fe334b54dd3289775c2930bc85 /src/view/com/util/post-embeds/QuoteEmbed.tsx
parentba0f5a9bdef5bd0447ded23cab1af222b65511cc (diff)
downloadvoidsky-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.tsx337
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,
-  },
-})