mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-17 23:44:39 -05:00
Cleaned up notification flows
no issue - Adds success and error notification messages for different actions - Cleans up notification flows and messages - Adds new helpers for members and site - Updates actions for email/name update
This commit is contained in:
parent
160e5e6e7d
commit
041de9f045
10 changed files with 336 additions and 184 deletions
|
@ -8,6 +8,8 @@ import {getActivePage, isAccountPage} from './pages';
|
|||
import * as Fixtures from './utils/fixtures';
|
||||
import ActionHandler from './actions';
|
||||
import './App.css';
|
||||
import NotificationParser from './utils/notifications';
|
||||
import {createPopupNotification} from './utils/helpers';
|
||||
const React = require('react');
|
||||
|
||||
const DEV_MODE_DATA = {
|
||||
|
@ -94,12 +96,13 @@ export default class App extends React.Component {
|
|||
async initSetup() {
|
||||
try {
|
||||
// Fetch data from API, links, preview, dev sources
|
||||
const {site, member, page, showPopup} = await this.fetchData();
|
||||
const {site, member, page, showPopup, popupNotification} = await this.fetchData();
|
||||
this.setState({
|
||||
site,
|
||||
member,
|
||||
page,
|
||||
showPopup,
|
||||
popupNotification,
|
||||
action: 'init:success',
|
||||
initStatus: 'success'
|
||||
});
|
||||
|
@ -126,14 +129,15 @@ export default class App extends React.Component {
|
|||
const {site: devSiteData, ...restDevData} = this.fetchDevData();
|
||||
const {site: linkSiteData, ...restLinkData} = this.fetchLinkData();
|
||||
const {site: previewSiteData, ...restPreviewData} = this.fetchPreviewData();
|
||||
const {site: notificationSiteData, ...restNotificationData} = this.fetchNotificationData();
|
||||
|
||||
const stripeParam = this.getStripeUrlParam();
|
||||
// const stripeParam = this.getStripeUrlParam();
|
||||
let page = '';
|
||||
|
||||
/** Set page for magic link popup on stripe success*/
|
||||
if (!member && stripeParam === 'success') {
|
||||
page = 'magiclink';
|
||||
}
|
||||
// /** Set page for magic link popup on stripe success*/
|
||||
// if (!member && stripeParam === 'success') {
|
||||
// page = 'magiclink';
|
||||
// }
|
||||
|
||||
return {
|
||||
member,
|
||||
|
@ -142,10 +146,12 @@ export default class App extends React.Component {
|
|||
...apiSiteData,
|
||||
...linkSiteData,
|
||||
...previewSiteData,
|
||||
...notificationSiteData,
|
||||
...devSiteData
|
||||
},
|
||||
...restDevData,
|
||||
...restLinkData,
|
||||
...restNotificationData,
|
||||
...restPreviewData
|
||||
};
|
||||
}
|
||||
|
@ -199,6 +205,18 @@ export default class App extends React.Component {
|
|||
return data;
|
||||
}
|
||||
|
||||
fetchNotificationData() {
|
||||
const {type, status, duration, autoHide, closeable} = NotificationParser({billingOnly: true}) || {};
|
||||
if (['stripe:billing-update'].includes(type)) {
|
||||
const popupNotification = createPopupNotification({type, status, duration, closeable, autoHide, state: this.state});
|
||||
return {
|
||||
showPopup: true,
|
||||
popupNotification
|
||||
};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/** Fetch state from Portal Links */
|
||||
fetchLinkData() {
|
||||
const [path] = window.location.hash.substr(1).split('?');
|
||||
|
@ -256,9 +274,17 @@ export default class App extends React.Component {
|
|||
action: ''
|
||||
});
|
||||
}, 2000);
|
||||
} catch (e) {
|
||||
} catch (error) {
|
||||
const popupNotification = createPopupNotification({
|
||||
type: `${action}:failed`,
|
||||
autoHide: true, closeable: true, status: 'error', state: this.state,
|
||||
meta: {
|
||||
error
|
||||
}
|
||||
});
|
||||
this.setState({
|
||||
action: `${action}:failed`
|
||||
action: `${action}:failed`,
|
||||
popupNotification
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,9 @@
|
|||
function createPopupNotification({type, status, autoHide, closeable, state}) {
|
||||
let count = 0;
|
||||
if (state.popupNotification) {
|
||||
count = (state.popupNotification.count || 0) + 1;
|
||||
}
|
||||
return {
|
||||
type,
|
||||
status,
|
||||
autoHide,
|
||||
closeable,
|
||||
count
|
||||
};
|
||||
}
|
||||
import {createPopupNotification, getMemberEmail, getMemberName} from './utils/helpers';
|
||||
|
||||
function switchPage({data}) {
|
||||
return {
|
||||
page: data.page,
|
||||
popupNotification: null,
|
||||
lastPage: data.lastPage || null
|
||||
};
|
||||
}
|
||||
|
@ -64,14 +53,24 @@ function closeNotification({state}) {
|
|||
};
|
||||
}
|
||||
|
||||
async function signout({api}) {
|
||||
await api.member.signout();
|
||||
return {
|
||||
action: 'signout:success'
|
||||
};
|
||||
async function signout({api, state}) {
|
||||
try {
|
||||
await api.member.signout();
|
||||
return {
|
||||
action: 'signout:success'
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
action: 'signout:failed',
|
||||
popupNotification: createPopupNotification({
|
||||
type: 'signout:failed', autoHide: true, closeable: true, state, status: 'error',
|
||||
message: 'Failed to logout, please try again.'
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function signin({data, api}) {
|
||||
async function signin({data, api, state}) {
|
||||
try {
|
||||
await api.member.sendMagicLink(data);
|
||||
return {
|
||||
|
@ -79,20 +78,16 @@ async function signin({data, api}) {
|
|||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
action: 'signin:failed',
|
||||
popupNotification: createPopupNotification({
|
||||
type: 'signin:failed',
|
||||
autoHide: false,
|
||||
closeable: true,
|
||||
status: 'error',
|
||||
meta: {
|
||||
reason: e.message
|
||||
}
|
||||
type: 'signin:failed', autoHide: true, closeable: true, state, status: 'error',
|
||||
message: 'Failed to login, please try again.'
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function signup({data, api}) {
|
||||
async function signup({data, state, api}) {
|
||||
try {
|
||||
const {plan, email, name} = data;
|
||||
if (plan.toLowerCase() === 'free') {
|
||||
|
@ -105,82 +100,95 @@ async function signup({data, api}) {
|
|||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
action: 'signup:failed',
|
||||
popupNotification: createPopupNotification({
|
||||
type: 'signup:failed',
|
||||
autoHide: false,
|
||||
closeable: true,
|
||||
status: 'error',
|
||||
meta: {
|
||||
reason: e.message
|
||||
}
|
||||
type: 'signup:failed', autoHide: true, closeable: true, state, status: 'error',
|
||||
message: 'Failed to signup, please try again.'
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function updateEmail({data, api}) {
|
||||
async function checkoutPlan({data, state, api}) {
|
||||
try {
|
||||
await api.member.sendMagicLink(data);
|
||||
return {
|
||||
action: 'updateEmail:success'
|
||||
};
|
||||
const {plan} = data;
|
||||
await api.member.checkoutPlan({
|
||||
plan
|
||||
});
|
||||
} catch (e) {
|
||||
return {
|
||||
action: 'checkoutPlan:failed',
|
||||
popupNotification: createPopupNotification({
|
||||
type: 'updateEmail:failed',
|
||||
autoHide: false,
|
||||
closeable: true,
|
||||
status: 'error',
|
||||
meta: {
|
||||
reason: e.message
|
||||
}
|
||||
type: 'checkoutPlan:failed', autoHide: true, closeable: true, state, status: 'error',
|
||||
message: 'Failed to checkout plan, please try again.'
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function checkoutPlan({data, api}) {
|
||||
const {plan} = data;
|
||||
await api.member.checkoutPlan({
|
||||
plan
|
||||
});
|
||||
}
|
||||
|
||||
async function updateSubscription({data, state, api}) {
|
||||
const {plan, subscriptionId, cancelAtPeriodEnd} = data;
|
||||
await api.member.updateSubscription({
|
||||
planName: plan, subscriptionId, cancelAtPeriodEnd
|
||||
});
|
||||
const member = await api.member.sessionData();
|
||||
const action = 'updateSubscription:success';
|
||||
return {
|
||||
action,
|
||||
popupNotification: createPopupNotification({
|
||||
type: action,
|
||||
autoHide: true,
|
||||
closeable: true,
|
||||
state
|
||||
}),
|
||||
page: 'accountHome',
|
||||
member: member
|
||||
};
|
||||
try {
|
||||
const {plan, subscriptionId, cancelAtPeriodEnd} = data;
|
||||
await api.member.updateSubscription({
|
||||
planName: plan, subscriptionId, cancelAtPeriodEnd
|
||||
});
|
||||
const member = await api.member.sessionData();
|
||||
const action = 'updateSubscription:success';
|
||||
return {
|
||||
action,
|
||||
popupNotification: createPopupNotification({
|
||||
type: action, autoHide: true, closeable: true, state, status: 'success',
|
||||
message: 'Subscription plan successfully updated'
|
||||
}),
|
||||
page: 'accountHome',
|
||||
member: member
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
action: 'updateSubscription:failed',
|
||||
popupNotification: createPopupNotification({
|
||||
type: 'updateSubscription:failed', autoHide: true, closeable: true, state, status: 'error',
|
||||
message: 'Failed to update subscription.'
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function cancelSubscription({data, api}) {
|
||||
const {subscriptionId, cancelAtPeriodEnd} = data;
|
||||
await api.member.updateSubscription({
|
||||
subscriptionId, cancelAtPeriodEnd
|
||||
});
|
||||
const member = await api.member.sessionData();
|
||||
return {
|
||||
action: 'cancelSubscription:success',
|
||||
page: 'accountHome',
|
||||
member: member
|
||||
};
|
||||
async function cancelSubscription({data, state, api}) {
|
||||
try {
|
||||
const {subscriptionId, cancelAtPeriodEnd} = data;
|
||||
await api.member.updateSubscription({
|
||||
subscriptionId, cancelAtPeriodEnd
|
||||
});
|
||||
const member = await api.member.sessionData();
|
||||
return {
|
||||
action: 'cancelSubscription:success',
|
||||
page: 'accountHome',
|
||||
member: member
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
action: 'cancelSubscription:failed',
|
||||
popupNotification: createPopupNotification({
|
||||
type: 'cancelSubscription:failed', autoHide: true, closeable: true, state, status: 'error',
|
||||
message: 'Failed to cancel subscription.'
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function editBilling({data, api}) {
|
||||
await api.member.editBilling();
|
||||
async function editBilling({data, state, api}) {
|
||||
try {
|
||||
await api.member.editBilling();
|
||||
} catch (e) {
|
||||
return {
|
||||
action: 'editBilling:failed',
|
||||
popupNotification: createPopupNotification({
|
||||
type: 'editBilling:failed', autoHide: true, closeable: true, state, status: 'error',
|
||||
message: 'Failed to update billing information.'
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function clearPopupNotification() {
|
||||
|
@ -190,57 +198,128 @@ async function clearPopupNotification() {
|
|||
}
|
||||
|
||||
async function updateNewsletter({data, state, api}) {
|
||||
const {subscribed} = data;
|
||||
const member = await api.member.update({subscribed});
|
||||
if (!member) {
|
||||
return {
|
||||
action: 'updateNewsletter:failed'
|
||||
};
|
||||
} else {
|
||||
try {
|
||||
const {subscribed} = data;
|
||||
const member = await api.member.update({subscribed});
|
||||
if (!member) {
|
||||
throw new Error('Failed to update newsletter');
|
||||
}
|
||||
const action = 'updateNewsletter:success';
|
||||
return {
|
||||
action,
|
||||
member: member,
|
||||
popupNotification: createPopupNotification({
|
||||
type: action,
|
||||
autoHide: true,
|
||||
closeable: true,
|
||||
state
|
||||
type: action, autoHide: true, closeable: true, state, status: 'success',
|
||||
message: 'Newsletter settings updated'
|
||||
})
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
action: 'updateNewsletter:failed',
|
||||
popupNotification: createPopupNotification({
|
||||
type: 'updateNewsletter:failed', autoHide: true, closeable: true, state, status: 'error',
|
||||
message: 'Failed to update newsletter settings'
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function updateMemberEmail({data, state, api}) {
|
||||
const {email} = data;
|
||||
const originalEmail = getMemberEmail({member: state.member});
|
||||
if (email !== originalEmail) {
|
||||
try {
|
||||
await api.member.sendMagicLink({email, oldEmail: originalEmail, emailType: 'updateEmail'});
|
||||
return {
|
||||
success: true
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
success: false,
|
||||
error: err
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function updateMemberData({data, state, api}) {
|
||||
const {name} = data;
|
||||
const originalName = getMemberName({member: state.member});
|
||||
if (originalName !== name) {
|
||||
try {
|
||||
const member = await api.member.update({name});
|
||||
if (!member) {
|
||||
throw new Error('Failed to update member');
|
||||
}
|
||||
return {
|
||||
member,
|
||||
success: true
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
success: false,
|
||||
error: err
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function updateProfile({data, state, api}) {
|
||||
const {name, subscribed} = data;
|
||||
const member = await api.member.update({name, subscribed});
|
||||
if (!member) {
|
||||
const action = 'updateProfile:failed';
|
||||
const [dataUpdate, emailUpdate] = await Promise.all([updateMemberData({data, state, api}), updateMemberEmail({data, state, api})]);
|
||||
if (dataUpdate && emailUpdate) {
|
||||
if (emailUpdate.success) {
|
||||
return {
|
||||
action: 'updateProfile:success',
|
||||
...(dataUpdate.success ? {member: dataUpdate.member} : {}),
|
||||
page: 'accountHome',
|
||||
popupNotification: createPopupNotification({
|
||||
type: 'updateProfile:success', autoHide: true, closeable: true, status: 'success', state,
|
||||
message: 'Check your inbox to verify email update'
|
||||
})
|
||||
};
|
||||
}
|
||||
const message = !dataUpdate.success ? 'Failed to update account data' : 'Failed to send verification email';
|
||||
|
||||
return {
|
||||
action,
|
||||
action: 'updateProfile:failed',
|
||||
...(dataUpdate.success ? {member: dataUpdate.member} : {}),
|
||||
popupNotification: createPopupNotification({
|
||||
type: action,
|
||||
autoHide: true,
|
||||
closeable: true,
|
||||
status: 'error',
|
||||
state
|
||||
type: 'updateProfile:failed', autoHide: true, closeable: true, status: 'error', message, state
|
||||
})
|
||||
};
|
||||
} else {
|
||||
const action = 'updateProfile:success';
|
||||
} else if (dataUpdate) {
|
||||
const action = dataUpdate.success ? 'updateProfile:success' : 'updateProfile:failed';
|
||||
const status = dataUpdate.success ? 'success' : 'error';
|
||||
const message = !dataUpdate.success ? 'Failed to update account data' : 'Account data successfully updated';
|
||||
return {
|
||||
action,
|
||||
member: member,
|
||||
...(dataUpdate.success ? {member: dataUpdate.member} : {}),
|
||||
page: 'accountHome',
|
||||
popupNotification: createPopupNotification({
|
||||
type: action,
|
||||
autoHide: true,
|
||||
closeable: true,
|
||||
status: 'success',
|
||||
state
|
||||
type: action, autoHide: true, closeable: true, status, state, message
|
||||
})
|
||||
};
|
||||
} else if (emailUpdate) {
|
||||
const action = emailUpdate.success ? 'updateProfile:success' : 'updateProfile:failed';
|
||||
const status = emailUpdate.success ? 'success' : 'error';
|
||||
const message = !emailUpdate.success ? 'Failed to send verification email' : 'Check your inbox to verify email update';
|
||||
return {
|
||||
action,
|
||||
popupNotification: createPopupNotification({
|
||||
type: action, autoHide: true, closeable: true, status, state, message
|
||||
})
|
||||
};
|
||||
}
|
||||
return {
|
||||
action: 'updateProfile:success',
|
||||
page: 'accountHome',
|
||||
popupNotification: createPopupNotification({
|
||||
type: 'updateProfile:success', autoHide: true, closeable: true, status: 'success', state,
|
||||
message: 'Account data successfully updated'
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
const Actions = {
|
||||
|
@ -254,7 +333,6 @@ const Actions = {
|
|||
signout,
|
||||
signin,
|
||||
signup,
|
||||
updateEmail,
|
||||
updateSubscription,
|
||||
cancelSubscription,
|
||||
updateNewsletter,
|
||||
|
|
|
@ -3,7 +3,7 @@ import AppContext from '../AppContext';
|
|||
import NotificationStyle from './Notification.styles';
|
||||
import {ReactComponent as CloseIcon} from '../images/icons/close.svg';
|
||||
import NotificationParser, {clearURLParams} from '../utils/notifications';
|
||||
import { getPortalLink } from '../utils/helpers';
|
||||
import {getPortalLink} from '../utils/helpers';
|
||||
|
||||
const React = require('react');
|
||||
|
||||
|
@ -52,15 +52,16 @@ const NotificationText = ({type, status, context}) => {
|
|||
</p>
|
||||
);
|
||||
} else if (type === 'stripe:checkout' && status === 'success') {
|
||||
if (context.member) {
|
||||
return (
|
||||
<p>
|
||||
Success! Your account is fully activated, you now have access to all content.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<p>
|
||||
Success! Your account is fully activated, you now have access to all content.
|
||||
</p>
|
||||
);
|
||||
} else if (type === 'stripe:billing-update' && status === 'success') {
|
||||
return (
|
||||
<p>
|
||||
You've successfully updated your billing information
|
||||
Success! Check your email for magic link to sign-in.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
@ -151,16 +152,14 @@ export default class Notification extends React.Component {
|
|||
}
|
||||
|
||||
onHideNotification() {
|
||||
const qs = window.location.search || '';
|
||||
const qsParams = new URLSearchParams(qs);
|
||||
const type = this.state.type;
|
||||
const deleteParams = [];
|
||||
if (['signin', 'signout'].includes(type)) {
|
||||
if (['signin', 'signup'].includes(type)) {
|
||||
deleteParams.push('action', 'success');
|
||||
} else if (['stripe:checkout', 'stripe:billing-update'].includes(type)) {
|
||||
} else if (['stripe:checkout'].includes(type)) {
|
||||
deleteParams.push('stripe');
|
||||
}
|
||||
clearURLParams(qsParams, deleteParams);
|
||||
clearURLParams(deleteParams);
|
||||
this.setState({
|
||||
active: false
|
||||
});
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import React from 'react';
|
||||
import AppContext from '../../AppContext';
|
||||
import {ReactComponent as CloseIcon} from '../../images/icons/close.svg';
|
||||
import {getSupportAddress} from '../../utils/helpers';
|
||||
import {clearURLParams} from '../../utils/notifications';
|
||||
|
||||
export const PopupNotificationStyles = `
|
||||
.gh-portal-popupnotification {
|
||||
|
@ -59,6 +61,14 @@ export const PopupNotificationStyles = `
|
|||
background: var(--red);
|
||||
}
|
||||
|
||||
.gh-portal-popupnotification.warning {
|
||||
background: var(--yellow);
|
||||
}
|
||||
|
||||
.gh-portal-popupnotification.warning p, .gh-portal-popupnotification.warning .closeicon {
|
||||
color: var(--grey1);
|
||||
}
|
||||
|
||||
@keyframes popupnotification-slidein {
|
||||
0% { transform: translateY(-100px); }
|
||||
60% { transform: translateY(8px); }
|
||||
|
@ -77,31 +87,20 @@ const CloseButton = ({hide = false, onClose}) => {
|
|||
return null;
|
||||
}
|
||||
return (
|
||||
<CloseIcon className='closeicon' alt='Close' onClick={onClose}/>
|
||||
<CloseIcon className='closeicon' alt='Close' onClick={onClose} />
|
||||
);
|
||||
};
|
||||
|
||||
const NotificationText = ({type, status}) => {
|
||||
if (type === 'updateNewsletter:success') {
|
||||
const NotificationText = ({message, site}) => {
|
||||
const supportAddress = getSupportAddress({site});
|
||||
const supportAddressMail = `mailto:${supportAddress}`;
|
||||
if (message) {
|
||||
return (
|
||||
<p> Newsletter settings updated</p>
|
||||
);
|
||||
} else if (type === 'updateSubscription:success') {
|
||||
return (
|
||||
<p> Subscription plan successfully updated</p>
|
||||
);
|
||||
} else if (type === 'updateProfile:success') {
|
||||
return (
|
||||
<p>Profile updated</p>
|
||||
);
|
||||
} else if (type === 'updateProfile:failed') {
|
||||
return (
|
||||
<p>Failed to update profile</p>
|
||||
<p>{message}</p>
|
||||
);
|
||||
}
|
||||
const label = status === 'success' ? 'Success' : 'Failed';
|
||||
return (
|
||||
<p> ${label}</p>
|
||||
<p> An unexpected error occured. Please try again or <a href={supportAddressMail}>contact support</a> if the error persists. </p>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -110,13 +109,17 @@ export default class PopupNotification extends React.Component {
|
|||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
className: '',
|
||||
notificationType: ''
|
||||
className: ''
|
||||
};
|
||||
}
|
||||
|
||||
onAnimationEnd(e) {
|
||||
const {popupNotification} = this.context;
|
||||
const {type} = popupNotification || {};
|
||||
if (e.animationName === 'popupnotification-slideout') {
|
||||
if (type === 'stripe:billing-update') {
|
||||
clearURLParams(['stripe']);
|
||||
}
|
||||
this.context.onAction('clearPopupNotification');
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +132,7 @@ export default class PopupNotification extends React.Component {
|
|||
|
||||
componentDidUpdate() {
|
||||
const {popupNotification} = this.context;
|
||||
if (popupNotification.count !== this.state.count) {
|
||||
if (popupNotification.count !== this.state.notificationCount) {
|
||||
clearTimeout(this.timeoutId);
|
||||
this.handlePopupNotification({popupNotification});
|
||||
}
|
||||
|
@ -144,7 +147,7 @@ export default class PopupNotification extends React.Component {
|
|||
return {
|
||||
className: 'slideout',
|
||||
notificationCount: popupNotification.count
|
||||
}
|
||||
};
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
@ -166,15 +169,15 @@ export default class PopupNotification extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const {popupNotification} = this.context;
|
||||
const {popupNotification, site} = this.context;
|
||||
const {className} = this.state;
|
||||
const {type, status, closeable} = popupNotification;
|
||||
const statusClass = status ? ` ${status}` : '';
|
||||
const {type, status, closeable, message} = popupNotification;
|
||||
const statusClass = status ? ` ${status}` : '';
|
||||
const slideClass = className ? ` ${className}` : '';
|
||||
|
||||
return (
|
||||
<div className={`gh-portal-popupnotification${statusClass}${slideClass}`} onAnimationEnd={e => this.onAnimationEnd(e)}>
|
||||
<NotificationText type={type} status={status} />
|
||||
<NotificationText type={type} status={status} message={message} site={site} />
|
||||
<CloseButton hide={!closeable} onClose={e => this.closeNotification(e)}/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -46,12 +46,8 @@ export default class AccountProfilePage extends React.Component {
|
|||
};
|
||||
}, () => {
|
||||
const {email, name, errors} = this.state;
|
||||
const originalEmail = this.context.member.email;
|
||||
const hasFormErrors = (errors && Object.values(errors).filter(d => !!d).length > 0);
|
||||
if (!hasFormErrors) {
|
||||
if (email !== originalEmail) {
|
||||
this.context.onAction('updateEmail', {email, oldEmail: originalEmail, emailType: 'updateEmail'});
|
||||
}
|
||||
this.context.onAction('updateProfile', {email, name});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -72,11 +72,18 @@ export default class SigninPage extends React.Component {
|
|||
}
|
||||
|
||||
renderSubmitButton() {
|
||||
const isRunning = (this.context.action === 'signin:running');
|
||||
const label = isRunning ? 'Sending login link...' : 'Send login link';
|
||||
const {action} = this.context;
|
||||
let retry = false;
|
||||
const isRunning = (action === 'signin:running');
|
||||
let label = isRunning ? 'Sending login link...' : 'Send login link';
|
||||
const disabled = isRunning ? true : false;
|
||||
if (action === 'signin:failed') {
|
||||
label = 'Retry';
|
||||
retry = true;
|
||||
}
|
||||
return (
|
||||
<ActionButton
|
||||
retry={retry}
|
||||
style={{width: '100%'}}
|
||||
onClick={e => this.handleSignin(e)}
|
||||
disabled={disabled}
|
||||
|
|
|
@ -6,7 +6,6 @@ import InputForm from '../common/InputForm';
|
|||
import {ValidateInputForm} from '../../utils/form';
|
||||
import CalculateDiscount from '../../utils/discount';
|
||||
import {getSitePlans, hasOnlyFreePlan} from '../../utils/helpers';
|
||||
// import {ReactComponent as CloseIcon} from '../../images/icons/close.svg';
|
||||
|
||||
const React = require('react');
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ export const site = {
|
|||
is_stripe_configured: true,
|
||||
portal_button: true,
|
||||
portal_name: true,
|
||||
portal_plans: ['monthly', 'yearly'],
|
||||
portal_plans: ['free','monthly', 'yearly'],
|
||||
portal_button_icon: 'icon-1',
|
||||
portal_button_signup_text: 'Subscribe now',
|
||||
portal_button_style: 'icon-and-text',
|
||||
|
|
|
@ -2,12 +2,12 @@ import CalculateDiscount from './discount';
|
|||
|
||||
export function getPortalLinkPath({page}) {
|
||||
const Links = {
|
||||
'default': '#/portal',
|
||||
'signin': '#/portal/signin',
|
||||
'signup': '#/portal/signup',
|
||||
'account': '#/portal/account',
|
||||
default: '#/portal',
|
||||
signin: '#/portal/signin',
|
||||
signup: '#/portal/signup',
|
||||
account: '#/portal/account',
|
||||
'account-plans': '#/portal/account/plans',
|
||||
'account-profile': '#/portal/account/profile',
|
||||
'account-profile': '#/portal/account/profile'
|
||||
};
|
||||
if (Object.keys(Links).includes(page)) {
|
||||
return Links[page];
|
||||
|
@ -124,3 +124,39 @@ export function getSitePlans({site = {}, includeFree = true}) {
|
|||
}
|
||||
return plansData;
|
||||
}
|
||||
|
||||
export const getMemberEmail = ({member}) => {
|
||||
if (!member) {
|
||||
return '';
|
||||
}
|
||||
return member.email;
|
||||
};
|
||||
|
||||
export const getMemberName = ({member}) => {
|
||||
if (!member) {
|
||||
return '';
|
||||
}
|
||||
return member.name;
|
||||
};
|
||||
|
||||
export const getSupportAddress = ({site}) => {
|
||||
const {members_support_address: supportAddress} = site || {};
|
||||
return supportAddress || '';
|
||||
};
|
||||
|
||||
export const createPopupNotification = ({type, status, autoHide, duration, closeable, state, message, meta}) => {
|
||||
let count = 0;
|
||||
if (state && state.popupNotification) {
|
||||
count = (state.popupNotification.count || 0) + 1;
|
||||
}
|
||||
return {
|
||||
type,
|
||||
status,
|
||||
autoHide,
|
||||
closeable,
|
||||
duration,
|
||||
meta,
|
||||
message,
|
||||
count
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export const handleAuthActions = ({action, status}) => {
|
||||
export const handleAuthActions = ({qsParams, action, status}) => {
|
||||
if (status && ['true', 'false'].includes(status)) {
|
||||
const successStatus = JSON.parse(status);
|
||||
return {
|
||||
|
@ -11,8 +11,8 @@ export const handleAuthActions = ({action, status}) => {
|
|||
return {};
|
||||
};
|
||||
|
||||
export const handleStripeActions = ({status}) => {
|
||||
if (['cancel', 'success'].includes(status)) {
|
||||
export const handleStripeActions = ({qsParams, status, billingOnly}) => {
|
||||
if (!billingOnly && ['cancel', 'success'].includes(status)) {
|
||||
const statusVal = status === 'success' ? 'success' : 'warning';
|
||||
return {
|
||||
type: 'stripe:checkout',
|
||||
|
@ -20,18 +20,23 @@ export const handleStripeActions = ({status}) => {
|
|||
duration: 3000,
|
||||
autoHide: true
|
||||
};
|
||||
} else if (['billing-update-success', 'billing-update-cancel'].includes(status)) {
|
||||
}
|
||||
|
||||
if (billingOnly && ['billing-update-success', 'billing-update-cancel'].includes(status)) {
|
||||
const statusVal = status === 'billing-update-success' ? 'success' : 'warning';
|
||||
return {
|
||||
type: 'stripe:billing-update',
|
||||
status: statusVal,
|
||||
duration: 3000,
|
||||
autoHide: true
|
||||
autoHide: true,
|
||||
closeable: true
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const clearURLParams = (qsParams, paramsToClear = []) => {
|
||||
export const clearURLParams = (paramsToClear = []) => {
|
||||
const qs = window.location.search || '';
|
||||
const qsParams = new URLSearchParams(qs);
|
||||
paramsToClear.forEach((param) => {
|
||||
qsParams.delete(param);
|
||||
});
|
||||
|
@ -40,7 +45,7 @@ export const clearURLParams = (qsParams, paramsToClear = []) => {
|
|||
};
|
||||
|
||||
/** Handle actions in the App, returns updated state */
|
||||
export default function NotificationParser() {
|
||||
export default function NotificationParser({billingOnly = false} = {}) {
|
||||
const qs = window.location.search;
|
||||
if (!qs) {
|
||||
return null;
|
||||
|
@ -50,10 +55,13 @@ export default function NotificationParser() {
|
|||
const successStatus = qsParams.get('success');
|
||||
const stripeStatus = qsParams.get('stripe');
|
||||
let notificationData = null;
|
||||
if (action && successStatus) {
|
||||
return handleAuthActions({action, status: successStatus});
|
||||
} else if (stripeStatus) {
|
||||
return handleStripeActions({status: stripeStatus});
|
||||
|
||||
if (stripeStatus) {
|
||||
return handleStripeActions({qsParams, status: stripeStatus, billingOnly});
|
||||
}
|
||||
|
||||
if (action && successStatus && !billingOnly) {
|
||||
return handleAuthActions({qsParams, action, status: successStatus});
|
||||
}
|
||||
|
||||
return notificationData;
|
||||
|
|
Loading…
Add table
Reference in a new issue