Skip to content

Commit 79a3840

Browse files
committed
Merge branch 'preview'
2 parents 67731e8 + c3afba6 commit 79a3840

File tree

7 files changed

+94
-28
lines changed

7 files changed

+94
-28
lines changed

src/app/dashboard/feeds/page.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export default function Page(props: Props) {
4343
<Search placeholder="Search for feeds" />
4444
</div>
4545
</div>
46-
<Suspense key={query} fallback={<FeedListSkeleton />}>
46+
<Suspense key={query} fallback={<FeedListSkeleton rounded={true} />}>
4747
<FeedList query={query} />
4848
</Suspense>
4949
</div>

src/components/contentDisplay/feedItem/FeedItem.tsx

+10-5
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ import { getAgentFromClient } from "@/lib/api/bsky/agent";
1515

1616
interface Props {
1717
feedItem: GeneratorView;
18-
saved: boolean;
18+
saved?: boolean;
19+
rounded?: boolean;
1920
}
2021

2122
export default function FeedItem(props: Props) {
22-
const { feedItem, saved } = props;
23+
const { feedItem, saved, rounded = true } = props;
2324
const { avatar, displayName, description, likeCount, creator } = feedItem;
2425
const [isSaved, setIsSaved] = useState(saved);
2526
const router = useRouter();
@@ -45,11 +46,13 @@ export default function FeedItem(props: Props) {
4546
<Link
4647
href={{
4748
pathname: `/dashboard/feeds/${encodeURIComponent(
48-
feedItem.uri.split(":")[3].split("/")[0],
49+
feedItem.uri.split(":")[3].split("/")[0]
4950
)}`,
5051
query: { uri: feedItem.uri },
5152
}}
52-
className="border-skin-base hover:bg-skin-secondary flex flex-col gap-2 border border-x-0 p-3 last:border-b md:border-x md:first:rounded-t-2xl md:last:rounded-b-2xl odd:[&:not(:last-child)]:border-b-0 even:[&:not(:last-child)]:border-b-0"
53+
className={`border-skin-base hover:bg-skin-secondary flex flex-col gap-2 border border-x-0 p-3 last:border-b md:border-x ${
54+
rounded && "md:first:rounded-t-2xl"
55+
} md:last:rounded-b-2xl odd:[&:not(:last-child)]:border-b-0 even:[&:not(:last-child)]:border-b-0`}
5356
>
5457
<div className="flex flex-wrap items-center justify-between gap-3">
5558
<div className="flex flex-wrap items-center gap-3">
@@ -58,7 +61,9 @@ export default function FeedItem(props: Props) {
5861
alt={displayName}
5962
width={40}
6063
height={40}
61-
className={`rounded-lg ${!avatar && "border-skin-base bg-skin-muted border"}`}
64+
className={`rounded-lg ${
65+
!avatar && "border-skin-base bg-skin-muted border"
66+
}`}
6267
/>
6368
<div className="flex flex-col">
6469
<h2 className="text-skin-base break-words font-semibold">

src/components/contentDisplay/feedList/FeedList.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default async function FeedList(props: Props) {
1818
<FeedItem
1919
key={feed.cid}
2020
feedItem={feed}
21-
saved={savedFeeds.some((savedFeed) => savedFeed.uri === feed.uri)}
21+
saved={savedFeeds.some((savedFeed) => savedFeed.uri === feed.uri)}
2222
/>
2323
))}
2424
{popularFeeds.length === 0 && (
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
1-
function Skeleton() {
1+
interface Props {
2+
rounded?: boolean;
3+
}
4+
5+
function Skeleton(props: Props) {
6+
const { rounded = false } = props;
7+
28
return (
3-
<article className="border-skin-base flex flex-col gap-2 border border-x-0 p-3 last:border-b md:border-x md:first:rounded-t-2xl md:last:rounded-b-2xl odd:[&:not(:last-child)]:border-b-0 even:[&:not(:last-child)]:border-b-0">
9+
<article
10+
className={`border-skin-base flex flex-col gap-2 border border-x-0 p-3 last:border-b md:border-x ${
11+
rounded && "md:first:rounded-t-2xl"
12+
} md:last:rounded-b-2xl odd:[&:not(:last-child)]:border-b-0 even:[&:not(:last-child)]:border-b-0`}
13+
>
414
<div className="flex flex-wrap items-center gap-3">
515
<div className="bg-skin-muted h-10 w-10 rounded-lg" />
616
<div className="flex flex-col gap-3">
@@ -16,21 +26,23 @@ function Skeleton() {
1626
);
1727
}
1828

19-
export default function FeedListSkeleton() {
29+
export default function FeedListSkeleton(props: Props) {
30+
const { rounded = false } = props;
31+
2032
return (
2133
<section className="flex flex-col">
22-
<Skeleton />
23-
<Skeleton />
24-
<Skeleton />
25-
<Skeleton />
26-
<Skeleton />
27-
<Skeleton />
28-
<Skeleton />
29-
<Skeleton />
30-
<Skeleton />
31-
<Skeleton />
32-
<Skeleton />
33-
<Skeleton />
34+
<Skeleton rounded={rounded} />
35+
<Skeleton rounded={rounded} />
36+
<Skeleton rounded={rounded} />
37+
<Skeleton rounded={rounded} />
38+
<Skeleton rounded={rounded} />
39+
<Skeleton rounded={rounded} />
40+
<Skeleton rounded={rounded} />
41+
<Skeleton rounded={rounded} />
42+
<Skeleton rounded={rounded} />
43+
<Skeleton rounded={rounded} />
44+
<Skeleton rounded={rounded} />
45+
<Skeleton rounded={rounded} />
3446
</section>
3547
);
3648
}

src/components/contentDisplay/searchList/SearchList.tsx

+12-4
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,20 @@ import UserSearchContainer from "@/containers/search/UserSearchContainer";
66
import { useSession } from "next-auth/react";
77
import Tabs from "@/components/navigational/tabs/Tabs";
88
import TabItem from "@/components/navigational/tabs/TabItem";
9+
import FeedSearchContainer from "@/containers/search/FeedSearchContainer";
910

1011
interface Props {
1112
query: string;
1213
}
1314

1415
export default function SearchList(props: Props) {
1516
const { query } = props;
16-
const [currentTab, setCurrentTab] = useState<"top" | "latest" | "users">(
17-
"top",
18-
);
17+
const [currentTab, setCurrentTab] = useState<
18+
"top" | "latest" | "users" | "feeds"
19+
>("top");
1920
const { data: session } = useSession();
2021

21-
const handleTabChange = (tab: "top" | "latest" | "users") => {
22+
const handleTabChange = (tab: "top" | "latest" | "users" | "feeds") => {
2223
setCurrentTab(tab);
2324
};
2425

@@ -51,6 +52,12 @@ export default function SearchList(props: Props) {
5152
label="Users"
5253
isActive={currentTab === "users"}
5354
/>
55+
<TabItem
56+
asButton
57+
onClick={() => handleTabChange("feeds")}
58+
label="Feeds"
59+
isActive={currentTab === "feeds"}
60+
/>
5461
</Tabs>
5562

5663
{currentTab === "latest" && (
@@ -60,6 +67,7 @@ export default function SearchList(props: Props) {
6067
<PostSearchContainer query={onSearchPost(query)} sort={currentTab} />
6168
)}
6269
{currentTab === "users" && <UserSearchContainer query={query} />}
70+
{currentTab === "feeds" && <FeedSearchContainer query={query} />}
6371
</section>
6472
);
6573
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"use client";
2+
3+
import { getPopularFeeds } from "@/lib/api/bsky/feed";
4+
import { useQuery } from "@tanstack/react-query";
5+
import FeedAlert from "@/components/feedback/feedAlert/FeedAlert";
6+
import { getAgentFromClient } from "@/lib/api/bsky/agent";
7+
import FeedListSkeleton from "@/components/contentDisplay/feedList/FeedListSkeleton";
8+
import FeedItem from "@/components/contentDisplay/feedItem/FeedItem";
9+
10+
interface Props {
11+
query: string;
12+
}
13+
14+
export default function FeedSearchContainer(props: Props) {
15+
const { query } = props;
16+
const { data: feeds, isFetching } = useQuery({
17+
queryKey: ["searchFeeds", query],
18+
queryFn: async () => {
19+
const agent = await getAgentFromClient();
20+
return getPopularFeeds(query, agent);
21+
},
22+
});
23+
24+
const isEmpty = !isFetching && feeds?.length === 0;
25+
26+
return (
27+
<section>
28+
{feeds &&
29+
feeds.map((feed) => (
30+
<FeedItem key={feed.cid} feedItem={feed} rounded={false} />
31+
))}
32+
33+
{isEmpty && (
34+
<div className="border-skin-base border-t">
35+
<FeedAlert variant="empty" message={`No feeds found for ${query}`} />
36+
</div>
37+
)}
38+
{isFetching && <FeedListSkeleton />}
39+
</section>
40+
);
41+
}

src/lib/api/bsky/feed/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { type Agent, AppBskyActorDefs } from "@atproto/api";
22
import { getAgentFromServer } from "../agent";
33
import { SavedFeed } from "../../../../../types/feed";
44

5-
export const getPopularFeeds = async (search?: string) => {
6-
const agent = await getAgentFromServer();
5+
export const getPopularFeeds = async (search?: string, agent?: Agent) => {
6+
if (!agent) agent = await getAgentFromServer();
77
const popularFeeds = await agent.app.bsky.unspecced.getPopularFeedGenerators({
88
query: search,
99
});

0 commit comments

Comments
 (0)