Sheesh more dialog hangers on

This commit is contained in:
shamoon
2026-07-08 07:52:28 -07:00
parent cdfb67f71b
commit e3c4c0ef7e
5 changed files with 211 additions and 55 deletions
@@ -4,7 +4,7 @@ import {
moveItemInArray,
} from '@angular/cdk/drag-drop'
import { AsyncPipe } from '@angular/common'
import { Component, OnInit, inject } from '@angular/core'
import { Component, OnInit, inject, signal } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { takeUntil } from 'rxjs'
@@ -36,15 +36,47 @@ export class MergeConfirmDialogComponent
private documentService = inject(DocumentService)
private permissionService = inject(PermissionsService)
public documentIDs: number[] = []
public archiveFallback: boolean = false
public deleteOriginals: boolean = false
private _documents: Document[] = []
get documents(): Document[] {
return this._documents
private documentIDsSignal = signal<number[]>([])
private archiveFallbackSignal = signal(false)
private deleteOriginalsSignal = signal(false)
private documentsSignal = signal<Document[]>([])
private metadataDocumentIDSignal = signal(-1)
public get documentIDs(): number[] {
return this.documentIDsSignal()
}
public metadataDocumentID: number = -1
public set documentIDs(documentIDs: number[]) {
this.documentIDsSignal.set(documentIDs)
}
public get archiveFallback(): boolean {
return this.archiveFallbackSignal()
}
public set archiveFallback(archiveFallback: boolean) {
this.archiveFallbackSignal.set(archiveFallback)
}
public get deleteOriginals(): boolean {
return this.deleteOriginalsSignal()
}
public set deleteOriginals(deleteOriginals: boolean) {
this.deleteOriginalsSignal.set(deleteOriginals)
}
get documents(): Document[] {
return this.documentsSignal()
}
public get metadataDocumentID(): number {
return this.metadataDocumentIDSignal()
}
public set metadataDocumentID(metadataDocumentID: number) {
this.metadataDocumentIDSignal.set(metadataDocumentID)
}
constructor() {
super()
@@ -55,12 +87,14 @@ export class MergeConfirmDialogComponent
.getFew(this.documentIDs)
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe((r) => {
this._documents = r.results
this.documentsSignal.set(r.results)
})
}
onDrop(event: CdkDragDrop<number[]>) {
moveItemInArray(this.documentIDs, event.previousIndex, event.currentIndex)
const documentIDs = this.documentIDs.concat()
moveItemInArray(documentIDs, event.previousIndex, event.currentIndex)
this.documentIDs = documentIDs
}
getDocument(documentID: number): Document {
@@ -1,4 +1,4 @@
import { Component, inject } from '@angular/core'
import { Component, inject, signal } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
const SYMBOLS = {
@@ -20,9 +20,24 @@ const SYMBOLS = {
})
export class HotkeyDialogComponent {
activeModal = inject(NgbActiveModal)
private titleSignal = signal($localize`Keyboard shortcuts`)
private hotkeysSignal = signal<Map<string, string>>(new Map())
public title: string = $localize`Keyboard shortcuts`
public hotkeys: Map<string, string> = new Map()
public get title(): string {
return this.titleSignal()
}
public set title(title: string) {
this.titleSignal.set(title)
}
public get hotkeys(): Map<string, string> {
return this.hotkeysSignal()
}
public set hotkeys(hotkeys: Map<string, string>) {
this.hotkeysSignal.set(hotkeys)
}
public close(): void {
this.activeModal.close()
@@ -1,6 +1,6 @@
import { Clipboard } from '@angular/cdk/clipboard'
import { CommonModule } from '@angular/common'
import { Component, Input, inject } from '@angular/core'
import { Component, Input, inject, signal } from '@angular/core'
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { Document } from 'src/app/data/document'
@@ -38,10 +38,27 @@ export class ShareLinkBundleDialogComponent extends ConfirmDialogComponent {
private readonly clipboard = inject(Clipboard)
private readonly toastService = inject(ToastService)
private _documents: Document[] = []
private documentsSignal = signal<Document[]>([])
private selectionCountSignal = signal(0)
private documentPreviewSignal = signal<Document[]>([])
private copiedSignal = signal(false)
get selectionCount(): number {
return this.selectionCountSignal()
}
get documentPreview(): Document[] {
return this.documentPreviewSignal()
}
get copied(): boolean {
return this.copiedSignal()
}
set copied(copied: boolean) {
this.copiedSignal.set(copied)
}
selectionCount = 0
documentPreview: Document[] = []
form: FormGroup = this.formBuilder.group({
shareArchiveVersion: true,
expirationDays: [7],
@@ -51,7 +68,6 @@ export class ShareLinkBundleDialogComponent extends ConfirmDialogComponent {
readonly expirationOptions = SHARE_LINK_EXPIRATION_OPTIONS
createdBundle: ShareLinkBundleSummary | null = null
copied = false
onOpenManage?: () => void
readonly statuses = ShareLinkBundleStatus
@@ -64,15 +80,16 @@ export class ShareLinkBundleDialogComponent extends ConfirmDialogComponent {
@Input()
set documents(docs: Document[]) {
this._documents = docs.concat()
this.selectionCount = this._documents.length
this.documentPreview = this._documents.slice(0, 10)
const documents = docs.concat()
this.documentsSignal.set(documents)
this.selectionCountSignal.set(documents.length)
this.documentPreviewSignal.set(documents.slice(0, 10))
}
submit() {
if (this.createdBundle) return
this.payload = {
document_ids: this._documents.map((doc) => doc.id),
document_ids: this.documentsSignal().map((doc) => doc.id),
file_version: this.form.value.shareArchiveVersion
? FileVersion.Archive
: FileVersion.Original,
@@ -1,5 +1,12 @@
import { Clipboard, ClipboardModule } from '@angular/cdk/clipboard'
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
import {
Component,
Input,
OnDestroy,
OnInit,
inject,
signal,
} from '@angular/core'
import {
NgbActiveModal,
NgbModalModule,
@@ -50,15 +57,40 @@ export class SystemStatusDialogComponent implements OnInit, OnDestroy {
public SystemStatusItemStatus = SystemStatusItemStatus
public PaperlessTaskType = PaperlessTaskType
public status: SystemStatus
private statusSignal = signal<SystemStatus>(undefined)
public frontendVersion: string = environment.version
public versionMismatch: boolean = false
private versionMismatchSignal = signal(false)
public copied: boolean = false
private copiedSignal = signal(false)
private runningTasks: Set<PaperlessTaskType> = new Set()
private runningTasksSignal = signal<Set<PaperlessTaskType>>(new Set())
private unsubscribeNotifier: Subject<any> = new Subject()
@Input()
get status(): SystemStatus {
return this.statusSignal()
}
set status(status: SystemStatus) {
this.statusSignal.set(status)
}
get versionMismatch(): boolean {
return this.versionMismatchSignal()
}
set versionMismatch(versionMismatch: boolean) {
this.versionMismatchSignal.set(versionMismatch)
}
get copied(): boolean {
return this.copiedSignal()
}
set copied(copied: boolean) {
this.copiedSignal.set(copied)
}
get currentUserIsSuperUser(): boolean {
return this.permissionsService.isSuperUser()
}
@@ -68,25 +100,23 @@ export class SystemStatusDialogComponent implements OnInit, OnDestroy {
}
public ngOnInit() {
const status = this.status
this.versionMismatch =
environment.production &&
this.status.pngx_version &&
status.pngx_version &&
this.frontendVersion &&
this.status.pngx_version !== this.frontendVersion
status.pngx_version !== this.frontendVersion
if (this.versionMismatch) {
this.status.pngx_version = `${this.status.pngx_version} (frontend: ${this.frontendVersion})`
this.status = {
...status,
pngx_version: `${status.pngx_version} (frontend: ${this.frontendVersion})`,
}
}
this.status.websocket_connected = this.websocketStatusService.isConnected()
? SystemStatusItemStatus.OK
: SystemStatusItemStatus.ERROR
this.updateWebsocketStatus(this.websocketStatusService.isConnected())
this.websocketStatusService
.onConnectionStatus()
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe((connected) => {
this.status.websocket_connected = connected
? SystemStatusItemStatus.OK
: SystemStatusItemStatus.ERROR
})
.subscribe((connected) => this.updateWebsocketStatus(connected))
}
public close() {
@@ -108,23 +138,26 @@ export class SystemStatusDialogComponent implements OnInit, OnDestroy {
}
public isRunning(taskName: PaperlessTaskType): boolean {
return this.runningTasks.has(taskName)
return this.runningTasksSignal().has(taskName)
}
public runTask(taskName: PaperlessTaskType) {
this.runningTasks.add(taskName)
this.setTaskRunning(taskName, true)
this.toastService.showInfo(`Task ${taskName} started`)
this.tasksService.run(taskName).subscribe({
next: () => {
this.runningTasks.delete(taskName)
this.setTaskRunning(taskName, false)
this.systemStatusService.get().subscribe({
next: (status) => {
Object.assign(this.status, status)
this.status = {
...this.status,
...status,
}
},
})
},
error: (err) => {
this.runningTasks.delete(taskName)
this.setTaskRunning(taskName, false)
this.toastService.showError(
`Failed to start task ${taskName}, see the logs for more details`,
err
@@ -133,6 +166,25 @@ export class SystemStatusDialogComponent implements OnInit, OnDestroy {
})
}
private updateWebsocketStatus(connected: boolean): void {
this.status = {
...this.status,
websocket_connected: connected
? SystemStatusItemStatus.OK
: SystemStatusItemStatus.ERROR,
}
}
private setTaskRunning(taskName: PaperlessTaskType, running: boolean): void {
const runningTasks = new Set(this.runningTasksSignal())
if (running) {
runningTasks.add(taskName)
} else {
runningTasks.delete(taskName)
}
this.runningTasksSignal.set(runningTasks)
}
ngOnDestroy(): void {
this.unsubscribeNotifier.next(this)
this.unsubscribeNotifier.complete()
@@ -1,5 +1,5 @@
import { SlicePipe } from '@angular/common'
import { Component, inject, Input, OnInit } from '@angular/core'
import { Component, inject, Input, OnInit, signal } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import {
NgbActiveModal,
@@ -36,15 +36,50 @@ export class ProcessedMailDialogComponent implements OnInit {
private readonly processedMailService = inject(ProcessedMailService)
private readonly toastService = inject(ToastService)
public processedMails: ProcessedMail[] = []
private ruleSignal = signal<MailRule>(undefined)
private processedMailsSignal = signal<ProcessedMail[]>([])
private loadingSignal = signal(true)
private toggleAllEnabledSignal = signal(false)
private selectedMailIdsSignal = signal<Set<number>>(new Set())
public loading: boolean = true
public toggleAllEnabled: boolean = false
public readonly selectedMailIds: Set<number> = new Set<number>()
public get processedMails(): ProcessedMail[] {
return this.processedMailsSignal()
}
public set processedMails(processedMails: ProcessedMail[]) {
this.processedMailsSignal.set(processedMails)
}
public get loading(): boolean {
return this.loadingSignal()
}
public set loading(loading: boolean) {
this.loadingSignal.set(loading)
}
public get toggleAllEnabled(): boolean {
return this.toggleAllEnabledSignal()
}
public set toggleAllEnabled(toggleAllEnabled: boolean) {
this.toggleAllEnabledSignal.set(toggleAllEnabled)
}
public get selectedMailIds(): Set<number> {
return this.selectedMailIdsSignal()
}
public page: number = 1
@Input() rule: MailRule
@Input()
get rule(): MailRule {
return this.ruleSignal()
}
set rule(rule: MailRule) {
this.ruleSignal.set(rule)
}
ngOnInit(): void {
this.loadProcessedMails()
@@ -75,9 +110,10 @@ export class ProcessedMailDialogComponent implements OnInit {
}
public toggleAll(event: PointerEvent) {
const selectedMailIds = new Set<number>()
if ((event.target as HTMLInputElement).checked) {
this.selectedMailIds.clear()
this.processedMails.forEach((mail) => this.selectedMailIds.add(mail.id))
this.processedMails.forEach((mail) => selectedMailIds.add(mail.id))
this.selectedMailIdsSignal.set(selectedMailIds)
} else {
this.clearSelection()
}
@@ -85,12 +121,14 @@ export class ProcessedMailDialogComponent implements OnInit {
public clearSelection() {
this.toggleAllEnabled = false
this.selectedMailIds.clear()
this.selectedMailIdsSignal.set(new Set())
}
public toggleSelected(mail: ProcessedMail) {
this.selectedMailIds.has(mail.id)
? this.selectedMailIds.delete(mail.id)
: this.selectedMailIds.add(mail.id)
const selectedMailIds = new Set(this.selectedMailIds)
selectedMailIds.has(mail.id)
? selectedMailIds.delete(mail.id)
: selectedMailIds.add(mail.id)
this.selectedMailIdsSignal.set(selectedMailIds)
}
}