From 3326877a67c7cb91e8c5ab8dd13c8899780bd257 Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Tue, 10 Jun 2025 18:03:17 -0400 Subject: [PATCH] [PM-21719] Remove Assign To Collections Modal When No Editable Collections (#15137) * remove assign to collections option when user does not have editable collections --- .../item-more-options.component.html | 6 +++- .../item-more-options.component.ts | 30 ++++++++++++------- .../vault/app/vault/add-edit.component.html | 8 ++++- .../vault-items/vault-items.component.ts | 5 +++- 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html index 6e6e30b359b..f9be1617d21 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html @@ -31,7 +31,11 @@ {{ "clone" | i18n }} - + {{ "assignToCollections" | i18n }} 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 165dd6d6d30..75bc984e977 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 @@ -1,11 +1,12 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { CommonModule } from "@angular/common"; -import { booleanAttribute, Component, Input, OnInit } from "@angular/core"; +import { booleanAttribute, Component, Input } from "@angular/core"; import { Router, RouterModule } from "@angular/router"; -import { BehaviorSubject, firstValueFrom, map, switchMap } from "rxjs"; +import { BehaviorSubject, combineLatest, firstValueFrom, map, switchMap } from "rxjs"; import { filter } from "rxjs/operators"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -32,7 +33,7 @@ import { AddEditQueryParams } from "../add-edit/add-edit-v2.component"; templateUrl: "./item-more-options.component.html", imports: [ItemModule, IconButtonModule, MenuModule, CommonModule, JslibModule, RouterModule], }) -export class ItemMoreOptionsComponent implements OnInit { +export class ItemMoreOptionsComponent { private _cipher$ = new BehaviorSubject(undefined); @Input({ @@ -71,8 +72,21 @@ export class ItemMoreOptionsComponent implements OnInit { switchMap((c) => this.cipherAuthorizationService.canCloneCipher$(c)), ); - /** Boolean dependent on the current user having access to an organization */ - protected hasOrganizations = false; + /** Observable Boolean dependent on the current user having access to an organization and editable collections */ + protected canAssignCollections$ = this.accountService.activeAccount$.pipe( + getUserId, + switchMap((userId) => { + return combineLatest([ + this.organizationService.hasOrganizations(userId), + this.collectionService.decryptedCollections$, + ]).pipe( + map(([hasOrgs, collections]) => { + const canEditCollections = collections.some((c) => !c.readOnly); + return hasOrgs && canEditCollections; + }), + ); + }), + ); constructor( private cipherService: CipherService, @@ -85,13 +99,9 @@ export class ItemMoreOptionsComponent implements OnInit { private accountService: AccountService, private organizationService: OrganizationService, private cipherAuthorizationService: CipherAuthorizationService, + private collectionService: CollectionService, ) {} - async ngOnInit(): Promise { - const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); - this.hasOrganizations = await firstValueFrom(this.organizationService.hasOrganizations(userId)); - } - get canEdit() { return this.cipher.edit; } diff --git a/apps/desktop/src/vault/app/vault/add-edit.component.html b/apps/desktop/src/vault/app/vault/add-edit.component.html index 8457e72bdc1..9c316813d1d 100644 --- a/apps/desktop/src/vault/app/vault/add-edit.component.html +++ b/apps/desktop/src/vault/app/vault/add-edit.component.html @@ -788,7 +788,13 @@ 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 0ca2ea86bf6..9679f0879b9 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 @@ -298,8 +298,11 @@ export class VaultItemsComponent { protected canAssignCollections(cipher: CipherView) { const organization = this.allOrganizations.find((o) => o.id === cipher.organizationId); + const editableCollections = this.allCollections.filter((c) => !c.readOnly); + return ( - (organization?.canEditAllCiphers && this.viewingOrgVault) || cipher.canAssignToCollections + (organization?.canEditAllCiphers && this.viewingOrgVault) || + (cipher.canAssignToCollections && editableCollections.length > 0) ); }