(
+ `${import.meta.env.PUBLIC_API_URL}/v1-get-sponsor`,
+ {
+ href: window.location.pathname,
+ mobile: isMobile() ? 'true' : 'false',
+ },
+ );
+
+ if (error) {
+ console.error(error);
+ return;
+ }
+
+ setStickyTopSponsor(response?.stickyTopAd);
+ setBottomRightSponsor(response?.bottomRightAd);
+ }
+
+ async function logSponsorImpression(
+ sponsor: BottomRightSponsorType | StickyTopSponsorType,
+ ) {
+ window.fireEvent({
+ category: 'SponsorImpression',
+ action: `${sponsor?.company} Impression`,
+ label:
+ sponsor?.gaLabel || `${gaPageIdentifier} / ${sponsor?.company} Link`,
+ });
+ }
+
+ async function clickSponsor(
+ sponsor: BottomRightSponsorType | StickyTopSponsorType,
+ ) {
+ const { id: sponsorId, company, gaLabel } = sponsor;
+
+ const labelValue = gaLabel || `${gaPageIdentifier} / ${company} Link`;
+
+ window.fireEvent({
+ category: 'SponsorClick',
+ action: `${company} Redirect`,
+ label: labelValue,
+ value: labelValue,
+ });
+
+ const clickUrl = new URL(
+ `${import.meta.env.PUBLIC_API_URL}/v1-view-sponsor/${sponsorId}`,
+ );
+
+ const { response, error } = await httpPatch<{ status: 'ok' }>(
+ clickUrl.toString(),
+ {
+ mobile: isMobile(),
+ },
+ );
+
+ if (error || !response) {
+ console.error(error);
+ return;
+ }
+
+ setViewSponsorCookie(sponsorId);
+ }
+
+ useEffect(() => {
+ window.setTimeout(loadSponsor);
+ }, []);
+
+ if ($isSponsorHidden) {
+ return null;
+ }
+
+ return (
+
+ {stickyTopSponsor && !isSponsorMarkedHidden(stickyTopSponsor.id) && (
+ {
+ logSponsorImpression(stickyTopSponsor).catch(console.error);
+ }}
+ onSponsorClick={() => {
+ clickSponsor(stickyTopSponsor).catch(console.error);
+ }}
+ onSponsorHidden={() => {
+ markSponsorHidden(stickyTopSponsor.id);
+ }}
+ />
+ )}
+ {bottomRightSponsor && !isSponsorMarkedHidden(bottomRightSponsor.id) && (
+ {
+ clickSponsor(bottomRightSponsor).catch(console.error);
+ }}
+ onSponsorHidden={() => {
+ markSponsorHidden(bottomRightSponsor.id);
+ }}
+ onSponsorImpression={() => {
+ logSponsorImpression(bottomRightSponsor).catch(console.error);
+ }}
+ />
+ )}
+
+ );
+}
diff --git a/src/components/PageSponsors/StickyTopSponsor.tsx b/src/components/PageSponsors/StickyTopSponsor.tsx
new file mode 100644
index 000000000..8e2e5fc2e
--- /dev/null
+++ b/src/components/PageSponsors/StickyTopSponsor.tsx
@@ -0,0 +1,73 @@
+import { cn } from '../../lib/classname.ts';
+import { useScrollPosition } from '../../hooks/use-scroll-position.ts';
+import { X } from 'lucide-react';
+import type { StickyTopSponsorType } from './PageSponsors.tsx';
+import { useEffect, useState } from 'react';
+
+type StickyTopSponsorProps = {
+ sponsor: StickyTopSponsorType;
+
+ onSponsorImpression: () => void;
+ onSponsorClick: () => void;
+ onSponsorHidden: () => void;
+};
+
+const SCROLL_DISTANCE = 100;
+
+export function StickyTopSponsor(props: StickyTopSponsorProps) {
+ const { sponsor, onSponsorHidden, onSponsorImpression, onSponsorClick } =
+ props;
+
+ const { y: scrollY } = useScrollPosition();
+ const [isImpressionLogged, setIsImpressionLogged] = useState(false);
+ const [isHidden, setIsHidden] = useState(false);
+
+ useEffect(() => {
+ if (scrollY < SCROLL_DISTANCE || isImpressionLogged) {
+ return;
+ }
+
+ setIsImpressionLogged(true);
+ onSponsorImpression();
+ }, [scrollY]);
+
+ if (scrollY < SCROLL_DISTANCE || isHidden) {
+ return null;
+ }
+
+ return (
+
+
+
+ Register for our free cloud workshop
+
+
+
+
+ );
+}
diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro
index d5f31f257..9983b3373 100644
--- a/src/layouts/BaseLayout.astro
+++ b/src/layouts/BaseLayout.astro
@@ -8,7 +8,7 @@ import Navigation from '../components/Navigation/Navigation.astro';
import OpenSourceBanner from '../components/OpenSourceBanner.astro';
import { PageProgress } from '../components/PageProgress';
import { Toaster } from '../components/Toast';
-import { PageSponsor } from '../components/PageSponsor';
+import { PageSponsors } from '../components/PageSponsors/PageSponsors';
import { siteConfig } from '../lib/config';
import '../styles/global.css';
import { PageVisit } from '../components/PageVisit/PageVisit';
@@ -184,7 +184,8 @@ const gaPageIdentifier = Astro.url.pathname
-