230 lines
6.1 KiB
TypeScript
230 lines
6.1 KiB
TypeScript
/* 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<Room | null>(null);
|
|
showLeaveConfirm = signal(false);
|
|
currentUser = this.store.selectSignal(selectCurrentUser);
|
|
bannedRoomLookup = signal<Record<string, boolean>>({});
|
|
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<void> {
|
|
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<void> {
|
|
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<boolean> {
|
|
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);
|
|
}
|
|
}
|