feat: add expo mobile application source code
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
import React, { createContext, useContext, useState, useCallback, useRef } from 'react';
|
||||
import { View, Text, StyleSheet, Animated, Platform, Dimensions } from 'react-native';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { useAppTheme } from './ThemeContext';
|
||||
|
||||
interface ToastContextType {
|
||||
showToast: (message: string, type?: 'success' | 'error' | 'info') => void;
|
||||
}
|
||||
|
||||
const { width } = Dimensions.get('window');
|
||||
const LIME = '#C6F135';
|
||||
|
||||
const ToastContext = createContext<ToastContextType | undefined>(undefined);
|
||||
|
||||
export function ToastProvider({ children }: { children: React.ReactNode }) {
|
||||
const { colors, isDark } = useAppTheme();
|
||||
const [toast, setToast] = useState<{ message: string, type: string } | null>(null);
|
||||
|
||||
const translateY = useRef(new Animated.Value(-120)).current;
|
||||
const opacity = useRef(new Animated.Value(0)).current;
|
||||
|
||||
const showToast = useCallback((message: string, type: 'success' | 'error' | 'info' = 'info') => {
|
||||
setToast({ message, type });
|
||||
|
||||
translateY.setValue(-120);
|
||||
opacity.setValue(0);
|
||||
|
||||
Animated.sequence([
|
||||
Animated.parallel([
|
||||
Animated.spring(translateY, { toValue: Platform.OS === 'ios' ? 60 : 40, useNativeDriver: true, friction: 9, tension: 50 }),
|
||||
Animated.timing(opacity, { toValue: 1, duration: 400, useNativeDriver: true })
|
||||
]),
|
||||
Animated.delay(2800),
|
||||
Animated.parallel([
|
||||
Animated.timing(translateY, { toValue: -120, duration: 300, useNativeDriver: true }),
|
||||
Animated.timing(opacity, { toValue: 0, duration: 300, useNativeDriver: true })
|
||||
])
|
||||
]).start(() => setToast(null));
|
||||
}, []);
|
||||
|
||||
// Theme configuration for the toast
|
||||
const getMeta = () => {
|
||||
if (!toast) return { icon: 'information', color: LIME };
|
||||
switch(toast.type) {
|
||||
case 'success': return { icon: 'check-circle', color: LIME };
|
||||
case 'error': return { icon: 'alert-circle', color: '#EF4444' };
|
||||
default: return { icon: 'information', color: '#3B82F6' };
|
||||
}
|
||||
};
|
||||
|
||||
const meta = getMeta();
|
||||
const toastBg = isDark ? '#1A1A1A' : '#1A1A1A'; // Solid dark toast for both modes is more premium
|
||||
const border = isDark ? '#2A2A2A' : '#333';
|
||||
|
||||
return (
|
||||
<ToastContext.Provider value={{ showToast }}>
|
||||
{children}
|
||||
{toast && (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.container,
|
||||
{
|
||||
opacity,
|
||||
transform: [{ translateY }],
|
||||
zIndex: 10000,
|
||||
}
|
||||
]}
|
||||
pointerEvents="none"
|
||||
>
|
||||
<View style={[
|
||||
styles.toast,
|
||||
{
|
||||
backgroundColor: toastBg,
|
||||
borderColor: border,
|
||||
}
|
||||
]}>
|
||||
<View style={[styles.iconBox, { backgroundColor: `${meta.color}20` }]}>
|
||||
<MaterialCommunityIcons
|
||||
name={meta.icon as any}
|
||||
size={22}
|
||||
color={meta.color}
|
||||
/>
|
||||
</View>
|
||||
<Text style={styles.text}>{toast.message}</Text>
|
||||
</View>
|
||||
</Animated.View>
|
||||
)}
|
||||
</ToastContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useToast() {
|
||||
const context = useContext(ToastContext);
|
||||
if (context === undefined) {
|
||||
return {
|
||||
showToast: (msg: string) => console.log('Toast (Fallback):', msg)
|
||||
};
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 20,
|
||||
right: 20,
|
||||
alignItems: 'center',
|
||||
},
|
||||
toast: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingVertical: 14,
|
||||
paddingHorizontal: 16,
|
||||
borderRadius: 20,
|
||||
borderWidth: 1,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 15 },
|
||||
shadowOpacity: 0.25,
|
||||
shadowRadius: 20,
|
||||
elevation: 12,
|
||||
width: '100%',
|
||||
maxWidth: 450,
|
||||
},
|
||||
iconBox: {
|
||||
width: 42,
|
||||
height: 42,
|
||||
borderRadius: 12,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
text: {
|
||||
marginLeft: 14,
|
||||
fontSize: 14,
|
||||
fontFamily: 'Outfit_700Bold',
|
||||
color: '#FFFFFF',
|
||||
flexShrink: 1,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user