- |
+ |
@if (device.pendingAuthRequest) {
-
+
{{ device.displayName }}
-
- {{ "needsApproval" | i18n }}
-
+
} @else {
{{ device.displayName }}
diff --git a/libs/angular/src/auth/device-management/device-management-table.component.ts b/libs/angular/src/auth/device-management/device-management-table.component.ts
index c3c835f05ed..d663e28b9e4 100644
--- a/libs/angular/src/auth/device-management/device-management-table.component.ts
+++ b/libs/angular/src/auth/device-management/device-management-table.component.ts
@@ -1,6 +1,5 @@
import { CommonModule } from "@angular/common";
-import { Component, Input, OnChanges, SimpleChanges } from "@angular/core";
-import { firstValueFrom } from "rxjs";
+import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from "@angular/core";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { DevicePendingAuthRequest } from "@bitwarden/common/auth/abstractions/devices/responses/device.response";
@@ -8,16 +7,12 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import {
BadgeModule,
ButtonModule,
- DialogService,
LinkModule,
TableDataSource,
TableModule,
} from "@bitwarden/components";
-import { LoginApprovalDialogComponent } from "../login-approval/login-approval-dialog.component";
-
import { DeviceDisplayData } from "./device-management.component";
-import { clearAuthRequestAndResortDevices } from "./resort-devices.helper";
/** Displays user devices in a sortable table view */
@Component({
@@ -28,6 +23,8 @@ import { clearAuthRequestAndResortDevices } from "./resort-devices.helper";
})
export class DeviceManagementTableComponent implements OnChanges {
@Input() devices: DeviceDisplayData[] = [];
+ @Output() onAuthRequestAnswered = new EventEmitter ();
+
protected tableDataSource = new TableDataSource();
protected readonly columnConfig = [
@@ -51,10 +48,7 @@ export class DeviceManagementTableComponent implements OnChanges {
},
];
- constructor(
- private i18nService: I18nService,
- private dialogService: DialogService,
- ) {}
+ constructor(private i18nService: I18nService) {}
ngOnChanges(changes: SimpleChanges): void {
if (changes.devices) {
@@ -62,24 +56,10 @@ export class DeviceManagementTableComponent implements OnChanges {
}
}
- protected async approveOrDenyAuthRequest(pendingAuthRequest: DevicePendingAuthRequest | null) {
+ protected answerAuthRequest(pendingAuthRequest: DevicePendingAuthRequest | null) {
if (pendingAuthRequest == null) {
return;
}
-
- const loginApprovalDialog = LoginApprovalDialogComponent.open(this.dialogService, {
- notificationId: pendingAuthRequest.id,
- });
-
- const result = await firstValueFrom(loginApprovalDialog.closed);
-
- if (result !== undefined && typeof result === "boolean") {
- // Auth request was approved or denied, so clear the
- // pending auth request and re-sort the device array
- this.tableDataSource.data = clearAuthRequestAndResortDevices(
- this.devices,
- pendingAuthRequest,
- );
- }
+ this.onAuthRequestAnswered.emit(pendingAuthRequest);
}
}
diff --git a/libs/angular/src/auth/device-management/device-management.component.html b/libs/angular/src/auth/device-management/device-management.component.html
index 6ee50a32e8e..2a91c2daae2 100644
--- a/libs/angular/src/auth/device-management/device-management.component.html
+++ b/libs/angular/src/auth/device-management/device-management.component.html
@@ -30,11 +30,13 @@
}
diff --git a/libs/angular/src/auth/device-management/device-management.component.ts b/libs/angular/src/auth/device-management/device-management.component.ts
index dc7700a9410..3ab9b2146c5 100644
--- a/libs/angular/src/auth/device-management/device-management.component.ts
+++ b/libs/angular/src/auth/device-management/device-management.component.ts
@@ -16,14 +16,18 @@ import { DeviceType, DeviceTypeMetadata } from "@bitwarden/common/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { MessageListener } from "@bitwarden/common/platform/messaging";
-import { ButtonModule, PopoverModule } from "@bitwarden/components";
+import { ButtonModule, DialogService, PopoverModule } from "@bitwarden/components";
import { I18nPipe } from "@bitwarden/ui-common";
+import { LoginApprovalDialogComponent } from "../login-approval";
+
import { DeviceManagementComponentServiceAbstraction } from "./device-management-component.service.abstraction";
import { DeviceManagementItemGroupComponent } from "./device-management-item-group.component";
import { DeviceManagementTableComponent } from "./device-management-table.component";
+import { clearAuthRequestAndResortDevices, resortDevices } from "./resort-devices.helper";
export interface DeviceDisplayData {
+ creationDate: string;
displayName: string;
firstLogin: Date;
icon: string;
@@ -66,6 +70,7 @@ export class DeviceManagementComponent implements OnInit {
private destroyRef: DestroyRef,
private deviceManagementComponentService: DeviceManagementComponentServiceAbstraction,
private devicesService: DevicesServiceAbstraction,
+ private dialogService: DialogService,
private i18nService: I18nService,
private messageListener: MessageListener,
private validationService: ValidationService,
@@ -130,6 +135,7 @@ export class DeviceManagementComponent implements OnInit {
}
return {
+ creationDate: device.creationDate,
displayName: this.devicesService.getReadableDeviceTypeName(device.type),
firstLogin: device.creationDate ? new Date(device.creationDate) : new Date(),
icon: this.getDeviceIcon(device.type),
@@ -141,7 +147,8 @@ export class DeviceManagementComponent implements OnInit {
pendingAuthRequest: device.response?.devicePendingAuthRequest ?? null,
};
})
- .filter((device) => device !== null);
+ .filter((device) => device !== null)
+ .sort(resortDevices);
}
private async upsertDeviceWithPendingAuthRequest(authRequestId: string) {
@@ -151,6 +158,7 @@ export class DeviceManagementComponent implements OnInit {
}
const upsertDevice: DeviceDisplayData = {
+ creationDate: "",
displayName: this.devicesService.getReadableDeviceTypeName(
authRequestResponse.requestDeviceTypeValue,
),
@@ -174,8 +182,9 @@ export class DeviceManagementComponent implements OnInit {
);
if (existingDevice?.id && existingDevice.creationDate) {
- upsertDevice.id = existingDevice.id;
+ upsertDevice.creationDate = existingDevice.creationDate;
upsertDevice.firstLogin = new Date(existingDevice.creationDate);
+ upsertDevice.id = existingDevice.id;
}
}
@@ -186,10 +195,10 @@ export class DeviceManagementComponent implements OnInit {
if (existingDeviceIndex >= 0) {
// Update existing device in device list
this.devices[existingDeviceIndex] = upsertDevice;
- this.devices = [...this.devices];
+ this.devices = [...this.devices].sort(resortDevices);
} else {
// Add new device to device list
- this.devices = [upsertDevice, ...this.devices];
+ this.devices = [upsertDevice, ...this.devices].sort(resortDevices);
}
}
@@ -227,4 +236,18 @@ export class DeviceManagementComponent implements OnInit {
const metadata = DeviceTypeMetadata[type];
return metadata ? (categoryIconMap[metadata.category] ?? defaultIcon) : defaultIcon;
}
+
+ protected async handleAuthRequestAnswered(pendingAuthRequest: DevicePendingAuthRequest) {
+ const loginApprovalDialog = LoginApprovalDialogComponent.open(this.dialogService, {
+ notificationId: pendingAuthRequest.id,
+ });
+
+ const result = await firstValueFrom(loginApprovalDialog.closed);
+
+ if (result !== undefined && typeof result === "boolean") {
+ // Auth request was approved or denied, so clear the
+ // pending auth request and re-sort the device array
+ this.devices = clearAuthRequestAndResortDevices(this.devices, pendingAuthRequest);
+ }
+ }
}
diff --git a/libs/angular/src/auth/device-management/resort-devices.helper.ts b/libs/angular/src/auth/device-management/resort-devices.helper.ts
index e739e943ee8..01661c18ef3 100644
--- a/libs/angular/src/auth/device-management/resort-devices.helper.ts
+++ b/libs/angular/src/auth/device-management/resort-devices.helper.ts
@@ -23,7 +23,7 @@ export function clearAuthRequestAndResortDevices(
*
* This is a helper function that gets passed to the `Array.sort()` method
*/
-function resortDevices(deviceA: DeviceDisplayData, deviceB: DeviceDisplayData) {
+export function resortDevices(deviceA: DeviceDisplayData, deviceB: DeviceDisplayData) {
// Devices with a pending auth request should be first
if (deviceA.pendingAuthRequest) {
return -1;
@@ -40,11 +40,11 @@ function resortDevices(deviceA: DeviceDisplayData, deviceB: DeviceDisplayData) {
return 1;
}
- // Then sort the rest by display name (alphabetically)
- if (deviceA.displayName < deviceB.displayName) {
+ // Then sort the rest by creation date (newest to oldest)
+ if (deviceA.creationDate > deviceB.creationDate) {
return -1;
}
- if (deviceA.displayName > deviceB.displayName) {
+ if (deviceA.creationDate < deviceB.creationDate) {
return 1;
}
|