Chorehancement: update to Angular v22, 'zoneless' / 'reactive' (#13114)

This commit is contained in:
shamoon
2026-07-10 00:42:16 -07:00
committed by GitHub
parent f244442c65
commit 106b41a15c
213 changed files with 5363 additions and 5842 deletions
@@ -1,7 +1,7 @@
<pngx-widget-frame title="Statistics" [loading]="loading" i18n-title>
<pngx-widget-frame title="Statistics" [loading]="loading()" i18n-title>
<ng-container content>
<div class="list-group border-light placeholder-glow">
@if (loading) {
@if (loading()) {
<div class="list-group-item d-flex">
<div class="placeholder w-50"></div>
<span class="placeholder badge rounded-pill ms-auto" style="width: 25px;">&nbsp;</span>
@@ -24,32 +24,32 @@
<div class="placeholder w-100 d-block"></div>
</div>
} @else {
@if (statistics?.documents_inbox !== null) {
@if (statistics()?.documents_inbox !== null) {
<a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" title="Go to inbox" i18n-title href="javascript:void(0)" (click)="goToInbox()">
<ng-container i18n>Documents in inbox</ng-container>:
<span class="badge rounded-pill" [class.bg-primary]="statistics?.documents_inbox > 0" [class.bg-muted]="statistics?.documents_inbox === 0">{{statistics?.documents_inbox}}</span>
<span class="badge rounded-pill" [class.bg-primary]="statistics()?.documents_inbox > 0" [class.bg-muted]="statistics()?.documents_inbox === 0">{{statistics()?.documents_inbox}}</span>
</a>
}
<a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" title="Go to documents" i18n-title routerLink="/documents/">
<ng-container i18n>Total documents</ng-container>:
<span class="badge bg-primary rounded-pill">{{statistics?.documents_total}}</span>
<span class="badge bg-primary rounded-pill">{{statistics()?.documents_total}}</span>
</a>
<div class="list-group-item d-flex justify-content-between align-items-center" routerLink="/documents/">
<ng-container i18n>Total characters</ng-container>:
<span class="badge bg-secondary text-light rounded-pill">{{statistics?.character_count | number}}</span>
<span class="badge bg-secondary text-light rounded-pill">{{statistics()?.character_count | number}}</span>
</div>
@if (statistics?.current_asn) {
@if (statistics()?.current_asn) {
<div class="list-group-item d-flex justify-content-between align-items-center" routerLink="/documents/">
<ng-container i18n>Current ASN</ng-container>:
<span class="badge bg-secondary text-light rounded-pill">{{statistics?.current_asn}}</span>
<span class="badge bg-secondary text-light rounded-pill">{{statistics()?.current_asn}}</span>
</div>
}
}
@if (statistics?.document_file_type_counts?.length > 1) {
@if (statistics()?.document_file_type_counts?.length > 1) {
<div class="list-group-item filetypes">
<div class="d-flex justify-content-between align-items-center my-2">
<div class="progress flex-grow-1">
@for (filetype of statistics?.document_file_type_counts; track filetype; let i = $index; let last = $last) {
@for (filetype of statistics()?.document_file_type_counts; track filetype; let i = $index; let last = $last) {
<div
class="progress-bar bg-primary"
role="progressbar"
@@ -69,7 +69,7 @@
</div>
</div>
<div class="d-flex flex-wrap align-items-start">
@for (filetype of statistics?.document_file_type_counts; track filetype; let i = $index) {
@for (filetype of statistics()?.document_file_type_counts; track filetype; let i = $index) {
<div class="d-flex">
<div class="text-nowrap me-2" [class.cursor-pointer]="!filetype.is_other" (click)="filterByFileType(filetype)">
<span class="badge rounded-pill bg-primary d-inline-block p-0 me-1" [style.opacity]="getItemOpacity(i)"></span>
@@ -84,54 +84,54 @@
<div class="list-group border-light mt-3">
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Tag }">
@if (loading) {
@if (loading()) {
<div class="placeholder-glow list-group-item">
<span class="placeholder w-100"></span>
</div>
}
@if (statistics?.tag_count > 0) {
@if (statistics()?.tag_count > 0) {
<a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/tags/">
<ng-container i18n>Tags</ng-container>:
<span class="badge bg-secondary text-light rounded-pill">{{statistics?.tag_count | number}}</span>
<span class="badge bg-secondary text-light rounded-pill">{{statistics()?.tag_count | number}}</span>
</a>
}
</ng-container>
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Correspondent }">
@if (loading) {
@if (loading()) {
<div class="placeholder-glow list-group-item">
<span class="placeholder w-100"></span>
</div>
}
@if (statistics?.correspondent_count > 0) {
@if (statistics()?.correspondent_count > 0) {
<a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/correspondents/">
<ng-container i18n>Correspondents</ng-container>:
<span class="badge bg-secondary text-light rounded-pill">{{statistics?.correspondent_count | number}}</span>
<span class="badge bg-secondary text-light rounded-pill">{{statistics()?.correspondent_count | number}}</span>
</a>
}
</ng-container>
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.DocumentType }">
@if (loading) {
@if (loading()) {
<div class="placeholder-glow list-group-item">
<span class="placeholder w-100"></span>
</div>
}
@if (statistics?.document_type_count > 0) {
@if (statistics()?.document_type_count > 0) {
<a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/documenttypes/">
<ng-container i18n>Document Types</ng-container>:
<span class="badge bg-secondary text-light rounded-pill">{{statistics?.document_type_count | number}}</span>
<span class="badge bg-secondary text-light rounded-pill">{{statistics()?.document_type_count | number}}</span>
</a>
}
</ng-container>
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.StoragePath }">
@if (loading) {
@if (loading()) {
<div class="placeholder-glow list-group-item">
<span class="placeholder w-100"></span>
</div>
}
@if (statistics?.storage_path_count > 0) {
@if (statistics()?.storage_path_count > 0) {
<a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/storagepaths/">
<ng-container i18n>Storage Paths</ng-container>:
<span class="badge bg-secondary text-light rounded-pill">{{statistics?.storage_path_count | number}}</span>
<span class="badge bg-secondary text-light rounded-pill">{{statistics()?.storage_path_count | number}}</span>
</a>
}
</ng-container>
@@ -74,7 +74,7 @@ describe('StatisticsWidgetComponent', () => {
it('should not call statistics endpoint on reload if already loading', () => {
httpTestingController.expectOne(`${environment.apiBaseUrl}statistics/`)
component.loading = true
component.loading.set(true)
component.reload()
httpTestingController.expectNone(`${environment.apiBaseUrl}statistics/`)
})
@@ -1,6 +1,6 @@
import { DecimalPipe } from '@angular/common'
import { HttpClient } from '@angular/common/http'
import { Component, inject, OnDestroy, OnInit } from '@angular/core'
import { Component, inject, OnDestroy, OnInit, signal } from '@angular/core'
import { RouterModule } from '@angular/router'
import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
import * as mimeTypeNames from 'mime-names'
@@ -55,21 +55,20 @@ export class StatisticsWidgetComponent
private websocketConnectionService = inject(WebsocketStatusService)
private documentListViewService = inject(DocumentListViewService)
loading: boolean = false
statistics: Statistics = {}
readonly loading = signal(false)
readonly statistics = signal<Statistics>({})
subscription: Subscription
private unsubscribeNotifer: Subject<any> = new Subject()
reload() {
if (this.loading) return
this.loading = true
if (this.loading()) return
this.loading.set(true)
this.http
.get<Statistics>(`${environment.apiBaseUrl}statistics/`)
.pipe(takeUntil(this.unsubscribeNotifer), first())
.subscribe((statistics) => {
this.loading = false
this.loading.set(false)
const fileTypeMax = 5
if (statistics.document_file_type_counts?.length > fileTypeMax) {
const others = statistics.document_file_type_counts.slice(fileTypeMax)
@@ -85,7 +84,7 @@ export class StatisticsWidgetComponent
),
})
}
this.statistics = statistics
this.statistics.set(statistics)
})
}
@@ -101,11 +100,11 @@ export class StatisticsWidgetComponent
}
getFileTypePercent(filetype: DocumentFileType): number {
return (filetype.mime_type_count / this.statistics?.documents_total) * 100
return (filetype.mime_type_count / this.statistics()?.documents_total) * 100
}
getItemOpacity(i: number): number {
return 1 - i / this.statistics?.document_file_type_counts.length
return 1 - i / this.statistics()?.document_file_type_counts.length
}
ngOnInit(): void {
@@ -127,8 +126,8 @@ export class StatisticsWidgetComponent
this.documentListViewService.quickFilter([
{
rule_type: FILTER_HAS_TAGS_ANY,
value: this.statistics.inbox_tags
.map((tagID) => tagID.toString())
value: this.statistics()
.inbox_tags.map((tagID) => tagID.toString())
.join(','),
},
])