"use client"; import type { ReactNode, Ref } from "react"; import { canDelete, type CardPost } from "@/lib/feed-model"; import type { DiscoverExpert, DiscoverMinnect } from "@/lib/discover"; import { Composer, type ComposerProps } from "./Composer"; import { GrowButton, IconButton } from "./Buttons"; import { PostCard } from "./PostCard"; import { useCompactLayout } from "./layout-context"; import { MobileFeed, type CurrentSlide } from "./MobileFeed"; export type FeedStatus = "loading" | "ready" | "error"; export interface FeedViewProps { posts: CardPost[]; status: FeedStatus; loadingMore: boolean; loadMoreFailed: boolean; newCount: number; onRetry: () => void; onShowNew: () => void; /** Reloads the first page; the header shows the button when it is given. */ onRefresh?: () => void; refreshing?: boolean; /** The last refresh failed; the posts stay and a small message shows in the header. */ refreshFailed?: boolean; onToggleLike?: (post: CardPost) => void; likeBusy?: ReadonlySet; likeErrors?: ReadonlySet; onSignedOut?: () => void; /** The signed-in person's feed user id; delete is offered only on their own posts. */ feedUserId?: string | null; onDelete?: (post: CardPost) => void; deleteBusy?: ReadonlySet; deleteErrors?: ReadonlySet; composer?: ComposerProps; sentinelRef?: Ref; /** The scrolling feed list; the page itself never scrolls. */ listRef?: Ref; now?: number; /** Another page can load; the phone feed ends with a loading slide. */ hasMore?: boolean; /** Phone layout only: experts for the featured slide after the second post. */ featuredExperts?: DiscoverExpert[]; /** Phone layout only: trending minnects for the Discover sheet. */ trendingMinnects?: DiscoverMinnect[]; /** Phone layout only: the Find experts panel shown in the Discover sheet. */ searchPanel?: ReactNode; /** Phone layout only: the slide on screen changed (for the dock). */ onCurrentChange?: (slide: CurrentSlide) => void; /** Phone layout only: the dock's comments button, for the post on screen. */ onOpenComments?: (postId: string) => void; /** Phone layout only: the dock's new-post button. */ onOpenComposer?: () => void; /** Phone layout only: the dock's More button (the Discover sheet). */ onOpenMore?: () => void; } function Spinner() { return ; } /** Shimmer placeholder card shown while the next page loads. */ function SkeletonCard() { return (
); } function RefreshIcon({ spinning }: { spinning: boolean }) { return ( ); } export function RefreshButton({ onRefresh, busy }: { onRefresh: () => void; busy?: boolean }) { return ( } disabled={busy} aria-busy={busy || undefined} onClick={onRefresh} /> ); } function FeedPanel({ children, composer, onRefresh, busy, refreshFailed }: { children: ReactNode; composer?: ReactNode; onRefresh?: () => void; busy?: boolean; refreshFailed?: boolean }) { return (

Home feed

{refreshFailed ? Could not refresh. Try again. : null} {onRefresh ? : null}
{composer} {children}
); } export function FeedView(p: FeedViewProps) { // At 760px and below the feed is the full-screen snap reel instead of the panel. const compact = useCompactLayout(); if (compact) return ; if (p.status === "loading") { return (
); } if (p.status === "error") { return (

The feed did not load.

); } return ( : null} onRefresh={p.onRefresh} busy={p.refreshing} refreshFailed={p.refreshFailed}>
{p.newCount > 0 ? (
) : null} {p.posts.length === 0 ? (

No posts yet.

) : ( p.posts.map((post, i) => ) )} {p.loadMoreFailed ? (

The feed did not load.

) : null} {p.loadingMore ? : null} ); }