mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-12-11 16:21:04 -05:00
Merge pull request #2660 from LLK/develop
[MASTER] Deploy reply chain fixes and remix fixes
This commit is contained in:
commit
ba4fcb9707
10 changed files with 145 additions and 49 deletions
|
@ -78,8 +78,10 @@
|
||||||
"lodash.defaultsdeep": "3.10.0",
|
"lodash.defaultsdeep": "3.10.0",
|
||||||
"lodash.isarray": "3.0.4",
|
"lodash.isarray": "3.0.4",
|
||||||
"lodash.merge": "3.3.2",
|
"lodash.merge": "3.3.2",
|
||||||
|
"lodash.mergewith": "4.6.1",
|
||||||
"lodash.omit": "3.1.0",
|
"lodash.omit": "3.1.0",
|
||||||
"lodash.range": "3.0.1",
|
"lodash.range": "3.0.1",
|
||||||
|
"lodash.uniqby": "4.7.0",
|
||||||
"minilog": "2.0.8",
|
"minilog": "2.0.8",
|
||||||
"node-dir": "0.1.16",
|
"node-dir": "0.1.16",
|
||||||
"node-sass": "4.6.1",
|
"node-sass": "4.6.1",
|
||||||
|
@ -100,7 +102,7 @@
|
||||||
"redux": "3.5.2",
|
"redux": "3.5.2",
|
||||||
"redux-thunk": "2.0.1",
|
"redux-thunk": "2.0.1",
|
||||||
"sass-loader": "6.0.6",
|
"sass-loader": "6.0.6",
|
||||||
"scratch-gui": "0.1.0-prerelease.20190109203727",
|
"scratch-gui": "0.1.0-prerelease.20190111141703",
|
||||||
"scratch-l10n": "latest",
|
"scratch-l10n": "latest",
|
||||||
"scratchr2_translations": "git://github.com/LLK/scratchr2_translations.git#master",
|
"scratchr2_translations": "git://github.com/LLK/scratchr2_translations.git#master",
|
||||||
"slick-carousel": "1.6.0",
|
"slick-carousel": "1.6.0",
|
||||||
|
|
|
@ -237,7 +237,7 @@
|
||||||
"comments.post": "Post",
|
"comments.post": "Post",
|
||||||
"comments.cancel": "Cancel",
|
"comments.cancel": "Cancel",
|
||||||
"comments.lengthWarning": "{remainingCharacters, plural, one {1 character left} other {{remainingCharacters} characters left}}",
|
"comments.lengthWarning": "{remainingCharacters, plural, one {1 character left} other {{remainingCharacters} characters left}}",
|
||||||
"comments.seeMoreReplies": "{repliesCount, plural, one {See 1 more reply} other {See all {repliesCount} replies}}",
|
"comments.loadMoreReplies": "See more replies",
|
||||||
"comments.status.delbyusr": "Deleted by project owner",
|
"comments.status.delbyusr": "Deleted by project owner",
|
||||||
"comments.status.censbyfilter": "Censored by filter",
|
"comments.status.censbyfilter": "Censored by filter",
|
||||||
"comments.status.delbyparentcomment": "Parent comment deleted",
|
"comments.status.delbyparentcomment": "Parent comment deleted",
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
const defaults = require('lodash.defaults');
|
const defaults = require('lodash.defaults');
|
||||||
const keyMirror = require('keymirror');
|
const keyMirror = require('keymirror');
|
||||||
const async = require('async');
|
const async = require('async');
|
||||||
const merge = require('lodash.merge');
|
const mergeWith = require('lodash.mergewith');
|
||||||
|
const uniqBy = require('lodash.uniqby');
|
||||||
|
|
||||||
const api = require('../lib/api');
|
const api = require('../lib/api');
|
||||||
const log = require('../lib/log');
|
const log = require('../lib/log');
|
||||||
|
|
||||||
|
const COMMENT_LIMIT = 20;
|
||||||
|
|
||||||
module.exports.Status = keyMirror({
|
module.exports.Status = keyMirror({
|
||||||
FETCHED: null,
|
FETCHED: null,
|
||||||
NOT_FETCHED: null,
|
NOT_FETCHED: null,
|
||||||
|
@ -100,7 +103,7 @@ module.exports.previewReducer = (state, action) => {
|
||||||
});
|
});
|
||||||
case 'SET_COMMENTS':
|
case 'SET_COMMENTS':
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
comments: [...state.comments, ...action.items] // TODO: consider a different way of doing this?
|
comments: uniqBy(state.comments.concat(action.items), 'id')
|
||||||
});
|
});
|
||||||
case 'UPDATE_COMMENT':
|
case 'UPDATE_COMMENT':
|
||||||
if (action.topLevelCommentId) {
|
if (action.topLevelCommentId) {
|
||||||
|
@ -149,7 +152,19 @@ module.exports.previewReducer = (state, action) => {
|
||||||
});
|
});
|
||||||
case 'SET_REPLIES':
|
case 'SET_REPLIES':
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
replies: merge({}, state.replies, action.replies)
|
// Append new replies to the state.replies structure
|
||||||
|
replies: mergeWith({}, state.replies, action.replies, (replies, newReplies) => (
|
||||||
|
uniqBy((replies || []).concat(newReplies || []), 'id')
|
||||||
|
)),
|
||||||
|
// Also set the `moreRepliesToLoad` property on the top-level comments
|
||||||
|
comments: state.comments.map(comment => {
|
||||||
|
if (action.replies[comment.id]) {
|
||||||
|
return Object.assign({}, comment, {
|
||||||
|
moreRepliesToLoad: action.replies[comment.id].length === COMMENT_LIMIT
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return comment;
|
||||||
|
})
|
||||||
});
|
});
|
||||||
case 'SET_LOVED':
|
case 'SET_LOVED':
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
|
@ -448,7 +463,6 @@ module.exports.getFavedStatus = (id, username, token) => (dispatch => {
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports.getTopLevelComments = (id, offset, isAdmin, token) => (dispatch => {
|
module.exports.getTopLevelComments = (id, offset, isAdmin, token) => (dispatch => {
|
||||||
const COMMENT_LIMIT = 20;
|
|
||||||
dispatch(module.exports.setFetchStatus('comments', module.exports.Status.FETCHING));
|
dispatch(module.exports.setFetchStatus('comments', module.exports.Status.FETCHING));
|
||||||
api({
|
api({
|
||||||
uri: `${isAdmin ? '/admin' : ''}/projects/${id}/comments`,
|
uri: `${isAdmin ? '/admin' : ''}/projects/${id}/comments`,
|
||||||
|
@ -467,7 +481,7 @@ module.exports.getTopLevelComments = (id, offset, isAdmin, token) => (dispatch =
|
||||||
}
|
}
|
||||||
dispatch(module.exports.setFetchStatus('comments', module.exports.Status.FETCHED));
|
dispatch(module.exports.setFetchStatus('comments', module.exports.Status.FETCHED));
|
||||||
dispatch(module.exports.setComments(body));
|
dispatch(module.exports.setComments(body));
|
||||||
dispatch(module.exports.getReplies(id, body.map(comment => comment.id), isAdmin, token));
|
dispatch(module.exports.getReplies(id, body.map(comment => comment.id), 0, isAdmin, token));
|
||||||
|
|
||||||
// If we loaded a full page of comments, assume there are more to load.
|
// 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
|
// This will be wrong (1 / COMMENT_LIMIT) of the time, but does not require
|
||||||
|
@ -503,17 +517,18 @@ module.exports.getCommentById = (projectId, commentId, isAdmin, token) => (dispa
|
||||||
// If the comment is not a reply, show it as top level and load replies
|
// If the comment is not a reply, show it as top level and load replies
|
||||||
dispatch(module.exports.setFetchStatus('comments', module.exports.Status.FETCHED));
|
dispatch(module.exports.setFetchStatus('comments', module.exports.Status.FETCHED));
|
||||||
dispatch(module.exports.setComments([body]));
|
dispatch(module.exports.setComments([body]));
|
||||||
dispatch(module.exports.getReplies(projectId, [body.id], isAdmin, token));
|
dispatch(module.exports.getReplies(projectId, [body.id], 0, isAdmin, token));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports.getReplies = (projectId, commentIds, isAdmin, token) => (dispatch => {
|
module.exports.getReplies = (projectId, commentIds, offset, isAdmin, token) => (dispatch => {
|
||||||
dispatch(module.exports.setFetchStatus('replies', module.exports.Status.FETCHING));
|
dispatch(module.exports.setFetchStatus('replies', module.exports.Status.FETCHING));
|
||||||
const fetchedReplies = {};
|
const fetchedReplies = {};
|
||||||
async.eachLimit(commentIds, 10, (parentId, callback) => {
|
async.eachLimit(commentIds, 10, (parentId, callback) => {
|
||||||
api({
|
api({
|
||||||
uri: `${isAdmin ? '/admin' : ''}/projects/${projectId}/comments/${parentId}/replies`,
|
uri: `${isAdmin ? '/admin' : ''}/projects/${projectId}/comments/${parentId}/replies`,
|
||||||
authentication: isAdmin ? token : null
|
authentication: isAdmin ? token : null,
|
||||||
|
params: {offset: offset || 0, limit: COMMENT_LIMIT}
|
||||||
}, (err, body) => {
|
}, (err, body) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(`Error fetching comment replies: ${err}`);
|
return callback(`Error fetching comment replies: ${err}`);
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
"faq.requirementsDesktopSafari":"Safari (11+)",
|
"faq.requirementsDesktopSafari":"Safari (11+)",
|
||||||
"faq.requirementsDesktopIE":"Internet Explorer is NOT supported.",
|
"faq.requirementsDesktopIE":"Internet Explorer is NOT supported.",
|
||||||
"faq.requirementsTablet":"Tablet",
|
"faq.requirementsTablet":"Tablet",
|
||||||
"faq.requirementsTabletChrome":"Mobile Chrome (62+)",
|
"faq.requirementsTabletChrome":"Mobile Chrome (63+)",
|
||||||
"faq.requirementsTabletSafari":"Mobile Safari (11+)",
|
"faq.requirementsTabletSafari":"Mobile Safari (11+)",
|
||||||
"faq.requirementsNote":"Note:",
|
"faq.requirementsNote":"Note:",
|
||||||
"faq.requirementsNoteDesktop":"If your computer doesn’t meet these requirements, you can try the <a href=\"/download\">Scratch Desktop</a> editor (see next item in FAQ). ",
|
"faq.requirementsNoteDesktop":"If your computer doesn’t meet these requirements, you can try the <a href=\"/download\">Scratch Desktop</a> editor (see next item in FAQ). ",
|
||||||
|
|
|
@ -235,22 +235,20 @@
|
||||||
|
|
||||||
.replies {
|
.replies {
|
||||||
width: calc(100% - 4rem);
|
width: calc(100% - 4rem);
|
||||||
|
}
|
||||||
|
|
||||||
&.collapsed .comment {
|
.replies.collapsed > .comment:last-of-type {
|
||||||
&:last-child {
|
&:after {
|
||||||
&:after {
|
position: absolute;
|
||||||
position: absolute;
|
bottom: 0;
|
||||||
bottom: 0;
|
background: linear-gradient(
|
||||||
background: linear-gradient(
|
$ui-light-primary-transparent,
|
||||||
$ui-light-primary-transparent,
|
$ui-light-primary
|
||||||
$ui-light-primary
|
);
|
||||||
);
|
width: 100%;
|
||||||
width: 100%;
|
height: 100%;
|
||||||
height: 100%;
|
content: "";
|
||||||
content: "";
|
pointer-events: none;
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,9 +28,11 @@ class TopLevelComment extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleExpandThread () {
|
handleExpandThread () {
|
||||||
this.setState({
|
if (this.state.expanded) {
|
||||||
expanded: true
|
this.props.onLoadMoreReplies(this.props.id, this.props.replies.length);
|
||||||
});
|
} else {
|
||||||
|
this.setState({expanded: true});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteReply (replyId) {
|
handleDeleteReply (replyId) {
|
||||||
|
@ -79,6 +81,7 @@ class TopLevelComment extends React.Component {
|
||||||
datetimeCreated,
|
datetimeCreated,
|
||||||
highlightedCommentId,
|
highlightedCommentId,
|
||||||
id,
|
id,
|
||||||
|
moreRepliesToLoad,
|
||||||
onDelete,
|
onDelete,
|
||||||
onReport,
|
onReport,
|
||||||
onRestore,
|
onRestore,
|
||||||
|
@ -141,21 +144,17 @@ class TopLevelComment extends React.Component {
|
||||||
onRestore={this.handleRestoreReply}
|
onRestore={this.handleRestoreReply}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
{((!this.state.expanded && replies.length > 3) ||
|
||||||
|
(this.state.expanded && moreRepliesToLoad)) &&
|
||||||
|
<a
|
||||||
|
className="expand-thread"
|
||||||
|
onClick={this.handleExpandThread}
|
||||||
|
>
|
||||||
|
<FormattedMessage id="comments.loadMoreReplies" />
|
||||||
|
</a>
|
||||||
|
}
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
}
|
}
|
||||||
{!this.state.expanded && replies.length > 3 &&
|
|
||||||
<a
|
|
||||||
className="expand-thread"
|
|
||||||
onClick={this.handleExpandThread}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id="comments.seeMoreReplies"
|
|
||||||
values={{
|
|
||||||
repliesCount: replies.length
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -178,8 +177,10 @@ TopLevelComment.propTypes = {
|
||||||
deletable: PropTypes.bool,
|
deletable: PropTypes.bool,
|
||||||
highlightedCommentId: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]),
|
highlightedCommentId: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]),
|
||||||
id: PropTypes.number,
|
id: PropTypes.number,
|
||||||
|
moreRepliesToLoad: PropTypes.bool,
|
||||||
onAddComment: PropTypes.func,
|
onAddComment: PropTypes.func,
|
||||||
onDelete: PropTypes.func,
|
onDelete: PropTypes.func,
|
||||||
|
onLoadMoreReplies: PropTypes.func,
|
||||||
onReport: PropTypes.func,
|
onReport: PropTypes.func,
|
||||||
onRestore: PropTypes.func,
|
onRestore: PropTypes.func,
|
||||||
parentId: PropTypes.number,
|
parentId: PropTypes.number,
|
||||||
|
@ -189,7 +190,8 @@ TopLevelComment.propTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
TopLevelComment.defaultProps = {
|
TopLevelComment.defaultProps = {
|
||||||
defaultExpanded: false
|
defaultExpanded: false,
|
||||||
|
moreRepliesToLoad: false
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = TopLevelComment;
|
module.exports = TopLevelComment;
|
||||||
|
|
|
@ -70,6 +70,7 @@ const PreviewPresentation = ({
|
||||||
isFullScreen,
|
isFullScreen,
|
||||||
isLoggedIn,
|
isLoggedIn,
|
||||||
isNewScratcher,
|
isNewScratcher,
|
||||||
|
isProjectLoaded,
|
||||||
isRemixing,
|
isRemixing,
|
||||||
isScratcher,
|
isScratcher,
|
||||||
isShared,
|
isShared,
|
||||||
|
@ -88,8 +89,10 @@ const PreviewPresentation = ({
|
||||||
onFavoriteClicked,
|
onFavoriteClicked,
|
||||||
onGreenFlag,
|
onGreenFlag,
|
||||||
onLoadMore,
|
onLoadMore,
|
||||||
|
onLoadMoreReplies,
|
||||||
onLoveClicked,
|
onLoveClicked,
|
||||||
onOpenAdminPanel,
|
onOpenAdminPanel,
|
||||||
|
onProjectLoaded,
|
||||||
onRemix,
|
onRemix,
|
||||||
onRemixing,
|
onRemixing,
|
||||||
onReportClicked,
|
onReportClicked,
|
||||||
|
@ -250,10 +253,11 @@ const PreviewPresentation = ({
|
||||||
className={classNames([
|
className={classNames([
|
||||||
'remix-button',
|
'remix-button',
|
||||||
{
|
{
|
||||||
remixing: isRemixing,
|
disabled: isRemixing || !isProjectLoaded,
|
||||||
spin: isRemixing
|
remixing: isRemixing
|
||||||
}
|
}
|
||||||
])}
|
])}
|
||||||
|
disabled={isRemixing || !isProjectLoaded}
|
||||||
title={intl.formatMessage({id: 'project.remixButton.altText'})}
|
title={intl.formatMessage({id: 'project.remixButton.altText'})}
|
||||||
onClick={onRemix}
|
onClick={onRemix}
|
||||||
>
|
>
|
||||||
|
@ -301,6 +305,7 @@ const PreviewPresentation = ({
|
||||||
projectHost={projectHost}
|
projectHost={projectHost}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
onGreenFlag={onGreenFlag}
|
onGreenFlag={onGreenFlag}
|
||||||
|
onProjectLoaded={onProjectLoaded}
|
||||||
onRemixing={onRemixing}
|
onRemixing={onRemixing}
|
||||||
onUpdateProjectId={onUpdateProjectId}
|
onUpdateProjectId={onUpdateProjectId}
|
||||||
onUpdateProjectThumbnail={onUpdateProjectThumbnail}
|
onUpdateProjectThumbnail={onUpdateProjectThumbnail}
|
||||||
|
@ -550,12 +555,14 @@ const PreviewPresentation = ({
|
||||||
highlightedCommentId={singleCommentId}
|
highlightedCommentId={singleCommentId}
|
||||||
id={comment.id}
|
id={comment.id}
|
||||||
key={comment.id}
|
key={comment.id}
|
||||||
|
moreRepliesToLoad={comment.moreRepliesToLoad}
|
||||||
parentId={comment.parent_id}
|
parentId={comment.parent_id}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
replies={replies && replies[comment.id] ? replies[comment.id] : []}
|
replies={replies && replies[comment.id] ? replies[comment.id] : []}
|
||||||
visibility={comment.visibility}
|
visibility={comment.visibility}
|
||||||
onAddComment={onAddComment}
|
onAddComment={onAddComment}
|
||||||
onDelete={onDeleteComment}
|
onDelete={onDeleteComment}
|
||||||
|
onLoadMoreReplies={onLoadMoreReplies}
|
||||||
onReport={onReportComment}
|
onReport={onReportComment}
|
||||||
onRestore={onRestoreComment}
|
onRestore={onRestoreComment}
|
||||||
/>
|
/>
|
||||||
|
@ -623,6 +630,7 @@ PreviewPresentation.propTypes = {
|
||||||
isFullScreen: PropTypes.bool,
|
isFullScreen: PropTypes.bool,
|
||||||
isLoggedIn: PropTypes.bool,
|
isLoggedIn: PropTypes.bool,
|
||||||
isNewScratcher: PropTypes.bool,
|
isNewScratcher: PropTypes.bool,
|
||||||
|
isProjectLoaded: PropTypes.bool,
|
||||||
isRemixing: PropTypes.bool,
|
isRemixing: PropTypes.bool,
|
||||||
isScratcher: PropTypes.bool,
|
isScratcher: PropTypes.bool,
|
||||||
isShared: PropTypes.bool,
|
isShared: PropTypes.bool,
|
||||||
|
@ -644,8 +652,10 @@ PreviewPresentation.propTypes = {
|
||||||
onFavoriteClicked: PropTypes.func,
|
onFavoriteClicked: PropTypes.func,
|
||||||
onGreenFlag: PropTypes.func,
|
onGreenFlag: PropTypes.func,
|
||||||
onLoadMore: PropTypes.func,
|
onLoadMore: PropTypes.func,
|
||||||
|
onLoadMoreReplies: PropTypes.func,
|
||||||
onLoveClicked: PropTypes.func,
|
onLoveClicked: PropTypes.func,
|
||||||
onOpenAdminPanel: PropTypes.func,
|
onOpenAdminPanel: PropTypes.func,
|
||||||
|
onProjectLoaded: PropTypes.func,
|
||||||
onRemix: PropTypes.func,
|
onRemix: PropTypes.func,
|
||||||
onRemixing: PropTypes.func,
|
onRemixing: PropTypes.func,
|
||||||
onReportClicked: PropTypes.func.isRequired,
|
onReportClicked: PropTypes.func.isRequired,
|
||||||
|
|
|
@ -283,9 +283,11 @@ $stage-width: 480px;
|
||||||
background-image: url("/svgs/project/remix-white.svg");
|
background-image: url("/svgs/project/remix-white.svg");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.remix-button.disabled {
|
||||||
|
opacity: .6;
|
||||||
|
}
|
||||||
|
|
||||||
.remix-button.remixing {
|
.remix-button.remixing {
|
||||||
opacity: .6;
|
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
animation-name: remix-intro, remix-spin;
|
animation-name: remix-intro, remix-spin;
|
||||||
|
|
|
@ -60,6 +60,7 @@ class Preview extends React.Component {
|
||||||
'handleToggleStudio',
|
'handleToggleStudio',
|
||||||
'handleFavoriteToggle',
|
'handleFavoriteToggle',
|
||||||
'handleLoadMore',
|
'handleLoadMore',
|
||||||
|
'handleLoadMoreReplies',
|
||||||
'handleLoveToggle',
|
'handleLoveToggle',
|
||||||
'handleMessage',
|
'handleMessage',
|
||||||
'handlePopState',
|
'handlePopState',
|
||||||
|
@ -74,6 +75,7 @@ class Preview extends React.Component {
|
||||||
'handleAddToStudioClick',
|
'handleAddToStudioClick',
|
||||||
'handleAddToStudioClose',
|
'handleAddToStudioClose',
|
||||||
'handleGreenFlag',
|
'handleGreenFlag',
|
||||||
|
'handleProjectLoaded',
|
||||||
'handleRemix',
|
'handleRemix',
|
||||||
'handleSeeAllComments',
|
'handleSeeAllComments',
|
||||||
'handleSeeInside',
|
'handleSeeInside',
|
||||||
|
@ -108,6 +110,7 @@ class Preview extends React.Component {
|
||||||
clientLoved: false,
|
clientLoved: false,
|
||||||
extensions: [],
|
extensions: [],
|
||||||
favoriteCount: 0,
|
favoriteCount: 0,
|
||||||
|
isProjectLoaded: false,
|
||||||
isRemixing: false,
|
isRemixing: false,
|
||||||
invalidProject: parts.length === 1,
|
invalidProject: parts.length === 1,
|
||||||
justRemixed: false,
|
justRemixed: false,
|
||||||
|
@ -382,6 +385,11 @@ class Preview extends React.Component {
|
||||||
this.props.setFullScreen(fullScreen);
|
this.props.setFullScreen(fullScreen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
handleProjectLoaded () {
|
||||||
|
// Currently project view only needs to know when the project becomes loaded. It
|
||||||
|
// does not currently handle (or need to handle) the case where a project becomes unloaded.
|
||||||
|
this.setState({isProjectLoaded: true});
|
||||||
|
}
|
||||||
pushHistory (push) {
|
pushHistory (push) {
|
||||||
// update URI to match mode
|
// update URI to match mode
|
||||||
const idPath = this.state.projectId ? `${this.state.projectId}/` : '';
|
const idPath = this.state.projectId ? `${this.state.projectId}/` : '';
|
||||||
|
@ -440,6 +448,11 @@ class Preview extends React.Component {
|
||||||
this.props.getTopLevelComments(this.state.projectId, this.props.comments.length,
|
this.props.getTopLevelComments(this.state.projectId, this.props.comments.length,
|
||||||
this.props.isAdmin, this.props.user && this.props.user.token);
|
this.props.isAdmin, this.props.user && this.props.user.token);
|
||||||
}
|
}
|
||||||
|
handleLoadMoreReplies (commentId, offset) {
|
||||||
|
this.props.getMoreReplies(this.state.projectId, commentId, offset,
|
||||||
|
this.props.isAdmin, this.props.user && this.props.user.token
|
||||||
|
);
|
||||||
|
}
|
||||||
handleLoveToggle () {
|
handleLoveToggle () {
|
||||||
if (!this.props.lovedLoaded) return;
|
if (!this.props.lovedLoaded) return;
|
||||||
|
|
||||||
|
@ -611,6 +624,7 @@ class Preview extends React.Component {
|
||||||
isFullScreen={this.state.isFullScreen}
|
isFullScreen={this.state.isFullScreen}
|
||||||
isLoggedIn={this.props.isLoggedIn}
|
isLoggedIn={this.props.isLoggedIn}
|
||||||
isNewScratcher={this.props.isNewScratcher}
|
isNewScratcher={this.props.isNewScratcher}
|
||||||
|
isProjectLoaded={this.state.isProjectLoaded}
|
||||||
isRemixing={this.state.isRemixing}
|
isRemixing={this.state.isRemixing}
|
||||||
isScratcher={this.props.isScratcher}
|
isScratcher={this.props.isScratcher}
|
||||||
isShared={this.props.isShared}
|
isShared={this.props.isShared}
|
||||||
|
@ -644,8 +658,10 @@ class Preview extends React.Component {
|
||||||
onFavoriteClicked={this.handleFavoriteToggle}
|
onFavoriteClicked={this.handleFavoriteToggle}
|
||||||
onGreenFlag={this.handleGreenFlag}
|
onGreenFlag={this.handleGreenFlag}
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
|
onLoadMoreReplies={this.handleLoadMoreReplies}
|
||||||
onLoveClicked={this.handleLoveToggle}
|
onLoveClicked={this.handleLoveToggle}
|
||||||
onOpenAdminPanel={this.handleOpenAdminPanel}
|
onOpenAdminPanel={this.handleOpenAdminPanel}
|
||||||
|
onProjectLoaded={this.handleProjectLoaded}
|
||||||
onRemix={this.handleRemix}
|
onRemix={this.handleRemix}
|
||||||
onRemixing={this.handleIsRemixing}
|
onRemixing={this.handleIsRemixing}
|
||||||
onReportClicked={this.handleReportClick}
|
onReportClicked={this.handleReportClick}
|
||||||
|
@ -691,6 +707,7 @@ class Preview extends React.Component {
|
||||||
onGreenFlag={this.handleGreenFlag}
|
onGreenFlag={this.handleGreenFlag}
|
||||||
onLogOut={this.props.handleLogOut}
|
onLogOut={this.props.handleLogOut}
|
||||||
onOpenRegistration={this.props.handleOpenRegistration}
|
onOpenRegistration={this.props.handleOpenRegistration}
|
||||||
|
onProjectLoaded={this.handleProjectLoaded}
|
||||||
onRemixing={this.handleIsRemixing}
|
onRemixing={this.handleIsRemixing}
|
||||||
onSetLanguage={this.handleSetLanguage}
|
onSetLanguage={this.handleSetLanguage}
|
||||||
onShare={this.handleShare}
|
onShare={this.handleShare}
|
||||||
|
@ -736,6 +753,7 @@ Preview.propTypes = {
|
||||||
getCuratedStudios: PropTypes.func.isRequired,
|
getCuratedStudios: PropTypes.func.isRequired,
|
||||||
getFavedStatus: PropTypes.func.isRequired,
|
getFavedStatus: PropTypes.func.isRequired,
|
||||||
getLovedStatus: PropTypes.func.isRequired,
|
getLovedStatus: PropTypes.func.isRequired,
|
||||||
|
getMoreReplies: PropTypes.func.isRequired,
|
||||||
getOriginalInfo: PropTypes.func.isRequired,
|
getOriginalInfo: PropTypes.func.isRequired,
|
||||||
getParentInfo: PropTypes.func.isRequired,
|
getParentInfo: PropTypes.func.isRequired,
|
||||||
getProjectInfo: PropTypes.func.isRequired,
|
getProjectInfo: PropTypes.func.isRequired,
|
||||||
|
@ -948,6 +966,9 @@ const mapDispatchToProps = dispatch => ({
|
||||||
getCommentById: (projectId, commentId, isAdmin, token) => {
|
getCommentById: (projectId, commentId, isAdmin, token) => {
|
||||||
dispatch(previewActions.getCommentById(projectId, commentId, isAdmin, token));
|
dispatch(previewActions.getCommentById(projectId, commentId, isAdmin, token));
|
||||||
},
|
},
|
||||||
|
getMoreReplies: (projectId, commentId, offset, isAdmin, token) => {
|
||||||
|
dispatch(previewActions.getReplies(projectId, [commentId], offset, isAdmin, token));
|
||||||
|
},
|
||||||
getFavedStatus: (id, username, token) => {
|
getFavedStatus: (id, username, token) => {
|
||||||
dispatch(previewActions.getFavedStatus(id, username, token));
|
dispatch(previewActions.getFavedStatus(id, username, token));
|
||||||
},
|
},
|
||||||
|
|
|
@ -59,9 +59,9 @@ tap.test('setComments', t => {
|
||||||
// Initial value
|
// Initial value
|
||||||
t.deepEqual(initialState.comments, []);
|
t.deepEqual(initialState.comments, []);
|
||||||
|
|
||||||
state = reducer(initialState, Preview.setComments([1, 2]));
|
state = reducer(initialState, Preview.setComments([{id: 1}, {id: 2}]));
|
||||||
state = reducer(state, Preview.setComments([3, 4]));
|
state = reducer(state, Preview.setComments([{id: 3}, {id: 4}]));
|
||||||
t.deepEqual(state.comments, [1, 2, 3, 4]);
|
t.deepEqual(state.comments, [{id: 1}, {id: 2}, {id: 3}, {id: 4}]);
|
||||||
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
@ -80,6 +80,13 @@ const commentState = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
tap.test('setComments, discards duplicates', t => {
|
||||||
|
state = reducer(commentState, Preview.setComments([{id: 'id1'}]));
|
||||||
|
// Does not increase the number of comments, still 3
|
||||||
|
t.equal(state.comments.length, 3);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
tap.test('setCommentDeleted, top level comment', t => {
|
tap.test('setCommentDeleted, top level comment', t => {
|
||||||
state = reducer(commentState, Preview.setCommentDeleted('id2'));
|
state = reducer(commentState, Preview.setCommentDeleted('id2'));
|
||||||
t.equal(state.comments[1].visibility, 'deleted');
|
t.equal(state.comments[1].visibility, 'deleted');
|
||||||
|
@ -128,3 +135,42 @@ tap.test('addNewComment, reply comment', t => {
|
||||||
t.equal(state.replies.id1[2].id, 'new comment');
|
t.equal(state.replies.id1[2].id, 'new comment');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
tap.test('setReplies', t => {
|
||||||
|
// setReplies should append new replies
|
||||||
|
state = reducer(commentState, Preview.setReplies({
|
||||||
|
id1: {id: 'id6'}
|
||||||
|
}));
|
||||||
|
t.equal(state.replies.id1[2].id, 'id6');
|
||||||
|
t.equal(state.comments[0].moreRepliesToLoad, false);
|
||||||
|
|
||||||
|
// setReplies should ignore duplicates, do the same as above again
|
||||||
|
t.equal(state.replies.id1.length, 3);
|
||||||
|
state = reducer(state, Preview.setReplies({id1: {id: 'id6'}}));
|
||||||
|
t.equal(state.replies.id1.length, 3);
|
||||||
|
|
||||||
|
// setReplies can add replies to a comment that didn't have any
|
||||||
|
state = reducer(state, Preview.setReplies({
|
||||||
|
id2: {id: 'id7'}
|
||||||
|
}));
|
||||||
|
t.equal(state.replies.id1.length, 3);
|
||||||
|
t.equal(state.replies.id2.length, 1);
|
||||||
|
t.equal(state.replies.id2[0].id, 'id7');
|
||||||
|
t.equal(state.comments[0].moreRepliesToLoad, false);
|
||||||
|
t.equal(state.comments[1].moreRepliesToLoad, false);
|
||||||
|
|
||||||
|
// Getting 20 (COMMENT_LIMIT) replies sets moreRepliesToLoad to true
|
||||||
|
state = reducer(state, Preview.setReplies({
|
||||||
|
id3: (new Array(20)).map((_, i) => ({id: `id${i + 1}`}))
|
||||||
|
}));
|
||||||
|
t.equal(state.comments[0].moreRepliesToLoad, false);
|
||||||
|
t.equal(state.comments[1].moreRepliesToLoad, false);
|
||||||
|
t.equal(state.comments[2].moreRepliesToLoad, true);
|
||||||
|
|
||||||
|
// Getting one more reply sets moreRepliesToLoad back to false
|
||||||
|
state = reducer(state, Preview.setReplies({
|
||||||
|
id3: {id: 'id21'}
|
||||||
|
}));
|
||||||
|
t.equal(state.comments[2].moreRepliesToLoad, false);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in a new issue