feat: Add TURN server support
All checks were successful
Queue Release Build / prepare (push) Successful in 15s
Deploy Web Apps / deploy (push) Successful in 5m35s
Queue Release Build / build-linux (push) Successful in 24m45s
Queue Release Build / build-windows (push) Successful in 13m52s
Queue Release Build / finalize (push) Successful in 23s

This commit is contained in:
2026-04-18 21:27:04 +02:00
parent 167c45ba8d
commit 44588e8789
60 changed files with 2404 additions and 365 deletions

View File

@@ -189,6 +189,14 @@
[ringClass]="getVoiceUserRingClass(u)"
/>
<span class="text-sm text-foreground/80 truncate flex-1">{{ u.displayName }}</span>
<!-- Connectivity warning -->
@if (hasConnectivityIssue(u)) {
<ng-icon
name="lucideAlertTriangle"
class="w-3.5 h-3.5 text-amber-500 shrink-0"
title="Connection issue - this user may not hear all participants. Consider adding a TURN server in Settings -> Network."
/>
}
<!-- Ping latency indicator -->
@if (u.id !== currentUser()?.id) {
<span
@@ -399,6 +407,15 @@
<!-- Voice controls pinned to sidebar bottom (hidden when floating controls visible) -->
@if (panelMode() === 'channels' && showVoiceControls() && voiceEnabled()) {
@if (localUserHasDesync()) {
<div class="mx-2 mb-1 flex items-center gap-2 rounded-md bg-amber-500/15 px-3 py-2 text-xs text-amber-400">
<ng-icon
name="lucideAlertTriangle"
class="w-4 h-4 shrink-0"
/>
<span>You may have connectivity issues. Adding a TURN server in Settings -> Network may help.</span>
</div>
}
<div
class="border-t border-border px-2 py-3"
[class.invisible]="showFloatingControls()"

View File

@@ -15,6 +15,7 @@ import {
lucideMic,
lucideMicOff,
lucideChevronLeft,
lucideAlertTriangle,
lucideMonitor,
lucideVideo,
lucideHash,
@@ -35,7 +36,11 @@ import { MessagesActions } from '../../../store/messages/messages.actions';
import { RealtimeSessionFacade } from '../../../core/realtime';
import { ScreenShareFacade } from '../../../domains/screen-share';
import { NotificationsFacade } from '../../../domains/notifications';
import { VoiceActivityService, VoiceConnectionFacade } from '../../../domains/voice-connection';
import {
VoiceActivityService,
VoiceConnectionFacade,
VoiceConnectivityHealthService
} from '../../../domains/voice-connection';
import { VoiceSessionFacade, VoiceWorkspaceService } from '../../../domains/voice-session';
import { VoicePlaybackService } from '../../../domains/voice-connection';
import { VoiceControlsComponent } from '../../../domains/voice-session/feature/voice-controls/voice-controls.component';
@@ -83,6 +88,7 @@ type PanelMode = 'channels' | 'users';
lucideMic,
lucideMicOff,
lucideChevronLeft,
lucideAlertTriangle,
lucideMonitor,
lucideVideo,
lucideHash,
@@ -104,6 +110,7 @@ export class RoomsSidePanelComponent {
private voicePlayback = inject(VoicePlaybackService);
private profileCard = inject(ProfileCardService);
private readonly voiceActivity = inject(VoiceActivityService);
private readonly voiceConnectivity = inject(VoiceConnectivityHealthService);
readonly panelMode = input<PanelMode>('channels');
readonly showVoiceControls = input(true);
@@ -115,6 +122,7 @@ export class RoomsSidePanelComponent {
activeChannelId = this.store.selectSignal(selectActiveChannelId);
textChannels = this.store.selectSignal(selectTextChannels);
voiceChannels = this.store.selectSignal(selectVoiceChannels);
localUserHasDesync = this.voiceConnectivity.localUserHasDesync;
roomMembers = computed(() => this.currentRoom()?.members ?? []);
roomMemberIdentifiers = computed(() => {
const identifiers = new Set<string>();
@@ -248,6 +256,10 @@ export class RoomsSidePanelComponent {
);
}
hasConnectivityIssue(user: User): boolean {
return this.voiceConnectivity.hasPeerDesync(user.oderId || user.id);
}
canManageChannels(): boolean {
const room = this.currentRoom();
const user = this.currentUser();