232 lines
6.4 KiB
TypeScript
232 lines
6.4 KiB
TypeScript
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<AdminTab>('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 }));
|
|
}
|
|
}
|