Wire admin studio SSR and search infrastructure
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import React, { lazy, Suspense } from 'react'
|
||||
import React, { lazy, Suspense, useEffect, useRef, useState } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import HomepageAnnouncement from '../../components/homepage/HomepageAnnouncement'
|
||||
|
||||
// Below-fold — lazy-loaded to keep initial bundle small
|
||||
const HomeWelcomeRow = lazy(() => import('./HomeWelcomeRow'))
|
||||
@@ -102,80 +103,150 @@ function SectionFallback({ variant = 'gallery' }) {
|
||||
)
|
||||
}
|
||||
|
||||
function SectionPlaceholder({ variant = 'gallery' }) {
|
||||
const heightClassName = variant === 'welcome'
|
||||
? 'h-20'
|
||||
: variant === 'tags'
|
||||
? 'h-28'
|
||||
: variant === 'cta'
|
||||
? 'h-40'
|
||||
: variant === 'news'
|
||||
? 'h-48'
|
||||
: variant === 'categories'
|
||||
? 'h-44'
|
||||
: variant === 'creators'
|
||||
? 'h-72'
|
||||
: variant === 'collections'
|
||||
? 'h-80'
|
||||
: variant === 'groups'
|
||||
? 'h-80'
|
||||
: 'h-[28rem]'
|
||||
|
||||
return (
|
||||
<section className="mt-14 px-4 sm:px-6 lg:px-8" aria-hidden="true">
|
||||
<div className={`rounded-[28px] border border-white/8 bg-nova-900/40 ${heightClassName}`} />
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
function DeferredSection({ children, fallback, variant = 'gallery', eager = false, rootMargin = '1200px 0px' }) {
|
||||
const anchorRef = useRef(null)
|
||||
const [isVisible, setIsVisible] = useState(eager)
|
||||
|
||||
useEffect(() => {
|
||||
if (eager || isVisible) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const node = anchorRef.current
|
||||
if (!node) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (typeof window === 'undefined' || typeof window.IntersectionObserver !== 'function') {
|
||||
setIsVisible(true)
|
||||
return undefined
|
||||
}
|
||||
|
||||
const observer = new window.IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (!entry.isIntersecting) {
|
||||
return
|
||||
}
|
||||
|
||||
setIsVisible(true)
|
||||
observer.disconnect()
|
||||
})
|
||||
}, { rootMargin, threshold: 0.01 })
|
||||
|
||||
observer.observe(node)
|
||||
|
||||
return () => observer.disconnect()
|
||||
}, [eager, isVisible, rootMargin])
|
||||
|
||||
return (
|
||||
<div ref={anchorRef}>
|
||||
{isVisible
|
||||
? <Suspense fallback={fallback}><>{children}</></Suspense>
|
||||
: <SectionPlaceholder variant={variant} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function GuestHomePage(props) {
|
||||
const { rising, trending, community_favorites, hall_of_fame, fresh, tags, creators, news, collections_featured, collections_trending, collections_editorial, collections_community, groups, world_spotlight } = props
|
||||
|
||||
return (
|
||||
<>
|
||||
<Suspense fallback={<SectionFallback variant="gallery" />}>
|
||||
<DeferredSection eager fallback={<SectionFallback variant="gallery" />}>
|
||||
<HomeRising items={rising} />
|
||||
</Suspense>
|
||||
<Suspense fallback={<SectionFallback variant="gallery" />}>
|
||||
</DeferredSection>
|
||||
<DeferredSection eager fallback={<SectionFallback variant="gallery" />}>
|
||||
<HomeTrending items={trending} />
|
||||
</Suspense>
|
||||
<Suspense fallback={<SectionFallback variant="gallery" />}>
|
||||
</DeferredSection>
|
||||
<DeferredSection fallback={<SectionFallback variant="gallery" />}>
|
||||
<HomeMedalHighlights
|
||||
title="Community Favorites"
|
||||
href="/explore/top-rated"
|
||||
href="/explore?sort=top-rated"
|
||||
description="Recent medal momentum from the community. This rail highlights the strongest 30-day medal signal."
|
||||
items={community_favorites}
|
||||
/>
|
||||
</Suspense>
|
||||
<Suspense fallback={<SectionFallback variant="gallery" />}>
|
||||
</DeferredSection>
|
||||
<DeferredSection fallback={<SectionFallback variant="gallery" />}>
|
||||
<HomeMedalHighlights
|
||||
title="Hall of Fame"
|
||||
href="/explore/best"
|
||||
description="All-time medal standouts that keep being remembered long after publication."
|
||||
items={hall_of_fame}
|
||||
/>
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
{/* 3. Fresh Uploads */}
|
||||
<Suspense fallback={<SectionFallback variant="gallery" />}>
|
||||
<DeferredSection fallback={<SectionFallback variant="gallery" />}>
|
||||
<HomeFresh items={fresh} />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
<Suspense fallback={<SectionFallback variant="collections" />}>
|
||||
<DeferredSection variant="collections" fallback={<SectionFallback variant="collections" />}>
|
||||
<HomeCollections
|
||||
featured={collections_featured}
|
||||
trending={collections_trending}
|
||||
editorial={collections_editorial}
|
||||
community={collections_community}
|
||||
/>
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
<Suspense fallback={<SectionFallback variant="collections" />}>
|
||||
<DeferredSection variant="collections" fallback={<SectionFallback variant="collections" />}>
|
||||
<HomeWorldSpotlight world={world_spotlight} />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
<Suspense fallback={<SectionFallback variant="groups" />}>
|
||||
<DeferredSection variant="groups" fallback={<SectionFallback variant="groups" />}>
|
||||
<HomeGroups groups={groups} />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
{/* 4. Explore Categories */}
|
||||
<Suspense fallback={<SectionFallback variant="categories" />}>
|
||||
<DeferredSection variant="categories" fallback={<SectionFallback variant="categories" />}>
|
||||
<HomeCategories />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
{/* 5. Popular Tags */}
|
||||
<Suspense fallback={<SectionFallback variant="tags" />}>
|
||||
<DeferredSection variant="tags" fallback={<SectionFallback variant="tags" />}>
|
||||
<HomeTags tags={tags} />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
{/* 6. Top Creators */}
|
||||
<Suspense fallback={<SectionFallback variant="creators" />}>
|
||||
<DeferredSection variant="creators" fallback={<SectionFallback variant="creators" />}>
|
||||
<HomeCreators creators={creators} />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
{/* 7. News */}
|
||||
<Suspense fallback={<SectionFallback variant="news" />}>
|
||||
<DeferredSection variant="news" fallback={<SectionFallback variant="news" />}>
|
||||
<HomeNews items={news} />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
{/* 8. CTA Upload */}
|
||||
<Suspense fallback={<SectionFallback variant="cta" />}>
|
||||
<DeferredSection variant="cta" fallback={<SectionFallback variant="cta" />}>
|
||||
<HomeCTA isLoggedIn={false} />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -208,57 +279,57 @@ function AuthHomePage(props) {
|
||||
return (
|
||||
<>
|
||||
{/* P0. Welcome/status row — below hero so featured image sits at 0px */}
|
||||
<Suspense fallback={<SectionFallback variant="welcome" />}>
|
||||
<DeferredSection eager variant="welcome" fallback={<SectionFallback variant="welcome" />}>
|
||||
<HomeWelcomeRow user_data={user_data} />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
{/* P2. From Creators You Follow */}
|
||||
<Suspense fallback={<SectionFallback variant="gallery" />}>
|
||||
<DeferredSection eager fallback={<SectionFallback variant="gallery" />}>
|
||||
<HomeFromFollowing items={from_following} />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
{/* P3. Personalized For You preview */}
|
||||
<Suspense fallback={<SectionFallback variant="gallery" />}>
|
||||
<DeferredSection eager fallback={<SectionFallback variant="gallery" />}>
|
||||
<HomeTrendingForYou items={for_you} preferences={preferences} />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
{/* Rising Now */}
|
||||
<Suspense fallback={<SectionFallback variant="gallery" />}>
|
||||
<DeferredSection fallback={<SectionFallback variant="gallery" />}>
|
||||
<HomeRising items={rising} />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
{/* 2. Global Trending Now */}
|
||||
<Suspense fallback={<SectionFallback variant="gallery" />}>
|
||||
<DeferredSection fallback={<SectionFallback variant="gallery" />}>
|
||||
<HomeTrending items={trending} />
|
||||
</Suspense>
|
||||
<Suspense fallback={<SectionFallback variant="gallery" />}>
|
||||
</DeferredSection>
|
||||
<DeferredSection fallback={<SectionFallback variant="gallery" />}>
|
||||
<HomeMedalHighlights
|
||||
title="Community Favorites"
|
||||
href="/explore/top-rated"
|
||||
href="/explore?sort=top-rated"
|
||||
description="Recent medal momentum from the community. This rail highlights the strongest 30-day medal signal."
|
||||
items={community_favorites}
|
||||
/>
|
||||
</Suspense>
|
||||
<Suspense fallback={<SectionFallback variant="gallery" />}>
|
||||
</DeferredSection>
|
||||
<DeferredSection fallback={<SectionFallback variant="gallery" />}>
|
||||
<HomeMedalHighlights
|
||||
title="Hall of Fame"
|
||||
href="/explore/best"
|
||||
description="All-time medal standouts that keep being remembered long after publication."
|
||||
items={hall_of_fame}
|
||||
/>
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
{/* P4. Because You Like {top tag} — uses by_categories for variety */}
|
||||
<Suspense fallback={<SectionFallback variant="gallery" />}>
|
||||
<DeferredSection fallback={<SectionFallback variant="gallery" />}>
|
||||
<HomeBecauseYouLike items={by_categories} preferences={preferences} />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
{/* 3. Fresh Uploads */}
|
||||
<Suspense fallback={<SectionFallback variant="gallery" />}>
|
||||
<DeferredSection fallback={<SectionFallback variant="gallery" />}>
|
||||
<HomeFresh items={fresh} />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
<Suspense fallback={<SectionFallback variant="collections" />}>
|
||||
<DeferredSection variant="collections" fallback={<SectionFallback variant="collections" />}>
|
||||
<HomeCollections
|
||||
featured={collections_featured}
|
||||
recent={collections_recent}
|
||||
@@ -267,45 +338,45 @@ function AuthHomePage(props) {
|
||||
community={collections_community}
|
||||
isLoggedIn
|
||||
/>
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
<Suspense fallback={<SectionFallback variant="collections" />}>
|
||||
<DeferredSection variant="collections" fallback={<SectionFallback variant="collections" />}>
|
||||
<HomeWorldSpotlight world={world_spotlight} />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
<Suspense fallback={<SectionFallback variant="groups" />}>
|
||||
<DeferredSection variant="groups" fallback={<SectionFallback variant="groups" />}>
|
||||
<HomeGroups groups={groups} />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
{/* 4. Explore Categories */}
|
||||
<Suspense fallback={<SectionFallback variant="categories" />}>
|
||||
<DeferredSection variant="categories" fallback={<SectionFallback variant="categories" />}>
|
||||
<HomeCategories />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
{/* P5. Suggested Creators */}
|
||||
<Suspense fallback={<SectionFallback variant="creators" />}>
|
||||
<DeferredSection variant="creators" fallback={<SectionFallback variant="creators" />}>
|
||||
<HomeSuggestedCreators creators={suggested_creators} />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
{/* 5. Popular Tags */}
|
||||
<Suspense fallback={<SectionFallback variant="tags" />}>
|
||||
<DeferredSection variant="tags" fallback={<SectionFallback variant="tags" />}>
|
||||
<HomeTags tags={tags} />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
{/* 6. Top Creators */}
|
||||
<Suspense fallback={<SectionFallback variant="creators" />}>
|
||||
<DeferredSection variant="creators" fallback={<SectionFallback variant="creators" />}>
|
||||
<HomeCreators creators={creators} />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
{/* 7. News */}
|
||||
<Suspense fallback={<SectionFallback variant="news" />}>
|
||||
<DeferredSection variant="news" fallback={<SectionFallback variant="news" />}>
|
||||
<HomeNews items={news} />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
|
||||
{/* 8. CTA Upload */}
|
||||
<Suspense fallback={<SectionFallback variant="cta" />}>
|
||||
<DeferredSection variant="cta" fallback={<SectionFallback variant="cta" />}>
|
||||
<HomeCTA isLoggedIn />
|
||||
</Suspense>
|
||||
</DeferredSection>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -313,6 +384,7 @@ function AuthHomePage(props) {
|
||||
function HomePage(props) {
|
||||
return (
|
||||
<div className="pb-24">
|
||||
<HomepageAnnouncement announcement={props.announcement || null} />
|
||||
{props.is_logged_in
|
||||
? <AuthHomePage {...props} />
|
||||
: <GuestHomePage {...props} />
|
||||
@@ -322,6 +394,7 @@ function HomePage(props) {
|
||||
}
|
||||
|
||||
// Auto-mount when the Blade view provides #homepage-root
|
||||
if (typeof document !== 'undefined') {
|
||||
const mountEl = document.getElementById('homepage-root')
|
||||
if (mountEl) {
|
||||
let props = {}
|
||||
@@ -334,5 +407,6 @@ if (mountEl) {
|
||||
|
||||
createRoot(mountEl).render(<HomePage {...props} />)
|
||||
}
|
||||
}
|
||||
|
||||
export default HomePage
|
||||
|
||||
Reference in New Issue
Block a user