mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
* feat(billing): add required messages * feat(billing): Add upgrade from free account dialog * feat(billing): Add payment dialog for premium upgrade * feat(billing): Add Upgrade Payment Service * feat(billing): Add Upgrade flow service * feat(billing): Add purchase premium subscription method to client * fix(billing): allow for nullable taxId for families organizations * fix(billing): Fix Cart Summary Tests * temp-fix(billing): add currency pipe to pricing card component * fix(billing): Fix NX error This should compile just the library files and not its dependency files which was making it error * fix: Update any type of private function * update account dialog * feat(billing): add upgrade error message * fix(billing): remove upgrade-flow service * feat(billing): add account billing client * fix(billing): Remove method from subscriber-billing client * fix(billing): rename and update upgrade payment component * fix(billing): Rename and update upgrade payment service * fix(billing): Rename and upgrade upgrade account component * fix(billing): Add unified upgrade dialog component * fix(billing): Update component and service to use new tax service * fix(billing): Update unified upgrade dialog * feat(billing): Add feature flag * feat(billing): Add vault dialog launch logic * fix(billing): Add stricter validation for payment component * fix(billing): Update custom dialog close button * fix(billing): Fix padding in cart summary component * fix(billing): Update payment method component spacing * fix(billing): Update Upgrade Payment component spacing * fix(billing): Update upgrade account component spacing * fix(billing): Fix accurate typing * feat(billing): adds unified upgrade prompt service * fix(billing): Update unified dialog to account for skipped steps * fix(billing): Use upgradePromptService for vault * fix(billing): Format * fix(billing): Fix premium check
115 lines
4.0 KiB
TypeScript
115 lines
4.0 KiB
TypeScript
import { Injectable } from "@angular/core";
|
|
import { combineLatest, firstValueFrom } from "rxjs";
|
|
import { switchMap, take } from "rxjs/operators";
|
|
|
|
import { VaultProfileService } from "@bitwarden/angular/vault/services/vault-profile.service";
|
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
|
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
|
import { DialogRef, DialogService } from "@bitwarden/components";
|
|
|
|
import {
|
|
UnifiedUpgradeDialogComponent,
|
|
UnifiedUpgradeDialogResult,
|
|
} from "../unified-upgrade-dialog/unified-upgrade-dialog.component";
|
|
|
|
@Injectable({
|
|
providedIn: "root",
|
|
})
|
|
export class UnifiedUpgradePromptService {
|
|
private unifiedUpgradeDialogRef: DialogRef<UnifiedUpgradeDialogResult> | null = null;
|
|
constructor(
|
|
private accountService: AccountService,
|
|
private configService: ConfigService,
|
|
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
|
private vaultProfileService: VaultProfileService,
|
|
private dialogService: DialogService,
|
|
) {}
|
|
|
|
private shouldShowPrompt$ = combineLatest([
|
|
this.accountService.activeAccount$,
|
|
this.configService.getFeatureFlag$(FeatureFlag.PM24996_ImplementUpgradeFromFreeDialog),
|
|
]).pipe(
|
|
switchMap(async ([account, isFlagEnabled]) => {
|
|
if (!account || !account?.id) {
|
|
return false;
|
|
}
|
|
// Early return if feature flag is disabled
|
|
if (!isFlagEnabled) {
|
|
return false;
|
|
}
|
|
|
|
// Check if user has premium
|
|
const hasPremium = await firstValueFrom(
|
|
this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id),
|
|
);
|
|
|
|
// Early return if user already has premium
|
|
if (hasPremium) {
|
|
return false;
|
|
}
|
|
|
|
// Check profile age only if needed
|
|
const isProfileLessThanFiveMinutesOld = await this.isProfileLessThanFiveMinutesOld(
|
|
account.id,
|
|
);
|
|
|
|
return isFlagEnabled && !hasPremium && isProfileLessThanFiveMinutesOld;
|
|
}),
|
|
take(1),
|
|
);
|
|
|
|
/**
|
|
* Conditionally prompt the user based on predefined criteria.
|
|
*
|
|
* @returns A promise that resolves to the dialog result if shown, or null if not shown
|
|
*/
|
|
async displayUpgradePromptConditionally(): Promise<UnifiedUpgradeDialogResult | null> {
|
|
const shouldShow = await firstValueFrom(this.shouldShowPrompt$);
|
|
|
|
if (shouldShow) {
|
|
return this.launchUpgradeDialog();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Checks if a user's profile was created less than five minutes ago
|
|
* @param userId User ID to check
|
|
* @returns Promise that resolves to true if profile was created less than five minutes ago
|
|
*/
|
|
private async isProfileLessThanFiveMinutesOld(userId: string): Promise<boolean> {
|
|
const createdAtDate = await this.vaultProfileService.getProfileCreationDate(userId);
|
|
if (!createdAtDate) {
|
|
return false;
|
|
}
|
|
const createdAtInMs = createdAtDate.getTime();
|
|
const nowInMs = new Date().getTime();
|
|
|
|
const differenceInMs = nowInMs - createdAtInMs;
|
|
const msInAMinute = 1000 * 60; // Milliseconds in a minute for conversion 1 minute = 60 seconds * 1000 ms
|
|
const differenceInMinutes = Math.round(differenceInMs / msInAMinute);
|
|
|
|
return differenceInMinutes <= 5;
|
|
}
|
|
|
|
private async launchUpgradeDialog(): Promise<UnifiedUpgradeDialogResult | null> {
|
|
const account = await firstValueFrom(this.accountService.activeAccount$);
|
|
if (!account) {
|
|
return null;
|
|
}
|
|
|
|
this.unifiedUpgradeDialogRef = UnifiedUpgradeDialogComponent.open(this.dialogService, {
|
|
data: { account },
|
|
});
|
|
|
|
const result = await firstValueFrom(this.unifiedUpgradeDialogRef.closed);
|
|
this.unifiedUpgradeDialogRef = null;
|
|
|
|
// Return the result or null if the dialog was dismissed without a result
|
|
return result || null;
|
|
}
|
|
}
|