Modularize chat page with new components (chat-box, server-list, chat-info). Introduce server filtering, real-time updates, and redesigned layout for enhanced usability.
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import {inject, Injectable, OnDestroy, signal} from '@angular/core';
|
||||
import {computed, inject, Injectable, OnDestroy, signal} from '@angular/core';
|
||||
import {AuthService} from '@services/auth.service';
|
||||
import {EventSourcePolyfill} from 'event-source-polyfill';
|
||||
import {ChatMessage} from '@pages/altitude/chat/objects/chat-message.object';
|
||||
import {MiniMessageComponent} from '@pages/altitude/chat/mini-message/mini-message.types';
|
||||
import {RawChatMessage} from '@pages/altitude/chat/objects/raw-chat-message.object';
|
||||
import {normalizeComponent} from '@pages/altitude/chat/mini-message/normalize.util';
|
||||
import {ChatServer} from '@pages/altitude/chat/objects/chat-server.object';
|
||||
|
||||
export interface ChatEvent {
|
||||
type: string;
|
||||
@@ -20,7 +20,11 @@ export class ChatService implements OnDestroy {
|
||||
private eventSource?: EventSourcePolyfill;
|
||||
private readonly authService: AuthService = inject(AuthService)
|
||||
private readonly _messages = signal<ChatMessage[]>([])
|
||||
public readonly messages = this._messages.asReadonly()
|
||||
public readonly messages = computed(() => this._messages().filter((message) => message.server === this._selectedServer()));
|
||||
private readonly _servers = signal<ChatServer[]>([])
|
||||
public readonly servers = computed(() => this._servers().sort((a, b) => a.server.localeCompare(b.server)));
|
||||
private readonly _selectedServer = signal<string>('');
|
||||
public readonly selectedServer = this._selectedServer.asReadonly()
|
||||
|
||||
connect() {
|
||||
if (this.eventSource) {
|
||||
@@ -43,21 +47,17 @@ export class ChatService implements OnDestroy {
|
||||
this.eventSource = source;
|
||||
|
||||
this.on(source, 'connected', (event) => {
|
||||
const raw = JSON.parse(event.data) as RawChatMessage[];
|
||||
const messages: ChatMessage[] = raw.map((m) => ({
|
||||
...m,
|
||||
messageJson: normalizeComponent(JSON.parse(m.messageJson)),
|
||||
}));
|
||||
this._messages.update((old) => [...old, ...messages]);
|
||||
this.processChatMessages(event);
|
||||
});
|
||||
|
||||
this.on(source, 'chat', (event) => {
|
||||
const raw = JSON.parse(event.data) as RawChatMessage[];
|
||||
const messages: ChatMessage[] = raw.map((m) => ({
|
||||
...m,
|
||||
messageJson: JSON.parse(m.messageJson) as MiniMessageComponent,
|
||||
}));
|
||||
this._messages.update((old) => [...old, ...messages]);
|
||||
const chatMessages = this.processChatMessages(event);
|
||||
chatMessages.forEach((message) => {
|
||||
const find = this.servers().find(server => message.server === server.server);
|
||||
if (find) {
|
||||
find.unreadMessages++;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
source.onerror = (err) => {
|
||||
@@ -65,7 +65,30 @@ export class ChatService implements OnDestroy {
|
||||
};
|
||||
}
|
||||
|
||||
// single, deliberate escape hatch from the broken DOM/polyfill type overlap —
|
||||
private processChatMessages(event: SsePayloadEvent): ChatMessage[] {
|
||||
const raw = JSON.parse(event.data) as RawChatMessage[];
|
||||
const messages: ChatMessage[] = raw.map((m) => ({
|
||||
...m,
|
||||
messageJson: normalizeComponent(JSON.parse(m.messageJson)),
|
||||
}));
|
||||
this._messages.update((old) => [...old, ...messages]);
|
||||
|
||||
//TODO [Stijn] [2026-07-19]: Handle servers by getting them from backend
|
||||
messages.map((message) => message.server).forEach((server) => {
|
||||
const found = this._servers().find(existing => existing.server === server);
|
||||
if (!found) {
|
||||
this._servers.update((old) => [...old, {server: server, unreadMessages: 0}]);
|
||||
}
|
||||
})
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
public selectServer(server: string) {
|
||||
this._selectedServer.set(server);
|
||||
}
|
||||
|
||||
// single, deliberate escape hatch from the broken DOM/polyfill type overlap —
|
||||
// everything else in this file stays fully typed
|
||||
private on(source: EventSourcePolyfill, eventName: string, handler: (event: SsePayloadEvent) => void): void {
|
||||
(source as unknown as { addEventListener: (type: string, listener: (event: SsePayloadEvent) => void) => void })
|
||||
|
||||
Reference in New Issue
Block a user