forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
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}