240 lines
7.7 KiB
JavaScript
240 lines
7.7 KiB
JavaScript
import React, { useState, useEffect, useCallback } from 'react'
|
||
import { usePage } from '@inertiajs/react'
|
||
import ProfileHero from '../../components/profile/ProfileHero'
|
||
import ProfileTabs from '../../components/profile/ProfileTabs'
|
||
import TabArtworks from '../../components/profile/tabs/TabArtworks'
|
||
import TabAchievements from '../../components/profile/tabs/TabAchievements'
|
||
import TabAbout from '../../components/profile/tabs/TabAbout'
|
||
import TabStats from '../../components/profile/tabs/TabStats'
|
||
import TabFavourites from '../../components/profile/tabs/TabFavourites'
|
||
import TabCollections from '../../components/profile/tabs/TabCollections'
|
||
import TabActivity from '../../components/profile/tabs/TabActivity'
|
||
import TabPosts from '../../components/profile/tabs/TabPosts'
|
||
import TabStories from '../../components/profile/tabs/TabStories'
|
||
|
||
const VALID_TABS = ['posts', 'artworks', 'stories', 'achievements', 'collections', 'about', 'stats', 'favourites', 'activity']
|
||
|
||
function getInitialTab(initialTab = 'posts') {
|
||
if (typeof window === 'undefined') {
|
||
return VALID_TABS.includes(initialTab) ? initialTab : 'posts'
|
||
}
|
||
|
||
try {
|
||
const pathname = window.location.pathname.replace(/\/+$/, '')
|
||
const segments = pathname.split('/').filter(Boolean)
|
||
const lastSegment = segments.at(-1)
|
||
|
||
if (VALID_TABS.includes(lastSegment)) {
|
||
return lastSegment
|
||
}
|
||
} catch {
|
||
return VALID_TABS.includes(initialTab) ? initialTab : 'posts'
|
||
}
|
||
|
||
return VALID_TABS.includes(initialTab) ? initialTab : 'posts'
|
||
}
|
||
|
||
/**
|
||
* ProfileShow – Inertia page for /@username
|
||
*
|
||
* Props injected by ProfileController::renderUserProfile()
|
||
*/
|
||
export default function ProfileShow() {
|
||
const { props } = usePage()
|
||
|
||
const {
|
||
user,
|
||
profile,
|
||
artworks,
|
||
featuredArtworks,
|
||
favourites,
|
||
stats,
|
||
socialLinks,
|
||
followerCount,
|
||
recentFollowers,
|
||
viewerIsFollowing,
|
||
heroBgUrl,
|
||
profileComments,
|
||
creatorStories,
|
||
achievements,
|
||
leaderboardRank,
|
||
countryName,
|
||
isOwner,
|
||
auth,
|
||
initialTab,
|
||
profileUrl,
|
||
galleryUrl,
|
||
profileTabUrls,
|
||
} = props
|
||
|
||
const [activeTab, setActiveTab] = useState(() => getInitialTab(initialTab))
|
||
|
||
const handleTabChange = useCallback((tab) => {
|
||
if (!VALID_TABS.includes(tab)) return
|
||
setActiveTab(tab)
|
||
|
||
try {
|
||
const currentUrl = new URL(window.location.href)
|
||
const targetBase = profileTabUrls?.[tab] || `${profileUrl || `${window.location.origin}`}/${tab}`
|
||
const nextUrl = new URL(targetBase, window.location.origin)
|
||
const sharedPostId = currentUrl.searchParams.get('post')
|
||
|
||
if (sharedPostId) {
|
||
nextUrl.searchParams.set('post', sharedPostId)
|
||
}
|
||
|
||
window.history.pushState({}, '', nextUrl.toString())
|
||
} catch (_) {}
|
||
}, [profileTabUrls, profileUrl])
|
||
|
||
useEffect(() => {
|
||
const onPop = () => setActiveTab(getInitialTab(initialTab))
|
||
window.addEventListener('popstate', onPop)
|
||
return () => window.removeEventListener('popstate', onPop)
|
||
}, [initialTab])
|
||
|
||
const isLoggedIn = !!(auth?.user)
|
||
|
||
// Normalise artwork list (SSR may send cursor-paginated object)
|
||
const artworkList = Array.isArray(artworks)
|
||
? artworks
|
||
: (artworks?.data ?? [])
|
||
const artworkNextCursor = artworks?.next_cursor ?? null
|
||
const favouriteList = Array.isArray(favourites)
|
||
? favourites
|
||
: (favourites?.data ?? [])
|
||
const favouriteNextCursor = favourites?.next_cursor ?? null
|
||
|
||
// Normalise social links (may be object keyed by platform, or array)
|
||
const socialLinksObj = Array.isArray(socialLinks)
|
||
? socialLinks.reduce((acc, l) => { acc[l.platform] = l; return acc }, {})
|
||
: (socialLinks ?? {})
|
||
|
||
const contentShellClassName = activeTab === 'artworks'
|
||
? 'w-full px-4 md:px-6'
|
||
: activeTab === 'posts'
|
||
? 'mx-auto max-w-7xl px-4 md:px-6'
|
||
: 'max-w-6xl mx-auto px-4'
|
||
|
||
return (
|
||
<div className="relative min-h-screen overflow-hidden pb-16">
|
||
<div
|
||
aria-hidden="true"
|
||
className="pointer-events-none absolute inset-x-0 top-0 -z-10 h-[34rem] opacity-90"
|
||
style={{
|
||
background: 'radial-gradient(circle at top left, rgba(56,189,248,0.18), transparent 32%), radial-gradient(circle at 82% 10%, rgba(249,115,22,0.16), transparent 28%), linear-gradient(180deg, #07101d 0%, #0a1220 42%, #0a1220 100%)',
|
||
}}
|
||
/>
|
||
<div
|
||
aria-hidden="true"
|
||
className="pointer-events-none absolute inset-0 -z-10 opacity-[0.06]"
|
||
style={{ backgroundImage: 'url(/gfx/noise.png)', backgroundSize: '180px' }}
|
||
/>
|
||
|
||
<ProfileHero
|
||
user={user}
|
||
profile={profile}
|
||
isOwner={isOwner}
|
||
viewerIsFollowing={viewerIsFollowing}
|
||
followerCount={followerCount}
|
||
heroBgUrl={heroBgUrl}
|
||
countryName={countryName}
|
||
leaderboardRank={leaderboardRank}
|
||
extraActions={galleryUrl ? (
|
||
<a
|
||
href={galleryUrl}
|
||
className="inline-flex items-center gap-2 rounded-xl border border-white/15 px-4 py-2.5 text-sm font-medium text-slate-300 transition-all hover:bg-white/5 hover:text-white"
|
||
>
|
||
<i className="fa-solid fa-images fa-fw" />
|
||
View Gallery
|
||
</a>
|
||
) : null}
|
||
/>
|
||
|
||
<div className="mt-6">
|
||
<ProfileTabs
|
||
activeTab={activeTab}
|
||
onTabChange={handleTabChange}
|
||
/>
|
||
</div>
|
||
|
||
<div className={`${contentShellClassName} pt-6`}>
|
||
{activeTab === 'artworks' && (
|
||
<TabArtworks
|
||
artworks={{ data: artworkList, next_cursor: artworkNextCursor }}
|
||
featuredArtworks={featuredArtworks}
|
||
username={user.username || user.name}
|
||
galleryUrl={galleryUrl}
|
||
isActive
|
||
/>
|
||
)}
|
||
{activeTab === 'posts' && (
|
||
<TabPosts
|
||
username={user.username || user.name}
|
||
isOwner={isOwner}
|
||
authUser={auth?.user ?? null}
|
||
user={user}
|
||
profile={profile}
|
||
stats={stats}
|
||
followerCount={followerCount}
|
||
recentFollowers={recentFollowers}
|
||
socialLinks={socialLinksObj}
|
||
countryName={countryName}
|
||
profileUrl={profileUrl}
|
||
onTabChange={handleTabChange}
|
||
/>
|
||
)}
|
||
{activeTab === 'stories' && (
|
||
<TabStories
|
||
stories={creatorStories}
|
||
username={user.username || user.name}
|
||
/>
|
||
)}
|
||
{activeTab === 'achievements' && (
|
||
<TabAchievements achievements={achievements} />
|
||
)}
|
||
{activeTab === 'collections' && (
|
||
<TabCollections collections={[]} />
|
||
)}
|
||
{activeTab === 'about' && (
|
||
<TabAbout
|
||
user={user}
|
||
profile={profile}
|
||
stats={stats}
|
||
achievements={achievements}
|
||
artworks={artworkList}
|
||
creatorStories={creatorStories}
|
||
profileComments={profileComments}
|
||
socialLinks={socialLinksObj}
|
||
countryName={countryName}
|
||
followerCount={followerCount}
|
||
recentFollowers={recentFollowers}
|
||
leaderboardRank={leaderboardRank}
|
||
/>
|
||
)}
|
||
{activeTab === 'stats' && (
|
||
<TabStats
|
||
stats={stats}
|
||
followerCount={followerCount}
|
||
/>
|
||
)}
|
||
{activeTab === 'favourites' && (
|
||
<TabFavourites
|
||
favourites={{ data: favouriteList, next_cursor: favouriteNextCursor }}
|
||
isOwner={isOwner}
|
||
username={user.username || user.name}
|
||
/>
|
||
)}
|
||
{activeTab === 'activity' && (
|
||
<TabActivity
|
||
profileComments={profileComments}
|
||
user={user}
|
||
isOwner={isOwner}
|
||
isLoggedIn={isLoggedIn}
|
||
/>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|