UNPKG

react-native-gifted-chat

Version:
244 lines 9.64 kB
import PropTypes from 'prop-types'; import React from 'react'; import { FlatList, View, StyleSheet, TouchableOpacity, Text, Platform, } from 'react-native'; import { LoadEarlier } from './LoadEarlier'; import Message from './Message'; import Color from './Color'; import TypingIndicator from './TypingIndicator'; import { StylePropType } from './utils'; import { warning } from './logging'; const styles = StyleSheet.create({ container: { flex: 1, }, containerAlignTop: { flexDirection: 'row', alignItems: 'flex-start', }, contentContainerStyle: { flexGrow: 1, justifyContent: 'flex-start', }, emptyChatContainer: { flex: 1, transform: [{ scaleY: -1 }], }, headerWrapper: { flex: 1, }, listStyle: { flex: 1, }, scrollToBottomStyle: { opacity: 0.8, position: 'absolute', right: 10, bottom: 30, zIndex: 999, height: 40, width: 40, borderRadius: 20, backgroundColor: Color.white, alignItems: 'center', justifyContent: 'center', shadowColor: Color.black, shadowOpacity: 0.5, shadowOffset: { width: 0, height: 0 }, shadowRadius: 1, }, }); export default class MessageContainer extends React.PureComponent { constructor() { super(...arguments); this.state = { showScrollBottom: false, hasScrolled: false, }; this.renderTypingIndicator = () => { if (Platform.OS === 'web') { return null; } return <TypingIndicator isTyping={this.props.isTyping || false}/>; }; this.renderFooter = () => { if (this.props.renderFooter) { return this.props.renderFooter(this.props); } return this.renderTypingIndicator(); }; this.renderLoadEarlier = () => { if (this.props.loadEarlier === true) { const loadEarlierProps = { ...this.props, }; if (this.props.renderLoadEarlier) { return this.props.renderLoadEarlier(loadEarlierProps); } return <LoadEarlier {...loadEarlierProps}/>; } return null; }; this.scrollToBottom = (animated = true) => { const { inverted } = this.props; if (inverted) { this.scrollTo({ offset: 0, animated }); } else if (this.props.forwardRef && this.props.forwardRef.current) { this.props.forwardRef.current.scrollToEnd({ animated }); } }; this.handleOnScroll = (event) => { const { nativeEvent: { contentOffset: { y: contentOffsetY }, contentSize: { height: contentSizeHeight }, layoutMeasurement: { height: layoutMeasurementHeight }, }, } = event; const { scrollToBottomOffset } = this.props; if (this.props.inverted) { if (contentOffsetY > scrollToBottomOffset) { this.setState({ showScrollBottom: true, hasScrolled: true }); } else { this.setState({ showScrollBottom: false, hasScrolled: true }); } } else { if (contentOffsetY < scrollToBottomOffset && contentSizeHeight - layoutMeasurementHeight > scrollToBottomOffset) { this.setState({ showScrollBottom: true, hasScrolled: true }); } else { this.setState({ showScrollBottom: false, hasScrolled: true }); } } }; this.renderRow = ({ item, index }) => { if (!item._id && item._id !== 0) { warning('GiftedChat: `_id` is missing for message', JSON.stringify(item)); } if (!item.user) { if (!item.system) { warning('GiftedChat: `user` is missing for message', JSON.stringify(item)); } item.user = { _id: 0 }; } const { messages, user, inverted, ...restProps } = this.props; if (messages && user) { const previousMessage = (inverted ? messages[index + 1] : messages[index - 1]) || {}; const nextMessage = (inverted ? messages[index - 1] : messages[index + 1]) || {}; const messageProps = { ...restProps, user, key: item._id, currentMessage: item, previousMessage, inverted, nextMessage, position: item.user._id === user._id ? 'right' : 'left', }; if (this.props.renderMessage) { return this.props.renderMessage(messageProps); } return <Message {...messageProps}/>; } return null; }; this.renderChatEmpty = () => { if (this.props.renderChatEmpty) { return this.props.inverted ? (this.props.renderChatEmpty()) : (<View style={styles.emptyChatContainer}> {this.props.renderChatEmpty()} </View>); } return <View style={styles.container}/>; }; this.renderHeaderWrapper = () => (<View style={styles.headerWrapper}>{this.renderLoadEarlier()}</View>); this.onLayoutList = () => { if (!this.props.inverted && !!this.props.messages && this.props.messages.length) { setTimeout(() => this.scrollToBottom && this.scrollToBottom(false), 15 * this.props.messages.length); } }; this.onEndReached = ({ distanceFromEnd }) => { const { loadEarlier, onLoadEarlier, infiniteScroll, isLoadingEarlier, } = this.props; if (infiniteScroll && (this.state.hasScrolled || distanceFromEnd > 0) && distanceFromEnd <= 100 && loadEarlier && onLoadEarlier && !isLoadingEarlier && Platform.OS !== 'web') { onLoadEarlier(); } }; this.keyExtractor = (item) => `${item._id}`; } scrollTo(options) { if (this.props.forwardRef && this.props.forwardRef.current && options) { this.props.forwardRef.current.scrollToOffset(options); } } renderScrollBottomComponent() { const { scrollToBottomComponent } = this.props; if (scrollToBottomComponent) { return scrollToBottomComponent(); } return <Text>V</Text>; } renderScrollToBottomWrapper() { const propsStyle = this.props.scrollToBottomStyle || {}; return (<View style={[styles.scrollToBottomStyle, propsStyle]}> <TouchableOpacity onPress={() => this.scrollToBottom()} hitSlop={{ top: 5, left: 5, right: 5, bottom: 5 }}> {this.renderScrollBottomComponent()} </TouchableOpacity> </View>); } render() { const { inverted } = this.props; return (<View style={this.props.alignTop ? styles.containerAlignTop : styles.container}> <FlatList ref={this.props.forwardRef} extraData={[this.props.extraData, this.props.isTyping]} keyExtractor={this.keyExtractor} enableEmptySections automaticallyAdjustContentInsets={false} inverted={inverted} data={this.props.messages} style={styles.listStyle} contentContainerStyle={styles.contentContainerStyle} renderItem={this.renderRow} {...this.props.invertibleScrollViewProps} ListEmptyComponent={this.renderChatEmpty} ListFooterComponent={inverted ? this.renderHeaderWrapper : this.renderFooter} ListHeaderComponent={inverted ? this.renderFooter : this.renderHeaderWrapper} onScroll={this.handleOnScroll} scrollEventThrottle={100} onLayout={this.onLayoutList} onEndReached={this.onEndReached} onEndReachedThreshold={0.1} {...this.props.listViewProps}/> {this.state.showScrollBottom && this.props.scrollToBottom ? this.renderScrollToBottomWrapper() : null} </View>); } } MessageContainer.defaultProps = { messages: [], user: {}, isTyping: false, renderChatEmpty: null, renderFooter: null, renderMessage: null, onLoadEarlier: () => { }, onQuickReply: () => { }, inverted: true, loadEarlier: false, listViewProps: {}, invertibleScrollViewProps: {}, extraData: null, scrollToBottom: false, scrollToBottomOffset: 200, alignTop: false, scrollToBottomStyle: {}, infiniteScroll: false, isLoadingEarlier: false, }; MessageContainer.propTypes = { messages: PropTypes.arrayOf(PropTypes.object), isTyping: PropTypes.bool, user: PropTypes.object, renderChatEmpty: PropTypes.func, renderFooter: PropTypes.func, renderMessage: PropTypes.func, renderLoadEarlier: PropTypes.func, onLoadEarlier: PropTypes.func, listViewProps: PropTypes.object, inverted: PropTypes.bool, loadEarlier: PropTypes.bool, invertibleScrollViewProps: PropTypes.object, extraData: PropTypes.object, scrollToBottom: PropTypes.bool, scrollToBottomOffset: PropTypes.number, scrollToBottomComponent: PropTypes.func, alignTop: PropTypes.bool, scrollToBottomStyle: StylePropType, infiniteScroll: PropTypes.bool, }; //# sourceMappingURL=MessageContainer.js.map