Show notifications for overdue subscriptions and exceeded limits

no issue

- Overdue subscriptions: when a subscription is in overdue state, we'd like to inform the customers so that the owner is aware and can take action
- Exceeded limits:
    - Show a warning when the members limit is exceeded so users are aware before trying to publish a post and hitting the limit
    - Allow to redirect directly to a child route in the Ghost(Pro) app, so plan updates get easier
This commit is contained in:
Aileen Nowak 2021-09-30 13:54:54 +01:00
parent dab672c7b8
commit 3ec373e33f
3 changed files with 48 additions and 2 deletions

View File

@ -1,4 +1,5 @@
import Component from '@ember/component';
import {htmlSafe} from '@ember/template';
import {inject as service} from '@ember/service';
export default Component.extend({
@ -6,6 +7,7 @@ export default Component.extend({
config: service(),
ghostPaths: service(),
ajax: service(),
notifications: service(),
didInsertElement() {
this._super(...arguments);
@ -47,6 +49,27 @@ export default Component.extend({
if (event && event.data && event.data.subscription) {
this.billing.set('subscription', event.data.subscription);
this.billing.set('checkoutRoute', event.data?.checkoutRoute || '/plans');
// Detect if the current subscription is in a grace state and render a notification
if (event.data.subscription.status === 'past_due' || event.data.subscription.status === 'unpaid') {
// This notification needs to be shown to every user regardless their permissions to see billing
this.notifications.showAlert('Billing error: This site is queued for suspension. The owner of this site must update payment information.', {type: 'error', key: 'billing.overdue'});
}
// Detect if the current member limits are exceeded and render a notification
if (
event.data?.exceededLimits
&& event.data?.exceededLimits.length
&& event.data?.exceededLimits.indexOf('members') >= 0
&& event.data?.checkoutRoute
) {
// The action param will be picked up on a transition from the router and can
// then send the destination route as a message to the BMA, which then handles the redirect.
const checkoutAction = this.billing.get('billingRouteRoot') + '?action=checkout';
this.notifications.showAlert(htmlSafe(`Your audience has grown! To continue publishing, the site owner must confirm pricing for this number of members <a href="${checkoutAction}">here</a>`), {type: 'warn', key: 'billing.exceeded'});
}
}
});
}

View File

@ -4,6 +4,7 @@ import {inject as service} from '@ember/service';
export default ModalComponent.extend({
router: service(),
billing: service(),
headerMessage: computed('details', function () {
let header = 'Upgrade to enable publishing';
@ -24,7 +25,7 @@ export default ModalComponent.extend({
actions: {
upgrade() {
this.router.transitionTo('pro');
this.router.transitionTo('pro', {queryParams: {action: 'checkout'}});
},
confirm() {

View File

@ -10,6 +10,7 @@ export default Service.extend({
billingWindowOpen: false,
subscription: null,
previousRoute: null,
action: null,
init() {
this._super(...arguments);
@ -51,15 +52,34 @@ export default Service.extend({
return url;
},
// Sends a route update to a child route in the BMA, because we can't control
// navigating to it otherwise
sendRouteUpdate() {
const action = this.get('action');
if (action) {
if (action === 'checkout') {
this.getBillingIframe().contentWindow.postMessage({
query: 'routeUpdate',
response: this.get('checkoutRoute')
}, '*');
}
this.set('action', null);
}
},
// Controls billing window modal visibility and sync of the URL visible in browser
// and the URL opened on the iframe. It is responsible to non user triggered iframe opening,
// for example: by entering "/pro" route in the URL or using history navigation (back and forward)
toggleProWindow(value) {
if (this.get('billingWindowOpen') && value) {
if (this.get('billingWindowOpen') && value && !this.get('action')) {
// don't attempt to open again
return;
}
this.sendRouteUpdate();
this.set('billingWindowOpen', value);
},
@ -79,6 +99,8 @@ export default Service.extend({
// in toggleProWindow
window.location.hash = childRoute || '/pro';
this.sendRouteUpdate();
this.router.transitionTo(childRoute || '/pro');
},