mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
Merge branch 'master' into revert-6103-newsfeed-bigger-gap-between-posts
This commit is contained in:
commit
f3f7e8c508
@ -51,6 +51,50 @@ const publishNotifications = async (context, promises) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleJoinGroup = async (resolve, root, args, context, resolveInfo) => {
|
||||||
|
const { groupId, userId } = args
|
||||||
|
const user = await resolve(root, args, context, resolveInfo)
|
||||||
|
if (user) {
|
||||||
|
await publishNotifications(context, [
|
||||||
|
notifyOwnersOfGroup(groupId, userId, 'user_joined_group', context),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleLeaveGroup = async (resolve, root, args, context, resolveInfo) => {
|
||||||
|
const { groupId, userId } = args
|
||||||
|
const user = await resolve(root, args, context, resolveInfo)
|
||||||
|
if (user) {
|
||||||
|
await publishNotifications(context, [
|
||||||
|
notifyOwnersOfGroup(groupId, userId, 'user_left_group', context),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChangeGroupMemberRole = async (resolve, root, args, context, resolveInfo) => {
|
||||||
|
const { groupId, userId } = args
|
||||||
|
const user = await resolve(root, args, context, resolveInfo)
|
||||||
|
if (user) {
|
||||||
|
await publishNotifications(context, [
|
||||||
|
notifyMemberOfGroup(groupId, userId, 'changed_group_member_role', context),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRemoveUserFromGroup = async (resolve, root, args, context, resolveInfo) => {
|
||||||
|
const { groupId, userId } = args
|
||||||
|
const user = await resolve(root, args, context, resolveInfo)
|
||||||
|
if (user) {
|
||||||
|
await publishNotifications(context, [
|
||||||
|
notifyMemberOfGroup(groupId, userId, 'removed_user_from_group', context),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
const handleContentDataOfPost = async (resolve, root, args, context, resolveInfo) => {
|
const handleContentDataOfPost = async (resolve, root, args, context, resolveInfo) => {
|
||||||
const idsOfUsers = extractMentionedUsers(args.content)
|
const idsOfUsers = extractMentionedUsers(args.content)
|
||||||
const post = await resolve(root, args, context, resolveInfo)
|
const post = await resolve(root, args, context, resolveInfo)
|
||||||
@ -94,6 +138,72 @@ const postAuthorOfComment = async (commentId, { context }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const notifyOwnersOfGroup = async (groupId, userId, reason, context) => {
|
||||||
|
const cypher = `
|
||||||
|
MATCH (group:Group { id: $groupId })<-[membership:MEMBER_OF]-(owner:User)
|
||||||
|
WHERE membership.role = 'owner'
|
||||||
|
WITH owner, group
|
||||||
|
MERGE (group)-[notification:NOTIFIED {reason: $reason}]->(owner)
|
||||||
|
WITH group, owner, notification
|
||||||
|
SET notification.read = FALSE
|
||||||
|
SET notification.createdAt = COALESCE(notification.createdAt, toString(datetime()))
|
||||||
|
SET notification.updatedAt = toString(datetime())
|
||||||
|
SET notification.relatedUserId = $userId
|
||||||
|
RETURN notification {.*, from: group, to: properties(owner)}
|
||||||
|
`
|
||||||
|
const session = context.driver.session()
|
||||||
|
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
|
||||||
|
const notificationTransactionResponse = await transaction.run(cypher, {
|
||||||
|
groupId,
|
||||||
|
reason,
|
||||||
|
userId,
|
||||||
|
})
|
||||||
|
return notificationTransactionResponse.records.map((record) => record.get('notification'))
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
const notifications = await writeTxResultPromise
|
||||||
|
return notifications
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error)
|
||||||
|
} finally {
|
||||||
|
session.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const notifyMemberOfGroup = async (groupId, userId, reason, context) => {
|
||||||
|
const { user: owner } = context
|
||||||
|
const cypher = `
|
||||||
|
MATCH (user:User { id: $userId })
|
||||||
|
MATCH (group:Group { id: $groupId })
|
||||||
|
WITH user, group
|
||||||
|
MERGE (group)-[notification:NOTIFIED {reason: $reason}]->(user)
|
||||||
|
WITH group, user, notification
|
||||||
|
SET notification.read = FALSE
|
||||||
|
SET notification.createdAt = COALESCE(notification.createdAt, toString(datetime()))
|
||||||
|
SET notification.updatedAt = toString(datetime())
|
||||||
|
SET notification.relatedUserId = $ownerId
|
||||||
|
RETURN notification {.*, from: group, to: properties(user)}
|
||||||
|
`
|
||||||
|
const session = context.driver.session()
|
||||||
|
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
|
||||||
|
const notificationTransactionResponse = await transaction.run(cypher, {
|
||||||
|
groupId,
|
||||||
|
reason,
|
||||||
|
userId,
|
||||||
|
ownerId: owner.id,
|
||||||
|
})
|
||||||
|
return notificationTransactionResponse.records.map((record) => record.get('notification'))
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
const notifications = await writeTxResultPromise
|
||||||
|
return notifications
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error)
|
||||||
|
} finally {
|
||||||
|
session.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => {
|
const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => {
|
||||||
if (!(idsOfUsers && idsOfUsers.length)) return []
|
if (!(idsOfUsers && idsOfUsers.length)) return []
|
||||||
await validateNotifyUsers(label, reason)
|
await validateNotifyUsers(label, reason)
|
||||||
@ -188,5 +298,9 @@ export default {
|
|||||||
UpdatePost: handleContentDataOfPost,
|
UpdatePost: handleContentDataOfPost,
|
||||||
CreateComment: handleContentDataOfComment,
|
CreateComment: handleContentDataOfComment,
|
||||||
UpdateComment: handleContentDataOfComment,
|
UpdateComment: handleContentDataOfComment,
|
||||||
|
JoinGroup: handleJoinGroup,
|
||||||
|
LeaveGroup: handleLeaveGroup,
|
||||||
|
ChangeGroupMemberRole: handleChangeGroupMemberRole,
|
||||||
|
RemoveUserFromGroup: handleRemoveUserFromGroup,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,13 @@ import { cleanDatabase } from '../../db/factories'
|
|||||||
import { createTestClient } from 'apollo-server-testing'
|
import { createTestClient } from 'apollo-server-testing'
|
||||||
import { getNeode, getDriver } from '../../db/neo4j'
|
import { getNeode, getDriver } from '../../db/neo4j'
|
||||||
import createServer, { pubsub } from '../../server'
|
import createServer, { pubsub } from '../../server'
|
||||||
|
import {
|
||||||
|
createGroupMutation,
|
||||||
|
joinGroupMutation,
|
||||||
|
leaveGroupMutation,
|
||||||
|
changeGroupMemberRoleMutation,
|
||||||
|
removeUserFromGroupMutation,
|
||||||
|
} from '../../graphql/groups'
|
||||||
|
|
||||||
let server, query, mutate, notifiedUser, authenticatedUser
|
let server, query, mutate, notifiedUser, authenticatedUser
|
||||||
let publishSpy
|
let publishSpy
|
||||||
@ -92,6 +99,9 @@ describe('notifications', () => {
|
|||||||
read
|
read
|
||||||
reason
|
reason
|
||||||
createdAt
|
createdAt
|
||||||
|
relatedUser {
|
||||||
|
id
|
||||||
|
}
|
||||||
from {
|
from {
|
||||||
__typename
|
__typename
|
||||||
... on Post {
|
... on Post {
|
||||||
@ -102,6 +112,9 @@ describe('notifications', () => {
|
|||||||
id
|
id
|
||||||
content
|
content
|
||||||
}
|
}
|
||||||
|
... on Group {
|
||||||
|
id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -185,6 +198,7 @@ describe('notifications', () => {
|
|||||||
id: 'c47',
|
id: 'c47',
|
||||||
content: commentContent,
|
content: commentContent,
|
||||||
},
|
},
|
||||||
|
relatedUser: null,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -357,6 +371,7 @@ describe('notifications', () => {
|
|||||||
id: 'p47',
|
id: 'p47',
|
||||||
content: expectedUpdatedContent,
|
content: expectedUpdatedContent,
|
||||||
},
|
},
|
||||||
|
relatedUser: null,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -513,6 +528,7 @@ describe('notifications', () => {
|
|||||||
id: 'c47',
|
id: 'c47',
|
||||||
content: commentContent,
|
content: commentContent,
|
||||||
},
|
},
|
||||||
|
relatedUser: null,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -547,6 +563,7 @@ describe('notifications', () => {
|
|||||||
id: 'c47',
|
id: 'c47',
|
||||||
content: commentContent,
|
content: commentContent,
|
||||||
},
|
},
|
||||||
|
relatedUser: null,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -616,4 +633,232 @@ describe('notifications', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('group notifications', () => {
|
||||||
|
let groupOwner
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
groupOwner = await neode.create(
|
||||||
|
'User',
|
||||||
|
{
|
||||||
|
id: 'group-owner',
|
||||||
|
name: 'Group Owner',
|
||||||
|
slug: 'group-owner',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
email: 'owner@example.org',
|
||||||
|
password: '1234',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
authenticatedUser = await groupOwner.toJson()
|
||||||
|
await mutate({
|
||||||
|
mutation: createGroupMutation(),
|
||||||
|
variables: {
|
||||||
|
id: 'closed-group',
|
||||||
|
name: 'The Closed Group',
|
||||||
|
about: 'Will test the closed group!',
|
||||||
|
description: 'Some description' + Array(50).join('_'),
|
||||||
|
groupType: 'public',
|
||||||
|
actionRadius: 'regional',
|
||||||
|
categoryIds,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('user joins group', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
authenticatedUser = await notifiedUser.toJson()
|
||||||
|
await mutate({
|
||||||
|
mutation: joinGroupMutation(),
|
||||||
|
variables: {
|
||||||
|
groupId: 'closed-group',
|
||||||
|
userId: authenticatedUser.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
authenticatedUser = await groupOwner.toJson()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has the notification in database', async () => {
|
||||||
|
await expect(
|
||||||
|
query({
|
||||||
|
query: notificationQuery,
|
||||||
|
}),
|
||||||
|
).resolves.toMatchObject({
|
||||||
|
data: {
|
||||||
|
notifications: [
|
||||||
|
{
|
||||||
|
read: false,
|
||||||
|
reason: 'user_joined_group',
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
from: {
|
||||||
|
__typename: 'Group',
|
||||||
|
id: 'closed-group',
|
||||||
|
},
|
||||||
|
relatedUser: {
|
||||||
|
id: 'you',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
errors: undefined,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('user leaves group', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
authenticatedUser = await notifiedUser.toJson()
|
||||||
|
await mutate({
|
||||||
|
mutation: joinGroupMutation(),
|
||||||
|
variables: {
|
||||||
|
groupId: 'closed-group',
|
||||||
|
userId: authenticatedUser.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await mutate({
|
||||||
|
mutation: leaveGroupMutation(),
|
||||||
|
variables: {
|
||||||
|
groupId: 'closed-group',
|
||||||
|
userId: authenticatedUser.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
authenticatedUser = await groupOwner.toJson()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has two the notification in database', async () => {
|
||||||
|
await expect(
|
||||||
|
query({
|
||||||
|
query: notificationQuery,
|
||||||
|
}),
|
||||||
|
).resolves.toMatchObject({
|
||||||
|
data: {
|
||||||
|
notifications: [
|
||||||
|
{
|
||||||
|
read: false,
|
||||||
|
reason: 'user_left_group',
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
from: {
|
||||||
|
__typename: 'Group',
|
||||||
|
id: 'closed-group',
|
||||||
|
},
|
||||||
|
relatedUser: {
|
||||||
|
id: 'you',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
read: false,
|
||||||
|
reason: 'user_joined_group',
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
from: {
|
||||||
|
__typename: 'Group',
|
||||||
|
id: 'closed-group',
|
||||||
|
},
|
||||||
|
relatedUser: {
|
||||||
|
id: 'you',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
errors: undefined,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('user role in group changes', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
authenticatedUser = await notifiedUser.toJson()
|
||||||
|
await mutate({
|
||||||
|
mutation: joinGroupMutation(),
|
||||||
|
variables: {
|
||||||
|
groupId: 'closed-group',
|
||||||
|
userId: authenticatedUser.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
authenticatedUser = await groupOwner.toJson()
|
||||||
|
await mutate({
|
||||||
|
mutation: changeGroupMemberRoleMutation(),
|
||||||
|
variables: {
|
||||||
|
groupId: 'closed-group',
|
||||||
|
userId: 'you',
|
||||||
|
roleInGroup: 'admin',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
authenticatedUser = await notifiedUser.toJson()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has notification in database', async () => {
|
||||||
|
await expect(
|
||||||
|
query({
|
||||||
|
query: notificationQuery,
|
||||||
|
}),
|
||||||
|
).resolves.toMatchObject({
|
||||||
|
data: {
|
||||||
|
notifications: [
|
||||||
|
{
|
||||||
|
read: false,
|
||||||
|
reason: 'changed_group_member_role',
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
from: {
|
||||||
|
__typename: 'Group',
|
||||||
|
id: 'closed-group',
|
||||||
|
},
|
||||||
|
relatedUser: {
|
||||||
|
id: 'group-owner',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
errors: undefined,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('user is removed from group', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
authenticatedUser = await notifiedUser.toJson()
|
||||||
|
await mutate({
|
||||||
|
mutation: joinGroupMutation(),
|
||||||
|
variables: {
|
||||||
|
groupId: 'closed-group',
|
||||||
|
userId: authenticatedUser.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
authenticatedUser = await groupOwner.toJson()
|
||||||
|
await mutate({
|
||||||
|
mutation: removeUserFromGroupMutation(),
|
||||||
|
variables: {
|
||||||
|
groupId: 'closed-group',
|
||||||
|
userId: 'you',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
authenticatedUser = await notifiedUser.toJson()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has notification in database', async () => {
|
||||||
|
await expect(
|
||||||
|
query({
|
||||||
|
query: notificationQuery,
|
||||||
|
}),
|
||||||
|
).resolves.toMatchObject({
|
||||||
|
data: {
|
||||||
|
notifications: [
|
||||||
|
{
|
||||||
|
read: false,
|
||||||
|
reason: 'removed_user_from_group',
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
from: {
|
||||||
|
__typename: 'Group',
|
||||||
|
id: 'closed-group',
|
||||||
|
},
|
||||||
|
relatedUser: {
|
||||||
|
id: 'group-owner',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
errors: undefined,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -47,12 +47,22 @@ export default {
|
|||||||
`
|
`
|
||||||
MATCH (resource {deleted: false, disabled: false})-[notification:NOTIFIED]->(user:User {id:$id})
|
MATCH (resource {deleted: false, disabled: false})-[notification:NOTIFIED]->(user:User {id:$id})
|
||||||
${whereClause}
|
${whereClause}
|
||||||
WITH user, notification, resource,
|
OPTIONAL MATCH (relatedUser:User { id: notification.relatedUserId })
|
||||||
|
OPTIONAL MATCH (resource)<-[membership:MEMBER_OF]-(relatedUser)
|
||||||
|
WITH user, notification, resource, membership, relatedUser,
|
||||||
[(resource)<-[:WROTE]-(author:User) | author {.*}] AS authors,
|
[(resource)<-[:WROTE]-(author:User) | author {.*}] AS authors,
|
||||||
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post{.*, author: properties(author)} ] AS posts
|
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post {.*, author: properties(author)} ] AS posts
|
||||||
WITH resource, user, notification, authors, posts,
|
WITH resource, user, notification, authors, posts, relatedUser, membership,
|
||||||
resource {.*, __typename: labels(resource)[0], author: authors[0], post: posts[0]} AS finalResource
|
resource {.*,
|
||||||
RETURN notification {.*, from: finalResource, to: properties(user)}
|
__typename: labels(resource)[0],
|
||||||
|
author: authors[0],
|
||||||
|
post: posts[0],
|
||||||
|
myRole: membership.role } AS finalResource
|
||||||
|
RETURN notification {.*,
|
||||||
|
from: finalResource,
|
||||||
|
to: properties(user),
|
||||||
|
relatedUser: properties(relatedUser)
|
||||||
|
}
|
||||||
${orderByClause}
|
${orderByClause}
|
||||||
${offset} ${limit}
|
${offset} ${limit}
|
||||||
`,
|
`,
|
||||||
@ -81,8 +91,9 @@ export default {
|
|||||||
WITH user, notification, resource,
|
WITH user, notification, resource,
|
||||||
[(resource)<-[:WROTE]-(author:User) | author {.*}] AS authors,
|
[(resource)<-[:WROTE]-(author:User) | author {.*}] AS authors,
|
||||||
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post{.*, author: properties(author)} ] AS posts
|
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post{.*, author: properties(author)} ] AS posts
|
||||||
WITH resource, user, notification, authors, posts,
|
OPTIONAL MATCH (resource)<-[membership:MEMBER_OF]-(user)
|
||||||
resource {.*, __typename: labels(resource)[0], author: authors[0], post: posts[0]} AS finalResource
|
WITH resource, user, notification, authors, posts, membership,
|
||||||
|
resource {.*, __typename: labels(resource)[0], author: authors[0], post: posts[0], myRole: membership.role } AS finalResource
|
||||||
RETURN notification {.*, from: finalResource, to: properties(user)}
|
RETURN notification {.*, from: finalResource, to: properties(user)}
|
||||||
`,
|
`,
|
||||||
{ resourceId: args.id, id: currentUser.id },
|
{ resourceId: args.id, id: currentUser.id },
|
||||||
@ -110,8 +121,9 @@ export default {
|
|||||||
WITH user, notification, resource,
|
WITH user, notification, resource,
|
||||||
[(resource)<-[:WROTE]-(author:User) | author {.*}] AS authors,
|
[(resource)<-[:WROTE]-(author:User) | author {.*}] AS authors,
|
||||||
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post{.*, author: properties(author)} ] AS posts
|
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post{.*, author: properties(author)} ] AS posts
|
||||||
WITH resource, user, notification, authors, posts,
|
OPTIONAL MATCH (resource)<-[membership:MEMBER_OF]-(user)
|
||||||
resource {.*, __typename: labels(resource)[0], author: authors[0], post: posts[0]} AS finalResource
|
WITH resource, user, notification, authors, posts, membership,
|
||||||
|
resource {.*, __typename: labels(resource)[0], author: authors[0], post: posts[0], myRole: membership.role} AS finalResource
|
||||||
RETURN notification {.*, from: finalResource, to: properties(user)}
|
RETURN notification {.*, from: finalResource, to: properties(user)}
|
||||||
`,
|
`,
|
||||||
{ id: currentUser.id },
|
{ id: currentUser.id },
|
||||||
|
|||||||
@ -397,18 +397,20 @@ describe('given some notifications', () => {
|
|||||||
|
|
||||||
it('returns all as read', async () => {
|
it('returns all as read', async () => {
|
||||||
const response = await mutate({ mutation: markAllAsReadMutation(), variables })
|
const response = await mutate({ mutation: markAllAsReadMutation(), variables })
|
||||||
expect(response.data.markAllAsRead).toEqual([
|
expect(response.data.markAllAsRead).toEqual(
|
||||||
{
|
expect.arrayContaining([
|
||||||
createdAt: '2019-08-30T19:33:48.651Z',
|
{
|
||||||
from: { __typename: 'Comment', content: 'You have been mentioned in a comment' },
|
createdAt: '2019-08-30T19:33:48.651Z',
|
||||||
read: true,
|
from: { __typename: 'Comment', content: 'You have been mentioned in a comment' },
|
||||||
},
|
read: true,
|
||||||
{
|
},
|
||||||
createdAt: '2019-08-31T17:33:48.651Z',
|
{
|
||||||
from: { __typename: 'Post', content: 'You have been mentioned in a post' },
|
createdAt: '2019-08-31T17:33:48.651Z',
|
||||||
read: true,
|
from: { __typename: 'Post', content: 'You have been mentioned in a post' },
|
||||||
},
|
read: true,
|
||||||
])
|
},
|
||||||
|
]),
|
||||||
|
)
|
||||||
expect(response.errors).toBeUndefined()
|
expect(response.errors).toBeUndefined()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
enum ReasonNotification {
|
|
||||||
mentioned_in_post
|
|
||||||
mentioned_in_comment
|
|
||||||
commented_on_post
|
|
||||||
}
|
|
||||||
@ -6,9 +6,10 @@ type NOTIFIED {
|
|||||||
updatedAt: String!
|
updatedAt: String!
|
||||||
read: Boolean
|
read: Boolean
|
||||||
reason: NotificationReason
|
reason: NotificationReason
|
||||||
|
relatedUser: User
|
||||||
}
|
}
|
||||||
|
|
||||||
union NotificationSource = Post | Comment
|
union NotificationSource = Post | Comment | Group
|
||||||
|
|
||||||
enum NotificationOrdering {
|
enum NotificationOrdering {
|
||||||
createdAt_asc
|
createdAt_asc
|
||||||
@ -21,6 +22,10 @@ enum NotificationReason {
|
|||||||
mentioned_in_post
|
mentioned_in_post
|
||||||
mentioned_in_comment
|
mentioned_in_comment
|
||||||
commented_on_post
|
commented_on_post
|
||||||
|
user_joined_group
|
||||||
|
user_left_group
|
||||||
|
changed_group_member_role
|
||||||
|
removed_user_from_group
|
||||||
}
|
}
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
|
|||||||
@ -63,7 +63,7 @@ export default {
|
|||||||
content: this.$t('group.joinLeaveButton.tooltip'),
|
content: this.$t('group.joinLeaveButton.tooltip'),
|
||||||
placement: 'right',
|
placement: 'right',
|
||||||
show: this.isMember && !this.isNonePendingMember && this.hovered,
|
show: this.isMember && !this.isNonePendingMember && this.hovered,
|
||||||
trigger: this.isMember && !this.isNonePendingMember ? 'hover' : 'manual',
|
trigger: 'manual',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,19 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<article :class="{ '--read': notification.read, notification: true }">
|
<article :class="{ '--read': notification.read, notification: true }">
|
||||||
<client-only>
|
<client-only>
|
||||||
<user-teaser :user="from.author" :date-time="from.createdAt" />
|
<user-teaser
|
||||||
|
:user="isGroup ? notification.relatedUser : from.author"
|
||||||
|
:date-time="from.createdAt"
|
||||||
|
/>
|
||||||
</client-only>
|
</client-only>
|
||||||
<p class="description">{{ $t(`notifications.reason.${notification.reason}`) }}</p>
|
<p class="description">{{ $t(`notifications.reason.${notification.reason}`) }}</p>
|
||||||
<nuxt-link
|
<nuxt-link
|
||||||
class="link"
|
class="link"
|
||||||
:to="{ name: 'post-id-slug', params, ...hashParam }"
|
:to="{ name: isGroup ? 'group-id-slug' : 'post-id-slug', params, hashParam }"
|
||||||
@click.native="$emit('read')"
|
@click.native="$emit('read')"
|
||||||
>
|
>
|
||||||
<base-card wideContent>
|
<base-card wideContent>
|
||||||
<h2 class="title">{{ from.title || from.post.title }}</h2>
|
<h2 class="title">{{ from.title || from.groupName || from.post.title }}</h2>
|
||||||
<p>
|
<p>
|
||||||
<strong v-if="isComment" class="comment">{{ $t(`notifications.comment`) }}:</strong>
|
<strong v-if="isComment" class="comment">{{ $t(`notifications.comment`) }}:</strong>
|
||||||
{{ from.contentExcerpt | removeHtml }}
|
{{ from.contentExcerpt | removeHtml }}
|
||||||
|
<strong v-if="isGroup" class="comment">{{ $t(`notifications.group`) }}:</strong>
|
||||||
|
{{ from.descriptionExcerpt | removeHtml }}
|
||||||
</p>
|
</p>
|
||||||
</base-card>
|
</base-card>
|
||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
@ -41,11 +46,14 @@ export default {
|
|||||||
isComment() {
|
isComment() {
|
||||||
return this.from.__typename === 'Comment'
|
return this.from.__typename === 'Comment'
|
||||||
},
|
},
|
||||||
|
isGroup() {
|
||||||
|
return this.from.__typename === 'Group'
|
||||||
|
},
|
||||||
params() {
|
params() {
|
||||||
const post = this.isComment ? this.from.post : this.from
|
const target = this.isComment ? this.from.post : this.from
|
||||||
return {
|
return {
|
||||||
id: post.id,
|
id: target.id,
|
||||||
slug: post.slug,
|
slug: target.slug,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hashParam() {
|
hashParam() {
|
||||||
|
|||||||
@ -39,7 +39,11 @@
|
|||||||
<ds-space margin-bottom="base">
|
<ds-space margin-bottom="base">
|
||||||
<client-only>
|
<client-only>
|
||||||
<user-teaser
|
<user-teaser
|
||||||
:user="notification.from.author"
|
:user="
|
||||||
|
isGroup(notification.from)
|
||||||
|
? notification.relatedUser
|
||||||
|
: notification.from.author
|
||||||
|
"
|
||||||
:date-time="notification.from.createdAt"
|
:date-time="notification.from.createdAt"
|
||||||
:class="{ 'notification-status': notification.read }"
|
:class="{ 'notification-status': notification.read }"
|
||||||
/>
|
/>
|
||||||
@ -61,14 +65,18 @@
|
|||||||
class="notification-mention-post"
|
class="notification-mention-post"
|
||||||
:class="{ 'notification-status': notification.read }"
|
:class="{ 'notification-status': notification.read }"
|
||||||
:to="{
|
:to="{
|
||||||
name: 'post-id-slug',
|
name: isGroup(notification.from) ? 'group-id-slug' : 'post-id-slug',
|
||||||
params: params(notification.from),
|
params: params(notification.from),
|
||||||
hash: hashParam(notification.from),
|
hash: hashParam(notification.from),
|
||||||
}"
|
}"
|
||||||
@click.native="markNotificationAsRead(notification.from.id)"
|
@click.native="markNotificationAsRead(notification.from.id)"
|
||||||
>
|
>
|
||||||
<b>
|
<b>
|
||||||
{{ notification.from.title || notification.from.post.title | truncate(50) }}
|
{{
|
||||||
|
notification.from.title ||
|
||||||
|
notification.from.groupName ||
|
||||||
|
notification.from.post.title | truncate(50)
|
||||||
|
}}
|
||||||
</b>
|
</b>
|
||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
</base-card>
|
</base-card>
|
||||||
@ -76,7 +84,10 @@
|
|||||||
<ds-flex-item>
|
<ds-flex-item>
|
||||||
<base-card :wide-content="true">
|
<base-card :wide-content="true">
|
||||||
<b :class="{ 'notification-status': notification.read }">
|
<b :class="{ 'notification-status': notification.read }">
|
||||||
{{ notification.from.contentExcerpt | removeHtml }}
|
{{
|
||||||
|
notification.from.contentExcerpt ||
|
||||||
|
notification.from.descriptionExcerpt | removeHtml
|
||||||
|
}}
|
||||||
</b>
|
</b>
|
||||||
</base-card>
|
</base-card>
|
||||||
</ds-flex-item>
|
</ds-flex-item>
|
||||||
@ -132,11 +143,16 @@ export default {
|
|||||||
isComment(notificationSource) {
|
isComment(notificationSource) {
|
||||||
return notificationSource.__typename === 'Comment'
|
return notificationSource.__typename === 'Comment'
|
||||||
},
|
},
|
||||||
|
isGroup(notificationSource) {
|
||||||
|
return notificationSource.__typename === 'Group'
|
||||||
|
},
|
||||||
params(notificationSource) {
|
params(notificationSource) {
|
||||||
const post = this.isComment(notificationSource) ? notificationSource.post : notificationSource
|
const target = this.isComment(notificationSource)
|
||||||
|
? notificationSource.post
|
||||||
|
: notificationSource
|
||||||
return {
|
return {
|
||||||
id: post.id,
|
id: target.id,
|
||||||
slug: post.slug,
|
slug: target.slug,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hashParam(notificationSource) {
|
hashParam(notificationSource) {
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import {
|
|||||||
userFragment,
|
userFragment,
|
||||||
postFragment,
|
postFragment,
|
||||||
commentFragment,
|
commentFragment,
|
||||||
|
groupFragment,
|
||||||
} from './Fragments'
|
} from './Fragments'
|
||||||
|
|
||||||
export const profileUserQuery = (i18n) => {
|
export const profileUserQuery = (i18n) => {
|
||||||
@ -113,6 +114,7 @@ export const notificationQuery = (_i18n) => {
|
|||||||
${userFragment}
|
${userFragment}
|
||||||
${commentFragment}
|
${commentFragment}
|
||||||
${postFragment}
|
${postFragment}
|
||||||
|
${groupFragment}
|
||||||
|
|
||||||
query ($read: Boolean, $orderBy: NotificationOrdering, $first: Int, $offset: Int) {
|
query ($read: Boolean, $orderBy: NotificationOrdering, $first: Int, $offset: Int) {
|
||||||
notifications(read: $read, orderBy: $orderBy, first: $first, offset: $offset) {
|
notifications(read: $read, orderBy: $orderBy, first: $first, offset: $offset) {
|
||||||
@ -121,6 +123,9 @@ export const notificationQuery = (_i18n) => {
|
|||||||
reason
|
reason
|
||||||
createdAt
|
createdAt
|
||||||
updatedAt
|
updatedAt
|
||||||
|
to {
|
||||||
|
...user
|
||||||
|
}
|
||||||
from {
|
from {
|
||||||
__typename
|
__typename
|
||||||
... on Post {
|
... on Post {
|
||||||
@ -141,6 +146,12 @@ export const notificationQuery = (_i18n) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
... on Group {
|
||||||
|
...group
|
||||||
|
}
|
||||||
|
}
|
||||||
|
relatedUser {
|
||||||
|
...user
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,6 +163,7 @@ export const markAsReadMutation = (_i18n) => {
|
|||||||
${userFragment}
|
${userFragment}
|
||||||
${commentFragment}
|
${commentFragment}
|
||||||
${postFragment}
|
${postFragment}
|
||||||
|
${groupFragment}
|
||||||
|
|
||||||
mutation ($id: ID!) {
|
mutation ($id: ID!) {
|
||||||
markAsRead(id: $id) {
|
markAsRead(id: $id) {
|
||||||
@ -177,6 +189,9 @@ export const markAsReadMutation = (_i18n) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
... on Group {
|
||||||
|
...group
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -188,6 +203,7 @@ export const markAllAsReadMutation = (_i18n) => {
|
|||||||
${userFragment}
|
${userFragment}
|
||||||
${commentFragment}
|
${commentFragment}
|
||||||
${postFragment}
|
${postFragment}
|
||||||
|
${groupFragment}
|
||||||
|
|
||||||
mutation {
|
mutation {
|
||||||
markAllAsRead {
|
markAllAsRead {
|
||||||
@ -213,6 +229,9 @@ export const markAllAsReadMutation = (_i18n) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
... on Group {
|
||||||
|
...group
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -640,20 +640,25 @@
|
|||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"comment": "Kommentar",
|
"comment": "Kommentar",
|
||||||
"content": "Inhalt",
|
"content": "Inhalt oder Beschreibung",
|
||||||
"empty": "Bedaure, Du hast momentan keinerlei Benachrichtigungen.",
|
"empty": "Bedaure, Du hast momentan keinerlei Benachrichtigungen.",
|
||||||
"filterLabel": {
|
"filterLabel": {
|
||||||
"all": "Alle",
|
"all": "Alle",
|
||||||
"read": "Gelesen",
|
"read": "Gelesen",
|
||||||
"unread": "Ungelesen"
|
"unread": "Ungelesen"
|
||||||
},
|
},
|
||||||
|
"group": "Beschreibung",
|
||||||
"markAllAsRead": "Markiere alle als gelesen",
|
"markAllAsRead": "Markiere alle als gelesen",
|
||||||
"pageLink": "Alle Benachrichtigungen",
|
"pageLink": "Alle Benachrichtigungen",
|
||||||
"post": "Beitrag",
|
"post": "Beitrag oder Gruppe",
|
||||||
"reason": {
|
"reason": {
|
||||||
|
"changed_group_member_role": "Hat Deine Rolle in der Gruppe geändert …",
|
||||||
"commented_on_post": "Hat Deinen Beitrag kommentiert …",
|
"commented_on_post": "Hat Deinen Beitrag kommentiert …",
|
||||||
"mentioned_in_comment": "Hat Dich in einem Kommentar erwähnt …",
|
"mentioned_in_comment": "Hat Dich in einem Kommentar erwähnt …",
|
||||||
"mentioned_in_post": "Hat Dich in einem Beitrag erwähnt …"
|
"mentioned_in_post": "Hat Dich in einem Beitrag erwähnt …",
|
||||||
|
"removed_user_from_group": "Hat Dich aus der Gruppe entfernt …",
|
||||||
|
"user_joined_group": "Ist Deiner Gruppe beigetreten …",
|
||||||
|
"user_left_group": "Hat deine Gruppe verlassen …"
|
||||||
},
|
},
|
||||||
"title": "Benachrichtigungen",
|
"title": "Benachrichtigungen",
|
||||||
"user": "Nutzer"
|
"user": "Nutzer"
|
||||||
|
|||||||
@ -640,20 +640,25 @@
|
|||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"comment": "Comment",
|
"comment": "Comment",
|
||||||
"content": "Content",
|
"content": "Content or Description",
|
||||||
"empty": "Sorry, you don't have any notifications at the moment.",
|
"empty": "Sorry, you don't have any notifications at the moment.",
|
||||||
"filterLabel": {
|
"filterLabel": {
|
||||||
"all": "All",
|
"all": "All",
|
||||||
"read": "Read",
|
"read": "Read",
|
||||||
"unread": "Unread"
|
"unread": "Unread"
|
||||||
},
|
},
|
||||||
|
"group": "Description",
|
||||||
"markAllAsRead": "Mark all as read",
|
"markAllAsRead": "Mark all as read",
|
||||||
"pageLink": "All notifications",
|
"pageLink": "All notifications",
|
||||||
"post": "Post",
|
"post": "Post or Group",
|
||||||
"reason": {
|
"reason": {
|
||||||
|
"changed_group_member_role": "Changed your role in group …",
|
||||||
"commented_on_post": "Commented on your post …",
|
"commented_on_post": "Commented on your post …",
|
||||||
"mentioned_in_comment": "Mentioned you in a comment …",
|
"mentioned_in_comment": "Mentioned you in a comment …",
|
||||||
"mentioned_in_post": "Mentioned you in a post …"
|
"mentioned_in_post": "Mentioned you in a post …",
|
||||||
|
"removed_user_from_group": "Removed you from group …",
|
||||||
|
"user_joined_group": "Joined your group …",
|
||||||
|
"user_left_group": "Left your group …"
|
||||||
},
|
},
|
||||||
"title": "Notifications",
|
"title": "Notifications",
|
||||||
"user": "User"
|
"user": "User"
|
||||||
|
|||||||
@ -565,7 +565,7 @@ export default {
|
|||||||
// },
|
// },
|
||||||
prepareJoinLeave() {
|
prepareJoinLeave() {
|
||||||
// "membersCountStartValue" is updated to avoid counting from 0 when join/leave
|
// "membersCountStartValue" is updated to avoid counting from 0 when join/leave
|
||||||
this.membersCountStartValue = this.GroupMembers.length
|
this.membersCountStartValue = (this.GroupMembers && this.GroupMembers.length) || 0
|
||||||
},
|
},
|
||||||
updateJoinLeave({ myRoleInGroup }) {
|
updateJoinLeave({ myRoleInGroup }) {
|
||||||
this.Group[0].myRole = myRoleInGroup
|
this.Group[0].myRole = myRoleInGroup
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user