fix: Corrigindo deduplicação de eventos no Rudderstack

This commit is contained in:
Lucas Santana 2025-01-17 12:51:36 -03:00
parent bcbdd07a41
commit 6a1a471ce5
2 changed files with 93 additions and 70 deletions

View File

@ -1,4 +1,4 @@
import { useEffect } from 'react'; import { useEffect, useRef } from 'react';
import { useRudderstack } from '../../hooks/useRudderstack'; import { useRudderstack } from '../../hooks/useRudderstack';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { useAuth } from '../../hooks/useAuth'; import { useAuth } from '../../hooks/useAuth';
@ -7,81 +7,106 @@ export function PageTracker() {
const location = useLocation(); const location = useLocation();
const { page } = useRudderstack(); const { page } = useRudderstack();
const { user } = useAuth(); const { user } = useAuth();
const lastPageTracked = useRef<string | null>(null);
const timeoutRef = useRef<NodeJS.Timeout>();
useEffect(() => { useEffect(() => {
// Coleta informações do dispositivo/navegador // Se já rastreamos esta página, não rastrear novamente
const deviceInfo = { if (lastPageTracked.current === location.pathname) {
screenWidth: window.screen.width, return;
screenHeight: window.screen.height, }
viewportWidth: window.innerWidth,
viewportHeight: window.innerHeight,
deviceType: getDeviceType(),
deviceOrientation: window.screen.orientation.type,
userAgent: navigator.userAgent,
language: navigator.language,
};
// Coleta informações de performance // Limpa o timeout anterior se existir
const performanceInfo = { if (timeoutRef.current) {
loadTime: window.performance.timing.loadEventEnd - window.performance.timing.navigationStart, clearTimeout(timeoutRef.current);
domInteractive: window.performance.timing.domInteractive - window.performance.timing.navigationStart, }
firstContentfulPaint: getFirstContentfulPaint(),
};
// Informações da sessão // Debounce de 300ms para evitar múltiplos eventos
const sessionInfo = { timeoutRef.current = setTimeout(() => {
sessionStartTime: sessionStorage.getItem('sessionStartTime') || new Date().toISOString(), // Coleta informações do dispositivo/navegador
isFirstVisit: !localStorage.getItem('returningVisitor'), const deviceInfo = {
lastVisitedPage: sessionStorage.getItem('lastVisitedPage'), screenWidth: window.screen.width,
}; screenHeight: window.screen.height,
viewportWidth: window.innerWidth,
viewportHeight: window.innerHeight,
deviceType: getDeviceType(),
deviceOrientation: window.screen.orientation.type,
userAgent: navigator.userAgent,
language: navigator.language,
};
// Traits do usuário (se autenticado) // Coleta informações de performance
const userTraits = user ? { const performanceInfo = {
user_id: user.id, loadTime: window.performance.timing.loadEventEnd - window.performance.timing.navigationStart,
email: user.email, domInteractive: window.performance.timing.domInteractive - window.performance.timing.navigationStart,
school_id: user.user_metadata?.school_id, firstContentfulPaint: getFirstContentfulPaint(),
class_id: user.user_metadata?.class_id, };
name: user.user_metadata?.name,
role: user.user_metadata?.role,
last_updated: user.updated_at,
created_at: user.created_at
} : {};
// Envia dados adicionais usando o page() do Rudderstack
page(undefined, {
// Informações da página
path: location.pathname,
search: location.search,
hash: location.hash,
title: document.title,
referrer: document.referrer,
url: window.location.href,
// Informações do dispositivo e navegador
...deviceInfo,
// Informações de performance
...performanceInfo,
// Informações da sessão // Informações da sessão
...sessionInfo, const sessionInfo = {
sessionStartTime: sessionStorage.getItem('sessionStartTime') || new Date().toISOString(),
isFirstVisit: !localStorage.getItem('returningVisitor'),
lastVisitedPage: sessionStorage.getItem('lastVisitedPage'),
};
// Traits do usuário // Traits do usuário (se autenticado)
...userTraits, const userTraits = user ? {
user_id: user.id,
email: user.email,
school_id: user.user_metadata?.school_id,
class_id: user.user_metadata?.class_id,
name: user.user_metadata?.name,
role: user.user_metadata?.role,
last_updated: user.updated_at,
created_at: user.created_at
} : {};
// Metadados adicionais // Envia dados adicionais usando o page() do Rudderstack
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, page(undefined, {
}); // Informações da página
path: location.pathname,
search: location.search,
hash: location.hash,
title: document.title,
referrer: document.referrer,
url: window.location.href,
// Atualiza informações da sessão // Informações do dispositivo e navegador
sessionStorage.setItem('lastVisitedPage', location.pathname); ...deviceInfo,
if (!localStorage.getItem('returningVisitor')) {
localStorage.setItem('returningVisitor', 'true'); // Informações de performance
} ...performanceInfo,
if (!sessionStorage.getItem('sessionStartTime')) {
sessionStorage.setItem('sessionStartTime', new Date().toISOString()); // Informações da sessão
} ...sessionInfo,
}, [location, page, user]);
// Traits do usuário
...userTraits,
// Metadados adicionais
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
});
// Atualiza a última página rastreada
lastPageTracked.current = location.pathname;
// Atualiza informações da sessão
sessionStorage.setItem('lastVisitedPage', location.pathname);
if (!localStorage.getItem('returningVisitor')) {
localStorage.setItem('returningVisitor', 'true');
}
if (!sessionStorage.getItem('sessionStartTime')) {
sessionStorage.setItem('sessionStartTime', new Date().toISOString());
}
}, 300);
// Cleanup
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, [location.pathname, user]); // Reduzido dependências para apenas pathname e user
return null; return null;
} }

View File

@ -33,12 +33,10 @@ import { TestWordHighlighter } from './pages/TestWordHighlighter';
import { ExercisePage } from './pages/student-dashboard/ExercisePage'; import { ExercisePage } from './pages/student-dashboard/ExercisePage';
import { EvidenceBased } from './pages/landing/EvidenceBased'; import { EvidenceBased } from './pages/landing/EvidenceBased';
import { TextSalesLetter } from './pages/landing/TextSalesLetter'; import { TextSalesLetter } from './pages/landing/TextSalesLetter';
import { PageTracker } from './components/analytics/PageTracker';
function RootLayout({ children }: { children: React.ReactNode }) { function RootLayout({ children }: { children: React.ReactNode }) {
return ( return (
<> <>
<PageTracker />
{children} {children}
</> </>
); );