diff --git a/frontend/src/app/pages/altitude/chat/chat.component.html b/frontend/src/app/pages/altitude/chat/chat.component.html
index d69253a..56cc064 100644
--- a/frontend/src/app/pages/altitude/chat/chat.component.html
+++ b/frontend/src/app/pages/altitude/chat/chat.component.html
@@ -24,20 +24,29 @@
-
-
+ @if (!isMobile() || !userListVisible()) {
+
+
+
+ }
+ @if (userListVisible()) {
+
+ }
diff --git a/frontend/src/app/pages/altitude/chat/chat.component.scss b/frontend/src/app/pages/altitude/chat/chat.component.scss
index db43c08..a76bf7a 100644
--- a/frontend/src/app/pages/altitude/chat/chat.component.scss
+++ b/frontend/src/app/pages/altitude/chat/chat.component.scss
@@ -60,6 +60,12 @@
min-width: 100px;
}
+ .user-list {
+ flex: 0 0 auto;
+ width: max-content;
+ min-width: 100px;
+ }
+
.chat {
display: flex;
flex-direction: column;
@@ -70,6 +76,16 @@
.chat-info {
flex: 0 0 auto;
padding: 5px 5px 2px 5px;
+
+ .chat-info-container {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+
+ app-chat-info {
+ flex: 1 1 auto;
+ }
+ }
}
.chat-box {
diff --git a/frontend/src/app/pages/altitude/chat/chat.component.ts b/frontend/src/app/pages/altitude/chat/chat.component.ts
index 76a00df..16277ce 100644
--- a/frontend/src/app/pages/altitude/chat/chat.component.ts
+++ b/frontend/src/app/pages/altitude/chat/chat.component.ts
@@ -8,6 +8,7 @@ import {FullSizeComponent} from '@shared-components/full-size/full-size.componen
import {ChatBoxComponent} from '@pages/altitude/chat/components/chat/chat-box.component';
import {ChannelListComponent} from '@pages/altitude/chat/components/channel-list/channel-list.component';
import {ChatInfoComponent} from '@pages/altitude/chat/components/chat-info/chat-info.component';
+import {UserListComponent} from '@pages/altitude/chat/components/user-list/user-list.component';
import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout';
@Component({
@@ -17,7 +18,8 @@ import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout';
FullSizeComponent,
ChatBoxComponent,
ChannelListComponent,
- ChatInfoComponent
+ ChatInfoComponent,
+ UserListComponent
],
templateUrl: './chat.component.html',
styleUrl: './chat.component.scss',
@@ -31,6 +33,7 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {
protected readonly selectedChannel = this.chatService.selectedChannel;
protected readonly showFullscreenPrompt = signal(false);
protected readonly isMobile = signal(false);
+ protected readonly userListVisible = this.chatService.userListVisible;
constructor(private interaction: MiniMessageInteractionService, private breakpointObserver: BreakpointObserver) {
interaction.clicks$.subscribe(({action, value}) => {
@@ -41,8 +44,14 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {
this.breakpointObserver
.observe([Breakpoints.Handset])
.subscribe(result => {
- this.isMobile.set(result.matches);
- this.showFullscreenPrompt.set(this.isMobile());
+ const isMobile = result.matches;
+ this.isMobile.set(isMobile);
+ this.showFullscreenPrompt.set(isMobile);
+ if (isMobile) {
+ this.userListVisible.set(false);
+ } else {
+ this.userListVisible.set(true);
+ }
});
}
@@ -50,6 +59,10 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {
this.chatService.selectChannel(channel);
}
+ protected toggleUserList(): void {
+ this.chatService.toggleUserList();
+ }
+
protected enterFullscreen(): void {
this.showFullscreenPrompt.set(false);
diff --git a/frontend/src/app/pages/altitude/chat/components/chat-info/chat-info.component.html b/frontend/src/app/pages/altitude/chat/components/chat-info/chat-info.component.html
index 752e87f..53fdb71 100644
--- a/frontend/src/app/pages/altitude/chat/components/chat-info/chat-info.component.html
+++ b/frontend/src/app/pages/altitude/chat/components/chat-info/chat-info.component.html
@@ -7,11 +7,19 @@
}
({{ chatChannel.totalMessages }})
+
+
+ @if (isMobile()) {
+
+ {{ userListVisible() ? 'Channels' : 'Users' }}
+
+ }
+
@if (notificationEnabled()) {
-
+
} @else {
-
+
}
diff --git a/frontend/src/app/pages/altitude/chat/components/chat-info/chat-info.component.scss b/frontend/src/app/pages/altitude/chat/components/chat-info/chat-info.component.scss
index fbcf524..95a63ef 100644
--- a/frontend/src/app/pages/altitude/chat/components/chat-info/chat-info.component.scss
+++ b/frontend/src/app/pages/altitude/chat/components/chat-info/chat-info.component.scss
@@ -20,10 +20,40 @@
}
}
+.user-list-toggle {
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-left: auto;
+ margin-right: 15px;
+ opacity: 0.8;
+ transition: opacity 0.2s;
+
+ &:hover {
+ opacity: 1;
+ }
+
+ &.active {
+ opacity: 1;
+ }
+
+ span {
+ font-weight: bold;
+ font-size: 0.9rem;
+ text-transform: uppercase;
+ }
+}
+
.notification-icon {
cursor: pointer;
width: 20px;
height: 20px;
- margin-left: auto;
- margin-right: 10px;
+ margin-right: 15px;
+ opacity: 0.8;
+ transition: opacity 0.2s;
+
+ &:hover {
+ opacity: 1;
+ }
}
diff --git a/frontend/src/app/pages/altitude/chat/components/chat-info/chat-info.component.ts b/frontend/src/app/pages/altitude/chat/components/chat-info/chat-info.component.ts
index 8a69c21..ec74d85 100644
--- a/frontend/src/app/pages/altitude/chat/components/chat-info/chat-info.component.ts
+++ b/frontend/src/app/pages/altitude/chat/components/chat-info/chat-info.component.ts
@@ -3,15 +3,18 @@ import {getServerColor, getServerTextColor} from '@pages/altitude/chat/util/serv
import {NotificationService} from '@pages/altitude/chat/service/chat-notification.service';
import {NotificationEnabledSvgComponent} from '@pages/altitude/chat/svg/notification-enabled.svg';
import {NotificationDisabledSvgComponent} from '@pages/altitude/chat/svg/notification-disabled.svg';
+import {UsersSvgComponent} from '@pages/altitude/chat/svg/users.svg';
import {ChatChannel} from '@pages/altitude/chat/objects/chat-channel.object';
import {ColoredNameSegment} from '@pages/altitude/chat/objects/colored-name-segment.object';
import {NameFormatService} from '@pages/altitude/chat/service/name-format.service';
+import {ChatService} from '@pages/altitude/chat/service/chat.service';
@Component({
selector: 'app-chat-info',
imports: [
NotificationEnabledSvgComponent,
- NotificationDisabledSvgComponent
+ NotificationDisabledSvgComponent,
+ UsersSvgComponent
],
templateUrl: './chat-info.component.html',
styleUrl: './chat-info.component.scss'
@@ -19,8 +22,11 @@ import {NameFormatService} from '@pages/altitude/chat/service/name-format.servic
export class ChatInfoComponent {
private readonly notificationService: NotificationService = inject(NotificationService);
private readonly nameFormatService: NameFormatService = inject(NameFormatService);
+ private readonly chatService: ChatService = inject(ChatService);
public readonly selectedChannel = input.required();
+ public readonly isMobile = input(false);
+ protected readonly userListVisible = this.chatService.userListVisible;
protected readonly channelKey = computed(() => {
const channel = this.selectedChannel();
return channel ? `${channel.type}:${channel.name}` : '';
@@ -37,6 +43,10 @@ export class ChatInfoComponent {
this.notificationService.disableNotifications(this.channelKey());
}
+ public toggleUserList() {
+ this.chatService.toggleUserList();
+ }
+
protected getDisplayName(chatChannel: ChatChannel): ColoredNameSegment[] {
return this.nameFormatService.getDisplayName(chatChannel);
}
diff --git a/frontend/src/app/pages/altitude/chat/components/user-list/user-list.component.html b/frontend/src/app/pages/altitude/chat/components/user-list/user-list.component.html
new file mode 100644
index 0000000..03a0e6e
--- /dev/null
+++ b/frontend/src/app/pages/altitude/chat/components/user-list/user-list.component.html
@@ -0,0 +1,16 @@
+
diff --git a/frontend/src/app/pages/altitude/chat/components/user-list/user-list.component.scss b/frontend/src/app/pages/altitude/chat/components/user-list/user-list.component.scss
new file mode 100644
index 0000000..8806867
--- /dev/null
+++ b/frontend/src/app/pages/altitude/chat/components/user-list/user-list.component.scss
@@ -0,0 +1,47 @@
+.user-list {
+ height: 100%;
+ background-color: #172133;
+ display: flex;
+ flex-direction: column;
+ min-height: 0;
+
+ .user {
+ position: relative;
+ display: flex;
+ align-items: center;
+ padding: 8px 20px;
+ border-bottom: 1px solid #222;
+ cursor: default;
+
+ .user-name {
+ color: white;
+ font-weight: bold;
+ }
+ }
+
+ .user.indented {
+ padding-left: 40px;
+ }
+
+ .server-title {
+ padding: 10px 20px;
+ color: #888;
+ font-size: 0.8rem;
+ font-weight: bold;
+ text-transform: uppercase;
+ user-select: none;
+ }
+
+ p {
+ margin: 0;
+ }
+
+ .scroll {
+ flex: 1 1 auto;
+ min-height: 0;
+ overflow-y: auto;
+ box-sizing: border-box;
+ scrollbar-width: thin;
+ scrollbar-color: rgba(255, 255, 255, 0.35) transparent;
+ }
+}
diff --git a/frontend/src/app/pages/altitude/chat/components/user-list/user-list.component.ts b/frontend/src/app/pages/altitude/chat/components/user-list/user-list.component.ts
new file mode 100644
index 0000000..688df81
--- /dev/null
+++ b/frontend/src/app/pages/altitude/chat/components/user-list/user-list.component.ts
@@ -0,0 +1,35 @@
+import {Component, computed, inject} from '@angular/core';
+import {ChatService} from '@pages/altitude/chat/service/chat.service';
+import {NameFormatService} from '@pages/altitude/chat/service/name-format.service';
+import {ColoredNameSegment} from '@pages/altitude/chat/objects/colored-name-segment.object';
+import {User} from '@pages/altitude/chat/objects/server-state.object';
+
+@Component({
+ selector: 'app-user-list',
+ standalone: true,
+ imports: [],
+ templateUrl: './user-list.component.html',
+ styleUrl: './user-list.component.scss'
+})
+export class UserListComponent {
+ private readonly chatService = inject(ChatService);
+ private readonly nameFormatService = inject(NameFormatService);
+
+ protected readonly serverState = computed(() => {
+ const state = this.chatService.serverState();
+ if (!state) return null;
+
+ return {
+ servers: [...state.servers]
+ .sort((a, b) => a.name.localeCompare(b.name))
+ .map(server => ({
+ ...server,
+ players: [...server.players].sort((a, b) => a.name.localeCompare(b.name))
+ }))
+ };
+ });
+
+ protected getDisplayName(user: User): ColoredNameSegment[] {
+ return this.nameFormatService.parseColoredName(user.styledName || user.name);
+ }
+}
diff --git a/frontend/src/app/pages/altitude/chat/service/chat.service.ts b/frontend/src/app/pages/altitude/chat/service/chat.service.ts
index 2f0ad47..666dc63 100644
--- a/frontend/src/app/pages/altitude/chat/service/chat.service.ts
+++ b/frontend/src/app/pages/altitude/chat/service/chat.service.ts
@@ -29,9 +29,14 @@ export class ChatService implements OnDestroy {
public readonly serverState = this._serverState.asReadonly();
public readonly partieMap = signal