"use client"; import { AtUri } from "@atproto/api"; import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; import { PubIcon } from "components/ActionBar/Publications"; import { ButtonPrimary } from "components/Buttons"; import { CommentTiny } from "components/Icons/CommentTiny"; import { DiscoverSmall } from "components/Icons/DiscoverSmall"; import { QuoteTiny } from "components/Icons/QuoteTiny"; import { Separator } from "components/Layout"; import { SpeedyLink } from "components/SpeedyLink"; import { usePubTheme } from "components/ThemeManager/PublicationThemeProvider"; import { BaseThemeProvider } from "components/ThemeManager/ThemeProvider"; import { useSmoker } from "components/Toast"; import { PubLeafletDocument, PubLeafletPublication } from "lexicons/api"; import { blobRefToSrc } from "src/utils/blobRefToSrc"; import { Json } from "supabase/database.types"; import type { Cursor, Post } from "./getReaderFeed"; import useSWRInfinite from "swr/infinite"; import { getReaderFeed } from "./getReaderFeed"; import { useEffect, useRef } from "react"; import { useRouter } from "next/navigation"; import Link from "next/link"; export const ReaderContent = (props: { root_entity: string; posts: Post[]; nextCursor: Cursor | null; }) => { const getKey = ( pageIndex: number, previousPageData: { posts: Post[]; nextCursor: Cursor | null } | null, ) => { // Reached the end if (previousPageData && !previousPageData.nextCursor) return null; // First page, we don't have previousPageData if (pageIndex === 0) return ["reader-feed", null] as const; // Add the cursor to the key return ["reader-feed", previousPageData?.nextCursor] as const; }; const { data, error, size, setSize, isValidating } = useSWRInfinite( getKey, ([_, cursor]) => getReaderFeed(cursor), { fallbackData: [{ posts: props.posts, nextCursor: props.nextCursor }], revalidateFirstPage: false, }, ); const loadMoreRef = useRef(null); // Set up intersection observer to load more when trigger element is visible useEffect(() => { const observer = new IntersectionObserver( (entries) => { if (entries[0].isIntersecting && !isValidating) { const hasMore = data && data[data.length - 1]?.nextCursor; if (hasMore) { setSize(size + 1); } } }, { threshold: 0.1 }, ); if (loadMoreRef.current) { observer.observe(loadMoreRef.current); } return () => observer.disconnect(); }, [data, size, setSize, isValidating]); const allPosts = data ? data.flatMap((page) => page.posts) : []; if (allPosts.length === 0 && !isValidating) return ; return (
{allPosts.map((p) => ( ))} {/* Trigger element for loading more posts */} ); }; const Post = (props: Post) => { let pubRecord = props.publication.pubRecord as PubLeafletPublication.Record; let postRecord = props.documents.data as PubLeafletDocument.Record; let postUri = new AtUri(props.documents.uri); let theme = usePubTheme(pubRecord); let backgroundImage = pubRecord?.theme?.backgroundImage?.image?.ref ? blobRefToSrc( pubRecord?.theme?.backgroundImage?.image?.ref, new AtUri(props.publication.uri).host, ) : null; let backgroundImageRepeat = pubRecord?.theme?.backgroundImage?.repeat; let backgroundImageSize = pubRecord?.theme?.backgroundImage?.width || 500; let showPageBackground = pubRecord.theme?.showPageBackground; let quotes = props.documents.document_mentions_in_bsky?.[0]?.count || 0; let comments = pubRecord.preferences?.showComments === false ? 0 : props.documents.comments_on_documents?.[0]?.count || 0; return ( ); }; const PubInfo = (props: { href: string; pubRecord: PubLeafletPublication.Record; uri: string; }) => { return ( {props.pubRecord.name} ); }; const PostInfo = (props: { author: string; publishedAt: string | undefined; }) => { return (
{props.author} {props.publishedAt && ( <> {new Date(props.publishedAt).toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric", })}{" "} )}
); }; const PostInterations = (props: { quotesCount: number; commentsCount: number; postUrl: string; showComments: boolean | undefined; }) => { let smoker = useSmoker(); let interactionsAvailable = props.quotesCount > 0 || (props.showComments !== false && props.commentsCount > 0); return (
{props.quotesCount === 0 ? null : (
Post quotes {props.quotesCount}
)} {props.showComments === false || props.commentsCount === 0 ? null : (
Post comments {props.commentsCount}
)} {interactionsAvailable && }
); }; export const ReaderEmpty = () => { return (
Nothing to read yet…
Subscribe to publications and find their posts here! Discover Publications
); };