diff --git a/src/middleware/includedFieldsMiddleware.js b/src/middleware/includedFieldsMiddleware.js index 6d8ccf8..ed9a141 100644 --- a/src/middleware/includedFieldsMiddleware.js +++ b/src/middleware/includedFieldsMiddleware.js @@ -24,6 +24,6 @@ const includeFieldsRecursively = (includedFields) => { } export default { - Query: includeFieldsRecursively(['id', 'disabled', 'deleted']), - Mutation: includeFieldsRecursively(['id', 'disabled', 'deleted']) + Query: includeFieldsRecursively(['id', 'disabled', 'deleted', 'preferences']), + Mutation: includeFieldsRecursively(['id', 'disabled', 'deleted', 'preferences']) } diff --git a/src/middleware/index.js b/src/middleware/index.js index 6ed0955..4e4064c 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -9,6 +9,7 @@ import xssMiddleware from './xssMiddleware' import permissionsMiddleware from './permissionsMiddleware' import userMiddleware from './userMiddleware' import includedFieldsMiddleware from './includedFieldsMiddleware' +import lastActiveMiddleware from './lastActiveMiddleware' export default schema => { let middleware = [ @@ -20,7 +21,8 @@ export default schema => { fixImageUrlsMiddleware, softDeleteMiddleware, userMiddleware, - includedFieldsMiddleware + includedFieldsMiddleware, + lastActiveMiddleware ] // add permisions middleware at the first position (unless we're seeding) diff --git a/src/middleware/lastActiveMiddleware.js b/src/middleware/lastActiveMiddleware.js new file mode 100644 index 0000000..c7f3708 --- /dev/null +++ b/src/middleware/lastActiveMiddleware.js @@ -0,0 +1,40 @@ +import values from 'lodash/values' + +const queue = { + users: {}, + driver: null, + add: (context, lastActiveAt) => { + queue.driver = context.driver + queue.users[context.user.id] = { + id: context.user.id, + lastActiveAt + } + }, + run: async () => { + if (!queue.driver || !values(queue.users).length) return + + const users = { ...queue.users } + queue.users = {} + + const session = queue.driver.session() + await session.run(` + UNWIND $users AS user + MATCH(u:User {id: user.id}) + SET u.lastActiveAt = user.lastActiveAt`, { + users: values(users) + }) + session.close() + } +} +setInterval(queue.run, 10000) + +export default { + Mutation: async (resolve, root, args, context, info) => { + if (context.user) queue.add(context, (new Date()).toISOString()) + return resolve(root, args, context, info) + }, + Query: async (resolve, root, args, context, info) => { + if (context.user) queue.add(context, (new Date()).toISOString()) + return resolve(root, args, context, info) + } +} diff --git a/src/middleware/permissionsMiddleware.js b/src/middleware/permissionsMiddleware.js index 62999c2..b85714f 100644 --- a/src/middleware/permissionsMiddleware.js +++ b/src/middleware/permissionsMiddleware.js @@ -41,6 +41,11 @@ const isAuthor = rule({ cache: 'no_cache' })(async (parent, args, { user, driver return authorId === user.id }) +const wantsToShowOnlineStatus = rule({ cache: 'no_cache' })(async (parent, args, { user, driver }) => { + const preferences = parent.preferences || [] + return preferences.indexOf('hideOnlineStatus') < 0 +}) + // Permissions const permissions = shield({ Query: { @@ -68,7 +73,9 @@ const permissions = shield({ }, User: { email: isMyOwn, - password: isMyOwn + password: isMyOwn, + preferences: or(isMyOwn, isAdmin), + lastActiveAt: or(wantsToShowOnlineStatus, isModerator) } }) diff --git a/src/schema.graphql b/src/schema.graphql index af7764f..b5ea3f4 100644 --- a/src/schema.graphql +++ b/src/schema.graphql @@ -121,6 +121,11 @@ type User { createdAt: String updatedAt: String + "Date of last interaction with the system" + lastActiveAt: String + + "User preferences like 'hideOnlineStatus'" + preferences: [UserPreferenceEnum] friends: [User]! @relation(name: "FRIENDS", direction: "BOTH") friendsCount: Int! @cypher(statement: "MATCH (this)<-[:FRIENDS]->(r:User) RETURN COUNT(DISTINCT r)") @@ -167,6 +172,10 @@ type User { badgesCount: Int! @cypher(statement: "MATCH (this)<-[:REWARDED]-(r:Badge) RETURN COUNT(r)") } +enum UserPreferenceEnum { + hideOnlineStatus +} + type Post { id: ID! activityId: String diff --git a/src/seed/factories/users.js b/src/seed/factories/users.js index 491b3f9..60d0da2 100644 --- a/src/seed/factories/users.js +++ b/src/seed/factories/users.js @@ -1,5 +1,13 @@ import faker from 'faker' import uuid from 'uuid/v4' +import helpers from '../seed-helpers' + +// random preferences, chance 1 to 3 +const preferences = [ + 'null', + '[hideOnlineStatus]', + 'null' +] export default function create (params) { const { @@ -25,7 +33,8 @@ export default function create (params) { about: "${about}", role: ${role}, disabled: ${disabled}, - deleted: ${deleted} + deleted: ${deleted}, + preferences: ${helpers.random(preferences)} ) { id name