mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-12 23:35:58 +00:00
Subscribe to notifications/remove polling
- We want to publish when a notification occurs for a specific user, not have the client poll the backend for ever user every minute. - Co-authored-by: @Tirokk <wolle.huss@pjannto.com>
This commit is contained in:
parent
04f0467d2d
commit
2f43069ea0
@ -1,10 +1,11 @@
|
||||
import createServer from './server'
|
||||
import CONFIG from './config'
|
||||
|
||||
const { app, server, httpServer } = createServer()
|
||||
const { server, httpServer } = createServer()
|
||||
const url = new URL(CONFIG.GRAPHQL_URI)
|
||||
httpServer.listen({ port: url.port }, () => {
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.log(`🚀 Server ready at http://localhost:${url.port}${server.graphqlPath}`)
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.log(`🚀 Subscriptions ready at ws://localhost:${url.port}${server.subscriptionsPath}`)
|
||||
})
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import extractMentionedUsers from './mentions/extractMentionedUsers'
|
||||
import { validateNotifyUsers } from '../validation/validationMiddleware'
|
||||
import { PubSub } from 'apollo-server'
|
||||
|
||||
const pubsub = new PubSub()
|
||||
const NOTIFICATION_ADDED = 'NOTIFICATION_ADDED'
|
||||
import {
|
||||
pubsub,
|
||||
NOTIFICATION_ADDED,
|
||||
transformReturnType,
|
||||
} from '../../schema/resolvers/notifications'
|
||||
|
||||
const handleContentDataOfPost = async (resolve, root, args, context, resolveInfo) => {
|
||||
const idsOfUsers = extractMentionedUsers(args.content)
|
||||
@ -56,6 +57,7 @@ const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => {
|
||||
WHERE user.id in $idsOfUsers
|
||||
AND NOT (user)-[:BLOCKED]-(author)
|
||||
MERGE (post)-[notification:NOTIFIED {reason: $reason}]->(user)
|
||||
WITH notification, post AS resource, user
|
||||
`
|
||||
break
|
||||
}
|
||||
@ -67,6 +69,7 @@ const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => {
|
||||
AND NOT (user)-[:BLOCKED]-(author)
|
||||
AND NOT (user)-[:BLOCKED]-(postAuthor)
|
||||
MERGE (comment)-[notification:NOTIFIED {reason: $reason}]->(user)
|
||||
WITH notification, comment AS resource, user
|
||||
`
|
||||
break
|
||||
}
|
||||
@ -78,12 +81,16 @@ const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => {
|
||||
WHEN notification.createdAt IS NULL
|
||||
THEN notification END ).createdAt = toString(datetime())
|
||||
SET notification.updatedAt = toString(datetime())
|
||||
RETURN notification
|
||||
RETURN notification, resource, user, labels(resource)[0] AS type
|
||||
`
|
||||
const session = context.driver.session()
|
||||
const writeTxResultPromise = session.writeTransaction(async transaction => {
|
||||
const notificationTransactionResponse = await transaction.run(mentionedCypher, { id, idsOfUsers, reason })
|
||||
return notificationTransactionResponse.records.map(record => record.get('notification').properties)
|
||||
const notificationTransactionResponse = await transaction.run(mentionedCypher, {
|
||||
id,
|
||||
idsOfUsers,
|
||||
reason,
|
||||
})
|
||||
return notificationTransactionResponse.records.map(transformReturnType)
|
||||
})
|
||||
try {
|
||||
const [notification] = await writeTxResultPromise
|
||||
|
||||
@ -1,16 +1,14 @@
|
||||
import log from './helpers/databaseLogger'
|
||||
import { PubSub } from 'apollo-server'
|
||||
import { withFilter } from 'graphql-subscriptions'
|
||||
|
||||
const pubsub = new PubSub()
|
||||
const NOTIFICATION_ADDED = 'NOTIFICATION_ADDED'
|
||||
|
||||
const resourceTypes = ['Post', 'Comment']
|
||||
|
||||
const transformReturnType = record => {
|
||||
export const pubsub = new PubSub()
|
||||
export const NOTIFICATION_ADDED = 'NOTIFICATION_ADDED'
|
||||
export const transformReturnType = record => {
|
||||
return {
|
||||
...record.get('notification').properties,
|
||||
from: {
|
||||
__typename: record.get('resource').labels.find(l => resourceTypes.includes(l)),
|
||||
__typename: record.get('type'),
|
||||
...record.get('resource').properties,
|
||||
},
|
||||
to: {
|
||||
@ -22,7 +20,12 @@ const transformReturnType = record => {
|
||||
export default {
|
||||
Subscription: {
|
||||
notificationAdded: {
|
||||
subscribe: () => pubsub.asyncIterator([NOTIFICATION_ADDED]),
|
||||
subscribe: withFilter(
|
||||
() => pubsub.asyncIterator(NOTIFICATION_ADDED),
|
||||
(payload, variables) => {
|
||||
return payload.notificationAdded.to.id === variables.userId
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
Query: {
|
||||
@ -90,7 +93,7 @@ export default {
|
||||
`
|
||||
MATCH (resource {id: $resourceId})-[notification:NOTIFIED {read: FALSE}]->(user:User {id:$id})
|
||||
SET notification.read = TRUE
|
||||
RETURN resource, notification, user
|
||||
RETURN resource, notification, user, labels(resource)[0] AS type
|
||||
`,
|
||||
{ resourceId: args.id, id: currentUser.id },
|
||||
)
|
||||
|
||||
@ -1,16 +1,14 @@
|
||||
import uuid from 'uuid/v4'
|
||||
import { neo4jgraphql } from 'neo4j-graphql-js'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { UserInputError } from 'apollo-server'
|
||||
import { UserInputError, PubSub } from 'apollo-server'
|
||||
import fileUpload from './fileUpload'
|
||||
import Resolver from './helpers/Resolver'
|
||||
import { filterForMutedUsers } from './helpers/filterForMutedUsers'
|
||||
import { PubSub } from 'apollo-server'
|
||||
|
||||
const pubsub = new PubSub()
|
||||
const POST_ADDED = 'POST_ADDED'
|
||||
|
||||
|
||||
const maintainPinnedPosts = params => {
|
||||
const pinnedPostFilter = { pinned: true }
|
||||
if (isEmpty(params.filter)) {
|
||||
|
||||
@ -30,3 +30,7 @@ type Query {
|
||||
type Mutation {
|
||||
markAsRead(id: ID!): NOTIFIED
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
notificationAdded(userId: ID!): NOTIFIED
|
||||
}
|
||||
@ -245,3 +245,7 @@ type Query {
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
postAdded: Post
|
||||
}
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
type Subscription {
|
||||
postAdded: Post
|
||||
notificationAdded: NOTIFIED
|
||||
}
|
||||
@ -3,7 +3,6 @@ import http from 'http'
|
||||
import helmet from 'helmet'
|
||||
import { ApolloServer } from 'apollo-server-express'
|
||||
|
||||
|
||||
import CONFIG from './config'
|
||||
import middleware from './middleware'
|
||||
import { getNeode, getDriver } from './db/neo4j'
|
||||
@ -11,23 +10,22 @@ import decode from './jwt/decode'
|
||||
import schema from './schema'
|
||||
import webfinger from './activitypub/routes/webfinger'
|
||||
|
||||
|
||||
const driver = getDriver()
|
||||
const neode = getNeode()
|
||||
|
||||
const getContext = async (req) => {
|
||||
const user = await decode(driver, req.headers.authorization)
|
||||
return {
|
||||
driver,
|
||||
neode,
|
||||
user,
|
||||
req,
|
||||
cypherParams: {
|
||||
currentUserId: user ? user.id : null,
|
||||
},
|
||||
}
|
||||
const getContext = async req => {
|
||||
const user = await decode(driver, req.headers.authorization)
|
||||
return {
|
||||
driver,
|
||||
neode,
|
||||
user,
|
||||
req,
|
||||
cypherParams: {
|
||||
currentUserId: user ? user.id : null,
|
||||
},
|
||||
}
|
||||
}
|
||||
export const context = async (options) => {
|
||||
export const context = async options => {
|
||||
const { connection, req } = options
|
||||
if (connection) {
|
||||
return connection.context
|
||||
@ -63,9 +61,8 @@ const createServer = options => {
|
||||
app.use('/.well-known/', webfinger())
|
||||
app.use(express.static('public'))
|
||||
server.applyMiddleware({ app, path: '/' })
|
||||
const httpServer = http.createServer(app);
|
||||
server.installSubscriptionHandlers(httpServer);
|
||||
|
||||
const httpServer = http.createServer(app)
|
||||
server.installSubscriptionHandlers(httpServer)
|
||||
|
||||
return { server, httpServer, app }
|
||||
}
|
||||
|
||||
@ -22,13 +22,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import unionBy from 'lodash/unionBy'
|
||||
import { NOTIFICATIONS_POLL_INTERVAL } from '~/constants/notifications'
|
||||
import { notificationQuery, markAsReadMutation } from '~/graphql/User'
|
||||
import { notificationQuery, markAsReadMutation, notificationAdded } from '~/graphql/User'
|
||||
import CounterIcon from '~/components/_new/generic/CounterIcon/CounterIcon'
|
||||
import Dropdown from '~/components/Dropdown'
|
||||
import NotificationList from '../NotificationList/NotificationList'
|
||||
import { notificationAdded } from '~/graphql/User'
|
||||
|
||||
export default {
|
||||
name: 'NotificationMenu',
|
||||
@ -59,6 +58,9 @@ export default {
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
user: 'auth/user',
|
||||
}),
|
||||
unreadNotificationsCount() {
|
||||
const result = this.notifications.reduce((count, notification) => {
|
||||
return notification.read ? count : count + 1
|
||||
@ -77,17 +79,24 @@ export default {
|
||||
orderBy: 'updatedAt_desc',
|
||||
}
|
||||
},
|
||||
// pollInterval: NOTIFICATIONS_POLL_INTERVAL,
|
||||
// update({ notifications }) {
|
||||
// return unionBy(notifications, this.notifications, notification => notification.id).sort(
|
||||
// (a, b) => new Date(b.createdAt) - new Date(a.createdAt),
|
||||
// )
|
||||
// },
|
||||
subscribeToMore: {
|
||||
document: notificationAdded(),
|
||||
variables() {
|
||||
return {
|
||||
userId: this.user.id,
|
||||
}
|
||||
},
|
||||
updateQuery: (previousResult, { subscriptionData }) => {
|
||||
const { data: { notificationAdded: newNotification } } = subscriptionData
|
||||
return { notifications: [newNotification, ...previousResult.notifications] }
|
||||
const {
|
||||
data: { notificationAdded: newNotification },
|
||||
} = subscriptionData
|
||||
return {
|
||||
notifications: unionBy(
|
||||
[newNotification],
|
||||
previousResult.notifications,
|
||||
notification => notification.id,
|
||||
).sort((a, b) => new Date(b.updatedAt) - new Date(a.updatedAt)),
|
||||
}
|
||||
},
|
||||
},
|
||||
error(error) {
|
||||
|
||||
@ -1 +0,0 @@
|
||||
export const NOTIFICATIONS_POLL_INTERVAL = 60000
|
||||
@ -70,6 +70,7 @@ export const notificationQuery = i18n => {
|
||||
read
|
||||
reason
|
||||
createdAt
|
||||
updatedAt
|
||||
from {
|
||||
__typename
|
||||
... on Post {
|
||||
@ -108,6 +109,7 @@ export const markAsReadMutation = i18n => {
|
||||
read
|
||||
reason
|
||||
createdAt
|
||||
updatedAt
|
||||
from {
|
||||
__typename
|
||||
... on Post {
|
||||
@ -137,12 +139,13 @@ export const notificationAdded = () => {
|
||||
${commentFragment}
|
||||
${postFragment}
|
||||
|
||||
subscription notifications {
|
||||
notificationAdded {
|
||||
subscription notifications($userId: ID!) {
|
||||
notificationAdded(userId: $userId) {
|
||||
id
|
||||
read
|
||||
reason
|
||||
createdAt
|
||||
updatedAt
|
||||
from {
|
||||
__typename
|
||||
... on Post {
|
||||
|
||||
@ -78,7 +78,6 @@ import gql from 'graphql-tag'
|
||||
import {
|
||||
userFragment,
|
||||
postFragment,
|
||||
commentFragment,
|
||||
postCountsFragment,
|
||||
userCountsFragment,
|
||||
locationAndBadgesFragment,
|
||||
@ -225,30 +224,33 @@ export default {
|
||||
fetchPolicy: 'cache-and-network',
|
||||
subscribeToMore: {
|
||||
document: gql`
|
||||
${userFragment}
|
||||
${userCountsFragment}
|
||||
${locationAndBadgesFragment('EN')}
|
||||
${postFragment}
|
||||
${postCountsFragment}
|
||||
${tagsCategoriesAndPinnedFragment}
|
||||
${userFragment}
|
||||
${userCountsFragment}
|
||||
${locationAndBadgesFragment('EN')}
|
||||
${postFragment}
|
||||
${postCountsFragment}
|
||||
${tagsCategoriesAndPinnedFragment}
|
||||
|
||||
subscription Post {
|
||||
postAdded {
|
||||
...post
|
||||
...postCounts
|
||||
...tagsCategoriesAndPinned
|
||||
author {
|
||||
...user
|
||||
...userCounts
|
||||
...locationAndBadges
|
||||
subscription Post {
|
||||
postAdded {
|
||||
...post
|
||||
...postCounts
|
||||
...tagsCategoriesAndPinned
|
||||
author {
|
||||
...user
|
||||
...userCounts
|
||||
...locationAndBadges
|
||||
}
|
||||
}
|
||||
}
|
||||
}`,
|
||||
`,
|
||||
updateQuery: (previousResult, { subscriptionData }) => {
|
||||
const { data: { postAdded: newPost } } = subscriptionData
|
||||
const {
|
||||
data: { postAdded: newPost },
|
||||
} = subscriptionData
|
||||
return { Post: [newPost, ...previousResult.Post] }
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ export default ({ app }) => {
|
||||
credentials: true,
|
||||
tokenName: 'human-connection-token',
|
||||
persisting: false,
|
||||
websocketsOnly: true,
|
||||
websocketsOnly: false,
|
||||
cache: new InMemoryCache({ fragmentMatcher }),
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user