wip: optimizations

This commit is contained in:
2026-05-23 15:28:40 +02:00
parent 5bf506af03
commit 155fe20862
89 changed files with 7431 additions and 392 deletions

View File

@@ -1,4 +1,8 @@
import { Component, computed, inject } from '@angular/core';
import {
Component,
computed,
inject
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { ThemeNodeDirective, ThemeService } from '../../../theme';
import { DmChatComponent } from '../dm-chat/dm-chat.component';

View File

@@ -22,19 +22,29 @@
<div
appThemeNode="dmConversationList"
class="min-h-0 flex-1 overflow-y-auto p-2"
class="flex min-h-0 flex-1 flex-col"
>
@if (directMessages.conversations().length === 0) {
<div class="flex h-full items-center justify-center px-4 text-center text-sm text-muted-foreground">No direct messages yet.</div>
} @else {
<div class="space-y-1">
@for (conversation of directMessages.conversations(); track trackConversationId($index, conversation)) {
<app-virtual-list
class="block h-full p-2"
[items]="directMessages.conversations()"
[estimateSize]="64"
[overscan]="6"
[trackBy]="trackConversationId"
>
<ng-template
#item
let-conversation
>
<app-dm-conversation-item
class="block pb-1"
[conversation]="conversation"
(conversationOpened)="conversationSelected.emit($event)"
/>
}
</div>
</ng-template>
</app-virtual-list>
}
</div>

View File

@@ -10,6 +10,7 @@ import { NgIcon, provideIcons } from '@ng-icons/core';
import { lucideMessageCircle } from '@ng-icons/lucide';
import { ThemeNodeDirective, ThemeService } from '../../../theme';
import { VoiceControlsComponent } from '../../../voice-session';
import { VirtualListComponent } from '../../../../shared/components/virtual-list';
import type { DirectMessageConversation } from '../../domain/models/direct-message.model';
import { DirectMessageService } from '../../application/services/direct-message.service';
import { DmConversationItemComponent } from './dm-conversation-item.component';
@@ -22,6 +23,7 @@ import { DmConversationItemComponent } from './dm-conversation-item.component';
DmConversationItemComponent,
NgIcon,
ThemeNodeDirective,
VirtualListComponent,
VoiceControlsComponent
],
viewProviders: [provideIcons({ lucideMessageCircle })],

View File

@@ -14,7 +14,7 @@
<div class="space-y-1.5">
@for (user of friendResults(); track user.id) {
<div
class="group flex items-center gap-2 rounded-lg border border-emerald-500/25 bg-emerald-500/10 p-2"
class="group flex items-center gap-2 rounded-lg border border-emerald-500/25 bg-emerald-500/10 p-2 [content-visibility:auto] [contain-intrinsic-size:auto_60px]"
[attr.data-testid]="'friend-card-' + userKey(user)"
>
<app-user-avatar
@@ -86,7 +86,7 @@
<div class="space-y-1.5">
@for (user of results(); track user.id) {
<div
class="group flex items-center gap-2 rounded-lg border border-border bg-card p-2 transition-colors hover:bg-card/80"
class="group flex items-center gap-2 rounded-lg border border-border bg-card p-2 transition-colors hover:bg-card/80 [content-visibility:auto] [contain-intrinsic-size:auto_64px]"
[attr.data-testid]="'user-card-' + userKey(user)"
>
<app-user-avatar

View File

@@ -1,10 +1,13 @@
import { Injectable } from '@angular/core';
import { Injectable, inject } from '@angular/core';
import { jsonStorage } from '../../../infrastructure/persistence/json-storage.service';
import type { Friend } from '../domain/models/direct-message.model';
const STORAGE_PREFIX = 'metoyou_friends';
@Injectable({ providedIn: 'root' })
export class FriendRepository {
private readonly storage = jsonStorage;
async loadFriends(ownerId: string): Promise<Friend[]> {
return this.read(ownerId);
}
@@ -24,23 +27,13 @@ export class FriendRepository {
}
private read(ownerId: string): Friend[] {
const rawValue = localStorage.getItem(this.key(ownerId));
const parsed = this.storage.read<Friend[]>(this.key(ownerId), []);
if (!rawValue) {
return [];
}
try {
const parsed = JSON.parse(rawValue) as Friend[];
return Array.isArray(parsed) ? parsed : [];
} catch {
return [];
}
return Array.isArray(parsed) ? parsed : [];
}
private write(ownerId: string, friends: Friend[]): void {
localStorage.setItem(this.key(ownerId), JSON.stringify(friends));
this.storage.write(this.key(ownerId), friends);
}
private key(ownerId: string): string {

View File

@@ -1,9 +1,12 @@
import { Injectable } from '@angular/core';
import { Injectable, inject } from '@angular/core';
import { jsonStorage } from '../../../infrastructure/persistence/json-storage.service';
const STORAGE_PREFIX = 'metoyou_direct_message_queue';
@Injectable({ providedIn: 'root' })
export class OfflineQueueRepository {
private readonly storage = jsonStorage;
async load(ownerId: string): Promise<string[]> {
return this.read(ownerId);
}
@@ -24,23 +27,13 @@ export class OfflineQueueRepository {
}
private read(ownerId: string): string[] {
const rawValue = localStorage.getItem(this.key(ownerId));
const parsed = this.storage.read<string[]>(this.key(ownerId), []);
if (!rawValue) {
return [];
}
try {
const parsed = JSON.parse(rawValue) as string[];
return Array.isArray(parsed) ? parsed.filter((entry) => typeof entry === 'string') : [];
} catch {
return [];
}
return Array.isArray(parsed) ? parsed.filter((entry) => typeof entry === 'string') : [];
}
private write(ownerId: string, messageIds: string[]): void {
localStorage.setItem(this.key(ownerId), JSON.stringify(messageIds));
this.storage.write(this.key(ownerId), messageIds);
}
private key(ownerId: string): string {