Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

95 feature add infinite scroll to posts grid #127

Merged
merged 8 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 76 additions & 34 deletions src/app/(site)/app/components/feed/index.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,85 @@
import { getSuapabaseServerComponent } from "@/supabase/models/index.models";
"use client";

import { Heading } from "@/components/ui/typography/heading";
import { PostsGrid } from "@/components/feature/posts-grid";
import { Suspense } from "react";
import { Skeleton } from "@/components/ui/skeleton";
import { useEffect, useState } from "react";
import { useSupabase } from "@/hooks/use-supabase";
import { Database } from "@/supabase/types";
import { PostsGrid } from "@/components/feature/posts-grid/posts-grid.component";
import { PostsGridSkeleton } from "@/components/feature/posts-grid/components/posts-grid-skeleton";

export function Feed() {
const { supabase } = useSupabase();

const [posts, setPosts] = useState<
Database["public"]["Tables"]["posts"]["Row"][]
>([]);

const [error, setError] = useState<boolean>(false);

const [isLoading, setIsLoading] = useState<boolean>(true);
const [isFetching, setIsFetching] = useState<boolean>(false);

const [lastPostIndex, setLastPostIndex] = useState<number>(1);

useEffect(() => {
const controller = new AbortController();

try {
setIsLoading(true);
setIsFetching(true);

supabase
.from("posts")
.select("*")
.order("id", { ascending: false })
.limit(32)
.range(lastPostIndex, lastPostIndex + 32)
.abortSignal(controller.signal)
.then(({ data: posts, error }) => {
if (error) return;

if (!posts) return;

setPosts((prev) => [...prev, ...posts]);
});
} catch (error: unknown) {
if ((error as Error).name === "AbortError") return;

setError(true);
} finally {
setIsLoading(false);
setIsFetching(false);
}

export async function Feed() {
const supabase = await getSuapabaseServerComponent();
return () => {
controller.abort();
};

const { data: posts, error } = await supabase
.from("posts")
.select("*")
.order("id", { ascending: false })
.limit(24);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [lastPostIndex]);

const doesPostsExist = posts && posts.length > 0 && !error;
function handleScroll() {
setLastPostIndex((prev) => prev + 32);
}

return (
<>
{doesPostsExist ? (
<main className="w-full h-full px-2">
<Suspense
fallback={
<ul className="break-inside-avoid gap-2 px-2 [column-count:3] md:[column-count:3]">
{Array(16)
.fill(" ")
.map((_, i) => (
<Skeleton key={i} className="w-full h-96 mb-2" />
))}
</ul>
}
>
<PostsGrid posts={posts} />
</Suspense>
</main>
) : (
<article className="flex items-center justify-center w-full max-h-96 py-16 text-center border-y border-neutral-300">
<Heading level={7}>Something wen wrong, reload the page</Heading>
</article>
<main className="w-full h-full flex flex-col gap-2">
{isLoading && <PostsGridSkeleton cuantity={32} />}
{!isLoading && !isFetching && (
<>
{!error ? (
<div>
<PostsGrid posts={posts} onFetchNewPosts={handleScroll} />
</div>
) : (
<article className="flex items-center justify-center w-full max-h-96 py-32 text-center border-y border-neutral-300">
<Heading level={10}>
Something wen wrong, please reload the page
</Heading>
</article>
)}
</>
)}
</>
</main>
);
}
2 changes: 1 addition & 1 deletion src/app/(site)/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default function AppLayout({ children }: { children: ReactNode }) {
<div className="md:hidden w-full flex fixed left-0 bottom-0 z-50">
<MobileNavMenu />
</div>
<div className="h-full w-full min-h-screen border-x border-neutral-300 dark:border-cm-lighter-gray">
<div className="h-full px-2 w-full min-h-screen border-x border-neutral-300 dark:border-cm-lighter-gray">
{children}
</div>
<SyncTheme />
Expand Down
18 changes: 2 additions & 16 deletions src/app/(site)/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,8 @@ export default function AppPage() {

function DesktopLayout() {
return (
<main className="w-full h-full flex flex-col justify-start py-2">
<Suspense
fallback={
<ul className="w-full flex flex-wrap gap-2">
{Array(8)
.fill("")
.map((_, i) => (
<li key={i}>
<Skeleton className="w-full bg-red-500 rounded-md h-[500px]" />
</li>
))}
</ul>
}
>
<Feed />
</Suspense>
<main className="w-full h-full flex flex-col justify-start">
<Feed />
</main>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export function PostOptions({
return router.push("/app"); // Navigate to '/app' if no history
}
}
router.refresh();
} catch (e) {
console.log(e);
}
Expand Down
75 changes: 37 additions & 38 deletions src/app/(site)/app/post/[postid]/components/post/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,48 +22,47 @@ export async function Post({
.single();

return (
<article className="w-full h-full flex lg:gap-4 gap-2 flex-col-reverse lg:flex-row-reverse items-start justify-end bg-neutral-200 dark:bg-neutral-900 overflow-hidden">
<header className="flex items-center justify-between w-96">
<section className="w-full flex flex-col items-start justify-center gap-4">
<PostOptions
post_id={post.id}
doesUserOwnPost={doesUserOwnPost}
image_url={post.asset_url}
/>
<h3 className="w-full text-neutral-800 dark:text-neutral-300 font-bold text-2xl">
{title}
</h3>
{postOwnerProfile && (
<Link
href={`/app/profile/${postOwnerProfile.username}`}
className="w-full"
>
<section className="flex flex-none gap-4 w-full items-center justify-start">
{postOwnerProfile.avatar_url ? (
<LazyImage
src={postOwnerProfile.avatar_url}
alt={post.title}
className="w-12 h-12 rounded-full object-cover object-center flex-0"
skeletonClassName="w-12 h-12 rounded-full"
width={48}
height={48}
/>
) : (
<div className="h-12 w-12 rounded-full bg-neutral-300" />
)}
<h3 className="text-neutral-800 dark:text-neutral-300 font-semibold text-xl">
{postOwnerProfile.name}
</h3>
</section>
</Link>
)}
</section>
<article className="w-full h-full flex flex-col-reverse gap-2 items-start justify-end bg-neutral-200 dark:bg-neutral-900">
<header className="flex flex-col items-start justify-center gap-2 w-max xl:min-w-64 min-w-full">
<h3 className="w-full text-neutral-800 dark:text-neutral-300 font-bold text-2xl">
{title}
</h3>
{postOwnerProfile && (
<Link
href={`/app/profile/${postOwnerProfile.username}`}
className="w-full"
>
<section className="flex gap-2 w-full justify-start items-center">
{postOwnerProfile.avatar_url ? (
<LazyImage
src={postOwnerProfile.avatar_url}
alt={post.title}
className="w-12 h-12 rounded-full object-cover object-center flex-0"
skeletonClassName="w-12 h-12 rounded-full"
width={48}
height={48}
/>
) : (
<div className="h-12 w-12 rounded-full bg-neutral-300" />
)}
<h3 className="text-neutral-800 dark:text-neutral-300 font-semibold text-xl">
{postOwnerProfile.name}
</h3>
</section>
</Link>
)}
<PostOptions
post_id={post.id}
doesUserOwnPost={doesUserOwnPost}
image_url={post.asset_url}
/>
</header>
<LazyImage
src={asset_url}
alt={title}
className="w-full h-full max-h-[100vh] lg:max-h-[80vh] object-cover object-center rounded-md"
skeletonClassName="w-full"
className="w-full h-full max-h-[80vh] object-cover object-center rounded-md"
skeletonClassName="w-full h-96 rounded-md"
containerClassname="w-full"
skeletonBgColor={post.asset_color || undefined}
/>
</article>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./recent-pots.component";
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"use client";

import { PostsGridSkeleton } from "@/components/feature/posts-grid/components/posts-grid-skeleton";
import { PostsGrid } from "@/components/feature/posts-grid/posts-grid.component";
import { Heading } from "@/components/ui/typography/heading";
import { useSupabase } from "@/hooks/use-supabase";
import { Database } from "@/supabase/types";
import { useEffect, useState } from "react";

export function RecentPosts({
excludedPostId,
}: {
excludedPostId: Database["public"]["Tables"]["posts"]["Row"]["id"];
}) {
const [posts, setPosts] = useState<
Database["public"]["Tables"]["posts"]["Row"][]
>([]);

const [isLoading, setIsLoading] = useState<boolean>(true);
const [isFetching, setIsFetching] = useState<boolean>(false);

const [lastPostIndex, setLastPostIndex] = useState<number>(1);
const [error, setError] = useState<boolean>(false);

const { supabase } = useSupabase();

useEffect(() => {
const controller = new AbortController();

try {
setIsLoading(true);
setIsFetching(true);

supabase
.from("posts")
.select("*")
.order("created_at", { ascending: false })
.limit(32)
.range(lastPostIndex, lastPostIndex + 32)
.abortSignal(controller.signal)
.then(({ data: posts, error }) => {
if (error) return;

if (!posts) return;

setPosts((prev) => [
...prev,
...posts.filter((post) => post.id !== excludedPostId),
]);
});
} catch (error: unknown) {
if ((error as Error).name === "AbortError") return;

setError(true);
} finally {
setIsLoading(false);
setIsFetching(false);
}

return () => {
controller.abort();
};

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [lastPostIndex]);

function handleScroll() {
if (isLoading || isFetching) return;

setLastPostIndex((prev) => prev + 32);
}

return (
<section>
{isLoading && <PostsGridSkeleton cuantity={32} />}
{!isLoading && !isFetching && (
<>
{!error ? (
<div>
<PostsGrid posts={posts} onFetchNewPosts={handleScroll} />
</div>
) : (
<article className="flex items-center justify-center w-full max-h-96 py-32 text-center border-y border-neutral-300">
<Heading level={10}>
Something wen wrong, please reload the page
</Heading>
</article>
)}
</>
)}
</section>
);
}
Loading
Loading