From b84b87777382c6dae60f276dc30f51042ddba42d Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:43:13 -0700 Subject: [PATCH] Direct signals in lots more components --- .../admin/config/config.component.ts | 16 ++-- .../components/admin/logs/logs.component.ts | 8 +- .../admin/settings/settings.component.spec.ts | 2 +- .../components/admin/tasks/tasks.component.ts | 6 +- .../components/admin/trash/trash.component.ts | 6 +- .../users-groups.component.spec.ts | 4 +- .../users-groups/users-groups.component.ts | 2 +- .../app-frame/app-frame.component.html | 22 ++--- .../app-frame/app-frame.component.ts | 80 +++++++------------ .../custom-fields-dropdown.component.spec.ts | 2 +- ...orrespondent-edit-dialog.component.spec.ts | 2 +- ...custom-field-edit-dialog.component.spec.ts | 2 +- ...ocument-type-edit-dialog.component.spec.ts | 2 +- .../group-edit-dialog.component.spec.ts | 2 +- ...mail-account-edit-dialog.component.spec.ts | 2 +- .../mail-rule-edit-dialog.component.spec.ts | 2 +- ...storage-path-edit-dialog.component.spec.ts | 2 +- .../tag-edit-dialog.component.spec.ts | 2 +- .../user-edit-dialog.component.spec.ts | 2 +- .../workflow-edit-dialog.component.spec.ts | 2 +- .../common/input/tags/tags.component.spec.ts | 2 +- .../permissions-filter-dropdown.component.ts | 4 +- .../profile-edit-dialog.component.html | 26 +++--- .../profile-edit-dialog.component.ts | 79 ++++-------------- .../system-status-dialog.component.ts | 80 +++++++------------ .../dashboard/dashboard.component.spec.ts | 8 +- .../saved-view-widget.component.html | 24 +++--- .../saved-view-widget.component.ts | 48 ++--------- .../document-detail.component.spec.ts | 2 +- .../document-card-large.component.html | 2 +- .../document-card-small.component.html | 2 +- .../document-list.component.html | 2 +- .../document-list.component.spec.ts | 9 +-- .../document-list/document-list.component.ts | 2 +- .../filter-editor.component.spec.ts | 2 +- .../file-drop/file-drop.component.spec.ts | 18 ++--- .../loading-component/loading.component.ts | 20 +---- .../custom-fields.component.spec.ts | 2 +- .../manage/mail/mail.component.html | 16 ++-- .../manage/mail/mail.component.spec.ts | 2 +- .../components/manage/mail/mail.component.ts | 71 ++++------------ .../document-list-view.service.spec.ts | 9 ++- .../app/services/rest/saved-view.service.ts | 45 ++++------- src-ui/src/app/services/settings.service.ts | 69 +++------------- .../services/websocket-status.service.spec.ts | 8 +- .../app/services/websocket-status.service.ts | 2 +- 46 files changed, 245 insertions(+), 477 deletions(-) diff --git a/src-ui/src/app/components/admin/config/config.component.ts b/src-ui/src/app/components/admin/config/config.component.ts index 9e69c9dc6..9c2946438 100644 --- a/src-ui/src/app/components/admin/config/config.component.ts +++ b/src-ui/src/app/components/admin/config/config.component.ts @@ -97,11 +97,11 @@ export class ConfigComponent .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe({ next: (config) => { - this.loading = false + this.loading.set(false) this.initialize(config) }, error: (e) => { - this.loading = false + this.loading.set(false) this.toastService.showError($localize`Error retrieving config`, e) }, }) @@ -162,20 +162,20 @@ export class ConfigComponent } public saveConfig() { - this.loading = true + this.loading.set(true) this.configService .saveConfig(this.configForm.value as PaperlessConfig) .pipe(takeUntil(this.unsubscribeNotifier), first()) .subscribe({ next: (config) => { - this.loading = false + this.loading.set(false) this.initialize(config) this.store.next(config) this.settingsService.initializeSettings().subscribe() this.toastService.showInfo($localize`Configuration updated`) }, error: (e) => { - this.loading = false + this.loading.set(false) this.toastService.showError( $localize`An error occurred updating configuration`, e @@ -189,20 +189,20 @@ export class ConfigComponent } public uploadFile(file: File, key: string) { - this.loading = true + this.loading.set(true) this.configService .uploadFile(file, this.configForm.value['id'], key) .pipe(takeUntil(this.unsubscribeNotifier), first()) .subscribe({ next: (config) => { - this.loading = false + this.loading.set(false) this.initialize(config) this.store.next(config) this.settingsService.initializeSettings().subscribe() this.toastService.showInfo($localize`File successfully updated`) }, error: (e) => { - this.loading = false + this.loading.set(false) this.toastService.showError( $localize`An error occurred uploading file`, e diff --git a/src-ui/src/app/components/admin/logs/logs.component.ts b/src-ui/src/app/components/admin/logs/logs.component.ts index 93bba057e..48c70218c 100644 --- a/src-ui/src/app/components/admin/logs/logs.component.ts +++ b/src-ui/src/app/components/admin/logs/logs.component.ts @@ -61,7 +61,7 @@ export class LogsComponent .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe((result) => { this.logFiles.set(result) - this.loading = false + this.loading.set(false) if (this.logFiles().length > 0) { this.activeLog.set(this.logFiles()[0]) this.reloadLogs() @@ -86,14 +86,14 @@ export class LogsComponent } reloadLogs() { - this.loading = true + this.loading.set(true) const shouldStickToBottom = this.isNearBottom() this.logService .get(this.activeLog(), this.limit()) .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe({ next: (result) => { - this.loading = false + this.loading.set(false) const parsed = this.parseLogsWithLevel(result) const hasChanges = parsed.length !== this.logs().length || @@ -115,7 +115,7 @@ export class LogsComponent }, error: () => { this.logs.set([]) - this.loading = false + this.loading.set(false) }, }) } diff --git a/src-ui/src/app/components/admin/settings/settings.component.spec.ts b/src-ui/src/app/components/admin/settings/settings.component.spec.ts index 854444e98..06d3da5b7 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.spec.ts +++ b/src-ui/src/app/components/admin/settings/settings.component.spec.ts @@ -161,7 +161,7 @@ describe('SettingsComponent', () => { viewportScroller = TestBed.inject(ViewportScroller) toastService = TestBed.inject(ToastService) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = users[0] + settingsService.currentUser.set(users[0]) userService = TestBed.inject(UserService) permissionsService = TestBed.inject(PermissionsService) modalService = TestBed.inject(NgbModal) diff --git a/src-ui/src/app/components/admin/tasks/tasks.component.ts b/src-ui/src/app/components/admin/tasks/tasks.component.ts index a8fd69911..edf606c12 100644 --- a/src-ui/src/app/components/admin/tasks/tasks.component.ts +++ b/src-ui/src/app/components/admin/tasks/tasks.component.ts @@ -732,7 +732,7 @@ export class TasksComponent this.reloadSectionCounts() - this.loading = true + this.loading.set(true) this.tasksService .list( this.page, @@ -748,7 +748,7 @@ export class TasksComponent if (this.selectedSection !== TaskSection.All) { this.setSectionCount(this.selectedSection, result.count) } - this.loading = false + this.loading.set(false) if ( this.page > 1 && this.pagedTasks().length === 0 && @@ -759,7 +759,7 @@ export class TasksComponent } }, error: () => { - this.loading = false + this.loading.set(false) }, }) } diff --git a/src-ui/src/app/components/admin/trash/trash.component.ts b/src-ui/src/app/components/admin/trash/trash.component.ts index 474a0e91c..68b3464eb 100644 --- a/src-ui/src/app/components/admin/trash/trash.component.ts +++ b/src-ui/src/app/components/admin/trash/trash.component.ts @@ -54,7 +54,7 @@ export class TrashComponent } reload() { - this.loading = true + this.loading.set(true) this.trashService .getTrash(this.page()) .pipe( @@ -62,11 +62,11 @@ export class TrashComponent this.documentsInTrash.set(r.results) this.totalDocuments.set(r.count) this.selectedDocuments.set(new Set()) - this.loading = false + this.loading.set(false) }) ) .subscribe(() => { - this.show = true + this.show.set(true) }) } diff --git a/src-ui/src/app/components/admin/users-groups/users-groups.component.spec.ts b/src-ui/src/app/components/admin/users-groups/users-groups.component.spec.ts index 20566a9b0..31bd6e9d4 100644 --- a/src-ui/src/app/components/admin/users-groups/users-groups.component.spec.ts +++ b/src-ui/src/app/components/admin/users-groups/users-groups.component.spec.ts @@ -109,7 +109,7 @@ describe('UsersAndGroupsComponent', () => { const toastInfoSpy = jest.spyOn(toastService, 'showInfo') editDialog.failed.emit() expect(toastErrorSpy).toHaveBeenCalled() - settingsService.currentUser = users[1] // simulate logged in as different user + settingsService.currentUser.set(users[1]) // simulate logged in as different user editDialog.succeeded.emit(users[0]) expect(toastInfoSpy).toHaveBeenCalledWith( `Saved user "${users[0].username}".` @@ -148,7 +148,7 @@ describe('UsersAndGroupsComponent', () => { .mockImplementation(() => {}) const editDialog = modal.componentInstance as UserEditDialogComponent editDialog.passwordIsSet = true - settingsService.currentUser = users[0] // simulate logged in as same user + settingsService.currentUser.set(users[0]) // simulate logged in as same user editDialog.succeeded.emit(users[0]) fixture.detectChanges() tick(2600) diff --git a/src-ui/src/app/components/admin/users-groups/users-groups.component.ts b/src-ui/src/app/components/admin/users-groups/users-groups.component.ts index 209749ffd..34994a8ec 100644 --- a/src-ui/src/app/components/admin/users-groups/users-groups.component.ts +++ b/src-ui/src/app/components/admin/users-groups/users-groups.component.ts @@ -109,7 +109,7 @@ export class UsersAndGroupsComponent .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe((newUser: User) => { if ( - newUser.id === this.settings.currentUser.id && + newUser.id === this.settings.currentUser().id && (modal.componentInstance as UserEditDialogComponent).passwordIsSet ) { this.toastService.showInfo( diff --git a/src-ui/src/app/components/app-frame/app-frame.component.html b/src-ui/src/app/components/app-frame/app-frame.component.html index 9fa8b8016..475bbac0c 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.html +++ b/src-ui/src/app/components/app-frame/app-frame.component.html @@ -1,7 +1,7 @@
+ [class.mobile-hidden]="mobileSearchHidden()">
@@ -68,8 +68,8 @@
+ [ngClass]="slimSidebarEnabled ? 'slim' : 'col-md-3 col-lg-2 col-xxxl-1'" [class.animating]="slimSidebarAnimating()" + [ngbCollapse]="isMenuCollapsed()"> @if (canSaveSettings) {
- @if (!settingsService.updateCheckingIsSet || appRemoteVersion) { + @if (!settingsService.updateCheckingIsSet || appRemoteVersion()) {
- Paperless-ngx {{ appRemoteVersion.version }} is + Paperless-ngx {{ appRemoteVersion().version }} is available.
Click to view.
@@ -350,13 +350,13 @@

@if (settingsService.updateCheckingIsSet) { - @if (appRemoteVersion.update_available) { + @if (appRemoteVersion().update_available) { - @if (appRemoteVersion?.update_available) { + @if (appRemoteVersion()?.update_available) { Update available } @@ -377,7 +377,7 @@
-
diff --git a/src-ui/src/app/components/app-frame/app-frame.component.ts b/src-ui/src/app/components/app-frame/app-frame.component.ts index db4dc2f6c..db752dde7 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.ts +++ b/src-ui/src/app/components/app-frame/app-frame.component.ts @@ -90,48 +90,12 @@ export class AppFrameComponent permissionsService = inject(PermissionsService) private djangoMessagesService = inject(DjangoMessagesService) - private appRemoteVersionSignal = signal(null) - - private isMenuCollapsedSignal = signal(true) - - private slimSidebarAnimatingSignal = signal(false) - - private mobileSearchHiddenSignal = signal(false) - + readonly appRemoteVersion = signal(null) + readonly isMenuCollapsed = signal(true) + readonly slimSidebarAnimating = signal(false) + readonly mobileSearchHidden = signal(false) private lastScrollY: number = 0 - get appRemoteVersion(): AppRemoteVersion { - return this.appRemoteVersionSignal() - } - - set appRemoteVersion(value: AppRemoteVersion) { - this.appRemoteVersionSignal.set(value) - } - - get isMenuCollapsed(): boolean { - return this.isMenuCollapsedSignal() - } - - set isMenuCollapsed(value: boolean) { - this.isMenuCollapsedSignal.set(value) - } - - get slimSidebarAnimating(): boolean { - return this.slimSidebarAnimatingSignal() - } - - set slimSidebarAnimating(value: boolean) { - this.slimSidebarAnimatingSignal.set(value) - } - - get mobileSearchHidden(): boolean { - return this.mobileSearchHiddenSignal() - } - - set mobileSearchHidden(value: boolean) { - this.mobileSearchHiddenSignal.set(value) - } - constructor() { super() const permissionsService = this.permissionsService @@ -179,7 +143,7 @@ export class AppFrameComponent } toggleSlimSidebar(): void { - this.slimSidebarAnimating = true + this.slimSidebarAnimating.set(true) const slimSidebarEnabled = !this.slimSidebarEnabled this.settingsService.set(SETTINGS_KEYS.SLIM_SIDEBAR, slimSidebarEnabled) if (slimSidebarEnabled) { @@ -199,7 +163,7 @@ export class AppFrameComponent }, }) setTimeout(() => { - this.slimSidebarAnimating = false + this.slimSidebarAnimating.set(false) }, 200) // slightly longer than css animation for slim sidebar } @@ -209,6 +173,18 @@ export class AppFrameComponent this.attributesSectionsCollapsed = !this.attributesSectionsCollapsed } + toggleMenuCollapsed(): void { + this.isMenuCollapsed.set(!this.isMenuCollapsed()) + } + + closeMobileSearch(): void { + this.mobileSearchHidden.set(false) + } + + setMobileSearchHidden(hidden: boolean): void { + this.mobileSearchHidden.set(hidden) + } + get versionString(): string { this.settingsService.trackChanges() return `${environment.appTitle} v${this.settingsService.get(SETTINGS_KEYS.VERSION)}${environment.tag === 'prod' ? '' : ` #${environment.tag}`}` @@ -311,7 +287,7 @@ export class AppFrameComponent @HostListener('window:resize') onWindowResize(): void { if (!this.isMobileViewport()) { - this.mobileSearchHidden = false + this.mobileSearchHidden.set(false) } } @@ -319,8 +295,8 @@ export class AppFrameComponent onWindowScroll(): void { const currentScrollY = window.scrollY - if (!this.isMobileViewport() || this.isMenuCollapsed === false) { - this.mobileSearchHidden = false + if (!this.isMobileViewport() || this.isMenuCollapsed() === false) { + this.mobileSearchHidden.set(false) this.lastScrollY = currentScrollY return } @@ -328,9 +304,9 @@ export class AppFrameComponent const delta = currentScrollY - this.lastScrollY if (currentScrollY <= 0 || delta < -SCROLL_THRESHOLD) { - this.mobileSearchHidden = false + this.mobileSearchHidden.set(false) } else if (currentScrollY > SCROLL_THRESHOLD && delta > SCROLL_THRESHOLD) { - this.mobileSearchHidden = true + this.mobileSearchHidden.set(true) } this.lastScrollY = currentScrollY @@ -341,7 +317,7 @@ export class AppFrameComponent } closeMenu() { - this.isMenuCollapsed = true + this.isMenuCollapsed.set(true) } editProfile() { @@ -404,11 +380,11 @@ export class AppFrameComponent } onDragStart(event: CdkDragStart) { - this.settingsService.globalDropzoneEnabled = false + this.settingsService.globalDropzoneEnabled.set(false) } onDragEnd(event: CdkDragEnd) { - this.settingsService.globalDropzoneEnabled = true + this.settingsService.globalDropzoneEnabled.set(true) } onDrop(event: CdkDragDrop) { @@ -429,7 +405,7 @@ export class AppFrameComponent this.remoteVersionService .checkForUpdates() .subscribe((appRemoteVersion: AppRemoteVersion) => { - this.appRemoteVersion = appRemoteVersion + this.appRemoteVersion.set(appRemoteVersion) }) } @@ -459,7 +435,7 @@ export class AppFrameComponent this.settingsService.trackChanges() return ( this.settingsService.get(SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT) && - !this.settingsService.organizingSidebarSavedViews + !this.settingsService.organizingSidebarSavedViews() ) } } diff --git a/src-ui/src/app/components/common/custom-fields-dropdown/custom-fields-dropdown.component.spec.ts b/src-ui/src/app/components/common/custom-fields-dropdown/custom-fields-dropdown.component.spec.ts index 78df9c74f..ec3cb4c35 100644 --- a/src-ui/src/app/components/common/custom-fields-dropdown/custom-fields-dropdown.component.spec.ts +++ b/src-ui/src/app/components/common/custom-fields-dropdown/custom-fields-dropdown.component.spec.ts @@ -74,7 +74,7 @@ describe('CustomFieldsDropdownComponent', () => { }) ) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 1, username: 'test' } + settingsService.currentUser.set({ id: 1, username: 'test' }) fixture = TestBed.createComponent(CustomFieldsDropdownComponent) component = fixture.componentInstance fixture.detectChanges() diff --git a/src-ui/src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.spec.ts index 467aabc4f..efd9dab8b 100644 --- a/src-ui/src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.spec.ts @@ -41,7 +41,7 @@ describe('CorrespondentEditDialogComponent', () => { fixture = TestBed.createComponent(CorrespondentEditDialogComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 99, username: 'user99' } + settingsService.currentUser.set({ id: 99, username: 'user99' }) component = fixture.componentInstance fixture.detectChanges() diff --git a/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.spec.ts index 419fd89bb..54d98cbb2 100644 --- a/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.spec.ts @@ -44,7 +44,7 @@ describe('CustomFieldEditDialogComponent', () => { fixture = TestBed.createComponent(CustomFieldEditDialogComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 99, username: 'user99' } + settingsService.currentUser.set({ id: 99, username: 'user99' }) component = fixture.componentInstance fixture.detectChanges() diff --git a/src-ui/src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.spec.ts index c3c46c980..335b842fb 100644 --- a/src-ui/src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.spec.ts @@ -41,7 +41,7 @@ describe('DocumentTypeEditDialogComponent', () => { fixture = TestBed.createComponent(DocumentTypeEditDialogComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 99, username: 'user99' } + settingsService.currentUser.set({ id: 99, username: 'user99' }) component = fixture.componentInstance fixture.detectChanges() diff --git a/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.spec.ts index 88e989942..86977840a 100644 --- a/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.spec.ts @@ -45,7 +45,7 @@ describe('GroupEditDialogComponent', () => { fixture = TestBed.createComponent(GroupEditDialogComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 99, username: 'user99' } + settingsService.currentUser.set({ id: 99, username: 'user99' }) component = fixture.componentInstance fixture.detectChanges() diff --git a/src-ui/src/app/components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component.spec.ts index bdb5863a7..5881eaced 100644 --- a/src-ui/src/app/components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component.spec.ts @@ -58,7 +58,7 @@ describe('MailAccountEditDialogComponent', () => { fixture = TestBed.createComponent(MailAccountEditDialogComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 99, username: 'user99' } + settingsService.currentUser.set({ id: 99, username: 'user99' }) component = fixture.componentInstance fixture.detectChanges() diff --git a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.spec.ts index af3b4dbd8..831702402 100644 --- a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.spec.ts @@ -75,7 +75,7 @@ describe('MailRuleEditDialogComponent', () => { fixture = TestBed.createComponent(MailRuleEditDialogComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 99, username: 'user99' } + settingsService.currentUser.set({ id: 99, username: 'user99' }) component = fixture.componentInstance fixture.detectChanges() diff --git a/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.spec.ts index 2466ced73..24fafcd6f 100644 --- a/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.spec.ts @@ -30,7 +30,7 @@ describe('StoragePathEditDialogComponent', () => { documentService = TestBed.inject(DocumentService) fixture = TestBed.createComponent(StoragePathEditDialogComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 99, username: 'user99' } + settingsService.currentUser.set({ id: 99, username: 'user99' }) component = fixture.componentInstance fixture.detectChanges() diff --git a/src-ui/src/app/components/common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component.spec.ts index 16fb7a9f3..bf3ed9912 100644 --- a/src-ui/src/app/components/common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component.spec.ts @@ -48,7 +48,7 @@ describe('TagEditDialogComponent', () => { fixture = TestBed.createComponent(TagEditDialogComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 99, username: 'user99' } + settingsService.currentUser.set({ id: 99, username: 'user99' }) component = fixture.componentInstance fixture.detectChanges() diff --git a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.spec.ts index 9ffa1ea95..ad4b09468 100644 --- a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.spec.ts @@ -73,7 +73,7 @@ describe('UserEditDialogComponent', () => { fixture = TestBed.createComponent(UserEditDialogComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 99, username: 'user99' } + settingsService.currentUser.set({ id: 99, username: 'user99' }) permissionsService = TestBed.inject(PermissionsService) toastService = TestBed.inject(ToastService) component = fixture.componentInstance diff --git a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts index 070e5124f..936b3708e 100644 --- a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts @@ -187,7 +187,7 @@ describe('WorkflowEditDialogComponent', () => { fixture = TestBed.createComponent(WorkflowEditDialogComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 99, username: 'user99' } + settingsService.currentUser.set({ id: 99, username: 'user99' }) component = fixture.componentInstance fixture.detectChanges() diff --git a/src-ui/src/app/components/common/input/tags/tags.component.spec.ts b/src-ui/src/app/components/common/input/tags/tags.component.spec.ts index 1b761a5db..90f0517bc 100644 --- a/src-ui/src/app/components/common/input/tags/tags.component.spec.ts +++ b/src-ui/src/app/components/common/input/tags/tags.component.spec.ts @@ -136,7 +136,7 @@ describe('TagsComponent', () => { }) it('should support create new using last search term and open a modal', () => { - settingsService.currentUser = { id: 1 } + settingsService.currentUser.set({ id: 1 }) let activeInstances: NgbModalRef[] modalService.activeInstances.subscribe((v) => (activeInstances = v)) component.select.filter('foobar') diff --git a/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.ts b/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.ts index 83e632b68..15a900eab 100644 --- a/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.ts +++ b/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.ts @@ -117,12 +117,12 @@ export class PermissionsFilterDropdownComponent extends ComponentWithPermissions if (this.selectionModel.ownerFilter === OwnerFilterType.SELF) { this.selectionModel.includeUsers = [] this.selectionModel.excludeUsers = [] - this.selectionModel.userID = this.settingsService.currentUser.id + this.selectionModel.userID = this.settingsService.currentUser().id this.selectionModel.hideUnowned = false } else if (this.selectionModel.ownerFilter === OwnerFilterType.NOT_SELF) { this.selectionModel.userID = null this.selectionModel.includeUsers = [] - this.selectionModel.excludeUsers = [this.settingsService.currentUser.id] + this.selectionModel.excludeUsers = [this.settingsService.currentUser().id] this.selectionModel.hideUnowned = false } else if (this.selectionModel.ownerFilter === OwnerFilterType.NONE) { this.selectionModel.userID = null diff --git a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html index c329beb92..e636bc8f1 100644 --- a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html +++ b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html @@ -7,38 +7,38 @@
- +
-
+
- +
- +
-
+
- +
- - + +
- Copied! + Copied!
Warning: changing the token cannot be undone
@@ -155,10 +155,10 @@ } diff --git a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts index 879d72a2a..7f22652a1 100644 --- a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts +++ b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts @@ -50,28 +50,12 @@ export class ProfileEditDialogComponent private toastService = inject(ToastService) private clipboard = inject(Clipboard) - private networkActiveSignal = signal(false) - private errorSignal = signal(undefined) - private showPasswordConfirmSignal = signal(false) - private showEmailConfirmSignal = signal(false) - private copiedSignal = signal(false) - private codesCopiedSignal = signal(false) - - public get networkActive(): boolean { - return this.networkActiveSignal() - } - - public set networkActive(networkActive: boolean) { - this.networkActiveSignal.set(networkActive) - } - - public get error(): any { - return this.errorSignal() - } - - public set error(error: any) { - this.errorSignal.set(error) - } + readonly networkActive = signal(false) + readonly error = signal(undefined) + readonly showPasswordConfirm = signal(false) + readonly showEmailConfirm = signal(false) + readonly copied = signal(false) + readonly codesCopied = signal(false) public form = new FormGroup({ email: new FormControl(''), @@ -87,49 +71,18 @@ export class ProfileEditDialogComponent private currentPassword: string private newPassword: string private passwordConfirm: string - public get showPasswordConfirm(): boolean { - return this.showPasswordConfirmSignal() - } - - public set showPasswordConfirm(showPasswordConfirm: boolean) { - this.showPasswordConfirmSignal.set(showPasswordConfirm) - } public hasUsablePassword: boolean = false private currentEmail: string private newEmail: string private emailConfirm: string - public get showEmailConfirm(): boolean { - return this.showEmailConfirmSignal() - } - - public set showEmailConfirm(showEmailConfirm: boolean) { - this.showEmailConfirmSignal.set(showEmailConfirm) - } public isTotpEnabled: boolean = false public totpSettings: TotpSettings public totpSettingsLoading: boolean = false public totpLoading: boolean = false public recoveryCodes: string[] - - public get copied(): boolean { - return this.copiedSignal() - } - - public set copied(copied: boolean) { - this.copiedSignal.set(copied) - } - - public get codesCopied(): boolean { - return this.codesCopiedSignal() - } - - public set codesCopied(codesCopied: boolean) { - this.codesCopiedSignal.set(codesCopied) - } - public socialAccounts: SocialAccount[] = [] public socialAccountProviders: SocialAccountProvider[] = [] @@ -141,12 +94,12 @@ export class ProfileEditDialogComponent } ngOnInit(): void { - this.networkActive = true + this.networkActive.set(true) this.profileService .get() .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe((profile) => { - this.networkActive = false + this.networkActive.set(false) this.form.patchValue(profile) this.currentEmail = profile.email this.form.get('email').valueChanges.subscribe((newEmail) => { @@ -186,7 +139,7 @@ export class ProfileEditDialogComponent } onEmailChange(): void { - this.showEmailConfirm = this.currentEmail !== this.newEmail + this.showEmailConfirm.set(this.currentEmail !== this.newEmail) if (this.showEmailConfirm) { this.form.get('email_confirm').enable() if (this.newEmail !== this.emailConfirm) { @@ -212,7 +165,7 @@ export class ProfileEditDialogComponent } onPasswordChange(): void { - this.showPasswordConfirm = this.currentPassword !== this.newPassword + this.showPasswordConfirm.set(this.currentPassword !== this.newPassword) if (this.showPasswordConfirm) { this.form.get('password_confirm').enable() @@ -228,17 +181,17 @@ export class ProfileEditDialogComponent } private setFieldError(fieldName: string, message: string): void { - this.error = { - ...(this.error ?? {}), + this.error.set({ + ...(this.error() ?? {}), [fieldName]: message, - } + }) } private clearFieldError(fieldName: string): void { - if (!this.error) return - const error = { ...this.error } + if (!this.error()) return + const error = { ...this.error() } delete error[fieldName] - this.error = Object.keys(error).length ? error : undefined + this.error.set(Object.keys(error).length ? error : undefined) } save(): void { diff --git a/src-ui/src/app/components/common/system-status-dialog/system-status-dialog.component.ts b/src-ui/src/app/components/common/system-status-dialog/system-status-dialog.component.ts index bc43c3873..cf9982400 100644 --- a/src-ui/src/app/components/common/system-status-dialog/system-status-dialog.component.ts +++ b/src-ui/src/app/components/common/system-status-dialog/system-status-dialog.component.ts @@ -57,40 +57,13 @@ export class SystemStatusDialogComponent implements OnInit, OnDestroy { public SystemStatusItemStatus = SystemStatusItemStatus public PaperlessTaskType = PaperlessTaskType - private statusSignal = signal(undefined) + @Input() status = signal(undefined) public frontendVersion: string = environment.version - private versionMismatchSignal = signal(false) - - private copiedSignal = signal(false) - - private runningTasksSignal = signal>(new Set()) + readonly versionMismatch = signal(false) + readonly copied = signal(false) + readonly runningTasks = signal>(new Set()) private unsubscribeNotifier: Subject = 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() } @@ -100,17 +73,18 @@ export class SystemStatusDialogComponent implements OnInit, OnDestroy { } public ngOnInit() { - const status = this.status - this.versionMismatch = + const status = this.status() + this.versionMismatch.set( environment.production && - status.pngx_version && - this.frontendVersion && - status.pngx_version !== this.frontendVersion - if (this.versionMismatch) { - this.status = { + status.pngx_version && + this.frontendVersion && + status.pngx_version !== this.frontendVersion + ) + if (this.versionMismatch()) { + this.status.set({ ...status, pngx_version: `${status.pngx_version} (frontend: ${this.frontendVersion})`, - } + }) } this.updateWebsocketStatus(this.websocketStatusService.isConnected()) this.websocketStatusService @@ -124,10 +98,10 @@ export class SystemStatusDialogComponent implements OnInit, OnDestroy { } public copy() { - this.clipboard.copy(JSON.stringify(this.status, null, 4)) - this.copied = true + this.clipboard.copy(JSON.stringify(this.status(), null, 4)) + this.copied.set(true) setTimeout(() => { - this.copied = false + this.copied.set(false) }, 3000) } @@ -138,7 +112,7 @@ export class SystemStatusDialogComponent implements OnInit, OnDestroy { } public isRunning(taskName: PaperlessTaskType): boolean { - return this.runningTasksSignal().has(taskName) + return this.runningTasks().has(taskName) } public runTask(taskName: PaperlessTaskType) { @@ -148,11 +122,11 @@ export class SystemStatusDialogComponent implements OnInit, OnDestroy { next: () => { this.setTaskRunning(taskName, false) this.systemStatusService.get().subscribe({ - next: (status) => { - this.status = { - ...this.status, - ...status, - } + next: (statusUpdate) => { + this.status.set({ + ...this.status(), + ...statusUpdate, + }) }, }) }, @@ -167,22 +141,22 @@ export class SystemStatusDialogComponent implements OnInit, OnDestroy { } private updateWebsocketStatus(connected: boolean): void { - this.status = { - ...this.status, + this.status.set({ + ...this.status(), websocket_connected: connected ? SystemStatusItemStatus.OK : SystemStatusItemStatus.ERROR, - } + }) } private setTaskRunning(taskName: PaperlessTaskType, running: boolean): void { - const runningTasks = new Set(this.runningTasksSignal()) + const runningTasks = new Set(this.runningTasks()) if (running) { runningTasks.add(taskName) } else { runningTasks.delete(taskName) } - this.runningTasksSignal.set(runningTasks) + this.runningTasks.set(runningTasks) } ngOnDestroy(): void { diff --git a/src-ui/src/app/components/dashboard/dashboard.component.spec.ts b/src-ui/src/app/components/dashboard/dashboard.component.spec.ts index a23eefb2b..22615cd8e 100644 --- a/src-ui/src/app/components/dashboard/dashboard.component.spec.ts +++ b/src-ui/src/app/components/dashboard/dashboard.component.spec.ts @@ -120,10 +120,10 @@ describe('DashboardComponent', () => { }).compileComponents() settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { + settingsService.currentUser.set({ first_name: 'Foo', last_name: 'Bar', - } + }) jest.spyOn(settingsService, 'get').mockImplementation((key) => { if (key === SETTINGS_KEYS.DASHBOARD_VIEWS_SORT_ORDER) return [0, 2, 3] }) @@ -137,9 +137,9 @@ describe('DashboardComponent', () => { it('should show a welcome message', () => { expect(component.subtitle).toEqual(`Hello Foo, welcome to Paperless-ngx`) - settingsService.currentUser = { + settingsService.currentUser.set({ id: 1, - } + }) expect(component.subtitle).toEqual(`Welcome to Paperless-ngx`) }) diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html index 71835525e..c724a9c86 100644 --- a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html +++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html @@ -5,21 +5,21 @@ [draggable]="savedView" > - @if (count !== null) { - {{count}} + @if (count() !== null) { + {{count()}} } - @if (documents.length) { + @if (documents().length) { Show all }
- @if (displayMode === DisplayMode.TABLE) { + @if (displayMode() === DisplayMode.TABLE) { - @for (field of displayFields; track field; let i = $index) { - @if (displayFields.includes(field)) { + @for (field of displayFields(); track field; let i = $index) { + @if (displayFields().includes(field)) {
- @if (loading && !documents.length) { + @if (loading && !documents().length) {
- +
} @else { @switch (field) { @@ -112,7 +112,7 @@ @if (field.startsWith(DisplayField.CUSTOM_FIELD)) { } - @if (j === displayFields.length - 1) { + @if (j === displayFields().length - 1) {
@@ -129,7 +129,7 @@ }
- } @else if (displayMode === DisplayMode.SMALL_CARDS) { + } @else if (displayMode() === DisplayMode.SMALL_CARDS) {
@if (loading && !documents.length) { @for (row of placeholderRows; track row) { diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts index 94b0b0f7e..f13b84a34 100644 --- a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts +++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts @@ -107,7 +107,7 @@ export class SavedViewWidgetComponent @Input() savedView: SavedView - private documentsSignal = signal([]) + readonly documents = signal([]) unsubscribeNotifier: Subject = new Subject() @@ -117,48 +117,16 @@ export class SavedViewWidgetComponent mouseOnPreview = false popoverHidden = true - private displayModeSignal = signal(null) + readonly displayMode = signal(null) - private displayFieldsSignal = signal( + readonly displayFields = signal( DEFAULT_DASHBOARD_DISPLAY_FIELDS ) - private countSignal = signal(null) + readonly count = signal(null) placeholderRows: number[] = [] - get documents(): Document[] { - return this.documentsSignal() - } - - set documents(value: Document[]) { - this.documentsSignal.set(value) - } - - get displayMode(): DisplayMode { - return this.displayModeSignal() - } - - set displayMode(value: DisplayMode) { - this.displayModeSignal.set(value) - } - - get displayFields(): DisplayField[] { - return this.displayFieldsSignal() - } - - set displayFields(value: DisplayField[]) { - this.displayFieldsSignal.set(value) - } - - get count(): number { - return this.countSignal() - } - - set count(value: number) { - this.countSignal.set(value) - } - ngOnInit(): void { this.placeholderRows = Array.from( { @@ -166,7 +134,7 @@ export class SavedViewWidgetComponent }, (_, index) => index ) - this.displayMode = this.savedView.display_mode ?? DisplayMode.TABLE + this.displayMode.set(this.savedView.display_mode ?? DisplayMode.TABLE) if ( this.permissionsService.currentUserCan( @@ -192,7 +160,7 @@ export class SavedViewWidgetComponent // filter by perms etc this.displayFields = this.displayFields.filter( (field) => - this.settingsService.allDisplayFields.find((f) => f.id === field) !== + this.settingsService.allDisplayFields().find((f) => f.id === field) !== undefined ) @@ -224,8 +192,8 @@ export class SavedViewWidgetComponent ) .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe((result) => { - this.documents = result.results - this.count = result.count + this.documents.set(result.results) + this.count.set(result.count) this.savedViewService.setDocumentCount(this.savedView, result.count) this.loading = false this.show = true 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 951064e30..8c5289fd4 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 @@ -286,7 +286,7 @@ describe('DocumentDetailComponent', () => { toastService = TestBed.inject(ToastService) documentListViewService = TestBed.inject(DocumentListViewService) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 1 } + settingsService.currentUser.set({ id: 1 }) customFieldsService = TestBed.inject(CustomFieldsService) deviceDetectorService = TestBed.inject(DeviceDetectorService) fixture = TestBed.createComponent(DocumentDetailComponent) diff --git a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html index 1c69d0f53..87acb1135 100644 --- a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html +++ b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html @@ -134,7 +134,7 @@ {document.page_count, plural, =1 {1 page} other {{{document.page_count}} pages}}
} - @if (displayFields.includes(DisplayField.OWNER) && document.owner && document.owner !== settingsService.currentUser.id) { + @if (displayFields.includes(DisplayField.OWNER) && document.owner && document.owner !== settingsService.currentUser().id) {
{{document.owner | username | async}}
diff --git a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html index b3a29aed4..fc0089600 100644 --- a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html +++ b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html @@ -113,7 +113,7 @@ #{{document.archive_serial_number}}
} - @if (displayFields.includes(DisplayField.OWNER) && document.owner && document.owner !== settingsService.currentUser.id) { + @if (displayFields.includes(DisplayField.OWNER) && document.owner && document.owner !== settingsService.currentUser().id) {
{{document.owner | username | async}} diff --git a/src-ui/src/app/components/document-list/document-list.component.html b/src-ui/src/app/components/document-list/document-list.component.html index aadec7d77..b33322cdf 100644 --- a/src-ui/src/app/components/document-list/document-list.component.html +++ b/src-ui/src/app/components/document-list/document-list.component.html @@ -36,7 +36,7 @@
- @for (field of settingsService.allDisplayFields; track field.id) { + @for (field of settingsService.allDisplayFields(); track field.id) {
diff --git a/src-ui/src/app/components/document-list/document-list.component.spec.ts b/src-ui/src/app/components/document-list/document-list.component.spec.ts index 0921ea38b..df8e56a38 100644 --- a/src-ui/src/app/components/document-list/document-list.component.spec.ts +++ b/src-ui/src/app/components/document-list/document-list.component.spec.ts @@ -837,11 +837,10 @@ describe('DocumentListComponent', () => { it('should get custom field title', () => { fixture.detectChanges() - jest - .spyOn(settingsService, 'allDisplayFields', 'get') - .mockReturnValue([ - { id: 'custom_field_1' as any, name: 'Custom Field 1' }, - ]) + const mockDisplayFields = [ + { id: 'custom_field_1' as any, name: 'Custom Field 1' }, + ] + settingsService.allDisplayFields = jest.fn(() => mockDisplayFields) as any expect(component.getDisplayCustomFieldTitle('custom_field_1')).toEqual( 'Custom Field 1' ) diff --git a/src-ui/src/app/components/document-list/document-list.component.ts b/src-ui/src/app/components/document-list/document-list.component.ts index bc83a50d7..68ba09da3 100644 --- a/src-ui/src/app/components/document-list/document-list.component.ts +++ b/src-ui/src/app/components/document-list/document-list.component.ts @@ -255,7 +255,7 @@ export class DocumentListComponent } public getDisplayCustomFieldTitle(field: string) { - return this.settingsService.allDisplayFields.find((f) => f.id === field) + return this.settingsService.allDisplayFields().find((f) => f.id === field) ?.name } diff --git a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.spec.ts b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.spec.ts index d75e38630..855c637fc 100644 --- a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.spec.ts +++ b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.spec.ts @@ -260,7 +260,7 @@ describe('FilterEditorComponent', () => { documentService = TestBed.inject(DocumentService) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = users[0] + settingsService.currentUser.set(users[0]) permissionsService = TestBed.inject(PermissionsService) searchService = TestBed.inject(SearchService) jest diff --git a/src-ui/src/app/components/file-drop/file-drop.component.spec.ts b/src-ui/src/app/components/file-drop/file-drop.component.spec.ts index febc90db6..75a19a912 100644 --- a/src-ui/src/app/components/file-drop/file-drop.component.spec.ts +++ b/src-ui/src/app/components/file-drop/file-drop.component.spec.ts @@ -55,7 +55,7 @@ describe('FileDropComponent', () => { it('should disable drag-drop if disabled in settings', fakeAsync(() => { jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) - settingsService.globalDropzoneEnabled = false + settingsService.globalDropzoneEnabled.set(false) expect(component.dragDropEnabled).toBeFalsy() component.onDragOver(new Event('dragover') as DragEvent) @@ -296,21 +296,21 @@ describe('FileDropComponent', () => { }) it('should ignore events if disabled', fakeAsync(() => { - settingsService.globalDropzoneEnabled = false - expect(settingsService.globalDropzoneActive).toBeFalsy() + settingsService.globalDropzoneEnabled.set(false) + expect(settingsService.globalDropzoneActive()).toBeFalsy() component.onDragOver(new Event('dragover') as DragEvent) - expect(settingsService.globalDropzoneActive).toBeFalsy() - settingsService.globalDropzoneActive = true + expect(settingsService.globalDropzoneActive()).toBeFalsy() + settingsService.globalDropzoneActive.set(true) component.onDragLeave(new Event('dragleave') as DragEvent) - expect(settingsService.globalDropzoneActive).toBeTruthy() + expect(settingsService.globalDropzoneActive()).toBeTruthy() component.onDrop(new Event('drop') as DragEvent) - expect(settingsService.globalDropzoneActive).toBeTruthy() + expect(settingsService.globalDropzoneActive()).toBeTruthy() })) it('should hide if app loses focus', fakeAsync(() => { const leaveSpy = jest.spyOn(component, 'onDragLeave') jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) - settingsService.globalDropzoneEnabled = true + settingsService.globalDropzoneEnabled.set(true) const overEvent = new Event('dragover') as DragEvent ;(overEvent as any).dataTransfer = { types: ['Files'] } component.onDragOver(overEvent) @@ -326,7 +326,7 @@ describe('FileDropComponent', () => { it('should hide on window blur', fakeAsync(() => { const leaveSpy = jest.spyOn(component, 'onDragLeave') jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) - settingsService.globalDropzoneEnabled = true + settingsService.globalDropzoneEnabled.set(true) const overEvent = new Event('dragover') as DragEvent ;(overEvent as any).dataTransfer = { types: ['Files'] } component.onDragOver(overEvent) diff --git a/src-ui/src/app/components/loading-component/loading.component.ts b/src-ui/src/app/components/loading-component/loading.component.ts index 1cf1d6dea..c3d2948e4 100644 --- a/src-ui/src/app/components/loading-component/loading.component.ts +++ b/src-ui/src/app/components/loading-component/loading.component.ts @@ -7,24 +7,8 @@ export abstract class LoadingComponentWithPermissions extends ComponentWithPermissions implements OnDestroy { - private loadingSignal = signal(true) - private showSignal = signal(false) - - public get loading(): boolean { - return this.loadingSignal() - } - - public set loading(value: boolean) { - this.loadingSignal.set(value) - } - - public get show(): boolean { - return this.showSignal() - } - - public set show(value: boolean) { - this.showSignal.set(value) - } + readonly loading = signal(true) + readonly show = signal(false) protected unsubscribeNotifier: Subject = new Subject() diff --git a/src-ui/src/app/components/manage/document-attributes/custom-fields/custom-fields.component.spec.ts b/src-ui/src/app/components/manage/document-attributes/custom-fields/custom-fields.component.spec.ts index 96eaab503..8910b249b 100644 --- a/src-ui/src/app/components/manage/document-attributes/custom-fields/custom-fields.component.spec.ts +++ b/src-ui/src/app/components/manage/document-attributes/custom-fields/custom-fields.component.spec.ts @@ -94,7 +94,7 @@ describe('CustomFieldsComponent', () => { toastService = TestBed.inject(ToastService) listViewService = TestBed.inject(DocumentListViewService) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 0, username: 'test' } + settingsService.currentUser.set({ id: 0, username: 'test' }) fixture = TestBed.createComponent(CustomFieldsComponent) component = fixture.componentInstance diff --git a/src-ui/src/app/components/manage/mail/mail.component.html b/src-ui/src/app/components/manage/mail/mail.component.html index 204bc9e1c..7d4552288 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.html +++ b/src-ui/src/app/components/manage/mail/mail.component.html @@ -34,7 +34,7 @@
- @if (loadingAccounts) { + @if (loadingAccounts()) {
  • Loading... @@ -43,7 +43,7 @@ @for (account of mailAccounts; track account) {
  • -
    +
  • } - @if (!loadingAccounts && mailAccounts.length === 0) { + @if (!loadingAccounts() && mailAccounts.length === 0) {
  • No mail accounts defined.
  • } @@ -117,19 +117,19 @@
    - @if (loadingRules) { + @if (loadingRules()) {
  • Loading...
  • } - @for (rule of mailRules; track rule) { + @for (rule of mailRules(); track rule) {
  • -
    +
    {{rule.order}}
    -
    {{ mailAccountsById.get(rule.account)?.name }}
    +
    {{ mailAccountsById().get(rule.account)?.name }}
    @@ -179,7 +179,7 @@
  • } - @if (!loadingRules && mailRules.length === 0) { + @if (!loadingRules() && mailRules().length === 0) {
  • No mail rules defined.
  • } diff --git a/src-ui/src/app/components/manage/mail/mail.component.spec.ts b/src-ui/src/app/components/manage/mail/mail.component.spec.ts index c866160d4..011426dbd 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.spec.ts +++ b/src-ui/src/app/components/manage/mail/mail.component.spec.ts @@ -113,7 +113,7 @@ describe('MailComponent', () => { permissionsService = TestBed.inject(PermissionsService) activatedRoute = TestBed.inject(ActivatedRoute) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 1 } + settingsService.currentUser.set({ id: 1 }) jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) jest .spyOn(permissionsService, 'currentUserHasObjectPermissions') diff --git a/src-ui/src/app/components/manage/mail/mail.component.ts b/src-ui/src/app/components/manage/mail/mail.component.ts index 1c56587b2..2f010427c 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.ts +++ b/src-ui/src/app/components/manage/mail/mail.component.ts @@ -63,25 +63,13 @@ export class MailComponent } private set mailAccounts(accounts: MailAccount[]) { this.mailAccountsSignal.set(accounts) - this.mailAccountsById = new Map( - accounts.map((account) => [account.id, account]) + this.mailAccountsById.set( + new Map(accounts.map((account) => [account.id, account])) ) } - private mailAccountsByIdSignal = signal>(new Map()) - public get mailAccountsById(): Map { - return this.mailAccountsByIdSignal() - } - private set mailAccountsById(value: Map) { - this.mailAccountsByIdSignal.set(value) - } + readonly mailAccountsById = signal>(new Map()) - private mailRulesSignal = signal([]) - public get mailRules(): MailRule[] { - return this.mailRulesSignal() - } - public set mailRules(value: MailRule[]) { - this.mailRulesSignal.set(value) - } + readonly mailRules = signal([]) unsubscribeNotifier: Subject = new Subject() oAuthAccountId: number @@ -94,35 +82,10 @@ export class MailComponent return this.settingsService.get(SETTINGS_KEYS.OUTLOOK_OAUTH_URL) } - private loadingRulesSignal = signal(true) - private showRulesSignal = signal(false) - private loadingAccountsSignal = signal(true) - private showAccountsSignal = signal(false) - - public get loadingRules(): boolean { - return this.loadingRulesSignal() - } - public set loadingRules(value: boolean) { - this.loadingRulesSignal.set(value) - } - public get showRules(): boolean { - return this.showRulesSignal() - } - public set showRules(value: boolean) { - this.showRulesSignal.set(value) - } - public get loadingAccounts(): boolean { - return this.loadingAccountsSignal() - } - public set loadingAccounts(value: boolean) { - this.loadingAccountsSignal.set(value) - } - public get showAccounts(): boolean { - return this.showAccountsSignal() - } - public set showAccounts(value: boolean) { - this.showAccountsSignal.set(value) - } + readonly loadingRules = signal(true) + readonly showRules = signal(false) + readonly loadingAccounts = signal(true) + readonly showAccounts = signal(false) ngOnInit(): void { this.mailAccountService @@ -132,8 +95,8 @@ export class MailComponent takeUntil(this.unsubscribeNotifier), tap((r) => { this.mailAccounts = r.results - this.loadingAccounts = false - this.showAccounts = true + this.loadingAccounts.set(false) + this.showAccounts.set(true) if (this.oAuthAccountId) { this.editMailAccount( this.mailAccounts.find( @@ -145,7 +108,7 @@ export class MailComponent ) .subscribe({ error: (e) => { - this.loadingAccounts = false + this.loadingAccounts.set(false) this.toastService.showError( $localize`Error retrieving mail accounts`, e @@ -159,14 +122,14 @@ export class MailComponent first(), takeUntil(this.unsubscribeNotifier), tap((r) => { - this.mailRules = r.results - this.loadingRules = false - this.showRules = true + this.mailRules.set(r.results) + this.loadingRules.set(false) + this.showRules.set(true) }) ) .subscribe({ error: (e) => { - this.loadingRules = false + this.loadingRules.set(false) this.toastService.showError($localize`Error retrieving mail rules`, e) }, }) @@ -292,7 +255,7 @@ export class MailComponent this.mailRuleService .listAll(null, null, { full_perms: true }) .subscribe((r) => { - this.mailRules = r.results + this.mailRules.set(r.results) }) }) modal.componentInstance.failed @@ -348,7 +311,7 @@ export class MailComponent this.mailRuleService .listAll(null, null, { full_perms: true }) .subscribe((r) => { - this.mailRules = r.results + this.mailRules.set(r.results) }) }, error: (e) => { diff --git a/src-ui/src/app/services/document-list-view.service.spec.ts b/src-ui/src/app/services/document-list-view.service.spec.ts index 30cb8c701..85c66268f 100644 --- a/src-ui/src/app/services/document-list-view.service.spec.ts +++ b/src-ui/src/app/services/document-list-view.service.spec.ts @@ -705,11 +705,16 @@ describe('DocumentListViewService', () => { const customFields = ['custom_field_1', 'custom_field_2'] documentListViewService.displayFields = customFields as any expect(documentListViewService.displayFields).toEqual(customFields) - jest.spyOn(settingsService, 'allDisplayFields', 'get').mockReturnValue([ + const mockDisplayFields = [ { id: DisplayField.ADDED, name: 'Added' }, { id: DisplayField.TITLE, name: 'Title' }, { id: 'custom_field_1', name: 'Custom Field 1' }, - ] as any) + ] as any + jest + .spyOn(settingsService.allDisplayFields, 'toString') + .mockReturnValue(mockDisplayFields.toString() as any) + // Mock the signal directly by overriding its value + settingsService.allDisplayFields = jest.fn(() => mockDisplayFields) as any settingsService.displayFieldsInit.emit(true) expect(documentListViewService.displayFields).toEqual(['custom_field_1']) diff --git a/src-ui/src/app/services/rest/saved-view.service.ts b/src-ui/src/app/services/rest/saved-view.service.ts index dea02cd0c..556f11aad 100644 --- a/src-ui/src/app/services/rest/saved-view.service.ts +++ b/src-ui/src/app/services/rest/saved-view.service.ts @@ -17,8 +17,10 @@ export class SavedViewService extends AbstractPaperlessService { private settingsService = inject(SettingsService) private documentService = inject(DocumentService) - private savedViewsSignal = signal([]) - private savedViewDocumentCountsSignal = signal>(new Map()) + private readonly savedViews = signal([]) + private readonly savedViewDocumentCounts = signal>( + new Map() + ) private unsubscribeNotifier: Subject = new Subject() constructor() { @@ -26,22 +28,6 @@ export class SavedViewService extends AbstractPaperlessService { this.resourceName = 'saved_views' } - private get savedViews(): SavedView[] { - return this.savedViewsSignal() - } - - private set savedViews(views: SavedView[]) { - this.savedViewsSignal.set(views) - } - - private get savedViewDocumentCounts(): Map { - return this.savedViewDocumentCountsSignal() - } - - private set savedViewDocumentCounts(counts: Map) { - this.savedViewDocumentCountsSignal.set(counts) - } - public list( page?: number, pageSize?: number, @@ -53,15 +39,16 @@ export class SavedViewService extends AbstractPaperlessService { tap({ next: (r) => { const views = r.results.map((view) => this.withUserVisibility(view)) - this.savedViews = views + this.savedViews.set(views) r.results = views this._loading = false - this.settingsService.dashboardIsEmpty = + this.settingsService.dashboardIsEmpty.set( this.dashboardViews.length === 0 + ) }, error: () => { this._loading = false - this.settingsService.dashboardIsEmpty = true + this.settingsService.dashboardIsEmpty.set(true) }, }) ) @@ -79,8 +66,8 @@ export class SavedViewService extends AbstractPaperlessService { .subscribe() } - get allViews() { - return this.savedViews + get allViews(): SavedView[] { + return this.savedViews() } private getVisibleViewIds(setting: string): number[] { @@ -111,7 +98,9 @@ export class SavedViewService extends AbstractPaperlessService { } get sidebarViews(): SavedView[] { - const sidebarViews = this.savedViews.filter((v) => this.isSidebarVisible(v)) + const sidebarViews = this.savedViews().filter((v) => + this.isSidebarVisible(v) + ) const sorted: number[] = this.settingsService.get( SETTINGS_KEYS.SIDEBAR_VIEWS_SORT_ORDER @@ -126,7 +115,7 @@ export class SavedViewService extends AbstractPaperlessService { } get dashboardViews(): SavedView[] { - const dashboardViews = this.savedViews.filter((v) => + const dashboardViews = this.savedViews().filter((v) => this.isDashboardVisible(v) ) @@ -192,12 +181,12 @@ export class SavedViewService extends AbstractPaperlessService { } public setDocumentCount(view: SavedView, count: number) { - const counts = new Map(this.savedViewDocumentCounts) + const counts = new Map(this.savedViewDocumentCounts()) counts.set(view.id, count) - this.savedViewDocumentCounts = counts + this.savedViewDocumentCounts.set(counts) } public getDocumentCount(view: SavedView): number { - return this.savedViewDocumentCounts.get(view.id) + return this.savedViewDocumentCounts().get(view.id) } } diff --git a/src-ui/src/app/services/settings.service.ts b/src-ui/src/app/services/settings.service.ts index 4bd96a3e7..1b14024ef 100644 --- a/src-ui/src/app/services/settings.service.ts +++ b/src-ui/src/app/services/settings.service.ts @@ -296,15 +296,7 @@ export class SettingsService { private settings: Record = {} private settingsVersion = signal(0) - private currentUserSignal = signal(undefined) - - get currentUser(): User { - return this.currentUserSignal() - } - - set currentUser(value: User) { - this.currentUserSignal.set(value) - } + readonly currentUser = signal(undefined) public settingsSaved: EventEmitter = new EventEmitter() @@ -313,49 +305,14 @@ export class SettingsService { return this._renderer } - private dashboardIsEmptySignal = signal(false) - private globalDropzoneEnabledSignal = signal(true) - private globalDropzoneActiveSignal = signal(false) - private organizingSidebarSavedViewsSignal = signal(false) + readonly dashboardIsEmpty = signal(false) + readonly globalDropzoneEnabled = signal(true) + readonly globalDropzoneActive = signal(false) + readonly organizingSidebarSavedViews = signal(false) - public get dashboardIsEmpty(): boolean { - return this.dashboardIsEmptySignal() - } - - public set dashboardIsEmpty(value: boolean) { - this.dashboardIsEmptySignal.set(value) - } - - public get globalDropzoneEnabled(): boolean { - return this.globalDropzoneEnabledSignal() - } - - public set globalDropzoneEnabled(value: boolean) { - this.globalDropzoneEnabledSignal.set(value) - } - - public get globalDropzoneActive(): boolean { - return this.globalDropzoneActiveSignal() - } - - public set globalDropzoneActive(value: boolean) { - this.globalDropzoneActiveSignal.set(value) - } - - public get organizingSidebarSavedViews(): boolean { - return this.organizingSidebarSavedViewsSignal() - } - - public set organizingSidebarSavedViews(value: boolean) { - this.organizingSidebarSavedViewsSignal.set(value) - } - - private allDisplayFieldsSignal = signal< - Array<{ id: DisplayField; name: string }> - >(DEFAULT_DISPLAY_FIELDS) - public get allDisplayFields(): Array<{ id: DisplayField; name: string }> { - return this.allDisplayFieldsSignal() - } + readonly allDisplayFields = signal>( + DEFAULT_DISPLAY_FIELDS + ) public displayFieldsInit: EventEmitter = new EventEmitter() constructor() { @@ -409,7 +366,7 @@ export class SettingsService { // to update lang cookie if (this.settings['language']?.length) this.setLanguage(this.settings['language']) - this.currentUser = uisettings.user + this.currentUser.set(uisettings.user) this.permissionsService.initialize( uisettings.permissions, this.currentUser @@ -453,7 +410,7 @@ export class SettingsService { : null }).filter((f) => f) - this.allDisplayFieldsSignal.set(displayFields) + this.allDisplayFields.set(displayFields) if ( this.permissionsService.currentUserCan( @@ -481,8 +438,8 @@ export class SettingsService { get displayName(): string { return ( - this.currentUser?.first_name ?? - this.currentUser?.username ?? + this.currentUser()?.first_name ?? + this.currentUser()?.username ?? '' ).trim() } @@ -615,7 +572,7 @@ export class SettingsService { // special case to fallback if (key === SETTINGS_KEYS.DEFAULT_PERMS_OWNER && value === undefined) { - return this.currentUser?.id + return this.currentUser()?.id } if (value !== undefined) { diff --git a/src-ui/src/app/services/websocket-status.service.spec.ts b/src-ui/src/app/services/websocket-status.service.spec.ts index d6fdbb07d..50ea62c53 100644 --- a/src-ui/src/app/services/websocket-status.service.spec.ts +++ b/src-ui/src/app/services/websocket-status.service.spec.ts @@ -45,11 +45,11 @@ describe('ConsumerStatusService', () => { httpTestingController = TestBed.inject(HttpTestingController) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { + settingsService.currentUser.set({ id: 1, username: 'testuser', is_superuser: false, - } + }) websocketStatusService = TestBed.inject(WebsocketStatusService) documentService = TestBed.inject(DocumentService) }) @@ -356,12 +356,12 @@ describe('ConsumerStatusService', () => { }) it('should notify user if user can view or is in group', () => { - settingsService.currentUser = { + settingsService.currentUser.set({ id: 1, username: 'testuser', is_superuser: false, groups: [1], - } + }) websocketStatusService.connect() server.send({ type: WebsocketStatusType.STATUS_UPDATE, diff --git a/src-ui/src/app/services/websocket-status.service.ts b/src-ui/src/app/services/websocket-status.service.ts index 9e09522b7..de4b24903 100644 --- a/src-ui/src/app/services/websocket-status.service.ts +++ b/src-ui/src/app/services/websocket-status.service.ts @@ -211,7 +211,7 @@ export class WebsocketStatusService { groups_can_view?: number[] }): boolean { // see paperless.consumers.StatusConsumer._can_view - const user: User = this.settingsService.currentUser + const user: User = this.settingsService.currentUser() return ( !messageData.owner_id || user.is_superuser ||