Build world campaigns rewards and recaps
This commit is contained in:
76
resources/js/components/worlds/UploadWorldHighlightCard.jsx
Normal file
76
resources/js/components/worlds/UploadWorldHighlightCard.jsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import WorldCampaignMeta from './WorldCampaignMeta'
|
||||
import WorldStatusBadge from './WorldStatusBadge'
|
||||
import { trackWorldSourceImpression } from '../../lib/worldAnalytics'
|
||||
|
||||
export default function UploadWorldHighlightCard({ world, sourceSurface = '', sourceDetail = '' }) {
|
||||
const cardRef = useRef(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!sourceSurface || !world?.id || typeof window === 'undefined') {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const node = cardRef.current
|
||||
if (!node) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (typeof window.IntersectionObserver !== 'function') {
|
||||
trackWorldSourceImpression({
|
||||
worldId: world.id,
|
||||
worldTitle: world.title,
|
||||
sourceSurface,
|
||||
sourceDetail,
|
||||
sectionKey: 'upload_highlight',
|
||||
})
|
||||
return undefined
|
||||
}
|
||||
|
||||
const observer = new window.IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (!entry.isIntersecting || entry.intersectionRatio < 0.5) {
|
||||
return
|
||||
}
|
||||
|
||||
trackWorldSourceImpression({
|
||||
worldId: world.id,
|
||||
worldTitle: world.title,
|
||||
sourceSurface,
|
||||
sourceDetail,
|
||||
sectionKey: 'upload_highlight',
|
||||
})
|
||||
observer.disconnect()
|
||||
})
|
||||
}, { threshold: [0.5] })
|
||||
|
||||
observer.observe(node)
|
||||
|
||||
return () => observer.disconnect()
|
||||
}, [sourceDetail, sourceSurface, world?.id, world?.title])
|
||||
|
||||
if (!world) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={cardRef} className="overflow-hidden rounded-[24px] border border-emerald-300/20 bg-[linear-gradient(135deg,rgba(16,185,129,0.14),rgba(15,23,42,0.84))] p-5">
|
||||
<div className="grid gap-4 md:grid-cols-[9rem_minmax(0,1fr)] md:items-center">
|
||||
<div className="h-28 overflow-hidden rounded-[20px] border border-white/12 bg-slate-950/80">
|
||||
{(world.teaser_image_url || world.cover_url) ? <img src={world.teaser_image_url || world.cover_url} alt={world.title} className="h-full w-full object-cover" /> : <div className="flex h-full items-center justify-center text-slate-500"><i className="fa-solid fa-globe" /></div>}
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-emerald-100/80">Upload spotlight</div>
|
||||
<div className="mt-3 flex flex-wrap gap-2">
|
||||
{(Array.isArray(world.status_badges) ? world.status_badges : []).map((badge) => <WorldStatusBadge key={badge.label} badge={badge} />)}
|
||||
{world.campaign_label ? <WorldStatusBadge badge={{ label: world.campaign_label, tone: 'slate' }} /> : null}
|
||||
</div>
|
||||
<h3 className="mt-4 text-xl font-semibold tracking-[-0.03em] text-white">{world.teaser_title || world.title}</h3>
|
||||
{world.teaser_title && world.teaser_title !== world.title ? <div className="mt-1 text-xs uppercase tracking-[0.16em] text-white/55">{world.title}</div> : null}
|
||||
{(world.teaser_summary || world.summary) ? <p className="mt-3 text-sm leading-6 text-slate-200/85">{world.teaser_summary || world.summary}</p> : null}
|
||||
<WorldCampaignMeta world={world} className="mt-4" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user