-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
feat: Analytics scroll depth #12079
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Analytics scroll depth #12079
Changes from 8 commits
0f81d80
c12c0ca
c26bb44
125bf64
6abf7e5
0f32831
6ccc03c
ca0eecb
4b82d07
e2cb24b
5eeb288
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,7 @@ import './globals.css'; | |
| import {Theme} from '@radix-ui/themes'; | ||
| import type {Metadata} from 'next'; | ||
| import {Rubik} from 'next/font/google'; | ||
| import Script from 'next/script'; | ||
| import PlausibleProvider from 'next-plausible'; | ||
|
|
||
| import {ThemeProvider} from 'sentry-docs/components/theme-provider'; | ||
|
|
||
|
|
@@ -31,6 +31,9 @@ export const metadata: Metadata = { | |
| export default function RootLayout({children}: {children: React.ReactNode}) { | ||
| return ( | ||
| <html lang="en" suppressHydrationWarning> | ||
| <head> | ||
| <PlausibleProvider domain="docs.sentry.io,rollup.sentry.io" /> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we need dev docs as well? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably, we should ask @lizokm There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It doesn't look like we have Plausible installed on dev docs (if it is, it's not hooked up to the company account). |
||
| </head> | ||
| <body className={rubik.variable} suppressHydrationWarning> | ||
| <ThemeProvider | ||
| attribute="class" | ||
|
|
@@ -43,12 +46,6 @@ export default function RootLayout({children}: {children: React.ReactNode}) { | |
| </Theme> | ||
| </ThemeProvider> | ||
| </body> | ||
| <Script | ||
| defer | ||
| data-domain="docs.sentry.io,rollup.sentry.io" | ||
| data-api="https://plausible.io/api/event" | ||
| src="https://plausible.io/js/script.tagged-events.js" | ||
| /> | ||
| </html> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| 'use client'; | ||
| import {useEffect} from 'react'; | ||
| import {usePlausible} from 'next-plausible'; | ||
|
|
||
| import {debounce} from 'sentry-docs/utils'; | ||
|
|
||
| const EVENT = 'Read Progress'; | ||
| const milestones = [25, 50, 75, 100] as const; | ||
| type Milestone = (typeof milestones)[number]; | ||
| type EVENT_PROPS = {page: string; readProgress: Milestone}; | ||
|
|
||
| export function ReaderDepthTracker() { | ||
| const plausible = usePlausible<{[EVENT]: EVENT_PROPS}>(); | ||
|
|
||
| const sendProgressToPlausible = (progress: Milestone) => { | ||
| // TODO: remove this after PR review | ||
| const args = [ | ||
| EVENT, | ||
| {props: {readProgress: progress, page: document.title}}, | ||
| ] as const; | ||
| plausible(...args); | ||
| console.log('plausible event', ...args); | ||
|
||
| }; | ||
|
|
||
| useEffect(() => { | ||
| const reachedMilestones = new Set<Milestone>(); | ||
|
|
||
| const trackProgress = () => { | ||
| // calculate the progress based on the scroll position | ||
| const scrollPosition = window.scrollY; | ||
| const totalHeight = document.documentElement.scrollHeight - window.innerHeight; | ||
| let progress = Math.floor((scrollPosition / totalHeight) * 100); | ||
| // it's hard to trigger the 100% milestone, so we'll just assume beyond 95% | ||
| if (progress > 95) { | ||
| progress = 100; | ||
| } | ||
|
|
||
| // find the biggest milestone that has not been reached yet | ||
| const milestone = milestones.findLast( | ||
| m => | ||
| progress >= m && | ||
| !reachedMilestones.has(m) && | ||
| // we shouldn't report smaller milestones once a bigger one has been reached | ||
| Array.from(reachedMilestones).every(r => m > r) | ||
| ); | ||
| if (milestone) { | ||
| reachedMilestones.add(milestone); | ||
| sendProgressToPlausible(milestone); | ||
| } | ||
| }; | ||
|
|
||
| // if the page is not scrollable, we don't need to track anything | ||
| if (document.documentElement.scrollHeight - window.innerHeight === 0) { | ||
| return () => {}; | ||
| } | ||
| const debouncedTrackProgress = debounce(trackProgress, 50); | ||
|
|
||
| window.addEventListener('scroll', debouncedTrackProgress); | ||
| return () => { | ||
| window.removeEventListener('scroll', debouncedTrackProgress); | ||
| }; | ||
| }); | ||
| // do not render anything | ||
| return null; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the advantage of having this instead of the script?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
send events dynamically through a hook, nice TS and local DX experience