diff --git a/jslib b/jslib index ef897695e9..c0e7e588ed 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit ef897695e9b5bfecbfcbbd4ad3aec62b4ecdca25 +Subproject commit c0e7e588ed59832a6f579ff63d85bfcdfb400d78 diff --git a/src/app/app.module.ts b/src/app/app.module.ts index d476181231..50c2be5b0d 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -33,6 +33,7 @@ import { TwoFactorOptionsComponent } from './accounts/two-factor-options.compone import { TwoFactorComponent } from './accounts/two-factor.component'; import { AccountComponent } from './settings/account.component'; +import { AdjustStorageComponent } from './settings/adjust-storage.component'; import { ChangeEmailComponent } from './settings/change-email.component'; import { ChangePasswordComponent } from './settings/change-password.component'; import { DeauthorizeSessionsComponent } from './settings/deauthorize-sessions.component'; @@ -106,6 +107,7 @@ import { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe'; declarations: [ AccountComponent, AddEditComponent, + AdjustStorageComponent, ApiActionDirective, AppComponent, AttachmentsComponent, diff --git a/src/app/settings/adjust-storage.component.html b/src/app/settings/adjust-storage.component.html new file mode 100644 index 0000000000..02c9eb2ba7 --- /dev/null +++ b/src/app/settings/adjust-storage.component.html @@ -0,0 +1,25 @@ +
+
+
+
+ + +
+
+
+ {{'total' | i18n}}: {{storageAdjustment || 0}} GB × {{storageGbPrice | currency:'$'}} = {{adjustedStorageTotal + | currency:'$'}} /{{interval | i18n}} +
+ + + + {{(add ? 'storageAddNote' : 'storageRemoveNote') | i18n}} + +
+
diff --git a/src/app/settings/adjust-storage.component.ts b/src/app/settings/adjust-storage.component.ts new file mode 100644 index 0000000000..7ac6f6c337 --- /dev/null +++ b/src/app/settings/adjust-storage.component.ts @@ -0,0 +1,60 @@ +import { + Component, + EventEmitter, + Input, + Output, +} from '@angular/core'; + +import { ToasterService } from 'angular2-toaster'; +import { Angulartics2 } from 'angulartics2'; + +import { ApiService } from 'jslib/abstractions/api.service'; +import { I18nService } from 'jslib/abstractions/i18n.service'; + +import { StorageRequest } from 'jslib/models/request/storageRequest'; + +@Component({ + selector: 'app-adjust-storage', + templateUrl: 'adjust-storage.component.html', +}) +export class AdjustStorageComponent { + @Input() storageGbPrice = 0; + @Input() add = true; + @Input() user = true; + @Input() interval = 'year'; + @Output() onAdjusted = new EventEmitter(); + @Output() onCanceled = new EventEmitter(); + + storageAdjustment = 0; + formPromise: Promise; + + constructor(private apiService: ApiService, private i18nService: I18nService, + private analytics: Angulartics2, private toasterService: ToasterService) { } + + async submit() { + try { + const request = new StorageRequest(); + request.storageGbAdjustment = this.storageAdjustment; + if (!this.add) { + request.storageGbAdjustment *= -1; + } + + if (this.user) { + this.formPromise = this.apiService.postAccountStorage(request); + } + await this.formPromise; + this.analytics.eventTrack.next({ action: this.add ? 'Added Storage' : 'Removed Storage' }); + this.toasterService.popAsync('success', null, + this.i18nService.t('adjustedStorage', request.storageGbAdjustment.toString())); + this.onAdjusted.emit(this.storageAdjustment); + } catch { } + } + + cancel() { + this.onCanceled.emit(); + } + + get adjustedStorageTotal(): number { + return this.storageGbPrice * this.storageAdjustment; + } +} diff --git a/src/app/settings/user-billing.component.html b/src/app/settings/user-billing.component.html index 9b01a2d50e..789940cb56 100644 --- a/src/app/settings/user-billing.component.html +++ b/src/app/settings/user-billing.component.html @@ -69,12 +69,16 @@
- - + + + + +

{{'paymentMethod' | i18n}}

diff --git a/src/app/settings/user-billing.component.ts b/src/app/settings/user-billing.component.ts index 54b6773fb0..2e8d03a068 100644 --- a/src/app/settings/user-billing.component.ts +++ b/src/app/settings/user-billing.component.ts @@ -15,6 +15,8 @@ import { TokenService } from 'jslib/abstractions/token.service'; import { PaymentMethodType } from 'jslib/enums/paymentMethodType'; +import { AdjustStorageComponent } from './adjust-storage.component'; + @Component({ selector: 'app-user-billing', templateUrl: 'user-billing.component.html', @@ -23,6 +25,8 @@ export class UserBillingComponent implements OnInit { premium = false; loading = false; firstLoaded = false; + adjustStorageAdd = true; + showAdjustStorage = false; billing: BillingResponse; paymentMethodType = PaymentMethodType; @@ -101,7 +105,17 @@ export class UserBillingComponent implements OnInit { } adjustStorage(add: boolean) { + this.adjustStorageAdd = add; + this.showAdjustStorage = true; + } + adjustedStorage(gbAmount: number) { + this.showAdjustStorage = false; + this.load(); + } + + canceledAdjustStorage() { + this.showAdjustStorage = false; } changePayment() { diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index 344c73f7f2..936f77f584 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -1383,5 +1383,26 @@ "example": "BITWARDEN" } } + }, + "gbStorageAdd": { + "message": "GB of Storage To Add" + }, + "gbStorageRemove": { + "message": "GB of Storage To Remove" + }, + "storageAddNote": { + "message": "Adding storage to your plan will result in adjustments to your billing totals and immediately charge your payment method on file. The first charge will be prorated for the remainder of the current billing cycle." + }, + "storageRemoveNote": { + "message": "Removing storage will result in adjustments to your billing totals that will be prorated as credits to your next billing charge." + }, + "adjustedStorage": { + "message": "Adjusted $AMOUNT$ GB of storage.", + "placeholders": { + "amount": { + "content": "$1", + "example": "5" + } + } } }