mirror of
				https://github.com/tooot-app/app
				synced 2025-06-05 22:19:13 +02:00 
			
		
		
		
	Test #700
This commit is contained in:
		@@ -38,7 +38,7 @@ const ComponentAccount: React.FC<PropsWithChildren & Props> = ({ account, props,
 | 
			
		||||
        <>
 | 
			
		||||
          <View style={{ flex: 1, flexDirection: 'row', alignItems: 'center' }}>
 | 
			
		||||
            <GracefullyImage
 | 
			
		||||
              uri={{ original: account.avatar, static: account.avatar_static }}
 | 
			
		||||
              sources={{ default: { uri: account.avatar }, static: { uri: account.avatar_static } }}
 | 
			
		||||
              style={{
 | 
			
		||||
                width: StyleConstants.Avatar.S,
 | 
			
		||||
                height: StyleConstants.Avatar.S,
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,7 @@ const AccountButton: React.FC<Props> = ({ account, additionalActions }) => {
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      <GracefullyImage
 | 
			
		||||
        uri={{ original: account.avatar_static }}
 | 
			
		||||
        sources={{ default: { uri: account.avatar_static } }}
 | 
			
		||||
        dimension={{
 | 
			
		||||
          width: StyleConstants.Font.Size.L,
 | 
			
		||||
          height: StyleConstants.Font.Size.L
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ import { getAccountStorage, setAccountStorage } from '@utils/storage/actions'
 | 
			
		||||
import { StyleConstants } from '@utils/styles/constants'
 | 
			
		||||
import layoutAnimation from '@utils/styles/layoutAnimation'
 | 
			
		||||
import { useTheme } from '@utils/styles/ThemeManager'
 | 
			
		||||
import { Image } from 'expo-image'
 | 
			
		||||
import { chunk } from 'lodash'
 | 
			
		||||
import React, { useContext, useEffect, useRef, useState } from 'react'
 | 
			
		||||
import { useTranslation } from 'react-i18next'
 | 
			
		||||
@@ -19,7 +20,6 @@ import {
 | 
			
		||||
  TextInput,
 | 
			
		||||
  View
 | 
			
		||||
} from 'react-native'
 | 
			
		||||
import FastImage from 'react-native-fast-image'
 | 
			
		||||
import EmojisContext from './Context'
 | 
			
		||||
 | 
			
		||||
const EmojisList = () => {
 | 
			
		||||
@@ -129,9 +129,7 @@ const EmojisList = () => {
 | 
			
		||||
              }}
 | 
			
		||||
              style={{ padding: StyleConstants.Spacing.S }}
 | 
			
		||||
            >
 | 
			
		||||
              <FastImage
 | 
			
		||||
                enterTransition='fadeIn'
 | 
			
		||||
                transitionDuration={60}
 | 
			
		||||
              <Image
 | 
			
		||||
                accessibilityLabel={t('common:customEmoji.accessibilityLabel', {
 | 
			
		||||
                  emoji: emoji.shortcode
 | 
			
		||||
                })}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,36 +1,26 @@
 | 
			
		||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
 | 
			
		||||
import { connectMedia } from '@utils/api/helpers/connect'
 | 
			
		||||
import { useTheme } from '@utils/styles/ThemeManager'
 | 
			
		||||
import React, { useEffect, useState } from 'react'
 | 
			
		||||
import {
 | 
			
		||||
  AccessibilityProps,
 | 
			
		||||
  Image,
 | 
			
		||||
  Pressable,
 | 
			
		||||
  StyleProp,
 | 
			
		||||
  StyleSheet,
 | 
			
		||||
  View,
 | 
			
		||||
  ViewStyle
 | 
			
		||||
} from 'react-native'
 | 
			
		||||
import { Blurhash } from 'react-native-blurhash'
 | 
			
		||||
import FastImage, { ImageStyle } from 'react-native-fast-image'
 | 
			
		||||
 | 
			
		||||
// blurhas -> if blurhash, show before any loading succeed
 | 
			
		||||
// original -> load original
 | 
			
		||||
// original, remote -> if original failed, then remote
 | 
			
		||||
// preview, original -> first show preview, then original
 | 
			
		||||
// preview, original, remote -> first show preview, then original, if original failed, then remote
 | 
			
		||||
import { Image, ImageSource, ImageStyle } from 'expo-image'
 | 
			
		||||
import React, { useState } from 'react'
 | 
			
		||||
import { AccessibilityProps, Pressable, StyleProp, View, ViewStyle } from 'react-native'
 | 
			
		||||
 | 
			
		||||
export interface Props {
 | 
			
		||||
  accessibilityLabel?: AccessibilityProps['accessibilityLabel']
 | 
			
		||||
  accessibilityHint?: AccessibilityProps['accessibilityHint']
 | 
			
		||||
 | 
			
		||||
  hidden?: boolean
 | 
			
		||||
  uri: { preview?: string; original?: string; remote?: string; static?: string }
 | 
			
		||||
  blurhash?: string
 | 
			
		||||
  sources: {
 | 
			
		||||
    preview?: ImageSource
 | 
			
		||||
    default?: ImageSource
 | 
			
		||||
    remote?: ImageSource
 | 
			
		||||
    static?: ImageSource
 | 
			
		||||
    blurhash?: string
 | 
			
		||||
  }
 | 
			
		||||
  dimension?: { width: number; height: number }
 | 
			
		||||
  onPress?: () => void
 | 
			
		||||
  style?: StyleProp<ViewStyle>
 | 
			
		||||
  imageStyle?: StyleProp<ImageStyle>
 | 
			
		||||
  imageStyle?: ImageStyle
 | 
			
		||||
  // For image viewer when there is no image size available
 | 
			
		||||
  setImageDimensions?: React.Dispatch<
 | 
			
		||||
    React.SetStateAction<{
 | 
			
		||||
@@ -39,49 +29,30 @@ export interface Props {
 | 
			
		||||
    }>
 | 
			
		||||
  >
 | 
			
		||||
  dim?: boolean
 | 
			
		||||
  enableLiveTextInteraction?: boolean
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const GracefullyImage = ({
 | 
			
		||||
  accessibilityLabel,
 | 
			
		||||
  accessibilityHint,
 | 
			
		||||
  hidden = false,
 | 
			
		||||
  uri,
 | 
			
		||||
  blurhash,
 | 
			
		||||
  sources,
 | 
			
		||||
  dimension,
 | 
			
		||||
  onPress,
 | 
			
		||||
  style,
 | 
			
		||||
  imageStyle,
 | 
			
		||||
  setImageDimensions,
 | 
			
		||||
  dim
 | 
			
		||||
  dim,
 | 
			
		||||
  enableLiveTextInteraction = false
 | 
			
		||||
}: Props) => {
 | 
			
		||||
  const { reduceMotionEnabled } = useAccessibility()
 | 
			
		||||
  const { colors, theme } = useTheme()
 | 
			
		||||
  const [imageLoaded, setImageLoaded] = useState(false)
 | 
			
		||||
  const { theme } = useTheme()
 | 
			
		||||
 | 
			
		||||
  const [currentUri, setCurrentUri] = useState<string | undefined>(uri.original || uri.remote)
 | 
			
		||||
  const source: { uri?: string } = {
 | 
			
		||||
    uri: reduceMotionEnabled && uri.static ? uri.static : currentUri
 | 
			
		||||
  }
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (
 | 
			
		||||
      (uri.original ? currentUri !== uri.original : true) &&
 | 
			
		||||
      (uri.remote ? currentUri !== uri.remote : true)
 | 
			
		||||
    ) {
 | 
			
		||||
      setCurrentUri(uri.original || uri.remote)
 | 
			
		||||
    }
 | 
			
		||||
  }, [currentUri, uri.original, uri.remote])
 | 
			
		||||
 | 
			
		||||
  const blurhashView = () => {
 | 
			
		||||
    if (hidden || !imageLoaded) {
 | 
			
		||||
      if (blurhash) {
 | 
			
		||||
        return <Blurhash decodeAsync blurhash={blurhash} style={styles.placeholder} />
 | 
			
		||||
      } else {
 | 
			
		||||
        return <View style={[styles.placeholder, { backgroundColor: colors.shimmerDefault }]} />
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      return null
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  const [currentSource, setCurrentSource] = useState<ImageSource | undefined>(
 | 
			
		||||
    sources.default || sources.remote
 | 
			
		||||
  )
 | 
			
		||||
  const source: ImageSource | undefined =
 | 
			
		||||
    reduceMotionEnabled && sources.static ? sources.static : currentSource
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Pressable
 | 
			
		||||
@@ -91,50 +62,40 @@ const GracefullyImage = ({
 | 
			
		||||
      style={[style, dimension]}
 | 
			
		||||
      {...(onPress ? (hidden ? { disabled: true } : { onPress }) : { disabled: true })}
 | 
			
		||||
    >
 | 
			
		||||
      {uri.preview && !imageLoaded ? (
 | 
			
		||||
        <FastImage
 | 
			
		||||
          source={connectMedia({ uri: uri.preview })}
 | 
			
		||||
          enterTransition='fadeIn'
 | 
			
		||||
          transitionDuration={60}
 | 
			
		||||
          style={[styles.placeholder]}
 | 
			
		||||
        />
 | 
			
		||||
      ) : null}
 | 
			
		||||
      <FastImage
 | 
			
		||||
        source={connectMedia(source)}
 | 
			
		||||
        enterTransition={!blurhash && !uri.preview ? 'fadeIn' : 'none'}
 | 
			
		||||
        transitionDuration={60}
 | 
			
		||||
        style={[{ flex: 1 }, imageStyle]}
 | 
			
		||||
        onLoad={() => {
 | 
			
		||||
          setImageLoaded(true)
 | 
			
		||||
          if (setImageDimensions && source.uri) {
 | 
			
		||||
            Image.getSize(source.uri, (width, height) => setImageDimensions({ width, height }))
 | 
			
		||||
      <Image
 | 
			
		||||
        placeholder={sources.blurhash || connectMedia(sources.preview)}
 | 
			
		||||
        source={hidden ? undefined : connectMedia(source)}
 | 
			
		||||
        transition={{ duration: 100 }}
 | 
			
		||||
        style={{ flex: 1, ...imageStyle }}
 | 
			
		||||
        onLoad={event => {
 | 
			
		||||
          if (setImageDimensions && event.source) {
 | 
			
		||||
            setImageDimensions(event.source)
 | 
			
		||||
          }
 | 
			
		||||
        }}
 | 
			
		||||
        onError={() => {
 | 
			
		||||
          if (uri.original && uri.original === currentUri && uri.remote) {
 | 
			
		||||
            setCurrentUri(uri.remote)
 | 
			
		||||
          if (
 | 
			
		||||
            sources.default?.uri &&
 | 
			
		||||
            sources.default?.uri === currentSource?.uri &&
 | 
			
		||||
            sources.remote
 | 
			
		||||
          ) {
 | 
			
		||||
            setCurrentSource(sources.remote)
 | 
			
		||||
          }
 | 
			
		||||
        }}
 | 
			
		||||
        enableLiveTextInteraction={enableLiveTextInteraction}
 | 
			
		||||
      />
 | 
			
		||||
      {blurhashView()}
 | 
			
		||||
      {dim && theme !== 'light' ? (
 | 
			
		||||
        <View
 | 
			
		||||
          style={[
 | 
			
		||||
            styles.placeholder,
 | 
			
		||||
            { backgroundColor: 'black', opacity: theme === 'dark_lighter' ? 0.18 : 0.36 }
 | 
			
		||||
          ]}
 | 
			
		||||
          style={{
 | 
			
		||||
            width: '100%',
 | 
			
		||||
            height: '100%',
 | 
			
		||||
            position: 'absolute',
 | 
			
		||||
            backgroundColor: 'black',
 | 
			
		||||
            opacity: theme === 'dark_lighter' ? 0.18 : 0.36
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
      ) : null}
 | 
			
		||||
    </Pressable>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const styles = StyleSheet.create({
 | 
			
		||||
  placeholder: {
 | 
			
		||||
    width: '100%',
 | 
			
		||||
    height: '100%',
 | 
			
		||||
    position: 'absolute'
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export default GracefullyImage
 | 
			
		||||
 
 | 
			
		||||
@@ -19,12 +19,13 @@ import {
 | 
			
		||||
import { StyleConstants } from '@utils/styles/constants'
 | 
			
		||||
import { useTheme } from '@utils/styles/ThemeManager'
 | 
			
		||||
import * as AuthSession from 'expo-auth-session'
 | 
			
		||||
import { Image } from 'expo-image'
 | 
			
		||||
import * as Random from 'expo-random'
 | 
			
		||||
import * as WebBrowser from 'expo-web-browser'
 | 
			
		||||
import { debounce } from 'lodash'
 | 
			
		||||
import React, { RefObject, useCallback, useState } from 'react'
 | 
			
		||||
import { Trans, useTranslation } from 'react-i18next'
 | 
			
		||||
import { Alert, Image, KeyboardAvoidingView, Platform, TextInput, View } from 'react-native'
 | 
			
		||||
import { Alert, KeyboardAvoidingView, Platform, TextInput, View } from 'react-native'
 | 
			
		||||
import { ScrollView } from 'react-native-gesture-handler'
 | 
			
		||||
import { fromByteArray } from 'react-native-quick-base64'
 | 
			
		||||
import parse from 'url-parse'
 | 
			
		||||
 
 | 
			
		||||
@@ -5,9 +5,9 @@ import { useGlobalStorage } from '@utils/storage/actions'
 | 
			
		||||
import { StyleConstants } from '@utils/styles/constants'
 | 
			
		||||
import { adaptiveScale } from '@utils/styles/scaling'
 | 
			
		||||
import { useTheme } from '@utils/styles/ThemeManager'
 | 
			
		||||
import { Image } from 'expo-image'
 | 
			
		||||
import React from 'react'
 | 
			
		||||
import { ColorValue, Platform, TextStyle } from 'react-native'
 | 
			
		||||
import FastImage from 'react-native-fast-image'
 | 
			
		||||
 | 
			
		||||
const regexEmoji = new RegExp(/(:[A-Za-z0-9_]+:)/)
 | 
			
		||||
 | 
			
		||||
@@ -77,8 +77,8 @@ const ParseEmojis: React.FC<Props> = ({
 | 
			
		||||
                return (
 | 
			
		||||
                  <CustomText key={emojiShortcode + i}>
 | 
			
		||||
                    {i === 0 ? ' ' : undefined}
 | 
			
		||||
                    <FastImage
 | 
			
		||||
                      source={connectMedia({ uri: uri.trim() })}
 | 
			
		||||
                    <Image
 | 
			
		||||
                      source={connectMedia({ uri })}
 | 
			
		||||
                      style={{
 | 
			
		||||
                        width: adaptedFontsize,
 | 
			
		||||
                        height: adaptedFontsize,
 | 
			
		||||
 
 | 
			
		||||
@@ -79,7 +79,10 @@ const TimelineConversation: React.FC<Props> = ({ conversation, queryKey, highlig
 | 
			
		||||
            {conversation.accounts.slice(0, 4).map(account => (
 | 
			
		||||
              <GracefullyImage
 | 
			
		||||
                key={account.id}
 | 
			
		||||
                uri={{ original: account.avatar, static: account.avatar_static }}
 | 
			
		||||
                sources={{
 | 
			
		||||
                  default: { uri: account.avatar },
 | 
			
		||||
                  static: { uri: account.avatar_static }
 | 
			
		||||
                }}
 | 
			
		||||
                dimension={{
 | 
			
		||||
                  width: StyleConstants.Avatar.M,
 | 
			
		||||
                  height:
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,6 @@ import { StackNavigationProp } from '@react-navigation/stack'
 | 
			
		||||
import { RootStackParamList } from '@utils/navigation/navigators'
 | 
			
		||||
import { usePreferencesQuery } from '@utils/queryHooks/preferences'
 | 
			
		||||
import { StyleConstants } from '@utils/styles/constants'
 | 
			
		||||
import layoutAnimation from '@utils/styles/layoutAnimation'
 | 
			
		||||
import React, { useContext, useState } from 'react'
 | 
			
		||||
import { useTranslation } from 'react-i18next'
 | 
			
		||||
import { Pressable, View } from 'react-native'
 | 
			
		||||
@@ -207,7 +206,6 @@ const TimelineAttachment = () => {
 | 
			
		||||
              content={t('shared.attachment.sensitive.button')}
 | 
			
		||||
              overlay
 | 
			
		||||
              onPress={() => {
 | 
			
		||||
                layoutAnimation()
 | 
			
		||||
                setSensitiveShown(false)
 | 
			
		||||
                haptics('Light')
 | 
			
		||||
              }}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,6 @@ import { useTheme } from '@utils/styles/ThemeManager'
 | 
			
		||||
import { Audio } from 'expo-av'
 | 
			
		||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
 | 
			
		||||
import { AppState, AppStateStatus, StyleSheet, View } from 'react-native'
 | 
			
		||||
import { Blurhash } from 'react-native-blurhash'
 | 
			
		||||
import AttachmentAltText from './AltText'
 | 
			
		||||
import { aspectRatio } from './dimensions'
 | 
			
		||||
 | 
			
		||||
@@ -72,19 +71,23 @@ const AttachmentAudio: React.FC<Props> = ({ total, index, sensitiveShown, audio
 | 
			
		||||
      <View style={styles.overlay}>
 | 
			
		||||
        {sensitiveShown ? (
 | 
			
		||||
          audio.blurhash ? (
 | 
			
		||||
            <Blurhash
 | 
			
		||||
              blurhash={audio.blurhash}
 | 
			
		||||
            <GracefullyImage
 | 
			
		||||
              sources={{ blurhash: audio.blurhash }}
 | 
			
		||||
              style={{
 | 
			
		||||
                width: '100%',
 | 
			
		||||
                height: '100%'
 | 
			
		||||
              }}
 | 
			
		||||
              dim
 | 
			
		||||
            />
 | 
			
		||||
          ) : null
 | 
			
		||||
        ) : (
 | 
			
		||||
          <>
 | 
			
		||||
            {audio.preview_url ? (
 | 
			
		||||
              <GracefullyImage
 | 
			
		||||
                uri={{ original: audio.preview_url, remote: audio.preview_remote_url }}
 | 
			
		||||
                sources={{
 | 
			
		||||
                  default: { uri: audio.preview_url },
 | 
			
		||||
                  remote: { uri: audio.preview_remote_url }
 | 
			
		||||
                }}
 | 
			
		||||
                style={styles.background}
 | 
			
		||||
                dim
 | 
			
		||||
              />
 | 
			
		||||
 
 | 
			
		||||
@@ -35,8 +35,11 @@ const AttachmentImage = ({
 | 
			
		||||
        <GracefullyImage
 | 
			
		||||
          accessibilityLabel={image.description}
 | 
			
		||||
          hidden={sensitiveShown}
 | 
			
		||||
          uri={{ original: image.preview_url, remote: image.remote_url }}
 | 
			
		||||
          blurhash={image.blurhash}
 | 
			
		||||
          sources={{
 | 
			
		||||
            default: { uri: image.preview_url },
 | 
			
		||||
            remote: { uri: image.remote_url },
 | 
			
		||||
            blurhash: image.blurhash
 | 
			
		||||
          }}
 | 
			
		||||
          onPress={() => navigateToImagesViewer(image.id)}
 | 
			
		||||
          style={{ aspectRatio: aspectRatio({ total, index, ...image.meta?.original }) }}
 | 
			
		||||
          dim
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import Button from '@components/Button'
 | 
			
		||||
import GracefullyImage from '@components/GracefullyImage'
 | 
			
		||||
import openLink from '@components/openLink'
 | 
			
		||||
import CustomText from '@components/Text'
 | 
			
		||||
import { StyleConstants } from '@utils/styles/constants'
 | 
			
		||||
@@ -6,7 +7,6 @@ import { useTheme } from '@utils/styles/ThemeManager'
 | 
			
		||||
import React from 'react'
 | 
			
		||||
import { useTranslation } from 'react-i18next'
 | 
			
		||||
import { View } from 'react-native'
 | 
			
		||||
import { Blurhash } from 'react-native-blurhash'
 | 
			
		||||
import AttachmentAltText from './AltText'
 | 
			
		||||
import { aspectRatio } from './dimensions'
 | 
			
		||||
 | 
			
		||||
@@ -33,8 +33,8 @@ const AttachmentUnsupported: React.FC<Props> = ({ total, index, sensitiveShown,
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      {attachment.blurhash ? (
 | 
			
		||||
        <Blurhash
 | 
			
		||||
          blurhash={attachment.blurhash}
 | 
			
		||||
        <GracefullyImage
 | 
			
		||||
          sources={{ blurhash: attachment.blurhash }}
 | 
			
		||||
          style={{
 | 
			
		||||
            position: 'absolute',
 | 
			
		||||
            width: '100%',
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import Button from '@components/Button'
 | 
			
		||||
import GracefullyImage from '@components/GracefullyImage'
 | 
			
		||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
 | 
			
		||||
import { connectMedia } from '@utils/api/helpers/connect'
 | 
			
		||||
import { useAccountStorage, useGlobalStorage } from '@utils/storage/actions'
 | 
			
		||||
@@ -8,7 +9,6 @@ import { Platform } from 'expo-modules-core'
 | 
			
		||||
import * as ScreenOrientation from 'expo-screen-orientation'
 | 
			
		||||
import React, { useRef, useState } from 'react'
 | 
			
		||||
import { Pressable, View } from 'react-native'
 | 
			
		||||
import { Blurhash } from 'react-native-blurhash'
 | 
			
		||||
import AttachmentAltText from './AltText'
 | 
			
		||||
import { aspectRatio } from './dimensions'
 | 
			
		||||
 | 
			
		||||
@@ -120,7 +120,10 @@ const AttachmentVideo: React.FC<Props> = ({
 | 
			
		||||
      >
 | 
			
		||||
        {sensitiveShown ? (
 | 
			
		||||
          video.blurhash ? (
 | 
			
		||||
            <Blurhash blurhash={video.blurhash} style={{ width: '100%', height: '100%' }} />
 | 
			
		||||
            <GracefullyImage
 | 
			
		||||
              sources={{ blurhash: video.blurhash }}
 | 
			
		||||
              style={{ width: '100%', height: '100%' }}
 | 
			
		||||
            />
 | 
			
		||||
          ) : null
 | 
			
		||||
        ) : !gifv || (gifv && (reduceMotionEnabled || !shouldAutoplayGifv)) ? (
 | 
			
		||||
          <Button
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,10 @@ const TimelineAvatar: React.FC<Props> = ({ account }) => {
 | 
			
		||||
      onPress={() =>
 | 
			
		||||
        !disableOnPress && navigation.push('Tab-Shared-Account', { account: actualAccount })
 | 
			
		||||
      }
 | 
			
		||||
      uri={{ original: actualAccount.avatar, static: actualAccount.avatar_static }}
 | 
			
		||||
      sources={{
 | 
			
		||||
        default: { uri: actualAccount.avatar },
 | 
			
		||||
        static: { uri: actualAccount.avatar_static }
 | 
			
		||||
      }}
 | 
			
		||||
      dimension={
 | 
			
		||||
        disableDetails || isConversation
 | 
			
		||||
          ? {
 | 
			
		||||
 
 | 
			
		||||
@@ -82,8 +82,7 @@ const TimelineCard: React.FC = () => {
 | 
			
		||||
      <>
 | 
			
		||||
        {status.card?.image ? (
 | 
			
		||||
          <GracefullyImage
 | 
			
		||||
            uri={{ original: status.card.image }}
 | 
			
		||||
            blurhash={status.card.blurhash}
 | 
			
		||||
            sources={{ default: { uri: status.card.image }, blurhash: status.card.blurhash }}
 | 
			
		||||
            style={{ flexBasis: StyleConstants.Font.LineHeight.M * 5 }}
 | 
			
		||||
            imageStyle={{ borderTopLeftRadius: 6, borderBottomLeftRadius: 6 }}
 | 
			
		||||
            dim
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user