mirror of
https://github.com/bitwarden/browser
synced 2026-01-04 17:43:39 +00:00
[PM-23513] Complete usage of OrganizationWarningsService (#16272)
* Use OrganizationWarningsService in AC VaultComponent * Use OrganizationWarningsService in OrgSwitcherComponent * Use OrganizationWarningsService in VaultFilterComponent * Use OrganizationWarningsService in VaultComponent * Use OrganizationWarningsService in SM OverviewComponent * Remove TrialFlowService from unused codepaths * Remove TrialFlowService * Refresh free trial warning on standard payment method update * Fix lint errors * Fix lint errors * Remove FF * Fix free trial banner on deprecated ac vault component
This commit is contained in:
@@ -1,45 +1,12 @@
|
||||
<app-organization-free-trial-warning
|
||||
*ngIf="useOrganizationWarningsService$ | async"
|
||||
[organization]="organization"
|
||||
(clicked)="navigateToPaymentMethod()"
|
||||
>
|
||||
</app-organization-free-trial-warning>
|
||||
<app-organization-reseller-renewal-warning
|
||||
*ngIf="useOrganizationWarningsService$ | async"
|
||||
[organization]="organization"
|
||||
>
|
||||
</app-organization-reseller-renewal-warning>
|
||||
<ng-container *ngIf="freeTrialWhenWarningsServiceDisabled$ | async as freeTrial">
|
||||
<bit-banner
|
||||
id="free-trial-banner"
|
||||
icon="bwi-billing"
|
||||
bannerType="premium"
|
||||
[showClose]="false"
|
||||
*ngIf="!refreshing && freeTrial.shownBanner"
|
||||
@if (organization) {
|
||||
<app-organization-free-trial-warning
|
||||
[organization]="organization"
|
||||
(clicked)="navigateToPaymentMethod()"
|
||||
>
|
||||
{{ freeTrial.message }}
|
||||
<a
|
||||
bitLink
|
||||
linkType="secondary"
|
||||
(click)="navigateToPaymentMethod()"
|
||||
class="tw-cursor-pointer"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
{{ "clickHereToAddPaymentMethod" | i18n }}
|
||||
</a>
|
||||
</bit-banner>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="resellerWarningWhenWarningsServiceDisabled$ | async as resellerWarning">
|
||||
<bit-banner
|
||||
id="reseller-warning-banner"
|
||||
icon="bwi-billing"
|
||||
bannerType="info"
|
||||
[showClose]="false"
|
||||
*ngIf="!refreshing"
|
||||
>
|
||||
{{ resellerWarning?.message }}
|
||||
</bit-banner>
|
||||
</ng-container>
|
||||
</app-organization-free-trial-warning>
|
||||
<app-organization-reseller-renewal-warning [organization]="organization">
|
||||
</app-organization-reseller-renewal-warning>
|
||||
}
|
||||
|
||||
<app-org-vault-header
|
||||
[filter]="filter"
|
||||
|
||||
@@ -6,14 +6,12 @@ import {
|
||||
BehaviorSubject,
|
||||
combineLatest,
|
||||
firstValueFrom,
|
||||
from,
|
||||
lastValueFrom,
|
||||
merge,
|
||||
Observable,
|
||||
of,
|
||||
Subject,
|
||||
} from "rxjs";
|
||||
import {
|
||||
catchError,
|
||||
concatMap,
|
||||
debounceTime,
|
||||
distinctUntilChanged,
|
||||
@@ -37,12 +35,10 @@ import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe";
|
||||
import { NoResults } from "@bitwarden/assets/svg";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
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";
|
||||
import { OrganizationBillingServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
||||
import { EventType } from "@bitwarden/common/enums";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
@@ -86,13 +82,6 @@ import {
|
||||
import { OrganizationWarningsService } from "@bitwarden/web-vault/app/billing/organizations/warnings/services";
|
||||
import { VaultItemsComponent } from "@bitwarden/web-vault/app/vault/components/vault-items/vault-items.component";
|
||||
|
||||
import { BillingNotificationService } from "../../../billing/services/billing-notification.service";
|
||||
import {
|
||||
ResellerWarning,
|
||||
ResellerWarningService,
|
||||
} from "../../../billing/services/reseller-warning.service";
|
||||
import { TrialFlowService } from "../../../billing/services/trial-flow.service";
|
||||
import { FreeTrial } from "../../../billing/types/free-trial";
|
||||
import { SharedModule } from "../../../shared";
|
||||
import { AssignCollectionsWebComponent } from "../../../vault/components/assign-collections";
|
||||
import {
|
||||
@@ -183,11 +172,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
protected selectedCollection: TreeNode<CollectionAdminView> | undefined;
|
||||
protected isEmpty: boolean;
|
||||
protected showCollectionAccessRestricted: boolean;
|
||||
private hasSubscription$ = new BehaviorSubject<boolean>(false);
|
||||
protected currentSearchText$: Observable<string>;
|
||||
protected useOrganizationWarningsService$: Observable<boolean>;
|
||||
protected freeTrialWhenWarningsServiceDisabled$: Observable<FreeTrial>;
|
||||
protected resellerWarningWhenWarningsServiceDisabled$: Observable<ResellerWarning | null>;
|
||||
protected prevCipherId: string | null = null;
|
||||
protected userId: UserId;
|
||||
/**
|
||||
@@ -209,31 +194,6 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
|
||||
@ViewChild("vaultItems", { static: false }) vaultItemsComponent: VaultItemsComponent<CipherView>;
|
||||
|
||||
private readonly unpaidSubscriptionDialog$ = this.accountService.activeAccount$.pipe(
|
||||
map((account) => account?.id),
|
||||
switchMap((id) =>
|
||||
this.organizationService.organizations$(id).pipe(
|
||||
filter((organizations) => organizations.length === 1),
|
||||
map(([organization]) => organization),
|
||||
switchMap((organization) =>
|
||||
from(this.billingApiService.getOrganizationBillingMetadata(organization.id)).pipe(
|
||||
tap((organizationMetaData) => {
|
||||
this.hasSubscription$.next(organizationMetaData.hasSubscription);
|
||||
}),
|
||||
switchMap((organizationMetaData) =>
|
||||
from(
|
||||
this.trialFlowService.handleUnpaidSubscriptionDialog(
|
||||
organization,
|
||||
organizationMetaData,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private organizationService: OrganizationService,
|
||||
@@ -262,13 +222,8 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
private toastService: ToastService,
|
||||
private configService: ConfigService,
|
||||
private cipherFormConfigService: CipherFormConfigService,
|
||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||
private trialFlowService: TrialFlowService,
|
||||
protected billingApiService: BillingApiServiceAbstraction,
|
||||
private organizationBillingService: OrganizationBillingServiceAbstraction,
|
||||
private resellerWarningService: ResellerWarningService,
|
||||
private accountService: AccountService,
|
||||
private billingNotificationService: BillingNotificationService,
|
||||
private organizationWarningsService: OrganizationWarningsService,
|
||||
private collectionService: CollectionService,
|
||||
) {}
|
||||
@@ -661,74 +616,17 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
.subscribe();
|
||||
|
||||
// Billing Warnings
|
||||
this.useOrganizationWarningsService$ = this.configService.getFeatureFlag$(
|
||||
FeatureFlag.UseOrganizationWarningsService,
|
||||
);
|
||||
|
||||
this.useOrganizationWarningsService$
|
||||
.pipe(
|
||||
switchMap((enabled) =>
|
||||
enabled
|
||||
? this.organizationWarningsService.showInactiveSubscriptionDialog$(this.organization)
|
||||
: this.unpaidSubscriptionDialog$,
|
||||
),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
organization$
|
||||
.pipe(
|
||||
switchMap((organization) =>
|
||||
this.organizationWarningsService.showSubscribeBeforeFreeTrialEndsDialog$(organization),
|
||||
merge(
|
||||
this.organizationWarningsService.showInactiveSubscriptionDialog$(organization),
|
||||
this.organizationWarningsService.showSubscribeBeforeFreeTrialEndsDialog$(organization),
|
||||
),
|
||||
),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
const freeTrial$ = combineLatest([
|
||||
organization$,
|
||||
this.hasSubscription$.pipe(filter((hasSubscription) => hasSubscription !== null)),
|
||||
]).pipe(
|
||||
filter(
|
||||
([org, hasSubscription]) => org.isOwner && hasSubscription && org.canViewBillingHistory,
|
||||
),
|
||||
switchMap(([org]) =>
|
||||
combineLatest([
|
||||
of(org),
|
||||
this.organizationApiService.getSubscription(org.id),
|
||||
from(this.organizationBillingService.getPaymentSource(org.id)).pipe(
|
||||
catchError((error: unknown) => {
|
||||
this.billingNotificationService.handleError(error);
|
||||
return of(null);
|
||||
}),
|
||||
),
|
||||
]),
|
||||
),
|
||||
map(([org, sub, paymentSource]) =>
|
||||
this.trialFlowService.checkForOrgsWithUpcomingPaymentIssues(org, sub, paymentSource),
|
||||
),
|
||||
filter((result) => result !== null),
|
||||
);
|
||||
|
||||
this.freeTrialWhenWarningsServiceDisabled$ = this.useOrganizationWarningsService$.pipe(
|
||||
filter((enabled) => !enabled),
|
||||
switchMap(() => freeTrial$),
|
||||
);
|
||||
|
||||
const resellerWarning$ = organization$.pipe(
|
||||
filter((org) => org.isOwner),
|
||||
switchMap((org) =>
|
||||
from(this.billingApiService.getOrganizationBillingMetadata(org.id)).pipe(
|
||||
map((metadata) => ({ org, metadata })),
|
||||
),
|
||||
),
|
||||
map(({ org, metadata }) => this.resellerWarningService.getWarning(org, metadata)),
|
||||
);
|
||||
|
||||
this.resellerWarningWhenWarningsServiceDisabled$ = this.useOrganizationWarningsService$.pipe(
|
||||
filter((enabled) => !enabled),
|
||||
switchMap(() => resellerWarning$),
|
||||
);
|
||||
// End Billing Warnings
|
||||
|
||||
firstSetup$
|
||||
|
||||
@@ -5,47 +5,16 @@
|
||||
@let loading = loading$ | async;
|
||||
|
||||
@if (organization) {
|
||||
@if (useOrganizationWarningsService$ | async) {
|
||||
@if (!refreshing) {
|
||||
<app-organization-free-trial-warning
|
||||
[organization]="organization"
|
||||
(clicked)="navigateToPaymentMethod()"
|
||||
>
|
||||
</app-organization-free-trial-warning>
|
||||
}
|
||||
|
||||
@if (useOrganizationWarningsService$ | async) {
|
||||
<app-organization-reseller-renewal-warning [organization]="organization">
|
||||
</app-organization-reseller-renewal-warning>
|
||||
}
|
||||
|
||||
@let freeTrial = freeTrialWhenWarningsServiceDisabled$ | async;
|
||||
@if (!refreshing && freeTrial?.shownBanner) {
|
||||
<bit-banner id="free-trial-banner" icon="bwi-billing" bannerType="premium" [showClose]="false">
|
||||
{{ freeTrial.message }}
|
||||
<a
|
||||
bitLink
|
||||
linkType="secondary"
|
||||
(click)="navigateToPaymentMethod()"
|
||||
class="tw-cursor-pointer"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
{{ "clickHereToAddPaymentMethod" | i18n }}
|
||||
</a>
|
||||
</bit-banner>
|
||||
}
|
||||
|
||||
@let resellerWarning = resellerWarningWhenWarningsServiceDisabled$ | async;
|
||||
@if (!refreshing && resellerWarning) {
|
||||
<bit-banner
|
||||
id="reseller-warning-banner"
|
||||
icon="bwi-billing"
|
||||
bannerType="info"
|
||||
[showClose]="false"
|
||||
>
|
||||
{{ resellerWarning?.message }}
|
||||
</bit-banner>
|
||||
}
|
||||
|
||||
@if (filter) {
|
||||
<app-org-vault-header
|
||||
[filter]="filter"
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
BehaviorSubject,
|
||||
combineLatest,
|
||||
firstValueFrom,
|
||||
from,
|
||||
lastValueFrom,
|
||||
merge,
|
||||
Observable,
|
||||
of,
|
||||
Subject,
|
||||
@@ -25,7 +25,6 @@ import {
|
||||
switchMap,
|
||||
take,
|
||||
takeUntil,
|
||||
tap,
|
||||
} from "rxjs/operators";
|
||||
|
||||
import {
|
||||
@@ -39,12 +38,10 @@ import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe";
|
||||
import { NoResults } from "@bitwarden/assets/svg";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
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";
|
||||
import { OrganizationBillingServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
||||
import { EventType } from "@bitwarden/common/enums";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
@@ -94,13 +91,6 @@ import {
|
||||
import { OrganizationWarningsService } from "@bitwarden/web-vault/app/billing/organizations/warnings/services";
|
||||
import { VaultItemsComponent } from "@bitwarden/web-vault/app/vault/components/vault-items/vault-items.component";
|
||||
|
||||
import { BillingNotificationService } from "../../../billing/services/billing-notification.service";
|
||||
import {
|
||||
ResellerWarning,
|
||||
ResellerWarningService,
|
||||
} from "../../../billing/services/reseller-warning.service";
|
||||
import { TrialFlowService } from "../../../billing/services/trial-flow.service";
|
||||
import { FreeTrial } from "../../../billing/types/free-trial";
|
||||
import { SharedModule } from "../../../shared";
|
||||
import { AssignCollectionsWebComponent } from "../../../vault/components/assign-collections";
|
||||
import {
|
||||
@@ -194,10 +184,6 @@ export class vNextVaultComponent implements OnInit, OnDestroy {
|
||||
protected showCollectionAccessRestricted$: Observable<boolean>;
|
||||
|
||||
protected isEmpty$: Observable<boolean> = of(false);
|
||||
private hasSubscription$ = new BehaviorSubject<boolean>(false);
|
||||
protected useOrganizationWarningsService$: Observable<boolean>;
|
||||
protected freeTrialWhenWarningsServiceDisabled$: Observable<FreeTrial>;
|
||||
protected resellerWarningWhenWarningsServiceDisabled$: Observable<ResellerWarning | null>;
|
||||
protected prevCipherId: string | null = null;
|
||||
protected userId$: Observable<UserId>;
|
||||
|
||||
@@ -227,31 +213,6 @@ export class vNextVaultComponent implements OnInit, OnDestroy {
|
||||
| VaultItemsComponent<CipherView>
|
||||
| undefined;
|
||||
|
||||
private readonly unpaidSubscriptionDialog$ = this.accountService.activeAccount$.pipe(
|
||||
getUserId,
|
||||
switchMap((id) =>
|
||||
this.organizationService.organizations$(id).pipe(
|
||||
filter((organizations) => organizations.length === 1),
|
||||
map(([organization]) => organization),
|
||||
switchMap((organization) =>
|
||||
from(this.billingApiService.getOrganizationBillingMetadata(organization.id)).pipe(
|
||||
tap((organizationMetaData) => {
|
||||
this.hasSubscription$.next(organizationMetaData.hasSubscription);
|
||||
}),
|
||||
switchMap((organizationMetaData) =>
|
||||
from(
|
||||
this.trialFlowService.handleUnpaidSubscriptionDialog(
|
||||
organization,
|
||||
organizationMetaData,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private organizationService: OrganizationService,
|
||||
@@ -280,13 +241,8 @@ export class vNextVaultComponent implements OnInit, OnDestroy {
|
||||
private toastService: ToastService,
|
||||
private configService: ConfigService,
|
||||
private cipherFormConfigService: CipherFormConfigService,
|
||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||
private trialFlowService: TrialFlowService,
|
||||
protected billingApiService: BillingApiServiceAbstraction,
|
||||
private organizationBillingService: OrganizationBillingServiceAbstraction,
|
||||
private resellerWarningService: ResellerWarningService,
|
||||
private accountService: AccountService,
|
||||
private billingNotificationService: BillingNotificationService,
|
||||
private organizationWarningsService: OrganizationWarningsService,
|
||||
private collectionService: CollectionService,
|
||||
private restrictedItemTypesService: RestrictedItemTypesService,
|
||||
@@ -461,68 +417,17 @@ export class vNextVaultComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
|
||||
// Billing Warnings
|
||||
this.useOrganizationWarningsService$ = this.configService.getFeatureFlag$(
|
||||
FeatureFlag.UseOrganizationWarningsService,
|
||||
);
|
||||
|
||||
const freeTrial$ = combineLatest([
|
||||
this.organization$,
|
||||
this.hasSubscription$.pipe(filter((hasSubscription) => hasSubscription !== null)),
|
||||
]).pipe(
|
||||
filter(
|
||||
([org, hasSubscription]) => org.isOwner && hasSubscription && org.canViewBillingHistory,
|
||||
),
|
||||
switchMap(([org]) =>
|
||||
combineLatest([
|
||||
of(org),
|
||||
this.organizationApiService.getSubscription(org.id),
|
||||
from(this.organizationBillingService.getPaymentSource(org.id)).pipe(
|
||||
map((paymentSource) => {
|
||||
if (paymentSource == null) {
|
||||
throw new Error("Payment source not found.");
|
||||
}
|
||||
return paymentSource;
|
||||
}),
|
||||
),
|
||||
]),
|
||||
),
|
||||
map(([org, sub, paymentSource]) =>
|
||||
this.trialFlowService.checkForOrgsWithUpcomingPaymentIssues(org, sub, paymentSource),
|
||||
),
|
||||
filter((result) => result !== null),
|
||||
catchError((error: unknown) => {
|
||||
this.billingNotificationService.handleError(error);
|
||||
return of();
|
||||
}),
|
||||
);
|
||||
|
||||
this.freeTrialWhenWarningsServiceDisabled$ = this.useOrganizationWarningsService$.pipe(
|
||||
filter((enabled) => !enabled),
|
||||
switchMap(() => freeTrial$),
|
||||
);
|
||||
|
||||
this.resellerWarningWhenWarningsServiceDisabled$ = combineLatest([
|
||||
this.organization$,
|
||||
this.useOrganizationWarningsService$,
|
||||
]).pipe(
|
||||
filter(([org, enabled]) => !enabled && org.isOwner),
|
||||
switchMap(([org]) =>
|
||||
from(this.billingApiService.getOrganizationBillingMetadata(org.id)).pipe(
|
||||
map((metadata) => ({ org, metadata })),
|
||||
),
|
||||
),
|
||||
map(({ org, metadata }) => this.resellerWarningService.getWarning(org, metadata)),
|
||||
);
|
||||
|
||||
this.organization$
|
||||
.pipe(
|
||||
switchMap((organization) =>
|
||||
this.organizationWarningsService.showSubscribeBeforeFreeTrialEndsDialog$(organization),
|
||||
merge(
|
||||
this.organizationWarningsService.showInactiveSubscriptionDialog$(organization),
|
||||
this.organizationWarningsService.showSubscribeBeforeFreeTrialEndsDialog$(organization),
|
||||
),
|
||||
),
|
||||
takeUntilDestroyed(),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
// End Billing Warnings
|
||||
|
||||
this.editableCollections$ = combineLatest([
|
||||
@@ -781,17 +686,6 @@ export class vNextVaultComponent implements OnInit, OnDestroy {
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
combineLatest([this.useOrganizationWarningsService$, this.organization$])
|
||||
.pipe(
|
||||
switchMap(([enabled, organization]) =>
|
||||
enabled
|
||||
? this.organizationWarningsService.showInactiveSubscriptionDialog$(organization)
|
||||
: this.unpaidSubscriptionDialog$,
|
||||
),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
// Handle last of initial setup - workaround for some state issues where we need to manually
|
||||
// push the collections we've loaded back into the VaultFilterService.
|
||||
// FIXME: figure out how we can remove this.
|
||||
|
||||
Reference in New Issue
Block a user