import {useCallback, useEffect, useRef} from 'react'
import {useHandleAction} from './useHandleAction'
import {useShopActions} from './useShopActions'
export interface UseProductImpressionParams {
/**
* The product GID string to report impressions for
*/
productId: string
/**
* Whether to skip reporting impressions (e.g., when product data is loading)
*/
skip?: boolean
}
export interface UseProductImpressionReturns {
/**
* Ref to attach to the product element for visibility tracking.
* When the element becomes visible, an impression will be reported.
*/
ref: {current: HTMLDivElement | null}
}
/**
* Hook to report product impressions when a product becomes visible.
* Attach the returned ref to the product container element.
*
* @example
* ```tsx
* function ProductCard({ product }) {
* const { ref } = useProductImpression({ productId: product.id });
* return
...
;
* }
* ```
*/
export function useProductImpression({
productId,
skip = false,
}: UseProductImpressionParams): UseProductImpressionReturns {
const {productRecommendationImpression} = useShopActions()
const handleAction = useHandleAction(productRecommendationImpression)
const ref = useRef(null)
const hasReportedRef = useRef(false)
const reportImpression = useCallback(() => {
if (hasReportedRef.current || skip || !productId) {
return
}
hasReportedRef.current = true
handleAction({productId})
}, [handleAction, productId, skip])
useEffect(() => {
// Reset reported state when productId changes
hasReportedRef.current = false
}, [productId])
useEffect(() => {
if (skip || !ref.current) {
return
}
const element = ref.current
const observer = new IntersectionObserver(
entries => {
const [entry] = entries
if (entry?.isIntersecting) {
reportImpression()
observer.disconnect()
}
},
{
threshold: 0.5, // Report when 50% of the element is visible
}
)
observer.observe(element)
return () => {
observer.disconnect()
}
}, [reportImpression, skip])
return {ref}
}