Files
contexta_mb/src/contexts/ToastContext.tsx
belviskhoremk 9e663bdc8b Initial commit
2026-05-08 13:01:47 +00:00

98 lines
2.6 KiB
TypeScript

import React, { createContext, useContext, useState, useCallback, useRef } from 'react';
import { View, Text, StyleSheet, Animated } from 'react-native';
import { COLORS, RADIUS, SPACING, FONT_SIZE } from '../theme';
type ToastType = 'success' | 'error' | 'info';
interface Toast {
id: number;
message: string;
type: ToastType;
}
interface ToastContextType {
success: (message: string) => void;
error: (message: string) => void;
info: (message: string) => void;
}
const ToastContext = createContext<ToastContextType>({
success: () => {},
error: () => {},
info: () => {},
});
export function ToastProvider({ children }: { children: React.ReactNode }) {
const [toasts, setToasts] = useState<Toast[]>([]);
const counter = useRef(0);
const show = useCallback((message: string, type: ToastType) => {
const id = counter.current++;
setToasts(prev => [...prev, { id, message, type }]);
setTimeout(() => {
setToasts(prev => prev.filter(t => t.id !== id));
}, 3500);
}, []);
const success = useCallback((msg: string) => show(msg, 'success'), [show]);
const error = useCallback((msg: string) => show(msg, 'error'), [show]);
const info = useCallback((msg: string) => show(msg, 'info'), [show]);
return (
<ToastContext.Provider value={{ success, error, info }}>
{children}
<View style={styles.container} pointerEvents="none">
{toasts.map(toast => (
<ToastItem key={toast.id} toast={toast} />
))}
</View>
</ToastContext.Provider>
);
}
function ToastItem({ toast }: { toast: Toast }) {
const opacity = useRef(new Animated.Value(0)).current;
React.useEffect(() => {
Animated.sequence([
Animated.timing(opacity, { toValue: 1, duration: 250, useNativeDriver: true }),
Animated.delay(2800),
Animated.timing(opacity, { toValue: 0, duration: 300, useNativeDriver: true }),
]).start();
}, [opacity]);
const bg =
toast.type === 'success' ? COLORS.success
: toast.type === 'error' ? COLORS.error
: COLORS.info;
return (
<Animated.View style={[styles.toast, { backgroundColor: bg, opacity }]}>
<Text style={styles.toastText}>{toast.message}</Text>
</Animated.View>
);
}
const styles = StyleSheet.create({
container: {
position: 'absolute',
bottom: 90,
left: SPACING.lg,
right: SPACING.lg,
zIndex: 9999,
gap: SPACING.sm,
},
toast: {
borderRadius: RADIUS.md,
paddingVertical: SPACING.md,
paddingHorizontal: SPACING.lg,
},
toastText: {
color: '#fff',
fontSize: FONT_SIZE.md,
fontWeight: '500',
},
});
export const useToast = () => useContext(ToastContext);