1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 16:23:44 +00:00

[PM-19152] Archive in Web (#16686)

* archive and unarchive an individual item

* bulk archive and unachive

* updates to text strings for archive empty state and tooltips

* update translation keys to have an archive verb and noun differentiation

* if premium member loses premium and has archive items. apply filter changes, and item more option changes

* updating unArchive text

* unarchive an archived item on edit if user loses premium

* updates for unarchive btn, refactor archive flag for less churn

* add services to cipher form stories

* add refresh to archive calls in vault, update bulk archive copy

* Do not show archive ability for deleted items

* add archive check for login menu actions

* remove assign to collections for archive filter

* update bulk success message

* add error handling for archive methods

* fix null reference check

* add unarchive icon

---------

Co-authored-by: Nick Krantz <nick@livefront.com>
This commit is contained in:
Jason Ng
2025-10-14 17:41:05 -04:00
committed by GitHub
parent 48c466436e
commit 98af7a13ed
19 changed files with 414 additions and 34 deletions

View File

@@ -48,6 +48,14 @@ export class VaultCipherRowComponent<C extends CipherViewLike> implements OnInit
* uses new permission restore logic from PM-15493
*/
@Input() canRestoreCipher: boolean;
/**
* user has archive permissions
*/
@Input() userCanArchive: boolean;
/**
* Enforge Org Data Ownership Policy Status
*/
@Input() enforceOrgDataOwnershipPolicy: boolean;
@Output() onEvent = new EventEmitter<VaultItemEvent<C>>();
@@ -76,6 +84,20 @@ export class VaultCipherRowComponent<C extends CipherViewLike> implements OnInit
}
}
protected get showArchiveButton() {
return (
this.userCanArchive &&
!CipherViewLikeUtils.isArchived(this.cipher) &&
!CipherViewLikeUtils.isDeleted(this.cipher) &&
!this.cipher.organizationId
);
}
// If item is archived always show unarchive button, even if user is not premium
protected get showUnArchiveButton() {
return CipherViewLikeUtils.isArchived(this.cipher);
}
protected get clickAction() {
if (this.decryptionFailure) {
return "showFailedToDecrypt";
@@ -100,7 +122,12 @@ export class VaultCipherRowComponent<C extends CipherViewLike> implements OnInit
return CipherViewLikeUtils.hasAttachments(this.cipher);
}
// Do not show attachments button if:
// item is archived AND user is not premium user
protected get showAttachments() {
if (CipherViewLikeUtils.isArchived(this.cipher) && !this.userCanArchive) {
return false;
}
return this.canEditCipher || this.hasAttachments;
}
@@ -124,7 +151,11 @@ export class VaultCipherRowComponent<C extends CipherViewLike> implements OnInit
return CipherViewLikeUtils.decryptionFailure(this.cipher);
}
// Do Not show Assign to Collections option if item is archived
protected get showAssignToCollections() {
if (CipherViewLikeUtils.isArchived(this.cipher)) {
return false;
}
return (
this.organizations?.length &&
this.canAssignCollections &&
@@ -132,7 +163,16 @@ export class VaultCipherRowComponent<C extends CipherViewLike> implements OnInit
);
}
// Do NOT show clone option if:
// item is archived AND user is not premium user
// item is archived AND enforce org data ownership policy is on
protected get showClone() {
if (
CipherViewLikeUtils.isArchived(this.cipher) &&
(!this.userCanArchive || this.enforceOrgDataOwnershipPolicy)
) {
return false;
}
return this.cloneable && !CipherViewLikeUtils.isDeleted(this.cipher);
}
@@ -140,10 +180,11 @@ export class VaultCipherRowComponent<C extends CipherViewLike> implements OnInit
return this.useEvents && this.cipher.organizationId;
}
protected get isNotDeletedLoginCipher() {
protected get isActiveLoginCipher() {
return (
CipherViewLikeUtils.getType(this.cipher) === this.CipherType.Login &&
!CipherViewLikeUtils.isDeleted(this.cipher)
!CipherViewLikeUtils.isDeleted(this.cipher) &&
!CipherViewLikeUtils.isArchived(this.cipher)
);
}
@@ -191,20 +232,20 @@ export class VaultCipherRowComponent<C extends CipherViewLike> implements OnInit
protected get showCopyUsername(): boolean {
const usernameCopy = CipherViewLikeUtils.hasCopyableValue(this.cipher, "username");
return this.isNotDeletedLoginCipher && usernameCopy;
return this.isActiveLoginCipher && usernameCopy;
}
protected get showCopyPassword(): boolean {
const passwordCopy = CipherViewLikeUtils.hasCopyableValue(this.cipher, "password");
return this.isNotDeletedLoginCipher && this.cipher.viewPassword && passwordCopy;
return this.isActiveLoginCipher && this.cipher.viewPassword && passwordCopy;
}
protected get showCopyTotp(): boolean {
return this.isNotDeletedLoginCipher && this.showTotpCopyButton;
return this.isActiveLoginCipher && this.showTotpCopyButton;
}
protected get showLaunchUri(): boolean {
return this.isNotDeletedLoginCipher && this.canLaunch;
return this.isActiveLoginCipher && this.canLaunch;
}
protected get isDeletedCanRestore(): boolean {
@@ -236,6 +277,14 @@ export class VaultCipherRowComponent<C extends CipherViewLike> implements OnInit
this.onEvent.emit({ type: "viewEvents", item: this.cipher });
}
protected archive() {
this.onEvent.emit({ type: "archive", items: [this.cipher] });
}
protected unarchive() {
this.onEvent.emit({ type: "unarchive", items: [this.cipher] });
}
protected restore() {
this.onEvent.emit({ type: "restore", items: [this.cipher] });
}