import React, { useEffect, useState } from 'react' import { View } from 'react-native' import FastImage from 'react-native-fast-image' import Video, { ResizeMode } from 'react-native-video' import AppAnalytics from 'src/analytics/AppAnalytics' import { NftEvents } from 'src/analytics/Events' import SkeletonPlaceholder from 'src/components/SkeletonPlaceholder' import { nftsLoadingSelector } from 'src/nfts/selectors' import { Nft, NftOrigin } from 'src/nfts/types' import { useSelector } from 'src/redux/hooks' import colors from 'src/styles/colors' import variables from 'src/styles/variables' import Logger from 'src/utils/Logger' import networkConfig from 'src/web3/networkConfig' const DEFAULT_HEIGHT = 360 interface PlaceHolderProps { testID: string height?: number width?: number borderRadius?: number mediaType: 'image' | 'video' } interface Props extends PlaceHolderProps { nft: Nft origin: NftOrigin shouldAutoScaleHeight?: boolean ErrorComponent: React.ReactNode } function Placeholder({ testID, width, height = DEFAULT_HEIGHT, borderRadius = 0, }: PlaceHolderProps) { return ( ) } type Status = 'loading' | 'error' | 'success' export default function NftMedia({ nft, height, width, shouldAutoScaleHeight, borderRadius, origin, ErrorComponent, testID, mediaType, }: Props) { const [status, setStatus] = useState(!nft.metadata ? 'error' : 'loading') const [scaledHeight, setScaledHeight] = useState(DEFAULT_HEIGHT) const [reloadAttempt, setReloadAttempt] = useState(0) const fetchingNfts = useSelector(nftsLoadingSelector) const imageUrl = nft.media.find((media) => media.raw === nft.metadata?.image)?.gateway const videoUrl = nft.media.find((media) => media.raw === nft.metadata?.animation_url)?.gateway useEffect(() => { if (nft.metadata) { setStatus('loading') } else { sendLoadEvent('No nft metadata') setStatus('error') } }, [`${nft.contractAddress}-${nft.tokenId}`]) // if the image failed to load before, try again when the user pulls to refresh useEffect(() => { if (status === 'error' && fetchingNfts) { setStatus('loading') setReloadAttempt((prev) => prev + 1) } }, [status, fetchingNfts]) function sendLoadEvent(error?: string) { const { contractAddress, tokenId } = nft AppAnalytics.track(NftEvents.nft_media_load, { tokenId, contractAddress, url: imageUrl, origin, error, mediaType, }) if (error) { Logger.error( origin, `ContractAddress=${contractAddress}, TokenId: ${tokenId}, Failed to load media from ${ mediaType === 'video' && videoUrl ? videoUrl : imageUrl }` ) } } function handleLoadError() { sendLoadEvent('Failed to load media') setStatus('error') } function handleLoadSuccess() { sendLoadEvent() setStatus('success') } return ( <> {status === 'error' ? ( ErrorComponent ) : mediaType === 'video' && videoUrl ? ( ) : ( { const aspectRatio = width / height setScaledHeight(variables.width / aspectRatio) }} onLoadEnd={handleLoadSuccess} onError={handleLoadError} resizeMode={ shouldAutoScaleHeight ? FastImage.resizeMode.contain : FastImage.resizeMode.cover } > {status === 'loading' && ( )} )} ) }