Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 97 additions & 84 deletions imports/plugins/core/accounts/client/containers/loginInline.js
Original file line number Diff line number Diff line change
@@ -1,116 +1,129 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import gql from "graphql-tag";
import { Mutation } from "react-apollo";
import { Meteor } from "meteor/meteor";
import { registerComponent, composeWithTracker } from "@reactioncommerce/reaction-components";
import { Reaction, i18next } from "/client/api";
import { i18next } from "/client/api";
import Logger from "/client/modules/logger";
import getCart from "/imports/plugins/core/cart/client/util/getCart";
import { getAnonymousCartsReactive } from "/imports/plugins/core/cart/client/util/anonymousCarts";
import LoginInline from "../components/loginInline";

class LoginInlineContainer extends Component {
static propTypes = {
isStripeEnabled: PropTypes.bool
}

constructor(props) {
super(props);

this.state = {
isStripeEnabled: props.isStripeEnabled,
renderEmailForm: false
};
const setEmailOnAnonymousCartMutation = gql`
mutation SetEmailOnAnonymousCart($input: SetEmailOnAnonymousCartInput!) {
setEmailOnAnonymousCart(input: $input) {
cart {
_id
email
}
}
}
`;

componentWillReceiveProps(nextProps) {
this.setState({
isStripeEnabled: nextProps.isStripeEnabled
/**
* @summary Push cart workflow past "checkoutLogin" and potentially past "checkoutAddressBook"
* @returns {undefined}
*/
function pushCartWorkflow() {
const { cart } = getCart();
if (cart) {
Meteor.call("workflow/pushCartWorkflow", "coreCartWorkflow", "checkoutLogin", cart._id, (error) => {
if (error) {
// Do not bother to try to advance workflow if we can't go beyond login.
Logger.error(error);
return;
}
// If there's already a billing and shipping address selected, push beyond address book
const { cart: updatedCart } = getCart();
if (updatedCart && updatedCart.billing[0] && updatedCart.billing[0].address
&& updatedCart.shipping[0] && updatedCart.shipping[0].address) {
Meteor.call("workflow/pushCartWorkflow", "coreCartWorkflow", "checkoutAddressBook", updatedCart._id, (error2) => {
if (error2) {
Logger.error(error2);
}
});
}
});
}
}

pushCartWorkflow = () => {
const { cart } = getCart();
if (cart) {
Meteor.call("workflow/pushCartWorkflow", "coreCartWorkflow", "checkoutLogin", cart._id, (error) => {
if (error) {
// Do not bother to try to advance workflow if we can't go beyond login.
return;
}
// If there's already a billing and shipping address selected, push beyond address book
const { cart: updatedCart } = getCart();
if (updatedCart && updatedCart.billing[0] && updatedCart.billing[0].address
&& updatedCart.shipping[0] && updatedCart.shipping[0].address) {
Meteor.call("workflow/pushCartWorkflow", "coreCartWorkflow", "checkoutAddressBook", updatedCart._id);
}
});
}
function handleEmailSubmitError(error) {
Logger.error(error);
Alerts.toast(i18next.t("mail.alerts.addCartEmailFailed"), "error");
}

class LoginInlineContainer extends Component {
static propTypes = {
cart: PropTypes.shape({
_id: PropTypes.string.isRequired
}),
token: PropTypes.string
}

continueAsGuest = (event) => {
event.preventDefault();
if (this.state.isStripeEnabled) {
this.setState({
renderEmailForm: true
});
} else {
this.pushCartWorkflow();
}
state = {
renderEmailForm: false
}

/**
* @method handleEmailSubmit
* @summary Handle submitting the email form
* @param {Event} event - the event that fired
* @param {String} email - anonymous user's email
* @return {undefined} undefined
* @private
*/
handleEmailSubmit = (event, email) => {
handleContinueAsGuest = (event) => {
event.preventDefault();
const { cart } = getCart();
if (cart) {
const anonymousCarts = getAnonymousCartsReactive();
const cartInfo = anonymousCarts.find((anonymousCart) => anonymousCart._id === cart._id);
Meteor.call("cart/setAnonymousUserEmail", cart._id, cartInfo.token, email, (error) => {
if (error) {
Alerts.toast(i18next.t("mail.alerts.addCartEmailFailed"), "error");
} else {
this.pushCartWorkflow();
}
});
}
this.setState({
renderEmailForm: true
});
}

render() {
const { cart, token } = this.props;

if (!cart) return null;

return (
<LoginInline
continueAsGuest={this.continueAsGuest}
renderEmailForm={this.state.renderEmailForm}
handleEmailSubmit={this.handleEmailSubmit}
/>
<Mutation
ignoreResults
mutation={setEmailOnAnonymousCartMutation}
onCompleted={pushCartWorkflow}
onError={handleEmailSubmitError}
>
{(setEmailOnAnonymousCart) => (
<LoginInline
continueAsGuest={this.handleContinueAsGuest}
renderEmailForm={this.state.renderEmailForm}
handleEmailSubmit={(event, email) => {
event.preventDefault();
Meteor.call("getOpaqueIdFromInternalId", "Cart", cart._id, (error, opaqueCartId) => {
if (error || !opaqueCartId) {
Logger.error(error || "No opaque cart ID returned");
return;
}
setEmailOnAnonymousCart({ variables: { input: { cartId: opaqueCartId, email, token } } });
});
}}
/>
)}
</Mutation>
);
}
}

/**
* @summary Composer for LoginInline component
* @param {Object} props Props from parent
* @param {Function} onData Data callback
* @returns {undefined}
*/
function composer(props, onData) {
let isStripeEnabled = false;
const subscription = Reaction.Subscriptions.Packages;
const primaryShopId = Reaction.getPrimaryShopId();
const { cart } = getCart();
let token = null;
if (cart) {
const anonymousCarts = getAnonymousCartsReactive();
const cartInfo = anonymousCarts.find((anonymousCart) => anonymousCart._id === cart._id);
({ token } = cartInfo || {});
}

const stripePkg = Reaction.getPackageSettingsWithOptions({
shopId: primaryShopId,
name: "reaction-stripe",
enabled: true
onData(null, {
cart,
token
});

if (subscription.ready()) {
if (stripePkg) {
isStripeEnabled = true;
}

onData(null, {
isStripeEnabled
});
}
}

registerComponent("LoginInline", LoginInlineContainer, composeWithTracker(composer));
Expand Down
2 changes: 0 additions & 2 deletions imports/plugins/core/accounts/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ import "./templates/accounts.html";
import "./templates/dashboard/dashboard.html";
import "./templates/dashboard/dashboard.js";
import "./templates/dropdown/helpers";
import "./templates/inline/inline.html";
import "./templates/inline/inline.js";
import "./templates/login/loginForm.html";
import "./templates/login/loginForm.js";
import "./templates/members/member.html";
Expand Down

This file was deleted.

14 changes: 0 additions & 14 deletions imports/plugins/core/accounts/client/templates/inline/inline.js

This file was deleted.

2 changes: 0 additions & 2 deletions imports/plugins/core/cart/server/methods/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import copyCartToOrder from "./copyCartToOrder";
import createCart from "./createCart";
import mergeCart from "./mergeCart";
import removeFromCart from "./removeFromCart";
import setAnonymousUserEmail from "./setAnonymousUserEmail";
import setPaymentAddress from "./setPaymentAddress";
import setShipmentAddress from "./setShipmentAddress";
import setShipmentMethod from "./setShipmentMethod";
Expand All @@ -23,7 +22,6 @@ export default {
"cart/createCart": createCart,
"cart/mergeCart": mergeCart,
"cart/removeFromCart": removeFromCart,
"cart/setAnonymousUserEmail": setAnonymousUserEmail,
"cart/setPaymentAddress": setPaymentAddress,
"cart/setShipmentAddress": setShipmentAddress,
"cart/setShipmentMethod": setShipmentMethod,
Expand Down
35 changes: 0 additions & 35 deletions imports/plugins/core/cart/server/methods/setAnonymousUserEmail.js

This file was deleted.

2 changes: 2 additions & 0 deletions imports/plugins/core/cart/server/no-meteor/mutations/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import reconcileCartsKeepAccountCart from "./reconcileCartsKeepAccountCart";
import reconcileCartsKeepAnonymousCart from "./reconcileCartsKeepAnonymousCart";
import reconcileCartsMerge from "./reconcileCartsMerge";
import removeCartItems from "./removeCartItems";
import setEmailOnAnonymousCart from "./setEmailOnAnonymousCart";
import setShippingAddressOnCart from "./setShippingAddressOnCart";
import updateCartItemsQuantity from "./updateCartItemsQuantity";

Expand All @@ -18,6 +19,7 @@ export default {
reconcileCartsKeepAnonymousCart,
reconcileCartsMerge,
removeCartItems,
setEmailOnAnonymousCart,
setShippingAddressOnCart,
updateCartItemsQuantity
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import SimpleSchema from "simpl-schema";
import ReactionError from "@reactioncommerce/reaction-error";
import hashLoginToken from "/imports/plugins/core/accounts/server/no-meteor/util/hashLoginToken";

const inputSchema = new SimpleSchema({
cartId: String,
token: String,
email: {
type: String,
regEx: SimpleSchema.RegEx.Email
}
});

/**
* @method setEmailOnAnonymousCart
* @summary Assigns email to anonymous user's cart instance
* @param {Object} context - an object containing the per-request state
* @param {Object} input - an object of all mutation arguments that were sent by the client
* @param {String} input.cartId - An anonymous cart ID
* @param {String} input.token - The token for accessing the anonymous cart
* @param {String} input.email - The email address to associate with this cart
* @return {Promise<Object>} An object with `cart` property containing the updated cart
*/
export default async function setEmailOnAnonymousCart(context, input) {
inputSchema.validate(input || {});

const { appEvents, collections } = context;
const { Cart } = collections;
const { cartId, email, token } = input;

const { matchedCount } = await Cart.updateOne({
_id: cartId,
anonymousAccessToken: hashLoginToken(token)
}, {
$set: { email }
});
if (matchedCount === 0) throw new ReactionError("server-error", "Unable to update cart");

const updatedCart = await Cart.findOne({ _id: cartId });

await appEvents.emit("afterCartUpdate", updatedCart._id, updatedCart);

return {
cart: updatedCart
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import mockContext from "/imports/test-utils/helpers/mockContext";
import setEmailOnAnonymousCart from "./setEmailOnAnonymousCart";

const dbCart = {
_id: "cartId"
};
const email = "[email protected]";
const token = "TOKEN";
const hashedToken = "+YED6SF/CZIIVp0pXBsnbxghNIY2wmjIVLsqCG4AN80=";

test("sets the email address on an anonymous cart", async () => {
mockContext.collections.Cart.findOne.mockReturnValueOnce(Promise.resolve(dbCart));

const result = await setEmailOnAnonymousCart(mockContext, {
cartId: "cartId",
email,
token
});

expect(mockContext.collections.Cart.updateOne).toHaveBeenCalledWith({
_id: "cartId",
anonymousAccessToken: hashedToken
}, {
$set: { email }
});

expect(mockContext.collections.Cart.findOne).toHaveBeenCalledWith({ _id: "cartId" });

expect(result).toEqual({
cart: dbCart
});
});
Loading