1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-12 06:13:38 +00:00

[PM-14952] - remove Edit button and show restore button when viewing item in trash (#12847)

* add restore function to vault item dialog

* update comment

* revert removing of delete button

* use canDeleteCipher$

* put canRestore in class property

* set showRestore after canDeleteCipher is set
This commit is contained in:
Jordan Aasen
2025-01-17 09:15:21 -08:00
committed by GitHub
parent 457aa07aa9
commit 3b4eb40990
4 changed files with 48 additions and 5 deletions

View File

@@ -36,7 +36,10 @@
</vault-cipher-form> </vault-cipher-form>
</div> </div>
<ng-container bitDialogFooter> <ng-container bitDialogFooter>
<ng-container *ngIf="showCipherView"> <button *ngIf="showRestore" [bitAction]="restore" bitButton buttonType="primary" type="button">
{{ "restore" | i18n }}
</button>
<ng-container *ngIf="showCipherView && !showRestore">
<button <button
bitButton bitButton
[bitAction]="switchToEdit" [bitAction]="switchToEdit"
@@ -53,7 +56,7 @@
form="cipherForm" form="cipherForm"
buttonType="primary" buttonType="primary"
#submitBtn #submitBtn
[hidden]="showCipherView" [hidden]="showCipherView || showRestore"
> >
{{ "save" | i18n }} {{ "save" | i18n }}
</button> </button>
@@ -62,7 +65,7 @@
type="button" type="button"
buttonType="secondary" buttonType="secondary"
(click)="cancel()" (click)="cancel()"
*ngIf="!showCipherView" *ngIf="!showCipherView && !showRestore"
> >
{{ "cancel" | i18n }} {{ "cancel" | i18n }}
</button> </button>

View File

@@ -49,6 +49,8 @@ import {
AttachmentDialogResult, AttachmentDialogResult,
AttachmentsV2Component, AttachmentsV2Component,
} from "../../individual-vault/attachments-v2.component"; } from "../../individual-vault/attachments-v2.component";
import { RoutedVaultFilterService } from "../../individual-vault/vault-filter/services/routed-vault-filter.service";
import { RoutedVaultFilterModel } from "../../individual-vault/vault-filter/shared/models/routed-vault-filter.model";
import { WebCipherFormGenerationService } from "../../services/web-cipher-form-generation.service"; import { WebCipherFormGenerationService } from "../../services/web-cipher-form-generation.service";
import { WebVaultPremiumUpgradePromptService } from "../../services/web-premium-upgrade-prompt.service"; import { WebVaultPremiumUpgradePromptService } from "../../services/web-premium-upgrade-prompt.service";
import { WebViewPasswordHistoryService } from "../../services/web-view-password-history.service"; import { WebViewPasswordHistoryService } from "../../services/web-view-password-history.service";
@@ -82,6 +84,11 @@ export interface VaultItemDialogParams {
* If true, the dialog is being opened from the admin console. * If true, the dialog is being opened from the admin console.
*/ */
isAdminConsoleAction?: boolean; isAdminConsoleAction?: boolean;
/**
* Function to restore a cipher from the trash.
*/
restore: (c: CipherView) => Promise<boolean>;
} }
export enum VaultItemDialogResult { export enum VaultItemDialogResult {
@@ -99,6 +106,11 @@ export enum VaultItemDialogResult {
* The dialog was closed to navigate the user the premium upgrade page. * The dialog was closed to navigate the user the premium upgrade page.
*/ */
PremiumUpgrade = "premiumUpgrade", PremiumUpgrade = "premiumUpgrade",
/**
* A cipher was restored
*/
Restored = "restored",
} }
@Component({ @Component({
@@ -121,6 +133,7 @@ export enum VaultItemDialogResult {
{ provide: PremiumUpgradePromptService, useClass: WebVaultPremiumUpgradePromptService }, { provide: PremiumUpgradePromptService, useClass: WebVaultPremiumUpgradePromptService },
{ provide: ViewPasswordHistoryService, useClass: WebViewPasswordHistoryService }, { provide: ViewPasswordHistoryService, useClass: WebViewPasswordHistoryService },
{ provide: CipherFormGenerationService, useClass: WebCipherFormGenerationService }, { provide: CipherFormGenerationService, useClass: WebCipherFormGenerationService },
RoutedVaultFilterService,
], ],
}) })
export class VaultItemDialogComponent implements OnInit, OnDestroy { export class VaultItemDialogComponent implements OnInit, OnDestroy {
@@ -191,6 +204,20 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy {
), ),
); );
/**
* Determines if the user may restore the item.
* A user may restore items if they have delete permissions and the item is in the trash.
*/
protected async canUserRestore() {
return (
this.filter?.type === "trash" &&
this.cipher?.isDeleted &&
(await firstValueFrom(this.canDeleteCipher$))
);
}
protected showRestore: boolean;
protected get loadingForm() { protected get loadingForm() {
return this.loadForm && !this.formReady; return this.loadForm && !this.formReady;
} }
@@ -230,6 +257,8 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy {
protected canDeleteCipher$: Observable<boolean>; protected canDeleteCipher$: Observable<boolean>;
protected filter: RoutedVaultFilterModel;
constructor( constructor(
@Inject(DIALOG_DATA) protected params: VaultItemDialogParams, @Inject(DIALOG_DATA) protected params: VaultItemDialogParams,
private dialogRef: DialogRef<VaultItemDialogResult>, private dialogRef: DialogRef<VaultItemDialogResult>,
@@ -246,6 +275,7 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy {
private cipherAuthorizationService: CipherAuthorizationService, private cipherAuthorizationService: CipherAuthorizationService,
private apiService: ApiService, private apiService: ApiService,
private eventCollectionService: EventCollectionService, private eventCollectionService: EventCollectionService,
private routedVaultFilterService: RoutedVaultFilterService,
) { ) {
this.updateTitle(); this.updateTitle();
} }
@@ -283,6 +313,9 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy {
); );
} }
this.filter = await firstValueFrom(this.routedVaultFilterService.filter$);
this.showRestore = await this.canUserRestore();
this.performingInitialLoad = false; this.performingInitialLoad = false;
} }
@@ -336,6 +369,11 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy {
this._formReadySubject.next(); this._formReadySubject.next();
} }
restore = async () => {
await this.params.restore(this.cipher);
this.dialogRef.close(VaultItemDialogResult.Restored);
};
delete = async () => { delete = async () => {
if (!this.cipher) { if (!this.cipher) {
return; return;

View File

@@ -717,6 +717,7 @@ export class VaultComponent implements OnInit, OnDestroy {
mode, mode,
formConfig, formConfig,
activeCollectionId, activeCollectionId,
restore: this.restore,
}); });
const result = await lastValueFrom(this.vaultItemDialogRef.closed); const result = await lastValueFrom(this.vaultItemDialogRef.closed);

View File

@@ -994,6 +994,7 @@ export class VaultComponent implements OnInit, OnDestroy {
disableForm, disableForm,
activeCollectionId, activeCollectionId,
isAdminConsoleAction: true, isAdminConsoleAction: true,
restore: this.restore,
}); });
const result = await lastValueFrom(this.vaultItemDialogRef.closed); const result = await lastValueFrom(this.vaultItemDialogRef.closed);
@@ -1033,7 +1034,7 @@ export class VaultComponent implements OnInit, OnDestroy {
}); });
} }
async restore(c: CipherView): Promise<boolean> { restore = async (c: CipherView): Promise<boolean> => {
if (!c.isDeleted) { if (!c.isDeleted) {
return; return;
} }
@@ -1064,7 +1065,7 @@ export class VaultComponent implements OnInit, OnDestroy {
} catch (e) { } catch (e) {
this.logService.error(e); this.logService.error(e);
} }
} };
async bulkRestore(ciphers: CipherView[]) { async bulkRestore(ciphers: CipherView[]) {
if ( if (