From 31a3694746f824a6d09ffeacc012cfaaf8f239a1 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Mon, 5 May 2025 09:57:32 -0400 Subject: [PATCH] refactor(change-password-component): Change Password Update [18720] - Just about done with changes. Need to get the filtering working. --- apps/desktop/src/locales/en/messages.json | 6 ++ .../vault/app/vault/item-footer.component.ts | 56 ++++++++++++++++++- .../filters/status-filter.component.html | 1 + .../vault/app/vault/vault-v2.component.html | 4 +- .../src/vault/app/vault/vault-v2.component.ts | 30 +++++++++- .../src/vault/app/vault/vault.component.html | 2 + .../src/vault/app/vault/vault.component.ts | 29 +++++++++- .../src/vault/app/vault/view.component.html | 19 ++++++- .../src/vault/components/view.component.ts | 33 +++++++++-- .../vault-filter/models/vault-filter.model.ts | 6 ++ 10 files changed, 171 insertions(+), 15 deletions(-) diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 3c87b80ce8f..9f96b8dab9e 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -1948,6 +1948,12 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archiveItem": { + "message": "Item archived" + }, + "unarchiveItem": { + "message": "Item unarchived" + }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/vault/app/vault/item-footer.component.ts b/apps/desktop/src/vault/app/vault/item-footer.component.ts index 639d1557ecd..76d9a1f940d 100644 --- a/apps/desktop/src/vault/app/vault/item-footer.component.ts +++ b/apps/desktop/src/vault/app/vault/item-footer.component.ts @@ -7,7 +7,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { CollectionId, UserId } from "@bitwarden/common/types/guid"; +import { CipherId, CollectionId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherRepromptType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -28,6 +28,8 @@ export class ItemFooterComponent implements OnInit { @Input() isSubmitting: boolean = false; @Output() onEdit = new EventEmitter(); @Output() onClone = new EventEmitter(); + @Output() onArchive = new EventEmitter(); + @Output() onUnarchive = new EventEmitter(); @Output() onDelete = new EventEmitter(); @Output() onRestore = new EventEmitter(); @Output() onCancel = new EventEmitter(); @@ -85,6 +87,58 @@ export class ItemFooterComponent implements OnInit { this.onCancel.emit(this.cipher); } + async archive(): Promise { + if (!(await this.promptPassword())) { + return false; + } + + const confirmed = await this.dialogService.openSimpleDialog({ + title: { key: "archiveItem" }, + content: { + key: "archiveItemConfirmation", + }, + type: "warning", + }); + + if (!confirmed) { + return false; + } + + try { + const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + await this.cipherService.archiveWithServer(this.cipher.id as CipherId, activeUserId); + this.toastService.showToast({ + variant: "success", + message: this.i18nService.t("archivedItem"), + }); + this.onArchive.emit(this.cipher); + } catch (e) { + this.logService.error(e); + } + + return true; + } + + async unarchive(): Promise { + if (!this.cipher.isArchived) { + return false; + } + + try { + const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + await this.cipherService.unarchiveWithServer(this.cipher.id as CipherId, activeUserId) + this.toastService.showToast({ + variant: "success", + message: this.i18nService.t("unarchivedItem"), + }); + this.onUnarchive.emit(this.cipher); + } catch (e) { + this.logService.error(e); + } + + return true; + } + async delete(): Promise { if (!(await this.promptPassword())) { return false; diff --git a/apps/desktop/src/vault/app/vault/vault-filter/filters/status-filter.component.html b/apps/desktop/src/vault/app/vault/vault-filter/filters/status-filter.component.html index 8a089b59396..d31beea23e8 100644 --- a/apps/desktop/src/vault/app/vault/vault-filter/filters/status-filter.component.html +++ b/apps/desktop/src/vault/app/vault/vault-filter/filters/status-filter.component.html @@ -29,6 +29,7 @@ +
  • diff --git a/apps/desktop/src/vault/app/vault/vault-v2.component.ts b/apps/desktop/src/vault/app/vault/vault-v2.component.ts index 05c6c5e261e..14dae5fc885 100644 --- a/apps/desktop/src/vault/app/vault/vault-v2.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-v2.component.ts @@ -73,6 +73,7 @@ import { ItemFooterComponent } from "./item-footer.component"; import { VaultFilterComponent } from "./vault-filter/vault-filter.component"; import { VaultFilterModule } from "./vault-filter/vault-filter.module"; import { VaultItemsV2Component } from "./vault-items-v2.component"; +import { CipherStatus } from "@bitwarden/angular/vault/vault-filter/models/cipher-status.model"; const BroadcasterSubscriptionId = "VaultComponent"; @@ -299,7 +300,7 @@ export class VaultV2Component implements OnInit, OnDestroy { this.cipherService .failedToDecryptCiphers$(this.activeUserId) .pipe( - map((ciphers) => ciphers?.filter((c) => !c.isDeleted) ?? []), + map((ciphers) => ciphers?.filter((c) => !c.isDeleted || !c.isArchived) ?? []), filter((ciphers) => ciphers.length > 0), take(1), takeUntil(this.componentIsDestroyed$), @@ -336,8 +337,18 @@ export class VaultV2Component implements OnInit, OnDestroy { await this.addCipher(this.addType).catch(() => {}); } + let cipherStatus: CipherStatus = "all"; + + if (params.deleted) { + cipherStatus = "trash"; + } else if (params.archived){ + cipherStatus = "archive"; + } else if (params.favorites){ + cipherStatus = "favorites"; + } + this.activeFilter = new VaultFilter({ - status: params.deleted ? "trash" : params.favorites ? "favorites" : "all", + status: cipherStatus, cipherType: params.action === "add" || params.type == null ? undefined @@ -558,6 +569,21 @@ export class VaultV2Component implements OnInit, OnDestroy { await this.vaultItemsComponent?.refresh().catch(() => {}); } + async archiveCipher() { + this.cipherId = null; + this.cipher = null; + this.action = null; + await this.go().catch(() => {}); + await this.vaultItemsComponent?.refresh().catch(() => {}); + } + + async unarchiveCipher() { + this.cipherId = null; + this.action = null; + await this.go().catch(() => {}); + await this.vaultItemsComponent?.refresh().catch(() => {}); + } + async deleteCipher() { this.cipherId = null; this.cipher = null; diff --git a/apps/desktop/src/vault/app/vault/vault.component.html b/apps/desktop/src/vault/app/vault/vault.component.html index 99131a848cc..ba9374b9e4a 100644 --- a/apps/desktop/src/vault/app/vault/vault.component.html +++ b/apps/desktop/src/vault/app/vault/vault.component.html @@ -18,6 +18,8 @@ (onCloneCipher)="cloneCipherWithoutPasswordPrompt($event)" (onEditCipher)="editCipher($event)" (onViewCipherPasswordHistory)="viewCipherPasswordHistory($event)" + (onArchivedCipher)="archivedCipher($event)" + (onUnarchivedCipher)="unarchivedCipher($event)" (onRestoredCipher)="restoredCipher($event)" (onDeletedCipher)="deletedCipher($event)" > diff --git a/apps/desktop/src/vault/app/vault/vault.component.ts b/apps/desktop/src/vault/app/vault/vault.component.ts index 6c0d5ef81d0..3f924649acf 100644 --- a/apps/desktop/src/vault/app/vault/vault.component.ts +++ b/apps/desktop/src/vault/app/vault/vault.component.ts @@ -51,6 +51,7 @@ import { ShareComponent } from "./share.component"; import { VaultFilterComponent } from "./vault-filter/vault-filter.component"; import { VaultItemsComponent } from "./vault-items.component"; import { ViewComponent } from "./view.component"; +import { CipherStatus } from "@bitwarden/angular/vault/vault-filter/models/cipher-status.model"; const BroadcasterSubscriptionId = "VaultComponent"; @@ -247,7 +248,7 @@ export class VaultComponent implements OnInit, OnDestroy { this.cipherService .failedToDecryptCiphers$(this.activeUserId) .pipe( - map((ciphers) => ciphers?.filter((c) => !c.isDeleted) ?? []), + map((ciphers) => ciphers?.filter((c) => !c.isDeleted || !c.isArchived) ?? []), filter((ciphers) => ciphers.length > 0), take(1), takeUntil(this.componentIsDestroyed$), @@ -286,8 +287,18 @@ export class VaultComponent implements OnInit, OnDestroy { this.addCipher(this.addType); } + let cipherStatus: CipherStatus = "all"; + + if (params.deleted) { + cipherStatus = "trash"; + } else if (params.archived){ + cipherStatus = "archive"; + } else if (params.favorites){ + cipherStatus = "favorites"; + } + this.activeFilter = new VaultFilter({ - status: params.deleted ? "trash" : params.favorites ? "favorites" : "all", + status: cipherStatus, cipherType: params.action === "add" || params.type == null ? null : parseInt(params.type, null), selectedFolderId: params.folderId, @@ -524,6 +535,20 @@ export class VaultComponent implements OnInit, OnDestroy { await this.vaultItemsComponent.refresh(); } + async archivedCipher(cipher: CipherView) { + this.cipherId = null; + this.action = null; + this.go(); + await this.vaultItemsComponent.refresh(); + } + + async unarchivedCipher(cipher: CipherView) { + this.cipherId = null; + this.action = null; + this.go(); + await this.vaultItemsComponent.refresh(); + } + async restoredCipher(cipher: CipherView) { this.cipherId = null; this.action = null; diff --git a/apps/desktop/src/vault/app/vault/view.component.html b/apps/desktop/src/vault/app/vault/view.component.html index b11a38e00a4..2f0e96325c9 100644 --- a/apps/desktop/src/vault/app/vault/view.component.html +++ b/apps/desktop/src/vault/app/vault/view.component.html @@ -651,6 +651,15 @@ > + -
    -
    diff --git a/libs/angular/src/vault/components/view.component.ts b/libs/angular/src/vault/components/view.component.ts index 6efa1ad39b2..eb2c0380ad4 100644 --- a/libs/angular/src/vault/components/view.component.ts +++ b/libs/angular/src/vault/components/view.component.ts @@ -40,7 +40,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer"; -import { CollectionId, UserId } from "@bitwarden/common/types/guid"; +import { CipherId, CollectionId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service"; @@ -76,6 +76,8 @@ export class ViewComponent implements OnDestroy, OnInit { @Output() onEditCipher = new EventEmitter(); @Output() onCloneCipher = new EventEmitter(); @Output() onShareCipher = new EventEmitter(); + @Output() onArchivedCipher = new EventEmitter(); + @Output() onUnarchivedCipher = new EventEmitter(); @Output() onDeletedCipher = new EventEmitter(); @Output() onRestoredCipher = new EventEmitter(); @@ -231,15 +233,34 @@ export class ViewComponent implements OnDestroy, OnInit { try { const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); - await this.deleteCipher(activeUserId); + await this.cipherService.archiveWithServer(this.cipher.id as CipherId, activeUserId); this.toastService.showToast({ variant: "success", title: null, - message: this.i18nService.t( - this.cipher.isDeleted ? "permanentlyDeletedItem" : "deletedItem", - ), + message: this.i18nService.t("archivedItem"), }); - this.onDeletedCipher.emit(this.cipher); + this.onArchivedCipher.emit(this.cipher); + } catch (e) { + this.logService.error(e); + } + + return true; + } + + async unarchive(): Promise { + if (!this.cipher.isArchived) { + return false; + } + + try { + const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + await this.cipherService.unarchiveWithServer(this.cipher.id as CipherId, activeUserId) + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("unarchivedItem"), + }); + this.onUnarchivedCipher.emit(this.cipher); } catch (e) { this.logService.error(e); } diff --git a/libs/angular/src/vault/vault-filter/models/vault-filter.model.ts b/libs/angular/src/vault/vault-filter/models/vault-filter.model.ts index 8f63c31d87a..94c9e983191 100644 --- a/libs/angular/src/vault/vault-filter/models/vault-filter.model.ts +++ b/libs/angular/src/vault/vault-filter/models/vault-filter.model.ts @@ -39,10 +39,16 @@ export class VaultFilter { buildFilter(): VaultFilterFunction { return (cipher) => { + console.log(cipher); + console.log(this.status); + let cipherPassesFilter = true; if (this.status === "favorites" && cipherPassesFilter) { cipherPassesFilter = cipher.favorite; } + if (this.status === "archive" && cipherPassesFilter) { + cipherPassesFilter = cipher.isArchived; + } if (this.status === "trash" && cipherPassesFilter) { cipherPassesFilter = cipher.isDeleted; }