perf: use lookup for chats
Some checks failed
Queue Release Build / prepare (push) Successful in 16s
Deploy Web Apps / deploy (push) Successful in 15m8s
Queue Release Build / build-linux (push) Successful in 26m49s
Queue Release Build / build-windows (push) Failing after 12m6s
Queue Release Build / finalize (push) Has been skipped

This commit is contained in:
2026-04-17 03:53:53 +02:00
parent 17738ec484
commit 28797a0141
3 changed files with 25 additions and 6 deletions

View File

@@ -12,7 +12,7 @@ import {
signal, signal,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { Store } from '@ngrx/store';
import { NgIcon, provideIcons } from '@ng-icons/core'; import { NgIcon, provideIcons } from '@ng-icons/core';
import { import {
lucideCheck, lucideCheck,
@@ -36,7 +36,7 @@ import {
Message, Message,
User User
} from '../../../../../../shared-kernel'; } from '../../../../../../shared-kernel';
import { selectAllUsers } from '../../../../../../store/users/users.selectors';
import { import {
ChatAudioPlayerComponent, ChatAudioPlayerComponent,
ChatVideoPlayerComponent, ChatVideoPlayerComponent,
@@ -121,8 +121,6 @@ export class ChatMessageItemComponent {
private readonly attachmentsSvc = inject(AttachmentFacade); private readonly attachmentsSvc = inject(AttachmentFacade);
private readonly klipy = inject(KlipyService); private readonly klipy = inject(KlipyService);
private readonly store = inject(Store);
private readonly allUsers = this.store.selectSignal(selectAllUsers);
private readonly profileCard = inject(ProfileCardService); private readonly profileCard = inject(ProfileCardService);
private readonly attachmentVersion = signal(this.attachmentsSvc.updated()); private readonly attachmentVersion = signal(this.attachmentsSvc.updated());
@@ -130,6 +128,7 @@ export class ChatMessageItemComponent {
readonly repliedMessage = input<Message | undefined>(); readonly repliedMessage = input<Message | undefined>();
readonly currentUserId = input<string | null>(null); readonly currentUserId = input<string | null>(null);
readonly isAdmin = input(false); readonly isAdmin = input(false);
readonly userLookup = input<ReadonlyMap<string, User>>(new Map());
readonly replyRequested = output<ChatMessageReplyEvent>(); readonly replyRequested = output<ChatMessageReplyEvent>();
readonly deleteRequested = output<ChatMessageDeleteEvent>(); readonly deleteRequested = output<ChatMessageDeleteEvent>();
@@ -148,7 +147,7 @@ export class ChatMessageItemComponent {
readonly showEmojiPicker = signal(false); readonly showEmojiPicker = signal(false);
readonly senderUser = computed<User>(() => { readonly senderUser = computed<User>(() => {
const msg = this.message(); 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 ?? { return found ?? {
id: msg.senderId, id: msg.senderId,

View File

@@ -53,6 +53,7 @@
[repliedMessage]="findRepliedMessage(message.replyToId)" [repliedMessage]="findRepliedMessage(message.replyToId)"
[currentUserId]="currentUserId()" [currentUserId]="currentUserId()"
[isAdmin]="isAdmin()" [isAdmin]="isAdmin()"
[userLookup]="userLookup()"
(replyRequested)="handleReplyRequested($event)" (replyRequested)="handleReplyRequested($event)"
(deleteRequested)="handleDeleteRequested($event)" (deleteRequested)="handleDeleteRequested($event)"
(editSaved)="handleEditSaved($event)" (editSaved)="handleEditSaved($event)"

View File

@@ -8,13 +8,15 @@ import {
ViewChild, ViewChild,
computed, computed,
effect, effect,
inject,
input, input,
output, output,
signal signal
} from '@angular/core'; } from '@angular/core';
import { Store } from '@ngrx/store';
import { Attachment } from '../../../../../attachment'; import { Attachment } from '../../../../../attachment';
import { getMessageTimestamp } from '../../../../domain/rules/message.rules'; import { getMessageTimestamp } from '../../../../domain/rules/message.rules';
import { Message } from '../../../../../../shared-kernel'; import { Message, User } from '../../../../../../shared-kernel';
import { import {
ChatMessageDeleteEvent, ChatMessageDeleteEvent,
ChatMessageEditEvent, ChatMessageEditEvent,
@@ -23,6 +25,7 @@ import {
ChatMessageReactionEvent, ChatMessageReactionEvent,
ChatMessageReplyEvent ChatMessageReplyEvent
} from '../../models/chat-messages.model'; } from '../../models/chat-messages.model';
import { selectAllUsers } from '../../../../../../store/users/users.selectors';
import { ChatMessageItemComponent } from '../message-item/chat-message-item.component'; import { ChatMessageItemComponent } from '../message-item/chat-message-item.component';
interface PrismGlobal { interface PrismGlobal {
@@ -47,6 +50,8 @@ declare global {
export class ChatMessageListComponent implements AfterViewChecked, OnDestroy { export class ChatMessageListComponent implements AfterViewChecked, OnDestroy {
@ViewChild('messagesContainer') messagesContainer?: ElementRef<HTMLDivElement>; @ViewChild('messagesContainer') messagesContainer?: ElementRef<HTMLDivElement>;
private readonly store = inject(Store);
private readonly allUsers = this.store.selectSignal(selectAllUsers);
private readonly dateSeparatorFormatter = new Intl.DateTimeFormat('en-GB', { private readonly dateSeparatorFormatter = new Intl.DateTimeFormat('en-GB', {
day: 'numeric', day: 'numeric',
month: 'long', month: 'long',
@@ -110,6 +115,20 @@ export class ChatMessageListComponent implements AfterViewChecked, OnDestroy {
return labels; return labels;
}); });
readonly userLookup = computed<ReadonlyMap<string, User>>(() => {
const lookup = new Map<string, User>();
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 initialScrollObserver: MutationObserver | null = null;
private initialScrollTimer: ReturnType<typeof setTimeout> | null = null; private initialScrollTimer: ReturnType<typeof setTimeout> | null = null;
private boundOnImageLoad: (() => void) | null = null; private boundOnImageLoad: (() => void) | null = null;