From c4c275604bcb51bbd4296ec14ea212421b689e10 Mon Sep 17 00:00:00 2001 From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Date: Thu, 21 Mar 2024 15:04:29 -0400 Subject: [PATCH] Remove FF 'AC-1607_present-user-offboarding-survey' and old cancel functionality (#8322) --- .../user-subscription.component.html | 2 +- .../individual/user-subscription.component.ts | 80 ++++--------------- ...nization-subscription-cloud.component.html | 23 +----- ...ganization-subscription-cloud.component.ts | 42 +--------- libs/common/src/abstractions/api.service.ts | 1 - .../organization-api.service.abstraction.ts | 1 - .../organization/organization-api.service.ts | 4 - .../billing/services/billing-api.service.ts | 4 +- libs/common/src/enums/feature-flag.enum.ts | 1 - libs/common/src/services/api.service.ts | 4 - 10 files changed, 22 insertions(+), 140 deletions(-) diff --git a/apps/web/src/app/billing/individual/user-subscription.component.html b/apps/web/src/app/billing/individual/user-subscription.component.html index 2fd831c71c..874983df84 100644 --- a/apps/web/src/app/billing/individual/user-subscription.component.html +++ b/apps/web/src/app/billing/individual/user-subscription.component.html @@ -145,7 +145,7 @@ type="button" buttonType="danger" class="btn-submit tw-ml-auto" - (click)="cancel()" + (click)="cancelSubscription()" [appApiAction]="cancelPromise" [disabled]="$any(cancelBtn).loading" *ngIf="subscription && !subscription.cancelled && !subscriptionMarkedForCancel" diff --git a/apps/web/src/app/billing/individual/user-subscription.component.ts b/apps/web/src/app/billing/individual/user-subscription.component.ts index 76e542c338..7d8c3a0f18 100644 --- a/apps/web/src/app/billing/individual/user-subscription.component.ts +++ b/apps/web/src/app/billing/individual/user-subscription.component.ts @@ -1,12 +1,10 @@ import { Component, OnInit } from "@angular/core"; import { Router } from "@angular/router"; -import { firstValueFrom, lastValueFrom, Observable } from "rxjs"; +import { firstValueFrom, lastValueFrom } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { SubscriptionResponse } from "@bitwarden/common/billing/models/response/subscription.response"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigServiceAbstraction as ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -34,7 +32,6 @@ export class UserSubscriptionComponent implements OnInit { cancelPromise: Promise; reinstatePromise: Promise; - presentUserWithOffboardingSurvey$: Observable; constructor( private apiService: ApiService, @@ -45,7 +42,6 @@ export class UserSubscriptionComponent implements OnInit { private fileDownloadService: FileDownloadService, private dialogService: DialogService, private environmentService: EnvironmentService, - private configService: ConfigService, private billingAccountProfileStateService: BillingAccountProfileStateService, ) { this.selfHosted = platformUtilsService.isSelfHost(); @@ -53,9 +49,6 @@ export class UserSubscriptionComponent implements OnInit { async ngOnInit() { this.cloudWebVaultUrl = await firstValueFrom(this.environmentService.cloudWebVaultUrl$); - this.presentUserWithOffboardingSurvey$ = this.configService.getFeatureFlag$( - FeatureFlag.AC1607_PresentUserOffboardingSurvey, - ); await this.load(); this.firstLoaded = true; } @@ -105,16 +98,22 @@ export class UserSubscriptionComponent implements OnInit { } } - cancel = async () => { - const presentUserWithOffboardingSurvey = await this.configService.getFeatureFlag( - FeatureFlag.AC1607_PresentUserOffboardingSurvey, - ); + cancelSubscription = async () => { + const reference = openOffboardingSurvey(this.dialogService, { + data: { + type: "User", + }, + }); - if (presentUserWithOffboardingSurvey) { - await this.cancelWithOffboardingSurvey(); - } else { - await this.cancelWithWarning(); + this.cancelPromise = lastValueFrom(reference.closed); + + const result = await this.cancelPromise; + + if (result === OffboardingSurveyDialogResultType.Closed) { + return; } + + await this.load(); }; downloadLicense() { @@ -159,55 +158,6 @@ export class UserSubscriptionComponent implements OnInit { } } - private cancelWithOffboardingSurvey = async () => { - const reference = openOffboardingSurvey(this.dialogService, { - data: { - type: "User", - }, - }); - - this.cancelPromise = lastValueFrom(reference.closed); - - const result = await this.cancelPromise; - - if (result === OffboardingSurveyDialogResultType.Closed) { - return; - } - - await this.load(); - }; - - private async cancelWithWarning() { - if (this.loading) { - return; - } - - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "cancelSubscription" }, - content: { key: "cancelConfirmation" }, - type: "warning", - }); - - if (!confirmed) { - return; - } - - try { - this.cancelPromise = this.apiService.postCancelPremium(); - await this.cancelPromise; - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("canceledSubscription"), - ); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.load(); - } catch (e) { - this.logService.error(e); - } - } - get subscriptionMarkedForCancel() { return ( this.subscription != null && !this.subscription.cancelled && this.subscription.cancelAtEndDate diff --git a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html index 484be34ce6..7f53fba1c0 100644 --- a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html +++ b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html @@ -232,28 +232,9 @@ - diff --git a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts index 24374ee896..2256a92756 100644 --- a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts +++ b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts @@ -1,6 +1,6 @@ import { Component, OnDestroy, OnInit } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; -import { concatMap, firstValueFrom, lastValueFrom, Observable, Subject, takeUntil } from "rxjs"; +import { concatMap, firstValueFrom, lastValueFrom, Subject, takeUntil } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; @@ -11,8 +11,6 @@ import { PlanType } from "@bitwarden/common/billing/enums"; import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response"; import { BillingSubscriptionItemResponse } from "@bitwarden/common/billing/models/response/subscription.response"; import { ProductType } from "@bitwarden/common/enums"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigServiceAbstraction as ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -43,7 +41,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy showSecretsManagerSubscribe = false; firstLoaded = false; loading: boolean; - presentUserWithOffboardingSurvey$: Observable; protected readonly teamsStarter = ProductType.TeamsStarter; @@ -58,7 +55,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy private organizationApiService: OrganizationApiServiceAbstraction, private route: ActivatedRoute, private dialogService: DialogService, - private configService: ConfigService, ) {} async ngOnInit() { @@ -78,10 +74,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy takeUntil(this.destroy$), ) .subscribe(); - - this.presentUserWithOffboardingSurvey$ = this.configService.getFeatureFlag$( - FeatureFlag.AC1607_PresentUserOffboardingSurvey, - ); } ngOnDestroy() { @@ -278,7 +270,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy ); } - cancelWithOffboardingSurvey = async () => { + cancelSubscription = async () => { const reference = openOffboardingSurvey(this.dialogService, { data: { type: "Organization", @@ -295,36 +287,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy await this.load(); }; - cancelWithWarning = async () => { - if (this.loading) { - return; - } - - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "cancelSubscription" }, - content: { key: "cancelConfirmation" }, - type: "warning", - }); - - if (!confirmed) { - return; - } - - try { - await this.organizationApiService.cancel(this.organizationId); - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("canceledSubscription"), - ); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.load(); - } catch (e) { - this.logService.error(e); - } - }; - reinstate = async () => { if (this.loading) { return; diff --git a/libs/common/src/abstractions/api.service.ts b/libs/common/src/abstractions/api.service.ts index 2ef6b327d3..20ed3216a5 100644 --- a/libs/common/src/abstractions/api.service.ts +++ b/libs/common/src/abstractions/api.service.ts @@ -170,7 +170,6 @@ export abstract class ApiService { postRegister: (request: RegisterRequest) => Promise; postPremium: (data: FormData) => Promise; postReinstatePremium: () => Promise; - postCancelPremium: () => Promise; postAccountStorage: (request: StorageRequest) => Promise; postAccountPayment: (request: PaymentRequest) => Promise; postAccountLicense: (data: FormData) => Promise; diff --git a/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts b/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts index ddf2fc5c01..7f1a40d140 100644 --- a/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts +++ b/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts @@ -51,7 +51,6 @@ export class OrganizationApiServiceAbstraction { updateSeats: (id: string, request: SeatRequest) => Promise; updateStorage: (id: string, request: StorageRequest) => Promise; verifyBank: (id: string, request: VerifyBankRequest) => Promise; - cancel: (id: string) => Promise; reinstate: (id: string) => Promise; leave: (id: string) => Promise; delete: (id: string, request: SecretVerificationRequest) => Promise; diff --git a/libs/common/src/admin-console/services/organization/organization-api.service.ts b/libs/common/src/admin-console/services/organization/organization-api.service.ts index 4e482112a8..883bf35260 100644 --- a/libs/common/src/admin-console/services/organization/organization-api.service.ts +++ b/libs/common/src/admin-console/services/organization/organization-api.service.ts @@ -184,10 +184,6 @@ export class OrganizationApiService implements OrganizationApiServiceAbstraction ); } - async cancel(id: string): Promise { - return this.apiService.send("POST", "/organizations/" + id + "/cancel", null, true, false); - } - async reinstate(id: string): Promise { return this.apiService.send("POST", "/organizations/" + id + "/reinstate", null, true, false); } diff --git a/libs/common/src/billing/services/billing-api.service.ts b/libs/common/src/billing/services/billing-api.service.ts index 677e500619..3d0ff550ea 100644 --- a/libs/common/src/billing/services/billing-api.service.ts +++ b/libs/common/src/billing/services/billing-api.service.ts @@ -12,7 +12,7 @@ export class BillingApiService implements BillingApiServiceAbstraction { ): Promise { return this.apiService.send( "POST", - "/organizations/" + organizationId + "/churn", + "/organizations/" + organizationId + "/cancel", request, true, false, @@ -20,7 +20,7 @@ export class BillingApiService implements BillingApiServiceAbstraction { } cancelPremiumUserSubscription(request: SubscriptionCancellationRequest): Promise { - return this.apiService.send("POST", "/accounts/churn-premium", request, true, false); + return this.apiService.send("POST", "/accounts/cancel", request, true, false); } async getBillingStatus(id: string): Promise { diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 8ca80759b2..8a5075e96f 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -7,7 +7,6 @@ export enum FeatureFlag { GeneratorToolsModernization = "generator-tools-modernization", KeyRotationImprovements = "key-rotation-improvements", FlexibleCollectionsMigration = "flexible-collections-migration", - AC1607_PresentUserOffboardingSurvey = "AC-1607_present-user-offboarding-survey", ShowPaymentMethodWarningBanners = "show-payment-method-warning-banners", } diff --git a/libs/common/src/services/api.service.ts b/libs/common/src/services/api.service.ts index 2ed80cc342..b6c2ab5c22 100644 --- a/libs/common/src/services/api.service.ts +++ b/libs/common/src/services/api.service.ts @@ -394,10 +394,6 @@ export class ApiService implements ApiServiceAbstraction { return this.send("POST", "/accounts/reinstate-premium", null, true, false); } - postCancelPremium(): Promise { - return this.send("POST", "/accounts/cancel-premium", null, true, false); - } - async postAccountStorage(request: StorageRequest): Promise { const r = await this.send("POST", "/accounts/storage", request, true, true); return new PaymentResponse(r);