1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-23 03:33:54 +00:00
Files
browser/apps/web/src/app/vault/individual-vault/view.component.ts
Alec Rippberger 3a31eb2f10 PM-9665: implement view item view (#10416)
* Add initial view cipher dialog.

* Add working view cipher modal dialog markup.

* Cleanup dialog markup and allow edit from dialog.

* Cleanup unused imports.

* Begin adding org-vault view-cipher functionality.

* Refactor to remove loose-components usage and use DialogService.

* Add edit and delete button functionality.

* Add delete functionality.

* Remove addition to loose components.

* Remove unused modal-dialog artifacts.

* Ensure dialog closes and URL updates properly on edit or close.

* Disable edit/delete buttons instead of hiding them.

* Add simple tests for view.component.ts.

* Adjust import order.

* Remove now unnecessary ng-template.

* Decrypt cipher to cipher view.

* Add cleanup function and additional delete test.

* Remove boolean return from delete promise.

* Remove fake timers.

* Remove unnecessary TestBed.inject calls.

* Add code comments.

* Hide new view cipher dialog behind feature flag.

* Keep "else if" statement intact.

* Simplify getting cipherTypeString.

* Add comments to vault.component.ts files.

* Change button type to "danger"

Update apps/web/src/app/vault/individual-vault/view.component.html

Co-authored-by: Nick Krantz <125900171+nick-livefront@users.noreply.github.com>

* Add a11y title to delete button.

* Simplify OrganizationService testing.

* Update comment to better reflect function.

* Use large dialog to better match designs.

* Add aria-haspopup to cipher row button.

* Add deleteCipher to messages.json.

* Remove extra argument from canEditAllCiphers.

* Use 'delete' instead of 'delete cipher' for a11y title.

* Remove 'bitFormButton' from non-form buttons.

* Rework view cipher view delete functionality.

* Add translations for cipher types.

* Remove unecesarry test.

* Add additional test coverage to ensure dialogs close.

* Add back delete functionality in view.component.ts.

* Update "secure note" to "note".

---------

Co-authored-by: Nick Krantz <125900171+nick-livefront@users.noreply.github.com>
2024-08-19 14:32:17 -07:00

194 lines
6.3 KiB
TypeScript

import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
import { CommonModule } from "@angular/common";
import { Component, Inject, OnInit, EventEmitter, OnDestroy } from "@angular/core";
import { Router } from "@angular/router";
import { Subject } from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
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 { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import {
AsyncActionsModule,
DialogModule,
DialogService,
ToastService,
} from "@bitwarden/components";
import { CipherViewComponent } from "../../../../../../libs/vault/src/cipher-view/cipher-view.component";
import { SharedModule } from "../../shared/shared.module";
export interface ViewCipherDialogParams {
cipher: CipherView;
}
export enum ViewCipherDialogResult {
edited = "edited",
deleted = "deleted",
}
export interface ViewCipherDialogCloseResult {
action: ViewCipherDialogResult;
}
/**
* Component for viewing a cipher, presented in a dialog.
*/
@Component({
selector: "app-vault-view",
templateUrl: "view.component.html",
standalone: true,
imports: [CipherViewComponent, CommonModule, AsyncActionsModule, DialogModule, SharedModule],
})
export class ViewComponent implements OnInit, OnDestroy {
cipher: CipherView;
onDeletedCipher = new EventEmitter<CipherView>();
cipherTypeString: string;
cipherEditUrl: string;
organization: Organization;
restrictProviderAccess = false;
protected destroy$ = new Subject<void>();
constructor(
@Inject(DIALOG_DATA) public params: ViewCipherDialogParams,
private dialogRef: DialogRef<ViewCipherDialogCloseResult>,
private i18nService: I18nService,
private dialogService: DialogService,
private messagingService: MessagingService,
private logService: LogService,
private cipherService: CipherService,
private toastService: ToastService,
private organizationService: OrganizationService,
private router: Router,
private configService: ConfigService,
) {}
/**
* Lifecycle hook for component initialization.
*/
async ngOnInit() {
this.cipher = this.params.cipher;
this.cipherTypeString = this.getCipherViewTypeString();
if (this.cipher.organizationId) {
this.organization = await this.organizationService.get(this.cipher.organizationId);
}
this.restrictProviderAccess = await this.configService.getFeatureFlag(
FeatureFlag.RestrictProviderAccess,
);
}
/**
* Lifecycle hook for component destruction.
*/
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
/**
* 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<void> {
const asAdmin = this.organization?.canEditAllCiphers(this.restrictProviderAccess);
if (this.cipher.isDeleted) {
await this.cipherService.deleteWithServer(this.cipher.id, asAdmin);
} else {
await this.cipherService.softDeleteWithServer(this.cipher.id, asAdmin);
}
}
/**
* Method to handle cipher editing. Called when a user clicks the edit button.
*/
async edit(): Promise<void> {
this.dialogRef.close({ action: ViewCipherDialogResult.edited });
await this.router.navigate([], {
queryParams: {
itemId: this.cipher.id,
action: "edit",
organizationId: this.cipher.organizationId,
},
});
}
/**
* 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("viewItemType", this.i18nService.t("typeLogin").toLowerCase());
case CipherType.SecureNote:
return this.i18nService.t("viewItemType", this.i18nService.t("note").toLowerCase());
case CipherType.Card:
return this.i18nService.t("viewItemType", this.i18nService.t("typeCard").toLowerCase());
case CipherType.Identity:
return this.i18nService.t("viewItemType", this.i18nService.t("typeIdentity").toLowerCase());
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<ViewCipherDialogParams>,
): DialogRef<ViewCipherDialogCloseResult> {
return dialogService.open(ViewComponent, config);
}