@shopgate/pwa-common-commerce
Version:
Commerce library for the Shopgate Connect PWA.
168 lines (152 loc) • 6.52 kB
JavaScript
import { createSelector } from 'reselect';
import { generateResultHash } from '@shopgate/pwa-common/helpers/redux';
import { isUserLoggedIn } from '@shopgate/pwa-common/selectors/user';
import { REVIEW_PREVIEW_COUNT } from "../constants";
import * as pipelines from "../constants/Pipelines";
import { getBaseProductId } from "../../product/selectors/product";
/**
* @param {Object} state The global state.
* @return {Object}
*/
const getReviewsState = state => state.reviews;
/**
* @param {Object} state The global state.
* @return {Object}
*/
const getProductReviewsExcerptState = state => state.reviews.reviewsByProductId;
/**
* Select the product reviews state.
* @param {Object} state The current application state.
* @return {Object} The product reviews state.
*/
const getReviewsByHash = createSelector(getReviewsState, state => state.reviewsByHash);
/**
* Retrieves the fetching state for the current product's reviews.
* @param {Object} state The current application state.
* @return {Object|null} The reviews for a product.
*/
const getCollectionForCurrentBaseProduct = createSelector(getBaseProductId, getReviewsByHash, (productId, reviews) => {
const hash = generateResultHash({
pipeline: pipelines.SHOPGATE_CATALOG_GET_PRODUCT_REVIEWS,
productId
}, false);
if (reviews.hasOwnProperty(hash)) {
return reviews[hash];
}
return null;
});
/**
* Select the product reviews state
* @param {Object} state The current application state.
* @return {Object} The product reviews state.
*/
const getReviewsByProductId = createSelector(getReviewsState, state => state.reviewsByProductId);
/**
* Retrieves the reviews collection which contains all reviews data.
* @param {Object} state The current application state.
* @return {Object} The reviews collection stored as reviewId => review pairs.
*/
export const getReviews = createSelector(getReviewsState, state => state.reviewsById || {});
/**
* Retrieves the number of reviews for a product
* @param {Object} state The current application state.
* @return {number} The total review count for a product
*/
export const getProductReviewCount = createSelector(getBaseProductId, getReviewsByProductId, (productId, reviewsState) => {
const collection = reviewsState[productId];
if (!collection || !collection.totalReviewCount) {
return null;
}
return collection.totalReviewCount;
});
/**
* Retrieves the total number of reviews for a current product.
* @param {Object} state The current application state.
* @return {number|null} The total number of reviews.
*/
export const getReviewsTotalCount = createSelector(getCollectionForCurrentBaseProduct, collection => {
if (!collection || !collection.totalReviewCount) {
return null;
}
return collection.totalReviewCount;
});
/**
* Retrieves the total number of currently fetched reviews for a current product.
* @param {Object} state The current application state.
* @return {number|null} The current number of fetched reviews.
*/
export const getCurrentReviewCount = createSelector(getCollectionForCurrentBaseProduct, collection => {
if (!collection || !collection.reviews) {
return null;
}
return collection.reviews.length;
});
/**
* Retrieves the information if reviews are currently fetched.
* @param {Object} state The current application state.
* @return {bool} The boolean information if reviews are currently being fetched.
*/
export const getReviewsFetchingState = createSelector(getCollectionForCurrentBaseProduct, collection => collection && collection.isFetching);
/**
* Select the user reviews state.
* @param {Object} state The current application state.
* @return {Object} The user reviews collection stored as productId => review.
*/
const getUserReviewsByProductId = createSelector(getReviewsState, state => state.userReviewsByProductId);
/**
* Retrieves a user review for a product.
*/
export const getUserReviewForProduct = createSelector(getUserReviewsByProductId, getReviews, (state, props = {}) => props.productId, (userReviews, allReviews, productId) => {
if (!userReviews || !userReviews[productId] || !allReviews[userReviews[productId].review]) {
return {};
}
return {
...allReviews[userReviews[productId].review]
};
});
/**
* Gets user reviews fetching state. Only the first fetch is considered.
* @return {bool} True if user review for current product is being fetched.
*/
export const getUserReviewFirstFetchState = createSelector(getBaseProductId, getUserReviewsByProductId, (productId, userReviews) => !!(userReviews && productId && userReviews[productId] && !userReviews[productId].review && userReviews[productId].isFetching));
/**
* Get a user name for the review form.
* @param {Object} state The state.
* @returns {string} A user name.
*/
export const getDefaultAuthorName = state => isUserLoggedIn && state.user.data && state.user.data.firstName ? `${state.user.data.firstName} ${state.user.data.lastName}` : '';
/**
* Retrieves the current product reviews.
* When the user review is available, it will always be the first entry.
* @param {Object} state The current application state.
* @return {Array|null} The reviews for a product.
*/
export const getProductReviews = createSelector(getCollectionForCurrentBaseProduct, getReviews, getUserReviewForProduct, (collection, allReviews, userReview) => {
if (!collection || !collection.reviews) {
return [];
}
const reviews = collection.reviews.map(id => allReviews[id]);
// There is no user review. Returning only from reviews collection.
if (!userReview.id) {
return reviews;
}
// User review always on top. Avoid duplicates.
return [userReview].concat(reviews.filter(r => r.id !== userReview.id));
});
/**
* Retrieves the current product reviews excerpt.
* When user review is available, it will always be the first entry.
* @param {Object} state The current application state.
* @return {Array|null} The reviews for a product
*/
export const getProductReviewsExcerpt = createSelector(getBaseProductId, getProductReviewsExcerptState, getReviews, getUserReviewForProduct, (productId, productReviewsState, reviewsState, userReview) => {
const collection = productReviewsState[productId];
if (!collection || !collection.reviews) {
return null;
}
const reviews = collection.reviews.map(id => reviewsState[id]);
if (!userReview.id) {
return reviews;
}
return [userReview].concat(reviews.filter(r => r.id !== userReview.id)).slice(0, REVIEW_PREVIEW_COUNT);
});