Full code of swipable Facebook’s comment modal

Spread the love

Full code of swipable Facebook’s comment modal, code explanation¬†http://reactnative.vn/create-swipable-modal-like-facebooks-comment-modal/

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import {
  Modal,
  View,
  StyleSheet,
  Animated,
  Dimensions,
  PanResponder,
  StatusBar,
  TouchableWithoutFeedback,
  Platform,
  Easing,
} from 'react-native';

import CommentList from './commentList';
import CommentBox from './commentBox';

const dismissKeyboard = require('dismissKeyboard');

const { height: screenHeight } = Dimensions.get('window');
const TIMING_CONFIG = { duration: 300, easing: Easing.inOut(Easing.ease) };
class CommentModal extends Component {
  constructor(props) {
    super(props);
    this.state = {
      text: '',
      height: 50,
      isSaving: false,
      canSendComment: false,
      pan: new Animated.ValueXY({ x: 0, y: screenHeight }),
      isAnimating: false,
    };

    this.handleOnClose = this.handleOnClose.bind(this);
  }

  componentWillMount() {
    if (!this) {
      return;
    }
    this.animatedValueX = 0;
    this.animatedValueY = 0;
    this.state.pan.x.addListener(value => (this.animatedValueX = value.value));
    this.state.pan.y.addListener(value => (this.animatedValueY = value.value));

    this.panResponder = PanResponder.create({
      // Ask to be the responder:
      onStartShouldSetPanResponder: () => false,
      onStartShouldSetPanResponderCapture: () => false,
      onMoveShouldSetPanResponder: (evt, gestureState) => {
        if (this.state.isAnimating) {
          return false;
        }
        if (gestureState.dy > 30 || gestureState.dy < -30) {
          return true;
        }
        return false;
      },
      onPanResponderGrant: () => {
        this.state.pan.setOffset({
          x: this.animatedValueX,
          y: this.animatedValueY,
        });
        this.state.pan.setValue({ x: 0, y: 0 }); // Initial value
      },
      onPanResponderMove: (evt, gestureState) => {
        this.setState({
          backgroundColor: 'transparent',
        });
        this.state.pan.setValue({ x: 0, y: gestureState.dy });
      },
      onPanResponderRelease: (evt, gestureState) => {
        // The user has released all touches while this view is the
        // responder. This typically means a gesture has succeeded
        // this.state.pan.flattenOffset(); // Flatten the offset so it resets the default positioning
        if (gestureState.vy <= -0.7 || gestureState.dy <= -300) {
          Animated.timing(this.state.pan, {
            toValue: { x: 0, y: -screenHeight },
            ...TIMING_CONFIG,
          }).start(() => {
            this.props.onClose();
          });
        } else if (gestureState.vy >= 0.5 || gestureState.dy >= 300) {
          Animated.timing(this.state.pan, {
            toValue: { x: 0, y: screenHeight },
            ...TIMING_CONFIG,
          }).start(() => {
            this.props.onClose();
          });
        } else {
          Animated.spring(this.state.pan, {
            toValue: 0,
          }).start();
          this.setState({
            backgroundColor: '#000',
          });
        }
      },
    });
  }

  componentWillReceiveProps(newProp) {
    if (newProp.modalVisible) {
      this.showCommentAnimation();
    }
  }

  showCommentAnimation() {
    setTimeout(() => {
      if (Platform.OS === 'android') {
        StatusBar.setBackgroundColor('#000');
      }
    }, 100);
    this.setState({
      isAnimating: true,
    });
    Animated.timing(this.state.pan, {
      ...TIMING_CONFIG,
      toValue: { x: 0, y: 0 },
    }).start(() => {
      this.setState({
        isAnimating: false,
      });
    });
  }

  handleOnClose() {
    dismissKeyboard();
    if (Platform.OS === 'android') {
      StatusBar.setBackgroundColor(this.props.appTheme.mainColor);
    }

    Animated.timing(this.state.pan, {
      ...TIMING_CONFIG,
      toValue: { x: 0, y: screenHeight },
    }).start(() => {
      this.props.onClose();
    });
  }

  handleOnChange(event) {
    this.setState({
      text: event.nativeEvent.text,
      height: event.nativeEvent.contentSize.height + 10,
    });

    if (event.nativeEvent.text) {
      this.setState({
        canSendComment: true,
      });
    } else {
      this.setState({
        canSendComment: false,
      });
    }
  }

  handleGetStyle(opacity) {
    return [
      styles.container,
      {
        transform: [...this.state.pan.getTranslateTransform()],
        opacity: opacity,
      },
    ];
  }

  render() {
    const body = <CommentList />;
    const interpolateBackgroundOpacity = this.state.pan.y.interpolate({
      inputRange: [-screenHeight, 0, screenHeight],
      outputRange: [0, 1, 0],
    });
    const { appTheme } = this.props;
    return (
      <Modal
        animationType="none"
        transparent
        visible={this.props.modalVisible}
        onRequestClose={() => {
          this.handleOnClose();
        }}
      >
        <Animated.View style={[styles.background, { opacity: interpolateBackgroundOpacity }]} />
        <Animated.View
          style={this.handleGetStyle(interpolateBackgroundOpacity)}
          {...this.panResponder.panHandlers}
        >
          <TouchableWithoutFeedback>
            <View style={{ flex: 1 }}>
              <View style={styles.body}>{body}</View>
              <View style={[styles.seperator, { backgroundColor: appTheme.mainColor }]} />
              <CommentBox isCommentModal isRoundCorner onClose={this.handleOnClose} />
            </View>
          </TouchableWithoutFeedback>
        </Animated.View>
      </Modal>
    );
  }
}

const styles = StyleSheet.create({
  loadingScreen: {
    flex: 1,
    marginLeft: 10,
    marginRight: 10,
  },
  background: {
    opacity: 0,
    flex: 1,
    backgroundColor: '#000',
  },
  container: {
    flex: 1,
    backgroundColor: '#fff',
    borderColor: '#000',
    borderRadius: 10,
    position: 'absolute',
    top: 0,
    right: 0,
    bottom: 0,
    left: 0,
  },
  body: {
    flex: 1,
    backgroundColor: '#fff',
    borderTopLeftRadius: 10,
    borderTopRightRadius: 10,
  },
  seperator: {
    backgroundColor: '#ddd',
    height: 0.5,
    marginLeft: 10,
    marginRight: 10,
  },
});

CommentModal.propTypes = {
  onClose: PropTypes.func.isRequired,
  appTheme: PropTypes.shape({
    mainColor: PropTypes.string,
  }).isRequired,
  modalVisible: PropTypes.bool.isRequired,
};

function mapStateToProps(state) {
  return {
    appTheme: state.appTheme,
  };
}

export default connect(mapStateToProps)(CommentModal);

Leave a Reply

Your email address will not be published. Required fields are marked *