UNPKG

react-native-gifted-chat

Version:

The most complete chat UI for React Native. Maintenance mode - active development continues in @kesha-antonov/react-native-chat

86 lines • 4.29 kB
import React, { useMemo } from 'react'; import { View } from 'react-native'; import Animated, { useAnimatedStyle, useDerivedValue } from 'react-native-reanimated'; import { Day } from '../../../Day'; import { Message } from '../../../Message'; import { isSameDay } from '../../../utils'; import { DAY_HANDOFF_OFFSET, dayPositionScreenTop, findDayPosition } from '../dayLayout'; export * from './types'; 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, isAnimated: false }) : <Day {...rest} createdAt={currentMessage.createdAt} isAnimated={false}/>} </View>); }; const AnimatedDayWrapper = (props) => { const { scrolledY, daysPositions, listHeight, floatingRenderedDate, ...rest } = props; const createdAt = useMemo(() => new Date(props.currentMessage.createdAt).getTime(), [props.currentMessage.createdAt]); // On-screen Y of this day's separator. Infinity (treated as below the pin, i.e. // visible) until the day has been measured. const separatorScreenTop = useDerivedValue(() => { 'worklet'; const day = findDayPosition(daysPositions.value, createdAt); if (!day) return Infinity; return dayPositionScreenTop(listHeight.value + scrolledY.value, day); }, [daysPositions, listHeight, scrolledY, createdAt]); const style = useAnimatedStyle(() => { // The inline separator is the in-conversation date marker. It is shown while its // day is below the handoff line, and hidden once its day is the one the floating // header is actually rendering at the pin - a hard step (no fade) so the date // goes floating(1) <-> inline(1) at the same pixel with no dip and no duplicate. // // Hiding on `floatingRenderedDate` (the header's *rendered* date) rather than on // position alone is what kills the 1-frame flash when scrolling into a newer day: // the worklet picks the new stuck day instantly but the header's text only // updates ~1 frame later on the JS thread; until it does, this inline separator // stays up and shows the correct date, so the header never flashes the old one. const belowHandoff = separatorScreenTop.value > DAY_HANDOFF_OFFSET; const headerShowsThisDay = floatingRenderedDate != null && floatingRenderedDate.value === createdAt; return { opacity: belowHandoff || !headerShowsThisDay ? 1 : 0, }; }, [separatorScreenTop, floatingRenderedDate, createdAt]); return (<Animated.View style={style}> <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