From 28797a0141b9d1978452da7fe3c0b987821e85b7 Mon Sep 17 00:00:00 2001 From: Myx Date: Fri, 17 Apr 2026 03:53:53 +0200 Subject: [PATCH] perf: use lookup for chats --- .../chat-message-item.component.ts | 9 ++++---- .../chat-message-list.component.html | 1 + .../chat-message-list.component.ts | 21 ++++++++++++++++++- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/toju-app/src/app/domains/chat/feature/chat-messages/components/message-item/chat-message-item.component.ts b/toju-app/src/app/domains/chat/feature/chat-messages/components/message-item/chat-message-item.component.ts index 06a3ba4..5b89824 100644 --- a/toju-app/src/app/domains/chat/feature/chat-messages/components/message-item/chat-message-item.component.ts +++ b/toju-app/src/app/domains/chat/feature/chat-messages/components/message-item/chat-message-item.component.ts @@ -12,7 +12,7 @@ import { signal, ViewChild } from '@angular/core'; -import { Store } from '@ngrx/store'; + import { NgIcon, provideIcons } from '@ng-icons/core'; import { lucideCheck, @@ -36,7 +36,7 @@ import { Message, User } from '../../../../../../shared-kernel'; -import { selectAllUsers } from '../../../../../../store/users/users.selectors'; + import { ChatAudioPlayerComponent, ChatVideoPlayerComponent, @@ -121,8 +121,6 @@ export class ChatMessageItemComponent { private readonly attachmentsSvc = inject(AttachmentFacade); private readonly klipy = inject(KlipyService); - private readonly store = inject(Store); - private readonly allUsers = this.store.selectSignal(selectAllUsers); private readonly profileCard = inject(ProfileCardService); private readonly attachmentVersion = signal(this.attachmentsSvc.updated()); @@ -130,6 +128,7 @@ export class ChatMessageItemComponent { readonly repliedMessage = input(); readonly currentUserId = input(null); readonly isAdmin = input(false); + readonly userLookup = input>(new Map()); readonly replyRequested = output(); readonly deleteRequested = output(); @@ -148,7 +147,7 @@ export class ChatMessageItemComponent { readonly showEmojiPicker = signal(false); readonly senderUser = computed(() => { const msg = this.message(); - const found = this.allUsers().find((userEntry) => userEntry.id === msg.senderId || userEntry.oderId === msg.senderId); + const found = this.userLookup().get(msg.senderId); return found ?? { id: msg.senderId, diff --git a/toju-app/src/app/domains/chat/feature/chat-messages/components/message-list/chat-message-list.component.html b/toju-app/src/app/domains/chat/feature/chat-messages/components/message-list/chat-message-list.component.html index 7119f5f..3b64d66 100644 --- a/toju-app/src/app/domains/chat/feature/chat-messages/components/message-list/chat-message-list.component.html +++ b/toju-app/src/app/domains/chat/feature/chat-messages/components/message-list/chat-message-list.component.html @@ -53,6 +53,7 @@ [repliedMessage]="findRepliedMessage(message.replyToId)" [currentUserId]="currentUserId()" [isAdmin]="isAdmin()" + [userLookup]="userLookup()" (replyRequested)="handleReplyRequested($event)" (deleteRequested)="handleDeleteRequested($event)" (editSaved)="handleEditSaved($event)" diff --git a/toju-app/src/app/domains/chat/feature/chat-messages/components/message-list/chat-message-list.component.ts b/toju-app/src/app/domains/chat/feature/chat-messages/components/message-list/chat-message-list.component.ts index bc0b614..b9cadfe 100644 --- a/toju-app/src/app/domains/chat/feature/chat-messages/components/message-list/chat-message-list.component.ts +++ b/toju-app/src/app/domains/chat/feature/chat-messages/components/message-list/chat-message-list.component.ts @@ -8,13 +8,15 @@ import { ViewChild, computed, effect, + inject, input, output, signal } from '@angular/core'; +import { Store } from '@ngrx/store'; import { Attachment } from '../../../../../attachment'; import { getMessageTimestamp } from '../../../../domain/rules/message.rules'; -import { Message } from '../../../../../../shared-kernel'; +import { Message, User } from '../../../../../../shared-kernel'; import { ChatMessageDeleteEvent, ChatMessageEditEvent, @@ -23,6 +25,7 @@ import { ChatMessageReactionEvent, ChatMessageReplyEvent } from '../../models/chat-messages.model'; +import { selectAllUsers } from '../../../../../../store/users/users.selectors'; import { ChatMessageItemComponent } from '../message-item/chat-message-item.component'; interface PrismGlobal { @@ -47,6 +50,8 @@ declare global { export class ChatMessageListComponent implements AfterViewChecked, OnDestroy { @ViewChild('messagesContainer') messagesContainer?: ElementRef; + private readonly store = inject(Store); + private readonly allUsers = this.store.selectSignal(selectAllUsers); private readonly dateSeparatorFormatter = new Intl.DateTimeFormat('en-GB', { day: 'numeric', month: 'long', @@ -110,6 +115,20 @@ export class ChatMessageListComponent implements AfterViewChecked, OnDestroy { return labels; }); + readonly userLookup = computed>(() => { + const lookup = new Map(); + + for (const user of this.allUsers()) { + lookup.set(user.id, user); + + if (user.oderId && user.oderId !== user.id) { + lookup.set(user.oderId, user); + } + } + + return lookup; + }); + private initialScrollObserver: MutationObserver | null = null; private initialScrollTimer: ReturnType | null = null; private boundOnImageLoad: (() => void) | null = null;