mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-02-22 00:49:35 -06:00
Make views use UISettings visibility instead of model
This commit is contained in:
@@ -7,11 +7,13 @@ import { NgbModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||
import { Subject, of, throwError } from 'rxjs'
|
||||
import { SavedView } from 'src/app/data/saved-view'
|
||||
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
||||
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
||||
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
|
||||
import { PermissionsService } from 'src/app/services/permissions.service'
|
||||
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
|
||||
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
import { ToastService } from 'src/app/services/toast.service'
|
||||
import { ConfirmButtonComponent } from '../../common/confirm-button/confirm-button.component'
|
||||
import { CheckComponent } from '../../common/input/check/check.component'
|
||||
@@ -31,6 +33,7 @@ describe('SavedViewsComponent', () => {
|
||||
let component: SavedViewsComponent
|
||||
let fixture: ComponentFixture<SavedViewsComponent>
|
||||
let savedViewService: SavedViewService
|
||||
let settingsService: SettingsService
|
||||
let toastService: ToastService
|
||||
let modalService: NgbModal
|
||||
|
||||
@@ -79,6 +82,7 @@ describe('SavedViewsComponent', () => {
|
||||
}).compileComponents()
|
||||
|
||||
savedViewService = TestBed.inject(SavedViewService)
|
||||
settingsService = TestBed.inject(SettingsService)
|
||||
toastService = TestBed.inject(ToastService)
|
||||
modalService = TestBed.inject(NgbModal)
|
||||
fixture = TestBed.createComponent(SavedViewsComponent)
|
||||
@@ -97,13 +101,12 @@ describe('SavedViewsComponent', () => {
|
||||
|
||||
it('should support save saved views, show error', () => {
|
||||
const toastErrorSpy = jest.spyOn(toastService, 'showError')
|
||||
const toastSpy = jest.spyOn(toastService, 'show')
|
||||
const savedViewPatchSpy = jest.spyOn(savedViewService, 'patchMany')
|
||||
const control = component.savedViewsForm
|
||||
.get('savedViews')
|
||||
.get(savedViews[0].id.toString())
|
||||
.get('show_on_dashboard')
|
||||
control.setValue(!savedViews[0].show_on_dashboard)
|
||||
.get('name')
|
||||
control.setValue(`${savedViews[0].name}-changed`)
|
||||
control.markAsDirty()
|
||||
|
||||
// saved views error first
|
||||
@@ -113,13 +116,12 @@ describe('SavedViewsComponent', () => {
|
||||
component.save()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
expect(savedViewPatchSpy).toHaveBeenCalled()
|
||||
toastSpy.mockClear()
|
||||
toastErrorSpy.mockClear()
|
||||
savedViewPatchSpy.mockClear()
|
||||
|
||||
// succeed saved views
|
||||
savedViewPatchSpy.mockReturnValueOnce(of(savedViews as SavedView[]))
|
||||
control.setValue(savedViews[0].show_on_dashboard)
|
||||
control.setValue(savedViews[0].name)
|
||||
control.markAsDirty()
|
||||
component.save()
|
||||
expect(toastErrorSpy).not.toHaveBeenCalled()
|
||||
@@ -135,24 +137,52 @@ describe('SavedViewsComponent', () => {
|
||||
component.savedViewsForm
|
||||
.get('savedViews')
|
||||
.get(view.id.toString())
|
||||
.get('show_on_dashboard')
|
||||
.setValue(!view.show_on_dashboard)
|
||||
.get('name')
|
||||
.setValue('changed-view-name')
|
||||
component.savedViewsForm
|
||||
.get('savedViews')
|
||||
.get(view.id.toString())
|
||||
.get('show_on_dashboard')
|
||||
.get('name')
|
||||
.markAsDirty()
|
||||
fixture.detectChanges()
|
||||
|
||||
component.save()
|
||||
expect(patchSpy).toHaveBeenCalledWith([
|
||||
expect.objectContaining({
|
||||
id: view.id,
|
||||
name: view.name,
|
||||
show_in_sidebar: view.show_in_sidebar,
|
||||
show_on_dashboard: !view.show_on_dashboard,
|
||||
}),
|
||||
])
|
||||
expect(patchSpy).toHaveBeenCalled()
|
||||
const patchBody = patchSpy.mock.calls[0][0][0]
|
||||
expect(patchBody).toMatchObject({
|
||||
id: view.id,
|
||||
name: 'changed-view-name',
|
||||
})
|
||||
expect(patchBody.show_on_dashboard).toBeUndefined()
|
||||
expect(patchBody.show_in_sidebar).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should persist visibility changes to user settings', () => {
|
||||
const patchSpy = jest.spyOn(savedViewService, 'patchMany')
|
||||
const setSpy = jest.spyOn(settingsService, 'set')
|
||||
const storeSpy = jest
|
||||
.spyOn(settingsService, 'storeSettings')
|
||||
.mockReturnValue(of({ success: true }))
|
||||
|
||||
const dashboardControl = component.savedViewsForm
|
||||
.get('savedViews')
|
||||
.get(savedViews[0].id.toString())
|
||||
.get('show_on_dashboard')
|
||||
dashboardControl.setValue(false)
|
||||
dashboardControl.markAsDirty()
|
||||
|
||||
component.save()
|
||||
|
||||
expect(patchSpy).not.toHaveBeenCalled()
|
||||
expect(setSpy).toHaveBeenCalledWith(
|
||||
SETTINGS_KEYS.DASHBOARD_VIEWS_VISIBLE_IDS,
|
||||
[]
|
||||
)
|
||||
expect(setSpy).toHaveBeenCalledWith(
|
||||
SETTINGS_KEYS.SIDEBAR_VIEWS_VISIBLE_IDS,
|
||||
[savedViews[0].id]
|
||||
)
|
||||
expect(storeSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should support delete saved view', () => {
|
||||
|
||||
@@ -8,10 +8,11 @@ import {
|
||||
} from '@angular/forms'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { dirtyCheck } from '@ngneat/dirty-check-forms'
|
||||
import { BehaviorSubject, Observable, takeUntil } from 'rxjs'
|
||||
import { BehaviorSubject, Observable, of, switchMap, takeUntil } from 'rxjs'
|
||||
import { PermissionsDialogComponent } from 'src/app/components/common/permissions-dialog/permissions-dialog.component'
|
||||
import { DisplayMode } from 'src/app/data/document'
|
||||
import { SavedView } from 'src/app/data/saved-view'
|
||||
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
||||
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
||||
import {
|
||||
PermissionAction,
|
||||
@@ -107,9 +108,9 @@ export class SavedViewsComponent
|
||||
name: new FormControl({ value: null, disabled: !canEdit }),
|
||||
show_on_dashboard: new FormControl({
|
||||
value: null,
|
||||
disabled: !canEdit,
|
||||
disabled: false,
|
||||
}),
|
||||
show_in_sidebar: new FormControl({ value: null, disabled: !canEdit }),
|
||||
show_in_sidebar: new FormControl({ value: null, disabled: false }),
|
||||
page_size: new FormControl({ value: null, disabled: !canEdit }),
|
||||
display_mode: new FormControl({ value: null, disabled: !canEdit }),
|
||||
display_fields: new FormControl({ value: [], disabled: !canEdit }),
|
||||
@@ -153,27 +154,80 @@ export class SavedViewsComponent
|
||||
}
|
||||
|
||||
public save() {
|
||||
// only patch views that have actually changed
|
||||
// Save only changed views, then save the visibility changes into user settings.
|
||||
const groups = Object.values(this.savedViewsGroup.controls) as FormGroup[]
|
||||
const visibilityChanged = groups.some(
|
||||
(group) =>
|
||||
group.get('show_on_dashboard')?.dirty ||
|
||||
group.get('show_in_sidebar')?.dirty
|
||||
)
|
||||
|
||||
const changed: SavedView[] = []
|
||||
Object.values(this.savedViewsGroup.controls)
|
||||
.filter((g: FormGroup) => g.enabled && !g.pristine)
|
||||
.forEach((group: FormGroup) => {
|
||||
changed.push(group.getRawValue())
|
||||
})
|
||||
if (changed.length) {
|
||||
this.savedViewService.patchMany(changed).subscribe({
|
||||
next: () => {
|
||||
this.toastService.showInfo($localize`Views saved successfully.`)
|
||||
this.reloadViews()
|
||||
},
|
||||
error: (error) => {
|
||||
this.toastService.showError(
|
||||
$localize`Error while saving views.`,
|
||||
error
|
||||
)
|
||||
},
|
||||
})
|
||||
const dashboardVisibleIds: number[] = []
|
||||
const sidebarVisibleIds: number[] = []
|
||||
|
||||
groups.forEach((group) => {
|
||||
const value = group.getRawValue()
|
||||
if (value.show_on_dashboard) {
|
||||
dashboardVisibleIds.push(value.id)
|
||||
}
|
||||
if (value.show_in_sidebar) {
|
||||
sidebarVisibleIds.push(value.id)
|
||||
}
|
||||
|
||||
if (!group.get('name')?.enabled) {
|
||||
// Quick check for user doesn't have permissions, then bail
|
||||
return
|
||||
}
|
||||
|
||||
const modelFieldsChanged =
|
||||
group.get('name')?.dirty ||
|
||||
group.get('page_size')?.dirty ||
|
||||
group.get('display_mode')?.dirty ||
|
||||
group.get('display_fields')?.dirty
|
||||
|
||||
if (!modelFieldsChanged) {
|
||||
return
|
||||
}
|
||||
|
||||
delete value.show_on_dashboard
|
||||
delete value.show_in_sidebar
|
||||
changed.push(value)
|
||||
})
|
||||
if (!changed.length && !visibilityChanged) {
|
||||
return
|
||||
}
|
||||
|
||||
// First save only changed views
|
||||
let saveOperation = of([])
|
||||
if (changed.length) {
|
||||
saveOperation = saveOperation.pipe(
|
||||
switchMap(() => this.savedViewService.patchMany(changed))
|
||||
)
|
||||
}
|
||||
// Then save the visibility changes in the settings
|
||||
if (visibilityChanged) {
|
||||
this.settings.set(SETTINGS_KEYS.DASHBOARD_VIEWS_VISIBLE_IDS, [
|
||||
...new Set(dashboardVisibleIds),
|
||||
])
|
||||
this.settings.set(SETTINGS_KEYS.SIDEBAR_VIEWS_VISIBLE_IDS, [
|
||||
...new Set(sidebarVisibleIds),
|
||||
])
|
||||
saveOperation = saveOperation.pipe(
|
||||
switchMap(() => this.settings.storeSettings())
|
||||
)
|
||||
}
|
||||
|
||||
saveOperation.subscribe({
|
||||
next: () => {
|
||||
this.toastService.showInfo($localize`Views saved successfully.`)
|
||||
this.savedViewService.clearCache()
|
||||
this.reloadViews()
|
||||
},
|
||||
error: (error) => {
|
||||
this.toastService.showError($localize`Error while saving views.`, error)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
public canEditSavedView(view: SavedView): boolean {
|
||||
@@ -223,7 +277,7 @@ export class SavedViewsComponent
|
||||
private reloadViews(): void {
|
||||
this.loading = true
|
||||
this.savedViewService
|
||||
.listAll(null, null, { full_perms: true })
|
||||
.list(1, 100000, null, null, { full_perms: true })
|
||||
.subscribe((r) => {
|
||||
this.savedViews = r.results
|
||||
this.initialize()
|
||||
|
||||
@@ -36,7 +36,9 @@ export class SavedViewService extends AbstractPaperlessService<SavedView> {
|
||||
return super.list(page, pageSize, sortField, sortReverse, extraParams).pipe(
|
||||
tap({
|
||||
next: (r) => {
|
||||
this.savedViews = r.results
|
||||
const views = r.results.map((view) => this.withUserVisibility(view))
|
||||
this.savedViews = views
|
||||
r.results = views
|
||||
this._loading = false
|
||||
this.settingsService.dashboardIsEmpty =
|
||||
this.dashboardViews.length === 0
|
||||
@@ -70,6 +72,14 @@ export class SavedViewService extends AbstractPaperlessService<SavedView> {
|
||||
return Array.isArray(configured) ? configured : null
|
||||
}
|
||||
|
||||
private withUserVisibility(view: SavedView): SavedView {
|
||||
return {
|
||||
...view,
|
||||
show_on_dashboard: this.isDashboardVisible(view),
|
||||
show_in_sidebar: this.isSidebarVisible(view),
|
||||
}
|
||||
}
|
||||
|
||||
private isDashboardVisible(view: SavedView): boolean {
|
||||
const visibleIds = this.getVisibleViewIds(
|
||||
SETTINGS_KEYS.DASHBOARD_VIEWS_VISIBLE_IDS
|
||||
|
||||
Reference in New Issue
Block a user