From a9d788b3197567b63ec2a7cb75b6a5a430bf6d5e Mon Sep 17 00:00:00 2001 From: akastijn Date: Sat, 1 Aug 2026 21:46:02 +0200 Subject: [PATCH] Group channels by type with toggleable collapsed view in ChannelListComponent: added grouping logic, collapsed state management, updated UI with group titles, and adjusted styles for indentation. --- .../channel-list/channel-list.component.html | 33 +++++++++++-------- .../channel-list/channel-list.component.scss | 14 ++++++++ .../channel-list/channel-list.component.ts | 29 +++++++++++++++- 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/frontend/src/app/pages/altitude/chat/components/channel-list/channel-list.component.html b/frontend/src/app/pages/altitude/chat/components/channel-list/channel-list.component.html index 12eaddb..78eff63 100644 --- a/frontend/src/app/pages/altitude/chat/components/channel-list/channel-list.component.html +++ b/frontend/src/app/pages/altitude/chat/components/channel-list/channel-list.component.html @@ -1,19 +1,26 @@

Channels

- @for (channel of channels(); track channel.type + channel.name) { -
- - @for (segment of parseColoredName(getDisplayName(channel)); track $index) { - {{ segment.text }} - } - - @if (channel.unreadMessages > 0) { -
- + @for (group of groupedChannels(); track group.type) { +

+ {{ group.type }} {{ collapsedGroups().has(group.type) ? '▼' : '▲' }} +

+ @if (!collapsedGroups().has(group.type)) { + @for (channel of group.channels; track channel.name) { +
+ + @for (segment of parseColoredName(getDisplayName(channel)); track $index) { + {{ segment.text }} + } + + @if (channel.unreadMessages > 0) { +
+ +
+ }
} -
+ } }
diff --git a/frontend/src/app/pages/altitude/chat/components/channel-list/channel-list.component.scss b/frontend/src/app/pages/altitude/chat/components/channel-list/channel-list.component.scss index 20094a1..54c9c3a 100644 --- a/frontend/src/app/pages/altitude/chat/components/channel-list/channel-list.component.scss +++ b/frontend/src/app/pages/altitude/chat/components/channel-list/channel-list.component.scss @@ -30,4 +30,18 @@ color: #3b82f6; } } + + .channel.indented { + padding-left: 40px; + } + + .channel-group-title { + padding: 10px 20px; + color: #888; + font-size: 0.8rem; + font-weight: bold; + text-transform: uppercase; + cursor: pointer; + user-select: none; + } } diff --git a/frontend/src/app/pages/altitude/chat/components/channel-list/channel-list.component.ts b/frontend/src/app/pages/altitude/chat/components/channel-list/channel-list.component.ts index 326eb1a..8b965c8 100644 --- a/frontend/src/app/pages/altitude/chat/components/channel-list/channel-list.component.ts +++ b/frontend/src/app/pages/altitude/chat/components/channel-list/channel-list.component.ts @@ -1,4 +1,4 @@ -import {Component, effect, inject, input, output} from '@angular/core'; +import {Component, computed, effect, inject, input, output, signal} from '@angular/core'; import {ChatChannel} from '@pages/altitude/chat/objects/chat-channel.object'; import {CircleSvgComponent} from '@pages/altitude/chat/svg/circle.svg'; import {ChatService} from '@pages/altitude/chat/service/chat.service'; @@ -22,6 +22,33 @@ export class ChannelListComponent { public readonly selectedChannel = input.required(); public readonly selectedChannelChange = output(); + protected readonly collapsedGroups = signal>(new Set()); + + protected toggleGroup(type: string) { + const next = new Set(this.collapsedGroups()); + if (next.has(type)) { + next.delete(type); + } else { + next.add(type); + } + this.collapsedGroups.set(next); + } + + protected readonly groupedChannels = computed(() => { + const channels = this.channels(); + const groups: { type: ChatChannel['type'], channels: ChatChannel[] }[] = []; + const types: ChatChannel['type'][] = ['SERVER', 'DM', 'PARTY', 'GAC']; + + for (const type of types) { + const typeChannels = channels.filter(c => c.type === type); + if (typeChannels.length > 0) { + groups.push({type, channels: typeChannels}); + } + } + + return groups; + }); + constructor() { effect(() => { const chatChannels = this.channels();