mirror of
https://github.com/bitwarden/browser
synced 2026-02-19 10:54:00 +00:00
[PM-31679] remove archive from browser edit (#18854)
* removing archive btns from browser edit form footer, remove archive items from showing in expired premium users vault
This commit is contained in:
committed by
jaasen-livefront
parent
c410f04b2f
commit
6fe2aecbf9
@@ -41,24 +41,6 @@
|
||||
</button>
|
||||
|
||||
<ng-container slot="end">
|
||||
@if (isEditMode) {
|
||||
@if ((archiveFlagEnabled$ | async) && isCipherArchived) {
|
||||
<button
|
||||
type="button"
|
||||
[bitAction]="unarchive"
|
||||
bitIconButton="bwi-unarchive"
|
||||
[label]="'unarchive' | i18n"
|
||||
></button>
|
||||
}
|
||||
@if ((userCanArchive$ | async) && canCipherBeArchived) {
|
||||
<button
|
||||
type="button"
|
||||
[bitAction]="archive"
|
||||
bitIconButton="bwi-archive"
|
||||
[label]="'archiveVerb' | i18n"
|
||||
></button>
|
||||
}
|
||||
}
|
||||
@if (canDeleteCipher$ | async) {
|
||||
<button
|
||||
[bitAction]="delete"
|
||||
|
||||
@@ -443,111 +443,6 @@ describe("AddEditComponent", () => {
|
||||
}));
|
||||
});
|
||||
|
||||
describe("archive", () => {
|
||||
it("calls archiveCipherUtilsService service to archive the cipher", async () => {
|
||||
buildConfigResponse.originalCipher = { id: "222-333-444-5555", edit: true } as Cipher;
|
||||
queryParams$.next({ cipherId: "222-333-444-5555" });
|
||||
|
||||
await fixture.whenStable();
|
||||
await component.archive();
|
||||
|
||||
expect(component["archiveCipherUtilsService"].archiveCipher).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ id: "222-333-444-5555" }),
|
||||
true,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("unarchive", () => {
|
||||
it("calls archiveCipherUtilsService service to unarchive the cipher", async () => {
|
||||
buildConfigResponse.originalCipher = {
|
||||
id: "222-333-444-5555",
|
||||
archivedDate: new Date(),
|
||||
edit: true,
|
||||
} as Cipher;
|
||||
queryParams$.next({ cipherId: "222-333-444-5555" });
|
||||
|
||||
await component.unarchive();
|
||||
|
||||
expect(component["archiveCipherUtilsService"].unarchiveCipher).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ id: "222-333-444-5555" }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("archive button", () => {
|
||||
beforeEach(() => {
|
||||
// prevent form from rendering
|
||||
jest.spyOn(component as any, "loading", "get").mockReturnValue(true);
|
||||
buildConfigResponse.originalCipher = { archivedDate: undefined, edit: true } as Cipher;
|
||||
});
|
||||
|
||||
it("shows the archive button when the user can archive and the cipher can be archived", fakeAsync(() => {
|
||||
cipherArchiveService.userCanArchive$.mockReturnValue(of(true));
|
||||
queryParams$.next({ cipherId: "222-333-444-5555" });
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
|
||||
const archiveBtn = fixture.debugElement.query(By.css("button[biticonbutton='bwi-archive']"));
|
||||
expect(archiveBtn).toBeTruthy();
|
||||
}));
|
||||
|
||||
it("does not show the archive button when the user cannot archive", fakeAsync(() => {
|
||||
cipherArchiveService.userCanArchive$.mockReturnValue(of(false));
|
||||
queryParams$.next({ cipherId: "222-333-444-5555" });
|
||||
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
|
||||
const archiveBtn = fixture.debugElement.query(By.css("button[biticonbutton='bwi-archive']"));
|
||||
expect(archiveBtn).toBeFalsy();
|
||||
}));
|
||||
|
||||
it("does not show the archive button when the cipher cannot be archived", fakeAsync(() => {
|
||||
cipherArchiveService.userCanArchive$.mockReturnValue(of(true));
|
||||
buildConfigResponse.originalCipher = { archivedDate: new Date(), edit: true } as Cipher;
|
||||
queryParams$.next({ cipherId: "222-333-444-5555" });
|
||||
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
|
||||
const archiveBtn = fixture.debugElement.query(By.css("button[biticonbutton='bwi-archive']"));
|
||||
expect(archiveBtn).toBeFalsy();
|
||||
}));
|
||||
});
|
||||
|
||||
describe("unarchive button", () => {
|
||||
beforeEach(() => {
|
||||
// prevent form from rendering
|
||||
jest.spyOn(component as any, "loading", "get").mockReturnValue(true);
|
||||
buildConfigResponse.originalCipher = { edit: true } as Cipher;
|
||||
});
|
||||
|
||||
it("shows the unarchive button when the cipher is archived", fakeAsync(() => {
|
||||
buildConfigResponse.originalCipher = { archivedDate: new Date(), edit: true } as Cipher;
|
||||
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
|
||||
const unarchiveBtn = fixture.debugElement.query(
|
||||
By.css("button[biticonbutton='bwi-unarchive']"),
|
||||
);
|
||||
expect(unarchiveBtn).toBeTruthy();
|
||||
}));
|
||||
|
||||
it("does not show the unarchive button when the cipher is not archived", fakeAsync(() => {
|
||||
queryParams$.next({ cipherId: "222-333-444-5555" });
|
||||
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
|
||||
const unarchiveBtn = fixture.debugElement.query(
|
||||
By.css("button[biticonbutton='bwi-unarchive']"),
|
||||
);
|
||||
expect(unarchiveBtn).toBeFalsy();
|
||||
}));
|
||||
});
|
||||
|
||||
describe("delete", () => {
|
||||
it("dialogService openSimpleDialog called when deleteBtn is hit", async () => {
|
||||
const dialogSpy = jest
|
||||
|
||||
@@ -201,14 +201,6 @@ export class AddEditComponent implements OnInit, OnDestroy {
|
||||
return new CipherView(this.config?.originalCipher);
|
||||
}
|
||||
|
||||
get canCipherBeArchived(): boolean {
|
||||
return this.cipher?.canBeArchived;
|
||||
}
|
||||
|
||||
get isCipherArchived(): boolean {
|
||||
return this.cipher?.isArchived;
|
||||
}
|
||||
|
||||
private fido2PopoutSessionData$ = fido2PopoutSessionData$();
|
||||
private fido2PopoutSessionData: Fido2SessionData;
|
||||
|
||||
@@ -370,10 +362,6 @@ export class AddEditComponent implements OnInit, OnDestroy {
|
||||
await BrowserApi.sendMessage("addEditCipherSubmitted");
|
||||
}
|
||||
|
||||
get isEditMode(): boolean {
|
||||
return ["edit", "partial-edit"].includes(this.config?.mode);
|
||||
}
|
||||
|
||||
subscribeToParams(): void {
|
||||
this.route.queryParams
|
||||
.pipe(
|
||||
@@ -487,40 +475,6 @@ export class AddEditComponent implements OnInit, OnDestroy {
|
||||
return this.i18nService.t(translation[type]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the cipher in the form after archiving/unarchiving.
|
||||
* @param revisionDate The new revision date.
|
||||
* @param archivedDate The new archived date (null if unarchived).
|
||||
**/
|
||||
updateCipherFromArchive = (revisionDate: Date, archivedDate: Date | null) => {
|
||||
this.cipherFormComponent().patchCipher((current) => {
|
||||
current.revisionDate = revisionDate;
|
||||
current.archivedDate = archivedDate;
|
||||
return current;
|
||||
});
|
||||
};
|
||||
|
||||
archive = async () => {
|
||||
const cipherResponse = await this.archiveCipherUtilsService.archiveCipher(this.cipher, true);
|
||||
|
||||
if (!cipherResponse) {
|
||||
return;
|
||||
}
|
||||
this.updateCipherFromArchive(
|
||||
new Date(cipherResponse.revisionDate),
|
||||
new Date(cipherResponse.archivedDate),
|
||||
);
|
||||
};
|
||||
|
||||
unarchive = async () => {
|
||||
const cipherResponse = await this.archiveCipherUtilsService.unarchiveCipher(this.cipher);
|
||||
|
||||
if (!cipherResponse) {
|
||||
return;
|
||||
}
|
||||
this.updateCipherFromArchive(new Date(cipherResponse.revisionDate), null);
|
||||
};
|
||||
|
||||
delete = async () => {
|
||||
const confirmed = await this.dialogService.openSimpleDialog({
|
||||
title: { key: "deleteItem" },
|
||||
|
||||
@@ -69,7 +69,7 @@ describe("VaultPopupItemsService", () => {
|
||||
const accountServiceMock = mockAccountServiceWith(userId);
|
||||
const configServiceMock = mock<ConfigService>();
|
||||
const cipherArchiveServiceMock = mock<CipherArchiveService>();
|
||||
cipherArchiveServiceMock.userCanArchive$.mockReturnValue(of(true));
|
||||
cipherArchiveServiceMock.hasArchiveFlagEnabled$ = of(true);
|
||||
|
||||
const restrictedItemTypesService = {
|
||||
restricted$: new BehaviorSubject<RestrictedCipherType[]>([]),
|
||||
|
||||
@@ -135,24 +135,23 @@ export class VaultPopupItemsService {
|
||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||
);
|
||||
|
||||
private userCanArchive$ = this.activeUserId$.pipe(
|
||||
switchMap((userId) => {
|
||||
return this.cipherArchiveService.userCanArchive$(userId);
|
||||
}),
|
||||
);
|
||||
|
||||
private _activeCipherList$: Observable<PopupCipherViewLike[]> = this._allDecryptedCiphers$.pipe(
|
||||
switchMap((ciphers) =>
|
||||
combineLatest([this.organizations$, this.decryptedCollections$, this.userCanArchive$]).pipe(
|
||||
map(([organizations, collections, canArchive]) => {
|
||||
combineLatest([
|
||||
this.organizations$,
|
||||
this.decryptedCollections$,
|
||||
this.cipherArchiveService.hasArchiveFlagEnabled$,
|
||||
]).pipe(
|
||||
map(([organizations, collections, archiveFlag]) => {
|
||||
const orgMap = Object.fromEntries(organizations.map((org) => [org.id, org]));
|
||||
const collectionMap = Object.fromEntries(collections.map((col) => [col.id, col]));
|
||||
return ciphers
|
||||
.filter(
|
||||
(c) =>
|
||||
!CipherViewLikeUtils.isDeleted(c) &&
|
||||
(!canArchive || !CipherViewLikeUtils.isArchived(c)),
|
||||
(!archiveFlag || !CipherViewLikeUtils.isArchived(c)),
|
||||
)
|
||||
|
||||
.map((cipher) => {
|
||||
(cipher as PopupCipherViewLike).collections = cipher.collectionIds?.map(
|
||||
(colId) => collectionMap[colId as CollectionId],
|
||||
|
||||
Reference in New Issue
Block a user