Add setting, trigger global refresh when needed

This commit is contained in:
shamoon 2025-06-20 11:35:54 -07:00
parent 516dcdcc9b
commit 6e1918a425
No known key found for this signature in database
10 changed files with 47 additions and 13 deletions

View File

@ -176,6 +176,7 @@
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<pngx-input-check i18n-title title="Show warning when closing saved views with unsaved changes" formControlName="savedViewsWarnOnUnsavedChange"></pngx-input-check> <pngx-input-check i18n-title title="Show warning when closing saved views with unsaved changes" formControlName="savedViewsWarnOnUnsavedChange"></pngx-input-check>
<pngx-input-check i18n-title title="Show document counts in sidebar saved views" formControlName="sidebarViewsShowCount"></pngx-input-check>
</div> </div>
</div> </div>

View File

@ -212,7 +212,7 @@ describe('SettingsComponent', () => {
expect(toastErrorSpy).toHaveBeenCalled() expect(toastErrorSpy).toHaveBeenCalled()
expect(storeSpy).toHaveBeenCalled() expect(storeSpy).toHaveBeenCalled()
expect(appearanceSettingsSpy).not.toHaveBeenCalled() expect(appearanceSettingsSpy).not.toHaveBeenCalled()
expect(setSpy).toHaveBeenCalledTimes(29) expect(setSpy).toHaveBeenCalledTimes(30)
// succeed // succeed
storeSpy.mockReturnValueOnce(of(true)) storeSpy.mockReturnValueOnce(of(true))

View File

@ -49,6 +49,7 @@ import {
PermissionsService, PermissionsService,
} from 'src/app/services/permissions.service' } from 'src/app/services/permissions.service'
import { GroupService } from 'src/app/services/rest/group.service' import { GroupService } from 'src/app/services/rest/group.service'
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
import { UserService } from 'src/app/services/rest/user.service' import { UserService } from 'src/app/services/rest/user.service'
import { import {
LanguageOption, LanguageOption,
@ -138,6 +139,7 @@ export class SettingsComponent
notificationsConsumerSuppressOnDashboard: new FormControl(null), notificationsConsumerSuppressOnDashboard: new FormControl(null),
savedViewsWarnOnUnsavedChange: new FormControl(null), savedViewsWarnOnUnsavedChange: new FormControl(null),
sidebarViewsShowCount: new FormControl(null),
}) })
SettingsNavIDs = SettingsNavIDs SettingsNavIDs = SettingsNavIDs
@ -192,11 +194,13 @@ export class SettingsComponent
private router: Router, private router: Router,
public permissionsService: PermissionsService, public permissionsService: PermissionsService,
private modalService: NgbModal, private modalService: NgbModal,
private systemStatusService: SystemStatusService private systemStatusService: SystemStatusService,
private savedViewsService: SavedViewService
) { ) {
super() super()
this.settings.settingsSaved.subscribe(() => { this.settings.settingsSaved.subscribe(() => {
if (!this.savePending) this.initialize() if (!this.savePending) this.initialize()
this.savedViewsService.maybeRefreshDocumentCounts()
}) })
} }
@ -308,6 +312,9 @@ export class SettingsComponent
savedViewsWarnOnUnsavedChange: this.settings.get( savedViewsWarnOnUnsavedChange: this.settings.get(
SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE
), ),
sidebarViewsShowCount: this.settings.get(
SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT
),
defaultPermsOwner: this.settings.get(SETTINGS_KEYS.DEFAULT_PERMS_OWNER), defaultPermsOwner: this.settings.get(SETTINGS_KEYS.DEFAULT_PERMS_OWNER),
defaultPermsViewUsers: this.settings.get( defaultPermsViewUsers: this.settings.get(
SETTINGS_KEYS.DEFAULT_PERMS_VIEW_USERS SETTINGS_KEYS.DEFAULT_PERMS_VIEW_USERS
@ -485,6 +492,10 @@ export class SettingsComponent
SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE, SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE,
this.settingsForm.value.savedViewsWarnOnUnsavedChange this.settingsForm.value.savedViewsWarnOnUnsavedChange
) )
this.settings.set(
SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT,
this.settingsForm.value.sidebarViewsShowCount
)
this.settings.set( this.settings.set(
SETTINGS_KEYS.DEFAULT_PERMS_OWNER, SETTINGS_KEYS.DEFAULT_PERMS_OWNER,
this.settingsForm.value.defaultPermsOwner this.settingsForm.value.defaultPermsOwner

View File

@ -113,11 +113,11 @@
[disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave"
popoverClass="popover-slim"> popoverClass="popover-slim">
<i-bs class="me-1" name="funnel"></i-bs><span>&nbsp;{{view.name}} <i-bs class="me-1" name="funnel"></i-bs><span>&nbsp;{{view.name}}
@if (!slimSidebarEnabled) { @if (showSidebarCounts && !slimSidebarEnabled) {
<span><span class="badge bg-info text-dark ms-2 d-inline">{{ savedViewService.getDocumentCount(view) }}</span></span> <span><span class="badge bg-info text-dark ms-2 d-inline">{{ savedViewService.getDocumentCount(view) }}</span></span>
} }
</span> </span>
@if (slimSidebarEnabled) { @if (showSidebarCounts && slimSidebarEnabled) {
<span class="badge bg-info text-dark position-absolute top-0 end-0 d-none d-md-block">{{ savedViewService.getDocumentCount(view) }}</span> <span class="badge bg-info text-dark position-absolute top-0 end-0 d-none d-md-block">{{ savedViewService.getDocumentCount(view) }}</span>
} }
</a> </a>

View File

@ -104,9 +104,7 @@ export class AppFrameComponent
) )
) { ) {
this.savedViewService.reload(() => { this.savedViewService.reload(() => {
this.savedViewService.getDocumentCounts( this.savedViewService.maybeRefreshDocumentCounts()
this.savedViewService.sidebarViews
)
}) })
} }
} }
@ -288,4 +286,8 @@ export class AppFrameComponent
onLogout() { onLogout() {
this.openDocumentsService.closeAll() this.openDocumentsService.closeAll()
} }
get showSidebarCounts(): boolean {
return this.settingsService.get(SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT)
}
} }

View File

@ -73,6 +73,7 @@ import { CorrespondentService } from 'src/app/services/rest/correspondent.servic
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service' import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service' import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { DocumentService } from 'src/app/services/rest/document.service' import { DocumentService } from 'src/app/services/rest/document.service'
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
import { StoragePathService } from 'src/app/services/rest/storage-path.service' import { StoragePathService } from 'src/app/services/rest/storage-path.service'
import { UserService } from 'src/app/services/rest/user.service' import { UserService } from 'src/app/services/rest/user.service'
import { SettingsService } from 'src/app/services/settings.service' import { SettingsService } from 'src/app/services/settings.service'
@ -278,7 +279,8 @@ export class DocumentDetailComponent
private http: HttpClient, private http: HttpClient,
private hotKeyService: HotKeyService, private hotKeyService: HotKeyService,
private componentRouterService: ComponentRouterService, private componentRouterService: ComponentRouterService,
private deviceDetectorService: DeviceDetectorService private deviceDetectorService: DeviceDetectorService,
private savedViewService: SavedViewService
) { ) {
super() super()
} }
@ -845,6 +847,7 @@ export class DocumentDetailComponent
} else { } else {
this.openDocumentService.refreshDocument(this.documentId) this.openDocumentService.refreshDocument(this.documentId)
} }
this.savedViewService.maybeRefreshDocumentCounts()
}, },
error: (error) => { error: (error) => {
this.networkActive = false this.networkActive = false
@ -1192,6 +1195,7 @@ export class DocumentDetailComponent
notesUpdated(notes: DocumentNote[]) { notesUpdated(notes: DocumentNote[]) {
this.document.notes = notes this.document.notes = notes
this.openDocumentService.refreshDocument(this.documentId) this.openDocumentService.refreshDocument(this.documentId)
this.savedViewService.maybeRefreshDocumentCounts()
} }
get userIsOwner(): boolean { get userIsOwner(): boolean {

View File

@ -32,6 +32,7 @@ import {
DocumentService, DocumentService,
SelectionDataItem, SelectionDataItem,
} from 'src/app/services/rest/document.service' } from 'src/app/services/rest/document.service'
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
import { StoragePathService } from 'src/app/services/rest/storage-path.service' import { StoragePathService } from 'src/app/services/rest/storage-path.service'
import { TagService } from 'src/app/services/rest/tag.service' import { TagService } from 'src/app/services/rest/tag.service'
import { SettingsService } from 'src/app/services/settings.service' import { SettingsService } from 'src/app/services/settings.service'
@ -106,7 +107,8 @@ export class BulkEditorComponent
private toastService: ToastService, private toastService: ToastService,
private storagePathService: StoragePathService, private storagePathService: StoragePathService,
private customFieldService: CustomFieldsService, private customFieldService: CustomFieldsService,
private permissionService: PermissionsService private permissionService: PermissionsService,
public savedViewService: SavedViewService
) { ) {
super() super()
} }
@ -274,6 +276,7 @@ export class BulkEditorComponent
this.list.selected.forEach((id) => { this.list.selected.forEach((id) => {
this.openDocumentService.refreshDocument(id) this.openDocumentService.refreshDocument(id)
}) })
this.savedViewService.maybeRefreshDocumentCounts()
if (modal) { if (modal) {
modal.close() modal.close()
} }

View File

@ -58,6 +58,8 @@ export const SETTINGS_KEYS = {
'general-settings:saved-views:dashboard-views-sort-order', 'general-settings:saved-views:dashboard-views-sort-order',
SIDEBAR_VIEWS_SORT_ORDER: SIDEBAR_VIEWS_SORT_ORDER:
'general-settings:saved-views:sidebar-views-sort-order', 'general-settings:saved-views:sidebar-views-sort-order',
SIDEBAR_VIEWS_SHOW_COUNT:
'general-settings:saved-views:sidebar-views-show-count',
TOUR_COMPLETE: 'general-settings:tour-complete', TOUR_COMPLETE: 'general-settings:tour-complete',
DEFAULT_PERMS_OWNER: 'general-settings:permissions:default-owner', DEFAULT_PERMS_OWNER: 'general-settings:permissions:default-owner',
DEFAULT_PERMS_VIEW_USERS: 'general-settings:permissions:default-view-users', DEFAULT_PERMS_VIEW_USERS: 'general-settings:permissions:default-view-users',
@ -227,6 +229,11 @@ export const SETTINGS: UiSetting[] = [
type: 'array', type: 'array',
default: [], default: [],
}, },
{
key: SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT,
type: 'boolean',
default: true,
},
{ {
key: SETTINGS_KEYS.APP_LOGO, key: SETTINGS_KEYS.APP_LOGO,
type: 'string', type: 'string',

View File

@ -235,7 +235,7 @@ describe(`Additional service tests for SavedViewService`, () => {
}) })
it('should support getting document counts for views', () => { it('should support getting document counts for views', () => {
service.getDocumentCounts(saved_views) service.maybeRefreshDocumentCounts(saved_views)
saved_views.forEach((saved_view) => { saved_views.forEach((saved_view) => {
const req = httpTestingController.expectOne( const req = httpTestingController.expectOne(
`${environment.apiBaseUrl}documents/?page=1&page_size=1&ordering=-${saved_view.sort_field}&fields=id&truncate_content=true` `${environment.apiBaseUrl}documents/?page=1&page_size=1&ordering=-${saved_view.sort_field}&fields=id&truncate_content=true`

View File

@ -1,7 +1,7 @@
import { HttpClient } from '@angular/common/http' import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { combineLatest, Observable } from 'rxjs' import { combineLatest, Observable, Subject } from 'rxjs'
import { tap } from 'rxjs/operators' import { takeUntil, tap } from 'rxjs/operators'
import { Results } from 'src/app/data/results' import { Results } from 'src/app/data/results'
import { SavedView } from 'src/app/data/saved-view' import { SavedView } from 'src/app/data/saved-view'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings' import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
@ -16,6 +16,7 @@ export class SavedViewService extends AbstractPaperlessService<SavedView> {
public loading: boolean = true public loading: boolean = true
private savedViews: SavedView[] = [] private savedViews: SavedView[] = []
private savedViewDocumentCounts: Map<number, number> = new Map() private savedViewDocumentCounts: Map<number, number> = new Map()
private unsubscribeNotifier: Subject<void> = new Subject<void>()
constructor( constructor(
protected http: HttpClient, protected http: HttpClient,
@ -121,7 +122,11 @@ export class SavedViewService extends AbstractPaperlessService<SavedView> {
return super.delete(o).pipe(tap(() => this.reload())) return super.delete(o).pipe(tap(() => this.reload()))
} }
getDocumentCounts(views: SavedView[]) { public maybeRefreshDocumentCounts(views: SavedView[] = this.sidebarViews) {
if (!this.settingsService.get(SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT)) {
return
}
this.unsubscribeNotifier.next() // clear previous subscriptions
views.forEach((view) => { views.forEach((view) => {
this.documentService this.documentService
.listFiltered( .listFiltered(
@ -132,6 +137,7 @@ export class SavedViewService extends AbstractPaperlessService<SavedView> {
view.filter_rules, view.filter_rules,
{ fields: 'id', truncate_content: true } { fields: 'id', truncate_content: true }
) )
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe((results: Results<Document>) => { .subscribe((results: Results<Document>) => {
this.savedViewDocumentCounts.set(view.id, results.count) this.savedViewDocumentCounts.set(view.id, results.count)
}) })