83 lines
2.2 KiB
TypeScript
83 lines
2.2 KiB
TypeScript
import React, { createContext, useContext, useState, useCallback } from 'react';
|
|
import { Animated, Text } from 'react-native';
|
|
import { useAppTheme } from '@theme';
|
|
import { getStyles } from './toast.styles';
|
|
|
|
export type ToastType = 'success' | 'error' | 'info';
|
|
|
|
interface ToastOptions {
|
|
message: string;
|
|
type?: ToastType;
|
|
duration?: number;
|
|
}
|
|
|
|
interface ToastContextType {
|
|
showToast: (options: ToastOptions) => void;
|
|
}
|
|
|
|
const ToastContext = createContext<ToastContextType | undefined>(undefined);
|
|
|
|
export const ToastProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
const { colors } = useAppTheme();
|
|
const styles = getStyles(colors);
|
|
|
|
const [toast, setToast] = useState<{ message: string; type: ToastType } | null>(null);
|
|
const [fadeAnim] = useState(new Animated.Value(0));
|
|
|
|
const showToast = useCallback(
|
|
({ message, type = 'info', duration = 3000 }: ToastOptions) => {
|
|
setToast({ message, type });
|
|
|
|
// Animate In
|
|
Animated.timing(fadeAnim, {
|
|
toValue: 1,
|
|
duration: 300,
|
|
useNativeDriver: true,
|
|
}).start();
|
|
|
|
// Dismiss after duration
|
|
setTimeout(() => {
|
|
Animated.timing(fadeAnim, {
|
|
toValue: 0,
|
|
duration: 300,
|
|
useNativeDriver: true,
|
|
}).start(() => setToast(null));
|
|
}, duration);
|
|
},
|
|
[fadeAnim]
|
|
);
|
|
|
|
const getStyleConfig = () => {
|
|
if (!toast) return { bg: null, text: null };
|
|
switch (toast.type) {
|
|
case 'success':
|
|
return { bg: styles.successBg, text: styles.successText };
|
|
case 'error':
|
|
return { bg: styles.errorBg, text: styles.errorText };
|
|
case 'info':
|
|
return { bg: styles.infoBg, text: styles.infoText };
|
|
}
|
|
};
|
|
|
|
const config = getStyleConfig();
|
|
|
|
return (
|
|
<ToastContext.Provider value={{ showToast }}>
|
|
{children}
|
|
{toast ? (
|
|
<Animated.View style={[styles.container, config.bg, { opacity: fadeAnim }]}>
|
|
<Text style={[styles.message, config.text]}>{toast.message}</Text>
|
|
</Animated.View>
|
|
) : null}
|
|
</ToastContext.Provider>
|
|
);
|
|
};
|
|
|
|
export const useToast = () => {
|
|
const context = useContext(ToastContext);
|
|
if (!context) {
|
|
throw new Error('useToast must be used within a ToastProvider');
|
|
}
|
|
return context;
|
|
};
|