refactor: Clean lint errors and organise files

This commit is contained in:
2026-04-17 01:06:01 +02:00
parent 2927a86fbb
commit 35b616fb77
60 changed files with 1161 additions and 728 deletions
@@ -0,0 +1,179 @@
<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="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()"
>
<ng-icon
name="lucidePlus"
class="w-5 h-5"
/>
</button>
<!-- Saved servers icons -->
<div class="no-scrollbar mt-2 flex w-full flex-1 flex-col items-center gap-2 overflow-y-auto pt-0.5">
@for (room of visibleSavedRooms(); track room.id) {
<div class="group/server relative flex w-full justify-center">
<span
aria-hidden="true"
class="pointer-events-none absolute left-0 top-1/2 w-[3px] -translate-y-1/2 rounded-r-full bg-primary transition-[height,opacity] duration-100"
[ngClass]="isSelectedRoom(room) ? 'h-5 opacity-100' : 'h-0 opacity-0 group-hover/server:h-2.5 group-hover/server:opacity-100'"
></span>
<button
type="button"
class="relative z-10 flex h-10 w-10 cursor-pointer flex-shrink-0 items-center justify-center border border-transparent transition-[border-radius,box-shadow,background-color] duration-100 hover:rounded-lg hover:bg-card"
[ngClass]="isSelectedRoom(room) ? 'rounded-lg ring-2 ring-primary/40 bg-primary/10' : 'rounded-xl bg-card'"
[title]="room.name"
[attr.aria-current]="isSelectedRoom(room) ? 'page' : null"
(click)="joinSavedRoom(room)"
(contextmenu)="openContextMenu($event, room)"
>
<div class="h-full w-full overflow-hidden rounded-[inherit]">
@if (room.icon) {
<img
[ngSrc]="room.icon"
[alt]="room.name"
class="h-full w-full object-cover"
/>
} @else {
<div
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
>
</div>
}
</div>
@if (roomUnreadCount(room.id) > 0) {
<span class="absolute -right-1 -top-1 min-w-5 rounded-full bg-amber-400 px-1.5 py-0.5 text-[10px] font-semibold text-black shadow-sm">
{{ 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>
}
</div>
<div
class="grid w-full overflow-hidden duration-200 ease-out motion-reduce:transition-none"
style="transition-property: grid-template-rows, opacity"
[style.gridTemplateRows]="isOnSearch() ? '1fr' : '0fr'"
[style.opacity]="isOnSearch() ? '1' : '0'"
[style.visibility]="isOnSearch() ? 'visible' : 'hidden'"
[class.pointer-events-none]="!isOnSearch()"
[attr.aria-hidden]="isOnSearch() ? null : 'true'"
>
<div class="overflow-hidden">
<app-user-bar />
</div>
</div>
</nav>
<!-- Context menu -->
@if (showMenu()) {
<app-context-menu
[x]="menuX()"
[y]="menuY()"
(closed)="closeMenu()"
[width]="'w-44'"
>
<button
type="button"
(click)="toggleRoomNotifications()"
class="context-menu-item"
>
{{ isRoomNotificationsMuted(contextRoom()?.id || '') ? 'Unmute Notifications' : 'Mute Notifications' }}
</button>
<div class="context-menu-divider"></div>
<button
type="button"
(click)="openLeaveConfirm()"
class="context-menu-item"
>
Leave Server
</button>
</app-context-menu>
}
@if (showBannedDialog()) {
<app-confirm-dialog
title="Banned"
confirmLabel="OK"
cancelLabel="Close"
variant="danger"
[widthClass]="'w-96 max-w-[90vw]'"
(confirmed)="closeBannedDialog()"
(cancelled)="closeBannedDialog()"
>
<p>You are banned from {{ bannedServerName() || 'this server' }}.</p>
</app-confirm-dialog>
}
@if (showPasswordDialog() && passwordPromptRoom()) {
<app-confirm-dialog
title="Password required"
confirmLabel="Join server"
cancelLabel="Cancel"
[widthClass]="'w-[420px] max-w-[92vw]'"
(confirmed)="confirmPasswordJoin()"
(cancelled)="closePasswordDialog()"
>
<div class="space-y-3 text-left">
<p>Enter the password to rejoin {{ passwordPromptRoom()!.name }}.</p>
<div>
<label
for="rail-join-password"
class="mb-1 block text-xs font-medium uppercase tracking-wide text-muted-foreground"
>
Server password
</label>
<input
id="rail-join-password"
type="password"
[(ngModel)]="joinPassword"
placeholder="Enter password"
class="w-full rounded-lg border border-border bg-secondary px-3 py-2 text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary"
/>
</div>
@if (joinPasswordError()) {
<p class="text-sm text-destructive">{{ joinPasswordError() }}</p>
}
</div>
</app-confirm-dialog>
}
@if (showLeaveConfirm() && contextRoom()) {
<app-leave-server-dialog
[room]="contextRoom()!"
[currentUser]="currentUser() ?? null"
(confirmed)="confirmLeave($event)"
(cancelled)="cancelLeave()"
/>
}