diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx index 27bfbd0..faba171 100644 --- a/src/components/Footer.jsx +++ b/src/components/Footer.jsx @@ -1,5 +1,7 @@ "use client"; +import { useEffect, useState } from "react"; import { usePathname } from "@/hooks/usePathname"; +import { fetchOpeningsCount } from "@/lib/careers"; import { Button } from "@/components/Button"; import routes from "@/generated/routes.json"; import clsx from "clsx"; @@ -137,7 +139,28 @@ export function PageNextPrevious({ navigation }) { ); } -function SmallPrint() { +// Live opening count for the Careers pill. `initialOpenings` is fetched +// server-side (BaseLayout) so the pill is in the initial HTML — no dependency on +// the visitor's browser reaching workatastartup.com. The client refresh keeps a +// statically-built page current; a blocked or failed refresh keeps the seed. +const CAREERS_HREF = "/careers"; + +function useOpeningsCount(initial) { + const [count, setCount] = useState(initial ?? null); + + useEffect(() => { + const controller = new AbortController(); + fetchOpeningsCount(controller.signal).then((next) => { + if (next !== null) setCount(next); + }); + return () => controller.abort(); + }, []); + + return count; +} + +function SmallPrint({ initialOpenings }) { + const openings = useOpeningsCount(initialOpenings); return (
@@ -220,13 +243,21 @@ function SmallPrint() {

Company

@@ -308,7 +339,7 @@ function SmallPrint() { // a dark override wrapper instead. const DARK_THEMED_PATH_PREFIXES = ['/learn']; -export function Footer({ initialPathname = "" }) { +export function Footer({ initialPathname = "", initialOpenings = null }) { // usePathname returns "" during SSR; fall back to the server-provided path // so docs pages do not flash a light footer before hydration. const pathname = usePathname() || initialPathname; @@ -331,7 +362,7 @@ export function Footer({ initialPathname = "" }) { - +
); diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index fee77ba..2260e9d 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -4,6 +4,7 @@ import '@fortawesome/fontawesome-svg-core/styles.css'; import { ViewTransitions } from 'astro:transitions'; import { EmbedDetector } from '@/components/EmbedDetector'; import { Footer } from '@/components/Footer'; +import { fetchOpeningsCount } from '@/lib/careers'; import { Providers } from '@/components/Providers'; import { TooltipProvider } from '@rivet-gg/components'; import CopyCodeScript from '@/components/CopyCodeScript.astro'; @@ -68,6 +69,13 @@ const effectiveCanonicalUrl = canonicalUrl || `https://rivet.dev${ensureTrailing // RSS feed is only relevant for blog and changelog pages const showRssFeed = currentPath.startsWith('/blog') || currentPath.startsWith('/changelog'); + +// Open-role count for the footer Careers pill, fetched server-side so the pill +// is baked into the HTML and does not depend on the visitor's browser reaching +// workatastartup.com. Bounded so a slow feed cannot stall the response; a null +// result (timeout/error) simply renders no pill. The Footer refreshes this +// client-side to keep statically-built pages current. +const footerOpenings = await fetchOpeningsCount(AbortSignal.timeout(4000)); --- @@ -201,7 +209,7 @@ const showRssFeed = currentPath.startsWith('/blog') || currentPath.startsWith('/ -