about summary refs log tree commit diff
path: root/src/view/com/util/post-embeds
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/util/post-embeds')
-rw-r--r--src/view/com/util/post-embeds/ExternalLinkEmbed.tsx59
-rw-r--r--src/view/com/util/post-embeds/QuoteEmbed.tsx85
-rw-r--r--src/view/com/util/post-embeds/index.tsx98
3 files changed, 158 insertions, 84 deletions
diff --git a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx b/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx
index a4cbb3e29..81f1ca560 100644
--- a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx
+++ b/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx
@@ -1,9 +1,11 @@
 import React from 'react'
+import {Image} from 'expo-image'
 import {Text} from '../text/Text'
-import {AutoSizedImage} from '../images/AutoSizedImage'
 import {StyleSheet, View} from 'react-native'
 import {usePalette} from 'lib/hooks/usePalette'
 import {AppBskyEmbedExternal} from '@atproto/api'
+import {isDesktopWeb} from 'platform/detection'
+import {toNiceDomain} from 'lib/strings/url-helpers'
 
 export const ExternalLinkEmbed = ({
   link,
@@ -14,44 +16,71 @@ export const ExternalLinkEmbed = ({
 }) => {
   const pal = usePalette('default')
   return (
-    <>
+    <View style={styles.extContainer}>
       {link.thumb ? (
-        <AutoSizedImage uri={link.thumb} style={styles.extImage}>
+        <View style={styles.extImageContainer}>
+          <Image
+            style={styles.extImage}
+            source={{uri: link.thumb}}
+            accessibilityIgnoresInvertColors
+          />
           {imageChild}
-        </AutoSizedImage>
+        </View>
       ) : undefined}
       <View style={styles.extInner}>
-        <Text type="md-bold" numberOfLines={2} style={[pal.text]}>
-          {link.title || link.uri}
-        </Text>
         <Text
           type="sm"
           numberOfLines={1}
           style={[pal.textLight, styles.extUri]}>
-          {link.uri}
+          {toNiceDomain(link.uri)}
+        </Text>
+        <Text
+          type="lg-bold"
+          numberOfLines={isDesktopWeb ? 2 : 4}
+          style={[pal.text]}>
+          {link.title || link.uri}
         </Text>
         {link.description ? (
           <Text
-            type="sm"
-            numberOfLines={2}
+            type="md"
+            numberOfLines={isDesktopWeb ? 2 : 4}
             style={[pal.text, styles.extDescription]}>
             {link.description}
           </Text>
         ) : undefined}
       </View>
-    </>
+    </View>
   )
 }
 
 const styles = StyleSheet.create({
+  extContainer: {
+    flexDirection: isDesktopWeb ? 'row' : 'column',
+  },
   extInner: {
-    padding: 10,
+    paddingHorizontal: isDesktopWeb ? 14 : 10,
+    paddingTop: 8,
+    paddingBottom: 10,
+    flex: isDesktopWeb ? 1 : undefined,
   },
+  extImageContainer: isDesktopWeb
+    ? {
+        borderTopLeftRadius: 6,
+        borderBottomLeftRadius: 6,
+        width: 120,
+        aspectRatio: 1,
+        overflow: 'hidden',
+      }
+    : {
+        borderTopLeftRadius: 6,
+        borderTopRightRadius: 6,
+        width: '100%',
+        height: 200,
+        overflow: 'hidden',
+      },
   extImage: {
-    borderTopLeftRadius: 6,
-    borderTopRightRadius: 6,
     width: '100%',
-    maxHeight: 200,
+    height: 200,
   },
   extUri: {
     marginTop: 2,
diff --git a/src/view/com/util/post-embeds/QuoteEmbed.tsx b/src/view/com/util/post-embeds/QuoteEmbed.tsx
index 4995562ac..f82b5b7df 100644
--- a/src/view/com/util/post-embeds/QuoteEmbed.tsx
+++ b/src/view/com/util/post-embeds/QuoteEmbed.tsx
@@ -1,6 +1,12 @@
 import React from 'react'
-import {StyleProp, StyleSheet, ViewStyle} from 'react-native'
-import {AppBskyEmbedImages, AppBskyEmbedRecordWithMedia} from '@atproto/api'
+import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
+import {
+  AppBskyEmbedRecord,
+  AppBskyFeedPost,
+  AppBskyEmbedImages,
+  AppBskyEmbedRecordWithMedia,
+  ModerationUI,
+} from '@atproto/api'
 import {AtUri} from '@atproto/api'
 import {PostMeta} from '../PostMeta'
 import {Link} from '../Link'
@@ -8,13 +14,68 @@ import {Text} from '../text/Text'
 import {usePalette} from 'lib/hooks/usePalette'
 import {ComposerOptsQuote} from 'state/models/ui/shell'
 import {PostEmbeds} from '.'
+import {PostAlerts} from '../moderation/PostAlerts'
 import {makeProfileLink} from 'lib/routes/links'
+import {InfoCircleIcon} from 'lib/icons'
+
+export function MaybeQuoteEmbed({
+  embed,
+  moderation,
+  style,
+}: {
+  embed: AppBskyEmbedRecord.View
+  moderation: ModerationUI
+  style?: StyleProp<ViewStyle>
+}) {
+  const pal = usePalette('default')
+  if (
+    AppBskyEmbedRecord.isViewRecord(embed.record) &&
+    AppBskyFeedPost.isRecord(embed.record.value) &&
+    AppBskyFeedPost.validateRecord(embed.record.value).success
+  ) {
+    return (
+      <QuoteEmbed
+        quote={{
+          author: embed.record.author,
+          cid: embed.record.cid,
+          uri: embed.record.uri,
+          indexedAt: embed.record.indexedAt,
+          text: embed.record.value.text,
+          embeds: embed.record.embeds,
+        }}
+        moderation={moderation}
+        style={style}
+      />
+    )
+  } else if (AppBskyEmbedRecord.isViewBlocked(embed.record)) {
+    return (
+      <View style={[styles.errorContainer, pal.borderDark]}>
+        <InfoCircleIcon size={18} style={pal.text} />
+        <Text type="lg" style={pal.text}>
+          Blocked
+        </Text>
+      </View>
+    )
+  } else if (AppBskyEmbedRecord.isViewNotFound(embed.record)) {
+    return (
+      <View style={[styles.errorContainer, pal.borderDark]}>
+        <InfoCircleIcon size={18} style={pal.text} />
+        <Text type="lg" style={pal.text}>
+          Deleted
+        </Text>
+      </View>
+    )
+  }
+  return null
+}
 
 export function QuoteEmbed({
   quote,
+  moderation,
   style,
 }: {
   quote: ComposerOptsQuote
+  moderation?: ModerationUI
   style?: StyleProp<ViewStyle>
 }) {
   const pal = usePalette('default')
@@ -46,16 +107,19 @@ export function QuoteEmbed({
         postHref={itemHref}
         timestamp={quote.indexedAt}
       />
+      {moderation ? (
+        <PostAlerts moderation={moderation} style={styles.alert} />
+      ) : null}
       {!isEmpty ? (
         <Text type="post-text" style={pal.text} numberOfLines={6}>
           {quote.text}
         </Text>
       ) : null}
       {AppBskyEmbedImages.isView(imagesEmbed) && (
-        <PostEmbeds embed={imagesEmbed} />
+        <PostEmbeds embed={imagesEmbed} moderation={{}} />
       )}
       {AppBskyEmbedRecordWithMedia.isView(imagesEmbed) && (
-        <PostEmbeds embed={imagesEmbed.media} />
+        <PostEmbeds embed={imagesEmbed.media} moderation={{}} />
       )}
     </Link>
   )
@@ -76,4 +140,17 @@ const styles = StyleSheet.create({
     paddingLeft: 13,
     paddingRight: 8,
   },
+  errorContainer: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    gap: 4,
+    borderRadius: 8,
+    marginTop: 8,
+    paddingVertical: 14,
+    paddingHorizontal: 14,
+    borderWidth: 1,
+  },
+  alert: {
+    marginBottom: 6,
+  },
 })
diff --git a/src/view/com/util/post-embeds/index.tsx b/src/view/com/util/post-embeds/index.tsx
index 7ffebff54..5d0090434 100644
--- a/src/view/com/util/post-embeds/index.tsx
+++ b/src/view/com/util/post-embeds/index.tsx
@@ -4,17 +4,18 @@ import {
   StyleProp,
   View,
   ViewStyle,
-  Image as RNImage,
   Text,
+  InteractionManager,
 } from 'react-native'
+import {Image} from 'expo-image'
 import {
   AppBskyEmbedImages,
   AppBskyEmbedExternal,
   AppBskyEmbedRecord,
   AppBskyEmbedRecordWithMedia,
-  AppBskyFeedPost,
   AppBskyFeedDefs,
   AppBskyGraphDefs,
+  ModerationUI,
 } from '@atproto/api'
 import {Link} from '../Link'
 import {ImageLayoutGrid} from '../images/ImageLayoutGrid'
@@ -24,11 +25,12 @@ import {usePalette} from 'lib/hooks/usePalette'
 import {YoutubeEmbed} from './YoutubeEmbed'
 import {ExternalLinkEmbed} from './ExternalLinkEmbed'
 import {getYoutubeVideoId} from 'lib/strings/url-helpers'
-import QuoteEmbed from './QuoteEmbed'
+import {MaybeQuoteEmbed} from './QuoteEmbed'
 import {AutoSizedImage} from '../images/AutoSizedImage'
 import {CustomFeedEmbed} from './CustomFeedEmbed'
 import {ListEmbed} from './ListEmbed'
 import {isDesktopWeb} from 'platform/detection'
+import {isCauseALabelOnUri} from 'lib/moderation'
 
 type Embed =
   | AppBskyEmbedRecord.View
@@ -39,9 +41,11 @@ type Embed =
 
 export function PostEmbeds({
   embed,
+  moderation,
   style,
 }: {
   embed?: Embed
+  moderation: ModerationUI
   style?: StyleProp<ViewStyle>
 }) {
   const pal = usePalette('default')
@@ -49,51 +53,37 @@ export function PostEmbeds({
 
   // quote post with media
   // =
-  if (
-    AppBskyEmbedRecordWithMedia.isView(embed) &&
-    AppBskyEmbedRecord.isViewRecord(embed.record.record) &&
-    AppBskyFeedPost.isRecord(embed.record.record.value) &&
-    AppBskyFeedPost.validateRecord(embed.record.record.value).success
-  ) {
+  if (AppBskyEmbedRecordWithMedia.isView(embed)) {
+    const isModOnQuote =
+      AppBskyEmbedRecord.isViewRecord(embed.record.record) &&
+      isCauseALabelOnUri(moderation.cause, embed.record.record.uri)
+    const mediaModeration = isModOnQuote ? {} : moderation
+    const quoteModeration = isModOnQuote ? moderation : {}
     return (
       <View style={[styles.stackContainer, style]}>
-        <PostEmbeds embed={embed.media} />
-        <QuoteEmbed
-          quote={{
-            author: embed.record.record.author,
-            cid: embed.record.record.cid,
-            uri: embed.record.record.uri,
-            indexedAt: embed.record.record.indexedAt,
-            text: embed.record.record.value.text,
-            embeds: embed.record.record.embeds,
-          }}
-        />
+        <PostEmbeds embed={embed.media} moderation={mediaModeration} />
+        <MaybeQuoteEmbed embed={embed.record} moderation={quoteModeration} />
       </View>
     )
   }
 
-  // quote post
-  // =
   if (AppBskyEmbedRecord.isView(embed)) {
-    if (
-      AppBskyEmbedRecord.isViewRecord(embed.record) &&
-      AppBskyFeedPost.isRecord(embed.record.value) &&
-      AppBskyFeedPost.validateRecord(embed.record.value).success
-    ) {
-      return (
-        <QuoteEmbed
-          quote={{
-            author: embed.record.author,
-            cid: embed.record.cid,
-            uri: embed.record.uri,
-            indexedAt: embed.record.indexedAt,
-            text: embed.record.value.text,
-            embeds: embed.record.embeds,
-          }}
-          style={style}
-        />
-      )
+    // custom feed embed (i.e. generator view)
+    // =
+    if (AppBskyFeedDefs.isGeneratorView(embed.record)) {
+      return <CustomFeedEmbed record={embed.record} />
     }
+
+    // list embed (e.g. mute lists; i.e. ListView)
+    if (AppBskyGraphDefs.isListView(embed.record)) {
+      return <ListEmbed item={embed.record} />
+    }
+
+    // quote post
+    // =
+    return (
+      <MaybeQuoteEmbed embed={embed} style={style} moderation={moderation} />
+    )
   }
 
   // image embed
@@ -106,14 +96,9 @@ export function PostEmbeds({
       const openLightbox = (index: number) => {
         store.shell.openLightbox(new ImagesLightbox(items, index))
       }
-      const onPressIn = (index: number) => {
-        const firstImageToShow = items[index].uri
-        RNImage.prefetch(firstImageToShow)
-        items.forEach(item => {
-          if (firstImageToShow !== item.uri) {
-            // First image already prefetched above
-            RNImage.prefetch(item.uri)
-          }
+      const onPressIn = (_: number) => {
+        InteractionManager.runAfterInteractions(() => {
+          Image.prefetch(items.map(i => i.uri))
         })
       }
 
@@ -152,23 +137,6 @@ export function PostEmbeds({
     }
   }
 
-  // custom feed embed (i.e. generator view)
-  // =
-  if (
-    AppBskyEmbedRecord.isView(embed) &&
-    AppBskyFeedDefs.isGeneratorView(embed.record)
-  ) {
-    return <CustomFeedEmbed record={embed.record} />
-  }
-
-  // list embed (e.g. mute lists; i.e. ListView)
-  if (
-    AppBskyEmbedRecord.isView(embed) &&
-    AppBskyGraphDefs.isListView(embed.record)
-  ) {
-    return <ListEmbed item={embed.record} />
-  }
-
   // external link embed
   // =
   if (AppBskyEmbedExternal.isView(embed)) {