255 lines
7.0 KiB
TypeScript
255 lines
7.0 KiB
TypeScript
import React, { useEffect, useRef } from 'react';
|
|
import {
|
|
View,
|
|
Text,
|
|
StyleSheet,
|
|
TouchableOpacity,
|
|
Animated,
|
|
Dimensions,
|
|
Image,
|
|
} from 'react-native';
|
|
import { Ionicons } from '@expo/vector-icons';
|
|
import { router } from 'expo-router';
|
|
import { useApp } from '../context/AppContext';
|
|
import { useColors } from '../constants/Colors';
|
|
import { ThemeBackdrop } from '../components/ThemeBackdrop';
|
|
|
|
const { height: SCREEN_H, width: SCREEN_W } = Dimensions.get('window');
|
|
|
|
export default function OnboardingScreen() {
|
|
const { isDarkMode, colorPalette, t } = useApp();
|
|
const colors = useColors(isDarkMode, colorPalette);
|
|
|
|
const FEATURES = [
|
|
{ icon: 'camera-outline' as const, label: t.onboardingFeatureScan },
|
|
{ icon: 'notifications-outline' as const, label: t.onboardingFeatureReminder },
|
|
{ icon: 'book-outline' as const, label: t.onboardingFeatureLexicon },
|
|
];
|
|
|
|
// Entrance animations
|
|
const logoAnim = useRef(new Animated.Value(0)).current;
|
|
const logoScale = useRef(new Animated.Value(0.85)).current;
|
|
const featuresAnim = useRef(new Animated.Value(0)).current;
|
|
const buttonsAnim = useRef(new Animated.Value(0)).current;
|
|
const featureAnims = useRef(FEATURES.map(() => new Animated.Value(0))).current;
|
|
|
|
useEffect(() => {
|
|
Animated.sequence([
|
|
Animated.parallel([
|
|
Animated.timing(logoAnim, { toValue: 1, duration: 700, useNativeDriver: true }),
|
|
Animated.spring(logoScale, { toValue: 1, tension: 50, friction: 8, useNativeDriver: true }),
|
|
]),
|
|
Animated.stagger(100, featureAnims.map(anim =>
|
|
Animated.timing(anim, { toValue: 1, duration: 400, useNativeDriver: true })
|
|
)),
|
|
Animated.timing(buttonsAnim, { toValue: 1, duration: 400, useNativeDriver: true }),
|
|
]).start();
|
|
}, []);
|
|
|
|
return (
|
|
<View style={[styles.container, { backgroundColor: colors.background }]}>
|
|
<ThemeBackdrop colors={colors} />
|
|
|
|
{/* Logo-Bereich */}
|
|
<Animated.View
|
|
style={[
|
|
styles.heroSection,
|
|
{ opacity: logoAnim, transform: [{ scale: logoScale }] },
|
|
]}
|
|
>
|
|
<View style={[styles.iconContainer, { shadowColor: colors.primary }]}>
|
|
<Image
|
|
source={require('../assets/icon.png')}
|
|
style={styles.appIcon}
|
|
resizeMode="cover"
|
|
/>
|
|
</View>
|
|
|
|
<Text style={[styles.appName, { color: colors.text }]}>GreenLens</Text>
|
|
<Text style={[styles.tagline, { color: colors.textSecondary }]}>
|
|
{t.onboardingTagline}
|
|
</Text>
|
|
</Animated.View>
|
|
|
|
{/* Feature-Liste */}
|
|
<View style={styles.featuresSection}>
|
|
{FEATURES.map((feat, i) => (
|
|
<Animated.View
|
|
key={feat.label}
|
|
style={[
|
|
styles.featureRow,
|
|
{
|
|
backgroundColor: colors.surface + '88', // Semi-transparent for backdrop effect
|
|
borderColor: colors.border,
|
|
opacity: featureAnims[i],
|
|
transform: [{
|
|
translateY: featureAnims[i].interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: [20, 0],
|
|
}),
|
|
}],
|
|
},
|
|
]}
|
|
>
|
|
<View style={[styles.featureIcon, { backgroundColor: colors.primary + '15' }]}>
|
|
<Ionicons name={feat.icon} size={22} color={colors.primary} />
|
|
</View>
|
|
<Text style={[styles.featureText, { color: colors.text }]}>{feat.label}</Text>
|
|
</Animated.View>
|
|
))}
|
|
</View>
|
|
|
|
{/* Buttons */}
|
|
<Animated.View style={[styles.buttonsSection, { opacity: buttonsAnim }]}>
|
|
<TouchableOpacity
|
|
style={[styles.primaryBtn, { backgroundColor: colors.primary }]}
|
|
onPress={() => router.push('/scanner')}
|
|
activeOpacity={0.85}
|
|
>
|
|
<Ionicons name="scan" size={20} color={colors.onPrimary} />
|
|
<Text style={[styles.primaryBtnText, { color: colors.onPrimary }]}>
|
|
{t.onboardingScanBtn}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
|
|
<View style={styles.authActions}>
|
|
<TouchableOpacity
|
|
style={[styles.secondaryBtn, { borderColor: colors.primary, backgroundColor: colors.surface }]}
|
|
onPress={() => router.push('/auth/signup')}
|
|
activeOpacity={0.82}
|
|
>
|
|
<Text style={[styles.secondaryBtnText, { color: colors.primary }]}>
|
|
{t.onboardingRegister}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity
|
|
style={[styles.secondaryBtn, { borderColor: colors.borderStrong, backgroundColor: colors.surface }]}
|
|
onPress={() => router.push('/auth/login')}
|
|
activeOpacity={0.82}
|
|
>
|
|
<Text style={[styles.secondaryBtnText, { color: colors.text }]}>
|
|
{t.onboardingLogin}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
<Text style={[styles.disclaimer, { color: colors.textMuted }]}>
|
|
{t.onboardingDisclaimer}
|
|
</Text>
|
|
</Animated.View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
paddingHorizontal: 32,
|
|
paddingTop: SCREEN_H * 0.12,
|
|
paddingBottom: 40,
|
|
},
|
|
heroSection: {
|
|
alignItems: 'center',
|
|
marginBottom: 40,
|
|
},
|
|
iconContainer: {
|
|
width: 120,
|
|
height: 120,
|
|
borderRadius: 28,
|
|
backgroundColor: '#fff',
|
|
elevation: 8,
|
|
shadowOffset: { width: 0, height: 4 },
|
|
shadowOpacity: 0.15,
|
|
shadowRadius: 12,
|
|
marginBottom: 24,
|
|
overflow: 'hidden',
|
|
},
|
|
appIcon: {
|
|
width: '100%',
|
|
height: '100%',
|
|
},
|
|
appName: {
|
|
fontSize: 40,
|
|
fontWeight: '900',
|
|
letterSpacing: -1.5,
|
|
marginBottom: 4,
|
|
},
|
|
tagline: {
|
|
fontSize: 17,
|
|
fontWeight: '500',
|
|
opacity: 0.8,
|
|
},
|
|
featuresSection: {
|
|
gap: 12,
|
|
flex: 1,
|
|
justifyContent: 'center',
|
|
},
|
|
featureRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 16,
|
|
paddingHorizontal: 20,
|
|
paddingVertical: 16,
|
|
borderRadius: 20,
|
|
borderWidth: 1,
|
|
},
|
|
featureIcon: {
|
|
width: 44,
|
|
height: 44,
|
|
borderRadius: 14,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
featureText: {
|
|
flex: 1,
|
|
fontSize: 15,
|
|
fontWeight: '600',
|
|
letterSpacing: 0.1,
|
|
},
|
|
buttonsSection: {
|
|
gap: 16,
|
|
marginTop: 20,
|
|
},
|
|
primaryBtn: {
|
|
height: 58,
|
|
borderRadius: 20,
|
|
flexDirection: 'row',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
gap: 12,
|
|
elevation: 4,
|
|
shadowColor: '#000',
|
|
shadowOffset: { width: 0, height: 2 },
|
|
shadowOpacity: 0.1,
|
|
shadowRadius: 4,
|
|
},
|
|
primaryBtnText: {
|
|
fontSize: 17,
|
|
fontWeight: '700',
|
|
},
|
|
authActions: {
|
|
flexDirection: 'row',
|
|
gap: 12,
|
|
},
|
|
secondaryBtn: {
|
|
flex: 1,
|
|
height: 54,
|
|
borderRadius: 20,
|
|
borderWidth: 1.5,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
secondaryBtnText: {
|
|
fontSize: 15,
|
|
fontWeight: '600',
|
|
},
|
|
disclaimer: {
|
|
fontSize: 12,
|
|
textAlign: 'center',
|
|
opacity: 0.6,
|
|
marginTop: 8,
|
|
},
|
|
});
|
|
|