Send initial tx message from checkout

This commit is contained in:
Kimmo Puputti 2017-11-08 16:23:00 +02:00
parent 18a808b3b8
commit 030e66c7d3
6 changed files with 71 additions and 14 deletions

View file

@ -110,18 +110,26 @@ export const initiateOrder = (orderParams, initialMessage) => (dispatch, getStat
transition: 'transition/preauthorize',
params: orderParams,
};
let orderResponse;
return sdk.transactions
.initiate(bodyParams)
.then(response => {
orderResponse = response;
console.log(`TODO: save initial message: "${initialMessage}"`);
})
.then(() => {
const orderId = orderResponse.data.data.id;
const orderId = response.data.data.id;
dispatch(initiateOrderSuccess(orderId));
dispatch(fetchCurrentUserHasOrdersSuccess(true));
return orderId;
if (initialMessage) {
return sdk.messages
.send({ transactionId: orderId, content: initialMessage })
.then(() => {
return { orderId, initialMessageSuccess: true };
})
.catch(e => {
log.error(e, 'initial-message-send-failed', { txId: orderId });
return { orderId, initialMessageSuccess: false };
});
} else {
return Promise.resolve({ orderId, initialMessageSuccess: true });
}
})
.catch(e => {
dispatch(initiateOrderError(storableError(e)));

View file

@ -6,7 +6,7 @@ import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
import { withRouter } from 'react-router-dom';
import classNames from 'classnames';
import routeConfiguration from '../../routeConfiguration';
import { pathByRouteName } from '../../util/routes';
import { pathByRouteName, findRouteByRouteName } from '../../util/routes';
import * as propTypes from '../../util/propTypes';
import { ensureListing, ensureUser, ensureTransaction, ensureBooking } from '../../util/data';
import { createSlug } from '../../util/urlHelpers';
@ -104,7 +104,7 @@ export class CheckoutPageComponent extends Component {
const cardToken = values.token;
const initialMessage = values.message;
const { history, sendOrderRequest, speculatedTransaction } = this.props;
const { history, sendOrderRequest, speculatedTransaction, dispatch } = this.props;
const requestParams = {
listingId: this.state.pageData.listing.id,
cardToken,
@ -113,9 +113,20 @@ export class CheckoutPageComponent extends Component {
};
sendOrderRequest(requestParams, initialMessage)
.then(orderId => {
.then(values => {
const { orderId, initialMessageSuccess } = values;
this.setState({ submitting: false });
const orderDetailsPath = pathByRouteName('OrderDetailsPage', routeConfiguration(), {
const routes = routeConfiguration();
const OrderPage = findRouteByRouteName('OrderDetailsPage', routes);
// Transaction is already created, but if the initial message
// sending failed, we tell it to the OrderDetailsPage.
dispatch(
OrderPage.setInitialValues({
messageSendingFailedToTransaction: initialMessageSuccess ? null : orderId,
})
);
const orderDetailsPath = pathByRouteName('OrderDetailsPage', routes, {
id: orderId.uuid,
});
clearData(STORAGE_KEY);
@ -401,6 +412,9 @@ CheckoutPageComponent.propTypes = {
}).isRequired,
sendOrderRequest: func.isRequired,
// from connect
dispatch: func.isRequired,
// from injectIntl
intl: intlShape.isRequired,
@ -433,6 +447,7 @@ const mapStateToProps = state => {
};
const mapDispatchToProps = dispatch => ({
dispatch,
sendOrderRequest: (params, initialMessage) => dispatch(initiateOrder(params, initialMessage)),
fetchSpeculatedTransaction: (listingId, bookingStart, bookingEnd) =>
dispatch(speculateTransaction(listingId, bookingStart, bookingEnd)),

View file

@ -14,6 +14,7 @@ describe('CheckoutPage', () => {
bookingStart: new Date(Date.UTC(2017, 3, 14)),
bookingEnd: new Date(Date.UTC(2017, 3, 16)),
},
dispatch: noop,
history: { push: noop },
intl: fakeIntl,
listing,

View file

@ -1,3 +1,4 @@
import { pick } from 'lodash';
import { types } from '../../util/sdkLoader';
import { storableError } from '../../util/errors';
import { addMarketplaceEntities } from '../../ducks/marketplaceData.duck';
@ -5,6 +6,8 @@ import { updatedEntities, denormalisedEntities } from '../../util/data';
// ================ Action types ================ //
export const SET_INITAL_VALUES = 'app/OrderPage/SET_INITIAL_VALUES';
export const FETCH_ORDER_REQUEST = 'app/OrderPage/FETCH_ORDER_REQUEST';
export const FETCH_ORDER_SUCCESS = 'app/OrderPage/FETCH_ORDER_SUCCESS';
export const FETCH_ORDER_ERROR = 'app/OrderPage/FETCH_ORDER_ERROR';
@ -22,11 +25,15 @@ const initialState = {
fetchMessagesInProgress: false,
fetchMessagesError: null,
messages: [],
messageSendingFailedToTransaction: null,
};
export default function checkoutPageReducer(state = initialState, action = {}) {
const { type, payload } = action;
switch (type) {
case SET_INITAL_VALUES:
return { ...initialState, ...payload };
case FETCH_ORDER_REQUEST:
return { ...state, fetchOrderInProgress: true, fetchOrderError: null };
case FETCH_ORDER_SUCCESS: {
@ -50,6 +57,11 @@ export default function checkoutPageReducer(state = initialState, action = {}) {
// ================ Action creators ================ //
export const setInitialValues = initialValues => ({
type: SET_INITAL_VALUES,
payload: pick(initialValues, Object.keys(initialState)),
});
const fetchOrderRequest = () => ({ type: FETCH_ORDER_REQUEST });
const fetchOrderSuccess = response => ({ type: FETCH_ORDER_SUCCESS, payload: response });
const fetchOrderError = e => ({ type: FETCH_ORDER_ERROR, error: true, payload: e });

View file

@ -20,17 +20,33 @@ import {
} from '../../components';
import { TopbarContainer } from '../../containers';
import { loadData } from './OrderPage.duck';
import { loadData, setInitialValues } from './OrderPage.duck';
import css from './OrderPage.css';
// OrderPage handles data loading
// It show loading data text or OrderDetailsPanel (and later also another panel for messages).
export const OrderPageComponent = props => {
const { currentUser, fetchOrderError, intl, params, scrollingDisabled, transaction } = props;
const {
currentUser,
fetchOrderError,
messageSendingFailedToTransaction,
intl,
params,
scrollingDisabled,
transaction,
} = props;
const currentTransaction = ensureTransaction(transaction);
const currentListing = ensureListing(currentTransaction.listing);
const listingTitle = currentListing.attributes.title;
if (messageSendingFailedToTransaction) {
// TODO: render error message with other messages
console.error(
'failed to send initial message to transaction:',
messageSendingFailedToTransaction
);
}
// Redirect users with someone else's direct link to their own inbox/orders page.
const isDataAvailable =
currentUser &&
@ -95,6 +111,7 @@ const { bool, oneOf, shape, string } = PropTypes;
OrderPageComponent.propTypes = {
currentUser: propTypes.currentUser,
fetchOrderError: propTypes.error,
messageSendingFailedToTransaction: propTypes.uuid,
intl: intlShape.isRequired,
params: shape({ id: string }).isRequired,
scrollingDisabled: bool.isRequired,
@ -103,7 +120,7 @@ OrderPageComponent.propTypes = {
};
const mapStateToProps = state => {
const { fetchOrderError, transactionRef } = state.OrderPage;
const { fetchOrderError, transactionRef, messageSendingFailedToTransaction } = state.OrderPage;
const { currentUser } = state.user;
const transactions = getMarketplaceEntities(state, transactionRef ? [transactionRef] : []);
const transaction = transactions.length > 0 ? transactions[0] : null;
@ -111,6 +128,7 @@ const mapStateToProps = state => {
return {
currentUser,
fetchOrderError,
messageSendingFailedToTransaction,
scrollingDisabled: isScrollingDisabled(state),
transaction,
};
@ -118,6 +136,7 @@ const mapStateToProps = state => {
const OrderPage = compose(connect(mapStateToProps), injectIntl)(OrderPageComponent);
OrderPage.setInitialValues = setInitialValues;
OrderPage.loadData = loadData;
export default OrderPage;

View file

@ -184,6 +184,7 @@ const routeConfiguration = () => {
name: 'OrderDetailsPage',
component: props => <OrderPage {...props} tab="details" />,
loadData: OrderPage.loadData,
setInitialValues: OrderPage.setInitialValues,
},
{
path: '/order/:id/discussion',
@ -191,6 +192,7 @@ const routeConfiguration = () => {
name: 'OrderDiscussionPage',
component: props => <OrderPage {...props} tab="discussion" />,
loadData: OrderPage.loadData,
setInitialValues: OrderPage.setInitialValues,
},
{
path: '/sale/:id',