diff --git a/src-ui/src/app/components/common/input/abstract-input.ts b/src-ui/src/app/components/common/input/abstract-input.ts index 5e95de0bf..2b20c92df 100644 --- a/src-ui/src/app/components/common/input/abstract-input.ts +++ b/src-ui/src/app/components/common/input/abstract-input.ts @@ -5,6 +5,7 @@ import { Input, OnInit, Output, + signal, ViewChild, } from '@angular/core' import { ControlValueAccessor } from '@angular/forms' @@ -61,7 +62,15 @@ export class AbstractInputComponent implements OnInit, ControlValueAccessor { @Output() removed: EventEmitter> = new EventEmitter() - value: T + private valueSignal = signal(undefined) + + get value(): T { + return this.valueSignal() + } + + set value(value: T) { + this.valueSignal.set(value) + } ngOnInit(): void { this.inputId = uuidv4() diff --git a/src-ui/src/app/components/common/input/tags/tags.component.ts b/src-ui/src/app/components/common/input/tags/tags.component.ts index 6b7d3150f..dbf0fbf32 100644 --- a/src-ui/src/app/components/common/input/tags/tags.component.ts +++ b/src-ui/src/app/components/common/input/tags/tags.component.ts @@ -6,6 +6,7 @@ import { Input, OnInit, Output, + signal, ViewChild, } from '@angular/core' import { @@ -109,7 +110,15 @@ export class TagsComponent implements OnInit, ControlValueAccessor { @ViewChild('tagSelect') select: NgSelectComponent - value: number[] = [] + private valueSignal = signal([]) + + get value(): number[] { + return this.valueSignal() + } + + set value(value: number[]) { + this.valueSignal.set(value) + } tags: Tag[] = [] diff --git a/src-ui/src/app/components/document-detail/document-detail.component.html b/src-ui/src/app/components/document-detail/document-detail.component.html index edcda7ce2..53c0a953b 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.html +++ b/src-ui/src/app/components/document-detail/document-detail.component.html @@ -445,8 +445,7 @@ @if (hasNext()) { - } - @if (!hasNext()) { + } @else { } diff --git a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts index c550534f9..951064e30 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts @@ -853,6 +853,7 @@ describe('DocumentDetailComponent', () => { .find((b) => b.nativeElement.textContent === 'Save & next') ).toBeUndefined() nextSpy.mockReturnValue(true) + component.networkActive = true fixture.detectChanges() expect( fixture.debugElement diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts index a0fee743b..ed7cdea42 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.ts @@ -1,6 +1,13 @@ import { AsyncPipe, NgTemplateOutlet } from '@angular/common' import { HttpClient, HttpResponse } from '@angular/common/http' -import { Component, inject, OnDestroy, OnInit, ViewChild } from '@angular/core' +import { + Component, + inject, + OnDestroy, + OnInit, + signal, + ViewChild, +} from '@angular/core' import { FormArray, FormControl, @@ -227,34 +234,34 @@ export class DocumentDetailComponent expandOriginalMetadata = false expandArchivedMetadata = false - error: any + private errorSignal = signal(undefined) - networkActive = false + private networkActiveSignal = signal(false) - documentId: number - document: Document - metadata: DocumentMetadata - suggestions: DocumentSuggestions - suggestionsLoading: boolean = false - users: User[] + private documentIdSignal = signal(undefined) + private documentSignal = signal(undefined) + private metadataSignal = signal(undefined) + private suggestionsSignal = signal(undefined) + private suggestionsLoadingSignal = signal(false) + private usersSignal = signal(undefined) - title: string + private titleSignal = signal(undefined) titleSubject: Subject = new Subject() - previewUrl: string - pdfSource?: string - pdfPassword?: string - thumbUrl: string - previewText: string - previewLoaded: boolean = false - tiffURL: string - tiffError: string + private previewUrlSignal = signal(undefined) + private pdfSourceSignal = signal(undefined) + private pdfPasswordSignal = signal(undefined) + private thumbUrlSignal = signal(undefined) + private previewTextSignal = signal(undefined) + private previewLoadedSignal = signal(false) + private tiffURLSignal = signal(undefined) + private tiffErrorSignal = signal(undefined) // Versioning - selectedVersionId: number + private selectedVersionIdSignal = signal(undefined) - correspondents: Correspondent[] - documentTypes: DocumentType[] - storagePaths: StoragePath[] + private correspondentsSignal = signal(undefined) + private documentTypesSignal = signal(undefined) + private storagePathsSignal = signal(undefined) documentForm: FormGroup = new FormGroup({ title: new FormControl(''), @@ -269,10 +276,10 @@ export class DocumentDetailComponent custom_fields: new FormArray([]), }) - previewCurrentPage: number = 1 - previewNumPages: number - previewZoomSetting: PdfZoomLevel = PdfZoomLevel.One - previewZoomScale: PdfZoomScale = PdfZoomScale.PageWidth + private previewCurrentPageSignal = signal(1) + private previewNumPagesSignal = signal(undefined) + private previewZoomSettingSignal = signal(PdfZoomLevel.One) + private previewZoomScaleSignal = signal(PdfZoomScale.PageWidth) store: BehaviorSubject isDirty$: Observable @@ -287,9 +294,9 @@ export class DocumentDetailComponent ogDate: Date - customFields: CustomField[] + private customFieldsSignal = signal(undefined) - public downloading: boolean = false + private downloadingSignal = signal(false) public useFormattedFilename: boolean = false public readonly CustomFieldDataType = CustomFieldDataType @@ -314,13 +321,238 @@ export class DocumentDetailComponent } DocumentDetailNavIDs = DocumentDetailNavIDs - activeNavID: number + private activeNavIDSignal = signal(undefined) + + get error(): any { + return this.errorSignal() + } + + set error(value: any) { + this.errorSignal.set(value) + } + + get networkActive(): boolean { + return this.networkActiveSignal() + } + + set networkActive(value: boolean) { + this.networkActiveSignal.set(value) + } + + get documentId(): number { + return this.documentIdSignal() + } + + set documentId(value: number) { + this.documentIdSignal.set(value) + } + + get document(): Document { + return this.documentSignal() + } + + set document(value: Document) { + this.documentSignal.set(value) + } + + get metadata(): DocumentMetadata { + return this.metadataSignal() + } + + set metadata(value: DocumentMetadata) { + this.metadataSignal.set(value) + } + + get suggestions(): DocumentSuggestions { + return this.suggestionsSignal() + } + + set suggestions(value: DocumentSuggestions) { + this.suggestionsSignal.set(value) + } + + get suggestionsLoading(): boolean { + return this.suggestionsLoadingSignal() + } + + set suggestionsLoading(value: boolean) { + this.suggestionsLoadingSignal.set(value) + } + + get users(): User[] { + return this.usersSignal() + } + + set users(value: User[]) { + this.usersSignal.set(value) + } + + get title(): string { + return this.titleSignal() + } + + set title(value: string) { + this.titleSignal.set(value) + } + + get previewUrl(): string { + return this.previewUrlSignal() + } + + set previewUrl(value: string) { + this.previewUrlSignal.set(value) + } + + get pdfSource(): string { + return this.pdfSourceSignal() + } + + set pdfSource(value: string) { + this.pdfSourceSignal.set(value) + } + + get pdfPassword(): string { + return this.pdfPasswordSignal() + } + + set pdfPassword(value: string) { + this.pdfPasswordSignal.set(value) + } + + get thumbUrl(): string { + return this.thumbUrlSignal() + } + + set thumbUrl(value: string) { + this.thumbUrlSignal.set(value) + } + + get previewText(): string { + return this.previewTextSignal() + } + + set previewText(value: string) { + this.previewTextSignal.set(value) + } + + get previewLoaded(): boolean { + return this.previewLoadedSignal() + } + + set previewLoaded(value: boolean) { + this.previewLoadedSignal.set(value) + } + + get tiffURL(): string { + return this.tiffURLSignal() + } + + set tiffURL(value: string) { + this.tiffURLSignal.set(value) + } + + get tiffError(): string { + return this.tiffErrorSignal() + } + + set tiffError(value: string) { + this.tiffErrorSignal.set(value) + } + + get selectedVersionId(): number { + return this.selectedVersionIdSignal() + } + + set selectedVersionId(value: number) { + this.selectedVersionIdSignal.set(value) + } + + get correspondents(): Correspondent[] { + return this.correspondentsSignal() + } + + set correspondents(value: Correspondent[]) { + this.correspondentsSignal.set(value) + } + + get documentTypes(): DocumentType[] { + return this.documentTypesSignal() + } + + set documentTypes(value: DocumentType[]) { + this.documentTypesSignal.set(value) + } + + get storagePaths(): StoragePath[] { + return this.storagePathsSignal() + } + + set storagePaths(value: StoragePath[]) { + this.storagePathsSignal.set(value) + } + + get previewCurrentPage(): number { + return this.previewCurrentPageSignal() + } + + set previewCurrentPage(value: number) { + this.previewCurrentPageSignal.set(value) + } + + get previewNumPages(): number { + return this.previewNumPagesSignal() + } + + set previewNumPages(value: number) { + this.previewNumPagesSignal.set(value) + } + + get previewZoomSetting(): PdfZoomLevel { + return this.previewZoomSettingSignal() + } + + set previewZoomSetting(value: PdfZoomLevel) { + this.previewZoomSettingSignal.set(value) + } + + get previewZoomScale(): PdfZoomScale { + return this.previewZoomScaleSignal() + } + + set previewZoomScale(value: PdfZoomScale) { + this.previewZoomScaleSignal.set(value) + } + + get downloading(): boolean { + return this.downloadingSignal() + } + + set downloading(value: boolean) { + this.downloadingSignal.set(value) + } + + get activeNavID(): number { + return this.activeNavIDSignal() + } + + set activeNavID(value: number) { + this.activeNavIDSignal.set(value) + } + + get customFields(): CustomField[] { + return this.customFieldsSignal() + } + + set customFields(value: CustomField[]) { + this.customFieldsSignal.set(value) + } titleKeyUp(event) { this.titleSubject.next(event.target?.value) } get useNativePdfViewer(): boolean { + this.settings.trackChanges() return this.settings.get(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER) } @@ -329,10 +561,12 @@ export class DocumentDetailComponent } get aiEnabled(): boolean { + this.settings.trackChanges() return this.settings.get(SETTINGS_KEYS.AI_ENABLED) } get archiveContentRenderType(): ContentRenderType { + this.settings.trackChanges() const hasArchiveVersion = this.metadata?.has_archive_version ?? !!this.document?.archived_file_name return hasArchiveVersion @@ -343,16 +577,19 @@ export class DocumentDetailComponent } get originalContentRenderType(): ContentRenderType { + this.settings.trackChanges() return this.getRenderType( this.metadata?.original_mime_type || this.document?.mime_type ) } get showThumbnailOverlay(): boolean { + this.settings.trackChanges() return this.settings.get(SETTINGS_KEYS.DOCUMENT_EDITING_OVERLAY_THUMBNAIL) } isFieldHidden(fieldId: DocumentDetailFieldID): boolean { + this.settings.trackChanges() return this.settings .get(SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS) .includes(fieldId) @@ -1492,9 +1729,7 @@ export class DocumentDetailComponent pdfPreviewLoaded(pdf: PngxPdfDocumentProxy) { this.previewNumPages = pdf.numPages if (this.password) this.requiresPassword = false - setTimeout(() => { - this.previewLoaded = true - }, 300) + this.previewLoaded = true } onError(event) { diff --git a/src-ui/src/app/services/settings.service.ts b/src-ui/src/app/services/settings.service.ts index 8768a2058..276b0dffd 100644 --- a/src-ui/src/app/services/settings.service.ts +++ b/src-ui/src/app/services/settings.service.ts @@ -7,6 +7,7 @@ import { LOCALE_ID, Renderer2, RendererFactory2, + signal, } from '@angular/core' import { Meta } from '@angular/platform-browser' import { CookieService } from 'ngx-cookie-service' @@ -294,6 +295,7 @@ export class SettingsService { protected baseUrl: string = environment.apiBaseUrl + 'ui_settings/' private settings: Record = {} + private settingsVersion = signal(0) currentUser: User public settingsSaved: EventEmitter = new EventEmitter() @@ -326,6 +328,10 @@ export class SettingsService { return !UNSAFE_OBJECT_KEYS.has(key) } + public trackChanges(): void { + this.settingsVersion() + } + private assignSafeSettings(source: Record) { if (!source || typeof source !== 'object' || Array.isArray(source)) { return @@ -606,6 +612,7 @@ export class SettingsService { if (index == keys.length - 1) settingObj[keyPart] = value else settingObj = settingObj[keyPart] }) + this.settingsVersion.update((version) => version + 1) } private settingIsSet(key: string): boolean {