Remove legacy frontend assets and update gallery routes

This commit is contained in:
2026-03-14 15:06:28 +01:00
parent 4f576ceb04
commit 78151aabfe
550 changed files with 826 additions and 108930 deletions

View File

@@ -124,6 +124,10 @@ function Rail({ title, emoji, items, seeAllHref }) {
const scrollRef = useRef(null)
const isResettingRef = useRef(false)
const scrollEndTimer = useRef(null)
const suppressClickTimerRef = useRef(null)
const touchStartRef = useRef({ x: 0, y: 0 })
const draggedRef = useRef(false)
const suppressClickRef = useRef(false)
const itemCount = items.length
/* Triple items so we can loop seamlessly: [clone|original|clone] */
@@ -139,6 +143,13 @@ function Rail({ title, emoji, items, seeAllHref }) {
return el.children[itemCount].offsetLeft - el.children[0].offsetLeft
}, [itemCount])
/* Scroll step based on rendered card width + gap for predictable smooth motion */
const getStepWidth = useCallback(() => {
const el = scrollRef.current
if (!el || el.children.length < 2) return el ? el.clientWidth * 0.75 : 0
return el.children[1].offsetLeft - el.children[0].offsetLeft
}, [])
/* Centre on the middle (real) set after mount / data change */
useEffect(() => {
const el = scrollRef.current
@@ -176,6 +187,19 @@ function Rail({ title, emoji, items, seeAllHref }) {
}
}, [getSetWidth, itemCount])
/* Keep user in the centre segment before scripted smooth scroll starts */
const normalizeToMiddle = useCallback(() => {
const el = scrollRef.current
if (!el || !itemCount) return
const setW = getSetWidth()
if (setW === 0) return
if (el.scrollLeft < setW || el.scrollLeft >= setW * 2) {
el.style.scrollBehavior = 'auto'
el.scrollLeft = ((el.scrollLeft % setW) + setW) % setW + setW
el.style.scrollBehavior = ''
}
}, [getSetWidth, itemCount])
/* Scroll listener: debounced boundary check + resize re-centre */
useEffect(() => {
const el = scrollRef.current
@@ -199,6 +223,7 @@ function Rail({ title, emoji, items, seeAllHref }) {
el.removeEventListener('scroll', onScroll)
window.removeEventListener('resize', onResize)
clearTimeout(scrollEndTimer.current)
clearTimeout(suppressClickTimerRef.current)
}
}, [loopItems, resetIfNeeded, getSetWidth])
@@ -219,8 +244,48 @@ function Rail({ title, emoji, items, seeAllHref }) {
const scroll = useCallback((dir) => {
const el = scrollRef.current
if (!el) return
const amount = el.clientWidth * 0.75
normalizeToMiddle()
const step = getStepWidth()
const amount = step > 0 ? step * 2 : el.clientWidth * 0.75
el.scrollBy({ left: dir === 'left' ? -amount : amount, behavior: 'smooth' })
clearTimeout(scrollEndTimer.current)
scrollEndTimer.current = setTimeout(resetIfNeeded, 260)
}, [getStepWidth, normalizeToMiddle, resetIfNeeded])
/* Prevent accidental link activation after horizontal swipe on touch devices */
const onTouchStart = useCallback((e) => {
if (!e.touches?.length) return
const t = e.touches[0]
touchStartRef.current = { x: t.clientX, y: t.clientY }
draggedRef.current = false
}, [])
const onTouchMove = useCallback((e) => {
if (!e.touches?.length) return
const t = e.touches[0]
const dx = Math.abs(t.clientX - touchStartRef.current.x)
const dy = Math.abs(t.clientY - touchStartRef.current.y)
if (dx > 10 && dx > dy) {
draggedRef.current = true
}
}, [])
const onTouchEnd = useCallback(() => {
if (!draggedRef.current) return
suppressClickRef.current = true
clearTimeout(suppressClickTimerRef.current)
suppressClickTimerRef.current = setTimeout(() => {
suppressClickRef.current = false
}, 260)
}, [])
const onClickCapture = useCallback((e) => {
if (!suppressClickRef.current) return
const link = e.target?.closest?.('a')
if (link) {
e.preventDefault()
e.stopPropagation()
}
}, [])
if (!items.length) return null
@@ -238,7 +303,7 @@ function Rail({ title, emoji, items, seeAllHref }) {
)}
</div>
<div className="relative">
<div className="relative" data-nav-swipe-ignore="1">
{/* Permanent edge fades for infinite illusion */}
<div className="pointer-events-none absolute inset-y-0 left-0 z-20 w-24 bg-gradient-to-r from-[#0F1724] to-transparent" />
<div className="pointer-events-none absolute inset-y-0 right-0 z-20 w-24 bg-gradient-to-l from-[#0F1724] to-transparent" />
@@ -248,7 +313,11 @@ function Rail({ title, emoji, items, seeAllHref }) {
<div
ref={scrollRef}
className="flex gap-4 overflow-x-auto px-4 pb-3 sm:px-6 lg:px-8 scrollbar-hide [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden"
onTouchStart={onTouchStart}
onTouchMove={onTouchMove}
onTouchEnd={onTouchEnd}
onClickCapture={onClickCapture}
className="flex gap-4 overflow-x-auto px-4 pb-3 sm:px-6 lg:px-8 snap-x snap-mandatory scroll-smooth scrollbar-hide [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden"
>
{loopItems.map((item, idx) => (
<RailCard key={`${item.id || item.url}-${idx}`} item={item} />
@@ -356,9 +425,13 @@ export default function ArtworkRecommendationsRails({ artwork, related = [] }) {
? `/discover/trending`
: '/discover/trending'
const similarHref = artwork?.name
? `/search?q=${encodeURIComponent(artwork.name)}`
: '/search'
return (
<div className="space-y-14">
<Rail title="Similar Artworks" emoji="✨" items={similarItems} />
<Rail title="Similar Artworks" emoji="✨" items={similarItems} seeAllHref={similarHref} />
<Rail title={trendingLabel} emoji="🔥" items={trendingRailItems} seeAllHref={trendingHref} />
</div>
)