From 0bc19cf226c0b5ed31af0fd40b853ce3b2e56e83 Mon Sep 17 00:00:00 2001 From: Markus Date: Wed, 19 Jul 2023 10:15:56 +0200 Subject: [PATCH 01/14] [fix] added usertag as allowed html tags, so user highlighting works for the chat --- backend/src/middleware/helpers/cleanHtml.ts | 1 + webapp/components/Chat/Chat.vue | 6 ------ 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/backend/src/middleware/helpers/cleanHtml.ts b/backend/src/middleware/helpers/cleanHtml.ts index ac71f6bdc..84497760d 100644 --- a/backend/src/middleware/helpers/cleanHtml.ts +++ b/backend/src/middleware/helpers/cleanHtml.ts @@ -30,6 +30,7 @@ const standardSanitizeHtmlOptions = { 'strike', 'span', 'blockquote', + 'usertag', ], allowedAttributes: { a: ['href', 'class', 'target', 'data-*', 'contenteditable'], diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index d7864ebef..171d75f63 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -334,12 +334,6 @@ export default { }, async sendMessage(message) { - // 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({ mutation: createMessageMutation(), From 961a260044573db0d92cd95ada7395635497899a Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 19 Jul 2023 10:39:54 +0200 Subject: [PATCH 02/14] remove default avatar, allow refetch rooms, count down read messages, update lastMessage & lastMessageAt, ... --- webapp/components/Chat/Chat.vue | 42 ++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index d7864ebef..4ac5606e8 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -35,7 +35,7 @@
@@ -46,7 +46,7 @@
@@ -222,9 +222,9 @@ export default { ...mapMutations({ commitUnreadRoomCount: 'chat/UPDATE_ROOM_COUNT', }), - async fetchRooms({ room } = {}) { - this.roomsLoaded = false - const offset = this.roomPage * this.roomPageSize + async fetchRooms({ room, options = {} } = {}) { + this.roomsLoaded = options.refetch ? this.roomsLoaded : false + const offset = (options.refetch ? 0 : this.roomPage) * this.roomPageSize try { const { data: { Room }, @@ -238,16 +238,19 @@ export default { fetchPolicy: 'no-cache', }) - const newRooms = Room.map((r) => { - return { - ...r, + + const rms = [] + const rmsIds = [] + ;[...Room, ...this.rooms].forEach((r) => { + if(!rmsIds.find((v) => v === r.id)){ + rms.push({...r, index: r.lastMessage.date, users: r.users.map((u) => { return { ...u, username: u.name, avatar: u.avatar?.url } - }), + })}) + rmsIds.push(r.id) } }) - - this.rooms = [...this.rooms, ...newRooms] + this.rooms = rms if (Room.length < this.roomPageSize) { this.roomsLoaded = true @@ -282,8 +285,12 @@ export default { fetchPolicy: 'no-cache', }) - const newMsgIds = Message.filter((m) => m.seen === false).map((m) => m.id) + const newMsgIds = Message.filter((m) => m.seen === false && m.senderId !== this.currentUser.id).map((m) => m.id) if (newMsgIds.length) { + const roomIndex = this.rooms.findIndex((r) => r.id === room.id) + const changedRoom = {...this.rooms[roomIndex]} + changedRoom.unreadCount = changedRoom.unreadCount - newMsgIds.length + this.rooms[roomIndex] = changedRoom this.$apollo .mutate({ mutation: markMessagesAsSeen(), @@ -322,14 +329,15 @@ export default { }, async chatMessageAdded({ data }) { + const roomIndex = this.rooms.findIndex((r) => r.id === data.chatMessageAdded.room.id) + const changedRoom = {...this.rooms[roomIndex]} + changedRoom.lastMessage = data.chatMessageAdded + changedRoom.lastMessageAt = data.chatMessageAdded.date + this.rooms[roomIndex] = changedRoom if (data.chatMessageAdded.room.id === this.selectedRoom?.id) { this.fetchMessages({ room: this.selectedRoom, options: { refetch: true } }) } else { - // TODO this might be optimized selectively (first page vs rest) - this.rooms = [] - this.roomPage = 0 - this.roomsLoaded = false - this.fetchRooms() + this.fetchRooms({ options: { refetch: true } }) } }, From 41cca4f4ec93d28a265aa3683973c24eec67db21 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 19 Jul 2023 10:42:21 +0200 Subject: [PATCH 03/14] lint --- webapp/components/Chat/Chat.vue | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index 4ac5606e8..698a270b7 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -238,15 +238,17 @@ export default { fetchPolicy: 'no-cache', }) - const rms = [] const rmsIds = [] ;[...Room, ...this.rooms].forEach((r) => { - if(!rmsIds.find((v) => v === r.id)){ - rms.push({...r, index: r.lastMessage.date, - users: r.users.map((u) => { - return { ...u, username: u.name, avatar: u.avatar?.url } - })}) + if (!rmsIds.find((v) => v === r.id)) { + rms.push({ + ...r, + index: r.lastMessage.date, + users: r.users.map((u) => { + return { ...u, username: u.name, avatar: u.avatar?.url } + }), + }) rmsIds.push(r.id) } }) @@ -285,10 +287,12 @@ export default { fetchPolicy: 'no-cache', }) - const newMsgIds = Message.filter((m) => m.seen === false && m.senderId !== this.currentUser.id).map((m) => m.id) + const newMsgIds = Message.filter( + (m) => m.seen === false && m.senderId !== this.currentUser.id, + ).map((m) => m.id) if (newMsgIds.length) { const roomIndex = this.rooms.findIndex((r) => r.id === room.id) - const changedRoom = {...this.rooms[roomIndex]} + const changedRoom = { ...this.rooms[roomIndex] } changedRoom.unreadCount = changedRoom.unreadCount - newMsgIds.length this.rooms[roomIndex] = changedRoom this.$apollo @@ -330,7 +334,7 @@ export default { async chatMessageAdded({ data }) { const roomIndex = this.rooms.findIndex((r) => r.id === data.chatMessageAdded.room.id) - const changedRoom = {...this.rooms[roomIndex]} + const changedRoom = { ...this.rooms[roomIndex] } changedRoom.lastMessage = data.chatMessageAdded changedRoom.lastMessageAt = data.chatMessageAdded.date this.rooms[roomIndex] = changedRoom From f7dd605838466b8d1acaf21de3c0ba05d6f7dc08 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 19 Jul 2023 11:36:35 +0200 Subject: [PATCH 04/14] shorten room message, update room message when sending --- webapp/components/Chat/Chat.vue | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index 698a270b7..320ea3357 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -245,6 +245,7 @@ export default { rms.push({ ...r, index: r.lastMessage.date, + lastMessage: {...r.lastMessage, content: r.lastMessage.content.trim().substring(0,30)}, users: r.users.map((u) => { return { ...u, username: u.name, avatar: u.avatar?.url } }), @@ -336,6 +337,7 @@ export default { const roomIndex = this.rooms.findIndex((r) => r.id === data.chatMessageAdded.room.id) const changedRoom = { ...this.rooms[roomIndex] } changedRoom.lastMessage = data.chatMessageAdded + changedRoom.lastMessage.content = changedRoom.lastMessage.content.trim().substring(0,30) changedRoom.lastMessageAt = data.chatMessageAdded.date this.rooms[roomIndex] = changedRoom if (data.chatMessageAdded.room.id === this.selectedRoom?.id) { @@ -360,6 +362,12 @@ export default { content: message.content, }, }) + const roomIndex = this.rooms.findIndex((r) => r.id === message.roomId) + const changedRoom = { ...this.rooms[roomIndex] } + console.log(changedRoom) + changedRoom.lastMessage = message + changedRoom.lastMessage.content = changedRoom.lastMessage.content.trim().substring(0,30) + this.rooms[roomIndex] = changedRoom } catch (error) { this.$toast.error(error.message) } From 24187bf8a92d08e85988d2dbe9f640d54e9ec9a2 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 19 Jul 2023 11:45:36 +0200 Subject: [PATCH 05/14] increase unreadCount when receiving a message, fix problem with empty room --- webapp/components/Chat/Chat.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index 320ea3357..9fec8d96f 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -244,8 +244,8 @@ export default { if (!rmsIds.find((v) => v === r.id)) { rms.push({ ...r, - index: r.lastMessage.date, - lastMessage: {...r.lastMessage, content: r.lastMessage.content.trim().substring(0,30)}, + index: r.lastMessage?.date, + lastMessage: {...r.lastMessage, content: r.lastMessage?.content.trim().substring(0,30)}, users: r.users.map((u) => { return { ...u, username: u.name, avatar: u.avatar?.url } }), @@ -339,6 +339,7 @@ export default { changedRoom.lastMessage = data.chatMessageAdded changedRoom.lastMessage.content = changedRoom.lastMessage.content.trim().substring(0,30) changedRoom.lastMessageAt = data.chatMessageAdded.date + changedRoom.unreadCount++ this.rooms[roomIndex] = changedRoom if (data.chatMessageAdded.room.id === this.selectedRoom?.id) { this.fetchMessages({ room: this.selectedRoom, options: { refetch: true } }) From 93aaea4aa1bb21c6841728c3c402fec3168ae414 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 19 Jul 2023 11:51:41 +0200 Subject: [PATCH 06/14] only take 2000 chat message characters --- backend/src/schema/resolvers/messages.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/schema/resolvers/messages.ts b/backend/src/schema/resolvers/messages.ts index 078584c9d..b7e7a7a73 100644 --- a/backend/src/schema/resolvers/messages.ts +++ b/backend/src/schema/resolvers/messages.ts @@ -81,7 +81,7 @@ export default { createdAt: toString(datetime()), id: apoc.create.uuid(), indexId: CASE WHEN maxIndex IS NOT NULL THEN maxIndex + 1 ELSE 0 END, - content: $content, + content: LEFT($content,2000), saved: true, distributed: false, seen: false From 779d5641592c4386dbafdd5134adafefe486df93 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 19 Jul 2023 11:53:13 +0200 Subject: [PATCH 07/14] lint fixes --- webapp/components/Chat/Chat.vue | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index 9fec8d96f..fdf1b3301 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -245,7 +245,10 @@ export default { rms.push({ ...r, index: r.lastMessage?.date, - lastMessage: {...r.lastMessage, content: r.lastMessage?.content.trim().substring(0,30)}, + lastMessage: { + ...r.lastMessage, + content: r.lastMessage?.content.trim().substring(0, 30), + }, users: r.users.map((u) => { return { ...u, username: u.name, avatar: u.avatar?.url } }), @@ -337,9 +340,9 @@ export default { const roomIndex = this.rooms.findIndex((r) => r.id === data.chatMessageAdded.room.id) const changedRoom = { ...this.rooms[roomIndex] } changedRoom.lastMessage = data.chatMessageAdded - changedRoom.lastMessage.content = changedRoom.lastMessage.content.trim().substring(0,30) + changedRoom.lastMessage.content = changedRoom.lastMessage.content.trim().substring(0, 30) changedRoom.lastMessageAt = data.chatMessageAdded.date - changedRoom.unreadCount++ + changedRoom.unreadCount++ this.rooms[roomIndex] = changedRoom if (data.chatMessageAdded.room.id === this.selectedRoom?.id) { this.fetchMessages({ room: this.selectedRoom, options: { refetch: true } }) @@ -365,9 +368,8 @@ export default { }) const roomIndex = this.rooms.findIndex((r) => r.id === message.roomId) const changedRoom = { ...this.rooms[roomIndex] } - console.log(changedRoom) changedRoom.lastMessage = message - changedRoom.lastMessage.content = changedRoom.lastMessage.content.trim().substring(0,30) + changedRoom.lastMessage.content = changedRoom.lastMessage.content.trim().substring(0, 30) this.rooms[roomIndex] = changedRoom } catch (error) { this.$toast.error(error.message) From 02cbe6c19b4c4d676094f849cd05a740feb2360a Mon Sep 17 00:00:00 2001 From: Markus Date: Wed, 19 Jul 2023 11:56:38 +0200 Subject: [PATCH 08/14] [feature] chat component can now show clickable urls --- 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 d7864ebef..d00133a56 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -315,6 +315,12 @@ export default { this.messagesLoaded = true } this.messagePage += 1 + + // hacky way to make urls clickable for the chat component + // --> linkify in the backend is changing the syntax of the url + this.messages.forEach((msg) => { + msg.content = msg.content.replace(/<\/?a[^>]*>/g, '') + }) } catch (error) { this.messages = [] this.$toast.error(error.message) From 6acdde177fc4d0aa5a885927bff337dea174cabf Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 19 Jul 2023 12:13:18 +0200 Subject: [PATCH 09/14] Revert "[feature] chat component can now show clickable urls" This reverts commit 02cbe6c19b4c4d676094f849cd05a740feb2360a. --- webapp/components/Chat/Chat.vue | 6 ------ 1 file changed, 6 deletions(-) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index d00133a56..d7864ebef 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -315,12 +315,6 @@ export default { this.messagesLoaded = true } this.messagePage += 1 - - // hacky way to make urls clickable for the chat component - // --> linkify in the backend is changing the syntax of the url - this.messages.forEach((msg) => { - msg.content = msg.content.replace(/<\/?a[^>]*>/g, '') - }) } catch (error) { this.messages = [] this.$toast.error(error.message) From 256bcc2af7d12e00a9253e92bab515fd2c51d9c6 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 19 Jul 2023 12:13:49 +0200 Subject: [PATCH 10/14] do not filter chat message content with xss-middleware --- backend/src/middleware/xssMiddleware.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/middleware/xssMiddleware.ts b/backend/src/middleware/xssMiddleware.ts index ede0cc199..9d8671137 100644 --- a/backend/src/middleware/xssMiddleware.ts +++ b/backend/src/middleware/xssMiddleware.ts @@ -3,7 +3,7 @@ import { cleanHtml } from '../middleware/helpers/cleanHtml' // exclamation mark separetes field names, that should not be sanitized const fields = [ - 'content', + 'content!message', 'contentExcerpt', 'reasonDescription', 'description!embed', From 4448ecd6fedea53ad45c8e29bd58b8066c4ff5d6 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 19 Jul 2023 14:12:25 +0200 Subject: [PATCH 11/14] fix walk recursive & field definitions --- backend/src/helpers/walkRecursive.ts | 8 ++++---- backend/src/middleware/xssMiddleware.ts | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/src/helpers/walkRecursive.ts b/backend/src/helpers/walkRecursive.ts index f560cf9cb..4f7adf497 100644 --- a/backend/src/helpers/walkRecursive.ts +++ b/backend/src/helpers/walkRecursive.ts @@ -9,10 +9,10 @@ function walkRecursive(data, fields, fieldName, callback, _key?) { if (!Array.isArray(fields)) { throw new Error('please provide an fields array for the walkRecursive helper') } - if (data && typeof data === 'string' && fields.includes(_key)) { - // well we found what we searched for, lets replace the value with our callback result - const key = _key.split('!') - if (key.length === 1 || key[1] !== fieldName) data = callback(data, key[0]) + // console.log(_key) + const fieldDef = fields.find((f) => f.field === _key) + if (data && typeof data === 'string' && fieldDef) { + if (!fieldDef.excludes?.includes(fieldName)) data = callback(data, _key) } else if (data && Array.isArray(data)) { // go into the rabbit hole and dig through that array data.forEach((res, index) => { diff --git a/backend/src/middleware/xssMiddleware.ts b/backend/src/middleware/xssMiddleware.ts index 9d8671137..33fdcf2c6 100644 --- a/backend/src/middleware/xssMiddleware.ts +++ b/backend/src/middleware/xssMiddleware.ts @@ -3,11 +3,11 @@ import { cleanHtml } from '../middleware/helpers/cleanHtml' // exclamation mark separetes field names, that should not be sanitized const fields = [ - 'content!message', - 'contentExcerpt', - 'reasonDescription', - 'description!embed', - 'descriptionExcerpt', + { field: 'content', excludes: ['message'] }, + { field: 'contentExcerpt' }, + { field: 'reasonDescription' }, + { field: 'description', excludes: ['embed'] }, + { field: 'descriptionExcerpt' }, ] export default { From bc1015da8c754bb28ffd0ab1616962b1d1ed7fd9 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 19 Jul 2023 14:22:04 +0200 Subject: [PATCH 12/14] corrected field names to exclude, remove comment --- backend/src/helpers/walkRecursive.ts | 1 - backend/src/middleware/xssMiddleware.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/src/helpers/walkRecursive.ts b/backend/src/helpers/walkRecursive.ts index 4f7adf497..f3be67575 100644 --- a/backend/src/helpers/walkRecursive.ts +++ b/backend/src/helpers/walkRecursive.ts @@ -9,7 +9,6 @@ function walkRecursive(data, fields, fieldName, callback, _key?) { if (!Array.isArray(fields)) { throw new Error('please provide an fields array for the walkRecursive helper') } - // console.log(_key) const fieldDef = fields.find((f) => f.field === _key) if (data && typeof data === 'string' && fieldDef) { if (!fieldDef.excludes?.includes(fieldName)) data = callback(data, _key) diff --git a/backend/src/middleware/xssMiddleware.ts b/backend/src/middleware/xssMiddleware.ts index 33fdcf2c6..c10997e8d 100644 --- a/backend/src/middleware/xssMiddleware.ts +++ b/backend/src/middleware/xssMiddleware.ts @@ -3,7 +3,7 @@ import { cleanHtml } from '../middleware/helpers/cleanHtml' // exclamation mark separetes field names, that should not be sanitized const fields = [ - { field: 'content', excludes: ['message'] }, + { field: 'content', excludes: ['CreateMessage', 'Message'] }, { field: 'contentExcerpt' }, { field: 'reasonDescription' }, { field: 'description', excludes: ['embed'] }, From 2f76d0b502936d0265dcb6e07eec0409868c568e Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 19 Jul 2023 14:38:23 +0200 Subject: [PATCH 13/14] fix lastMessage to contain proper values --- webapp/components/Chat/Chat.vue | 4 ++-- webapp/graphql/Messages.js | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index d333deecc..af8a2490f 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -353,7 +353,7 @@ export default { async sendMessage(message) { try { - await this.$apollo.mutate({ + const { data: { CreateMessage: createdMessage }} = await this.$apollo.mutate({ mutation: createMessageMutation(), variables: { roomId: message.roomId, @@ -362,7 +362,7 @@ export default { }) const roomIndex = this.rooms.findIndex((r) => r.id === message.roomId) const changedRoom = { ...this.rooms[roomIndex] } - changedRoom.lastMessage = message + changedRoom.lastMessage = createdMessage changedRoom.lastMessage.content = changedRoom.lastMessage.content.trim().substring(0, 30) this.rooms[roomIndex] = changedRoom } catch (error) { diff --git a/webapp/graphql/Messages.js b/webapp/graphql/Messages.js index e6292c509..cb5d37df9 100644 --- a/webapp/graphql/Messages.js +++ b/webapp/graphql/Messages.js @@ -4,8 +4,23 @@ export const createMessageMutation = () => { return gql` mutation ($roomId: ID!, $content: String!) { CreateMessage(roomId: $roomId, content: $content) { + #_id id + indexId content + senderId + author { + id + } + username + avatar + date + room { + id + } + saved + distributed + seen } } ` @@ -26,6 +41,9 @@ export const messageQuery = () => { username avatar date + room { + id + } saved distributed seen From ea368cd0f308831550ba27d28f682a17ae5e39cc Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 19 Jul 2023 15:09:13 +0200 Subject: [PATCH 14/14] lint fixes --- webapp/components/Chat/Chat.vue | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index af8a2490f..63cf045e8 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -353,7 +353,9 @@ export default { async sendMessage(message) { try { - const { data: { CreateMessage: createdMessage }} = await this.$apollo.mutate({ + const { + data: { CreateMessage: createdMessage }, + } = await this.$apollo.mutate({ mutation: createMessageMutation(), variables: { roomId: message.roomId,