mirror of
https://github.com/bitwarden/browser
synced 2026-01-06 10:33:57 +00:00
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>
This commit is contained in:
@@ -89,6 +89,12 @@ import {
|
||||
RoutedVaultFilterModel,
|
||||
Unassigned,
|
||||
} from "../individual-vault/vault-filter/shared/models/routed-vault-filter.model";
|
||||
import {
|
||||
openViewCipherDialog,
|
||||
ViewCipherDialogCloseResult,
|
||||
ViewCipherDialogResult,
|
||||
ViewComponent,
|
||||
} from "../individual-vault/view.component";
|
||||
import { VaultHeaderComponent } from "../org-vault/vault-header/vault-header.component";
|
||||
import { getNestedCollectionTree } from "../utils/collection-utils";
|
||||
|
||||
@@ -121,6 +127,7 @@ enum AddAccessStatusType {
|
||||
VaultItemsModule,
|
||||
SharedModule,
|
||||
NoItemsModule,
|
||||
ViewComponent,
|
||||
],
|
||||
providers: [RoutedVaultFilterService, RoutedVaultFilterBridgeService],
|
||||
})
|
||||
@@ -517,7 +524,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
(await firstValueFrom(allCipherMap$))[cipherId] != undefined;
|
||||
|
||||
if (canEditCipher) {
|
||||
await this.editCipherId(cipherId);
|
||||
if (qParams.action === "view") {
|
||||
await this.viewCipherById(cipherId);
|
||||
} else {
|
||||
await this.editCipherId(cipherId);
|
||||
}
|
||||
} else {
|
||||
this.toastService.showToast({
|
||||
variant: "error",
|
||||
@@ -848,12 +859,64 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
modal.onClosedPromise().then(() => {
|
||||
this.go({ cipherId: null, itemId: null });
|
||||
this.go({ cipherId: null, itemId: null, action: null });
|
||||
});
|
||||
|
||||
return childComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a CipherView and opens a dialog where it can be viewed (wraps viewCipherById).
|
||||
* @param cipher - CipherView
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
viewCipher(cipher: CipherView) {
|
||||
return this.viewCipherById(cipher.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a cipher id and opens a dialog where it can be viewed.
|
||||
* @param id - string
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
async viewCipherById(id: string) {
|
||||
const cipher = await this.cipherService.get(id);
|
||||
// if cipher exists (cipher is null when new) and MP reprompt
|
||||
// is on for this cipher, then show password reprompt.
|
||||
if (
|
||||
cipher &&
|
||||
cipher.reprompt !== 0 &&
|
||||
!(await this.passwordRepromptService.showPasswordPrompt())
|
||||
) {
|
||||
// didn't pass password prompt, so don't open add / edit modal.
|
||||
this.go({ cipherId: null, itemId: null });
|
||||
return;
|
||||
}
|
||||
|
||||
// Decrypt the cipher.
|
||||
const cipherView = await cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher),
|
||||
);
|
||||
|
||||
// Open the dialog.
|
||||
const dialogRef = openViewCipherDialog(this.dialogService, {
|
||||
data: { cipher: cipherView },
|
||||
});
|
||||
|
||||
// Wait for the dialog to close.
|
||||
const result: ViewCipherDialogCloseResult = await lastValueFrom(dialogRef.closed);
|
||||
|
||||
// If the dialog was closed by deleting the cipher, refresh the vault.
|
||||
if (result.action === ViewCipherDialogResult.deleted) {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
// If the dialog was closed by any other action (close button, escape key, etc), navigate back to the vault.
|
||||
if (!result.action) {
|
||||
this.go({ cipherId: null, itemId: null, action: null });
|
||||
}
|
||||
}
|
||||
|
||||
async cloneCipher(cipher: CipherView) {
|
||||
if (cipher.login?.hasFido2Credentials) {
|
||||
const confirmed = await this.dialogService.openSimpleDialog({
|
||||
|
||||
Reference in New Issue
Block a user