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:
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
import {
|
||||
Component,
|
||||
inject,
|
||||
@@ -81,37 +82,39 @@ import { AppI18nService, APP_TRANSLATE_IMPORTS } from '../../../core/i18n';
|
||||
* Electron-style title bar with window controls, navigation, and server menu.
|
||||
*/
|
||||
export class TitleBarComponent {
|
||||
private readonly appI18n = inject(AppI18nService);
|
||||
private store = inject(Store);
|
||||
private electronBridge = inject(ElectronBridgeService);
|
||||
private serverDirectory = inject(ServerDirectoryFacade);
|
||||
private router = inject(Router);
|
||||
private webrtc = inject(RealtimeSessionFacade);
|
||||
private platform = inject(PlatformService);
|
||||
private voiceWorkspace = inject(VoiceWorkspaceService);
|
||||
private settingsModal = inject(SettingsModalService);
|
||||
private pluginRegistry = inject(PluginRegistryService);
|
||||
private pluginRequirements = inject(PluginRequirementStateService);
|
||||
private pluginStore = inject(PluginStoreService);
|
||||
|
||||
private getWindowControlsApi() {
|
||||
return this.electronBridge.getApi();
|
||||
}
|
||||
|
||||
isElectron = computed(() => this.platform.isElectron);
|
||||
|
||||
showMenuState = computed(() => false);
|
||||
|
||||
currentUser = this.store.selectSignal(selectCurrentUser);
|
||||
|
||||
username = computed(() => this.currentUser()?.displayName || this.appI18n.instant('shell.titleBar.guest'));
|
||||
|
||||
serverName = computed(() => this.serverDirectory.activeServer()?.name || this.appI18n.instant('shell.titleBar.noServer'));
|
||||
|
||||
isConnected = computed(() => this.webrtc.isConnected());
|
||||
|
||||
isReconnecting = computed(() => !this.webrtc.isConnected() && this.webrtc.hasEverConnected());
|
||||
|
||||
isAuthed = computed(() => !!this.currentUser());
|
||||
|
||||
currentRoom = this.store.selectSignal(selectCurrentRoom);
|
||||
|
||||
activeChannelId = this.store.selectSignal(selectActiveChannelId);
|
||||
|
||||
textChannels = this.store.selectSignal(selectTextChannels);
|
||||
|
||||
voiceChannels = this.store.selectSignal(selectVoiceChannels);
|
||||
|
||||
isVoiceWorkspaceExpanded = this.voiceWorkspace.isExpanded;
|
||||
|
||||
isSignalServerReconnecting = this.store.selectSignal(selectIsSignalServerReconnecting);
|
||||
|
||||
signalServerCompatibilityError = this.store.selectSignal(selectSignalServerCompatibilityError);
|
||||
|
||||
isInDirectMessage = toSignal(
|
||||
this.router.events.pipe(
|
||||
filter((navigationEvent): navigationEvent is NavigationEnd => navigationEvent instanceof NavigationEnd),
|
||||
@@ -119,7 +122,6 @@ export class TitleBarComponent {
|
||||
),
|
||||
{ initialValue: this.router.url.startsWith('/dm/') }
|
||||
);
|
||||
|
||||
isInRoomView = toSignal(
|
||||
this.router.events.pipe(
|
||||
filter((navigationEvent): navigationEvent is NavigationEnd => navigationEvent instanceof NavigationEnd),
|
||||
@@ -127,11 +129,8 @@ export class TitleBarComponent {
|
||||
),
|
||||
{ initialValue: this.router.url.startsWith('/room/') }
|
||||
);
|
||||
|
||||
inRoom = computed(() => !!this.currentRoom() && this.isInRoomView());
|
||||
|
||||
roomName = computed(() => this.currentRoom()?.name || '');
|
||||
|
||||
activeTextChannelName = computed(() => {
|
||||
const textChannels = this.textChannels();
|
||||
|
||||
@@ -144,14 +143,12 @@ export class TitleBarComponent {
|
||||
|
||||
return activeChannel ? activeChannel.name : id;
|
||||
});
|
||||
|
||||
connectedVoiceChannelName = computed(() => {
|
||||
const voiceChannelId = this.currentUser()?.voiceState?.roomId;
|
||||
const voiceChannel = this.voiceChannels().find((channel) => channel.id === voiceChannelId);
|
||||
|
||||
return voiceChannel?.name || this.appI18n.instant('shell.titleBar.voiceLounge');
|
||||
});
|
||||
|
||||
roomContextMeta = computed(() => {
|
||||
if (!this.currentRoom()) {
|
||||
return '';
|
||||
@@ -165,11 +162,9 @@ export class TitleBarComponent {
|
||||
|
||||
return parts.join(' | ');
|
||||
});
|
||||
|
||||
showRoomCompatibilityNotice = computed(() =>
|
||||
this.inRoom() && !!this.signalServerCompatibilityError()
|
||||
);
|
||||
|
||||
showRoomReconnectNotice = computed(() =>
|
||||
this.inRoom()
|
||||
&& !this.signalServerCompatibilityError()
|
||||
@@ -179,56 +174,20 @@ export class TitleBarComponent {
|
||||
|| this.isReconnecting()
|
||||
)
|
||||
);
|
||||
|
||||
serverPluginCount = computed(() => this.pluginRegistry.entries()
|
||||
.filter((entry) => getPluginInstallScope(entry.manifest) === 'server')
|
||||
.length);
|
||||
|
||||
hasServerPlugins = computed(() => this.inRoom() && this.serverPluginCount() > 0);
|
||||
|
||||
requiredPluginRequirements = this.pluginRequirements.missingRequiredRequirements;
|
||||
|
||||
optionalPluginRequirement = computed(() => this.inRoom() ? this.pluginRequirements.visibleOptionalRequirements()[0] ?? null : null);
|
||||
|
||||
optionalPluginRequirementCount = computed(() => this.pluginRequirements.visibleOptionalRequirements().length);
|
||||
|
||||
showMenu = computed(() => this._showMenu());
|
||||
|
||||
showLeaveConfirm = signal(false);
|
||||
|
||||
inviteStatus = signal<string | null>(null);
|
||||
|
||||
creatingInvite = signal(false);
|
||||
|
||||
pluginRequirementBusy = signal(false);
|
||||
|
||||
pluginRequirementError = signal<string | null>(null);
|
||||
|
||||
private readonly appI18n = inject(AppI18nService);
|
||||
|
||||
private store = inject(Store);
|
||||
|
||||
private electronBridge = inject(ElectronBridgeService);
|
||||
|
||||
private serverDirectory = inject(ServerDirectoryFacade);
|
||||
|
||||
private router = inject(Router);
|
||||
|
||||
private webrtc = inject(RealtimeSessionFacade);
|
||||
|
||||
private platform = inject(PlatformService);
|
||||
|
||||
private voiceWorkspace = inject(VoiceWorkspaceService);
|
||||
|
||||
private settingsModal = inject(SettingsModalService);
|
||||
|
||||
private pluginRegistry = inject(PluginRegistryService);
|
||||
|
||||
private pluginRequirements = inject(PluginRequirementStateService);
|
||||
|
||||
private pluginStore = inject(PluginStoreService);
|
||||
|
||||
private _showMenu = signal(false);
|
||||
showMenu = computed(() => this._showMenu());
|
||||
showLeaveConfirm = signal(false);
|
||||
inviteStatus = signal<string | null>(null);
|
||||
creatingInvite = signal(false);
|
||||
pluginRequirementBusy = signal(false);
|
||||
pluginRequirementError = signal<string | null>(null);
|
||||
|
||||
/** Minimize the Electron window. */
|
||||
minimize() {
|
||||
@@ -297,6 +256,15 @@ export class TitleBarComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/** Open the unified leave-server confirmation dialog. */
|
||||
private openLeaveConfirm() {
|
||||
this._showMenu.set(false);
|
||||
|
||||
if (this.currentRoom()) {
|
||||
this.showLeaveConfirm.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
/** Toggle the server dropdown menu. */
|
||||
toggleMenu() {
|
||||
this.inviteStatus.set(null);
|
||||
@@ -386,34 +354,6 @@ export class TitleBarComponent {
|
||||
this._showMenu.set(false);
|
||||
}
|
||||
|
||||
/** Log out the current user, disconnect from signaling, and navigate to login. */
|
||||
logout() {
|
||||
this._showMenu.set(false);
|
||||
// Disconnect from signaling server - this broadcasts "user_left" to all
|
||||
// servers the user was a member of, so other users see them go offline.
|
||||
this.webrtc.disconnect();
|
||||
|
||||
clearStoredCurrentUserId();
|
||||
this.store.dispatch(MessagesActions.clearMessages());
|
||||
this.store.dispatch(RoomsActions.resetRoomsState());
|
||||
this.store.dispatch(UsersActions.resetUsersState());
|
||||
|
||||
this.router.navigate(['/login']);
|
||||
}
|
||||
|
||||
private getWindowControlsApi() {
|
||||
return this.electronBridge.getApi();
|
||||
}
|
||||
|
||||
/** Open the unified leave-server confirmation dialog. */
|
||||
private openLeaveConfirm() {
|
||||
this._showMenu.set(false);
|
||||
|
||||
if (this.currentRoom()) {
|
||||
this.showLeaveConfirm.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
private async installServerRequirements(requirements: PluginRequirementSummary[]): Promise<void> {
|
||||
const room = this.currentRoom();
|
||||
|
||||
@@ -433,6 +373,21 @@ export class TitleBarComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/** Log out the current user, disconnect from signaling, and navigate to login. */
|
||||
logout() {
|
||||
this._showMenu.set(false);
|
||||
// Disconnect from signaling server - this broadcasts "user_left" to all
|
||||
// servers the user was a member of, so other users see them go offline.
|
||||
this.webrtc.disconnect();
|
||||
|
||||
clearStoredCurrentUserId();
|
||||
this.store.dispatch(MessagesActions.clearMessages());
|
||||
this.store.dispatch(RoomsActions.resetRoomsState());
|
||||
this.store.dispatch(UsersActions.resetUsersState());
|
||||
|
||||
this.router.navigate(['/login']);
|
||||
}
|
||||
|
||||
private async copyInviteLink(inviteUrl: string): Promise<void> {
|
||||
if (navigator.clipboard?.writeText) {
|
||||
try {
|
||||
@@ -472,5 +427,4 @@ export class TitleBarComponent {
|
||||
sourceUrl: room.sourceUrl
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user