From d22e608094d816ae9e8c1e94c704b4f5957f0188 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 2 Apr 2025 10:27:54 +0200 Subject: [PATCH] also save awaySince timestamp --- backend/src/models/User.ts | 1 + backend/src/schema/resolvers/users.spec.ts | 35 ++++++++++++++++++++++ backend/src/schema/resolvers/users.ts | 25 +++++++++++----- 3 files changed, 54 insertions(+), 7 deletions(-) diff --git a/backend/src/models/User.ts b/backend/src/models/User.ts index 6d95ec4a4..9b828e27e 100644 --- a/backend/src/models/User.ts +++ b/backend/src/models/User.ts @@ -55,6 +55,7 @@ export default { invitedBy: { type: 'relationship', relationship: 'INVITED', target: 'User', direction: 'in' }, lastActiveAt: { type: 'string', isoDate: true }, lastOnlineStatus: { type: 'string' }, + awaySince: { type: 'string', isoDate: true }, createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() }, updatedAt: { type: 'string', diff --git a/backend/src/schema/resolvers/users.spec.ts b/backend/src/schema/resolvers/users.spec.ts index 0cf8cfe71..09f98ad53 100644 --- a/backend/src/schema/resolvers/users.spec.ts +++ b/backend/src/schema/resolvers/users.spec.ts @@ -777,6 +777,9 @@ describe('updateOnlineStatus', () => { await expect(dbUser.toJson()).resolves.toMatchObject({ lastOnlineStatus: 'online', }) + await expect(dbUser.toJson()).resolves.not.toMatchObject({ + awaySince: expect.any(String), + }) }) }) @@ -799,6 +802,38 @@ describe('updateOnlineStatus', () => { const dbUser = neode.hydrateFirst(result, 'u', neode.model('User')) await expect(dbUser.toJson()).resolves.toMatchObject({ lastOnlineStatus: 'away', + awaySince: expect.any(String), + }) + }) + + it('stores the timestamp of the first away call', async () => { + await expect(mutate({ mutation: updateOnlineStatus, variables })).resolves.toEqual( + expect.objectContaining({ + data: { updateOnlineStatus: true }, + }), + ) + + const cypher = 'MATCH (u:User {id: $id}) RETURN u' + const result = await neode.cypher(cypher, { id: authenticatedUser.id }) + const dbUser = neode.hydrateFirst(result, 'u', neode.model('User')) + await expect(dbUser.toJson()).resolves.toMatchObject({ + lastOnlineStatus: 'away', + awaySince: expect.any(String), + }) + + const awaySince = (await dbUser.toJson()).awaySince + + await expect(mutate({ mutation: updateOnlineStatus, variables })).resolves.toEqual( + expect.objectContaining({ + data: { updateOnlineStatus: true }, + }), + ) + + const result2 = await neode.cypher(cypher, { id: authenticatedUser.id }) + const dbUser2 = neode.hydrateFirst(result2, 'u', neode.model('User')) + await expect(dbUser2.toJson()).resolves.toMatchObject({ + lastOnlineStatus: 'away', + awaySince, }) }) }) diff --git a/backend/src/schema/resolvers/users.ts b/backend/src/schema/resolvers/users.ts index da36b3316..cab0bc8a3 100644 --- a/backend/src/schema/resolvers/users.ts +++ b/backend/src/schema/resolvers/users.ts @@ -320,16 +320,27 @@ export default { user: { id }, } = context + const CYPHER_AWAY = ` + MATCH (user:User {id: $id}) + WITH user, + CASE user.lastOnlineStatus + WHEN 'away' THEN user.awaySince + ELSE toString(datetime()) + END AS awaySince + SET user.awaySince = awaySince + SET user.lastOnlineStatus = $status + ` + const CYPHER_ONLINE = ` + MATCH (user:User {id: $id}) + SET user.awaySince = null + SET user.lastOnlineStatus = $status + ` + // Last Online Time is saved as `lastActiveAt` const session = context.driver.session() await session.writeTransaction((transaction) => { - return transaction.run( - ` - MATCH (user:User {id: $id}) - SET user.lastOnlineStatus = $status - `, - { id, status }, - ) + // return transaction.run(status === 'away' ? CYPHER_AWAY : CYPHER_ONLINE, { id, status }) + return transaction.run(status === 'away' ? CYPHER_AWAY : CYPHER_ONLINE, { id, status }) }) return true