Refactor: loading component

This commit is contained in:
shamoon
2024-12-06 00:26:38 -08:00
parent 0647812699
commit 65b48952cd
27 changed files with 267 additions and 258 deletions

View File

@@ -17,9 +17,9 @@ import {
} from 'src/app/data/paperless-config'
import { ConfigService } from 'src/app/services/config.service'
import { ToastService } from 'src/app/services/toast.service'
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
import { DirtyComponent, dirtyCheck } from '@ngneat/dirty-check-forms'
import { SettingsService } from 'src/app/services/settings.service'
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
@Component({
selector: 'pngx-config',
@@ -27,7 +27,7 @@ import { SettingsService } from 'src/app/services/settings.service'
styleUrl: './config.component.scss',
})
export class ConfigComponent
extends ComponentWithPermissions
extends LoadingComponentWithPermissions
implements OnInit, OnDestroy, DirtyComponent
{
public readonly ConfigOptionType = ConfigOptionType
@@ -45,15 +45,11 @@ export class ConfigComponent
return PaperlessConfigOptions.filter((o) => o.category === category)
}
public loading: boolean = false
initialConfig: PaperlessConfig
store: BehaviorSubject<any>
storeSub: Subscription
isDirty$: Observable<boolean>
private unsubscribeNotifier: Subject<any> = new Subject()
constructor(
private configService: ConfigService,
private toastService: ToastService,
@@ -67,7 +63,6 @@ export class ConfigComponent
}
ngOnInit(): void {
this.loading = true
this.configService
.getConfig()
.pipe(takeUntil(this.unsubscribeNotifier))

View File

@@ -17,7 +17,7 @@
</a>
</li>
}
@if (isLoading || !logFiles.length) {
@if (loading || !logFiles.length) {
<div class="ps-2 d-flex align-items-center">
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
@if (!logFiles.length) {
@@ -30,7 +30,7 @@
<div [ngbNavOutlet]="nav" class="mt-2"></div>
<div class="bg-dark p-3 text-light font-monospace log-container" #logContainer>
@if (isLoading && logFiles.length) {
@if (loading && logFiles.length) {
<div>
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
<ng-container i18n>Loading...</ng-container>

View File

@@ -3,22 +3,28 @@ import {
ElementRef,
OnInit,
ViewChild,
OnDestroy,
ChangeDetectorRef,
OnDestroy,
} from '@angular/core'
import { Subject, takeUntil } from 'rxjs'
import { takeUntil } from 'rxjs'
import { LogService } from 'src/app/services/rest/log.service'
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
@Component({
selector: 'pngx-logs',
templateUrl: './logs.component.html',
styleUrls: ['./logs.component.scss'],
})
export class LogsComponent implements OnInit, OnDestroy {
export class LogsComponent
extends LoadingComponentWithPermissions
implements OnInit, OnDestroy
{
constructor(
private logService: LogService,
private changedetectorRef: ChangeDetectorRef
) {}
) {
super()
}
public logs: string[] = []
@@ -26,22 +32,17 @@ export class LogsComponent implements OnInit, OnDestroy {
public activeLog: string
private unsubscribeNotifier: Subject<any> = new Subject()
public isLoading: boolean = false
public autoRefreshInterval: any
@ViewChild('logContainer') logContainer: ElementRef
ngOnInit(): void {
this.isLoading = true
this.logService
.list()
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe((result) => {
this.logFiles = result
this.isLoading = false
this.loading = false
if (this.logFiles.length > 0) {
this.activeLog = this.logFiles[0]
this.reloadLogs()
@@ -51,25 +52,24 @@ export class LogsComponent implements OnInit, OnDestroy {
}
ngOnDestroy(): void {
this.unsubscribeNotifier.next(true)
this.unsubscribeNotifier.complete()
super.ngOnDestroy()
clearInterval(this.autoRefreshInterval)
}
reloadLogs() {
this.isLoading = true
this.loading = true
this.logService
.get(this.activeLog)
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe({
next: (result) => {
this.logs = result
this.isLoading = false
this.loading = false
this.scrollToBottom()
},
error: () => {
this.logs = []
this.isLoading = false
this.loading = false
},
})
}

View File

@@ -12,7 +12,7 @@ import {
import { PaperlessTask } from 'src/app/data/paperless-task'
import { TasksService } from 'src/app/services/tasks.service'
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
export enum TaskTab {
Queued = 'queued',
@@ -37,7 +37,7 @@ const FILTER_TARGETS = [
styleUrls: ['./tasks.component.scss'],
})
export class TasksComponent
extends ComponentWithPermissions
extends LoadingComponentWithPermissions
implements OnInit, OnDestroy
{
public activeTab: TaskTab
@@ -70,8 +70,6 @@ export class TasksComponent
: FILTER_TARGETS.slice(0, 1)
}
private unsubscribeNotifier: Subject<any> = new Subject()
get dismissButtonText(): string {
return this.selectedTasks.size > 0
? $localize`Dismiss selected`
@@ -101,9 +99,9 @@ export class TasksComponent
}
ngOnDestroy() {
super.ngOnDestroy()
this.tasksService.cancelPending()
clearInterval(this.autoRefreshInterval)
this.unsubscribeNotifier.next(this)
}
dismissTask(task: PaperlessTask) {

View File

@@ -38,7 +38,7 @@
</tr>
</thead>
<tbody>
@if (isLoading) {
@if (loading) {
<tr>
<td colspan="5">
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
@@ -88,7 +88,7 @@
</table>
</div>
@if (!isLoading) {
@if (!loading) {
<div class="d-flex mb-2">
<div>
<ng-container i18n>{totalDocuments, plural, =1 {One document in trash} other {{{totalDocuments || 0}} total documents in trash}}</ng-container>

View File

@@ -4,25 +4,26 @@ import { Document } from 'src/app/data/document'
import { ToastService } from 'src/app/services/toast.service'
import { TrashService } from 'src/app/services/trash.service'
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
import { delay, Subject, takeUntil, tap } from 'rxjs'
import { delay, takeUntil, tap } from 'rxjs'
import { SettingsService } from 'src/app/services/settings.service'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
import { Router } from '@angular/router'
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
@Component({
selector: 'pngx-trash',
templateUrl: './trash.component.html',
styleUrl: './trash.component.scss',
})
export class TrashComponent implements OnDestroy {
export class TrashComponent
extends LoadingComponentWithPermissions
implements OnDestroy
{
public documentsInTrash: Document[] = []
public selectedDocuments: Set<number> = new Set()
public allToggled: boolean = false
public page: number = 1
public totalDocuments: number
public isLoading: boolean = false
public reveal: boolean = false
unsubscribeNotifier: Subject<void> = new Subject()
constructor(
private trashService: TrashService,
@@ -31,16 +32,12 @@ export class TrashComponent implements OnDestroy {
private settingsService: SettingsService,
private router: Router
) {
super()
this.reload()
}
ngOnDestroy() {
this.unsubscribeNotifier.next()
this.unsubscribeNotifier.complete()
}
reload() {
this.isLoading = true
this.loading = true
this.trashService
.getTrash(this.page)
.pipe(
@@ -48,12 +45,12 @@ export class TrashComponent implements OnDestroy {
this.documentsInTrash = r.results
this.totalDocuments = r.count
this.selectedDocuments.clear()
this.loading = false
}),
delay(100)
)
.subscribe(() => {
this.reveal = true
this.isLoading = false
})
}

View File

@@ -1,14 +1,17 @@
import { Component, EventEmitter, Input, Output } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { interval, Subject, take } from 'rxjs'
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
@Component({
selector: 'pngx-confirm-dialog',
templateUrl: './confirm-dialog.component.html',
styleUrls: ['./confirm-dialog.component.scss'],
})
export class ConfirmDialogComponent {
constructor(public activeModal: NgbActiveModal) {}
export class ConfirmDialogComponent extends LoadingComponentWithPermissions {
constructor(public activeModal: NgbActiveModal) {
super()
}
@Output()
public confirmClicked = new EventEmitter()

View File

@@ -25,8 +25,6 @@ export class MergeConfirmDialogComponent
public metadataDocumentID: number = -1
private unsubscribeNotifier: Subject<any> = new Subject()
constructor(
activeModal: NgbActiveModal,
private documentService: DocumentService,

View File

@@ -1,25 +1,22 @@
import { getLocaleCurrencyCode } from '@angular/common'
import {
Component,
Inject,
Input,
LOCALE_ID,
OnDestroy,
OnInit,
} from '@angular/core'
import { Subject, takeUntil } from 'rxjs'
import { Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'
import { takeUntil } from 'rxjs'
import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
import { DisplayField, Document } from 'src/app/data/document'
import { Results } from 'src/app/data/results'
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
import { DocumentService } from 'src/app/services/rest/document.service'
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
@Component({
selector: 'pngx-custom-field-display',
templateUrl: './custom-field-display.component.html',
styleUrl: './custom-field-display.component.scss',
})
export class CustomFieldDisplayComponent implements OnInit, OnDestroy {
export class CustomFieldDisplayComponent
extends LoadingComponentWithPermissions
implements OnInit
{
CustomFieldDataType = CustomFieldDataType
private _document: Document
@@ -61,7 +58,6 @@ export class CustomFieldDisplayComponent implements OnInit, OnDestroy {
private docLinkDocuments: Document[] = []
private unsubscribeNotifier: Subject<any> = new Subject()
private defaultCurrencyCode: any
constructor(
@@ -69,6 +65,7 @@ export class CustomFieldDisplayComponent implements OnInit, OnDestroy {
private documentService: DocumentService,
@Inject(LOCALE_ID) currentLocale: string
) {
super()
this.defaultCurrencyCode = getLocaleCurrencyCode(currentLocale)
this.customFieldService.listAll().subscribe((r) => {
this.customFields = r.results
@@ -120,9 +117,4 @@ export class CustomFieldDisplayComponent implements OnInit, OnDestroy {
public getSelectValue(field: CustomField, id: string): string {
return field.extra_data.select_options?.find((o) => o.id === id)?.label
}
ngOnDestroy(): void {
this.unsubscribeNotifier.next(true)
this.unsubscribeNotifier.complete()
}
}

View File

@@ -10,7 +10,7 @@ import {
ViewChildren,
} from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { Subject, first, takeUntil } from 'rxjs'
import { first, takeUntil } from 'rxjs'
import { CustomField, DATA_TYPE_LABELS } from 'src/app/data/custom-field'
import { CustomFieldInstance } from 'src/app/data/custom-field-instance'
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
@@ -21,13 +21,14 @@ import {
PermissionType,
PermissionsService,
} from 'src/app/services/permissions.service'
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
@Component({
selector: 'pngx-custom-fields-dropdown',
templateUrl: './custom-fields-dropdown.component.html',
styleUrls: ['./custom-fields-dropdown.component.scss'],
})
export class CustomFieldsDropdownComponent implements OnDestroy {
export class CustomFieldsDropdownComponent extends LoadingComponentWithPermissions {
@Input()
documentId: number
@@ -60,8 +61,6 @@ export class CustomFieldsDropdownComponent implements OnDestroy {
public filterText: string
private unsubscribeNotifier: Subject<any> = new Subject()
get canCreateFields(): boolean {
return this.permissionsService.currentUserCan(
PermissionAction.Add,
@@ -75,14 +74,10 @@ export class CustomFieldsDropdownComponent implements OnDestroy {
private toastService: ToastService,
private permissionsService: PermissionsService
) {
super()
this.getFields()
}
ngOnDestroy(): void {
this.unsubscribeNotifier.next(this)
this.unsubscribeNotifier.complete()
}
private getFields() {
this.customFieldsService
.listAll()

View File

@@ -2,7 +2,6 @@ import {
Component,
EventEmitter,
Input,
OnDestroy,
Output,
QueryList,
ViewChild,
@@ -29,6 +28,7 @@ import {
CustomFieldQueryAtom,
} from 'src/app/utils/custom-field-query-element'
import { popperOptionsReenablePreventOverflow } from 'src/app/utils/popper-options'
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
export class CustomFieldQueriesModel {
public queries: CustomFieldQueryElement[] = []
@@ -157,7 +157,7 @@ export class CustomFieldQueriesModel {
templateUrl: './custom-fields-query-dropdown.component.html',
styleUrls: ['./custom-fields-query-dropdown.component.scss'],
})
export class CustomFieldsQueryDropdownComponent implements OnDestroy {
export class CustomFieldsQueryDropdownComponent extends LoadingComponentWithPermissions {
public CustomFieldQueryComponentType = CustomFieldQueryElementType
public CustomFieldQueryOperator = CustomFieldQueryOperator
public CustomFieldDataType = CustomFieldDataType
@@ -223,19 +223,13 @@ export class CustomFieldsQueryDropdownComponent implements OnDestroy {
customFields: CustomField[] = []
private unsubscribeNotifier: Subject<any> = new Subject()
constructor(protected customFieldsService: CustomFieldsService) {
super()
this.selectionModel = new CustomFieldQueriesModel()
this.getFields()
this.reset()
}
ngOnDestroy(): void {
this.unsubscribeNotifier.next(this)
this.unsubscribeNotifier.complete()
}
public onOpenChange(open: boolean) {
if (open) {
if (this.selectionModel.queries.length === 0) {

View File

@@ -2,7 +2,6 @@ import {
AfterViewInit,
Component,
ElementRef,
OnDestroy,
OnInit,
QueryList,
ViewChildren,
@@ -18,7 +17,7 @@ import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service
import { UserService } from 'src/app/services/rest/user.service'
import { SettingsService } from 'src/app/services/settings.service'
import { EditDialogComponent, EditDialogMode } from '../edit-dialog.component'
import { Subject, takeUntil } from 'rxjs'
import { takeUntil } from 'rxjs'
@Component({
selector: 'pngx-custom-field-edit-dialog',
@@ -27,15 +26,13 @@ import { Subject, takeUntil } from 'rxjs'
})
export class CustomFieldEditDialogComponent
extends EditDialogComponent<CustomField>
implements OnInit, AfterViewInit, OnDestroy
implements OnInit, AfterViewInit
{
CustomFieldDataType = CustomFieldDataType
@ViewChildren('selectOption')
private selectOptionInputs: QueryList<ElementRef>
private unsubscribeNotifier: Subject<any> = new Subject()
private get selectOptions(): FormArray {
return (this.objectForm.controls.extra_data as FormGroup).controls
.select_options as FormArray
@@ -78,11 +75,6 @@ export class CustomFieldEditDialogComponent
})
}
ngOnDestroy(): void {
this.unsubscribeNotifier.next(true)
this.unsubscribeNotifier.complete()
}
getCreateTitle() {
return $localize`Create new custom field`
}

View File

@@ -15,6 +15,7 @@ import { UserService } from 'src/app/services/rest/user.service'
import { PermissionsFormObject } from '../input/permissions/permissions-form/permissions-form.component'
import { SettingsService } from 'src/app/services/settings.service'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
export enum EditDialogMode {
CREATE = 0,
@@ -23,15 +24,19 @@ export enum EditDialogMode {
@Directive()
export abstract class EditDialogComponent<
T extends ObjectWithPermissions | ObjectWithId,
> implements OnInit
T extends ObjectWithPermissions | ObjectWithId,
>
extends LoadingComponentWithPermissions
implements OnInit
{
constructor(
protected service: AbstractPaperlessService<T>,
private activeModal: NgbActiveModal,
private userService: UserService,
private settingsService: SettingsService
) {}
) {
super()
}
users: User[]

View File

@@ -41,8 +41,6 @@ export class StoragePathEditDialogComponent
public loading = false
public testLoading = false
private unsubscribeNotifier: Subject<any> = new Subject()
constructor(
service: StoragePathService,
activeModal: NgbActiveModal,
@@ -54,11 +52,6 @@ export class StoragePathEditDialogComponent
this.initPathObservables()
}
ngOnDestroy(): void {
this.unsubscribeNotifier.next(this)
this.unsubscribeNotifier.complete()
}
getCreateTitle() {
return $localize`Create new storage path`
}

View File

@@ -6,7 +6,6 @@ import {
ElementRef,
ViewChild,
OnInit,
OnDestroy,
} from '@angular/core'
import { FilterPipe } from 'src/app/pipes/filter.pipe'
import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
@@ -17,6 +16,7 @@ import { SelectionDataItem } from 'src/app/services/rest/document.service'
import { ObjectWithPermissions } from 'src/app/data/object-with-permissions'
import { HotKeyService } from 'src/app/services/hot-key.service'
import { popperOptionsReenablePreventOverflow } from 'src/app/utils/popper-options'
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
export interface ChangedItems {
itemsToAdd: MatchingModel[]
@@ -353,7 +353,10 @@ export class FilterableDropdownSelectionModel {
templateUrl: './filterable-dropdown.component.html',
styleUrls: ['./filterable-dropdown.component.scss'],
})
export class FilterableDropdownComponent implements OnDestroy, OnInit {
export class FilterableDropdownComponent
extends LoadingComponentWithPermissions
implements OnInit
{
@ViewChild('listFilterTextInput') listFilterTextInput: ElementRef
@ViewChild('dropdown') dropdown: NgbDropdown
@ViewChild('buttonItems') buttonItems: ElementRef
@@ -467,12 +470,11 @@ export class FilterableDropdownComponent implements OnDestroy, OnInit {
private keyboardIndex: number
private unsubscribeNotifier: Subject<any> = new Subject()
constructor(
private filterPipe: FilterPipe,
private hotkeyService: HotKeyService
) {
super()
this.selectionModelChange.subscribe((updatedModel) => {
this.modelIsDirty = updatedModel.isDirty()
})
@@ -495,11 +497,6 @@ export class FilterableDropdownComponent implements OnDestroy, OnInit {
}
}
ngOnDestroy(): void {
this.unsubscribeNotifier.next(true)
this.unsubscribeNotifier.complete()
}
applyClicked() {
if (this.selectionModel.isDirty()) {
this.dropdown.close()

View File

@@ -1,4 +1,4 @@
import { Component, OnDestroy, OnInit } from '@angular/core'
import { Component, OnInit } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { ProfileService } from 'src/app/services/profile.service'
@@ -8,18 +8,21 @@ import {
SocialAccountProvider,
} from 'src/app/data/user-profile'
import { ToastService } from 'src/app/services/toast.service'
import { Subject, takeUntil } from 'rxjs'
import { takeUntil } from 'rxjs'
import { Clipboard } from '@angular/cdk/clipboard'
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
@Component({
selector: 'pngx-profile-edit-dialog',
templateUrl: './profile-edit-dialog.component.html',
styleUrls: ['./profile-edit-dialog.component.scss'],
})
export class ProfileEditDialogComponent implements OnInit, OnDestroy {
export class ProfileEditDialogComponent
extends LoadingComponentWithPermissions
implements OnInit
{
public networkActive: boolean = false
public error: any
private unsubscribeNotifier: Subject<any> = new Subject()
public form = new FormGroup({
email: new FormControl(''),
@@ -60,7 +63,9 @@ export class ProfileEditDialogComponent implements OnInit, OnDestroy {
public activeModal: NgbActiveModal,
private toastService: ToastService,
private clipboard: Clipboard
) {}
) {
super()
}
ngOnInit(): void {
this.networkActive = true
@@ -93,11 +98,6 @@ export class ProfileEditDialogComponent implements OnInit, OnDestroy {
})
}
ngOnDestroy(): void {
this.unsubscribeNotifier.next(true)
this.unsubscribeNotifier.complete()
}
get saveDisabled(): boolean {
return this.error?.password_confirm || this.error?.email_confirm
}

View File

@@ -28,7 +28,6 @@ import {
} from 'src/app/data/filter-rule-type'
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { ComponentWithPermissions } from 'src/app/components/with-permissions/with-permissions.component'
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'
import {
PermissionAction,
@@ -38,6 +37,7 @@ import {
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
import { SettingsService } from 'src/app/services/settings.service'
import { LoadingComponentWithPermissions } from 'src/app/components/loading-component/loading.component'
@Component({
selector: 'pngx-saved-view-widget',
@@ -45,16 +45,13 @@ import { SettingsService } from 'src/app/services/settings.service'
styleUrls: ['./saved-view-widget.component.scss'],
})
export class SavedViewWidgetComponent
extends ComponentWithPermissions
extends LoadingComponentWithPermissions
implements OnInit, OnDestroy
{
public DisplayMode = DisplayMode
public DisplayField = DisplayField
public CustomFieldDataType = CustomFieldDataType
public loading: boolean = true
public reveal: boolean = false
private customFields: CustomField[] = []
constructor(

View File

@@ -1,12 +1,18 @@
import { AfterViewInit, Component, Input } from '@angular/core'
import { LoadingComponentWithPermissions } from 'src/app/components/loading-component/loading.component'
@Component({
selector: 'pngx-widget-frame',
templateUrl: './widget-frame.component.html',
styleUrls: ['./widget-frame.component.scss'],
})
export class WidgetFrameComponent implements AfterViewInit {
constructor() {}
export class WidgetFrameComponent
extends LoadingComponentWithPermissions
implements AfterViewInit
{
constructor() {
super()
}
@Input()
title: string
@@ -17,8 +23,6 @@ export class WidgetFrameComponent implements AfterViewInit {
@Input()
draggable: any
public reveal: boolean = false
ngAfterViewInit(): void {
setTimeout(() => {
this.reveal = true

View File

@@ -0,0 +1,43 @@
import { TestBed } from '@angular/core/testing'
import { Subject } from 'rxjs'
import { LoadingComponentWithPermissions } from './loading.component'
import { ComponentWithPermissions } from '../with-permissions/with-permissions.component'
import { Component } from '@angular/core'
class MockComponentWithPermissions extends ComponentWithPermissions {}
@Component({
template: '',
})
class MockLoadingComponent extends LoadingComponentWithPermissions {}
describe('LoadingComponentWithPermissions', () => {
let component: LoadingComponentWithPermissions
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [LoadingComponentWithPermissions],
})
component = new MockLoadingComponent()
})
it('should create', () => {
expect(component).toBeTruthy()
})
it('should have loading set to true by default', () => {
expect(component.loading).toBeTruthy()
})
it('should have reveal set to false by default', () => {
expect(component.reveal).toBeFalsy()
})
it('should call next and complete on unsubscribeNotifier with itself on destroy', () => {
const nextSpy = jest.spyOn(component['unsubscribeNotifier'], 'next')
const completeSpy = jest.spyOn(component['unsubscribeNotifier'], 'complete')
component.ngOnDestroy()
expect(nextSpy).toHaveBeenCalledWith(component)
expect(completeSpy).toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,23 @@
import { Subject } from 'rxjs'
import { ComponentWithPermissions } from '../with-permissions/with-permissions.component'
import { Directive, OnDestroy } from '@angular/core'
@Directive()
export abstract class LoadingComponentWithPermissions
extends ComponentWithPermissions
implements OnDestroy
{
public loading: boolean = true
public reveal: boolean = false
protected unsubscribeNotifier: Subject<any> = new Subject()
constructor() {
super()
}
ngOnDestroy(): void {
this.unsubscribeNotifier.next(this)
this.unsubscribeNotifier.complete()
}
}

View File

@@ -28,6 +28,7 @@ import {
CustomFieldQueryLogicalOperator,
CustomFieldQueryOperator,
} from 'src/app/data/custom-field-query'
import { SettingsService } from 'src/app/services/settings.service'
const fields: CustomField[] = [
{
@@ -49,6 +50,7 @@ describe('CustomFieldsComponent', () => {
let modalService: NgbModal
let toastService: ToastService
let listViewService: DocumentListViewService
let settingsService: SettingsService
beforeEach(() => {
TestBed.configureTestingModule({
@@ -91,6 +93,8 @@ describe('CustomFieldsComponent', () => {
modalService = TestBed.inject(NgbModal)
toastService = TestBed.inject(ToastService)
listViewService = TestBed.inject(DocumentListViewService)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = { id: 0, username: 'test' }
fixture = TestBed.createComponent(CustomFieldsComponent)
component = fixture.componentInstance

View File

@@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { delay, Subject, takeUntil, tap } from 'rxjs'
import { delay, takeUntil, tap } from 'rxjs'
import { DATA_TYPE_LABELS, CustomField } from 'src/app/data/custom-field'
import { PermissionsService } from 'src/app/services/permissions.service'
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
@@ -8,7 +8,6 @@ import { ToastService } from 'src/app/services/toast.service'
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
import { CustomFieldEditDialogComponent } from '../../common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component'
import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component'
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { FILTER_CUSTOM_FIELDS_QUERY } from 'src/app/data/filter-rule-type'
import {
@@ -16,6 +15,7 @@ import {
CustomFieldQueryOperator,
} from 'src/app/data/custom-field-query'
import { SettingsService } from 'src/app/services/settings.service'
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
@Component({
selector: 'pngx-custom-fields',
@@ -23,15 +23,11 @@ import { SettingsService } from 'src/app/services/settings.service'
styleUrls: ['./custom-fields.component.scss'],
})
export class CustomFieldsComponent
extends ComponentWithPermissions
extends LoadingComponentWithPermissions
implements OnInit
{
public fields: CustomField[] = []
public loading: boolean = true
public reveal: boolean = false
private unsubscribeNotifier: Subject<any> = new Subject()
constructor(
private customFieldsService: CustomFieldsService,
public permissionsService: PermissionsService,

View File

@@ -44,7 +44,7 @@
</tr>
</thead>
<tbody>
@if (isLoading) {
@if (loading) {
<tr>
<td colspan="5">
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
@@ -109,7 +109,7 @@
</table>
</div>
@if (!isLoading) {
@if (!loading) {
<div class="d-flex mb-2">
@if (collectionSize > 0) {
<div>

View File

@@ -39,8 +39,8 @@ import {
import { ToastService } from 'src/app/services/toast.service'
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component'
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
import { PermissionsDialogComponent } from '../../common/permissions-dialog/permissions-dialog.component'
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
export interface ManagementListColumn {
key: string
@@ -56,7 +56,7 @@ export interface ManagementListColumn {
@Directive()
export abstract class ManagementListComponent<T extends ObjectWithId>
extends ComponentWithPermissions
extends LoadingComponentWithPermissions
implements OnInit, OnDestroy
{
constructor(
@@ -86,8 +86,6 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
public sortField: string
public sortReverse: boolean
public isLoading: boolean = false
private nameFilterDebounce: Subject<string>
protected unsubscribeNotifier: Subject<any> = new Subject()
protected _nameFilter: string
@@ -95,8 +93,6 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
public selectedObjects: Set<number> = new Set()
public togggleAll: boolean = false
public reveal: boolean = false
ngOnInit(): void {
this.reloadData()
@@ -115,11 +111,6 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
})
}
ngOnDestroy() {
this.unsubscribeNotifier.next(true)
this.unsubscribeNotifier.complete()
}
getMatching(o: MatchingModel) {
if (o.matching_algorithm == MATCH_AUTO) {
return $localize`Automatic`
@@ -141,7 +132,7 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
}
reloadData(extraParams: { [key: string]: any } = null) {
this.isLoading = true
this.loading = true
this.clearSelection()
this.service
.listFiltered(
@@ -163,7 +154,7 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
)
.subscribe(() => {
this.reveal = true
this.isLoading = false
this.loading = false
})
}

View File

@@ -3,11 +3,11 @@ import { FormControl, FormGroup } from '@angular/forms'
import { SavedView } from 'src/app/data/saved-view'
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
import { SettingsService } from 'src/app/services/settings.service'
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
import { DisplayMode } from 'src/app/data/document'
import { BehaviorSubject, Observable, Subject, Subscription } from 'rxjs'
import { BehaviorSubject, Observable, takeUntil } from 'rxjs'
import { dirtyCheck } from '@ngneat/dirty-check-forms'
import { ToastService } from 'src/app/services/toast.service'
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
@Component({
selector: 'pngx-saved-views',
@@ -15,7 +15,7 @@ import { ToastService } from 'src/app/services/toast.service'
styleUrl: './saved-views.component.scss',
})
export class SavedViewsComponent
extends ComponentWithPermissions
extends LoadingComponentWithPermissions
implements OnInit, OnDestroy
{
DisplayMode = DisplayMode
@@ -27,11 +27,7 @@ export class SavedViewsComponent
})
private store: BehaviorSubject<any>
private storeSub: Subscription
public isDirty$: Observable<boolean>
private isDirty: boolean = false
private unsubscribeNotifier: Subject<any> = new Subject()
private savePending: boolean = false
get displayFields() {
return this.settings.allDisplayFields
@@ -56,9 +52,7 @@ export class SavedViewsComponent
ngOnDestroy(): void {
this.settings.organizingSidebarSavedViews = false
this.unsubscribeNotifier.next(this)
this.unsubscribeNotifier.complete()
this.storeSub.unsubscribe()
super.ngOnDestroy()
}
private initialize() {
@@ -93,9 +87,12 @@ export class SavedViewsComponent
}
this.store = new BehaviorSubject(storeData)
this.storeSub = this.store.asObservable().subscribe((state) => {
this.savedViewsForm.patchValue(state, { emitEvent: false })
})
this.store
.asObservable()
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe((state) => {
this.savedViewsForm.patchValue(state, { emitEvent: false })
})
// Initialize dirtyCheck
this.isDirty$ = dirtyCheck(this.savedViewsForm, this.store.asObservable())

View File

@@ -1,7 +1,6 @@
import { Component, OnInit } from '@angular/core'
import { WorkflowService } from 'src/app/services/rest/workflow.service'
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
import { delay, Subject, takeUntil, tap } from 'rxjs'
import { delay, takeUntil, tap } from 'rxjs'
import { Workflow } from 'src/app/data/workflow'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ToastService } from 'src/app/services/toast.service'
@@ -12,6 +11,7 @@ import {
} from '../../common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component'
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component'
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
@Component({
selector: 'pngx-workflows',
@@ -19,16 +19,11 @@ import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component'
styleUrls: ['./workflows.component.scss'],
})
export class WorkflowsComponent
extends ComponentWithPermissions
extends LoadingComponentWithPermissions
implements OnInit
{
public workflows: Workflow[] = []
private unsubscribeNotifier: Subject<any> = new Subject()
public loading: boolean = false
public reveal: boolean = false
constructor(
private workflowService: WorkflowService,
public permissionsService: PermissionsService,