Save workspace changes
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
import React, { useState, useCallback } from 'react'
|
||||
import axios from 'axios'
|
||||
import PostCard from '../../Feed/PostCard'
|
||||
import PostComposer from '../../Feed/PostComposer'
|
||||
import PostCardSkeleton from '../../Feed/PostCardSkeleton'
|
||||
import FeedSidebar from '../../Feed/FeedSidebar'
|
||||
|
||||
function EmptyPostsState({ isOwner, username }) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center rounded-[28px] border border-dashed border-white/12 bg-white/[0.03] px-6 py-20 text-center">
|
||||
<div className="mb-4 flex h-16 w-16 items-center justify-center rounded-3xl border border-white/10 bg-white/[0.04] text-slate-500">
|
||||
<i className="fa-regular fa-newspaper text-2xl" />
|
||||
</div>
|
||||
<p className="mb-1 text-lg font-semibold text-white">No posts yet</p>
|
||||
{isOwner ? (
|
||||
<p className="max-w-sm text-sm leading-relaxed text-slate-400">
|
||||
Share works in progress, announce releases, or add a bit of personality beyond the gallery.
|
||||
</p>
|
||||
) : (
|
||||
<p className="max-w-sm text-sm leading-relaxed text-slate-400">@{username} has not published any profile posts yet.</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ErrorPostsState({ onRetry }) {
|
||||
return (
|
||||
<div className="rounded-[28px] border border-rose-400/20 bg-rose-400/10 px-6 py-12 text-center">
|
||||
<div className="mx-auto flex h-14 w-14 items-center justify-center rounded-2xl border border-rose-300/20 bg-rose-500/10 text-rose-200">
|
||||
<i className="fa-solid fa-triangle-exclamation text-lg" />
|
||||
</div>
|
||||
<h3 className="mt-4 text-lg font-semibold text-white">Posts could not be loaded</h3>
|
||||
<p className="mx-auto mt-2 max-w-md text-sm leading-relaxed text-rose-100/80">
|
||||
The profile shell loaded, but the posts feed request failed. Retry without leaving the page.
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onRetry}
|
||||
className="mt-5 inline-flex items-center gap-2 rounded-2xl border border-rose-300/20 bg-white/10 px-4 py-2.5 text-sm font-medium text-white transition-colors hover:bg-white/15"
|
||||
>
|
||||
<i className="fa-solid fa-rotate-right" />
|
||||
Retry loading posts
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* TabPosts
|
||||
* Profile Posts tab — shows the user's post feed with optional composer (for owner).
|
||||
*
|
||||
* Props:
|
||||
* username string
|
||||
* isOwner boolean
|
||||
* authUser object|null { id, username, name, avatar }
|
||||
* user object full user from ProfileController
|
||||
* profile object
|
||||
* stats object|null
|
||||
* followerCount number
|
||||
* recentFollowers array
|
||||
* socialLinks object
|
||||
* countryName string|null
|
||||
* onTabChange function(tab)
|
||||
*/
|
||||
export default function TabPosts({
|
||||
username,
|
||||
isOwner,
|
||||
authUser,
|
||||
user,
|
||||
profile,
|
||||
stats,
|
||||
followerCount,
|
||||
recentFollowers,
|
||||
suggestedUsers,
|
||||
socialLinks,
|
||||
countryName,
|
||||
onTabChange,
|
||||
}) {
|
||||
const [posts, setPosts] = useState([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [page, setPage] = useState(1)
|
||||
const [hasMore, setHasMore] = useState(false)
|
||||
const [loaded, setLoaded] = useState(false)
|
||||
const [error, setError] = useState(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
fetchFeed(1)
|
||||
}, [username])
|
||||
|
||||
const fetchFeed = async (p = 1) => {
|
||||
setLoading(true)
|
||||
setError(false)
|
||||
try {
|
||||
const { data } = await axios.get(`/api/posts/profile/${username}`, { params: { page: p } })
|
||||
setPosts((prev) => p === 1 ? data.data : [...prev, ...data.data])
|
||||
setHasMore(data.meta.current_page < data.meta.last_page)
|
||||
setPage(p)
|
||||
} catch {
|
||||
setError(true)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
setLoaded(true)
|
||||
}
|
||||
}
|
||||
|
||||
const handlePosted = useCallback((newPost) => {
|
||||
setPosts((prev) => [newPost, ...prev])
|
||||
}, [])
|
||||
|
||||
const handleDeleted = useCallback((postId) => {
|
||||
setPosts((prev) => prev.filter((p) => p.id !== postId))
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="py-6">
|
||||
<div className="grid items-start gap-6 xl:grid-cols-[minmax(0,1fr)_20rem]">
|
||||
<div className="min-w-0 space-y-4">
|
||||
{isOwner && authUser && (
|
||||
<div className="sticky top-24 z-20">
|
||||
<PostComposer user={authUser} onPosted={handlePosted} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!loaded && loading && (
|
||||
<div className="space-y-4">
|
||||
{[1, 2, 3].map((i) => <PostCardSkeleton key={i} />)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{loaded && error && posts.length === 0 && (
|
||||
<ErrorPostsState onRetry={() => fetchFeed(1)} />
|
||||
)}
|
||||
|
||||
{loaded && !loading && !error && posts.length === 0 && (
|
||||
<EmptyPostsState isOwner={isOwner} username={username} />
|
||||
)}
|
||||
|
||||
{posts.length > 0 && (
|
||||
<div className="space-y-4">
|
||||
{posts.map((post) => (
|
||||
<PostCard
|
||||
key={post.id}
|
||||
post={post}
|
||||
isLoggedIn={!!authUser}
|
||||
viewerUsername={authUser?.username ?? null}
|
||||
onDelete={handleDeleted}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{loaded && hasMore && (
|
||||
<div className="flex justify-center py-4">
|
||||
<button
|
||||
onClick={() => fetchFeed(page + 1)}
|
||||
disabled={loading}
|
||||
className="rounded-2xl border border-white/10 bg-white/[0.04] px-6 py-2.5 text-sm font-medium text-slate-200 transition-colors hover:bg-white/[0.08] disabled:opacity-50"
|
||||
>
|
||||
{loading ? (
|
||||
<><i className="fa-solid fa-spinner fa-spin mr-2" />Loading…</>
|
||||
) : 'Load more posts'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<aside className="hidden xl:block xl:sticky xl:top-24">
|
||||
<FeedSidebar
|
||||
user={user}
|
||||
profile={profile}
|
||||
stats={stats}
|
||||
followerCount={followerCount}
|
||||
recentFollowers={recentFollowers}
|
||||
suggestedUsers={suggestedUsers}
|
||||
socialLinks={socialLinks}
|
||||
countryName={countryName}
|
||||
isLoggedIn={!!authUser}
|
||||
onTabChange={onTabChange}
|
||||
/>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user