Skip to content

Commit

Permalink
Implement time-travel comment highlighting
Browse files Browse the repository at this point in the history
Closes #62
  • Loading branch information
insin committed May 16, 2020
1 parent 8d7f64d commit 5edf9b6
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 0 deletions.
32 changes: 32 additions & 0 deletions src/Item.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var ItemMixin = require('./mixins/ItemMixin').default

var cx = require('./utils/buildClassName').default
var setTitle = require('./utils/setTitle').default
var pluralise = require('./utils/pluralise').default

function timeUnitsAgo(value, unit, suffix) {
if (value === 1) {
Expand Down Expand Up @@ -125,6 +126,21 @@ var Item = React.createClass({
this.forceUpdate()
},

getButtonLabel() {
var showCommentsAfter = this.state.showNewCommentsAfter || this.threadStore.commentCount - 1
var howMany = this.threadStore.commentCount - showCommentsAfter
var timeComment = this.threadStore.getCommentByTimeIndex(showCommentsAfter + 1)
var text = `highlight ${howMany} comment${pluralise(howMany)} from `
return <span>
{text}<TimeAgo date={new Date(timeComment.time * 1000)}/>
</span>
},

highlightRecentComments() {
var showCommentsAfter = this.state.showNewCommentsAfter || this.threadStore.commentCount - 1
this.threadStore.highlightNewCommentsSince(showCommentsAfter)
},

render() {
var state = this.state
var item = state.item
Expand All @@ -142,6 +158,22 @@ var Item = React.createClass({
mark as read
</span>
</span>))}
{!threadStore.loading && threadStore.commentCount > 1 && <div style={{marginTop: '1em'}}>
<input
max={threadStore.commentCount - 1}
min={1}
style={{margin: 0, verticalAlign: 'middle'}}
type="range"
value={state.showNewCommentsAfter || threadStore.commentCount - 1}
onChange={(e) => {
var showNewCommentsAfter = Number(e.target.value)
this.setState({showNewCommentsAfter})
}}
/>
<button type="button" onClick={this.highlightRecentComments}>
{this.getButtonLabel()}
</button>
</div>}
{item.text && <div className="Item__text">
<div dangerouslySetInnerHTML={{__html: item.text}}/>
</div>}
Expand Down
20 changes: 20 additions & 0 deletions src/stores/CommentThreadStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ function CommentThreadStore(item, onCommentsChanged) {
this.itemId = item.id
this.onCommentsChanged = onCommentsChanged

/**
* Lookup from a comment id to the comment.
* @type {Object.<id,Comment>}
*/
this.comments = {}

/**
* Lookup from a comment id to its child comment ids.
* @type {Object.<id,Array.<Number>>}
Expand All @@ -22,6 +28,12 @@ function CommentThreadStore(item, onCommentsChanged) {
* @type {Object.<id,Boolean>}
*/
this.isCollapsed = {}

/**
* Lookup for dead comment ids
* @type {Object.<id,Boolean>}
*/
this.deadComments = {}
}

extend(CommentThreadStore.prototype, {
Expand Down Expand Up @@ -64,8 +76,12 @@ extend(CommentThreadStore.prototype, {
commentAdded(comment) {
if (comment.deleted) { return }

this.comments[comment.id] = comment
this.children[comment.id] = []
this.children[comment.parent].push(comment.id)
if (comment.dead) {
this.deadComments[comment.id] = true
}
},

/**
Expand All @@ -76,8 +92,12 @@ extend(CommentThreadStore.prototype, {
// deleted by the time the API catches up.
if (!comment) { return }

delete this.comments[comment.id]
var siblings = this.children[comment.parent]
siblings.splice(siblings.indexOf(comment.id), 1)
if (comment.dead) {
delete this.deadComments[comment.id]
}
},

toggleCollapse(commentId) {
Expand Down
36 changes: 36 additions & 0 deletions src/stores/StoryCommentThreadStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,42 @@ StoryCommentThreadStore.prototype = extend(Object.create(CommentThreadStore.prot
this.onCommentsChanged({type: 'collapse'})
},

getCommentByTimeIndex(timeIndex) {
var sortedCommentIds = Object.keys(this.comments).map(id => Number(id))
if (!SettingsStore.showDead) {
sortedCommentIds = sortedCommentIds.filter(id => !this.deadComments[id])
}
sortedCommentIds.sort()
var commentId = sortedCommentIds[timeIndex - 1]
return this.comments[commentId]
},

highlightNewCommentsSince(showCommentsAfter) {
var referenceComment = this.getCommentByTimeIndex(showCommentsAfter)

// Walk the tree of comments and create a new isNew lookup for comments
// newer than the reference comment we're using for highlighting.
var isNew = {}
var commentIds = this.children[this.itemId]
while (commentIds.length) {
var nextCommentIds = []
for (var i = 0, l = commentIds.length; i < l; i++) {
var commentId = commentIds[i]
if (commentId > referenceComment.id) {
isNew[commentId] = true
}
var childCommentIds = this.children[commentId]
if (childCommentIds.length) {
nextCommentIds.push.apply(nextCommentIds, childCommentIds)
}
}
commentIds = nextCommentIds
}

this.isNew = isNew
this.collapseThreadsWithoutNewComments()
},

/**
* Merk the thread as read.
*/
Expand Down

0 comments on commit 5edf9b6

Please sign in to comment.