fix: user post feed view

This commit is contained in:
gosangam
2026-06-21 18:35:48 +05:30
parent 25db9d0eee
commit 5679f7a639
5 changed files with 195 additions and 95 deletions
+23 -38
View File
@@ -4,53 +4,34 @@
* /u/me/posts — the owner's Instagram-style scroll feed.
*
* Reached by tapping a thumbnail from the /u/me Posts grid. The clicked
* post's id arrives as a `#post-<id>` hash, and `scroll-mt-*` on each
* card spaces it below the sticky header so the anchor lands cleanly.
* post's id arrives as a `#post-<id>` hash; usePostScrollFeed fetches that
* post directly so it lands at the top instantly (even for old posts), then
* keyset-pages older posts beneath it as you scroll.
*
* Owner controls (Delete + Update background) are mounted per-card.
*/
import { useEffect, useRef, useState } from "react";
import { useState } from "react";
import { Loader2 } from "lucide-react";
import { useAuth } from "@/components/auth/auth-provider";
import { PostScrollCard } from "@/components/social/post/post-scroll-card";
import { usePostScrollFeed } from "@/components/social/post/use-post-scroll-feed";
import { ConfirmModal } from "@/components/ui/confirm-modal";
import { posts, SocialError, type Post } from "@/lib/social";
export default function MyPostsFeedPage() {
const { user } = useAuth();
const [feed, setFeed] = useState<Post[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
// Defer the feed (which calls the authed /posts/mine) until we have a user.
if (!user) return null;
return <OwnerScrollFeed />;
}
function OwnerScrollFeed() {
const { feed, setFeed, loading, hasMore, error, setError, sentinelRef } =
usePostScrollFeed((before, limit) =>
posts.mine({ limit, before: before ?? undefined }),
);
const [pendingDelete, setPendingDelete] = useState<string | null>(null);
const scrolledToHashRef = useRef(false);
useEffect(() => {
if (!user) return;
posts
.mine()
.then((rows) => setFeed(rows ?? []))
.catch((err) =>
setError(
err instanceof SocialError ? err.message : "Could not load posts.",
),
)
.finally(() => setLoading(false));
}, [user]);
// Hash anchor: when the feed mounts, jump to the post identified in the
// URL hash. Defer one tick so the cards have laid out and `scroll-mt-*`
// can position the anchor below the sticky header.
useEffect(() => {
if (loading || feed.length === 0 || scrolledToHashRef.current) return;
if (typeof window === "undefined") return;
const hash = window.location.hash;
if (!hash) return;
scrolledToHashRef.current = true;
requestAnimationFrame(() => {
const el = document.getElementById(hash.slice(1));
if (el) el.scrollIntoView({ block: "start", behavior: "auto" });
});
}, [loading, feed.length]);
async function performDelete() {
const id = pendingDelete;
@@ -79,13 +60,11 @@ export default function MyPostsFeedPage() {
if (hidden) setFeed((rows) => rows.filter((p) => p.id !== id));
}
if (!user) return null;
return (
<div className="max-w-xl w-full mx-auto space-y-6">
{error ? (
<p className="text-sm text-destructive">{error}</p>
) : loading ? (
) : loading && feed.length === 0 ? (
<p className="text-sm text-muted-foreground">Loading…</p>
) : feed.length === 0 ? (
<p className="text-sm text-muted-foreground">No posts yet.</p>
@@ -101,6 +80,12 @@ export default function MyPostsFeedPage() {
))
)}
{hasMore && feed.length > 0 ? (
<div ref={sentinelRef} className="flex justify-center py-6">
<Loader2 className="size-5 animate-spin text-muted-foreground" />
</div>
) : null}
<ConfirmModal
open={!!pendingDelete}
onClose={() => setPendingDelete(null)}