import React, {useCallback, useEffect} from 'react' import { View, StyleSheet, Image as RNImage, AccessibilityInfo, useColorScheme, } from 'react-native' import * as SplashScreen from 'expo-splash-screen' import {Image} from 'expo-image' import Animated, { interpolate, runOnJS, useAnimatedStyle, useSharedValue, withTiming, Easing, } from 'react-native-reanimated' import MaskedView from '@react-native-masked-view/masked-view' import {useSafeAreaInsets} from 'react-native-safe-area-context' import Svg, {Path, SvgProps} from 'react-native-svg' import {isAndroid} from '#/platform/detection' import {Logotype} from '#/view/icons/Logotype' // @ts-ignore import splashImagePointer from '../assets/splash.png' // @ts-ignore import darkSplashImagePointer from '../assets/splash-dark.png' const splashImageUri = RNImage.resolveAssetSource(splashImagePointer).uri const darkSplashImageUri = RNImage.resolveAssetSource( darkSplashImagePointer, ).uri export const Logo = React.forwardRef(function LogoImpl(props: SvgProps, ref) { const width = 1000 const height = width * (67 / 64) return ( ) }) type Props = { isReady: boolean } const AnimatedLogo = Animated.createAnimatedComponent(Logo) export function Splash(props: React.PropsWithChildren) { const insets = useSafeAreaInsets() const intro = useSharedValue(0) const outroLogo = useSharedValue(0) const outroApp = useSharedValue(0) const outroAppOpacity = useSharedValue(0) const [isAnimationComplete, setIsAnimationComplete] = React.useState(false) const [isImageLoaded, setIsImageLoaded] = React.useState(false) const [isLayoutReady, setIsLayoutReady] = React.useState(false) const [reduceMotion, setReduceMotion] = React.useState( false, ) const isReady = props.isReady && isImageLoaded && isLayoutReady && reduceMotion !== undefined const colorScheme = useColorScheme() const isDarkMode = colorScheme === 'dark' const logoAnimation = useAnimatedStyle(() => { return { transform: [ { scale: interpolate(intro.value, [0, 1], [0.8, 1], 'clamp'), }, { scale: interpolate( outroLogo.value, [0, 0.08, 1], [1, 0.8, 500], 'clamp', ), }, ], opacity: interpolate(intro.value, [0, 1], [0, 1], 'clamp'), } }) const bottomLogoAnimation = useAnimatedStyle(() => { return { opacity: interpolate(intro.value, [0, 1], [0, 1], 'clamp'), } }) const reducedLogoAnimation = useAnimatedStyle(() => { return { transform: [ { scale: interpolate(intro.value, [0, 1], [0.8, 1], 'clamp'), }, ], opacity: interpolate(intro.value, [0, 1], [0, 1], 'clamp'), } }) const logoWrapperAnimation = useAnimatedStyle(() => { return { opacity: interpolate( outroAppOpacity.value, [0, 0.1, 0.2, 1], [1, 1, 0, 0], 'clamp', ), } }) const appAnimation = useAnimatedStyle(() => { return { transform: [ { scale: interpolate(outroApp.value, [0, 1], [1.1, 1], 'clamp'), }, ], opacity: interpolate( outroAppOpacity.value, [0, 0.1, 0.2, 1], [0, 0, 1, 1], 'clamp', ), } }) const onFinish = useCallback(() => setIsAnimationComplete(true), []) const onLayout = useCallback(() => setIsLayoutReady(true), []) const onLoadEnd = useCallback(() => setIsImageLoaded(true), []) useEffect(() => { if (isReady) { SplashScreen.hideAsync() .then(() => { intro.value = withTiming( 1, {duration: 400, easing: Easing.out(Easing.cubic)}, async () => { // set these values to check animation at specific point // outroLogo.value = 0.1 // outroApp.value = 0.1 outroLogo.value = withTiming( 1, {duration: 1200, easing: Easing.in(Easing.cubic)}, () => { runOnJS(onFinish)() }, ) outroApp.value = withTiming(1, { duration: 1200, easing: Easing.inOut(Easing.cubic), }) outroAppOpacity.value = withTiming(1, { duration: 1200, easing: Easing.in(Easing.cubic), }) }, ) }) .catch(() => {}) } }, [onFinish, intro, outroLogo, outroApp, outroAppOpacity, isReady]) useEffect(() => { AccessibilityInfo.isReduceMotionEnabled().then(setReduceMotion) }, []) const logoAnimations = reduceMotion === true ? reducedLogoAnimation : logoAnimation // special off-spec color for dark mode const logoBg = isDarkMode ? '#0F1824' : '#fff' return ( {!isAnimationComplete && ( )} {isReady && (isAndroid || reduceMotion === true ? ( // Use a simple fade on older versions of android (work around a bug) <> {props.children} {!isAnimationComplete && ( )} ) : ( }> {!isAnimationComplete && ( )} {props.children} ))} ) }