/* eslint-disable @typescript-eslint/member-ordering */ import { Component, computed, effect, inject, signal } from '@angular/core'; import { CommonModule, NgOptimizedImage } from '@angular/common'; import { Store } from '@ngrx/store'; import { Router } from '@angular/router'; import { NgIcon, provideIcons } from '@ng-icons/core'; import { lucidePlus } from '@ng-icons/lucide'; import { Room, User } from '../../core/models/index'; import { selectSavedRooms, selectCurrentRoom } from '../../store/rooms/rooms.selectors'; import { selectCurrentUser } from '../../store/users/users.selectors'; import { VoiceSessionService } from '../../core/services/voice-session.service'; import { WebRTCService } from '../../core/services/webrtc.service'; import { RoomsActions } from '../../store/rooms/rooms.actions'; import { DatabaseService } from '../../core/services/database.service'; import { hasRoomBanForUser } from '../../core/helpers/room-ban.helpers'; import { ConfirmDialogComponent, ContextMenuComponent, LeaveServerDialogComponent } from '../../shared'; @Component({ selector: 'app-servers-rail', standalone: true, imports: [ CommonModule, NgIcon, ConfirmDialogComponent, ContextMenuComponent, LeaveServerDialogComponent, NgOptimizedImage ], viewProviders: [provideIcons({ lucidePlus })], templateUrl: './servers-rail.component.html' }) export class ServersRailComponent { private store = inject(Store); private router = inject(Router); private voiceSession = inject(VoiceSessionService); private webrtc = inject(WebRTCService); private db = inject(DatabaseService); private banLookupRequestVersion = 0; savedRooms = this.store.selectSignal(selectSavedRooms); currentRoom = this.store.selectSignal(selectCurrentRoom); showMenu = signal(false); menuX = signal(72); menuY = signal(100); contextRoom = signal(null); showLeaveConfirm = signal(false); currentUser = this.store.selectSignal(selectCurrentUser); bannedRoomLookup = signal>({}); bannedServerName = signal(''); showBannedDialog = signal(false); visibleSavedRooms = computed(() => this.savedRooms().filter((room) => !this.isRoomMarkedBanned(room))); constructor() { effect(() => { const rooms = this.savedRooms(); const currentUser = this.currentUser(); void this.refreshBannedLookup(rooms, currentUser ?? null); }); } initial(name?: string): string { if (!name) return '?'; const ch = name.trim()[0]?.toUpperCase(); return ch || '?'; } trackRoomId = (index: number, room: Room) => room.id; createServer(): void { const voiceServerId = this.voiceSession.getVoiceServerId(); if (voiceServerId) { this.voiceSession.setViewingVoiceServer(false); } this.router.navigate(['/search']); } async joinSavedRoom(room: Room): Promise { const currentUserId = localStorage.getItem('metoyou_currentUserId'); if (!currentUserId) { this.router.navigate(['/login']); return; } if (await this.isRoomBanned(room)) { this.bannedServerName.set(room.name); this.showBannedDialog.set(true); return; } const voiceServerId = this.voiceSession.getVoiceServerId(); if (voiceServerId && voiceServerId !== room.id) { this.voiceSession.setViewingVoiceServer(false); } else if (voiceServerId === room.id) { this.voiceSession.setViewingVoiceServer(true); } if (this.webrtc.hasJoinedServer(room.id)) { this.store.dispatch(RoomsActions.viewServer({ room })); } else { this.store.dispatch( RoomsActions.joinRoom({ roomId: room.id, serverInfo: { name: room.name, description: room.description, hostName: room.hostId || 'Unknown' } }) ); } } closeBannedDialog(): void { this.showBannedDialog.set(false); this.bannedServerName.set(''); } isRoomMarkedBanned(room: Room): boolean { return !!this.bannedRoomLookup()[room.id]; } openContextMenu(evt: MouseEvent, room: Room): void { evt.preventDefault(); this.contextRoom.set(room); this.menuX.set(Math.max(evt.clientX + 8, 72)); this.menuY.set(evt.clientY); this.showMenu.set(true); } closeMenu(): void { this.showMenu.set(false); } isCurrentContextRoom(): boolean { const ctx = this.contextRoom(); const cur = this.currentRoom(); return !!ctx && !!cur && ctx.id === cur.id; } openLeaveConfirm(): void { this.closeMenu(); if (this.contextRoom()) { this.showLeaveConfirm.set(true); } } confirmLeave(result: { nextOwnerKey?: string }): void { const ctx = this.contextRoom(); if (!ctx) return; const isCurrentRoom = this.currentRoom()?.id === ctx.id; this.store.dispatch(RoomsActions.forgetRoom({ roomId: ctx.id, nextOwnerKey: result.nextOwnerKey })); if (isCurrentRoom) { this.router.navigate(['/search']); } this.showLeaveConfirm.set(false); this.contextRoom.set(null); } cancelLeave(): void { this.showLeaveConfirm.set(false); } private async refreshBannedLookup(rooms: Room[], currentUser: User | null): Promise { const requestVersion = ++this.banLookupRequestVersion; if (!currentUser || rooms.length === 0) { this.bannedRoomLookup.set({}); return; } const persistedUserId = localStorage.getItem('metoyou_currentUserId'); const entries = await Promise.all( rooms.map(async (room) => { const bans = await this.db.getBansForRoom(room.id); return [room.id, hasRoomBanForUser(bans, currentUser, persistedUserId)] as const; }) ); if (requestVersion !== this.banLookupRequestVersion) { return; } this.bannedRoomLookup.set(Object.fromEntries(entries)); } private async isRoomBanned(room: Room): Promise { const currentUser = this.currentUser(); const persistedUserId = localStorage.getItem('metoyou_currentUserId'); if (!currentUser && !persistedUserId) { return false; } const bans = await this.db.getBansForRoom(room.id); return hasRoomBanForUser(bans, currentUser, persistedUserId); } }