Merge pull request #2686 from Human-Connection/279-blacklist-users-content

feat: Convert block/unblock to mute/unmute
This commit is contained in:
mattwr18 2020-01-21 17:05:09 +01:00 committed by GitHub
commit 8fe6ad77e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 390 additions and 367 deletions

View File

@ -6,7 +6,6 @@ addons:
- libgconf-2-4
snaps:
- docker
- chromium
install:
- yarn global add wait-on

View File

@ -101,7 +101,7 @@ export default shield(
Badge: allow,
PostsEmotionsCountByEmotion: allow,
PostsEmotionsByCurrentUser: isAuthenticated,
blockedUsers: isAuthenticated,
mutedUsers: isAuthenticated,
notifications: isAuthenticated,
Donations: isAuthenticated,
},
@ -137,8 +137,8 @@ export default shield(
resetPassword: allow,
AddPostEmotions: isAuthenticated,
RemovePostEmotions: isAuthenticated,
block: isAuthenticated,
unblock: isAuthenticated,
muteUser: isAuthenticated,
unmuteUser: isAuthenticated,
markAsRead: isAuthenticated,
AddEmailAddress: isAuthenticated,
VerifyEmailAddress: isAuthenticated,

View File

@ -78,6 +78,12 @@ export default {
target: 'User',
direction: 'out',
},
muted: {
type: 'relationship',
relationship: 'MUTED',
target: 'User',
direction: 'out',
},
notifications: {
type: 'relationship',
relationship: 'NOTIFIED',

View File

@ -1,25 +0,0 @@
import { getBlockedUsers, getBlockedByUsers } from '../users.js'
import { mergeWith, isArray } from 'lodash'
export const filterForBlockedUsers = async (params, context) => {
if (!context.user) return params
const [blockedUsers, blockedByUsers] = await Promise.all([
getBlockedUsers(context),
getBlockedByUsers(context),
])
const blockedUsersIds = [...blockedByUsers.map(b => b.id), ...blockedUsers.map(b => b.id)]
if (!blockedUsersIds.length) return params
params.filter = mergeWith(
params.filter,
{
author_not: { id_in: blockedUsersIds },
},
(objValue, srcValue) => {
if (isArray(objValue)) {
return objValue.concat(srcValue)
}
},
)
return params
}

View File

@ -0,0 +1,22 @@
import { getMutedUsers } from '../users.js'
import { mergeWith, isArray } from 'lodash'
export const filterForMutedUsers = async (params, context) => {
if (!context.user) return params
const [mutedUsers] = await Promise.all([getMutedUsers(context)])
const mutedUsersIds = [...mutedUsers.map(user => user.id)]
if (!mutedUsersIds.length) return params
params.filter = mergeWith(
params.filter,
{
author_not: { id_in: mutedUsersIds },
},
(objValue, srcValue) => {
if (isArray(objValue)) {
return objValue.concat(srcValue)
}
},
)
return params
}

View File

@ -1,10 +1,10 @@
import uuid from 'uuid/v4'
import { neo4jgraphql } from 'neo4j-graphql-js'
import { isEmpty } from 'lodash'
import fileUpload from './fileUpload'
import { UserInputError } from 'apollo-server'
import fileUpload from './fileUpload'
import Resolver from './helpers/Resolver'
import { filterForBlockedUsers } from './helpers/filterForBlockedUsers'
import { filterForMutedUsers } from './helpers/filterForMutedUsers'
const maintainPinnedPosts = params => {
const pinnedPostFilter = { pinned: true }
@ -19,16 +19,16 @@ const maintainPinnedPosts = params => {
export default {
Query: {
Post: async (object, params, context, resolveInfo) => {
params = await filterForBlockedUsers(params, context)
params = await filterForMutedUsers(params, context)
params = await maintainPinnedPosts(params)
return neo4jgraphql(object, params, context, resolveInfo)
},
findPosts: async (object, params, context, resolveInfo) => {
params = await filterForBlockedUsers(params, context)
params = await filterForMutedUsers(params, context)
return neo4jgraphql(object, params, context, resolveInfo)
},
profilePagePosts: async (object, params, context, resolveInfo) => {
params = await filterForBlockedUsers(params, context)
params = await filterForMutedUsers(params, context)
return neo4jgraphql(object, params, context, resolveInfo)
},
PostsEmotionsCountByEmotion: async (object, params, context, resolveInfo) => {

View File

@ -8,42 +8,26 @@ import createOrUpdateLocations from './users/location'
const neode = getNeode()
export const getBlockedUsers = async context => {
export const getMutedUsers = async context => {
const { neode } = context
const userModel = neode.model('User')
let blockedUsers = neode
let mutedUsers = neode
.query()
.match('user', userModel)
.where('user.id', context.user.id)
.relationship(userModel.relationships().get('blocked'))
.to('blocked', userModel)
.return('blocked')
blockedUsers = await blockedUsers.execute()
blockedUsers = blockedUsers.records.map(r => r.get('blocked').properties)
return blockedUsers
}
export const getBlockedByUsers = async context => {
if (context.user.role === 'moderator' || context.user.role === 'admin') return []
const { neode } = context
const userModel = neode.model('User')
let blockedByUsers = neode
.query()
.match('user', userModel)
.relationship(userModel.relationships().get('blocked'))
.to('blocked', userModel)
.where('blocked.id', context.user.id)
.return('user')
blockedByUsers = await blockedByUsers.execute()
blockedByUsers = blockedByUsers.records.map(r => r.get('user').properties)
return blockedByUsers
.relationship(userModel.relationships().get('muted'))
.to('muted', userModel)
.return('muted')
mutedUsers = await mutedUsers.execute()
mutedUsers = mutedUsers.records.map(r => r.get('muted').properties)
return mutedUsers
}
export default {
Query: {
blockedUsers: async (object, args, context, resolveInfo) => {
mutedUsers: async (object, args, context, resolveInfo) => {
try {
return getBlockedUsers(context)
return getMutedUsers(context)
} catch (e) {
throw new UserInputError(e.message)
}
@ -72,6 +56,36 @@ export default {
},
},
Mutation: {
muteUser: async (_parent, params, context, _resolveInfo) => {
const { user: currentUser } = context
if (currentUser.id === params.id) return null
await neode.cypher(
`
MATCH(u:User {id: $currentUser.id})-[previousRelationship:FOLLOWS]->(b:User {id: $params.id})
DELETE previousRelationship
`,
{ currentUser, params },
)
const [user, mutedUser] = await Promise.all([
neode.find('User', currentUser.id),
neode.find('User', params.id),
])
await user.relateTo(mutedUser, 'muted')
return mutedUser.toJson()
},
unmuteUser: async (_parent, params, context, _resolveInfo) => {
const { user: currentUser } = context
if (currentUser.id === params.id) return null
await neode.cypher(
`
MATCH(u:User {id: $currentUser.id})-[previousRelationship:MUTED]->(b:User {id: $params.id})
DELETE previousRelationship
`,
{ currentUser, params },
)
const unmutedUser = await neode.find('User', params.id)
return unmutedUser.toJson()
},
block: async (object, args, context, resolveInfo) => {
const { user: currentUser } = context
if (currentUser.id === args.id) return null
@ -217,6 +231,8 @@ export default {
'MATCH (this)<-[:FOLLOWS]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',
isBlocked:
'MATCH (this)<-[:BLOCKED]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',
isMuted:
'MATCH (this)<-[:MUTED]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',
},
count: {
contributionsCount:

View File

@ -9,7 +9,7 @@ const factory = Factory()
const neode = getNeode()
let currentUser
let blockedUser
let mutedUser
let authenticatedUser
let server
@ -33,15 +33,15 @@ afterEach(async () => {
await factory.cleanDatabase()
})
describe('blockedUsers', () => {
let blockedUserQuery
describe('mutedUsers', () => {
let mutedUserQuery
beforeEach(() => {
blockedUserQuery = gql`
mutedUserQuery = gql`
query {
blockedUsers {
mutedUsers {
id
name
isBlocked
isMuted
}
}
`
@ -49,34 +49,34 @@ describe('blockedUsers', () => {
it('throws permission error', async () => {
const { query } = createTestClient(server)
const result = await query({ query: blockedUserQuery })
const result = await query({ query: mutedUserQuery })
expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!')
})
describe('authenticated and given a blocked user', () => {
describe('authenticated and given a muted user', () => {
beforeEach(async () => {
currentUser = await neode.create('User', {
name: 'Current User',
id: 'u1',
})
blockedUser = await neode.create('User', {
name: 'Blocked User',
mutedUser = await neode.create('User', {
name: 'Muted User',
id: 'u2',
})
await currentUser.relateTo(blockedUser, 'blocked')
await currentUser.relateTo(mutedUser, 'muted')
authenticatedUser = await currentUser.toJson()
})
it('returns a list of blocked users', async () => {
it('returns a list of muted users', async () => {
const { query } = createTestClient(server)
await expect(query({ query: blockedUserQuery })).resolves.toEqual(
await expect(query({ query: mutedUserQuery })).resolves.toEqual(
expect.objectContaining({
data: {
blockedUsers: [
mutedUsers: [
{
name: 'Blocked User',
name: 'Muted User',
id: 'u2',
isBlocked: true,
isMuted: true,
},
],
},
@ -86,28 +86,28 @@ describe('blockedUsers', () => {
})
})
describe('block', () => {
let blockAction
describe('muteUser', () => {
let muteAction
beforeEach(() => {
currentUser = undefined
blockAction = variables => {
muteAction = variables => {
const { mutate } = createTestClient(server)
const blockMutation = gql`
const muteUserMutation = gql`
mutation($id: ID!) {
block(id: $id) {
muteUser(id: $id) {
id
name
isBlocked
isMuted
}
}
`
return mutate({ mutation: blockMutation, variables })
return mutate({ mutation: muteUserMutation, variables })
}
})
it('throws permission error', async () => {
const result = await blockAction({ id: 'u2' })
const result = await muteAction({ id: 'u2' })
expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!')
})
@ -120,45 +120,47 @@ describe('block', () => {
authenticatedUser = await currentUser.toJson()
})
describe('block yourself', () => {
describe('mute yourself', () => {
it('returns null', async () => {
await expect(blockAction({ id: 'u1' })).resolves.toEqual(
expect.objectContaining({ data: { block: null } }),
await expect(muteAction({ id: 'u1' })).resolves.toEqual(
expect.objectContaining({ data: { muteUser: null } }),
)
})
})
describe('block not existing user', () => {
describe('mute not existing user', () => {
it('returns null', async () => {
await expect(blockAction({ id: 'u2' })).resolves.toEqual(
expect.objectContaining({ data: { block: null } }),
await expect(muteAction({ id: 'u2' })).resolves.toEqual(
expect.objectContaining({ data: { muteUser: null } }),
)
})
})
describe('given a to-be-blocked user', () => {
describe('given a to-be-muted user', () => {
beforeEach(async () => {
blockedUser = await neode.create('User', {
name: 'Blocked User',
mutedUser = await neode.create('User', {
name: 'Muted User',
id: 'u2',
})
})
it('blocks a user', async () => {
await expect(blockAction({ id: 'u2' })).resolves.toEqual(
it('mutes a user', async () => {
await expect(muteAction({ id: 'u2' })).resolves.toEqual(
expect.objectContaining({
data: { block: { id: 'u2', name: 'Blocked User', isBlocked: true } },
data: {
muteUser: { id: 'u2', name: 'Muted User', isMuted: true },
},
}),
)
})
it('unfollows the user', async () => {
await currentUser.relateTo(blockedUser, 'following')
await currentUser.relateTo(mutedUser, 'following')
const queryUser = gql`
query {
User(id: "u2") {
id
isBlocked
isMuted
followedByCurrentUser
}
}
@ -166,18 +168,18 @@ describe('block', () => {
const { query } = createTestClient(server)
await expect(query({ query: queryUser })).resolves.toEqual(
expect.objectContaining({
data: { User: [{ id: 'u2', isBlocked: false, followedByCurrentUser: true }] },
data: { User: [{ id: 'u2', isMuted: false, followedByCurrentUser: true }] },
}),
)
await blockAction({ id: 'u2' })
await muteAction({ id: 'u2' })
await expect(query({ query: queryUser })).resolves.toEqual(
expect.objectContaining({
data: { User: [{ id: 'u2', isBlocked: true, followedByCurrentUser: false }] },
data: { User: [{ id: 'u2', isMuted: true, followedByCurrentUser: false }] },
}),
)
})
describe('given both the current user and the to-be-blocked user write a post', () => {
describe('given both the current user and the to-be-muted user write a post', () => {
let postQuery
beforeEach(async () => {
@ -187,11 +189,11 @@ describe('block', () => {
})
const post2 = await neode.create('Post', {
id: 'p23',
title: 'A post written by the blocked user',
title: 'A post written by the muted user',
})
await Promise.all([
post1.relateTo(currentUser, 'author'),
post2.relateTo(blockedUser, 'author'),
post2.relateTo(mutedUser, 'author'),
])
postQuery = gql`
query {
@ -223,9 +225,9 @@ describe('block', () => {
},
{
id: 'p23',
title: 'A post written by the blocked user',
title: 'A post written by the muted user',
author: {
name: 'Blocked User',
name: 'Muted User',
id: 'u2',
},
},
@ -238,12 +240,12 @@ describe('block', () => {
describe('from the perspective of the current user', () => {
it('both posts are in the newsfeed', bothPostsAreInTheNewsfeed)
describe('but if the current user blocks the other user', () => {
describe('but if the current user mutes the other user', () => {
beforeEach(async () => {
await currentUser.relateTo(blockedUser, 'blocked')
await currentUser.relateTo(mutedUser, 'muted')
})
it("the blocked user's post won't show up in the newsfeed of the current user", async () => {
it("the muted user's post won't show up in the newsfeed of the current user", async () => {
const { query } = createTestClient(server)
await expect(query({ query: postQuery })).resolves.toEqual(
expect.objectContaining({
@ -262,29 +264,34 @@ describe('block', () => {
})
})
describe('from the perspective of the blocked user', () => {
describe('from the perspective of the muted user', () => {
beforeEach(async () => {
authenticatedUser = await blockedUser.toJson()
authenticatedUser = await mutedUser.toJson()
})
it('both posts are in the newsfeed', bothPostsAreInTheNewsfeed)
describe('but if the current user blocks the other user', () => {
describe('but if the current user mutes the other user', () => {
beforeEach(async () => {
await currentUser.relateTo(blockedUser, 'blocked')
await currentUser.relateTo(mutedUser, 'muted')
})
it("the current user's post won't show up in the newsfeed of the blocked user", async () => {
it("the current user's post will show up in the newsfeed of the muted user", async () => {
const { query } = createTestClient(server)
await expect(query({ query: postQuery })).resolves.toEqual(
expect.objectContaining({
data: {
Post: [
Post: expect.arrayContaining([
{
id: 'p23',
title: 'A post written by the blocked user',
author: { name: 'Blocked User', id: 'u2' },
title: 'A post written by the muted user',
author: { name: 'Muted User', id: 'u2' },
},
],
{
id: 'p12',
title: 'A post written by the current user',
author: { name: 'Current User', id: 'u1' },
},
]),
},
}),
)
@ -296,28 +303,28 @@ describe('block', () => {
})
})
describe('unblock', () => {
let unblockAction
describe('unmuteUser', () => {
let unmuteAction
beforeEach(() => {
currentUser = undefined
unblockAction = variables => {
unmuteAction = variables => {
const { mutate } = createTestClient(server)
const unblockMutation = gql`
const unmuteUserMutation = gql`
mutation($id: ID!) {
unblock(id: $id) {
unmuteUser(id: $id) {
id
name
isBlocked
isMuted
}
}
`
return mutate({ mutation: unblockMutation, variables })
return mutate({ mutation: unmuteUserMutation, variables })
}
})
it('throws permission error', async () => {
const result = await unblockAction({ id: 'u2' })
const result = await unmuteAction({ id: 'u2' })
expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!')
})
@ -330,59 +337,69 @@ describe('unblock', () => {
authenticatedUser = await currentUser.toJson()
})
describe('unblock yourself', () => {
describe('unmute yourself', () => {
it('returns null', async () => {
await expect(unblockAction({ id: 'u1' })).resolves.toEqual(
expect.objectContaining({ data: { unblock: null } }),
await expect(unmuteAction({ id: 'u1' })).resolves.toEqual(
expect.objectContaining({ data: { unmuteUser: null } }),
)
})
})
describe('unblock not-existing user', () => {
describe('unmute not-existing user', () => {
it('returns null', async () => {
await expect(unblockAction({ id: 'lksjdflksfdj' })).resolves.toEqual(
expect.objectContaining({ data: { unblock: null } }),
await expect(unmuteAction({ id: 'lksjdflksfdj' })).resolves.toEqual(
expect.objectContaining({ data: { unmuteUser: null } }),
)
})
})
describe('given another user', () => {
beforeEach(async () => {
blockedUser = await neode.create('User', {
name: 'Blocked User',
mutedUser = await neode.create('User', {
name: 'Muted User',
id: 'u2',
})
})
describe('unblocking a not yet blocked user', () => {
describe('unmuting a not yet muted user', () => {
it('does not hurt', async () => {
await expect(unblockAction({ id: 'u2' })).resolves.toEqual(
await expect(unmuteAction({ id: 'u2' })).resolves.toEqual(
expect.objectContaining({
data: { unblock: { id: 'u2', name: 'Blocked User', isBlocked: false } },
data: {
unmuteUser: { id: 'u2', name: 'Muted User', isMuted: false },
},
}),
)
})
})
describe('given a blocked user', () => {
describe('given a muted user', () => {
beforeEach(async () => {
await currentUser.relateTo(blockedUser, 'blocked')
await currentUser.relateTo(mutedUser, 'muted')
})
it('unblocks a user', async () => {
await expect(unblockAction({ id: 'u2' })).resolves.toEqual(
it('unmutes a user', async () => {
await expect(unmuteAction({ id: 'u2' })).resolves.toEqual(
expect.objectContaining({
data: { unblock: { id: 'u2', name: 'Blocked User', isBlocked: false } },
data: {
unmuteUser: { id: 'u2', name: 'Muted User', isMuted: false },
},
}),
)
})
describe('unblocking twice', () => {
describe('unmuting twice', () => {
it('has no effect', async () => {
await unblockAction({ id: 'u2' })
await expect(unblockAction({ id: 'u2' })).resolves.toEqual(
await unmuteAction({ id: 'u2' })
await expect(unmuteAction({ id: 'u2' })).resolves.toEqual(
expect.objectContaining({
data: { unblock: { id: 'u2', name: 'Blocked User', isBlocked: false } },
data: {
unmuteUser: {
id: 'u2',
name: 'Muted User',
isMuted: false,
},
},
}),
)
})

View File

@ -75,6 +75,13 @@ type User {
"""
)
isMuted: Boolean! @cypher(
statement: """
MATCH (this)<-[:MUTED]-(user:User { id: $cypherParams.currentUserId})
RETURN COUNT(user) >= 1
"""
)
# contributions: [WrittenPost]!
# contributions2(first: Int = 10, offset: Int = 0): [WrittenPost2]!
# @cypher(
@ -160,6 +167,7 @@ type Query {
filter: _UserFilter
): [User]
mutedUsers: [User]
blockedUsers: [User]
isLoggedIn: Boolean!
currentUser: User
@ -197,7 +205,8 @@ type Mutation {
DeleteUser(id: ID!, resource: [Deletable]): User
muteUser(id: ID!): User
unmuteUser(id: ID!): User
block(id: ID!): User
unblock(id: ID!): User
}

View File

@ -36,7 +36,6 @@ export default function create() {
if (categoryIds)
categories = await Promise.all(categoryIds.map(id => neodeInstance.find('Category', id)))
categories = categories || (await Promise.all([factoryInstance.create('Category')]))
const { tagIds = [] } = args
delete args.tags
const tags = await Promise.all(

View File

@ -226,6 +226,10 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
dewey.relateTo(huey, 'following'),
louie.relateTo(jennyRostock, 'following'),
huey.relateTo(dagobert, 'muted'),
dewey.relateTo(dagobert, 'muted'),
louie.relateTo(dagobert, 'muted'),
dagobert.relateTo(huey, 'blocked'),
dagobert.relateTo(dewey, 'blocked'),
dagobert.relateTo(louie, 'blocked'),

View File

@ -7,7 +7,7 @@ import { gql } from '../../../backend/src/helpers/jest'
let lastReportTitle
let davidIrvingPostTitle = 'The Truth about the Holocaust'
let davidIrvingPostSlug = 'the-truth-about-the-holocaust'
let annoyingUserWhoBlockedModeratorTitle = 'Fake news'
let annoyingUserWhoMutedModeratorTitle = 'Fake news'
const savePostTitle = $post => {
return $post
@ -147,9 +147,9 @@ Then('I see all the reported posts including the one from above', () => {
})
})
Then('I see all the reported posts including from the user who blocked me', () => {
Then('I see all the reported posts including from the user who muted me', () => {
cy.get('table tbody').within(() => {
cy.contains('tr', annoyingUserWhoBlockedModeratorTitle)
cy.contains('tr', annoyingUserWhoMutedModeratorTitle)
})
})
@ -159,9 +159,9 @@ Then('each list item links to the post page', () => {
})
Then('I can visit the post page', () => {
cy.contains(annoyingUserWhoBlockedModeratorTitle).click()
cy.contains(annoyingUserWhoMutedModeratorTitle).click()
cy.location('pathname').should('contain', '/post')
.get('h3').should('contain', annoyingUserWhoBlockedModeratorTitle)
.get('h3').should('contain', annoyingUserWhoMutedModeratorTitle)
})
When("they have a post someone has reported", () => {

View File

@ -29,10 +29,20 @@ const narratorParams = {
...termsAndConditionsAgreedVersion,
};
const annoyingParams = {
email: "spammy-spammer@example.org",
password: "1234",
...termsAndConditionsAgreedVersion
};
Given("I am logged in", () => {
cy.login(loginCredentials);
});
Given("I am logged in as the muted user", () => {
cy.login({ email: annoyingParams.email, password: '1234' });
});
Given("we have a selection of categories", () => {
cy.createCategories("cat0", "just-for-fun");
});
@ -227,7 +237,6 @@ Given("I previously created a post", () => {
lastPost.authorId = narratorParams.id
lastPost.title = "previously created post";
lastPost.content = "with some content";
lastPost.categoryIds = ["cat0"];
cy.factory()
.create("Post", lastPost);
});
@ -407,11 +416,6 @@ Then("there are no notifications in the top menu", () => {
});
Given("there is an annoying user called {string}", name => {
const annoyingParams = {
email: "spammy-spammer@example.org",
password: "1234",
...termsAndConditionsAgreedVersion
};
cy.factory().create("User", {
...annoyingParams,
id: "annoying-user",
@ -420,17 +424,17 @@ Given("there is an annoying user called {string}", name => {
});
});
Given("there is an annoying user who has blocked me", () => {
Given("there is an annoying user who has muted me", () => {
cy.neode()
.first("User", {
role: 'moderator'
})
.then(blocked => {
.then(mutedUser => {
cy.neode()
.first("User", {
id: 'annoying-user'
})
.relateTo(blocked, "blocked");
.relateTo(mutedUser, "muted");
});
});
@ -514,17 +518,17 @@ Given("I wrote a post {string}", title => {
});
});
When("I block the user {string}", name => {
When("I mute the user {string}", name => {
cy.neode()
.first("User", {
name
})
.then(blocked => {
.then(mutedUser => {
cy.neode()
.first("User", {
name: narratorParams.name
})
.relateTo(blocked, "blocked");
.relateTo(mutedUser, "muted");
});
});

View File

@ -11,7 +11,7 @@ Feature: Report and Moderate
Given we have the following user accounts:
| id | name |
| u67 | David Irving |
| annoying-user | I'm gonna block Moderators and Admins HA HA HA |
| annoying-user | I'm gonna mute Moderators and Admins HA HA HA |
Given we have the following posts in our database:
| authorId | id | title | content |
@ -58,16 +58,16 @@ Feature: Report and Moderate
Then I see all the reported posts including the one from above
And each list item links to the post page
Scenario: Review reported posts of a user who's blocked a moderator
Scenario: Review reported posts of a user who's muted a moderator
Given somebody reported the following posts:
| submitterEmail | resourceId | reasonCategory | reasonDescription |
| p2.submitter@example.org | p2 | other | Offensive content |
And my user account has the role "moderator"
And there is an annoying user who has blocked me
And there is an annoying user who has muted me
And I am logged in
When I click on the avatar menu in the top right corner
And I click on "Moderation"
Then I see all the reported posts including from the user who blocked me
Then I see all the reported posts including from the user who muted me
And I can visit the post page
Scenario: Normal user can't see the moderation page

View File

@ -1,22 +0,0 @@
Feature: Block a User
As a user
I'd like to have a button to block another user
To prevent him from seeing and interacting with my contributions and also to avoid seeing his/her posts
Background:
Given I have a user account
And there is an annoying user called "Spammy Spammer"
Scenario Outline: Blocked users cannot see each others posts
Given "Spammy Spammer" wrote a post "Spam Spam Spam"
And I wrote a post "I hate spammers"
And I block the user "Spammy Spammer"
When I log in with:
| Email | Password |
| <email> | <password> |
Then I see only one post with the title "<expected_title>"
Examples:
| email | password | expected_title |
| peterpan@example.org | 1234 | I hate spammers |
| spammy-spammer@example.org | 1234 | Spam Spam Spam |

View File

@ -1,6 +1,6 @@
Feature: Block a User
Feature: Mute a User
As a user
I'd like to have a button to block another user
I'd like to have a button to mute another user
To prevent him from seeing and interacting with my contributions and also to avoid seeing his/her posts
Background:
@ -8,36 +8,46 @@ Feature: Block a User
And there is an annoying user called "Spammy Spammer"
And I am logged in
Scenario: Block a user
Scenario: Mute a user
Given I am on the profile page of the annoying user
When I click on "Block user" from the content menu in the user info box
And I navigate to my "Blocked users" settings page
When I click on "Mute user" from the content menu in the user info box
And I navigate to my "Muted users" settings page
Then I can see the following table:
| Avatar | Name |
| | Spammy Spammer |
Scenario: Block a previously followed user
Scenario: Mute a previously followed user
Given I follow the user "Spammy Spammer"
And "Spammy Spammer" wrote a post "Spam Spam Spam"
When I visit the profile page of the annoying user
And I click on "Block user" from the content menu in the user info box
And I click on "Mute user" from the content menu in the user info box
Then the list of posts of this user is empty
And nobody is following the user profile anymore
Scenario: Posts of blocked users are filtered from search results
Scenario: Posts of muted users are filtered from search results
Given we have the following posts in our database:
| id | title | content |
| im-not-blocked | Post that should be seen | cause I'm not blocked |
| id | title | content |
| im-not-muted | Post that should be seen | cause I'm not muted |
Given "Spammy Spammer" wrote a post "Spam Spam Spam"
When I search for "Spam"
Then I should see the following posts in the select dropdown:
| title |
| Spam Spam Spam |
When I block the user "Spammy Spammer"
When I mute the user "Spammy Spammer"
And I refresh the page
And I search for "Spam"
Then the search has no results
But I search for "not blocked"
But I search for "not muted"
Then I should see the following posts in the select dropdown:
| title |
| Post that should be seen |
Scenario: Muted users can still see my posts
Given I previously created a post
And I mute the user "Spammy Spammer"
Given I log out
And I am logged in as the muted user
When I search for "previously created"
Then I should see the following posts in the select dropdown:
| title |
| previously created post |

View File

@ -60,6 +60,7 @@ Cypress.Commands.add("login", ({ email, password }) => {
.as("submitButton")
.click();
cy.get(".iziToast-message").should("contain", "You are logged in!");
cy.location("pathname").should("eq", "/");
});
Cypress.Commands.add("logout", (email, password) => {

View File

@ -18,8 +18,8 @@
"cypress:backend": "cd backend && yarn run dev",
"cypress:webapp": "cd webapp && yarn run dev",
"cypress:setup": "run-p cypress:backend cypress:webapp",
"cypress:run": "cross-env cypress run --browser chromium",
"cypress:open": "cross-env cypress open --browser chromium",
"cypress:run": "cross-env cypress run",
"cypress:open": "cross-env cypress open",
"cucumber:setup": "cd backend && yarn run dev",
"cucumber": "wait-on tcp:4000 && cucumber-js --require-module @babel/register --exit",
"release": "standard-version"

View File

@ -407,49 +407,49 @@ describe('ContentMenu.vue', () => {
).toBe('/settings')
})
it('can block other users', () => {
it('can mute other users', () => {
const wrapper = openContentMenu({
isOwner: false,
resourceType: 'user',
resource: {
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
isBlocked: false,
isMuted: false,
},
})
wrapper
.findAll('.ds-menu-item')
.filter(item => item.text() === 'settings.blocked-users.block')
.filter(item => item.text() === 'settings.muted-users.mute')
.at(0)
.trigger('click')
expect(wrapper.emitted('block')).toEqual([
expect(wrapper.emitted('mute')).toEqual([
[
{
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
isBlocked: false,
isMuted: false,
},
],
])
})
it('can unblock blocked users', () => {
it('can unmute muted users', () => {
const wrapper = openContentMenu({
isOwner: false,
resourceType: 'user',
resource: {
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
isBlocked: true,
isMuted: true,
},
})
wrapper
.findAll('.ds-menu-item')
.filter(item => item.text() === 'settings.blocked-users.unblock')
.filter(item => item.text() === 'settings.muted-users.unmute')
.at(0)
.trigger('click')
expect(wrapper.emitted('unblock')).toEqual([
expect(wrapper.emitted('unmute')).toEqual([
[
{
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
isBlocked: true,
isMuted: true,
},
],
])

View File

@ -155,19 +155,19 @@ export default {
icon: 'edit',
})
} else {
if (this.resource.isBlocked) {
if (this.resource.isMuted) {
routes.push({
label: this.$t(`settings.blocked-users.unblock`),
label: this.$t(`settings.muted-users.unmute`),
callback: () => {
this.$emit('unblock', this.resource)
this.$emit('unmute', this.resource)
},
icon: 'user-plus',
})
} else {
routes.push({
label: this.$t(`settings.blocked-users.block`),
label: this.$t(`settings.muted-users.mute`),
callback: () => {
this.$emit('block', this.resource)
this.$emit('mute', this.resource)
},
icon: 'user-times',
})

View File

@ -48,7 +48,7 @@ export const user = {
],
followedByCount: 0,
followedByCurrentUser: false,
isBlocked: false,
isMuted: false,
followedBy: [],
socialMedia: [],
}

View File

@ -23,7 +23,7 @@ export default i18n => {
locationName
createdAt
followedByCurrentUser
isBlocked
isMuted
following(first: 7) {
...user
...userCounts

View File

@ -1,9 +1,9 @@
import gql from 'graphql-tag'
export const BlockedUsers = () => {
export const mutedUsers = () => {
return gql`
{
blockedUsers {
mutedUsers {
id
name
slug
@ -16,26 +16,26 @@ export const BlockedUsers = () => {
`
}
export const Block = () => {
export const muteUser = () => {
return gql`
mutation($id: ID!) {
block(id: $id) {
muteUser(id: $id) {
id
name
isBlocked
isMuted
followedByCurrentUser
}
}
`
}
export const Unblock = () => {
export const unmuteUser = () => {
return gql`
mutation($id: ID!) {
unblock(id: $id) {
unmuteUser(id: $id) {
id
name
isBlocked
isMuted
followedByCurrentUser
}
}

View File

@ -142,26 +142,23 @@
"successAdd": "Social-Media hinzugefügt. Profil aktualisiert!",
"successDelete": "Social-Media gelöscht. Profil aktualisiert!"
},
"blocked-users": {
"name": "Blockierte Benutzer",
"muted-users": {
"name": "Stummgeschaltete Benutzer",
"explanation": {
"intro": "Wenn ein anderer Benutzer von dir blockiert wurde, dann passiert folgendes:",
"your-perspective": "In deiner Beitragsübersicht tauchen keine Beiträge der blockierten Person mehr auf.",
"their-perspective": "Umgekehrt das gleiche: Die blockierte Person sieht deine Beiträge auch nicht mehr in ihrer Übersicht.",
"search": "Die Beiträge von blockierten Personen verschwinden aus deinen Suchergebnissen.",
"notifications": "Von dir blockierte Personen erhalten keine Benachrichtigungen mehr, wenn sie in deinen Beiträgen erwähnt werden.",
"closing": "Das sollte fürs Erste genügen, damit blockierte Benutzer dich nicht mehr länger belästigen können."
"intro": "Wenn ein anderer Benutzer von dir stummgeschaltet wurde, dann passiert folgendes:",
"your-perspective": "In deiner Beitragsübersicht tauchen keine Beiträge der stummgeschalteten Person mehr auf.",
"search": "Die Beiträge von stummgeschalteten Personen verschwinden aus deinen Suchergebnissen."
},
"columns": {
"name": "Name",
"slug": "Alias",
"unblock": "Entsperren"
"unmute": "Entsperren"
},
"empty": "Bislang hast du niemanden blockiert.",
"how-to": "Du kannst andere Benutzer auf deren Profilseite über das Inhaltsmenü blockieren.",
"block": "Nutzer blockieren",
"unblock": "Nutzer entblocken",
"unblocked": "{name} ist wieder entsperrt"
"empty": "Bislang hast du niemanden stummgeschaltet.",
"how-to": "Du kannst andere Benutzer auf deren Profilseite über das Inhaltsmenü stummschalten.",
"mute": "Stumm schalten",
"unmute": "Stummschaltung aufheben",
"unmuted": "{name} ist nicht mehr stummgeschaltet"
},
"privacy": {
"name": "Privatsphäre",
@ -475,7 +472,7 @@
},
"teaserImage": {
"cropperConfirm": "Bestätigen"
},
},
"inappropriatePicture" : "Dieses Bild kann für einige Menschen unangemessen sein.",
"inappropriatePictureText" : "Wann soll ein Foto versteckt werden"
},

View File

@ -312,26 +312,23 @@
"successAdd": "Added social media. Updated user profile!",
"successDelete": "Deleted social media. Updated user profile!"
},
"blocked-users": {
"name": "Blocked users",
"muted-users": {
"name": "Muted users",
"explanation": {
"intro": "If another user has been blocked by you, this is what happens:",
"your-perspective": "The blocked person's posts will no longer appear in your news feed.",
"their-perspective": "Vice versa: The blocked person will also no longer see your posts in their news feed.",
"search": "Posts of blocked people disappear from your search results.",
"notifications": "Blocked users will no longer receive notifications if they are mentioned in your posts.",
"closing": "This should be sufficient for now so that blocked users can no longer bother you."
"intro": "If another user has been muted by you, this is what happens:",
"your-perspective": "The muted person's posts will no longer appear in your news feed.",
"search": "Posts of muted people disappear from your search results."
},
"columns": {
"name": "Name",
"slug": "Slug",
"unblock": "Unblock"
"unmute": "Unmute"
},
"empty": "So far, you have not blocked anybody.",
"how-to": "You can block other users on their profile page via the content menu.",
"block": "Block user",
"unblock": "Unblock user",
"unblocked": "{name} is unblocked again"
"empty": "So far, you have not muted anybody.",
"how-to": "You can mute other users on their profile page via the content menu.",
"mute": "Mute user",
"unmute": "Unmute user",
"unmuted": "{name} is unmuted again"
}
},
"admin": {

View File

@ -145,26 +145,23 @@
"successAdd": "Social-Media agregó. Perfil actualizado!",
"successDelete": "Social-Media borrado. Perfil actualizado!"
},
"blocked-users": {
"muted-users": {
"name": "Usuarios bloqueados",
"explanation": {
"intro": "Si otro usuario ha sido bloqueado por usted, esto es lo que sucede:",
"your-perspective": "Las contribuciones de la persona bloqueada no aparecerán más en su canal de noticias.",
"their-perspective": "Viceversa: la persona bloqueada tampoco verá más sus contribuciones en sus noticias.",
"search": "Las contribuciones de personas bloqueadas desaparecen de los resultados de búsqueda.",
"notifications": "Los usuarios bloqueados no recibirán más notificaciones si se mencionan en sus contribuciones.",
"closing": "Esto debería ser suficiente por ahora para que los usuarios bloqueados no puedan molestarle más."
"search": "Las contribuciones de personas bloqueadas desaparecen de los resultados de búsqueda."
},
"columns": {
"name": "Nombre",
"slug": "Alias",
"unblock": "Desbloquear"
"unmute": "Desbloquear"
},
"empty": "Hasta ahora, no ha bloqueado a nadie.",
"how-to": "Puede bloquear a otros usuarios en la página de perfil de aquellos a través del menú de contenido.",
"block": "Bloquear usuario",
"unblock": "Desbloquear usuario",
"unblocked": "{name} está desbloqueado nuevamente"
"mute": "Bloquear usuario",
"unmute": "Desbloquear usuario",
"unmuted": "{name} está desbloqueado nuevamente"
},
"privacy": {
"name": "Privacidad",

View File

@ -145,26 +145,23 @@
"successAdd": "Les médias sociaux ont été ajoutés. Profil mis à jour !",
"successDelete": "Médias sociaux supprimé. Profil mis à jour !"
},
"blocked-users": {
"muted-users": {
"name": "Utilisateurs bloqués",
"explanation": {
"intro": "Si vous avez bloqué un autre utilisateur, voici ce qui se passe:",
"your-perspective": "Les postes de la personne bloquée n'apparaîtront dans votre fil d'actualités.",
"their-perspective": "Vice versa: la personne bloquée ne verra plus non plus vos postes dans son fil d'actualités.",
"search": "Les postes des personnes bloquées disparaissent de vos résultats de recherche.",
"notifications": "Les utilisateurs bloqués ne recevront plus de notifications s'ils sont mentionnés dans vos postes.",
"closing": "Ceci devrait être suffisant pour le moment afin que les utilisateurs bloqués ne puissent plus vous déranger."
"search": "Les postes des personnes bloquées disparaissent de vos résultats de recherche."
},
"columns": {
"name": "Nom",
"slug": "Slug",
"unblock": ""
"unmute": ""
},
"empty": "Jusqu'à présent, vous n'avez bloqué personne.",
"how-to": "Vous pouvez bloquer d'autres utilisateurs sur leur page de profil via le menu de contenu.",
"block": "Bloquer l'utilisateur",
"unblock": "Débloquer l'utilisateur",
"unblocked": "{name} est à nouveau débloqué"
"mute": "Bloquer l'utilisateur",
"unmute": "Débloquer l'utilisateur",
"unmuted": "{name} est à nouveau débloqué"
},
"privacy": {
"name": "",

View File

@ -145,26 +145,23 @@
"successAdd": "Social media aggiunti. \nProfilo utente aggiornato ",
"successDelete": "Social media cancellati. Profilo utente aggiornato!"
},
"blocked-users": {
"name": "",
"muted-users": {
"name": null,
"explanation": {
"intro": "",
"your-perspective": "",
"their-perspective": "",
"search": "",
"notifications": "",
"closing": ""
"intro": null,
"your-perspective": null,
"search": null
},
"columns": {
"name": "",
"slug": "",
"unblock": ""
"name": null,
"slug": null,
"unmute": null
},
"empty": "",
"how-to": "",
"block": "",
"unblock": "",
"unblocked": ""
"empty": null,
"how-to": null,
"mute": null,
"unmute": null,
"unmuted": null
},
"privacy": {
"name": "",

View File

@ -145,26 +145,23 @@
"successAdd": "Mídias sociais adicionadas. Perfil de usuário atualizado!",
"successDelete": "Mídias sociais removidas. Perfil de usuário atualizado!"
},
"blocked-users": {
"muted-users": {
"name": "Usuários bloqueados",
"explanation": {
"intro": "Se outro usuário foi bloqueado por você, isto é o que acontece:",
"your-perspective": "As mensagens da pessoa bloqueada não aparecerão mais no seu feed de notícias.",
"their-perspective": "Vice versa: A pessoa bloqueada também não verá mais suas mensagens em seu feed de notícias.",
"search": "Publicações de pessoas bloqueadas desaparecem dos resultados da sua pesquisa.",
"notifications": "Usuários bloqueados não receberão mais notificações se forem mencionados em suas mensagens.",
"closing": "Isso deve ser suficiente por enquanto para que os usuários bloqueados não possam mais incomodá-lo."
"search": "Publicações de pessoas bloqueadas desaparecem dos resultados da sua pesquisa."
},
"columns": {
"name": "Nome",
"slug": "Slug",
"unblock": "Desbloquear"
"unmute": "Desbloquear"
},
"empty": "Até agora, você não bloqueou ninguém.",
"how-to": "Você pode bloquear outros usuários em suas páginas de perfil através do menu de conteúdo.",
"block": "Bloquear usuário",
"unblock": "Desbloquear usuário",
"unblocked": "{name} está desbloqueado novamente"
"mute": "Bloquear usuário",
"unmute": "Desbloquear usuário",
"unmuted": "{name} está desbloqueado novamente"
},
"privacy": {
"name": "Privacidade",

View File

@ -595,26 +595,23 @@
"placeholder": "Поиск"
},
"settings": {
"blocked-users": {
"block": "Блокировать",
"muted-users": {
"mute": "Блокировать",
"columns": {
"name": "Имя",
"slug": "Псевдоним",
"unblock": "Разблокировать"
"unmute": "Разблокировать"
},
"empty": "Вы пока никого не блокировали.",
"explanation": {
"closing": "На данный момент этого должно быть достаточно, чтобы заблокированные пользователи больше вас не беспокоили.",
"intro": "Если блокируете другого пользователя, происходит следующее:",
"notifications": "Заблокированные пользователи больше не будут получать уведомления об упоминаниях в ваших постах.",
"search": "Посты заблокированных пользователей не отображаются в результатах поиска.",
"their-perspective": "И наоборот — заблокированный пользователь больше не видит ваши посты в своей ленте.",
"your-perspective": "Посты заблокированного пользователя не отображаются в персональной ленте."
},
"how-to": "Вы можете блокировать других пользователей на странице их профиля с помощью меню профиля.",
"name": "Заблокированные пользователи",
"unblock": "Разблокировать пользователей",
"unblocked": "{name} - снова разблокирован"
"unmute": "Разблокировать пользователей",
"unmuted": "{name} - снова разблокирован"
},
"data": {
"labelBio": "О себе",

View File

@ -22,8 +22,8 @@
:resource="user"
:is-owner="myProfile"
class="user-content-menu"
@block="block"
@unblock="unblock"
@mute="muteUser"
@unmute="unmuteUser"
/>
</client-only>
<ds-space margin="small">
@ -67,14 +67,14 @@
<ds-space margin="small">
<template v-if="!myProfile">
<hc-follow-button
v-if="!user.isBlocked"
v-if="!user.isMuted"
:follow-id="user.id"
:is-followed="user.followedByCurrentUser"
@optimistic="optimisticFollow"
@update="updateFollow"
/>
<base-button v-else @click="unblock(user)" class="unblock-user-button">
{{ $t('settings.blocked-users.unblock') }}
<base-button v-else @click="unmuteUser(user)" class="unblock-user-button">
{{ $t('settings.muted-users.unmute') }}
</base-button>
</template>
</ds-space>
@ -284,7 +284,7 @@ import MasonryGrid from '~/components/MasonryGrid/MasonryGrid.vue'
import MasonryGridItem from '~/components/MasonryGrid/MasonryGridItem.vue'
import { profilePagePosts } from '~/graphql/PostQuery'
import UserQuery from '~/graphql/User'
import { Block, Unblock } from '~/graphql/settings/BlockedUsers'
import { muteUser, unmuteUser } from '~/graphql/settings/MutedUsers'
import PostMutations from '~/graphql/PostMutations'
import UpdateQuery from '~/components/utils/UpdateQuery'
@ -396,17 +396,27 @@ export default {
this.posts = []
this.hasMore = true
},
async block(user) {
await this.$apollo.mutate({ mutation: Block(), variables: { id: user.id } })
this.$apollo.queries.User.refetch()
this.resetPostList()
this.$apollo.queries.profilePagePosts.refetch()
async muteUser(user) {
try {
await this.$apollo.mutate({ mutation: muteUser(), variables: { id: user.id } })
} catch (error) {
this.$toast.error(error.message)
} finally {
this.$apollo.queries.User.refetch()
this.resetPostList()
this.$apollo.queries.profilePagePosts.refetch()
}
},
async unblock(user) {
await this.$apollo.mutate({ mutation: Unblock(), variables: { id: user.id } })
this.$apollo.queries.User.refetch()
this.resetPostList()
this.$apollo.queries.profilePagePosts.refetch()
async unmuteUser(user) {
try {
this.$apollo.mutate({ mutation: unmuteUser(), variables: { id: user.id } })
} catch (error) {
this.$toast.error(error.message)
} finally {
this.$apollo.queries.User.refetch()
this.resetPostList()
this.$apollo.queries.profilePagePosts.refetch()
}
},
pinPost(post) {
this.$apollo

View File

@ -40,8 +40,8 @@ export default {
path: `/settings/my-social-media`,
},
{
name: this.$t('settings.blocked-users.name'),
path: `/settings/blocked-users`,
name: this.$t('settings.muted-users.name'),
path: `/settings/muted-users`,
},
{
name: this.$t('settings.embeds.name'),

View File

@ -1,8 +1,8 @@
import { config, mount, createLocalVue } from '@vue/test-utils'
import BlockedUsers from './blocked-users.vue'
import MutedUsers from './muted-users.vue'
import Styleguide from '@human-connection/styleguide'
import Filters from '~/plugins/vue-filters'
import { Unblock } from '~/graphql/settings/BlockedUsers'
import { unmuteUser } from '~/graphql/settings/MutedUsers'
const localVue = createLocalVue()
@ -11,7 +11,7 @@ localVue.use(Filters)
config.stubs['nuxt-link'] = '<span><slot /></span>'
describe('blocked-users.vue', () => {
describe('muted-users.vue', () => {
let wrapper
let mocks
@ -21,7 +21,7 @@ describe('blocked-users.vue', () => {
$apollo: {
mutate: jest.fn(),
queries: {
blockedUsers: {
mutedUsers: {
refetch: jest.fn(),
},
},
@ -35,7 +35,7 @@ describe('blocked-users.vue', () => {
describe('mount', () => {
const Wrapper = () => {
return mount(BlockedUsers, { mocks, localVue })
return mount(MutedUsers, { mocks, localVue })
}
beforeEach(() => {
@ -48,18 +48,18 @@ describe('blocked-users.vue', () => {
describe('given a list of blocked users', () => {
beforeEach(() => {
const blockedUsers = [{ id: 'u1', name: 'John Doe', slug: 'john-doe', avatar: '' }]
wrapper.setData({ blockedUsers })
const mutedUsers = [{ id: 'u1', name: 'John Doe', slug: 'john-doe', avatar: '' }]
wrapper.setData({ mutedUsers })
})
describe('click unblock', () => {
describe('click unmute', () => {
beforeEach(() => {
wrapper.find('button').trigger('click')
})
it('calls unblock mutation with given user', () => {
it('calls unmute mutation with given user', () => {
expect(mocks.$apollo.mutate).toHaveBeenCalledWith({
mutation: Unblock(),
mutation: unmuteUser(),
variables: { id: 'u1' },
})
})

View File

@ -1,31 +1,22 @@
<template>
<div>
<ds-space>
<ds-card :header="$t('settings.blocked-users.name')">
<ds-card :header="$t('settings.muted-users.name')">
<ds-text>
{{ $t('settings.blocked-users.explanation.intro') }}
{{ $t('settings.muted-users.explanation.intro') }}
</ds-text>
<ds-list>
<ds-list-item>
{{ $t('settings.blocked-users.explanation.your-perspective') }}
{{ $t('settings.muted-users.explanation.your-perspective') }}
</ds-list-item>
<ds-list-item>
{{ $t('settings.blocked-users.explanation.their-perspective') }}
</ds-list-item>
<ds-list-item>
{{ $t('settings.blocked-users.explanation.search') }}
</ds-list-item>
<ds-list-item>
{{ $t('settings.blocked-users.explanation.notifications') }}
{{ $t('settings.muted-users.explanation.search') }}
</ds-list-item>
</ds-list>
<ds-text>
{{ $t('settings.blocked-users.explanation.closing') }}
</ds-text>
</ds-card>
</ds-space>
<ds-card v-if="blockedUsers && blockedUsers.length">
<ds-table :data="blockedUsers" :fields="fields" condensed>
<ds-card v-if="mutedUsers && mutedUsers.length">
<ds-table :data="mutedUsers" :fields="fields" condensed>
<template slot="avatar" slot-scope="scope">
<nuxt-link
:to="{
@ -57,20 +48,20 @@
</nuxt-link>
</template>
<template slot="unblock" slot-scope="scope">
<base-button circle size="small" @click="unblock(scope)" icon="user-plus" />
<template slot="unmuteUser" slot-scope="scope">
<base-button circle size="small" @click="unmuteUser(scope)" icon="user-plus" />
</template>
</ds-table>
</ds-card>
<ds-card v-else>
<ds-space>
<ds-placeholder>
{{ $t('settings.blocked-users.empty') }}
{{ $t('settings.muted-users.empty') }}
</ds-placeholder>
</ds-space>
<ds-space>
<ds-text align="center">
{{ $t('settings.blocked-users.how-to') }}
{{ $t('settings.muted-users.how-to') }}
</ds-text>
</ds-space>
</ds-card>
@ -78,7 +69,7 @@
</template>
<script>
import { BlockedUsers, Unblock } from '~/graphql/settings/BlockedUsers'
import { mutedUsers, unmuteUser } from '~/graphql/settings/MutedUsers'
import HcAvatar from '~/components/Avatar/Avatar.vue'
export default {
@ -87,28 +78,31 @@ export default {
},
data() {
return {
blockedUsers: [],
mutedUsers: [],
}
},
computed: {
fields() {
return {
avatar: '',
name: this.$t('settings.blocked-users.columns.name'),
slug: this.$t('settings.blocked-users.columns.slug'),
unblock: this.$t('settings.blocked-users.columns.unblock'),
name: this.$t('settings.muted-users.columns.name'),
slug: this.$t('settings.muted-users.columns.slug'),
unmuteUser: this.$t('settings.muted-users.columns.unmute'),
}
},
},
apollo: {
blockedUsers: { query: BlockedUsers, fetchPolicy: 'cache-and-network' },
mutedUsers: { query: mutedUsers, fetchPolicy: 'cache-and-network' },
},
methods: {
async unblock(user) {
await this.$apollo.mutate({ mutation: Unblock(), variables: { id: user.row.id } })
this.$apollo.queries.blockedUsers.refetch()
async unmuteUser(user) {
await this.$apollo.mutate({
mutation: unmuteUser(),
variables: { id: user.row.id },
})
this.$apollo.queries.mutedUsers.refetch()
const { name } = user.row
this.$toast.success(this.$t('settings.blocked-users.unblocked', { name }))
this.$toast.success(this.$t('settings.muted-users.unmuted', { name }))
},
},
}