Allow filtering variant images by option type

This commit is contained in:
tniezg
2021-09-22 14:30:00 +02:00
parent 76596fd765
commit ed7e110188
10 changed files with 141 additions and 22 deletions

View File

@@ -17,3 +17,4 @@ NEXT_PUBLIC_SPREE_SHOW_SINGLE_VARIANT_OPTIONS=false
NEXT_PUBLIC_SPREE_LAST_UPDATED_PRODUCTS_PRERENDER_COUNT=10
NEXT_PUBLIC_SPREE_PRODUCT_PLACEHOLDER_IMAGE_URL=/product-img-placeholder.svg
NEXT_PUBLIC_SPREE_LINE_ITEM_PLACEHOLDER_IMAGE_URL=/product-img-placeholder.svg
NEXT_PUBLIC_SPREE_IMAGES_OPTION_FILTER=false

View File

@@ -0,0 +1 @@
export default class MissingOptionTypeError extends Error {}

View File

@@ -0,0 +1 @@
export default class MissingOptionValueError extends Error {}

View File

@@ -1,9 +1,10 @@
import forceIsomorphicConfigValues from './utils/force-isomorphic-config-values'
import requireConfig from './utils/require-config'
import validateAllProductsTaxonomyId from './utils/validate-all-products-taxonomy-id'
import validateCookieExpire from './utils/validate-cookie-expire'
import validatePlaceholderImageUrl from './utils/validate-placeholder-image-url'
import validateProductsPrerenderCount from './utils/validate-products-prerender-count'
import validateAllProductsTaxonomyId from './utils/validations/validate-all-products-taxonomy-id'
import validateCookieExpire from './utils/validations/validate-cookie-expire'
import validateImagesOptionFilter from './utils/validations/validate-images-option-filter'
import validatePlaceholderImageUrl from './utils/validations/validate-placeholder-image-url'
import validateProductsPrerenderCount from './utils/validations/validate-products-prerender-count'
const isomorphicConfig = {
apiHost: process.env.NEXT_PUBLIC_SPREE_API_HOST,
@@ -31,6 +32,9 @@ const isomorphicConfig = {
lineItemPlaceholderImageUrl: validatePlaceholderImageUrl(
process.env.NEXT_PUBLIC_SPREE_LINE_ITEM_PLACEHOLDER_IMAGE_URL
),
imagesOptionFilter: validateImagesOptionFilter(
process.env.NEXT_PUBLIC_SPREE_IMAGES_OPTION_FILTER
),
}
export default forceIsomorphicConfigValues(
@@ -49,6 +53,7 @@ export default forceIsomorphicConfigValues(
'lastUpdatedProductsPrerenderCount',
'productPlaceholderImageUrl',
'lineItemPlaceholderImageUrl',
'imagesOptionFilter',
]
)

View File

@@ -13,13 +13,20 @@ import expandOptions from './expand-options'
import getMediaGallery from './get-media-gallery'
import getProductPath from './get-product-path'
import MissingPrimaryVariantError from '../errors/MissingPrimaryVariantError'
import MissingOptionTypeError from '../errors/MissingOptionTypeError'
import MissingOptionValueError from '../errors/MissingOptionValueError'
import type { SpreeSdkResponse, VariantAttr } from '@framework/types'
import { jsonApi } from '@spree/storefront-api-v2-sdk'
import { JsonApiDocument } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi'
const placeholderImage = requireConfigValue('productPlaceholderImageUrl') as
| string
| false
const imagesOptionFilter = requireConfigValue('imagesOptionFilter') as
| string
| false
const normalizeProduct = (
spreeSuccessResponse: SpreeSdkResponse,
spreeProduct: ProductAttr
@@ -38,24 +45,6 @@ const normalizeProduct = (
const sku = primaryVariant.attributes.sku
const spreeImageRecords = jsonApi.findRelationshipDocuments(
spreeSuccessResponse,
spreeProduct,
'images'
)
const productImages = getMediaGallery(
spreeImageRecords,
createGetAbsoluteImageUrl(requireConfigValue('imageHost') as string)
)
const images: ProductImage[] =
productImages.length === 0
? placeholderImage === false
? []
: [{ url: placeholderImage }]
: productImages
const price: ProductPrice = {
value: parseFloat(spreeProduct.attributes.price),
currencyCode: spreeProduct.attributes.currency,
@@ -106,6 +95,113 @@ const normalizeProduct = (
}
})
const spreePrimaryVariantImageRecords = jsonApi.findRelationshipDocuments(
spreeSuccessResponse,
primaryVariant,
'images'
)
let spreeVariantImageRecords: JsonApiDocument[]
if (imagesOptionFilter === false) {
spreeVariantImageRecords = spreeVariantRecords.reduce<JsonApiDocument[]>(
(accumulatedImageRecords, spreeVariantRecord) => {
return [
...accumulatedImageRecords,
...jsonApi.findRelationshipDocuments(
spreeSuccessResponse,
spreeVariantRecord,
'images'
),
]
},
[]
)
} else {
const spreeOptionTypes = jsonApi.findRelationshipDocuments(
spreeSuccessResponse,
spreeProduct,
'option_types'
)
const imagesFilterOptionType = spreeOptionTypes.find(
(spreeOptionType) =>
spreeOptionType.attributes.name === imagesOptionFilter
)
if (!imagesFilterOptionType) {
throw new MissingOptionTypeError(
`Couldn't find option type having name ${imagesOptionFilter}.`
)
}
const imagesOptionTypeFilterId = imagesFilterOptionType.id
const includedOptionValuesImagesIds: string[] = []
spreeVariantImageRecords = spreeVariantRecords.reduce<JsonApiDocument[]>(
(accumulatedImageRecords, spreeVariantRecord) => {
const spreeVariantOptionValuesIdentifiers: RelationType[] =
spreeVariantRecord.relationships.option_values.data
const spreeOptionValueOfFilterTypeIdentifier =
spreeVariantOptionValuesIdentifiers.find(
(spreeVariantOptionValuesIdentifier: RelationType) =>
imagesFilterOptionType.relationships.option_values.data.some(
(filterOptionTypeValueIdentifier: RelationType) =>
filterOptionTypeValueIdentifier.id ===
spreeVariantOptionValuesIdentifier.id
)
)
if (!spreeOptionValueOfFilterTypeIdentifier) {
throw new MissingOptionValueError(
`Couldn't find option value related to option type with id ${imagesOptionTypeFilterId}.`
)
}
const optionValueImagesAlreadyIncluded =
includedOptionValuesImagesIds.includes(
spreeOptionValueOfFilterTypeIdentifier.id
)
if (optionValueImagesAlreadyIncluded) {
return accumulatedImageRecords
}
includedOptionValuesImagesIds.push(
spreeOptionValueOfFilterTypeIdentifier.id
)
return [
...accumulatedImageRecords,
...jsonApi.findRelationshipDocuments(
spreeSuccessResponse,
spreeVariantRecord,
'images'
),
]
},
[]
)
}
const spreeImageRecords = [
...spreePrimaryVariantImageRecords,
...spreeVariantImageRecords,
]
const productImages = getMediaGallery(
spreeImageRecords,
createGetAbsoluteImageUrl(requireConfigValue('imageHost') as string)
)
const images: ProductImage[] =
productImages.length === 0
? placeholderImage === false
? []
: [{ url: placeholderImage }]
: productImages
const slug = spreeProduct.attributes.slug
const path = getProductPath(spreeProduct)

View File

@@ -0,0 +1,15 @@
const validateImagesOptionFilter = (
optionTypeNameOrFalse: unknown
): string | false => {
if (!optionTypeNameOrFalse || optionTypeNameOrFalse === 'false') {
return false
}
if (typeof optionTypeNameOrFalse === 'string') {
return optionTypeNameOrFalse
}
throw new TypeError('optionTypeNameOrFalse must be a string or falsy.')
}
export default validateImagesOptionFilter