mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-23 15:47:53 -05:00
Fix comment toggling and add permissions test
This commit is contained in:
parent
aab4c9aca6
commit
f9419ac8fc
7 changed files with 64 additions and 22 deletions
|
@ -191,7 +191,7 @@ const mutateStudioCommentsAllowed = shouldAllow => ((dispatch, getState) => {
|
||||||
api({
|
api({
|
||||||
host: '',
|
host: '',
|
||||||
uri: `/site-api/comments/gallery/${studioId}/toggle-comments/`,
|
uri: `/site-api/comments/gallery/${studioId}/toggle-comments/`,
|
||||||
method: 'PUT',
|
method: 'POST',
|
||||||
useCsrf: true
|
useCsrf: true
|
||||||
}, (err, body, res) => {
|
}, (err, body, res) => {
|
||||||
const error = normalizeError(err, body, res);
|
const error = normalizeError(err, body, res);
|
||||||
|
|
|
@ -25,6 +25,10 @@ const selectCanDeleteCommentWithoutConfirm = state => selectIsAdmin(state);
|
||||||
|
|
||||||
const selectCanFollowStudio = state => selectIsLoggedIn(state);
|
const selectCanFollowStudio = state => selectIsLoggedIn(state);
|
||||||
|
|
||||||
|
// Matching existing behavior, only the creator is allowed to toggle comments.
|
||||||
|
const selectCanEditCommentsAllowed = state => selectIsAdmin(state) || isCreator(state);
|
||||||
|
const selectCanEditOpenToAll = state => selectIsAdmin(state) || isManager(state);
|
||||||
|
|
||||||
export {
|
export {
|
||||||
selectCanEditInfo,
|
selectCanEditInfo,
|
||||||
selectCanAddProjects,
|
selectCanAddProjects,
|
||||||
|
@ -33,5 +37,7 @@ export {
|
||||||
selectCanDeleteComment,
|
selectCanDeleteComment,
|
||||||
selectCanDeleteCommentWithoutConfirm,
|
selectCanDeleteCommentWithoutConfirm,
|
||||||
selectCanReportComment,
|
selectCanReportComment,
|
||||||
selectCanRestoreComment
|
selectCanRestoreComment,
|
||||||
|
selectCanEditCommentsAllowed,
|
||||||
|
selectCanEditOpenToAll
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,13 +4,12 @@ import PropTypes from 'prop-types';
|
||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
|
|
||||||
import {selectStudioCommentsAllowed, selectIsLoadingInfo} from '../../redux/studio';
|
import {selectStudioCommentsAllowed, selectIsLoadingInfo} from '../../redux/studio';
|
||||||
import {selectCanEditInfo} from '../../redux/studio-permissions';
|
|
||||||
import {
|
import {
|
||||||
mutateStudioCommentsAllowed, selectIsMutatingCommentsAllowed, selectCommentsAllowedMutationError
|
mutateStudioCommentsAllowed, selectIsMutatingCommentsAllowed, selectCommentsAllowedMutationError
|
||||||
} from '../../redux/studio-mutations';
|
} from '../../redux/studio-mutations';
|
||||||
|
|
||||||
const StudioCommentsAllowed = ({
|
const StudioCommentsAllowed = ({
|
||||||
commentsAllowedError, isLoading, isMutating, commentsAllowed, canEditInfo, handleUpdate
|
commentsAllowedError, isLoading, isMutating, commentsAllowed, handleUpdate
|
||||||
}) => (
|
}) => (
|
||||||
<div>
|
<div>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
|
@ -19,12 +18,12 @@ const StudioCommentsAllowed = ({
|
||||||
<div>
|
<div>
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input
|
||||||
disabled={isMutating || !canEditInfo}
|
disabled={isMutating}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={commentsAllowed}
|
checked={commentsAllowed}
|
||||||
onChange={e => handleUpdate(e.target.checked)}
|
onChange={e => handleUpdate(e.target.checked)}
|
||||||
/>
|
/>
|
||||||
<h4>{commentsAllowed ? 'Comments allowed' : 'Comments not allowed'}</h4>
|
<span>{commentsAllowed ? 'Comments allowed' : 'Comments not allowed'}</span>
|
||||||
{commentsAllowedError && <div>Error mutating commentsAllowed: {commentsAllowedError}</div>}
|
{commentsAllowedError && <div>Error mutating commentsAllowed: {commentsAllowedError}</div>}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -34,7 +33,6 @@ const StudioCommentsAllowed = ({
|
||||||
|
|
||||||
StudioCommentsAllowed.propTypes = {
|
StudioCommentsAllowed.propTypes = {
|
||||||
commentsAllowedError: PropTypes.string,
|
commentsAllowedError: PropTypes.string,
|
||||||
canEditInfo: PropTypes.bool,
|
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
isMutating: PropTypes.bool,
|
isMutating: PropTypes.bool,
|
||||||
commentsAllowed: PropTypes.bool,
|
commentsAllowed: PropTypes.bool,
|
||||||
|
@ -44,7 +42,6 @@ StudioCommentsAllowed.propTypes = {
|
||||||
export default connect(
|
export default connect(
|
||||||
state => ({
|
state => ({
|
||||||
commentsAllowed: selectStudioCommentsAllowed(state),
|
commentsAllowed: selectStudioCommentsAllowed(state),
|
||||||
canEditInfo: selectCanEditInfo(state),
|
|
||||||
isLoading: selectIsLoadingInfo(state),
|
isLoading: selectIsLoadingInfo(state),
|
||||||
isMutating: selectIsMutatingCommentsAllowed(state),
|
isMutating: selectIsMutatingCommentsAllowed(state),
|
||||||
commentsAllowedError: selectCommentsAllowedMutationError(state)
|
commentsAllowedError: selectCommentsAllowedMutationError(state)
|
||||||
|
|
|
@ -14,11 +14,14 @@ import {
|
||||||
selectCanDeleteComment,
|
selectCanDeleteComment,
|
||||||
selectCanDeleteCommentWithoutConfirm,
|
selectCanDeleteCommentWithoutConfirm,
|
||||||
selectCanReportComment,
|
selectCanReportComment,
|
||||||
selectCanRestoreComment
|
selectCanRestoreComment,
|
||||||
|
selectCanEditCommentsAllowed
|
||||||
} from '../../redux/studio-permissions';
|
} from '../../redux/studio-permissions';
|
||||||
|
import {selectStudioCommentsAllowed} from '../../redux/studio.js';
|
||||||
|
|
||||||
const StudioComments = ({
|
const StudioComments = ({
|
||||||
comments,
|
comments,
|
||||||
|
commentsAllowed,
|
||||||
handleLoadMoreComments,
|
handleLoadMoreComments,
|
||||||
handleNewComment,
|
handleNewComment,
|
||||||
moreCommentsToLoad,
|
moreCommentsToLoad,
|
||||||
|
@ -27,6 +30,7 @@ const StudioComments = ({
|
||||||
shouldShowCommentComposer,
|
shouldShowCommentComposer,
|
||||||
canDeleteComment,
|
canDeleteComment,
|
||||||
canDeleteCommentWithoutConfirm,
|
canDeleteCommentWithoutConfirm,
|
||||||
|
canEditCommentsAllowed,
|
||||||
canReportComment,
|
canReportComment,
|
||||||
canRestoreComment,
|
canRestoreComment,
|
||||||
handleDeleteComment,
|
handleDeleteComment,
|
||||||
|
@ -41,9 +45,9 @@ const StudioComments = ({
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h2>Comments</h2>
|
<h2>Comments</h2>
|
||||||
<StudioCommentsAllowed />
|
{canEditCommentsAllowed && <StudioCommentsAllowed />}
|
||||||
<div>
|
<div>
|
||||||
{shouldShowCommentComposer &&
|
{shouldShowCommentComposer && commentsAllowed &&
|
||||||
<ComposeComment
|
<ComposeComment
|
||||||
postURI={postURI}
|
postURI={postURI}
|
||||||
onAddComment={handleNewComment}
|
onAddComment={handleNewComment}
|
||||||
|
@ -88,6 +92,7 @@ const StudioComments = ({
|
||||||
|
|
||||||
StudioComments.propTypes = {
|
StudioComments.propTypes = {
|
||||||
comments: PropTypes.arrayOf(PropTypes.shape({})),
|
comments: PropTypes.arrayOf(PropTypes.shape({})),
|
||||||
|
commentsAllowed: PropTypes.bool,
|
||||||
handleLoadMoreComments: PropTypes.func,
|
handleLoadMoreComments: PropTypes.func,
|
||||||
handleNewComment: PropTypes.func,
|
handleNewComment: PropTypes.func,
|
||||||
moreCommentsToLoad: PropTypes.bool,
|
moreCommentsToLoad: PropTypes.bool,
|
||||||
|
@ -95,6 +100,7 @@ StudioComments.propTypes = {
|
||||||
shouldShowCommentComposer: PropTypes.bool,
|
shouldShowCommentComposer: PropTypes.bool,
|
||||||
canDeleteComment: PropTypes.bool,
|
canDeleteComment: PropTypes.bool,
|
||||||
canDeleteCommentWithoutConfirm: PropTypes.bool,
|
canDeleteCommentWithoutConfirm: PropTypes.bool,
|
||||||
|
canEditCommentsAllowed: PropTypes.bool,
|
||||||
canReportComment: PropTypes.bool,
|
canReportComment: PropTypes.bool,
|
||||||
canRestoreComment: PropTypes.bool,
|
canRestoreComment: PropTypes.bool,
|
||||||
handleDeleteComment: PropTypes.func,
|
handleDeleteComment: PropTypes.func,
|
||||||
|
@ -109,9 +115,11 @@ export default connect(
|
||||||
comments: state.comments.comments,
|
comments: state.comments.comments,
|
||||||
moreCommentsToLoad: state.comments.moreCommentsToLoad,
|
moreCommentsToLoad: state.comments.moreCommentsToLoad,
|
||||||
replies: state.comments.replies,
|
replies: state.comments.replies,
|
||||||
|
commentsAllowed: selectStudioCommentsAllowed(state),
|
||||||
shouldShowCommentComposer: selectShowCommentComposer(state),
|
shouldShowCommentComposer: selectShowCommentComposer(state),
|
||||||
canDeleteComment: selectCanDeleteComment(state),
|
canDeleteComment: selectCanDeleteComment(state),
|
||||||
canDeleteCommentWithoutConfirm: selectCanDeleteCommentWithoutConfirm(state),
|
canDeleteCommentWithoutConfirm: selectCanDeleteCommentWithoutConfirm(state),
|
||||||
|
canEditCommentsAllowed: selectCanEditCommentsAllowed(state),
|
||||||
canReportComment: selectCanReportComment(state),
|
canReportComment: selectCanReportComment(state),
|
||||||
canRestoreComment: selectCanRestoreComment(state),
|
canRestoreComment: selectCanRestoreComment(state),
|
||||||
postURI: `/proxy/comments/studio/${state.studio.id}`
|
postURI: `/proxy/comments/studio/${state.studio.id}`
|
||||||
|
|
|
@ -4,13 +4,12 @@ import PropTypes from 'prop-types';
|
||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
|
|
||||||
import {selectStudioOpenToAll, selectIsLoadingInfo} from '../../redux/studio';
|
import {selectStudioOpenToAll, selectIsLoadingInfo} from '../../redux/studio';
|
||||||
import {selectCanEditInfo} from '../../redux/studio-permissions';
|
|
||||||
import {
|
import {
|
||||||
mutateStudioOpenToAll, selectIsMutatingOpenToAll, selectOpenToAllMutationError
|
mutateStudioOpenToAll, selectIsMutatingOpenToAll, selectOpenToAllMutationError
|
||||||
} from '../../redux/studio-mutations';
|
} from '../../redux/studio-mutations';
|
||||||
|
|
||||||
const StudioOpenToAll = ({
|
const StudioOpenToAll = ({
|
||||||
openToAllError, isLoading, isMutating, openToAll, canEditInfo, handleUpdate
|
openToAllError, isLoading, isMutating, openToAll, handleUpdate
|
||||||
}) => (
|
}) => (
|
||||||
<div>
|
<div>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
|
@ -19,12 +18,12 @@ const StudioOpenToAll = ({
|
||||||
<div>
|
<div>
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input
|
||||||
disabled={isMutating || !canEditInfo}
|
disabled={isMutating}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={openToAll}
|
checked={openToAll}
|
||||||
onChange={e => handleUpdate(e.target.checked)}
|
onChange={e => handleUpdate(e.target.checked)}
|
||||||
/>
|
/>
|
||||||
<h4>{openToAll ? 'Open to all' : 'Not open to all'}</h4>
|
<span>{openToAll ? 'Open to all' : 'Not open to all'}</span>
|
||||||
{openToAllError && <div>Error mutating openToAll: {openToAllError}</div>}
|
{openToAllError && <div>Error mutating openToAll: {openToAllError}</div>}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -34,7 +33,6 @@ const StudioOpenToAll = ({
|
||||||
|
|
||||||
StudioOpenToAll.propTypes = {
|
StudioOpenToAll.propTypes = {
|
||||||
openToAllError: PropTypes.string,
|
openToAllError: PropTypes.string,
|
||||||
canEditInfo: PropTypes.bool,
|
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
isMutating: PropTypes.bool,
|
isMutating: PropTypes.bool,
|
||||||
openToAll: PropTypes.bool,
|
openToAll: PropTypes.bool,
|
||||||
|
@ -44,7 +42,6 @@ StudioOpenToAll.propTypes = {
|
||||||
export default connect(
|
export default connect(
|
||||||
state => ({
|
state => ({
|
||||||
openToAll: selectStudioOpenToAll(state),
|
openToAll: selectStudioOpenToAll(state),
|
||||||
canEditInfo: selectCanEditInfo(state),
|
|
||||||
isLoading: selectIsLoadingInfo(state),
|
isLoading: selectIsLoadingInfo(state),
|
||||||
isMutating: selectIsMutatingOpenToAll(state),
|
isMutating: selectIsMutatingOpenToAll(state),
|
||||||
openToAllError: selectOpenToAllMutationError(state)
|
openToAllError: selectOpenToAllMutationError(state)
|
||||||
|
|
|
@ -6,13 +6,13 @@ import StudioOpenToAll from './studio-open-to-all.jsx';
|
||||||
|
|
||||||
import {projectFetcher} from './lib/fetchers';
|
import {projectFetcher} from './lib/fetchers';
|
||||||
import {projects} from './lib/redux-modules';
|
import {projects} from './lib/redux-modules';
|
||||||
import {selectCanAddProjects} from '../../redux/studio-permissions';
|
import {selectCanAddProjects, selectCanEditOpenToAll} from '../../redux/studio-permissions';
|
||||||
import Debug from './debug.jsx';
|
import Debug from './debug.jsx';
|
||||||
|
|
||||||
const {actions, selector: projectsSelector} = projects;
|
const {actions, selector: projectsSelector} = projects;
|
||||||
|
|
||||||
const StudioProjects = ({
|
const StudioProjects = ({
|
||||||
canAddProjects, items, error, loading, moreToLoad, onLoadMore
|
canAddProjects, canEditOpenToAll, items, error, loading, moreToLoad, onLoadMore
|
||||||
}) => {
|
}) => {
|
||||||
const {studioId} = useParams();
|
const {studioId} = useParams();
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ const StudioProjects = ({
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h2>Projects</h2>
|
<h2>Projects</h2>
|
||||||
<StudioOpenToAll />
|
{canEditOpenToAll && <StudioOpenToAll />}
|
||||||
{error && <Debug
|
{error && <Debug
|
||||||
label="Error"
|
label="Error"
|
||||||
data={error}
|
data={error}
|
||||||
|
@ -56,6 +56,7 @@ const StudioProjects = ({
|
||||||
|
|
||||||
StudioProjects.propTypes = {
|
StudioProjects.propTypes = {
|
||||||
canAddProjects: PropTypes.bool,
|
canAddProjects: PropTypes.bool,
|
||||||
|
canEditOpenToAll: PropTypes.bool,
|
||||||
items: PropTypes.array, // eslint-disable-line react/forbid-prop-types
|
items: PropTypes.array, // eslint-disable-line react/forbid-prop-types
|
||||||
loading: PropTypes.bool,
|
loading: PropTypes.bool,
|
||||||
error: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
error: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||||
|
@ -65,7 +66,8 @@ StudioProjects.propTypes = {
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
...projectsSelector(state),
|
...projectsSelector(state),
|
||||||
canAddProjects: selectCanAddProjects(state)
|
canAddProjects: selectCanAddProjects(state),
|
||||||
|
canEditOpenToAll: selectCanEditOpenToAll(state)
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
|
|
@ -6,7 +6,9 @@ import {
|
||||||
selectCanDeleteCommentWithoutConfirm,
|
selectCanDeleteCommentWithoutConfirm,
|
||||||
selectCanReportComment,
|
selectCanReportComment,
|
||||||
selectCanRestoreComment,
|
selectCanRestoreComment,
|
||||||
selectCanFollowStudio
|
selectCanFollowStudio,
|
||||||
|
selectCanEditCommentsAllowed,
|
||||||
|
selectCanEditOpenToAll
|
||||||
} from '../../../src/redux/studio-permissions';
|
} from '../../../src/redux/studio-permissions';
|
||||||
|
|
||||||
import {getInitialState as getInitialStudioState} from '../../../src/redux/studio';
|
import {getInitialState as getInitialStudioState} from '../../../src/redux/studio';
|
||||||
|
@ -176,4 +178,34 @@ describe('studio comments', () => {
|
||||||
expect(selectCanFollowStudio(state)).toBe(expected);
|
expect(selectCanFollowStudio(state)).toBe(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('can set "comments allowed" on a studio', () => {
|
||||||
|
test.each([
|
||||||
|
['admin', true],
|
||||||
|
['curator', false],
|
||||||
|
['manager', false],
|
||||||
|
['creator', true],
|
||||||
|
['logged in', false],
|
||||||
|
['unconfirmed', false],
|
||||||
|
['logged out', false]
|
||||||
|
])('%s: %s', (role, expected) => {
|
||||||
|
setStateByRole(role);
|
||||||
|
expect(selectCanEditCommentsAllowed(state)).toBe(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('can set "open to all" on a studio', () => {
|
||||||
|
test.each([
|
||||||
|
['admin', true],
|
||||||
|
['curator', false],
|
||||||
|
['manager', true],
|
||||||
|
['creator', true],
|
||||||
|
['logged in', false],
|
||||||
|
['unconfirmed', false],
|
||||||
|
['logged out', false]
|
||||||
|
])('%s: %s', (role, expected) => {
|
||||||
|
setStateByRole(role);
|
||||||
|
expect(selectCanEditOpenToAll(state)).toBe(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue