Skip to content

Commit 6f07ed6

Browse files
DevipriyaS17madhavilosetty-intel
authored andcommitted
fix: address review comments
1 parent f9a4bf8 commit 6f07ed6

3 files changed

Lines changed: 57 additions & 28 deletions

File tree

src/app/devices/device-toolbar/device-toolbar.component.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,7 @@
5555
mat-icon-button
5656
type="button"
5757
[matTooltip]="powerState() | translate"
58-
aria-label="Refresh power status"
59-
style="padding: 0 18px 0 12px"
58+
[attr.aria-label]="'deviceToolbar.power.refreshAriaLabel.value' | translate"
6059
[style.color]="
6160
powerState() === 'deviceToolbar.power.on.value'
6261
? 'green'

src/app/devices/device-toolbar/device-toolbar.component.ts

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Component, OnInit, inject, signal, input, DestroyRef } from '@angular/c
77
import { catchError, finalize, switchMap } from 'rxjs/operators'
88
import { MatSnackBar } from '@angular/material/snack-bar'
99
import { Router } from '@angular/router'
10-
import { Observable, of, forkJoin, EMPTY } from 'rxjs'
10+
import { Observable, of, forkJoin, EMPTY, timer } from 'rxjs'
1111
import { DevicesService } from '../devices.service'
1212
import SnackbarDefaults from '../../shared/config/snackBarDefault'
1313
import { AMTFeaturesResponse, BootDetails, Device, UserConsentResponse } from '../../../models/models'
@@ -217,6 +217,22 @@ export class DeviceToolbarComponent implements OnInit {
217217
this.powerOptions.set(options)
218218
}
219219

220+
private setOptimisticPowerState(action: number): void {
221+
// Immediately reflect the expected outcome so the UI doesn't show the
222+
// old state while waiting for the device to transition.
223+
switch (action) {
224+
case 2: // power on
225+
this.powerState.set('deviceToolbar.power.on.value')
226+
break
227+
case 8: // power off
228+
case 12: // soft off
229+
this.powerState.set('deviceToolbar.power.off.value')
230+
break
231+
default: // reset, cycle, etc. — state is transitioning
232+
this.powerState.set('Unknown')
233+
}
234+
}
235+
220236
private setPowerStateLabel(powerstate: number): void {
221237
switch (powerstate) {
222238
case 2:
@@ -233,39 +249,41 @@ export class DeviceToolbarComponent implements OnInit {
233249

234250
private loadPowerState(): void {
235251
// Use cached to dedupe simultaneous requests (KVM deep-link + toolbar both loading).
236-
this.isLoading().set(true)
237-
this.devicesService
238-
.getPowerStateCached(this.deviceId())
239-
.pipe(
240-
catchError(() => {
241-
// Network/backend failure; silently skip update and let UI show previous state.
242-
return EMPTY
243-
}),
244-
finalize(() => {
245-
this.isLoading().set(false)
246-
}),
247-
takeUntilDestroyed(this.destroyRef)
248-
)
249-
.subscribe((powerState) => {
250-
this.setPowerStateLabel(powerState.powerstate)
251-
})
252+
this.fetchPowerState(this.devicesService.getPowerStateCached(this.deviceId()))
252253
}
253254

254255
refreshPowerState(): void {
255-
this.isLoading().set(true)
256-
// Bypass the cache so manual refresh always fetches the latest state from the
257-
// device rather than returning a previously cached value.
258-
this.devicesService
259-
.getPowerState(this.deviceId())
256+
// Bypass the cache so manual refresh always fetches the latest state from the device.
257+
this.fetchPowerState(this.devicesService.getPowerState(this.deviceId()), false, () => {
258+
const msg: string = this.translate.instant('kvm.errorRetrievePower.value')
259+
this.snackBar.open(msg, undefined, SnackbarDefaults.defaultError)
260+
})
261+
}
262+
263+
private silentRefreshPowerState(): void {
264+
// Refresh without showing the loading spinner — used after a power action
265+
// where the optimistic state is already shown to the user.
266+
this.fetchPowerState(this.devicesService.getPowerState(this.deviceId()), true)
267+
}
268+
269+
private fetchPowerState(source: Observable<any>, silent = false, onError?: () => void): void {
270+
if (!silent) {
271+
this.isLoading().set(true)
272+
}
273+
274+
source
260275
.pipe(
261276
catchError((err) => {
262-
console.error(err)
263-
const msg: string = this.translate.instant('kvm.errorRetrievePower.value')
264-
this.snackBar.open(msg, undefined, SnackbarDefaults.defaultError)
277+
if (onError) {
278+
console.error(err)
279+
onError()
280+
}
265281
return EMPTY
266282
}),
267283
finalize(() => {
268-
this.isLoading().set(false)
284+
if (!silent) {
285+
this.isLoading().set(false)
286+
}
269287
}),
270288
takeUntilDestroyed(this.destroyRef)
271289
)
@@ -475,6 +493,14 @@ export class DeviceToolbarComponent implements OnInit {
475493
const msg: string = this.translate.instant('devices.powerActionSent.value')
476494
console.log('Power action sent successfully:', data)
477495
this.snackBar.open(msg, undefined, SnackbarDefaults.defaultSuccess)
496+
// Set expected state immediately for instant feedback.
497+
this.setOptimisticPowerState(action)
498+
// Then silently confirm actual state after device transitions — no loading spinner.
499+
timer(5000)
500+
.pipe(takeUntilDestroyed(this.destroyRef))
501+
.subscribe(() => {
502+
this.silentRefreshPowerState()
503+
})
478504
} else {
479505
console.log('Power action failed:', data)
480506
const msg: string = this.translate.instant('devices.failPowerAction.value')

src/assets/i18n/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,10 @@
12531253
"description": "Power tooltip for Off",
12541254
"value": "Power: Off"
12551255
},
1256+
"deviceToolbar.power.refreshAriaLabel": {
1257+
"description": "Aria label for the refresh power status button",
1258+
"value": "Refresh power status"
1259+
},
12561260
"deviceUserConsent.description": {
12571261
"description": "Description for user consent for devices",
12581262
"value": "A user consent code generated by Intel AMT is required to access the system."

0 commit comments

Comments
 (0)