fix(mobile): Chat startet bei neuester Nachricht und Chats nach letzter Aktivität sortieren

Der Verlauf nutzt eine invertierte Liste und öffnet dadurch immer ganz unten.
Raumlisten liefern lastActivityAt; die App sortiert alle Chats danach und
zieht einen Chat nach dem Senden sofort nach oben.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 13:40:09 +02:00
parent e0a94f5e29
commit f0775ad52a
7 changed files with 64 additions and 21 deletions

View File

@@ -158,6 +158,16 @@ export default async function communicationRoutes(server: FastifyInstance) {
entityUuid: room.entityUuid,
exists: true,
})
// Rooms without messages fall back to their creation date so new rooms still sort by recency.
const withActivity = async <T extends { room: typeof communicationRooms.$inferSelect }>(rows: T[]) => {
const ids = rows.map(({ room }) => room.id)
const latest = ids.length ? await server.db.select({
roomId: communicationMessages.roomId,
at: sql<Date>`max(${communicationMessages.createdAt})`.mapWith(communicationMessages.createdAt),
}).from(communicationMessages).where(inArray(communicationMessages.roomId, ids)).groupBy(communicationMessages.roomId) : []
const byRoom = new Map(latest.map((row) => [row.roomId, row.at]))
return (room: typeof communicationRooms.$inferSelect) => (byRoom.get(room.id) || room.createdAt).toISOString()
}
const messagesForRoom = async (roomId: string, currentUserId: string, tenantId: number, afterId = 0, limit = 50) => {
const conditions = [eq(communicationMessages.roomId, roomId)]
@@ -295,7 +305,8 @@ export default async function communicationRoutes(server: FastifyInstance) {
eq(communicationRooms.archived, false),
eq(communicationRoomMembers.userId, req.user.user_id)
)).orderBy(asc(communicationRooms.name))
return { rooms: rows.map(({ room }) => roomToApi(room)) }
const activity = await withActivity(rows)
return { rooms: rows.map(({ room }) => ({ ...roomToApi(room), lastActivityAt: activity(room) })) }
})
server.post("/communication/chat/rooms", async (req: any, reply) => {
@@ -335,9 +346,10 @@ export default async function communicationRoutes(server: FastifyInstance) {
projectChatAccess(server.db, req),
])
const roomsByProject = new Map(roomRows.map(({ room }) => [room.entityId, room]))
const activity = await withActivity(roomRows)
return { rooms: projectRows.map((project) => {
const room = roomsByProject.get(project.id)
return room ? { ...roomToApi(room), projectId: project.id, projectNumber: project.projectNumber } : {
return room ? { ...roomToApi(room), lastActivityAt: activity(room), projectId: project.id, projectNumber: project.projectNumber } : {
key: `project_${project.id}`,
name: project.projectNumber ? `${project.projectNumber} · ${project.name}` : project.name,
topic: `Projektkommunikation zu ${project.name}`,
@@ -387,11 +399,13 @@ export default async function communicationRoutes(server: FastifyInstance) {
.innerJoin(communicationRoomMembers, eq(communicationRoomMembers.roomId, communicationRooms.id))
.where(and(eq(communicationRooms.tenantId, tenantId), eq(communicationRooms.type, "direct"), eq(communicationRoomMembers.userId, req.user.user_id)))
const roomsByKey = new Map(existingRows.map(({ room }) => [room.key, room]))
const activity = await withActivity(existingRows)
return { rooms: users.map((user) => {
const key = directRoomKey(req.user.user_id, user.userId)
const room = roomsByKey.get(key)
return room ? {
...roomToApi(room),
lastActivityAt: activity(room),
name: displayName(user),
topic: `Direktnachricht mit ${displayName(user)}`,
entityUuid: user.userId,