From c3afba6b28da753dc781b3fefc59acadaef0a520 Mon Sep 17 00:00:00 2001 From: Pouria Delfanazari Date: Wed, 20 Nov 2024 15:06:21 -0800 Subject: [PATCH] Add feeds tab to search --- src/app/dashboard/feeds/page.tsx | 2 +- .../contentDisplay/feedItem/FeedItem.tsx | 15 ++++--- .../contentDisplay/feedList/FeedList.tsx | 2 +- .../feedList/FeedListSkeleton.tsx | 42 ++++++++++++------- .../contentDisplay/searchList/SearchList.tsx | 16 +++++-- src/containers/search/FeedSearchContainer.tsx | 41 ++++++++++++++++++ src/lib/api/bsky/feed/index.ts | 4 +- 7 files changed, 94 insertions(+), 28 deletions(-) create mode 100644 src/containers/search/FeedSearchContainer.tsx diff --git a/src/app/dashboard/feeds/page.tsx b/src/app/dashboard/feeds/page.tsx index ed3acfdf..c6e4545c 100644 --- a/src/app/dashboard/feeds/page.tsx +++ b/src/app/dashboard/feeds/page.tsx @@ -43,7 +43,7 @@ export default function Page(props: Props) { - }> + }> diff --git a/src/components/contentDisplay/feedItem/FeedItem.tsx b/src/components/contentDisplay/feedItem/FeedItem.tsx index f5e1350b..fbbcd477 100644 --- a/src/components/contentDisplay/feedItem/FeedItem.tsx +++ b/src/components/contentDisplay/feedItem/FeedItem.tsx @@ -15,11 +15,12 @@ import { getAgentFromClient } from "@/lib/api/bsky/agent"; interface Props { feedItem: GeneratorView; - saved: boolean; + saved?: boolean; + rounded?: boolean; } export default function FeedItem(props: Props) { - const { feedItem, saved } = props; + const { feedItem, saved, rounded = true } = props; const { avatar, displayName, description, likeCount, creator } = feedItem; const [isSaved, setIsSaved] = useState(saved); const router = useRouter(); @@ -45,11 +46,13 @@ export default function FeedItem(props: Props) {
@@ -58,7 +61,9 @@ export default function FeedItem(props: Props) { alt={displayName} width={40} height={40} - className={`rounded-lg ${!avatar && "border-skin-base bg-skin-muted border"}`} + className={`rounded-lg ${ + !avatar && "border-skin-base bg-skin-muted border" + }`} />

diff --git a/src/components/contentDisplay/feedList/FeedList.tsx b/src/components/contentDisplay/feedList/FeedList.tsx index 2f46a35d..be2d7e26 100644 --- a/src/components/contentDisplay/feedList/FeedList.tsx +++ b/src/components/contentDisplay/feedList/FeedList.tsx @@ -18,7 +18,7 @@ export default async function FeedList(props: Props) { savedFeed.uri === feed.uri)} + saved={savedFeeds.some((savedFeed) => savedFeed.uri === feed.uri)} /> ))} {popularFeeds.length === 0 && ( diff --git a/src/components/contentDisplay/feedList/FeedListSkeleton.tsx b/src/components/contentDisplay/feedList/FeedListSkeleton.tsx index 77e82efb..1356671c 100644 --- a/src/components/contentDisplay/feedList/FeedListSkeleton.tsx +++ b/src/components/contentDisplay/feedList/FeedListSkeleton.tsx @@ -1,6 +1,16 @@ -function Skeleton() { +interface Props { + rounded?: boolean; +} + +function Skeleton(props: Props) { + const { rounded = false } = props; + return ( -
+
@@ -16,21 +26,23 @@ function Skeleton() { ); } -export default function FeedListSkeleton() { +export default function FeedListSkeleton(props: Props) { + const { rounded = false } = props; + return (
- - - - - - - - - - - - + + + + + + + + + + + +
); } diff --git a/src/components/contentDisplay/searchList/SearchList.tsx b/src/components/contentDisplay/searchList/SearchList.tsx index 90d59cec..ec1fc4c2 100644 --- a/src/components/contentDisplay/searchList/SearchList.tsx +++ b/src/components/contentDisplay/searchList/SearchList.tsx @@ -6,6 +6,7 @@ import UserSearchContainer from "@/containers/search/UserSearchContainer"; import { useSession } from "next-auth/react"; import Tabs from "@/components/navigational/tabs/Tabs"; import TabItem from "@/components/navigational/tabs/TabItem"; +import FeedSearchContainer from "@/containers/search/FeedSearchContainer"; interface Props { query: string; @@ -13,12 +14,12 @@ interface Props { export default function SearchList(props: Props) { const { query } = props; - const [currentTab, setCurrentTab] = useState<"top" | "latest" | "users">( - "top", - ); + const [currentTab, setCurrentTab] = useState< + "top" | "latest" | "users" | "feeds" + >("top"); const { data: session } = useSession(); - const handleTabChange = (tab: "top" | "latest" | "users") => { + const handleTabChange = (tab: "top" | "latest" | "users" | "feeds") => { setCurrentTab(tab); }; @@ -51,6 +52,12 @@ export default function SearchList(props: Props) { label="Users" isActive={currentTab === "users"} /> + handleTabChange("feeds")} + label="Feeds" + isActive={currentTab === "feeds"} + /> {currentTab === "latest" && ( @@ -60,6 +67,7 @@ export default function SearchList(props: Props) { )} {currentTab === "users" && } + {currentTab === "feeds" && } ); } diff --git a/src/containers/search/FeedSearchContainer.tsx b/src/containers/search/FeedSearchContainer.tsx new file mode 100644 index 00000000..b54d45aa --- /dev/null +++ b/src/containers/search/FeedSearchContainer.tsx @@ -0,0 +1,41 @@ +"use client"; + +import { getPopularFeeds } from "@/lib/api/bsky/feed"; +import { useQuery } from "@tanstack/react-query"; +import FeedAlert from "@/components/feedback/feedAlert/FeedAlert"; +import { getAgentFromClient } from "@/lib/api/bsky/agent"; +import FeedListSkeleton from "@/components/contentDisplay/feedList/FeedListSkeleton"; +import FeedItem from "@/components/contentDisplay/feedItem/FeedItem"; + +interface Props { + query: string; +} + +export default function FeedSearchContainer(props: Props) { + const { query } = props; + const { data: feeds, isFetching } = useQuery({ + queryKey: ["searchFeeds", query], + queryFn: async () => { + const agent = await getAgentFromClient(); + return getPopularFeeds(query, agent); + }, + }); + + const isEmpty = !isFetching && feeds?.length === 0; + + return ( +
+ {feeds && + feeds.map((feed) => ( + + ))} + + {isEmpty && ( +
+ +
+ )} + {isFetching && } +
+ ); +} diff --git a/src/lib/api/bsky/feed/index.ts b/src/lib/api/bsky/feed/index.ts index 33af1cb1..4d470340 100644 --- a/src/lib/api/bsky/feed/index.ts +++ b/src/lib/api/bsky/feed/index.ts @@ -2,8 +2,8 @@ import { type Agent, AppBskyActorDefs } from "@atproto/api"; import { getAgentFromServer } from "../agent"; import { SavedFeed } from "../../../../../types/feed"; -export const getPopularFeeds = async (search?: string) => { - const agent = await getAgentFromServer(); +export const getPopularFeeds = async (search?: string, agent?: Agent) => { + if (!agent) agent = await getAgentFromServer(); const popularFeeds = await agent.app.bsky.unspecced.getPopularFeedGenerators({ query: search, });