fix: Major bug cleanup pass 1
All checks were successful
Queue Release Build / prepare (push) Successful in 19s
Deploy Web Apps / deploy (push) Successful in 8m12s
Queue Release Build / build-windows (push) Successful in 27m44s
Queue Release Build / build-linux (push) Successful in 48m1s
Queue Release Build / build-android (push) Successful in 22m7s
Queue Release Build / finalize (push) Successful in 2m42s

This commit is contained in:
2026-06-09 17:59:54 +02:00
parent 80d7728e66
commit eb51f043ac
127 changed files with 2731 additions and 322 deletions

View File

@@ -45,10 +45,7 @@ import { loadVoiceSettingsFromStorage, saveVoiceSettingsToStorage } from '../../
import { ScreenShareQualityDialogComponent } from '../../shared';
import { ViewportService } from '../../core/platform';
import { RealtimeSessionFacade } from '../../core/realtime';
import {
isLocalVoiceOwner,
isVoiceOnAnotherClient
} from '../../domains/voice-session';
import { isLocalVoiceOwner, isVoiceOnAnotherClient } from '../../domains/voice-session';
import { MobileMediaService, MobilePlatformService } from '../../infrastructure/mobile';
import { selectAllUsers, selectCurrentUser } from '../../store/users/users.selectors';
import { UsersActions } from '../../store/users/users.actions';

View File

@@ -32,6 +32,8 @@ import {
lucidePackage
} from '@ng-icons/lucide';
import { selectOnlineUsers, selectCurrentUser } from '../../../store/users/users.selectors';
import { SignalServerAuthService } from '../../../domains/authentication/application/services/signal-server-auth.service';
import { isSelfPresenceUserId } from '../../../domains/authentication/domain/logic/self-presence-identity.rules';
import {
selectCurrentRoom,
selectActiveChannelId,
@@ -140,6 +142,7 @@ const SKELETON_REVEAL_DELAY_MS = 180;
})
export class RoomsSidePanelComponent implements OnDestroy {
private store = inject(Store);
private signalServerAuth = inject(SignalServerAuthService);
private router = inject(Router);
private realtime = inject(RealtimeSessionFacade);
private voiceConnection = inject(VoiceConnectionFacade);
@@ -208,7 +211,7 @@ export class RoomsSidePanelComponent implements OnDestroy {
this.addIdentifiers(onlineIdentifiers, user);
}
this.addIdentifiers(onlineIdentifiers, this.currentUser());
this.addSelfPresenceIdentifiers(onlineIdentifiers);
return this.roomMembers().filter((member) => !this.matchesIdentifiers(onlineIdentifiers, member));
});
@@ -408,10 +411,33 @@ export class RoomsSidePanelComponent implements OnDestroy {
private isCurrentUserIdentity(entity: { id?: string; oderId?: string }): boolean {
const current = this.currentUser();
return (
!!current &&
((typeof entity.id === 'string' && entity.id === current.id) || (typeof entity.oderId === 'string' && entity.oderId === current.oderId))
if (!current) {
return false;
}
const selfIds = this.signalServerAuth.resolveSelfPresenceUserIdsForRoom(
current,
this.currentRoom()?.sourceUrl
);
return isSelfPresenceUserId(entity.oderId, selfIds) || isSelfPresenceUserId(entity.id, selfIds);
}
private addSelfPresenceIdentifiers(identifiers: Set<string>): void {
const current = this.currentUser();
if (!current) {
return;
}
this.addIdentifiers(identifiers, current);
for (const selfId of this.signalServerAuth.resolveSelfPresenceUserIdsForRoom(
current,
this.currentRoom()?.sourceUrl
)) {
identifiers.add(selfId);
}
}
private queueProfileCardOpen(anchor: HTMLElement, user: User, editable: boolean): void {

View File

@@ -280,6 +280,7 @@ export class ServersRailComponent {
this.router.navigate(['/login'], {
queryParams: buildLoginReturnQueryParams(this.router.url)
});
return;
}

View File

@@ -22,7 +22,11 @@ import { APP_TRANSLATE_IMPORTS } from '../../../../core/i18n';
@Component({
selector: 'app-bans-settings',
standalone: true,
imports: [CommonModule, NgIcon, ...APP_TRANSLATE_IMPORTS],
imports: [
CommonModule,
NgIcon,
...APP_TRANSLATE_IMPORTS
],
viewProviders: [
provideIcons({
lucideX

View File

@@ -23,7 +23,11 @@ type DataAction = 'open' | 'export' | 'import' | 'erase' | 'restart';
@Component({
selector: 'app-data-settings',
standalone: true,
imports: [CommonModule, NgIcon, ...APP_TRANSLATE_IMPORTS],
imports: [
CommonModule,
NgIcon,
...APP_TRANSLATE_IMPORTS
],
viewProviders: [
provideIcons({
lucideDatabase,

View File

@@ -31,7 +31,11 @@ const APP_METRICS_POLL_INTERVAL_MS = 2_000;
@Component({
selector: 'app-debugging-settings',
standalone: true,
imports: [CommonModule, NgIcon, ...APP_TRANSLATE_IMPORTS],
imports: [
CommonModule,
NgIcon,
...APP_TRANSLATE_IMPORTS
],
viewProviders: [
provideIcons({
lucideBug,

View File

@@ -14,10 +14,7 @@ import { ElectronBridgeService } from '../../../../core/platform/electron/electr
import { PlatformService } from '../../../../core/platform';
import { ExperimentalMediaSettingsService } from '../../../../domains/experimental-media/application/services/experimental-media-settings.service';
import { APP_TRANSLATE_IMPORTS } from '../../../../core/i18n';
import {
SelectOnFocusDirective,
SubmitOnEnterDirective
} from '../../../../shared/directives';
import { SelectOnFocusDirective, SubmitOnEnterDirective } from '../../../../shared/directives';
@Component({
selector: 'app-general-settings',

View File

@@ -83,7 +83,7 @@
type="button"
(click)="removeEntry(entry.id)"
class="grid h-7 w-7 place-items-center rounded-lg transition-colors hover:bg-destructive/10"
[title]="'settings.network.ice.moveDown' | translate"
[title]="'settings.network.ice.remove' | translate"
>
<ng-icon
name="lucideTrash2"

View File

@@ -81,6 +81,7 @@ export class IceServerSettingsComponent {
? 'settings.network.ice.errors.urlPrefixStun'
: 'settings.network.ice.errors.urlPrefixTurn'
));
return;
}

View File

@@ -21,7 +21,11 @@ import { APP_TRANSLATE_IMPORTS, AppI18nService } from '../../../../core/i18n';
@Component({
selector: 'app-local-api-settings',
standalone: true,
imports: [CommonModule, FormsModule, ...APP_TRANSLATE_IMPORTS],
imports: [
CommonModule,
FormsModule,
...APP_TRANSLATE_IMPORTS
],
templateUrl: './local-api-settings.component.html'
})
export class LocalApiSettingsComponent implements OnInit, OnDestroy {

View File

@@ -39,6 +39,27 @@
{{ 'settings.network.serverEndpoints.descriptionModal' | translate }}
</p>
@if (provisionNotice(); as notice) {
<div class="mb-3 rounded-lg border border-border bg-secondary/40 px-3 py-2 text-xs text-foreground">
{{
'auth.provision.usernameCollision'
| translate
: {
serverName: notice.serverName,
preferredUsername: notice.preferredUsername,
provisionedUsername: notice.provisionedUsername
}
}}
<button
type="button"
class="ml-2 text-primary hover:underline"
(click)="dismissProvisionNotice()"
>
{{ 'common.dismiss' | translate }}
</button>
</div>
}
<!-- Server List -->
<div class="space-y-2 mb-3">
@for (server of servers(); track server.id) {
@@ -70,11 +91,21 @@
@if (server.latency !== undefined && server.status === 'online') {
<p class="text-[10px] text-muted-foreground">{{ server.latency }}ms</p>
}
<p class="text-[10px] text-muted-foreground">{{ authStatusKey(server.url) | translate }}</p>
@if (server.status === 'incompatible') {
<p class="text-[10px] text-destructive">{{ 'settings.network.serverEndpoints.incompatible' | translate }}</p>
}
</div>
<div class="flex items-center gap-1 flex-shrink-0">
@if (!signalServerAuth.hasValidCredential(server.url)) {
<button
type="button"
(click)="authorizeServer(server.url)"
class="px-2 py-1 text-[10px] rounded-lg bg-secondary text-foreground hover:bg-secondary/80 transition-colors"
>
{{ 'settings.network.serverEndpoints.auth.signIn' | translate }}
</button>
}
@if (!server.isActive && server.status !== 'incompatible') {
<button
type="button"
@@ -143,6 +174,7 @@
</div>
<button
type="button"
data-testid="add-signal-server-button"
(click)="addServer()"
[disabled]="!newServerName || !newServerUrl"
class="grid h-9 w-9 place-items-center self-end rounded-lg bg-primary text-primary-foreground transition-colors hover:bg-primary/90 disabled:cursor-not-allowed disabled:opacity-50"

View File

@@ -19,13 +19,13 @@ import {
} from '@ng-icons/lucide';
import { ServerDirectoryFacade } from '../../../../domains/server-directory';
import { SignalServerAuthService } from '../../../../domains/authentication/application/services/signal-server-auth.service';
import { SignalServerProvisionNoticeService } from '../../../../domains/authentication/application/services/signal-server-provision-notice.service';
import { SignalServerAuthorizeService } from '../../../../domains/authentication/application/services/signal-server-authorize.service';
import { STORAGE_KEY_CONNECTION_SETTINGS } from '../../../../core/constants';
import { IceServerSettingsComponent } from '../ice-server-settings/ice-server-settings.component';
import { APP_TRANSLATE_IMPORTS, AppI18nService } from '../../../../core/i18n';
import {
SelectOnFocusDirective,
SubmitOnEnterDirective
} from '../../../../shared/directives';
import { SelectOnFocusDirective, SubmitOnEnterDirective } from '../../../../shared/directives';
@Component({
selector: 'app-network-settings',
@@ -55,6 +55,10 @@ import {
export class NetworkSettingsComponent {
private serverDirectory = inject(ServerDirectoryFacade);
private readonly appI18n = inject(AppI18nService);
readonly signalServerAuth = inject(SignalServerAuthService);
private readonly signalServerAuthorize = inject(SignalServerAuthorizeService);
private readonly provisionNoticeService = inject(SignalServerProvisionNoticeService);
readonly provisionNotice = this.provisionNoticeService.notice;
servers = this.serverDirectory.servers;
activeServers = this.serverDirectory.activeServers;
@@ -135,6 +139,22 @@ export class NetworkSettingsComponent {
}
}
authStatusKey(serverUrl: string): string {
if (this.signalServerAuth.hasValidCredential(serverUrl)) {
return 'settings.network.serverEndpoints.auth.authorized';
}
return 'settings.network.serverEndpoints.auth.needsSignIn';
}
async authorizeServer(serverUrl: string): Promise<void> {
await this.signalServerAuthorize.navigateToAuthorize(serverUrl, '/settings');
}
dismissProvisionNotice(): void {
this.provisionNoticeService.clear();
}
saveConnectionSettings(): void {
localStorage.setItem(
STORAGE_KEY_CONNECTION_SETTINGS,

View File

@@ -39,10 +39,7 @@ import {
withUpdatedRole
} from '../../../../domains/access-control';
import { APP_TRANSLATE_IMPORTS, AppI18nService } from '../../../../core/i18n';
import {
SelectOnFocusDirective,
SubmitOnEnterDirective
} from '../../../../shared/directives';
import { SelectOnFocusDirective, SubmitOnEnterDirective } from '../../../../shared/directives';
function upsertRoleChannelOverride(
overrides: readonly ChannelPermissionOverride[] | undefined,

View File

@@ -26,10 +26,7 @@ import { ConfirmDialogComponent } from '../../../../shared';
import { SettingsModalService } from '../../../../core/services/settings-modal.service';
import { ServerIconImageService } from '../../../../domains/server-directory/infrastructure/services/server-icon-image.service';
import { APP_TRANSLATE_IMPORTS, AppI18nService } from '../../../../core/i18n';
import {
SelectOnFocusDirective,
SubmitOnEnterDirective
} from '../../../../shared/directives';
import { SelectOnFocusDirective, SubmitOnEnterDirective } from '../../../../shared/directives';
@Component({
selector: 'app-server-settings',

View File

@@ -29,10 +29,7 @@ import { VoiceConnectionFacade } from '../../domains/voice-connection';
import { NotificationAudioService, AppSound } from '../../core/services/notification-audio.service';
import { STORAGE_KEY_CONNECTION_SETTINGS, STORAGE_KEY_VOICE_SETTINGS } from '../../core/constants';
import { APP_TRANSLATE_IMPORTS, AppI18nService } from '../../core/i18n';
import {
SelectOnFocusDirective,
SubmitOnEnterDirective
} from '../../shared/directives';
import { SelectOnFocusDirective, SubmitOnEnterDirective } from '../../shared/directives';
@Component({
selector: 'app-settings',