1b85f12f7S猫头猫import {timingConfig} from '@/constants/commonConst'; 22a3194f5S猫头猫import {fontSizeConst} from '@/constants/uiConst'; 3277c5280S猫头猫import useColors from '@/hooks/useColors'; 4b85f12f7S猫头猫import rpx from '@/utils/rpx'; 5b85f12f7S猫头猫import {GlobalState} from '@/utils/stateMapper'; 6b85f12f7S猫头猫import {nanoid} from 'nanoid'; 7b85f12f7S猫头猫import React, {useCallback, useEffect} from 'react'; 8b85f12f7S猫头猫import {Pressable, StyleSheet, Text, View} from 'react-native'; 9b85f12f7S猫头猫import { 10b85f12f7S猫头猫 Directions, 11b85f12f7S猫头猫 Gesture, 12b85f12f7S猫头猫 GestureDetector, 13b85f12f7S猫头猫} from 'react-native-gesture-handler'; 14b85f12f7S猫头猫import Animated, { 15b85f12f7S猫头猫 cancelAnimation, 16b85f12f7S猫头猫 runOnJS, 17b85f12f7S猫头猫 useAnimatedStyle, 18b85f12f7S猫头猫 useSharedValue, 19b85f12f7S猫头猫 withDelay, 20b85f12f7S猫头猫 withTiming, 21b85f12f7S猫头猫} from 'react-native-reanimated'; 225589cdf3S猫头猫import Icon from '@/components/base/icon.tsx'; 232a3194f5S猫头猫 24b85f12f7S猫头猫export interface IToastConfig { 25b85f12f7S猫头猫 /** 类型 */ 26b85f12f7S猫头猫 type: 'success' | 'warn'; 27b85f12f7S猫头猫 /** 消息内容 */ 28b85f12f7S猫头猫 message?: string; 29b85f12f7S猫头猫 /** 行动点 */ 30b85f12f7S猫头猫 actionText?: string; 31b85f12f7S猫头猫 /** 行动点按钮行为 */ 32b85f12f7S猫头猫 onActionClick?: () => void; 33b85f12f7S猫头猫 /** 展示时间 */ 34b85f12f7S猫头猫 duration?: number; 352a3194f5S猫头猫} 362a3194f5S猫头猫 37b85f12f7S猫头猫type IToastConfigInner = IToastConfig & { 38b85f12f7S猫头猫 id: string; 392a3194f5S猫头猫}; 402a3194f5S猫头猫 41b85f12f7S猫头猫const toastQueue: IToastConfigInner[] = []; 42b85f12f7S猫头猫 43b85f12f7S猫头猫const fixedTop = rpx(250); 44b85f12f7S猫头猫 45b85f12f7S猫头猫const activeToastStore = new GlobalState<IToastConfigInner | null>(null); 46b85f12f7S猫头猫 47b85f12f7S猫头猫const typeConfig = { 48b85f12f7S猫头猫 success: { 49b85f12f7S猫头猫 name: 'check-circle', 50b85f12f7S猫头猫 color: '#457236', 51b85f12f7S猫头猫 }, 52b85f12f7S猫头猫 warn: { 535589cdf3S猫头猫 name: 'exclamation-circle', 54b85f12f7S猫头猫 color: '#de7622', 55b85f12f7S猫头猫 }, 565589cdf3S猫头猫} as const; 57b85f12f7S猫头猫 58b85f12f7S猫头猫export function ToastBaseComponent() { 59b85f12f7S猫头猫 const activeToast = activeToastStore.useValue(); 60b85f12f7S猫头猫 const colors = useColors(); 61b85f12f7S猫头猫 62b85f12f7S猫头猫 const toastAnim = useSharedValue(0); 63b85f12f7S猫头猫 64b85f12f7S猫头猫 const setNextToast = useCallback(() => { 65b85f12f7S猫头猫 activeToastStore.setValue(toastQueue.shift() || null); 66b85f12f7S猫头猫 }, []); 67b85f12f7S猫头猫 68b85f12f7S猫头猫 useEffect(() => { 69b85f12f7S猫头猫 if (activeToast) { 70b85f12f7S猫头猫 toastAnim.value = withTiming(1, timingConfig.animationSlow, () => { 71b85f12f7S猫头猫 toastAnim.value = withDelay( 72b85f12f7S猫头猫 activeToast.duration || 1200, 73b85f12f7S猫头猫 withTiming(0, timingConfig.animationSlow, finished => { 74b85f12f7S猫头猫 if (finished) { 75b85f12f7S猫头猫 runOnJS(setNextToast)(); 76b85f12f7S猫头猫 } 77b85f12f7S猫头猫 }), 78b85f12f7S猫头猫 ); 79b85f12f7S猫头猫 }); 80b85f12f7S猫头猫 } 81b85f12f7S猫头猫 }, [activeToast]); 82b85f12f7S猫头猫 83b85f12f7S猫头猫 function removeCurrentToast() { 84b85f12f7S猫头猫 if (toastAnim.value === 1) { 85b85f12f7S猫头猫 cancelAnimation(toastAnim); 86b85f12f7S猫头猫 toastAnim.value = withTiming( 87b85f12f7S猫头猫 0, 88b85f12f7S猫头猫 timingConfig.animationSlow, 89b85f12f7S猫头猫 finished => { 90b85f12f7S猫头猫 if (finished) { 91b85f12f7S猫头猫 runOnJS(setNextToast)(); 92b85f12f7S猫头猫 } 93b85f12f7S猫头猫 }, 94b85f12f7S猫头猫 ); 95b85f12f7S猫头猫 } 96b85f12f7S猫头猫 } 97b85f12f7S猫头猫 98b85f12f7S猫头猫 const flingGesture = Gesture.Fling() 99b85f12f7S猫头猫 .direction(Directions.UP) 100b85f12f7S猫头猫 .onEnd(() => { 101b85f12f7S猫头猫 removeCurrentToast(); 102b85f12f7S猫头猫 }) 103b85f12f7S猫头猫 .runOnJS(true); 104b85f12f7S猫头猫 105b85f12f7S猫头猫 const toastAnimStyle = useAnimatedStyle(() => { 106b85f12f7S猫头猫 return { 107b85f12f7S猫头猫 transform: [ 108b85f12f7S猫头猫 { 109b85f12f7S猫头猫 translateY: (toastAnim.value - 1) * fixedTop, 110b85f12f7S猫头猫 }, 111b85f12f7S猫头猫 ], 112b85f12f7S猫头猫 opacity: toastAnim.value, 113b85f12f7S猫头猫 }; 114b85f12f7S猫头猫 }); 115b85f12f7S猫头猫 116b85f12f7S猫头猫 return activeToast ? ( 117b85f12f7S猫头猫 <GestureDetector gesture={flingGesture}> 118b85f12f7S猫头猫 <View style={styles.container}> 119b85f12f7S猫头猫 <Animated.View 120b85f12f7S猫头猫 style={[ 121b85f12f7S猫头猫 styles.contentContainer, 122b85f12f7S猫头猫 { 123b85f12f7S猫头猫 backgroundColor: colors.backdrop, 124b85f12f7S猫头猫 shadowColor: colors.shadow, 125b85f12f7S猫头猫 }, 126b85f12f7S猫头猫 toastAnimStyle, 127b85f12f7S猫头猫 ]}> 128b85f12f7S猫头猫 <Icon 1295589cdf3S猫头猫 size={fontSizeConst.appbar} 130b85f12f7S猫头猫 name={typeConfig[activeToast.type].name} 131b85f12f7S猫头猫 color={typeConfig[activeToast.type].color} 132b85f12f7S猫头猫 /> 133b85f12f7S猫头猫 <Text 134*dcb7566cS猫头猫 numberOfLines={2} 135b85f12f7S猫头猫 style={[styles.text, {color: colors.text}]}> 136b85f12f7S猫头猫 {activeToast.message} 137b85f12f7S猫头猫 </Text> 138b85f12f7S猫头猫 {activeToast.actionText && activeToast.onActionClick ? ( 139b85f12f7S猫头猫 <Pressable 140b85f12f7S猫头猫 style={[ 141b85f12f7S猫头猫 styles.actionTextContainer, 142b85f12f7S猫头猫 {backgroundColor: colors.primary}, 143b85f12f7S猫头猫 ]} 144b85f12f7S猫头猫 onPress={activeToast.onActionClick}> 145b85f12f7S猫头猫 <Text style={styles.actionText} numberOfLines={1}> 146b85f12f7S猫头猫 {activeToast.actionText} 147b85f12f7S猫头猫 </Text> 148b85f12f7S猫头猫 </Pressable> 149b85f12f7S猫头猫 ) : null} 150b85f12f7S猫头猫 </Animated.View> 151b85f12f7S猫头猫 </View> 152b85f12f7S猫头猫 </GestureDetector> 153b85f12f7S猫头猫 ) : null; 154b85f12f7S猫头猫} 1552a3194f5S猫头猫 1562a3194f5S猫头猫const styles = StyleSheet.create({ 157b85f12f7S猫头猫 container: { 158b85f12f7S猫头猫 position: 'absolute', 159b85f12f7S猫头猫 top: rpx(128), 160b85f12f7S猫头猫 width: '100%', 1612a3194f5S猫头猫 alignItems: 'center', 162b85f12f7S猫头猫 height: rpx(100), 163d1fd80edS猫头猫 zIndex: 20000, 1642a3194f5S猫头猫 }, 165b85f12f7S猫头猫 contentContainer: { 166b85f12f7S猫头猫 width: rpx(688), 167b85f12f7S猫头猫 height: '100%', 168b85f12f7S猫头猫 borderRadius: rpx(12), 169b85f12f7S猫头猫 backgroundColor: 'blue', 170b85f12f7S猫头猫 flexDirection: 'row', 171b85f12f7S猫头猫 alignItems: 'center', 172b85f12f7S猫头猫 paddingHorizontal: rpx(24), 173b85f12f7S猫头猫 shadowOffset: { 174b85f12f7S猫头猫 width: 0, 175b85f12f7S猫头猫 height: 2, 176b85f12f7S猫头猫 }, 177b85f12f7S猫头猫 shadowOpacity: 0.2, 178b85f12f7S猫头猫 shadowRadius: 1.41, 179b85f12f7S猫头猫 180b85f12f7S猫头猫 elevation: 2, 1812a3194f5S猫头猫 }, 182b85f12f7S猫头猫 text: { 183b85f12f7S猫头猫 fontSize: fontSizeConst.content, 184b85f12f7S猫头猫 includeFontPadding: false, 185b85f12f7S猫头猫 flex: 1, 186b85f12f7S猫头猫 marginLeft: rpx(24), 187b85f12f7S猫头猫 }, 188b85f12f7S猫头猫 actionText: { 189b85f12f7S猫头猫 fontSize: fontSizeConst.content, 190b85f12f7S猫头猫 includeFontPadding: false, 191b85f12f7S猫头猫 color: 'white', 192b85f12f7S猫头猫 }, 193b85f12f7S猫头猫 actionTextContainer: { 194b85f12f7S猫头猫 marginLeft: rpx(24), 195b85f12f7S猫头猫 width: rpx(120), 1965589cdf3S猫头猫 paddingHorizontal: rpx(12), 197b85f12f7S猫头猫 flexDirection: 'row', 198b85f12f7S猫头猫 alignItems: 'center', 199b85f12f7S猫头猫 justifyContent: 'center', 200b85f12f7S猫头猫 borderRadius: rpx(30), 201b85f12f7S猫头猫 height: rpx(58), 2022a3194f5S猫头猫 }, 2032a3194f5S猫头猫}); 204b85f12f7S猫头猫 205b85f12f7S猫头猫export function showToast(config: IToastConfig) { 206b85f12f7S猫头猫 const id = nanoid(); 207b85f12f7S猫头猫 const _config = { 208b85f12f7S猫头猫 ...config, 209b85f12f7S猫头猫 id, 210b85f12f7S猫头猫 }; 211b85f12f7S猫头猫 const activeToast = activeToastStore.getValue(); 212b85f12f7S猫头猫 if (!activeToast) { 213b85f12f7S猫头猫 activeToastStore.setValue(_config); 214b85f12f7S猫头猫 } else { 215b85f12f7S猫头猫 toastQueue.push(_config); 216b85f12f7S猫头猫 } 217b85f12f7S猫头猫 218b85f12f7S猫头猫 return id; 219b85f12f7S猫头猫} 220