fix: Fix multiple bugs with new authentication flow
This commit is contained in:
@@ -21,12 +21,15 @@
|
||||
<input
|
||||
#searchInput
|
||||
type="text"
|
||||
appAutoFocus
|
||||
appSelectOnFocus
|
||||
appSubmitOnEnter
|
||||
(submitOnEnter)="submitSearch()"
|
||||
[attr.aria-label]="'dashboard.searchAriaLabel' | translate"
|
||||
class="h-12 w-full min-w-0 rounded-xl border border-border bg-secondary py-2 pl-11 pr-4 text-base text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary sm:pr-20"
|
||||
[placeholder]="isMobile() ? ('dashboard.searchPlaceholderMobile' | translate) : ('dashboard.searchPlaceholderDesktop' | translate)"
|
||||
[ngModel]="searchQuery()"
|
||||
(ngModelChange)="onSearchChange($event)"
|
||||
(keydown.enter)="submitSearch()"
|
||||
/>
|
||||
<kbd
|
||||
class="pointer-events-none absolute right-3 top-1/2 hidden -translate-y-1/2 items-center gap-1 rounded border border-border bg-card px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground sm:flex"
|
||||
|
||||
@@ -46,6 +46,11 @@ import { FriendButtonComponent } from '../../domains/direct-message/feature/frie
|
||||
import { UserAvatarComponent } from '../../shared/components/user-avatar/user-avatar.component';
|
||||
import { parseInviteQuery } from './invite-query.util';
|
||||
import { AppI18nService, APP_TRANSLATE_IMPORTS } from '../../core/i18n';
|
||||
import {
|
||||
AutoFocusDirective,
|
||||
SelectOnFocusDirective,
|
||||
SubmitOnEnterDirective
|
||||
} from '../../shared/directives';
|
||||
|
||||
/** Maximum quick-search rows shown per group on the dashboard. */
|
||||
const QUICK_RESULT_LIMIT = 5;
|
||||
@@ -72,6 +77,9 @@ const RECENT_SEARCHES_STORAGE_KEY = 'metoyou_dashboard_recent_searches';
|
||||
NgIcon,
|
||||
FriendButtonComponent,
|
||||
UserAvatarComponent,
|
||||
AutoFocusDirective,
|
||||
SelectOnFocusDirective,
|
||||
SubmitOnEnterDirective,
|
||||
...APP_TRANSLATE_IMPORTS
|
||||
],
|
||||
viewProviders: [
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
[class.shadow-[0_0_0_6px_rgba(16,185,129,0.12)]]="speaking() && compact()"
|
||||
[class.shadow-[0_0_0_8px_rgba(16,185,129,0.12)]]="speaking() && !compact()"
|
||||
[class.ring-border]="!speaking()"
|
||||
[class.opacity-55]="!connected()"
|
||||
[class.opacity-55]="!connected() || passive()"
|
||||
>
|
||||
@if (user().avatarUrl) {
|
||||
<img
|
||||
|
||||
@@ -18,6 +18,7 @@ export class PrivateCallParticipantCardComponent {
|
||||
readonly speaking = input.required<boolean>();
|
||||
readonly issueLabel = input<string | null>(null);
|
||||
readonly compact = input(false);
|
||||
readonly passive = input(false);
|
||||
|
||||
avatarSize(): string {
|
||||
return this.compact() ? '5.75rem' : 'clamp(6.5rem, 38vw, 13rem)';
|
||||
|
||||
@@ -148,6 +148,7 @@
|
||||
[connected]="isParticipantConnected(user)"
|
||||
[speaking]="isSpeaking(user)"
|
||||
[issueLabel]="participantIssueLabel(user)"
|
||||
[passive]="isPassiveCallParticipant(user)"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
@@ -164,6 +165,7 @@
|
||||
[connected]="isParticipantConnected(user)"
|
||||
[speaking]="isSpeaking(user)"
|
||||
[issueLabel]="participantIssueLabel(user)"
|
||||
[passive]="isPassiveCallParticipant(user)"
|
||||
[compact]="true"
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -44,6 +44,11 @@ import {
|
||||
import { loadVoiceSettingsFromStorage, saveVoiceSettingsToStorage } from '../../domains/voice-session';
|
||||
import { ScreenShareQualityDialogComponent } from '../../shared';
|
||||
import { ViewportService } from '../../core/platform';
|
||||
import { RealtimeSessionFacade } from '../../core/realtime';
|
||||
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';
|
||||
@@ -85,6 +90,7 @@ export class PrivateCallComponent {
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
private readonly store = inject(Store);
|
||||
private readonly calls = inject(DirectCallService);
|
||||
private readonly realtime = inject(RealtimeSessionFacade);
|
||||
private readonly voice = inject(VoiceConnectionFacade);
|
||||
private readonly voiceActivity = inject(VoiceActivityService);
|
||||
private readonly playback = inject(VoicePlaybackService);
|
||||
@@ -437,18 +443,38 @@ export class PrivateCallComponent {
|
||||
isParticipantConnected(user: User): boolean {
|
||||
const session = this.session();
|
||||
const userId = this.userKey(user);
|
||||
const current = this.currentUser();
|
||||
|
||||
if (!session) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
!!session.participants[userId]?.joined ||
|
||||
!!(user.voiceState?.isConnected && user.voiceState.roomId === session.callId && user.voiceState.serverId === session.callId)
|
||||
const inCallVoice = !!(
|
||||
user.voiceState?.isConnected
|
||||
&& user.voiceState.roomId === session.callId
|
||||
&& user.voiceState.serverId === session.callId
|
||||
);
|
||||
const isSelf = !!current && (user.id === current.id || user.oderId === current.oderId);
|
||||
|
||||
if (isSelf && inCallVoice) {
|
||||
return isLocalVoiceOwner(user.voiceState, this.realtime.getClientInstanceId());
|
||||
}
|
||||
|
||||
return !!session.participants[userId]?.joined || inCallVoice;
|
||||
}
|
||||
|
||||
isPassiveCallParticipant(user: User): boolean {
|
||||
const current = this.currentUser();
|
||||
const isSelf = !!current && (user.id === current.id || user.oderId === current.oderId);
|
||||
|
||||
return isSelf && isVoiceOnAnotherClient(user.voiceState, this.realtime.getClientInstanceId());
|
||||
}
|
||||
|
||||
participantIssueLabel(user: User): string | null {
|
||||
if (this.isPassiveCallParticipant(user)) {
|
||||
return this.i18n.instant('call.private.voiceOnOtherDevice');
|
||||
}
|
||||
|
||||
return this.isParticipantConnected(user) ? null : this.i18n.instant('call.private.waiting');
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,8 @@
|
||||
<input
|
||||
#renameInput
|
||||
type="text"
|
||||
appAutoFocus
|
||||
appSelectOnFocus
|
||||
[value]="ch.name"
|
||||
[class.border-destructive]="renamingChannelId() === ch.id && !!channelNameError()"
|
||||
[title]="renamingChannelId() === ch.id ? (channelNameError() ? (channelNameError()! | translate) : '') : ''"
|
||||
@@ -173,7 +175,7 @@
|
||||
(contextmenu)="openChannelContextMenu($event, ch)"
|
||||
[class.bg-secondary]="isCurrentRoom(ch.id)"
|
||||
[disabled]="!voiceEnabled()"
|
||||
[title]="isCurrentRoom(ch.id) ? ('room.panel.openStreamWorkspace' | translate) : ('room.panel.joinVoiceChannel' | translate)"
|
||||
[title]="voiceChannelActionLabel(ch.id)"
|
||||
data-channel-type="voice"
|
||||
[attr.data-channel-name]="ch.name"
|
||||
>
|
||||
@@ -186,6 +188,8 @@
|
||||
<input
|
||||
#renameInput
|
||||
type="text"
|
||||
appAutoFocus
|
||||
appSelectOnFocus
|
||||
[value]="ch.name"
|
||||
[class.border-destructive]="renamingChannelId() === ch.id && !!channelNameError()"
|
||||
[title]="renamingChannelId() === ch.id ? (channelNameError() ? (channelNameError()! | translate) : '') : ''"
|
||||
@@ -205,6 +209,10 @@
|
||||
<span class="rounded-full bg-primary/15 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-primary">
|
||||
{{ isVoiceWorkspaceExpanded() ? ('room.panel.open' | translate) : ('room.panel.view' | translate) }}
|
||||
</span>
|
||||
} @else if (isPassiveInVoiceRoom(ch.id)) {
|
||||
<span class="rounded-full bg-muted px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground">
|
||||
{{ 'room.panel.takeOverVoice' | translate }}
|
||||
</span>
|
||||
} @else if (voiceOccupancy(ch.id) > 0) {
|
||||
<span class="text-xs text-muted-foreground">{{ voiceOccupancy(ch.id) }}</span>
|
||||
}
|
||||
@@ -220,6 +228,7 @@
|
||||
appThemeNode="roomVoiceUserItem"
|
||||
class="flex items-center gap-2 rounded-md px-2 py-1.5 transition-colors hover:bg-secondary/50"
|
||||
[class.cursor-pointer]="canDragVoiceUser(u)"
|
||||
[class.opacity-50]="isPassiveVoiceUser(u)"
|
||||
[class.opacity-60]="draggedVoiceUserId() === (u.id || u.oderId)"
|
||||
[draggable]="canDragVoiceUser(u)"
|
||||
(dragstart)="onVoiceUserDragStart($event, u)"
|
||||
@@ -368,6 +377,7 @@
|
||||
<h4 class="text-xs uppercase tracking-wide text-muted-foreground font-medium mb-2 px-1">{{ 'room.panel.you' | translate }}</h4>
|
||||
<div
|
||||
class="flex items-center gap-2 rounded-md bg-secondary/60 px-3 py-2 hover:bg-secondary/80 transition-colors cursor-pointer"
|
||||
[class.opacity-50]="isPassiveVoiceClient()"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
(click)="openProfileCard($event, currentUser()!, true); $event.stopPropagation()"
|
||||
@@ -413,7 +423,11 @@
|
||||
name="lucideMic"
|
||||
class="w-2.5 h-2.5"
|
||||
/>
|
||||
{{ 'room.panel.inVoice' | translate }}
|
||||
@if (isPassiveVoiceClient()) {
|
||||
{{ 'room.panel.voiceOnOtherDevice' | translate }}
|
||||
} @else {
|
||||
{{ 'room.panel.inVoice' | translate }}
|
||||
}
|
||||
</p>
|
||||
}
|
||||
@if (currentUser() && isUserStreaming(currentUser()!.oderId || currentUser()!.id)) {
|
||||
@@ -763,7 +777,6 @@
|
||||
class="w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary"
|
||||
[class.border-destructive]="!!channelNameError()"
|
||||
(ngModelChange)="clearChannelNameError()"
|
||||
(keydown.enter)="confirmCreateChannel()"
|
||||
/>
|
||||
@if (channelNameError()) {
|
||||
<p class="mt-2 text-sm text-destructive">{{ channelNameError()! | translate }}</p>
|
||||
|
||||
@@ -52,7 +52,12 @@ import {
|
||||
VoiceConnectionFacade,
|
||||
VoiceConnectivityHealthService
|
||||
} from '../../../domains/voice-connection';
|
||||
import { VoiceSessionFacade, VoiceWorkspaceService } from '../../../domains/voice-session';
|
||||
import {
|
||||
VoiceSessionFacade,
|
||||
VoiceWorkspaceService,
|
||||
isLocalVoiceOwner,
|
||||
isVoiceOnAnotherClient
|
||||
} from '../../../domains/voice-session';
|
||||
import { DirectMessageService } from '../../../domains/direct-message';
|
||||
import { DirectCallService } from '../../../domains/direct-call';
|
||||
import { VoicePlaybackService } from '../../../domains/voice-connection';
|
||||
@@ -88,6 +93,7 @@ import {
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { visibilityAwareInterval$ } from '../../../shared/rxjs';
|
||||
import { APP_TRANSLATE_IMPORTS, AppI18nService } from '../../../core/i18n';
|
||||
import { AutoFocusDirective, SelectOnFocusDirective } from '../../../shared/directives';
|
||||
|
||||
type PanelMode = 'channels' | 'users';
|
||||
|
||||
@@ -109,6 +115,8 @@ const SKELETON_REVEAL_DELAY_MS = 180;
|
||||
ThemeNodeDirective,
|
||||
SkeletonComponent,
|
||||
SkeletonListComponent,
|
||||
AutoFocusDirective,
|
||||
SelectOnFocusDirective,
|
||||
...APP_TRANSLATE_IMPORTS
|
||||
],
|
||||
viewProviders: [
|
||||
@@ -689,7 +697,13 @@ export class RoomsSidePanelComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
private openExistingVoiceWorkspace(room: Room | null, current: User | null, roomId: string): boolean {
|
||||
if (!room || !current?.voiceState?.isConnected || current.voiceState.roomId !== roomId || current.voiceState.serverId !== room.id) {
|
||||
if (
|
||||
!room
|
||||
|| !current?.voiceState?.isConnected
|
||||
|| current.voiceState.roomId !== roomId
|
||||
|| current.voiceState.serverId !== room.id
|
||||
|| !isLocalVoiceOwner(current.voiceState, this.realtime.getClientInstanceId())
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -697,6 +711,47 @@ export class RoomsSidePanelComponent implements OnDestroy {
|
||||
return true;
|
||||
}
|
||||
|
||||
isPassiveInVoiceRoom(roomId: string): boolean {
|
||||
const current = this.currentUser();
|
||||
const room = this.currentRoom();
|
||||
|
||||
return !!current?.voiceState?.isConnected
|
||||
&& current.voiceState.roomId === roomId
|
||||
&& current.voiceState.serverId === room?.id
|
||||
&& isVoiceOnAnotherClient(current.voiceState, this.realtime.getClientInstanceId());
|
||||
}
|
||||
|
||||
isPassiveVoiceClient(): boolean {
|
||||
const current = this.currentUser();
|
||||
|
||||
return isVoiceOnAnotherClient(current?.voiceState, this.realtime.getClientInstanceId());
|
||||
}
|
||||
|
||||
isPassiveVoiceUser(user: User | null): boolean {
|
||||
const current = this.currentUser();
|
||||
|
||||
if (!user || !current) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (user.id === current.id || user.oderId === current.oderId)
|
||||
&& this.isPassiveVoiceClient();
|
||||
}
|
||||
|
||||
voiceChannelActionLabel(roomId: string): string {
|
||||
if (this.isCurrentRoom(roomId)) {
|
||||
return this.isVoiceWorkspaceExpanded()
|
||||
? this.appI18n.instant('room.panel.open')
|
||||
: this.appI18n.instant('room.panel.view');
|
||||
}
|
||||
|
||||
if (this.isPassiveInVoiceRoom(roomId)) {
|
||||
return this.appI18n.instant('room.panel.takeOverVoice');
|
||||
}
|
||||
|
||||
return this.appI18n.instant('room.panel.joinVoiceChannel');
|
||||
}
|
||||
|
||||
private canJoinRequestedVoiceRoom(room: Room, current: User | null, roomId: string): boolean {
|
||||
return !current || resolveRoomPermission(room, current, 'joinVoice', roomId);
|
||||
}
|
||||
@@ -737,9 +792,19 @@ export class RoomsSidePanelComponent implements OnDestroy {
|
||||
this.directCalls.leaveCurrentJoinedCall();
|
||||
this.prepareVoiceJoin(room, current ?? null);
|
||||
|
||||
this.enableVoiceForJoin(room, current ?? null, roomId)
|
||||
.then(() => this.onVoiceJoinSucceeded(roomId, room, current ?? null))
|
||||
.catch((error) => this.handleVoiceJoinFailure(error));
|
||||
const startJoin = () => {
|
||||
this.enableVoiceForJoin(room, current ?? null, roomId)
|
||||
.then(() => this.onVoiceJoinSucceeded(roomId, room, current ?? null))
|
||||
.catch((error) => this.handleVoiceJoinFailure(error));
|
||||
};
|
||||
|
||||
if (this.isPassiveInVoiceRoom(roomId) || this.isPassiveVoiceClient()) {
|
||||
this.realtime.requestVoiceClientTakeover();
|
||||
window.setTimeout(startJoin, 300);
|
||||
return;
|
||||
}
|
||||
|
||||
startJoin();
|
||||
}
|
||||
|
||||
private onVoiceJoinSucceeded(roomId: string, room: Room, current: User | null): void {
|
||||
@@ -786,7 +851,8 @@ export class RoomsSidePanelComponent implements OnDestroy {
|
||||
isMuted: current.voiceState?.isMuted ?? false,
|
||||
isDeafened: current.voiceState?.isDeafened ?? false,
|
||||
roomId,
|
||||
serverId: room.id
|
||||
serverId: room.id,
|
||||
clientInstanceId: this.realtime.getClientInstanceId()
|
||||
}
|
||||
})
|
||||
);
|
||||
@@ -797,6 +863,8 @@ export class RoomsSidePanelComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
private broadcastVoiceConnected(roomId: string, room: Room, current: User | null): void {
|
||||
const clientInstanceId = this.realtime.getClientInstanceId();
|
||||
|
||||
this.voiceConnection.broadcastMessage({
|
||||
type: 'voice-state',
|
||||
oderId: current?.oderId || current?.id,
|
||||
@@ -806,7 +874,8 @@ export class RoomsSidePanelComponent implements OnDestroy {
|
||||
isMuted: current?.voiceState?.isMuted ?? false,
|
||||
isDeafened: current?.voiceState?.isDeafened ?? false,
|
||||
roomId,
|
||||
serverId: room.id
|
||||
serverId: room.id,
|
||||
clientInstanceId
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -851,7 +920,8 @@ export class RoomsSidePanelComponent implements OnDestroy {
|
||||
isMuted: false,
|
||||
isDeafened: false,
|
||||
roomId: undefined,
|
||||
serverId: undefined
|
||||
serverId: undefined,
|
||||
clientInstanceId: undefined
|
||||
}
|
||||
})
|
||||
);
|
||||
@@ -873,7 +943,8 @@ export class RoomsSidePanelComponent implements OnDestroy {
|
||||
isMuted: false,
|
||||
isDeafened: false,
|
||||
roomId: previousVoiceState?.roomId,
|
||||
serverId: previousVoiceState?.serverId
|
||||
serverId: previousVoiceState?.serverId,
|
||||
clientInstanceId: undefined
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1110,7 +1181,12 @@ export class RoomsSidePanelComponent implements OnDestroy {
|
||||
const me = this.currentUser();
|
||||
const room = this.currentRoom();
|
||||
|
||||
return !!(me?.voiceState?.isConnected && me.voiceState?.roomId === roomId && me.voiceState?.serverId === room?.id);
|
||||
return !!(
|
||||
me?.voiceState?.isConnected
|
||||
&& me.voiceState.roomId === roomId
|
||||
&& me.voiceState.serverId === room?.id
|
||||
&& isLocalVoiceOwner(me.voiceState, this.realtime.getClientInstanceId())
|
||||
);
|
||||
}
|
||||
|
||||
voiceEnabled(): boolean {
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
} from 'rxjs';
|
||||
|
||||
import { Room, User } from '../../../shared-kernel';
|
||||
import { buildLoginReturnQueryParams } from '../../../domains/authentication/domain/logic/auth-navigation.rules';
|
||||
import { UserBarComponent } from '../../../domains/authentication/feature/user-bar/user-bar.component';
|
||||
import { VoiceSessionFacade } from '../../../domains/voice-session';
|
||||
import { selectSavedRooms, selectCurrentRoom } from '../../../store/rooms/rooms.selectors';
|
||||
@@ -276,7 +277,9 @@ export class ServersRailComponent {
|
||||
const currentUserId = localStorage.getItem('metoyou_currentUserId');
|
||||
|
||||
if (!currentUserId) {
|
||||
this.router.navigate(['/login']);
|
||||
this.router.navigate(['/login'], {
|
||||
queryParams: buildLoginReturnQueryParams(this.router.url)
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -154,11 +154,13 @@
|
||||
<div class="flex items-center gap-2">
|
||||
<input
|
||||
type="text"
|
||||
appSelectOnFocus
|
||||
appSubmitOnEnter
|
||||
(submitOnEnter)="addIgnoredProcess()"
|
||||
class="flex-1 rounded-md border border-border bg-background px-3 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary"
|
||||
[placeholder]="'settings.general.gameDetection.processPlaceholder' | translate"
|
||||
[value]="ignoredProcessDraft()"
|
||||
(input)="onIgnoredProcessDraftChange($event)"
|
||||
(keydown.enter)="addIgnoredProcess()"
|
||||
[attr.aria-label]="'settings.general.gameDetection.processAria' | translate"
|
||||
/>
|
||||
<button
|
||||
|
||||
@@ -14,11 +14,21 @@ 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';
|
||||
|
||||
@Component({
|
||||
selector: 'app-general-settings',
|
||||
standalone: true,
|
||||
imports: [CommonModule, NgIcon, ...APP_TRANSLATE_IMPORTS],
|
||||
imports: [
|
||||
CommonModule,
|
||||
NgIcon,
|
||||
SelectOnFocusDirective,
|
||||
SubmitOnEnterDirective,
|
||||
...APP_TRANSLATE_IMPORTS
|
||||
],
|
||||
viewProviders: [
|
||||
provideIcons({
|
||||
lucidePower
|
||||
|
||||
@@ -113,6 +113,10 @@
|
||||
</select>
|
||||
<input
|
||||
type="text"
|
||||
appAutoFocus
|
||||
appSelectOnFocus
|
||||
appSubmitOnEnter
|
||||
(submitOnEnter)="addEntry()"
|
||||
[(ngModel)]="newUrl"
|
||||
data-testid="ice-url-input"
|
||||
[placeholder]="(newType === 'stun' ? 'settings.network.ice.stunPlaceholder' : 'settings.network.ice.turnPlaceholder') | translate"
|
||||
@@ -123,6 +127,7 @@
|
||||
<div class="flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
appSelectOnFocus
|
||||
[(ngModel)]="newUsername"
|
||||
data-testid="ice-username-input"
|
||||
[placeholder]="'settings.network.ice.username' | translate"
|
||||
@@ -130,6 +135,8 @@
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
appSubmitOnEnter
|
||||
(submitOnEnter)="addEntry()"
|
||||
[(ngModel)]="newCredential"
|
||||
data-testid="ice-credential-input"
|
||||
[placeholder]="'settings.network.ice.credential' | translate"
|
||||
|
||||
@@ -18,6 +18,11 @@ import {
|
||||
|
||||
import { IceServerSettingsService, IceServerEntry } from '../../../../infrastructure/realtime/ice-server-settings.service';
|
||||
import { APP_TRANSLATE_IMPORTS, AppI18nService } from '../../../../core/i18n';
|
||||
import {
|
||||
AutoFocusDirective,
|
||||
SelectOnFocusDirective,
|
||||
SubmitOnEnterDirective
|
||||
} from '../../../../shared/directives';
|
||||
|
||||
@Component({
|
||||
selector: 'app-ice-server-settings',
|
||||
@@ -29,6 +34,9 @@ import { APP_TRANSLATE_IMPORTS, AppI18nService } from '../../../../core/i18n';
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
NgIcon,
|
||||
AutoFocusDirective,
|
||||
SelectOnFocusDirective,
|
||||
SubmitOnEnterDirective,
|
||||
...APP_TRANSLATE_IMPORTS
|
||||
],
|
||||
viewProviders: [
|
||||
|
||||
@@ -126,12 +126,16 @@
|
||||
<div class="flex-1 space-y-1.5">
|
||||
<input
|
||||
type="text"
|
||||
appSelectOnFocus
|
||||
[(ngModel)]="newServerName"
|
||||
[placeholder]="'settings.network.serverEndpoints.serverNamePlaceholderShort' | translate"
|
||||
class="w-full px-3 py-1.5 bg-secondary rounded-lg border border-border text-foreground text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary"
|
||||
/>
|
||||
<input
|
||||
type="url"
|
||||
appSelectOnFocus
|
||||
appSubmitOnEnter
|
||||
(submitOnEnter)="addServer()"
|
||||
[(ngModel)]="newServerUrl"
|
||||
[placeholder]="'settings.network.serverEndpoints.serverUrlPlaceholder' | translate"
|
||||
class="w-full px-3 py-1.5 bg-secondary rounded-lg border border-border text-foreground text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary"
|
||||
|
||||
@@ -22,6 +22,10 @@ import { ServerDirectoryFacade } from '../../../../domains/server-directory';
|
||||
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';
|
||||
|
||||
@Component({
|
||||
selector: 'app-network-settings',
|
||||
@@ -31,6 +35,8 @@ import { APP_TRANSLATE_IMPORTS, AppI18nService } from '../../../../core/i18n';
|
||||
FormsModule,
|
||||
NgIcon,
|
||||
IceServerSettingsComponent,
|
||||
SelectOnFocusDirective,
|
||||
SubmitOnEnterDirective,
|
||||
...APP_TRANSLATE_IMPORTS
|
||||
],
|
||||
viewProviders: [
|
||||
|
||||
@@ -110,6 +110,9 @@
|
||||
}}</span>
|
||||
<input
|
||||
type="text"
|
||||
appSelectOnFocus
|
||||
appSubmitOnEnter
|
||||
(submitOnEnter)="saveRoleDetails()"
|
||||
[ngModel]="roleName"
|
||||
(ngModelChange)="roleName = $event"
|
||||
[disabled]="!canEditSelectedRoleMetadata()"
|
||||
|
||||
@@ -39,6 +39,10 @@ import {
|
||||
withUpdatedRole
|
||||
} from '../../../../domains/access-control';
|
||||
import { APP_TRANSLATE_IMPORTS, AppI18nService } from '../../../../core/i18n';
|
||||
import {
|
||||
SelectOnFocusDirective,
|
||||
SubmitOnEnterDirective
|
||||
} from '../../../../shared/directives';
|
||||
|
||||
function upsertRoleChannelOverride(
|
||||
overrides: readonly ChannelPermissionOverride[] | undefined,
|
||||
@@ -75,6 +79,8 @@ function upsertRoleChannelOverride(
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
NgIcon,
|
||||
SelectOnFocusDirective,
|
||||
SubmitOnEnterDirective,
|
||||
...APP_TRANSLATE_IMPORTS
|
||||
],
|
||||
viewProviders: [
|
||||
|
||||
@@ -82,6 +82,9 @@
|
||||
[(ngModel)]="roomName"
|
||||
[readOnly]="!isAdmin()"
|
||||
id="room-name"
|
||||
appSelectOnFocus
|
||||
appSubmitOnEnter
|
||||
(submitOnEnter)="saveServerSettings()"
|
||||
class="w-full px-3 py-2 bg-secondary rounded-lg border border-border text-foreground text-sm focus:outline-none focus:ring-2 focus:ring-primary"
|
||||
[class.opacity-60]="!isAdmin()"
|
||||
[class.cursor-not-allowed]="!isAdmin()"
|
||||
|
||||
@@ -26,6 +26,10 @@ 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';
|
||||
|
||||
@Component({
|
||||
selector: 'app-server-settings',
|
||||
@@ -35,6 +39,8 @@ import { APP_TRANSLATE_IMPORTS, AppI18nService } from '../../../../core/i18n';
|
||||
FormsModule,
|
||||
NgIcon,
|
||||
ConfirmDialogComponent,
|
||||
SelectOnFocusDirective,
|
||||
SubmitOnEnterDirective,
|
||||
...APP_TRANSLATE_IMPORTS
|
||||
],
|
||||
viewProviders: [
|
||||
|
||||
@@ -163,12 +163,16 @@
|
||||
<div class="flex-1 space-y-2">
|
||||
<input
|
||||
type="text"
|
||||
appSelectOnFocus
|
||||
[(ngModel)]="newServerName"
|
||||
[placeholder]="'settings.network.serverEndpoints.serverNamePlaceholder' | translate"
|
||||
class="w-full px-3 py-2 bg-secondary rounded-lg border border-border text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary"
|
||||
/>
|
||||
<input
|
||||
type="url"
|
||||
appSelectOnFocus
|
||||
appSubmitOnEnter
|
||||
(submitOnEnter)="addServer()"
|
||||
[(ngModel)]="newServerUrl"
|
||||
[placeholder]="'settings.network.serverEndpoints.serverUrlPlaceholder' | translate"
|
||||
class="w-full px-3 py-2 bg-secondary rounded-lg border border-border text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary"
|
||||
|
||||
@@ -29,6 +29,10 @@ 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';
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings',
|
||||
@@ -37,6 +41,8 @@ import { APP_TRANSLATE_IMPORTS, AppI18nService } from '../../core/i18n';
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
NgIcon,
|
||||
SelectOnFocusDirective,
|
||||
SubmitOnEnterDirective,
|
||||
...APP_TRANSLATE_IMPORTS
|
||||
],
|
||||
viewProviders: [
|
||||
|
||||
@@ -40,6 +40,7 @@ import { RealtimeSessionFacade } from '../../../core/realtime';
|
||||
import { ServerDirectoryFacade } from '../../../domains/server-directory';
|
||||
import { PlatformService } from '../../../core/platform';
|
||||
import { clearStoredCurrentUserId } from '../../../core/storage/current-user-storage';
|
||||
import { buildLoginReturnQueryParams } from '../../../domains/authentication/domain/logic/auth-navigation.rules';
|
||||
import { SettingsModalService } from '../../../core/services/settings-modal.service';
|
||||
import { LeaveServerDialogComponent, ModalBackdropComponent } from '../../../shared';
|
||||
import { Room, type PluginRequirementSummary } from '../../../shared-kernel';
|
||||
@@ -211,7 +212,9 @@ export class TitleBarComponent {
|
||||
|
||||
/** Navigate to the login page. */
|
||||
goLogin() {
|
||||
this.router.navigate(['/login']);
|
||||
this.router.navigate(['/login'], {
|
||||
queryParams: buildLoginReturnQueryParams(this.router.url)
|
||||
});
|
||||
}
|
||||
|
||||
openPluginStore(): void {
|
||||
|
||||
Reference in New Issue
Block a user