From 6d690b50439ddefbfecde30be12997ea58f5d747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BENEDEK=20L=C3=A1szl=C3=B3?= Date: Sat, 7 Jun 2025 15:11:06 +0200 Subject: [PATCH] move chat data do chat component, notifications --- .../channel-entry.component.html | 2 +- .../channel-entry/channel-entry.component.ts | 11 ++-- .../channel-list/channel-list.component.html | 10 ++-- .../channel-list/channel-list.component.ts | 38 +++++-------- src/app/chat/chat/chat.component.html | 8 ++- src/app/chat/chat/chat.component.ts | 55 +++++++++++++++++-- src/app/chat/chat/feed/feed.component.ts | 41 +++----------- 7 files changed, 90 insertions(+), 75 deletions(-) diff --git a/src/app/chat/chat/channel-list/channel-entry/channel-entry.component.html b/src/app/chat/chat/channel-list/channel-entry/channel-entry.component.html index 3580943..2927ab3 100644 --- a/src/app/chat/chat/channel-list/channel-entry/channel-entry.component.html +++ b/src/app/chat/chat/channel-list/channel-entry/channel-entry.component.html @@ -3,7 +3,7 @@ mat-stroked-button [matTooltip]="channel.description" matTooltipPosition="right" - [matBadge]="this.hasAlert ? '1' : ''" + [matBadge]="this.notifications != 0 ? this.notifications : ''" matBadgeSize="medium" [disabled]="this.selected"> {{channel.name}} diff --git a/src/app/chat/chat/channel-list/channel-entry/channel-entry.component.ts b/src/app/chat/chat/channel-list/channel-entry/channel-entry.component.ts index b03a8f0..6366a19 100644 --- a/src/app/chat/chat/channel-list/channel-entry/channel-entry.component.ts +++ b/src/app/chat/chat/channel-list/channel-entry/channel-entry.component.ts @@ -11,14 +11,15 @@ export class ChannelEntryComponent implements OnChanges { @Input("channel") public channel!: Channel; @Input("selected") public selected!: boolean; - public hasAlert: boolean = false; + public notifications: number = 0; - ngOnChanges(changes: SimpleChanges): void { + ngOnChanges(_: SimpleChanges): void { if (this.selected) { - this.hasAlert = false; + this.notifications = 0; } } - // TODO: subsribe to message alerts and display them - // unsubscirbe when leaving + public Notify() { + if (!this.selected) this.notifications++; + } } diff --git a/src/app/chat/chat/channel-list/channel-list.component.html b/src/app/chat/chat/channel-list/channel-list.component.html index f2c8970..ab25431 100644 --- a/src/app/chat/chat/channel-list/channel-list.component.html +++ b/src/app/chat/chat/channel-list/channel-list.component.html @@ -1,6 +1,4 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/src/app/chat/chat/channel-list/channel-list.component.ts b/src/app/chat/chat/channel-list/channel-list.component.ts index 39059a9..9f166bb 100644 --- a/src/app/chat/chat/channel-list/channel-list.component.ts +++ b/src/app/chat/chat/channel-list/channel-list.component.ts @@ -1,7 +1,6 @@ -import { Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, Output, QueryList, ViewChildren } from '@angular/core'; import { Channel } from '../../../models/channel'; -import { ChatService } from '../../../services/chat.service'; -import { Subject, takeUntil } from 'rxjs'; +import { ChannelEntryComponent } from './channel-entry/channel-entry.component'; @Component({ selector: 'app-channel-list', @@ -9,31 +8,24 @@ import { Subject, takeUntil } from 'rxjs'; templateUrl: './channel-list.component.html', styleUrl: './channel-list.component.scss' }) -export class ChannelListComponent implements OnInit, OnDestroy { - @Output("select") selectEmitter: EventEmitter = new EventEmitter(); +export class ChannelListComponent { + @Input('channels') public channels!: Channel[]; + @Output("select") selectEmitter: EventEmitter = new EventEmitter(); - public channels!: Channel[]; - public selectedChannel?: Channel; + @ViewChildren("entry") entries!: QueryList; - private destroy = new Subject(); - - constructor(private chatService: ChatService) { } - - ngOnInit() { - this.chatService.ListAvailableChannels() - .pipe(takeUntil(this.destroy)) - .subscribe(channels => { - this.channels = channels; - this.selectedChannel = this.channels[0]; - }); + private selectedChannel?: number; + public get SelectedChannel() : number { + return this.selectedChannel ?? 0; } - ngOnDestroy(): void { - this.destroy.next(); - this.destroy.complete(); - } + constructor() { } public Select(index: number): void { - this.selectEmitter.emit(this.selectedChannel = this.channels[index]); + this.selectEmitter.emit(this.selectedChannel = index); + } + + public Notify(channel: number): void { + this.entries.find(entry=>entry.channel.id == channel)?.Notify() } } diff --git a/src/app/chat/chat/chat.component.html b/src/app/chat/chat/chat.component.html index 13e9e6f..4de38f6 100644 --- a/src/app/chat/chat/chat.component.html +++ b/src/app/chat/chat/chat.component.html @@ -1,9 +1,11 @@
- + - + - +
\ No newline at end of file diff --git a/src/app/chat/chat/chat.component.ts b/src/app/chat/chat/chat.component.ts index f1488fb..b4a669f 100644 --- a/src/app/chat/chat/chat.component.ts +++ b/src/app/chat/chat/chat.component.ts @@ -1,5 +1,10 @@ -import { Component } from '@angular/core'; +import { Component, OnInit, ViewChild } from '@angular/core'; import { Channel } from '../../models/channel'; +import { Message } from '../../models/message'; +import { ChatService } from '../../services/chat.service'; +import { ToastrService } from 'ngx-toastr'; +import { FeedComponent } from './feed/feed.component'; +import { ChannelListComponent } from './channel-list/channel-list.component'; @Component({ selector: 'app-chat', @@ -7,10 +12,52 @@ import { Channel } from '../../models/channel'; templateUrl: './chat.component.html', styleUrl: './chat.component.scss' }) -export class ChatComponent { - public selectedChannel?: Channel; +export class ChatComponent implements OnInit { + public selectedChannel: number = 0; + public channels: { channel: Channel, messages: Message[] }[] = []; - public Select(channel: Channel): void { + @ViewChild("feed") feed!: FeedComponent; + @ViewChild("channelList") channelList!: ChannelListComponent; + + constructor(private chatService: ChatService, private toastrService: ToastrService) { } + + ngOnInit(): void { + this.getChannels(); + } + + private getChannels(): void { + this.chatService.ListAvailableChannels().subscribe({ + next: channels => { + this.channels = channels.map(channel => { return { channel: channel, messages: [] as Message[] } }); + this.getMessages(); + }, + error: _ => { + this.toastrService.error("Failed to fetch channels.", "Error"); + } + }) + } + + private getMessages(): void { + this.channels.forEach((channelObj, i) => { + this.chatService.GetMessages(channelObj.channel.id).subscribe({ + next: messages => { + channelObj.messages.push(messages); + this.feed.ScrollEventHandler(); + this.channelList.Notify(channelObj.channel.id); + }, + error: _ => { + this.toastrService.error(`Failed to fetch messages for channel ${channelObj.channel.name}.`, "Error"); + } + }); + }); + } + + public GetChannelList(): Channel[] { + return this.channels.map(c => c.channel); + } + + public Select(channel: number): void { this.selectedChannel = channel; + this.feed.ScrollEventHandler(); } } diff --git a/src/app/chat/chat/feed/feed.component.ts b/src/app/chat/chat/feed/feed.component.ts index ce468b8..7ad0317 100644 --- a/src/app/chat/chat/feed/feed.component.ts +++ b/src/app/chat/chat/feed/feed.component.ts @@ -1,8 +1,5 @@ -import { AfterContentInit, AfterViewChecked, AfterViewInit, Component, ElementRef, Input, OnChanges, OnDestroy, ViewChild } from '@angular/core'; +import { AfterViewInit, Component, DoCheck, ElementRef, EventEmitter, Input, IterableDiffer, IterableDiffers, Output } from '@angular/core'; import { Message } from '../../../models/message'; -import { ChatService } from '../../../services/chat.service'; -import { Channel } from '../../../models/channel'; -import { Subscription } from 'rxjs'; @Component({ selector: 'app-feed', @@ -10,41 +7,15 @@ import { Subscription } from 'rxjs'; templateUrl: './feed.component.html', styleUrl: './feed.component.scss' }) -export class FeedComponent implements OnChanges, OnDestroy, AfterViewInit { - private readonly DEFAULT_CHANNEL_ID: number = 1; +export class FeedComponent implements AfterViewInit { + @Input('messages') public messages?: Message[]; - @Input('channel') public channel?: Channel; - public messages: Message[] = []; - - public subscription?: Subscription; - - constructor(private chatService: ChatService, private element: ElementRef) { } + constructor(private element: ElementRef) { } ngAfterViewInit(): void { this.scrollToBottom(); } - ngOnChanges(): void { - if (this.subscription) { - this.subscription.unsubscribe(); - this.messages = []; - } - - this.subscription = - this.chatService.GetMessages(this.channel?.id ?? this.DEFAULT_CHANNEL_ID) - .subscribe(message => { - this.messages.push(message); - - if (this.isAtBottom()) this.scrollToBottom(); - }); - } - - ngOnDestroy(): void { - if (this.subscription) { - this.subscription.unsubscribe(); - } - } - private isAtBottom(): boolean { let element = this.element.nativeElement; return Math.abs(element.scrollHeight - element.clientHeight - element.scrollTop) <= 100; @@ -53,4 +24,8 @@ export class FeedComponent implements OnChanges, OnDestroy, AfterViewInit { private scrollToBottom(): void { setTimeout(() => this.element.nativeElement.scrollTop = this.element.nativeElement.scrollHeight, 100); } + + public ScrollEventHandler(): void { + if (this.isAtBottom()) this.scrollToBottom(); + } }