mirror of
https://github.com/Cleanuparr/Cleanuparr.git
synced 2026-02-20 07:46:34 -05:00
fixed some stuff
This commit is contained in:
@@ -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(() => {
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user