WellNuo/components/ui/Toast.tsx
Sergei 7cb07c09ce Major UI/UX updates: Voice, Subscription, Beneficiaries, Profile
- Voice tab: simplified interface, voice picker improvements
- Subscription: Stripe integration, purchase flow updates
- Beneficiaries: dashboard, sharing, improved management
- Profile: drawer, edit, help, privacy sections
- Theme: expanded constants, new colors
- New components: MockDashboard, ProfileDrawer, Toast
- Backend: Stripe routes additions
- Auth: activate, add-loved-one, purchase screens
2025-12-29 15:36:44 -08:00

102 lines
2.5 KiB
TypeScript

import React, { useEffect, useRef } from 'react';
import { View, Text, StyleSheet, Animated } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { AppColors, BorderRadius, FontSizes, Spacing, FontWeights, Shadows } from '@/constants/theme';
interface ToastProps {
visible: boolean;
message: string;
icon?: keyof typeof Ionicons.glyphMap;
duration?: number;
onHide: () => void;
}
export function Toast({ visible, message, icon = 'checkmark-circle', duration = 2000, onHide }: ToastProps) {
const fadeAnim = useRef(new Animated.Value(0)).current;
const translateY = useRef(new Animated.Value(20)).current;
useEffect(() => {
if (visible) {
Animated.parallel([
Animated.timing(fadeAnim, {
toValue: 1,
duration: 200,
useNativeDriver: true,
}),
Animated.timing(translateY, {
toValue: 0,
duration: 200,
useNativeDriver: true,
}),
]).start();
const timer = setTimeout(() => {
Animated.parallel([
Animated.timing(fadeAnim, {
toValue: 0,
duration: 200,
useNativeDriver: true,
}),
Animated.timing(translateY, {
toValue: 20,
duration: 200,
useNativeDriver: true,
}),
]).start(() => onHide());
}, duration);
return () => clearTimeout(timer);
}
}, [visible, duration, onHide]);
if (!visible) return null;
return (
<Animated.View
style={[
styles.container,
{
opacity: fadeAnim,
transform: [{ translateY }],
},
]}
>
<View style={styles.iconContainer}>
<Ionicons name={icon} size={20} color={AppColors.white} />
</View>
<Text style={styles.message}>{message}</Text>
</Animated.View>
);
}
const styles = StyleSheet.create({
container: {
position: 'absolute',
bottom: 100,
left: Spacing.xl,
right: Spacing.xl,
backgroundColor: AppColors.textPrimary,
borderRadius: BorderRadius.lg,
paddingVertical: Spacing.md,
paddingHorizontal: Spacing.lg,
flexDirection: 'row',
alignItems: 'center',
gap: Spacing.sm,
...Shadows.lg,
},
iconContainer: {
width: 28,
height: 28,
borderRadius: 14,
backgroundColor: AppColors.success,
justifyContent: 'center',
alignItems: 'center',
},
message: {
flex: 1,
fontSize: FontSizes.base,
fontWeight: FontWeights.medium,
color: AppColors.white,
},
});