Skip to content

Commit 30ba937

Browse files
committed
feat: avoid reloading AMT Summary when updating AMT Features
1 parent 4ce9d94 commit 30ba937

3 files changed

Lines changed: 81 additions & 4 deletions

File tree

src/app/devices/general/general.component.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@
6363
<mat-card-title>{{ 'general.amtEnabledFeatures.value' | translate }}</mat-card-title>
6464
<mat-card-subtitle>{{ 'general.amtEnabledFeaturesDescription.value' | translate }}</mat-card-subtitle>
6565
</mat-card-header>
66+
@if (isUpdatingFeatures()) {
67+
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
68+
}
6669
<mat-card-content class="amt-features" [formGroup]="amtEnabledFeatures">
6770
<div class="flex-row flex-wrap">
6871
@if (isRedirectionRequired && !amtEnabledFeatures.get('redirection')?.value) {

src/app/devices/general/general.component.spec.ts

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'
77

88
import { GeneralComponent } from './general.component'
99
import { ActivatedRoute } from '@angular/router'
10-
import { of } from 'rxjs'
10+
import { of, Subject } from 'rxjs'
1111
import { DevicesService } from '../devices.service'
1212
import { provideTranslateService } from '@ngx-translate/core'
1313
import { By } from '@angular/platform-browser'
@@ -32,7 +32,8 @@ describe('GeneralComponent', () => {
3232
'bulkPowerAction',
3333
'sendDeactivate',
3434
'sendBulkDeactivate',
35-
'getWsmanOperations'
35+
'getWsmanOperations',
36+
'setAmtFeatures'
3637
])
3738
const amtFeaturesResponse = {
3839
userConsent: 'ALL',
@@ -143,4 +144,72 @@ describe('GeneralComponent', () => {
143144
jasmine.objectContaining({ remoteErase: true })
144145
)
145146
})
147+
148+
it('should update only feature loading state while setAmtFeatures is in flight', () => {
149+
const response$ = new Subject<any>()
150+
devicesServiceSpy.setAmtFeatures.and.returnValue(response$)
151+
const loadingBefore = component.isLoading()
152+
153+
component.setAmtFeatures()
154+
155+
expect(component.isUpdatingFeatures()).toBeTrue()
156+
expect(component.isLoading()).toBe(loadingBefore)
157+
158+
response$.next({ redirection: true, status: 'ok' })
159+
response$.complete()
160+
161+
expect(component.isUpdatingFeatures()).toBeFalse()
162+
expect(component.isLoading()).toBe(loadingBefore)
163+
})
164+
165+
it('keeps summary loading state independent from feature update state', () => {
166+
const response$ = new Subject<any>()
167+
devicesServiceSpy.setAmtFeatures.and.returnValue(response$)
168+
169+
component.isLoading.set(false)
170+
component.setAmtFeatures()
171+
172+
expect(component.isLoading()).toBeFalse()
173+
expect(component.isUpdatingFeatures()).toBeTrue()
174+
175+
response$.next({ redirection: true, status: 'ok' })
176+
response$.complete()
177+
178+
expect(component.isLoading()).toBeFalse()
179+
expect(component.isUpdatingFeatures()).toBeFalse()
180+
})
181+
182+
it('keeps feature loading state true until the last overlapping update completes', () => {
183+
const firstResponse$ = new Subject<any>()
184+
const secondResponse$ = new Subject<any>()
185+
devicesServiceSpy.setAmtFeatures.and.returnValues(firstResponse$, secondResponse$)
186+
187+
component.setAmtFeatures()
188+
component.setAmtFeatures()
189+
190+
expect(component.isUpdatingFeatures()).toBeTrue()
191+
192+
firstResponse$.next({ redirection: true, status: 'ok' })
193+
firstResponse$.complete()
194+
195+
expect(component.isUpdatingFeatures()).toBeTrue()
196+
197+
secondResponse$.next({ redirection: true, status: 'ok' })
198+
secondResponse$.complete()
199+
200+
expect(component.isUpdatingFeatures()).toBeFalse()
201+
})
202+
203+
it('stops tracking an in-flight feature update when the component is destroyed', () => {
204+
const response$ = new Subject<any>()
205+
devicesServiceSpy.setAmtFeatures.and.returnValue(response$)
206+
207+
component.setAmtFeatures()
208+
209+
expect(component.isUpdatingFeatures()).toBeTrue()
210+
211+
component.ngOnDestroy()
212+
213+
expect(component.isUpdatingFeatures()).toBeFalse()
214+
})
146215
})

src/app/devices/general/general.component.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export class GeneralComponent implements OnInit, OnDestroy {
7878
})
7979

8080
public isLoading = signal(true)
81+
public isUpdatingFeatures = signal(false)
8182
public amtDHCPDNSSuffix: string | null = null
8283
public amtTrustedDNSSuffix: string | null = null
8384
public amtVersion: string | null = null
@@ -95,6 +96,7 @@ export class GeneralComponent implements OnInit, OnDestroy {
9596
public isCloudMode: boolean = environment.cloud
9697

9798
private readonly destroy$ = new Subject<void>()
99+
private pendingFeatureUpdates = 0
98100

99101
ngOnInit(): void {
100102
forkJoin({
@@ -180,15 +182,18 @@ export class GeneralComponent implements OnInit, OnDestroy {
180182
}
181183

182184
setAmtFeatures(): void {
183-
this.isLoading.set(true)
185+
this.pendingFeatureUpdates += 1
186+
this.isUpdatingFeatures.set(true)
184187
this.devicesService
185188
.setAmtFeatures(this.deviceId(), {
186189
...this.amtEnabledFeatures.getRawValue(),
187190
remoteErase: this.amtFeatures.remoteErase
188191
} as AMTFeaturesRequest)
189192
.pipe(
193+
takeUntil(this.destroy$),
190194
finalize(() => {
191-
this.isLoading.set(false)
195+
this.pendingFeatureUpdates = Math.max(0, this.pendingFeatureUpdates - 1)
196+
this.isUpdatingFeatures.set(this.pendingFeatureUpdates > 0)
192197
})
193198
)
194199
.subscribe({

0 commit comments

Comments
 (0)