From 24c6884b9afe1d902b4aeb4d7983cf22d391ca3b Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 13 Jul 2023 14:38:28 +0200 Subject: [PATCH 01/39] [feature] changed usertag in chat messages to user-slug --- webapp/components/Chat/Chat.vue | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index 2b9514bf3..d27824fa8 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -164,6 +164,12 @@ export default { }, async sendMessage(message) { + //check for usersTag and change userid to username + message.usersTag.forEach(userTag =>{ + let needle = `${userTag.id}` + let replacement = `@${userTag.name.replaceAll(" ","-").toLowerCase()}` + message.content = message.content.replaceAll(needle,replacement) + }) try { await this.$apollo.mutate({ mutation: createMessageMutation(), From 08b78aad7c55d66cab3ec23d98b32eb710dc58d0 Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 13 Jul 2023 14:45:44 +0200 Subject: [PATCH 02/39] fixed linting --- webapp/components/Chat/Chat.vue | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index d27824fa8..6ed548ab0 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -164,11 +164,11 @@ export default { }, async sendMessage(message) { - //check for usersTag and change userid to username - message.usersTag.forEach(userTag =>{ - let needle = `${userTag.id}` - let replacement = `@${userTag.name.replaceAll(" ","-").toLowerCase()}` - message.content = message.content.replaceAll(needle,replacement) + // check for usersTag and change userid to username + message.usersTag.forEach((userTag) => { + const needle = `${userTag.id}` + const replacement = `@${userTag.name.replaceAll(' ', '-').toLowerCase()}` + message.content = message.content.replaceAll(needle, replacement) }) try { await this.$apollo.mutate({ From 032bca1b64a68fe3080bdcaf0aa51878ae87a6f5 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 13 Jul 2023 19:09:17 +0200 Subject: [PATCH 03/39] add last message at, use cypher statements for roomName and avatar --- backend/src/graphql/rooms.ts | 1 + backend/src/schema/resolvers/messages.spec.ts | 17 ++++++++++++++++- backend/src/schema/resolvers/messages.ts | 1 + backend/src/schema/resolvers/rooms.spec.ts | 3 +++ backend/src/schema/resolvers/rooms.ts | 6 +----- backend/src/schema/types/type/Room.gql | 6 ++++-- 6 files changed, 26 insertions(+), 8 deletions(-) diff --git a/backend/src/graphql/rooms.ts b/backend/src/graphql/rooms.ts index 109bf1d55..c42da6166 100644 --- a/backend/src/graphql/rooms.ts +++ b/backend/src/graphql/rooms.ts @@ -18,6 +18,7 @@ export const roomQuery = () => { id roomId roomName + lastMessageAt users { _id id diff --git a/backend/src/schema/resolvers/messages.spec.ts b/backend/src/schema/resolvers/messages.spec.ts index 0deccb4e9..bc16c1ef2 100644 --- a/backend/src/schema/resolvers/messages.spec.ts +++ b/backend/src/schema/resolvers/messages.spec.ts @@ -1,7 +1,7 @@ import { createTestClient } from 'apollo-server-testing' import Factory, { cleanDatabase } from '../../db/factories' import { getNeode, getDriver } from '../../db/neo4j' -import { createRoomMutation } from '../../graphql/rooms' +import { createRoomMutation, roomQuery } from '../../graphql/rooms' import { createMessageMutation, messageQuery, markMessagesAsSeen } from '../../graphql/messages' import createServer from '../../server' @@ -129,6 +129,21 @@ describe('Message', () => { }, }) }) + + describe('room is updated as well', () => { + it('has last message at set', async () => { + await expect(query({ query: roomQuery() })).resolves.toMatchObject({ + errors: undefined, + data: { + Room: [ + expect.objectContaining({ + lastMessageAt: expect.any(String), + }), + ], + }, + }) + }) + }) }) describe('user does not chat in room', () => { diff --git a/backend/src/schema/resolvers/messages.ts b/backend/src/schema/resolvers/messages.ts index 45de0b4a4..266758128 100644 --- a/backend/src/schema/resolvers/messages.ts +++ b/backend/src/schema/resolvers/messages.ts @@ -68,6 +68,7 @@ export default { distributed: false, seen: false })-[:INSIDE]->(room) + SET room.lastMessageAt = toString(datetime()) RETURN message { .* } ` const createMessageTxResponse = await transaction.run(createMessageCypher, { diff --git a/backend/src/schema/resolvers/rooms.spec.ts b/backend/src/schema/resolvers/rooms.spec.ts index 03c3d4456..d247c42b4 100644 --- a/backend/src/schema/resolvers/rooms.spec.ts +++ b/backend/src/schema/resolvers/rooms.spec.ts @@ -21,6 +21,9 @@ beforeAll(async () => { driver, neode, user: authenticatedUser, + cypherParams: { + currentUserId: authenticatedUser ? authenticatedUser.id : null, + }, } }, }) diff --git a/backend/src/schema/resolvers/rooms.ts b/backend/src/schema/resolvers/rooms.ts index d5015a03b..c8077211a 100644 --- a/backend/src/schema/resolvers/rooms.ts +++ b/backend/src/schema/resolvers/rooms.ts @@ -12,11 +12,6 @@ export default { if (resolved) { resolved.forEach((room) => { if (room.users) { - // buggy, you must query the username for this to function correctly - room.roomName = room.users.filter((user) => user.id !== context.user.id)[0].name - room.avatar = - room.users.filter((user) => user.id !== context.user.id)[0].avatar?.url || - 'default-avatar' room.users.forEach((user) => { user._id = user.id }) @@ -68,6 +63,7 @@ export default { }, Room: { ...Resolver('Room', { + undefinedToNull: ['lastMessageAt'], hasMany: { users: '<-[:CHATS_IN]-(related:User)', }, diff --git a/backend/src/schema/types/type/Room.gql b/backend/src/schema/types/type/Room.gql index 2ce6556f6..eb5ed02e5 100644 --- a/backend/src/schema/types/type/Room.gql +++ b/backend/src/schema/types/type/Room.gql @@ -13,8 +13,10 @@ type Room { users: [User]! @relation(name: "CHATS_IN", direction: "IN") roomId: String! @cypher(statement: "RETURN this.id") - roomName: String! ## @cypher(statement: "MATCH (this)<-[:CHATS_IN]-(user:User) WHERE NOT user.id = $cypherParams.currentUserId RETURN user[0].name") - avatar: String! ## @cypher match not own user in users array + roomName: String! @cypher(statement: "MATCH (this)<-[:CHATS_IN]-(user:User) WHERE NOT user.id = $cypherParams.currentUserId RETURN user.name") + avatar: String! @cypher(statement: "MATCH (this)<-[:CHATS_IN]-(user:User) WHERE NOT user.id = $cypherParams.currentUserId RETURN user.avatar.url") + + lastMessageAt: String } type Mutation { From d73fd0cbb75d7080f3c55612e727e62e78b6ef8e Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 13 Jul 2023 22:35:48 +0200 Subject: [PATCH 04/39] [bug] chat language is now reactive --- webapp/components/Chat/Chat.vue | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index 8aeb4e7de..855f1e14b 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -114,20 +114,6 @@ export default { text: 'This is the action', }, ], - textMessages: { - ROOMS_EMPTY: this.$t('chat.roomsEmpty'), - ROOM_EMPTY: this.$t('chat.roomEmpty'), - NEW_MESSAGES: this.$t('chat.newMessages'), - MESSAGE_DELETED: this.$t('chat.messageDeleted'), - MESSAGES_EMPTY: this.$t('chat.messagesEmpty'), - CONVERSATION_STARTED: this.$t('chat.conversationStarted'), - TYPE_MESSAGE: this.$t('chat.typeMessage'), - SEARCH: this.$t('chat.search'), - IS_ONLINE: this.$t('chat.isOnline'), - LAST_SEEN: this.$t('chat.lastSeen'), - IS_TYPING: this.$t('chat.isTyping'), - CANCEL_SELECT_MESSAGE: this.$t('chat.cancelSelectMessage'), - }, roomActions: [ /* { @@ -177,6 +163,22 @@ export default { // return this.theme === 'light' ? chatStyle.STYLE.light : chatStyle.STYLE.dark return chatStyle.STYLE.light }, + textMessages() { + return{ + ROOMS_EMPTY: this.$t('chat.roomsEmpty'), + ROOM_EMPTY: this.$t('chat.roomEmpty'), + NEW_MESSAGES: this.$t('chat.newMessages'), + MESSAGE_DELETED: this.$t('chat.messageDeleted'), + MESSAGES_EMPTY: this.$t('chat.messagesEmpty'), + CONVERSATION_STARTED: this.$t('chat.conversationStarted'), + TYPE_MESSAGE: this.$t('chat.typeMessage'), + SEARCH: this.$t('chat.search'), + IS_ONLINE: this.$t('chat.isOnline'), + LAST_SEEN: this.$t('chat.lastSeen'), + IS_TYPING: this.$t('chat.isTyping'), + CANCEL_SELECT_MESSAGE: this.$t('chat.cancelSelectMessage'), + } + } }, methods: { fetchMessages({ room, options = {} }) { From d496aef0a42abb9fdd17a7c8ac95c2653c0172b8 Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 13 Jul 2023 22:56:38 +0200 Subject: [PATCH 05/39] fixed linting --- webapp/components/Chat/Chat.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index 855f1e14b..eb7d7124a 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -164,7 +164,7 @@ export default { return chatStyle.STYLE.light }, textMessages() { - return{ + return { ROOMS_EMPTY: this.$t('chat.roomsEmpty'), ROOM_EMPTY: this.$t('chat.roomEmpty'), NEW_MESSAGES: this.$t('chat.newMessages'), @@ -178,7 +178,7 @@ export default { IS_TYPING: this.$t('chat.isTyping'), CANCEL_SELECT_MESSAGE: this.$t('chat.cancelSelectMessage'), } - } + }, }, methods: { fetchMessages({ room, options = {} }) { From 5376e171bd50494700ffa5452a447e72557c6a61 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Fri, 14 Jul 2023 09:11:32 +0200 Subject: [PATCH 06/39] trying last message prop in room --- backend/src/schema/resolvers/messages.spec.ts | 6 +++++- backend/src/schema/types/type/Room.gql | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/backend/src/schema/resolvers/messages.spec.ts b/backend/src/schema/resolvers/messages.spec.ts index bc16c1ef2..bad78a240 100644 --- a/backend/src/schema/resolvers/messages.spec.ts +++ b/backend/src/schema/resolvers/messages.spec.ts @@ -131,13 +131,17 @@ describe('Message', () => { }) describe('room is updated as well', () => { - it('has last message at set', async () => { + it('has last message set', async () => { await expect(query({ query: roomQuery() })).resolves.toMatchObject({ errors: undefined, data: { Room: [ expect.objectContaining({ lastMessageAt: expect.any(String), + lastMessage: expect.objectContaining({ + id: expect.any(String), + content: 'Some nice message to other chatting user', + }) }), ], }, diff --git a/backend/src/schema/types/type/Room.gql b/backend/src/schema/types/type/Room.gql index eb5ed02e5..3f8a22e70 100644 --- a/backend/src/schema/types/type/Room.gql +++ b/backend/src/schema/types/type/Room.gql @@ -17,6 +17,8 @@ type Room { avatar: String! @cypher(statement: "MATCH (this)<-[:CHATS_IN]-(user:User) WHERE NOT user.id = $cypherParams.currentUserId RETURN user.avatar.url") lastMessageAt: String + + lastMessage: Message @cypher(statement: "MATCH (this)<-[:INSIDE]-(message:Message) RETURN message ORDER BY message.createdAt DESC LIMIT 1") } type Mutation { From 54cf5b37a5f353de187a95b7b608791c5ffdfd30 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 14 Jul 2023 11:09:04 +0200 Subject: [PATCH 07/39] order by indexId instead of createdAt --- backend/src/graphql/messages.ts | 2 +- backend/src/schema/types/type/Message.gql | 3 +-- webapp/graphql/Messages.js | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/backend/src/graphql/messages.ts b/backend/src/graphql/messages.ts index ca5ffb952..26eb32750 100644 --- a/backend/src/graphql/messages.ts +++ b/backend/src/graphql/messages.ts @@ -17,7 +17,7 @@ export const createMessageMutation = () => { export const messageQuery = () => { return gql` query ($roomId: ID!, $first: Int, $offset: Int) { - Message(roomId: $roomId, first: $first, offset: $offset, orderBy: createdAt_desc) { + Message(roomId: $roomId, first: $first, offset: $offset, orderBy: indexId_desc) { _id id indexId diff --git a/backend/src/schema/types/type/Message.gql b/backend/src/schema/types/type/Message.gql index 671c5523a..764181dd9 100644 --- a/backend/src/schema/types/type/Message.gql +++ b/backend/src/schema/types/type/Message.gql @@ -3,8 +3,7 @@ # } enum _MessageOrdering { - createdAt_asc - createdAt_desc + indexId_desc } type Message { diff --git a/webapp/graphql/Messages.js b/webapp/graphql/Messages.js index d017f816c..bede62d01 100644 --- a/webapp/graphql/Messages.js +++ b/webapp/graphql/Messages.js @@ -3,7 +3,7 @@ import gql from 'graphql-tag' export const messageQuery = () => { return gql` query ($roomId: ID!, $first: Int, $offset: Int) { - Message(roomId: $roomId, first: $first, offset: $offset, orderBy: createdAt_desc) { + Message(roomId: $roomId, first: $first, offset: $offset, orderBy: indexId_desc) { _id id indexId From 1d92b40f506d1999858cef5448726c02de35ee7d Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Fri, 14 Jul 2023 11:52:51 +0200 Subject: [PATCH 08/39] lastMessage in room working --- backend/src/graphql/rooms.ts | 12 ++++++++++ backend/src/middleware/chatMiddleware.ts | 22 +++++++++++++++++++ backend/src/middleware/index.ts | 3 +++ backend/src/schema/resolvers/messages.spec.ts | 16 ++++++++++++-- backend/src/schema/resolvers/rooms.ts | 12 +--------- backend/src/schema/types/type/Room.gql | 6 ++++- 6 files changed, 57 insertions(+), 14 deletions(-) create mode 100644 backend/src/middleware/chatMiddleware.ts diff --git a/backend/src/graphql/rooms.ts b/backend/src/graphql/rooms.ts index c42da6166..28626071c 100644 --- a/backend/src/graphql/rooms.ts +++ b/backend/src/graphql/rooms.ts @@ -19,6 +19,18 @@ export const roomQuery = () => { roomId roomName lastMessageAt + lastMessage { + _id + id + content + senderId + username + avatar + date + saved + distributed + seen + } users { _id id diff --git a/backend/src/middleware/chatMiddleware.ts b/backend/src/middleware/chatMiddleware.ts new file mode 100644 index 000000000..38b299450 --- /dev/null +++ b/backend/src/middleware/chatMiddleware.ts @@ -0,0 +1,22 @@ +const roomProperties = async (resolve, root, args, context, info) => { + const resolved = await resolve(root, args, context, info) + if (resolved) { + resolved.forEach((room) => { + if (room.users) { + room.users.forEach((user) => { + user._id = user.id + }) + } + if (room.lastMessage) { + room.lastMessage._id = room.lastMessage.id + } + }) + } + return resolved +} + +export default { + Query: { + Room: roomProperties, + }, +} diff --git a/backend/src/middleware/index.ts b/backend/src/middleware/index.ts index 813bbe9a7..08c872db7 100644 --- a/backend/src/middleware/index.ts +++ b/backend/src/middleware/index.ts @@ -14,6 +14,7 @@ import login from './login/loginMiddleware' import sentry from './sentryMiddleware' import languages from './languages/languages' import userInteractions from './userInteractions' +import chatMiddleware from './chatMiddleware' export default (schema) => { const middlewares = { @@ -31,6 +32,7 @@ export default (schema) => { orderBy, languages, userInteractions, + chatMiddleware, } let order = [ @@ -49,6 +51,7 @@ export default (schema) => { 'softDelete', 'includedFields', 'orderBy', + 'chatMiddleware', ] // add permisions middleware at the first position (unless we're seeding) diff --git a/backend/src/schema/resolvers/messages.spec.ts b/backend/src/schema/resolvers/messages.spec.ts index bad78a240..ecdf90873 100644 --- a/backend/src/schema/resolvers/messages.spec.ts +++ b/backend/src/schema/resolvers/messages.spec.ts @@ -22,6 +22,9 @@ beforeAll(async () => { driver, neode, user: authenticatedUser, + cypherParams: { + currentUserId: authenticatedUser ? authenticatedUser.id : null, + }, } }, }) @@ -132,16 +135,25 @@ describe('Message', () => { describe('room is updated as well', () => { it('has last message set', async () => { - await expect(query({ query: roomQuery() })).resolves.toMatchObject({ + const result = await query({ query: roomQuery() }) + await expect(result).toMatchObject({ errors: undefined, data: { Room: [ expect.objectContaining({ lastMessageAt: expect.any(String), lastMessage: expect.objectContaining({ + _id: result.data.Room[0].lastMessage.id, id: expect.any(String), content: 'Some nice message to other chatting user', - }) + senderId: 'chatting-user', + username: 'Chatting User', + avatar: expect.any(String), + date: expect.any(String), + saved: true, + distributed: false, + seen: false, + }), }), ], }, diff --git a/backend/src/schema/resolvers/rooms.ts b/backend/src/schema/resolvers/rooms.ts index c8077211a..d1e14aaba 100644 --- a/backend/src/schema/resolvers/rooms.ts +++ b/backend/src/schema/resolvers/rooms.ts @@ -8,17 +8,7 @@ export default { params.filter.users_some = { id: context.user.id, } - const resolved = await neo4jgraphql(object, params, context, resolveInfo) - if (resolved) { - resolved.forEach((room) => { - if (room.users) { - room.users.forEach((user) => { - user._id = user.id - }) - } - }) - } - return resolved + return neo4jgraphql(object, params, context, resolveInfo) }, }, Mutation: { diff --git a/backend/src/schema/types/type/Room.gql b/backend/src/schema/types/type/Room.gql index 3f8a22e70..2cf51014e 100644 --- a/backend/src/schema/types/type/Room.gql +++ b/backend/src/schema/types/type/Room.gql @@ -18,7 +18,11 @@ type Room { lastMessageAt: String - lastMessage: Message @cypher(statement: "MATCH (this)<-[:INSIDE]-(message:Message) RETURN message ORDER BY message.createdAt DESC LIMIT 1") + lastMessage: Message @cypher(statement: """ + MATCH (this)<-[:INSIDE]-(message:Message) + WITH message ORDER BY message.createdAt DESC LIMIT 1 + RETURN message + """) } type Mutation { From 2fe93892a1c1a52ae224e04c1fbb391b15c0bc4a Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Fri, 14 Jul 2023 12:21:40 +0200 Subject: [PATCH 09/39] full room object for create room mutation --- backend/src/graphql/rooms.ts | 10 +++++++ backend/src/middleware/chatMiddleware.ts | 33 +++++++++++++++------- backend/src/schema/resolvers/rooms.spec.ts | 19 +++++++++++++ backend/src/schema/resolvers/rooms.ts | 8 +++++- 4 files changed, 59 insertions(+), 11 deletions(-) diff --git a/backend/src/graphql/rooms.ts b/backend/src/graphql/rooms.ts index 28626071c..e48ffe943 100644 --- a/backend/src/graphql/rooms.ts +++ b/backend/src/graphql/rooms.ts @@ -6,6 +6,16 @@ export const createRoomMutation = () => { CreateRoom(userId: $userId) { id roomId + roomName + lastMessageAt + users { + _id + id + name + avatar { + url + } + } } } ` diff --git a/backend/src/middleware/chatMiddleware.ts b/backend/src/middleware/chatMiddleware.ts index 38b299450..5b0e4d7e2 100644 --- a/backend/src/middleware/chatMiddleware.ts +++ b/backend/src/middleware/chatMiddleware.ts @@ -1,16 +1,26 @@ +import { isArray } from 'lodash' + +const setRoomProps = (room) => { + if (room.users) { + room.users.forEach((user) => { + user._id = user.id + }) + } + if (room.lastMessage) { + room.lastMessage._id = room.lastMessage.id + } +} + const roomProperties = async (resolve, root, args, context, info) => { const resolved = await resolve(root, args, context, info) if (resolved) { - resolved.forEach((room) => { - if (room.users) { - room.users.forEach((user) => { - user._id = user.id - }) - } - if (room.lastMessage) { - room.lastMessage._id = room.lastMessage.id - } - }) + if (isArray(resolved)) { + resolved.forEach((room) => { + setRoomProps(room) + }) + } else { + setRoomProps(resolved) + } } return resolved } @@ -19,4 +29,7 @@ export default { Query: { Room: roomProperties, }, + Mutation: { + CreateRoom: roomProperties, + }, } diff --git a/backend/src/schema/resolvers/rooms.spec.ts b/backend/src/schema/resolvers/rooms.spec.ts index d247c42b4..daed6815e 100644 --- a/backend/src/schema/resolvers/rooms.spec.ts +++ b/backend/src/schema/resolvers/rooms.spec.ts @@ -125,6 +125,25 @@ describe('Room', () => { CreateRoom: { id: expect.any(String), roomId: result.data.CreateRoom.id, + roomName: 'Other Chatting User', + users: expect.arrayContaining([ + { + _id: 'chatting-user', + id: 'chatting-user', + name: 'Chatting User', + avatar: { + url: expect.any(String), + }, + }, + { + _id: 'other-chatting-user', + id: 'other-chatting-user', + name: 'Other Chatting User', + avatar: { + url: expect.any(String), + }, + }, + ]), }, }, }) diff --git a/backend/src/schema/resolvers/rooms.ts b/backend/src/schema/resolvers/rooms.ts index d1e14aaba..885688643 100644 --- a/backend/src/schema/resolvers/rooms.ts +++ b/backend/src/schema/resolvers/rooms.ts @@ -29,7 +29,13 @@ export default { ON CREATE SET room.createdAt = toString(datetime()), room.id = apoc.create.uuid() - RETURN room { .* } + WITH room, user, currentUser, + user.name AS roomName + RETURN room { + .*, + users: [properties(currentUser), properties(user)], + roomName: roomName + } ` const createRommTxResponse = await transaction.run(createRoomCypher, { userId, From 1fd67aa581c58cf4f17f26811cc8280ee94026ff Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Fri, 14 Jul 2023 12:34:12 +0200 Subject: [PATCH 10/39] add unreadCount property --- backend/src/graphql/rooms.ts | 2 ++ backend/src/schema/resolvers/rooms.spec.ts | 2 ++ backend/src/schema/resolvers/rooms.ts | 3 ++- backend/src/schema/types/type/Room.gql | 7 +++++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/rooms.ts b/backend/src/graphql/rooms.ts index e48ffe943..554c346dc 100644 --- a/backend/src/graphql/rooms.ts +++ b/backend/src/graphql/rooms.ts @@ -8,6 +8,7 @@ export const createRoomMutation = () => { roomId roomName lastMessageAt + unreadCount users { _id id @@ -29,6 +30,7 @@ export const roomQuery = () => { roomId roomName lastMessageAt + unreadCount lastMessage { _id id diff --git a/backend/src/schema/resolvers/rooms.spec.ts b/backend/src/schema/resolvers/rooms.spec.ts index daed6815e..0ff8669fb 100644 --- a/backend/src/schema/resolvers/rooms.spec.ts +++ b/backend/src/schema/resolvers/rooms.spec.ts @@ -126,6 +126,7 @@ describe('Room', () => { id: expect.any(String), roomId: result.data.CreateRoom.id, roomName: 'Other Chatting User', + unreadCount: 0, users: expect.arrayContaining([ { _id: 'chatting-user', @@ -241,6 +242,7 @@ describe('Room', () => { id: expect.any(String), roomId: result.data.Room[0].id, roomName: 'Chatting User', + unreadCount: 0, users: expect.arrayContaining([ { _id: 'chatting-user', diff --git a/backend/src/schema/resolvers/rooms.ts b/backend/src/schema/resolvers/rooms.ts index 885688643..f87f89f2e 100644 --- a/backend/src/schema/resolvers/rooms.ts +++ b/backend/src/schema/resolvers/rooms.ts @@ -34,7 +34,8 @@ export default { RETURN room { .*, users: [properties(currentUser), properties(user)], - roomName: roomName + roomName: roomName, + unreadCount: toString(0) } ` const createRommTxResponse = await transaction.run(createRoomCypher, { diff --git a/backend/src/schema/types/type/Room.gql b/backend/src/schema/types/type/Room.gql index 2cf51014e..a1ff56e34 100644 --- a/backend/src/schema/types/type/Room.gql +++ b/backend/src/schema/types/type/Room.gql @@ -23,6 +23,13 @@ type Room { WITH message ORDER BY message.createdAt DESC LIMIT 1 RETURN message """) + + unreadCount: Int @cypher(statement: """ + MATCH (this)<-[:INSIDE]-(message:Message)<-[:CREATED]-(user:User) + WHERE NOT user.id = $cypherParams.currentUserId + AND NOT message.seen + RETURN count(message) + """) } type Mutation { From b9b9fea1ddedaf48771b6ca43ddd72dfbb3dcb14 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Fri, 14 Jul 2023 12:38:46 +0200 Subject: [PATCH 11/39] test unread count --- backend/src/schema/resolvers/messages.spec.ts | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/backend/src/schema/resolvers/messages.spec.ts b/backend/src/schema/resolvers/messages.spec.ts index ecdf90873..9a9e84c1e 100644 --- a/backend/src/schema/resolvers/messages.spec.ts +++ b/backend/src/schema/resolvers/messages.spec.ts @@ -142,6 +142,7 @@ describe('Message', () => { Room: [ expect.objectContaining({ lastMessageAt: expect.any(String), + unreadCount: 0, lastMessage: expect.objectContaining({ _id: result.data.Room[0].lastMessage.id, id: expect.any(String), @@ -160,6 +161,35 @@ describe('Message', () => { }) }) }) + + describe('unread count for other user', () => { + it('has unread count = 1', async () => { + authenticatedUser = await otherChattingUser.toJson() + await expect(query({ query: roomQuery() })).resolves.toMatchObject({ + errors: undefined, + data: { + Room: [ + expect.objectContaining({ + lastMessageAt: expect.any(String), + unreadCount: 1, + lastMessage: expect.objectContaining({ + _id: expect.any(String), + id: expect.any(String), + content: 'Some nice message to other chatting user', + senderId: 'chatting-user', + username: 'Chatting User', + avatar: expect.any(String), + date: expect.any(String), + saved: true, + distributed: false, + seen: false, + }), + }), + ], + }, + }) + }) + }) }) describe('user does not chat in room', () => { From 4310b3fa48d9c2f36df5e992d2f973458cf68fb4 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Fri, 14 Jul 2023 12:51:54 +0200 Subject: [PATCH 12/39] create message mutation returns full message object --- backend/src/graphql/messages.ts | 4 ++++ backend/src/middleware/chatMiddleware.ts | 22 +++++++++++++++++++ backend/src/schema/resolvers/messages.spec.ts | 4 ++++ backend/src/schema/resolvers/messages.ts | 15 +++++++------ 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/backend/src/graphql/messages.ts b/backend/src/graphql/messages.ts index 34c7d559b..e042fc600 100644 --- a/backend/src/graphql/messages.ts +++ b/backend/src/graphql/messages.ts @@ -6,6 +6,10 @@ export const createMessageMutation = () => { CreateMessage(roomId: $roomId, content: $content) { id content + senderId + username + avatar + date saved distributed seen diff --git a/backend/src/middleware/chatMiddleware.ts b/backend/src/middleware/chatMiddleware.ts index 5b0e4d7e2..c28d6a70d 100644 --- a/backend/src/middleware/chatMiddleware.ts +++ b/backend/src/middleware/chatMiddleware.ts @@ -11,6 +11,13 @@ const setRoomProps = (room) => { } } +const setMessageProps = (message, context) => { + message._id = message.id + if (message.senderId !== context.user.id) { + message.distributed = true + } +} + const roomProperties = async (resolve, root, args, context, info) => { const resolved = await resolve(root, args, context, info) if (resolved) { @@ -25,9 +32,24 @@ const roomProperties = async (resolve, root, args, context, info) => { return resolved } +const messageProperties = async (resolve, root, args, context, info) => { + const resolved = await resolve(root, args, context, info) + if (resolved) { + if (isArray(resolved)) { + resolved.forEach((message) => { + setMessageProps(message, context) + }) + } else { + setMessageProps(resolved, context) + } + } + return resolved +} + export default { Query: { Room: roomProperties, + Message: messageProperties, }, Mutation: { CreateRoom: roomProperties, diff --git a/backend/src/schema/resolvers/messages.spec.ts b/backend/src/schema/resolvers/messages.spec.ts index 9a9e84c1e..68b84ad69 100644 --- a/backend/src/schema/resolvers/messages.spec.ts +++ b/backend/src/schema/resolvers/messages.spec.ts @@ -125,6 +125,10 @@ describe('Message', () => { CreateMessage: { id: expect.any(String), content: 'Some nice message to other chatting user', + senderId: 'chatting-user', + username: 'Chatting User', + avatar: expect.any(String), + date: expect.any(String), saved: true, distributed: false, seen: false, diff --git a/backend/src/schema/resolvers/messages.ts b/backend/src/schema/resolvers/messages.ts index 266758128..fa80a7b3f 100644 --- a/backend/src/schema/resolvers/messages.ts +++ b/backend/src/schema/resolvers/messages.ts @@ -40,12 +40,6 @@ export default { } // send subscription to author to updated the messages } - resolved.forEach((message) => { - message._id = message.id - if (message.senderId !== context.user.id) { - message.distributed = true - } - }) } return resolved }, @@ -60,6 +54,7 @@ export default { const writeTxResultPromise = session.writeTransaction(async (transaction) => { const createMessageCypher = ` MATCH (currentUser:User { id: $currentUserId })-[:CHATS_IN]->(room:Room { id: $roomId }) + OPTIONAL MATCH (currentUser)-[:AVATAR_IMAGE]->(image:Image) CREATE (currentUser)-[:CREATED]->(message:Message { createdAt: toString(datetime()), id: apoc.create.uuid(), @@ -69,7 +64,13 @@ export default { seen: false })-[:INSIDE]->(room) SET room.lastMessageAt = toString(datetime()) - RETURN message { .* } + RETURN message { + .*, + senderId: currentUser.id, + username: currentUser.name, + avatar: image.url, + date: message.createdAt + } ` const createMessageTxResponse = await transaction.run(createMessageCypher, { currentUserId, From db594650f9308e8c3bdd5e396c087598ae932d82 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Fri, 14 Jul 2023 18:53:56 +0200 Subject: [PATCH 13/39] feat(webapp): mark messages as seen --- webapp/components/Chat/Chat.vue | 71 +++++++++++++++++++-------------- webapp/graphql/Messages.js | 8 ++++ 2 files changed, 49 insertions(+), 30 deletions(-) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index 95bf5da95..8c4aaff69 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -59,7 +59,7 @@