mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-11-04 03:27:12 -05:00 
			
		
		
		
	Frontend better handle slow backend requests
This commit is contained in:
		
							parent
							
								
									46a39190a4
								
							
						
					
					
						commit
						faed7683be
					
				@ -1,5 +1,5 @@
 | 
				
			|||||||
  <div class="btn-group w-100" ngbDropdown role="group">
 | 
					  <div class="btn-group w-100" ngbDropdown role="group">
 | 
				
			||||||
  <button class="btn btn-sm" id="dropdown{{title}}" ngbDropdownToggle [ngClass]="dateBefore || dateAfter ? 'btn-primary' : 'btn-outline-primary'">
 | 
					  <button class="btn btn-sm" id="dropdown{{title}}" ngbDropdownToggle [ngClass]="dateBefore || dateAfter ? 'btn-primary' : 'btn-outline-primary'" [disabled]="disabled">
 | 
				
			||||||
    {{title}}
 | 
					    {{title}}
 | 
				
			||||||
    <app-clearable-badge [selected]="isActive" (cleared)="reset()"></app-clearable-badge><span class="visually-hidden">selected</span>
 | 
					    <app-clearable-badge [selected]="isActive" (cleared)="reset()"></app-clearable-badge><span class="visually-hidden">selected</span>
 | 
				
			||||||
  </button>
 | 
					  </button>
 | 
				
			||||||
 | 
				
			|||||||
@ -85,6 +85,9 @@ export class DateDropdownComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  @Output()
 | 
					  @Output()
 | 
				
			||||||
  datesSet = new EventEmitter<DateSelection>()
 | 
					  datesSet = new EventEmitter<DateSelection>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Input()
 | 
				
			||||||
 | 
					  disabled: boolean = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get isActive(): boolean {
 | 
					  get isActive(): boolean {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      this.relativeDate !== null ||
 | 
					      this.relativeDate !== null ||
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
<div class="btn-group w-100" ngbDropdown role="group">
 | 
					<div class="btn-group w-100" ngbDropdown role="group">
 | 
				
			||||||
    <button class="btn btn-sm" id="dropdown{{title}}" ngbDropdownToggle [ngClass]="isActive ? 'btn-primary' : 'btn-outline-primary'">
 | 
					    <button class="btn btn-sm" id="dropdown{{title}}" ngbDropdownToggle [ngClass]="isActive ? 'btn-primary' : 'btn-outline-primary'" [disabled]="disabled">
 | 
				
			||||||
        <svg class="toolbaricon" fill="currentColor">
 | 
					        <svg class="toolbaricon" fill="currentColor">
 | 
				
			||||||
           <use xlink:href="assets/bootstrap-icons.svg#person-fill-lock" />
 | 
					           <use xlink:href="assets/bootstrap-icons.svg#person-fill-lock" />
 | 
				
			||||||
        </svg>
 | 
					        </svg>
 | 
				
			||||||
 | 
				
			|||||||
@ -4,11 +4,10 @@ import {
 | 
				
			|||||||
  OnDestroy,
 | 
					  OnDestroy,
 | 
				
			||||||
  OnInit,
 | 
					  OnInit,
 | 
				
			||||||
  QueryList,
 | 
					  QueryList,
 | 
				
			||||||
  ViewChild,
 | 
					 | 
				
			||||||
  ViewChildren,
 | 
					  ViewChildren,
 | 
				
			||||||
} from '@angular/core'
 | 
					} from '@angular/core'
 | 
				
			||||||
import { Router } from '@angular/router'
 | 
					import { Router } from '@angular/router'
 | 
				
			||||||
import { Subscription } from 'rxjs'
 | 
					import { Subject, takeUntil } from 'rxjs'
 | 
				
			||||||
import { PaperlessDocument } from 'src/app/data/paperless-document'
 | 
					import { PaperlessDocument } from 'src/app/data/paperless-document'
 | 
				
			||||||
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
 | 
					import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
 | 
				
			||||||
import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
 | 
					import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
 | 
				
			||||||
@ -49,7 +48,7 @@ export class SavedViewWidgetComponent
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  documents: PaperlessDocument[] = []
 | 
					  documents: PaperlessDocument[] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  subscription: Subscription
 | 
					  unsubscribeNotifier: Subject<any> = new Subject()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @ViewChildren('popover') popovers: QueryList<NgbPopover>
 | 
					  @ViewChildren('popover') popovers: QueryList<NgbPopover>
 | 
				
			||||||
  popover: NgbPopover
 | 
					  popover: NgbPopover
 | 
				
			||||||
@ -59,15 +58,17 @@ export class SavedViewWidgetComponent
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    this.reload()
 | 
					    this.reload()
 | 
				
			||||||
    this.subscription = this.consumerStatusService
 | 
					    this.consumerStatusService
 | 
				
			||||||
      .onDocumentConsumptionFinished()
 | 
					      .onDocumentConsumptionFinished()
 | 
				
			||||||
      .subscribe((status) => {
 | 
					      .pipe(takeUntil(this.unsubscribeNotifier))
 | 
				
			||||||
 | 
					      .subscribe(() => {
 | 
				
			||||||
        this.reload()
 | 
					        this.reload()
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnDestroy(): void {
 | 
					  ngOnDestroy(): void {
 | 
				
			||||||
    this.subscription.unsubscribe()
 | 
					    this.unsubscribeNotifier.next(true)
 | 
				
			||||||
 | 
					    this.unsubscribeNotifier.complete()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  reload() {
 | 
					  reload() {
 | 
				
			||||||
@ -81,6 +82,7 @@ export class SavedViewWidgetComponent
 | 
				
			|||||||
        this.savedView.filter_rules,
 | 
					        this.savedView.filter_rules,
 | 
				
			||||||
        { truncate_content: true }
 | 
					        { truncate_content: true }
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
 | 
					      .pipe(takeUntil(this.unsubscribeNotifier))
 | 
				
			||||||
      .subscribe((result) => {
 | 
					      .subscribe((result) => {
 | 
				
			||||||
        this.loading = false
 | 
					        this.loading = false
 | 
				
			||||||
        this.documents = result.results
 | 
					        this.documents = result.results
 | 
				
			||||||
 | 
				
			|||||||
@ -185,7 +185,7 @@ export class DocumentListComponent
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnDestroy() {
 | 
					  ngOnDestroy() {
 | 
				
			||||||
    // unsubscribes all
 | 
					    this.list.cancelPending()
 | 
				
			||||||
    this.unsubscribeNotifier.next(this)
 | 
					    this.unsubscribeNotifier.next(this)
 | 
				
			||||||
    this.unsubscribeNotifier.complete()
 | 
					    this.unsubscribeNotifier.complete()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -2,16 +2,23 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
</app-page-header>
 | 
					</app-page-header>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
<ul ngbNav #nav="ngbNav" [(activeId)]="activeLog" (activeIdChange)="reloadLogs()" class="nav-tabs">
 | 
					<ul ngbNav #nav="ngbNav" [(activeId)]="activeLog" (activeIdChange)="reloadLogs()" class="nav-tabs">
 | 
				
			||||||
  <li *ngFor="let logFile of logFiles" [ngbNavItem]="logFile">
 | 
					  <li *ngFor="let logFile of logFiles" [ngbNavItem]="logFile">
 | 
				
			||||||
    <a ngbNavLink>{{logFile}}.log</a>
 | 
					    <a ngbNavLink>{{logFile}}.log</a>
 | 
				
			||||||
  </li>
 | 
					  </li>
 | 
				
			||||||
 | 
					  <div *ngIf="isLoading && !logFiles.length" class="pb-2">
 | 
				
			||||||
 | 
					    <div class="spinner-border spinner-border-sm me-2" role="status"></div>
 | 
				
			||||||
 | 
					    <ng-container i18n>Loading...</ng-container>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
</ul>
 | 
					</ul>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div [ngbNavOutlet]="nav" class="mt-2"></div>
 | 
					<div [ngbNavOutlet]="nav" class="mt-2"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="bg-dark p-3 text-light font-monospace log-container" #logContainer>
 | 
					<div class="bg-dark p-3 text-light font-monospace log-container" #logContainer>
 | 
				
			||||||
 | 
					  <div *ngIf="isLoading && logFiles.length">
 | 
				
			||||||
 | 
					    <div class="spinner-border spinner-border-sm me-2" role="status"></div>
 | 
				
			||||||
 | 
					    <ng-container i18n>Loading...</ng-container>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
  <p
 | 
					  <p
 | 
				
			||||||
    class="m-0 p-0 log-entry-{{getLogLevel(log)}}"
 | 
					    class="m-0 p-0 log-entry-{{getLogLevel(log)}}"
 | 
				
			||||||
    *ngFor="let log of logs">{{log}}</p>
 | 
					    *ngFor="let log of logs">{{log}}</p>
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,9 @@ import {
 | 
				
			|||||||
  OnInit,
 | 
					  OnInit,
 | 
				
			||||||
  AfterViewChecked,
 | 
					  AfterViewChecked,
 | 
				
			||||||
  ViewChild,
 | 
					  ViewChild,
 | 
				
			||||||
 | 
					  OnDestroy,
 | 
				
			||||||
} from '@angular/core'
 | 
					} from '@angular/core'
 | 
				
			||||||
 | 
					import { Subject, takeUntil } from 'rxjs'
 | 
				
			||||||
import { LogService } from 'src/app/services/rest/log.service'
 | 
					import { LogService } from 'src/app/services/rest/log.service'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
@ -12,40 +14,60 @@ import { LogService } from 'src/app/services/rest/log.service'
 | 
				
			|||||||
  templateUrl: './logs.component.html',
 | 
					  templateUrl: './logs.component.html',
 | 
				
			||||||
  styleUrls: ['./logs.component.scss'],
 | 
					  styleUrls: ['./logs.component.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class LogsComponent implements OnInit, AfterViewChecked {
 | 
					export class LogsComponent implements OnInit, AfterViewChecked, OnDestroy {
 | 
				
			||||||
  constructor(private logService: LogService) {}
 | 
					  constructor(private logService: LogService) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  logs: string[] = []
 | 
					  public logs: string[] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  logFiles: string[] = []
 | 
					  public logFiles: string[] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  activeLog: string
 | 
					  public activeLog: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private unsubscribeNotifier: Subject<any> = new Subject()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public isLoading: boolean = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @ViewChild('logContainer') logContainer: ElementRef
 | 
					  @ViewChild('logContainer') logContainer: ElementRef
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    this.logService.list().subscribe((result) => {
 | 
					    this.isLoading = true
 | 
				
			||||||
      this.logFiles = result
 | 
					    this.logService
 | 
				
			||||||
      if (this.logFiles.length > 0) {
 | 
					      .list()
 | 
				
			||||||
        this.activeLog = this.logFiles[0]
 | 
					      .pipe(takeUntil(this.unsubscribeNotifier))
 | 
				
			||||||
        this.reloadLogs()
 | 
					      .subscribe((result) => {
 | 
				
			||||||
      }
 | 
					        this.logFiles = result
 | 
				
			||||||
    })
 | 
					        this.isLoading = false
 | 
				
			||||||
 | 
					        if (this.logFiles.length > 0) {
 | 
				
			||||||
 | 
					          this.activeLog = this.logFiles[0]
 | 
				
			||||||
 | 
					          this.reloadLogs()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngAfterViewChecked() {
 | 
					  ngAfterViewChecked() {
 | 
				
			||||||
    this.scrollToBottom()
 | 
					    this.scrollToBottom()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ngOnDestroy(): void {
 | 
				
			||||||
 | 
					    this.unsubscribeNotifier.next(true)
 | 
				
			||||||
 | 
					    this.unsubscribeNotifier.complete()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  reloadLogs() {
 | 
					  reloadLogs() {
 | 
				
			||||||
    this.logService.get(this.activeLog).subscribe({
 | 
					    this.isLoading = true
 | 
				
			||||||
      next: (result) => {
 | 
					    this.logService
 | 
				
			||||||
        this.logs = result
 | 
					      .get(this.activeLog)
 | 
				
			||||||
      },
 | 
					      .pipe(takeUntil(this.unsubscribeNotifier))
 | 
				
			||||||
      error: () => {
 | 
					      .subscribe({
 | 
				
			||||||
        this.logs = []
 | 
					        next: (result) => {
 | 
				
			||||||
      },
 | 
					          this.logs = result
 | 
				
			||||||
    })
 | 
					          this.isLoading = false
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        error: () => {
 | 
				
			||||||
 | 
					          this.logs = []
 | 
				
			||||||
 | 
					          this.isLoading = false
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getLogLevel(log: string) {
 | 
					  getLogLevel(log: string) {
 | 
				
			||||||
 | 
				
			|||||||
@ -24,6 +24,12 @@
 | 
				
			|||||||
    </tr>
 | 
					    </tr>
 | 
				
			||||||
  </thead>
 | 
					  </thead>
 | 
				
			||||||
  <tbody>
 | 
					  <tbody>
 | 
				
			||||||
 | 
					    <tr *ngIf="isLoading">
 | 
				
			||||||
 | 
					      <td colspan="5">
 | 
				
			||||||
 | 
					        <div class="spinner-border spinner-border-sm me-2" role="status"></div>
 | 
				
			||||||
 | 
					        <ng-container i18n>Loading...</ng-container>
 | 
				
			||||||
 | 
					      </td>
 | 
				
			||||||
 | 
					    </tr>
 | 
				
			||||||
    <tr *ngFor="let object of data">
 | 
					    <tr *ngFor="let object of data">
 | 
				
			||||||
      <td scope="row">{{ object.name }}</td>
 | 
					      <td scope="row">{{ object.name }}</td>
 | 
				
			||||||
      <td scope="row" class="d-none d-sm-table-cell">{{ getMatching(object) }}</td>
 | 
					      <td scope="row" class="d-none d-sm-table-cell">{{ getMatching(object) }}</td>
 | 
				
			||||||
@ -69,7 +75,7 @@
 | 
				
			|||||||
  </tbody>
 | 
					  </tbody>
 | 
				
			||||||
</table>
 | 
					</table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="d-flex">
 | 
					<div class="d-flex" *ngIf="!isLoading">
 | 
				
			||||||
  <div i18n *ngIf="collectionSize > 0">{collectionSize, plural, =1 {One {{typeName}}} other {{{collectionSize || 0}} total {{typeNamePlural}}}}</div>
 | 
					  <div i18n *ngIf="collectionSize > 0">{collectionSize, plural, =1 {One {{typeName}}} other {{{collectionSize || 0}} total {{typeNamePlural}}}}</div>
 | 
				
			||||||
  <ngb-pagination *ngIf="collectionSize > 20" class="ms-auto" [pageSize]="25" [collectionSize]="collectionSize" [(page)]="page" [maxSize]="5" (pageChange)="reloadData()" size="sm" aria-label="Pagination"></ngb-pagination>
 | 
					  <ngb-pagination *ngIf="collectionSize > 20" class="ms-auto" [pageSize]="25" [collectionSize]="collectionSize" [(page)]="page" [maxSize]="5" (pageChange)="reloadData()" size="sm" aria-label="Pagination"></ngb-pagination>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,7 @@ import {
 | 
				
			|||||||
} from '@angular/core'
 | 
					} from '@angular/core'
 | 
				
			||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
 | 
					import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
import { Subject, Subscription } from 'rxjs'
 | 
					import { Subject, Subscription } from 'rxjs'
 | 
				
			||||||
import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
 | 
					import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators'
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  MatchingModel,
 | 
					  MatchingModel,
 | 
				
			||||||
  MATCHING_ALGORITHMS,
 | 
					  MATCHING_ALGORITHMS,
 | 
				
			||||||
@ -76,8 +76,10 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
 | 
				
			|||||||
  public sortField: string
 | 
					  public sortField: string
 | 
				
			||||||
  public sortReverse: boolean
 | 
					  public sortReverse: boolean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public isLoading: boolean = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private nameFilterDebounce: Subject<string>
 | 
					  private nameFilterDebounce: Subject<string>
 | 
				
			||||||
  private subscription: Subscription
 | 
					  private unsubscribeNotifier: Subject<any> = new Subject()
 | 
				
			||||||
  private _nameFilter: string
 | 
					  private _nameFilter: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
@ -85,8 +87,12 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    this.nameFilterDebounce = new Subject<string>()
 | 
					    this.nameFilterDebounce = new Subject<string>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.subscription = this.nameFilterDebounce
 | 
					    this.nameFilterDebounce
 | 
				
			||||||
      .pipe(debounceTime(400), distinctUntilChanged())
 | 
					      .pipe(
 | 
				
			||||||
 | 
					        takeUntil(this.unsubscribeNotifier),
 | 
				
			||||||
 | 
					        debounceTime(400),
 | 
				
			||||||
 | 
					        distinctUntilChanged()
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
      .subscribe((title) => {
 | 
					      .subscribe((title) => {
 | 
				
			||||||
        this._nameFilter = title
 | 
					        this._nameFilter = title
 | 
				
			||||||
        this.page = 1
 | 
					        this.page = 1
 | 
				
			||||||
@ -95,7 +101,8 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnDestroy() {
 | 
					  ngOnDestroy() {
 | 
				
			||||||
    this.subscription.unsubscribe()
 | 
					    this.unsubscribeNotifier.next(true)
 | 
				
			||||||
 | 
					    this.unsubscribeNotifier.complete()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getMatching(o: MatchingModel) {
 | 
					  getMatching(o: MatchingModel) {
 | 
				
			||||||
@ -119,6 +126,7 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  reloadData() {
 | 
					  reloadData() {
 | 
				
			||||||
 | 
					    this.isLoading = true
 | 
				
			||||||
    this.service
 | 
					    this.service
 | 
				
			||||||
      .listFiltered(
 | 
					      .listFiltered(
 | 
				
			||||||
        this.page,
 | 
					        this.page,
 | 
				
			||||||
@ -128,9 +136,11 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
 | 
				
			|||||||
        this._nameFilter,
 | 
					        this._nameFilter,
 | 
				
			||||||
        true
 | 
					        true
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
 | 
					      .pipe(takeUntil(this.unsubscribeNotifier))
 | 
				
			||||||
      .subscribe((c) => {
 | 
					      .subscribe((c) => {
 | 
				
			||||||
        this.data = c.results
 | 
					        this.data = c.results
 | 
				
			||||||
        this.collectionSize = c.count
 | 
					        this.collectionSize = c.count
 | 
				
			||||||
 | 
					        this.isLoading = false
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -192,19 +202,22 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
 | 
				
			|||||||
    activeModal.componentInstance.btnCaption = $localize`Delete`
 | 
					    activeModal.componentInstance.btnCaption = $localize`Delete`
 | 
				
			||||||
    activeModal.componentInstance.confirmClicked.subscribe(() => {
 | 
					    activeModal.componentInstance.confirmClicked.subscribe(() => {
 | 
				
			||||||
      activeModal.componentInstance.buttonsEnabled = false
 | 
					      activeModal.componentInstance.buttonsEnabled = false
 | 
				
			||||||
      this.service.delete(object).subscribe({
 | 
					      this.service
 | 
				
			||||||
        next: () => {
 | 
					        .delete(object)
 | 
				
			||||||
          activeModal.close()
 | 
					        .pipe(takeUntil(this.unsubscribeNotifier))
 | 
				
			||||||
          this.reloadData()
 | 
					        .subscribe({
 | 
				
			||||||
        },
 | 
					          next: () => {
 | 
				
			||||||
        error: (error) => {
 | 
					            activeModal.close()
 | 
				
			||||||
          activeModal.componentInstance.buttonsEnabled = true
 | 
					            this.reloadData()
 | 
				
			||||||
          this.toastService.showError(
 | 
					          },
 | 
				
			||||||
            $localize`Error while deleting element`,
 | 
					          error: (error) => {
 | 
				
			||||||
            error
 | 
					            activeModal.componentInstance.buttonsEnabled = true
 | 
				
			||||||
          )
 | 
					            this.toastService.showError(
 | 
				
			||||||
        },
 | 
					              $localize`Error while deleting element`,
 | 
				
			||||||
      })
 | 
					              error
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
import { Component, OnInit, OnDestroy } from '@angular/core'
 | 
					import { Component, OnInit, OnDestroy } from '@angular/core'
 | 
				
			||||||
import { Router } from '@angular/router'
 | 
					import { Router } from '@angular/router'
 | 
				
			||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
 | 
					import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
import { Subject, first } from 'rxjs'
 | 
					import { first } from 'rxjs'
 | 
				
			||||||
import { PaperlessTask } from 'src/app/data/paperless-task'
 | 
					import { PaperlessTask } from 'src/app/data/paperless-task'
 | 
				
			||||||
import { TasksService } from 'src/app/services/tasks.service'
 | 
					import { TasksService } from 'src/app/services/tasks.service'
 | 
				
			||||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
 | 
					import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
 | 
				
			||||||
@ -18,7 +18,6 @@ export class TasksComponent
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  public activeTab: string
 | 
					  public activeTab: string
 | 
				
			||||||
  public selectedTasks: Set<number> = new Set()
 | 
					  public selectedTasks: Set<number> = new Set()
 | 
				
			||||||
  private unsubscribeNotifer = new Subject()
 | 
					 | 
				
			||||||
  public expandedTask: number
 | 
					  public expandedTask: number
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public pageSize: number = 25
 | 
					  public pageSize: number = 25
 | 
				
			||||||
@ -43,7 +42,7 @@ export class TasksComponent
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnDestroy() {
 | 
					  ngOnDestroy() {
 | 
				
			||||||
    this.unsubscribeNotifer.next(true)
 | 
					    this.tasksService.cancelPending()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  dismissTask(task: PaperlessTask) {
 | 
					  dismissTask(task: PaperlessTask) {
 | 
				
			||||||
 | 
				
			|||||||
@ -103,6 +103,7 @@ describe('DocumentListViewService', () => {
 | 
				
			|||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  afterEach(() => {
 | 
					  afterEach(() => {
 | 
				
			||||||
 | 
					    documentListViewService.cancelPending()
 | 
				
			||||||
    httpTestingController.verify()
 | 
					    httpTestingController.verify()
 | 
				
			||||||
    sessionStorage.clear()
 | 
					    sessionStorage.clear()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
@ -425,4 +426,13 @@ describe('DocumentListViewService', () => {
 | 
				
			|||||||
    })
 | 
					    })
 | 
				
			||||||
    expect(documentListViewService.selected.size).toEqual(3)
 | 
					    expect(documentListViewService.selected.size).toEqual(3)
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should cancel on reload the list', () => {
 | 
				
			||||||
 | 
					    const cancelSpy = jest.spyOn(documentListViewService, 'cancelPending')
 | 
				
			||||||
 | 
					    documentListViewService.reload()
 | 
				
			||||||
 | 
					    httpTestingController.expectOne(
 | 
				
			||||||
 | 
					      `${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&tags__id__all=9`
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    expect(cancelSpy).toHaveBeenCalled()
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
import { Injectable } from '@angular/core'
 | 
					import { Injectable } from '@angular/core'
 | 
				
			||||||
import { ParamMap, Router } from '@angular/router'
 | 
					import { ParamMap, Router } from '@angular/router'
 | 
				
			||||||
import { Observable, first } from 'rxjs'
 | 
					import { Observable, Subject, first, takeUntil } from 'rxjs'
 | 
				
			||||||
import { FilterRule } from '../data/filter-rule'
 | 
					import { FilterRule } from '../data/filter-rule'
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  filterRulesDiffer,
 | 
					  filterRulesDiffer,
 | 
				
			||||||
@ -82,6 +82,8 @@ export class DocumentListViewService {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  currentPageSize: number = this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE)
 | 
					  currentPageSize: number = this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private unsubscribeNotifier: Subject<any> = new Subject()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private listViewStates: Map<number, ListViewState> = new Map()
 | 
					  private listViewStates: Map<number, ListViewState> = new Map()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private _activeSavedViewId: number = null
 | 
					  private _activeSavedViewId: number = null
 | 
				
			||||||
@ -143,6 +145,10 @@ export class DocumentListViewService {
 | 
				
			|||||||
    return this.listViewStates.get(this._activeSavedViewId)
 | 
					    return this.listViewStates.get(this._activeSavedViewId)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public cancelPending(): void {
 | 
				
			||||||
 | 
					    this.unsubscribeNotifier.next(true)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  activateSavedView(view: PaperlessSavedView) {
 | 
					  activateSavedView(view: PaperlessSavedView) {
 | 
				
			||||||
    this.rangeSelectionAnchorIndex = this.lastRangeSelectionToIndex = null
 | 
					    this.rangeSelectionAnchorIndex = this.lastRangeSelectionToIndex = null
 | 
				
			||||||
    if (view) {
 | 
					    if (view) {
 | 
				
			||||||
@ -210,6 +216,7 @@ export class DocumentListViewService {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  reload(onFinish?, updateQueryParams: boolean = true) {
 | 
					  reload(onFinish?, updateQueryParams: boolean = true) {
 | 
				
			||||||
 | 
					    this.cancelPending()
 | 
				
			||||||
    this.isReloading = true
 | 
					    this.isReloading = true
 | 
				
			||||||
    this.error = null
 | 
					    this.error = null
 | 
				
			||||||
    let activeListViewState = this.activeListViewState
 | 
					    let activeListViewState = this.activeListViewState
 | 
				
			||||||
@ -222,6 +229,7 @@ export class DocumentListViewService {
 | 
				
			|||||||
        activeListViewState.filterRules,
 | 
					        activeListViewState.filterRules,
 | 
				
			||||||
        { truncate_content: true }
 | 
					        { truncate_content: true }
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
 | 
					      .pipe(takeUntil(this.unsubscribeNotifier))
 | 
				
			||||||
      .subscribe({
 | 
					      .subscribe({
 | 
				
			||||||
        next: (result) => {
 | 
					        next: (result) => {
 | 
				
			||||||
          this.initialized = true
 | 
					          this.initialized = true
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +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 { first } from 'rxjs/operators'
 | 
					import { Subject } from 'rxjs'
 | 
				
			||||||
 | 
					import { first, takeUntil } from 'rxjs/operators'
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  PaperlessTask,
 | 
					  PaperlessTask,
 | 
				
			||||||
  PaperlessTaskStatus,
 | 
					  PaperlessTaskStatus,
 | 
				
			||||||
@ -14,10 +15,12 @@ import { environment } from 'src/environments/environment'
 | 
				
			|||||||
export class TasksService {
 | 
					export class TasksService {
 | 
				
			||||||
  private baseUrl: string = environment.apiBaseUrl
 | 
					  private baseUrl: string = environment.apiBaseUrl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  loading: boolean
 | 
					  public loading: boolean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private fileTasks: PaperlessTask[] = []
 | 
					  private fileTasks: PaperlessTask[] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private unsubscribeNotifer: Subject<any> = new Subject()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public get total(): number {
 | 
					  public get total(): number {
 | 
				
			||||||
    return this.fileTasks.length
 | 
					    return this.fileTasks.length
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -51,7 +54,7 @@ export class TasksService {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    this.http
 | 
					    this.http
 | 
				
			||||||
      .get<PaperlessTask[]>(`${this.baseUrl}tasks/`)
 | 
					      .get<PaperlessTask[]>(`${this.baseUrl}tasks/`)
 | 
				
			||||||
      .pipe(first())
 | 
					      .pipe(takeUntil(this.unsubscribeNotifer), first())
 | 
				
			||||||
      .subscribe((r) => {
 | 
					      .subscribe((r) => {
 | 
				
			||||||
        this.fileTasks = r.filter((t) => t.type == PaperlessTaskType.File) // they're all File tasks, for now
 | 
					        this.fileTasks = r.filter((t) => t.type == PaperlessTaskType.File) // they're all File tasks, for now
 | 
				
			||||||
        this.loading = false
 | 
					        this.loading = false
 | 
				
			||||||
@ -63,9 +66,13 @@ export class TasksService {
 | 
				
			|||||||
      .post(`${this.baseUrl}acknowledge_tasks/`, {
 | 
					      .post(`${this.baseUrl}acknowledge_tasks/`, {
 | 
				
			||||||
        tasks: [...task_ids],
 | 
					        tasks: [...task_ids],
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
      .pipe(first())
 | 
					      .pipe(takeUntil(this.unsubscribeNotifer), first())
 | 
				
			||||||
      .subscribe((r) => {
 | 
					      .subscribe((r) => {
 | 
				
			||||||
        this.reload()
 | 
					        this.reload()
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public cancelPending(): void {
 | 
				
			||||||
 | 
					    this.unsubscribeNotifer.next(true)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user