fix: restore build and stabilize E2E cross-signal behavior

Revert the automated member-ordering pass that broke Angular field init
(TS2729) and disable that rule until a safe reorder strategy exists.
Fix modal/confirm dialog i18n defaults via template fallbacks, search all
active endpoints (including offline), register foreign rooms with actor
owner IDs, sync profile display names from avatar summaries, and guard
dm-chat when a private call converts to a group conversation.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-11 12:16:40 +02:00
co-authored by Cursor
parent 79c6f91cd6
commit 31962aeb1a
131 changed files with 2483 additions and 3896 deletions
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/member-ordering */
import {
Injectable,
computed,
@@ -36,13 +37,31 @@ import { toDirectMessageParticipant } from '../../../direct-message';
@Injectable({ providedIn: 'root' })
export class DirectCallService {
private readonly router = inject(Router);
private readonly store = inject(Store);
private readonly delivery = inject(PeerDeliveryService);
private readonly directMessages = inject(DirectMessageService);
private readonly audio = inject(NotificationAudioService);
private readonly voice = inject(VoiceConnectionFacade);
private readonly voiceSession = inject(VoiceSessionFacade);
private readonly realtime = inject(RealtimeSessionFacade);
private readonly voiceActivity = inject(VoiceActivityService);
private readonly playback = inject(VoicePlaybackService);
private readonly viewport = inject(ViewportService);
private readonly mobileNotifications = inject(MobileNotificationsService);
private readonly mobileCallSession = inject(MobileCallSessionService);
private readonly mobileMedia = inject(MobileMediaService);
private readonly i18n = inject(AppI18nService);
private readonly currentUser = this.store.selectSignal(selectCurrentUser);
private readonly users = this.store.selectSignal(selectAllUsers);
private readonly sessionsSignal = signal<DirectCallSession[]>([]);
private readonly mobileOverlayCallId = signal<string | null>(null);
private readonly pendingIncomingCallPayloads: DirectCallEventPayload[] = [];
private readonly declinedCallIds = new Set<string>();
readonly sessions = computed(() => this.sessionsSignal());
readonly activeSessions = computed(() => this.sessions().filter((session) => session.status !== 'ended'));
readonly visibleActiveSessions = computed(() => this.activeSessions().filter((session) => this.hasOngoingActivity(session)));
readonly incomingCall = computed<DirectCallSession | null>(() => {
if (this.isDoNotDisturb()) {
return null;
@@ -61,11 +80,8 @@ export class DirectCallService {
&& !session.participants[meId]?.joined
&& this.hasConnectedParticipant(session)) ?? null;
});
readonly currentSession = signal<DirectCallSession | null>(null);
readonly hasActiveCall = computed(() => this.visibleActiveSessions().length > 0);
readonly mobileOverlaySession = computed(() => {
const callId = this.mobileOverlayCallId();
@@ -76,48 +92,6 @@ export class DirectCallService {
return this.visibleActiveSessions().find((session) => session.callId === callId) ?? null;
});
private readonly router = inject(Router);
private readonly store = inject(Store);
private readonly delivery = inject(PeerDeliveryService);
private readonly directMessages = inject(DirectMessageService);
private readonly audio = inject(NotificationAudioService);
private readonly voice = inject(VoiceConnectionFacade);
private readonly voiceSession = inject(VoiceSessionFacade);
private readonly realtime = inject(RealtimeSessionFacade);
private readonly voiceActivity = inject(VoiceActivityService);
private readonly playback = inject(VoicePlaybackService);
private readonly viewport = inject(ViewportService);
private readonly mobileNotifications = inject(MobileNotificationsService);
private readonly mobileCallSession = inject(MobileCallSessionService);
private readonly mobileMedia = inject(MobileMediaService);
private readonly i18n = inject(AppI18nService);
private readonly currentUser = this.store.selectSignal(selectCurrentUser);
private readonly users = this.store.selectSignal(selectAllUsers);
private readonly sessionsSignal = signal<DirectCallSession[]>([]);
private readonly mobileOverlayCallId = signal<string | null>(null);
private readonly pendingIncomingCallPayloads: DirectCallEventPayload[] = [];
private readonly declinedCallIds = new Set<string>();
constructor() {
this.mobileCallSession.initialize();
this.mobileCallSession.onCallControlAction((intent, callId) => {
@@ -418,6 +392,19 @@ export class DirectCallService {
}
}
private leaveJoinedSession(session: DirectCallSession, endForEveryone = false): void {
const action = endForEveryone ? 'end' : 'leave';
const nextSession = this.markCurrentUserLeft(session, endForEveryone);
this.audio.stop(AppSound.Call);
this.broadcastCallEvent(action, nextSession);
this.stopLocalMedia(nextSession);
this.upsertSession(nextSession);
this.currentSession.set(null);
void this.mobileCallSession.endActiveCall(session.callId);
}
async inviteUser(callId: string, user: User): Promise<void> {
const session = this.sessionById(callId);
@@ -458,19 +445,6 @@ export class DirectCallService {
return participant ? participantToUser(participant) : null;
}
private leaveJoinedSession(session: DirectCallSession, endForEveryone = false): void {
const action = endForEveryone ? 'end' : 'leave';
const nextSession = this.markCurrentUserLeft(session, endForEveryone);
this.audio.stop(AppSound.Call);
this.broadcastCallEvent(action, nextSession);
this.stopLocalMedia(nextSession);
this.upsertSession(nextSession);
this.currentSession.set(null);
void this.mobileCallSession.endActiveCall(session.callId);
}
private async drainPendingIncomingCallPayloads(): Promise<void> {
if (this.pendingIncomingCallPayloads.length === 0) {
return;
@@ -1067,5 +1041,4 @@ export class DirectCallService {
return user;
}
}