Scrapboard.org client
1"use client";
2
3import { Feed, feedAsMap } from "@/components/Feed";
4import { useFetchTimeline } from "@/lib/hooks/useTimeline";
5import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
6import { useEffect, useState } from "react";
7import { useFeedStore } from "@/lib/stores/feeds";
8import { useFeeds } from "@/lib/hooks/useFeeds";
9import { LoaderCircle } from "lucide-react";
10import { useFeedDefsStore } from "@/lib/stores/feedDefs";
11import { useAuth } from "@/lib/hooks/useAuth";
12import { InfiniteScrollWrapper } from "@/components/InfiniteScrollWrapper";
13
14export default function Home() {
15 const { fetchFeed } = useFetchTimeline();
16 const feedStore = useFeedStore();
17 const { isLoading } = useFeeds();
18 const { feeds, defaultFeed, setDefaultFeed } = useFeedDefsStore();
19 const { session, loading } = useAuth();
20 const [feed, setFeed] = useState<"timeline" | string>(
21 defaultFeed ?? "timeline"
22 );
23
24 const loadMore = async () => {
25 await fetchFeed(feed);
26 };
27
28 useEffect(() => {
29 console.log(`Loading feed: ${feed}`);
30 loadMore();
31 }, [feed]);
32
33 if (session == null) {
34 return (
35 <div className="items-center justify-items-center text-center">
36 <h1 className="text-lg mb-0.5 font-medium">
37 You're not logged in
38 </h1>
39 <p>Log in to see your feeds</p>
40 </div>
41 );
42 }
43
44 if (isLoading || loading)
45 return (
46 <div className="flex justify-center py-6 text-sm text-black/70 dark:text-white/70">
47 <LoaderCircle className="animate-spin" />
48 </div>
49 );
50
51 const triggerClass =
52 "shrink-0 cursor-pointer dark:hover:bg-white/5 hover:bg-black/5 transition-colors";
53
54 const currentFeedData =
55 feed === "timeline" ? feedStore.timeline : feedStore.customFeeds[feed];
56
57 const hasMore =
58 currentFeedData?.cursor !== null && currentFeedData?.cursor !== undefined;
59 const isLoadingMore = currentFeedData?.isLoading || false;
60
61 return (
62 <main className="px-5">
63 <Tabs defaultValue={defaultFeed} className="w-full">
64 <TabsList
65 className="overflow-x-auto w-full justify-start"
66 style={{ justifyItems: "unset" }}
67 >
68 <TabsTrigger
69 onClick={() => {
70 setFeed("timeline");
71 setDefaultFeed("timeline");
72 }}
73 value="timeline"
74 className={triggerClass}
75 >
76 Timeline
77 </TabsTrigger>
78 {Object.entries(feeds).map(([value, it]) => (
79 <TabsTrigger
80 onClick={() => {
81 setFeed(value);
82 setDefaultFeed(value);
83 }}
84 key={value}
85 value={value}
86 className={triggerClass}
87 >
88 {it?.displayName}
89 </TabsTrigger>
90 ))}
91 </TabsList>
92
93 <TabsContent value="timeline">
94 <InfiniteScrollWrapper
95 hasMore={hasMore}
96 onLoadMore={loadMore}
97 isLoadingMore={isLoadingMore}
98 >
99 <Feed
100 feed={feedAsMap(feedStore.timeline.posts.map((it) => it.post))}
101 isLoading={
102 feedStore.timeline.isLoading &&
103 feedStore.timeline.posts.length === 0
104 }
105 />
106 </InfiniteScrollWrapper>
107 </TabsContent>
108
109 {Object.entries(feeds)
110 .filter((it) => feedStore.customFeeds?.[it[0]] != null)
111 .map(([value]) => (
112 <TabsContent key={value} value={value}>
113 <InfiniteScrollWrapper
114 hasMore={hasMore}
115 onLoadMore={loadMore}
116 isLoadingMore={isLoadingMore}
117 >
118 <Feed
119 feed={feedAsMap(feedStore.customFeeds[value].posts)}
120 isLoading={
121 feedStore.customFeeds[value].isLoading &&
122 feedStore.customFeeds[value].posts.length === 0
123 }
124 />
125 </InfiniteScrollWrapper>
126 </TabsContent>
127 ))}
128 </Tabs>
129 </main>
130 );
131}