diff --git a/imports/plugins/core/catalog/client/components/publishControls.js b/imports/plugins/core/catalog/client/components/publishControls.js
index faa6c5c6be8..b06ba372fd2 100644
--- a/imports/plugins/core/catalog/client/components/publishControls.js
+++ b/imports/plugins/core/catalog/client/components/publishControls.js
@@ -1,5 +1,8 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
+import classnames from "classnames";
+import { Meteor } from "meteor/meteor";
+import { ReactiveVar } from "meteor/reactive-var";
import { Components } from "@reactioncommerce/reaction-components";
import {
Button,
@@ -10,7 +13,7 @@ import {
import { Translatable } from "/imports/plugins/core/ui/client/providers";
/** TMP **/
-import { Reaction } from "/client/api";
+import { i18next, Reaction } from "/client/api";
class PublishControls extends Component {
static propTypes = {
@@ -37,14 +40,28 @@ class PublishControls extends Component {
constructor(props) {
super(props);
- this.state = {
- showDiffs: false
- };
+ this.currentProductHash = new ReactiveVar([]);
this.handleToggleShowChanges = this.handleToggleShowChanges.bind(this);
this.handlePublishClick = this.handlePublishClick.bind(this);
}
+ state = {
+ isHashUpdating: false,
+ showDiffs: false
+ };
+
+ componentWillReceiveProps() {
+ this.setState({
+ isHashUpdating: false
+ });
+ }
+
+ componentDidUpdate() {
+ // Re-calculate hash after publishing
+ this.renderHashCalculation();
+ }
+
handleToggleShowChanges() {
this.setState({
showDiffs: !this.state.showDiffs
@@ -54,6 +71,10 @@ class PublishControls extends Component {
handlePublishClick() {
if (this.props.onPublishClick) {
this.props.onPublishClick(this.props.revisions);
+
+ this.setState({
+ isHashUpdating: true
+ });
}
}
@@ -200,13 +221,13 @@ class PublishControls extends Component {
return (
+ {this.renderChangesNotification()}
@@ -304,6 +325,64 @@ class PublishControls extends Component {
);
}
+ renderHashCalculation = () => {
+ const productDocument = this.props && this.props.documents && this.props.documents[0];
+
+ if (productDocument) {
+ Meteor.call("products/getpublishedProductHash", productDocument._id, (err, result) => {
+ if (err) {
+ Alerts.toast(i18next.t("admin.catalogCalculateHashError", { err: err.reason }), "error");
+ }
+ if (result) {
+ this.currentProductHash.set(result);
+ }
+ });
+ }
+ return;
+ }
+
+ renderChangesNotification = () => {
+ const publishedProductHash = (this.props && this.props.documents && this.props.documents[0] && this.props.documents[0].publishedProductHash) || null;
+ const { isHashUpdating } = this.state;
+
+ // Calculate hash to compare
+ this.renderHashCalculation();
+ const currentProductHash = this.currentProductHash.get();
+
+ const hashIndicator = classnames({
+ "rui": true,
+ "hash-icon": true,
+ "fa-stack": true,
+ "fa-lg": true,
+ "hash-icon-visible": publishedProductHash !== currentProductHash,
+ "hash-icon-hidden": publishedProductHash === currentProductHash
+ });
+
+ const primaryIcon = classnames({
+ "fa": true,
+ "fa-stack-2x": true,
+ "fa-circle": isHashUpdating,
+ "fa-circle-o": !isHashUpdating
+ });
+
+ const secondaryIcon = classnames({
+ "fa": true,
+ "fa-stack-1x": true,
+ "fa-refresh": isHashUpdating,
+ "fa-circle": !isHashUpdating,
+ "fa-spin": isHashUpdating
+ });
+
+ // We don't use the `` component here as we do not have layered
+ // icons built in to the existing component
+ return (
+
+
+
+
+ );
+ }
+
render() {
return (
diff --git a/imports/plugins/core/catalog/server/i18n/en.json b/imports/plugins/core/catalog/server/i18n/en.json
index 99ed92cdbe5..9ab301eaab4 100644
--- a/imports/plugins/core/catalog/server/i18n/en.json
+++ b/imports/plugins/core/catalog/server/i18n/en.json
@@ -5,6 +5,7 @@
"reaction-catalog": {
"admin": {
"catalogProductPublishSuccess": "Product published to catalog",
+ "catalogCalculateHashError": "There was an error trying to calculate this products hash.",
"shortcut": {
"catalogLabel": "Catalog",
"catalogTitle": "Catalog"
diff --git a/imports/plugins/core/catalog/server/methods/catalog.js b/imports/plugins/core/catalog/server/methods/catalog.js
index 0b59d19f18a..2d6baba2270 100644
--- a/imports/plugins/core/catalog/server/methods/catalog.js
+++ b/imports/plugins/core/catalog/server/methods/catalog.js
@@ -8,6 +8,7 @@ import { Hooks, Logger, Reaction } from "/server/api";
import { MediaRecords, Products, Tags } from "/lib/collections";
import { Media } from "/imports/plugins/core/files/server";
import rawCollections from "/imports/collections/rawCollections";
+import { createProductHash } from "../no-meteor/mutations/hashProduct";
import getProductPriceRange from "../no-meteor/utils/getProductPriceRange";
import getVariants from "../no-meteor/utils/getVariants";
import hasChildVariant from "../no-meteor/utils/hasChildVariant";
@@ -1576,5 +1577,23 @@ Meteor.methods({
// if collection updated we return new `isVisible` state
return res === 1 && !product.isVisible;
+ },
+
+ /**
+ * @name products/getpublishedProductHash
+ * @memberof Methods/Products
+ * @method
+ * @summary hashes product information for comparison purposes
+ * @param {String} productId - the product _id of the product to hash
+ * @return {String} hash of product
+ */
+ "products/getpublishedProductHash"(productId) {
+ check(productId, String);
+
+ const product = Products.findOne({ _id: productId });
+
+ const productHash = createProductHash(product, rawCollections);
+
+ return productHash;
}
});
diff --git a/imports/plugins/core/catalog/server/no-meteor/mutations/hashProduct.js b/imports/plugins/core/catalog/server/no-meteor/mutations/hashProduct.js
index 5e43fd9d576..c784fe726e2 100644
--- a/imports/plugins/core/catalog/server/no-meteor/mutations/hashProduct.js
+++ b/imports/plugins/core/catalog/server/no-meteor/mutations/hashProduct.js
@@ -9,7 +9,7 @@ import createCatalogProduct from "../utils/createCatalogProduct";
* @param {Object} collections - Raw mongo collections
* @return {String} product hash
*/
-async function createProductHash(productToConvert, collections) {
+export async function createProductHash(productToConvert, collections) {
const product = await createCatalogProduct(productToConvert, collections);
const hashableFields = {
diff --git a/imports/plugins/included/default-theme/client/styles/toolbar.less b/imports/plugins/included/default-theme/client/styles/toolbar.less
index d126d00e3a0..dda47661a28 100644
--- a/imports/plugins/included/default-theme/client/styles/toolbar.less
+++ b/imports/plugins/included/default-theme/client/styles/toolbar.less
@@ -56,3 +56,15 @@ html.rtl .rui.toolbar.toolbar-vertical {
display: flex !important;
}
}
+
+.rui.hash-icon {
+ transition: opacity 200ms linear;
+}
+
+.rui.hash-icon.hash-icon-visible {
+ opacity: 1.0;
+}
+
+.rui.hash-icon.hash-icon-hidden {
+ opacity: 0.0;
+}