diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js index 86bd29c13..a7a0b5740 100644 --- a/client/coral-embed-stream/src/Embed.js +++ b/client/coral-embed-stream/src/Embed.js @@ -15,7 +15,7 @@ import {NEW_COMMENT_COUNT_POLL_INTERVAL} from 'coral-framework/constants/comment import {queryStream} from 'coral-framework/graphql/queries'; import {postComment, postFlag, postLike, postDontAgree, deleteAction, addCommentTag, removeCommentTag, ignoreUser} from 'coral-framework/graphql/mutations'; -import {editName, ignoreUserSuccess} from 'coral-framework/actions/user'; +import {editName} from 'coral-framework/actions/user'; import {updateCountCache, viewAllComments} from 'coral-framework/actions/asset'; import {notificationActions, authActions, assetActions, pym} from 'coral-framework'; @@ -117,15 +117,6 @@ class Embed extends Component { } } - ignoreUser = async ({id}) => { - const {ignoreUser, dispatch} = this.props; - await ignoreUser({id}); - - // dispatch ignoreUserSuccess so other reducers can know about the newly - // ignored user (e.g. to hide coments by that user) - dispatch(ignoreUserSuccess({id})); - } - render () { const {activeTab} = this.state; const {closedAt, countCache = {}} = this.props.asset; @@ -264,7 +255,7 @@ class Embed extends Component { postDontAgree={this.props.postDontAgree} addCommentTag={this.props.addCommentTag} removeCommentTag={this.props.removeCommentTag} - ignoreUser={this.ignoreUser} + ignoreUser={this.props.ignoreUser} loadMore={this.props.loadMore} deleteAction={this.props.deleteAction} showSignInDialog={this.props.showSignInDialog} diff --git a/client/coral-framework/actions/user.js b/client/coral-framework/actions/user.js index dd63cf215..3e80b718c 100644 --- a/client/coral-framework/actions/user.js +++ b/client/coral-framework/actions/user.js @@ -1,7 +1,6 @@ import {addNotification} from '../actions/notification'; import coralApi from '../helpers/response'; import * as actions from '../constants/auth'; -import {IGNORE_USER_SUCCESS} from '../constants/user'; import I18n from 'coral-framework/modules/i18n/i18n'; import translations from './../translations'; @@ -20,6 +19,3 @@ export const editName = (username) => (dispatch) => { dispatch(editUsernameFailure(lang.t(`error.${error.translation_key}`))); }); }; - -// a user was successfully ignored -export const ignoreUserSuccess = ({id}) => ({type: IGNORE_USER_SUCCESS, id}); diff --git a/client/coral-framework/constants/user.js b/client/coral-framework/constants/user.js index 66e58e930..1557a42c9 100644 --- a/client/coral-framework/constants/user.js +++ b/client/coral-framework/constants/user.js @@ -7,3 +7,4 @@ export const COMMENTS_BY_USER_FAILURE = 'COMMENTS_BY_USER_FAILURE'; export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS'; export const UPDATE_USERNAME = 'UPDATE_USERNAME'; export const IGNORE_USER_SUCCESS = 'IGNORE_USER_SUCCESS'; +export const STOP_IGNORING_USER_SUCCESS = 'STOP_IGNORING_USER_SUCCESS'; diff --git a/client/coral-framework/graphql/mutations/index.js b/client/coral-framework/graphql/mutations/index.js index 114303d2d..64a39d108 100644 --- a/client/coral-framework/graphql/mutations/index.js +++ b/client/coral-framework/graphql/mutations/index.js @@ -7,6 +7,9 @@ import DELETE_ACTION from './deleteAction.graphql'; import ADD_COMMENT_TAG from './addCommentTag.graphql'; import REMOVE_COMMENT_TAG from './removeCommentTag.graphql'; import IGNORE_USER from './ignoreUser.graphql'; +import STOP_IGNORING_USER from './stopIgnoringUser.graphql'; + +import MY_IGNORED_USERS from '../queries/myIgnoredUsers.graphql'; import commentView from '../fragments/commentView.graphql'; @@ -156,7 +159,24 @@ export const ignoreUser = graphql(IGNORE_USER, { return mutate({ variables: { id, - } + }, + refetchQueries: [{ + query: MY_IGNORED_USERS, + }] + }); + }}), +}); + +export const stopIgnoringUser = graphql(STOP_IGNORING_USER, { + props: ({mutate}) => ({ + stopIgnoringUser: ({id}) => { + return mutate({ + variables: { + id, + }, + refetchQueries: [{ + query: MY_IGNORED_USERS, + }] }); }}), }); diff --git a/client/coral-framework/graphql/mutations/stopIgnoringUser.graphql b/client/coral-framework/graphql/mutations/stopIgnoringUser.graphql new file mode 100644 index 000000000..042452ff5 --- /dev/null +++ b/client/coral-framework/graphql/mutations/stopIgnoringUser.graphql @@ -0,0 +1,7 @@ +mutation stopIgnoringUser ($id: ID!) { + stopIgnoringUser(id:$id) { + errors { + translation_key + } + } +} diff --git a/client/coral-framework/reducers/user.js b/client/coral-framework/reducers/user.js index 0c77b3f69..efa967cf0 100644 --- a/client/coral-framework/reducers/user.js +++ b/client/coral-framework/reducers/user.js @@ -39,9 +39,14 @@ export default function user (state = initialState, action) { return state.set('myAssets', action.assets); case actions.LOGOUT_SUCCESS: return initialState; - case actions.IGNORE_USER_SUCCESS: - return state.updateIn(['ignoredUsers'], i => i.add(action.id)); - default : - return state; + case 'APOLLO_MUTATION_RESULT': + switch (action.operationName) { + case 'ignoreUser': + return state.updateIn(['ignoredUsers'], i => i.add(action.variables.id)); + case 'stopIgnoringUser': + return state.updateIn(['ignoredUsers'], i => i.delete(action.variables.id)); + } + break; } + return state; } diff --git a/client/coral-settings/components/IgnoredUsers.js b/client/coral-settings/components/IgnoredUsers.js index 5719337d4..4bbf6cc50 100644 --- a/client/coral-settings/components/IgnoredUsers.js +++ b/client/coral-settings/components/IgnoredUsers.js @@ -1,10 +1,19 @@ -import React, {Component} from 'react'; +import React, {Component, PropTypes} from 'react'; import styles from './IgnoredUsers.css'; export class IgnoredUsers extends Component { + static propTypes = { + users: PropTypes.arrayOf(PropTypes.shape({ + username: PropTypes.string, + id: PropTypes.string, + })).isRequired, + + // accepts { id } + stopIgnoring: PropTypes.func.isRequired, + } render() { - const {users} = this.props; + const {users, stopIgnoring} = this.props; return (
{ @@ -15,10 +24,12 @@ export class IgnoredUsers extends Component {
{ users.map(({username, id}) => ( - +
{ username }
- Stop ignoring + stopIgnoring({id})} + className={styles.link}>Stop ignoring
)) diff --git a/client/coral-settings/containers/ProfileContainer.js b/client/coral-settings/containers/ProfileContainer.js index 480c9e1fe..2d756077d 100644 --- a/client/coral-settings/containers/ProfileContainer.js +++ b/client/coral-settings/containers/ProfileContainer.js @@ -4,6 +4,7 @@ import React, {Component} from 'react'; import I18n from 'coral-framework/modules/i18n/i18n'; import {myCommentHistory, myIgnoredUsers} from 'coral-framework/graphql/queries'; +import {stopIgnoringUser} from 'coral-framework/graphql/mutations'; import {link} from 'coral-framework/services/PymConnection'; import NotLoggedIn from '../components/NotLoggedIn'; @@ -31,7 +32,7 @@ class ProfileContainer extends Component { } render() { - const {loggedIn, asset, showSignInDialog, data, myIgnoredUsersData} = this.props; + const {loggedIn, asset, showSignInDialog, data, myIgnoredUsersData, stopIgnoringUser} = this.props; const {me} = this.props.data; if (!loggedIn || !me) { @@ -60,6 +61,7 @@ class ProfileContainer extends Component {

Ignored users

) @@ -100,4 +102,5 @@ export default compose( connect(mapStateToProps, mapDispatchToProps), myCommentHistory, myIgnoredUsers, + stopIgnoringUser, )(ProfileContainer); diff --git a/graph/mutators/user.js b/graph/mutators/user.js index 04235c217..fd888a402 100644 --- a/graph/mutators/user.js +++ b/graph/mutators/user.js @@ -17,12 +17,18 @@ const ignoreUser = async ({user}, userToIgnore) => { return await UsersService.ignoreUsers(user.id, [userToIgnore.id]); }; +const stopIgnoringUser = async ({user}, userToStopIgnoring) => { + console.log('stopIgnoringUser!!'); + return await UsersService.stopIgnoringUsers(user.id, [userToStopIgnoring.id]); +}; + module.exports = (context) => { let mutators = { User: { setUserStatus: () => Promise.reject(errors.ErrNotAuthorized), suspendUser: () => Promise.reject(errors.ErrNotAuthorized), ignoreUser: (action) => ignoreUser(context, action), + stopIgnoringUser: (action) => stopIgnoringUser(context, action), } }; diff --git a/graph/resolvers/root_mutation.js b/graph/resolvers/root_mutation.js index 0e763889f..38183bff6 100644 --- a/graph/resolvers/root_mutation.js +++ b/graph/resolvers/root_mutation.js @@ -26,6 +26,9 @@ const RootMutation = { ignoreUser(_, {id}, {mutators: {User}}) { return wrapResponse(null)(User.ignoreUser({id})); }, + stopIgnoringUser(_, {id}, {mutators: {User}}) { + return wrapResponse(null)(User.stopIgnoringUser({id})); + }, setCommentStatus(_, {id, status}, {mutators: {Comment}}) { return wrapResponse(null)(Comment.setCommentStatus({id, status})); }, diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index 86aafbb0e..87926a5da 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -712,6 +712,11 @@ type IgnoreUserResponse implements Response { errors: [UserError] } +# Response to stopIgnoringUser mutation +type StopIgnoringUserResponse implements Response { + # An array of errors relating to the mutation that occured. + errors: [UserError] +} # All mutations for the application are defined on this object. type RootMutation { @@ -748,6 +753,9 @@ type RootMutation { # Ignore comments by another user ignoreUser(id: ID!): IgnoreUserResponse + + # Stop Ignoring comments by another user + stopIgnoringUser(id: ID!): StopIgnoringUserResponse } ################################################################################ diff --git a/services/users.js b/services/users.js index 45b113f35..da3b23bc6 100644 --- a/services/users.js +++ b/services/users.js @@ -854,4 +854,20 @@ module.exports = class UsersService { } }); } + + /** + * Stop ignoring other users + * @param {String} userId the id of the user that is ignoring another users + * @param {String[]} usersToStopIgnoring Array of user IDs to stop ignoring + */ + static async stopIgnoringUsers(userId, usersToStopIgnoring) { + assert(Array.isArray(usersToStopIgnoring), 'usersToStopIgnoring is an array'); + assert(usersToStopIgnoring.every(u => typeof u === 'string'), 'usersToStopIgnoring is an array of string user IDs'); + await UserModel.update({id: userId}, { + $pullAll: { + ignoresUsers: usersToStopIgnoring + } + }); + console.log('Mongo wrote stopIgnoringUsers', usersToStopIgnoring); + } }; diff --git a/test/graph/mutations/ignoreUser.js b/test/graph/mutations/ignoreUser.js index 6ede39477..fc6035839 100644 --- a/test/graph/mutations/ignoreUser.js +++ b/test/graph/mutations/ignoreUser.js @@ -6,30 +6,30 @@ const Context = require('../../../graph/context'); const UsersService = require('../../../services/users'); const SettingsService = require('../../../services/settings'); +const ignoreUserMutation = ` + mutation ignoreUser ($id: ID!) { + ignoreUser(id:$id) { + errors { + translation_key + } + } + } +`; + +const getMyIgnoredUsersQuery = ` + query myIgnoredUsers { + myIgnoredUsers { + id, + username + } + } +`; + describe('graph.mutations.ignoreUser', () => { beforeEach(async () => { await SettingsService.init(); }); - const ignoreUserMutation = ` - mutation ignoreUser ($id: ID!) { - ignoreUser(id:$id) { - errors { - translation_key - } - } - } - `; - - const getMyIgnoredUsersQuery = ` - query myIgnoredUsers { - myIgnoredUsers { - id, - username - } - } - `; - // @TODO (bengo) - test a user can't ignore themselves it('users can ignoreUser', async () => { const user = await UsersService.createLocalUser('usernameA@example.com', 'password', 'usernameA'); @@ -54,3 +54,60 @@ describe('graph.mutations.ignoreUser', () => { }); }); + +describe('graph.mutations.stopIgnoringUser', () => { + beforeEach(async () => { + await SettingsService.init(); + }); + + it('users can stop ignoring another user they ignore', async () => { + + // We're going to ignore 2 users, + // then stopIgnoring 1 of them + // then assert myIgnoredUsers only lists the one remaining + const user = await UsersService.createLocalUser('usernameA@example.com', 'password', 'usernameA'); + const usersToIgnore = await Promise.all([ + UsersService.createLocalUser('usernameB@example.com', 'password', 'usernameB'), + UsersService.createLocalUser('usernameC@example.com', 'password', 'usernameC'), + ]); + const context = new Context({user}); + + // ignore two users + const ignoreUserResponses = await Promise.all(usersToIgnore.map(u => graphql(schema, ignoreUserMutation, {}, context, {id: u.id}))); + ignoreUserResponses.forEach(response => { + if (response.errors && response.errors.length) { + console.error(response.errors); + } + expect(response.errors).to.be.empty; + }); + + const stopIgnoringUserMutation = ` + mutation stopIgnoringUser ($id: ID!) { + stopIgnoringUser(id:$id) { + errors { + translation_key + } + } + } + `; + + // stop ignoring one user + const stopIgnoringUserResponse = await graphql(schema, stopIgnoringUserMutation, {}, context, {id: usersToIgnore[0].id}); + if (stopIgnoringUserResponse.errors && stopIgnoringUserResponse.errors.length) { + console.error(stopIgnoringUserResponse.errors); + } + expect(stopIgnoringUserResponse.errors).to.be.empty; + + // now check my ignored users + const myIgnoredUsersResponse = await graphql(schema, getMyIgnoredUsersQuery, {}, context, {}); + if (myIgnoredUsersResponse.errors && myIgnoredUsersResponse.errors.length) { + console.error(myIgnoredUsersResponse.errors); + } + expect(myIgnoredUsersResponse.errors).to.be.empty; + const myIgnoredUsers = myIgnoredUsersResponse.data.myIgnoredUsers; + expect(myIgnoredUsers.length).to.equal(1); + expect(myIgnoredUsers[0].id).to.equal(usersToIgnore[1].id); + expect(myIgnoredUsers[0].username).to.equal(usersToIgnore[1].username); + }); + +});