From 6d356a5b24c3dd64be8aff6b9a6db6783d261317 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 4 Apr 2026 02:05:10 +0200 Subject: [PATCH] simplify things --- webapp/components/Chat/Chat.vue | 139 +++++++++++--------------------- webapp/graphql/Rooms.js | 14 ++++ 2 files changed, 61 insertions(+), 92 deletions(-) diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index 2071db1e6..759183869 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -71,30 +71,21 @@
- + - - +
- + {{ selectedRoom ? selectedRoom.roomName : '' }} - - - - {{ selectedRoom ? selectedRoom.roomName : '' }} - +
@@ -131,8 +122,7 @@ import { OsButton, OsIcon } from '@ocelot-social/ui' import { iconRegistry } from '~/utils/iconRegistry' import ProfileAvatar from '~/components/_new/generic/ProfileAvatar/ProfileAvatar' import locales from '~/locales/index.js' -import gql from 'graphql-tag' -import { roomQuery, createGroupRoom, unreadRoomsQuery } from '~/graphql/Rooms' +import { roomQuery, createGroupRoom, unreadRoomsQuery, userProfileQuery } from '~/graphql/Rooms' import { messageQuery, createMessageMutation, @@ -274,6 +264,10 @@ export default { } return { name: 'chat', query } }, + selectedRoomProfile() { + if (!this.selectedRoom) return null + return this.selectedRoom.isGroupRoom ? this.selectedRoom.groupProfile : this.selectedRoom.userProfile + }, roomHeaderLink() { if (!this.selectedRoom) return null if (this.selectedRoom.isGroupRoom && this.selectedRoom.groupProfile?.id) { @@ -309,12 +303,16 @@ export default { commitUnreadRoomCount: 'chat/UPDATE_ROOM_COUNT', }), - lastMessageContent(content, files) { - const text = (content || '').trim() - if (text) return text.substring(0, 30) - if (!files?.length) return '' - if (files[0].type?.startsWith('audio/') || files[0].audio) return '' - return `\uD83D\uDCCE ${files[0].name || ''}` + buildLastMessage(msg) { + const content = (msg.content || '').trim() + let preview = content ? content.substring(0, 30) : '' + if (!preview && msg.files?.length) { + const f = msg.files[0] + if (!f.type?.startsWith('audio/') && !f.audio) { + preview = `\uD83D\uDCCE ${f.name || ''}` + } + } + return { ...msg, content: preview, files: msg.files } }, markAsSeen(messageIds) { @@ -526,7 +524,7 @@ export default { }) }, - async fetchRooms({ room, search } = {}) { + async fetchRooms({ room, search, replace } = {}) { this.roomsLoaded = false try { const variables = room?.id @@ -541,11 +539,15 @@ export default { fetchPolicy: 'no-cache', }) - const existingIds = new Set(this.rooms.map((r) => r.id)) - const newRooms = Room.filter((r) => !existingIds.has(r.id)).map((r) => - this.fixRoomObject(r), - ) - this.rooms = [...this.rooms, ...newRooms] + if (replace) { + this.rooms = Room.map((r) => this.fixRoomObject(r)) + } else { + const existingIds = new Set(this.rooms.map((r) => r.id)) + const newRooms = Room.filter((r) => !existingIds.has(r.id)).map((r) => + this.fixRoomObject(r), + ) + this.rooms = [...this.rooms, ...newRooms] + } if (!room?.id && Room.length > 0) { const lastRoom = Room[Room.length - 1] @@ -571,39 +573,14 @@ export default { this.roomSearch = value || '' this.roomCursor = null if (this.roomsLoaded) this.roomObserverDirty = true - this.roomsLoaded = false this.roomSearchGeneration = (this.roomSearchGeneration || 0) + 1 const generation = this.roomSearchGeneration - try { - const variables = { first: this.roomPageSize, ...(this.roomSearch && { search: this.roomSearch }) } - const { data: { Room } } = await this.$apollo.query({ - query: roomQuery(), - variables, - fetchPolicy: 'no-cache', - }) - if (generation !== this.roomSearchGeneration) return - this.rooms = Room.map((r) => this.fixRoomObject(r)) - if (Room.length > 0) { - const lastRoom = Room[Room.length - 1] - this.roomCursor = lastRoom.lastMessageAt || lastRoom.createdAt - } - if (Room.length < this.roomPageSize) { - this.roomsLoaded = true - } - } catch (error) { - if (generation !== this.roomSearchGeneration) return - this.rooms = [] - this.$toast.error(error.message) - } - // Re-init IntersectionObserver after it was disabled by roomsLoaded=true. - // The library only calls initIntersectionObserver on loadingRooms true→false, - // but toggling loadingRooms destroys the search input. Instead, we directly - // access the web component's shadow DOM to re-enable the loader and observer. + await this.fetchRooms({ search: this.roomSearch || undefined, replace: true }) + if (generation !== this.roomSearchGeneration) return + // Re-init IntersectionObserver after it was disabled by roomsLoaded=true if (this.roomObserverDirty && !this.roomsLoaded) { this.roomObserverDirty = false - this.$nextTick(() => { - this.reinitRoomLoader() - }) + this.$nextTick(() => this.reinitRoomLoader()) } }, @@ -713,11 +690,7 @@ export default { } } const changedRoom = { ...this.rooms[roomIndex] } - changedRoom.lastMessage = { - ...msg, - content: this.lastMessageContent(msg.content, msg.files), - files: msg.files, - } + changedRoom.lastMessage = this.buildLastMessage(msg) changedRoom.lastMessageAt = msg.date changedRoom.index = new Date().toISOString() const isCurrentRoom = msg.room.id === this.selectedRoom?.id @@ -725,8 +698,7 @@ export default { if (!isCurrentRoom && !isOwnMessage) { changedRoom.unreadCount++ } - // Reassign array to trigger Vue reactivity and vue-advanced-chat re-sort - this.rooms = [changedRoom, ...this.rooms.filter((r) => r.id !== msg.room.id)] + this.moveRoomToTop(changedRoom, msg.room.id) // Only add incoming messages to the chat — own messages are handled via mutation response if (isCurrentRoom && !isOwnMessage) { this.addSocketMessage(msg) @@ -824,13 +796,10 @@ export default { const roomIndex = this.rooms.findIndex((r) => r.id === roomId) if (roomIndex !== -1) { const changedRoom = { ...this.rooms[roomIndex] } - changedRoom.lastMessage.content = this.lastMessageContent(content, files) - changedRoom.lastMessage.files = files + changedRoom.lastMessage = this.buildLastMessage({ content, files }) changedRoom.index = new Date().toISOString() - this.rooms = [changedRoom, ...this.rooms.filter((r) => r.id !== roomId)] - this.$nextTick(() => { - this.scrollRoomsListToTop() - }) + this.moveRoomToTop(changedRoom, roomId) + this.$nextTick(() => this.scrollRoomsListToTop()) } try { @@ -903,13 +872,7 @@ export default { userProfile: null, index: room.lastMessage ? room.lastMessage.date : room.createdAt, avatar: room.avatar?.w320 || room.avatar, - lastMessage: room.lastMessage - ? { - ...room.lastMessage, - content: this.lastMessageContent(room.lastMessage?.content, room.lastMessage?.files), - files: room.lastMessage?.files, - } - : { content: '' }, + lastMessage: room.lastMessage ? this.buildLastMessage(room.lastMessage) : { content: '' }, users: room.users.map((u) => { return { ...u, username: u.name, avatar: u.avatar?.w320 } }), @@ -937,13 +900,18 @@ export default { return fixedRoom }, + moveRoomToTop(room, roomId) { + const id = roomId || room.id + this.rooms = [room, ...this.rooms.filter((r) => r.id !== id)] + }, + selectRoom(room) { this.activeRoomId = room.roomId }, bringRoomToTopAndSelect(room) { room.index = new Date().toISOString() - this.rooms = [room, ...this.rooms.filter((r) => r.id !== room.id)] + this.moveRoomToTop(room) this.$nextTick(() => this.selectRoom(room)) }, @@ -958,20 +926,7 @@ export default { if (typeof userOrId === 'string') { try { const { data } = await this.$apollo.query({ - query: gql` - query ($id: ID!) { - User(id: $id) { - id - name - avatar { - url - w320 - w640 - w1024 - } - } - } - `, + query: userProfileQuery(), variables: { id: userId }, fetchPolicy: 'no-cache', }) diff --git a/webapp/graphql/Rooms.js b/webapp/graphql/Rooms.js index 80b964e03..d4c3968ca 100644 --- a/webapp/graphql/Rooms.js +++ b/webapp/graphql/Rooms.js @@ -86,6 +86,20 @@ export const roomQuery = () => gql` } ` +export const userProfileQuery = () => gql` + ${imageUrls} + + query ($id: ID!) { + User(id: $id) { + id + name + avatar { + ...imageUrls + } + } + } +` + export const unreadRoomsQuery = () => { return gql` query {