chahinebrini 14452b2a46 refactor(native): Pressable → TouchableOpacity sweep (style-fn swallows Android styles)
Alle <Pressable style={({pressed}) => ({...})}> ersetzt — style-Funktion
droppt auf Android (New Arch) intermittierend width/height, führt zu 0×0
unsichtbaren Elementen. TouchableOpacity mit activeOpacity ist stabil.

Außerdem übrige Pressables (plain style) aus components/ und app/
migriert sowie zwei überschüssige </View>-Tags in chat.tsx + RoomCard.tsx
entfernt die TS-Fehler verursacht haben.

64 Dateien, typecheck sauber.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 15:43:10 +02:00

176 lines
5.3 KiB
TypeScript

import { useEffect, useRef } from 'react';
import { Modal, View, Text, TouchableOpacity, Animated, Easing } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { useTranslation } from 'react-i18next';
type Props = {
visible: boolean;
title: string;
message?: string;
onClose: () => void;
};
/**
* iOS-style success alert mit animiertem Check-Icon.
* - Card scaled mit Spring-overshoot rein
* - Check-Icon sequenced danach mit eigenem Spring + rotation-pop
* - Tap auf Backdrop schließt
* - OK-Button schließt
*/
export function SuccessAlert({ visible, title, message, onClose }: Props) {
const { t } = useTranslation();
const cardScale = useRef(new Animated.Value(0.8)).current;
const cardOpacity = useRef(new Animated.Value(0)).current;
const checkScale = useRef(new Animated.Value(0)).current;
const checkRotate = useRef(new Animated.Value(0)).current;
useEffect(() => {
if (visible) {
cardScale.setValue(0.8);
cardOpacity.setValue(0);
checkScale.setValue(0);
checkRotate.setValue(0);
Animated.parallel([
Animated.spring(cardScale, {
toValue: 1,
useNativeDriver: true,
friction: 7,
tension: 80,
}),
Animated.timing(cardOpacity, {
toValue: 1,
duration: 220,
useNativeDriver: true,
easing: Easing.out(Easing.cubic),
}),
Animated.sequence([
Animated.delay(140),
Animated.parallel([
Animated.spring(checkScale, {
toValue: 1,
useNativeDriver: true,
friction: 5,
tension: 180,
}),
Animated.timing(checkRotate, {
toValue: 1,
duration: 380,
useNativeDriver: true,
easing: Easing.out(Easing.back(1.7)),
}),
]),
]),
]).start();
}
}, [visible, cardScale, cardOpacity, checkScale, checkRotate]);
const rotateInterpolate = checkRotate.interpolate({
inputRange: [0, 1],
outputRange: ['-30deg', '0deg'],
});
return (
<Modal visible={visible} transparent animationType="fade" onRequestClose={onClose}>
{/* Backdrop — Pressable damit Tap-outside schließt */}
<TouchableOpacity
activeOpacity={1}
onPress={onClose}
style={{
flex: 1,
backgroundColor: 'rgba(0,0,0,0.4)',
justifyContent: 'center',
alignItems: 'center',
padding: 24,
}}
>
{/* Card — Pressable mit onPress={()=>{}} damit Tap auf Card NICHT bubbelt
* zum Backdrop und das Modal schließt. */}
<TouchableOpacity activeOpacity={1} onPress={() => {}} style={{ width: '85%', maxWidth: 320 }}>
<Animated.View
style={{
backgroundColor: '#fff',
borderRadius: 22,
padding: 20,
width: '100%',
transform: [{ scale: cardScale }],
opacity: cardOpacity,
shadowColor: '#000',
shadowOffset: { width: 0, height: 8 },
shadowOpacity: 0.2,
shadowRadius: 24,
elevation: 16,
}}
>
{/* Animated Check-Circle */}
<Animated.View
style={{
width: 56,
height: 56,
borderRadius: 28,
backgroundColor: '#16a34a',
alignItems: 'center',
justifyContent: 'center',
marginBottom: 12,
alignSelf: 'center',
transform: [{ scale: checkScale }, { rotate: rotateInterpolate }],
shadowColor: '#16a34a',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.4,
shadowRadius: 8,
elevation: 8,
}}
>
<Ionicons name="checkmark" size={32} color="#fff" />
</Animated.View>
<Text
style={{
fontSize: 16,
fontFamily: 'Nunito_700Bold',
color: '#0a0a0a',
textAlign: 'center',
marginBottom: 6,
}}
>
{title}
</Text>
{message && (
<Text
style={{
fontSize: 13,
fontFamily: 'Nunito_400Regular',
color: '#525252',
textAlign: 'center',
lineHeight: 19,
marginBottom: 14,
}}
>
{message}
</Text>
)}
<TouchableOpacity
activeOpacity={0.7}
onPress={onClose}
style={{
paddingVertical: 10,
borderRadius: 10,
backgroundColor: '#eff6ff',
borderWidth: 1,
borderColor: '#bfdbfe',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text style={{ fontSize: 14, fontFamily: 'Nunito_700Bold', color: '#007AFF' }}>
{t('common.ok')}
</Text>
</TouchableOpacity>
</Animated.View>
</TouchableOpacity>
</TouchableOpacity>
</Modal>
);
}