diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json
index d91a33c67968..a5d0852eed55 100644
--- a/apps/browser/src/_locales/en/messages.json
+++ b/apps/browser/src/_locales/en/messages.json
@@ -558,7 +558,7 @@
"message": "Archive",
"description": "Verb"
},
- "unarchive": {
+ "unArchive": {
"message": "Unarchive"
},
"itemsInArchive": {
@@ -570,11 +570,11 @@
"noItemsInArchiveDesc": {
"message": "Archived items will appear here and will be excluded from general search results and autofill suggestions."
},
- "itemSentToArchive": {
- "message": "Item sent to archive"
+ "itemWasSentToArchive": {
+ "message": "Item was sent to archive"
},
- "itemRemovedFromArchive": {
- "message": "Item removed from archive"
+ "itemUnarchived": {
+ "message": "Item was unarchived"
},
"archiveItem": {
"message": "Archive item"
diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts
index ebcd8707597b..83535b09e662 100644
--- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts
+++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts
@@ -302,7 +302,7 @@ export class ItemMoreOptionsComponent {
await this.cipherArchiveService.archiveWithServer(this.cipher.id as CipherId, activeUserId);
this.toastService.showToast({
variant: "success",
- message: this.i18nService.t("itemSentToArchive"),
+ message: this.i18nService.t("itemWasSentToArchive"),
});
}
}
diff --git a/apps/browser/src/vault/popup/settings/archive.component.html b/apps/browser/src/vault/popup/settings/archive.component.html
index 5fb57814fff1..faaf0243fc78 100644
--- a/apps/browser/src/vault/popup/settings/archive.component.html
+++ b/apps/browser/src/vault/popup/settings/archive.component.html
@@ -49,7 +49,7 @@
{{ "clone" | i18n }}
-
+
+ @if (showArchiveButton) {
+
+ }
+
+ @if (showUnArchiveButton) {
+
+ }
+
+
+
+
+
+
diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts
index 82ddda66110c..b40124c39e4c 100644
--- a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts
+++ b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts
@@ -64,6 +64,8 @@ export class VaultItemsComponent {
@Input() addAccessStatus: number;
@Input() addAccessToggle: boolean;
@Input() activeCollection: CollectionView | undefined;
+ @Input() userCanArchive: boolean;
+ @Input() enforceOrgDataOwnershipPolicy: boolean;
private restrictedPolicies = toSignal(this.restrictedItemTypesService.restricted$);
@@ -191,6 +193,30 @@ export class VaultItemsComponent {
);
}
+ get bulkArchiveAllowed() {
+ if (this.selection.selected.length === 0 || !this.userCanArchive) {
+ return false;
+ }
+
+ return (
+ this.userCanArchive &&
+ !this.selection.selected.find(
+ (item) => item.cipher && (item.cipher.organizationId || item.cipher.archivedDate),
+ )
+ );
+ }
+
+ // Bulk Unarchive button should appear for Archive vault even if user does not have archive permissions
+ get bulkUnarchiveAllowed() {
+ if (this.selection.selected.length === 0) {
+ return false;
+ }
+
+ return !this.selection.selected.find(
+ (item) => !item.cipher.archivedDate || item.cipher.organizationId,
+ );
+ }
+
//@TODO: remove this function when removing the limitItemDeletion$ feature flag.
get showDelete(): boolean {
if (this.selection.selected.length === 0) {
@@ -221,7 +247,17 @@ export class VaultItemsComponent {
}
get bulkAssignToCollectionsAllowed() {
- return this.showBulkAddToCollections && this.ciphers.length > 0;
+ return (
+ this.showBulkAddToCollections &&
+ this.ciphers.length > 0 &&
+ !this.anySelectedCiphersAreArchived
+ );
+ }
+
+ get anySelectedCiphersAreArchived() {
+ return this.selection.selected.some(
+ (item) => item.cipher && CipherViewLikeUtils.isArchived(item.cipher),
+ );
}
protected canEditCollection(collection: CollectionView): boolean {
@@ -270,6 +306,24 @@ export class VaultItemsComponent {
});
}
+ protected bulkArchive() {
+ this.event({
+ type: "archive",
+ items: this.selection.selected
+ .filter((item) => item.cipher !== undefined)
+ .map((item) => item.cipher),
+ });
+ }
+
+ protected bulkUnarchive() {
+ this.event({
+ type: "unarchive",
+ items: this.selection.selected
+ .filter((item) => item.cipher !== undefined)
+ .map((item) => item.cipher),
+ });
+ }
+
protected bulkRestore() {
this.event({
type: "restore",
diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts b/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts
index 278ae62aaa6d..180152f054c2 100644
--- a/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts
+++ b/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts
@@ -242,16 +242,13 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
};
async buildAllFilters(): Promise {
- const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
+ const hasArchiveFlag = await firstValueFrom(this.cipherArchiveService.hasArchiveFlagEnabled$());
const builderFilter = {} as VaultFilterList;
builderFilter.organizationFilter = await this.addOrganizationFilter();
builderFilter.typeFilter = await this.addTypeFilter();
builderFilter.folderFilter = await this.addFolderFilter();
builderFilter.collectionFilter = await this.addCollectionFilter();
- if (
- (await firstValueFrom(this.cipherArchiveService.userCanArchive$(userId))) ||
- (await firstValueFrom(this.cipherArchiveService.showArchiveVault$(userId)))
- ) {
+ if (hasArchiveFlag) {
builderFilter.archiveFilter = await this.addArchiveFilter();
}
builderFilter.trashFilter = await this.addTrashFilter();
diff --git a/apps/web/src/app/vault/individual-vault/vault.component.html b/apps/web/src/app/vault/individual-vault/vault.component.html
index 68219173debd..711a34413b5b 100644
--- a/apps/web/src/app/vault/individual-vault/vault.component.html
+++ b/apps/web/src/app/vault/individual-vault/vault.component.html
@@ -50,6 +50,8 @@
[useEvents]="false"
[showAdminActions]="false"
[showBulkAddToCollections]="true"
+ [userCanArchive]="userCanArchive$ | async"
+ [enforceOrgDataOwnershipPolicy]="enforceOrgDataOwnershipPolicy$ | async"
(onEvent)="onVaultItemsEvent($event)"
>
diff --git a/apps/web/src/app/vault/individual-vault/vault.component.ts b/apps/web/src/app/vault/individual-vault/vault.component.ts
index cdb40c843a29..97905d81f38b 100644
--- a/apps/web/src/app/vault/individual-vault/vault.component.ts
+++ b/apps/web/src/app/vault/individual-vault/vault.component.ts
@@ -46,6 +46,8 @@ import {
getOrganizationById,
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
+import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
+import { PolicyType } from "@bitwarden/common/admin-console/enums";
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";
@@ -210,7 +212,7 @@ export class VaultComponent implements OnInit, OnDestr
.pipe(map((a) => a?.id))
.pipe(switchMap((id) => this.organizationService.organizations$(id)));
- private userCanArchive$ = this.accountService.activeAccount$.pipe(
+ protected userCanArchive$ = this.accountService.activeAccount$.pipe(
getUserId,
switchMap((userId) => {
return this.cipherArchiveService.userCanArchive$(userId);
@@ -256,7 +258,7 @@ export class VaultComponent implements OnInit, OnDestr
},
archive: {
title: "noItemsInArchive",
- description: "archivedItemsDescription",
+ description: "noItemsInArchiveDesc",
icon: this.itemTypesIcon,
},
};
@@ -275,6 +277,15 @@ export class VaultComponent implements OnInit, OnDestr
}),
);
+ protected enforceOrgDataOwnershipPolicy$ = this.accountService.activeAccount$.pipe(
+ getUserId,
+ switchMap((userId) =>
+ this.policyService.policyAppliesToUser$(PolicyType.OrganizationDataOwnership, userId),
+ ),
+ );
+
+ private userId$ = this.accountService.activeAccount$.pipe(getUserId);
+
constructor(
private syncService: SyncService,
private route: ActivatedRoute,
@@ -307,6 +318,7 @@ export class VaultComponent implements OnInit, OnDestr
private restrictedItemTypesService: RestrictedItemTypesService,
private cipherArchiveService: CipherArchiveService,
private organizationWarningsService: OrganizationWarningsService,
+ private policyService: PolicyService,
private unifiedUpgradePromptService: UnifiedUpgradePromptService,
) {}
@@ -405,7 +417,7 @@ export class VaultComponent implements OnInit, OnDestr
allowedCiphers$,
filter$,
this.currentSearchText$,
- this.userCanArchive$,
+ this.cipherArchiveService.hasArchiveFlagEnabled$(),
]).pipe(
filter(([ciphers, filter]) => ciphers != undefined && filter != undefined),
concatMap(async ([ciphers, filter, searchText, archiveEnabled]) => {
@@ -653,12 +665,140 @@ export class VaultComponent implements OnInit, OnDestr
case "assignToCollections":
await this.bulkAssignToCollections(event.items);
break;
+ case "archive":
+ if (event.items.length === 1) {
+ await this.archive(event.items[0]);
+ } else {
+ await this.bulkArchive(event.items);
+ }
+ break;
+ case "unarchive":
+ if (event.items.length === 1) {
+ await this.unarchive(event.items[0]);
+ } else {
+ await this.bulkUnarchive(event.items);
+ }
+ break;
}
} finally {
this.processingEvent = false;
}
}
+ async archive(cipher: C) {
+ const confirmed = await this.dialogService.openSimpleDialog({
+ title: { key: "archiveItem" },
+ content: { key: "archiveItemConfirmDesc" },
+ type: "info",
+ });
+
+ if (!confirmed) {
+ return;
+ }
+
+ const repromptPassed = await this.passwordRepromptService.passwordRepromptCheck(cipher);
+ if (!repromptPassed) {
+ return;
+ }
+ const activeUserId = await firstValueFrom(this.userId$);
+ try {
+ await this.cipherArchiveService.archiveWithServer(cipher.id as CipherId, activeUserId);
+ this.toastService.showToast({
+ variant: "success",
+ message: this.i18nService.t("itemWasSentToArchive"),
+ });
+ this.refresh();
+ } catch (e) {
+ this.logService.error("Error archiving cipher", e);
+ this.toastService.showToast({
+ variant: "error",
+ message: this.i18nService.t("errorOccurred"),
+ });
+ }
+ }
+
+ async bulkArchive(ciphers: C[]) {
+ const confirmed = await this.dialogService.openSimpleDialog({
+ title: { key: "archiveBulkItems" },
+ content: { key: "archiveBulkItemsConfirmDesc" },
+ type: "info",
+ });
+
+ if (!confirmed) {
+ return;
+ }
+
+ if (!(await this.repromptCipher(ciphers))) {
+ return;
+ }
+
+ const activeUserId = await firstValueFrom(this.userId$);
+ const cipherIds = ciphers.map((c) => c.id as CipherId);
+ try {
+ await this.cipherArchiveService.archiveWithServer(cipherIds as CipherId[], activeUserId);
+ this.toastService.showToast({
+ variant: "success",
+ message: this.i18nService.t("itemsWereSentToArchive"),
+ });
+ this.refresh();
+ } catch (e) {
+ this.logService.error("Error archiving ciphers", e);
+ this.toastService.showToast({
+ variant: "error",
+ message: this.i18nService.t("errorOccurred"),
+ });
+ }
+ }
+
+ async unarchive(cipher: C) {
+ const repromptPassed = await this.passwordRepromptService.passwordRepromptCheck(cipher);
+ if (!repromptPassed) {
+ return;
+ }
+ const activeUserId = await firstValueFrom(this.userId$);
+
+ try {
+ await this.cipherArchiveService.unarchiveWithServer(cipher.id as CipherId, activeUserId);
+
+ this.toastService.showToast({
+ variant: "success",
+ message: this.i18nService.t("itemUnarchived"),
+ });
+
+ this.refresh();
+ } catch (e) {
+ this.logService.error("Error unarchiving cipher", e);
+ this.toastService.showToast({
+ variant: "error",
+ message: this.i18nService.t("errorOccurred"),
+ });
+ }
+ }
+
+ async bulkUnarchive(ciphers: C[]) {
+ if (!(await this.repromptCipher(ciphers))) {
+ return;
+ }
+
+ const activeUserId = await firstValueFrom(this.userId$);
+ const cipherIds = ciphers.map((c) => c.id as CipherId);
+ try {
+ await this.cipherArchiveService.unarchiveWithServer(cipherIds as CipherId[], activeUserId);
+ this.toastService.showToast({
+ variant: "success",
+ message: this.i18nService.t("bulkUnarchiveItems"),
+ });
+
+ this.refresh();
+ } catch (e) {
+ this.logService.error("Error unarchiving ciphers", e);
+ this.toastService.showToast({
+ variant: "error",
+ message: this.i18nService.t("errorOccurred"),
+ });
+ }
+ }
+
async applyOrganizationFilter(orgId: string) {
if (orgId == null) {
orgId = "MyVault";
diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json
index fd237992d6b7..1b547021b275 100644
--- a/apps/web/src/locales/en/messages.json
+++ b/apps/web/src/locales/en/messages.json
@@ -11298,12 +11298,47 @@
"message": "Archive",
"description": "Verb"
},
+ "unArchive": {
+ "message": "Unarchive"
+ },
+ "itemsInArchive": {
+ "message": "Items in archive"
+ },
"noItemsInArchive": {
"message": "No items in archive"
},
- "archivedItemsDescription": {
+ "noItemsInArchiveDesc": {
"message": "Archived items will appear here and will be excluded from general search results and autofill suggestions."
},
+ "itemWasSentToArchive": {
+ "message": "Item was sent to archive"
+ },
+ "itemsWereSentToArchive": {
+ "message": "Items were sent to archive"
+ },
+ "itemUnarchived": {
+ "message": "Item was unarchived"
+ },
+ "bulkArchiveItems": {
+ "message": "Items archived"
+ },
+ "bulkUnarchiveItems": {
+ "message": "Items unarchived"
+ },
+ "archiveItem": {
+ "message": "Archive item",
+ "description": "Verb"
+ },
+ "archiveItemConfirmDesc": {
+ "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?"
+ },
+ "archiveBulkItems": {
+ "message": "Archive items",
+ "description": "Verb"
+ },
+ "archiveBulkItemsConfirmDesc": {
+ "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive these items?"
+ },
"businessUnit": {
"message": "Business Unit"
},
diff --git a/libs/common/src/vault/abstractions/cipher-archive.service.ts b/libs/common/src/vault/abstractions/cipher-archive.service.ts
index cb6c38ddf679..d33fc5e7cc7e 100644
--- a/libs/common/src/vault/abstractions/cipher-archive.service.ts
+++ b/libs/common/src/vault/abstractions/cipher-archive.service.ts
@@ -4,6 +4,7 @@ import { CipherId, UserId } from "@bitwarden/common/types/guid";
import { CipherViewLike } from "@bitwarden/common/vault/utils/cipher-view-like-utils";
export abstract class CipherArchiveService {
+ abstract hasArchiveFlagEnabled$(): Observable;
abstract archivedCiphers$(userId: UserId): Observable;
abstract userCanArchive$(userId: UserId): Observable;
abstract showArchiveVault$(userId: UserId): Observable;
diff --git a/libs/common/src/vault/services/default-cipher-archive.service.ts b/libs/common/src/vault/services/default-cipher-archive.service.ts
index 5c627d687b20..a56a22474a34 100644
--- a/libs/common/src/vault/services/default-cipher-archive.service.ts
+++ b/libs/common/src/vault/services/default-cipher-archive.service.ts
@@ -27,6 +27,10 @@ export class DefaultCipherArchiveService implements CipherArchiveService {
private configService: ConfigService,
) {}
+ hasArchiveFlagEnabled$(): Observable {
+ return this.configService.getFeatureFlag$(FeatureFlag.PM19148_InnovationArchive);
+ }
+
/**
* Observable that contains the list of ciphers that have been archived.
*/
diff --git a/libs/vault/src/cipher-form/cipher-form.stories.ts b/libs/vault/src/cipher-form/cipher-form.stories.ts
index 3059deb58148..3b1017ffe32d 100644
--- a/libs/vault/src/cipher-form/cipher-form.stories.ts
+++ b/libs/vault/src/cipher-form/cipher-form.stories.ts
@@ -10,7 +10,7 @@ import {
moduleMetadata,
StoryObj,
} from "@storybook/angular";
-import { BehaviorSubject } from "rxjs";
+import { BehaviorSubject, of } from "rxjs";
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
// eslint-disable-next-line no-restricted-imports
@@ -155,6 +155,20 @@ export default {
} as NudgeStatus),
},
},
+ {
+ provide: CipherArchiveService,
+ useValue: {
+ userCanArchive$: of(false),
+ },
+ },
+ {
+ provide: AccountService,
+ useValue: {
+ activeAccount$: of({
+ name: "User 1",
+ }),
+ } as Partial,
+ },
{
provide: CipherFormService,
useClass: TestAddEditFormService,
diff --git a/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts b/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts
index ac384ee3fd86..08afee33ae36 100644
--- a/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts
+++ b/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts
@@ -2,14 +2,18 @@ import { ChangeDetectorRef } from "@angular/core";
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { ReactiveFormsModule } from "@angular/forms";
import { mock } from "jest-mock-extended";
+import { of } from "rxjs";
import { ViewCacheService } from "@bitwarden/angular/platform/view-cache";
+import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
+import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { Fido2CredentialView } from "@bitwarden/common/vault/models/view/fido2-credential.view";
import { ToastService } from "@bitwarden/components";
+import { UserId } from "@bitwarden/user-core";
import { CipherFormConfig } from "../abstractions/cipher-form-config.service";
import { CipherFormService } from "../abstractions/cipher-form.service";
@@ -23,6 +27,10 @@ describe("CipherFormComponent", () => {
const decryptCipher = jest.fn().mockResolvedValue(new CipherView());
+ const mockAccountService = mock();
+ const mockCipherArchiveService = mock();
+ const mockAddEditFormService = { saveCipher: jest.fn(), decryptCipher };
+
beforeEach(async () => {
decryptCipher.mockClear();
@@ -32,13 +40,15 @@ describe("CipherFormComponent", () => {
{ provide: ChangeDetectorRef, useValue: {} },
{ provide: I18nService, useValue: { t: (key: string) => key } },
{ provide: ToastService, useValue: { showToast: jest.fn() } },
- { provide: CipherFormService, useValue: { saveCipher: jest.fn(), decryptCipher } },
+ { provide: CipherFormService, useValue: mockAddEditFormService },
{
provide: CipherFormCacheService,
useValue: { init: jest.fn(), getCachedCipherView: jest.fn() },
},
{ provide: ViewCacheService, useValue: { signal: jest.fn(() => (): any => null) } },
{ provide: ConfigService, useValue: mock() },
+ { provide: AccountService, useValue: mockAccountService },
+ { provide: CipherArchiveService, useValue: mockCipherArchiveService },
],
}).compileComponents();
});
@@ -53,6 +63,29 @@ describe("CipherFormComponent", () => {
expect(component).toBeTruthy();
});
+ describe("submit", () => {
+ beforeEach(() => {
+ component.config = { mode: "edit" } as CipherFormConfig;
+
+ component["updatedCipherView"] = new CipherView();
+ component["updatedCipherView"].archivedDate = new Date();
+ });
+
+ it("should remove archivedDate when user cannot archive and cipher is archived", async () => {
+ mockAccountService.activeAccount$ = of({ id: "user-id" as UserId } as Account);
+ mockCipherArchiveService.userCanArchive$.mockReturnValue(of(false));
+ mockAddEditFormService.saveCipher = jest.fn().mockResolvedValue(new CipherView());
+
+ const originalArchivedDate = component["updatedCipherView"]?.archivedDate;
+ expect(originalArchivedDate).not.toBeNull();
+
+ await component.submit();
+
+ expect(component["updatedCipherView"]?.archivedDate).toBeNull();
+ expect(mockCipherArchiveService.userCanArchive$).toHaveBeenCalledWith("user-id");
+ });
+ });
+
describe("website", () => {
it("should return null if updatedCipherView is null", () => {
component["updatedCipherView"] = null as any;
diff --git a/libs/vault/src/cipher-form/components/cipher-form.component.ts b/libs/vault/src/cipher-form/components/cipher-form.component.ts
index 19e7f9d2d903..117dd98ba435 100644
--- a/libs/vault/src/cipher-form/components/cipher-form.component.ts
+++ b/libs/vault/src/cipher-form/components/cipher-form.component.ts
@@ -17,9 +17,12 @@ import {
} from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormBuilder, FormGroup, ReactiveFormsModule } from "@angular/forms";
-import { BehaviorSubject, Subject } from "rxjs";
+import { BehaviorSubject, firstValueFrom, Subject, switchMap } from "rxjs";
+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 { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service";
import { CipherType, SecureNoteType } from "@bitwarden/common/vault/enums";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import {
@@ -301,6 +304,8 @@ export class CipherFormComponent implements AfterViewInit, OnInit, OnChanges, Ci
private i18nService: I18nService,
private changeDetectorRef: ChangeDetectorRef,
private cipherFormCacheService: CipherFormCacheService,
+ private cipherArchiveService: CipherArchiveService,
+ private accountService: AccountService,
) {}
/**
@@ -342,6 +347,18 @@ export class CipherFormComponent implements AfterViewInit, OnInit, OnChanges, Ci
}
}
+ const userCanArchive = await firstValueFrom(
+ this.accountService.activeAccount$.pipe(
+ getUserId,
+ switchMap((userId) => this.cipherArchiveService.userCanArchive$(userId)),
+ ),
+ );
+
+ // If the item is archived but user has lost archive permissions, unarchive the item.
+ if (!userCanArchive && this.updatedCipherView.archivedDate) {
+ this.updatedCipherView.archivedDate = null;
+ }
+
const savedCipher = await this.addEditFormService.saveCipher(
this.updatedCipherView,
this.config,