Do not rely on total comment number for load more functionality

Show the load more comments button any time the last comment page was filled to the requested limit. As noted in the comment, this heuristic will be wrong at most 5% of the time but the failure mode (showing load more which, when clicked doesn't load any more, just goes away) is very mild, and for the overwhelming majority of project views that happen on projects with many, many comments, this is very unlikely to ever be noticed. It obviously isn't a perfect solution, but I cannot think of another that does not need the server to do another query to find out the total number of visible comments, or to find out if there are more comments after the requested offset+limit.
This commit is contained in:
Paul Kaplan 2018-10-24 09:18:45 -04:00
parent 48e70307c8
commit ae626d5244
3 changed files with 26 additions and 4 deletions

View file

@ -37,7 +37,8 @@ module.exports.getInitialState = () => ({
parent: {},
projectStudios: [],
curatedStudios: [],
currentStudioIds: []
currentStudioIds: [],
moreCommentsToLoad: false
});
module.exports.previewReducer = (state, action) => {
@ -153,6 +154,10 @@ module.exports.previewReducer = (state, action) => {
state = JSON.parse(JSON.stringify(state));
state.status.studioRequests[action.studioId] = action.status;
return state;
case 'SET_MORE_COMMENTS_TO_LOAD':
return Object.assign({}, state, {
moreCommentsToLoad: action.moreCommentsToLoad
});
case 'ERROR':
log.error(action.error);
return state;
@ -291,6 +296,11 @@ module.exports.addNewComment = (comment, topLevelCommentId) => ({
topLevelCommentId: topLevelCommentId
});
module.exports.setMoreCommentsToLoad = moreCommentsToLoad => ({
type: 'SET_MORE_COMMENTS_TO_LOAD',
moreCommentsToLoad: moreCommentsToLoad
});
module.exports.getProjectInfo = (id, token) => (dispatch => {
const opts = {
uri: `/projects/${id}`
@ -377,11 +387,12 @@ module.exports.getFavedStatus = (id, username, token) => (dispatch => {
});
module.exports.getTopLevelComments = (id, offset, isAdmin, token) => (dispatch => {
const COMMENT_LIMIT = 20;
dispatch(module.exports.setFetchStatus('comments', module.exports.Status.FETCHING));
api({
uri: `${isAdmin ? '/admin' : ''}/comments/project/${id}`,
authentication: isAdmin ? token : null,
params: {offset: offset || 0}
params: {offset: offset || 0, limit: COMMENT_LIMIT}
}, (err, body) => {
if (err) {
dispatch(module.exports.setFetchStatus('comments', module.exports.Status.ERROR));
@ -396,6 +407,13 @@ module.exports.getTopLevelComments = (id, offset, isAdmin, token) => (dispatch =
dispatch(module.exports.setFetchStatus('comments', module.exports.Status.FETCHED));
dispatch(module.exports.setComments(body));
dispatch(module.exports.getReplies(id, body.map(comment => comment.id), isAdmin, token));
// If we loaded a full page of comments, assume there are more to load.
// This will be wrong (1 / COMMENT_LIMIT) of the time, but does not require
// any more server query complexity, so seems worth it. In the case of a project with
// number of comments divisible by the COMMENT_LIMIT, the load more button will be
// clickable, but upon clicking it will go away.
dispatch(module.exports.setMoreCommentsToLoad(body.length === COMMENT_LIMIT));
});
});

View file

@ -59,6 +59,7 @@ const PreviewPresentation = ({
isShared,
loved,
loveCount,
moreCommentsToLoad,
originalInfo,
parentInfo,
projectHost,
@ -89,7 +90,6 @@ const PreviewPresentation = ({
onUpdate
}) => {
const shareDate = ((projectInfo.history && projectInfo.history.shared)) ? projectInfo.history.shared : '';
const loadedCommentCount = comments.length + Object.keys(replies).reduce((acc, id) => acc + replies[id].length, 0);
return (
<div className="preview">
{!isShared && (
@ -381,7 +381,7 @@ const PreviewPresentation = ({
onRestore={onRestoreComment}
/>
))}
{loadedCommentCount < projectInfo.stats.comments &&
{moreCommentsToLoad &&
<Button
className="button load-more-button"
onClick={onLoadMore}
@ -426,6 +426,7 @@ PreviewPresentation.propTypes = {
isShared: PropTypes.bool,
loveCount: PropTypes.number,
loved: PropTypes.bool,
moreCommentsToLoad: PropTypes.bool,
onAddComment: PropTypes.func,
onAddToStudioClicked: PropTypes.func,
onAddToStudioClosed: PropTypes.func,

View file

@ -400,6 +400,7 @@ class Preview extends React.Component {
isShared={this.props.isShared}
loveCount={this.state.loveCount}
loved={this.props.loved}
moreCommentsToLoad={this.props.moreCommentsToLoad}
originalInfo={this.props.original}
parentInfo={this.props.parent}
projectHost={this.props.projectHost}
@ -501,6 +502,7 @@ Preview.propTypes = {
isLoggedIn: PropTypes.bool,
isShared: PropTypes.bool,
loved: PropTypes.bool,
moreCommentsToLoad: PropTypes.bool,
original: projectShape,
parent: projectShape,
playerMode: PropTypes.bool,
@ -579,6 +581,7 @@ const mapStateToProps = state => {
// if we don't have projectInfo, assume it's shared until we know otherwise
isShared: !projectInfoPresent || state.preview.projectInfo.is_published,
loved: state.preview.loved,
moreCommentsToLoad: state.preview.moreCommentsToLoad,
original: state.preview.original,
parent: state.preview.parent,
playerMode: state.scratchGui.mode.isPlayerOnly,