diff --git a/client/coral-embed-stream/src/tabs/stream/components/ChangedUsername.js b/client/coral-embed-stream/src/tabs/stream/components/ChangedUsername.js new file mode 100644 index 000000000..3a0ba5790 --- /dev/null +++ b/client/coral-embed-stream/src/tabs/stream/components/ChangedUsername.js @@ -0,0 +1,17 @@ +import React, { Component } from 'react'; +import t from 'coral-framework/services/i18n'; +import RestrictedMessageBox from 'coral-framework/components/RestrictedMessageBox'; + +class ChangeUsername extends Component { + render() { + return ( + +
+ {t('framework.changed_name.msg')} +
+
+ ); + } +} + +export default ChangeUsername; diff --git a/client/coral-embed-stream/src/tabs/stream/components/Stream.js b/client/coral-embed-stream/src/tabs/stream/components/Stream.js index 6288a2b6b..65020cb08 100644 --- a/client/coral-embed-stream/src/tabs/stream/components/Stream.js +++ b/client/coral-embed-stream/src/tabs/stream/components/Stream.js @@ -25,6 +25,7 @@ import AllCommentsPane from './AllCommentsPane'; import ExtendableTabPanel from '../../../containers/ExtendableTabPanel'; import styles from './Stream.css'; +import ChangedUsername from './ChangedUsername'; class Stream extends React.Component { constructor(props) { @@ -237,6 +238,7 @@ class Stream extends React.Component { const banned = get(user, 'status.banned.status'); const suspensionUntil = get(user, 'status.suspension.until'); const rejectedUsername = get(user, 'status.username.status') === 'REJECTED'; + const changedUsername = get(user, 'status.username.status') === 'CHANGED'; const temporarilySuspended = user && suspensionUntil && new Date(suspensionUntil) > new Date(); @@ -246,6 +248,7 @@ class Stream extends React.Component { ((!banned && !temporarilySuspended && !rejectedUsername && + !changedUsername && !highlightedComment) || keepCommentBox); const slotProps = { data }; @@ -285,6 +288,7 @@ class Stream extends React.Component { )} )} + {changedUsername && } {!banned && rejectedUsername && } {banned && } {showCommentBox && ( diff --git a/locales/en.yml b/locales/en.yml index efcd7a1d9..eb472b373 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -254,6 +254,8 @@ en: error: "Usernames can contain letters numbers and _ only" label: "New Username" msg: "Your account is currently suspended because your username has been deemed inappropriate. To restore your account please enter a new username. Please contact us if you have any questions." + changed_name: + msg: "Your username change is under review by our moderation team." my_comments: "My Comments" my_profile: "My profile" new_count: "View {0} more {1}" diff --git a/perms/reducers/mutation.js b/perms/reducers/mutation.js index 3e73afbc9..73ee7ef28 100644 --- a/perms/reducers/mutation.js +++ b/perms/reducers/mutation.js @@ -15,7 +15,9 @@ module.exports = (user, perm) => { case types.EDIT_COMMENT: // Anyone can do these things if they aren't suspended, banned, or blocked // as they're editing their username. - return !['UNSET', 'REJECTED'].includes(user.status.username.status); + return !['UNSET', 'REJECTED', 'CHANGED'].includes( + user.status.username.status + ); case types.ADD_COMMENT_TAG: case types.REMOVE_COMMENT_TAG: diff --git a/test/e2e/specs/04_userStatus.js b/test/e2e/specs/04_userStatus.js index b63b051bb..8d659221d 100644 --- a/test/e2e/specs/04_userStatus.js +++ b/test/e2e/specs/04_userStatus.js @@ -15,13 +15,13 @@ module.exports = { client.end(); }, - 'admin logs in': client => { + 'Admin logs in': client => { const adminPage = client.page.admin(); const { testData: { admin } } = client.globals; adminPage.navigateAndLogin(admin); }, - 'admin flags users username as offensive': client => { + 'Admin flags users username as offensive': client => { const embedStream = client.page.embedStream(); const comments = embedStream.navigate().ready(); @@ -42,7 +42,7 @@ module.exports = { .waitForElementVisible('@popUpText') .click('@continueButton'); }, - 'admin goes to Reported Usernames': client => { + 'Admin goes to Reported Usernames': client => { const adminPage = client.page.admin(); const community = adminPage @@ -54,14 +54,14 @@ module.exports = { .waitForElementVisible('@flaggedAccountsContainer') .waitForElementVisible('@flaggedUser'); }, - 'admin rejects the user flag': client => { + 'Admin rejects the user flag': client => { const community = client.page.admin().section.community; community .waitForElementVisible('@flaggedUserRejectButton') .click('@flaggedUserRejectButton'); }, - 'admin suspends the user': client => { + 'Admin suspends the user': client => { const community = client.page.admin().section.community; const usernameDialog = client.page.admin().section.usernameDialog; @@ -76,7 +76,7 @@ module.exports = { community.waitForElementNotPresent('@flaggedUser'); }, - 'admin logs out': client => { + 'Admin logs out': client => { client.page.admin().logout(); }, 'user logs in': client => { @@ -114,6 +114,48 @@ module.exports = { .click('@changeUsernameSubmitButton') .waitForElementNotPresent('@changeUsernameInput'); }, + 'user should not be able to comment still': client => { + const embedStream = client.page.embedStream(); + const comments = embedStream.section.comments; + + comments + .waitForElementNotPresent('@commentBoxTextarea') + .waitForElementNotPresent('@commentBoxPostButton'); + }, + 'user logs out': client => { + const embedStream = client.page.embedStream(); + const comments = embedStream.section.comments; + + comments.logout(); + }, + 'Admin accepts the user flag': client => { + const adminPage = client.page.admin(); + const { testData: { admin } } = client.globals; + + adminPage.navigateAndLogin(admin); + + const community = adminPage + .navigate() + .ready() + .goToCommunity(); + + community + .waitForElementVisible('@flaggedAccountsContainer') + .waitForElementVisible('@flaggedUser') + .waitForElementVisible('@flaggedUserApproveButton') + .click('@flaggedUserApproveButton'); + + client.page.admin().logout(); + }, + 'user logs in to check comment': client => { + const { testData: { user } } = client.globals; + const embedStream = client.page.embedStream(); + + embedStream + .navigate() + .ready() + .openLoginPopup(popup => popup.login(user)); + }, 'user should be able to comment': client => { const embedStream = client.page.embedStream(); const comments = embedStream.section.comments; diff --git a/test/server/graph/mutations/createComment.js b/test/server/graph/mutations/createComment.js index 720297e12..058b19a41 100644 --- a/test/server/graph/mutations/createComment.js +++ b/test/server/graph/mutations/createComment.js @@ -270,6 +270,49 @@ describe('graph.mutations.createComment', () => { }); }); + describe('user with different username statuses', () => { + beforeEach(() => AssetModel.create({ id: '123' })); + + [ + { status: 'UNSET', error: true }, + { status: 'SET', error: false }, + { status: 'APPROVED', error: false }, + { status: 'REJECTED', error: true }, + { status: 'CHANGED', error: true }, + ].forEach(({ status, error }) => { + describe(`user.status.username.status=${status}`, () => { + it(`${error ? 'can not' : 'can'} create a comment`, async () => { + const context = new Context({ + user: new UserModel({ status: { username: { status } } }), + }); + + const { data, errors } = await graphql(schema, query, {}, context); + + if (errors) { + console.error(errors); + } + expect(errors).to.be.undefined; + + if (error) { + expect(data.createComment).to.have.property('errors').not.null; + expect(data.createComment).to.have.property('comment').null; + expect(data.createComment.errors).to.have.length(1); + expect(data.createComment.errors[0]).to.have.property( + 'translation_key', + 'NOT_AUTHORIZED' + ); + } else { + if (data.createComment.errors) { + console.error(data.createComment.errors); + } + expect(data.createComment).to.have.property('errors').null; + expect(data.createComment).to.have.property('comment').not.null; + } + }); + }); + }); + }); + describe('users with different roles', () => { beforeEach(() => AssetModel.create({ id: '123' }));