mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-16 00:39:39 +02:00
Move inactive projects out of active workspace: - bauntown (community website) - maerchenzauber (AI story generation) - memoro (voice memo app) - news (news aggregation) - nutriphi (nutrition tracking) - reader (reading app) - uload (URL shortener) - wisekeep (AI wisdom extraction) Update CLAUDE.md documentation: - Add presi to active projects - Document archived projects section - Update workspace configuration Archived apps can be re-activated by moving back to apps/ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
227 lines
6 KiB
TypeScript
227 lines
6 KiB
TypeScript
import React, { useEffect, useState, useRef } from 'react';
|
|
import { View, StyleSheet, Animated, Easing, Dimensions } from 'react-native';
|
|
import Text from '../atoms/Text';
|
|
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
|
|
|
type LoadingContext = 'character' | 'story' | 'image' | 'saving' | 'loading' | 'cuddly_toy';
|
|
|
|
const magicalPhrases: Record<LoadingContext, string[]> = {
|
|
character: [
|
|
'Zauberstaub wird gesammelt...',
|
|
'Charaktere erwachen zum Leben...',
|
|
'Magische Eigenschaften werden geformt...',
|
|
'Persönlichkeit wird erschaffen...',
|
|
'Die Geschichte deines Charakters beginnt...',
|
|
'Kreative Funken fliegen...',
|
|
'Charakterzüge werden verfeinert...',
|
|
],
|
|
cuddly_toy: [
|
|
'Dein Kuscheltier wird zum Leben erweckt...',
|
|
'Magische Verwandlung beginnt...',
|
|
'Persönlichkeit wird erschaffen...',
|
|
'Charakter nimmt Gestalt an...',
|
|
'Zauberhafte Details werden hinzugefügt...',
|
|
'Fast fertig, gleich ist es soweit...',
|
|
'Letzte magische Berührungen...',
|
|
],
|
|
story: [
|
|
'Märchenwelten öffnen sich...',
|
|
'Magische Tinte fließt...',
|
|
'Geschichten weben sich...',
|
|
'Abenteuer entfalten sich...',
|
|
'Zauberhafte Momente entstehen...',
|
|
'Fantasie nimmt Gestalt an...',
|
|
'Worte tanzen auf Seiten...',
|
|
],
|
|
image: [
|
|
'Bilder werden zum Leben erweckt...',
|
|
'Farben und Formen verschmelzen...',
|
|
'Künstliche Kreativität entfaltet sich...',
|
|
'Visuelle Magie wird erschaffen...',
|
|
'Digitale Leinwand wird bemalt...',
|
|
],
|
|
saving: [
|
|
'Deine Kreation wird gesichert...',
|
|
'Magische Speicherung läuft...',
|
|
'Daten werden in Sternenstaub verwandelt...',
|
|
'Fortschritt wird konserviert...',
|
|
],
|
|
loading: [
|
|
'Daten werden aus dem Äther geholt...',
|
|
'Magische Verbindungen werden hergestellt...',
|
|
'Informationen materialisieren sich...',
|
|
'Digitale Schätze werden geborgen...',
|
|
],
|
|
};
|
|
|
|
interface MagicalLoadingScreenProps {
|
|
context: LoadingContext;
|
|
}
|
|
|
|
export default function MagicalLoadingScreen({ context }: MagicalLoadingScreenProps) {
|
|
const phrases = magicalPhrases[context];
|
|
const [currentPhrase, setCurrentPhrase] = useState(0);
|
|
const [animations] = useState(() => ({
|
|
sparkleRotation: new Animated.Value(0),
|
|
sparkleScale: new Animated.Value(0.3),
|
|
glowOpacity: new Animated.Value(0.4),
|
|
textOpacity: new Animated.Value(1),
|
|
}));
|
|
|
|
const textOpacityRef = useRef(animations.textOpacity);
|
|
|
|
useEffect(() => {
|
|
const startAnimations = () => {
|
|
// Rotation Animation
|
|
Animated.loop(
|
|
Animated.timing(animations.sparkleRotation, {
|
|
toValue: 1,
|
|
duration: 4000,
|
|
easing: Easing.linear,
|
|
useNativeDriver: true,
|
|
})
|
|
).start();
|
|
|
|
// Scale Animation
|
|
Animated.loop(
|
|
Animated.sequence([
|
|
Animated.timing(animations.sparkleScale, {
|
|
toValue: 1,
|
|
duration: 1500,
|
|
easing: Easing.bezier(0.4, 0, 0.2, 1),
|
|
useNativeDriver: true,
|
|
}),
|
|
Animated.timing(animations.sparkleScale, {
|
|
toValue: 0.3,
|
|
duration: 1500,
|
|
easing: Easing.bezier(0.4, 0, 0.2, 1),
|
|
useNativeDriver: true,
|
|
}),
|
|
])
|
|
).start();
|
|
|
|
// Glow Animation
|
|
Animated.loop(
|
|
Animated.sequence([
|
|
Animated.timing(animations.glowOpacity, {
|
|
toValue: 1,
|
|
duration: 1000,
|
|
easing: Easing.bezier(0.4, 0, 0.2, 1),
|
|
useNativeDriver: true,
|
|
}),
|
|
Animated.timing(animations.glowOpacity, {
|
|
toValue: 0.4,
|
|
duration: 1000,
|
|
easing: Easing.bezier(0.4, 0, 0.2, 1),
|
|
useNativeDriver: true,
|
|
}),
|
|
])
|
|
).start();
|
|
};
|
|
|
|
startAnimations();
|
|
|
|
// Phrase rotation with fade effect
|
|
const interval = setInterval(() => {
|
|
// Fade out - slower animation (800ms instead of 500ms)
|
|
Animated.timing(textOpacityRef.current, {
|
|
toValue: 0,
|
|
duration: 800,
|
|
easing: Easing.ease,
|
|
useNativeDriver: true,
|
|
}).start(() => {
|
|
// Change phrase when fully faded out
|
|
setCurrentPhrase((prev) => (prev + 1) % phrases.length);
|
|
|
|
// Fade in - slower animation (800ms instead of 500ms)
|
|
Animated.timing(textOpacityRef.current, {
|
|
toValue: 1,
|
|
duration: 800,
|
|
easing: Easing.ease,
|
|
useNativeDriver: true,
|
|
}).start();
|
|
});
|
|
}, 6000); // Longer display time (6000ms instead of 4000ms)
|
|
|
|
return () => clearInterval(interval);
|
|
}, [phrases]);
|
|
|
|
const spin = animations.sparkleRotation.interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: ['0deg', '360deg'],
|
|
});
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
<View style={styles.content}>
|
|
<Animated.View
|
|
style={[
|
|
styles.sparkleContainer,
|
|
{
|
|
transform: [{ rotate: spin }, { scale: animations.sparkleScale }],
|
|
opacity: animations.glowOpacity,
|
|
},
|
|
]}
|
|
>
|
|
<MaterialCommunityIcons name="star-four-points" size={80} color="#FFD700" />
|
|
</Animated.View>
|
|
<Animated.View style={{ opacity: animations.textOpacity, paddingHorizontal: 32 }}>
|
|
<Text style={styles.loadingText}>{phrases[currentPhrase]}</Text>
|
|
</Animated.View>
|
|
</View>
|
|
<View style={styles.infoContainer}>
|
|
<Text style={styles.infoText}>
|
|
Die Verarbeitung dauert circa eine Minute, du kannst die App auch schließen.
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
position: 'absolute',
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
backgroundColor: '#000000',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
zIndex: 1000,
|
|
},
|
|
content: {
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
},
|
|
sparkleContainer: {
|
|
marginBottom: 20,
|
|
},
|
|
loadingText: {
|
|
color: '#FFD700',
|
|
fontSize: 20,
|
|
textAlign: 'center',
|
|
marginTop: 20,
|
|
fontFamily: 'serif',
|
|
fontWeight: 'bold', // Text fetter machen
|
|
minHeight: 60, // Fixed height to prevent layout shifts during text changes
|
|
minWidth: 280, // Minimum width to maintain consistent layout
|
|
paddingHorizontal: 20, // Additional padding within the text component
|
|
lineHeight: 30, // Increased line height for better readability
|
|
},
|
|
infoContainer: {
|
|
position: 'absolute',
|
|
bottom: 40,
|
|
left: 0,
|
|
right: 0,
|
|
alignItems: 'center',
|
|
paddingHorizontal: 20,
|
|
},
|
|
infoText: {
|
|
color: 'rgba(255, 255, 255, 0.7)',
|
|
fontSize: 14,
|
|
textAlign: 'center',
|
|
fontFamily: 'system',
|
|
maxWidth: 300,
|
|
},
|
|
});
|