sse für live collab eingebaut

This commit is contained in:
Bastian Wagner
2026-06-10 14:35:18 +02:00
parent 93a91c7bf6
commit 67b5fb8532
9 changed files with 507 additions and 17 deletions

View File

@@ -1,5 +1,6 @@
import { DatePipe } from '@angular/common';
import { Component, OnInit, computed, inject, signal } from '@angular/core';
import { Component, DestroyRef, OnInit, computed, inject, signal } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { NonNullableFormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { finalize } from 'rxjs';
@@ -13,7 +14,8 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
import { getAuthErrorMessage } from '../../auth/error-message';
import { OnboardingService } from '../../onboarding/onboarding.service';
import { UserList, UserListItem } from '../lists.models';
import { ListRealtimeEvent, UserList, UserListItem } from '../lists.models';
import { ListsRealtimeService } from '../lists-realtime.service';
import { ListsService } from '../lists.service';
@Component({
@@ -35,8 +37,10 @@ import { ListsService } from '../lists.service';
styleUrl: './list-detail.component.scss',
})
export class ListDetailComponent implements OnInit {
private readonly destroyRef = inject(DestroyRef);
private readonly formBuilder = inject(NonNullableFormBuilder);
private readonly listsService = inject(ListsService);
private readonly listsRealtimeService = inject(ListsRealtimeService);
private readonly route = inject(ActivatedRoute);
private readonly router = inject(Router);
private readonly snackBar = inject(MatSnackBar);
@@ -77,6 +81,7 @@ export class ListDetailComponent implements OnInit {
if (listId) {
this.onboarding.listOpened(listId);
this.subscribeToRealtime(listId);
}
this.loadList();
@@ -233,12 +238,42 @@ export class ListDetailComponent implements OnInit {
await this.router.navigateByUrl('/lists');
}
private setList(list: UserList): void {
private subscribeToRealtime(listId: string): void {
this.listsRealtimeService
.events()
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe({
next: (event) => this.applyRealtimeEvent(listId, event),
});
}
private applyRealtimeEvent(listId: string, event: ListRealtimeEvent): void {
if (event.type === 'list.snapshot' && event.data.id === listId) {
this.errorMessage.set(null);
this.loading.set(false);
// Remote snapshots should update visible item state immediately, but they
// must not overwrite a title/description form while the user is editing it.
this.setList(event.data, !this.showEditor());
return;
}
if (event.type === 'list.deleted' && event.data.listId === listId) {
this.list.set(null);
this.loading.set(false);
this.editing.set(false);
this.errorMessage.set('Diese Liste wurde geloescht.');
}
}
private setList(list: UserList, resetForm = true): void {
this.list.set(list);
this.listForm.reset({
name: list.name,
description: list.description ?? '',
});
if (resetForm) {
this.listForm.reset({
name: list.name,
description: list.description ?? '',
});
}
}
private listId(): string | null {