From d0b09202c6684fd3b5482baf3abfa49a788908ce Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Wed, 25 Sep 2024 10:40:23 -0700 Subject: [PATCH 1/2] [PM-12504] - hide create send button and send tab when sends are disabled (#11186) * hide create send button and send tab when sends are disabled * reverse logic * tidy up filter. * fix popup tab navigation filter * fix popup tab nav state * fix popup-layout stories --- .../popup/layout/popup-layout.stories.ts | 26 +++++++ .../layout/popup-tab-navigation.component.ts | 77 ++++++++++++------- .../popup/send-v2/send-v2.component.html | 19 +++-- .../popup/send-v2/send-v2.component.spec.ts | 6 ++ .../tools/popup/send-v2/send-v2.component.ts | 15 +++- 5 files changed, 108 insertions(+), 35 deletions(-) diff --git a/apps/browser/src/platform/popup/layout/popup-layout.stories.ts b/apps/browser/src/platform/popup/layout/popup-layout.stories.ts index affa804cc79..4851541576f 100644 --- a/apps/browser/src/platform/popup/layout/popup-layout.stories.ts +++ b/apps/browser/src/platform/popup/layout/popup-layout.stories.ts @@ -3,7 +3,9 @@ import { Component, importProvidersFrom } from "@angular/core"; import { RouterModule } from "@angular/router"; import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/angular"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; import { AvatarModule, BadgeModule, @@ -318,6 +320,30 @@ export default { }); }, }, + { + provide: PolicyService, + useFactory: () => { + return { + policyAppliesToActiveUser$: () => { + return { + pipe: () => ({ + subscribe: () => ({}), + }), + }; + }, + }; + }, + }, + { + provide: SendService, + useFactory: () => { + return { + sends$: () => { + return { pipe: () => ({}) }; + }, + }; + }, + }, ], }), applicationConfig({ diff --git a/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.ts b/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.ts index ced3f6462e9..8463bbe6e9f 100644 --- a/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.ts +++ b/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.ts @@ -1,9 +1,41 @@ import { CommonModule } from "@angular/common"; import { Component } from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { RouterModule } from "@angular/router"; +import { filter, map, switchMap } from "rxjs"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; import { LinkModule } from "@bitwarden/components"; +const allNavButtons = [ + { + label: "Vault", + page: "/tabs/vault", + iconKey: "lock", + iconKeyActive: "lock-f", + }, + { + label: "Generator", + page: "/tabs/generator", + iconKey: "generate", + iconKeyActive: "generate-f", + }, + { + label: "Send", + page: "/tabs/send", + iconKey: "send", + iconKeyActive: "send-f", + }, + { + label: "Settings", + page: "/tabs/settings", + iconKey: "cog", + iconKeyActive: "cog-f", + }, +]; + @Component({ selector: "popup-tab-navigation", templateUrl: "popup-tab-navigation.component.html", @@ -14,30 +46,23 @@ import { LinkModule } from "@bitwarden/components"; }, }) export class PopupTabNavigationComponent { - navButtons = [ - { - label: "Vault", - page: "/tabs/vault", - iconKey: "lock", - iconKeyActive: "lock-f", - }, - { - label: "Generator", - page: "/tabs/generator", - iconKey: "generate", - iconKeyActive: "generate-f", - }, - { - label: "Send", - page: "/tabs/send", - iconKey: "send", - iconKeyActive: "send-f", - }, - { - label: "Settings", - page: "/tabs/settings", - iconKey: "cog", - iconKeyActive: "cog-f", - }, - ]; + navButtons = allNavButtons; + constructor( + private policyService: PolicyService, + private sendService: SendService, + ) { + this.policyService + .policyAppliesToActiveUser$(PolicyType.DisableSend) + .pipe( + filter((policyAppliesToActiveUser) => policyAppliesToActiveUser), + switchMap(() => this.sendService.sends$), + map((sends) => sends.length > 1), + takeUntilDestroyed(), + ) + .subscribe((hasSends) => { + this.navButtons = hasSends + ? allNavButtons + : allNavButtons.filter((b) => b.page !== "/tabs/send"); + }); + } } diff --git a/apps/browser/src/tools/popup/send-v2/send-v2.component.html b/apps/browser/src/tools/popup/send-v2/send-v2.component.html index a8dd3e24f29..698901d8460 100644 --- a/apps/browser/src/tools/popup/send-v2/send-v2.component.html +++ b/apps/browser/src/tools/popup/send-v2/send-v2.component.html @@ -1,12 +1,20 @@ - - + +
+ + {{ "sendDisabledWarning" | i18n }} + + + + + +
{{ "sendsNoItemsTitle" | i18n }} {{ "sendsNoItemsMessage" | i18n }} - +
@@ -31,9 +39,4 @@ - -
- - -
diff --git a/apps/browser/src/tools/popup/send-v2/send-v2.component.spec.ts b/apps/browser/src/tools/popup/send-v2/send-v2.component.spec.ts index 50e5531743a..63e3c2d2fc6 100644 --- a/apps/browser/src/tools/popup/send-v2/send-v2.component.spec.ts +++ b/apps/browser/src/tools/popup/send-v2/send-v2.component.spec.ts @@ -7,6 +7,7 @@ import { of, BehaviorSubject } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { SearchService } from "@bitwarden/common/abstractions/search.service"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service"; @@ -46,6 +47,7 @@ describe("SendV2Component", () => { let sendListFiltersServiceFilters$: BehaviorSubject<{ sendType: SendType | null }>; let sendItemsServiceEmptyList$: BehaviorSubject; let sendItemsServiceNoFilteredResults$: BehaviorSubject; + let policyService: MockProxy; beforeEach(async () => { sendListFiltersServiceFilters$ = new BehaviorSubject({ sendType: null }); @@ -60,6 +62,9 @@ describe("SendV2Component", () => { latestSearchText$: of(""), }); + policyService = mock(); + policyService.policyAppliesToActiveUser$.mockReturnValue(of(true)); // Return `true` by default + sendListFiltersService = new SendListFiltersService(mock(), new FormBuilder()); sendListFiltersService.filters$ = sendListFiltersServiceFilters$; @@ -104,6 +109,7 @@ describe("SendV2Component", () => { { provide: I18nService, useValue: { t: (key: string) => key } }, { provide: SendListFiltersService, useValue: sendListFiltersService }, { provide: PopupRouterCacheService, useValue: mock() }, + { provide: PolicyService, useValue: policyService }, ], }).compileComponents(); diff --git a/apps/browser/src/tools/popup/send-v2/send-v2.component.ts b/apps/browser/src/tools/popup/send-v2/send-v2.component.ts index 5c1ec89fde9..19fff402e2e 100644 --- a/apps/browser/src/tools/popup/send-v2/send-v2.component.ts +++ b/apps/browser/src/tools/popup/send-v2/send-v2.component.ts @@ -5,8 +5,10 @@ import { RouterLink } from "@angular/router"; import { combineLatest } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; -import { ButtonModule, Icons, NoItemsModule } from "@bitwarden/components"; +import { ButtonModule, CalloutModule, Icons, NoItemsModule } from "@bitwarden/components"; import { NoSendsIcon, NewSendDropdownComponent, @@ -31,6 +33,7 @@ export enum SendState { templateUrl: "send-v2.component.html", standalone: true, imports: [ + CalloutModule, PopupPageComponent, PopupHeaderComponent, PopOutComponent, @@ -61,9 +64,12 @@ export class SendV2Component implements OnInit, OnDestroy { protected noResultsIcon = Icons.NoResults; + protected sendsDisabled = false; + constructor( protected sendItemsService: SendItemsService, protected sendListFiltersService: SendListFiltersService, + private policyService: PolicyService, ) { combineLatest([ this.sendItemsService.emptyList$, @@ -90,6 +96,13 @@ export class SendV2Component implements OnInit, OnDestroy { this.listState = null; }); + + this.policyService + .policyAppliesToActiveUser$(PolicyType.DisableSend) + .pipe(takeUntilDestroyed()) + .subscribe((sendsDisabled) => { + this.sendsDisabled = sendsDisabled; + }); } ngOnInit(): void {} From 7f339543165b351d2c5e4de8857a9d3a39131309 Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Wed, 25 Sep 2024 10:56:04 -0700 Subject: [PATCH 2/2] don't display free bitwarden families button (#11184) --- .../about-page/more-from-bitwarden-page-v2.component.ts | 2 +- .../organization/organization.service.abstraction.ts | 4 ++++ .../services/organization/organization.service.ts | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page-v2.component.ts b/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page-v2.component.ts index 0f05480ea12..7cdb691d56c 100644 --- a/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page-v2.component.ts +++ b/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page-v2.component.ts @@ -38,7 +38,7 @@ export class MoreFromBitwardenPageV2Component { private organizationService: OrganizationService, ) { this.canAccessPremium$ = billingAccountProfileStateService.hasPremiumFromAnySource$; - this.familySponsorshipAvailable$ = this.organizationService.canManageSponsorships$; + this.familySponsorshipAvailable$ = this.organizationService.familySponsorshipAvailable$; } async openFreeBitwardenFamiliesPage() { diff --git a/libs/common/src/admin-console/abstractions/organization/organization.service.abstraction.ts b/libs/common/src/admin-console/abstractions/organization/organization.service.abstraction.ts index 0cea2aee539..a2ea6aa8861 100644 --- a/libs/common/src/admin-console/abstractions/organization/organization.service.abstraction.ts +++ b/libs/common/src/admin-console/abstractions/organization/organization.service.abstraction.ts @@ -117,6 +117,10 @@ export abstract class OrganizationService { * Emits true if the user can create or manage a Free Bitwarden Families sponsorship. */ canManageSponsorships$: Observable; + /** + * Emits true if any of the user's organizations have a Free Bitwarden Families sponsorship available. + */ + familySponsorshipAvailable$: Observable; hasOrganizations: () => Promise; get$: (id: string) => Observable; get: (id: string) => Promise; diff --git a/libs/common/src/admin-console/services/organization/organization.service.ts b/libs/common/src/admin-console/services/organization/organization.service.ts index d8fe18dc5cb..91bfcbd0d5d 100644 --- a/libs/common/src/admin-console/services/organization/organization.service.ts +++ b/libs/common/src/admin-console/services/organization/organization.service.ts @@ -88,6 +88,10 @@ export class OrganizationService implements InternalOrganizationServiceAbstracti mapToBooleanHasAnyOrganizations(), ); + familySponsorshipAvailable$ = this.organizations$.pipe( + map((orgs) => orgs.some((o) => o.familySponsorshipAvailable)), + ); + async hasOrganizations(): Promise { return await firstValueFrom(this.organizations$.pipe(mapToBooleanHasAnyOrganizations())); }