From 2d6cd4d2f5905e1ebf85b83d130be906e136b191 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Fri, 3 Aug 2018 16:14:07 -0400 Subject: [PATCH 01/65] (perf): use pure CSS animations for cart drawer (no jQuery or velocity) --- .../client/components/emptyCartDrawer.js | 3 +- .../client/containers/cartDrawerContainer.js | 2 +- .../client/containers/cartIconContainer.js | 6 ++-- .../client/containers/cartPanelContainer.js | 2 +- .../client/templates/cartDrawer/cartDrawer.js | 34 +++++++++---------- .../client/templates/cartIcon/cartIcon.js | 3 +- .../client/styles/cart/cartDrawer.less | 17 ++++++++++ 7 files changed, 41 insertions(+), 26 deletions(-) diff --git a/imports/plugins/core/checkout/client/components/emptyCartDrawer.js b/imports/plugins/core/checkout/client/components/emptyCartDrawer.js index 20e83eaa002..f1896ca8f29 100644 --- a/imports/plugins/core/checkout/client/components/emptyCartDrawer.js +++ b/imports/plugins/core/checkout/client/components/emptyCartDrawer.js @@ -7,7 +7,8 @@ import { Reaction } from "/client/api"; function handleKeepShopping(event) { event.stopPropagation(); event.preventDefault(); - return $("#cart-drawer-container").fadeOut(300, () => Reaction.toggleSession("displayCart")); + document.querySelector("#cart-drawer-container").classList.remove("opened"); + Reaction.toggleSession("displayCart"); } const EmptyCartDrawer = () => ( diff --git a/imports/plugins/core/checkout/client/containers/cartDrawerContainer.js b/imports/plugins/core/checkout/client/containers/cartDrawerContainer.js index 36921f5d4c4..067399ae319 100644 --- a/imports/plugins/core/checkout/client/containers/cartDrawerContainer.js +++ b/imports/plugins/core/checkout/client/containers/cartDrawerContainer.js @@ -49,7 +49,7 @@ const handlers = { }, handleCheckout() { - $("#cart-drawer-container").fadeOut(); + document.querySelector("#cart-drawer-container").classList.remove("opened"); Session.set("displayCart", false); return Reaction.Router.go("cart/checkout"); } diff --git a/imports/plugins/core/checkout/client/containers/cartIconContainer.js b/imports/plugins/core/checkout/client/containers/cartIconContainer.js index 86b25c789fe..ed58b667f5e 100644 --- a/imports/plugins/core/checkout/client/containers/cartIconContainer.js +++ b/imports/plugins/core/checkout/client/containers/cartIconContainer.js @@ -8,10 +8,8 @@ import CartIcon from "../components/cartIcon"; const handlers = { handleClick(e) { e.preventDefault(); - const cartDrawer = document.querySelector("#cart-drawer-container"); - Velocity(cartDrawer, { opacity: 1 }, 300, () => { - Reaction.toggleSession("displayCart"); - }); + document.querySelector("#cart-drawer-container").classList.toggle("opened"); + Reaction.toggleSession("displayCart"); } }; diff --git a/imports/plugins/core/checkout/client/containers/cartPanelContainer.js b/imports/plugins/core/checkout/client/containers/cartPanelContainer.js index a6b717fdc22..9f83af7d872 100644 --- a/imports/plugins/core/checkout/client/containers/cartPanelContainer.js +++ b/imports/plugins/core/checkout/client/containers/cartPanelContainer.js @@ -7,7 +7,7 @@ import CartPanel from "../components/cartPanel"; const handlers = { checkout() { - $("#cart-drawer-container").fadeOut(); + document.querySelector("#cart-drawer-container").classList.remove("opened"); Session.set("displayCart", false); return Reaction.Router.go("cart/checkout"); } diff --git a/imports/plugins/core/checkout/client/templates/cartDrawer/cartDrawer.js b/imports/plugins/core/checkout/client/templates/cartDrawer/cartDrawer.js index 7de860bee18..51be89dbc73 100644 --- a/imports/plugins/core/checkout/client/templates/cartDrawer/cartDrawer.js +++ b/imports/plugins/core/checkout/client/templates/cartDrawer/cartDrawer.js @@ -45,23 +45,21 @@ Template.openCartDrawer.onRendered(() => { */ let swiper; - - $("#cart-drawer-container").fadeIn(() => { - if (!swiper) { - swiper = new Swiper(".cart-drawer-swiper-container", { - direction: "horizontal", - setWrapperSize: true, - loop: false, - grabCursor: true, - slidesPerView: "auto", - wrapperClass: "cart-drawer-swiper-wrapper", - slideClass: "cart-drawer-swiper-slide", - slideActiveClass: "cart-drawer-swiper-slide-active", - pagination: ".cart-drawer-pagination", - paginationClickable: true - }); - } - }); + document.querySelector("#cart-drawer-container").classList.add("opened"); + if (!swiper) { + swiper = new Swiper(".cart-drawer-swiper-container", { + direction: "horizontal", + setWrapperSize: true, + loop: false, + grabCursor: true, + slidesPerView: "auto", + wrapperClass: "cart-drawer-swiper-wrapper", + slideClass: "cart-drawer-swiper-slide", + slideActiveClass: "cart-drawer-swiper-slide-active", + pagination: ".cart-drawer-pagination", + paginationClickable: true + }); + } }); Template.openCartDrawer.helpers({ @@ -70,7 +68,7 @@ Template.openCartDrawer.helpers({ } }); -Template.emptyCartDrawer.onRendered(() => $("#cart-drawer-container").fadeIn()); +Template.emptyCartDrawer.onRendered(() => document.querySelector("#cart-drawer-container").classList.add("opened")); Template.emptyCartDrawer.helpers({ EmptyCartDrawer() { diff --git a/imports/plugins/core/checkout/client/templates/cartIcon/cartIcon.js b/imports/plugins/core/checkout/client/templates/cartIcon/cartIcon.js index 6ae51d499a0..1f43e67538b 100644 --- a/imports/plugins/core/checkout/client/templates/cartIcon/cartIcon.js +++ b/imports/plugins/core/checkout/client/templates/cartIcon/cartIcon.js @@ -16,6 +16,7 @@ Template.cartIcon.helpers({ Template.cartIcon.events({ "click .cart-icon"() { - return $("#cart-drawer-container").fadeOut(300, () => Reaction.toggleSession("displayCart")); + document.querySelector("#cart-drawer-container").classList.remove("opened"); + Reaction.toggleSession("displayCart"); } }); diff --git a/imports/plugins/included/default-theme/client/styles/cart/cartDrawer.less b/imports/plugins/included/default-theme/client/styles/cart/cartDrawer.less index aed74ee64bc..e54152e3c4d 100644 --- a/imports/plugins/included/default-theme/client/styles/cart/cartDrawer.less +++ b/imports/plugins/included/default-theme/client/styles/cart/cartDrawer.less @@ -1,6 +1,23 @@ #cart-drawer-container { display: none; + opacity: 0; + &.opened { + display: block; + opacity: 1; + animation-name: fadeIn; + animation-duration: 300ms; + } +} + +@keyframes fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } } + .cart-drawer { height: 100%; width: 100%; From 9bf5896d56e999dfc73980ea57b26c750e90d272 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Mon, 6 Aug 2018 13:20:36 -0400 Subject: [PATCH 02/65] (feat): helper function for flashing input animation w/ CSS --- .../core/ui/client/helpers/animations.js | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 imports/plugins/core/ui/client/helpers/animations.js diff --git a/imports/plugins/core/ui/client/helpers/animations.js b/imports/plugins/core/ui/client/helpers/animations.js new file mode 100644 index 00000000000..ab16d64412d --- /dev/null +++ b/imports/plugins/core/ui/client/helpers/animations.js @@ -0,0 +1,24 @@ +import { Meteor } from "meteor/meteor"; + +/** + * @name highlightInput + * @summary Uses a CSS animation to briefly highlight an input + * @param {HTMLInputElement} inputRef - Input to animate + * @returns {undefined} + */ +export function highlightInput (inputRef, className="highlight") { + inputRef.classList.add(className); + Meteor.setTimeout(() => { + inputRef.classList.remove(className); + }, 500); +} + +/** + * @name highlightVariantInput + * @summary Does the same as highlightInput, but with a lighter green color + * @param {HTMLInputElement} inputRef - Input to animate + * @returns {undefined} + */ +export function highlightVariantInput (inputRef) { + highlightInput(inputRef, "highlight-variant"); +} From a2c1b8f24b39904844b4769a632b0c865b1d1911 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Mon, 6 Aug 2018 13:21:07 -0400 Subject: [PATCH 03/65] (feat): pure CSS animation for highlighting an input --- .../default-theme/client/styles/forms.less | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/imports/plugins/included/default-theme/client/styles/forms.less b/imports/plugins/included/default-theme/client/styles/forms.less index 86ed424a20c..a3c7928ec96 100644 --- a/imports/plugins/included/default-theme/client/styles/forms.less +++ b/imports/plugins/included/default-theme/client/styles/forms.less @@ -249,3 +249,43 @@ display: inline-block; } } + +input.highlight { + animation-name: 'highlightInput'; + animation-duration: 300ms; +} + +@keyframes highlightInput { + 0% { + background-color: auto; + } + 30% { + background-color: #e2f2e2; + } + 70% { + background-color: #e2f2e2; + } + 100% { + background-color: auto; + } +} + +input.highlight-variant { + animation-name: 'highlightVariantInput'; + animation-duration: 300ms; +} + +@keyframes highlightVariantInput { + 0% { + background-color: auto; + } + 30% { + background-color: #f0fff4; + } + 70% { + background-color: #f0fff4; + } + 100% { + background-color: auto; + } +} From d82c6549f0e4467f9bc50f4b4ea2f14a0caaec98 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Mon, 6 Aug 2018 13:21:32 -0400 Subject: [PATCH 04/65] (feat): replace metafield velocity animations w/ css --- .../client/components/metadata/metafield.js | 69 ++++++++++++------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/imports/plugins/core/ui/client/components/metadata/metafield.js b/imports/plugins/core/ui/client/components/metadata/metafield.js index e46adece097..f2cfd384db1 100644 --- a/imports/plugins/core/ui/client/components/metadata/metafield.js +++ b/imports/plugins/core/ui/client/components/metadata/metafield.js @@ -1,28 +1,15 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; -import Velocity from "velocity-animate"; -import "velocity-animate/velocity.ui"; import { Components, registerComponent } from "@reactioncommerce/reaction-components"; +import { highlightInput } from "../../helpers/animations"; class Metafield extends Component { - componentWillReceiveProps(nextProps) { - if (nextProps.metafield.key !== this.props.metafield.key) { - const { input } = this.refs.keyInput.refs; - - Velocity.RunSequence([ - { e: input, p: { backgroundColor: "#e2f2e2" }, o: { duration: 200 } }, - { e: input, p: { backgroundColor: "#fff" }, o: { duration: 100 } } - ]); - } - - if (nextProps.metafield.value !== this.props.metafield.value) { - const { input } = this.refs.valueInput.refs; - - Velocity.RunSequence([ - { e: input, p: { backgroundColor: "#e2f2e2" }, o: { duration: 200 } }, - { e: input, p: { backgroundColor: "#fff" }, o: { duration: 100 } } - ]); - } + constructor(props) { + super(props); + this.state = { + // Currently focused input's original value, used to determine whether to highlight on blur/enter + inputOriginalValue: "" + }; } get detailNamePlaceholder() { @@ -61,13 +48,43 @@ class Metafield extends Component { } } + handleFocus = (event) => { + this.setState({ inputOriginalValue: event.target.value }); + } + + handleKeyBlur = (event) => { + const keyInput = this.refs.keyInput.refs.input; + + if (this.state.inputOriginalValue !== keyInput.value) { + // Value has changed, highlight + this.highlightInput(keyInput); + } + + this.handleBlur(event); + }; + + highlightInput = (inputRef) => { + highlightInput(inputRef); + } + + handleValueBlur = (event) => { + + const valueInput = this.refs.valueInput.refs.input; + + if (this.state.inputOriginalValue !== valueInput.value) { + // Value has changed, highlight + this.highlightInput(valueInput); + } + + this.handleBlur(event); + }; + handleBlur = (event) => { if (this.props.onBlur) { const newMetadata = { key: this.refs.keyInput.refs.input.value, value: this.refs.valueInput.refs.input.value }; - if (newMetadata.key && newMetadata.value) { this.props.onBlur(event, newMetadata, this.props.index); } @@ -104,9 +121,10 @@ class Metafield extends Component { className="metafield-key-input" i18nKeyPlaceholder={this.i18nKeyDetailName} name="key" - onBlur={this.handleBlur} + onFocus={this.handleFocus} + onBlur={this.handleKeyBlur} onChange={this.handleChange} - onReturnKeyDown={this.handleBlur} + onReturnKeyDown={this.handleKeyBlur} placeholder={this.detailNamePlaceholder} ref="keyInput" value={this.props.metafield.key} @@ -115,9 +133,10 @@ class Metafield extends Component { className="metafield-value-input" i18nKeyPlaceholder={this.i18nKeyDetailInformation} name="value" - onBlur={this.handleBlur} + onFocus={this.handleFocus} + onBlur={this.handleValueBlur} onChange={this.handleChange} - onReturnKeyDown={this.handleBlur} + onReturnKeyDown={this.handleValueBlur} placeholder={this.detailInfoPlaceholder} ref="valueInput" value={this.props.metafield.value} From ecfe21cb149fb2ed038fe00180110c849ea180fb Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Mon, 6 Aug 2018 13:21:50 -0400 Subject: [PATCH 05/65] (feat): replace tag item velocity animations w/ css --- .../core/ui/client/components/tags/tagItem.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/imports/plugins/core/ui/client/components/tags/tagItem.js b/imports/plugins/core/ui/client/components/tags/tagItem.js index 9da72a78ba2..1325cad2027 100644 --- a/imports/plugins/core/ui/client/components/tags/tagItem.js +++ b/imports/plugins/core/ui/client/components/tags/tagItem.js @@ -2,28 +2,25 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; import classnames from "classnames"; import Autosuggest from "react-autosuggest"; -import Velocity from "velocity-animate"; -import "velocity-animate/velocity.ui"; import { registerComponent } from "@reactioncommerce/reaction-components"; import { i18next } from "/client/api"; import { Button, Handle } from "/imports/plugins/core/ui/client/components"; import { SortableItem } from "/imports/plugins/core/ui/client/containers"; import { Router } from "@reactioncommerce/reaction-router"; +import { highlightInput } from "../../helpers/animations"; class TagItem extends Component { - componentWillReceiveProps(nextProps) { + componentDidUpdate(prevProps) { if (this._updated && this._saved && this.refs.autoSuggestInput) { const { input } = this.refs.autoSuggestInput; - Velocity.RunSequence([ - { e: input, p: { backgroundColor: "#e2f2e2" }, o: { duration: 200 } }, - { e: input, p: { backgroundColor: "#fff" }, o: { duration: 100 } } - ]); + highlightInput(input); + this._saved = false; this._updated = false; } - if ((nextProps.tag.name !== this.props.tag.name)) { + if (prevProps.tag.name !== this.props.tag.name) { this._updated = true; } } From 09c0e77a93c1e8b6285c7812898a128a07cde5d8 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Mon, 6 Aug 2018 13:22:16 -0400 Subject: [PATCH 06/65] (feat): replace admin input highlight animations w/ css --- .../checkout/client/containers/cartIconContainer.js | 1 - .../core/dashboard/client/components/actionView.js | 2 -- .../product-admin/client/components/productAdmin.js | 9 ++------- .../client/components/productField.js | 10 ++-------- .../product-variant/components/variantForm.js | 12 +++--------- 5 files changed, 7 insertions(+), 27 deletions(-) diff --git a/imports/plugins/core/checkout/client/containers/cartIconContainer.js b/imports/plugins/core/checkout/client/containers/cartIconContainer.js index ed58b667f5e..956eec1270a 100644 --- a/imports/plugins/core/checkout/client/containers/cartIconContainer.js +++ b/imports/plugins/core/checkout/client/containers/cartIconContainer.js @@ -1,4 +1,3 @@ -import Velocity from "velocity-animate"; import { compose, withProps } from "recompose"; import { registerComponent, composeWithTracker } from "@reactioncommerce/reaction-components"; import { Reaction } from "/client/api"; diff --git a/imports/plugins/core/dashboard/client/components/actionView.js b/imports/plugins/core/dashboard/client/components/actionView.js index 29d3c9ebd15..a608682b052 100644 --- a/imports/plugins/core/dashboard/client/components/actionView.js +++ b/imports/plugins/core/dashboard/client/components/actionView.js @@ -4,8 +4,6 @@ import classnames from "classnames"; import Blaze from "meteor/gadicc:blaze-react-component"; import { Admin } from "/imports/plugins/core/ui/client/providers"; import Radium from "radium"; -import "velocity-animate"; -import "velocity-animate/velocity.ui"; import { VelocityTransitionGroup } from "velocity-react"; import debounce from "lodash/debounce"; import { diff --git a/imports/plugins/included/product-admin/client/components/productAdmin.js b/imports/plugins/included/product-admin/client/components/productAdmin.js index 33a6731a2ef..eca224e48bb 100644 --- a/imports/plugins/included/product-admin/client/components/productAdmin.js +++ b/imports/plugins/included/product-admin/client/components/productAdmin.js @@ -1,11 +1,10 @@ import { isEqual } from "lodash"; import React, { Component } from "react"; import PropTypes from "prop-types"; -import Velocity from "velocity-animate"; -import "velocity-animate/velocity.ui"; import { Components } from "@reactioncommerce/reaction-components"; import { Router } from "/client/api"; import update from "immutability-helper"; +import { highlightInput } from "/imports/plugins/core/ui/client/helpers/animations"; const fieldNames = [ "title", @@ -97,11 +96,7 @@ class ProductAdmin extends Component { if (fieldRef) { const { input } = fieldRef.refs; - - Velocity.RunSequence([ - { e: input, p: { backgroundColor: "#e2f2e2" }, o: { duration: 200 } }, - { e: input, p: { backgroundColor: "#fff" }, o: { duration: 100 } } - ]); + highlightInput(input); } } diff --git a/imports/plugins/included/product-detail-simple/client/components/productField.js b/imports/plugins/included/product-detail-simple/client/components/productField.js index 78ceb24577f..f4c929410bf 100644 --- a/imports/plugins/included/product-detail-simple/client/components/productField.js +++ b/imports/plugins/included/product-detail-simple/client/components/productField.js @@ -1,11 +1,9 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; import classnames from "classnames"; -import Velocity from "velocity-animate"; -import "velocity-animate/velocity.ui"; import { Components, registerComponent } from "@reactioncommerce/reaction-components"; - import { Reaction } from "client/api"; +import { highlightInput } from "/imports/plugins/core/ui/client/helpers/animations"; class ProductField extends Component { state = { @@ -19,11 +17,7 @@ class ProductField extends Component { }, () => { if (this._input && this._input.refs.input) { const { input } = this._input.refs; - - Velocity.RunSequence([ - { e: input, p: { backgroundColor: "#e2f2e2" }, o: { duration: 200 } }, - { e: input, p: { backgroundColor: "#fff" }, o: { duration: 100 } } - ]); + highlightInput(input); } }); } else { diff --git a/imports/plugins/included/product-variant/components/variantForm.js b/imports/plugins/included/product-variant/components/variantForm.js index a32d299a1ed..c0084dda6ef 100644 --- a/imports/plugins/included/product-variant/components/variantForm.js +++ b/imports/plugins/included/product-variant/components/variantForm.js @@ -2,12 +2,12 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; import classnames from "classnames"; import _ from "lodash"; -import Velocity from "velocity-animate"; -import "velocity-animate/velocity.ui"; import { Components } from "@reactioncommerce/reaction-components"; import { findCurrency } from "/client/api"; +import { highlightVariantInput } from "/imports/plugins/core/ui/client/helpers/animations"; const fieldNames = [ + "optionTitle", "title", "originCountry", "compareAtPrice", @@ -107,13 +107,7 @@ class VariantForm extends Component { if (fieldRef) { const { input } = fieldRef.refs; - const isFieldValid = this.props.validation.isFieldValid(fieldName); - const flashColor = isFieldValid ? "#f0fff4" : "#ffeeef"; - - Velocity.RunSequence([ - { e: input, p: { backgroundColor: flashColor }, o: { duration: 200 } }, - { e: input, p: { backgroundColor: "#fff" }, o: { duration: 100 } } - ]); + highlightVariantInput(input); } } From 7540d2f489779d2647da58f30e0192241f4fa565 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Mon, 6 Aug 2018 13:22:52 -0400 Subject: [PATCH 07/65] (chore): remove velocity-animate from package.json --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 10b9cd1841a..a669d9abac3 100644 --- a/package.json +++ b/package.json @@ -131,7 +131,6 @@ "tether-tooltip": "^1.2.0", "transliteration": "github:reactioncommerce/transliteration", "url": "^0.11.0", - "velocity-animate": "^1.5.1", "velocity-react": "1.4.1" }, "devDependencies": { From bd1a957f0819d911e52217d8f3f4a92fb8bb5896 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Mon, 6 Aug 2018 17:27:53 -0400 Subject: [PATCH 08/65] (style): linting issues --- .../core/checkout/client/components/emptyCartDrawer.js | 8 +++++++- .../checkout/client/containers/cartDrawerContainer.js | 10 +++++++++- .../checkout/client/containers/cartIconContainer.js | 4 ++-- .../checkout/client/containers/cartPanelContainer.js | 1 - .../checkout/client/templates/cartDrawer/cartDrawer.js | 1 - .../checkout/client/templates/cartIcon/cartIcon.js | 1 - .../core/dashboard/client/components/actionView.js | 4 +++- .../core/ui/client/components/metadata/metafield.js | 1 - imports/plugins/core/ui/client/helpers/animations.js | 5 +++-- .../product-admin/client/components/productAdmin.js | 2 +- .../client/components/productField.js | 2 +- .../included/product-variant/components/variantForm.js | 4 +++- 12 files changed, 29 insertions(+), 14 deletions(-) diff --git a/imports/plugins/core/checkout/client/components/emptyCartDrawer.js b/imports/plugins/core/checkout/client/components/emptyCartDrawer.js index f1896ca8f29..ac04d09d994 100644 --- a/imports/plugins/core/checkout/client/components/emptyCartDrawer.js +++ b/imports/plugins/core/checkout/client/components/emptyCartDrawer.js @@ -1,9 +1,15 @@ import React from "react"; import PropTypes from "prop-types"; -import { $ } from "meteor/jquery"; import { Components, registerComponent } from "@reactioncommerce/reaction-components"; import { Reaction } from "/client/api"; +/** + * @name handleKeepShopping + * @private + * @summary Closes cart drawer so user can continue shopping + * @param {Object} event - JS event + * @returns {undefined} + */ function handleKeepShopping(event) { event.stopPropagation(); event.preventDefault(); diff --git a/imports/plugins/core/checkout/client/containers/cartDrawerContainer.js b/imports/plugins/core/checkout/client/containers/cartDrawerContainer.js index 067399ae319..4b003f7ffcd 100644 --- a/imports/plugins/core/checkout/client/containers/cartDrawerContainer.js +++ b/imports/plugins/core/checkout/client/containers/cartDrawerContainer.js @@ -36,6 +36,7 @@ const handlers = { } }); } + return ""; }, handleRemoveItem(event, item) { @@ -55,7 +56,14 @@ const handlers = { } }; -// reactive Tracker wrapped function +/** + * @name composer + * @private + * @summary Reactive Tracker wrapped function + * @param {Object} props - React props + * @param {Function} onData - Function to call when data is ready + * @returns {undefined} + */ function composer(props, onData) { const { cart } = getCart(); if (!cart) return; diff --git a/imports/plugins/core/checkout/client/containers/cartIconContainer.js b/imports/plugins/core/checkout/client/containers/cartIconContainer.js index 956eec1270a..729c94df856 100644 --- a/imports/plugins/core/checkout/client/containers/cartIconContainer.js +++ b/imports/plugins/core/checkout/client/containers/cartIconContainer.js @@ -5,8 +5,8 @@ import getCart from "/imports/plugins/core/cart/client/util/getCart"; import CartIcon from "../components/cartIcon"; const handlers = { - handleClick(e) { - e.preventDefault(); + handleClick(event) { + event.preventDefault(); document.querySelector("#cart-drawer-container").classList.toggle("opened"); Reaction.toggleSession("displayCart"); } diff --git a/imports/plugins/core/checkout/client/containers/cartPanelContainer.js b/imports/plugins/core/checkout/client/containers/cartPanelContainer.js index 9f83af7d872..fbb50872c77 100644 --- a/imports/plugins/core/checkout/client/containers/cartPanelContainer.js +++ b/imports/plugins/core/checkout/client/containers/cartPanelContainer.js @@ -1,6 +1,5 @@ import { withProps } from "recompose"; import { registerComponent } from "@reactioncommerce/reaction-components"; -import { $ } from "meteor/jquery"; import { Session } from "meteor/session"; import { Reaction } from "/client/api"; import CartPanel from "../components/cartPanel"; diff --git a/imports/plugins/core/checkout/client/templates/cartDrawer/cartDrawer.js b/imports/plugins/core/checkout/client/templates/cartDrawer/cartDrawer.js index 51be89dbc73..776aa83caf2 100644 --- a/imports/plugins/core/checkout/client/templates/cartDrawer/cartDrawer.js +++ b/imports/plugins/core/checkout/client/templates/cartDrawer/cartDrawer.js @@ -1,6 +1,5 @@ import Swiper from "swiper"; import { Components } from "@reactioncommerce/reaction-components"; -import { $ } from "meteor/jquery"; import { Session } from "meteor/session"; import { Template } from "meteor/templating"; import getCart from "/imports/plugins/core/cart/client/util/getCart"; diff --git a/imports/plugins/core/checkout/client/templates/cartIcon/cartIcon.js b/imports/plugins/core/checkout/client/templates/cartIcon/cartIcon.js index 1f43e67538b..1e5ff8097f8 100644 --- a/imports/plugins/core/checkout/client/templates/cartIcon/cartIcon.js +++ b/imports/plugins/core/checkout/client/templates/cartIcon/cartIcon.js @@ -1,4 +1,3 @@ -import { $ } from "meteor/jquery"; import { Template } from "meteor/templating"; import { Reaction } from "/client/api"; import getCart from "/imports/plugins/core/cart/client/util/getCart"; diff --git a/imports/plugins/core/dashboard/client/components/actionView.js b/imports/plugins/core/dashboard/client/components/actionView.js index a608682b052..6985db47d75 100644 --- a/imports/plugins/core/dashboard/client/components/actionView.js +++ b/imports/plugins/core/dashboard/client/components/actionView.js @@ -228,7 +228,7 @@ class ActionView extends Component { {React.createElement(component, this.props.actionView.data)} ); - } catch (e) { + } catch (error) { return (
); } + + return null; } renderActionView() { diff --git a/imports/plugins/core/ui/client/components/metadata/metafield.js b/imports/plugins/core/ui/client/components/metadata/metafield.js index f2cfd384db1..7b53e5c5ca2 100644 --- a/imports/plugins/core/ui/client/components/metadata/metafield.js +++ b/imports/plugins/core/ui/client/components/metadata/metafield.js @@ -68,7 +68,6 @@ class Metafield extends Component { } handleValueBlur = (event) => { - const valueInput = this.refs.valueInput.refs.input; if (this.state.inputOriginalValue !== valueInput.value) { diff --git a/imports/plugins/core/ui/client/helpers/animations.js b/imports/plugins/core/ui/client/helpers/animations.js index ab16d64412d..b5264b9a5ee 100644 --- a/imports/plugins/core/ui/client/helpers/animations.js +++ b/imports/plugins/core/ui/client/helpers/animations.js @@ -4,9 +4,10 @@ import { Meteor } from "meteor/meteor"; * @name highlightInput * @summary Uses a CSS animation to briefly highlight an input * @param {HTMLInputElement} inputRef - Input to animate + * @param {String} className - The class name to toggle that contains animation * @returns {undefined} */ -export function highlightInput (inputRef, className="highlight") { +export function highlightInput(inputRef, className = "highlight") { inputRef.classList.add(className); Meteor.setTimeout(() => { inputRef.classList.remove(className); @@ -19,6 +20,6 @@ export function highlightInput (inputRef, className="highlight") { * @param {HTMLInputElement} inputRef - Input to animate * @returns {undefined} */ -export function highlightVariantInput (inputRef) { +export function highlightVariantInput(inputRef) { highlightInput(inputRef, "highlight-variant"); } diff --git a/imports/plugins/included/product-admin/client/components/productAdmin.js b/imports/plugins/included/product-admin/client/components/productAdmin.js index eca224e48bb..c91296561ee 100644 --- a/imports/plugins/included/product-admin/client/components/productAdmin.js +++ b/imports/plugins/included/product-admin/client/components/productAdmin.js @@ -44,7 +44,7 @@ class ProductAdmin extends Component { }; } - componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps) { // eslint-disable-line camelcase const nextProduct = nextProps.product || {}; const currentProduct = this.props.product || {}; diff --git a/imports/plugins/included/product-detail-simple/client/components/productField.js b/imports/plugins/included/product-detail-simple/client/components/productField.js index f4c929410bf..263c531a96e 100644 --- a/imports/plugins/included/product-detail-simple/client/components/productField.js +++ b/imports/plugins/included/product-detail-simple/client/components/productField.js @@ -10,7 +10,7 @@ class ProductField extends Component { value: this.value } - componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps) { // eslint-disable-line camelcase if (nextProps.product[this.fieldName] !== this.props.product[this.fieldName]) { this.setState({ value: nextProps.product[this.fieldName] diff --git a/imports/plugins/included/product-variant/components/variantForm.js b/imports/plugins/included/product-variant/components/variantForm.js index c0084dda6ef..868c2cad4b8 100644 --- a/imports/plugins/included/product-variant/components/variantForm.js +++ b/imports/plugins/included/product-variant/components/variantForm.js @@ -52,7 +52,7 @@ class VariantForm extends Component { }; } - componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps) { // eslint-disable-line camelcase const nextVariant = nextProps.variant || {}; const currentVariant = this.props.variant || {}; @@ -251,6 +251,8 @@ class VariantForm extends Component {
); } + + return null; } renderInventoryPolicyField() { From ee871818dfeb754374782b56433e3b160d639bf5 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Tue, 7 Aug 2018 14:41:23 -0400 Subject: [PATCH 09/65] (perf): dynamically import VelocityTransitionGroup --- .../dashboard/client/components/actionView.js | 14 +++++++++++++- .../core/ui/client/components/cards/cardBody.js | 15 ++++++++++++++- .../core/ui/client/components/modal/overlay.js | 12 +++++++++++- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/imports/plugins/core/dashboard/client/components/actionView.js b/imports/plugins/core/dashboard/client/components/actionView.js index 6985db47d75..17bef9f184d 100644 --- a/imports/plugins/core/dashboard/client/components/actionView.js +++ b/imports/plugins/core/dashboard/client/components/actionView.js @@ -4,7 +4,6 @@ import classnames from "classnames"; import Blaze from "meteor/gadicc:blaze-react-component"; import { Admin } from "/imports/plugins/core/ui/client/providers"; import Radium from "radium"; -import { VelocityTransitionGroup } from "velocity-react"; import debounce from "lodash/debounce"; import { IconButton, @@ -163,6 +162,7 @@ class ActionView extends Component { super(props); this.state = { + VelocityTransitionGroup: undefined, isMobile: this.isMobile, enterAnimation: { animation: { translateX: 0 }, @@ -405,6 +405,7 @@ class ActionView extends Component { renderDetailView() { const { actionView } = this.props; + const { VelocityTransitionGroup } = this.state; const baseClassName = classnames({ "rui": true, @@ -450,6 +451,7 @@ class ActionView extends Component { } renderActionView() { + const { VelocityTransitionGroup } = this.state; const baseClassName = classnames({ "rui": true, "admin": true, @@ -488,6 +490,16 @@ class ActionView extends Component { } render() { + const { VelocityTransitionGroup } = this.state; + if (VelocityTransitionGroup === undefined) { + import("velocity-react").then((module) => { + this.setState({ + VelocityTransitionGroup: module.VelocityTransitionGroup + }); + }); + return null; + } + const isRtl = document.querySelector("html").className === "rtl"; return (
diff --git a/imports/plugins/core/ui/client/components/cards/cardBody.js b/imports/plugins/core/ui/client/components/cards/cardBody.js index 6a49de9ce56..7ced4cd5006 100644 --- a/imports/plugins/core/ui/client/components/cards/cardBody.js +++ b/imports/plugins/core/ui/client/components/cards/cardBody.js @@ -1,6 +1,5 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; -import { VelocityTransitionGroup } from "velocity-react"; import Radium from "radium"; import classnames from "classnames"; import { registerComponent } from "@reactioncommerce/reaction-components"; @@ -23,6 +22,10 @@ class CardBody extends Component { padded: PropTypes.bool // eslint-disable-line react/boolean-prop-naming }; + state = { + VelocityTransitionGroup: undefined + }; + renderCard() { if (this.props.expanded) { const baseClassName = classnames({ @@ -47,6 +50,16 @@ class CardBody extends Component { } render() { + const { VelocityTransitionGroup } = this.state; + if (VelocityTransitionGroup === undefined) { + import("velocity-react").then((module) => { + this.setState({ + VelocityTransitionGroup: module.VelocityTransitionGroup + }); + }); + return null; + } + return ( { + this.setState({ + VelocityTransitionGroup: module.VelocityTransitionGroup + }); + }); + return null; + } + return ( Date: Wed, 8 Aug 2018 10:39:03 +0530 Subject: [PATCH 10/65] (fix): email validation --- lib/api/account-validation.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/api/account-validation.js b/lib/api/account-validation.js index 7c2220cbd1f..f365be1ea33 100644 --- a/lib/api/account-validation.js +++ b/lib/api/account-validation.js @@ -50,13 +50,15 @@ const validationMethods = { check(email, Match.OptionalOrNull(String)); check(optional, Match.OptionalOrNull(Boolean)); - const processedEmail = email.trim(); + if(email) { + const processedEmail = email.trim(); - // Valid - if (optional === true && processedEmail.length === 0) { - return true; - } else if (processedEmail.indexOf("@") !== -1) { - return true; + // Valid + if (optional === true && processedEmail.length === 0) { + return true; + } else if (processedEmail.indexOf("@") !== -1) { + return true; + } } // Invalid From 5cc9c37df2b6e940f91971ea272b3ca7743e586c Mon Sep 17 00:00:00 2001 From: pardyot-s Date: Wed, 8 Aug 2018 11:10:41 +0530 Subject: [PATCH 11/65] lint fixed --- lib/api/account-validation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/api/account-validation.js b/lib/api/account-validation.js index f365be1ea33..fb3701dc278 100644 --- a/lib/api/account-validation.js +++ b/lib/api/account-validation.js @@ -50,7 +50,7 @@ const validationMethods = { check(email, Match.OptionalOrNull(String)); check(optional, Match.OptionalOrNull(Boolean)); - if(email) { + if (email) { const processedEmail = email.trim(); // Valid From 65ac3948761d3a5d17ceccbdb53621cf58c122c1 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Wed, 8 Aug 2018 17:10:29 -0400 Subject: [PATCH 12/65] (style): correct eslint issues --- .../dashboard/client/components/actionView.js | 17 +++++++++++------ .../core/ui/client/components/cards/cardBody.js | 16 +++++++++++----- .../core/ui/client/components/modal/overlay.js | 14 ++++++++++---- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/imports/plugins/core/dashboard/client/components/actionView.js b/imports/plugins/core/dashboard/client/components/actionView.js index 17bef9f184d..f09e4576f7c 100644 --- a/imports/plugins/core/dashboard/client/components/actionView.js +++ b/imports/plugins/core/dashboard/client/components/actionView.js @@ -1,6 +1,8 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; import classnames from "classnames"; +import Logger from "@reactioncommerce/logger"; +import { getComponent } from "@reactioncommerce/reaction-components"; import Blaze from "meteor/gadicc:blaze-react-component"; import { Admin } from "/imports/plugins/core/ui/client/providers"; import Radium from "radium"; @@ -10,8 +12,6 @@ import { Translation, Overlay } from "/imports/plugins/core/ui/client/components"; -import { getComponent } from "@reactioncommerce/reaction-components"; - const getStyles = (props) => { const minWidth = Math.min(props.viewportWidth, 400); @@ -492,11 +492,16 @@ class ActionView extends Component { render() { const { VelocityTransitionGroup } = this.state; if (VelocityTransitionGroup === undefined) { - import("velocity-react").then((module) => { - this.setState({ - VelocityTransitionGroup: module.VelocityTransitionGroup + import("velocity-react") + .then((module) => { + this.setState({ + VelocityTransitionGroup: module.VelocityTransitionGroup + }); + return module; + }) + .catch((error) => { + Logger.error(error.message, "Unable to load velocity-react"); }); - }); return null; } diff --git a/imports/plugins/core/ui/client/components/cards/cardBody.js b/imports/plugins/core/ui/client/components/cards/cardBody.js index 7ced4cd5006..81db94df59d 100644 --- a/imports/plugins/core/ui/client/components/cards/cardBody.js +++ b/imports/plugins/core/ui/client/components/cards/cardBody.js @@ -2,6 +2,7 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; import Radium from "radium"; import classnames from "classnames"; +import Logger from "@reactioncommerce/logger"; import { registerComponent } from "@reactioncommerce/reaction-components"; const styles = { @@ -52,14 +53,19 @@ class CardBody extends Component { render() { const { VelocityTransitionGroup } = this.state; if (VelocityTransitionGroup === undefined) { - import("velocity-react").then((module) => { - this.setState({ - VelocityTransitionGroup: module.VelocityTransitionGroup + import("velocity-react") + .then((module) => { + this.setState({ + VelocityTransitionGroup: module.VelocityTransitionGroup + }); + return module; + }) + .catch((error) => { + Logger.error(error.message, "Unable to load velocity-react"); }); - }); return null; } - + return ( { - this.setState({ - VelocityTransitionGroup: module.VelocityTransitionGroup + import("velocity-react") + .then((module) => { + this.setState({ + VelocityTransitionGroup: module.VelocityTransitionGroup + }); + return module; + }) + .catch((error) => { + Logger.error(error.message, "Unable to load velocity-react"); }); - }); return null; } From a630ce332bc0f34d27db41132c8b1d872d503b5b Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Thu, 9 Aug 2018 10:22:50 -0400 Subject: [PATCH 13/65] (style): better comment for cart drawer container's composer function --- .../core/checkout/client/containers/cartDrawerContainer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imports/plugins/core/checkout/client/containers/cartDrawerContainer.js b/imports/plugins/core/checkout/client/containers/cartDrawerContainer.js index 4b003f7ffcd..f1e362a8710 100644 --- a/imports/plugins/core/checkout/client/containers/cartDrawerContainer.js +++ b/imports/plugins/core/checkout/client/containers/cartDrawerContainer.js @@ -59,7 +59,7 @@ const handlers = { /** * @name composer * @private - * @summary Reactive Tracker wrapped function + * @summary Subscribes to images for products in cart & passes cart products to CartDrawer * @param {Object} props - React props * @param {Function} onData - Function to call when data is ready * @returns {undefined} From 82761cc83636e089ff01ff8d8af4eaa4fafeb6bf Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Thu, 9 Aug 2018 16:35:24 -0400 Subject: [PATCH 14/65] (perf): replace VelocityTransitionGroup with CSSTransitionGroup and AnimateHeight --- .../dashboard/client/components/actionView.js | 93 +++++-------------- .../ui/client/components/cards/cardBody.js | 59 +++++------- .../ui/client/components/modal/overlay.js | 28 +++--- .../client/styles/dashboard/console.less | 28 +++++- .../default-theme/client/styles/main.less | 1 + .../default-theme/client/styles/overlay.less | 14 +++ 6 files changed, 105 insertions(+), 118 deletions(-) create mode 100644 imports/plugins/included/default-theme/client/styles/overlay.less diff --git a/imports/plugins/core/dashboard/client/components/actionView.js b/imports/plugins/core/dashboard/client/components/actionView.js index f09e4576f7c..adea9d946d0 100644 --- a/imports/plugins/core/dashboard/client/components/actionView.js +++ b/imports/plugins/core/dashboard/client/components/actionView.js @@ -63,7 +63,6 @@ const getStyles = (props) => { "flex": "0 0 auto", "backgroundColor": "white", "overflow": "hidden", - "transition": "width 300ms cubic-bezier(0.455, 0.03, 0.515, 0.955))", "zIndex": 1050, "@media only screen and (max-width: 949px)": { width: "auto", @@ -162,28 +161,8 @@ class ActionView extends Component { super(props); this.state = { - VelocityTransitionGroup: undefined, + CSSTransitionGroup: undefined, isMobile: this.isMobile, - enterAnimation: { - animation: { translateX: 0 }, - duration: 200, - easing: "easeInOutQuad" - }, - leaveAnimation: { - animation: { translateX: 400 }, - duration: 200, - easing: "easeInOutQuad" - }, - rtlEnterAnimation: { - animation: { translateX: ["0%", "-100%"] }, - duration: 200, - easing: "easeInOutQuad" - }, - rtlLeaveAnimation: { - animation: { translateX: "-100%" }, - duration: 200, - easing: "easeInOutQuad" - }, enterAnimationForDetailView: { animation: { width: 400 }, duration: 200, @@ -348,31 +327,6 @@ class ActionView extends Component { return getStyles(this.props); } - get backButtonEnterAnimation() { - return { - animation: { - display: "flex", - position: "absolute", - left: 20, - opacity: 1 - }, - duration: 200 - }; - } - - get backButtonLeaveAnimation() { - return { - animation: { - display: "flex", - position: "absolute", - left: -30, - opacity: 0 - }, - duration: 200 - - }; - } - renderMasterView() { const { actionView } = this.props; @@ -405,7 +359,6 @@ class ActionView extends Component { renderDetailView() { const { actionView } = this.props; - const { VelocityTransitionGroup } = this.state; const baseClassName = classnames({ "rui": true, @@ -416,14 +369,9 @@ class ActionView extends Component { if (this.props.detailViewIsOpen) { return ( -
+
- - {this.renderDetailViewBackButton()} - + {this.renderDetailViewBackButton()}

@@ -451,7 +399,6 @@ class ActionView extends Component { } renderActionView() { - const { VelocityTransitionGroup } = this.state; const baseClassName = classnames({ "rui": true, "admin": true, @@ -461,20 +408,23 @@ class ActionView extends Component { }); if (this.props.actionViewIsOpen) { + const isRtl = document.querySelector("html").className === "rtl"; + return ( -
+
{this.renderMasterView()} - {this.renderDetailView()} - +
@@ -490,31 +440,32 @@ class ActionView extends Component { } render() { - const { VelocityTransitionGroup } = this.state; - if (VelocityTransitionGroup === undefined) { - import("velocity-react") + const { CSSTransitionGroup } = this.state; + if (CSSTransitionGroup === undefined) { + import("react-transition-group") .then((module) => { this.setState({ - VelocityTransitionGroup: module.VelocityTransitionGroup + CSSTransitionGroup: module.CSSTransitionGroup }); return module; }) .catch((error) => { - Logger.error(error.message, "Unable to load velocity-react"); + Logger.error(error.message, "Unable to load react-transition-group"); }); + return null; } const isRtl = document.querySelector("html").className === "rtl"; return (
- {this.renderActionView()} - - + - {this.props.children} -
- ); - } - - return null; - } - render() { - const { VelocityTransitionGroup } = this.state; - if (VelocityTransitionGroup === undefined) { - import("velocity-react") + const { AnimateHeight } = this.state; + if (AnimateHeight === undefined) { + import("react-animate-height") .then((module) => { this.setState({ - VelocityTransitionGroup: module.VelocityTransitionGroup + AnimateHeight: module.default }); return module; }) .catch((error) => { - Logger.error(error.message, "Unable to load velocity-react"); + Logger.error(error.message, "Unable to load react-animate-height"); }); return null; } + const baseClassName = classnames({ + "rui": true, + "panel-body": true, + "no-padding": this.props.padded === false + }); + const height = this.props.expanded && "auto" || 0; + return ( - - {this.renderCard()} - +
+ {this.props.children} +
+ ); } } diff --git a/imports/plugins/core/ui/client/components/modal/overlay.js b/imports/plugins/core/ui/client/components/modal/overlay.js index 6e2d8c28d06..c70d0302059 100644 --- a/imports/plugins/core/ui/client/components/modal/overlay.js +++ b/imports/plugins/core/ui/client/components/modal/overlay.js @@ -30,7 +30,7 @@ class Overlay extends Component { }; state = { - VelocityTransitionGroup: undefined, + CSSTransitionGroup: undefined, enterAnimation: { animation: { opacity: 1 }, duration: 200 @@ -44,7 +44,8 @@ class Overlay extends Component { renderOverlay() { if (this.props.isVisible) { const baseClassName = classnames({ - rui: true + rui: true, + overlay: true }); return ( @@ -53,6 +54,7 @@ class Overlay extends Component { className={baseClassName} style={styles.base} onClick={this.props.onClick} + key="overlay" /> ); } @@ -61,28 +63,30 @@ class Overlay extends Component { } render() { - const { VelocityTransitionGroup } = this.state; - if (VelocityTransitionGroup === undefined) { - import("velocity-react") + const { CSSTransitionGroup } = this.state; + if (CSSTransitionGroup === undefined) { + import("react-transition-group") .then((module) => { this.setState({ - VelocityTransitionGroup: module.VelocityTransitionGroup + CSSTransitionGroup: module.CSSTransitionGroup }); return module; }) .catch((error) => { - Logger.error(error.message, "Unable to load velocity-react"); + Logger.error(error.message, "Unable to load react-transition-group"); }); + return null; } - + return ( - {this.renderOverlay()} - + ); } } diff --git a/imports/plugins/included/default-theme/client/styles/dashboard/console.less b/imports/plugins/included/default-theme/client/styles/dashboard/console.less index 73b9d2433dc..bd4f46b0010 100644 --- a/imports/plugins/included/default-theme/client/styles/dashboard/console.less +++ b/imports/plugins/included/default-theme/client/styles/dashboard/console.less @@ -63,7 +63,9 @@ html:not(.rtl) .rui.admin.action-view { flex: 0 0 auto; flex-direction: column; align-items: center; - // justify-content: center; + // Disable horizontal scrollbar, and vertical unless needed + overflow-x: hidden; + overflow-y: auto; min-width: @admin-controls-menu-width; width: @admin-controls-menu-width; @@ -79,6 +81,30 @@ html:not(.rtl) .rui.admin.action-view { } } +// Admin action pane slide in/out animation +.admin.rui.action-view-pane { + &.slide-in-out-enter { + transform: translateX(100%); + } + &.slide-in-out-rtl-enter { + transform: translateX(-100%); + } + &.slide-in-out-enter-active, &.slide-in-out-rtl-enter-active { + transform: translateX(0%); + transition: transform 200ms ease-in-out; + } + &.slide-in-out-leave-active { + transform: translateX(100%); + transition: transform 200ms ease-in-out; + } + &.slide-in-out-rtl-leave-active { + transform: translateX(-100%); + transition: transform 200ms ease-in-out; + } +} + +// Admin detail pane (i.e. order detail) sidebar slide in/out animation + .admin-shortcut-bar nav { flex-direction: column; diff --git a/imports/plugins/included/default-theme/client/styles/main.less b/imports/plugins/included/default-theme/client/styles/main.less index 35aed6a3b3d..cf723321f02 100644 --- a/imports/plugins/included/default-theme/client/styles/main.less +++ b/imports/plugins/included/default-theme/client/styles/main.less @@ -85,6 +85,7 @@ @import "navbar.less"; @import "numericInput.less"; @import "navs.less"; +@import "overlay.less"; @import "panels.less"; @import "popover.less"; @import "popovers.less"; diff --git a/imports/plugins/included/default-theme/client/styles/overlay.less b/imports/plugins/included/default-theme/client/styles/overlay.less new file mode 100644 index 00000000000..8adcc4fda4c --- /dev/null +++ b/imports/plugins/included/default-theme/client/styles/overlay.less @@ -0,0 +1,14 @@ +// Overlay opacity animation +.rui.overlay { + &.fade-in-out-enter { + opacity: 0; + } + &.fade-in-out-enter-active { + opacity: 1; + transition: opacity 200ms; + } + &.fade-in-out-leave-active { + opacity: 0; + transition: opacity 200ms; + } +} From 76220a7aa01b2acf34578faa8b0ce94ff441f476 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Thu, 9 Aug 2018 16:43:15 -0400 Subject: [PATCH 15/65] (fix): CSSTransitionGroup undefined --- imports/plugins/core/dashboard/client/components/actionView.js | 1 + 1 file changed, 1 insertion(+) diff --git a/imports/plugins/core/dashboard/client/components/actionView.js b/imports/plugins/core/dashboard/client/components/actionView.js index adea9d946d0..242dce632e3 100644 --- a/imports/plugins/core/dashboard/client/components/actionView.js +++ b/imports/plugins/core/dashboard/client/components/actionView.js @@ -399,6 +399,7 @@ class ActionView extends Component { } renderActionView() { + const { CSSTransitionGroup } = this.state; const baseClassName = classnames({ "rui": true, "admin": true, From 840a2878760682aadbd85f281cded91a21e1a612 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Fri, 10 Aug 2018 10:28:08 -0400 Subject: [PATCH 16/65] (feat): Animations with dynamically imported HOCs - CSSTransitionGroup + VelocityTransitionGroup --- imports/plugins/core/components/lib/hoc.js | 48 ++++++++++++ .../dashboard/client/components/actionView.js | 29 +++---- .../ui/client/components/cards/cardBody.js | 78 ++++++++++--------- .../ui/client/components/modal/overlay.js | 31 ++++---- .../client/components/productAdmin.js | 7 +- package.json | 1 + 6 files changed, 121 insertions(+), 73 deletions(-) diff --git a/imports/plugins/core/components/lib/hoc.js b/imports/plugins/core/components/lib/hoc.js index b23059cf28c..6687b728691 100644 --- a/imports/plugins/core/components/lib/hoc.js +++ b/imports/plugins/core/components/lib/hoc.js @@ -177,3 +177,51 @@ export function withIsOwner(component) { onData(null, { isOwner: Reaction.hasOwnerAccess() }); })(component); } + +/** + * @name withCSSTransitionGroup + * @method + * @summary A wrapper to reactively inject react-transition-group's into a component + * @param {Function|React.Component} component - the component to wrap + * @memberof Components/Helpers + */ +export function withCSSTransitionGroup(component) { + return lifecycle({ + componentDidMount() { + import("react-transition-group") + .then((module) => { + this.setState({ + CSSTransitionGroup: module.CSSTransitionGroup + }); + return null; + }) + .catch((error) => { + Logger.error(error.message, "Unable to load react-transition-group"); + }); + } + })(component); +} + +/** + * @name withVelocityTransitionGroup + * @method + * @summary A wrapper to reactively inject velocity-react's into a component + * @param {Function|React.Component} component - the component to wrap + * @memberof Components/Helpers + */ +export function withVelocityTransitionGroup(component) { + return lifecycle({ + componentDidMount() { + import("velocity-react") + .then((module) => { + this.setState({ + VelocityTransitionGroup: module.VelocityTransitionGroup + }); + return null; + }) + .catch((error) => { + Logger.error(error.message, "Unable to load velocty-react"); + }); + } + })(component); +} diff --git a/imports/plugins/core/dashboard/client/components/actionView.js b/imports/plugins/core/dashboard/client/components/actionView.js index 242dce632e3..7deaa544503 100644 --- a/imports/plugins/core/dashboard/client/components/actionView.js +++ b/imports/plugins/core/dashboard/client/components/actionView.js @@ -1,8 +1,8 @@ import React, { Component } from "react"; +import { compose } from "recompose"; import PropTypes from "prop-types"; import classnames from "classnames"; -import Logger from "@reactioncommerce/logger"; -import { getComponent } from "@reactioncommerce/reaction-components"; +import { getComponent, withCSSTransitionGroup } from "@reactioncommerce/reaction-components"; import Blaze from "meteor/gadicc:blaze-react-component"; import { Admin } from "/imports/plugins/core/ui/client/providers"; import Radium from "radium"; @@ -145,6 +145,7 @@ class ActionView extends Component { actionView: PropTypes.object, actionViewIsOpen: PropTypes.bool, // eslint-disable-line react/boolean-prop-naming buttons: PropTypes.array, + CSSTransitionGroup: PropTypes.func, detailView: PropTypes.object, detailViewIsOpen: PropTypes.bool, // eslint-disable-line react/boolean-prop-naming handleActionViewBack: PropTypes.func, @@ -161,7 +162,6 @@ class ActionView extends Component { super(props); this.state = { - CSSTransitionGroup: undefined, isMobile: this.isMobile, enterAnimationForDetailView: { animation: { width: 400 }, @@ -399,7 +399,8 @@ class ActionView extends Component { } renderActionView() { - const { CSSTransitionGroup } = this.state; + const { CSSTransitionGroup } = this.props; + const baseClassName = classnames({ "rui": true, "admin": true, @@ -441,19 +442,8 @@ class ActionView extends Component { } render() { - const { CSSTransitionGroup } = this.state; + const { CSSTransitionGroup } = this.props; if (CSSTransitionGroup === undefined) { - import("react-transition-group") - .then((module) => { - this.setState({ - CSSTransitionGroup: module.CSSTransitionGroup - }); - return module; - }) - .catch((error) => { - Logger.error(error.message, "Unable to load react-transition-group"); - }); - return null; } @@ -476,4 +466,9 @@ class ActionView extends Component { } } -export default Admin()(Radium(ActionView)); +export default Admin()( + compose( + withCSSTransitionGroup, + Radium + )(ActionView) +); diff --git a/imports/plugins/core/ui/client/components/cards/cardBody.js b/imports/plugins/core/ui/client/components/cards/cardBody.js index c08ea40db53..22c4a2b5879 100644 --- a/imports/plugins/core/ui/client/components/cards/cardBody.js +++ b/imports/plugins/core/ui/client/components/cards/cardBody.js @@ -1,9 +1,9 @@ import React, { Component } from "react"; +import { compose } from "recompose"; import PropTypes from "prop-types"; import Radium from "radium"; import classnames from "classnames"; -import Logger from "@reactioncommerce/logger"; -import { registerComponent } from "@reactioncommerce/reaction-components"; +import { registerComponent, withVelocityTransitionGroup } from "@reactioncommerce/reaction-components"; const styles = { noPadding: { @@ -20,41 +20,23 @@ class CardBody extends Component { static propTypes = { children: PropTypes.node, expanded: PropTypes.bool, // eslint-disable-line react/boolean-prop-naming - padded: PropTypes.bool // eslint-disable-line react/boolean-prop-naming + padded: PropTypes.bool, // eslint-disable-line react/boolean-prop-naming + VelocityTransitionGroup: PropTypes.func }; - state = { - AnimateHeight: undefined - }; - - render() { - const { AnimateHeight } = this.state; - if (AnimateHeight === undefined) { - import("react-animate-height") - .then((module) => { - this.setState({ - AnimateHeight: module.default - }); - return module; - }) - .catch((error) => { - Logger.error(error.message, "Unable to load react-animate-height"); - }); - return null; - } + constructor(props) { + super(props); + } - const baseClassName = classnames({ - "rui": true, - "panel-body": true, - "no-padding": this.props.padded === false - }); - const height = this.props.expanded && "auto" || 0; + renderCard() { + if (this.props.expanded) { + const baseClassName = classnames({ + "rui": true, + "panel-body": true, + "no-padding": this.props.padded === false + }); - return ( - + return (
{this.props.children}
-
+ ); + } + + return null; + } + + render() { + const { VelocityTransitionGroup } = this.props; + if (VelocityTransitionGroup === undefined) { + return null; + } + + return ( + + {this.renderCard()} + ); } } -registerComponent("CardBody", CardBody, Radium); +registerComponent("CardBody", CardBody, [ + withVelocityTransitionGroup, + Radium +]); -export default Radium(CardBody); +export default compose( + withVelocityTransitionGroup, + Radium +)(CardBody); diff --git a/imports/plugins/core/ui/client/components/modal/overlay.js b/imports/plugins/core/ui/client/components/modal/overlay.js index c70d0302059..a60f10b0676 100644 --- a/imports/plugins/core/ui/client/components/modal/overlay.js +++ b/imports/plugins/core/ui/client/components/modal/overlay.js @@ -1,9 +1,9 @@ import React, { Component } from "react"; +import { compose } from "recompose"; import PropTypes from "prop-types"; import Radium from "radium"; import classnames from "classnames"; -import { registerComponent } from "@reactioncommerce/reaction-components"; -import Logger from "@reactioncommerce/logger"; +import { registerComponent, withCSSTransitionGroup } from "@reactioncommerce/reaction-components"; const styles = { base: { @@ -25,12 +25,12 @@ class Overlay extends Component { static propTypes = { children: PropTypes.node, + CSSTransitionGroup: PropTypes.func, isVisible: PropTypes.bool, onClick: PropTypes.func }; state = { - CSSTransitionGroup: undefined, enterAnimation: { animation: { opacity: 1 }, duration: 200 @@ -63,22 +63,11 @@ class Overlay extends Component { } render() { - const { CSSTransitionGroup } = this.state; + const { CSSTransitionGroup } = this.props; if (CSSTransitionGroup === undefined) { - import("react-transition-group") - .then((module) => { - this.setState({ - CSSTransitionGroup: module.CSSTransitionGroup - }); - return module; - }) - .catch((error) => { - Logger.error(error.message, "Unable to load react-transition-group"); - }); - return null; } - + return ( Date: Fri, 10 Aug 2018 10:44:43 -0400 Subject: [PATCH 17/65] (refactor): cleanup css animation code --- .../client/styles/dashboard/console.less | 26 +++++++++---------- .../default-theme/client/styles/overlay.less | 14 +++++----- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/imports/plugins/included/default-theme/client/styles/dashboard/console.less b/imports/plugins/included/default-theme/client/styles/dashboard/console.less index bd4f46b0010..5310583abe3 100644 --- a/imports/plugins/included/default-theme/client/styles/dashboard/console.less +++ b/imports/plugins/included/default-theme/client/styles/dashboard/console.less @@ -83,27 +83,27 @@ html:not(.rtl) .rui.admin.action-view { // Admin action pane slide in/out animation .admin.rui.action-view-pane { - &.slide-in-out-enter { - transform: translateX(100%); - } - &.slide-in-out-rtl-enter { - transform: translateX(-100%); - } - &.slide-in-out-enter-active, &.slide-in-out-rtl-enter-active { - transform: translateX(0%); - transition: transform 200ms ease-in-out; - } + &.slide-in-out-enter, &.slide-in-out-leave-active { transform: translateX(100%); - transition: transform 200ms ease-in-out; } + &.slide-in-out-rtl-enter, &.slide-in-out-rtl-leave-active { transform: translateX(-100%); + } + + &.slide-in-out-enter-active, + &.slide-in-out-rtl-enter-active, + &.slide-in-out-leave-active, + &.slide-in-out-rtl-leave-active { transition: transform 200ms ease-in-out; } -} -// Admin detail pane (i.e. order detail) sidebar slide in/out animation + &.slide-in-out-enter-active, + &.slide-in-out-rtl-enter-active { + transform: translateX(0%); + } +} .admin-shortcut-bar nav { flex-direction: column; diff --git a/imports/plugins/included/default-theme/client/styles/overlay.less b/imports/plugins/included/default-theme/client/styles/overlay.less index 8adcc4fda4c..e12fbb9a322 100644 --- a/imports/plugins/included/default-theme/client/styles/overlay.less +++ b/imports/plugins/included/default-theme/client/styles/overlay.less @@ -1,14 +1,16 @@ // Overlay opacity animation .rui.overlay { - &.fade-in-out-enter { + &.fade-in-out-enter, + &.fade-in-out-leave-active { opacity: 0; } - &.fade-in-out-enter-active { - opacity: 1; - transition: opacity 200ms; - } + + &.fade-in-out-enter-active, &.fade-in-out-leave-active { - opacity: 0; transition: opacity 200ms; } + + &.fade-in-out-enter-active { + opacity: 1; + } } From 70c9d7496a3777078e358d943997676356b91df7 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Fri, 10 Aug 2018 11:02:35 -0400 Subject: [PATCH 18/65] (fix): prevent 'can't set state on unmounted component' error on animation HOCs --- imports/plugins/core/components/lib/hoc.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/imports/plugins/core/components/lib/hoc.js b/imports/plugins/core/components/lib/hoc.js index 6687b728691..db324334e5b 100644 --- a/imports/plugins/core/components/lib/hoc.js +++ b/imports/plugins/core/components/lib/hoc.js @@ -190,14 +190,23 @@ export function withCSSTransitionGroup(component) { componentDidMount() { import("react-transition-group") .then((module) => { + if (this.willUnmount == true) { + return null; + } + this.setState({ CSSTransitionGroup: module.CSSTransitionGroup }); + return null; }) .catch((error) => { Logger.error(error.message, "Unable to load react-transition-group"); }); + }, + componentWillUnmount() { + // Prevent dynamic import from setting state if component is about to unmount + this.willUnmount = true; } })(component); } @@ -214,14 +223,23 @@ export function withVelocityTransitionGroup(component) { componentDidMount() { import("velocity-react") .then((module) => { + if (this.willUnmount == true) { + return null; + } + this.setState({ VelocityTransitionGroup: module.VelocityTransitionGroup }); + return null; }) .catch((error) => { Logger.error(error.message, "Unable to load velocty-react"); }); + }, + componentWillUnmount() { + // Prevent dynamic import from setting state if component is about to unmount + this.willUnmount = true; } })(component); } From 54a45cf95ae3f1a98fbef9b8e5a50c9f694a205e Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Fri, 10 Aug 2018 12:24:22 -0400 Subject: [PATCH 19/65] (fix): sidebar unable to be opened, edge condition fix --- .../containers/productGridContainer.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/imports/plugins/included/product-variant/containers/productGridContainer.js b/imports/plugins/included/product-variant/containers/productGridContainer.js index 7412dcb7134..62acd563b90 100644 --- a/imports/plugins/included/product-variant/containers/productGridContainer.js +++ b/imports/plugins/included/product-variant/containers/productGridContainer.js @@ -39,14 +39,15 @@ const wrapComponent = (Comp) => ( const selectedProducts = Reaction.getUserPreferences("reaction-product-variant", "selectedGridItems"); const { products } = this; - if (_.isEmpty(selectedProducts)) { + if (Array.isArray(selectedProducts) && _.isEmpty(selectedProducts)) { + Reaction.setUserPreferences("reaction-product-variant", "selectedGridItems", undefined); return Reaction.hideActionView(); } - // Save the selected items to the Session - Session.set("productGrid/selectedProducts", _.uniq(selectedProducts)); - if (products) { + if (products && selectedProducts) { + // Save the selected items to the Session + Session.set("productGrid/selectedProducts", _.uniq(selectedProducts)); const filteredProducts = products.filter((product) => selectedProducts.includes(product._id)); if (Reaction.isPreview() === false) { @@ -80,12 +81,11 @@ const wrapComponent = (Comp) => ( Reaction.setUserPreferences("reaction-product-variant", "selectedGridItems", selectedProducts); - // Save the selected items to the Session - Session.set("productGrid/selectedProducts", _.uniq(selectedProducts)); - const { products } = this; - if (products) { + if (products && selectedProducts) { + // Save the selected items to the Session + Session.set("productGrid/selectedProducts", _.uniq(selectedProducts)); const filteredProducts = products.filter((product) => selectedProducts.includes(product._id)); Reaction.showActionView({ From f9d42a51b007a668260c3e9750dc43711bf80a06 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Fri, 10 Aug 2018 12:43:08 -0400 Subject: [PATCH 20/65] (style): correct linting issues --- imports/plugins/core/components/lib/hoc.js | 10 +++++++--- .../dashboard/client/components/actionView.js | 16 +++++++--------- .../core/ui/client/components/cards/cardBody.js | 8 ++------ .../core/ui/client/components/modal/overlay.js | 2 +- .../containers/productGridContainer.js | 13 +++++++++++-- 5 files changed, 28 insertions(+), 21 deletions(-) diff --git a/imports/plugins/core/components/lib/hoc.js b/imports/plugins/core/components/lib/hoc.js index db324334e5b..c081539c6a3 100644 --- a/imports/plugins/core/components/lib/hoc.js +++ b/imports/plugins/core/components/lib/hoc.js @@ -159,6 +159,8 @@ export function withPermissions({ roles = ["guest", "anonymous"], group }) { onData(null, { hasPermissions }); } + + return null; }); } @@ -183,6 +185,7 @@ export function withIsOwner(component) { * @method * @summary A wrapper to reactively inject react-transition-group's into a component * @param {Function|React.Component} component - the component to wrap + * @return {Function} the new wrapped component with a "CSSTransitionGroup" prop * @memberof Components/Helpers */ export function withCSSTransitionGroup(component) { @@ -190,7 +193,7 @@ export function withCSSTransitionGroup(component) { componentDidMount() { import("react-transition-group") .then((module) => { - if (this.willUnmount == true) { + if (this.willUnmount === true) { return null; } @@ -216,6 +219,7 @@ export function withCSSTransitionGroup(component) { * @method * @summary A wrapper to reactively inject velocity-react's into a component * @param {Function|React.Component} component - the component to wrap + * @return {Function} the new wrapped component with a "VelocityTransitionGroup" prop * @memberof Components/Helpers */ export function withVelocityTransitionGroup(component) { @@ -223,14 +227,14 @@ export function withVelocityTransitionGroup(component) { componentDidMount() { import("velocity-react") .then((module) => { - if (this.willUnmount == true) { + if (this.willUnmount === true) { return null; } this.setState({ VelocityTransitionGroup: module.VelocityTransitionGroup }); - + return null; }) .catch((error) => { diff --git a/imports/plugins/core/dashboard/client/components/actionView.js b/imports/plugins/core/dashboard/client/components/actionView.js index 7deaa544503..005b4e14ca4 100644 --- a/imports/plugins/core/dashboard/client/components/actionView.js +++ b/imports/plugins/core/dashboard/client/components/actionView.js @@ -142,10 +142,10 @@ const getStyles = (props) => { class ActionView extends Component { static propTypes = { + CSSTransitionGroup: PropTypes.func, actionView: PropTypes.object, actionViewIsOpen: PropTypes.bool, // eslint-disable-line react/boolean-prop-naming buttons: PropTypes.array, - CSSTransitionGroup: PropTypes.func, detailView: PropTypes.object, detailViewIsOpen: PropTypes.bool, // eslint-disable-line react/boolean-prop-naming handleActionViewBack: PropTypes.func, @@ -421,7 +421,7 @@ class ActionView extends Component { onClick={this.props.handleActionViewDetailClose} /> @@ -451,7 +451,7 @@ class ActionView extends Component { return (
@@ -466,9 +466,7 @@ class ActionView extends Component { } } -export default Admin()( - compose( - withCSSTransitionGroup, - Radium - )(ActionView) -); +export default Admin()(compose( + withCSSTransitionGroup, + Radium +)(ActionView)); diff --git a/imports/plugins/core/ui/client/components/cards/cardBody.js b/imports/plugins/core/ui/client/components/cards/cardBody.js index 22c4a2b5879..7d2a94730e6 100644 --- a/imports/plugins/core/ui/client/components/cards/cardBody.js +++ b/imports/plugins/core/ui/client/components/cards/cardBody.js @@ -18,16 +18,12 @@ class CardBody extends Component { }; static propTypes = { + VelocityTransitionGroup: PropTypes.func, children: PropTypes.node, expanded: PropTypes.bool, // eslint-disable-line react/boolean-prop-naming - padded: PropTypes.bool, // eslint-disable-line react/boolean-prop-naming - VelocityTransitionGroup: PropTypes.func + padded: PropTypes.bool // eslint-disable-line react/boolean-prop-naming }; - constructor(props) { - super(props); - } - renderCard() { if (this.props.expanded) { const baseClassName = classnames({ diff --git a/imports/plugins/core/ui/client/components/modal/overlay.js b/imports/plugins/core/ui/client/components/modal/overlay.js index a60f10b0676..0303c3ff17c 100644 --- a/imports/plugins/core/ui/client/components/modal/overlay.js +++ b/imports/plugins/core/ui/client/components/modal/overlay.js @@ -24,8 +24,8 @@ class Overlay extends Component { }; static propTypes = { - children: PropTypes.node, CSSTransitionGroup: PropTypes.func, + children: PropTypes.node, isVisible: PropTypes.bool, onClick: PropTypes.func }; diff --git a/imports/plugins/included/product-variant/containers/productGridContainer.js b/imports/plugins/included/product-variant/containers/productGridContainer.js index 62acd563b90..53d7c76d2e1 100644 --- a/imports/plugins/included/product-variant/containers/productGridContainer.js +++ b/imports/plugins/included/product-variant/containers/productGridContainer.js @@ -35,7 +35,7 @@ const wrapComponent = (Comp) => ( }; } - componentWillMount() { + UNSAFE_componentWillMount() { // eslint-disable-line camelcase const selectedProducts = Reaction.getUserPreferences("reaction-product-variant", "selectedGridItems"); const { products } = this; @@ -60,9 +60,11 @@ const wrapComponent = (Comp) => ( }); } } + + return null; } - componentWillReceiveProps = (nextProps) => { + UNSAFE_componentWillReceiveProps = (nextProps) => { // eslint-disable-line camelcase this.setState({ products: nextProps.products, productIds: nextProps.productIds, @@ -174,6 +176,13 @@ const wrapComponent = (Comp) => ( } ); +/** + * @name composer + * @summary Builds productMediaById object and passes to child component + * @param {Object} props - Props passed down from parent components + * @param {Function} onData - Callback to execute with props + * @returns {undefined} + */ function composer(props, onData) { // Instantiate an object for use as a map. This object does not inherit prototype or methods from `Object` const productMediaById = Object.create(null); From e4d8a8101caffd38f3e87521e9e1174bec384ae5 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Tue, 14 Aug 2018 12:58:16 -0400 Subject: [PATCH 21/65] (perf): Use react-animate-height to animate admin sidebar height --- imports/plugins/core/components/lib/hoc.js | 14 ++--- .../ui/client/components/cards/cardBody.js | 58 ++++++++----------- package.json | 4 +- 3 files changed, 33 insertions(+), 43 deletions(-) diff --git a/imports/plugins/core/components/lib/hoc.js b/imports/plugins/core/components/lib/hoc.js index c081539c6a3..76928bbc2df 100644 --- a/imports/plugins/core/components/lib/hoc.js +++ b/imports/plugins/core/components/lib/hoc.js @@ -215,30 +215,30 @@ export function withCSSTransitionGroup(component) { } /** - * @name withVelocityTransitionGroup + * @name withAnimateHeight * @method - * @summary A wrapper to reactively inject velocity-react's into a component + * @summary A wrapper to reactively inject react-animate-height's into a component * @param {Function|React.Component} component - the component to wrap - * @return {Function} the new wrapped component with a "VelocityTransitionGroup" prop + * @return {Function} the new wrapped component with a "AnimateHeight" prop * @memberof Components/Helpers */ -export function withVelocityTransitionGroup(component) { +export function withAnimateHeight(component) { return lifecycle({ componentDidMount() { - import("velocity-react") + import("react-animate-height") .then((module) => { if (this.willUnmount === true) { return null; } this.setState({ - VelocityTransitionGroup: module.VelocityTransitionGroup + AnimateHeight: module.default }); return null; }) .catch((error) => { - Logger.error(error.message, "Unable to load velocty-react"); + Logger.error(error.message, "Unable to load react-animate-height"); }); }, componentWillUnmount() { diff --git a/imports/plugins/core/ui/client/components/cards/cardBody.js b/imports/plugins/core/ui/client/components/cards/cardBody.js index 7d2a94730e6..4b42d5006b2 100644 --- a/imports/plugins/core/ui/client/components/cards/cardBody.js +++ b/imports/plugins/core/ui/client/components/cards/cardBody.js @@ -3,7 +3,7 @@ import { compose } from "recompose"; import PropTypes from "prop-types"; import Radium from "radium"; import classnames from "classnames"; -import { registerComponent, withVelocityTransitionGroup } from "@reactioncommerce/reaction-components"; +import { registerComponent, withAnimateHeight } from "@reactioncommerce/reaction-components"; const styles = { noPadding: { @@ -18,58 +18,48 @@ class CardBody extends Component { }; static propTypes = { - VelocityTransitionGroup: PropTypes.func, + AnimateHeight: PropTypes.func, children: PropTypes.node, expanded: PropTypes.bool, // eslint-disable-line react/boolean-prop-naming padded: PropTypes.bool // eslint-disable-line react/boolean-prop-naming }; - renderCard() { - if (this.props.expanded) { - const baseClassName = classnames({ - "rui": true, - "panel-body": true, - "no-padding": this.props.padded === false - }); - - return ( -
- {this.props.children} -
- ); - } - - return null; - } - render() { - const { VelocityTransitionGroup } = this.props; - if (VelocityTransitionGroup === undefined) { + const { AnimateHeight } = this.props; + if (!AnimateHeight) { return null; } + const baseClassName = classnames({ + "rui": true, + "panel-body": true, + "no-padding": this.props.padded === false + }); + const height = this.props.expanded && "auto" || 0; return ( - +
- {this.renderCard()} - + {this.props.children} +
+
); } } registerComponent("CardBody", CardBody, [ - withVelocityTransitionGroup, + withAnimateHeight, Radium ]); export default compose( - withVelocityTransitionGroup, + withAnimateHeight, Radium )(CardBody); diff --git a/package.json b/package.json index d5d737630d0..b056f8a51f8 100644 --- a/package.json +++ b/package.json @@ -97,6 +97,7 @@ "radium": "^0.22.0", "ramda": "^0.25.0", "react": "16.4.2", + "react-animate-height": "2.0.3", "react-apollo": "2.1.9", "react-autosuggest": "^9.3.3", "react-avatar": "^2.5.1", @@ -137,8 +138,7 @@ "tether-drop": "^1.4.2", "tether-tooltip": "^1.2.0", "transliteration": "github:reactioncommerce/transliteration", - "url": "^0.11.0", - "velocity-react": "1.4.1" + "url": "^0.11.0" }, "devDependencies": { "@babel/cli": "7.0.0-beta.49", From 4a2a7dc3c564b1fd875b93a67313fa758d768c5c Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Tue, 14 Aug 2018 13:00:38 -0400 Subject: [PATCH 22/65] (style): correct linting issues --- .../ui/client/components/cards/cardBody.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/imports/plugins/core/ui/client/components/cards/cardBody.js b/imports/plugins/core/ui/client/components/cards/cardBody.js index 4b42d5006b2..adff09378c0 100644 --- a/imports/plugins/core/ui/client/components/cards/cardBody.js +++ b/imports/plugins/core/ui/client/components/cards/cardBody.js @@ -34,21 +34,21 @@ class CardBody extends Component { "panel-body": true, "no-padding": this.props.padded === false }); - const height = this.props.expanded && "auto" || 0; + const height = (this.props.expanded && "auto") || 0; return ( -
- {this.props.children} -
+
+ {this.props.children} +
); } From ac55955c60e7917d674c704372fbe78173d01128 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Thu, 16 Aug 2018 17:18:08 -0400 Subject: [PATCH 23/65] fix: don't highlight pdp metadata field twice (after enter key, then leaving input) --- imports/plugins/core/ui/client/components/metadata/metafield.js | 1 + 1 file changed, 1 insertion(+) diff --git a/imports/plugins/core/ui/client/components/metadata/metafield.js b/imports/plugins/core/ui/client/components/metadata/metafield.js index 7b53e5c5ca2..92596997329 100644 --- a/imports/plugins/core/ui/client/components/metadata/metafield.js +++ b/imports/plugins/core/ui/client/components/metadata/metafield.js @@ -64,6 +64,7 @@ class Metafield extends Component { }; highlightInput = (inputRef) => { + this.setState({ inputOriginalValue: inputRef.value }); highlightInput(inputRef); } From 71f627334a0de19346727e5ea8c0b09cea9abf0c Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Fri, 17 Aug 2018 10:24:35 -0400 Subject: [PATCH 24/65] fix: can't setState error on AnimateHeight --- imports/plugins/core/components/lib/hoc.js | 25 ++++++++++++++++--- .../ui/client/components/cards/cardBody.js | 1 + 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/imports/plugins/core/components/lib/hoc.js b/imports/plugins/core/components/lib/hoc.js index 76928bbc2df..d668b1ae282 100644 --- a/imports/plugins/core/components/lib/hoc.js +++ b/imports/plugins/core/components/lib/hoc.js @@ -1,4 +1,5 @@ import _ from "lodash"; +import React, { Component } from "react"; import Logger from "@reactioncommerce/logger"; import { Meteor } from "meteor/meteor"; import { Roles } from "meteor/alanning:roles"; @@ -217,7 +218,8 @@ export function withCSSTransitionGroup(component) { /** * @name withAnimateHeight * @method - * @summary A wrapper to reactively inject react-animate-height's into a component + * @summary A wrapper that reactively injects an extended version of react-animate-height's + * into a component. * @param {Function|React.Component} component - the component to wrap * @return {Function} the new wrapped component with a "AnimateHeight" prop * @memberof Components/Helpers @@ -227,12 +229,29 @@ export function withAnimateHeight(component) { componentDidMount() { import("react-animate-height") .then((module) => { - if (this.willUnmount === true) { + if (this.willUnmount) { return null; } + // Extend AnimateHeight so that setState can't be called when component is unmounted (prevents memory leak) + const AnimateHeight = module.default; + class ExtendedAnimateHeight extends AnimateHeight { + componentWillUnmount() { + this.willUnmount = true; + super.componentWillUnmount(); + } + + setState(partialState, callback) { + if (this.willUnmount) { + return; + } + super.setState(partialState, callback); + } + } + + // Pass extended AnimateHeight to child component this.setState({ - AnimateHeight: module.default + AnimateHeight: ExtendedAnimateHeight }); return null; diff --git a/imports/plugins/core/ui/client/components/cards/cardBody.js b/imports/plugins/core/ui/client/components/cards/cardBody.js index adff09378c0..4a970ee32a7 100644 --- a/imports/plugins/core/ui/client/components/cards/cardBody.js +++ b/imports/plugins/core/ui/client/components/cards/cardBody.js @@ -29,6 +29,7 @@ class CardBody extends Component { if (!AnimateHeight) { return null; } + const baseClassName = classnames({ "rui": true, "panel-body": true, From 043a735aadfae641ba4b0c94f337a89d429d856c Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Fri, 17 Aug 2018 10:46:56 -0400 Subject: [PATCH 25/65] style: correct eslint issues --- imports/plugins/core/components/lib/hoc.js | 1 - imports/plugins/core/ui/client/components/cards/cardBody.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/imports/plugins/core/components/lib/hoc.js b/imports/plugins/core/components/lib/hoc.js index d668b1ae282..9082a254e96 100644 --- a/imports/plugins/core/components/lib/hoc.js +++ b/imports/plugins/core/components/lib/hoc.js @@ -1,5 +1,4 @@ import _ from "lodash"; -import React, { Component } from "react"; import Logger from "@reactioncommerce/logger"; import { Meteor } from "meteor/meteor"; import { Roles } from "meteor/alanning:roles"; diff --git a/imports/plugins/core/ui/client/components/cards/cardBody.js b/imports/plugins/core/ui/client/components/cards/cardBody.js index 4a970ee32a7..752b43f04c8 100644 --- a/imports/plugins/core/ui/client/components/cards/cardBody.js +++ b/imports/plugins/core/ui/client/components/cards/cardBody.js @@ -29,7 +29,7 @@ class CardBody extends Component { if (!AnimateHeight) { return null; } - + const baseClassName = classnames({ "rui": true, "panel-body": true, From d6a405855b81b180084fae1bf144d6bae732c2a4 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Tue, 21 Aug 2018 10:05:42 -0400 Subject: [PATCH 26/65] feat: make all graphql media URLs absolute --- .../resolvers/catalog/CatalogProduct/index.js | 5 +++- .../server/no-meteor/resolvers/xforms/cart.js | 2 ++ .../resolvers/xforms/catalogProduct.js | 24 ++++++++++--------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js b/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js index 0d7e009c4f8..cf1e88d1b5c 100644 --- a/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js +++ b/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js @@ -1,6 +1,7 @@ import { encodeCatalogProductOpaqueId } from "@reactioncommerce/reaction-graphql-xforms/catalogProduct"; import { encodeProductOpaqueId } from "@reactioncommerce/reaction-graphql-xforms/product"; import { resolveShopFromShopId } from "@reactioncommerce/reaction-graphql-utils"; +import { xformProductMedia } from "../../xforms/catalogProduct"; import pricing from "./pricing"; import tagIds from "./tagIds"; import tags from "./tags"; @@ -11,5 +12,7 @@ export default { shop: resolveShopFromShopId, pricing, tagIds, - tags + tags, + media: (node) => node.media.map(mediaItem => xformProductMedia(mediaItem)), + primaryImage: (node) => xformProductMedia(node.primaryImage) }; diff --git a/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/cart.js b/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/cart.js index d71441a66f3..489abf1d296 100644 --- a/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/cart.js +++ b/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/cart.js @@ -3,6 +3,7 @@ import ReactionError from "@reactioncommerce/reaction-error"; import findVariantInCatalogProduct from "/imports/plugins/core/catalog/server/no-meteor/utils/findVariantInCatalogProduct"; import { assocInternalId, assocOpaqueId, decodeOpaqueIdForNamespace, encodeOpaqueId } from "./id"; import { decodeProductOpaqueId } from "./product"; +import { xformProductMedia } from "./catalogProduct"; export const assocCartInternalId = assocInternalId(namespaces.Cart); export const assocCartOpaqueId = assocOpaqueId(namespaces.Cart); @@ -68,6 +69,7 @@ function xformCartItem(catalogItems, products, cartItem) { if (catalogProduct.media) { media = catalogProduct.media.find((mediaItem) => mediaItem.variantId === variantId); if (!media) [media] = catalogProduct.media; + media = xformProductMedia(media); } const variantSourceProduct = products.find((product) => product._id === variantId); diff --git a/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js b/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js index aef60296d62..69d9db044e9 100644 --- a/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js +++ b/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js @@ -1,4 +1,5 @@ import { namespaces } from "@reactioncommerce/reaction-graphql-utils"; +import Reaction from "/imports/plugins/core/core/server/Reaction"; import { encodeProductOpaqueId } from "./product"; import { assocInternalId, assocOpaqueId, decodeOpaqueIdForNamespace, encodeOpaqueId } from "./id"; @@ -17,19 +18,20 @@ export const encodeCatalogProductOpaqueId = encodeOpaqueId(namespaces.CatalogPro export function xformProductMedia(mediaItem) { if (!mediaItem) return null; - const { metadata, large, medium, image, small, thumbnail } = mediaItem; - + const { priority, toGrid, productId, variantId, URLs: { large, medium, original, small, thumbnail } } = mediaItem; + const absoluteUrl = Reaction.absoluteUrl().slice(0, -1); + return { - priority: metadata && metadata.priority, - toGrid: metadata && metadata.toGrid, - productId: encodeProductOpaqueId(metadata && metadata.productId), - variantId: encodeProductOpaqueId(metadata && metadata.variantId), + priority: priority, + toGrid: toGrid, + productId: encodeProductOpaqueId(productId), + variantId: encodeProductOpaqueId(variantId), URLs: { - large, - medium, - original: image, - small, - thumbnail + large: `${absoluteUrl}${large}`, + medium: `${absoluteUrl}${medium}`, + original: `${absoluteUrl}${original}`, + small: `${absoluteUrl}${small}`, + thumbnail: `${absoluteUrl}${thumbnail}` } }; } From dbb8f145511ec74a35acb3b54eaf6b841587e8ac Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Tue, 21 Aug 2018 17:12:58 -0400 Subject: [PATCH 27/65] fix: use process.env.ROOT_URL --- .../server/no-meteor/resolvers/xforms/catalogProduct.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js b/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js index 69d9db044e9..955931ca30a 100644 --- a/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js +++ b/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js @@ -1,5 +1,4 @@ import { namespaces } from "@reactioncommerce/reaction-graphql-utils"; -import Reaction from "/imports/plugins/core/core/server/Reaction"; import { encodeProductOpaqueId } from "./product"; import { assocInternalId, assocOpaqueId, decodeOpaqueIdForNamespace, encodeOpaqueId } from "./id"; @@ -19,8 +18,8 @@ export function xformProductMedia(mediaItem) { if (!mediaItem) return null; const { priority, toGrid, productId, variantId, URLs: { large, medium, original, small, thumbnail } } = mediaItem; - const absoluteUrl = Reaction.absoluteUrl().slice(0, -1); - + const absoluteUrl = process.env.ROOT_URL.slice(0, -1); + return { priority: priority, toGrid: toGrid, From 1714f4cfaafe355318bdefefea4579b51d21b2d4 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Tue, 21 Aug 2018 17:15:56 -0400 Subject: [PATCH 28/65] chore: fix eslint issues --- .../no-meteor/resolvers/catalog/CatalogProduct/index.js | 2 +- .../server/no-meteor/resolvers/xforms/catalogProduct.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js b/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js index cf1e88d1b5c..55cb657a5ed 100644 --- a/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js +++ b/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js @@ -13,6 +13,6 @@ export default { pricing, tagIds, tags, - media: (node) => node.media.map(mediaItem => xformProductMedia(mediaItem)), + media: (node) => node.media.map((mediaItem) => xformProductMedia(mediaItem)), primaryImage: (node) => xformProductMedia(node.primaryImage) }; diff --git a/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js b/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js index 955931ca30a..c5e985501f8 100644 --- a/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js +++ b/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js @@ -12,7 +12,7 @@ export const encodeCatalogProductOpaqueId = encodeOpaqueId(namespaces.CatalogPro * @method * @memberof GraphQL/Transforms * @param {Object} mediaItem object from a catalog product - * @return transformed product media array + * @return {Object} transformed product media item */ export function xformProductMedia(mediaItem) { if (!mediaItem) return null; @@ -21,8 +21,8 @@ export function xformProductMedia(mediaItem) { const absoluteUrl = process.env.ROOT_URL.slice(0, -1); return { - priority: priority, - toGrid: toGrid, + priority, + toGrid, productId: encodeProductOpaqueId(productId), variantId: encodeProductOpaqueId(variantId), URLs: { From 4ca6910d774cf82a3bc82919323f0cf6d65a826b Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Wed, 22 Aug 2018 15:05:06 -0400 Subject: [PATCH 29/65] feat: pass request object w/ headers and user to buildContext + getAbsoluteUrl() added to context --- .../accounts/server/methods/addressBookAdd.js | 2 +- .../core/cart/server/methods/addToCart.js | 3 ++- .../core/cart/server/methods/createCart.js | 2 +- .../core/cart/server/methods/mergeCart.js | 2 +- .../cart/server/methods/removeFromCart.js | 2 +- .../cart/server/methods/setShipmentAddress.js | 2 +- .../cart/server/methods/setShipmentMethod.js | 2 +- .../catalog/server/methods/publishProducts.js | 2 +- .../__snapshots__/getAbsoluteUrl.test.js.snap | 5 ++++ .../core/core/server/util/getAbsoluteUrl.js | 26 +++++++++++++++++++ .../core/server/util/getAbsoluteUrl.test.js | 26 +++++++++++++++++++ .../server/getGraphQLContextInMeteorMethod.js | 16 ++++++++++-- .../graphql/server/no-meteor/buildContext.js | 13 ++++++++-- .../server/no-meteor/buildContext.test.js | 10 ++++--- .../server/no-meteor/createApolloServer.js | 2 +- .../resolvers/catalog/CatalogProduct/index.js | 4 +-- .../resolvers/xforms/catalogProduct.js | 5 ++-- .../server/methods/updateShipmentQuotes.js | 2 +- 18 files changed, 104 insertions(+), 22 deletions(-) create mode 100644 imports/plugins/core/core/server/util/__snapshots__/getAbsoluteUrl.test.js.snap create mode 100644 imports/plugins/core/core/server/util/getAbsoluteUrl.js create mode 100644 imports/plugins/core/core/server/util/getAbsoluteUrl.test.js diff --git a/imports/plugins/core/accounts/server/methods/addressBookAdd.js b/imports/plugins/core/accounts/server/methods/addressBookAdd.js index d2143ef67fb..4a963481396 100644 --- a/imports/plugins/core/accounts/server/methods/addressBookAdd.js +++ b/imports/plugins/core/accounts/server/methods/addressBookAdd.js @@ -22,6 +22,6 @@ export default function addressBookAdd(address, accountUserId, cartId) { this.unblock(); - const context = Promise.await(getGraphQLContextInMeteorMethod(Meteor.userId())); + const context = Promise.await(getGraphQLContextInMeteorMethod(Meteor.userId(), this.connection)); return addressBookAddMutation(context, address, accountUserId); } diff --git a/imports/plugins/core/cart/server/methods/addToCart.js b/imports/plugins/core/cart/server/methods/addToCart.js index 0ba277dc3a9..973b51df348 100644 --- a/imports/plugins/core/cart/server/methods/addToCart.js +++ b/imports/plugins/core/cart/server/methods/addToCart.js @@ -35,7 +35,8 @@ export default function addToCart(cartId, token, items) { const anonymousUser = Roles.userIsInRole(userId, "anonymous", shopId); const userIdForContext = anonymousUser ? null : userId; - const context = Promise.await(getGraphQLContextInMeteorMethod(userIdForContext)); + const context = Promise.await(getGraphQLContextInMeteorMethod(userIdForContext, this.connection)); + const { cart: updatedCart, incorrectPriceFailures, diff --git a/imports/plugins/core/cart/server/methods/createCart.js b/imports/plugins/core/cart/server/methods/createCart.js index 63f93172be1..0e821dc333a 100644 --- a/imports/plugins/core/cart/server/methods/createCart.js +++ b/imports/plugins/core/cart/server/methods/createCart.js @@ -28,7 +28,7 @@ export default function createCartMethod(items) { const anonymousUser = Roles.userIsInRole(userId, "anonymous", shopId); const userIdForContext = anonymousUser ? null : userId; - const context = Promise.await(getGraphQLContextInMeteorMethod(userIdForContext)); + const context = Promise.await(getGraphQLContextInMeteorMethod(userIdForContext, this.connection)); const result = Promise.await(createCart(context, { items, shopId })); const { cart } = result; diff --git a/imports/plugins/core/cart/server/methods/mergeCart.js b/imports/plugins/core/cart/server/methods/mergeCart.js index 30fd5671152..1a490d16d0a 100644 --- a/imports/plugins/core/cart/server/methods/mergeCart.js +++ b/imports/plugins/core/cart/server/methods/mergeCart.js @@ -20,7 +20,7 @@ export default function mergeCart(anonymousCartId, anonymousCartToken) { check(anonymousCartToken, String); // Pass through to the new mutation function at this point - const context = Promise.await(getGraphQLContextInMeteorMethod(Meteor.userId())); + const context = Promise.await(getGraphQLContextInMeteorMethod(Meteor.userId(), this.connection)); const { cart } = Promise.await(reconcileCarts(context, { anonymousCartId, anonymousCartToken, diff --git a/imports/plugins/core/cart/server/methods/removeFromCart.js b/imports/plugins/core/cart/server/methods/removeFromCart.js index 1c5d56f3b6f..6dcaca54248 100644 --- a/imports/plugins/core/cart/server/methods/removeFromCart.js +++ b/imports/plugins/core/cart/server/methods/removeFromCart.js @@ -43,7 +43,7 @@ export default function removeFromCart(cartId, cartToken, cartItemId, quantityDe } // Pass through to the new mutation function - const context = Promise.await(getGraphQLContextInMeteorMethod(account.userId)); + const context = Promise.await(getGraphQLContextInMeteorMethod(account.userId, this.connection)); const { cart: updatedCart } = Promise.await(updateCartItemsQuantity(context, { cartId: cart._id, items: [ diff --git a/imports/plugins/core/cart/server/methods/setShipmentAddress.js b/imports/plugins/core/cart/server/methods/setShipmentAddress.js index aae9c7dfbb9..3a5a5d2a0da 100644 --- a/imports/plugins/core/cart/server/methods/setShipmentAddress.js +++ b/imports/plugins/core/cart/server/methods/setShipmentAddress.js @@ -33,7 +33,7 @@ export default function setShipmentAddress(cartId, cartToken, address) { const anonymousUser = Roles.userIsInRole(userId, "anonymous", shopId); const userIdForContext = anonymousUser ? null : userId; - const context = Promise.await(getGraphQLContextInMeteorMethod(userIdForContext)); + const context = Promise.await(getGraphQLContextInMeteorMethod(userIdForContext, this.connection)); const result = Promise.await(setShippingAddressOnCart(context, { address, cartId, diff --git a/imports/plugins/core/cart/server/methods/setShipmentMethod.js b/imports/plugins/core/cart/server/methods/setShipmentMethod.js index c1cf8811558..dac501fb4f6 100644 --- a/imports/plugins/core/cart/server/methods/setShipmentMethod.js +++ b/imports/plugins/core/cart/server/methods/setShipmentMethod.js @@ -41,7 +41,7 @@ export default function setShipmentMethod(cartId, cartToken, methodId) { const anonymousUser = Roles.userIsInRole(userId, "anonymous", shopId); const userIdForContext = anonymousUser ? null : userId; - const context = Promise.await(getGraphQLContextInMeteorMethod(userIdForContext)); + const context = Promise.await(getGraphQLContextInMeteorMethod(userIdForContext, this.connection)); const result = Promise.await(selectFulfillmentOptionForGroup(context, { cartId, cartToken, diff --git a/imports/plugins/core/catalog/server/methods/publishProducts.js b/imports/plugins/core/catalog/server/methods/publishProducts.js index 99394c913d4..2c5238b5859 100644 --- a/imports/plugins/core/catalog/server/methods/publishProducts.js +++ b/imports/plugins/core/catalog/server/methods/publishProducts.js @@ -6,7 +6,7 @@ import publishProductsMutation from "../no-meteor/mutations/publishProducts"; Meteor.methods({ "catalog/publish/products"(productIds) { check(productIds, [String]); - const context = Promise.await(getGraphQLContextInMeteorMethod(this.userId)); + const context = Promise.await(getGraphQLContextInMeteorMethod(this.userId, this.connection)); return publishProductsMutation(context, productIds); } }); diff --git a/imports/plugins/core/core/server/util/__snapshots__/getAbsoluteUrl.test.js.snap b/imports/plugins/core/core/server/util/__snapshots__/getAbsoluteUrl.test.js.snap new file mode 100644 index 00000000000..c34d61e97f4 --- /dev/null +++ b/imports/plugins/core/core/server/util/__snapshots__/getAbsoluteUrl.test.js.snap @@ -0,0 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`throws error if no host header is provided 1`] = `"getAbsoluteUrl requires 'host' from request headers"`; + +exports[`throws error if no origin header is provided 1`] = `"getAbsoluteUrl requires 'origin' from request headers"`; diff --git a/imports/plugins/core/core/server/util/getAbsoluteUrl.js b/imports/plugins/core/core/server/util/getAbsoluteUrl.js new file mode 100644 index 00000000000..05adc2cf2bf --- /dev/null +++ b/imports/plugins/core/core/server/util/getAbsoluteUrl.js @@ -0,0 +1,26 @@ +import url from "url"; +import ReactionError from "@reactioncommerce/reaction-error"; + +/** + * @name getAbsoluteUrl + * @summary Returns the absolute/base URL for where a request was made, using the request's headers + * @param {Object} requestHeaders Headers sent with original request + * @param {String} requestHeaders.host Domain (including port) request was made to + * @param {String} requestHeaders.origin URL request originated from + * @returns {String} protocol://hostname[:port]/, with protocol being from the origin URL + */ +export default function getAbsoluteUrl(requestHeaders) { + const { host = "", origin = "" } = requestHeaders; + + if (host === "") { + throw new ReactionError("invalid-parameters", "getAbsoluteUrl requires 'host' from request headers"); + } + if (origin === "") { + throw new ReactionError("invalid-parameters", "getAbsoluteUrl requires 'origin' from request headers"); + } + + const originParsedUrl = url.parse(origin); + const { protocol } = originParsedUrl; + + return `${protocol}//${host}/`; +} diff --git a/imports/plugins/core/core/server/util/getAbsoluteUrl.test.js b/imports/plugins/core/core/server/util/getAbsoluteUrl.test.js new file mode 100644 index 00000000000..15d229a2ae2 --- /dev/null +++ b/imports/plugins/core/core/server/util/getAbsoluteUrl.test.js @@ -0,0 +1,26 @@ +import getAbsoluteUrl from "./getAbsoluteUrl"; + +test("throws error if no host header is provided", () => { + const requestHeaders = { + origin: "http://localhost:3000" + }; + + expect(() => getAbsoluteUrl(requestHeaders)).toThrowErrorMatchingSnapshot(); +}); + +test("throws error if no origin header is provided", () => { + const requestHeaders = { + host: "localhost:3000" + }; + + expect(() => getAbsoluteUrl(requestHeaders)).toThrowErrorMatchingSnapshot(); +}); + +test("returns the correct absolute url", () => { + const requestHeaders = { + origin: "http://localhost:3000", + host: "localhost:3000" + }; + + expect(getAbsoluteUrl(requestHeaders)).toBe("http://localhost:3000/"); +}); diff --git a/imports/plugins/core/graphql/server/getGraphQLContextInMeteorMethod.js b/imports/plugins/core/graphql/server/getGraphQLContextInMeteorMethod.js index 109ef87918e..755cd987b05 100644 --- a/imports/plugins/core/graphql/server/getGraphQLContextInMeteorMethod.js +++ b/imports/plugins/core/graphql/server/getGraphQLContextInMeteorMethod.js @@ -1,4 +1,5 @@ import { Meteor } from "meteor/meteor"; +import Reaction from "/imports/plugins/core/core/server/Reaction"; import appEvents from "/imports/plugins/core/core/server/appEvents"; import buildContext from "./no-meteor/buildContext"; import collections from "/imports/collections/rawCollections"; @@ -17,9 +18,10 @@ import collections from "/imports/collections/rawCollections"; * @summary Call this in a Meteor method that wraps a GraphQL mutation, to get a context * to pass to the mutation. * @param {String} userId - The user ID for the current request + * @param {Object} methodConnection - this.connection called from within a Meteor method * @return {Object} A GraphQL context object */ -export default async function getGraphQLContextInMeteorMethod(userId) { +export default async function getGraphQLContextInMeteorMethod(userId, methodConnection) { let user; if (userId) { user = await collections.users.findOne({ _id: userId }); @@ -27,7 +29,17 @@ export default async function getGraphQLContextInMeteorMethod(userId) { } const meteorContext = { appEvents, collections }; - await buildContext(meteorContext, user); + + const { httpHeaders } = methodConnection; + const request = { + user, + headers: { + origin: Reaction.absoluteUrl().slice(0, -1), + ...httpHeaders + } + }; + + await buildContext(meteorContext, request); // Since getGraphQLContextInMeteorMethod is to be called within a Meteor method with Meteor running, // we can pass through callMeteorMethod to Meteor.apply. diff --git a/imports/plugins/core/graphql/server/no-meteor/buildContext.js b/imports/plugins/core/graphql/server/no-meteor/buildContext.js index a89119f3745..2d3afa8cb0c 100644 --- a/imports/plugins/core/graphql/server/no-meteor/buildContext.js +++ b/imports/plugins/core/graphql/server/no-meteor/buildContext.js @@ -1,5 +1,6 @@ import { getHasPermissionFunctionForUser } from "/imports/plugins/core/accounts/server/no-meteor/hasPermission"; import getShopIdForContext from "/imports/plugins/core/accounts/server/no-meteor/getShopIdForContext"; +import getAbsoluteUrl from "/imports/plugins/core/core/server/util/getAbsoluteUrl"; import mutations from "./mutations"; import queries from "./queries"; @@ -10,10 +11,16 @@ import queries from "./queries"; * @summary Mutates the provided context object, adding `user`, `userId`, `shopId`, and * `userHasPermission` properties. * @param {Object} context - A context object on which to set additional context properties - * @param {Object} [user] - The user who authenticated this request, if applicable + * @param {Object} request - Request object + * @param {Object} request.headers - HTTP headers on request + * @param {String} request.headers.origin - Where the request originated from + * @param {String} request.headers.host - Host request was made for + * @param {Object} [request.user] - The user who authenticated this request, if applicable * @returns {undefined} No return */ -export default async function buildContext(context, user) { +export default async function buildContext(context, request) { + const { user, headers } = request; + context.mutations = mutations; context.queries = queries; @@ -32,4 +39,6 @@ export default async function buildContext(context, user) { // Add a curried hasPermission tied to the current user (or to no user) context.userHasPermission = getHasPermissionFunctionForUser(context.user); + + context.getAbsoluteUrl = () => getAbsoluteUrl(headers); } diff --git a/imports/plugins/core/graphql/server/no-meteor/buildContext.test.js b/imports/plugins/core/graphql/server/no-meteor/buildContext.test.js index 4bd7fa20fd5..e85cfa77fcd 100644 --- a/imports/plugins/core/graphql/server/no-meteor/buildContext.test.js +++ b/imports/plugins/core/graphql/server/no-meteor/buildContext.test.js @@ -9,7 +9,7 @@ const fakeUser = { test("properly mutates the context object without user", async () => { const context = { collections: mockContext.collections }; - await buildContext(context, undefined); + await buildContext(context, { user: undefined }); expect(context).toEqual({ collections: mockContext.collections, mutations, @@ -17,7 +17,8 @@ test("properly mutates the context object without user", async () => { shopId: null, user: null, userHasPermission: jasmine.any(Function), - userId: null + userId: null, + getAbsoluteUrl: jasmine.any(Function) }); }); @@ -26,7 +27,7 @@ test("properly mutates the context object with user", async () => { mockContext.collections.Accounts.findOne.mockReturnValueOnce(Promise.resolve(mockAccount)); const context = { collections: mockContext.collections }; - await buildContext(context, fakeUser); + await buildContext(context, { user: fakeUser }); expect(context).toEqual({ account: mockAccount, accountId: mockAccount._id, @@ -36,7 +37,8 @@ test("properly mutates the context object with user", async () => { shopId: null, user: fakeUser, userHasPermission: jasmine.any(Function), - userId: fakeUser._id + userId: fakeUser._id, + getAbsoluteUrl: jasmine.any(Function) }); // Make sure the hasPermission currying works with one arg diff --git a/imports/plugins/core/graphql/server/no-meteor/createApolloServer.js b/imports/plugins/core/graphql/server/no-meteor/createApolloServer.js index 165e6c5d5d1..03e081875e9 100644 --- a/imports/plugins/core/graphql/server/no-meteor/createApolloServer.js +++ b/imports/plugins/core/graphql/server/no-meteor/createApolloServer.js @@ -44,7 +44,7 @@ export default function createApolloServer(options = {}) { const context = { ...contextFromOptions }; // meteorTokenMiddleware will have set req.user if there is one - await buildContext(context, req.user); + await buildContext(context, req); addCallMeteorMethod(context); diff --git a/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js b/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js index 55cb657a5ed..3f6f9d10c24 100644 --- a/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js +++ b/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js @@ -13,6 +13,6 @@ export default { pricing, tagIds, tags, - media: (node) => node.media.map((mediaItem) => xformProductMedia(mediaItem)), - primaryImage: (node) => xformProductMedia(node.primaryImage) + media: (node, args, context) => node.media.map((mediaItem) => xformProductMedia(mediaItem, context)), + primaryImage: (node, args, context) => xformProductMedia(node.primaryImage, context) }; diff --git a/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js b/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js index c5e985501f8..c486f23c21e 100644 --- a/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js +++ b/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js @@ -12,13 +12,14 @@ export const encodeCatalogProductOpaqueId = encodeOpaqueId(namespaces.CatalogPro * @method * @memberof GraphQL/Transforms * @param {Object} mediaItem object from a catalog product + * @param {Object} context - an object containing the per-request state * @return {Object} transformed product media item */ -export function xformProductMedia(mediaItem) { +export function xformProductMedia(mediaItem, context) { if (!mediaItem) return null; const { priority, toGrid, productId, variantId, URLs: { large, medium, original, small, thumbnail } } = mediaItem; - const absoluteUrl = process.env.ROOT_URL.slice(0, -1); + const absoluteUrl = context.getAbsoluteUrl().slice(0, -1); return { priority, diff --git a/imports/plugins/core/shipping/server/methods/updateShipmentQuotes.js b/imports/plugins/core/shipping/server/methods/updateShipmentQuotes.js index 0079d25bac8..35bc499961e 100644 --- a/imports/plugins/core/shipping/server/methods/updateShipmentQuotes.js +++ b/imports/plugins/core/shipping/server/methods/updateShipmentQuotes.js @@ -18,7 +18,7 @@ export default function updateShipmentQuotesMethod(cartId, fulfillmentGroupId, c check(cartToken, Match.Maybe(String)); this.unblock(); - const context = Promise.await(getGraphQLContextInMeteorMethod(Meteor.userId())); + const context = Promise.await(getGraphQLContextInMeteorMethod(Meteor.userId(), this.connection)); return context.mutations.fulfillment.updateFulfillmentOptionsForGroup(context, { cartId, cartToken, From 2c787edd09a1338f23d8f6053dc101f33eea509b Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Thu, 23 Aug 2018 10:05:51 -0400 Subject: [PATCH 30/65] fix: use request.headers.host and request.protocol to build context.getAbsoluteURl --- .../__snapshots__/getAbsoluteUrl.test.js.snap | 5 ----- .../core/core/server/util/getAbsoluteUrl.js | 17 ++++++----------- .../core/server/util/getAbsoluteUrl.test.js | 19 ++++--------------- .../server/getGraphQLContextInMeteorMethod.js | 5 +---- .../graphql/server/no-meteor/buildContext.js | 3 +-- .../server/no-meteor/createApolloServer.js | 1 + 6 files changed, 13 insertions(+), 37 deletions(-) delete mode 100644 imports/plugins/core/core/server/util/__snapshots__/getAbsoluteUrl.test.js.snap diff --git a/imports/plugins/core/core/server/util/__snapshots__/getAbsoluteUrl.test.js.snap b/imports/plugins/core/core/server/util/__snapshots__/getAbsoluteUrl.test.js.snap deleted file mode 100644 index c34d61e97f4..00000000000 --- a/imports/plugins/core/core/server/util/__snapshots__/getAbsoluteUrl.test.js.snap +++ /dev/null @@ -1,5 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`throws error if no host header is provided 1`] = `"getAbsoluteUrl requires 'host' from request headers"`; - -exports[`throws error if no origin header is provided 1`] = `"getAbsoluteUrl requires 'origin' from request headers"`; diff --git a/imports/plugins/core/core/server/util/getAbsoluteUrl.js b/imports/plugins/core/core/server/util/getAbsoluteUrl.js index 05adc2cf2bf..99ae466f3c2 100644 --- a/imports/plugins/core/core/server/util/getAbsoluteUrl.js +++ b/imports/plugins/core/core/server/util/getAbsoluteUrl.js @@ -6,21 +6,16 @@ import ReactionError from "@reactioncommerce/reaction-error"; * @summary Returns the absolute/base URL for where a request was made, using the request's headers * @param {Object} requestHeaders Headers sent with original request * @param {String} requestHeaders.host Domain (including port) request was made to - * @param {String} requestHeaders.origin URL request originated from - * @returns {String} protocol://hostname[:port]/, with protocol being from the origin URL + * @returns {String} protocol://hostname[:port]/ */ -export default function getAbsoluteUrl(requestHeaders) { - const { host = "", origin = "" } = requestHeaders; +export default function getAbsoluteUrl(requestHeaders, protocol) { + let { host = "" } = requestHeaders; if (host === "") { - throw new ReactionError("invalid-parameters", "getAbsoluteUrl requires 'host' from request headers"); - } - if (origin === "") { - throw new ReactionError("invalid-parameters", "getAbsoluteUrl requires 'origin' from request headers"); + return ""; } - const originParsedUrl = url.parse(origin); - const { protocol } = originParsedUrl; + host = host.replace("reaction-api", "localhost"); - return `${protocol}//${host}/`; + return `${protocol}://${host}/`; } diff --git a/imports/plugins/core/core/server/util/getAbsoluteUrl.test.js b/imports/plugins/core/core/server/util/getAbsoluteUrl.test.js index 15d229a2ae2..10cdc416b70 100644 --- a/imports/plugins/core/core/server/util/getAbsoluteUrl.test.js +++ b/imports/plugins/core/core/server/util/getAbsoluteUrl.test.js @@ -1,26 +1,15 @@ import getAbsoluteUrl from "./getAbsoluteUrl"; -test("throws error if no host header is provided", () => { - const requestHeaders = { - origin: "http://localhost:3000" - }; - - expect(() => getAbsoluteUrl(requestHeaders)).toThrowErrorMatchingSnapshot(); -}); - -test("throws error if no origin header is provided", () => { - const requestHeaders = { - host: "localhost:3000" - }; +test("returns empty string if no host header is provided", () => { + const requestHeaders = {}; - expect(() => getAbsoluteUrl(requestHeaders)).toThrowErrorMatchingSnapshot(); + expect(getAbsoluteUrl(requestHeaders, "http")).toBe(""); }); test("returns the correct absolute url", () => { const requestHeaders = { - origin: "http://localhost:3000", host: "localhost:3000" }; - expect(getAbsoluteUrl(requestHeaders)).toBe("http://localhost:3000/"); + expect(getAbsoluteUrl(requestHeaders, "https")).toBe("https://localhost:3000/"); }); diff --git a/imports/plugins/core/graphql/server/getGraphQLContextInMeteorMethod.js b/imports/plugins/core/graphql/server/getGraphQLContextInMeteorMethod.js index 755cd987b05..3426fb01f9b 100644 --- a/imports/plugins/core/graphql/server/getGraphQLContextInMeteorMethod.js +++ b/imports/plugins/core/graphql/server/getGraphQLContextInMeteorMethod.js @@ -33,10 +33,7 @@ export default async function getGraphQLContextInMeteorMethod(userId, methodConn const { httpHeaders } = methodConnection; const request = { user, - headers: { - origin: Reaction.absoluteUrl().slice(0, -1), - ...httpHeaders - } + headers: httpHeaders }; await buildContext(meteorContext, request); diff --git a/imports/plugins/core/graphql/server/no-meteor/buildContext.js b/imports/plugins/core/graphql/server/no-meteor/buildContext.js index 2d3afa8cb0c..fd9dabf7ecb 100644 --- a/imports/plugins/core/graphql/server/no-meteor/buildContext.js +++ b/imports/plugins/core/graphql/server/no-meteor/buildContext.js @@ -13,7 +13,6 @@ import queries from "./queries"; * @param {Object} context - A context object on which to set additional context properties * @param {Object} request - Request object * @param {Object} request.headers - HTTP headers on request - * @param {String} request.headers.origin - Where the request originated from * @param {String} request.headers.host - Host request was made for * @param {Object} [request.user] - The user who authenticated this request, if applicable * @returns {undefined} No return @@ -40,5 +39,5 @@ export default async function buildContext(context, request) { // Add a curried hasPermission tied to the current user (or to no user) context.userHasPermission = getHasPermissionFunctionForUser(context.user); - context.getAbsoluteUrl = () => getAbsoluteUrl(headers); + context.getAbsoluteUrl = () => getAbsoluteUrl(headers, request.protocol); } diff --git a/imports/plugins/core/graphql/server/no-meteor/createApolloServer.js b/imports/plugins/core/graphql/server/no-meteor/createApolloServer.js index 03e081875e9..946e5b0a92b 100644 --- a/imports/plugins/core/graphql/server/no-meteor/createApolloServer.js +++ b/imports/plugins/core/graphql/server/no-meteor/createApolloServer.js @@ -44,6 +44,7 @@ export default function createApolloServer(options = {}) { const context = { ...contextFromOptions }; // meteorTokenMiddleware will have set req.user if there is one + await buildContext(context, req); addCallMeteorMethod(context); From 801dc4a7d1280d4a7cf8e055c058437e66ef130f Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Thu, 23 Aug 2018 10:08:57 -0400 Subject: [PATCH 31/65] chore: comment for protocol param for getAbsoluteUrl --- imports/plugins/core/core/server/util/getAbsoluteUrl.js | 1 + 1 file changed, 1 insertion(+) diff --git a/imports/plugins/core/core/server/util/getAbsoluteUrl.js b/imports/plugins/core/core/server/util/getAbsoluteUrl.js index 99ae466f3c2..4ac3e430daa 100644 --- a/imports/plugins/core/core/server/util/getAbsoluteUrl.js +++ b/imports/plugins/core/core/server/util/getAbsoluteUrl.js @@ -6,6 +6,7 @@ import ReactionError from "@reactioncommerce/reaction-error"; * @summary Returns the absolute/base URL for where a request was made, using the request's headers * @param {Object} requestHeaders Headers sent with original request * @param {String} requestHeaders.host Domain (including port) request was made to + * @param {String} protocol URL protocol to use * @returns {String} protocol://hostname[:port]/ */ export default function getAbsoluteUrl(requestHeaders, protocol) { From b2b0a3a8f10acca2cfed8401647281fb35d30db0 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Thu, 23 Aug 2018 11:08:59 -0400 Subject: [PATCH 32/65] fix: context.getAbsoluteUrl should default to ROOT_URL --- .../accounts/server/methods/addressBookAdd.js | 2 +- .../core/cart/server/methods/addToCart.js | 2 +- .../core/cart/server/methods/createCart.js | 2 +- .../core/cart/server/methods/mergeCart.js | 2 +- .../cart/server/methods/removeFromCart.js | 2 +- .../cart/server/methods/setShipmentAddress.js | 2 +- .../cart/server/methods/setShipmentMethod.js | 2 +- .../catalog/server/methods/publishProducts.js | 2 +- .../core/core/server/util/getAbsoluteUrl.js | 26 +++++++++---------- .../core/server/util/getAbsoluteUrl.test.js | 20 +++++++++----- .../server/getGraphQLContextInMeteorMethod.js | 10 ++----- .../graphql/server/no-meteor/buildContext.js | 6 ++--- .../server/methods/updateShipmentQuotes.js | 2 +- 13 files changed, 39 insertions(+), 41 deletions(-) diff --git a/imports/plugins/core/accounts/server/methods/addressBookAdd.js b/imports/plugins/core/accounts/server/methods/addressBookAdd.js index 4a963481396..d2143ef67fb 100644 --- a/imports/plugins/core/accounts/server/methods/addressBookAdd.js +++ b/imports/plugins/core/accounts/server/methods/addressBookAdd.js @@ -22,6 +22,6 @@ export default function addressBookAdd(address, accountUserId, cartId) { this.unblock(); - const context = Promise.await(getGraphQLContextInMeteorMethod(Meteor.userId(), this.connection)); + const context = Promise.await(getGraphQLContextInMeteorMethod(Meteor.userId())); return addressBookAddMutation(context, address, accountUserId); } diff --git a/imports/plugins/core/cart/server/methods/addToCart.js b/imports/plugins/core/cart/server/methods/addToCart.js index 973b51df348..d78a5a971b0 100644 --- a/imports/plugins/core/cart/server/methods/addToCart.js +++ b/imports/plugins/core/cart/server/methods/addToCart.js @@ -35,7 +35,7 @@ export default function addToCart(cartId, token, items) { const anonymousUser = Roles.userIsInRole(userId, "anonymous", shopId); const userIdForContext = anonymousUser ? null : userId; - const context = Promise.await(getGraphQLContextInMeteorMethod(userIdForContext, this.connection)); + const context = Promise.await(getGraphQLContextInMeteorMethod(userIdForContext)); const { cart: updatedCart, diff --git a/imports/plugins/core/cart/server/methods/createCart.js b/imports/plugins/core/cart/server/methods/createCart.js index 0e821dc333a..63f93172be1 100644 --- a/imports/plugins/core/cart/server/methods/createCart.js +++ b/imports/plugins/core/cart/server/methods/createCart.js @@ -28,7 +28,7 @@ export default function createCartMethod(items) { const anonymousUser = Roles.userIsInRole(userId, "anonymous", shopId); const userIdForContext = anonymousUser ? null : userId; - const context = Promise.await(getGraphQLContextInMeteorMethod(userIdForContext, this.connection)); + const context = Promise.await(getGraphQLContextInMeteorMethod(userIdForContext)); const result = Promise.await(createCart(context, { items, shopId })); const { cart } = result; diff --git a/imports/plugins/core/cart/server/methods/mergeCart.js b/imports/plugins/core/cart/server/methods/mergeCart.js index 1a490d16d0a..30fd5671152 100644 --- a/imports/plugins/core/cart/server/methods/mergeCart.js +++ b/imports/plugins/core/cart/server/methods/mergeCart.js @@ -20,7 +20,7 @@ export default function mergeCart(anonymousCartId, anonymousCartToken) { check(anonymousCartToken, String); // Pass through to the new mutation function at this point - const context = Promise.await(getGraphQLContextInMeteorMethod(Meteor.userId(), this.connection)); + const context = Promise.await(getGraphQLContextInMeteorMethod(Meteor.userId())); const { cart } = Promise.await(reconcileCarts(context, { anonymousCartId, anonymousCartToken, diff --git a/imports/plugins/core/cart/server/methods/removeFromCart.js b/imports/plugins/core/cart/server/methods/removeFromCart.js index 6dcaca54248..1c5d56f3b6f 100644 --- a/imports/plugins/core/cart/server/methods/removeFromCart.js +++ b/imports/plugins/core/cart/server/methods/removeFromCart.js @@ -43,7 +43,7 @@ export default function removeFromCart(cartId, cartToken, cartItemId, quantityDe } // Pass through to the new mutation function - const context = Promise.await(getGraphQLContextInMeteorMethod(account.userId, this.connection)); + const context = Promise.await(getGraphQLContextInMeteorMethod(account.userId)); const { cart: updatedCart } = Promise.await(updateCartItemsQuantity(context, { cartId: cart._id, items: [ diff --git a/imports/plugins/core/cart/server/methods/setShipmentAddress.js b/imports/plugins/core/cart/server/methods/setShipmentAddress.js index 3a5a5d2a0da..aae9c7dfbb9 100644 --- a/imports/plugins/core/cart/server/methods/setShipmentAddress.js +++ b/imports/plugins/core/cart/server/methods/setShipmentAddress.js @@ -33,7 +33,7 @@ export default function setShipmentAddress(cartId, cartToken, address) { const anonymousUser = Roles.userIsInRole(userId, "anonymous", shopId); const userIdForContext = anonymousUser ? null : userId; - const context = Promise.await(getGraphQLContextInMeteorMethod(userIdForContext, this.connection)); + const context = Promise.await(getGraphQLContextInMeteorMethod(userIdForContext)); const result = Promise.await(setShippingAddressOnCart(context, { address, cartId, diff --git a/imports/plugins/core/cart/server/methods/setShipmentMethod.js b/imports/plugins/core/cart/server/methods/setShipmentMethod.js index dac501fb4f6..c1cf8811558 100644 --- a/imports/plugins/core/cart/server/methods/setShipmentMethod.js +++ b/imports/plugins/core/cart/server/methods/setShipmentMethod.js @@ -41,7 +41,7 @@ export default function setShipmentMethod(cartId, cartToken, methodId) { const anonymousUser = Roles.userIsInRole(userId, "anonymous", shopId); const userIdForContext = anonymousUser ? null : userId; - const context = Promise.await(getGraphQLContextInMeteorMethod(userIdForContext, this.connection)); + const context = Promise.await(getGraphQLContextInMeteorMethod(userIdForContext)); const result = Promise.await(selectFulfillmentOptionForGroup(context, { cartId, cartToken, diff --git a/imports/plugins/core/catalog/server/methods/publishProducts.js b/imports/plugins/core/catalog/server/methods/publishProducts.js index 2c5238b5859..99394c913d4 100644 --- a/imports/plugins/core/catalog/server/methods/publishProducts.js +++ b/imports/plugins/core/catalog/server/methods/publishProducts.js @@ -6,7 +6,7 @@ import publishProductsMutation from "../no-meteor/mutations/publishProducts"; Meteor.methods({ "catalog/publish/products"(productIds) { check(productIds, [String]); - const context = Promise.await(getGraphQLContextInMeteorMethod(this.userId, this.connection)); + const context = Promise.await(getGraphQLContextInMeteorMethod(this.userId)); return publishProductsMutation(context, productIds); } }); diff --git a/imports/plugins/core/core/server/util/getAbsoluteUrl.js b/imports/plugins/core/core/server/util/getAbsoluteUrl.js index 4ac3e430daa..3a88b2c5fd7 100644 --- a/imports/plugins/core/core/server/util/getAbsoluteUrl.js +++ b/imports/plugins/core/core/server/util/getAbsoluteUrl.js @@ -1,22 +1,20 @@ -import url from "url"; import ReactionError from "@reactioncommerce/reaction-error"; /** * @name getAbsoluteUrl - * @summary Returns the absolute/base URL for where a request was made, using the request's headers - * @param {Object} requestHeaders Headers sent with original request - * @param {String} requestHeaders.host Domain (including port) request was made to - * @param {String} protocol URL protocol to use - * @returns {String} protocol://hostname[:port]/ + * @summary Returns the absolute/base URL, returning process.env.ROOT_URL if set, + * otherwise using the request's protocol & hostname + * @param {Object} request Express request object + * @param {Object} request.hostname Hostname derived from Host or X-Forwarded-Host heaer + * @param {String} request.protocol Either http or https + * @returns {String} URL */ -export default function getAbsoluteUrl(requestHeaders, protocol) { - let { host = "" } = requestHeaders; - - if (host === "") { - return ""; +export default function getAbsoluteUrl(request) { + const { ROOT_URL } = process.env; + if (ROOT_URL) { + return `${ROOT_URL}/`; } - host = host.replace("reaction-api", "localhost"); - - return `${protocol}://${host}/`; + const { hostname, protocol } = request; + return `${protocol}://${hostname}/`; } diff --git a/imports/plugins/core/core/server/util/getAbsoluteUrl.test.js b/imports/plugins/core/core/server/util/getAbsoluteUrl.test.js index 10cdc416b70..3a8fb7039d8 100644 --- a/imports/plugins/core/core/server/util/getAbsoluteUrl.test.js +++ b/imports/plugins/core/core/server/util/getAbsoluteUrl.test.js @@ -1,15 +1,21 @@ import getAbsoluteUrl from "./getAbsoluteUrl"; -test("returns empty string if no host header is provided", () => { - const requestHeaders = {}; +test("returns process.env.ROOT_URL with trailing slash if set", () => { + process.env.ROOT_URL = "http://localhost:3000"; + const request = { + protocol: "https", + hostname: "reaction-api" + }; - expect(getAbsoluteUrl(requestHeaders, "http")).toBe(""); + expect(getAbsoluteUrl(request)).toBe("http://localhost:3000/"); }); -test("returns the correct absolute url", () => { - const requestHeaders = { - host: "localhost:3000" +test("returns correct URL if process.env.ROOT_URL is not set", () => { + process.env.ROOT_URL = ""; + const request = { + protocol: "https", + hostname: "abc.com" }; - expect(getAbsoluteUrl(requestHeaders, "https")).toBe("https://localhost:3000/"); + expect(getAbsoluteUrl(request)).toBe("https://abc.com/"); }); diff --git a/imports/plugins/core/graphql/server/getGraphQLContextInMeteorMethod.js b/imports/plugins/core/graphql/server/getGraphQLContextInMeteorMethod.js index 3426fb01f9b..8017acea23e 100644 --- a/imports/plugins/core/graphql/server/getGraphQLContextInMeteorMethod.js +++ b/imports/plugins/core/graphql/server/getGraphQLContextInMeteorMethod.js @@ -1,5 +1,4 @@ import { Meteor } from "meteor/meteor"; -import Reaction from "/imports/plugins/core/core/server/Reaction"; import appEvents from "/imports/plugins/core/core/server/appEvents"; import buildContext from "./no-meteor/buildContext"; import collections from "/imports/collections/rawCollections"; @@ -18,10 +17,9 @@ import collections from "/imports/collections/rawCollections"; * @summary Call this in a Meteor method that wraps a GraphQL mutation, to get a context * to pass to the mutation. * @param {String} userId - The user ID for the current request - * @param {Object} methodConnection - this.connection called from within a Meteor method * @return {Object} A GraphQL context object */ -export default async function getGraphQLContextInMeteorMethod(userId, methodConnection) { +export default async function getGraphQLContextInMeteorMethod(userId) { let user; if (userId) { user = await collections.users.findOne({ _id: userId }); @@ -30,11 +28,7 @@ export default async function getGraphQLContextInMeteorMethod(userId, methodConn const meteorContext = { appEvents, collections }; - const { httpHeaders } = methodConnection; - const request = { - user, - headers: httpHeaders - }; + const request = { user }; await buildContext(meteorContext, request); diff --git a/imports/plugins/core/graphql/server/no-meteor/buildContext.js b/imports/plugins/core/graphql/server/no-meteor/buildContext.js index fd9dabf7ecb..cff46e02a76 100644 --- a/imports/plugins/core/graphql/server/no-meteor/buildContext.js +++ b/imports/plugins/core/graphql/server/no-meteor/buildContext.js @@ -12,8 +12,8 @@ import queries from "./queries"; * `userHasPermission` properties. * @param {Object} context - A context object on which to set additional context properties * @param {Object} request - Request object - * @param {Object} request.headers - HTTP headers on request - * @param {String} request.headers.host - Host request was made for + * @param {String} request.hostname - Hostname derived from Host or X-Forwarded-Host heaer + * @param {Object} request.protocol - Either http or https * @param {Object} [request.user] - The user who authenticated this request, if applicable * @returns {undefined} No return */ @@ -39,5 +39,5 @@ export default async function buildContext(context, request) { // Add a curried hasPermission tied to the current user (or to no user) context.userHasPermission = getHasPermissionFunctionForUser(context.user); - context.getAbsoluteUrl = () => getAbsoluteUrl(headers, request.protocol); + context.getAbsoluteUrl = () => getAbsoluteUrl(request); } diff --git a/imports/plugins/core/shipping/server/methods/updateShipmentQuotes.js b/imports/plugins/core/shipping/server/methods/updateShipmentQuotes.js index 35bc499961e..0079d25bac8 100644 --- a/imports/plugins/core/shipping/server/methods/updateShipmentQuotes.js +++ b/imports/plugins/core/shipping/server/methods/updateShipmentQuotes.js @@ -18,7 +18,7 @@ export default function updateShipmentQuotesMethod(cartId, fulfillmentGroupId, c check(cartToken, Match.Maybe(String)); this.unblock(); - const context = Promise.await(getGraphQLContextInMeteorMethod(Meteor.userId(), this.connection)); + const context = Promise.await(getGraphQLContextInMeteorMethod(Meteor.userId())); return context.mutations.fulfillment.updateFulfillmentOptionsForGroup(context, { cartId, cartToken, From 9433b943da42ad8c43584fd09f7942cc886e84ef Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Thu, 23 Aug 2018 11:10:17 -0400 Subject: [PATCH 33/65] chore: fix eslint issues --- imports/plugins/core/core/server/util/getAbsoluteUrl.js | 2 -- imports/plugins/core/graphql/server/no-meteor/buildContext.js | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/imports/plugins/core/core/server/util/getAbsoluteUrl.js b/imports/plugins/core/core/server/util/getAbsoluteUrl.js index 3a88b2c5fd7..a3cec2d926c 100644 --- a/imports/plugins/core/core/server/util/getAbsoluteUrl.js +++ b/imports/plugins/core/core/server/util/getAbsoluteUrl.js @@ -1,5 +1,3 @@ -import ReactionError from "@reactioncommerce/reaction-error"; - /** * @name getAbsoluteUrl * @summary Returns the absolute/base URL, returning process.env.ROOT_URL if set, diff --git a/imports/plugins/core/graphql/server/no-meteor/buildContext.js b/imports/plugins/core/graphql/server/no-meteor/buildContext.js index cff46e02a76..0f31e0221b5 100644 --- a/imports/plugins/core/graphql/server/no-meteor/buildContext.js +++ b/imports/plugins/core/graphql/server/no-meteor/buildContext.js @@ -18,7 +18,7 @@ import queries from "./queries"; * @returns {undefined} No return */ export default async function buildContext(context, request) { - const { user, headers } = request; + const { user } = request; context.mutations = mutations; context.queries = queries; From 14fedc7b9a4ee81c17f586949655976441e6aee5 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Thu, 23 Aug 2018 15:59:12 -0400 Subject: [PATCH 34/65] fix: error when loading cart items --- .../server/no-meteor/resolvers/cart/Cart/items.js | 2 +- .../server/no-meteor/resolvers/xforms/cart.js | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/imports/plugins/core/graphql/server/no-meteor/resolvers/cart/Cart/items.js b/imports/plugins/core/graphql/server/no-meteor/resolvers/cart/Cart/items.js index e9ee98a5155..4682b6c6d2b 100644 --- a/imports/plugins/core/graphql/server/no-meteor/resolvers/cart/Cart/items.js +++ b/imports/plugins/core/graphql/server/no-meteor/resolvers/cart/Cart/items.js @@ -12,5 +12,5 @@ import { xformCartItems } from "@reactioncommerce/reaction-graphql-xforms/cart"; export default async function items(cart, connectionArgs, context) { if (!Array.isArray(cart.items)) return null; - return xformArrayToConnection(connectionArgs, xformCartItems(context.collections, cart.items)); + return xformArrayToConnection(connectionArgs, xformCartItems(context, cart.items)); } diff --git a/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/cart.js b/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/cart.js index 489abf1d296..5265f405994 100644 --- a/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/cart.js +++ b/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/cart.js @@ -40,12 +40,13 @@ export function decodeCartItemsOpaqueIds(items) { } /** + * @param {Object} context - an object containing the per-request state * @param {Object[]} catalogItems Array of CatalogItem docs from the db * @param {Object[]} products Array of Product docs from the db * @param {Object} cartItem CartItem * @return {Object} Same object with GraphQL-only props added */ -function xformCartItem(catalogItems, products, cartItem) { +function xformCartItem(context, catalogItems, products, cartItem) { const { priceWhenAdded, productId, variantId } = cartItem; const { currencyCode } = priceWhenAdded; @@ -69,7 +70,7 @@ function xformCartItem(catalogItems, products, cartItem) { if (catalogProduct.media) { media = catalogProduct.media.find((mediaItem) => mediaItem.variantId === variantId); if (!media) [media] = catalogProduct.media; - media = xformProductMedia(media); + media = xformProductMedia(media, context); } const variantSourceProduct = products.find((product) => product._id === variantId); @@ -97,11 +98,12 @@ function xformCartItem(catalogItems, products, cartItem) { } /** - * @param {Object} collections Map of raw collections + * @param {Object} context - an object containing the per-request state * @param {Object[]} items Array of CartItem * @return {Object[]} Same array with GraphQL-only props added */ -export async function xformCartItems(collections, items) { +export async function xformCartItems(context, items) { + const { collections } = context; const { Catalog, Products } = collections; const productIds = items.map((item) => item.productId); @@ -121,7 +123,7 @@ export async function xformCartItems(collections, items) { } }).toArray(); - return items.map((item) => xformCartItem(catalogItems, products, item)); + return items.map((item) => xformCartItem(context, catalogItems, products, item)); } /** From 37c3a4813bdb2479107c173bfc74fe0cd40a6408 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Thu, 23 Aug 2018 15:59:38 -0400 Subject: [PATCH 35/65] fix: use full URLs for variant images --- .../no-meteor/resolvers/catalog/CatalogProduct/index.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js b/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js index 3f6f9d10c24..486e7da3f22 100644 --- a/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js +++ b/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js @@ -14,5 +14,12 @@ export default { tagIds, tags, media: (node, args, context) => node.media.map((mediaItem) => xformProductMedia(mediaItem, context)), - primaryImage: (node, args, context) => xformProductMedia(node.primaryImage, context) + primaryImage: (node, args, context) => xformProductMedia(node.primaryImage, context), + variants: (node, args, context) => { + return node.variants.map((variant) => { + variant.media = variant.media.map((mediaItem) => xformProductMedia(mediaItem, context)); + variant.primaryImage = xformProductMedia(variant.primaryImage, context); + return variant; + }); + } }; From ed457a6aca9a50c551b18aeaeb0b6d88b10f9652 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Thu, 23 Aug 2018 17:07:08 -0400 Subject: [PATCH 36/65] chore: fix eslint issue --- .../resolvers/catalog/CatalogProduct/index.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js b/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js index 486e7da3f22..b9a6d82b66d 100644 --- a/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js +++ b/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js @@ -15,11 +15,9 @@ export default { tags, media: (node, args, context) => node.media.map((mediaItem) => xformProductMedia(mediaItem, context)), primaryImage: (node, args, context) => xformProductMedia(node.primaryImage, context), - variants: (node, args, context) => { - return node.variants.map((variant) => { - variant.media = variant.media.map((mediaItem) => xformProductMedia(mediaItem, context)); - variant.primaryImage = xformProductMedia(variant.primaryImage, context); - return variant; - }); - } + variants: (node, args, context) => node.variants.map((variant) => { + variant.media = variant.media.map((mediaItem) => xformProductMedia(mediaItem, context)); + variant.primaryImage = xformProductMedia(variant.primaryImage, context); + return variant; + }) }; From 79f06e9e3277161e1b8fd19ab60a5d49062d3879 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Fri, 24 Aug 2018 08:57:36 -0400 Subject: [PATCH 37/65] fix: return full URLs for variant option's media --- .../no-meteor/resolvers/catalog/CatalogProduct/index.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js b/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js index b9a6d82b66d..705b9178674 100644 --- a/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js +++ b/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js @@ -18,6 +18,15 @@ export default { variants: (node, args, context) => node.variants.map((variant) => { variant.media = variant.media.map((mediaItem) => xformProductMedia(mediaItem, context)); variant.primaryImage = xformProductMedia(variant.primaryImage, context); + + if (variant.options) { + variant.options = variant.options.map((option) => { + option.media = option.media.map((mediaItem) => xformProductMedia(mediaItem, context)); + option.primaryImage = xformProductMedia(option.primaryImage, context); + return option; + }); + } + return variant; }) }; From a2c7959a19a345c88edd323c5fb177fe3d0fd4e1 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Fri, 24 Aug 2018 09:35:03 -0400 Subject: [PATCH 38/65] feat: getRootUrl and getAbsoluteUrl context functions --- .../core/core/server/util/getAbsoluteUrl.js | 20 +++++++--------- .../core/server/util/getAbsoluteUrl.test.js | 24 +++++++------------ .../core/core/server/util/getRootUrl.js | 21 ++++++++++++++++ .../core/core/server/util/getRootUrl.test.js | 21 ++++++++++++++++ .../graphql/server/no-meteor/buildContext.js | 4 +++- .../server/no-meteor/buildContext.test.js | 4 ++++ .../resolvers/xforms/catalogProduct.js | 11 ++++----- 7 files changed, 70 insertions(+), 35 deletions(-) create mode 100644 imports/plugins/core/core/server/util/getRootUrl.js create mode 100644 imports/plugins/core/core/server/util/getRootUrl.test.js diff --git a/imports/plugins/core/core/server/util/getAbsoluteUrl.js b/imports/plugins/core/core/server/util/getAbsoluteUrl.js index a3cec2d926c..cebec7d4d60 100644 --- a/imports/plugins/core/core/server/util/getAbsoluteUrl.js +++ b/imports/plugins/core/core/server/util/getAbsoluteUrl.js @@ -1,18 +1,14 @@ /** * @name getAbsoluteUrl - * @summary Returns the absolute/base URL, returning process.env.ROOT_URL if set, - * otherwise using the request's protocol & hostname - * @param {Object} request Express request object - * @param {Object} request.hostname Hostname derived from Host or X-Forwarded-Host heaer - * @param {String} request.protocol Either http or https - * @returns {String} URL + * @summary Combines and returns the given root URL and path + * @param {String} rootUrl URL ending with / + * @param {String} path Path that may or may not start with / + * @returns {String} */ -export default function getAbsoluteUrl(request) { - const { ROOT_URL } = process.env; - if (ROOT_URL) { - return `${ROOT_URL}/`; +export default function getAbsoluteUrl(rootUrl, path = "") { + if (path.startsWith("/")) { + path = path.slice(1); } - const { hostname, protocol } = request; - return `${protocol}://${hostname}/`; + return `${rootUrl}${path}`; } diff --git a/imports/plugins/core/core/server/util/getAbsoluteUrl.test.js b/imports/plugins/core/core/server/util/getAbsoluteUrl.test.js index 3a8fb7039d8..eea2b6858ac 100644 --- a/imports/plugins/core/core/server/util/getAbsoluteUrl.test.js +++ b/imports/plugins/core/core/server/util/getAbsoluteUrl.test.js @@ -1,21 +1,13 @@ import getAbsoluteUrl from "./getAbsoluteUrl"; -test("returns process.env.ROOT_URL with trailing slash if set", () => { - process.env.ROOT_URL = "http://localhost:3000"; - const request = { - protocol: "https", - hostname: "reaction-api" - }; - - expect(getAbsoluteUrl(request)).toBe("http://localhost:3000/"); +test("returns the correct URL when given an empty path", () => { + const rootUrl = "http://localhost:3000/"; + const path = ""; + expect(getAbsoluteUrl(rootUrl, path)).toBe(rootUrl); }); -test("returns correct URL if process.env.ROOT_URL is not set", () => { - process.env.ROOT_URL = ""; - const request = { - protocol: "https", - hostname: "abc.com" - }; - - expect(getAbsoluteUrl(request)).toBe("https://abc.com/"); +test("returns the correct URL when given a path", () => { + const rootUrl = "http://localhost:3000/"; + const path = "/media/test.jpg"; + expect(getAbsoluteUrl(rootUrl, path)).toBe("http://localhost:3000/media/test.jpg"); }); diff --git a/imports/plugins/core/core/server/util/getRootUrl.js b/imports/plugins/core/core/server/util/getRootUrl.js new file mode 100644 index 00000000000..b45285f228f --- /dev/null +++ b/imports/plugins/core/core/server/util/getRootUrl.js @@ -0,0 +1,21 @@ +/** + * @name getRootUrl + * @summary Returns the root URL, returning process.env.ROOT_URL if set, + * otherwise using the request's protocol & hostname + * @param {Object} request Express request object + * @param {Object} request.hostname Hostname derived from Host or X-Forwarded-Host heaer + * @param {String} request.protocol Either http or https + * @returns {String} URL + */ +export default function getRootURL(request) { + const { ROOT_URL } = process.env; + if (ROOT_URL) { + if (ROOT_URL.endsWith("/")) { + return ROOT_URL; + } + return `${ROOT_URL}/`; + } + + const { hostname, protocol } = request; + return `${protocol}://${hostname}/`; +} diff --git a/imports/plugins/core/core/server/util/getRootUrl.test.js b/imports/plugins/core/core/server/util/getRootUrl.test.js new file mode 100644 index 00000000000..bcd2672be09 --- /dev/null +++ b/imports/plugins/core/core/server/util/getRootUrl.test.js @@ -0,0 +1,21 @@ +import getRootUrl from "./getRootUrl"; + +test("returns process.env.ROOT_URL with trailing slash if set", () => { + process.env.ROOT_URL = "http://localhost:3000"; + const request = { + protocol: "https", + hostname: "reaction-api" + }; + + expect(getRootUrl(request)).toBe("http://localhost:3000/"); +}); + +test("returns correct root URL if process.env.ROOT_URL is not set", () => { + process.env.ROOT_URL = ""; + const request = { + protocol: "https", + hostname: "abc.com" + }; + + expect(getRootUrl(request)).toBe("https://abc.com/"); +}); diff --git a/imports/plugins/core/graphql/server/no-meteor/buildContext.js b/imports/plugins/core/graphql/server/no-meteor/buildContext.js index 0f31e0221b5..969975cabfe 100644 --- a/imports/plugins/core/graphql/server/no-meteor/buildContext.js +++ b/imports/plugins/core/graphql/server/no-meteor/buildContext.js @@ -1,5 +1,6 @@ import { getHasPermissionFunctionForUser } from "/imports/plugins/core/accounts/server/no-meteor/hasPermission"; import getShopIdForContext from "/imports/plugins/core/accounts/server/no-meteor/getShopIdForContext"; +import getRootUrl from "/imports/plugins/core/core/server/util/getRootUrl"; import getAbsoluteUrl from "/imports/plugins/core/core/server/util/getAbsoluteUrl"; import mutations from "./mutations"; import queries from "./queries"; @@ -39,5 +40,6 @@ export default async function buildContext(context, request) { // Add a curried hasPermission tied to the current user (or to no user) context.userHasPermission = getHasPermissionFunctionForUser(context.user); - context.getAbsoluteUrl = () => getAbsoluteUrl(request); + context.rootUrl = getRootUrl(request); + context.getAbsoluteUrl = (path) => getAbsoluteUrl(context.rootUrl, path); } diff --git a/imports/plugins/core/graphql/server/no-meteor/buildContext.test.js b/imports/plugins/core/graphql/server/no-meteor/buildContext.test.js index e85cfa77fcd..c5df8878176 100644 --- a/imports/plugins/core/graphql/server/no-meteor/buildContext.test.js +++ b/imports/plugins/core/graphql/server/no-meteor/buildContext.test.js @@ -8,6 +8,7 @@ const fakeUser = { }; test("properly mutates the context object without user", async () => { + process.env.ROOT_URL = "http://localhost:3000"; const context = { collections: mockContext.collections }; await buildContext(context, { user: undefined }); expect(context).toEqual({ @@ -18,11 +19,13 @@ test("properly mutates the context object without user", async () => { user: null, userHasPermission: jasmine.any(Function), userId: null, + rootUrl: "http://localhost:3000/", getAbsoluteUrl: jasmine.any(Function) }); }); test("properly mutates the context object with user", async () => { + process.env.ROOT_URL = "https://localhost:3000"; const mockAccount = { _id: "accountId", userId: fakeUser._id }; mockContext.collections.Accounts.findOne.mockReturnValueOnce(Promise.resolve(mockAccount)); @@ -38,6 +41,7 @@ test("properly mutates the context object with user", async () => { user: fakeUser, userHasPermission: jasmine.any(Function), userId: fakeUser._id, + rootUrl: "https://localhost:3000/", getAbsoluteUrl: jasmine.any(Function) }); diff --git a/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js b/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js index c486f23c21e..119a4f8673f 100644 --- a/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js +++ b/imports/plugins/core/graphql/server/no-meteor/resolvers/xforms/catalogProduct.js @@ -19,7 +19,6 @@ export function xformProductMedia(mediaItem, context) { if (!mediaItem) return null; const { priority, toGrid, productId, variantId, URLs: { large, medium, original, small, thumbnail } } = mediaItem; - const absoluteUrl = context.getAbsoluteUrl().slice(0, -1); return { priority, @@ -27,11 +26,11 @@ export function xformProductMedia(mediaItem, context) { productId: encodeProductOpaqueId(productId), variantId: encodeProductOpaqueId(variantId), URLs: { - large: `${absoluteUrl}${large}`, - medium: `${absoluteUrl}${medium}`, - original: `${absoluteUrl}${original}`, - small: `${absoluteUrl}${small}`, - thumbnail: `${absoluteUrl}${thumbnail}` + large: context.getAbsoluteUrl(large), + medium: context.getAbsoluteUrl(medium), + original: context.getAbsoluteUrl(original), + small: context.getAbsoluteUrl(small), + thumbnail: context.getAbsoluteUrl(thumbnail) } }; } From 1dcc03d160942ca73ad81fafe158d99330288bc2 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Fri, 24 Aug 2018 10:01:01 -0400 Subject: [PATCH 39/65] chore: fix eslint issue --- imports/plugins/core/core/server/util/getAbsoluteUrl.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/imports/plugins/core/core/server/util/getAbsoluteUrl.js b/imports/plugins/core/core/server/util/getAbsoluteUrl.js index cebec7d4d60..b4e124a08e1 100644 --- a/imports/plugins/core/core/server/util/getAbsoluteUrl.js +++ b/imports/plugins/core/core/server/util/getAbsoluteUrl.js @@ -3,12 +3,13 @@ * @summary Combines and returns the given root URL and path * @param {String} rootUrl URL ending with / * @param {String} path Path that may or may not start with / - * @returns {String} + * @returns {String} Full URL */ export default function getAbsoluteUrl(rootUrl, path = "") { + let pathNoSlash = path; if (path.startsWith("/")) { - path = path.slice(1); + pathNoSlash = path.slice(1); } - return `${rootUrl}${path}`; + return `${rootUrl}${pathNoSlash}`; } From 64b2d9ee1a3d0c04b47bfcd917199a7fe7aaa4cf Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Fri, 24 Aug 2018 12:18:29 -0400 Subject: [PATCH 40/65] test: update test related to image URLs --- .../resolvers/catalog/CatalogProduct/index.js | 8 ++-- tests/catalog/catalogItemProduct.test.js | 40 +++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js b/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js index 705b9178674..5a4ff63a5a6 100644 --- a/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js +++ b/imports/plugins/core/graphql/server/no-meteor/resolvers/catalog/CatalogProduct/index.js @@ -13,15 +13,15 @@ export default { pricing, tagIds, tags, - media: (node, args, context) => node.media.map((mediaItem) => xformProductMedia(mediaItem, context)), + media: (node, args, context) => node.media && node.media.map((mediaItem) => xformProductMedia(mediaItem, context)), primaryImage: (node, args, context) => xformProductMedia(node.primaryImage, context), - variants: (node, args, context) => node.variants.map((variant) => { - variant.media = variant.media.map((mediaItem) => xformProductMedia(mediaItem, context)); + variants: (node, args, context) => node.variants && node.variants.map((variant) => { + variant.media = variant.media && variant.media.map((mediaItem) => xformProductMedia(mediaItem, context)); variant.primaryImage = xformProductMedia(variant.primaryImage, context); if (variant.options) { variant.options = variant.options.map((option) => { - option.media = option.media.map((mediaItem) => xformProductMedia(mediaItem, context)); + option.media = option.media && option.media.map((mediaItem) => xformProductMedia(mediaItem, context)); option.primaryImage = xformProductMedia(option.primaryImage, context); return option; }); diff --git a/tests/catalog/catalogItemProduct.test.js b/tests/catalog/catalogItemProduct.test.js index ebce8f603c5..e89323e54fd 100644 --- a/tests/catalog/catalogItemProduct.test.js +++ b/tests/catalog/catalogItemProduct.test.js @@ -179,11 +179,11 @@ const mockCatalogProduct = { productId: internalProductId, variantId: null, URLs: { - thumbnail: "http://localhost/thumbnail", - small: "http://localhost/small", - medium: "http://localhost/medium", - large: "http://localhost/large", - original: "http://localhost/original" + thumbnail: "/thumbnail", + small: "/small", + medium: "/medium", + large: "/large", + original: "/original" } } ], @@ -193,11 +193,11 @@ const mockCatalogProduct = { productId: internalProductId, variantId: null, URLs: { - thumbnail: "http://localhost/thumbnail", - small: "http://localhost/small", - medium: "http://localhost/medium", - large: "http://localhost/large", - original: "http://localhost/original" + thumbnail: "/thumbnail", + small: "/small", + medium: "/medium", + large: "/large", + original: "/original" } }, productType: "productType", @@ -399,11 +399,11 @@ const expectedItemsResponse = { productId: opaqueProductId, variantId: null, URLs: { - thumbnail: "http://localhost/thumbnail", - small: "http://localhost/small", - medium: "http://localhost/medium", - large: "http://localhost/large", - original: "http://localhost/original" + thumbnail: "https://shop.fake.site/thumbnail", + small: "https://shop.fake.site/small", + medium: "https://shop.fake.site/medium", + large: "https://shop.fake.site/large", + original: "https://shop.fake.site/original" } } ], @@ -413,11 +413,11 @@ const expectedItemsResponse = { productId: opaqueProductId, variantId: null, URLs: { - thumbnail: "http://localhost/thumbnail", - small: "http://localhost/small", - medium: "http://localhost/medium", - large: "http://localhost/large", - original: "http://localhost/original" + thumbnail: "https://shop.fake.site/thumbnail", + small: "https://shop.fake.site/small", + medium: "https://shop.fake.site/medium", + large: "https://shop.fake.site/large", + original: "https://shop.fake.site/original" } }, productType: "productType", From 7c01ed81b9b09c740ce4dc4b3379bd09c6bd44eb Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Tue, 21 Aug 2018 16:23:03 +0100 Subject: [PATCH 41/65] Create getUserId util function for /client --- client/modules/core/helpers/utils.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/client/modules/core/helpers/utils.js b/client/modules/core/helpers/utils.js index 0a3249eddfd..c70005dc82a 100644 --- a/client/modules/core/helpers/utils.js +++ b/client/modules/core/helpers/utils.js @@ -1,4 +1,5 @@ import { latinLangs, getShopLang } from "/lib/api/helpers"; +import { Meteor } from "meteor/meteor"; // dynamic import of slugiy/transliteration.slugify let slugify; @@ -45,3 +46,12 @@ export function getSlug(slugString) { } return slug; } + +/** + * @method getUserId + * @summary returns the userId of logged in user (e.g Meteor.userId()) + * @return {String} String + */ +export function getUserId() { + return Meteor.userId(); +} From daed9a1464563c6289e88c9ce2574b5ffbcbb5cc Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Tue, 21 Aug 2018 16:31:09 +0100 Subject: [PATCH 42/65] Remove unused code that was marked for deletion long ago --- client/modules/core/helpers/globals.js | 30 -------------------------- 1 file changed, 30 deletions(-) diff --git a/client/modules/core/helpers/globals.js b/client/modules/core/helpers/globals.js index 1cf28473f01..e00b5b6701c 100644 --- a/client/modules/core/helpers/globals.js +++ b/client/modules/core/helpers/globals.js @@ -1,10 +1,5 @@ import _ from "lodash"; import { Session } from "meteor/session"; -import { Meteor } from "meteor/meteor"; -import { Roles } from "meteor/alanning:roles"; - -/* eslint "no-extend-native": [2, {"exceptions": ["String"]}] */ -/* eslint "no-alert": 0 */ /** * @name toggleSession @@ -56,28 +51,3 @@ export function getCardType(cardNumber) { } return ""; } - -/** - * @name getGuestLoginState - * @method - * @memberof Helpers - * @todo These should all be removed. PR's happily accepted. - * @summary Determines if a guest checkout is enabled and the login state for users - * @return {Boolean} true if authenticated user - */ -export function getGuestLoginState() { - if (Meteor.userId() === "string" && this.getShopId() && this.allowGuestCheckout()) { - const isGuestFlow = Session.equals("guestCheckoutFlow", true); - const isGuest = Roles.userIsInRole(Meteor.userId(), "guest", this.getShopId()); - const isAnonymous = Roles.userIsInRole(Meteor.userId(), "anonymous", this.getShopId()); - if (!isGuestFlow && !isGuest && isAnonymous) { - return false; - } else if (!isGuestFlow && isGuest && !isAnonymous) { - return true; - } - } else if (Session.equals("guestCheckoutFlow", true) && _.pluck(Meteor.user() - .emails, "address")) { - return true; - } - return false; -} From 524c759da2abda24b925dbc42e351aec05a4a8c9 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Tue, 21 Aug 2018 16:23:29 +0100 Subject: [PATCH 43/65] Change Meteor.userId() to getUserId() in /client/modules --- client/modules/core/helpers/apps.js | 4 ++-- client/modules/core/helpers/permissions.js | 4 +--- client/modules/core/main.js | 15 ++++++++------- client/modules/core/startup.js | 7 ++++--- client/modules/core/subscriptions.js | 7 ++++--- client/modules/i18n/currency.js | 3 +-- client/modules/i18n/helpers.js | 3 +-- client/modules/i18n/startup.js | 2 +- 8 files changed, 22 insertions(+), 23 deletions(-) diff --git a/client/modules/core/helpers/apps.js b/client/modules/core/helpers/apps.js index 6f25f861d19..3c0f3c8b9f2 100644 --- a/client/modules/core/helpers/apps.js +++ b/client/modules/core/helpers/apps.js @@ -1,6 +1,5 @@ import _ from "lodash"; import { Template } from "meteor/templating"; -import { Meteor } from "meteor/meteor"; import { Roles } from "meteor/alanning:roles"; import { Reaction } from "/client/api"; import { Packages, Shops } from "/lib/collections"; @@ -21,6 +20,7 @@ import { Packages, Shops } from "/lib/collections"; * @return {Object[]} returns an array of filtered, structure reactionApps */ export function Apps(optionHash) { + const { getUserId } = Reaction; const filter = {}; const registryFilter = {}; let key; @@ -94,7 +94,7 @@ export function Apps(optionHash) { // This checks that the registry item contains a permissions matches with the user's permission for the shop const hasPermissionToRegistryItem = item.permissions.indexOf(permission) > -1; // This checks that the user's permission set have the right value that is on the registry item - const hasRoleAccessForShop = Roles.userIsInRole(Meteor.userId(), permission, Reaction.getShopId()); + const hasRoleAccessForShop = Roles.userIsInRole(getUserId(), permission, Reaction.getShopId()); // both checks must pass for access to be granted if (hasPermissionToRegistryItem && hasRoleAccessForShop) { diff --git a/client/modules/core/helpers/permissions.js b/client/modules/core/helpers/permissions.js index 6bc203c9423..867d83f1383 100644 --- a/client/modules/core/helpers/permissions.js +++ b/client/modules/core/helpers/permissions.js @@ -1,4 +1,3 @@ -import { Meteor } from "meteor/meteor"; import { Template } from "meteor/templating"; import { Reaction } from "/client/api"; @@ -12,8 +11,7 @@ import { Reaction } from "/client/api"; * @return {Boolean} */ Template.registerHelper("hasPermission", (permissions, options) => { - // default to checking this.userId - const loggedInUser = Meteor.userId(); + const loggedInUser = Reaction.getUserId(); const shopId = Reaction.getShopId(); // we don't necessarily need to check here // as these same checks and defaults are diff --git a/client/modules/core/main.js b/client/modules/core/main.js index 34ecfe80148..3b51473f9d4 100644 --- a/client/modules/core/main.js +++ b/client/modules/core/main.js @@ -14,6 +14,7 @@ import { localeDep } from "/client/modules/i18n"; import { Packages, Shops, Accounts } from "/lib/collections"; import { Router } from "/client/modules/router"; import { DomainsMixin } from "./domains"; +import { getUserId } from "./helpers/utils"; /** * Reaction core namespace for client code @@ -200,7 +201,7 @@ export default { * @method * @memberof Core/Client * @param {String | Array} checkPermissions -String or Array of permissions if empty, defaults to "admin, owner" - * @param {String} checkUserId - userId, defaults to Meteor.userId() + * @param {String} checkUserId - userId, defaults to logged in user ID * @param {String} checkGroup group - default to shopId * @return {Boolean} Boolean - true if has permission */ @@ -215,7 +216,7 @@ export default { let permissions = ["owner"]; let id = ""; - const userId = checkUserId || Meteor.userId(); + const userId = checkUserId || getUserId(); // // local roleCheck function // is the bulk of the logic @@ -271,7 +272,7 @@ export default { // in line 156 setTimeout // function validateUserId() { - if (Meteor.userId()) { + if (getUserId()) { Meteor.clearTimeout(id); Router.reload(); return roleCheck(); @@ -368,7 +369,7 @@ export default { hasAdminAccess(shopId) { const adminPermissions = ["owner", "admin"]; if (shopId) { - return this.hasPermission(adminPermissions, Meteor.userId(), shopId); + return this.hasPermission(adminPermissions, getUserId(), shopId); } return this.hasPermission(adminPermissions); }, @@ -397,7 +398,7 @@ export default { * @method * @memberof Core/Client */ - getSellerShopId(userId = Meteor.userId(), noFallback = false) { + getSellerShopId(userId = getUserId(), noFallback = false) { if (userId) { const group = Roles.getGroupsForUser(userId, "admin")[0]; if (group) { @@ -444,7 +445,7 @@ export default { // the Accounts collection. const syncedPackages = ["reaction"]; if (syncedPackages.indexOf(packageName) > -1) { - Accounts.update(Meteor.userId(), { + Accounts.update(getUserId(), { $set: { [`profile.preferences.${packageName}.${preference}`]: value } @@ -721,7 +722,7 @@ export default { const groupPermissions = group.permissions; // granting invitation right for user with `owner` role in a shop - if (this.hasPermission(["owner"], Meteor.userId(), group.shopId)) { + if (this.hasPermission(["owner"], getUserId(), group.shopId)) { return true; } diff --git a/client/modules/core/startup.js b/client/modules/core/startup.js index fc974dee7aa..61e4604299e 100644 --- a/client/modules/core/startup.js +++ b/client/modules/core/startup.js @@ -7,6 +7,7 @@ import { Accounts } from "meteor/accounts-base"; import { Reaction, Logger } from "/client/api"; import { userPrefs } from "./main"; +import { getUserId } from "./helpers/utils"; /** * Startup Reaction @@ -18,7 +19,7 @@ Meteor.startup(() => { // Log in anonymous guest users Tracker.autorun(() => { - const userId = Meteor.userId(); + const userId = getUserId(); if (userId) return; // This autorun is only for when we DO NOT have a user const loggingIn = Tracker.nonreactive(() => Accounts.loggingIn()); @@ -42,7 +43,7 @@ Tracker.autorun(() => { }); Tracker.autorun(() => { - const userId = Meteor.userId(); // The only reactive thing in this autorun. Reruns on login/logout only. + const userId = getUserId(); // The only reactive thing in this autorun. Reruns on login/logout only. if (!userId) return; // Load data from Accounts collection into the localStorage @@ -72,7 +73,7 @@ Tracker.autorun(() => { // Fine-grained reactivity on only the user preferences Tracker.autorun(() => { - const userId = Meteor.userId(); + const userId = getUserId(); if (!userId) return; const user = Meteor.users.findOne(userId, { fields: { profile: 1 } }); diff --git a/client/modules/core/subscriptions.js b/client/modules/core/subscriptions.js index 54ecd961d16..33bbf8fb1d2 100644 --- a/client/modules/core/subscriptions.js +++ b/client/modules/core/subscriptions.js @@ -6,6 +6,7 @@ import { SubsManager } from "meteor/meteorhacks:subs-manager"; import { Roles } from "meteor/alanning:roles"; import { Accounts } from "/lib/collections"; import Reaction from "./main"; +import { getUserId } from "./helpers/utils"; import { getAnonymousCartsReactive, unstoreAnonymousCart } from "/imports/plugins/core/cart/client/util/anonymousCarts"; export const Subscriptions = {}; @@ -19,7 +20,7 @@ Subscriptions.Manager = new SubsManager(); */ Tracker.autorun(() => { - const userId = Meteor.userId(); + const userId = getUserId(); Subscriptions.Account = Subscriptions.Manager.subscribe("Accounts"); Subscriptions.UserProfile = Meteor.subscribe("UserProfile", userId); }); @@ -47,7 +48,7 @@ Subscriptions.BrandAssets = Subscriptions.Manager.subscribe("BrandAssets"); const cartSubCreated = new ReactiveVar(false); Tracker.autorun(() => { - const userId = Meteor.userId(); + const userId = getUserId(); if (!userId) return; const account = Accounts.findOne({ userId }); @@ -79,7 +80,7 @@ function mergeCart(_id, token) { // call the mergeCart function to merge it into an account cart and destroy let isMerging = false; Tracker.autorun(() => { - const userId = Meteor.userId(); + const userId = getUserId(); if (!userId) return; const shopId = Reaction.getCartShopId(); diff --git a/client/modules/i18n/currency.js b/client/modules/i18n/currency.js index cd52e1cc3ae..87ac2e6c61f 100644 --- a/client/modules/i18n/currency.js +++ b/client/modules/i18n/currency.js @@ -1,5 +1,4 @@ import accounting from "accounting-js"; -import { Meteor } from "meteor/meteor"; import { Reaction, Logger } from "/client/api"; import ReactionError from "@reactioncommerce/reaction-error"; import { Shops, Accounts } from "/lib/collections"; @@ -23,7 +22,7 @@ export function findCurrency(defaultCurrency, useDefaultShopCurrency) { const shopCurrency = (shop && shop.currency) || "USD"; const user = Accounts.findOne({ - _id: Meteor.userId() + _id: Reaction.getUserId() }); const profileCurrency = user && user.profile && user.profile.currency; if (typeof shop === "object" && shop.currencies && profileCurrency) { diff --git a/client/modules/i18n/helpers.js b/client/modules/i18n/helpers.js index f53751770f2..9fbe92ed71b 100644 --- a/client/modules/i18n/helpers.js +++ b/client/modules/i18n/helpers.js @@ -1,4 +1,3 @@ -import { Meteor } from "meteor/meteor"; import { Template } from "meteor/templating"; import { check, Match } from "meteor/check"; import { Reaction, Logger, i18next } from "/client/api"; @@ -41,7 +40,7 @@ Template.registerHelper("i18n", (i18nKey, i18nMessage) => { Template.registerHelper("currencySymbol", () => { const locale = Reaction.Locale.get(); const user = Accounts.findOne({ - _id: Meteor.userId() + _id: Reaction.getUserId() }); const profileCurrency = user.profile && user.profile.currency; if (profileCurrency) { diff --git a/client/modules/i18n/startup.js b/client/modules/i18n/startup.js index 69fd91a9986..eeaa3fab17a 100644 --- a/client/modules/i18n/startup.js +++ b/client/modules/i18n/startup.js @@ -91,7 +91,7 @@ Meteor.startup(() => { // We need to ensure fine-grained reactivity on only the profile.lang because // user.profile changed frequently and causes excessive reruns Tracker.autorun(() => { - const userId = Meteor.userId(); + const userId = Reaction.getUserId(); const user = userId && Meteor.users.findOne(userId, { fields: { profile: 1 } }); userProfileLanguage.set((user && user.profile && user.profile.lang) || null); }); From 549cb23c3ab7138257e9fe568c42c10bcfbba95b Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Tue, 21 Aug 2018 16:25:22 +0100 Subject: [PATCH 44/65] fix: Fix incorrect jsdoc --- client/modules/core/helpers/permissions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/modules/core/helpers/permissions.js b/client/modules/core/helpers/permissions.js index 867d83f1383..6bc8d8734a1 100644 --- a/client/modules/core/helpers/permissions.js +++ b/client/modules/core/helpers/permissions.js @@ -7,7 +7,7 @@ import { Reaction } from "/client/api"; * @summary check current user hasPermission, uses [alanning:meteor-roles](http://alanning.github.io/meteor-roles/classes/Roles.html) * @example {{hasPermission admin userId}} * @param {String|Array} "permissions" - * @param {String} checkUserId - optional Meteor.userId, default to current + * @param {String} options - object * @return {Boolean} */ Template.registerHelper("hasPermission", (permissions, options) => { From 180b17466ca408f600b9ba08d1128990bf84be43 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Tue, 21 Aug 2018 21:41:12 +0100 Subject: [PATCH 45/65] Create getUserId util function in server Reaction --- .../plugins/core/core/server/Reaction/accountUtils.js | 10 ++++++++++ imports/plugins/core/core/server/Reaction/index.js | 2 ++ 2 files changed, 12 insertions(+) create mode 100644 imports/plugins/core/core/server/Reaction/accountUtils.js diff --git a/imports/plugins/core/core/server/Reaction/accountUtils.js b/imports/plugins/core/core/server/Reaction/accountUtils.js new file mode 100644 index 00000000000..63b6f6e0daf --- /dev/null +++ b/imports/plugins/core/core/server/Reaction/accountUtils.js @@ -0,0 +1,10 @@ +import { Meteor } from "meteor/meteor"; + +/** + * @method getUserId + * @summary returns the userId of logged in user (e.g Meteor.userId()) + * @return {String} String + */ +export function getUserId() { + return Meteor.userId(); +} diff --git a/imports/plugins/core/core/server/Reaction/index.js b/imports/plugins/core/core/server/Reaction/index.js index 3aebb962ba8..577c23c14be 100644 --- a/imports/plugins/core/core/server/Reaction/index.js +++ b/imports/plugins/core/core/server/Reaction/index.js @@ -12,9 +12,11 @@ import setDomain from "./setDomain"; import setShopName from "./setShopName"; import * as Collections from "/lib/collections"; import * as Schemas from "/lib/collections/schemas"; +import * as accountUtils from "./accountUtils"; export default { ...Core, + ...accountUtils, addRolesToGroups, assignOwnerRoles, Collections, From 5fe72ff16340bebe79fb8802bbdd21b976744895 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Tue, 21 Aug 2018 21:50:36 +0100 Subject: [PATCH 46/65] Change Meteor.userId() to getUserId() in /imports/plugins/core --- .../accounts/client/components/groupsTableCell.js | 3 +-- .../client/containers/addressBookContainer.js | 4 ++-- .../accounts/client/containers/mainDropdown.js | 2 +- .../accounts/client/containers/updateEmail.js | 4 ++-- .../accounts/client/helpers/accountsHelper.js | 3 +-- .../core/accounts/client/helpers/templates.js | 3 +-- .../client/templates/dashboard/dashboard.js | 2 +- .../accounts/client/templates/members/member.js | 4 ++-- .../accounts/client/templates/profile/profile.js | 10 +++++----- .../accounts/server/methods/addUserPermissions.js | 3 +-- .../accounts/server/methods/addressBookAdd.js | 4 ++-- .../accounts/server/methods/addressBookRemove.js | 15 ++++++--------- .../accounts/server/methods/addressBookUpdate.js | 12 +++++------- .../core/accounts/server/methods/group/addUser.js | 8 ++++---- .../accounts/server/methods/group/createGroup.js | 3 +-- .../accounts/server/methods/group/removeUser.js | 5 ++--- .../accounts/server/methods/group/updateGroup.js | 3 +-- .../methods/markAddressValidationBypassed.js | 4 ++-- .../server/methods/markTaxCalculationFailed.js | 4 ++-- .../server/methods/removeUserPermissions.js | 3 +-- .../accounts/server/methods/setUserPermissions.js | 3 +-- .../server/no-meteor/mutations/addressBookAdd.js | 3 +-- imports/plugins/core/cart/client/util/getCart.js | 3 +-- .../plugins/core/cart/server/methods/addToCart.js | 2 +- .../core/cart/server/methods/createCart.js | 2 +- .../plugins/core/cart/server/methods/mergeCart.js | 3 ++- .../cart/server/methods/setShipmentAddress.js | 2 +- .../core/cart/server/methods/setShipmentMethod.js | 2 +- .../core/server/methods/shop/updateBrandAssets.js | 1 - .../core/core/server/methods/updatePackage.js | 1 - .../dashboard/client/templates/import/import.js | 1 - .../core/orders/client/components/lineItems.js | 1 - .../core/orders/server/methods/updateHistory.js | 1 - .../plugins/core/ui/client/containers/avatar.js | 1 - 34 files changed, 52 insertions(+), 73 deletions(-) diff --git a/imports/plugins/core/accounts/client/components/groupsTableCell.js b/imports/plugins/core/accounts/client/components/groupsTableCell.js index 11239ab6831..0de70ae7f6c 100644 --- a/imports/plugins/core/accounts/client/components/groupsTableCell.js +++ b/imports/plugins/core/accounts/client/components/groupsTableCell.js @@ -1,4 +1,3 @@ -import { Meteor } from "meteor/meteor"; import React from "react"; import _ from "lodash"; import PropTypes from "prop-types"; @@ -54,7 +53,7 @@ const GroupsTableCell = (props) => { if (columnName === "dropdown") { const groupName = {_.startCase(groups[0].name)}; const ownerGroup = groups.find((grp) => grp.slug === "owner") || {}; - const hasOwnerAccess = Reaction.hasPermission("owner", Meteor.userId(), Reaction.getShopId()); + const hasOwnerAccess = Reaction.hasPermission("owner", Reaction.getUserId(), Reaction.getShopId()); if (groups.length === 1) { return groupName; diff --git a/imports/plugins/core/accounts/client/containers/addressBookContainer.js b/imports/plugins/core/accounts/client/containers/addressBookContainer.js index 820bd1b66ed..d474a5b7b70 100644 --- a/imports/plugins/core/accounts/client/containers/addressBookContainer.js +++ b/imports/plugins/core/accounts/client/containers/addressBookContainer.js @@ -2,7 +2,7 @@ import { compose, withProps } from "recompose"; import { registerComponent, composeWithTracker } from "@reactioncommerce/reaction-components"; import { Meteor } from "meteor/meteor"; import { Template } from "meteor/templating"; -import { i18next, Logger } from "/client/api"; +import { i18next, Logger, Reaction } from "/client/api"; import { Countries } from "/client/collections"; import * as Collections from "/lib/collections"; import getCart from "/imports/plugins/core/cart/client/util/getCart"; @@ -286,7 +286,7 @@ const handlers = { * @returns {undefined} */ function composer(props, onData) { - const userId = Meteor.userId(); + const userId = Reaction.getUserId(); const account = Collections.Accounts.findOne({ userId }); if (!account) { // Subscription not ready diff --git a/imports/plugins/core/accounts/client/containers/mainDropdown.js b/imports/plugins/core/accounts/client/containers/mainDropdown.js index 82c6b4de394..2c16afd6fc3 100644 --- a/imports/plugins/core/accounts/client/containers/mainDropdown.js +++ b/imports/plugins/core/accounts/client/containers/mainDropdown.js @@ -37,7 +37,7 @@ function displayName(displayUser) { function getAdminShortcutIcons() { // get shortcuts with audience permissions based on user roles - const roles = Roles.getRolesForUser(Meteor.userId(), Reaction.getShopId()); + const roles = Roles.getRolesForUser(Reaction.getUserId(), Reaction.getShopId()); return { provides: "shortcut", diff --git a/imports/plugins/core/accounts/client/containers/updateEmail.js b/imports/plugins/core/accounts/client/containers/updateEmail.js index a1dac4b0c30..eae70148a4d 100644 --- a/imports/plugins/core/accounts/client/containers/updateEmail.js +++ b/imports/plugins/core/accounts/client/containers/updateEmail.js @@ -1,7 +1,7 @@ import { compose, withProps } from "recompose"; import { registerComponent, composeWithTracker } from "@reactioncommerce/reaction-components"; import { Meteor } from "meteor/meteor"; -import { i18next } from "/client/api"; +import { i18next, Reaction } from "/client/api"; import { Accounts } from "/lib/collections"; import UpdateEmail from "../components/updateEmail"; @@ -30,7 +30,7 @@ const handlers = { }; const composer = (props, onData) => { - const user = Accounts.findOne(Meteor.userId()); + const user = Accounts.findOne(Reaction.getUserId()); const email = user.emails.length > 0 ? user.emails[0].address : ""; onData(null, { email }); }; diff --git a/imports/plugins/core/accounts/client/helpers/accountsHelper.js b/imports/plugins/core/accounts/client/helpers/accountsHelper.js index 19f2a268481..c64ef036d98 100644 --- a/imports/plugins/core/accounts/client/helpers/accountsHelper.js +++ b/imports/plugins/core/accounts/client/helpers/accountsHelper.js @@ -1,4 +1,3 @@ -import { Meteor } from "meteor/meteor"; import _ from "lodash"; import { Reaction } from "/client/api"; import * as Collections from "/lib/collections"; @@ -62,7 +61,7 @@ export function getInvitableGroups(groups) { */ export function getDefaultUserInviteGroup(groups) { let result; - const user = Collections.Accounts.findOne({ userId: Meteor.userId() }); + const user = Collections.Accounts.findOne({ userId: Reaction.getUserId() }); result = groups.find((grp) => user && user.groups.indexOf(grp._id) > -1); if (result && result.slug === "owner") { diff --git a/imports/plugins/core/accounts/client/helpers/templates.js b/imports/plugins/core/accounts/client/helpers/templates.js index 43c4f44a566..af72b5eaa00 100644 --- a/imports/plugins/core/accounts/client/helpers/templates.js +++ b/imports/plugins/core/accounts/client/helpers/templates.js @@ -1,4 +1,3 @@ -import { Meteor } from "meteor/meteor"; import { Template } from "meteor/templating"; import { Roles } from "meteor/alanning:roles"; import { Reaction, i18next, i18nextDep } from "/client/api"; @@ -13,7 +12,7 @@ import * as Collections from "/lib/collections"; Template.registerHelper("displayName", (displayUser) => { i18nextDep.depend(); - const user = displayUser || Collections.Accounts.findOne(Meteor.userId()); + const user = displayUser || Collections.Accounts.findOne(Reaction.getUserId()); if (user) { if (user.name) { return user.name; diff --git a/imports/plugins/core/accounts/client/templates/dashboard/dashboard.js b/imports/plugins/core/accounts/client/templates/dashboard/dashboard.js index 9d0c9e37cf8..7b8e4c8b81e 100644 --- a/imports/plugins/core/accounts/client/templates/dashboard/dashboard.js +++ b/imports/plugins/core/accounts/client/templates/dashboard/dashboard.js @@ -90,7 +90,7 @@ Template.accountsDashboard.helpers({ }); Template.accountsSettings.onCreated(function () { - this.subscribe("ServiceConfiguration", Meteor.userId()); + this.subscribe("ServiceConfiguration", Reaction.getUserId()); }); Template.accountsSettings.helpers({ diff --git a/imports/plugins/core/accounts/client/templates/members/member.js b/imports/plugins/core/accounts/client/templates/members/member.js index 31021832215..1b75488c23e 100644 --- a/imports/plugins/core/accounts/client/templates/members/member.js +++ b/imports/plugins/core/accounts/client/templates/members/member.js @@ -38,14 +38,14 @@ Template.member.events({ Template.memberSettings.helpers({ isOwnerDisabled() { - if (Meteor.userId() === this.userId) { + if (Reaction.getUserId() === this.userId) { if (Roles.userIsInRole(this.userId, "owner", this.shopId)) { return true; } } }, userId() { - return Meteor.userId(); + return Reaction.getUserId(); }, hasPermissionChecked(permission, userId) { if (userId && Roles.userIsInRole(userId, permission, this.shopId || Roles.userIsInRole( diff --git a/imports/plugins/core/accounts/client/templates/profile/profile.js b/imports/plugins/core/accounts/client/templates/profile/profile.js index c0c3ca8606a..c11a7d7dc08 100644 --- a/imports/plugins/core/accounts/client/templates/profile/profile.js +++ b/imports/plugins/core/accounts/client/templates/profile/profile.js @@ -16,7 +16,7 @@ import { Components } from "@reactioncommerce/reaction-components"; */ function isOwnerOfProfile() { const targetUserId = Reaction.Router.getQueryParam("userId"); - const loggedInUserId = Meteor.userId(); + const loggedInUserId = Reaction.getUserId(); return targetUserId === undefined || targetUserId === loggedInUserId; } @@ -28,7 +28,7 @@ function isOwnerOfProfile() { * @return {Object} - the account of the identified user. */ function getTargetAccount() { - const targetUserId = Reaction.Router.getQueryParam("userId") || Meteor.userId(); + const targetUserId = Reaction.Router.getQueryParam("userId") || Reaction.getUserId(); const account = Collections.Accounts.findOne(targetUserId); return account; @@ -98,7 +98,7 @@ Template.accountProfile.helpers({ * @ignore */ ReactionAvatar() { - const account = Collections.Accounts.findOne({ _id: Meteor.userId() }); + const account = Collections.Accounts.findOne({ _id: Reaction.getUserId() }); if (account && account.profile && account.profile.picture) { const { picture } = account.profile; return { @@ -145,7 +145,7 @@ Template.accountProfile.helpers({ * @ignore */ userOrders() { - const targetUserId = Reaction.Router.getQueryParam("userId") || Meteor.userId(); + const targetUserId = Reaction.Router.getQueryParam("userId") || Reaction.getUserId(); const account = Collections.Accounts.findOne({ userId: targetUserId }); const accountId = (account && account._id) || targetUserId; const orderSub = Meteor.subscribe("AccountOrders", accountId); @@ -216,7 +216,7 @@ Template.accountProfile.helpers({ */ showMerchantSignup() { if (Reaction.Subscriptions && Reaction.Subscriptions.Account && Reaction.Subscriptions.Account.ready()) { - const account = Collections.Accounts.findOne({ _id: Meteor.userId() }); + const account = Collections.Accounts.findOne({ _id: Reaction.getUserId() }); const marketplaceEnabled = Reaction.marketplace && Reaction.marketplace.enabled === true; const allowMerchantSignup = Reaction.marketplace && Reaction.marketplace.allowMerchantSignup === true; // A user has the primaryShopId until a shop is created for them. diff --git a/imports/plugins/core/accounts/server/methods/addUserPermissions.js b/imports/plugins/core/accounts/server/methods/addUserPermissions.js index 0572f003beb..9ba4c5c8f61 100644 --- a/imports/plugins/core/accounts/server/methods/addUserPermissions.js +++ b/imports/plugins/core/accounts/server/methods/addUserPermissions.js @@ -1,5 +1,4 @@ import Logger from "@reactioncommerce/logger"; -import { Meteor } from "meteor/meteor"; import { check, Match } from "meteor/check"; import { Roles } from "meteor/alanning:roles"; import Reaction from "/imports/plugins/core/core/server/Reaction"; @@ -16,7 +15,7 @@ import ReactionError from "@reactioncommerce/reaction-error"; * @returns {Boolean} success/failure */ export default function addUserPermissions(userId, permissions, group) { - if (!Reaction.hasPermission("reaction-accounts", Meteor.userId(), group)) { + if (!Reaction.hasPermission("reaction-accounts", Reaction.getUserId(), group)) { throw new ReactionError("access-denied", "Access denied"); } check(userId, Match.OneOf(String, Array)); diff --git a/imports/plugins/core/accounts/server/methods/addressBookAdd.js b/imports/plugins/core/accounts/server/methods/addressBookAdd.js index d2143ef67fb..ebc69a75acf 100644 --- a/imports/plugins/core/accounts/server/methods/addressBookAdd.js +++ b/imports/plugins/core/accounts/server/methods/addressBookAdd.js @@ -1,8 +1,8 @@ -import { Meteor } from "meteor/meteor"; import { check, Match } from "meteor/check"; import * as Schemas from "/lib/collections/schemas"; import addressBookAddMutation from "../no-meteor/mutations/addressBookAdd"; import getGraphQLContextInMeteorMethod from "/imports/plugins/core/graphql/server/getGraphQLContextInMeteorMethod"; +import Reaction from "/imports/plugins/core/core/server/Reaction"; /** * @name accounts/addressBookAdd @@ -22,6 +22,6 @@ export default function addressBookAdd(address, accountUserId, cartId) { this.unblock(); - const context = Promise.await(getGraphQLContextInMeteorMethod(Meteor.userId())); + const context = Promise.await(getGraphQLContextInMeteorMethod(Reaction.getUserId())); return addressBookAddMutation(context, address, accountUserId); } diff --git a/imports/plugins/core/accounts/server/methods/addressBookRemove.js b/imports/plugins/core/accounts/server/methods/addressBookRemove.js index 46a59fa20b9..9c2eac88632 100644 --- a/imports/plugins/core/accounts/server/methods/addressBookRemove.js +++ b/imports/plugins/core/accounts/server/methods/addressBookRemove.js @@ -1,5 +1,4 @@ import Hooks from "@reactioncommerce/hooks"; -import { Meteor } from "meteor/meteor"; import { check, Match } from "meteor/check"; import { Accounts } from "/lib/collections"; import Reaction from "/imports/plugins/core/core/server/Reaction"; @@ -17,17 +16,15 @@ import ReactionError from "@reactioncommerce/reaction-error"; export default function addressBookRemove(addressId, accountUserId) { check(addressId, String); check(accountUserId, Match.Optional(String)); - // security, check for admin access. We don't need to check every user call - // here because we are calling `Meteor.userId` from within this Method. - if (typeof accountUserId === "string") { // if this will not be a String - - // `check` will not pass it. - if (Meteor.userId() !== accountUserId && !Reaction.hasPermission("reaction-accounts")) { + + if (typeof accountUserId === "string") { + if (Reaction.getUserId() !== accountUserId && !Reaction.hasPermission("reaction-accounts")) { throw new ReactionError("access-denied", "Access denied"); } } this.unblock(); - const userId = accountUserId || Meteor.userId(); + const userId = accountUserId || Reaction.getUserId(); const account = Accounts.findOne({ userId }); const updatedAccountResult = Accounts.update({ @@ -42,12 +39,12 @@ export default function addressBookRemove(addressId, accountUserId) { }, { bypassCollection2: true }); // forceIndex when removing an address - Hooks.Events.run("afterAccountsUpdate", Meteor.userId(), { + Hooks.Events.run("afterAccountsUpdate", userId, { accountId: account._id, updatedFields: ["forceIndex"] }); - // If the address remove was successful, then return the removed addrtess + // If the address remove was successful, then return the removed address if (updatedAccountResult === 1) { // Pull the address from the account before it was updated and return it return account.profile.addressBook.find((removedAddress) => addressId === removedAddress._id); diff --git a/imports/plugins/core/accounts/server/methods/addressBookUpdate.js b/imports/plugins/core/accounts/server/methods/addressBookUpdate.js index 0c6834e1f24..d78b9f08a0d 100644 --- a/imports/plugins/core/accounts/server/methods/addressBookUpdate.js +++ b/imports/plugins/core/accounts/server/methods/addressBookUpdate.js @@ -22,18 +22,16 @@ export default function addressBookUpdate(address, accountUserId, type) { check(accountUserId, Match.Maybe(String)); check(type, Match.Maybe(String)); - // security, check for admin access. We don't need to check every user call - // here because we are calling `Meteor.userId` from within this Method. - if (typeof accountUserId === "string") { // if this will not be a String - - // `check` will not pass it. - if (Meteor.userId() !== accountUserId && !Reaction.hasPermission("reaction-accounts")) { + // security check for admin access + if (typeof accountUserId === "string") { + if (Reaction.getUserId() !== accountUserId && !Reaction.hasPermission("reaction-accounts")) { throw new ReactionError("access-denied", "Access denied"); } } this.unblock(); // If no userId is provided, use the current user - const userId = accountUserId || Meteor.userId(); + const userId = accountUserId || Reaction.getUserId(); // Find old state of isShippingDefault & isBillingDefault to compare and reflect in cart const account = Accounts.findOne({ userId }); const oldAddress = (account.profile.addressBook || []).find((addr) => addr._id === address._id); @@ -92,7 +90,7 @@ export default function addressBookUpdate(address, accountUserId, type) { }); // Run afterAccountsUpdate hook to update Accounts Search - Hooks.Events.run("afterAccountsUpdate", Meteor.userId(), { + Hooks.Events.run("afterAccountsUpdate", userId, { accountId: account._id, updatedFields }); diff --git a/imports/plugins/core/accounts/server/methods/group/addUser.js b/imports/plugins/core/accounts/server/methods/group/addUser.js index 948e2e714a7..1d6d1825565 100644 --- a/imports/plugins/core/accounts/server/methods/group/addUser.js +++ b/imports/plugins/core/accounts/server/methods/group/addUser.js @@ -21,7 +21,7 @@ function changeMarketplaceOwner({ userId, permissions }) { // give global marketplace role to new owner Roles.setUserRoles(userId, permissions, Roles.GLOBAL_GROUP); // remove global from previous owner - Meteor.users.update({ _id: Meteor.userId() }, { $unset: { [`roles.${Roles.GLOBAL_GROUP}`]: "" } }); + Meteor.users.update({ _id: Reaction.getUserId() }, { $unset: { [`roles.${Roles.GLOBAL_GROUP}`]: "" } }); } /** @@ -40,7 +40,7 @@ export default function addUser(userId, groupId) { check(groupId, String); const group = Groups.findOne({ _id: groupId }) || {}; const { permissions, shopId, slug } = group; - const loggedInUserId = Meteor.userId(); + const loggedInUserId = Reaction.getUserId(); const canInvite = Reaction.canInviteToGroup({ group }); // we are limiting group method actions to only users with admin roles @@ -58,7 +58,7 @@ export default function addUser(userId, groupId) { if (slug === "owner") { // if adding a user to the owner group, check that the request is done by current owner - if (!Reaction.hasPermission("owner", Meteor.userId(), shopId)) { + if (!Reaction.hasPermission("owner", Reaction.getUserId(), shopId)) { throw new ReactionError("access-denied", "Access Denied"); } } @@ -90,7 +90,7 @@ export default function addUser(userId, groupId) { changeMarketplaceOwner({ userId, permissions }); } // remove current shop owner after setting another admin as the new owner - Meteor.call("group/addUser", Meteor.userId(), currentUserGrpInShop); + Meteor.call("group/addUser", Reaction.getUserId(), currentUserGrpInShop); } // Return the group the account as added to diff --git a/imports/plugins/core/accounts/server/methods/group/createGroup.js b/imports/plugins/core/accounts/server/methods/group/createGroup.js index 6c3513e48b4..ef1d17cc763 100644 --- a/imports/plugins/core/accounts/server/methods/group/createGroup.js +++ b/imports/plugins/core/accounts/server/methods/group/createGroup.js @@ -1,7 +1,6 @@ import _ from "lodash"; import Logger from "@reactioncommerce/logger"; import { check, Match } from "meteor/check"; -import { Meteor } from "meteor/meteor"; import Reaction from "/imports/plugins/core/core/server/Reaction"; import ReactionError from "@reactioncommerce/reaction-error"; import { Groups } from "/lib/collections"; @@ -30,7 +29,7 @@ export default function createGroup(groupData, shopId) { // we are limiting group method actions to only users with admin roles // this also include shop owners, since they have the `admin` role in their Roles.GLOBAL_GROUP - if (!Reaction.hasPermission("admin", Meteor.userId(), shopId)) { + if (!Reaction.hasPermission("admin", Reaction.getUserId(), shopId)) { throw new ReactionError("access-denied", "Access Denied"); } diff --git a/imports/plugins/core/accounts/server/methods/group/removeUser.js b/imports/plugins/core/accounts/server/methods/group/removeUser.js index 3593e84fc68..2f8fbd6a004 100644 --- a/imports/plugins/core/accounts/server/methods/group/removeUser.js +++ b/imports/plugins/core/accounts/server/methods/group/removeUser.js @@ -1,7 +1,6 @@ import Hooks from "@reactioncommerce/hooks"; import Logger from "@reactioncommerce/logger"; import { check } from "meteor/check"; -import { Meteor } from "meteor/meteor"; import Reaction from "/imports/plugins/core/core/server/Reaction"; import ReactionError from "@reactioncommerce/reaction-error"; import { Accounts, Groups } from "/lib/collections"; @@ -28,7 +27,7 @@ export default function removeUser(userId, groupId) { // we are limiting group method actions to only users with admin roles // this also include shop owners, since they have the `admin` role in their Roles.GLOBAL_GROUP - if (!Reaction.hasPermission("admin", Meteor.userId(), shopId)) { + if (!Reaction.hasPermission("admin", Reaction.getUserId(), shopId)) { throw new ReactionError("access-denied", "Access Denied"); } @@ -39,7 +38,7 @@ export default function removeUser(userId, groupId) { try { setUserPermissions(user, defaultCustomerGroupForShop.permissions, shopId); Accounts.update({ _id: userId, groups: groupId }, { $set: { "groups.$": defaultCustomerGroupForShop._id } }); // replace the old id with new id - Hooks.Events.run("afterAccountsUpdate", Meteor.userId(), { + Hooks.Events.run("afterAccountsUpdate", Reaction.getUserId(), { accountId: userId, updatedFields: ["groups"] }); diff --git a/imports/plugins/core/accounts/server/methods/group/updateGroup.js b/imports/plugins/core/accounts/server/methods/group/updateGroup.js index f76efd97581..8a32b77d2d6 100644 --- a/imports/plugins/core/accounts/server/methods/group/updateGroup.js +++ b/imports/plugins/core/accounts/server/methods/group/updateGroup.js @@ -1,6 +1,5 @@ import Logger from "@reactioncommerce/logger"; import { check } from "meteor/check"; -import { Meteor } from "meteor/meteor"; import Reaction from "/imports/plugins/core/core/server/Reaction"; import ReactionError from "@reactioncommerce/reaction-error"; import { Accounts, Groups } from "/lib/collections"; @@ -27,7 +26,7 @@ export default function updateGroup(groupId, newGroupData, shopId) { // we are limiting group method actions to only users with admin roles // this also include shop owners, since they have the `admin` role in their Roles.GLOBAL_GROUP - if (!Reaction.hasPermission("admin", Meteor.userId(), shopId)) { + if (!Reaction.hasPermission("admin", Reaction.getUserId(), shopId)) { throw new ReactionError("access-denied", "Access Denied"); } diff --git a/imports/plugins/core/accounts/server/methods/markAddressValidationBypassed.js b/imports/plugins/core/accounts/server/methods/markAddressValidationBypassed.js index 7436ce5831e..cf737646804 100644 --- a/imports/plugins/core/accounts/server/methods/markAddressValidationBypassed.js +++ b/imports/plugins/core/accounts/server/methods/markAddressValidationBypassed.js @@ -1,7 +1,7 @@ -import { Meteor } from "meteor/meteor"; import { check } from "meteor/check"; import { Accounts, Cart } from "/lib/collections"; import appEvents from "/imports/plugins/core/core/server/appEvents"; +import Reaction from "/imports/plugins/core/core/server/Reaction"; /** * @name accounts/markAddressValidationBypassed @@ -13,7 +13,7 @@ import appEvents from "/imports/plugins/core/core/server/appEvents"; */ export default function markAddressValidationBypassed(value = true) { check(value, Boolean); - const userId = Meteor.userId(); + const userId = Reaction.getUserId(); const account = Accounts.findOne({ userId }, { fields: { _id: 1 } }); const updateResult = Cart.update({ accountId: account._id }, { $set: { bypassAddressValidation: value } }); diff --git a/imports/plugins/core/accounts/server/methods/markTaxCalculationFailed.js b/imports/plugins/core/accounts/server/methods/markTaxCalculationFailed.js index 3a041a6866c..d9527cb2ace 100644 --- a/imports/plugins/core/accounts/server/methods/markTaxCalculationFailed.js +++ b/imports/plugins/core/accounts/server/methods/markTaxCalculationFailed.js @@ -1,7 +1,7 @@ -import { Meteor } from "meteor/meteor"; import { check } from "meteor/check"; import { Accounts, Cart } from "/lib/collections"; import appEvents from "/imports/plugins/core/core/server/appEvents"; +import Reaction from "/imports/plugins/core/core/server/Reaction"; /** * @name accounts/markTaxCalculationFailed @@ -13,7 +13,7 @@ import appEvents from "/imports/plugins/core/core/server/appEvents"; */ export default function markTaxCalculationFailed(value = true) { check(value, Boolean); - const userId = Meteor.userId(); + const userId = Reaction.getUserId(); const account = Accounts.findOne({ userId }, { fields: { _id: 1 } }); const updateResult = Cart.update({ accountId: account._id }, { $set: { taxCalculationFailed: value } }); diff --git a/imports/plugins/core/accounts/server/methods/removeUserPermissions.js b/imports/plugins/core/accounts/server/methods/removeUserPermissions.js index 397f1690f10..3d7a4272dc2 100644 --- a/imports/plugins/core/accounts/server/methods/removeUserPermissions.js +++ b/imports/plugins/core/accounts/server/methods/removeUserPermissions.js @@ -1,5 +1,4 @@ import Logger from "@reactioncommerce/logger"; -import { Meteor } from "meteor/meteor"; import { check, Match } from "meteor/check"; import { Roles } from "meteor/alanning:roles"; import Reaction from "/imports/plugins/core/core/server/Reaction"; @@ -16,7 +15,7 @@ import ReactionError from "@reactioncommerce/reaction-error"; * @returns {Boolean} success/failure */ export default function removeUserPermissions(userId, permissions, group) { - if (!Reaction.hasPermission("reaction-accounts", Meteor.userId(), group)) { + if (!Reaction.hasPermission("reaction-accounts", Reaction.getUserId(), group)) { throw new ReactionError("access-denied", "Access denied"); } check(userId, String); diff --git a/imports/plugins/core/accounts/server/methods/setUserPermissions.js b/imports/plugins/core/accounts/server/methods/setUserPermissions.js index 52df0704bca..d5786da3f98 100644 --- a/imports/plugins/core/accounts/server/methods/setUserPermissions.js +++ b/imports/plugins/core/accounts/server/methods/setUserPermissions.js @@ -1,5 +1,4 @@ import Logger from "@reactioncommerce/logger"; -import { Meteor } from "meteor/meteor"; import { check, Match } from "meteor/check"; import { Roles } from "meteor/alanning:roles"; import Reaction from "/imports/plugins/core/core/server/Reaction"; @@ -15,7 +14,7 @@ import ReactionError from "@reactioncommerce/reaction-error"; * @returns {Boolean} returns Roles.setUserRoles result */ export default function setUserPermissions(userId, permissions, group) { - if (!Reaction.hasPermission("reaction-accounts", Meteor.userId(), group)) { + if (!Reaction.hasPermission("reaction-accounts", Reaction.getUserId(), group)) { throw new ReactionError("access-denied", "Access denied"); } check(userId, String); diff --git a/imports/plugins/core/accounts/server/no-meteor/mutations/addressBookAdd.js b/imports/plugins/core/accounts/server/no-meteor/mutations/addressBookAdd.js index a7015380da1..06dd73c9f7a 100644 --- a/imports/plugins/core/accounts/server/no-meteor/mutations/addressBookAdd.js +++ b/imports/plugins/core/accounts/server/no-meteor/mutations/addressBookAdd.js @@ -22,8 +22,7 @@ export default async function addressBookAdd(context, address, accountUserId) { if (!account) throw new ReactionError("not-found", "No account found"); - // security, check for admin access. We don't need to check every user call - // here because we are calling `Meteor.userId` from within this Method. + // Security check for admin access if (typeof accountUserId === "string" && userIdFromContext !== accountUserId) { if (!userHasPermission(["reaction-accounts"], account.shopId)) throw new ReactionError("access-denied", "Access denied"); } diff --git a/imports/plugins/core/cart/client/util/getCart.js b/imports/plugins/core/cart/client/util/getCart.js index 01ba4209319..12fac223b4a 100644 --- a/imports/plugins/core/cart/client/util/getCart.js +++ b/imports/plugins/core/cart/client/util/getCart.js @@ -1,4 +1,3 @@ -import { Meteor } from "meteor/meteor"; import { Reaction } from "/client/api"; import { Accounts, Cart } from "/lib/collections"; import { getAnonymousCartsReactive } from "./anonymousCarts"; @@ -8,7 +7,7 @@ import { getAnonymousCartsReactive } from "./anonymousCarts"; * @returns {Object|null} The cart document or null */ export default function getCart() { - const userId = Meteor.userId(); + const userId = Reaction.getUserId(); const account = (userId && Accounts.findOne({ userId })) || null; const shopId = Reaction.getCartShopId(); diff --git a/imports/plugins/core/cart/server/methods/addToCart.js b/imports/plugins/core/cart/server/methods/addToCart.js index 0ba277dc3a9..e94e3b8bd0a 100644 --- a/imports/plugins/core/cart/server/methods/addToCart.js +++ b/imports/plugins/core/cart/server/methods/addToCart.js @@ -31,7 +31,7 @@ export default function addToCart(cartId, token, items) { // In Meteor app we always have a user, but it may have "anonymous" role, meaning // it was auto-created as a kind of session. If so, we fool the createCart mutation // into thinking there is no user so that it will create an anonymous cart. - const userId = Meteor.userId(); + const userId = Reaction.getUserId(); const anonymousUser = Roles.userIsInRole(userId, "anonymous", shopId); const userIdForContext = anonymousUser ? null : userId; diff --git a/imports/plugins/core/cart/server/methods/createCart.js b/imports/plugins/core/cart/server/methods/createCart.js index 63f93172be1..1844f5e2873 100644 --- a/imports/plugins/core/cart/server/methods/createCart.js +++ b/imports/plugins/core/cart/server/methods/createCart.js @@ -24,7 +24,7 @@ export default function createCartMethod(items) { // In Meteor app we always have a user, but it may have "anonymous" role, meaning // it was auto-created as a kind of session. If so, we fool the createCart mutation // into thinking there is no user so that it will create an anonymous cart. - const userId = Meteor.userId(); + const userId = Reaction.getUserId(); const anonymousUser = Roles.userIsInRole(userId, "anonymous", shopId); const userIdForContext = anonymousUser ? null : userId; diff --git a/imports/plugins/core/cart/server/methods/mergeCart.js b/imports/plugins/core/cart/server/methods/mergeCart.js index 30fd5671152..7a599a24cec 100644 --- a/imports/plugins/core/cart/server/methods/mergeCart.js +++ b/imports/plugins/core/cart/server/methods/mergeCart.js @@ -2,6 +2,7 @@ import { Meteor } from "meteor/meteor"; import { check } from "meteor/check"; import getGraphQLContextInMeteorMethod from "/imports/plugins/core/graphql/server/getGraphQLContextInMeteorMethod"; import reconcileCarts from "../no-meteor/mutations/reconcileCarts"; +import Reaction from "/imports/plugins/core/core/server/Reaction"; /** * @method cart/mergeCart @@ -20,7 +21,7 @@ export default function mergeCart(anonymousCartId, anonymousCartToken) { check(anonymousCartToken, String); // Pass through to the new mutation function at this point - const context = Promise.await(getGraphQLContextInMeteorMethod(Meteor.userId())); + const context = Promise.await(getGraphQLContextInMeteorMethod(Reaction.getUserId())); const { cart } = Promise.await(reconcileCarts(context, { anonymousCartId, anonymousCartToken, diff --git a/imports/plugins/core/cart/server/methods/setShipmentAddress.js b/imports/plugins/core/cart/server/methods/setShipmentAddress.js index aae9c7dfbb9..4faf3965945 100644 --- a/imports/plugins/core/cart/server/methods/setShipmentAddress.js +++ b/imports/plugins/core/cart/server/methods/setShipmentAddress.js @@ -29,7 +29,7 @@ export default function setShipmentAddress(cartId, cartToken, address) { // In Meteor app we always have a user, but it may have "anonymous" role, meaning // it was auto-created as a kind of session. - const userId = Meteor.userId(); + const userId = Reaction.getUserId(); const anonymousUser = Roles.userIsInRole(userId, "anonymous", shopId); const userIdForContext = anonymousUser ? null : userId; diff --git a/imports/plugins/core/cart/server/methods/setShipmentMethod.js b/imports/plugins/core/cart/server/methods/setShipmentMethod.js index c1cf8811558..6b85f0af373 100644 --- a/imports/plugins/core/cart/server/methods/setShipmentMethod.js +++ b/imports/plugins/core/cart/server/methods/setShipmentMethod.js @@ -37,7 +37,7 @@ export default function setShipmentMethod(cartId, cartToken, methodId) { // In Meteor app we always have a user, but it may have "anonymous" role, meaning // it was auto-created as a kind of session. - const userId = Meteor.userId(); + const userId = Reaction.getUserId(); const anonymousUser = Roles.userIsInRole(userId, "anonymous", shopId); const userIdForContext = anonymousUser ? null : userId; diff --git a/imports/plugins/core/core/server/methods/shop/updateBrandAssets.js b/imports/plugins/core/core/server/methods/shop/updateBrandAssets.js index c8c254c07bb..7307f37f057 100644 --- a/imports/plugins/core/core/server/methods/shop/updateBrandAssets.js +++ b/imports/plugins/core/core/server/methods/shop/updateBrandAssets.js @@ -1,5 +1,4 @@ import { check } from "meteor/check"; -import { Meteor } from "meteor/meteor"; import { Reaction } from "/lib/api"; import ReactionError from "@reactioncommerce/reaction-error"; import { Shops } from "/lib/collections"; diff --git a/imports/plugins/core/core/server/methods/updatePackage.js b/imports/plugins/core/core/server/methods/updatePackage.js index f90dac01b99..988745339c6 100644 --- a/imports/plugins/core/core/server/methods/updatePackage.js +++ b/imports/plugins/core/core/server/methods/updatePackage.js @@ -1,4 +1,3 @@ -import { Meteor } from "meteor/meteor"; import { check } from "meteor/check"; import { Packages } from "/lib/collections"; import Reaction from "/imports/plugins/core/core/server/Reaction"; diff --git a/imports/plugins/core/dashboard/client/templates/import/import.js b/imports/plugins/core/dashboard/client/templates/import/import.js index cd440d88c56..cf6e1793c0e 100644 --- a/imports/plugins/core/dashboard/client/templates/import/import.js +++ b/imports/plugins/core/dashboard/client/templates/import/import.js @@ -1,5 +1,4 @@ import { Template } from "meteor/templating"; -import { Meteor } from "meteor/meteor"; import { FileRecord } from "@reactioncommerce/file-collections"; import { Logger, Reaction } from "/client/api"; import { Products } from "/lib/collections"; diff --git a/imports/plugins/core/orders/client/components/lineItems.js b/imports/plugins/core/orders/client/components/lineItems.js index a5648b9f687..b8f322cd992 100644 --- a/imports/plugins/core/orders/client/components/lineItems.js +++ b/imports/plugins/core/orders/client/components/lineItems.js @@ -2,7 +2,6 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; import { isEmpty } from "lodash"; import classnames from "classnames"; -import { Meteor } from "meteor/meteor"; import { Roles } from "meteor/alanning:roles"; import { formatPriceString, Reaction } from "/client/api"; import { Components, registerComponent } from "@reactioncommerce/reaction-components"; diff --git a/imports/plugins/core/orders/server/methods/updateHistory.js b/imports/plugins/core/orders/server/methods/updateHistory.js index 29805bbe7e0..6a0c724c41b 100644 --- a/imports/plugins/core/orders/server/methods/updateHistory.js +++ b/imports/plugins/core/orders/server/methods/updateHistory.js @@ -1,4 +1,3 @@ -import { Meteor } from "meteor/meteor"; import { check, Match } from "meteor/check"; import { Orders } from "/lib/collections"; import Reaction from "/imports/plugins/core/core/server/Reaction"; diff --git a/imports/plugins/core/ui/client/containers/avatar.js b/imports/plugins/core/ui/client/containers/avatar.js index 89c957663a3..d89f3804eef 100644 --- a/imports/plugins/core/ui/client/containers/avatar.js +++ b/imports/plugins/core/ui/client/containers/avatar.js @@ -1,5 +1,4 @@ import { registerComponent, composeWithTracker } from "@reactioncommerce/reaction-components"; -import { Meteor } from "meteor/meteor"; import { Accounts } from "/lib/collections"; import { Reaction } from "/client/api"; import { ReactionAvatar } from "../components/avatar"; From b4b1212bdf6cc0c65c137af2e4bb986539c5506b Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Thu, 23 Aug 2018 14:01:28 +0100 Subject: [PATCH 47/65] Change Meteor.userId() to getUserId() - lib dir --- lib/api/core.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/api/core.js b/lib/api/core.js index d34ae0eec90..91a099a31d3 100644 --- a/lib/api/core.js +++ b/lib/api/core.js @@ -111,7 +111,7 @@ function getSellerShop(user, noFallback = false) { * @return {Array} - shopIds user has provided permissions for * @private */ -function getShopsForUser(roles, userId = Meteor.userId()) { +function getShopsForUser(roles, userId = Reaction.getUserId()) { // Get full user object, and get shopIds of all shops they are attached to const user = Meteor.user(userId); if (!user || !user.roles) { @@ -148,7 +148,7 @@ function getShopsForUser(roles, userId = Meteor.userId()) { * @return {Boolean} - true if the user has permissions for all the shops * @private */ -function hasPermissionForAll(roles, userId = Meteor.userId(), shopsOfUser) { +function hasPermissionForAll(roles, userId = Reaction.getUserId(), shopsOfUser) { return !isEqual(getShopsForUser(["admin"], userId), shopsOfUser); } From e84eb73e7a5bc0296dc9c84b256493aa4efa33f1 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Thu, 23 Aug 2018 17:09:16 +0100 Subject: [PATCH 48/65] Change Meteor.userId() to getUserId() in /imports/plugins/core --- .../server/methods/currentUserHasPassword.js | 3 ++- .../plugins/core/cart/server/util/getCart.js | 4 ++-- .../plugins/core/core/server/Reaction/core.js | 17 +++++++++-------- .../core/core/server/methods/shop/createShop.js | 7 ++++--- .../core/core/server/methods/shop/getLocale.js | 2 +- .../server/methods/shop/updateBrandAssets.js | 4 ++-- .../core/core/server/methods/updatePackage.js | 2 +- .../server/publications/collections/accounts.js | 2 +- .../server/publications/collections/cart.js | 3 ++- .../server/publications/collections/orders.js | 4 ++-- .../core/server/startup/collection-security.js | 8 ++++---- .../core/server/util/connectionDataStore.js | 1 - .../client/containers/packageListContainer.js | 3 +-- .../client/containers/toolbarContainer.js | 4 ++-- .../dashboard/client/templates/import/import.js | 2 +- .../client/templates/shop/settings/settings.js | 2 +- .../core/discounts/server/methods/methods.js | 2 +- .../i18n/client/containers/currencyDropdown.js | 2 +- .../i18n/client/containers/languageDropdown.js | 2 +- .../i18n/server/methods/flushAllTranslations.js | 3 +-- .../core/orders/client/components/lineItems.js | 2 +- .../core/orders/server/methods/addOrderEmail.js | 4 ++-- .../core/orders/server/methods/cancelOrder.js | 2 +- .../core/orders/server/methods/updateHistory.js | 2 +- imports/plugins/core/router/lib/router.js | 5 +++-- .../server/methods/updateShipmentQuotes.js | 4 ++-- .../plugins/core/templates/server/methods.js | 2 +- .../plugins/core/ui/client/containers/avatar.js | 2 +- .../core/ui/client/containers/mediaGallery.js | 2 +- 29 files changed, 52 insertions(+), 50 deletions(-) diff --git a/imports/plugins/core/accounts/server/methods/currentUserHasPassword.js b/imports/plugins/core/accounts/server/methods/currentUserHasPassword.js index 42e59ca9162..738c224de52 100644 --- a/imports/plugins/core/accounts/server/methods/currentUserHasPassword.js +++ b/imports/plugins/core/accounts/server/methods/currentUserHasPassword.js @@ -1,4 +1,5 @@ import { Meteor } from "meteor/meteor"; +import Reaction from "/imports/plugins/core/core/server/Reaction"; /** * @name accounts/currentUserHasPassword @@ -9,6 +10,6 @@ import { Meteor } from "meteor/meteor"; * @private */ export default function currentUserHasPassword() { - const user = Meteor.users.findOne(Meteor.userId()); + const user = Meteor.users.findOne(Reaction.getUserId()); return !!user.services.password; } diff --git a/imports/plugins/core/cart/server/util/getCart.js b/imports/plugins/core/cart/server/util/getCart.js index 588a8adc7b9..4efb4c4030d 100644 --- a/imports/plugins/core/cart/server/util/getCart.js +++ b/imports/plugins/core/cart/server/util/getCart.js @@ -6,7 +6,7 @@ import Reaction from "/imports/plugins/core/core/server/Reaction"; import hashLoginToken from "/imports/plugins/core/accounts/server/no-meteor/util/hashLoginToken"; /** - * @summary Gets the current cart. Assumes a calling context where Meteor.userId() works. It works + * @summary Gets the current cart. Assumes a calling context where logged in userID can be retrieved. It works * in all client code, in server methods, and in server publications. * @param {String} [cartId] Limit the search by this cart ID if provided. * @param {Object} [options] Options @@ -21,7 +21,7 @@ export default function getCart(cartId, { cartToken, throwIfNotFound = false } = throw new Meteor.Error("not-found", "Cart not found"); } - const userId = Meteor.userId(); + const userId = Reaction.getUserId(); let account = null; const selector = { shopId }; if (cartId) { diff --git a/imports/plugins/core/core/server/Reaction/core.js b/imports/plugins/core/core/server/Reaction/core.js index f9190621cdb..955f057f1b0 100644 --- a/imports/plugins/core/core/server/Reaction/core.js +++ b/imports/plugins/core/core/server/Reaction/core.js @@ -15,6 +15,7 @@ import processJobs from "./processJobs"; import sendVerificationEmail from "./sendVerificationEmail"; import { registerTemplate } from "./templates"; import { AbsoluteUrlMixin } from "./absoluteUrl"; +import { getUserId } from "./accountUtils"; /** * @file Server core methods @@ -99,7 +100,7 @@ export default { const groupPermissions = group.permissions; // granting invitation right for user with `owner` role in a shop - if (this.hasPermission(["owner"], Meteor.userId(), group.shopId)) { + if (this.hasPermission(["owner"], getUserId(), group.shopId)) { return true; } @@ -123,11 +124,11 @@ export default { * @memberof Core * @summary server permissions checks hasPermission exists on both the server and the client. * @param {String | Array} checkPermissions -String or Array of permissions if empty, defaults to "admin, owner" - * @param {String} userId - userId, defaults to Meteor.userId() + * @param {String} userId - userId, defaults to logged in userId * @param {String} checkGroup group - default to shopId * @return {Boolean} Boolean - true if has permission */ - hasPermission(checkPermissions, userId = Meteor.userId(), checkGroup = this.getShopId()) { + hasPermission(checkPermissions, userId = getUserId(), checkGroup = this.getShopId()) { // check(checkPermissions, Match.OneOf(String, Array)); check(userId, String); check(checkGroup, // Match.Optional(String)); let permissions; @@ -192,11 +193,11 @@ export default { * @method * @memberof Core * @param {array} roles an array of roles to check. Will return a shopId if the user has _any_ of the roles - * @param {string} [userId=Meteor.userId()] Optional userId, defaults to Meteor.userId() + * @param {string} userId Optional userId, defaults to logged in userId * Must pass this.userId from publications to avoid error! * @return {array} Array of shopIds that the user has at least one of the given set of roles for */ - getShopsWithRoles(roles, userId = Meteor.userId()) { + getShopsWithRoles(roles, userId = getUserId()) { // Owner permission for a shop superceeds grantable permissions, so we always check for owner permissions as well roles.push("owner"); @@ -373,9 +374,9 @@ export default { try { // otherwise, find the shop by user settings - shopId = this.getUserShopId(Meteor.userId()); + shopId = this.getUserShopId(getUserId()); } catch (_e) { - // `Meteor.userId` will raise an error when invoked outside of a method + // an error when invoked outside of a method // call or publication, i.e., at startup. That's ok here. } @@ -448,7 +449,7 @@ export default { * @memberof Core * @summary Get a user's shop ID, as stored in preferences * @todo This should intelligently find the correct default shop Probably whatever the main shop is or marketplace - * @param {String} userId (probably Meteor.userId()) + * @param {String} userId (probably logged in userId) * @return {StringId} active shop ID */ getUserShopId(userId) { diff --git a/imports/plugins/core/core/server/methods/shop/createShop.js b/imports/plugins/core/core/server/methods/shop/createShop.js index f398987c92c..3e6269d7ffa 100644 --- a/imports/plugins/core/core/server/methods/shop/createShop.js +++ b/imports/plugins/core/core/server/methods/shop/createShop.js @@ -73,9 +73,10 @@ export default function createShop(shopAdminUserId, partialShopData) { // Get the current marketplace settings const marketplace = Reaction.getMarketplaceSettings(); + const userId = Reaction.getUserId(); // check to see if the current user has owner permissions for the primary shop - const hasPrimaryShopOwnerPermission = Reaction.hasPermission("owner", Meteor.userId(), Reaction.getPrimaryShopId()); + const hasPrimaryShopOwnerPermission = Reaction.hasPermission("owner", userId, Reaction.getPrimaryShopId()); // only permit merchant signup if marketplace is enabled and allowMerchantSignup is enabled let allowMerchantShopCreation = false; @@ -89,13 +90,13 @@ export default function createShop(shopAdminUserId, partialShopData) { } // Non-admin users may only create shops for themselves - if (!hasPrimaryShopOwnerPermission && shopAdminUserId !== Meteor.userId()) { + if (!hasPrimaryShopOwnerPermission && shopAdminUserId !== userId) { throw new ReactionError("access-denied", "Access Denied"); } // Anonymous users should never be permitted to create a shop if (!hasPrimaryShopOwnerPermission && - Reaction.hasPermission("anonymous", Meteor.userId(), Reaction.getPrimaryShopId())) { + Reaction.hasPermission("anonymous", userId, Reaction.getPrimaryShopId())) { throw new ReactionError("access-denied", "Access Denied"); } diff --git a/imports/plugins/core/core/server/methods/shop/getLocale.js b/imports/plugins/core/core/server/methods/shop/getLocale.js index 91ad64ad697..665e1e41929 100644 --- a/imports/plugins/core/core/server/methods/shop/getLocale.js +++ b/imports/plugins/core/core/server/methods/shop/getLocale.js @@ -95,7 +95,7 @@ export default function getLocale() { }); // adjust user currency - const account = Accounts.findOne({ userId: Meteor.userId() }); + const account = Accounts.findOne({ userId: Reaction.getUserId() }); let profileCurrency = account && account.profile && account.profile.currency; if (account && !profileCurrency) { [localeCurrency] = localeCurrency; diff --git a/imports/plugins/core/core/server/methods/shop/updateBrandAssets.js b/imports/plugins/core/core/server/methods/shop/updateBrandAssets.js index 7307f37f057..a1b2cf9cbff 100644 --- a/imports/plugins/core/core/server/methods/shop/updateBrandAssets.js +++ b/imports/plugins/core/core/server/methods/shop/updateBrandAssets.js @@ -10,11 +10,11 @@ import { Shops } from "/lib/collections"; * @param {String} shopId - the shop id corresponding to the shop for which * the asset should be applied (defaults to Reaction.getShopId()) * @param {String} userId - the user id on whose behalf we are performing this - * action (defaults to Meteor.userId()) + * action (defaults to logged in user ID) * @return {Int} returns update result * @private */ -function updateShopBrandAssets(asset, shopId = Reaction.getShopId(), userId = Meteor.userId()) { +function updateShopBrandAssets(asset, shopId = Reaction.getShopId(), userId = Reaction.getUserId) { check(asset, { mediaId: String, type: String diff --git a/imports/plugins/core/core/server/methods/updatePackage.js b/imports/plugins/core/core/server/methods/updatePackage.js index 988745339c6..52da943ac64 100644 --- a/imports/plugins/core/core/server/methods/updatePackage.js +++ b/imports/plugins/core/core/server/methods/updatePackage.js @@ -20,7 +20,7 @@ export default function updatePackage(packageName, field, value) { check(field, String); check(value, Object); - const userId = Meteor.userId(); + const userId = Reaction.getUserId(); const shopId = Reaction.getShopId(); if (!Reaction.hasPermission([packageName], userId, shopId)) { throw new ReactionError("access-denied", `Access Denied. You don't have permissions for the ${packageName} package.`); diff --git a/imports/plugins/core/core/server/publications/collections/accounts.js b/imports/plugins/core/core/server/publications/collections/accounts.js index eeba54dba03..dc2709179fc 100644 --- a/imports/plugins/core/core/server/publications/collections/accounts.js +++ b/imports/plugins/core/core/server/publications/collections/accounts.js @@ -9,7 +9,7 @@ import Reaction from "/imports/plugins/core/core/server/Reaction"; */ Meteor.publish("Accounts", function () { - const userId = Meteor.userId(); + const userId = Reaction.getUserId(); if (!userId) { return this.ready(); diff --git a/imports/plugins/core/core/server/publications/collections/cart.js b/imports/plugins/core/core/server/publications/collections/cart.js index 81353cf07e5..a0a8282c8cb 100644 --- a/imports/plugins/core/core/server/publications/collections/cart.js +++ b/imports/plugins/core/core/server/publications/collections/cart.js @@ -2,13 +2,14 @@ import { Meteor } from "meteor/meteor"; import { check, Match } from "meteor/check"; import { Accounts, Cart, MediaRecords } from "/lib/collections"; import hashLoginToken from "/imports/plugins/core/accounts/server/no-meteor/util/hashLoginToken"; +import Reaction from "/imports/plugins/core/core/server/Reaction"; Meteor.publish("Cart", function (accountId, anonymousCarts, shopId) { check(accountId, Match.Maybe(String)); check(anonymousCarts, Match.Maybe([Object])); check(shopId, Match.Maybe(String)); - const userId = Meteor.userId(); + const userId = Reaction.getUserId(); let account; if (userId) { account = Accounts.findOne({ userId }, { fields: { _id: 1 } }); diff --git a/imports/plugins/core/core/server/publications/collections/orders.js b/imports/plugins/core/core/server/publications/collections/orders.js index 53a54bf48b3..2bbe1111681 100644 --- a/imports/plugins/core/core/server/publications/collections/orders.js +++ b/imports/plugins/core/core/server/publications/collections/orders.js @@ -138,7 +138,7 @@ Meteor.publish("AccountOrders", function (accountId) { const account = this.userId ? Accounts.findOne({ userId: this.userId }) : null; const contextAccountId = account && account._id; - if (accountId !== contextAccountId && !Reaction.hasPermission("orders", Meteor.userId(), shopId)) { + if (accountId !== contextAccountId && !Reaction.hasPermission("orders", Reaction.getUserId(), shopId)) { return this.ready(); } @@ -159,7 +159,7 @@ Meteor.publish("AccountOrders", function (accountId) { Meteor.publish("CompletedCartOrder", (cartId) => { check(cartId, String); - const userId = Meteor.userId(); + const userId = Reaction.getUserId(); const account = userId ? Accounts.findOne({ userId }) : null; const accountId = account && account._id; diff --git a/imports/plugins/core/core/server/startup/collection-security.js b/imports/plugins/core/core/server/startup/collection-security.js index 5e84a01bb74..12e18f58a32 100644 --- a/imports/plugins/core/core/server/startup/collection-security.js +++ b/imports/plugins/core/core/server/startup/collection-security.js @@ -46,7 +46,7 @@ export default function () { if (!arg) throw new Error("ifHasRole security rule method requires an argument"); if (arg.role) { // Note: userId is passed to getShopId to ensure that it returns the correct shop based on the User Preference - // if not passed, getShopId can default to primaryShopId if Meteor.userId is not available in the context the code is run + // if not passed, getShopId can default to primaryShopId if userId is not available in the context the code is run const shopId = Reaction.getUserShopId(userId) || Reaction.getShopId(); return Roles.userIsInRole(userId, arg.role, shopId); @@ -61,7 +61,7 @@ export default function () { fetch: [], deny(type, arg, userId, doc) { // Note: userId is passed to getShopId to ensure that it returns the correct shop based on the User Preference - // if not passed, getShopId can default to primaryShopId if Meteor.userId is not available in the context the code is run + // if not passed, getShopId can default to primaryShopId if userId is not available in the context the code is run const shopId = Reaction.getUserShopId(userId) || Reaction.getShopId(); return doc.shopId !== shopId; @@ -73,7 +73,7 @@ export default function () { fetch: [], deny(type, arg, userId, doc) { // Note: userId is passed to getShopId to ensure that it returns the correct shop based on the User Preference - // if not passed, getShopId can default to primaryShopId if Meteor.userId is not available in the context the code is run + // if not passed, getShopId can default to primaryShopId if userId is not available in the context the code is run const shopId = Reaction.getUserShopId(userId) || Reaction.getShopId(); return doc._id !== shopId; @@ -84,7 +84,7 @@ export default function () { fetch: [], deny(type, arg, userId, doc) { // Note: userId is passed to getShopId to ensure that it returns the correct shop based on the User Preference - // if not passed, getShopId can default to primaryShopId if Meteor.userId is not available in the context the code is run + // if not passed, getShopId can default to primaryShopId if userId is not available in the context the code is run const shopId = Reaction.getUserShopId(userId) || Reaction.getShopId(); return doc.metadata.shopId !== shopId; diff --git a/imports/plugins/core/core/server/util/connectionDataStore.js b/imports/plugins/core/core/server/util/connectionDataStore.js index 312f8fe38f0..84ca7083079 100644 --- a/imports/plugins/core/core/server/util/connectionDataStore.js +++ b/imports/plugins/core/core/server/util/connectionDataStore.js @@ -4,7 +4,6 @@ const inMemoryCache = {}; const connectionKey = "connection-data"; function connectionData() { - // similar to Meteor.userId() const invocation = DDP._CurrentMethodInvocation.get() || DDP._CurrentPublicationInvocation.get(); diff --git a/imports/plugins/core/dashboard/client/containers/packageListContainer.js b/imports/plugins/core/dashboard/client/containers/packageListContainer.js index 795f5d476fa..7a1edc3b46d 100644 --- a/imports/plugins/core/dashboard/client/containers/packageListContainer.js +++ b/imports/plugins/core/dashboard/client/containers/packageListContainer.js @@ -1,6 +1,5 @@ import React from "react"; import { composeWithTracker } from "@reactioncommerce/reaction-components"; -import { Meteor } from "meteor/meteor"; import { Template } from "meteor/templating"; import { Roles } from "meteor/alanning:roles"; import { Reaction } from "/client/api"; @@ -43,7 +42,7 @@ function handleOpenShortcut(event, app) { } function composer(props, onData) { - const audience = Roles.getRolesForUser(Meteor.userId(), Reaction.getShopId()); + const audience = Roles.getRolesForUser(Reaction.getUserId(), Reaction.getShopId()); const settings = Reaction.Apps({ provides: "settings", enabled: true, audience }) || []; const dashboard = Reaction.Apps({ provides: "dashboard", enabled: true, audience }) diff --git a/imports/plugins/core/dashboard/client/containers/toolbarContainer.js b/imports/plugins/core/dashboard/client/containers/toolbarContainer.js index a3f930b4bec..d2020b11a19 100644 --- a/imports/plugins/core/dashboard/client/containers/toolbarContainer.js +++ b/imports/plugins/core/dashboard/client/containers/toolbarContainer.js @@ -59,7 +59,7 @@ function composer(props, onData) { const registryItems = Reaction.Apps({ provides: "settings", container: "dashboard" }); for (const item of registryItems) { - if (Reaction.hasPermission(item.route, Meteor.userId())) { + if (Reaction.hasPermission(item.route, Reaction.getUserId())) { let { icon } = item; if (!item.icon && item.provides && item.provides.includes("settings")) { icon = "gear"; @@ -84,7 +84,7 @@ function composer(props, onData) { isPreview: Reaction.isPreview(), isActionViewAtRootView: Reaction.isActionViewAtRootView(), actionViewIsOpen: Reaction.isActionViewOpen(), - hasCreateProductAccess: Reaction.hasPermission("createProduct", Meteor.userId(), Reaction.getShopId()), + hasCreateProductAccess: Reaction.hasPermission("createProduct", Reaction.getUserId(), Reaction.getShopId()), shopId: Reaction.getShopId(), shops, diff --git a/imports/plugins/core/dashboard/client/templates/import/import.js b/imports/plugins/core/dashboard/client/templates/import/import.js index cf6e1793c0e..67f5c00b4a5 100644 --- a/imports/plugins/core/dashboard/client/templates/import/import.js +++ b/imports/plugins/core/dashboard/client/templates/import/import.js @@ -6,7 +6,7 @@ import { Media } from "/imports/plugins/core/files/client"; function uploadHandler(event) { const shopId = Reaction.getShopId(); - const userId = Meteor.userId(); + const userId = Reaction.getUserId(); const { files } = event.target.files; for (let i = 0; i < files.length; i += 1) { diff --git a/imports/plugins/core/dashboard/client/templates/shop/settings/settings.js b/imports/plugins/core/dashboard/client/templates/shop/settings/settings.js index 59752af9b12..fe959bb95eb 100644 --- a/imports/plugins/core/dashboard/client/templates/shop/settings/settings.js +++ b/imports/plugins/core/dashboard/client/templates/shop/settings/settings.js @@ -38,7 +38,7 @@ Template.shopSettings.helpers({ selectedMediaId = shop.brandAssets[0].mediaId; } - const userId = Meteor.userId(); + const userId = Reaction.getUserId(); const metadata = { type: "brandAsset", ownerId: userId, shopId }; const brandMediaList = Media.findLocal({ diff --git a/imports/plugins/core/discounts/server/methods/methods.js b/imports/plugins/core/discounts/server/methods/methods.js index 019eeed3940..16689380133 100644 --- a/imports/plugins/core/discounts/server/methods/methods.js +++ b/imports/plugins/core/discounts/server/methods/methods.js @@ -90,7 +90,7 @@ export const methods = { const transaction = { cartId, - userId: Meteor.userId(), + userId: Reaction.getUserId(), appliedAt: new Date() }; // double duty validation, plus we need the method diff --git a/imports/plugins/core/i18n/client/containers/currencyDropdown.js b/imports/plugins/core/i18n/client/containers/currencyDropdown.js index a2bd971c5e8..ebcc9aa7af5 100644 --- a/imports/plugins/core/i18n/client/containers/currencyDropdown.js +++ b/imports/plugins/core/i18n/client/containers/currencyDropdown.js @@ -44,7 +44,7 @@ const composer = (props, onData) => { }); const user = Accounts.findOne({ - _id: Meteor.userId() + _id: Reaction.getUserId() }); const profileCurrency = user && user.profile && user.profile.currency; diff --git a/imports/plugins/core/i18n/client/containers/languageDropdown.js b/imports/plugins/core/i18n/client/containers/languageDropdown.js index 83196aed853..dcd33317dec 100644 --- a/imports/plugins/core/i18n/client/containers/languageDropdown.js +++ b/imports/plugins/core/i18n/client/containers/languageDropdown.js @@ -7,7 +7,7 @@ import LanguageDropdown from "../components/languageDropdown"; const handlers = { handleChange(value) { - Meteor.users.update(Meteor.userId(), { $set: { "profile.lang": value } }); + Meteor.users.update(Reaction.getUserId(), { $set: { "profile.lang": value } }); } }; diff --git a/imports/plugins/core/i18n/server/methods/flushAllTranslations.js b/imports/plugins/core/i18n/server/methods/flushAllTranslations.js index b47d2c79b0c..960abfdb3b9 100644 --- a/imports/plugins/core/i18n/server/methods/flushAllTranslations.js +++ b/imports/plugins/core/i18n/server/methods/flushAllTranslations.js @@ -1,4 +1,3 @@ -import { Meteor } from "meteor/meteor"; import Reaction from "/imports/plugins/core/core/server/Reaction"; import ReactionError from "@reactioncommerce/reaction-error"; import { reloadAllTranslations } from "/imports/plugins/core/core/server/startup/i18n"; @@ -12,7 +11,7 @@ import { reloadAllTranslations } from "/imports/plugins/core/core/server/startup * @return {undefined} */ export default function flushAllTranslations() { - if (!Reaction.hasPermission("admin", Meteor.userId(), Reaction.getPrimaryShopId())) { + if (!Reaction.hasPermission("admin", Reaction.getUserId(), Reaction.getPrimaryShopId())) { throw new ReactionError("access-denied", "Access Denied"); } diff --git a/imports/plugins/core/orders/client/components/lineItems.js b/imports/plugins/core/orders/client/components/lineItems.js index b8f322cd992..371edc6f4d5 100644 --- a/imports/plugins/core/orders/client/components/lineItems.js +++ b/imports/plugins/core/orders/client/components/lineItems.js @@ -343,7 +343,7 @@ class LineItems extends Component { ))} { - Roles.userIsInRole(Meteor.userId(), ["orders", "dashboard/orders"], Reaction.getShopId()) && + Roles.userIsInRole(Reaction.getUserId(), ["orders", "dashboard/orders"], Reaction.getShopId()) && this.renderPopOver() } diff --git a/imports/plugins/core/orders/server/methods/addOrderEmail.js b/imports/plugins/core/orders/server/methods/addOrderEmail.js index 48a9dbbf7a7..12b45b1784c 100644 --- a/imports/plugins/core/orders/server/methods/addOrderEmail.js +++ b/imports/plugins/core/orders/server/methods/addOrderEmail.js @@ -1,7 +1,7 @@ -import { Meteor } from "meteor/meteor"; import { check } from "meteor/check"; import ReactionError from "@reactioncommerce/reaction-error"; import { Orders } from "/lib/collections"; +import Reaction from "/imports/plugins/core/core/server/Reaction"; /** * @name orders/addOrderEmail @@ -21,7 +21,7 @@ export default function addOrderEmail(cartId, email) { *provided for tracking order progress. */ - if (!Meteor.userId()) { + if (!Reaction.getUserId()) { throw new ReactionError("access-denied", "Access Denied. You are not connected."); } diff --git a/imports/plugins/core/orders/server/methods/cancelOrder.js b/imports/plugins/core/orders/server/methods/cancelOrder.js index 6b6c919fc7c..1de1e745c23 100644 --- a/imports/plugins/core/orders/server/methods/cancelOrder.js +++ b/imports/plugins/core/orders/server/methods/cancelOrder.js @@ -36,7 +36,7 @@ export default function cancelOrder(order, returnToStock) { // Run this Product update inline instead of using ordersInventoryAdjust because the collection hooks fail // in some instances which causes the order not to cancel order.items.forEach((item) => { - if (Reaction.hasPermission("orders", Meteor.userId(), item.shopId)) { + if (Reaction.hasPermission("orders", Reaction.getUserId(), item.shopId)) { Products.update( { _id: item.variantId, diff --git a/imports/plugins/core/orders/server/methods/updateHistory.js b/imports/plugins/core/orders/server/methods/updateHistory.js index 6a0c724c41b..b214bb12567 100644 --- a/imports/plugins/core/orders/server/methods/updateHistory.js +++ b/imports/plugins/core/orders/server/methods/updateHistory.js @@ -29,7 +29,7 @@ export default function updateHistory(orderId, event, value) { history: { event, value, - userId: Meteor.userId(), + userId: Reaction.getUserId(), updatedAt: new Date() } } diff --git a/imports/plugins/core/router/lib/router.js b/imports/plugins/core/router/lib/router.js index 3902ded2297..4715a4c6802 100644 --- a/imports/plugins/core/router/lib/router.js +++ b/imports/plugins/core/router/lib/router.js @@ -14,6 +14,7 @@ import { Tracker } from "meteor/tracker"; import { Packages, Shops } from "/lib/collections"; import { getComponent } from "@reactioncommerce/reaction-components/components"; import Hooks from "./hooks"; +import { Reaction } from "/lib/api"; // Using a ternary operator here to avoid a mutable export - open to suggestions for a better way to do this export const history = Meteor.isClient ? createBrowserHistory() : createMemoryHistory(); @@ -348,7 +349,7 @@ function hasRoutePermission(route) { return routeName === "index" || routeName === "not-found" || - Router.Reaction.hasPermission(route.permissions, Meteor.userId()); + Router.Reaction.hasPermission(route.permissions, Reaction.getUserId()); } @@ -515,7 +516,7 @@ function ReactionLayout(options = {}) { // If the current route is unauthorized, and is not the "not-found" route, // then override the template to use the default unauthorized template if (hasRoutePermission({ ...route, permissions }) === false && route.name !== "not-found" && !Meteor.user()) { - if (!Router.Reaction.hasPermission(route.permissions, Meteor.userId())) { + if (!Router.Reaction.hasPermission(route.permissions, Reaction.getUserId())) { structure.template = "unauthorized"; } return false; diff --git a/imports/plugins/core/shipping/server/methods/updateShipmentQuotes.js b/imports/plugins/core/shipping/server/methods/updateShipmentQuotes.js index 0079d25bac8..0d4dfeee096 100644 --- a/imports/plugins/core/shipping/server/methods/updateShipmentQuotes.js +++ b/imports/plugins/core/shipping/server/methods/updateShipmentQuotes.js @@ -1,6 +1,6 @@ -import { Meteor } from "meteor/meteor"; import { check, Match } from "meteor/check"; import getGraphQLContextInMeteorMethod from "/imports/plugins/core/graphql/server/getGraphQLContextInMeteorMethod"; +import Reaction from "/imports/plugins/core/core/server/Reaction"; /** * @name shipping/updateShipmentQuotes @@ -18,7 +18,7 @@ export default function updateShipmentQuotesMethod(cartId, fulfillmentGroupId, c check(cartToken, Match.Maybe(String)); this.unblock(); - const context = Promise.await(getGraphQLContextInMeteorMethod(Meteor.userId())); + const context = Promise.await(getGraphQLContextInMeteorMethod(Reaction.getUserId())); return context.mutations.fulfillment.updateFulfillmentOptionsForGroup(context, { cartId, cartToken, diff --git a/imports/plugins/core/templates/server/methods.js b/imports/plugins/core/templates/server/methods.js index 1841eff8ebd..a0116d383b5 100644 --- a/imports/plugins/core/templates/server/methods.js +++ b/imports/plugins/core/templates/server/methods.js @@ -27,7 +27,7 @@ export const methods = { check(doc, Object); const shopId = Reaction.getShopId(); - const userId = Meteor.userId(); + const userId = Reaction.getUserId(); // Check that this user has permission to update templates for the active shop if (!Reaction.hasPermission("reaction-templates", userId, shopId)) { diff --git a/imports/plugins/core/ui/client/containers/avatar.js b/imports/plugins/core/ui/client/containers/avatar.js index d89f3804eef..d4d33224f7c 100644 --- a/imports/plugins/core/ui/client/containers/avatar.js +++ b/imports/plugins/core/ui/client/containers/avatar.js @@ -15,7 +15,7 @@ const composer = (props, onData) => { // If there is no email provided, no query param provide, and the avatar is for the current user, find their account if (!email && !account && props.currentUser) { - account = Accounts.findOne(Meteor.userId()); + account = Accounts.findOne(Reaction.getUserId()); } // If we now have an account, and that account has an email address, return it diff --git a/imports/plugins/core/ui/client/containers/mediaGallery.js b/imports/plugins/core/ui/client/containers/mediaGallery.js index 49200aef679..da8a0e29374 100644 --- a/imports/plugins/core/ui/client/containers/mediaGallery.js +++ b/imports/plugins/core/ui/client/containers/mediaGallery.js @@ -136,7 +136,7 @@ const wrapComponent = (Comp) => ( } const variantId = variant._id; const shopId = ReactionProduct.selectedProduct().shopId || Reaction.getShopId(); - const userId = Meteor.userId(); + const userId = Reaction.getUserId(); let count = Media.findLocal({ "metadata.variantId": variantId }).length; From 143e1dfdbf11bdecd888ff24391f46f11914bb05 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Thu, 23 Aug 2018 17:09:26 +0100 Subject: [PATCH 49/65] Change Meteor.userId() to getUserId() in /imports/plugins/included --- .../plugins/included/discount-codes/server/methods/methods.js | 2 +- .../client/templates/becomeSellerButton/becomeSellerButton.js | 2 +- .../included/notifications/client/containers/notification.js | 3 ++- .../notifications/client/containers/notificationRoute.js | 3 ++- .../included/notifications/server/hooks/notification.js | 2 +- .../included/payments-stripe/server/methods/stripe-connect.js | 4 ++-- .../included/product-variant/containers/productsContainer.js | 3 +-- 7 files changed, 10 insertions(+), 9 deletions(-) diff --git a/imports/plugins/included/discount-codes/server/methods/methods.js b/imports/plugins/included/discount-codes/server/methods/methods.js index da4f13796d0..ea562ce40ef 100644 --- a/imports/plugins/included/discount-codes/server/methods/methods.js +++ b/imports/plugins/included/discount-codes/server/methods/methods.js @@ -272,7 +272,7 @@ export const methods = { const users = Array.from(discount.transactions, (trans) => trans.userId); const transactionCount = new Map([...new Set(users)].map((userX) => [userX, users.filter((userY) => userY === userX).length])); const orders = Array.from(discount.transactions, (trans) => trans.cartId); - userCount = transactionCount.get(Meteor.userId()); + userCount = transactionCount.get(Reaction.getUserId()); orderCount = orders.length; } // check limits diff --git a/imports/plugins/included/marketplace/client/templates/becomeSellerButton/becomeSellerButton.js b/imports/plugins/included/marketplace/client/templates/becomeSellerButton/becomeSellerButton.js index 66e16cb15e6..fc1c8ca484d 100644 --- a/imports/plugins/included/marketplace/client/templates/becomeSellerButton/becomeSellerButton.js +++ b/imports/plugins/included/marketplace/client/templates/becomeSellerButton/becomeSellerButton.js @@ -4,7 +4,7 @@ import { Reaction, i18next } from "/client/api"; Template.becomeSellerButton.events({ "click [data-event-action='button-click-become-seller']"() { - Meteor.call("shop/createShop", Meteor.userId(), (error, response) => { + Meteor.call("shop/createShop", Reaction.getUserId(), (error, response) => { if (error) { const errorMessage = i18next.t("marketplace.errorCannotCreateShop", { defaultValue: "Could not create shop for current user {{user}}" }); return Alerts.toast(`${errorMessage} ${error}`, "error"); diff --git a/imports/plugins/included/notifications/client/containers/notification.js b/imports/plugins/included/notifications/client/containers/notification.js index ca6108bd789..2e4080ff559 100644 --- a/imports/plugins/included/notifications/client/containers/notification.js +++ b/imports/plugins/included/notifications/client/containers/notification.js @@ -2,10 +2,11 @@ import { compose, withProps } from "recompose"; import { registerComponent, composeWithTracker } from "@reactioncommerce/reaction-components"; import { Meteor } from "meteor/meteor"; import { Notifications } from "/lib/collections"; +import { Reaction } from "/client/api"; import { Notification } from "../components"; function composer(props, onData) { - if (Meteor.subscribe("Notification", Meteor.userId()).ready()) { + if (Meteor.subscribe("Notification", Reaction.getUserId()).ready()) { const notificationList = Notifications.find({}, { sort: { timeSent: -1 }, limit: 5 }).fetch(); const unread = Notifications.find({ status: "unread" }).count(); diff --git a/imports/plugins/included/notifications/client/containers/notificationRoute.js b/imports/plugins/included/notifications/client/containers/notificationRoute.js index 2fe1047f2ba..81294474303 100644 --- a/imports/plugins/included/notifications/client/containers/notificationRoute.js +++ b/imports/plugins/included/notifications/client/containers/notificationRoute.js @@ -2,6 +2,7 @@ import { compose, withProps } from "recompose"; import { registerComponent, composeWithTracker } from "@reactioncommerce/reaction-components"; import { Meteor } from "meteor/meteor"; import { Notifications } from "/lib/collections"; +import { Reaction } from "/client/api"; import { NotificationRoute } from "../components"; const handlers = { @@ -14,7 +15,7 @@ const handlers = { }; function composer(props, onData) { - if (Meteor.subscribe("Notification", Meteor.userId()).ready()) { + if (Meteor.subscribe("Notification", Reaction.getUserId()).ready()) { const notificationList = Notifications.find({}, { sort: { timeSent: -1 } }).fetch(); const unread = Notifications.find({ status: "unread" }).count(); diff --git a/imports/plugins/included/notifications/server/hooks/notification.js b/imports/plugins/included/notifications/server/hooks/notification.js index 8f9546e5eb9..caf7bd0ce01 100644 --- a/imports/plugins/included/notifications/server/hooks/notification.js +++ b/imports/plugins/included/notifications/server/hooks/notification.js @@ -33,7 +33,7 @@ const sendNotificationToAdmin = (adminUserId) => { }; MethodHooks.after("cart/copyCartToOrder", (options) => { - const userId = Meteor.userId(); + const userId = Reaction.getUserId(); const type = "newOrder"; const prefix = Reaction.getShopPrefix(); const url = `${prefix}/notifications`; diff --git a/imports/plugins/included/payments-stripe/server/methods/stripe-connect.js b/imports/plugins/included/payments-stripe/server/methods/stripe-connect.js index 2084f630c5b..c8d9658e603 100644 --- a/imports/plugins/included/payments-stripe/server/methods/stripe-connect.js +++ b/imports/plugins/included/payments-stripe/server/methods/stripe-connect.js @@ -13,8 +13,8 @@ export const methods = { check(shopId, String); check(authCode, String); - if (!Reaction.hasPermission(["owner", "admin", "reaction-stripe"], Meteor.userId(), shopId)) { - Logger.warn(`user: ${Meteor.userId()} attempted to authorize merchant account + if (!Reaction.hasPermission(["owner", "admin", "reaction-stripe"], Reaction.getUserId(), shopId)) { + Logger.warn(`user: ${Reaction.getUserId()} attempted to authorize merchant account for shopId ${shopId} but was denied access due to insufficient privileges.`); throw new ReactionError("access-denied", "Access Denied"); } diff --git a/imports/plugins/included/product-variant/containers/productsContainer.js b/imports/plugins/included/product-variant/containers/productsContainer.js index 4427aeb72b5..46626cee39b 100644 --- a/imports/plugins/included/product-variant/containers/productsContainer.js +++ b/imports/plugins/included/product-variant/containers/productsContainer.js @@ -2,7 +2,6 @@ import React from "react"; import PropTypes from "prop-types"; import { compose } from "recompose"; import { registerComponent, composeWithTracker, Components } from "@reactioncommerce/reaction-components"; -import { Meteor } from "meteor/meteor"; import { Reaction } from "/client/api"; const ProductsContainer = ({ isAdmin }) => { @@ -17,7 +16,7 @@ ProductsContainer.propTypes = { }; function composer(props, onData) { - const isAdmin = Reaction.hasPermission("createProduct", Meteor.userId()); + const isAdmin = Reaction.hasPermission("createProduct", Reaction.getUserId()); onData(null, { isAdmin From 49b9613b5d66c37fc87f2fc0321d6fe3194b065c Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Mon, 27 Aug 2018 14:55:05 +0100 Subject: [PATCH 50/65] Deprecate reaction/getUserId in favor of util function --- imports/plugins/core/accounts/server/methods/getUserId.js | 1 + 1 file changed, 1 insertion(+) diff --git a/imports/plugins/core/accounts/server/methods/getUserId.js b/imports/plugins/core/accounts/server/methods/getUserId.js index 32b0ee7725d..ea632781ce9 100644 --- a/imports/plugins/core/accounts/server/methods/getUserId.js +++ b/imports/plugins/core/accounts/server/methods/getUserId.js @@ -1,6 +1,7 @@ import { Meteor } from "meteor/meteor"; /** + * @deprecated Use Reaction.getUserId instead * @name reaction/getUserId * @method * @memberof Reaction/Methods From 3d6cd92e2a1f14da7eadb000d978bf619741858e Mon Sep 17 00:00:00 2001 From: Spencer Norman Date: Thu, 30 Aug 2018 17:52:34 -0600 Subject: [PATCH 51/65] fix: update file-collections to 0.6.0 Update @reactioncommerce/file-collections from 0.5.0 to 0.6.0. This eliminates vulnerabilities introduced through dependencies on `hoek`, `tunnel-agent`, and `cryptiles` --- package-lock.json | 503 +++++++++++++++++----------------------------- package.json | 2 +- 2 files changed, 189 insertions(+), 316 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1413a6af7ae..c6dbf0e97ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1725,9 +1725,9 @@ "integrity": "sha512-dEv1n+IFtlvLQ8/FsTOtBCC1aNT4B5abE8ODF5wk2tpWnjvgGNRMvHCeJGbVHjFfer4h8MH2w9c2/6eoJHclMg==" }, "@google-cloud/common": { - "version": "0.13.6", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.13.6.tgz", - "integrity": "sha1-qdjhN7xCmkSrqWif5qDkMxeE+FM=", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.17.0.tgz", + "integrity": "sha512-HRZLSU762E6HaKoGfJGa8W95yRjb9rY7LePhjaHK9ILAnFacMuUGVamDbTHu1csZomm1g3tZTtXfX/aAhtie/Q==", "requires": { "array-uniq": "1.0.3", "arrify": "1.0.1", @@ -1736,7 +1736,7 @@ "duplexify": "3.6.0", "ent": "2.2.0", "extend": "3.0.2", - "google-auto-auth": "0.7.2", + "google-auto-auth": "0.10.1", "is": "3.2.1", "log-driver": "1.2.7", "methmeth": "1.1.0", @@ -1747,42 +1747,34 @@ "stream-events": "1.0.4", "string-format-obj": "1.1.1", "through2": "2.0.3" - }, - "dependencies": { - "google-auto-auth": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/google-auto-auth/-/google-auto-auth-0.7.2.tgz", - "integrity": "sha512-ux2n2AE2g3+vcLXwL4dP/M12SFMRX5dzCzBfhAEkTeAB7dpyGdOIEj7nmUx0BHKaCcUQrRWg9kT63X/Mmtk1+A==", - "requires": { - "async": "2.6.1", - "gcp-metadata": "0.3.1", - "google-auth-library": "0.10.0", - "request": "2.87.0" - } - } } }, "@google-cloud/storage": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-1.1.1.tgz", - "integrity": "sha1-ZZC1zm53lVbJzHBDvWRJ1rwHgd4=", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-1.7.0.tgz", + "integrity": "sha512-QaAxzCkbhspwajoaEnT0GcnQcpjPRcBrHYuQsXtD05BtOJgVnHCLXSsfUiRdU0nVpK+Thp7+sTkQ0fvk5PanKg==", "requires": { - "@google-cloud/common": "0.13.6", + "@google-cloud/common": "0.17.0", "arrify": "1.0.1", "async": "2.6.1", + "compressible": "2.0.14", "concat-stream": "1.6.2", "create-error-class": "3.0.2", "duplexify": "3.6.0", "extend": "3.0.2", - "gcs-resumable-upload": "0.7.7", + "gcs-resumable-upload": "0.10.2", "hash-stream-validation": "0.2.1", "is": "3.2.1", + "mime": "2.3.1", "mime-types": "2.1.19", "once": "1.4.0", "pumpify": "1.5.1", + "request": "2.87.0", + "safe-buffer": "5.1.2", + "snakeize": "0.1.0", "stream-events": "1.0.4", - "string-format-obj": "1.1.1", - "through2": "2.0.3" + "through2": "2.0.3", + "xdg-basedir": "3.0.0" } }, "@material-ui/core": { @@ -1931,9 +1923,9 @@ "dev": true }, "@reactioncommerce/file-collections": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@reactioncommerce/file-collections/-/file-collections-0.5.0.tgz", - "integrity": "sha512-92983/xIT6xAnHR0+4XKVY9jUp0vpobLhG0wCyegvX/dkSRO5srL4tz1ZN/77pxe7d1MfEEYo6MLQ/YmluIw6A==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@reactioncommerce/file-collections/-/file-collections-0.6.0.tgz", + "integrity": "sha512-Y2ymZlCqY+tx8qMjIU+UHwiwBrgd01h72h7sIIMc//NagouEn9bKjMntJkmytPU4j8ExJSVgYtbal1wJTzHPVA==", "requires": { "babel-runtime": "6.26.0", "content-disposition": "0.5.2", @@ -1941,7 +1933,7 @@ "path-parser": "4.2.0", "query-string": "5.1.1", "tus-js-client": "1.5.1", - "tus-node-server": "0.2.11" + "tus-node-server": "0.3.1" }, "dependencies": { "debug": { @@ -2611,6 +2603,63 @@ "resolved": "https://registry.npmjs.org/autosize/-/autosize-4.0.2.tgz", "integrity": "sha512-jnSyH2d+qdfPGpWlcuhGiHmqBJ6g3X+8T+iRwFrHPLVcdoGJE/x6Qicm6aDHfTsbgZKxyV8UU/YB2p4cjKDRRA==" }, + "aws-sdk": { + "version": "2.306.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.306.0.tgz", + "integrity": "sha512-GEvZJ8J9+bSMHUjlXcUS5TOWCxirQDiMIdvSwXV/okKC4lJ/mQc3C/pPiSM/ppsMQRoMFJlYxO9uubkmGgwCwQ==", + "requires": { + "buffer": "4.9.1", + "events": "1.1.1", + "ieee754": "1.1.8", + "jmespath": "0.15.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.1.0", + "xml2js": "0.4.19" + }, + "dependencies": { + "buffer": { + "version": "4.9.1", + "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "requires": { + "base64-js": "1.3.0", + "ieee754": "1.1.8", + "isarray": "1.0.0" + } + }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + }, + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" + } + } + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -2621,6 +2670,15 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==" }, + "axios": { + "version": "0.18.0", + "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", + "requires": { + "follow-redirects": "1.5.7", + "is-buffer": "1.1.6" + } + }, "axobject-query": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.1.tgz", @@ -3376,14 +3434,6 @@ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "dev": true }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "requires": { - "hoek": "2.16.3" - } - }, "bootstrap": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.3.7.tgz", @@ -3594,11 +3644,6 @@ "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", "dev": true }, - "buffer-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", - "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=" - }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -4003,7 +4048,8 @@ "commander": { "version": "2.16.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.16.0.tgz", - "integrity": "sha512-sVXqklSaotK9at437sFlFpyOcJonxe0yST/AG9DkQKUdIE6IqGIMv4SfAQSKaJbSdVEJYItASCrBiVQHq1HQew==" + "integrity": "sha512-sVXqklSaotK9at437sFlFpyOcJonxe0yST/AG9DkQKUdIE6IqGIMv4SfAQSKaJbSdVEJYItASCrBiVQHq1HQew==", + "dev": true }, "commondir": { "version": "1.0.1", @@ -4023,6 +4069,14 @@ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", "dev": true }, + "compressible": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.14.tgz", + "integrity": "sha1-MmxfUH+7BV9UEWeCuWmoG2einac=", + "requires": { + "mime-db": "1.35.0" + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4205,14 +4259,6 @@ "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "requires": { - "boom": "2.10.1" - } - }, "crypto-rand": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/crypto-rand/-/crypto-rand-0.0.2.tgz", @@ -5466,6 +5512,11 @@ "through": "2.3.8" } }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, "exec-sh": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.2.tgz", @@ -6079,6 +6130,24 @@ "resolved": "https://registry.npmjs.org/flushwritable/-/flushwritable-1.0.0.tgz", "integrity": "sha1-PjKNj95BKtR+c44751C00pAENJg=" }, + "follow-redirects": { + "version": "1.5.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.7.tgz", + "integrity": "sha512-NONJVIFiX7Z8k2WxfqBjtwqMifx7X42ORLFrOZ2LTKGj71G3C0kfdyTqGqr8fx5zSX6Foo/D95dgGWbPUiwnew==", + "requires": { + "debug": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, "font-awesome": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", @@ -6865,166 +6934,25 @@ } }, "gcp-metadata": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.3.1.tgz", - "integrity": "sha512-5kJPX/RXuqoLmHiOOgkSDk/LI0QaXpEvZ3pvQP4ifjGGDKZKVSOjL/GcDjXA5kLxppFCOjmmsu0Uoop9d1upaQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.6.3.tgz", + "integrity": "sha512-MSmczZctbz91AxCvqp9GHBoZOSbJKAICV7Ow/AIWSJZRrRchUd5NL1b2P4OfP+4m490BEUPhhARfpHdqCxuCvg==", "requires": { + "axios": "0.18.0", "extend": "3.0.2", - "retry-request": "3.3.2" + "retry-axios": "0.3.2" } }, "gcs-resumable-upload": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-0.7.7.tgz", - "integrity": "sha1-2clyWvlwu8hsvwr+8kBtwizpGGQ=", + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-0.10.2.tgz", + "integrity": "sha1-fymz7iPc7EFwNnwHEUGCScZgVF8=", "requires": { - "buffer-equal": "1.0.0", "configstore": "3.1.2", - "google-auto-auth": "0.6.1", + "google-auto-auth": "0.10.1", "pumpify": "1.5.1", "request": "2.87.0", - "stream-events": "1.0.4", - "through2": "2.0.3" - }, - "dependencies": { - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" - }, - "caseless": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", - "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=" - }, - "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.6", - "mime-types": "2.1.19" - } - }, - "gcp-metadata": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.1.0.tgz", - "integrity": "sha1-q+IfHqMk3Qs0o/BsqBdj+x7uN9k=", - "requires": { - "extend": "3.0.2", - "retry-request": "1.3.2" - } - }, - "google-auto-auth": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/google-auto-auth/-/google-auto-auth-0.6.1.tgz", - "integrity": "sha1-wF2CDpRUc57PKKiJLuqz0WJPLLM=", - "requires": { - "async": "2.6.1", - "gcp-metadata": "0.1.0", - "google-auth-library": "0.10.0", - "object-assign": "3.0.0", - "request": "2.87.0" - } - }, - "har-validator": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", - "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", - "requires": { - "chalk": "1.1.3", - "commander": "2.16.0", - "is-my-json-valid": "2.17.2", - "pinkie-promise": "2.0.1" - } - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.1", - "sshpk": "1.14.2" - } - }, - "node-uuid": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=" - }, - "object-assign": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", - "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=" - }, - "qs": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", - "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=" - }, - "retry-request": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-1.3.2.tgz", - "integrity": "sha1-Wa0k5x+K4/MS1fe0vPRnpeWle9Y=", - "requires": { - "request": "2.76.0", - "through2": "2.0.3" - }, - "dependencies": { - "request": { - "version": "2.76.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.76.0.tgz", - "integrity": "sha1-vkRQWv73A2CgQ2lVEGvjlF2VVg4=", - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.7.0", - "caseless": "0.11.0", - "combined-stream": "1.0.6", - "extend": "3.0.2", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "2.0.6", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.19", - "node-uuid": "1.4.8", - "oauth-sign": "0.8.2", - "qs": "6.3.2", - "stringstream": "0.0.6", - "tough-cookie": "2.3.4", - "tunnel-agent": "0.4.3" - } - } - } - }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=" - } - } - }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=" - }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "requires": { - "is-property": "1.0.2" + "stream-events": "1.0.4" } }, "get-caller-file": { @@ -7235,47 +7163,37 @@ } }, "google-auth-library": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-0.10.0.tgz", - "integrity": "sha1-bhW6vuhf0d0U2NEoopW2g41SE24=", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-1.6.1.tgz", + "integrity": "sha512-jYiWC8NA9n9OtQM7ANn0Tk464do9yhKEtaJ72pKcaBiEwn4LwcGYIYOfwtfsSm3aur/ed3tlSxbmg24IAT6gAg==", "requires": { - "gtoken": "1.2.3", + "axios": "0.18.0", + "gcp-metadata": "0.6.3", + "gtoken": "2.3.0", "jws": "3.1.5", - "lodash.noop": "3.0.1", - "request": "2.87.0" + "lodash.isstring": "4.0.1", + "lru-cache": "4.1.3", + "retry-axios": "0.3.2" } }, "google-auto-auth": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/google-auto-auth/-/google-auto-auth-0.8.2.tgz", - "integrity": "sha512-W91J1paFbyG45gpDWdTu9tKDxbiTDWYkOAxytNVF4oHVVgTCBV/8+lWdjj/6ldjN3eb+sEd9PKJBjm0kmCxvcw==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/google-auto-auth/-/google-auto-auth-0.10.1.tgz", + "integrity": "sha512-iIqSbY7Ypd32mnHGbYctp80vZzXoDlvI9gEfvtl3kmyy5HzOcrZCIGCBdSlIzRsg7nHpQiHE3Zl6Ycur6TSodQ==", "requires": { "async": "2.6.1", - "gcp-metadata": "0.3.1", - "google-auth-library": "0.12.0", + "gcp-metadata": "0.6.3", + "google-auth-library": "1.6.1", "request": "2.87.0" - }, - "dependencies": { - "google-auth-library": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-0.12.0.tgz", - "integrity": "sha512-79qCXtJ1VweBmmLr4yLq9S4clZB2p5Y+iACvuKk9gu4JitEnPc+bQFmYvtCYehVR44MQzD1J8DVmYW2w677IEw==", - "requires": { - "gtoken": "1.2.3", - "jws": "3.1.5", - "lodash.isstring": "4.0.1", - "lodash.merge": "4.6.1", - "request": "2.87.0" - } - } } }, "google-p12-pem": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-0.1.2.tgz", - "integrity": "sha1-M8RqsCGqc0+gMys5YKmj/8svMXc=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.2.tgz", + "integrity": "sha512-+EuKr4CLlGsnXx4XIJIVkcKYrsa2xkAmCvxRhX2HsazJzUBAJ35wARGeApHUn4nNfPD03Vl057FskNr20VaCyg==", "requires": { - "node-forge": "0.7.5" + "node-forge": "0.7.6", + "pify": "3.0.0" } }, "got": { @@ -7426,14 +7344,15 @@ "dev": true }, "gtoken": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-1.2.3.tgz", - "integrity": "sha512-wQAJflfoqSgMWrSBk9Fg86q+sd6s7y6uJhIvvIPz++RElGlMtEqsdAR2oWwZ/WTEtp7P9xFbJRrT976oRgzJ/w==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.0.tgz", + "integrity": "sha512-Jc9/8mV630cZE9FC5tIlJCZNdUjwunvlwOtCz6IDlaiB4Sz68ki29a1+q97sWTnTYroiuF9B135rod9zrQdHLw==", "requires": { - "google-p12-pem": "0.1.2", + "axios": "0.18.0", + "google-p12-pem": "1.0.2", "jws": "3.1.5", - "mime": "1.6.0", - "request": "2.87.0" + "mime": "2.3.1", + "pify": "3.0.0" } }, "handlebars": { @@ -7564,17 +7483,6 @@ "through2": "2.0.3" } }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, "history": { "version": "4.7.2", "resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz", @@ -7587,11 +7495,6 @@ "warning": "3.0.0" } }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" - }, "hoist-non-react-statics": { "version": "2.5.5", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", @@ -8110,23 +8013,6 @@ "is-path-inside": "1.0.1" } }, - "is-my-ip-valid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", - "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==" - }, - "is-my-json-valid": { - "version": "2.17.2", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz", - "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==", - "requires": { - "generate-function": "2.0.0", - "generate-object-property": "1.2.0", - "is-my-ip-valid": "1.0.0", - "jsonpointer": "4.0.1", - "xtend": "4.0.1" - } - }, "is-natural-number": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", @@ -8215,11 +8101,6 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" - }, "is-redirect": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", @@ -9902,6 +9783,11 @@ "merge-stream": "1.0.1" } }, + "jmespath": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" + }, "jquery-i18next": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/jquery-i18next/-/jquery-i18next-1.2.1.tgz", @@ -10072,11 +9958,6 @@ "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", "dev": true }, - "jsonpointer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=" - }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -10284,9 +10165,9 @@ } }, "libphonenumber-js": { - "version": "1.2.21", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.2.21.tgz", - "integrity": "sha512-JmBdkEXfmadHZBVqGQtga+oVkK0bIxfSTIeq7+AmsxCcvezJv9qF6J6R0MpISHQhtvTb81WjPbVKN1QAioZdRQ==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.4.2.tgz", + "integrity": "sha512-yt2aUW10S+07wM/ZCrR4GCBVwBUuiiiitVOR0Zahgaa23zXElY3GUCGpoUl4sxj5aQb9XS1WAotAwtq+27Istg==", "requires": { "minimist": "1.2.0", "semver-compare": "1.0.0", @@ -10569,11 +10450,6 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==" }, - "lodash.noop": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash.noop/-/lodash.noop-3.0.1.tgz", - "integrity": "sha1-OBiPTWUKOkdCWEObluxFsyYXEzw=" - }, "lodash.omit": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", @@ -11478,9 +11354,9 @@ } }, "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", + "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==" }, "mime-db": { "version": "1.35.0", @@ -11874,9 +11750,9 @@ "integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=" }, "node-forge": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", - "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==" + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.6.tgz", + "integrity": "sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw==" }, "node-geocoder": { "version": "3.22.0", @@ -12582,12 +12458,14 @@ "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true }, "pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, "requires": { "pinkie": "2.0.4" } @@ -14048,6 +13926,11 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, + "retry-axios": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/retry-axios/-/retry-axios-0.3.2.tgz", + "integrity": "sha512-jp4YlI0qyDFfXiXGhkCOliBN1G7fRH03Nqy8YdShzGqbY5/9S2x/IR6C88ls2DFkbWuL3ASkP7QD3pVrNpPgwQ==" + }, "retry-request": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-3.3.2.tgz", @@ -14493,6 +14376,11 @@ "integrity": "sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY=", "dev": true }, + "snakeize": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/snakeize/-/snakeize-0.1.0.tgz", + "integrity": "sha1-EMCI2LWOsHazIpu1oE4jLOEmQi0=" + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -14601,14 +14489,6 @@ "kind-of": "3.2.2" } }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "requires": { - "hoek": "2.16.3" - } - }, "snyk": { "version": "1.89.0", "resolved": "https://registry.npmjs.org/snyk/-/snyk-1.89.0.tgz", @@ -15265,11 +15145,6 @@ "safe-buffer": "5.1.2" } }, - "stringstream": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", - "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==" - }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -15897,17 +15772,15 @@ } }, "tus-node-server": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/tus-node-server/-/tus-node-server-0.2.11.tgz", - "integrity": "sha512-1Eb/GGejUTUpCpuHbol8OsQDk38Mx8jPLne+0VvPTKVv6Gvoj+Q7trjc5mQHQqCd8vIYsXHEEJ/46YR1bZZJig==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/tus-node-server/-/tus-node-server-0.3.1.tgz", + "integrity": "sha512-tcb9HLfX8TSnjpQZ2jOtuSHx5vxf4TEG5XZ2/TDrJxnQO7lgftxEZ9XiEqrIbtH5xmunRN2ao0GC1nfF/t5IAg==", "requires": { - "@google-cloud/storage": "1.1.1", + "@google-cloud/storage": "1.7.0", + "aws-sdk": "2.306.0", "configstore": "3.1.2", "crypto-rand": "0.0.2", - "debug": "3.1.0", - "google-auto-auth": "0.8.2", - "object-assign": "4.1.1", - "request": "2.87.0" + "debug": "3.1.0" }, "dependencies": { "debug": { diff --git a/package.json b/package.json index 7f4e8e82675..f576087a3bf 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "@reactioncommerce/components": "0.21.0", "@reactioncommerce/components-context": "1.0.0", "@reactioncommerce/data-factory": "^1.0.0", - "@reactioncommerce/file-collections": "0.5.0", + "@reactioncommerce/file-collections": "0.6.0", "@reactioncommerce/file-collections-sa-gridfs": "0.0.2", "@reactioncommerce/hooks": "1.0.2", "@reactioncommerce/job-queue": "1.0.4", From 51fae0ea2ec0aa583954276d7634665010fe0c14 Mon Sep 17 00:00:00 2001 From: Spencer Norman Date: Thu, 30 Aug 2018 17:53:37 -0600 Subject: [PATCH 52/65] chore: update .synk ignore file Update snyk ignore file to remove dependencies that we've eliminated and update descriptions of ignores that need to continue to be in place. --- .snyk | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/.snyk b/.snyk index c8f83d03c78..35dc207f22f 100644 --- a/.snyk +++ b/.snyk @@ -4,21 +4,12 @@ version: v1.11.0 ignore: 'npm:bootstrap:20160627': - bootstrap: - reason: We're not going to update to Bootstrap 4 any time soon - expires: '2018-08-26T20:23:03.274Z' - 'npm:hoek:20180212': - - '*': - reason: "PR Accepted to tus-node-server which will resolve this vulnerability https://github.com/tus/tus-node-server/pull/120" - expires: '2018-08-26T20:23:03.274Z' - 'npm:tunnel-agent:20170305': - - '*': - reason: "PR Accepted to tus-node-server which will resolve this vulnerability https://github.com/tus/tus-node-server/pull/120" - expires: '2018-08-26T20:23:03.274Z' - 'npm:cryptiles:20180710': - - '*': - reason: "PR Accepted to tus-node-server which will resolve this vulnerability https://github.com/tus/tus-node-server/pull/120" - expires: '2018-08-26T20:23:03.274Z' + reason: We're not going to update to Bootstrap 4 any time soon + expires: '2018-10-26T20:23:03.274Z' 'npm:chownr:20180731': - '*': - reason: "waiting on chownr to resolve issue" - expires: '2018-09-01T20:23:03.274Z' + reason: "PR in chownr to resolve issue will require node >= 10.8 https://github.com/isaacs/chownr/pull/15" + expires: '2018-09-29T20:23:03.274Z' + 'npm:mem:20180117': + - '*': + reason: String of dependencies that need to be updated - oslocale (PR https://github.com/sindresorhus/os-locale/pull/31) followed by yargs then transliteration From 4bce0521ea9f85e2aeade2129464e05a24a10196 Mon Sep 17 00:00:00 2001 From: Spencer Norman Date: Thu, 30 Aug 2018 18:01:42 -0600 Subject: [PATCH 53/65] chore: bump package version to 1.16.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1413a6af7ae..8a33601bef0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "reaction", - "version": "1.15.0", + "version": "1.16.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 7f4e8e82675..3ba75910bc2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "reaction", "description": "Reaction is a modern reactive, real-time event driven ecommerce platform.", - "version": "1.15.0", + "version": "1.16.0", "main": "main.js", "directories": { "test": "tests" From adb0d197c78d3911785c770b208cc68e68215d84 Mon Sep 17 00:00:00 2001 From: Spencer Norman Date: Thu, 30 Aug 2018 18:03:47 -0600 Subject: [PATCH 54/65] chore: update staging deploy CI workflow to use 1.16.x --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 57e51d0033c..bcefa4ca219 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -335,7 +335,7 @@ workflows: - docker-push filters: branches: - only: /^release-1\.15\.\d+$/ + only: /^release-1\.16\.\d+$/ - deploy-docs: requires: - test-unit From 06cc32c6cf38cf4c027e71d18742f4b290b22e48 Mon Sep 17 00:00:00 2001 From: Spencer Norman Date: Thu, 30 Aug 2018 18:24:50 -0600 Subject: [PATCH 55/65] fix: update package-lock.json file with changes from #4441 --- package-lock.json | 59 +++++++++++++++++++++++++++++++---------------- package.json | 2 +- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9ca279e713d..13e7ebcc41d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1830,6 +1830,17 @@ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.1.1.tgz", "integrity": "sha512-urQxA1smbLZ2cBbXbaYObM1dJ82aJ2H57A1C/Kklfh/ZN1bgH4G/n5KWhdNfOK11W98gqZfyYj7W4frJJRwA2w==" }, + "react-transition-group": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.4.0.tgz", + "integrity": "sha512-Xv5d55NkJUxUzLCImGSanK8Cl/30sgpOEMGc5m86t8+kZwrPxPCPcFqyx83kkr+5Lz5gs6djuvE5By+gce+VjA==", + "requires": { + "dom-helpers": "^3.3.1", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + } + }, "recompose": { "version": "0.28.2", "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.28.2.tgz", @@ -1896,6 +1907,19 @@ "raf": "3.4.0", "react-input-autosize": "2.2.1", "react-transition-group": "2.4.0" + }, + "dependencies": { + "react-transition-group": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.4.0.tgz", + "integrity": "sha512-Xv5d55NkJUxUzLCImGSanK8Cl/30sgpOEMGc5m86t8+kZwrPxPCPcFqyx83kkr+5Lz5gs6djuvE5By+gce+VjA==", + "requires": { + "dom-helpers": "^3.3.1", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + } + } } } } @@ -12956,6 +12980,16 @@ "integrity": "sha1-wStu/cIkfBDae4dw0YUICnsEcVY=", "dev": true }, + "react-animate-height": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/react-animate-height/-/react-animate-height-2.0.3.tgz", + "integrity": "sha512-IrzXf029kby15pnpsd7SOYML+pT0wZBZe/0O51dggw3l4D59BYmMXPhOUPYjzfrGcy9DNN0PKyZZN65kCMcO6A==", + "requires": { + "@types/react": ">=16", + "classnames": "^2.2.5", + "prop-types": "^15.6.1" + } + }, "react-apollo": { "version": "2.1.9", "resolved": "https://registry.npmjs.org/react-apollo/-/react-apollo-2.1.9.tgz", @@ -13440,10 +13474,10 @@ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.4.0.tgz", "integrity": "sha512-Xv5d55NkJUxUzLCImGSanK8Cl/30sgpOEMGc5m86t8+kZwrPxPCPcFqyx83kkr+5Lz5gs6djuvE5By+gce+VjA==", "requires": { - "dom-helpers": "3.3.1", - "loose-envify": "1.4.0", - "prop-types": "15.6.2", - "react-lifecycles-compat": "3.0.4" + "dom-helpers": "^3.3.1", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" } }, "react-with-direction": { @@ -15694,6 +15728,7 @@ }, "transliteration": { "version": "github:reactioncommerce/transliteration#699d48cc8dd9a64f1a2773e1b36b6faa4bbdca2f", + "from": "transliteration@github:reactioncommerce/transliteration#699d48cc8dd9a64f1a2773e1b36b6faa4bbdca2f", "requires": { "yargs": "8.0.2" }, @@ -16203,22 +16238,6 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, - "velocity-animate": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/velocity-animate/-/velocity-animate-1.5.1.tgz", - "integrity": "sha512-VJ3csMz5zP1ifkbBlsNYpxnoWkPHfVRQ8tUongS78W5DxSGHB68pjYHDTgUYBkVM7P/HpYdVukgVUFcxjr1gGg==" - }, - "velocity-react": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/velocity-react/-/velocity-react-1.4.1.tgz", - "integrity": "sha512-ZyXBm+9C/6kNUNyc+aeNKEhtTu/Mn+OfpsNBGuTxU8S2DUcis/KQL0rTN6jWL+7ygdOrun18qhheNZTA7YERmg==", - "requires": { - "lodash": "4.17.10", - "prop-types": "15.6.2", - "react-transition-group": "2.4.0", - "velocity-animate": "1.5.1" - } - }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", diff --git a/package.json b/package.json index b9b9e7664b4..73c93ee075c 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,7 @@ "react-taco-table": "^0.5.1", "react-tether": "^0.6.1", "react-textarea-autosize": "^5.2.1", - "react-transition-group": "1.2.1", + "react-transition-group": "2.4.0", "reacto-form": "0.0.2", "recompose": "^0.26.0", "shallowequal": "^1.0.2", From b3dc2d9be62afe5fa3e719186de50d6a21eff424 Mon Sep 17 00:00:00 2001 From: Eric Dobbertin Date: Fri, 31 Aug 2018 14:43:29 -0500 Subject: [PATCH 56/65] fix: extend Error directly Remove es6-error NPM package and do what it was doing manually. This might not fix issues, but it makes it easier to debug. --- imports/utils/ReactionError.js | 22 ++++++++++--- package-lock.json | 56 +++++++++++++++------------------- package.json | 1 - 3 files changed, 43 insertions(+), 36 deletions(-) diff --git a/imports/utils/ReactionError.js b/imports/utils/ReactionError.js index ed9a2c2292f..50913e9111c 100644 --- a/imports/utils/ReactionError.js +++ b/imports/utils/ReactionError.js @@ -1,5 +1,3 @@ -import ExtendableError from "es6-error"; - /** * @name ReactionError * @class @@ -7,11 +5,22 @@ import ExtendableError from "es6-error"; * @summary A type of error that allows additional details to be provided, which can * then be sent back to the client for a GraphQL request. */ -export default class ReactionError extends ExtendableError { - constructor(error, message, eventData = {}) { +export default class ReactionError extends Error { + constructor(error, message = "", eventData = {}) { super(message); + + // In Node (7.2) console.log will print custom errors a bit differently unless all properties are defined as non-enumerable + Object.defineProperty(this, "name", { + value: this.constructor.name + }); + + Object.defineProperty(this, "message", { + value: message + }); + this.eventData = eventData; this.error = error; + // Newer versions of DDP use this property to signify that an error // can be sent back and reconstructed on the calling client. this.isClientSafe = true; @@ -19,6 +28,11 @@ export default class ReactionError extends ExtendableError { this.reason = message; // DDP expects this to be in a property named `details` this.details = eventData; + + // Keep this after everything is defined on `this` + if ({}.hasOwnProperty.call(Error, "captureStackTrace")) { + Error.captureStackTrace(this, this.constructor); + } } clone() { diff --git a/package-lock.json b/package-lock.json index 9ca279e713d..13c1022a6e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1806,7 +1806,6 @@ "prop-types": "15.6.2", "react-event-listener": "0.6.2", "react-jss": "8.6.1", - "react-transition-group": "2.4.0", "recompose": "0.28.2", "warning": "4.0.1" }, @@ -1894,8 +1893,7 @@ "memoize-one": "4.0.0", "prop-types": "15.6.2", "raf": "3.4.0", - "react-input-autosize": "2.2.1", - "react-transition-group": "2.4.0" + "react-input-autosize": "2.2.1" } } } @@ -3794,6 +3792,11 @@ "type-detect": "4.0.8" } }, + "chain-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chain-function/-/chain-function-1.0.1.tgz", + "integrity": "sha512-SxltgMwL9uCko5/ZCLiyG2B7R9fY4pDZUw7hJ4MhirdjBLosoDqkWABi3XMucddHdLiFJMb7PD2MZifZriuMTg==" + }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", @@ -5117,11 +5120,6 @@ "is-symbol": "1.0.1" } }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" - }, "es6-promise": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", @@ -12956,6 +12954,16 @@ "integrity": "sha1-wStu/cIkfBDae4dw0YUICnsEcVY=", "dev": true }, + "react-animate-height": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/react-animate-height/-/react-animate-height-2.0.3.tgz", + "integrity": "sha512-IrzXf029kby15pnpsd7SOYML+pT0wZBZe/0O51dggw3l4D59BYmMXPhOUPYjzfrGcy9DNN0PKyZZN65kCMcO6A==", + "requires": { + "@types/react": ">=16", + "classnames": "^2.2.5", + "prop-types": "^15.6.1" + } + }, "react-apollo": { "version": "2.1.9", "resolved": "https://registry.npmjs.org/react-apollo/-/react-apollo-2.1.9.tgz", @@ -13436,14 +13444,15 @@ } }, "react-transition-group": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.4.0.tgz", - "integrity": "sha512-Xv5d55NkJUxUzLCImGSanK8Cl/30sgpOEMGc5m86t8+kZwrPxPCPcFqyx83kkr+5Lz5gs6djuvE5By+gce+VjA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-1.2.1.tgz", + "integrity": "sha512-CWaL3laCmgAFdxdKbhhps+c0HRGF4c+hdM4H23+FI1QBNUyx/AMeIJGWorehPNSaKnQNOAxL7PQmqMu78CDj3Q==", "requires": { - "dom-helpers": "3.3.1", - "loose-envify": "1.4.0", - "prop-types": "15.6.2", - "react-lifecycles-compat": "3.0.4" + "chain-function": "^1.0.0", + "dom-helpers": "^3.2.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.5.6", + "warning": "^3.0.0" } }, "react-with-direction": { @@ -15694,6 +15703,7 @@ }, "transliteration": { "version": "github:reactioncommerce/transliteration#699d48cc8dd9a64f1a2773e1b36b6faa4bbdca2f", + "from": "github:reactioncommerce/transliteration", "requires": { "yargs": "8.0.2" }, @@ -16203,22 +16213,6 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, - "velocity-animate": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/velocity-animate/-/velocity-animate-1.5.1.tgz", - "integrity": "sha512-VJ3csMz5zP1ifkbBlsNYpxnoWkPHfVRQ8tUongS78W5DxSGHB68pjYHDTgUYBkVM7P/HpYdVukgVUFcxjr1gGg==" - }, - "velocity-react": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/velocity-react/-/velocity-react-1.4.1.tgz", - "integrity": "sha512-ZyXBm+9C/6kNUNyc+aeNKEhtTu/Mn+OfpsNBGuTxU8S2DUcis/KQL0rTN6jWL+7ygdOrun18qhheNZTA7YERmg==", - "requires": { - "lodash": "4.17.10", - "prop-types": "15.6.2", - "react-transition-group": "2.4.0", - "velocity-animate": "1.5.1" - } - }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", diff --git a/package.json b/package.json index b9b9e7664b4..9288a2a1c8a 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,6 @@ "deep-diff": "^0.3.8", "deep-equal": "^1.0.1", "dnd-core": "^2.5.4", - "es6-error": "^4.1.1", "escape-string-regexp": "^1.0.5", "express": "4.16.2", "faker": "^4.1.0", From 4e0e8de9c656fac1b599a17081532fdfa8f10a2c Mon Sep 17 00:00:00 2001 From: Eric Dobbertin Date: Fri, 31 Aug 2018 14:49:27 -0500 Subject: [PATCH 57/65] fix: catch Promise.await errors in migrations --- .../24_publish_all_existing_visible_products.js | 9 ++++++++- .../server/migrations/28_add_hash_to_products.js | 7 ++++++- .../32_publish_all_existing_visible_products.js | 8 +++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/imports/plugins/core/versions/server/migrations/24_publish_all_existing_visible_products.js b/imports/plugins/core/versions/server/migrations/24_publish_all_existing_visible_products.js index 403efe329c5..8555a99d634 100644 --- a/imports/plugins/core/versions/server/migrations/24_publish_all_existing_visible_products.js +++ b/imports/plugins/core/versions/server/migrations/24_publish_all_existing_visible_products.js @@ -13,7 +13,14 @@ Migrations.add({ isVisible: true, type: "simple" }, { _id: 1 }).map((product) => product._id); - const success = Promise.await(publishProductsToCatalog(visiblePublishedProducts, collections)); + + let success = false; + try { + success = Promise.await(publishProductsToCatalog(visiblePublishedProducts, collections)); + } catch (error) { + Logger.error("Error in migration 24, publishProductsToCatalog", error); + } + if (!success) { Logger.error("Migration 24 failed to create catalog products for some published products."); } diff --git a/imports/plugins/core/versions/server/migrations/28_add_hash_to_products.js b/imports/plugins/core/versions/server/migrations/28_add_hash_to_products.js index e4374939825..7a3012d7f45 100644 --- a/imports/plugins/core/versions/server/migrations/28_add_hash_to_products.js +++ b/imports/plugins/core/versions/server/migrations/28_add_hash_to_products.js @@ -1,3 +1,4 @@ +import Logger from "@reactioncommerce/logger"; import { Migrations } from "meteor/percolate:migrations"; import { Catalog } from "/lib/collections"; import collections from "/imports/collections/rawCollections"; @@ -10,6 +11,10 @@ Migrations.add({ "product.type": "product-simple" }).fetch(); - products.forEach((product) => Promise.await(hashProduct(product.product._id, collections))); + try { + products.forEach((product) => Promise.await(hashProduct(product.product._id, collections))); + } catch (error) { + Logger.error("Error in migration 28, hashProduct", error); + } } }); diff --git a/imports/plugins/core/versions/server/migrations/32_publish_all_existing_visible_products.js b/imports/plugins/core/versions/server/migrations/32_publish_all_existing_visible_products.js index 6b1dba81060..0c685af257d 100644 --- a/imports/plugins/core/versions/server/migrations/32_publish_all_existing_visible_products.js +++ b/imports/plugins/core/versions/server/migrations/32_publish_all_existing_visible_products.js @@ -13,7 +13,13 @@ Migrations.add({ isVisible: true, type: "simple" }, { _id: 1 }).map((product) => product._id); - const success = Promise.await(publishProductsToCatalog(visiblePublishedProducts, collections)); + let success = false; + try { + success = Promise.await(publishProductsToCatalog(visiblePublishedProducts, collections)); + } catch (error) { + Logger.error("Error in migration 32, publishProductsToCatalog", error); + } + if (!success) { Logger.error("Migration 32 failed to create catalog products for some published products."); } From f0f57c2ae0d27a28c50c387076f5a9f78262f200 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Tue, 4 Sep 2018 13:14:20 -0400 Subject: [PATCH 58/65] fix: migrate CSSTransitionGroup to new CSSTransition for admin panel animations --- imports/plugins/core/components/lib/hoc.js | 10 +- .../dashboard/client/components/actionView.js | 167 +++++++++--------- .../ui/client/components/modal/overlay.js | 60 +++---- .../client/styles/dashboard/console.less | 8 +- .../default-theme/client/styles/overlay.less | 6 +- 5 files changed, 114 insertions(+), 137 deletions(-) diff --git a/imports/plugins/core/components/lib/hoc.js b/imports/plugins/core/components/lib/hoc.js index 9082a254e96..1bfbcd93a3f 100644 --- a/imports/plugins/core/components/lib/hoc.js +++ b/imports/plugins/core/components/lib/hoc.js @@ -181,14 +181,14 @@ export function withIsOwner(component) { } /** - * @name withCSSTransitionGroup + * @name withCSSTransition * @method - * @summary A wrapper to reactively inject react-transition-group's into a component + * @summary A wrapper to dynamically import & inject react-transition-group's into a component * @param {Function|React.Component} component - the component to wrap - * @return {Function} the new wrapped component with a "CSSTransitionGroup" prop + * @return {Object} the new wrapped component with a "CSSTransition" prop * @memberof Components/Helpers */ -export function withCSSTransitionGroup(component) { +export function withCSSTransition(component) { return lifecycle({ componentDidMount() { import("react-transition-group") @@ -198,7 +198,7 @@ export function withCSSTransitionGroup(component) { } this.setState({ - CSSTransitionGroup: module.CSSTransitionGroup + CSSTransition: module.CSSTransition }); return null; diff --git a/imports/plugins/core/dashboard/client/components/actionView.js b/imports/plugins/core/dashboard/client/components/actionView.js index 005b4e14ca4..ffaca317295 100644 --- a/imports/plugins/core/dashboard/client/components/actionView.js +++ b/imports/plugins/core/dashboard/client/components/actionView.js @@ -2,7 +2,7 @@ import React, { Component } from "react"; import { compose } from "recompose"; import PropTypes from "prop-types"; import classnames from "classnames"; -import { getComponent, withCSSTransitionGroup } from "@reactioncommerce/reaction-components"; +import { getComponent, withCSSTransition } from "@reactioncommerce/reaction-components"; import Blaze from "meteor/gadicc:blaze-react-component"; import { Admin } from "/imports/plugins/core/ui/client/providers"; import Radium from "radium"; @@ -23,6 +23,7 @@ const getStyles = (props) => { // prototype.includes has the fortunate side affect of checking string equality as well as array inclusion. const isBigView = provides.includes("dashboard") || (provides.includes("shortcut") && actionView.container === "dashboard"); + if (isBigView) { viewSize = "90vw"; viewMaxSize = "100%"; @@ -47,10 +48,6 @@ const getStyles = (props) => { } } - if (props.actionViewIsOpen === false) { - viewSize = 400; - } - return { base: { "display": "flex", @@ -142,7 +139,7 @@ const getStyles = (props) => { class ActionView extends Component { static propTypes = { - CSSTransitionGroup: PropTypes.func, + CSSTransition: PropTypes.func, actionView: PropTypes.object, actionViewIsOpen: PropTypes.bool, // eslint-disable-line react/boolean-prop-naming buttons: PropTypes.array, @@ -163,16 +160,8 @@ class ActionView extends Component { this.state = { isMobile: this.isMobile, - enterAnimationForDetailView: { - animation: { width: 400 }, - duration: 200, - easing: "easeInOutQuad" - }, - leaveAnimationForDetailView: { - animation: { width: 0 }, - duration: 200, - easing: "easeInOutQuad" - } + actionView: {}, + detailView: null }; this.handleResize = debounce(() => { @@ -190,6 +179,14 @@ class ActionView extends Component { } } + componentDidUpdate(prevProps) { + const { actionView } = this.props; + + if (actionView.template && actionView.template !== prevProps.actionView.template) { + this.setState({ actionView }); + } + } + componentWillUnmount() { if (window) { window.removeEventListener("resize", this.handleResize); @@ -197,22 +194,23 @@ class ActionView extends Component { } renderControlComponent() { - if (this.props.actionView && typeof this.props.actionView.template === "string") { + const { actionView } = this.state; + if (actionView && typeof actionView.template === "string") { // Render a react component if one has been registered by name try { - const component = getComponent(this.props.actionView.template); + const component = getComponent(actionView.template); return (
- {React.createElement(component, this.props.actionView.data)} + {React.createElement(component, actionView.data)}
); } catch (error) { return (
); @@ -223,12 +221,13 @@ class ActionView extends Component { } renderDetailComponent() { - if (this.props.detailView && typeof this.props.detailView.template === "string") { + const { detailView } = this.state; + if (detailView && typeof detailView.template === "string") { return (
); @@ -280,9 +279,10 @@ class ActionView extends Component { } get actionViewIsLargeSize() { - const { meta } = this.props.actionView; + const { actionView } = this.props; + const { meta } = actionView; const dashboardSize = (meta && meta.actionView && meta.actionView.dashboardSize) || "sm"; - const includesDashboard = this.props.actionView.provides && this.props.actionView.provides.includes("dashboard"); + const includesDashboard = actionView.provides && actionView.provides.includes("dashboard"); return includesDashboard || dashboardSize !== "sm"; } @@ -367,39 +367,35 @@ class ActionView extends Component { "action-view-detail": true }); - if (this.props.detailViewIsOpen) { - return ( -
-
- {this.renderDetailViewBackButton()} - -
-

- -

-
- -
- {/* Controls */} -
+ return ( +
+
+ {this.renderDetailViewBackButton()} + +
+

+ +

-
- {/* this.renderControlComponent() */} - {this.renderDetailComponent()} +
+ {/* Controls */}
- ); - } +
- return null; + {/* this.renderControlComponent() */} + {this.renderDetailComponent()} +
+
+ ); } renderActionView() { - const { CSSTransitionGroup } = this.props; + const { CSSTransition, detailView } = this.props; const baseClassName = classnames({ "rui": true, @@ -409,54 +405,55 @@ class ActionView extends Component { "open": this.props.actionViewIsOpen }); - if (this.props.actionViewIsOpen) { - const isRtl = document.querySelector("html").className === "rtl"; + const isRtl = document.querySelector("html").className === "rtl"; - return ( -
+ return ( +
+ {this.renderMasterView()} + + this.setState({ detailView }) } + onExited={() => this.setState({ detailView }) } + > + {this.renderDetailView()} + - {this.renderMasterView()} - - - {this.renderDetailView()} - - - -
-
- {this.renderFooter()} -
+ +
+
+ {this.renderFooter()}
- ); - } - - return null; +
+ ); } render() { - const { CSSTransitionGroup } = this.props; - if (CSSTransitionGroup === undefined) { + const { CSSTransition, actionView } = this.props; + if (CSSTransition === undefined) { return null; } const isRtl = document.querySelector("html").className === "rtl"; return (
- this.setState({ actionView }) } + onExited={() => this.setState({ actionView }) } > {this.renderActionView()} - +
- ); - } - - return null; - } - - render() { - const { CSSTransitionGroup } = this.props; - if (CSSTransitionGroup === undefined) { - return null; - } - - return ( - - {this.renderOverlay()} - + ); } } registerComponent("Overlay", Overlay, [ - withCSSTransitionGroup, + withCSSTransition, Radium ]); export default compose( - withCSSTransitionGroup, + withCSSTransition, Radium )(Overlay); diff --git a/imports/plugins/included/default-theme/client/styles/dashboard/console.less b/imports/plugins/included/default-theme/client/styles/dashboard/console.less index b75cf3ed9c4..b791b7affd1 100644 --- a/imports/plugins/included/default-theme/client/styles/dashboard/console.less +++ b/imports/plugins/included/default-theme/client/styles/dashboard/console.less @@ -84,18 +84,18 @@ html:not(.rtl) .rui.admin.action-view { // Admin action pane slide in/out animation .admin.rui.action-view-pane { &.slide-in-out-enter, - &.slide-in-out-leave-active { + &.slide-in-out-exit-active { transform: translateX(100%); } &.slide-in-out-rtl-enter, - &.slide-in-out-rtl-leave-active { + &.slide-in-out-rtl-exit-active { transform: translateX(-100%); } &.slide-in-out-enter-active, &.slide-in-out-rtl-enter-active, - &.slide-in-out-leave-active, - &.slide-in-out-rtl-leave-active { + &.slide-in-out-exit-active, + &.slide-in-out-rtl-exit-active { transition: transform 200ms ease-in-out; } diff --git a/imports/plugins/included/default-theme/client/styles/overlay.less b/imports/plugins/included/default-theme/client/styles/overlay.less index e12fbb9a322..61e0014b6a9 100644 --- a/imports/plugins/included/default-theme/client/styles/overlay.less +++ b/imports/plugins/included/default-theme/client/styles/overlay.less @@ -1,16 +1,16 @@ // Overlay opacity animation .rui.overlay { &.fade-in-out-enter, - &.fade-in-out-leave-active { + &.fade-in-out-exit-active { opacity: 0; } &.fade-in-out-enter-active, - &.fade-in-out-leave-active { + &.fade-in-out-exit-active { transition: opacity 200ms; } &.fade-in-out-enter-active { - opacity: 1; + opacity: 1; } } From f39229915b0f8f09098e0422939232b3ae6ae517 Mon Sep 17 00:00:00 2001 From: Eric Dobbertin Date: Tue, 4 Sep 2018 17:28:51 -0500 Subject: [PATCH 59/65] feat: make tag.heroMediaUrl absolute if it is not --- .../no-meteor/resolvers/tag/Tag/heroMediaUrl.js | 15 +++++++++++++++ .../server/no-meteor/resolvers/tag/Tag/index.js | 2 ++ 2 files changed, 17 insertions(+) create mode 100644 imports/plugins/core/graphql/server/no-meteor/resolvers/tag/Tag/heroMediaUrl.js diff --git a/imports/plugins/core/graphql/server/no-meteor/resolvers/tag/Tag/heroMediaUrl.js b/imports/plugins/core/graphql/server/no-meteor/resolvers/tag/Tag/heroMediaUrl.js new file mode 100644 index 00000000000..0ff5b1d8ece --- /dev/null +++ b/imports/plugins/core/graphql/server/no-meteor/resolvers/tag/Tag/heroMediaUrl.js @@ -0,0 +1,15 @@ +/** + * @name "Tag.heroMediaUrl" + * @method + * @memberof Tag/GraphQL + * @summary Makes the URL absolute if it is relative + * @param {Object} tag - Tag response from parent resolver + * @param {SubTagConnectionArgs} args - arguments sent by the client {@link ConnectionArgs|See default connection arguments} + * @param {Object} context - an object containing the per-request state + * @return {String|null} The absolute URL + */ +export default function heroMediaUrl({ heroMediaUrl: url }, connectionArgs, context) { + if (typeof url !== "string") return null; + if (url.startsWith("http")) return url; + return context.getAbsoluteUrl(url); +} diff --git a/imports/plugins/core/graphql/server/no-meteor/resolvers/tag/Tag/index.js b/imports/plugins/core/graphql/server/no-meteor/resolvers/tag/Tag/index.js index 0fa3afe63a9..c3385fa5751 100644 --- a/imports/plugins/core/graphql/server/no-meteor/resolvers/tag/Tag/index.js +++ b/imports/plugins/core/graphql/server/no-meteor/resolvers/tag/Tag/index.js @@ -1,8 +1,10 @@ import { encodeTagOpaqueId } from "@reactioncommerce/reaction-graphql-xforms/tag"; +import heroMediaUrl from "./heroMediaUrl"; import subTags from "./subTags"; export default { _id: (tag) => encodeTagOpaqueId(tag._id), + heroMediaUrl, subTagIds: (tag) => (tag.relatedTagIds || []).map(encodeTagOpaqueId), subTags }; From a481b7f60598ed52ab80153ed4e34547f0c04958 Mon Sep 17 00:00:00 2001 From: Eric Dobbertin Date: Thu, 6 Sep 2018 19:58:12 -0500 Subject: [PATCH 60/65] fix: add missing migration for Shops.layout.structure.template --- .../38_registry_products_template.js | 82 +++++++++++++++++++ .../core/versions/server/migrations/index.js | 1 + 2 files changed, 83 insertions(+) create mode 100644 imports/plugins/core/versions/server/migrations/38_registry_products_template.js diff --git a/imports/plugins/core/versions/server/migrations/38_registry_products_template.js b/imports/plugins/core/versions/server/migrations/38_registry_products_template.js new file mode 100644 index 00000000000..5695efee68a --- /dev/null +++ b/imports/plugins/core/versions/server/migrations/38_registry_products_template.js @@ -0,0 +1,82 @@ +import { Migrations } from "meteor/percolate:migrations"; +import { Packages, Shops } from "/lib/collections"; + +/** + * This is an update to migration 33, which missed a couple things: + * (1) The $ by default updates only the first matching array, but we want to update all + * (2) The layouts are also stored in the Shops collection and need to be updated there as well. + * + * Note that we use rawCollection because Meteor collections do not forward the arrayFilters option yet. + */ + +Migrations.add({ + version: 38, + up() { + Packages.rawCollection().update({ + "layout.structure.template": "products" + }, { + $set: { + "layout.$[elem].structure.template": "Products" + } + }, { + arrayFilters: [{ "elem.structure.template": "products" }], + multi: true + }); + + Packages.rawCollection().update({ + "registry.template": "products" + }, { + $set: { + "registry.$[elem].template": "Products" + } + }, { + arrayFilters: [{ "elem.template": "products" }], + multi: true + }); + + Shops.rawCollection().update({ + "layout.structure.template": "products" + }, { + $set: { + "layout.$[elem].structure.template": "Products" + } + }, { + arrayFilters: [{ "elem.structure.template": "products" }], + multi: true + }); + }, + down() { + Packages.rawCollection().update({ + "layout.structure.template": "Products" + }, { + $set: { + "layout.$[elem].structure.template": "products" + } + }, { + arrayFilters: [{ "elem.structure.template": "Products" }], + multi: true + }); + + Packages.rawCollection().update({ + "registry.template": "Products" + }, { + $set: { + "registry.$[elem].template": "products" + } + }, { + arrayFilters: [{ "elem.template": "Products" }], + multi: true + }); + + Shops.rawCollection().update({ + "layout.structure.template": "Products" + }, { + $set: { + "layout.$[elem].structure.template": "products" + } + }, { + arrayFilters: [{ "elem.structure.template": "Products" }], + multi: true + }); + } +}); diff --git a/imports/plugins/core/versions/server/migrations/index.js b/imports/plugins/core/versions/server/migrations/index.js index 0d96f76f16f..1de2269a7a7 100644 --- a/imports/plugins/core/versions/server/migrations/index.js +++ b/imports/plugins/core/versions/server/migrations/index.js @@ -35,3 +35,4 @@ import "./34_remove_order_shipping_items"; import "./35_add_type_to_carts"; import "./36_convert_requiresShipping"; import "./37_change_shipping_rate_settings_template_name"; +import "./38_registry_products_template"; From a94fafce22b8aab20c426ebc9b80450e960ea558 Mon Sep 17 00:00:00 2001 From: Eric Dobbertin Date: Thu, 6 Sep 2018 19:58:48 -0500 Subject: [PATCH 61/65] fix: fix a couple minor issues with migration 13 --- .../server/migrations/13_add_shopId_on_shipping.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/imports/plugins/core/versions/server/migrations/13_add_shopId_on_shipping.js b/imports/plugins/core/versions/server/migrations/13_add_shopId_on_shipping.js index bf4709ba887..052a6b106ba 100644 --- a/imports/plugins/core/versions/server/migrations/13_add_shopId_on_shipping.js +++ b/imports/plugins/core/versions/server/migrations/13_add_shopId_on_shipping.js @@ -2,6 +2,8 @@ import { Migrations } from "meteor/percolate:migrations"; import { Cart, Orders } from "/lib/collections"; import Reaction from "/imports/plugins/core/core/server/Reaction"; +const shippingFieldExistsAndHasItems = { shipping: { $exists: true, $ne: [] } }; + Migrations.add({ version: 13, up() { @@ -9,11 +11,11 @@ Migrations.add({ // This adds shopId field to each shipping object in orders and carts. const shopId = Reaction.getShopId(); - Orders.update({}, { + Orders.update(shippingFieldExistsAndHasItems, { $set: { "shipping.0.shopId": shopId } }, { bypassCollection2: true, multi: true }); - Cart.update({}, { + Cart.update(shippingFieldExistsAndHasItems, { $set: { "shipping.0.shopId": shopId } }, { bypassCollection2: true, multi: true }); }, @@ -23,7 +25,7 @@ Migrations.add({ }, { bypassCollection2: true, multi: true }); Cart.update({}, { - $set: { "shipping.0.shopId": "" } + $unset: { "shipping.0.shopId": "" } }, { bypassCollection2: true, multi: true }); } }); From 7e48498084ab345174427038cbfced22d45e1aba Mon Sep 17 00:00:00 2001 From: Spencer Norman Date: Fri, 7 Sep 2018 15:22:36 -0600 Subject: [PATCH 62/65] chore: update CHANGELOG for 1.16.0 --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10146b27cb4..01b35c57e25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +# v1.16.0 +## GraphQL +### Features + - feat: return absolute media URLs from GraphQL (#4565) + +## Meteor App +### Features + - feat 4571 Replace all Meteor.userId() with util function (#4582) + - feat: Improve animations and dynamically import animation libraries to reduce bundle size (#4500) .. Resolves #4441 + +### Fixes + - fix: reaction error swallowing (#4592) + - fix: update file-collections dependency from 0.5.0 to 0.6.0 (#4589) + - fix: null check in email validation (#4520) .. Resolves #4502 + - fix: Add missing Shops.layout migration (#4609) .. Resolves #4608 + + # v1.15.0 ## Sitemap Generator From f0f7441df9e19a3fedc7039535c091d50b05cacb Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Mon, 10 Sep 2018 14:12:25 -0400 Subject: [PATCH 63/65] fix: app crashes when changing textarea field on PDP --- imports/plugins/core/ui/client/helpers/animations.js | 9 +++++++-- .../included/default-theme/client/styles/forms.less | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/imports/plugins/core/ui/client/helpers/animations.js b/imports/plugins/core/ui/client/helpers/animations.js index b5264b9a5ee..c2017719b6e 100644 --- a/imports/plugins/core/ui/client/helpers/animations.js +++ b/imports/plugins/core/ui/client/helpers/animations.js @@ -8,9 +8,14 @@ import { Meteor } from "meteor/meteor"; * @returns {undefined} */ export function highlightInput(inputRef, className = "highlight") { - inputRef.classList.add(className); + let input = inputRef; + if (input._rootDOMNode) { + input = input._rootDOMNode; + } + + input.classList.add(className); Meteor.setTimeout(() => { - inputRef.classList.remove(className); + input.classList.remove(className); }, 500); } diff --git a/imports/plugins/included/default-theme/client/styles/forms.less b/imports/plugins/included/default-theme/client/styles/forms.less index a3c7928ec96..e577b841e39 100644 --- a/imports/plugins/included/default-theme/client/styles/forms.less +++ b/imports/plugins/included/default-theme/client/styles/forms.less @@ -250,7 +250,7 @@ } } -input.highlight { +input.highlight, textarea.highlight { animation-name: 'highlightInput'; animation-duration: 300ms; } @@ -270,7 +270,7 @@ input.highlight { } } -input.highlight-variant { +input.highlight-variant, textarea.highlight-variant { animation-name: 'highlightVariantInput'; animation-duration: 300ms; } From fdaea3de5815efaa06a471a75f94fb3eb45d3a64 Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Mon, 10 Sep 2018 15:05:10 -0400 Subject: [PATCH 64/65] fix: sidebar not updating when clicking products in grid as admin --- .../core/dashboard/client/components/actionView.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/imports/plugins/core/dashboard/client/components/actionView.js b/imports/plugins/core/dashboard/client/components/actionView.js index ffaca317295..12acdc5d788 100644 --- a/imports/plugins/core/dashboard/client/components/actionView.js +++ b/imports/plugins/core/dashboard/client/components/actionView.js @@ -177,14 +177,23 @@ class ActionView extends Component { if (window) { window.addEventListener("resize", this.handleResize, false); } + + const { actionView } = this.props; + if (actionView) { + this.setState({ actionView }); + } } componentDidUpdate(prevProps) { const { actionView } = this.props; - if (actionView.template && actionView.template !== prevProps.actionView.template) { + if (EJSON.equals(actionView, prevProps.actionView) === false) { this.setState({ actionView }); } + + if (actionView.template && actionView.template !== prevProps.actionView.template) { + + } } componentWillUnmount() { From c217559fb173412be593b32983550e473ad3846e Mon Sep 17 00:00:00 2001 From: Daniel Castellon Date: Mon, 10 Sep 2018 16:22:28 -0400 Subject: [PATCH 65/65] chore: fix eslint issues --- .../plugins/core/dashboard/client/components/actionView.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/imports/plugins/core/dashboard/client/components/actionView.js b/imports/plugins/core/dashboard/client/components/actionView.js index 12acdc5d788..64d7dad69a1 100644 --- a/imports/plugins/core/dashboard/client/components/actionView.js +++ b/imports/plugins/core/dashboard/client/components/actionView.js @@ -4,6 +4,7 @@ import PropTypes from "prop-types"; import classnames from "classnames"; import { getComponent, withCSSTransition } from "@reactioncommerce/reaction-components"; import Blaze from "meteor/gadicc:blaze-react-component"; +import { EJSON } from "meteor/ejson"; import { Admin } from "/imports/plugins/core/ui/client/providers"; import Radium from "radium"; import debounce from "lodash/debounce"; @@ -190,10 +191,6 @@ class ActionView extends Component { if (EJSON.equals(actionView, prevProps.actionView) === false) { this.setState({ actionView }); } - - if (actionView.template && actionView.template !== prevProps.actionView.template) { - - } } componentWillUnmount() {