-
Notifications
You must be signed in to change notification settings - Fork 331
/
Comment.js
205 lines (185 loc) · 6.62 KB
/
Comment.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
var React = require('react')
var ReactFireMixin = require('reactfire')
var CommentThreadStore = require('./stores/CommentThreadStore')
// var HNService = require('./services/HNService')
var HNServiceRest = require('./services/HNServiceRest')
var SettingsStore = require('./stores/SettingsStore')
var CommentMixin = require('./mixins/CommentMixin')
var cx = require('./utils/buildClassName')
var resolve = require('react-resolver').resolve
/**
* A comment in a thread.
*/
var Comment = React.createClass({
mixins: [CommentMixin, ReactFireMixin],
propTypes: {
id: React.PropTypes.number.isRequired,
level: React.PropTypes.number.isRequired,
loadingSpinner: React.PropTypes.bool,
threadStore: React.PropTypes.instanceOf(CommentThreadStore).isRequired
},
getDefaultProps() {
return {
loadingSpinner: false
}
},
getInitialState() {
return {
comment: {}
}
},
componentWillMount() {
this.bindFirebaseRef()
},
componentWillUnmount() {
this.clearDelayTimeout()
},
componentDidUpdate(prevProps, prevState) {
// Huge, fast-growing threads like https://news.ycombinator.com/item?id=9784470
// seem to break the API - some comments are coming back from Firebase as null.
if (!this.state.comment) {
this.props.threadStore.adjustExpectedComments(-1)
return
}
// On !prevState.comment: a comment which was initially null - see
// above - may eventually load when the API catches up.
if (!prevState.comment || !prevState.comment.id) {
// Register a newly-loaded comment with the thread store
if (this.state.comment.id) {
// If the comment was delayed, cancel any pending timeout
if (prevState.comment && prevState.comment.delayed) {
this.clearDelayTimeout()
}
this.props.threadStore.commentAdded(this.state.comment)
}
if (prevState.comment && !prevState.comment.delayed && this.state.comment.delayed) {
this.props.threadStore.commentDelayed(this.props.id)
}
}
// The comment was already loaded, look for changes to it
else {
if (!prevState.comment.deleted && this.state.comment.deleted) {
this.props.threadStore.commentDeleted(this.state.comment)
}
if (!prevState.comment.dead && this.state.comment.dead) {
this.props.threadStore.commentDied(this.state.comment)
}
// If the comment has been updated and the initial set of comments is
// still loading, the number of expected comments might need to be
// adjusted.
else if (prevState.comment !== this.state.comment &&
this.props.threadStore.loading) {
var kids = (this.state.comment.kids ? this.state.comment.kids.length : 0)
var prevKids = (prevState.comment.kids ? prevState.comment.kids.length : 0)
this.props.threadStore.adjustExpectedComments(kids - prevKids)
}
}
},
bindFirebaseRef(props) {
console.log('bindFirebaseRef', props)
// if (SettingsStore.offlineMode) {
// HNServiceRest.itemRef(props.id).then(function(res) {
// return res.json()
// }).then(function(snapshot) {
// this.replaceState({ comment: snapshot })
// }.bind(this))
// }
// else {
// this.bindAsObject(HNService.itemRef(props.id), 'comment', this.handleFirebaseRefCancelled)
// }
// if (this.timeout) {
// this.timeout = null
// }
},
/**
* This is usually caused by a permissions error loading the comment due to
* its author using the delay setting (note: this is conjecture), which is
* measured in minutes - try again in 30 seconds.
*/
handleFirebaseRefCancelled(e) {
if (process.env.NODE_ENV !== 'production') {
console.error('Firebase ref for comment ' + this.props.id + ' was cancelled: ' + e.message)
}
this.unbind('comment')
this.timeout = setTimeout(this.bindFirebaseRef, 30000)
if (this.state.comment && !this.state.comment.delayed) {
this.state.comment.delayed = true
this.forceUpdate()
}
},
clearDelayTimeout() {
if (this.timeout) {
clearTimeout(this.timeout)
this.timeout = null
}
},
toggleCollapse(e) {
e.preventDefault()
this.props.threadStore.toggleCollapse(this.state.comment.id)
},
render() {
var comment = this.state.comment
var props = this.props
if (!comment) {
return this.renderError(comment, {
id: this.props.id,
className: 'Comment Comment--error Comment--level' + props.level
})
}
// Render a placeholder while we're waiting for the comment to load
if (!comment.id) { return this.renderCommentLoading(comment) }
// Don't show dead coments or their children, when configured
if (comment.dead && !SettingsStore.showDead) { return null }
// Render a link to HN for deleted comments if they're being displayed
if (comment.deleted) {
if (!SettingsStore.showDeleted) { return null }
return this.renderCommentDeleted(comment, {
className: 'Comment Comment--deleted Comment--level' + props.level
})
}
var isNew = props.threadStore.isNew[comment.id]
var collapsed = !!props.threadStore.isCollapsed[comment.id]
var childCounts = (collapsed && props.threadStore.getChildCounts(comment))
if (collapsed && isNew) { childCounts.newComments = 0 }
var className = cx('Comment Comment--level' + props.level, {
'Comment--collapsed': collapsed,
'Comment--dead': comment.dead,
'Comment--new': isNew
})
return <div className={className}>
<div className="Comment__content">
{this.renderCommentMeta(comment, {
collapsible: true,
collapsed: collapsed,
link: true,
childCounts: childCounts
})}
{this.renderCommentText(comment, {replyLink: true})}
</div>
{comment.kids && <div className="Comment__kids">
{comment.kids.map(function(id) {
return <Comment key={id} id={id}
level={props.level + 1}
loadingSpinner={props.loadingSpinner}
threadStore={props.threadStore}
/>
})}
</div>}
</div>
}
})
/*
What I'm attempting to do here is resolve comment so that we
go through react-resolver anytime we need that data instead of
directly through bindFirebaseRef per the resolver examples I have
seen. Data seems to get returned client-side, but nothing correctly
when doing the server-side render.
*/
module.exports = resolve('comment', function(props) {
return HNServiceRest.itemRef(props.id).then(function(res) {
return res.json()
}).then(function(snapshot) {
console.log('Comment snapshot:', snapshot)
return snapshot
})
})(Comment)