Rework design part 1
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
<nav class="h-full w-16 flex flex-col items-center gap-3 py-3 border-r border-border bg-card relative">
|
||||
<nav class="relative flex h-full w-full flex-col items-center gap-2 border-r border-border bg-secondary/35 px-2 py-3">
|
||||
<!-- Create button -->
|
||||
<button
|
||||
type="button"
|
||||
class="w-10 h-10 rounded-2xl flex items-center justify-center bg-primary text-primary-foreground hover:bg-primary/90 transition-colors"
|
||||
class="flex h-10 w-10 items-center justify-center rounded-md bg-primary text-primary-foreground transition-colors hover:bg-primary/90"
|
||||
title="Create Server"
|
||||
(click)="createServer()"
|
||||
>
|
||||
@@ -13,22 +13,21 @@
|
||||
</button>
|
||||
|
||||
<!-- Saved servers icons -->
|
||||
<div class="flex-1 w-full overflow-y-auto flex flex-col items-center gap-2 mt-2">
|
||||
<div class="no-scrollbar mt-2 flex w-full flex-1 flex-col items-center gap-2 overflow-y-auto">
|
||||
@for (room of visibleSavedRooms(); track room.id) {
|
||||
<div class="relative flex w-full justify-center pl-2">
|
||||
<div class="relative flex w-full justify-center">
|
||||
@if (isSelectedRoom(room)) {
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="absolute left-1 top-1/2 h-7 w-1 -translate-y-1/2 rounded-full bg-primary shadow-[0_0_10px_rgba(59,130,246,0.45)]"
|
||||
class="pointer-events-none absolute left-0 top-1/2 h-10 w-1 -translate-y-1/2 rounded-l-full bg-primary"
|
||||
></span>
|
||||
}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="relative w-10 h-10 flex-shrink-0 rounded-2xl border border-border hover:border-primary/60 hover:shadow-sm transition-all"
|
||||
[class.border-primary]="isSelectedRoom(room)"
|
||||
[class.shadow-[0_0_0_1px_rgba(59,130,246,0.45),0_10px_20px_rgba(15,23,42,0.25)]]="isSelectedRoom(room)"
|
||||
[class.scale-105]="isSelectedRoom(room)"
|
||||
class="relative z-10 flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-md border border-transparent bg-card transition-colors hover:border-border hover:bg-card"
|
||||
[class.border-primary/30]="isSelectedRoom(room)"
|
||||
[class.bg-primary/10]="isSelectedRoom(room)"
|
||||
[title]="room.name"
|
||||
[attr.aria-current]="isSelectedRoom(room) ? 'page' : null"
|
||||
(click)="joinSavedRoom(room)"
|
||||
@@ -39,17 +38,18 @@
|
||||
<img
|
||||
[ngSrc]="room.icon"
|
||||
[alt]="room.name"
|
||||
class="w-full h-full object-cover"
|
||||
class="h-full w-full object-cover"
|
||||
/>
|
||||
} @else {
|
||||
<div
|
||||
class="w-full h-full flex items-center justify-center bg-secondary transition-colors"
|
||||
class="flex h-full w-full items-center justify-center bg-secondary transition-colors"
|
||||
[class.bg-primary/15]="isSelectedRoom(room)"
|
||||
>
|
||||
<span
|
||||
class="text-sm font-semibold text-muted-foreground transition-colors"
|
||||
[class.text-foreground]="isSelectedRoom(room)"
|
||||
>{{ initial(room.name) }}</span>
|
||||
>{{ initial(room.name) }}</span
|
||||
>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@@ -59,6 +59,23 @@
|
||||
{{ formatUnreadCount(roomUnreadCount(room.id)) }}
|
||||
</span>
|
||||
}
|
||||
|
||||
@if (voicePresenceCount(room.id) > 0) {
|
||||
<span
|
||||
class="absolute -bottom-1 -right-1 flex h-4 w-4 items-center justify-center rounded-full bg-emerald-500 text-white shadow-sm ring-2 ring-card"
|
||||
[title]="voicePresenceCount(room.id) + (voicePresenceCount(room.id) === 1 ? ' user in voice' : ' users in voice')"
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
aria-hidden="true"
|
||||
class="h-2.5 w-2.5 fill-current"
|
||||
>
|
||||
<path
|
||||
d="M6.25 4.1a.75.75 0 0 1 1.25.57v6.66a.75.75 0 0 1-1.25.57L3.68 9.6H2.25A1.25 1.25 0 0 1 1 8.35v-.7c0-.69.56-1.25 1.25-1.25h1.43L6.25 4.1Zm4.1 1.35a.75.75 0 0 1 1.05.1 4.2 4.2 0 0 1 0 4.9.75.75 0 0 1-1.15-.96 2.7 2.7 0 0 0 0-2.98.75.75 0 0 1 .1-1.06Z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -25,7 +25,10 @@ import {
|
||||
import { Room, User } from '../../shared-kernel';
|
||||
import { VoiceSessionFacade } from '../../domains/voice-session';
|
||||
import { selectSavedRooms, selectCurrentRoom } from '../../store/rooms/rooms.selectors';
|
||||
import { selectCurrentUser } from '../../store/users/users.selectors';
|
||||
import {
|
||||
selectCurrentUser,
|
||||
selectOnlineUsers
|
||||
} from '../../store/users/users.selectors';
|
||||
import { RoomsActions } from '../../store/rooms/rooms.actions';
|
||||
import { DatabaseService } from '../../infrastructure/persistence';
|
||||
import { NotificationsFacade } from '../../domains/notifications';
|
||||
@@ -74,6 +77,7 @@ export class ServersRailComponent {
|
||||
contextRoom = signal<Room | null>(null);
|
||||
showLeaveConfirm = signal(false);
|
||||
currentUser = this.store.selectSignal(selectCurrentUser);
|
||||
onlineUsers = this.store.selectSignal(selectOnlineUsers);
|
||||
bannedRoomLookup = signal<Record<string, boolean>>({});
|
||||
bannedServerName = signal('');
|
||||
showBannedDialog = signal(false);
|
||||
@@ -82,6 +86,46 @@ export class ServersRailComponent {
|
||||
joinPassword = signal('');
|
||||
joinPasswordError = signal<string | null>(null);
|
||||
visibleSavedRooms = computed(() => this.savedRooms().filter((room) => !this.isRoomMarkedBanned(room)));
|
||||
voicePresenceByRoom = computed(() => {
|
||||
const presence: Record<string, number> = {};
|
||||
const seenByRoom = new Map<string, Set<string>>();
|
||||
|
||||
const addVoicePresence = (user: User | null | undefined): void => {
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
|
||||
const voiceState = user?.voiceState;
|
||||
const roomId = voiceState?.serverId;
|
||||
|
||||
if (!voiceState?.isConnected || !roomId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const userKey = user.oderId || user.id;
|
||||
let seenUsers = seenByRoom.get(roomId);
|
||||
|
||||
if (!seenUsers) {
|
||||
seenUsers = new Set<string>();
|
||||
seenByRoom.set(roomId, seenUsers);
|
||||
}
|
||||
|
||||
if (seenUsers.has(userKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
seenUsers.add(userKey);
|
||||
presence[roomId] = (presence[roomId] ?? 0) + 1;
|
||||
};
|
||||
|
||||
for (const user of this.onlineUsers()) {
|
||||
addVoicePresence(user);
|
||||
}
|
||||
|
||||
addVoicePresence(this.currentUser());
|
||||
|
||||
return presence;
|
||||
});
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
@@ -236,6 +280,10 @@ export class ServersRailComponent {
|
||||
return this.notifications.roomUnreadCount(roomId);
|
||||
}
|
||||
|
||||
voicePresenceCount(roomId: string): number {
|
||||
return this.voicePresenceByRoom()[roomId] ?? 0;
|
||||
}
|
||||
|
||||
formatUnreadCount(count: number): string {
|
||||
return count > 99 ? '99+' : String(count);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user