about summary refs log tree commit diff
path: root/src/components/live/LinkPreview.tsx
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2025-05-20 18:49:20 +0300
committerGitHub <noreply@github.com>2025-05-20 18:49:20 +0300
commita18b25d16c7ff4e5233cc6ca45511ba42b12f55f (patch)
tree6aca1c86eac4b69fdc1aecd3f081e7e5135c7ebb /src/components/live/LinkPreview.tsx
parentc7101870944a34f874fd80b18c16e38e24d6b51b (diff)
downloadvoidsky-a18b25d16c7ff4e5233cc6ca45511ba42b12f55f.tar.zst
[Live] Add warning if link is missing image (#8393)
Diffstat (limited to 'src/components/live/LinkPreview.tsx')
-rw-r--r--src/components/live/LinkPreview.tsx98
1 files changed, 98 insertions, 0 deletions
diff --git a/src/components/live/LinkPreview.tsx b/src/components/live/LinkPreview.tsx
new file mode 100644
index 000000000..98320a9e8
--- /dev/null
+++ b/src/components/live/LinkPreview.tsx
@@ -0,0 +1,98 @@
+import {useState} from 'react'
+import {View} from 'react-native'
+import {Image} from 'expo-image'
+import {Trans} from '@lingui/macro'
+
+import {type LinkMeta} from '#/lib/link-meta/link-meta'
+import {toNiceDomain} from '#/lib/strings/url-helpers'
+import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
+import {atoms as a, useTheme} from '#/alf'
+import {Globe_Stroke2_Corner0_Rounded as GlobeIcon} from '#/components/icons/Globe'
+import {Image_Stroke2_Corner0_Rounded as ImageIcon} from '#/components/icons/Image'
+import {Text} from '#/components/Typography'
+
+export function LinkPreview({
+  linkMeta,
+  loading,
+}: {
+  linkMeta?: LinkMeta
+  loading: boolean
+}) {
+  const t = useTheme()
+  const [imageLoadError, setImageLoadError] = useState(false)
+
+  if (!linkMeta && !loading) {
+    return null
+  }
+
+  return (
+    <View
+      style={[
+        a.w_full,
+        a.border,
+        t.atoms.border_contrast_low,
+        t.atoms.bg,
+        a.flex_row,
+        a.rounded_sm,
+        a.overflow_hidden,
+        a.align_stretch,
+      ]}>
+      <View
+        style={[
+          t.atoms.bg_contrast_25,
+          {minHeight: 64, width: 114},
+          a.justify_center,
+          a.align_center,
+          a.gap_xs,
+        ]}>
+        {linkMeta?.image && (
+          <Image
+            source={linkMeta.image}
+            accessibilityIgnoresInvertColors
+            transition={200}
+            style={[a.absolute, a.inset_0]}
+            contentFit="cover"
+            onLoad={() => setImageLoadError(false)}
+            onError={() => setImageLoadError(true)}
+          />
+        )}
+        {linkMeta && (!linkMeta.image || imageLoadError) && (
+          <>
+            <ImageIcon style={[t.atoms.text_contrast_low]} />
+            <Text style={[t.atoms.text_contrast_low, a.text_xs, a.text_center]}>
+              <Trans>No image</Trans>
+            </Text>
+          </>
+        )}
+      </View>
+      <View style={[a.flex_1, a.justify_center, a.py_sm, a.gap_xs, a.px_md]}>
+        {linkMeta ? (
+          <>
+            <Text
+              numberOfLines={2}
+              style={[a.leading_snug, a.font_bold, a.text_md]}>
+              {linkMeta.title || linkMeta.url}
+            </Text>
+            <View style={[a.flex_row, a.align_center, a.gap_2xs]}>
+              <GlobeIcon size="xs" style={[t.atoms.text_contrast_low]} />
+              <Text
+                numberOfLines={1}
+                style={[
+                  a.text_xs,
+                  a.leading_snug,
+                  t.atoms.text_contrast_medium,
+                ]}>
+                {toNiceDomain(linkMeta.url)}
+              </Text>
+            </View>
+          </>
+        ) : (
+          <>
+            <LoadingPlaceholder height={16} width={128} />
+            <LoadingPlaceholder height={12} width={72} />
+          </>
+        )}
+      </View>
+    </View>
+  )
+}