mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-23 15:47:53 -05:00
185 lines
6.6 KiB
JavaScript
185 lines
6.6 KiB
JavaScript
const eachLimit = require('async/eachLimit');
|
|
|
|
const api = require('../lib/api');
|
|
const log = require('../lib/log');
|
|
|
|
const COMMENT_LIMIT = 20;
|
|
const REPLY_LIMIT = 25; // Number of replies to fetch at a time
|
|
|
|
const {
|
|
addNewComment,
|
|
resetComments,
|
|
Status,
|
|
setFetchStatus,
|
|
setCommentDeleted,
|
|
setCommentReported,
|
|
setCommentRestored,
|
|
setMoreCommentsToLoad,
|
|
setComments,
|
|
setError,
|
|
setReplies,
|
|
setRepliesDeleted,
|
|
setRepliesRestored
|
|
} = require('../redux/comments.js');
|
|
|
|
const getReplies = (projectId, commentIds, offset, ownerUsername, isAdmin, token) => (dispatch => {
|
|
dispatch(setFetchStatus('replies', Status.FETCHING));
|
|
const fetchedReplies = {};
|
|
eachLimit(commentIds, 10, (parentId, callback) => {
|
|
api({
|
|
uri: `${isAdmin ? '/admin' : `/users/${ownerUsername}`}/projects/${projectId}/comments/${parentId}/replies`,
|
|
authentication: token ? token : null,
|
|
params: {offset: offset || 0, limit: REPLY_LIMIT}
|
|
}, (err, body, res) => {
|
|
if (err) {
|
|
return callback(`Error fetching comment replies: ${err}`);
|
|
}
|
|
if (typeof body === 'undefined' || res.statusCode >= 400) { // NotFound
|
|
return callback('No comment reply information');
|
|
}
|
|
fetchedReplies[parentId] = body;
|
|
callback(null, body);
|
|
});
|
|
}, err => {
|
|
if (err) {
|
|
dispatch(setFetchStatus('replies', Status.ERROR));
|
|
dispatch(setError(err));
|
|
return;
|
|
}
|
|
dispatch(setFetchStatus('replies', Status.FETCHED));
|
|
dispatch(setReplies(fetchedReplies));
|
|
});
|
|
});
|
|
|
|
const getTopLevelComments = (id, offset, ownerUsername, isAdmin, token) => (dispatch => {
|
|
dispatch(setFetchStatus('comments', Status.FETCHING));
|
|
api({
|
|
uri: `${isAdmin ? '/admin' : `/users/${ownerUsername}`}/projects/${id}/comments`,
|
|
authentication: token ? token : null,
|
|
params: {offset: offset || 0, limit: COMMENT_LIMIT}
|
|
}, (err, body, res) => {
|
|
if (err) {
|
|
dispatch(setFetchStatus('comments', Status.ERROR));
|
|
dispatch(setError(err));
|
|
return;
|
|
}
|
|
if (typeof body === 'undefined' || res.statusCode >= 400) { // NotFound
|
|
dispatch(setFetchStatus('comments', Status.ERROR));
|
|
dispatch(setError('No comment info'));
|
|
return;
|
|
}
|
|
dispatch(setFetchStatus('comments', Status.FETCHED));
|
|
dispatch(setComments(body));
|
|
const commentsWithReplies = body.filter(comment => comment.reply_count > 0);
|
|
if (commentsWithReplies.length > 0) {
|
|
dispatch(getReplies(id, commentsWithReplies.map(comment => comment.id), 0, ownerUsername, 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(setMoreCommentsToLoad(body.length === COMMENT_LIMIT));
|
|
});
|
|
});
|
|
|
|
const getCommentById = (projectId, commentId, ownerUsername, isAdmin, token) => (dispatch => {
|
|
dispatch(setFetchStatus('comments', Status.FETCHING));
|
|
api({
|
|
uri: `${isAdmin ? '/admin' : `/users/${ownerUsername}`}/projects/${projectId}/comments/${commentId}`,
|
|
authentication: token ? token : null
|
|
}, (err, body, res) => {
|
|
if (err) {
|
|
dispatch(setFetchStatus('comments', Status.ERROR));
|
|
dispatch(setError(err));
|
|
return;
|
|
}
|
|
if (!body || res.statusCode >= 400) { // NotFound
|
|
dispatch(setFetchStatus('comments', Status.ERROR));
|
|
dispatch(setError('No comment info'));
|
|
return;
|
|
}
|
|
|
|
if (body.parent_id) {
|
|
// If the comment is a reply, load the parent
|
|
return dispatch(getCommentById(projectId, body.parent_id, ownerUsername, isAdmin, token));
|
|
}
|
|
|
|
// If the comment is not a reply, show it as top level and load replies
|
|
dispatch(setFetchStatus('comments', Status.FETCHED));
|
|
dispatch(setComments([body]));
|
|
if (body.reply_count > 0) {
|
|
dispatch(getReplies(projectId, [body.id], 0, ownerUsername, isAdmin, token));
|
|
}
|
|
});
|
|
});
|
|
|
|
const deleteComment = (projectId, commentId, topLevelCommentId, token) => (dispatch => {
|
|
/* TODO fetching/fetched/error states updates for comment deleting */
|
|
api({
|
|
uri: `/proxy/comments/project/${projectId}/comment/${commentId}`,
|
|
authentication: token,
|
|
withCredentials: true,
|
|
method: 'DELETE',
|
|
useCsrf: true
|
|
}, (err, body, res) => {
|
|
if (err || res.statusCode !== 200) {
|
|
log.error(err || res.body);
|
|
return;
|
|
}
|
|
dispatch(setCommentDeleted(commentId, topLevelCommentId));
|
|
if (!topLevelCommentId) {
|
|
dispatch(setRepliesDeleted(commentId));
|
|
}
|
|
});
|
|
});
|
|
|
|
const reportComment = (projectId, commentId, topLevelCommentId, token) => (dispatch => {
|
|
api({
|
|
uri: `/proxy/project/${projectId}/comment/${commentId}/report`,
|
|
authentication: token,
|
|
withCredentials: true,
|
|
method: 'POST',
|
|
useCsrf: true
|
|
}, (err, body, res) => {
|
|
if (err || res.statusCode !== 200) {
|
|
log.error(err || res.body);
|
|
return;
|
|
}
|
|
// TODO use the reportId in the response for unreporting functionality
|
|
dispatch(setCommentReported(commentId, topLevelCommentId));
|
|
});
|
|
});
|
|
|
|
const restoreComment = (projectId, commentId, topLevelCommentId, token) => (dispatch => {
|
|
api({
|
|
uri: `/proxy/admin/project/${projectId}/comment/${commentId}/undelete`,
|
|
authentication: token,
|
|
withCredentials: true,
|
|
method: 'PUT',
|
|
useCsrf: true
|
|
}, (err, body, res) => {
|
|
if (err || res.statusCode !== 200) {
|
|
log.error(err || res.body);
|
|
return;
|
|
}
|
|
dispatch(setCommentRestored(commentId, topLevelCommentId));
|
|
if (!topLevelCommentId) {
|
|
dispatch(setRepliesRestored(commentId));
|
|
}
|
|
});
|
|
});
|
|
|
|
module.exports = {
|
|
getTopLevelComments,
|
|
getCommentById,
|
|
getReplies,
|
|
deleteComment,
|
|
reportComment,
|
|
restoreComment,
|
|
|
|
// Re-export these specific action creators directly so the implementer
|
|
// does not need to go to two places for comment actions
|
|
addNewComment,
|
|
resetComments
|
|
};
|