Skip to content

Commit ed7f851

Browse files
committed
feat: migrate to react-query
Signed-off-by: rajput-hemant <[email protected]>
1 parent 83dc9fb commit ed7f851

File tree

16 files changed

+340
-250
lines changed

16 files changed

+340
-250
lines changed

bun.lockb

793 Bytes
Binary file not shown.

next.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const config = {
2121
hostname: "c.sop.saavncdn.com",
2222
},
2323
],
24-
unoptimized: isDocker,
24+
unoptimized: !isDocker,
2525
},
2626
experimental: {
2727
ppr: true,

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"@radix-ui/react-toggle-group": "^1.0.4",
4646
"@radix-ui/react-tooltip": "^1.0.7",
4747
"@t3-oss/env-nextjs": "^0.10.1",
48+
"@tanstack/react-query": "^5.40.0",
4849
"@upstash/ratelimit": "^1.1.3",
4950
"@upstash/redis": "^1.31.3",
5051
"babel-plugin-react-compiler": "^0.0.0-experimental-592953e-20240517",
Lines changed: 27 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use client";
22

33
import React from "react";
4+
import { useInfiniteQuery } from "@tanstack/react-query";
45
import { Loader2 } from "lucide-react";
56

67
import type { Lang, TopAlbum } from "@/types";
@@ -15,35 +16,29 @@ type TopAlbumsProps = {
1516
};
1617

1718
export function TopAlbums({ initialAlbums, lang }: TopAlbumsProps) {
18-
const { data, last_page } = initialAlbums;
19+
const { data, fetchNextPage, isFetchingNextPage, hasNextPage } =
20+
useInfiniteQuery({
21+
queryKey: ["top-albums", lang],
22+
queryFn: ({ pageParam }) => getTopAlbums(pageParam, 50, lang),
23+
getNextPageParam: ({ last_page }, allPages) =>
24+
last_page ? null : allPages.length + 1,
25+
initialPageParam: 1,
26+
initialData: { pages: [initialAlbums], pageParams: [1] },
27+
});
1928

20-
const [topAlbums, setTopAlbums] = React.useState(data);
21-
const [page, setPage] = React.useState(1);
22-
const [isLoading, setIsLoading] = React.useState(false);
23-
const [hasMore, setHasMore] = React.useState(!last_page);
29+
const topAlbums = data.pages.flatMap((page) => page.data);
2430

25-
const loadMoreRef = React.useRef<HTMLDivElement | null>(null);
26-
const isLoadMoreVisible = !!useIntersectionObserver(loadMoreRef, {})
27-
?.isIntersecting;
28-
29-
async function loadMoreAlbums() {
30-
setIsLoading(true);
31-
const nextPage = page + 1;
32-
const albums = await getTopAlbums(nextPage, 50, lang);
33-
setTopAlbums((prevAlbums) => [...prevAlbums, ...albums.data]);
34-
setPage(nextPage);
35-
setHasMore(!albums.last_page);
36-
setIsLoading(false);
37-
}
38-
39-
React.useEffect(() => {
40-
if (isLoadMoreVisible) {
41-
loadMoreAlbums();
42-
}
43-
}, [isLoadMoreVisible]); // eslint-disable-line react-hooks/exhaustive-deps
31+
const [ref] = useIntersectionObserver({
32+
threshold: 0.5,
33+
onChange(isIntersecting) {
34+
if (isIntersecting) {
35+
fetchNextPage();
36+
}
37+
},
38+
});
4439

4540
return (
46-
<>
41+
<div className="py-6">
4742
<div className="flex w-full flex-wrap justify-between gap-y-4">
4843
{topAlbums.map(({ id, name, url, subtitle, type, image, explicit }) => (
4944
<SliderCard
@@ -58,21 +53,22 @@ export function TopAlbums({ initialAlbums, lang }: TopAlbumsProps) {
5853
))}
5954
</div>
6055

61-
{hasMore ?
56+
{hasNextPage ?
6257
<div
63-
ref={loadMoreRef}
58+
ref={ref}
6459
className="flex items-center justify-center gap-2 font-bold text-muted-foreground"
6560
>
66-
{isLoading && (
61+
{isFetchingNextPage && (
6762
<>
6863
<Loader2 className="size-5 animate-spin" /> Loading...
6964
</>
7065
)}
7166
</div>
72-
: <h3 className="py-6 text-center font-heading text-xl drop-shadow-md dark:bg-gradient-to-br dark:from-neutral-200 dark:to-neutral-600 dark:bg-clip-text dark:text-transparent sm:text-2xl md:text-3xl">
73-
<em>Yay! You have seen it all</em> 🤩
67+
: <h3 className="text-center font-heading text-xl drop-shadow-md dark:bg-gradient-to-br dark:from-neutral-200 dark:to-neutral-600 dark:bg-clip-text dark:text-transparent sm:text-2xl md:text-3xl">
68+
<em>Yay! You have seen it all</em>{" "}
69+
<span className="text-foreground">🤩</span>
7470
</h3>
7571
}
76-
</>
72+
</div>
7773
);
7874
}

src/app/(root)/artist/[name]/[token]/_components/artists-top-items.tsx

Lines changed: 50 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import { useEffect, useState } from "react";
3+
import { useInfiniteQuery } from "@tanstack/react-query";
44

55
import type { User } from "next-auth";
66
import type { Favorite, MyPlaylist } from "@/lib/db/schema";
@@ -11,7 +11,7 @@ import { SongListClient } from "@/components/song-list/song-list.client";
1111
import { Button } from "@/components/ui/button";
1212
import { getArtistsAlbums, getArtistsSongs } from "@/lib/jiosaavn-api";
1313

14-
type ArtistsTopItemsProps = {
14+
type Props = {
1515
id: string;
1616
type: "songs" | "albums";
1717
initialSongs?: Song[];
@@ -22,7 +22,7 @@ type ArtistsTopItemsProps = {
2222
userPlaylists?: MyPlaylist[];
2323
};
2424

25-
export function ArtistsTopItems(props: ArtistsTopItemsProps) {
25+
export function ArtistsTopItems(props: Props) {
2626
const {
2727
id,
2828
type,
@@ -34,54 +34,52 @@ export function ArtistsTopItems(props: ArtistsTopItemsProps) {
3434
userPlaylists,
3535
} = props;
3636

37-
const [songs, setSongs] = useState(initialSongs ?? []);
38-
const [albums, setAlbums] = useState(initialAlbums ?? []);
39-
const [page, setPage] = useState(5);
40-
const [isLoading, setIsLoading] = useState(false);
41-
const [hasMore, setHasMore] = useState(true);
42-
43-
useEffect(() => {
44-
if (category === undefined) return;
45-
46-
const sort = category === "latest" ? "desc" : "asc";
47-
37+
const sort = category === "latest" ? "desc" : "asc";
38+
39+
const songResults = useInfiniteQuery({
40+
queryKey: [id, type === "songs" ? "artists-top-songs" : null],
41+
queryFn: async ({ pageParam }) => {
42+
const songs = await getArtistsSongs(id, pageParam, category, sort);
43+
return songs.top_songs;
44+
},
45+
getNextPageParam: ({ last_page }, allPages) =>
46+
last_page ? null : allPages.length + 5,
47+
initialPageParam: 1,
48+
initialData: {
49+
pages: [{ songs: initialSongs, total: 0, last_page: false }],
50+
pageParams: [1],
51+
},
52+
});
53+
54+
const albumsResults = useInfiniteQuery({
55+
queryKey: [id, type === "albums" ? "artists-top-albums" : null],
56+
queryFn: async ({ pageParam }) => {
57+
const albums = await getArtistsAlbums(id, pageParam, category, sort);
58+
return albums.top_albums;
59+
},
60+
getNextPageParam: ({ last_page }, allPages) =>
61+
last_page ? null : allPages.length + 2,
62+
initialPageParam: 1,
63+
initialData: {
64+
pages: [{ albums: initialAlbums, total: 0, last_page: false }],
65+
pageParams: [1],
66+
},
67+
});
68+
69+
const songs = songResults.data.pages.flatMap((page) => page.songs ?? []);
70+
const albums = albumsResults.data.pages.flatMap((page) => page.albums ?? []);
71+
72+
const hasNextPage = songResults.hasNextPage || albumsResults.hasNextPage;
73+
const isLoading =
74+
songResults.isFetchingNextPage || albumsResults.isFetchingNextPage;
75+
76+
const clickHandler = () => {
4877
if (type === "songs") {
49-
(async () => {
50-
const songs = await getArtistsSongs(id, 0, category, sort);
51-
setSongs(songs.top_songs.songs);
52-
setHasMore(!songs.top_songs.last_page);
53-
})();
78+
songResults.fetchNextPage();
79+
} else if (type === "albums") {
80+
albumsResults.fetchNextPage();
5481
}
55-
56-
if (type === "albums") {
57-
(async () => {
58-
const albums = await getArtistsAlbums(id, 0, category, sort);
59-
setAlbums(albums.top_albums.albums);
60-
setHasMore(!albums.top_albums.last_page);
61-
})();
62-
}
63-
}, [id, type, category]);
64-
65-
async function clickHandler() {
66-
setIsLoading(true);
67-
68-
const nextPage = page + 1;
69-
70-
if (type === "songs") {
71-
const songs = await getArtistsSongs(id, nextPage, category);
72-
setSongs((s) => [...s, ...songs.top_songs.songs]);
73-
setHasMore(!songs.top_songs.last_page);
74-
}
75-
76-
if (type === "albums") {
77-
const albums = await getArtistsAlbums(id, nextPage, category);
78-
setAlbums((a) => [...a, ...albums.top_albums.albums]);
79-
setHasMore(!albums.top_albums.last_page);
80-
}
81-
82-
setPage(nextPage);
83-
setIsLoading(false);
84-
}
82+
};
8583

8684
return (
8785
<>
@@ -106,7 +104,7 @@ export function ArtistsTopItems(props: ArtistsTopItemsProps) {
106104
))}
107105
</div>
108106

109-
{hasMore ?
107+
{hasNextPage ?
110108
<Button
111109
variant="outline"
112110
className="mx-auto my-4 flex rounded-full text-center"
@@ -115,7 +113,8 @@ export function ArtistsTopItems(props: ArtistsTopItemsProps) {
115113
{isLoading ? "Loading..." : "Load More"}
116114
</Button>
117115
: <h3 className="py-6 text-center font-heading text-xl drop-shadow-md dark:bg-gradient-to-br dark:from-neutral-200 dark:to-neutral-600 dark:bg-clip-text dark:text-transparent sm:text-2xl md:text-3xl">
118-
<em>Yay! You have seen it all</em> 🤩
116+
<em>Yay! You have seen it all</em>{" "}
117+
<span className="text-foreground">🤩</span>
119118
</h3>
120119
}
121120
</>

src/app/(root)/artist/[name]/[token]/page.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,12 @@ import { ArtistsTopItems } from "./_components/artists-top-items";
1414
import { CategoryFilter } from "./_components/category-filter";
1515
import { TABS } from "./_components/tabs";
1616

17-
type ArtistDetailsPageProps = {
17+
type Props = {
1818
params: { name: string; token: string };
1919
searchParams: { cat?: Category };
2020
};
2121

22-
export async function generateMetadata({
23-
params,
24-
}: ArtistDetailsPageProps): Promise<Metadata> {
22+
export async function generateMetadata({ params }: Props): Promise<Metadata> {
2523
const { name, token } = params;
2624

2725
const artist = await getArtistDetails(token);
@@ -40,7 +38,8 @@ export async function generateMetadata({
4038
},
4139
};
4240
}
43-
export default async function ArtistDetailsPage(props: ArtistDetailsPageProps) {
41+
42+
export default async function ArtistDetailsPage(props: Props) {
4443
const {
4544
params: { name, token },
4645
searchParams: { cat },
@@ -91,6 +90,7 @@ export default async function ArtistDetailsPage(props: ArtistDetailsPageProps) {
9190
<CategoryFilter category={cat ?? "popularity"} />
9291

9392
<ArtistsTopItems
93+
key={artist.top_songs[0].id}
9494
id={artist.id}
9595
type="songs"
9696
category={cat}
@@ -105,6 +105,7 @@ export default async function ArtistDetailsPage(props: ArtistDetailsPageProps) {
105105
<CategoryFilter category={cat ?? "popularity"} />
106106

107107
<ArtistsTopItems
108+
key={artist.top_albums[0].id}
108109
id={artist.id}
109110
type="albums"
110111
category={cat}

src/app/(root)/artist/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ export default async function TopArtistsPage() {
4343
</div>
4444

4545
<h3 className="py-6 text-center font-heading text-xl drop-shadow-md dark:bg-gradient-to-br dark:from-neutral-200 dark:to-neutral-600 dark:bg-clip-text dark:text-transparent sm:text-2xl md:text-3xl">
46-
<em>Yay! You have seen it all</em> 🤩
46+
<em>Yay! You have seen it all</em>{" "}
47+
<span className="text-foreground">🤩</span>
4748
</h3>
4849
</div>
4950
);

src/app/(root)/chart/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ export default async function ChartsPage() {
4343
</div>
4444

4545
<h3 className="py-6 text-center font-heading text-xl drop-shadow-md dark:bg-gradient-to-br dark:from-neutral-200 dark:to-neutral-600 dark:bg-clip-text dark:text-transparent sm:text-2xl md:text-3xl">
46-
<em>Yay! You have seen it all</em> 🤩
46+
<em>Yay! You have seen it all</em>{" "}
47+
<span className="text-foreground">🤩</span>
4748
</h3>
4849
</div>
4950
);

src/app/(root)/me/playlist/[id]/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ export default async function MyPlaylistsPage({ params: { id } }: Props) {
120120
<SongList items={songsDetails.songs} />
121121

122122
<h3 className="py-6 text-center font-heading text-xl drop-shadow-md dark:bg-gradient-to-br dark:from-neutral-200 dark:to-neutral-600 dark:bg-clip-text dark:text-transparent sm:text-2xl md:text-3xl">
123-
<em>Yay! You have seen it all</em> 🤩
123+
<em>Yay! You have seen it all</em>{" "}
124+
<span className="text-foreground">🤩</span>
124125
</h3>
125126
</>
126127
: <div className="h-96">

0 commit comments

Comments
 (0)