11import type { ComponentEvent } from '@guardian/ophan-tracker-js' ;
2- import { useEffect } from 'react' ;
2+ import { useEffect , useRef } from 'react' ;
33import { submitComponentEvent } from '../client/ophan/ophan' ;
44
55const collectionIdentifier = '[data-collection-tracking="true"]' ;
@@ -8,15 +8,20 @@ const getCollectionElements = (): HTMLElement[] => {
88 return Array . from ( document . querySelectorAll ( collectionIdentifier ) ) ;
99} ;
1010
11+ /**
12+ * Calculate the distance from the top of the element to the top of the page.
13+ */
14+ const calculateDistanceFromTop = ( collection : HTMLElement ) => {
15+ return (
16+ collection . getBoundingClientRect ( ) . top + window . pageYOffset
17+ ) . toFixed ( 0 ) ;
18+ } ;
19+
1120const reportInsertEvent = ( elements : HTMLElement [ ] ) => {
1221 for ( const [ index , element ] of elements . entries ( ) ) {
1322 const sectionName = element . id ;
1423 if ( sectionName === '' ) continue ;
1524
16- const distanceFromTop = (
17- element . getBoundingClientRect ( ) . top + window . pageYOffset
18- ) . toFixed ( 0 ) ;
19-
2025 const ophanComponentEvent : ComponentEvent = {
2126 component : {
2227 componentType : 'CONTAINER' ,
@@ -27,7 +32,11 @@ const reportInsertEvent = (elements: HTMLElement[]) => {
2732 * - The number of the collection in the list (the top collection is 1)
2833 * - The distance from the top of the collection to the top of the page
2934 */
30- labels : [ sectionName , ( index + 1 ) . toString ( ) , distanceFromTop ] ,
35+ labels : [
36+ sectionName ,
37+ ( index + 1 ) . toString ( ) ,
38+ calculateDistanceFromTop ( element ) ,
39+ ] ,
3140 } ,
3241 action : 'INSERT' ,
3342 } ;
@@ -36,24 +45,23 @@ const reportInsertEvent = (elements: HTMLElement[]) => {
3645 }
3746} ;
3847
39- const reportViewEvent = ( sectionName : string ) => {
48+ const reportViewEvent = ( element : HTMLElement ) => {
4049 const ophanComponentEvent : ComponentEvent = {
4150 component : {
4251 componentType : 'CONTAINER' ,
43- labels : [ sectionName ] ,
52+ /**
53+ * Labels:
54+ * - The name of the collection
55+ * - The distance from the top of the collection to the top of the page
56+ */
57+ labels : [ element . id , calculateDistanceFromTop ( element ) ] ,
4458 } ,
4559 action : 'VIEW' ,
4660 } ;
4761
4862 void submitComponentEvent ( ophanComponentEvent , 'Web' ) ;
4963} ;
5064
51- const viewedCollections = new Set < string > ( ) ;
52-
53- const setCollectionAsViewed = ( id : string ) => {
54- viewedCollections . add ( id ) ;
55- } ;
56-
5765/**
5866 * Report fronts section data to Ophan.
5967 *
@@ -64,6 +72,12 @@ const setCollectionAsViewed = (id: string) => {
6472 * - The id of the section/collection is also the human-readable name of the collection
6573 */
6674export const FrontSectionTracker = ( ) => {
75+ /**
76+ * Use a ref to persist this across renders.
77+ * Rendering is not affected by what collections have been viewed by the user.
78+ */
79+ const viewedCollectionsRef = useRef < Set < string > > ( new Set ( ) ) ;
80+
6781 useEffect ( ( ) => {
6882 if ( ! ( 'IntersectionObserver' in window ) ) return ;
6983
@@ -74,11 +88,13 @@ export const FrontSectionTracker = () => {
7488 const callback = ( entries : IntersectionObserverEntry [ ] ) => {
7589 for ( const entry of entries ) {
7690 if ( entry . isIntersecting ) {
77- const collectionName = entry . target . id ;
78- if ( ! viewedCollections . has ( collectionName ) ) {
79- setCollectionAsViewed ( collectionName ) ;
80- reportViewEvent ( collectionName ) ;
81- observer . unobserve ( entry . target ) ;
91+ const collectionElement = entry . target as HTMLElement ;
92+ const collectionName = collectionElement . id ;
93+
94+ if ( ! viewedCollectionsRef . current . has ( collectionName ) ) {
95+ viewedCollectionsRef . current . add ( collectionName ) ;
96+ reportViewEvent ( collectionElement ) ;
97+ observer . unobserve ( collectionElement ) ;
8298 }
8399 }
84100 }
0 commit comments