fixed some stuff

This commit is contained in:
Flaminel
2026-02-15 15:43:11 +02:00
parent 49f0ce9969
commit 0be7e125c9
4 changed files with 98 additions and 7 deletions

View File

@@ -1,4 +1,4 @@
import { Component, ChangeDetectionStrategy, inject, signal, computed, viewChild, effect, afterNextRender } from '@angular/core';
import { Component, ChangeDetectionStrategy, inject, signal, computed, viewChild, effect, afterNextRender, OnDestroy } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { ButtonComponent, InputComponent, SpinnerComponent } from '@ui';
@@ -17,7 +17,7 @@ import { QRCodeComponent } from 'angularx-qrcode';
changeDetection: ChangeDetectionStrategy.OnPush,
viewProviders: [provideIcons({ tablerCheck, tablerCopy, tablerShieldLock })],
})
export class SetupComponent {
export class SetupComponent implements OnDestroy {
private readonly auth = inject(AuthService);
private readonly router = inject(Router);
private readonly toast = inject(ToastService);
@@ -181,6 +181,12 @@ export class SetupComponent {
private plexPollTimer: ReturnType<typeof setInterval> | null = null;
ngOnDestroy(): void {
if (this.plexPollTimer) {
clearInterval(this.plexPollTimer);
}
}
private pollPlexPin(): void {
let attempts = 0;
this.plexPollTimer = setInterval(() => {

View File

@@ -77,6 +77,21 @@
@if (newRecoveryCodes().length > 0) {
<div class="recovery-section">
<p class="recovery-title">New Authenticator Setup</p>
<p class="recovery-desc">Scan this QR code with your authenticator app to complete the setup.</p>
<div class="qr-section">
<div class="qr-code-wrapper">
<qrcode [qrdata]="newQrCodeUri()" [width]="200" errorCorrectionLevel="M" [margin]="2" />
</div>
<details class="qr-manual-entry">
<summary>Can't scan? Enter manually</summary>
<div class="qr-manual-content">
<p class="qr-manual-label">Secret key:</p>
<code class="qr-secret">{{ newTotpSecret() }}</code>
</div>
</details>
</div>
<div class="form-divider"></div>
<p class="recovery-title">New Recovery Codes</p>
<p class="recovery-desc">Save these codes in a secure location. Each code can only be used once.</p>
<div class="recovery-codes">

View File

@@ -42,6 +42,58 @@
}
}
// QR code (2FA regeneration)
.qr-section {
margin-bottom: var(--space-4);
}
.qr-code-wrapper {
display: flex;
justify-content: center;
margin-bottom: var(--space-4);
qrcode {
background: #ffffff;
padding: var(--space-3);
border-radius: var(--radius-lg);
}
}
.qr-manual-entry {
font-size: var(--font-size-sm);
color: var(--text-secondary);
summary {
cursor: pointer;
text-align: center;
margin-bottom: var(--space-2);
&:hover {
color: var(--text-primary);
}
}
}
.qr-manual-content {
background: var(--surface-secondary);
border-radius: var(--radius-md);
padding: var(--space-3);
text-align: center;
}
.qr-manual-label {
font-size: var(--font-size-xs);
color: var(--text-secondary);
margin-bottom: var(--space-1);
}
.qr-secret {
font-size: var(--font-size-sm);
color: var(--text-primary);
font-family: monospace;
word-break: break-all;
}
// Recovery codes
.recovery-section {
margin-top: var(--space-2);

View File

@@ -1,4 +1,4 @@
import { Component, ChangeDetectionStrategy, inject, signal, computed, OnInit } from '@angular/core';
import { Component, ChangeDetectionStrategy, inject, signal, computed, OnInit, OnDestroy } from '@angular/core';
import { PageHeaderComponent } from '@layout/page-header/page-header.component';
import {
CardComponent, ButtonComponent, InputComponent, SpinnerComponent,
@@ -8,19 +8,20 @@ import { AccountApi, AccountInfo } from '@core/api/account.api';
import { ToastService } from '@core/services/toast.service';
import { ConfirmService } from '@core/services/confirm.service';
import { DeferredLoader } from '@shared/utils/loading.util';
import { QRCodeComponent } from 'angularx-qrcode';
@Component({
selector: 'app-account-settings',
standalone: true,
imports: [
PageHeaderComponent, CardComponent, ButtonComponent, InputComponent,
SpinnerComponent, EmptyStateComponent, LoadingStateComponent,
SpinnerComponent, EmptyStateComponent, LoadingStateComponent, QRCodeComponent,
],
templateUrl: './account-settings.component.html',
styleUrl: './account-settings.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AccountSettingsComponent implements OnInit {
export class AccountSettingsComponent implements OnInit, OnDestroy {
private readonly api = inject(AccountApi);
private readonly toast = inject(ToastService);
private readonly confirmService = inject(ConfirmService);
@@ -55,6 +56,8 @@ export class AccountSettingsComponent implements OnInit {
readonly twoFaCode = signal('');
readonly regenerating2fa = signal(false);
readonly newRecoveryCodes = signal<string[]>([]);
readonly newQrCodeUri = signal('');
readonly newTotpSecret = signal('');
// API key
readonly apiKey = signal('');
@@ -70,6 +73,12 @@ export class AccountSettingsComponent implements OnInit {
this.loadAccount();
}
ngOnDestroy(): void {
if (this.plexPollTimer) {
clearInterval(this.plexPollTimer);
}
}
private loadAccount(): void {
this.loader.start();
this.api.getInfo().subscribe({
@@ -132,12 +141,14 @@ export class AccountSettingsComponent implements OnInit {
this.regenerating2fa.set(true);
this.api.regenerate2fa({
currentPassword: this.twoFaPassword(),
password: this.twoFaPassword(),
totpCode: this.twoFaCode(),
}).subscribe({
next: (result) => {
this.newRecoveryCodes.set(result.recoveryCodes);
this.toast.success('2FA regenerated. Save your new recovery codes!');
this.newQrCodeUri.set(result.qrCodeUri);
this.newTotpSecret.set(result.secret);
this.toast.success('2FA regenerated. Scan the QR code and save your recovery codes!');
this.twoFaPassword.set('');
this.twoFaCode.set('');
this.regenerating2fa.set(false);
@@ -157,6 +168,8 @@ export class AccountSettingsComponent implements OnInit {
dismissRecoveryCodes(): void {
this.newRecoveryCodes.set([]);
this.newQrCodeUri.set('');
this.newTotpSecret.set('');
}
// API key
@@ -240,6 +253,11 @@ export class AccountSettingsComponent implements OnInit {
this.loadAccount();
}
},
error: () => {
clearInterval(this.plexPollTimer!);
this.plexLinking.set(false);
this.toast.error('Plex linking failed');
},
});
}, 2000);
}