UNPKG

react-native-lifetime-livechat

Version:

LiveChat implementation for LifeTime application

374 lines (317 loc) 10 kB
import React, {Component} from 'react'; import { View, Dimensions, StyleSheet, Image, Platform, Text, TouchableOpacity, Keyboard, Animated, Easing } from 'react-native'; import PropTypes from 'prop-types'; import moment from 'moment'; import { PanGestureHandler, State, } from 'react-native-gesture-handler'; import {init} from './sdk/livechat-visitor-sdk.min'; import {GiftedChat, Bubble} from './components/GiftedChat'; import InputField from "./components/InputField"; const {height, width} = Dimensions.get('window'); const sliderImage = require('./assets/slider.png'); const chatHeights = [height * 0.18, height * 0.4, height]; const animationTime = 300; const initialMessage = [{ _id: '1', text: 'How can we help you today?', createdAt: new Date(), user: { _id: 'LifeTime', name: 'LifeTime', }, }]; function flatten(arr) { return arr.reduce(function (flat, toFlatten) { return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten); }, []); } export default class LiveChat extends Component { state = { visitorSDK: null, chatState: 0, chatHeight: new Animated.Value(0), keyboardHeight: new Animated.Value(0), messages: [] }; componentWillUpdate(nextProps) { const messages = flatten([initialMessage, ...this.extractMessagesFromChatData(nextProps.chats)]); if (this.state.messages.length !== messages.length) { this.setState({ messages, }); } } componentDidMount() { const {license, group, chats} = this.props; if (Platform.OS === 'ios') { this.keyboardShowListener = Keyboard.addListener('keyboardWillShow', this._keyboardDidShow); this.keyboardHideListener = Keyboard.addListener('keyboardWillHide', this._keyboardDidHide); } else { this.keyboardShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow); this.keyboardHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide); } GLOBAL.cookie = ''; const visitorSDK = init({ license, group }); visitorSDK.on('new_message', this.handleNewMessage); this.setState({ visitorSDK, messages: flatten([initialMessage, ...this.extractMessagesFromChatData(chats)]) }); this.animateChatHeight(); } componentWillUnmount() { this.keyboardShowListener.remove(); this.keyboardHideListener.remove(); this.state.visitorSDK.closeChat(); this.state.visitorSDK.destroy(); } scrollToGivenChat = (chatId) => { const {chats} = this.props; let index = { found: false, index: 0 }; chats.forEach(data => { if (!index.found) { if (data.chat.id === chatId) { index.found = true; } else { index.index += data.chat.messages.length } } }); this.chat._messageContainerRef.scrollTo(index.index); }; extractMessagesFromChatData = (chats) => { return chats.map(chatData => { const chat = chatData.chat || {}; const messages = chat.messages || []; return messages.slice(0).reverse().map(message => ({ _id: Math.random().toString().substring(2), text: message.text, createdAt: message.timestamp, user: { _id: message.user_type === 'agent' ? 'LifeTime' : message.author_name, name: message.user_type === 'agent' ? 'LifeTime' : message.author_name, } })); }) }; animateChatHeight = () => { const {chatHeight, chatState} = this.state; Animated.timing(chatHeight, { toValue: chatHeights[chatState], easing: Easing.out(Easing.quad), duration: animationTime, }).start(); }; animateKeyboardBottom = (height) => { const {keyboardHeight} = this.state; Animated.timing(keyboardHeight, { toValue: height, easing: Easing.out(Easing.quad), duration: animationTime, }).start(); }; _keyboardDidShow = (e) => { const {chatState} = this.state; this.setState({ chatState: chatState === 0 ? 1 : chatState }, () => { this.animateChatHeight(); this.animateKeyboardBottom(e.endCoordinates.height) }) }; _keyboardDidHide = () => { this.animateKeyboardBottom(0) }; onSendClicked = async (message) => { const {visitorSDK, chatId, keyboardHeight, chatState} = this.state; if (keyboardHeight._value > 0) { Keyboard.dismiss(); } if (chatState === 0) { this.smallChat(); } if (message) { try { this.addMessageToChat(message, 'User'); if (!chatId) { const data = await visitorSDK.startChat(); this.setState({ chatId: data.chatId }); } } catch (e) { console.log(e); } visitorSDK.sendMessage({ text: message, customId: Math.random().toString().substring(1), }); } }; addMessageToChat = (message, user) => { this.setState({ chatId: message.chatId, messages: [{ text: message, _id: Math.random().toString().substring(1), createdAt: new Date(), user: { _id: user, name: user, } }, ...this.state.messages], }); }; handleNewMessage = (message) => { if (message.id === 'welcome_message') return; if (message.authorId.toString().indexOf('.') > -1) return; this.setState({ messages: [{ text: message.text, _id: message.id.toString(), createdAt: message.timestamp, user: { _id: 'LifeTime', name: 'LifeTime', }, }, ...this.state.messages], }); }; openChat = () => { const {chatState} = this.state; if (chatState === 0) { this.setState({ chatState: 1 }, this.animateChatHeight); } }; makeChatFullScreen = () => { this.setState({chatState: 2}, this.animateChatHeight); }; closeChat = () => { this.setState({chatState: 0}, this.animateChatHeight); Keyboard.dismiss(); }; smallChat = () => { this.setState({chatState: 1}, this.animateChatHeight); }; onSliderClicked = () => { const {chatState} = this.state; if (chatState === 2) { this.closeChat(); } else { this.makeChatFullScreen(); } }; renderBubble = props => { return ( <Bubble key={props.id} {...props} wrapperStyle={{ left: {backgroundColor: '#f2cca2'}, right: {backgroundColor: '#dce9d5'} }} textStyle={{ right: {color: '#444'}, left: {color: '#444'}, }} /> ); }; _onGestureEvent = (event) => { const {chatHeight, keyboardHeight} = this.state; chatHeight.setValue(height - event.nativeEvent.absoluteY + width / 20 - keyboardHeight._value); }; _onHandlerStateChange = event => { const {chatState} = this.state; if (event.nativeEvent.state === State.BEGAN) { this.dragStartY = event.nativeEvent.absoluteY; } else if (event.nativeEvent.state === State.END) { this.dragEndY = event.nativeEvent.absoluteY; if (this.dragEndY - this.dragStartY < 0) { this.makeChatFullScreen(); } else { if (chatState === 2) { this.smallChat(); } else { this.closeChat(event); } } } }; render() { const {chatState, messages, keyboardHeight, chatHeight} = this.state; const {container, title, slider, gap, time} = styles; const {onLoadEarlier, isLoadingEarlier, loadEarlier, scrollToBottom, scrollToBottomOffset, onLoadAfter, isLoadingAfter, loadAfter, onJumpBackClicked} = this.props; return ( <Animated.View style={[container, { bottom: keyboardHeight, height: chatHeight, }]}> {chatState > 0 && <PanGestureHandler onGestureEvent={this._onGestureEvent} onHandlerStateChange={this._onHandlerStateChange} > <View> <TouchableOpacity onPress={this.onSliderClicked}> <Image source={sliderImage} style={slider} /> </TouchableOpacity> </View> </PanGestureHandler>} {chatState === 0 && <Text style={title}> Lifetime Chat (Available 9a - 9p PST) </Text>} <GiftedChat renderAvatar={null} messages={chatState > 0 ? messages : []} ref={ref => this.chat = ref} renderBubble={this.renderBubble} scrollToBottom={scrollToBottom} scrollToBottomOffset={scrollToBottomOffset} loadEarlier={loadEarlier} onLoadEarlier={onLoadEarlier} isLoadingEarlier={isLoadingEarlier} onLoadAfter={onLoadAfter} isLoadingAfter={isLoadingAfter} loadAfter={loadAfter} onJumpBackClicked={onJumpBackClicked} renderDay={day => ( <Text style={time}> {moment(day.currentMessage.createdAt) .format(day.timeFormat)} </Text> )} renderInputToolbar={() => ( <InputField chatState={chatState} onSend={this.onSendClicked} openChat={this.openChat} /> )} onSend={messages => this.onSend(messages)} user={{ _id: "LifeTime", }} /> <View style={gap}/> </Animated.View> ); } } const styles = StyleSheet.create({ container: { backgroundColor: '#9dc284', position: 'absolute', left: 0, right: 0, width: '100%', zIndex: 999999 }, title: { color: 'white', fontSize: 16, padding: 14, }, slider: { alignSelf: 'center', marginTop: 24, width: width / 7, height: width / 11, }, gap: { height: 25, }, time: { color: 'white', width: '100%', textAlign: 'center', fontSize: 12, paddingVertical: 10, } }); LiveChat.propTypes = { license: PropTypes.string.isRequired, chats: PropTypes.array.isRequired, onLoadEarlier: PropTypes.func.isRequired }; LiveChat.defaultProps = { group: 0, chats: [] };