Something went wrong. Try again.
hardened bluesky app fork with microcosm
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245import {forwardRef, useCallback, useEffect, useState} from 'react'import { AccessibilityInfo, Image as RNImage, StyleSheet, useColorScheme, View,} from 'react-native'import Animated, { Easing, interpolate, runOnJS, useAnimatedStyle, useSharedValue, withTiming,} from 'react-native-reanimated'import {useSafeAreaInsets} from 'react-native-safe-area-context'import Svg, {Path, type SvgProps} from 'react-native-svg'import {Image} from 'expo-image'import * as SplashScreen from 'expo-splash-screen'
import {Logotype} from '#/view/icons/Logotype'// @ts-ignoreimport splashImagePointer from '../assets/splash/splash.png'// @ts-ignoreimport darkSplashImagePointer from '../assets/splash/splash-dark.png'const splashImageUri = RNImage.resolveAssetSource(splashImagePointer).uriconst darkSplashImageUri = RNImage.resolveAssetSource( darkSplashImagePointer,).uri
export const Logo = forwardRef(function LogoImpl(props: SvgProps, ref) { const width = 1000 const height = width * (67 / 64) return ( <Svg fill="none" // @ts-ignore it's fiiiiine ref={ref} viewBox="0 0 64 66" style={[{width, height}, props.style]}> <Path fill={props.fill || '#fff'} d="M13.873 3.77C21.21 9.243 29.103 20.342 32 26.3v15.732c0-.335-.13.043-.41.858-1.512 4.414-7.418 21.642-20.923 7.87-7.111-7.252-3.819-14.503 9.125-16.692-7.405 1.252-15.73-.817-18.014-8.93C1.12 22.804 0 8.431 0 6.488 0-3.237 8.579-.18 13.873 3.77ZM50.127 3.77C42.79 9.243 34.897 20.342 32 26.3v15.732c0-.335.13.043.41.858 1.512 4.414 7.418 21.642 20.923 7.87 7.111-7.252 3.819-14.503-9.125-16.692 7.405 1.252 15.73-.817 18.014-8.93C62.88 22.804 64 8.431 64 6.488 64-3.237 55.422-.18 50.127 3.77Z" /> </Svg> )})
type Props = { isReady: boolean}
export function Splash(props: React.PropsWithChildren<Props>) { 'use no memo' const insets = useSafeAreaInsets() const intro = useSharedValue(0) const outroLogo = useSharedValue(0) const outroApp = useSharedValue(0) const outroAppOpacity = useSharedValue(0) const [isAnimationComplete, setIsAnimationComplete] = useState(false) const [isImageLoaded, setIsImageLoaded] = useState(false) const [isLayoutReady, setIsLayoutReady] = useState(false) const [reduceMotion, setReduceMotion] = useState<boolean | undefined>(false) const isReady = props.isReady && isImageLoaded && isLayoutReady && reduceMotion !== undefined
const colorScheme = useColorScheme() const isDarkMode = colorScheme === 'dark'
const logoAnimation = useAnimatedStyle(() => { return { transform: [ { scale: interpolate(intro.get(), [0, 1], [0.8, 1], 'clamp'), }, { scale: interpolate( outroLogo.get(), [0, 0.08, 1], [1, 0.8, 500], 'clamp', ), }, ], opacity: interpolate(intro.get(), [0, 1], [0, 1], 'clamp'), } }) const bottomLogoAnimation = useAnimatedStyle(() => { return { opacity: interpolate(intro.get(), [0, 1], [0, 1], 'clamp'), } }) const reducedLogoAnimation = useAnimatedStyle(() => { return { transform: [ { scale: interpolate(intro.get(), [0, 1], [0.8, 1], 'clamp'), }, ], opacity: interpolate(intro.get(), [0, 1], [0, 1], 'clamp'), } })
const logoWrapperAnimation = useAnimatedStyle(() => { return { opacity: interpolate( outroAppOpacity.get(), [0, 0.1, 0.2, 1], [1, 1, 0, 0], 'clamp', ), } })
const appAnimation = useAnimatedStyle(() => { return { transform: [ { scale: interpolate(outroApp.get(), [0, 1], [1.1, 1], 'clamp'), }, ], opacity: interpolate( outroAppOpacity.get(), [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.set(() => withTiming( 1, {duration: 400, easing: Easing.out(Easing.cubic)}, () => { 'worklet' // set these values to check animation at specific point outroLogo.set(() => withTiming( 1, {duration: 1200, easing: Easing.in(Easing.cubic)}, () => { runOnJS(onFinish)() }, ), ) outroApp.set(() => withTiming(1, { duration: 1200, easing: Easing.inOut(Easing.cubic), }), ) outroAppOpacity.set(() => 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 ( <View style={{flex: 1}} onLayout={onLayout}> {!isAnimationComplete && ( <View style={StyleSheet.absoluteFillObject}> <Image accessibilityIgnoresInvertColors onLoadEnd={onLoadEnd} source={{uri: isDarkMode ? darkSplashImageUri : splashImageUri}} style={StyleSheet.absoluteFillObject} />
<Animated.View style={[ bottomLogoAnimation, { position: 'absolute', bottom: insets.bottom + 40, left: 0, right: 0, alignItems: 'center', justifyContent: 'center', opacity: 0, }, ]}> <Logotype fill="#fff" width={90} /> </Animated.View> </View> )}
{isReady && ( <> <Animated.View style={[{flex: 1}, appAnimation]}> {props.children} </Animated.View>
{!isAnimationComplete && ( <Animated.View style={[ StyleSheet.absoluteFillObject, logoWrapperAnimation, { flex: 1, justifyContent: 'center', alignItems: 'center', transform: [{translateY: -(insets.top / 2)}, {scale: 0.1}], // scale from 1000px to 100px }, ]}> <Animated.View style={[logoAnimations]}> <Logo fill={logoBg} /> </Animated.View> </Animated.View> )} </> )} </View> )}