From 0e60c9023bc5ffcaacca9620a54ac5a0610755c1 Mon Sep 17 00:00:00 2001 From: Leo McArdle Date: Wed, 16 Oct 2024 14:23:57 +0000 Subject: [PATCH 1/5] remove scrimba discount fallback banner --- client/src/ui/organisms/placement/index.tsx | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/client/src/ui/organisms/placement/index.tsx b/client/src/ui/organisms/placement/index.tsx index e9f7a051ceb3..53850d0fcf4c 100644 --- a/client/src/ui/organisms/placement/index.tsx +++ b/client/src/ui/organisms/placement/index.tsx @@ -92,22 +92,7 @@ export function SidePlacement() { function TopPlacementFallbackContent() { const gleanClick = useGleanClick(); - return Date.now() < Date.parse("2024-10-12") ? ( -

- Learn front-end development with a 30% discount on{" "} - { - gleanClick(BANNER_SCRIMBA_CLICK); - }} - > - Scrimba - {" "} - — limited time offer! -

- ) : ( + return (

Learn front-end development with high quality, interactive courses from{" "} Date: Wed, 16 Oct 2024 14:51:36 +0000 Subject: [PATCH 2/5] refactor placement view logic into useViewed hook --- client/src/hooks.ts | 56 ++++++++++++++++++++- client/src/ui/organisms/placement/index.tsx | 53 ++----------------- 2 files changed, 58 insertions(+), 51 deletions(-) diff --git a/client/src/hooks.ts b/client/src/hooks.ts index b6c9cedb235b..91b1974f55e6 100644 --- a/client/src/hooks.ts +++ b/client/src/hooks.ts @@ -1,4 +1,10 @@ -import React, { useEffect, useMemo, useState } from "react"; +import React, { + useCallback, + useEffect, + useRef, + useState, + useMemo, +} from "react"; import { useLocation, useNavigationType, useParams } from "react-router-dom"; import { DEFAULT_LOCALE } from "../../libs/constants"; import { isValidLocale } from "../../libs/locale-utils"; @@ -269,3 +275,51 @@ export const useScrollToAnchor = () => { } }); }; + +interface Timer { + timeout: number | null; +} + +const INTERSECTION_OPTIONS = { + root: null, + rootMargin: "0px", + threshold: 0.5, +}; + +export function useViewed(callback: Function) { + const timer = useRef({ timeout: null }); + const isVisible = usePageVisibility(); + + const [node, setNode] = useState(); + const isIntersecting = useIsIntersecting(node, INTERSECTION_OPTIONS); + + const sendViewed = useCallback(() => { + timer.current = { timeout: -1 }; + callback(); + }, [callback]); + + useEffect(() => { + if (timer.current.timeout !== -1) { + // timeout !== -1 means the viewed has not been sent + if (isVisible && isIntersecting) { + if (timer.current.timeout === null) { + timer.current = { + timeout: window.setTimeout(sendViewed, 1000), + }; + } + } + } + return () => { + if (timer.current.timeout !== null && timer.current.timeout !== -1) { + clearTimeout(timer.current.timeout); + timer.current = { timeout: null }; + } + }; + }, [isVisible, isIntersecting, sendViewed]); + + return useCallback((node: HTMLElement | null) => { + if (node) { + setNode(node); + } + }, []); +} diff --git a/client/src/ui/organisms/placement/index.tsx b/client/src/ui/organisms/placement/index.tsx index 53850d0fcf4c..5467299e7512 100644 --- a/client/src/ui/organisms/placement/index.tsx +++ b/client/src/ui/organisms/placement/index.tsx @@ -1,9 +1,4 @@ -import { useCallback, useEffect, useRef, useState } from "react"; -import { - useIsIntersecting, - useIsServer, - usePageVisibility, -} from "../../../hooks"; +import { useIsServer, useViewed } from "../../../hooks"; import { User, useUserData } from "../../../user-context"; import "./index.scss"; @@ -12,10 +7,6 @@ import { Status, usePlacement } from "../../../placement-context"; import { Payload as PlacementData } from "../../../../../libs/pong/types"; import { BANNER_SCRIMBA_CLICK } from "../../../telemetry/constants"; -interface Timer { - timeout: number | null; -} - interface PlacementRenderArgs { place: any; extraClassNames?: string[]; @@ -33,12 +24,6 @@ interface PlacementRenderArgs { heading?: string; } -const INTERSECTION_OPTIONS = { - root: null, - rootMargin: "0px", - threshold: 0.5, -}; - function viewed(pong?: PlacementData) { pong?.view && navigator.sendBeacon?.( @@ -262,44 +247,12 @@ export function PlacementInner({ }) { const isServer = useIsServer(); const user = useUserData(); - const isVisible = usePageVisibility(); const gleanClick = useGleanClick(); - const timer = useRef({ timeout: null }); - - const [node, setNode] = useState(); - const isIntersecting = useIsIntersecting(node, INTERSECTION_OPTIONS); - - const sendViewed = useCallback(() => { + const place = useViewed(() => { viewed(pong); gleanClick(`pong: pong->viewed ${typ}`); - timer.current = { timeout: -1 }; - }, [pong, gleanClick, typ]); - - const place = useCallback((node: HTMLElement | null) => { - if (node) { - setNode(node); - } - }, []); - - useEffect(() => { - if (timer.current.timeout !== -1) { - // timeout !== -1 means the viewed has not been sent - if (isVisible && isIntersecting) { - if (timer.current.timeout === null) { - timer.current = { - timeout: window.setTimeout(sendViewed, 1000), - }; - } - } - } - return () => { - if (timer.current.timeout !== null && timer.current.timeout !== -1) { - clearTimeout(timer.current.timeout); - timer.current = { timeout: null }; - } - }; - }, [isVisible, isIntersecting, sendViewed]); + }); const { image, copy, alt, click, version, heading } = pong || {}; return ( From 2f34f4fa3ffc42ae50a17b7b6e68a40a547a45f0 Mon Sep 17 00:00:00 2001 From: Leo McArdle Date: Wed, 16 Oct 2024 14:59:12 +0000 Subject: [PATCH 3/5] simplify/clean up useViewed code --- client/src/hooks.ts | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/client/src/hooks.ts b/client/src/hooks.ts index 91b1974f55e6..9a691f443c15 100644 --- a/client/src/hooks.ts +++ b/client/src/hooks.ts @@ -276,27 +276,19 @@ export const useScrollToAnchor = () => { }); }; -interface Timer { +interface ViewedTimer { timeout: number | null; } -const INTERSECTION_OPTIONS = { - root: null, - rootMargin: "0px", - threshold: 0.5, -}; - export function useViewed(callback: Function) { - const timer = useRef({ timeout: null }); + const timer = useRef({ timeout: null }); const isVisible = usePageVisibility(); - const [node, setNode] = useState(); - const isIntersecting = useIsIntersecting(node, INTERSECTION_OPTIONS); - - const sendViewed = useCallback(() => { - timer.current = { timeout: -1 }; - callback(); - }, [callback]); + const isIntersecting = useIsIntersecting(node, { + root: null, + rootMargin: "0px", + threshold: 0.5, + }); useEffect(() => { if (timer.current.timeout !== -1) { @@ -304,7 +296,10 @@ export function useViewed(callback: Function) { if (isVisible && isIntersecting) { if (timer.current.timeout === null) { timer.current = { - timeout: window.setTimeout(sendViewed, 1000), + timeout: window.setTimeout(() => { + timer.current = { timeout: -1 }; + callback(); + }, 1000), }; } } @@ -315,7 +310,7 @@ export function useViewed(callback: Function) { timer.current = { timeout: null }; } }; - }, [isVisible, isIntersecting, sendViewed]); + }, [isVisible, isIntersecting, callback]); return useCallback((node: HTMLElement | null) => { if (node) { From 2d7c9ad6d3aed021f2d78dce1aa42041c0cd5f3d Mon Sep 17 00:00:00 2001 From: Leo McArdle Date: Wed, 16 Oct 2024 15:12:30 +0000 Subject: [PATCH 4/5] add view pings for scrimba --- client/src/curriculum/landing.tsx | 11 ++++++++++- client/src/curriculum/partner-banner.tsx | 11 ++++++++++- client/src/telemetry/constants.ts | 1 + client/src/ui/organisms/placement/index.tsx | 9 ++++++++- 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/client/src/curriculum/landing.tsx b/client/src/curriculum/landing.tsx index b6d36e9808c5..bc3cbd5c96bd 100644 --- a/client/src/curriculum/landing.tsx +++ b/client/src/curriculum/landing.tsx @@ -15,8 +15,10 @@ import "./index.scss"; import "./landing.scss"; import { ProseSection } from "../../../libs/types/document"; import { PartnerBanner } from "./partner-banner"; -import { useIsServer } from "../hooks"; +import { useIsServer, useViewed } from "../hooks"; import scrimBg from "../assets/curriculum/landing-scrim.png"; +import { useGleanClick } from "../telemetry/glean-context"; +import { CURRICULUM } from "../telemetry/constants"; const ScrimInline = lazy(() => import("./scrim-inline")); @@ -133,6 +135,12 @@ function About({ section }) { const { title, content, id } = section.value; const html = useMemo(() => ({ __html: content }), [content]); const isServer = useIsServer(); + const gleanClick = useGleanClick(); + const observedNode = useViewed(() => { + const url = new URL(SCRIM_URL); + const id = url.pathname.slice(1); + gleanClick(`${CURRICULUM}: scrim view id:${id}`); + }); return (

@@ -148,6 +156,7 @@ function About({ section }) { url={SCRIM_URL} img={scrimBg} scrimTitle="MDN + Scrimba partnership announcement scrim" + ref={observedNode} /> )} diff --git a/client/src/curriculum/partner-banner.tsx b/client/src/curriculum/partner-banner.tsx index 5374e6f1c41a..80be1b7f73b0 100644 --- a/client/src/curriculum/partner-banner.tsx +++ b/client/src/curriculum/partner-banner.tsx @@ -1,12 +1,21 @@ import ThemedPicture from "../ui/atoms/themed-picture"; +import { useGleanClick } from "../telemetry/glean-context"; +import { useViewed } from "../hooks"; +import { CURRICULUM } from "../telemetry/constants"; + import bannerDark from "../../public/assets/curriculum/curriculum-partner-banner-illustration-large-dark.svg"; import bannerLight from "../../public/assets/curriculum/curriculum-partner-banner-illustration-large-light.svg"; import "./partner-banner.scss"; export function PartnerBanner() { + const gleanClick = useGleanClick(); + const observedNode = useViewed(() => { + gleanClick(`${CURRICULUM}: partner banner view`); + }); + return ( -
+

Learn the curriculum with Scrimba and become job ready

diff --git a/client/src/telemetry/constants.ts b/client/src/telemetry/constants.ts index 89c8ee0ce0b9..5fb1f05048a3 100644 --- a/client/src/telemetry/constants.ts +++ b/client/src/telemetry/constants.ts @@ -22,6 +22,7 @@ export const BANNER_BLOG_LAUNCH_CLICK = "banner_blog_launch_click"; export const AI_HELP = "ai_help"; export const BANNER_AI_HELP_CLICK = "banner_ai_help_click"; export const BANNER_SCRIMBA_CLICK = "banner_scrimba_click"; +export const BANNER_SCRIMBA_VIEW = "banner_scrimba_view"; export const PLAYGROUND = "play_action"; export const AI_EXPLAIN = "ai_explain"; export const SETTINGS = "settings"; diff --git a/client/src/ui/organisms/placement/index.tsx b/client/src/ui/organisms/placement/index.tsx index 5467299e7512..a02859efbb52 100644 --- a/client/src/ui/organisms/placement/index.tsx +++ b/client/src/ui/organisms/placement/index.tsx @@ -5,7 +5,10 @@ import "./index.scss"; import { useGleanClick } from "../../../telemetry/glean-context"; import { Status, usePlacement } from "../../../placement-context"; import { Payload as PlacementData } from "../../../../../libs/pong/types"; -import { BANNER_SCRIMBA_CLICK } from "../../../telemetry/constants"; +import { + BANNER_SCRIMBA_CLICK, + BANNER_SCRIMBA_VIEW, +} from "../../../telemetry/constants"; interface PlacementRenderArgs { place: any; @@ -76,6 +79,9 @@ export function SidePlacement() { function TopPlacementFallbackContent() { const gleanClick = useGleanClick(); + const observedNode = useViewed(() => { + gleanClick(BANNER_SCRIMBA_VIEW); + }); return (

@@ -84,6 +90,7 @@ function TopPlacementFallbackContent() { href="https://scrimba.com/learn/frontend?via=mdn" target="_blank" rel="noreferrer" + ref={observedNode} onClick={() => { gleanClick(BANNER_SCRIMBA_CLICK); }} From ca6b7481ca9db6f13b7244c9242ecd450b547431 Mon Sep 17 00:00:00 2001 From: Leo McArdle Date: Wed, 16 Oct 2024 15:30:03 +0000 Subject: [PATCH 5/5] add click pings for curriculum landing scrimba banner and scrim click outs --- client/src/curriculum/partner-banner.tsx | 6 ++++++ client/src/curriculum/scrim-inline.ts | 1 + 2 files changed, 7 insertions(+) diff --git a/client/src/curriculum/partner-banner.tsx b/client/src/curriculum/partner-banner.tsx index 80be1b7f73b0..831c1ae8b2bb 100644 --- a/client/src/curriculum/partner-banner.tsx +++ b/client/src/curriculum/partner-banner.tsx @@ -25,6 +25,9 @@ export function PartnerBanner() { target="_blank" rel="origin noreferrer" className="external" + onClick={() => { + gleanClick(`${CURRICULUM}: partner banner click`); + }} > Scrimba's Frontend Developer Career Path {" "} @@ -37,6 +40,9 @@ export function PartnerBanner() { target="_blank" rel="origin noreferrer" className="external" + onClick={() => { + gleanClick(`${CURRICULUM}: partner banner click`); + }} > Find out more diff --git a/client/src/curriculum/scrim-inline.ts b/client/src/curriculum/scrim-inline.ts index 196a88ee6204..7bab646ab3bf 100644 --- a/client/src/curriculum/scrim-inline.ts +++ b/client/src/curriculum/scrim-inline.ts @@ -80,6 +80,7 @@ class ScrimInline extends LitElement { target="_blank" rel="origin noreferrer" class="external" + data-glean="${CURRICULUM}: scrim link id:${this._scrimId}" > Open on Scrimba