import { Component, inject, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { Store } from '@ngrx/store'; import { NgIcon, provideIcons } from '@ng-icons/core'; import { lucideShield, lucideBan, lucideUserX, lucideSettings, lucideUsers, lucideTrash2, lucideCheck, lucideX, lucideLock, lucideUnlock } from '@ng-icons/lucide'; import { UsersActions } from '../../../store/users/users.actions'; import { RoomsActions } from '../../../store/rooms/rooms.actions'; import { selectCurrentRoom } from '../../../store/rooms/rooms.selectors'; import { selectBannedUsers, selectIsCurrentUserAdmin, selectCurrentUser, selectOnlineUsers } from '../../../store/users/users.selectors'; import { BanEntry, User } from '../../../core/models/index'; import { WebRTCService } from '../../../core/services/webrtc.service'; import { UserAvatarComponent, ConfirmDialogComponent } from '../../../shared'; type AdminTab = 'settings' | 'members' | 'bans' | 'permissions'; @Component({ selector: 'app-admin-panel', standalone: true, imports: [ CommonModule, FormsModule, NgIcon, UserAvatarComponent, ConfirmDialogComponent ], viewProviders: [ provideIcons({ lucideShield, lucideBan, lucideUserX, lucideSettings, lucideUsers, lucideTrash2, lucideCheck, lucideX, lucideLock, lucideUnlock }) ], templateUrl: './admin-panel.component.html' }) /** * Admin panel for managing room settings, members, bans, and permissions. * Only accessible to users with admin privileges. */ export class AdminPanelComponent { store = inject(Store); currentRoom = this.store.selectSignal(selectCurrentRoom); currentUser = this.store.selectSignal(selectCurrentUser); isAdmin = this.store.selectSignal(selectIsCurrentUserAdmin); bannedUsers = this.store.selectSignal(selectBannedUsers); onlineUsers = this.store.selectSignal(selectOnlineUsers); activeTab = signal('settings'); showDeleteConfirm = signal(false); // Settings roomName = ''; roomDescription = ''; isPrivate = signal(false); maxUsers = 0; // Permissions allowVoice = true; allowScreenShare = true; allowFileUploads = true; slowModeInterval = '0'; adminsManageRooms = false; moderatorsManageRooms = false; adminsManageIcon = false; moderatorsManageIcon = false; private webrtc = inject(WebRTCService); constructor() { // Initialize from current room const room = this.currentRoom(); if (room) { this.roomName = room.name; this.roomDescription = room.description || ''; this.isPrivate.set(room.isPrivate); this.maxUsers = room.maxUsers || 0; const perms = room.permissions || {}; this.allowVoice = perms.allowVoice !== false; this.allowScreenShare = perms.allowScreenShare !== false; this.allowFileUploads = perms.allowFileUploads !== false; this.slowModeInterval = String(perms.slowModeInterval ?? 0); this.adminsManageRooms = !!perms.adminsManageRooms; this.moderatorsManageRooms = !!perms.moderatorsManageRooms; this.adminsManageIcon = !!perms.adminsManageIcon; this.moderatorsManageIcon = !!perms.moderatorsManageIcon; } } /** Toggle the room's private visibility setting. */ togglePrivate(): void { this.isPrivate.update((current) => !current); } /** Save the current room name, description, privacy, and max-user settings. */ saveSettings(): void { const room = this.currentRoom(); if (!room) return; this.store.dispatch( RoomsActions.updateRoomSettings({ roomId: room.id, settings: { name: this.roomName, description: this.roomDescription, isPrivate: this.isPrivate(), maxUsers: this.maxUsers } }) ); } /** Persist updated room permissions (voice, screen-share, uploads, slow-mode, role grants). */ savePermissions(): void { const room = this.currentRoom(); if (!room) return; this.store.dispatch( RoomsActions.updateRoomPermissions({ roomId: room.id, permissions: { allowVoice: this.allowVoice, allowScreenShare: this.allowScreenShare, allowFileUploads: this.allowFileUploads, slowModeInterval: parseInt(this.slowModeInterval, 10), adminsManageRooms: this.adminsManageRooms, moderatorsManageRooms: this.moderatorsManageRooms, adminsManageIcon: this.adminsManageIcon, moderatorsManageIcon: this.moderatorsManageIcon } }) ); } /** Remove a user's ban entry. */ unbanUser(ban: BanEntry): void { this.store.dispatch(UsersActions.unbanUser({ roomId: ban.roomId, oderId: ban.oderId })); } /** Show the delete-room confirmation dialog. */ confirmDeleteRoom(): void { this.showDeleteConfirm.set(true); } /** Delete the current room after confirmation. */ deleteRoom(): void { const room = this.currentRoom(); if (!room) return; this.store.dispatch(RoomsActions.deleteRoom({ roomId: room.id })); this.showDeleteConfirm.set(false); } /** Format a ban expiry timestamp into a human-readable date/time string. */ formatExpiry(timestamp: number): string { const date = new Date(timestamp); return date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); } // Members tab: get all users except self /** Return online users excluding the current user (for the members list). */ membersFiltered(): User[] { const me = this.currentUser(); return this.onlineUsers().filter(user => user.id !== me?.id && user.oderId !== me?.oderId); } /** Change a member's role and notify connected peers. */ changeRole(user: User, role: 'admin' | 'moderator' | 'member'): void { const roomId = this.currentRoom()?.id; this.store.dispatch(UsersActions.updateUserRole({ userId: user.id, role })); this.webrtc.broadcastMessage({ type: 'role-change', roomId, targetUserId: user.id, role }); } /** Kick a member from the server. */ kickMember(user: User): void { this.store.dispatch(UsersActions.kickUser({ userId: user.id })); } /** Ban a member from the server. */ banMember(user: User): void { this.store.dispatch(UsersActions.banUser({ userId: user.id })); } }