Save CheckoutPage data to sessionStore

This commit is contained in:
Vesa Luusua 2017-04-11 17:43:52 +03:00
parent f899a51472
commit cecae6b23e

View file

@ -3,6 +3,9 @@ import { compose } from 'redux';
import { connect } from 'react-redux';
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
import { withRouter } from 'react-router-dom';
import { reduce } from 'lodash';
import moment from 'moment';
import { types } from '../../util/sdkLoader';
import { pathByRouteName } from '../../util/routes';
import * as propTypes from '../../util/propTypes';
import { withFlattenedRoutes } from '../../util/contextHelpers';
@ -18,9 +21,102 @@ const ensureListingProperties = listing => {
return { ...empty, ...listing };
};
// Validate that given 'obj' has all the keys of defined by validPropTypes parameter
// and values must pass related test-value-format function.
const validateProperties = (obj, validPropTypes) => {
return reduce(
Object.entries(validPropTypes),
(acc, [prop, fn]) => {
if (Object.prototype.hasOwnProperty.call(obj, prop) && fn(obj[prop])) {
return acc;
}
return false;
},
true
);
};
// Validate content of booking dates object received from SessionStore
const isValidBookingDates = bookingDates => {
const props = {
bookingStart: d => d instanceof Date,
bookingEnd: d => d instanceof Date,
};
return validateProperties(bookingDates, props);
};
// Validate content of listing object received from SessionStore.
// Currently only id & attributes.price are needed.
const isValidListing = listing => {
const props = {
id: id => id instanceof types.UUID,
attributes: v => {
return typeof v === 'object' && v.price instanceof types.Money;
},
};
return validateProperties(listing, props);
};
// Stores given bookingDates and listing to sessionStorage
const storeData = (bookingDates, listing) => {
if (window && window.sessionStorage && listing && bookingDates) {
// TODO: How should we deal with Dates when data is serialized?
// Hard coded serializable date objects atm.
/* eslint-disable no-underscore-dangle */
const data = {
bookingDates: {
bookingStart: { date: bookingDates.bookingStart, _serializedType: 'SerializableDate' },
bookingEnd: { date: bookingDates.bookingEnd, _serializedType: 'SerializableDate' },
},
listing,
storedAt: { date: new Date(), _serializedType: 'SerializableDate' },
};
/* eslint-enable no-underscore-dangle */
const storableData = JSON.stringify(data, types.replacer);
window.sessionStorage.setItem('CheckoutPage', storableData);
}
};
// Get stored data
const storedData = () => {
if (window && window.sessionStorage) {
const checkoutPageData = window.sessionStorage.getItem('CheckoutPage');
// TODO How should we deal with Dates when data is serialized?
// Dates are expected to be in format: { date: new Date(), _serializedType: 'SerializableDate' }
const reviver = (k, v) => {
// eslint-disable-next-line no-underscore-dangle
if (typeof v === 'object' && v._serializedType === 'SerializableDate') {
return new Date(v.date);
}
return types.reviver(k, v);
};
const { bookingDates, listing, storedAt } = checkoutPageData
? JSON.parse(checkoutPageData, reviver)
: {};
// If sessionStore contains freshly saved data (max 1 day old), use it
const isFreshlySaved = storedAt
? moment(storedAt).isAfter(moment().subtract(1, 'days'))
: false;
if (isFreshlySaved && isValidBookingDates(bookingDates) && isValidListing(listing)) {
return { bookingDates, listing };
}
}
return {};
};
export class CheckoutPageComponent extends Component {
constructor(props) {
super(props);
const { bookingDates, listing } = props;
// Store received data
storeData(bookingDates, listing);
this.state = { submitting: false };
this.handleSubmit = this.handleSubmit.bind(this);
}
@ -48,20 +144,23 @@ export class CheckoutPageComponent extends Component {
render() {
const { bookingDates, initiateOrderError, intl, listing, params, currentUser } = this.props;
const { bookingStart, bookingEnd } = bookingDates || {};
const currentListing = ensureListingProperties(listing);
// Get page data from passed-in props or from storage
const pageData = bookingDates && listing ? { bookingDates, listing } : storedData();
const { bookingStart, bookingEnd } = pageData.bookingDates || {};
const currentListing = ensureListingProperties(pageData.listing);
const price = currentListing.attributes.price;
const isOwnListing = listing &&
const isOwnListing = currentListing &&
currentUser &&
listing.author &&
listing.author.id.uuid === currentUser.id.uuid;
currentListing.author &&
currentListing.author.id.uuid === currentUser.id.uuid;
// Allow showing page when currentUser is still being downloaded,
// but show payment form only when user info is loaded.
const showPaymentForm = currentUser && !isOwnListing;
if (!listing || !price || isOwnListing) {
if (!currentListing.id || !price || isOwnListing) {
// eslint-disable-next-line no-console
console.error(
'Listing, price, or user invalid for checkout, redirecting back to listing page.'