import { Component, OnInit, ViewChild } from "@angular/core"; import { Router } from "@angular/router"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { StateService } from "@bitwarden/common/abstractions/state.service"; import { SyncService } from "@bitwarden/common/abstractions/sync.service"; import { TokenService } from "@bitwarden/common/abstractions/token.service"; import { PaymentComponent } from "./payment.component"; import { TaxInfoComponent } from "./tax-info.component"; @Component({ selector: "app-premium", templateUrl: "premium.component.html", }) export class PremiumComponent implements OnInit { @ViewChild(PaymentComponent) paymentComponent: PaymentComponent; @ViewChild(TaxInfoComponent) taxInfoComponent: TaxInfoComponent; canAccessPremium = false; selfHosted = false; premiumPrice = 10; storageGbPrice = 4; additionalStorage = 0; formPromise: Promise; constructor( private apiService: ApiService, private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, private tokenService: TokenService, private router: Router, private messagingService: MessagingService, private syncService: SyncService, private logService: LogService, private stateService: StateService ) { this.selfHosted = platformUtilsService.isSelfHost(); } async ngOnInit() { this.canAccessPremium = await this.stateService.getCanAccessPremium(); const premiumPersonally = await this.stateService.getHasPremiumPersonally(); if (premiumPersonally) { this.router.navigate(["/settings/subscription/user-subscription"]); return; } } async submit() { let files: FileList = null; if (this.selfHosted) { const fileEl = document.getElementById("file") as HTMLInputElement; files = fileEl.files; if (files == null || files.length === 0) { this.platformUtilsService.showToast( "error", this.i18nService.t("errorOccurred"), this.i18nService.t("selectFile") ); return; } } try { if (this.selfHosted) { if (!this.tokenService.getEmailVerified()) { this.platformUtilsService.showToast( "error", this.i18nService.t("errorOccurred"), this.i18nService.t("verifyEmailFirst") ); return; } const fd = new FormData(); fd.append("license", files[0]); this.formPromise = this.apiService.postAccountLicense(fd).then(() => { return this.finalizePremium(); }); } else { this.formPromise = this.paymentComponent .createPaymentToken() .then((result) => { const fd = new FormData(); fd.append("paymentMethodType", result[1].toString()); if (result[0] != null) { fd.append("paymentToken", result[0]); } fd.append("additionalStorageGb", (this.additionalStorage || 0).toString()); fd.append("country", this.taxInfoComponent.taxInfo.country); fd.append("postalCode", this.taxInfoComponent.taxInfo.postalCode); return this.apiService.postPremium(fd); }) .then((paymentResponse) => { if (!paymentResponse.success && paymentResponse.paymentIntentClientSecret != null) { return this.paymentComponent.handleStripeCardPayment( paymentResponse.paymentIntentClientSecret, () => this.finalizePremium() ); } else { return this.finalizePremium(); } }); } await this.formPromise; } catch (e) { this.logService.error(e); } } async finalizePremium() { await this.apiService.refreshIdentityToken(); await this.syncService.fullSync(true); this.platformUtilsService.showToast("success", null, this.i18nService.t("premiumUpdated")); this.messagingService.send("purchasedPremium"); this.router.navigate(["/settings/subscription/user-subscription"]); } get additionalStorageTotal(): number { return this.storageGbPrice * Math.abs(this.additionalStorage || 0); } get subtotal(): number { return this.premiumPrice + this.additionalStorageTotal; } get taxCharges(): number { return this.taxInfoComponent != null && this.taxInfoComponent.taxRate != null ? (this.taxInfoComponent.taxRate / 100) * this.subtotal : 0; } get total(): number { return this.subtotal + this.taxCharges || 0; } }