Cleaning up comments
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/member-ordering, @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
import {
|
||||
Component,
|
||||
inject,
|
||||
@@ -51,7 +51,7 @@ import {
|
||||
RoomMember,
|
||||
Room,
|
||||
User
|
||||
} from '../../../core/models';
|
||||
} from '../../../core/models/index';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
type TabView = 'channels' | 'users';
|
||||
@@ -84,9 +84,6 @@ type TabView = 'channels' | 'users';
|
||||
],
|
||||
templateUrl: './rooms-side-panel.component.html'
|
||||
})
|
||||
/**
|
||||
* Side panel listing text and voice channels, online users, and channel management actions.
|
||||
*/
|
||||
export class RoomsSidePanelComponent {
|
||||
private store = inject(Store);
|
||||
private webrtc = inject(WebRTCService);
|
||||
@@ -129,35 +126,28 @@ export class RoomsSidePanelComponent {
|
||||
return memberIds.size;
|
||||
});
|
||||
|
||||
// Channel context menu state
|
||||
showChannelMenu = signal(false);
|
||||
channelMenuX = signal(0);
|
||||
channelMenuY = signal(0);
|
||||
contextChannel = signal<Channel | null>(null);
|
||||
|
||||
// Rename state
|
||||
renamingChannelId = signal<string | null>(null);
|
||||
|
||||
// Create channel dialog state
|
||||
showCreateChannelDialog = signal(false);
|
||||
createChannelType = signal<'text' | 'voice'>('text');
|
||||
newChannelName = '';
|
||||
|
||||
// User context menu state
|
||||
showUserMenu = signal(false);
|
||||
userMenuX = signal(0);
|
||||
userMenuY = signal(0);
|
||||
contextMenuUser = signal<User | null>(null);
|
||||
|
||||
// Per-user volume context menu state
|
||||
showVolumeMenu = signal(false);
|
||||
volumeMenuX = signal(0);
|
||||
volumeMenuY = signal(0);
|
||||
volumeMenuPeerId = signal('');
|
||||
volumeMenuDisplayName = signal('');
|
||||
|
||||
/** Return online users excluding the current user. */
|
||||
// Filter out current user from online users list
|
||||
onlineUsersFiltered() {
|
||||
const current = this.currentUser();
|
||||
const currentId = current?.id;
|
||||
@@ -170,7 +160,6 @@ export class RoomsSidePanelComponent {
|
||||
return member.oderId || member.id;
|
||||
}
|
||||
|
||||
/** Check whether the current user has permission to manage channels. */
|
||||
canManageChannels(): boolean {
|
||||
const room = this.currentRoom();
|
||||
const user = this.currentUser();
|
||||
@@ -178,7 +167,6 @@ export class RoomsSidePanelComponent {
|
||||
if (!room || !user)
|
||||
return false;
|
||||
|
||||
// Owner always can
|
||||
if (room.hostId === user.id)
|
||||
return true;
|
||||
|
||||
@@ -193,17 +181,13 @@ export class RoomsSidePanelComponent {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Select a text channel (no-op if currently renaming). */
|
||||
// ---- Text channel selection ----
|
||||
selectTextChannel(channelId: string) {
|
||||
if (this.renamingChannelId())
|
||||
return; // don't switch while renaming
|
||||
return;
|
||||
|
||||
this.store.dispatch(RoomsActions.selectChannel({ channelId }));
|
||||
}
|
||||
|
||||
/** Open the context menu for a channel at the cursor position. */
|
||||
// ---- Channel context menu ----
|
||||
openChannelContextMenu(evt: MouseEvent, channel: Channel) {
|
||||
evt.preventDefault();
|
||||
this.contextChannel.set(channel);
|
||||
@@ -212,12 +196,10 @@ export class RoomsSidePanelComponent {
|
||||
this.showChannelMenu.set(true);
|
||||
}
|
||||
|
||||
/** Close the channel context menu. */
|
||||
closeChannelMenu() {
|
||||
this.showChannelMenu.set(false);
|
||||
}
|
||||
|
||||
/** Begin inline renaming of the context-menu channel. */
|
||||
startRename() {
|
||||
const ch = this.contextChannel();
|
||||
|
||||
@@ -228,7 +210,6 @@ export class RoomsSidePanelComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/** Commit the channel rename from the inline input value. */
|
||||
confirmRename(event: Event) {
|
||||
const input = event.target as HTMLInputElement;
|
||||
const name = input.value.trim();
|
||||
@@ -241,12 +222,10 @@ export class RoomsSidePanelComponent {
|
||||
this.renamingChannelId.set(null);
|
||||
}
|
||||
|
||||
/** Cancel the inline rename operation. */
|
||||
cancelRename() {
|
||||
this.renamingChannelId.set(null);
|
||||
}
|
||||
|
||||
/** Delete the context-menu channel. */
|
||||
deleteChannel() {
|
||||
const ch = this.contextChannel();
|
||||
|
||||
@@ -257,7 +236,6 @@ export class RoomsSidePanelComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/** Trigger a message inventory re-sync from all connected peers. */
|
||||
resyncMessages() {
|
||||
this.closeChannelMenu();
|
||||
const room = this.currentRoom();
|
||||
@@ -266,36 +244,26 @@ export class RoomsSidePanelComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
// Dispatch startSync for UI spinner
|
||||
this.store.dispatch(MessagesActions.startSync());
|
||||
|
||||
// Request inventory from all connected peers
|
||||
const peers = this.webrtc.getConnectedPeers();
|
||||
|
||||
if (peers.length === 0) {
|
||||
// No connected peers - sync will time out
|
||||
}
|
||||
|
||||
const inventoryRequest: ChatEvent = { type: 'chat-inventory-request', roomId: room.id };
|
||||
|
||||
peers.forEach((pid) => {
|
||||
try {
|
||||
this.webrtc.sendToPeer(pid, inventoryRequest);
|
||||
} catch (_error) {
|
||||
// Failed to send inventory request to this peer
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Open the create-channel dialog for the given channel type. */
|
||||
// ---- Create channel ----
|
||||
createChannel(type: 'text' | 'voice') {
|
||||
this.createChannelType.set(type);
|
||||
this.newChannelName = '';
|
||||
this.showCreateChannelDialog.set(true);
|
||||
}
|
||||
|
||||
/** Confirm channel creation and dispatch the add-channel action. */
|
||||
confirmCreateChannel() {
|
||||
const name = this.newChannelName.trim();
|
||||
|
||||
@@ -315,13 +283,10 @@ export class RoomsSidePanelComponent {
|
||||
this.showCreateChannelDialog.set(false);
|
||||
}
|
||||
|
||||
/** Cancel channel creation and close the dialog. */
|
||||
cancelCreateChannel() {
|
||||
this.showCreateChannelDialog.set(false);
|
||||
}
|
||||
|
||||
/** Open the user context menu for admin actions (kick/role change). */
|
||||
// ---- User context menu (kick/role) ----
|
||||
openUserContextMenu(evt: MouseEvent, user: User) {
|
||||
evt.preventDefault();
|
||||
|
||||
@@ -334,16 +299,12 @@ export class RoomsSidePanelComponent {
|
||||
this.showUserMenu.set(true);
|
||||
}
|
||||
|
||||
/** Close the user context menu. */
|
||||
closeUserMenu() {
|
||||
this.showUserMenu.set(false);
|
||||
}
|
||||
|
||||
/** Open the per-user volume context menu for a voice channel participant. */
|
||||
openVoiceUserVolumeMenu(evt: MouseEvent, user: User) {
|
||||
evt.preventDefault();
|
||||
|
||||
// Don't show volume menu for the local user
|
||||
const me = this.currentUser();
|
||||
|
||||
if (user.id === me?.id || user.oderId === me?.oderId)
|
||||
@@ -356,7 +317,6 @@ export class RoomsSidePanelComponent {
|
||||
this.showVolumeMenu.set(true);
|
||||
}
|
||||
|
||||
/** Change a user's role and broadcast the update to connected peers. */
|
||||
changeUserRole(role: 'admin' | 'moderator' | 'member') {
|
||||
const user = this.contextMenuUser();
|
||||
const roomId = this.currentRoom()?.id;
|
||||
@@ -365,7 +325,6 @@ export class RoomsSidePanelComponent {
|
||||
|
||||
if (user) {
|
||||
this.store.dispatch(UsersActions.updateUserRole({ userId: user.id, role }));
|
||||
// Broadcast role change to peers
|
||||
this.webrtc.broadcastMessage({
|
||||
type: 'role-change',
|
||||
roomId,
|
||||
@@ -375,7 +334,6 @@ export class RoomsSidePanelComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/** Kick a user and broadcast the action to peers. */
|
||||
kickUserAction() {
|
||||
const user = this.contextMenuUser();
|
||||
|
||||
@@ -383,7 +341,6 @@ export class RoomsSidePanelComponent {
|
||||
|
||||
if (user) {
|
||||
this.store.dispatch(UsersActions.kickUser({ userId: user.id }));
|
||||
// Broadcast kick to peers
|
||||
this.webrtc.broadcastMessage({
|
||||
type: 'kick',
|
||||
targetUserId: user.id,
|
||||
@@ -392,14 +349,10 @@ export class RoomsSidePanelComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/** Join a voice channel, managing permissions and existing voice connections. */
|
||||
// ---- Voice ----
|
||||
joinVoice(roomId: string) {
|
||||
// Gate by room permissions
|
||||
const room = this.currentRoom();
|
||||
|
||||
if (room && room.permissions && room.permissions.allowVoice === false) {
|
||||
// Voice is disabled by room permissions
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -408,12 +361,8 @@ export class RoomsSidePanelComponent {
|
||||
|
||||
const current = this.currentUser();
|
||||
|
||||
// Check if already connected to voice in a DIFFERENT server - must disconnect first
|
||||
// Also handle stale voice state: if the store says connected but voice isn't actually active,
|
||||
// clear it so the user can join.
|
||||
if (current?.voiceState?.isConnected && current.voiceState.serverId !== room?.id) {
|
||||
if (!this.webrtc.isVoiceConnected()) {
|
||||
// Stale state - clear it so the user can proceed
|
||||
if (current.id) {
|
||||
this.store.dispatch(
|
||||
UsersActions.updateVoiceState({
|
||||
@@ -429,21 +378,16 @@ export class RoomsSidePanelComponent {
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Already connected to voice in another server; must disconnect first
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If switching channels within the same server, just update the room
|
||||
const isSwitchingChannels = current?.voiceState?.isConnected && current.voiceState.serverId === room?.id && current.voiceState.roomId !== roomId;
|
||||
// Enable microphone and broadcast voice-state
|
||||
const enableVoicePromise = isSwitchingChannels ? Promise.resolve() : this.webrtc.enableVoice();
|
||||
|
||||
enableVoicePromise
|
||||
.then(() => this.onVoiceJoinSucceeded(roomId, room, current ?? null))
|
||||
.catch((_error) => {
|
||||
// Failed to join voice room
|
||||
});
|
||||
.catch(() => undefined);
|
||||
}
|
||||
|
||||
private onVoiceJoinSucceeded(roomId: string, room: Room, current: User | null): void {
|
||||
@@ -523,23 +467,18 @@ export class RoomsSidePanelComponent {
|
||||
});
|
||||
}
|
||||
|
||||
/** Leave a voice channel and broadcast the disconnect state. */
|
||||
leaveVoice(roomId: string) {
|
||||
const current = this.currentUser();
|
||||
|
||||
// Only leave if currently in this room
|
||||
if (!(current?.voiceState?.isConnected && current.voiceState.roomId === roomId))
|
||||
return;
|
||||
|
||||
// Stop voice heartbeat
|
||||
this.webrtc.stopVoiceHeartbeat();
|
||||
|
||||
this.untrackCurrentUserMic();
|
||||
|
||||
// Disable voice locally
|
||||
this.webrtc.disableVoice();
|
||||
|
||||
// Update store voice state
|
||||
if (current?.id) {
|
||||
this.store.dispatch(
|
||||
UsersActions.updateVoiceState({
|
||||
@@ -555,7 +494,6 @@ export class RoomsSidePanelComponent {
|
||||
);
|
||||
}
|
||||
|
||||
// Broadcast disconnect
|
||||
this.webrtc.broadcastMessage({
|
||||
type: 'voice-state',
|
||||
oderId: current?.oderId || current?.id,
|
||||
@@ -569,37 +507,31 @@ export class RoomsSidePanelComponent {
|
||||
}
|
||||
});
|
||||
|
||||
// End voice session
|
||||
this.voiceSessionService.endSession();
|
||||
}
|
||||
|
||||
/** Count the number of users connected to a voice channel in the current room. */
|
||||
voiceOccupancy(roomId: string): number {
|
||||
return this.voiceUsersInRoom(roomId).length;
|
||||
}
|
||||
|
||||
/** Dispatch a viewer:focus event to display a remote user's screen share. */
|
||||
viewShare(userId: string) {
|
||||
const evt = new CustomEvent('viewer:focus', { detail: { userId } });
|
||||
|
||||
window.dispatchEvent(evt);
|
||||
}
|
||||
|
||||
/** Dispatch a viewer:focus event to display a remote user's stream. */
|
||||
viewStream(userId: string) {
|
||||
const evt = new CustomEvent('viewer:focus', { detail: { userId } });
|
||||
|
||||
window.dispatchEvent(evt);
|
||||
}
|
||||
|
||||
/** Check whether the local user has muted a specific voice user. */
|
||||
isUserLocallyMuted(user: User): boolean {
|
||||
const peerId = user.oderId || user.id;
|
||||
|
||||
return this.voicePlayback.isUserMuted(peerId);
|
||||
}
|
||||
|
||||
/** Check whether a user is currently sharing their screen. */
|
||||
isUserSharing(userId: string): boolean {
|
||||
const me = this.currentUser();
|
||||
|
||||
@@ -618,7 +550,6 @@ export class RoomsSidePanelComponent {
|
||||
return !!stream && stream.getVideoTracks().length > 0;
|
||||
}
|
||||
|
||||
/** Return all users currently connected to a specific voice channel, including the local user. */
|
||||
voiceUsersInRoom(roomId: string) {
|
||||
const room = this.currentRoom();
|
||||
const me = this.currentUser();
|
||||
@@ -626,13 +557,11 @@ export class RoomsSidePanelComponent {
|
||||
(user) => !!user.voiceState?.isConnected && user.voiceState?.roomId === roomId && user.voiceState?.serverId === room?.id
|
||||
);
|
||||
|
||||
// Include the local user at the top if they are in this voice channel
|
||||
if (
|
||||
me?.voiceState?.isConnected &&
|
||||
me.voiceState?.roomId === roomId &&
|
||||
me.voiceState?.serverId === room?.id
|
||||
) {
|
||||
// Avoid duplicates if the current user is already in onlineUsers
|
||||
const meId = me.id;
|
||||
const meOderId = me.oderId;
|
||||
const alreadyIncluded = remoteUsers.some(
|
||||
@@ -647,7 +576,6 @@ export class RoomsSidePanelComponent {
|
||||
return remoteUsers;
|
||||
}
|
||||
|
||||
/** Check whether the current user is connected to the specified voice channel. */
|
||||
isCurrentRoom(roomId: string): boolean {
|
||||
const me = this.currentUser();
|
||||
const room = this.currentRoom();
|
||||
@@ -655,32 +583,18 @@ export class RoomsSidePanelComponent {
|
||||
return !!(me?.voiceState?.isConnected && me.voiceState?.roomId === roomId && me.voiceState?.serverId === room?.id);
|
||||
}
|
||||
|
||||
/** Check whether voice is enabled by the current room's permissions. */
|
||||
voiceEnabled(): boolean {
|
||||
const room = this.currentRoom();
|
||||
|
||||
return room?.permissions?.allowVoice !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the measured latency (ms) to a voice user.
|
||||
* Returns `null` when no measurement is available yet.
|
||||
*/
|
||||
getPeerLatency(user: User): number | null {
|
||||
const latencies = this.webrtc.peerLatencies();
|
||||
|
||||
// Try oderId first (primary peer key), then fall back to user id
|
||||
return latencies.get(user.oderId ?? '') ?? latencies.get(user.id) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Tailwind `bg-*` class representing the latency quality.
|
||||
* - green : < 100 ms
|
||||
* - yellow : 100-199 ms
|
||||
* - orange : 200-349 ms
|
||||
* - red : >= 350 ms
|
||||
* - gray : no data yet
|
||||
*/
|
||||
getPingColorClass(user: User): string {
|
||||
const ms = this.getPeerLatency(user);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user