Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(webapp): 🍰 allows mark all notifications as read #3922

Merged
merged 26 commits into from
Mar 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0bffede
feat: add mutation for new functionality (mark-all-as-read)
ftonato Oct 24, 2020
e37fb53
feat: add translations for notifications label (mark all as read)
ftonato Oct 24, 2020
1df69c9
feat: add tests for mark all as read (backend)
ftonato Oct 24, 2020
bfe66ad
feat: add button to mark all notifications as read on notification menu
ftonato Oct 24, 2020
d831e14
feat: add button to mark all notifications as read on notification page
ftonato Oct 24, 2020
4a23087
feat: add tests for mark all as read (webapp)
ftonato Oct 24, 2020
cc99e27
refactor: updating locales in alphabetical order
ftonato Oct 27, 2020
ae4f53e
Correct backend test
Tirokk Nov 2, 2020
a94d718
Merge branch 'master' of https://github.com/Ocelot-Social-Community/O…
Tirokk Nov 12, 2020
89e0340
fix: update based on code-review suggestions
ftonato Nov 5, 2020
7f345c3
fix: update based on code-review suggestions
ftonato Nov 5, 2020
e1ef6f8
fix: update tests for markAllAsRead
ftonato Nov 5, 2020
b404355
Fix linting
Tirokk Nov 16, 2020
b398eab
Merge branch 'master' of github.com:Ocelot-Social-Community/Ocelot-So…
Tirokk Nov 16, 2022
e1060b4
Fix Neo4j README
Tirokk Nov 16, 2022
dd80dc7
Fix unused parameters naming
Tirokk Nov 16, 2022
3a96836
Fix locales to be 'null' instead of '""'
Tirokk Nov 16, 2022
8234615
Fix template slot notation
Tirokk Nov 16, 2022
4ebae57
Add 'pl' translation
Tirokk Nov 16, 2022
61effbe
Refine 'NotificationMenu.vue'
Tirokk Nov 16, 2022
d26c37a
Refine notifications page
Tirokk Nov 16, 2022
f63a12d
Move GQL in notification tests in separate file
Tirokk Nov 16, 2022
bdc1cb3
Merge branch 'master' into feature/mark-all-notification-as-read
Mogge Mar 8, 2023
b64e35c
fix typo
Mogge Mar 8, 2023
c865b72
refresh notifications after mark all as read, fix tests
Mogge Mar 8, 2023
5bd8317
Merge branch 'master' into feature/mark-all-notification-as-read
Mogge Mar 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions backend/src/graphql/notifications.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import gql from 'graphql-tag'

// ------ mutations

export const markAsReadMutation = () => {
return gql`
mutation ($id: ID!) {
markAsRead(id: $id) {
from {
__typename
... on Post {
content
}
... on Comment {
content
}
}
read
createdAt
}
}
`
}

export const markAllAsReadMutation = () => {
return gql`
mutation {
markAllAsRead {
from {
__typename
... on Post {
content
}
... on Comment {
content
}
}
read
createdAt
}
}
`
}

// ------ queries

export const notificationQuery = () => {
return gql`
query ($read: Boolean, $orderBy: NotificationOrdering) {
notifications(read: $read, orderBy: $orderBy) {
from {
__typename
... on Post {
content
}
... on Comment {
content
}
}
read
createdAt
}
}
`
}
1 change: 1 addition & 0 deletions backend/src/middleware/permissionsMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ export default shield(
blockUser: isAuthenticated,
unblockUser: isAuthenticated,
markAsRead: isAuthenticated,
markAllAsRead: isAuthenticated,
AddEmailAddress: isAuthenticated,
VerifyEmailAddress: isAuthenticated,
pinPost: isAdmin,
Expand Down
29 changes: 29 additions & 0 deletions backend/src/schema/resolvers/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,35 @@ export default {
session.close()
}
},
markAllAsRead: async (parent, args, context, resolveInfo) => {
const { user: currentUser } = context
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
const markAllNotificationAsReadTransactionResponse = await transaction.run(
`
MATCH (resource)-[notification:NOTIFIED {read: FALSE}]->(user:User {id:$id})
SET notification.read = TRUE
WITH user, notification, resource,
[(resource)<-[:WROTE]-(author:User) | author {.*}] AS authors,
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post{.*, author: properties(author)} ] AS posts
WITH resource, user, notification, authors, posts,
resource {.*, __typename: labels(resource)[0], author: authors[0], post: posts[0]} AS finalResource
RETURN notification {.*, from: finalResource, to: properties(user)}
`,
{ id: currentUser.id },
)
log(markAllNotificationAsReadTransactionResponse)
return markAllNotificationAsReadTransactionResponse.records.map((record) =>
record.get('notification'),
)
})
try {
const notifications = await writeTxResultPromise
return notifications
} finally {
session.close()
}
},
},
NOTIFIED: {
id: async (parent) => {
Expand Down
101 changes: 57 additions & 44 deletions backend/src/schema/resolvers/notifications.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import gql from 'graphql-tag'
import { getDriver } from '../../db/neo4j'
import { createTestClient } from 'apollo-server-testing'
import createServer from '../.././server'
import {
markAsReadMutation,
markAllAsReadMutation,
notificationQuery,
} from '../../graphql/notifications'

const driver = getDriver()
let authenticatedUser
Expand Down Expand Up @@ -146,26 +151,9 @@ describe('given some notifications', () => {
})

describe('notifications', () => {
const notificationQuery = gql`
query ($read: Boolean, $orderBy: NotificationOrdering) {
notifications(read: $read, orderBy: $orderBy) {
from {
__typename
... on Post {
content
}
... on Comment {
content
}
}
read
createdAt
}
}
`
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await query({ query: notificationQuery })
const { errors } = await query({ query: notificationQuery() })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
Expand Down Expand Up @@ -212,7 +200,7 @@ describe('given some notifications', () => {
},
]

await expect(query({ query: notificationQuery, variables })).resolves.toMatchObject({
await expect(query({ query: notificationQuery(), variables })).resolves.toMatchObject({
data: {
notifications: expect.arrayContaining(expected),
},
Expand Down Expand Up @@ -246,7 +234,7 @@ describe('given some notifications', () => {
},
})
const response = await query({
query: notificationQuery,
query: notificationQuery(),
variables: { ...variables, read: false },
})
await expect(response).toMatchObject(expected)
Expand Down Expand Up @@ -275,14 +263,14 @@ describe('given some notifications', () => {

it('reduces notifications list', async () => {
await expect(
query({ query: notificationQuery, variables: { ...variables, read: false } }),
query({ query: notificationQuery(), variables: { ...variables, read: false } }),
).resolves.toMatchObject({
data: { notifications: [expect.any(Object), expect.any(Object)] },
errors: undefined,
})
await deletePostAction()
await expect(
query({ query: notificationQuery, variables: { ...variables, read: false } }),
query({ query: notificationQuery(), variables: { ...variables, read: false } }),
).resolves.toMatchObject({ data: { notifications: [] }, errors: undefined })
})
})
Expand All @@ -291,27 +279,10 @@ describe('given some notifications', () => {
})

describe('markAsRead', () => {
const markAsReadMutation = gql`
mutation ($id: ID!) {
markAsRead(id: $id) {
from {
__typename
... on Post {
content
}
... on Comment {
content
}
}
read
createdAt
}
}
`
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const result = await mutate({
mutation: markAsReadMutation,
mutation: markAsReadMutation(),
variables: { ...variables, id: 'p1' },
})
expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!')
Expand All @@ -332,7 +303,7 @@ describe('given some notifications', () => {
})

it('returns null', async () => {
const response = await mutate({ mutation: markAsReadMutation, variables })
const response = await mutate({ mutation: markAsReadMutation(), variables })
expect(response.data.markAsRead).toEqual(null)
expect(response.errors).toBeUndefined()
})
Expand All @@ -348,7 +319,7 @@ describe('given some notifications', () => {
})

it('updates `read` attribute and returns NOTIFIED relationship', async () => {
const { data } = await mutate({ mutation: markAsReadMutation, variables })
const { data } = await mutate({ mutation: markAsReadMutation(), variables })
expect(data).toEqual({
markAsRead: {
from: {
Expand All @@ -369,7 +340,7 @@ describe('given some notifications', () => {
}
})
it('returns null', async () => {
const response = await mutate({ mutation: markAsReadMutation, variables })
const response = await mutate({ mutation: markAsReadMutation(), variables })
expect(response.data.markAsRead).toEqual(null)
expect(response.errors).toBeUndefined()
})
Expand All @@ -385,7 +356,7 @@ describe('given some notifications', () => {
})

it('updates `read` attribute and returns NOTIFIED relationship', async () => {
const { data } = await mutate({ mutation: markAsReadMutation, variables })
const { data } = await mutate({ mutation: markAsReadMutation(), variables })
expect(data).toEqual({
markAsRead: {
from: {
Expand All @@ -401,4 +372,46 @@ describe('given some notifications', () => {
})
})
})

describe('markAllAsRead', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const result = await mutate({
mutation: markAllAsReadMutation(),
})
expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})

describe('authenticated', () => {
beforeEach(async () => {
authenticatedUser = await user.toJson()
})

describe('not being notified at all', () => {
beforeEach(async () => {
variables = {
...variables,
}
})

it('returns all as read', async () => {
const response = await mutate({ mutation: markAllAsReadMutation(), variables })
expect(response.data.markAllAsRead).toEqual([
{
createdAt: '2019-08-30T19:33:48.651Z',
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' },
read: true,
},
])
expect(response.errors).toBeUndefined()
})
})
})
})
})
1 change: 1 addition & 0 deletions backend/src/schema/types/type/NOTIFIED.gql
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type Query {

type Mutation {
markAsRead(id: ID!): NOTIFIED
markAllAsRead: [NOTIFIED]
}

type Subscription {
Expand Down
2 changes: 1 addition & 1 deletion neo4j/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Start Neo4J and confirm the database is running at [http://localhost:7474](http:

Here we describe some rarely used Cypher commands for Neo4j that are needed from time to time:

### Index And Contraint Commands
### Index And Constraint Commands

If indexes or constraints are missing or not set correctly, the browser search will not work or the database seed for development will not work.

Expand Down
2 changes: 1 addition & 1 deletion webapp/components/FilterMenu/CategoriesMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<a href="#" slot="default" slot-scope="{ toggleMenu }" @click.prevent="toggleMenu()">
<ds-text bold size="large">{{ $t('admin.categories.name') }}</ds-text>
</a>
<template slot="popover">
<template #popover>
<div class="category-menu-options">
<h2 class="title">{{ $t('filter-menu.filter-by') }}</h2>
<categories-filter v-if="categoriesActive" />
Expand Down
2 changes: 1 addition & 1 deletion webapp/components/FilterMenu/FilterMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
>
<base-icon class="dropdown-arrow" name="angle-down" />
</base-button>
<template slot="popover">
<template #popover>
<filter-menu-component />
</template>
</dropdown>
Expand Down
Loading