import { BottomSheetView } from '@gorhom/bottom-sheet' import React, { useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { StyleSheet, Text, View } from 'react-native' import { useSafeAreaInsets } from 'react-native-safe-area-context' import AppAnalytics from 'src/analytics/AppAnalytics' import { HomeEvents } from 'src/analytics/Events' import { BottomSheetModalRefType } from 'src/components/BottomSheet' import BottomSheetBase from 'src/components/BottomSheetBase' import Button, { BtnSizes, BtnTypes } from 'src/components/Button' import { nftCelebrationDisplayed } from 'src/home/actions' import ConfettiCelebration from 'src/home/celebration/ConfettiCelebration' import { isSameNftContract } from 'src/home/celebration/utils' import { nftCelebrationSelector, showNftCelebrationSelector } from 'src/home/selectors' import ImageErrorIcon from 'src/icons/ImageErrorIcon' import NftMedia from 'src/nfts/NftMedia' import { nftsWithMetadataSelector } from 'src/nfts/selectors' import { NftOrigin } from 'src/nfts/types' import { useDispatch, useSelector } from 'src/redux/hooks' import Colors from 'src/styles/colors' import { typeScale } from 'src/styles/fonts' import { vibrateSuccess } from 'src/styles/hapticFeedback' import { Spacing } from 'src/styles/styles' export default function NftCelebration() { const { t } = useTranslation() const dispatch = useDispatch() const insets = useSafeAreaInsets() const insetsStyle = { paddingBottom: Math.max(insets.bottom, Spacing.Regular16) } const bottomSheetRef = useRef(null) const [showConfetti, setShowConfetti] = useState(false) const confettiStartTime = useRef(0) const canShowNftCelebration = useSelector(showNftCelebrationSelector) const celebratedNft = useSelector(nftCelebrationSelector) const nfts = useSelector(nftsWithMetadataSelector) const matchingNft = useMemo( () => nfts.find((nft) => isSameNftContract(nft, celebratedNft)), [celebratedNft] ) const isVisible = canShowNftCelebration && matchingNft useEffect(() => { if (isVisible) { // Wait for the home screen to have less ongoing async tasks. // This should help the bottom sheet animation run smoothly. const timeoutId = setTimeout(() => { bottomSheetRef.current?.expand() }, 1000) return () => clearTimeout(timeoutId) } }, [isVisible]) const handleBottomSheetPositionChange = (index: number) => { if (!matchingNft) { return // this should never happen } if (index === -1) { AppAnalytics.track(HomeEvents.nft_celebration_displayed, { networkId: matchingNft.networkId, contractAddress: matchingNft.contractAddress, }) vibrateSuccess() confettiStartTime.current = Date.now() setShowConfetti(true) } } const handleCtaPress = () => { bottomSheetRef.current?.close() } const handleConfettiFinish = () => { finishCelebration({ userInterrupted: false }) } const handleConfettiDismiss = () => { finishCelebration({ userInterrupted: true }) } const finishCelebration = ({ userInterrupted }: { userInterrupted: boolean }) => { if (!matchingNft) { return // this should never happen } AppAnalytics.track(HomeEvents.nft_celebration_animation_displayed, { userInterrupted, durationInSeconds: Math.round((Date.now() - confettiStartTime.current) / 1000), }) setShowConfetti(false) dispatch(nftCelebrationDisplayed()) } if (!isVisible) { return null } return ( <> null} // handle is rendered within the content body backgroundStyle={styles.bottomSheetBackground} onChange={handleBottomSheetPositionChange} > {matchingNft && ( } origin={NftOrigin.NftCelebration} mediaType="image" testID="NftMedia" /> )} {t('nftCelebration.bottomSheet.title')} {t('nftCelebration.bottomSheet.description')}