Skip to content

Commit

Permalink
feat[HOMEPAGE]: Rough RIP from zephyr old repo, many fixes needed
Browse files Browse the repository at this point in the history
Just barely working and fixed colors
  • Loading branch information
parazeeknova committed Oct 8, 2024
1 parent 90aa900 commit fd2fa18
Show file tree
Hide file tree
Showing 33 changed files with 2,911 additions and 3 deletions.
6 changes: 6 additions & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,21 @@
"@hookform/resolvers": "^3.9.0",
"@node-rs/argon2": "^2.0.0",
"@prisma/client": "^5.20.0",
"@radix-ui/react-avatar": "^1.1.1",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-scroll-area": "^1.2.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.1",
"@radix-ui/react-toast": "^1.2.2",
"@radix-ui/react-tooltip": "^1.1.3",
"@tanstack/react-query": "^5.59.0",
"@tanstack/react-query-devtools": "^5.59.0",
"@tiptap/extension-placeholder": "^2.8.0",
"@tiptap/react": "^2.8.0",
"@tiptap/starter-kit": "^2.8.0",
"@tsparticles/engine": "^3.5.0",
"@tsparticles/react": "^3.0.0",
"@tsparticles/slim": "^3.5.0",
Expand All @@ -42,6 +47,7 @@
"react": "19.0.0-rc-459fd418-20241001",
"react-dom": "19.0.0-rc.0",
"react-hook-form": "^7.53.0",
"react-intersection-observer": "^9.13.1",
"tailwind-merge": "^2.5.3",
"tailwindcss-animate": "^1.0.7"
},
Expand Down
8 changes: 5 additions & 3 deletions apps/web/src/app/(main)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import ZephyrHomePage from "@/pages/Home/Homepage";

export default function Home() {
return (
<>
<h1 className="mt-5 flex justify-center text-center font-semibold">
Welcome to Zephyr
</h1>
<main>
<ZephyrHomePage />
</main>
</>
);
}
2 changes: 2 additions & 0 deletions apps/web/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ body:hover::-webkit-scrollbar-thumb {
@layer base {
:root {
--background: 0 0% 97.65%;
--background-alt: 220 14% 96%;
--foreground: 0 0% 12.55%;
--muted: 0 0% 93.73%;
--muted-foreground: 0 0% 39.22%;
Expand All @@ -79,6 +80,7 @@ body:hover::-webkit-scrollbar-thumb {

.dark {
--background: 0 0% 6.67%;
--background-alt: 240 6% 10%;
--foreground: 0 0% 93.33%;
--muted: 0 0% 13.33%;
--muted-foreground: 0 0% 70.59%;
Expand Down
256 changes: 256 additions & 0 deletions apps/web/src/components/Home/FeedView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
"use client";

import { motion } from "framer-motion";
import { ChevronLeft, ChevronRight } from "lucide-react";
import React, { useEffect, useRef, useState } from "react";

import { Card, CardContent } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import type { PostData } from "@zephyr/db/prisma/client";
import PostCard from "./feedview/postCard";
import StoryCard from "./feedview/storyCard";

import PostEditor from "@zephyr-ui/Posts/editor/PostEditor";

const stories = [
{
title: "Thinking Components",
creator: "Lena Logic",
image: "/blogi.png",
avatar: "/user-boy-default.png",
width: 200,
height: 200
},
{
title: "Functional Fury",
creator: "Beth Binary",
image: "/blogii.png",
avatar: "/user-girlaltstyled-default.png",
width: 200,
height: 200
},
{
title: "React Rendezvous",
creator: "Ethan Byte",
image: "/blogiii.png",
avatar: "/user-girlstyled-default.png",
width: 200,
height: 200
},
{
title: "Stateful Symphony",
creator: "Beth Binary",
image: "/blogiv.png",
avatar: "/user-boystyled-default.png",
width: 200,
height: 200
},
{
title: "Async Awakenings",
creator: "Nina Netcode",
image: "/blogi.png",
avatar: "/user-boyalt-default.png",
width: 200,
height: 200
},
{
title: "The Art of Reusability",
creator: "Lena Logic",
image: "/blogii.png",
avatar: "/user-girlstyled-default.png",
width: 200,
height: 200
}
];

interface FeedViewProps {
posts: PostData[];
}

export const FeedView: React.FC<FeedViewProps> = ({ posts }) => {
const scrollRef = useRef<HTMLDivElement>(null);
const [showLeftButton, setShowLeftButton] = useState(false);
const [showRightButton, setShowRightButton] = useState(true);
const [isHovering, setIsHovering] = useState(false);

const checkScrollButtons = () => {
if (scrollRef.current) {
const { scrollLeft, scrollWidth, clientWidth } = scrollRef.current;
setShowLeftButton(scrollLeft > 0);
setShowRightButton(scrollLeft < scrollWidth - clientWidth - 1);
}
};

const hotPosts = React.useMemo(() => {
return [...posts].sort((a, b) => b.aura - a.aura).slice(0, 2);
}, [posts]);

useEffect(() => {
checkScrollButtons();
window.addEventListener("resize", checkScrollButtons);
return () => window.removeEventListener("resize", checkScrollButtons);
}, []);

const scroll = (direction: "left" | "right") => {
if (scrollRef.current) {
const scrollAmount = 200;
scrollRef.current.scrollBy({
left: direction === "left" ? -scrollAmount : scrollAmount,
behavior: "smooth"
});
setTimeout(checkScrollButtons, 300);
}
};

const allPosts = [...posts].sort(
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
);
const scribbles = allPosts.filter(
(post) => !post.images || post.images.length === 0
);
const snapshots = allPosts.filter(
(post) => post.images && post.images.length > 0
);

return (
<main className="flex-1 overflow-y-auto bg-background p-6 pb-24">
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
>
<Card className="mb-8 bg-card shadow-lg">
<CardContent className="p-4">
<h2 className="mb-2 text-left font-semibold text-2xl text-foreground uppercase">
Stories 🌱
</h2>
<p className="mb-4 text-muted-foreground">
Check out the latest stories from your network.
</p>

<div
className="relative"
onMouseEnter={() => setIsHovering(true)}
onMouseLeave={() => setIsHovering(false)}
>
<div
ref={scrollRef}
className={`flex space-x-4 overflow-x-hidden ${isHovering ? "scrollbar-show" : "scrollbar-hide"}`}
onScroll={checkScrollButtons}
style={{ paddingBottom: "10px" }}
>
{stories.map((story, index) => (
<StoryCard key={index} story={story} />
))}
</div>
{showLeftButton && (
<button
type="button"
onClick={() => scroll("left")}
className="-translate-y-1/2 absolute top-1/2 left-0 transform rounded-full bg-background/50 p-2 shadow-md transition-all duration-200 hover:bg-background/75 focus:outline-none dark:bg-foreground/10 dark:hover:bg-foreground/20"
>
<ChevronLeft className="h-6 w-6 text-foreground" />
</button>
)}
{showRightButton && (
<button
type="button"
onClick={() => scroll("right")}
className="-translate-y-1/2 absolute top-1/2 right-0 transform rounded-full bg-background/50 p-2 shadow-md transition-all duration-200 hover:bg-background/75 focus:outline-none dark:bg-foreground/10 dark:hover:bg-foreground/20"
>
<ChevronRight className="h-6 w-6 text-foreground" />
</button>
)}
</div>
</CardContent>
</Card>
</motion.div>

<div className="mt-4 mb-6">
<PostEditor />
</div>

<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="mb-8"
>
<Card className="bg-card shadow-lg">
<CardContent className="p-4">
<h2 className="mb-2 text-left font-semibold text-2xl text-foreground uppercase">
Rising Fleets 🚀
</h2>
<p className="mb-4 text-muted-foreground">
Top 2 posts with the highest aura in your fleet.
</p>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
{hotPosts.map((post, index) => (
<PostCard key={`hot-post-${index}`} post={post} />
))}
</div>
</CardContent>
</Card>
</motion.div>

<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5, delay: 0.2 }}
>
<Card className="mb-8 bg-card shadow-lg">
<CardContent className="p-4">
<h2 className="mb-2 text-left font-semibold text-2xl text-foreground uppercase">
Fleets 🚢
</h2>
<p className="mb-4 text-muted-foreground">
Check out the latest fleets from around the world.
</p>

<Tabs defaultValue="all" className="w-full">
<div className="mb-6 flex justify-center">
<TabsList className="grid w-full max-w-md grid-cols-3">
<TabsTrigger value="all">All</TabsTrigger>
<TabsTrigger value="scribbles">Scribbles</TabsTrigger>
<TabsTrigger value="snapshots">Snapshots</TabsTrigger>
</TabsList>
</div>

<TabsContent value="all">
<div className="flex justify-center">
<div className="w-full max-w-6xl space-y-8">
{allPosts.map((post, index) => (
<PostCard key={`all-${index}`} post={post} />
))}
</div>
</div>
</TabsContent>
<TabsContent value="scribbles">
<div className="flex justify-center">
<div className="w-full max-w-5xl">
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
{scribbles.map((post, index) => (
<PostCard key={`scribble-${index}`} post={post} />
))}
</div>
</div>
</div>
</TabsContent>
<TabsContent value="snapshots">
<div className="flex justify-center">
<div className="w-full max-w-6xl space-y-8">
{snapshots.map((post, index) => (
<PostCard key={`snapshot-${index}`} post={post} />
))}
</div>
</div>
</TabsContent>
</Tabs>
</CardContent>
</Card>
</motion.div>
</main>
);
};

export default FeedView;
66 changes: 66 additions & 0 deletions apps/web/src/components/Home/ForYouFeed.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"use client";

import { useInfiniteQuery } from "@tanstack/react-query";
import { Loader2 } from "lucide-react";

import kyInstance from "@/lib/ky";
import InfiniteScrollContainer from "@zephyr-ui/Layouts/InfiniteScrollContainer";
import PostsLoadingSkeleton from "@zephyr-ui/Posts/PostsLoadingSkeleton";
import type { PostsPage } from "@zephyr/db/prisma/client";

import FeedView from "./FeedView";

export default function ForYouFeed() {
const {
data,
fetchNextPage,
hasNextPage,
isFetching,
isFetchingNextPage,
status
} = useInfiniteQuery({
queryKey: ["post-feed", "for-you"],
queryFn: ({ pageParam }) =>
kyInstance
.get(
"/api/posts/for-you",
pageParam ? { searchParams: { cursor: pageParam } } : {}
)
.json<PostsPage>(),
initialPageParam: null as string | null,
getNextPageParam: (lastPage) => lastPage.nextCursor
});

const posts = data?.pages.flatMap((page) => page.posts) || [];

if (status === "pending") {
return <PostsLoadingSkeleton />;
}

if (status === "success" && !posts.length && !hasNextPage) {
return (
<p className="text-center text-muted-foreground">
No Fleets to show here.
</p>
);
}

if (status === "error") {
return (
<p className="text-center text-destructive">
An error occurred while loading posts.
</p>
);
}

return (
<InfiniteScrollContainer
onBottomReached={() => hasNextPage && !isFetching && fetchNextPage()}
>
<FeedView posts={posts} />
{isFetchingNextPage && (
<Loader2 className="mx-auto my-3 animate-spin bg-background" />
)}
</InfiniteScrollContainer>
);
}
Loading

0 comments on commit fd2fa18

Please sign in to comment.