diff --git a/Comment.js b/Comment.js index 999736a..172206f 100755 --- a/Comment.js +++ b/Comment.js @@ -125,7 +125,7 @@ Comment.propTypes = { image: PropTypes.string, likeAction: PropTypes.func, liked: PropTypes.bool, - likesNr: PropTypes.int, + likesNr: PropTypes.number, likesTapAction: PropTypes.func, replyAction: PropTypes.func, reportAction: PropTypes.func, diff --git a/index.js b/index.js index 297511b..efabd79 100755 --- a/index.js +++ b/index.js @@ -7,22 +7,43 @@ import { Text, Image, FlatList, + Modal, + Dimensions, ActivityIndicator, + TextInput, TouchableHighlight, TouchableOpacity } from 'react-native' import PropTypes from 'prop-types' -import TimeAgo from 'react-native-timeago' import Icon from 'react-native-vector-icons/FontAwesome' import styles from './styles' import Collapsible from 'react-native-collapsible' -import * as commentActions from './ExampleActions' +import Comment from './Comment' -export default class Comment extends PureComponent { +const screen = Dimensions.get('screen') + +export default class Comments extends PureComponent { constructor (props) { super(props) + this.bookmark = null + this.props = props + this.state = { + loadingComments: props.data && props.data.length ? false : true, + likesModalVisible: false, + likesModalData: null, + editModalVisible: false, + commentsLastUpdated: null, + expanded: [], + pagination: [] + } + this.newCommentText = null + this.replyCommentText = null + this.editCommentText = null + this.editingComment = null + this.textInputs = [] + this.renderComment = this.renderComment.bind(this) this.handleReport = this.handleReport.bind(this) this.handleReply = this.handleReply.bind(this) @@ -30,99 +51,440 @@ export default class Comment extends PureComponent { this.handleEdit = this.handleEdit.bind(this) this.handleUsernameTap = this.handleUsernameTap.bind(this) this.handleLikesTap = this.handleLikesTap.bind(this) + this.handleEditAction = this.handleEditAction.bind(this) + this.renderLike = this.renderLike.bind(this) + } + + setLikesModalVisible (visible) { + this.setState({likesModalVisible: visible}) + } + + setEditModalVisible (visible) { + this.setState({editModalVisible: visible}) + } + + componentWillReceiveProps (nextProps) { + if (nextProps.data) { + this.setState({ + commentsLastUpdated: new Date().getTime(), + loadingComments: false + }) + } + + } + + isExpanded (id) { + return this.state.expanded.indexOf(id) !== -1 + } + + toggleExpand (id) { + + let expanded = this.state.expanded + + let index = expanded.indexOf(id) + + if (index === -1) { + expanded.push(id) + } else { + + expanded.splice(index, 1) + + } + this.forceUpdate() + this.setState({expanded: expanded}) + + } + + handleReport (c) { + this.props.reportAction(c) + } + + handleReply (c) { + if (!this.props.isChild(c)) { + this.toggleExpand(this.props.keyExtractor(c)) + } else { + let input = this.textInputs['input' + this.props.parentIdExtractor(c)] + input.measure((x, y, width, height, pageX, pageY) => { + input.focus() + this.props.replyAction(pageY) + }) + } } - handleReport(){ - this.props.reportAction(this.props.data) + handleLike (c) { + this.props.likeAction(c) } - handleReply(){ - this.props.replyAction(this.props.data) + + handleEdit (c) { + this.editCommentText = this.props.bodyExtractor(c) + this.editingComment = c + this.setEditModalVisible(!this.state.editModalVisible) + } + + handleUsernameTap (username) { + this.props.usernameTapAction(username) + } + + handleLikesTap (c) { + this.setState({likesModalData: this.props.likesExtractor(c)}) + this.setLikesModalVisible(!this.state.likesModalVisible) + } + + handleEditAction (c) { + this.props.editAction(this.editCommentText, c) + } + + /** + * + * Generates a single comment + * */ + generateComment (c) { + return + } + + /** + * Renders comments children + * */ + renderChildren (items) { + if (!items || !items.length) return + let self = this + return items.map(function (c) { + + return + {self.generateComment(c)} + + + + }) + + } + + /** + * Returns last child id + * */ + getLastChildCommentId (item) { + if (!item) return + const items = item[this.props.childPropName] + return this.props.keyExtractor(items[items.length - 1]) + } + + /** + * Returns first child id + * */ + getFirstChildCommentId (item) { + if (!item) return + const items = item[this.props.childPropName] + + return this.props.keyExtractor(items[0]) } - handleLike(){ - this.props.likeAction(this.props.data) + + /** + * Does a pagination action + * */ + paginate (fromCommentId, direction, parentCommentId) { + this.setState({loadingComments: true}) + this.props.paginateAction(fromCommentId, direction, parentCommentId) + } - handleEdit(){ - this.props.editComment(this.props.data) + + /** + * Can user edit a comment + * */ + canUserEdit (item) { + if (this.props.viewingUserName == this.props.usernameExtractor(item)) { + if (!this.props.editMinuteLimit) return true + let created = new Date(this.props.createdTimeExtractor(item)).getTime() / 1000 + return new Date().getTime() / 1000 - created < this.props.editMinuteLimit * 60 + } + return false } - handleUsernameTap(){ - this.props.usernameTapAction(this.props.username) + + renderLike (l) { + let like = l.item + return {this.setLikesModalVisible(false), like.tap(like.name)}} + style={styles.likeButton} key={like.user_id}> + + + {like.name} + + } - handleLikesTap(){ - this.props.likesTapAction(this.props.data) + + /** + * Renders a comment with pagination + * */ + renderComment (c) { + + const item = c.item + return + {this.generateComment(item)} + + {item.childrenCount ? this.toggleExpand(this.props.keyExtractor(item))}> + + + {this.props.usernameExtractor(item.children[0])} + replied + * {this.props.childrenCountExtractor(item)} + {this.props.childrenCountExtractor(item) > 1 ? ' replies' : ' reply'} + + : null} + + {this.props.childrenCountExtractor(item) ? + {this.props.childrenCountExtractor(item) > item[this.props.childPropName].length + ? + this.paginate(this.getFirstChildCommentId(item), 'down', + this.props.keyExtractor(item)) + }> + Show + previous... + + : null} + + {this.renderChildren(item[this.props.childPropName], this.props.keyExtractor(item))} + + {this.props.childrenCountExtractor(item) > item[this.props.childPropName].length + ? this.paginate(this.getLastChildCommentId(item), 'up', + this.props.keyExtractor(item))}> + Show + more... + + : null} + : null} + + this.textInputs['input' + this.props.keyExtractor(item)] = input} + style={styles.input} + multiline={true} + onChangeText={(text => this.replyCommentText = text)} + placeholder={'Write comment'} + numberOfLines={3} + /> + { + this.props.saveAction( + this.replyCommentText, this.props.keyExtractor(item)) + this.replyCommentText = null + this.textInputs['input' + this.props.keyExtractor(item)].clear() + } + }> + + + + + + + + } render () { return ( - - - - - - {this.props.likesNr ? - - - {this.props.likesNr} - - : null} - + + + this.textInputs['inputMain'] = input} + multiline={true} + onChangeText={((text) => this.newCommentText = text)} + placeholder={'Write comment'} + numberOfLines={3} + /> + { + this.props.saveAction(this.newCommentText, false) + this.newCommentText = null + this.textInputs['inputMain'].clear() + + }}> + - - - - - {this.props.username} - - - {this.props.canEdit ? - - : null} + {!this.state.loadingComments && !this.props.data ? + No comments yet : null} + + + {!this.state.loadingComments && this.props.data ? { + this.paginate(this.props.keyExtractor(this.props.data[0]), 'down') + }}> + + Show previous - {this.props.body} - - - - - - Like + + : null} + {/*Comments*/} + {this.props.data + ? this.props.keyExtractor(item)} + renderItem={this.renderComment}/> + : null} + + {this.state.loadingComments ? + + : null} + + + {!this.state.loadingComments + && this.props.data + ? { + this.paginate(this.props.keyExtractor(this.props.data[this.props.data.length - 1]), 'up') + }}> + + Show more + + : null} + + + { + this.setLikesModalVisible(false) + + }}> + this.setLikesModalVisible(false)} + style={{ + position: "absolute", + width: 100, + zIndex: 9, + alignSelf: "flex-end", + top: 10 + }}> + + + + + Users that liked the comment + {this.state.likesModalVisible ? item.like_id} + data={this.state.likesModalData} + renderItem={this.renderLike} + /> : null} + + + {this.textInputs['editCommentInput'].focus()}} + transparent={true} + visible={this.state.editModalVisible} + onRequestClose={() => { + this.setEditModalVisible(false) + this.setState({editModalData: null}) + }}> + + + this.textInputs['editCommentInput'] = input} + style={styles.input} + multiline={true} + defaultValue={this.editCommentText} + onChangeText={(text => this.editCommentText = text)} + placeholder={'Edit comment'} + numberOfLines={3} + /> + + this.setEditModalVisible(false)}> + + Cancel + + + + { + this.props.editAction(this.editCommentText, this.editingComment) + this.setEditModalVisible(!this.state.editModalVisible) + }}> + + Save + + + - - - Reply - - - {this.props.reported ? Reported - : Report} - + + - + ) } } -Comment.propTypes = { - data: PropTypes.object, - body: PropTypes.string, - canEdit: PropTypes.bool, - child: PropTypes.bool, - editComment: PropTypes.func, - image: PropTypes.string, - likeAction: PropTypes.func, - liked: PropTypes.bool, - likesNr: PropTypes.numeric, - likesTapAction: PropTypes.func, - replyAction: PropTypes.func, - reportAction: PropTypes.func, - reported: PropTypes.bool, - updatedAt: PropTypes.string, - username: PropTypes.string, - usernameTapAction: PropTypes.func -} \ No newline at end of file +Comments.propTypes = { + data: PropTypes.array.isRequired, + viewingUserName: PropTypes.string, + initialDisplayCount: PropTypes.number, + editMinuteLimit: PropTypes.number, + usernameTapAction: PropTypes.func.isRequired, + childPropName: PropTypes.string.isRequired, + isChild: PropTypes.func.isRequired, + keyExtractor: PropTypes.func.isRequired, + parentIdExtractor: PropTypes.func.isRequired, + usernameExtractor: PropTypes.func.isRequired, + editTimeExtractor: PropTypes.func.isRequired, + createdTimeExtractor: PropTypes.func.isRequired, + bodyExtractor: PropTypes.func.isRequired, + imageExtractor: PropTypes.func.isRequired, + likeExtractor: PropTypes.func.isRequired, + reportedExtractor: PropTypes.func.isRequired, + likesExtractor: PropTypes.func.isRequired, + childrenCountExtractor: PropTypes.func.isRequired, + replyAction: PropTypes.func.isRequired, + saveAction: PropTypes.func.isRequired, + editAction: PropTypes.func.isRequired, + reportAction: PropTypes.func.isRequired, + likeAction: PropTypes.func.isRequired, + paginateAction: PropTypes.func.isRequired +} + +