Merge pull request #1658 from Human-Connection/1637-notifications-self-update

🍰 Notifications self update and refactoring
This commit is contained in:
Robert Schäfer 2019-09-30 17:52:41 +02:00 committed by GitHub
commit 1fed79a674
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 121 additions and 29 deletions

View File

@ -9,4 +9,4 @@
],
"editor.formatOnSave": false,
"eslint.autoFixOnSave": true
}
}

View File

@ -351,10 +351,12 @@ When("I log in with the following credentials:", table => {
});
When("open the notification menu and click on the first item", () => {
cy.get(".notifications-menu").click();
cy.get(".notifications-menu").invoke('show').click(); // "invoke('show')" because of the delay for show the menu
cy.get(".notification-mention-post")
.first()
.click();
.click({
force: true
});
});
Then("see {int} unread notifications in the top menu", count => {

View File

@ -1,4 +1,4 @@
Feature: Notifications for a mentions
Feature: Notification for a mention
As a user
I want to be notified if sb. mentions me in a post or comment
In order join conversations about or related to me

View File

@ -69,16 +69,16 @@ describe('Notification', () => {
it('renders reason', () => {
wrapper = Wrapper()
expect(wrapper.find('.reason-text-for-test').text()).toEqual(
'notifications.menu.commented_on_post',
'notifications.reason.commented_on_post',
)
})
it('renders title', () => {
wrapper = Wrapper()
expect(wrapper.text()).toContain("It's a post title")
})
it('renders the "Comment:"', () => {
it('renders the identifier "notifications.comment"', () => {
wrapper = Wrapper()
expect(wrapper.text()).toContain('Comment:')
expect(wrapper.text()).toContain('notifications.comment')
})
it('renders the contentExcerpt', () => {
wrapper = Wrapper()
@ -119,7 +119,7 @@ describe('Notification', () => {
it('renders reason', () => {
wrapper = Wrapper()
expect(wrapper.find('.reason-text-for-test').text()).toEqual(
'notifications.menu.mentioned_in_post',
'notifications.reason.mentioned_in_post',
)
})
it('renders title', () => {
@ -169,7 +169,7 @@ describe('Notification', () => {
it('renders reason', () => {
wrapper = Wrapper()
expect(wrapper.find('.reason-text-for-test').text()).toEqual(
'notifications.menu.mentioned_in_comment',
'notifications.reason.mentioned_in_comment',
)
})
it('renders title', () => {
@ -177,9 +177,9 @@ describe('Notification', () => {
expect(wrapper.text()).toContain("It's a post title")
})
it('renders the "Comment:"', () => {
it('renders the identifier "notifications.comment"', () => {
wrapper = Wrapper()
expect(wrapper.text()).toContain('Comment:')
expect(wrapper.text()).toContain('notifications.comment')
})
it('renders the contentExcerpt', () => {

View File

@ -5,7 +5,7 @@
<hc-user :user="from.author" :date-time="from.createdAt" :trunc="35" />
</ds-space>
<ds-text class="reason-text-for-test" color="soft">
{{ $t(`notifications.menu.${notification.reason}`) }}
{{ $t(`notifications.reason.${notification.reason}`) }}
</ds-text>
</client-only>
<ds-space margin-bottom="x-small" />
@ -23,7 +23,9 @@
>
<ds-space margin-bottom="x-small" />
<div>
<span v-if="isComment" class="comment-notification-header">Comment:</span>
<span v-if="isComment" class="comment-notification-header">
{{ $t(`notifications.comment`) }}:
</span>
{{ from.contentExcerpt | removeHtml }}
</div>
</ds-card>

View File

@ -46,11 +46,46 @@ describe('NotificationMenu.vue', () => {
expect(wrapper.contains('.dropdown')).toBe(false)
})
describe('given only unread notifications', () => {
beforeEach(() => {
data = () => {
return {
displayedNotifications: [
{
id: 'notification-41',
read: true,
post: {
id: 'post-1',
title: 'some post title',
contentExcerpt: 'this is a post content',
author: {
id: 'john-1',
slug: 'john-doe',
name: 'John Doe',
},
},
},
],
}
}
})
it('counter displays 0', () => {
wrapper = Wrapper()
expect(wrapper.find('ds-button-stub').text()).toEqual('0')
})
it('button is not primary', () => {
wrapper = Wrapper()
expect(wrapper.find('ds-button-stub').props('primary')).toBe(false)
})
})
describe('given some notifications', () => {
beforeEach(() => {
data = () => {
return {
notifications: [
displayedNotifications: [
{
id: 'notification-41',
read: false,
@ -79,15 +114,34 @@ describe('NotificationMenu.vue', () => {
},
},
},
{
id: 'notification-43',
read: true,
post: {
id: 'post-3',
title: 'read post title',
contentExcerpt: 'this is yet another post content',
author: {
id: 'john-1',
slug: 'john-doe',
name: 'John Doe',
},
},
},
],
}
}
})
it('displays the total number of notifications', () => {
it('displays the number of unread notifications', () => {
wrapper = Wrapper()
expect(wrapper.find('ds-button-stub').text()).toEqual('2')
})
it('renders primary button', () => {
wrapper = Wrapper()
expect(wrapper.find('ds-button-stub').props('primary')).toBe(true)
})
})
})
})

View File

@ -1,16 +1,16 @@
<template>
<ds-button v-if="totalNotifications <= 0" class="notifications-menu" disabled icon="bell">
{{ totalNotifications }}
<ds-button v-if="!notificationsCount" class="notifications-menu" disabled icon="bell">
{{ unreadNotificationsCount }}
</ds-button>
<dropdown v-else class="notifications-menu" :placement="placement">
<template slot="default" slot-scope="{ toggleMenu }">
<ds-button primary icon="bell" @click.prevent="toggleMenu">
{{ totalNotifications }}
<ds-button :primary="!!unreadNotificationsCount" icon="bell" @click.prevent="toggleMenu">
{{ unreadNotificationsCount }}
</ds-button>
</template>
<template slot="popover">
<div class="notifications-menu-popover">
<notification-list :notifications="notifications" @markAsRead="markAsRead" />
<notification-list :notifications="displayedNotifications" @markAsRead="markAsRead" />
</div>
</template>
</dropdown>
@ -18,6 +18,7 @@
<script>
import Dropdown from '~/components/Dropdown'
import { NOTIFICATIONS_POLL_INTERVAL } from '~/constants/notifications'
import { notificationQuery, markAsReadMutation } from '~/graphql/User'
import NotificationList from '../NotificationList/NotificationList'
@ -29,6 +30,7 @@ export default {
},
data() {
return {
displayedNotifications: [],
notifications: [],
}
},
@ -46,17 +48,29 @@ export default {
variables,
})
if (!(markAsRead && markAsRead.read === true)) return
this.notifications = this.notifications.map(n => {
return n.from.id === markAsRead.from.id ? markAsRead : n
this.displayedNotifications = this.displayedNotifications.map(n => {
return this.equalNotification(n, markAsRead) ? markAsRead : n
})
} catch (err) {
throw new Error(err)
this.$toast.error(err.message)
}
},
equalNotification(a, b) {
return a.from.id === b.from.id && a.createdAt === b.createdAt && a.reason === b.reason
},
},
computed: {
totalNotifications() {
return (this.notifications || []).length
notificationsCount() {
return (this.displayedNotifications || []).length
},
unreadNotificationsCount() {
let countUnread = 0
if (this.displayedNotifications) {
this.displayedNotifications.forEach(notification => {
if (!notification.read) countUnread++
})
}
return countUnread
},
},
apollo: {
@ -64,6 +78,23 @@ export default {
query() {
return notificationQuery(this.$i18n)
},
pollInterval() {
return NOTIFICATIONS_POLL_INTERVAL
},
update(data) {
const newNotifications = data.notifications.filter(newN => {
return !this.displayedNotifications.find(oldN => this.equalNotification(newN, oldN))
})
this.displayedNotifications = newNotifications
.concat(this.displayedNotifications)
.sort((a, b) => {
return new Date(b.createdAt) - new Date(a.createdAt)
})
return data.notifications
},
error(error) {
this.$toast.error(error)
},
},
},
}

View File

@ -0,0 +1 @@
export const NOTIFICATIONS_POLL_INTERVAL = 60000

View File

@ -135,11 +135,12 @@
}
},
"notifications": {
"menu": {
"reason": {
"mentioned_in_post": "Hat dich in einem Beitrag erwähnt …",
"mentioned_in_comment": "Hat dich in einem Kommentar erwähnt …",
"commented_on_post": "Hat deinen Beitrag kommentiert …"
}
},
"comment": "Kommentar"
},
"search": {
"placeholder": "Suchen",

View File

@ -136,11 +136,12 @@
}
},
"notifications": {
"menu": {
"reason": {
"mentioned_in_post": "Mentioned you in a post …",
"mentioned_in_comment": "Mentioned you in a comment …",
"commented_on_post": "Commented on your post …"
}
},
"comment": "Comment"
},
"search": {
"placeholder": "Search",