From 7e77bc82fb949ed7c45fce05af7ecb1b46f39a2d Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 12 Jul 2023 13:14:43 +0200 Subject: [PATCH 001/151] new query including index & first, offset and orderBy --- webapp/graphql/Messages.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/webapp/graphql/Messages.js b/webapp/graphql/Messages.js index 41d647d4b..d017f816c 100644 --- a/webapp/graphql/Messages.js +++ b/webapp/graphql/Messages.js @@ -2,10 +2,11 @@ import gql from 'graphql-tag' export const messageQuery = () => { return gql` - query ($roomId: ID!) { - Message(roomId: $roomId) { + query ($roomId: ID!, $first: Int, $offset: Int) { + Message(roomId: $roomId, first: $first, offset: $offset, orderBy: createdAt_desc) { _id id + indexId senderId content author { From 635d3a22326a4acad8b1fa406bd4cdba28889662 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 12 Jul 2023 13:15:06 +0200 Subject: [PATCH 002/151] new room query --- webapp/graphql/Rooms.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp/graphql/Rooms.js b/webapp/graphql/Rooms.js index 7bab25509..e28702f77 100644 --- a/webapp/graphql/Rooms.js +++ b/webapp/graphql/Rooms.js @@ -1,8 +1,8 @@ import gql from 'graphql-tag' export const roomQuery = () => gql` - query { - Room { + query Room($first: Int, $offset: Int) { + Room(first: $first, offset: $offset) { id roomId roomName From 730e15a9ea91d1ecb00a73f8d3b7d8e2d3ca1b2e Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 12 Jul 2023 13:15:19 +0200 Subject: [PATCH 003/151] working message pagination --- webapp/components/Chat/Chat.vue | 43 +++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index 2b9514bf3..8c4ae8447 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -45,7 +45,8 @@ export default { data() { return { menuActions: [ - /* { + /* + { name: 'inviteUser', title: 'Invite User', }, @@ -56,9 +57,11 @@ export default { { name: 'deleteRoom', title: 'Delete Room', - }, */ + }, + */ ], messageActions: [ + /* { name: 'addMessageToFavorite', title: 'Add To Favorite', @@ -67,6 +70,7 @@ export default { name: 'shareMessage', title: 'Share Message', }, + */ ], templatesText: [ { @@ -109,6 +113,11 @@ export default { showDemoOptions: true, responsiveBreakpoint: 600, singleRoom: !!this.singleRoomId || false, + messagePage: 0, + messagePageSize: 20, + roomPage: 0, + roomPageSize: 999, //TODO pagination is a problem with single rooms - cant use + lastRoom: null } }, mounted() { @@ -138,7 +147,13 @@ export default { }, methods: { fetchMessages({ room, options = {} }) { + if(this.lastRoom != room.id) { + this.messages = [] + this.messagePage = 0, + this.lastRoom = room.id + } this.messagesLoaded = false + const offset = (options.refetch ? 0 : this.messagePage) * this.messagePageSize setTimeout(async () => { try { const { @@ -147,20 +162,32 @@ export default { query: messageQuery(), variables: { roomId: room.id, + first: this.messagePageSize, + offset, }, fetchPolicy: 'no-cache', }) - this.messages = Message + + const msgs = [] + ;[...this.messages, ...Message].forEach((m) => { + msgs[m.indexId] = m + }) + this.messages = msgs.filter( Boolean ) + + + if(Message.length < this.messagePageSize){ + this.messagesLoaded = true + } + this.messagePage += 1 } catch (error) { this.messages = [] this.$toast.error(error.message) } - this.messagesLoaded = true }) }, refetchMessage(roomId) { - this.fetchMessages({ room: this.rooms.find((r) => r.roomId === roomId) }) + this.fetchMessages({ room: this.rooms.find((r) => r.roomId === roomId), options: { refetch: true} }) }, async sendMessage(message) { @@ -183,6 +210,12 @@ export default { query() { return roomQuery() }, + variables() { + return { + first: this.roomPageSize, + offset: this.roomPage* this.roomPageSize, + } + }, update({ Room }) { if (!Room) { this.rooms = [] From 481158f81586b949fb518ba5b8e09b47d38e3c2a Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 12 Jul 2023 13:15:27 +0200 Subject: [PATCH 004/151] message type definition --- backend/src/schema/types/type/Message.gql | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/backend/src/schema/types/type/Message.gql b/backend/src/schema/types/type/Message.gql index 4a3346079..f97d86dd8 100644 --- a/backend/src/schema/types/type/Message.gql +++ b/backend/src/schema/types/type/Message.gql @@ -2,8 +2,14 @@ # room: _RoomFilter # } +enum _MessageOrdering { + createdAt_asc + createdAt_desc +} + type Message { id: ID! + indexId: String! createdAt: String updatedAt: String @@ -26,5 +32,10 @@ type Mutation { } type Query { - Message(roomId: ID!): [Message] + Message( + roomId: ID!, + first: Int + offset: Int + orderBy: [_MessageOrdering] + ): [Message] } From 0cce43500a13a795d4a3c0a471b4d694982c33e4 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 12 Jul 2023 13:36:16 +0200 Subject: [PATCH 005/151] backend implementation for message pagination --- backend/src/schema/resolvers/messages.ts | 25 ++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/backend/src/schema/resolvers/messages.ts b/backend/src/schema/resolvers/messages.ts index b93cffe06..5e2335c9b 100644 --- a/backend/src/schema/resolvers/messages.ts +++ b/backend/src/schema/resolvers/messages.ts @@ -13,11 +13,32 @@ export default { id: context.user.id, }, } + // this does not work + // params.orderBy = ['createdAt_desc'] const resolved = await neo4jgraphql(object, params, context, resolveInfo) + if (resolved) { - resolved.forEach((message) => { - message._id = message.id + const session = context.driver.session() + const countMessageTxPromise = session.readTransaction(async (transaction) => { + const countMessageCypher = ` + MATCH((message:Message)-[:INSIDE]->(:Room { id: $roomId })) RETURN COUNT(message) AS count + ` + const countMessageTxResponse = await transaction.run(countMessageCypher, { + roomId, + }) + return await countMessageTxResponse.records[0].get('count') }) + try { + const count = await countMessageTxPromise + for (let i = 0; i < resolved.length; i++) { + resolved[i]._id = resolved[i].id + resolved[i].indexId = count - 1 - params.offset - i + } + } catch (error) { + throw new Error(error) + } finally { + session.close() + } } return resolved }, From 70c310bc885f40ff7bc4f4b444e68d6d8fab341e Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 13 Jul 2023 10:40:31 +0200 Subject: [PATCH 006/151] create indexId when creating the message --- backend/src/schema/resolvers/messages.ts | 25 +++++------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/backend/src/schema/resolvers/messages.ts b/backend/src/schema/resolvers/messages.ts index 5e2335c9b..d00d365b1 100644 --- a/backend/src/schema/resolvers/messages.ts +++ b/backend/src/schema/resolvers/messages.ts @@ -18,26 +18,8 @@ export default { const resolved = await neo4jgraphql(object, params, context, resolveInfo) if (resolved) { - const session = context.driver.session() - const countMessageTxPromise = session.readTransaction(async (transaction) => { - const countMessageCypher = ` - MATCH((message:Message)-[:INSIDE]->(:Room { id: $roomId })) RETURN COUNT(message) AS count - ` - const countMessageTxResponse = await transaction.run(countMessageCypher, { - roomId, - }) - return await countMessageTxResponse.records[0].get('count') - }) - try { - const count = await countMessageTxPromise - for (let i = 0; i < resolved.length; i++) { - resolved[i]._id = resolved[i].id - resolved[i].indexId = count - 1 - params.offset - i - } - } catch (error) { - throw new Error(error) - } finally { - session.close() + for (let i = 0; i < resolved.length; i++) { + resolved[i]._id = resolved[i].id } } return resolved @@ -53,9 +35,12 @@ export default { const writeTxResultPromise = session.writeTransaction(async (transaction) => { const createMessageCypher = ` MATCH (currentUser:User { id: $currentUserId })-[:CHATS_IN]->(room:Room { id: $roomId }) + OPTIONAL MATCH (m:Message)-[:INSIDE]->(room) + WITH MAX(m.indexId) as maxIndex, room, currentUser CREATE (currentUser)-[:CREATED]->(message:Message { createdAt: toString(datetime()), id: apoc.create.uuid(), + indexId: CASE WHEN maxIndex IS NOT NULL THEN maxIndex + 1 ELSE 0 END, content: $content })-[:INSIDE]->(room) RETURN message { .* } From 8065529d0acb67265a84f8afa23fbf096feecf4e Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 13 Jul 2023 10:41:55 +0200 Subject: [PATCH 007/151] reverted change - use forEach instead of for loop --- backend/src/schema/resolvers/messages.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/schema/resolvers/messages.ts b/backend/src/schema/resolvers/messages.ts index d00d365b1..70786e50f 100644 --- a/backend/src/schema/resolvers/messages.ts +++ b/backend/src/schema/resolvers/messages.ts @@ -18,9 +18,9 @@ export default { const resolved = await neo4jgraphql(object, params, context, resolveInfo) if (resolved) { - for (let i = 0; i < resolved.length; i++) { - resolved[i]._id = resolved[i].id - } + resolved.forEach((message) => { + message._id = message.id + }) } return resolved }, From 1648b25516b86616dff14ab22ae5690b7683eff0 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 13 Jul 2023 11:06:29 +0200 Subject: [PATCH 008/151] lint fixes --- webapp/components/Chat/Chat.vue | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index 8c4ae8447..507cce9da 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -116,8 +116,8 @@ export default { messagePage: 0, messagePageSize: 20, roomPage: 0, - roomPageSize: 999, //TODO pagination is a problem with single rooms - cant use - lastRoom: null + roomPageSize: 999, // TODO pagination is a problem with single rooms - cant use + lastRoom: null, } }, mounted() { @@ -147,13 +147,13 @@ export default { }, methods: { fetchMessages({ room, options = {} }) { - if(this.lastRoom != room.id) { + if (this.lastRoom !== room.id) { this.messages = [] - this.messagePage = 0, + this.messagePage = 0 this.lastRoom = room.id } this.messagesLoaded = false - const offset = (options.refetch ? 0 : this.messagePage) * this.messagePageSize + const offset = (options.refetch ? 0 : this.messagePage) * this.messagePageSize setTimeout(async () => { try { const { @@ -167,15 +167,14 @@ export default { }, fetchPolicy: 'no-cache', }) - + const msgs = [] ;[...this.messages, ...Message].forEach((m) => { msgs[m.indexId] = m }) - this.messages = msgs.filter( Boolean ) + this.messages = msgs.filter(Boolean) - - if(Message.length < this.messagePageSize){ + if (Message.length < this.messagePageSize) { this.messagesLoaded = true } this.messagePage += 1 @@ -187,7 +186,10 @@ export default { }, refetchMessage(roomId) { - this.fetchMessages({ room: this.rooms.find((r) => r.roomId === roomId), options: { refetch: true} }) + this.fetchMessages({ + room: this.rooms.find((r) => r.roomId === roomId), + options: { refetch: true }, + }) }, async sendMessage(message) { @@ -213,7 +215,7 @@ export default { variables() { return { first: this.roomPageSize, - offset: this.roomPage* this.roomPageSize, + offset: this.roomPage * this.roomPageSize, } }, update({ Room }) { From a82e76f3666969840952600e31a55e7cb294e304 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 13 Jul 2023 11:13:06 +0200 Subject: [PATCH 009/151] test message ordering & pagination --- backend/src/graphql/messages.ts | 1 + backend/src/schema/resolvers/messages.spec.ts | 69 +++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/backend/src/graphql/messages.ts b/backend/src/graphql/messages.ts index 4d2220f18..8a17d5208 100644 --- a/backend/src/graphql/messages.ts +++ b/backend/src/graphql/messages.ts @@ -17,6 +17,7 @@ export const messageQuery = () => { Message(roomId: $roomId) { _id id + indexId content senderId username diff --git a/backend/src/schema/resolvers/messages.spec.ts b/backend/src/schema/resolvers/messages.spec.ts index a43bd3226..c7733aa9b 100644 --- a/backend/src/schema/resolvers/messages.spec.ts +++ b/backend/src/schema/resolvers/messages.spec.ts @@ -212,6 +212,7 @@ describe('Message', () => { { id: expect.any(String), _id: result.data.Message[0].id, + indexId: '0', content: 'Some nice message to other chatting user', senderId: 'chatting-user', username: 'Chatting User', @@ -256,6 +257,7 @@ describe('Message', () => { Message: expect.arrayContaining([ expect.objectContaining({ id: expect.any(String), + indexId: '0', content: 'Some nice message to other chatting user', senderId: 'chatting-user', username: 'Chatting User', @@ -264,6 +266,7 @@ describe('Message', () => { }), expect.objectContaining({ id: expect.any(String), + indexId: '1', content: 'A nice response message to chatting user', senderId: 'other-chatting-user', username: 'Other Chatting User', @@ -272,6 +275,72 @@ describe('Message', () => { }), expect.objectContaining({ id: expect.any(String), + indexId: '2', + content: 'And another nice message to other chatting user', + senderId: 'chatting-user', + username: 'Chatting User', + avatar: expect.any(String), + date: expect.any(String), + }), + ]), + }, + }) + }) + + it('returns the messages paginated', async () => { + await expect( + query({ + query: messageQuery(), + variables: { + roomId, + first: 2, + offset: 0, + orderBy: 'created_desc', + }, + }), + ).resolves.toMatchObject({ + errors: undefined, + data: { + Message: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + indexId: '0', + content: 'Some nice message to other chatting user', + senderId: 'chatting-user', + username: 'Chatting User', + avatar: expect.any(String), + date: expect.any(String), + }), + expect.objectContaining({ + id: expect.any(String), + indexId: '1', + content: 'A nice response message to chatting user', + senderId: 'other-chatting-user', + username: 'Other Chatting User', + avatar: expect.any(String), + date: expect.any(String), + }), + ]), + }, + }) + + await expect( + query({ + query: messageQuery(), + variables: { + roomId, + first: 2, + offset: 2, + orderBy: 'created_desc', + }, + }), + ).resolves.toMatchObject({ + errors: undefined, + data: { + Message: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + indexId: '2', content: 'And another nice message to other chatting user', senderId: 'chatting-user', username: 'Chatting User', From 507be4eca2515c285368e02d843fc9d69a50b77a Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 13 Jul 2023 11:15:46 +0200 Subject: [PATCH 010/151] [feature] removed message actions for chat --- webapp/components/Chat/Chat.vue | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index 2b9514bf3..7f36d3c39 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -8,6 +8,7 @@ :template-actions="JSON.stringify(templatesText)" :menu-actions="JSON.stringify(menuActions)" :text-messages="JSON.stringify(textMessages)" + :message-actions="messageActions" :messages="JSON.stringify(messages)" :messages-loaded="messagesLoaded" :rooms="JSON.stringify(rooms)" @@ -59,14 +60,14 @@ export default { }, */ ], messageActions: [ - { - name: 'addMessageToFavorite', - title: 'Add To Favorite', - }, - { - name: 'shareMessage', - title: 'Share Message', - }, + // { + // name: 'addMessageToFavorite', + // title: 'Add To Favorite', + // }, + // { + // name: 'shareMessage', + // title: 'Share Message', + // }, ], templatesText: [ { From c21026e28ad393e7ff4c8d728d454e70f7cb2d53 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 13 Jul 2023 11:23:06 +0200 Subject: [PATCH 011/151] fixed message loading state when writing and refetching --- webapp/components/Chat/Chat.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index 507cce9da..b080f0763 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -152,7 +152,7 @@ export default { this.messagePage = 0 this.lastRoom = room.id } - this.messagesLoaded = false + this.messagesLoaded = options.refetch ? this.messagesLoaded : false const offset = (options.refetch ? 0 : this.messagePage) * this.messagePageSize setTimeout(async () => { try { From cbf1bab2f2cd31a965b528588308255e2d6a50ac Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 13 Jul 2023 11:31:04 +0200 Subject: [PATCH 012/151] [feature] remove reply emojis for chat component --- webapp/components/Chat/Chat.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index 2b9514bf3..b625eeba3 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -20,6 +20,7 @@ @fetch-messages="fetchMessages($event.detail[0])" :responsive-breakpoint="responsiveBreakpoint" :single-room="singleRoom" + show-reaction-emojis="false" @show-demo-options="showDemoOptions = $event" /> From ac6bbf18406560bdb4ef49e7670662c3a7598add Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 13 Jul 2023 11:33:45 +0200 Subject: [PATCH 013/151] remove setTimeout --- webapp/components/Chat/Chat.vue | 52 ++++++++++++++++----------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index b080f0763..99715ea97 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -146,7 +146,7 @@ export default { }), }, methods: { - fetchMessages({ room, options = {} }) { + async fetchMessages({ room, options = {} }) { if (this.lastRoom !== room.id) { this.messages = [] this.messagePage = 0 @@ -154,35 +154,33 @@ export default { } this.messagesLoaded = options.refetch ? this.messagesLoaded : false const offset = (options.refetch ? 0 : this.messagePage) * this.messagePageSize - setTimeout(async () => { - try { - const { - data: { Message }, - } = await this.$apollo.query({ - query: messageQuery(), - variables: { - roomId: room.id, - first: this.messagePageSize, - offset, - }, - fetchPolicy: 'no-cache', - }) + try { + const { + data: { Message }, + } = await this.$apollo.query({ + query: messageQuery(), + variables: { + roomId: room.id, + first: this.messagePageSize, + offset, + }, + fetchPolicy: 'no-cache', + }) - const msgs = [] - ;[...this.messages, ...Message].forEach((m) => { - msgs[m.indexId] = m - }) - this.messages = msgs.filter(Boolean) + const msgs = [] + ;[...this.messages, ...Message].forEach((m) => { + msgs[m.indexId] = m + }) + this.messages = msgs.filter(Boolean) - if (Message.length < this.messagePageSize) { - this.messagesLoaded = true - } - this.messagePage += 1 - } catch (error) { - this.messages = [] - this.$toast.error(error.message) + if (Message.length < this.messagePageSize) { + this.messagesLoaded = true } - }) + this.messagePage += 1 + } catch (error) { + this.messages = [] + this.$toast.error(error.message) + } }, refetchMessage(roomId) { From 751c8b60a66d6615abd03816f6c809236f0b55c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Thu, 13 Jul 2023 12:02:53 +0200 Subject: [PATCH 014/151] Add headline to chat page --- webapp/locales/de.json | 3 +++ webapp/locales/en.json | 3 +++ webapp/pages/chat.vue | 5 ++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/webapp/locales/de.json b/webapp/locales/de.json index a9a8e9ed4..450f5b3a0 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -78,6 +78,9 @@ } }, "chat": { + "page": { + "headline": "Chat" + }, "userProfileButton": { "label": "Chat", "tooltip": "Chatte mit „{name}“" diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 9af3e11af..bfb8b34c2 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -78,6 +78,9 @@ } }, "chat": { + "page": { + "headline": "Chat" + }, "userProfileButton": { "label": "Chat", "tooltip": "Chat with “{name}”" diff --git a/webapp/pages/chat.vue b/webapp/pages/chat.vue index 61192ea49..b23a6e8dc 100644 --- a/webapp/pages/chat.vue +++ b/webapp/pages/chat.vue @@ -1,5 +1,8 @@ diff --git a/webapp/graphql/Rooms.js b/webapp/graphql/Rooms.js index e28702f77..c659ba85c 100644 --- a/webapp/graphql/Rooms.js +++ b/webapp/graphql/Rooms.js @@ -27,3 +27,11 @@ export const createRoom = () => gql` } } ` + +export const unreadRoomsQuery = () => { + return gql` + query { + UnreadRooms + } + ` +} From db594650f9308e8c3bdd5e396c087598ae932d82 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Fri, 14 Jul 2023 18:53:56 +0200 Subject: [PATCH 039/151] 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 @@