1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-10 13:23:34 +00:00

[PM-25633] - fix premium upgrade prompt (#16445)

* fix premium upgrade prompt

* use map instead of adding tap dep

* update route
This commit is contained in:
Jordan Aasen
2025-09-17 09:30:39 -07:00
committed by GitHub
parent 814305a778
commit caf4ca6980
3 changed files with 36 additions and 13 deletions

View File

@@ -2,6 +2,7 @@
// @ts-strict-ignore // @ts-strict-ignore
import { CommonModule } from "@angular/common"; import { CommonModule } from "@angular/common";
import { Component, ElementRef, Inject, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { Component, ElementRef, Inject, OnDestroy, OnInit, ViewChild } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { firstValueFrom, Subject, switchMap } from "rxjs"; import { firstValueFrom, Subject, switchMap } from "rxjs";
import { map } from "rxjs/operators"; import { map } from "rxjs/operators";
@@ -277,6 +278,8 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy {
protected attachmentsButtonDisabled = false; protected attachmentsButtonDisabled = false;
protected confirmedPremiumUpgrade = false;
constructor( constructor(
@Inject(DIALOG_DATA) protected params: VaultItemDialogParams, @Inject(DIALOG_DATA) protected params: VaultItemDialogParams,
private dialogRef: DialogRef<VaultItemDialogResult>, private dialogRef: DialogRef<VaultItemDialogResult>,
@@ -296,6 +299,12 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy {
private routedVaultFilterService: RoutedVaultFilterService, private routedVaultFilterService: RoutedVaultFilterService,
) { ) {
this.updateTitle(); this.updateTitle();
this.premiumUpgradeService.upgradeConfirmed$
.pipe(
map((c) => c && (this.confirmedPremiumUpgrade = true)),
takeUntilDestroyed(),
)
.subscribe();
} }
async ngOnInit() { async ngOnInit() {
@@ -339,6 +348,10 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy {
} }
ngOnDestroy() { ngOnDestroy() {
// If the user already confirmed a premium upgrade, don't emit any other result as it will overwrite the premium upgrade result.
if (this.confirmedPremiumUpgrade) {
return;
}
// If the cipher was modified, be sure we emit the saved result in case the dialog was closed with the X button or ESC key. // If the cipher was modified, be sure we emit the saved result in case the dialog was closed with the X button or ESC key.
if (this._cipherModified) { if (this._cipherModified) {
this.dialogRef.close(VaultItemDialogResult.Saved); this.dialogRef.close(VaultItemDialogResult.Saved);

View File

@@ -1,5 +1,6 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { Subject } from "rxjs";
import { OrganizationId } from "@bitwarden/common/types/guid"; import { OrganizationId } from "@bitwarden/common/types/guid";
import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service";
@@ -7,11 +8,11 @@ import { DialogRef, DialogService } from "@bitwarden/components";
import { VaultItemDialogResult } from "../components/vault-item-dialog/vault-item-dialog.component"; import { VaultItemDialogResult } from "../components/vault-item-dialog/vault-item-dialog.component";
/**
* This service is used to prompt the user to upgrade to premium.
*/
@Injectable() @Injectable()
export class WebVaultPremiumUpgradePromptService implements PremiumUpgradePromptService { export class WebVaultPremiumUpgradePromptService implements PremiumUpgradePromptService {
private readonly _upgradeConfirmed$ = new Subject<boolean>();
readonly upgradeConfirmed$ = this._upgradeConfirmed$.asObservable();
constructor( constructor(
private dialogService: DialogService, private dialogService: DialogService,
private router: Router, private router: Router,
@@ -19,34 +20,40 @@ export class WebVaultPremiumUpgradePromptService implements PremiumUpgradePrompt
) {} ) {}
/** /**
* Prompts the user to upgrade to premium. * Prompts the user for a premium upgrade.
* @param organizationId The ID of the organization to upgrade.
*/ */
async promptForPremium(organizationId?: OrganizationId) { async promptForPremium(organizationId?: OrganizationId) {
let upgradeConfirmed; let confirmed = false;
let route: string[] | null = null;
if (organizationId) { if (organizationId) {
upgradeConfirmed = await this.dialogService.openSimpleDialog({ confirmed = await this.dialogService.openSimpleDialog({
title: { key: "upgradeOrganization" }, title: { key: "upgradeOrganization" },
content: { key: "upgradeOrganizationDesc" }, content: { key: "upgradeOrganizationDesc" },
acceptButtonText: { key: "upgradeOrganization" }, acceptButtonText: { key: "upgradeOrganization" },
type: "info", type: "info",
}); });
if (upgradeConfirmed) { if (confirmed) {
await this.router.navigate(["organizations", organizationId, "billing", "subscription"]); route = ["organizations", organizationId, "billing", "subscription"];
} }
} else { } else {
upgradeConfirmed = await this.dialogService.openSimpleDialog({ confirmed = await this.dialogService.openSimpleDialog({
title: { key: "premiumRequired" }, title: { key: "premiumRequired" },
content: { key: "premiumRequiredDesc" }, content: { key: "premiumRequiredDesc" },
acceptButtonText: { key: "upgrade" }, acceptButtonText: { key: "upgrade" },
type: "success", type: "success",
}); });
if (upgradeConfirmed) { if (confirmed) {
await this.router.navigate(["settings/subscription/premium"]); route = ["settings/subscription/premium"];
} }
} }
if (upgradeConfirmed) { this._upgradeConfirmed$.next(confirmed);
if (route) {
await this.router.navigate(route);
}
if (confirmed) {
this.dialog.close(VaultItemDialogResult.PremiumUpgrade); this.dialog.close(VaultItemDialogResult.PremiumUpgrade);
} }
} }

View File

@@ -1,7 +1,10 @@
import { Observable } from "rxjs";
/** /**
* This interface defines the a contract for a service that prompts the user to upgrade to premium. * This interface defines the a contract for a service that prompts the user to upgrade to premium.
* It ensures that PremiumUpgradePromptService contains a promptForPremium method. * It ensures that PremiumUpgradePromptService contains a promptForPremium method.
*/ */
export abstract class PremiumUpgradePromptService { export abstract class PremiumUpgradePromptService {
abstract promptForPremium(organizationId?: string): Promise<void>; abstract promptForPremium(organizationId?: string): Promise<void>;
abstract upgradeConfirmed$?: Observable<boolean>;
} }