feat: add expo mobile application source code
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { StyleSheet, Dimensions, Text, View, StatusBar, Animated, Easing } from 'react-native';
|
||||
import { useAppTheme } from '../context/ThemeContext';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { useAppConfig } from '../context/ConfigContext';
|
||||
import { Image } from 'expo-image';
|
||||
|
||||
const { width, height } = Dimensions.get('window');
|
||||
|
||||
interface AnimatedSplashProps {
|
||||
onAnimationComplete: () => void;
|
||||
}
|
||||
|
||||
export const AnimatedSplash: React.FC<AnimatedSplashProps> = ({ onAnimationComplete }) => {
|
||||
const { isDark, colors } = useAppTheme();
|
||||
const { config } = useAppConfig();
|
||||
|
||||
// Using standard React Native Animated for safety
|
||||
const logoScale = useRef(new Animated.Value(0.5)).current;
|
||||
const logoOpacity = useRef(new Animated.Value(0)).current;
|
||||
const containerOpacity = useRef(new Animated.Value(1)).current;
|
||||
const textOpacity = useRef(new Animated.Value(0)).current;
|
||||
|
||||
useEffect(() => {
|
||||
// 1. Entry Animation
|
||||
Animated.parallel([
|
||||
Animated.timing(logoOpacity, { toValue: 1, duration: 1000, useNativeDriver: true }),
|
||||
Animated.timing(logoScale, {
|
||||
toValue: 1,
|
||||
duration: 1200,
|
||||
easing: Easing.out(Easing.back(1.5)),
|
||||
useNativeDriver: true
|
||||
}),
|
||||
Animated.timing(textOpacity, {
|
||||
toValue: 1,
|
||||
duration: 800,
|
||||
delay: 1000,
|
||||
useNativeDriver: true
|
||||
})
|
||||
]).start();
|
||||
|
||||
// 2. Clear Exit
|
||||
const timer = setTimeout(() => {
|
||||
Animated.timing(containerOpacity, {
|
||||
toValue: 0,
|
||||
duration: 600,
|
||||
useNativeDriver: true
|
||||
}).start(() => {
|
||||
// Delay to ensure the animation frame finishes before state change triggers unmount
|
||||
setTimeout(() => {
|
||||
onAnimationComplete();
|
||||
}, 100);
|
||||
});
|
||||
}, 3500);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Animated.View style={[styles.container, { opacity: containerOpacity, backgroundColor: isDark ? '#020617' : '#F8FAFC' }]}>
|
||||
<StatusBar hidden />
|
||||
<View style={styles.centerBox}>
|
||||
<Animated.View style={[
|
||||
styles.logoWrapper,
|
||||
{
|
||||
opacity: logoOpacity,
|
||||
transform: [{ scale: logoScale }],
|
||||
}
|
||||
]}>
|
||||
{config?.branding?.logo_url ? (
|
||||
<Image
|
||||
source={{ uri: config.branding.logo_url }}
|
||||
style={{ width: 100, height: 100 }}
|
||||
contentFit="contain"
|
||||
/>
|
||||
) : (
|
||||
<MaterialCommunityIcons name="shield-check" size={80} color={isDark ? '#38BDF8' : colors.primary || '#6C63FF'} />
|
||||
)}
|
||||
</Animated.View>
|
||||
|
||||
<Animated.View style={[
|
||||
styles.textWrapper,
|
||||
{
|
||||
opacity: textOpacity,
|
||||
transform: [{
|
||||
translateY: textOpacity.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [30, 0]
|
||||
})
|
||||
}]
|
||||
}
|
||||
]}>
|
||||
<View style={styles.brandContainer}>
|
||||
<Text style={[styles.brandAI, { color: isDark ? '#FFF' : '#0F172A' }]}>
|
||||
{config?.branding?.app_name || 'biiproject'}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.taglineBox}>
|
||||
<View style={styles.line} />
|
||||
<Text style={styles.tagline}>{config?.branding?.app_tagline || 'DIGITAL SOLUTIONS'}</Text>
|
||||
<View style={styles.line} />
|
||||
</View>
|
||||
</Animated.View>
|
||||
</View>
|
||||
</Animated.View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
zIndex: 9999,
|
||||
},
|
||||
centerBox: {
|
||||
alignItems: 'center',
|
||||
},
|
||||
logoWrapper: {
|
||||
width: 150,
|
||||
height: 150,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
brandContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'baseline',
|
||||
marginTop: 35,
|
||||
},
|
||||
brandAI: {
|
||||
fontSize: 46,
|
||||
fontFamily: 'Outfit_700Bold',
|
||||
letterSpacing: 2,
|
||||
},
|
||||
textWrapper: {
|
||||
alignItems: 'center',
|
||||
},
|
||||
taglineBox: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginTop: 15,
|
||||
},
|
||||
line: {
|
||||
width: 25,
|
||||
height: 1,
|
||||
backgroundColor: '#E2E8F0',
|
||||
marginHorizontal: 12,
|
||||
},
|
||||
tagline: {
|
||||
fontSize: 11,
|
||||
color: '#94A3B8',
|
||||
fontFamily: 'Outfit_700Bold',
|
||||
letterSpacing: 3,
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user