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 {
Component,
DestroyRef,
@@ -69,32 +70,36 @@ const ACTIVATION_DEBOUNCE_MS = 150;
templateUrl: './servers-rail.component.html'
})
export class ServersRailComponent {
private readonly appI18n = inject(AppI18nService);
private store = inject(Store);
private router = inject(Router);
private voiceSession = inject(VoiceSessionFacade);
private db = inject(DatabaseService);
private notifications = inject(NotificationsFacade);
readonly directCalls = inject(DirectCallService);
private serverDirectory = inject(ServerDirectoryFacade);
private destroyRef = inject(DestroyRef);
private banLookupRequestVersion = 0;
private bannedLookupUserKey: string | null = null;
private activationRequestVersion = 0;
private activationTimer: ReturnType<typeof window.setTimeout> | null = null;
private joinRequestVersion = 0;
private joinRequestTimer: ReturnType<typeof window.setTimeout> | null = null;
private visibleSavedRoomCache: Room[] = [];
private savedRoomJoinRequests = new Subject<{ room: Room; password?: string }>();
savedRooms = this.store.selectSignal(selectSavedRooms);
currentRoom = this.store.selectSignal(selectCurrentRoom);
showMenu = signal(false);
menuX = signal(72);
menuY = signal(100);
contextRoom = signal<Room | null>(null);
optimisticSelectedRoomId = signal<string | null>(null);
showLeaveConfirm = signal(false);
showCreateDialog = signal(false);
currentUser = this.store.selectSignal(selectCurrentUser);
onlineUsers = this.store.selectSignal(selectOnlineUsers);
bannedRoomLookup = signal<Record<string, boolean>>({});
isOnServers = toSignal(
this.router.events.pipe(
filter((navigationEvent): navigationEvent is NavigationEnd => navigationEvent instanceof NavigationEnd),
@@ -102,7 +107,6 @@ export class ServersRailComponent {
),
{ initialValue: this.router.url.startsWith('/servers') }
);
isOnDirectMessage = toSignal(
this.router.events.pipe(
filter((navigationEvent): navigationEvent is NavigationEnd => navigationEvent instanceof NavigationEnd),
@@ -110,7 +114,6 @@ export class ServersRailComponent {
),
{ initialValue: this.isDirectMessageUrl(this.router.url) }
);
isOnDashboard = toSignal(
this.router.events.pipe(
filter((navigationEvent): navigationEvent is NavigationEnd => navigationEvent instanceof NavigationEnd),
@@ -118,7 +121,6 @@ export class ServersRailComponent {
),
{ initialValue: this.router.url.startsWith('/dashboard') }
);
isOnCall = toSignal(
this.router.events.pipe(
filter((navigationEvent): navigationEvent is NavigationEnd => navigationEvent instanceof NavigationEnd),
@@ -126,7 +128,6 @@ export class ServersRailComponent {
),
{ initialValue: this.router.url.startsWith('/call/') }
);
currentCallId = toSignal(
this.router.events.pipe(
filter((navigationEvent): navigationEvent is NavigationEnd => navigationEvent instanceof NavigationEnd),
@@ -134,7 +135,6 @@ export class ServersRailComponent {
),
{ initialValue: this.callIdFromUrl(this.router.url) }
);
selectedCallIndex = computed(() => {
const routeCallId = this.currentCallId();
const visibleCalls = this.directCalls.visibleActiveSessions();
@@ -155,21 +155,13 @@ export class ServersRailComponent {
return visibleCalls.findIndex((call) => call.callId === currentSession.callId);
});
bannedServerName = signal('');
showBannedDialog = signal(false);
showPasswordDialog = signal(false);
passwordPromptRoom = signal<Room | null>(null);
joinPassword = signal('');
joinPasswordError = signal<string | null>(null);
visibleSavedRooms = computed(() => this.stabilizeVisibleSavedRooms(this.savedRooms().filter((room) => !this.isRoomMarkedBanned(room))));
voicePresenceByRoom = computed(() => {
const presence: Record<string, number> = {};
const seenByRoom = new Map<string, Set<string>>();
@@ -211,38 +203,6 @@ export class ServersRailComponent {
return presence;
});
private readonly appI18n = inject(AppI18nService);
private store = inject(Store);
private router = inject(Router);
private voiceSession = inject(VoiceSessionFacade);
private db = inject(DatabaseService);
private notifications = inject(NotificationsFacade);
private serverDirectory = inject(ServerDirectoryFacade);
private destroyRef = inject(DestroyRef);
private banLookupRequestVersion = 0;
private bannedLookupUserKey: string | null = null;
private activationRequestVersion = 0;
private activationTimer: ReturnType<typeof window.setTimeout> | null = null;
private joinRequestVersion = 0;
private joinRequestTimer: ReturnType<typeof window.setTimeout> | null = null;
private visibleSavedRoomCache: Room[] = [];
private savedRoomJoinRequests = new Subject<{ room: Room; password?: string }>();
constructor() {
effect(() => {
const rooms = this.savedRooms();
@@ -281,8 +241,6 @@ export class ServersRailComponent {
});
}
trackRoomId = (index: number, room: Room) => room.id;
initial(name?: string): string {
if (!name)
return '?';
@@ -292,6 +250,8 @@ export class ServersRailComponent {
return ch || '?';
}
trackRoomId = (index: number, room: Room) => room.id;
goToDashboard(): void {
const voiceServerId = this.voiceSession.getVoiceServerId();
@@ -382,6 +342,13 @@ export class ServersRailComponent {
return !!this.bannedRoomLookup()[room.id];
}
private callIdFromUrl(url: string): string | null {
const path = url.split(/[?#]/, 1)[0];
const match = path.match(/^\/call\/([^/]+)/);
return match?.[1] ? decodeURIComponent(match[1]) : null;
}
openContextMenu(evt: MouseEvent, room: Room): void {
evt.preventDefault();
this.contextRoom.set(room);
@@ -487,13 +454,6 @@ export class ServersRailComponent {
return this.currentRoom()?.id === room.id;
}
private callIdFromUrl(url: string): string | null {
const path = url.split(/[?#]/, 1)[0];
const match = path.match(/^\/call\/([^/]+)/);
return match?.[1] ? decodeURIComponent(match[1]) : null;
}
private stabilizeVisibleSavedRooms(nextRooms: Room[]): Room[] {
const previousById = new Map(this.visibleSavedRoomCache.map((room) => [room.id, room]));
const stabilizedRooms = nextRooms.map((room) => {
@@ -846,5 +806,4 @@ export class ServersRailComponent {
return nextRoom;
}
}