mirror of
http://88.130.71.182:3000/BlitTech/contexta_mb.git
synced 2026-06-12 23:23:22 +00:00
98 lines
2.6 KiB
TypeScript
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);
|