import React, { useState, useEffect, useRef, forwardRef, useImperativeHandle, } from 'react'; import { View, Text, Animated, Pressable, useWindowDimensions, } from 'react-native'; import EventRegister from '../EvenRegister'; import styles from './styles'; import { getBackgroundColor, getTextColor, generateId } from './functions'; import type { ToastItemType, ToastItemOptions, ToastProps, ToastRefType, } from './types'; const DEFAULT_DURATION = 3000; const DEFAULT_TYPE = 'standard'; const DEFAULT_POSITION = 'top'; const ToastSection = forwardRef( (props, forwardedRef) => { const { wrapStyle, toastTextStyle, toastContainerStyle, duration = DEFAULT_DURATION, type = DEFAULT_TYPE, position = DEFAULT_POSITION, colors, holdable = true, } = props; const timeoutActionIdsRef = useRef< Record> >({}); const [toastListDataState, setToastListDataState] = useState< Record >({}); const { width, height } = useWindowDimensions(); const extraProps = { [position]: height * 0.1, }; useEffect(() => { EventRegister.on('show-toast', async (data) => { if (Array.isArray(data)) { for (const el of data) { await _show(el); } } else { _show(data); } }); return () => { EventRegister.rm('show-toast'); }; }, []); useEffect(() => { const theLastItemKey = position === 'top' ? Object.keys(toastListDataState).shift() : Object.keys(toastListDataState).pop(); if (!theLastItemKey) return; const theLastToastItem = toastListDataState[theLastItemKey]; if (!!theLastToastItem?.visible) return; _showToastItem(theLastItemKey); }, [toastListDataState]); const _addToastItemToWaitingList = ( toastItem: Omit ) => { return new Promise((resolve) => { const id = generateId(); setToastListDataState((_state) => { const _toastItem = { ...toastItem, id }; if (position === 'top') { return { [id]: _toastItem, ..._state, }; } else { return { ..._state, [id]: _toastItem, }; } }); setTimeout(() => { resolve(id); }, 100); }); }; const _clearTimeoutActionByKey = (key: string | number) => { const timeoutId = timeoutActionIdsRef.current[key]; if (timeoutId) { clearTimeout(timeoutId); delete timeoutActionIdsRef.current[key]; } }; const _hideToastItem = (key: string | number) => { const toastItem = toastListDataState[key]; if (toastItem == undefined) return; Animated.timing(toastItem.fadeAnim, { toValue: 0, duration: 500, useNativeDriver: true, }).start(() => { setToastListDataState((_state) => { const { [key]: needDeleteItem, ...newState } = _state; return newState; }); }); }; const _timeoutHideToastItem = (key: string | number, duration: number) => { _clearTimeoutActionByKey(key); timeoutActionIdsRef.current[key] = setTimeout(() => { _hideToastItem(key); delete timeoutActionIdsRef.current[key]; }, duration); }; const _showToastItem = (key: string | number) => { const toastItem = toastListDataState[key]; if (toastItem == undefined) return; Animated.timing(toastItem.fadeAnim, { toValue: 1, duration: 500, useNativeDriver: true, }).start(() => { setToastListDataState((_state) => { const _toastItem = _state[key]; if (!_toastItem) return _state; return { ..._state, [key]: { ..._toastItem, visible: true }, }; }); _timeoutHideToastItem(key, toastItem.duration); }); }; const _onPressInShowedToastItem = (toastItemData: ToastItemType) => () => { _clearTimeoutActionByKey(toastItemData.id); }; const _onPressOutShowedToastItem = (toastItemData: ToastItemType) => () => { _timeoutHideToastItem(toastItemData.id, toastItemData.duration); }; const _show = async (option: ToastItemOptions) => { const { content, type: _type, duration: _duration, AdditionItem, backgroundColor, textColor, textStyle, } = option || {}; const newToastItem: Omit = { visible: false, content: !content ? '' : content, type: !!_type ? _type : type, duration: _duration != undefined ? _duration : duration, backgroundColor, textColor, textStyle, AdditionItem, fadeAnim: new Animated.Value(0), }; return await _addToastItemToWaitingList(newToastItem); }; useImperativeHandle(forwardedRef, () => ({ show: _show, })); const _renderToastList = ( toastList: Record ) => { let result = [], index = 0; for (const key in toastList) { result[index++] = _renderToast(toastListDataState[key]); } return result; }; const _renderToast = (toastItemData?: ToastItemType) => { if (!toastItemData) return null; const { id, fadeAnim, content, type: _type, AdditionItem, backgroundColor, textColor, textStyle, } = toastItemData; const _backgroundColor = !!backgroundColor ? backgroundColor : getBackgroundColor(_type, colors); const _textColor = !!textColor ? textColor : getTextColor(_type, colors); return ( {AdditionItem} {content} ); }; if ( toastListDataState == undefined || Object.keys(toastListDataState).length === 0 ) return null; return ( {_renderToastList(toastListDataState)} ); } ); export default ToastSection;