UNPKG

react-native-gifted-chat

Version:
120 lines 6.09 kB
import React, { useCallback, useMemo } from 'react'; import { View } from 'react-native'; import Animated, { interpolate, useAnimatedStyle, useDerivedValue, useSharedValue } from 'react-native-reanimated'; import { Day } from '../../../Day'; import { Message } from '../../../Message'; import { isSameDay } from '../../../utils'; export * from './types'; // y-position of current scroll position relative to the bottom of the day container. (since we have inverted list it is bottom) export const useAbsoluteScrolledPositionToBottomOfDay = (listHeight, scrolledY, containerHeight, dayBottomMargin, dayTopOffset) => { const absoluteScrolledPositionToBottomOfDay = useDerivedValue(() => listHeight.value + scrolledY.value - containerHeight.value - dayBottomMargin - dayTopOffset, [listHeight, scrolledY, containerHeight, dayBottomMargin, dayTopOffset]); return absoluteScrolledPositionToBottomOfDay; }; export const useRelativeScrolledPositionToBottomOfDay = (listHeight, scrolledY, daysPositions, containerHeight, dayBottomMargin, dayTopOffset, createdAt) => { const dayMarginTop = useMemo(() => 5, []); const absoluteScrolledPositionToBottomOfDay = useAbsoluteScrolledPositionToBottomOfDay(listHeight, scrolledY, containerHeight, dayBottomMargin, dayTopOffset); // find current day position by scrolled position const currentDayPosition = useDerivedValue(() => { 'worklet'; // When createdAt is provided (called from AnimatedDayWrapper for a specific message), // directly find the day position by createdAt without sorting the entire array. // This avoids O(n log n) sorting and O(n) search for each message item. if (createdAt != null) { const values = Object.values(daysPositions.value); for (let i = 0; i < values.length; i++) if (values[i].createdAt === createdAt) return values[i]; } // Fallback: sort and search when createdAt is not provided (e.g., from DayAnimated) const sortedArray = Object.values(daysPositions.value).sort((a, b) => { 'worklet'; return a.y - b.y; }); for (let i = 0; i < sortedArray.length; i++) { const day = sortedArray[i]; const dayPosition = day.y + day.height; if (absoluteScrolledPositionToBottomOfDay.value < dayPosition || i === sortedArray.length - 1) return day; } return undefined; }, [daysPositions, absoluteScrolledPositionToBottomOfDay, createdAt]); const relativeScrolledPositionToBottomOfDay = useDerivedValue(() => { const scrolledBottomY = listHeight.value + scrolledY.value - ((currentDayPosition.value?.y ?? 0) + (currentDayPosition.value?.height ?? 0) + dayMarginTop); return scrolledBottomY; }, [listHeight, scrolledY, currentDayPosition, dayMarginTop]); return relativeScrolledPositionToBottomOfDay; }; const DayWrapper = (props) => { const { renderDay: renderDayProp, currentMessage, previousMessage, } = props; if (!currentMessage?.createdAt || isSameDay(currentMessage, previousMessage)) return null; const { /* eslint-disable @typescript-eslint/no-unused-vars */ containerStyle, onMessageLayout, /* eslint-enable @typescript-eslint/no-unused-vars */ ...rest } = props; return (<View> {renderDayProp ? renderDayProp({ ...rest, createdAt: currentMessage.createdAt }) : <Day {...rest} createdAt={currentMessage.createdAt}/>} </View>); }; const AnimatedDayWrapper = (props) => { const { scrolledY, daysPositions, listHeight, ...rest } = props; const dayContainerHeight = useSharedValue(0); const dayTopOffset = useMemo(() => 10, []); const dayBottomMargin = useMemo(() => 10, []); const createdAt = useMemo(() => new Date(props.currentMessage.createdAt).getTime(), [props.currentMessage.createdAt]); const relativeScrolledPositionToBottomOfDay = useRelativeScrolledPositionToBottomOfDay(listHeight, scrolledY, daysPositions, dayContainerHeight, dayBottomMargin, dayTopOffset, createdAt); const handleLayoutDayContainer = useCallback(({ nativeEvent }) => { dayContainerHeight.value = nativeEvent.layout.height; }, [dayContainerHeight]); const style = useAnimatedStyle(() => ({ opacity: interpolate(relativeScrolledPositionToBottomOfDay.value, [ -dayTopOffset, -0.0001, 0, dayContainerHeight.value + dayTopOffset, ], [ 0, 0, 1, 1, ], 'clamp'), }), [relativeScrolledPositionToBottomOfDay, dayContainerHeight, dayTopOffset]); return (<Animated.View style={style} onLayout={handleLayoutDayContainer}> <DayWrapper {...rest}/> </Animated.View>); }; export const Item = (props) => { const { renderMessage: renderMessageProp, isDayAnimationEnabled, reply, /* eslint-disable @typescript-eslint/no-unused-vars */ scrolledY: _scrolledY, daysPositions: _daysPositions, listHeight: _listHeight, /* eslint-enable @typescript-eslint/no-unused-vars */ ...rest } = props; // Transform reply props for Message and Bubble const messageProps = useMemo(() => ({ ...rest, // Swipe to reply for Message component swipeToReply: reply?.swipe, // Message reply styling for Bubble component messageReply: reply ? { renderMessageReply: reply.renderMessageReply, onPress: reply.onPress, ...reply.messageStyle, } : undefined, }), [rest, reply]); return ( // do not remove key. it helps to get correct position of the day container <View key={props.currentMessage._id.toString()}> {isDayAnimationEnabled ? <AnimatedDayWrapper {...props}/> : <DayWrapper {...messageProps}/>} {renderMessageProp ? renderMessageProp(messageProps) : <Message {...messageProps}/>} </View>); }; //# sourceMappingURL=index.js.map