diff --git a/packages/snap-controller/src/Autocomplete/AutocompleteController.ts b/packages/snap-controller/src/Autocomplete/AutocompleteController.ts index 214e8649d..2cbc43c4a 100644 --- a/packages/snap-controller/src/Autocomplete/AutocompleteController.ts +++ b/packages/snap-controller/src/Autocomplete/AutocompleteController.ts @@ -77,7 +77,7 @@ export class AutocompleteController extends AbstractController { declare store: AutocompleteStore; declare config: AutocompleteControllerConfig; public storage: StorageStore; - private lastSearchQuery: string | undefined; + private lastSearchKey: string | undefined; private events: { [responseId: string]: { @@ -773,8 +773,8 @@ export class AutocompleteController extends AbstractController { const responseId = response.tracking.responseId; this.events[responseId] = this.events[responseId] || { product: {}, banner: {} }; - - if (response.search?.query === this.lastSearchQuery) { + const currentSearchKey = response.search?.query + JSON.stringify(this.urlManager.state.filter || {}); + if (currentSearchKey === this.lastSearchKey) { const impressedResultIds = Object.keys(this.events[responseId].product || {}).filter( (resultId) => this.events[responseId].product?.[resultId]?.impression ); @@ -787,7 +787,7 @@ export class AutocompleteController extends AbstractController { }; } else { this.events[responseId] = { product: {}, banner: {} }; - this.lastSearchQuery = response.search?.query; + this.lastSearchKey = currentSearchKey; } const afterSearchProfile = this.profiler.create({ type: 'event', name: 'afterSearch', context: params }).start(); diff --git a/packages/snap-preact-components/src/hooks/useIntersectionAdvanced.tsx b/packages/snap-preact-components/src/hooks/useIntersectionAdvanced.tsx index da2056909..7bfae751c 100644 --- a/packages/snap-preact-components/src/hooks/useIntersectionAdvanced.tsx +++ b/packages/snap-preact-components/src/hooks/useIntersectionAdvanced.tsx @@ -9,7 +9,10 @@ export interface UseIntersectionOptions { } const VISIBILITY_POLL_INTERVAL = 250; -export const useIntersectionAdvanced = (ref: MutableRef, options: UseIntersectionOptions = {}): boolean => { +export const useIntersectionAdvanced = ( + ref: MutableRef, + options: UseIntersectionOptions = {} +): { inViewport: boolean; updateRef: (el: HTMLElement | null) => void } => { const { rootMargin = '0px', fireOnce = false, threshold = 0, minVisibleTime = 0, resetKey } = options; // State and setter for storing whether element is visible const [isIntersecting, setIntersecting] = useState(false); @@ -22,6 +25,15 @@ export const useIntersectionAdvanced = (ref: MutableRef, opt // Track the last reset key to detect changes const lastResetKeyRef = useRef(resetKey); + const [counter, setCounter] = useState(0); + const updateRef = (el: HTMLElement | null) => { + // setting ref.current does not update useEffect, counter used to force useEffect + if (!ref.current || ref.current !== el) { + ref.current = el; + setCounter((c) => c + 1); + } + }; + // Reset state if resetKey has changed if (resetKey !== lastResetKeyRef.current) { setIntersecting(false); @@ -31,7 +43,7 @@ export const useIntersectionAdvanced = (ref: MutableRef, opt } visibleStartRef.current = null; lastResetKeyRef.current = resetKey; - return false; + return { inViewport: false, updateRef }; } useEffect(() => { @@ -140,9 +152,9 @@ export const useIntersectionAdvanced = (ref: MutableRef, opt observer.unobserve(ref.current); } }; - }, [ref, resetKey]); + }, [ref, resetKey, counter]); - return isIntersecting; + return { inViewport: isIntersecting, updateRef }; }; function elementIsVisible(el: HTMLElement): boolean { diff --git a/packages/snap-preact-components/src/providers/withTracking.tsx b/packages/snap-preact-components/src/providers/withTracking.tsx index 1194525dd..ae20f933f 100644 --- a/packages/snap-preact-components/src/providers/withTracking.tsx +++ b/packages/snap-preact-components/src/providers/withTracking.tsx @@ -51,7 +51,7 @@ export function withTracking(WrappedComponent: }); } - const { ref, inViewport } = createImpressionObserver({ resetKey }); + const { ref, inViewport, updateRef } = createImpressionObserver({ resetKey }); if (inViewport) { // TODO: add support for disabling tracking events via config like in ResultTracker @@ -90,7 +90,14 @@ export function withTracking(WrappedComponent: banner, type, content, - trackingRef: ref, + trackingRef: useCallback( + (el: HTMLElement | null) => { + if (!ref.current) { + updateRef(el); + } + }, + [ref, updateRef] + ), }; return ; diff --git a/packages/snap-preact-components/src/utilities/createImpressionObserver.ts b/packages/snap-preact-components/src/utilities/createImpressionObserver.ts index 49589852d..f1f594bb6 100644 --- a/packages/snap-preact-components/src/utilities/createImpressionObserver.ts +++ b/packages/snap-preact-components/src/utilities/createImpressionObserver.ts @@ -6,9 +6,10 @@ const IMPRESSION_MIN_VISIBLE_TIME = 1000; export function createImpressionObserver(options?: UseIntersectionOptions): { ref: Ref; inViewport: boolean; + updateRef: (el: HTMLElement | null) => void; } { - const ref = useRef(null); - const inViewport = useIntersectionAdvanced(ref, { + const ref = useRef(null); + const { inViewport, updateRef } = useIntersectionAdvanced(ref, { ...options, fireOnce: true, threshold: IMPRESSION_VISIBILITY_THRESHOLD, @@ -17,5 +18,6 @@ export function createImpressionObserver(options?: UseIntersectionOptions): { return { ref, inViewport, + updateRef, }; } diff --git a/packages/snap-preact-demo/src/components/Results/Results.tsx b/packages/snap-preact-demo/src/components/Results/Results.tsx index 8a2dff294..ee04c7613 100644 --- a/packages/snap-preact-demo/src/components/Results/Results.tsx +++ b/packages/snap-preact-demo/src/components/Results/Results.tsx @@ -37,13 +37,13 @@ export class Results extends Component { const infiniteEnabled = Boolean(controller.config.settings.infinite); const infiniteRef = useRef(null); if (infiniteEnabled) { - const atBottom = useIntersectionAdvanced(infiniteRef, { + const { inViewport } = useIntersectionAdvanced(infiniteRef, { rootMargin: '0px', threshold: 1, minVisibleTime: 300, }); - if (atBottom && pagination.next && !loading && pagination.totalResults > 0) { + if (inViewport && pagination.next && !loading && pagination.totalResults > 0) { setTimeout(() => { pagination.next.url.go({ history: 'replace' }); });