Wishlist Swym Integration

Here is how to integrate a wishlist app using the app Swym as an example.
We will start with a little step-by-step guide and provide a final version of the example at the end of the page.

Integration Step-By-Step

First of all, let us create a new file where you can add all wishlist related functionality. You can then build a basic structure for the component that we are going to include in the product card as in the "components/Cards/Product/view.tsx" file shown below.

import React from 'react';
// include recompose, which we are going to work with in this example
import { withHandlers, compose, withStateHandlers, lifecycle } from 'recompose';

export const Wishlist = compose(
 // TODO: add logic
  
// use item that we passed down in "components/Cards/Product/view.tsx"
)(({ item }) => {
  
  return (
    <div className="findify-wishlist-heart">
      <button>
    
    	</button>
    </div>
  )
})
// import Wishlist
import { Wishlist } from 'Wishlist.tsx';

// include Wishlist component where you want it to appear
<Wishlist
	// You need to pass "item" to the component which contains information we will need in the Wishlist file
	item={item} />

Then you need to fetch the wishlist to check if there is already anything on the list. In the example below, the function can be found within "componentDidMount". The function is checking if a wishlist item's ID matches with the "selectedVariantId" and then changes the styling according to it.

import React from 'react';
import { withHandlers, compose, withStateHandlers, lifecycle } from 'recompose';

export const Wishlist = compose(
  lifecycle({
    // fetch wishlist 
    componentDidMount() {
    const p = this.props;
    const item = p.item;
    const selectedVariantId = item.get('selected_variant_id');

    function checkIfAlreadyInWishlist(){
      window._swat.fetch( function(response) {
        response.map(function(item) {
          // add a class to change the CSS of the already existing wishlist items
          $('button[findify-product-variant-id='+item.epi+']').addClass('findify-added-to-wishlist disabled swym-added')
        });
      });
    }
    setTimeout(checkIfAlreadyInWishlist(selectedVariantId), 500);
    }
  }),
)(({ item }) => {
  // selectedVariantId provides the ID that checkIfAlreadyInWishlist() is checking against
  const selectedVariantId = item.get('selected_variant_id');
  
  return (
    <div className="findify-wishlist-heart">
      <button
    		findify-product-variant-id={selectedVariantId}>
    	</button>
    </div>
  )
})

Within "withStateHandlers" we can add a new state, that we can use for the styling later. The idea is to have a default state of "isAdded: false" and turn it into "isAdded: true" to adjust the styling as needed.

import React from 'react';
import { withHandlers, compose, withStateHandlers, lifecycle } from 'recompose';

export const Wishlist = compose(
  lifecycle({
    // fetch wishlist 
    componentDidMount() {
    const p = this.props;
    const item = p.item;
    const selectedVariantId = item.get('selected_variant_id');

    function checkIfAlreadyInWishlist(){
      window._swat.fetch( function(response) {
        response.map(function(item) {
          // add a class to change the CSS of the already existing wishlist items
          $('button[findify-product-variant-id='+item.epi+']').addClass('findify-added-to-wishlist disabled swym-added')
        });
      });
    }
    setTimeout(checkIfAlreadyInWishlist(selectedVariantId), 500);
    }
  }),
   withStateHandlers(
    () => ({
    	// default state of wishlist items
      isAdded: false
    }),
    {
      // function to toggle the state to true or false
      addToWishlist: ({isAdded}) => (isAdded) => ({isAdded: isAdded})
    }
  ),
  
  // add the state and function that we just added within "withStateHandlers"
)(({ item, addToWishlist, isAdded }) => {
   const selectedVariantId = item.get('selected_variant_id');

  // 
  return (
    <div className="findify-wishlist-heart">
      <button
    // depending if "isAdded" is true or false, we return different classNames
        className={isAdded 
          ? 'findify-wishlist-button findify-added-to-wishlist swym-button swym-add-to-wishlist-view-product swym-icontext swym-heart disabled swym-added swym-loaded' 
          : 'findify-wishlist-button swym-button swym-add-to-wishlist-view-product swym-icontext swym-heart swym-loaded'} 
        findify-product-variant-id={selectedVariantId}>
      </button>
    </div>
  )
})

In a last step we are going to add an onClick event to add or remove products from our wishlist. We can do that by adding fetch requests.

import React from 'react';
import { withHandlers, compose, withStateHandlers, lifecycle } from 'recompose';

export const Wishlist = compose(
  lifecycle({
    // fetch wishlist 
    componentDidMount() {
    const p = this.props;
    const item = p.item;
    const selectedVariantId = item.get('selected_variant_id');

    function checkIfAlreadyInWishlist(){
      window._swat.fetch( function(response) {
        response.map(function(item) {
          // add a class to change the CSS of the already existing wishlist items
          $('button[findify-product-variant-id='+item.epi+']').addClass('findify-added-to-wishlist disabled swym-added')
        });
      });
    }
    setTimeout(checkIfAlreadyInWishlist(selectedVariantId), 500);
    }
  }),
   withStateHandlers(
    () => ({
    	// default state of wishlist items
      isAdded: false
    }),
    {
      // function to toggle the state to true or false
      addToWishlist: ({isAdded}) => (isAdded) => ({isAdded: isAdded})
    }
  ),
  // add withHandlers
  withHandlers({
    // onClick is going to use "addToWishlist" & "isAdded" as well as information from "item"
    onClick: ({ item, addToWishlist, isAdded }) => async (event) => {
      event.stopPropagation();
      event.preventDefault();

      const selectedVariantId = item.get('selected_variant_id');

      // remove product
      if ($('button[findify-product-variant-id='+selectedVariantId+']').hasClass('findify-added-to-wishlist')) {
        addToWishlist(true);
        function removeProductFromWishlist() {
          var wishlist = $('<div class="findify-wishlist-heart"><button class="findify-wishlist-button" findify-product-variant-id='+selectedVariantId+'></button></div>');
            window._swat.removeFromWishList({
              // the fetch request is going to need some information, be aware that you need to replace <ADD STORE URL HERE> with the url of your store
              "epi": selectedVariantId,
              "du": '<ADD STORE URL HERE>' + item.get('product_url').split('?')[0],
              "empi": item.get('id'),
              "iu" : item.get('image_url'),
              "pr": item.getIn(['price', 0]),
              "stk": item.get('quantity'),
              "dt": item.get('title')
            }, function(r) {
              addToWishlist(false)
            });
          return wishlist;
        }

        removeProductFromWishlist(selectedVariantId);
      } else {
        // add product
        function addWishlist(){
          var wishlist = $('<div class="findify-wishlist-heart"><button class="findify-wishlist-button" findify-product-variant-id='+selectedVariantId+'></button></div>');
          debugger;
            window._swat.addToWishList({
              // the fetch request is going to need some information, be aware that you need to replace <ADD STORE URL HERE> with the url of your store
              "epi": selectedVariantId,
              "du": '<add store url here>' + item.get('product_url').split('?')[0],
              "empi": item.get('id'),
              "iu" : item.get('image_url'),
              "pr": item.getIn(['price', 0]),
              "stk": item.get('quantity'),
              "dt": item.get('title')
            }, function(r) {
              addToWishlist(true)
            });
          return wishlist;
        }
        
        addWishlist(selectedVariantId);
      }
    }
})
  // add "onClick" so we can use it below
)(({ item, addToWishlist, isAdded, onClick }) => {
   const selectedVariantId = item.get('selected_variant_id');

  // 
  return (
    // add the onClick event
    <div className="findify-wishlist-heart" onClick={onClick}>
      <button
    // depending if "isAdded" is true or false, we return different classNames
        className={isAdded 
          ? 'findify-wishlist-button findify-added-to-wishlist swym-button swym-add-to-wishlist-view-product swym-icontext swym-heart disabled swym-added swym-loaded' 
          : 'findify-wishlist-button swym-button swym-add-to-wishlist-view-product swym-icontext swym-heart swym-loaded'} 
        findify-product-variant-id={selectedVariantId}>
      </button>
    </div>
  )
})

Final Version Example

Note for the CSS: In this example we mainly worked using the classes that the app provides, so the styling comes from there and we barely need to add custom styling, but we could do so by adding some CSS rules within the "Styles" file.

/**
 * @module components/Cards/Product
 */

import React from 'react'
import classNames from 'classnames'
import Image from 'components/common/Image'
import Truncate from 'components/common/Truncate'
import Text from 'components/Text'
import Rating from 'components/Cards/Product/Rating';
import Price from 'components/Cards/Product/Price';
import template from 'helpers/template';
import { DiscountSticker, OutOfStockSticker  } from 'components/Cards/Product/Stickers';
import { List } from 'immutable'
import { IProduct, MJSConfiguration, ThemedSFCProps } from 'types/index';
import BundleAction from 'components/Cards/Product/BundleAction';

// import Wishlist
import { Wishlist } from 'Wishlist.tsx';


const Title: any = ({ text, theme, ...rest }) => (
  <Text display-if={!!text} className={theme.title} {...rest}>{text}</Text>
);

const Description: any = ({ text, theme, ...rest }) => (
  <p
    display-if={!!text}
    className={theme.description}
    {...rest}
  >
    <Truncate>{text}</Truncate>
  </p>
);


export interface IProductCardProps extends ThemedSFCProps {
  item: IProduct;
  config: MJSConfiguration;
}

const ProductCardView: React.SFC<IProductCardProps> = ({
  item,
  config,
  theme,
}: any) => (
  <a
    onClick={item.onClick}
    href={item.get('product_url')}
    className={classNames(
      theme.root,
      config.get('simple') && theme.simple,
      theme.productCard,
    )}
  >
    <div className={classNames(theme.imageWrap)}>
      <BundleAction display-if={config.get('bundle')} item={item} />
      <Image
        className={classNames(theme.image)}
        aspectRatio={config.getIn(['product', 'image', 'aspectRatio'], 1)}
        thumbnail={item.get('thumbnail_url')}
        src={item.get('image_url') || item.get('thumbnail_url')}
        alt={item.get('title')}
      />
      <div display-if={config.getIn(['product', 'stickers', 'display'])}>
        <DiscountSticker
          config={config}
          className={theme.discountSticker}
          discount={item.get('discount')}
          display-if={
            config.getIn(['stickers', 'discount']) &&
            config.getIn(['product', 'stickers', 'display']) &&
            item.get('discount', List()).size &&
            item.getIn(['stickers', 'discount'])
          } />
      </div>
    </div>
    <div
      display-if={
        config.getIn(['product', 'reviews', 'display']) &&
        (!!item.getIn(['reviews', 'count']) || !!item.getIn(['reviews', 'total_reviews']))
      }
      className={theme.rating}>
      <Rating
        value={item.getIn(['reviews', 'average_score'])}
        count={item.getIn(['reviews', 'count']) || item.getIn(['reviews', 'total_reviews'])} />
    </div>
    <div
      className={theme.variants}
      display-if={
        config.getIn(['product', 'variants', 'display']) &&
        item.get('variants', List()).size > 1
      }
      >
      {
        template(config.getIn(['product', 'i18n', 'variants'], 'Available in %s variants'))(
          item.get('variants', List()).size
        )
      }
    </div>
    <div className={theme.content}>
      <Title
        theme={theme}
        display-if={config.getIn(['product', 'title', 'display'])}
        text={item.get('title')}
        config={config.getIn(['product', 'title'])} />
      <Description
        theme={theme}
        display-if={config.getIn(['product', 'description', 'display'])}
        text={item.get('description')}
        config={config.getIn(['product', 'description'])} />
      <Price
        className={theme.priceWrapper}
        display-if={config.getIn(['product', 'price', 'display'])}
        price={item.get('price')}
        oldPrice={item.get('compare_at')}
        discount={item.get('discount')}
        currency={config.get('currency_config').toJS()} />
      <OutOfStockSticker
        display-if={item.getIn(['stickers', 'out-of-stock'])}
        config={config} />
          
      // add Wishlist where you want it to appear    
      <Wishlist
          item={item} />
    </div>
  </a>
)

export default ProductCardView;
import React from 'react';
import { withHandlers, compose, withStateHandlers, lifecycle } from 'recompose';

export const Wishlist = compose(
  lifecycle({
    // fetch wishlist 
    componentDidMount() {
    const p = this.props;
    const item = p.item;
    const selectedVariantId = item.get('selected_variant_id');

    function checkIfAlreadyInWishlist(){
      window._swat.fetch( function(response) {
        response.map(function(item) {
          // add a class to change the CSS of the already existing wishlist items
          $('button[findify-product-variant-id='+item.epi+']').addClass('findify-added-to-wishlist disabled swym-added')
        });
      });
    }
    setTimeout(checkIfAlreadyInWishlist(selectedVariantId), 500);
    }
  }),
   withStateHandlers(
    () => ({
    	// default state of wishlist items
      isAdded: false
    }),
    {
      // function to toggle the state to true or false
      addToWishlist: ({isAdded}) => (isAdded) => ({isAdded: isAdded})
    }
  ),
  withHandlers({
    // onClick is going to use "addToWishlist" & "isAdded" as well as information from "item"
    onClick: ({ item, addToWishlist, isAdded }) => async (event) => {
      event.stopPropagation();
      event.preventDefault();

      const selectedVariantId = item.get('selected_variant_id');

      // remove product
      if ($('button[findify-product-variant-id='+selectedVariantId+']').hasClass('findify-added-to-wishlist')) {
        addToWishlist(true);
        function removeProductFromWishlist() {
          var wishlist = $('<div class="findify-wishlist-heart"><button class="findify-wishlist-button" findify-product-variant-id='+selectedVariantId+'></button></div>');
            window._swat.removeFromWishList({
              // the fetch request is going to need some information, be aware that you need to replace <ADD STORE URL HERE> with the url of your store
              "epi": selectedVariantId,
              "du": '<ADD STORE URL HERE>' + item.get('product_url').split('?')[0],
              "empi": item.get('id'),
              "iu" : item.get('image_url'),
              "pr": item.getIn(['price', 0]),
              "stk": item.get('quantity'),
              "dt": item.get('title')
            }, function(r) {
              addToWishlist(false)
            });
          return wishlist;
        }

        removeProductFromWishlist(selectedVariantId);
      } else {
        // add product
        function addWishlist(){
          var wishlist = $('<div class="findify-wishlist-heart"><button class="findify-wishlist-button" findify-product-variant-id='+selectedVariantId+'></button></div>');
          debugger;
            window._swat.addToWishList({
              // the fetch request is going to need some information, be aware that you need to replace <ADD STORE URL HERE> with the url of your store
              "epi": selectedVariantId,
              "du": '<add store url here>' + item.get('product_url').split('?')[0],
              "empi": item.get('id'),
              "iu" : item.get('image_url'),
              "pr": item.getIn(['price', 0]),
              "stk": item.get('quantity'),
              "dt": item.get('title')
            }, function(r) {
              addToWishlist(true)
            });
          return wishlist;
        }
        
        addWishlist(selectedVariantId);
      }
    }
})
  // don't forget to include the props we need below
)(({ onClick, item, addToWishlist, isAdded }) => {
   const selectedVariantId = item.get('selected_variant_id');

  // 
  return (
    <div className="findify-wishlist-heart" onClick={onClick}>
      <button
    // depending if "isAdded" is true or false, we return different classNames
        className={isAdded 
          ? 'findify-wishlist-button findify-added-to-wishlist swym-button swym-add-to-wishlist-view-product swym-icontext swym-heart disabled swym-added swym-loaded' 
          : 'findify-wishlist-button swym-button swym-add-to-wishlist-view-product swym-icontext swym-heart swym-loaded'} 
        findify-product-variant-id={selectedVariantId}>
      </button>
    </div>
  )
})