From a4fd63695a6a3e2de8f26dfaa1ad6fccf14a89cc Mon Sep 17 00:00:00 2001 From: gosangam Date: Mon, 15 Jun 2026 09:53:01 +0530 Subject: [PATCH] fix: ui fixes --- components/feed/home-feed.tsx | 28 +++++--- components/feed/new-users-strip.tsx | 102 +++++++++++++++++++++------- 2 files changed, 95 insertions(+), 35 deletions(-) diff --git a/components/feed/home-feed.tsx b/components/feed/home-feed.tsx index d278a432..a033fbb3 100644 --- a/components/feed/home-feed.tsx +++ b/components/feed/home-feed.tsx @@ -9,8 +9,9 @@ * • Infinite scroll: 10 posts per page, fetched as a bottom sentinel nears * the viewport. The server orders not-yet-liked posts from people you * follow first, then everyone else — seamless, no visible boundary. - * • When the feed is exhausted, a "new on the platform" strip (≤5 accounts - * that joined in the last 24h) caps it off — hidden if there are none. + * • A "new on the platform" strip (≤5 accounts that joined in the last 24h) + * sits at the TOP of the scroll flow, so it scrolls out of view as you go + * down — hidden entirely if there are none. * * The card's Follow / Follow back affordance is enabled here (enableFollow); * the server hydrates each post's viewer↔author relationship. @@ -49,11 +50,6 @@ export function HomeFeed() { }); if (rows.length < PAGE) { setHasMore(false); - // Feed exhausted → load the discovery strip once. - feed - .newUsers() - .then((u) => setNewUsers(u ?? [])) - .catch(() => setNewUsers([])); } } catch { setHasMore(false); @@ -67,6 +63,15 @@ export function HomeFeed() { void loadPage(); }, [loadPage]); + // Discovery strip (new accounts) loads upfront — it sits at the top of the + // scroll flow, not at the end. + useEffect(() => { + feed + .newUsers() + .then((u) => setNewUsers(u ?? [])) + .catch(() => setNewUsers([])); + }, []); + // The window virtualiser needs `scrollMargin` = how far down the document the // list begins, to map window scroll → which items are visible. `.offsetTop` // is relative to the offsetParent (wrong inside the page layout), so measure @@ -135,6 +140,11 @@ export function HomeFeed() { return (
+ {/* Top of the scroll flow — scrolls out of view as the feed advances. */} +
+ +
+
- ) : ( - - )} + ) : null}
); } diff --git a/components/feed/new-users-strip.tsx b/components/feed/new-users-strip.tsx index c0792113..0fdcabf3 100644 --- a/components/feed/new-users-strip.tsx +++ b/components/feed/new-users-strip.tsx @@ -1,21 +1,62 @@ "use client"; /** - * NewUsersStrip — the "new on the platform" discovery row shown at the very - * end of the home feed. Up to 5 accounts that joined in the last 24h; each - * avatar links to the user's profile. Renders nothing when the list is empty - * (the feed simply ends). + * NewUsersStrip — the "new on the platform" discovery row at the top of the + * home feed. Up to 5 accounts that joined in the last 24h, each rendered as a + * card tile. The row scrolls horizontally (native touch/trackpad swipe + a + * mouse drag-to-scroll affordance) and bleeds to the section's edges with a + * gutter so tiles peek in from the sides. Renders nothing when empty. */ +import { useRef } from "react"; import Link from "next/link"; import { Sparkles } from "lucide-react"; import type { UserSummary } from "@/lib/social"; import { UserAvatar } from "@/components/ui/user-avatar"; export function NewUsersStrip({ users }: { users: UserSummary[] }) { + const scrollRef = useRef(null); + // Mouse drag-to-scroll. Touch/trackpad use native overflow scrolling, so we + // only engage for mouse; `moved` suppresses the click that would otherwise + // fire after a drag (and navigate to a profile). + const drag = useRef({ down: false, startX: 0, startScroll: 0, moved: false }); + if (users.length === 0) return null; + + function onPointerDown(e: React.PointerEvent) { + if (e.pointerType !== "mouse") return; + const el = scrollRef.current; + if (!el) return; + drag.current = { + down: true, + startX: e.clientX, + startScroll: el.scrollLeft, + moved: false, + }; + el.setPointerCapture(e.pointerId); + } + function onPointerMove(e: React.PointerEvent) { + if (!drag.current.down) return; + const el = scrollRef.current; + if (!el) return; + const dx = e.clientX - drag.current.startX; + if (Math.abs(dx) > 4) drag.current.moved = true; + el.scrollLeft = drag.current.startScroll - dx; + } + function onPointerUp(e: React.PointerEvent) { + if (!drag.current.down) return; + drag.current.down = false; + scrollRef.current?.releasePointerCapture(e.pointerId); + } + function onClickCapture(e: React.MouseEvent) { + if (drag.current.moved) { + e.preventDefault(); + e.stopPropagation(); + } + } + return ( -
+
-
    + + {/* Bleed to the section edges (-mx-5) but keep a px-5 gutter so the + first/last tiles have breathing room as they scroll past. */} +
    {users.map((u) => ( -
  • - - - - {u.name} - - - @{u.handle} - - -
  • + + + + {u.name} + + + @{u.handle} + + ))} -
+
); }