feat: Add pm

This commit is contained in:
2026-04-27 00:45:16 +02:00
parent bc2fa7de22
commit 11c2588e45
65 changed files with 3653 additions and 214 deletions

View File

@@ -8,6 +8,7 @@ import {
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { Store } from '@ngrx/store';
import { NgIcon, provideIcons } from '@ng-icons/core';
import {
@@ -42,6 +43,7 @@ import {
VoiceConnectivityHealthService
} from '../../../domains/voice-connection';
import { VoiceSessionFacade, VoiceWorkspaceService } from '../../../domains/voice-session';
import { DirectMessageService } from '../../../domains/direct-message';
import { VoicePlaybackService } from '../../../domains/voice-connection';
import { VoiceControlsComponent } from '../../../domains/voice-session/feature/voice-controls/voice-controls.component';
import { isChannelNameTaken, normalizeChannelName } from '../../../store/rooms/room-channels.rules';
@@ -101,6 +103,7 @@ type PanelMode = 'channels' | 'users';
})
export class RoomsSidePanelComponent {
private store = inject(Store);
private router = inject(Router);
private realtime = inject(RealtimeSessionFacade);
private voiceConnection = inject(VoiceConnectionFacade);
private screenShare = inject(ScreenShareFacade);
@@ -109,8 +112,10 @@ export class RoomsSidePanelComponent {
private voiceWorkspace = inject(VoiceWorkspaceService);
private voicePlayback = inject(VoicePlaybackService);
private profileCard = inject(ProfileCardService);
private directMessages = inject(DirectMessageService);
private readonly voiceActivity = inject(VoiceActivityService);
private readonly voiceConnectivity = inject(VoiceConnectivityHealthService);
private profileCardOpenTimer: ReturnType<typeof setTimeout> | null = null;
readonly panelMode = input<PanelMode>('channels');
readonly showVoiceControls = input(true);
@@ -201,8 +206,41 @@ export class RoomsSidePanelComponent {
this.profileCard.open(el, user, { placement: 'left', editable });
}
openUserCard(event: Event, user: User): void {
event.stopPropagation();
this.queueProfileCardOpen(event.currentTarget as HTMLElement, user, false);
}
openProfileCardForMember(event: Event, member: RoomMember): void {
const user: User = {
const user = this.roomMemberToUser(member);
this.openProfileCard(event, user, false);
}
openMemberCard(event: Event, member: RoomMember): void {
event.stopPropagation();
this.queueProfileCardOpen(event.currentTarget as HTMLElement, this.roomMemberToUser(member), false);
}
async openDirectMessage(event: Event, user: User): Promise<void> {
event.stopPropagation();
this.cancelQueuedProfileCardOpen();
if (this.isCurrentUserIdentity(user)) {
return;
}
const conversation = await this.directMessages.createConversation(user);
await this.router.navigate(['/dm', conversation.id]);
}
async openDirectMessageForMember(event: Event, member: RoomMember): Promise<void> {
await this.openDirectMessage(event, this.roomMemberToUser(member));
}
private roomMemberToUser(member: RoomMember): User {
return {
id: member.id,
oderId: member.oderId || member.id,
username: member.username,
@@ -210,12 +248,13 @@ export class RoomsSidePanelComponent {
description: member.description,
profileUpdatedAt: member.profileUpdatedAt,
avatarUrl: member.avatarUrl,
avatarHash: member.avatarHash,
avatarMime: member.avatarMime,
avatarUpdatedAt: member.avatarUpdatedAt,
status: 'disconnected',
role: member.role,
joinedAt: member.joinedAt
};
this.openProfileCard(event, user, false);
}
private roomMemberKey(member: RoomMember): string {
@@ -256,6 +295,23 @@ export class RoomsSidePanelComponent {
);
}
private queueProfileCardOpen(anchor: HTMLElement, user: User, editable: boolean): void {
this.cancelQueuedProfileCardOpen();
this.profileCardOpenTimer = setTimeout(() => {
this.profileCardOpenTimer = null;
this.profileCard.open(anchor, user, { placement: 'left', editable });
}, 180);
}
private cancelQueuedProfileCardOpen(): void {
if (!this.profileCardOpenTimer) {
return;
}
clearTimeout(this.profileCardOpenTimer);
this.profileCardOpenTimer = null;
}
hasConnectivityIssue(user: User): boolean {
return this.voiceConnectivity.hasPeerDesync(user.oderId || user.id);
}