Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 0ac5b07bfd31a6b39706ca9b6a42d2db09501e52 105 lines 2.8 kB view raw
1import React, {useCallback, useEffect} from 'react' 2import { 3 AccessibilityInfo, 4 Image as RNImage, 5 StyleSheet, 6 useColorScheme, 7 View, 8} from 'react-native' 9import Animated, { 10 Easing, 11 interpolate, 12 useAnimatedStyle, 13 useSharedValue, 14 withTiming, 15} from 'react-native-reanimated' 16import {Image} from 'expo-image' 17import * as SplashScreen from 'expo-splash-screen' 18 19// @ts-ignore 20import splashImagePointer from '../assets/splash/splash-mobile.png' 21// @ts-ignore 22import darkSplashImagePointer from '../assets/splash/splash-mobile-dark.png' 23const splashImageUri = RNImage.resolveAssetSource(splashImagePointer).uri 24const darkSplashImageUri = RNImage.resolveAssetSource( 25 darkSplashImagePointer, 26).uri 27 28type Props = { 29 isReady: boolean 30} 31 32export function Splash(props: React.PropsWithChildren<Props>) { 33 'use no memo' 34 const outroAppOpacity = useSharedValue(0) 35 const colorScheme = useColorScheme() 36 const [isAnimationComplete, setIsAnimationComplete] = React.useState(false) 37 const [isImageLoaded, setIsImageLoaded] = React.useState(false) 38 const [isLayoutReady, setIsLayoutReady] = React.useState(false) 39 const [reduceMotion, setReduceMotion] = React.useState<boolean | undefined>( 40 false, 41 ) 42 const isReady = 43 props.isReady && 44 isImageLoaded && 45 isLayoutReady && 46 reduceMotion !== undefined 47 const isDarkMode = colorScheme === 'dark' 48 49 const appAnimation = useAnimatedStyle(() => { 50 return { 51 opacity: interpolate( 52 outroAppOpacity.get(), 53 [0, 0.1, 0.2, 1], 54 [0, 0, 1, 1], 55 'clamp', 56 ), 57 } 58 }) 59 60 const onFinish = useCallback(() => setIsAnimationComplete(true), []) 61 const onLayout = useCallback(() => setIsLayoutReady(true), []) 62 const onLoadEnd = useCallback(() => setIsImageLoaded(true), []) 63 64 useEffect(() => { 65 if (isReady) { 66 SplashScreen.hideAsync() 67 .then(() => { 68 outroAppOpacity.set(() => 69 withTiming(1, { 70 duration: 1200, 71 easing: Easing.in(Easing.cubic), 72 }), 73 ) 74 }) 75 .catch(() => {}) 76 } 77 }, [onFinish, outroAppOpacity, isReady]) 78 79 useEffect(() => { 80 AccessibilityInfo.isReduceMotionEnabled().then(setReduceMotion) 81 }, []) 82 83 return ( 84 <View style={{flex: 1}} onLayout={onLayout}> 85 {!isAnimationComplete && ( 86 <View style={StyleSheet.absoluteFillObject}> 87 <Image 88 accessibilityIgnoresInvertColors 89 onLoadEnd={onLoadEnd} 90 source={{uri: isDarkMode ? darkSplashImageUri : splashImageUri}} 91 style={StyleSheet.absoluteFillObject} 92 /> 93 </View> 94 )} 95 96 {isReady && ( 97 <> 98 <Animated.View style={[{flex: 1}, appAnimation]}> 99 {props.children} 100 </Animated.View> 101 </> 102 )} 103 </View> 104 ) 105}