xrdwb17xu: Add the full-screen snap feed with media and quote slides on phones

This commit is contained in:
2026-10-20 08:21:28 -06:00
parent 25fd360a2b
commit bf5e0ed475
12 changed files with 815 additions and 61 deletions
+37 -3
View File
@@ -3,7 +3,7 @@
// Dev-only: the real signed-in shell with fixture data and no network calls.
// Likes, search and the composer work on local state so the panels can be
// checked by hand. Used by src/app/dev/feed/page.tsx.
import { useEffect, useMemo, useRef, useState } from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { AppShell } from "@/components/AppShell";
import { DiscoverRailsView } from "@/components/DiscoverRails";
import { ExpertSearchView } from "@/components/ExpertSearch";
@@ -31,6 +31,38 @@ export function FeedPreview() {
const [loadingMore, setLoadingMore] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const list = useRef<HTMLDivElement | null>(null);
// The list element swaps between the panel and the phone reel; paging re-attaches to it.
const [listEl, setListEl] = useState<HTMLDivElement | null>(null);
const listRef = useCallback((el: HTMLDivElement | null) => {
list.current = el;
setListEl(el);
}, []);
// Paging: two more fixture pages load when the sentinel scrolls into view,
// the same way Feed pages (the observer root is the list or the phone reel).
const sentinel = useRef<HTMLDivElement | null>(null);
const [page, setPage] = useState(0);
const [paging, setPaging] = useState(false);
const hasMore = page < 2;
useEffect(() => {
const el = sentinel.current;
if (!el || !hasMore || paging) return;
const io = new IntersectionObserver((entries) => {
if (!entries.some((e) => e.isIntersecting)) return;
setPaging(true);
window.setTimeout(() => {
const next = page + 1;
const items = FIXTURE_FEED_ITEMS.map((raw) => {
const it = raw as { post: { id: string } };
return { ...it, post: { ...it.post, id: `${it.post.id}-page${next}` } };
});
setPosts((cur) => [...cur, ...toCardPosts(items)]);
setPage(next);
setPaging(false);
}, 900);
}, { root: listEl, rootMargin: "400px" });
io.observe(el);
return () => io.disconnect();
}, [hasMore, paging, page, posts.length, listEl]);
// The comments calls answer from the fixtures; the preview never reaches the network.
useEffect(() => {
@@ -91,8 +123,10 @@ export function FeedPreview() {
<FeedView
posts={posts}
status="ready"
loadingMore={loadingMore}
listRef={list}
loadingMore={loadingMore || paging}
hasMore={hasMore}
sentinelRef={sentinel}
listRef={listRef}
loadMoreFailed={false}
newCount={newCount}
onRetry={() => {}}