Files
SkinbaseNova/resources/js/Pages/Profile/ProfileShow.jsx
Gregor Klevze dc51d65440 feat: forum rich-text editor, emoji picker, mentions, discover nav, feed, uploads, profile
Forum:
- TipTap WYSIWYG editor with full toolbar
- @emoji-mart/react emoji picker (consistent with tweets)
- @mention autocomplete with user search API
- Fix PHP 8.4 parse errors in Blade templates
- Fix thread data display (paginator items)
- Align forum page widths to max-w-5xl

Discover:
- Extract shared _nav.blade.php partial
- Add missing nav links to for-you page
- Add Following link for authenticated users

Feed/Posts:
- Post model, controllers, policies, migrations
- Feed page components (PostComposer, FeedCard, etc)
- Post reactions, comments, saves, reports, sharing
- Scheduled publishing support
- Link preview controller

Profile:
- Profile page components (ProfileHero, ProfileTabs)
- Profile API controller

Uploads:
- Upload wizard enhancements
- Scheduled publish picker
- Studio status bar and readiness checklist
2026-03-03 09:48:31 +01:00

178 lines
5.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useEffect, useCallback } from 'react'
import { usePage } from '@inertiajs/react'
import ProfileHero from '../../Components/Profile/ProfileHero'
import ProfileStatsRow from '../../Components/Profile/ProfileStatsRow'
import ProfileTabs from '../../Components/Profile/ProfileTabs'
import TabArtworks from '../../Components/Profile/tabs/TabArtworks'
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'
const VALID_TABS = ['artworks', 'posts', 'collections', 'about', 'stats', 'favourites', 'activity']
function getInitialTab() {
try {
const sp = new URLSearchParams(window.location.search)
const t = sp.get('tab')
return VALID_TABS.includes(t) ? t : 'artworks'
} catch {
return 'artworks'
}
}
/**
* 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,
countryName,
isOwner,
auth,
} = props
const [activeTab, setActiveTab] = useState(getInitialTab)
const handleTabChange = useCallback((tab) => {
if (!VALID_TABS.includes(tab)) return
setActiveTab(tab)
// Update URL query param without full navigation
try {
const url = new URL(window.location.href)
if (tab === 'artworks') {
url.searchParams.delete('tab')
} else {
url.searchParams.set('tab', tab)
}
window.history.pushState({}, '', url.toString())
} catch (_) {}
}, [])
// Handle browser back/forward
useEffect(() => {
const onPop = () => setActiveTab(getInitialTab())
window.addEventListener('popstate', onPop)
return () => window.removeEventListener('popstate', onPop)
}, [])
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
// 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 ?? {})
return (
<div className="min-h-screen pb-16">
{/* Hero section */}
<ProfileHero
user={user}
profile={profile}
isOwner={isOwner}
viewerIsFollowing={viewerIsFollowing}
followerCount={followerCount}
heroBgUrl={heroBgUrl}
countryName={countryName}
/>
{/* Stats pills row */}
<ProfileStatsRow
stats={stats}
followerCount={followerCount}
onTabChange={handleTabChange}
/>
{/* Sticky tabs */}
<ProfileTabs
activeTab={activeTab}
onTabChange={handleTabChange}
/>
{/* Tab content area */}
<div className="max-w-6xl mx-auto px-4">
{activeTab === 'artworks' && (
<TabArtworks
artworks={{ data: artworkList, next_cursor: artworkNextCursor }}
featuredArtworks={featuredArtworks}
username={user.username || user.name}
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}
onTabChange={handleTabChange}
/>
)}
{activeTab === 'collections' && (
<TabCollections collections={[]} />
)}
{activeTab === 'about' && (
<TabAbout
user={user}
profile={profile}
socialLinks={socialLinksObj}
countryName={countryName}
followerCount={followerCount}
/>
)}
{activeTab === 'stats' && (
<TabStats
stats={stats}
followerCount={followerCount}
/>
)}
{activeTab === 'favourites' && (
<TabFavourites
favourites={favourites}
isOwner={isOwner}
username={user.username || user.name}
/>
)}
{activeTab === 'activity' && (
<TabActivity
profileComments={profileComments}
user={user}
isOwner={isOwner}
isLoggedIn={isLoggedIn}
/>
)}
</div>
</div>
)
}