Implement creator studio and upload updates
This commit is contained in:
@@ -4,7 +4,7 @@ const FALLBACK_MD = 'https://files.skinbase.org/default/missing_md.webp'
|
||||
const FALLBACK_LG = 'https://files.skinbase.org/default/missing_lg.webp'
|
||||
const FALLBACK_XL = 'https://files.skinbase.org/default/missing_xl.webp'
|
||||
|
||||
export default function ArtworkHero({ artwork, presentMd, presentLg, presentXl, onOpenViewer, hasPrev, hasNext, onPrev, onNext }) {
|
||||
export default function ArtworkHero({ artwork, presentMd, presentLg, presentXl, mediaWidth = null, mediaHeight = null, mediaKey = 'cover', onOpenViewer, hasPrev, hasNext, onPrev, onNext }) {
|
||||
const [isLoaded, setIsLoaded] = useState(false)
|
||||
|
||||
const mdSource = presentMd?.url || artwork?.thumbs?.md?.url || null
|
||||
@@ -18,8 +18,8 @@ export default function ArtworkHero({ artwork, presentMd, presentLg, presentXl,
|
||||
const hasRealArtworkImage = Boolean(mdSource || lgSource || xlSource)
|
||||
const blurBackdropSrc = mdSource || lgSource || xlSource || null
|
||||
|
||||
const dbWidth = Number(artwork?.width)
|
||||
const dbHeight = Number(artwork?.height)
|
||||
const dbWidth = Number(mediaWidth ?? artwork?.width)
|
||||
const dbHeight = Number(mediaHeight ?? artwork?.height)
|
||||
const hasDbDims = dbWidth > 0 && dbHeight > 0
|
||||
|
||||
// Natural dimensions — seeded from DB if available, otherwise probed from
|
||||
@@ -28,6 +28,16 @@ export default function ArtworkHero({ artwork, presentMd, presentLg, presentXl,
|
||||
hasDbDims ? { w: dbWidth, h: dbHeight } : null
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
setIsLoaded(false)
|
||||
if (hasDbDims) {
|
||||
setNaturalDims({ w: dbWidth, h: dbHeight })
|
||||
return
|
||||
}
|
||||
|
||||
setNaturalDims(null)
|
||||
}, [mediaKey, hasDbDims, dbWidth, dbHeight])
|
||||
|
||||
// Probe the xl image to discover real dimensions when DB has none
|
||||
useEffect(() => {
|
||||
if (naturalDims || !xlSource) return
|
||||
|
||||
69
resources/js/components/artwork/ArtworkMediaStrip.jsx
Normal file
69
resources/js/components/artwork/ArtworkMediaStrip.jsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import React from 'react'
|
||||
|
||||
export default function ArtworkMediaStrip({ items = [], selectedId = null, onSelect }) {
|
||||
if (!Array.isArray(items) || items.length <= 1) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-4 rounded-[1.75rem] border border-white/10 bg-[linear-gradient(180deg,rgba(255,255,255,0.05),rgba(255,255,255,0.02))] p-4 shadow-[0_18px_60px_rgba(2,8,23,0.24)] backdrop-blur">
|
||||
<div className="mb-3 flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-white/45">Gallery</p>
|
||||
<p className="mt-1 text-sm text-white/60">Switch between the default cover and additional archive screenshots.</p>
|
||||
</div>
|
||||
<span className="rounded-full border border-white/10 bg-white/5 px-3 py-1 text-[11px] text-white/65">
|
||||
{items.length} views
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3 overflow-x-auto pb-1">
|
||||
{items.map((item) => {
|
||||
const active = item.id === selectedId
|
||||
|
||||
return (
|
||||
<button
|
||||
key={item.id}
|
||||
type="button"
|
||||
onClick={() => onSelect?.(item.id)}
|
||||
aria-pressed={active}
|
||||
className={[
|
||||
'group shrink-0 rounded-2xl border p-2 text-left transition-all',
|
||||
active
|
||||
? 'border-sky-300/45 bg-sky-400/12 shadow-[0_0_0_1px_rgba(56,189,248,0.18)]'
|
||||
: 'border-white/10 bg-white/[0.03] hover:border-white/20 hover:bg-white/[0.06]',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className="h-20 w-28 overflow-hidden rounded-xl bg-black/30 ring-1 ring-white/10 sm:h-24 sm:w-36">
|
||||
{item.thumbUrl ? (
|
||||
<img
|
||||
src={item.thumbUrl}
|
||||
alt={item.label}
|
||||
className="h-full w-full object-cover transition duration-300 group-hover:scale-[1.03]"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
) : (
|
||||
<div className="grid h-full w-full place-items-center text-white/30">
|
||||
<svg className="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" aria-hidden="true">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-2 flex items-center justify-between gap-2 px-1">
|
||||
<span className="truncate text-xs font-medium text-white/80">{item.label}</span>
|
||||
<span className={[
|
||||
'rounded-full px-2 py-0.5 text-[10px]',
|
||||
active ? 'bg-sky-300/20 text-sky-100' : 'bg-white/10 text-white/45',
|
||||
].join(' ')}>
|
||||
{active ? 'Showing' : 'View'}
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -425,9 +425,7 @@ export default function ArtworkRecommendationsRails({ artwork, related = [] }) {
|
||||
? `/discover/trending`
|
||||
: '/discover/trending'
|
||||
|
||||
const similarHref = artwork?.name
|
||||
? `/search?q=${encodeURIComponent(artwork.name)}`
|
||||
: '/search'
|
||||
const similarHref = artwork?.id ? `/art/${artwork.id}/similar` : null
|
||||
|
||||
return (
|
||||
<div className="space-y-14">
|
||||
|
||||
Reference in New Issue
Block a user