From 6d98360b04d036104f299804ae1d0cd727ff7696 Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Mon, 6 Oct 2025 11:42:21 -0500 Subject: [PATCH] remove unused view component (#16582) --- .../organizations/collections/vault.module.ts | 2 - .../vault/individual-vault/vault.module.ts | 2 - .../individual-vault/view.component.html | 31 --- .../individual-vault/view.component.spec.ts | 148 ------------ .../vault/individual-vault/view.component.ts | 218 ------------------ 5 files changed, 401 deletions(-) delete mode 100644 apps/web/src/app/vault/individual-vault/view.component.html delete mode 100644 apps/web/src/app/vault/individual-vault/view.component.spec.ts delete mode 100644 apps/web/src/app/vault/individual-vault/view.component.ts diff --git a/apps/web/src/app/admin-console/organizations/collections/vault.module.ts b/apps/web/src/app/admin-console/organizations/collections/vault.module.ts index 1a093ff835..d7c6a468eb 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault.module.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault.module.ts @@ -2,7 +2,6 @@ import { NgModule } from "@angular/core"; import { SharedModule } from "../../../shared/shared.module"; import { OrganizationBadgeModule } from "../../../vault/individual-vault/organization-badge/organization-badge.module"; -import { ViewComponent } from "../../../vault/individual-vault/view.component"; import { CollectionDialogComponent } from "../shared/components/collection-dialog"; import { CollectionNameBadgeComponent } from "./collection-badge"; @@ -19,7 +18,6 @@ import { VaultComponent } from "./vault.component"; OrganizationBadgeModule, CollectionDialogComponent, VaultComponent, - ViewComponent, ], }) export class VaultModule {} diff --git a/apps/web/src/app/vault/individual-vault/vault.module.ts b/apps/web/src/app/vault/individual-vault/vault.module.ts index 573eceef64..156e73b439 100644 --- a/apps/web/src/app/vault/individual-vault/vault.module.ts +++ b/apps/web/src/app/vault/individual-vault/vault.module.ts @@ -10,7 +10,6 @@ import { OrganizationBadgeModule } from "./organization-badge/organization-badge import { PipesModule } from "./pipes/pipes.module"; import { VaultRoutingModule } from "./vault-routing.module"; import { VaultComponent } from "./vault.component"; -import { ViewComponent } from "./view.component"; @NgModule({ imports: [ @@ -23,7 +22,6 @@ import { ViewComponent } from "./view.component"; BulkDialogsModule, CollectionDialogComponent, VaultComponent, - ViewComponent, ], }) export class VaultModule {} diff --git a/apps/web/src/app/vault/individual-vault/view.component.html b/apps/web/src/app/vault/individual-vault/view.component.html deleted file mode 100644 index ac6db21236..0000000000 --- a/apps/web/src/app/vault/individual-vault/view.component.html +++ /dev/null @@ -1,31 +0,0 @@ - - - {{ cipherTypeString }} - - - - - - -
- -
-
-
diff --git a/apps/web/src/app/vault/individual-vault/view.component.spec.ts b/apps/web/src/app/vault/individual-vault/view.component.spec.ts deleted file mode 100644 index d60a6313c6..0000000000 --- a/apps/web/src/app/vault/individual-vault/view.component.spec.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { mock } from "jest-mock-extended"; -import { of } from "rxjs"; - -import { CollectionService } from "@bitwarden/admin-console/common"; -import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; -import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; -import { UserId } from "@bitwarden/common/types/guid"; -import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; -import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; -import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; -import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; -import { TaskService } from "@bitwarden/common/vault/tasks"; -import { DIALOG_DATA, DialogRef, DialogService, ToastService } from "@bitwarden/components"; -import { KeyService } from "@bitwarden/key-management"; -import { ChangeLoginPasswordService } from "@bitwarden/vault"; - -import { ViewCipherDialogParams, ViewCipherDialogResult, ViewComponent } from "./view.component"; - -describe("ViewComponent", () => { - let component: ViewComponent; - let fixture: ComponentFixture; - - const mockCipher: CipherView = { - id: "cipher-id", - type: 1, - organizationId: "org-id", - isDeleted: false, - } as CipherView; - - const mockOrganization: Organization = { - id: "org-id", - name: "Test Organization", - } as Organization; - - const mockParams: ViewCipherDialogParams = { - cipher: mockCipher, - }; - const userId = Utils.newGuid() as UserId; - const accountService: FakeAccountService = mockAccountServiceWith(userId); - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ViewComponent], - providers: [ - { provide: DIALOG_DATA, useValue: mockParams }, - { provide: DialogRef, useValue: mock() }, - { provide: I18nService, useValue: { t: jest.fn().mockReturnValue("login") } }, - { provide: DialogService, useValue: mock() }, - { provide: CipherService, useValue: mock() }, - { provide: ToastService, useValue: mock() }, - { provide: MessagingService, useValue: mock() }, - { - provide: AccountService, - useValue: accountService, - }, - { provide: LogService, useValue: mock() }, - { - provide: OrganizationService, - useValue: { organizations$: jest.fn().mockReturnValue(of([mockOrganization])) }, - }, - { provide: CollectionService, useValue: mock() }, - { provide: FolderService, useValue: mock() }, - { provide: KeyService, useValue: mock() }, - { - provide: BillingAccountProfileStateService, - useValue: mock(), - }, - { provide: ConfigService, useValue: mock() }, - { provide: AccountService, useValue: mockAccountServiceWith("UserId" as UserId) }, - { - provide: CipherAuthorizationService, - useValue: { - canDeleteCipher$: jest.fn().mockReturnValue(true), - }, - }, - { provide: TaskService, useValue: mock() }, - ], - }) - .overrideComponent(ViewComponent, { - remove: { - providers: [ - { provide: PlatformUtilsService, useValue: PlatformUtilsService }, - { - provide: ChangeLoginPasswordService, - useValue: ChangeLoginPasswordService, - }, - ], - }, - add: { - providers: [ - { provide: PlatformUtilsService, useValue: mock() }, - { - provide: ChangeLoginPasswordService, - useValue: mock(), - }, - ], - }, - }) - .compileComponents(); - - fixture = TestBed.createComponent(ViewComponent); - component = fixture.componentInstance; - component.params = mockParams; - component.cipher = mockCipher; - }); - - describe("ngOnInit", () => { - it("initializes the component with cipher and organization", async () => { - await component.ngOnInit(); - - expect(component.cipher).toEqual(mockCipher); - expect(component.organization).toEqual(mockOrganization); - }); - }); - - describe("edit", () => { - it("closes the dialog with the proper arguments", async () => { - const dialogRefCloseSpy = jest.spyOn(component["dialogRef"], "close"); - - await component.edit(); - - expect(dialogRefCloseSpy).toHaveBeenCalledWith({ action: ViewCipherDialogResult.Edited }); - }); - }); - - describe("delete", () => { - it("calls the delete method on delete and closes the dialog with the proper arguments", async () => { - const deleteSpy = jest.spyOn(component, "delete"); - const dialogRefCloseSpy = jest.spyOn(component["dialogRef"], "close"); - jest.spyOn(component["dialogService"], "openSimpleDialog").mockResolvedValue(true); - - await component.delete(); - - expect(deleteSpy).toHaveBeenCalled(); - expect(dialogRefCloseSpy).toHaveBeenCalledWith({ action: ViewCipherDialogResult.Deleted }); - }); - }); -}); diff --git a/apps/web/src/app/vault/individual-vault/view.component.ts b/apps/web/src/app/vault/individual-vault/view.component.ts deleted file mode 100644 index 6de29f8e32..0000000000 --- a/apps/web/src/app/vault/individual-vault/view.component.ts +++ /dev/null @@ -1,218 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { CommonModule } from "@angular/common"; -import { Component, EventEmitter, Inject, OnInit } from "@angular/core"; -import { firstValueFrom, map, Observable } from "rxjs"; - -import { CollectionView } from "@bitwarden/admin-console/common"; -import { VaultViewPasswordHistoryService } from "@bitwarden/angular/services/view-password-history.service"; -import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; -import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { getUserId } from "@bitwarden/common/auth/services/account.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; -import { CollectionId } from "@bitwarden/common/types/guid"; -import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; -import { ViewPasswordHistoryService } from "@bitwarden/common/vault/abstractions/view-password-history.service"; -import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; -import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; -import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; -import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; -import { - DIALOG_DATA, - DialogRef, - DialogConfig, - AsyncActionsModule, - DialogModule, - DialogService, - ToastService, -} from "@bitwarden/components"; -import { CipherViewComponent } from "@bitwarden/vault"; - -import { SharedModule } from "../../shared/shared.module"; - -export interface ViewCipherDialogParams { - cipher: CipherView; - - /** - * Optional list of collections the cipher is assigned to. If none are provided, they will be loaded using the - * `CipherService` and the `collectionIds` property of the cipher. - */ - collections?: CollectionView[]; - - /** - * Optional collection ID used to know the collection filter selected. - */ - activeCollectionId?: CollectionId; - - /** - * If true, the edit button will be disabled in the dialog. - */ - disableEdit?: boolean; -} - -export const ViewCipherDialogResult = { - Edited: "edited", - Deleted: "deleted", - PremiumUpgrade: "premiumUpgrade", -} as const; - -type ViewCipherDialogResult = UnionOfValues; - -export interface ViewCipherDialogCloseResult { - action: ViewCipherDialogResult; -} - -/** - * Component for viewing a cipher, presented in a dialog. - * @deprecated Use the VaultItemDialogComponent instead. - */ -@Component({ - selector: "app-vault-view", - templateUrl: "view.component.html", - imports: [CipherViewComponent, CommonModule, AsyncActionsModule, DialogModule, SharedModule], - providers: [{ provide: ViewPasswordHistoryService, useClass: VaultViewPasswordHistoryService }], -}) -export class ViewComponent implements OnInit { - cipher: CipherView; - collections?: CollectionView[]; - onDeletedCipher = new EventEmitter(); - cipherTypeString: string; - organization: Organization; - - canDeleteCipher$: Observable; - - constructor( - @Inject(DIALOG_DATA) public params: ViewCipherDialogParams, - private dialogRef: DialogRef, - private i18nService: I18nService, - private dialogService: DialogService, - private messagingService: MessagingService, - private logService: LogService, - private cipherService: CipherService, - private toastService: ToastService, - private organizationService: OrganizationService, - private cipherAuthorizationService: CipherAuthorizationService, - private accountService: AccountService, - ) {} - - /** - * Lifecycle hook for component initialization. - */ - async ngOnInit() { - this.cipher = this.params.cipher; - this.collections = this.params.collections; - this.cipherTypeString = this.getCipherViewTypeString(); - - const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); - - if (this.cipher.organizationId) { - this.organization = await firstValueFrom( - this.organizationService - .organizations$(userId) - .pipe( - map((organizations) => organizations.find((o) => o.id === this.cipher.organizationId)), - ), - ); - } - - this.canDeleteCipher$ = this.cipherAuthorizationService.canDeleteCipher$(this.cipher); - } - - /** - * Method to handle cipher deletion. Called when a user clicks the delete button. - */ - delete = async () => { - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "deleteItem" }, - content: { - key: this.cipher.isDeleted ? "permanentlyDeleteItemConfirmation" : "deleteItemConfirmation", - }, - type: "warning", - }); - - if (!confirmed) { - return; - } - - try { - await this.deleteCipher(); - this.toastService.showToast({ - variant: "success", - title: this.i18nService.t("success"), - message: this.i18nService.t( - this.cipher.isDeleted ? "permanentlyDeletedItem" : "deletedItem", - ), - }); - this.onDeletedCipher.emit(this.cipher); - this.messagingService.send( - this.cipher.isDeleted ? "permanentlyDeletedCipher" : "deletedCipher", - ); - } catch (e) { - this.logService.error(e); - } - - this.dialogRef.close({ action: ViewCipherDialogResult.Deleted }); - }; - - /** - * Helper method to delete cipher. - */ - protected async deleteCipher(): Promise { - const asAdmin = this.organization?.canEditAllCiphers; - const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); - if (this.cipher.isDeleted) { - await this.cipherService.deleteWithServer(this.cipher.id, userId, asAdmin); - } else { - await this.cipherService.softDeleteWithServer(this.cipher.id, userId, asAdmin); - } - } - - /** - * Method to handle cipher editing. Called when a user clicks the edit button. - */ - async edit(): Promise { - this.dialogRef.close({ action: ViewCipherDialogResult.Edited }); - } - - /** - * Method to get cipher view type string, used for the dialog title. - * E.g. "View login" or "View note". - * @returns The localized string for the cipher type - */ - getCipherViewTypeString(): string { - if (!this.cipher) { - return null; - } - - switch (this.cipher.type) { - case CipherType.Login: - return this.i18nService.t("viewItemHeaderLogin"); - case CipherType.SecureNote: - return this.i18nService.t("viewItemHeaderCard"); - case CipherType.Card: - return this.i18nService.t("viewItemHeaderIdentity"); - case CipherType.Identity: - return this.i18nService.t("viewItemHeaderNote"); - case CipherType.SshKey: - return this.i18nService.t("viewItemHeaderSshKey"); - default: - return null; - } - } -} - -/** - * Strongly typed helper to open a cipher view dialog - * @param dialogService Instance of the dialog service that will be used to open the dialog - * @param config Configuration for the dialog - * @returns A reference to the opened dialog - */ -export function openViewCipherDialog( - dialogService: DialogService, - config: DialogConfig, -): DialogRef { - return dialogService.open(ViewComponent, config); -}