Search & SearchResultPlaylist see all added. Spotify Infinite Query added for infinite scroll

This commit is contained in:
KRTirtho 2021-03-18 11:11:03 +06:00
parent be612610d9
commit d695172804
10 changed files with 135 additions and 47 deletions

View File

@ -75,8 +75,11 @@ const CategoryCard = ({ id, name }: CategoryCardProps) => {
{ initialData: [] }
);
function goToGenre() {
history.push(`/genre/playlists/${id}`, { name });
function goToGenre(native: any) {
const mouse = new QMouseEvent(native);
if (mouse.button() === 1) {
history.push(`/genre/playlists/${id}`, { name });
}
}
if (isError) {
return <></>;

View File

@ -1,5 +1,5 @@
import { Direction } from "@nodegui/nodegui";
import { BoxView, ScrollArea, Text, View, GridView, GridColumn, GridRow } from "@nodegui/react-nodegui";
import { QAbstractButtonSignals } from "@nodegui/nodegui";
import { Button, ScrollArea, Text, View } from "@nodegui/react-nodegui";
import React from "react";
import { useLocation, useParams } from "react-router";
import { QueryCacheKeys } from "../conf";
@ -12,11 +12,23 @@ function PlaylistGenreView() {
const location = useLocation<{ name: string }>();
const { data: playlists } = useSpotifyQuery<SpotifyApi.PlaylistObjectSimplified[]>(
[QueryCacheKeys.genrePlaylists, id],
(spotifyApi) => spotifyApi.getPlaylistsForCategory(id)
.then((playlistsRes) => playlistsRes.body.playlists.items),
(spotifyApi) => spotifyApi.getPlaylistsForCategory(id).then((playlistsRes) => playlistsRes.body.playlists.items),
{ initialData: [] }
)
);
return <GenreView heading={location.state.name} playlists={playlists ?? []} />;
}
export default PlaylistGenreView;
interface GenreViewProps {
heading: string;
playlists: SpotifyApi.PlaylistObjectSimplified[];
loadMore?: QAbstractButtonSignals["clicked"];
isLoadable?: boolean;
}
export function GenreView({ heading, playlists, loadMore, isLoadable }: GenreViewProps) {
const playlistGenreViewStylesheet = `
#genre-container{
flex-direction: column;
@ -38,25 +50,18 @@ function PlaylistGenreView() {
width: 330px;
}
`;
return (
<View id="genre-container" styleSheet={playlistGenreViewStylesheet}>
<BackButton />
<Text id="heading">{`<h2>${location.state.name}</h2>`}</Text>
<Text id="heading">{`<h2>${heading}</h2>`}</Text>
<ScrollArea id="scroll-view">
<View id="child-container">
{playlists?.map((playlist, index) => {
return (
<PlaylistCard
key={index + playlist.id}
playlist={playlist}
/>
);
return <PlaylistCard key={index + playlist.id} playlist={playlist} />;
})}
{loadMore && <Button text="Load more" on={{ clicked: loadMore }} enabled={isLoadable} />}
</View>
</ScrollArea>
</View>
);
}
export default PlaylistGenreView;

View File

@ -76,20 +76,22 @@ const PlaylistView: FC = () => {
</>
)}
{tracks?.map(({ track }, index) => {
return (
<TrackButton
key={index + track.id}
track={track}
index={index}
active={currentTrack?.id === track.id && currentPlaylist?.id === params.id}
on={{
MouseButtonRelease: () => trackClickHandler(track),
}}
onTrackClick={() => {
handlePlaylistPlayPause(index);
}}
/>
);
if (track) {
return (
<TrackButton
key={index + track.id}
track={track}
index={index}
active={currentTrack?.id === track.id && currentPlaylist?.id === params.id}
on={{
MouseButtonRelease: () => trackClickHandler(track),
}}
onTrackClick={() => {
handlePlaylistPlayPause(index);
}}
/>
);
}
})}
</View>
</ScrollArea>

View File

@ -1,6 +1,7 @@
import { QIcon } from "@nodegui/nodegui";
import { QIcon, QMouseEvent } from "@nodegui/nodegui";
import { LineEdit, ScrollArea, Text, View } from "@nodegui/react-nodegui";
import React, { useState } from "react";
import { useHistory } from "react-router";
import { QueryCacheKeys } from "../conf";
import showError from "../helpers/showError";
import useSpotifyQuery from "../hooks/useSpotifyQuery";
@ -10,10 +11,11 @@ import { TrackButton, TrackTableIndex } from "./PlaylistView";
import IconButton from "./shared/IconButton";
function Search() {
const history = useHistory<{ searchQuery: string }>();
const [searchQuery, setSearchQuery] = useState<string>("");
const { data: searchResults, isError, refetch } = useSpotifyQuery<SpotifyApi.SearchResponse>(
QueryCacheKeys.search,
(spotifyApi) => spotifyApi.search(searchQuery, ["playlist", "track"]).then((res) => res.body),
(spotifyApi) => spotifyApi.search(searchQuery, ["playlist", "track"], { limit: 4 }).then((res) => res.body),
{ enabled: false }
);
@ -45,18 +47,23 @@ function Search() {
<Text>{`<h2>Songs</h2>`}</Text>
<TrackTableIndex />
{searchResults?.tracks?.items.map((track, index) => (
<TrackButton active={false} index={index} track={track} on={{}} onTrackClick={() => {}} />
<TrackButton key={index+track.id} active={false} index={index} track={track} on={{}} onTrackClick={() => {}} />
))}
</View>
<View style="flex: 1; flex-direction: 'column';">
<Text>{`<h2>Playlists</h2>`}</Text>
<ScrollArea style="flex: 1;">
<View style="flex-direction: 'row'; align-items: 'center'; flex-wrap: 'wrap'; width: 330px;">
{searchResults?.playlists?.items.map((playlist) => (
<PlaylistCard playlist={playlist} />
))}
</View>
</ScrollArea>
<Text
on={{
MouseButtonRelease(native: any) {
if (new QMouseEvent(native).button() === 1) {
history.push("/search/playlists", { searchQuery });
}
},
}}>{`<h2>Playlists</h2>`}</Text>
<View style="flex: 1; justify-content: 'space-around'; align-items: 'center';">
{searchResults?.playlists?.items.map((playlist, index) => (
<PlaylistCard key={index+playlist.id} playlist={playlist} />
))}
</View>
</View>
</View>
</ScrollArea>

View File

@ -0,0 +1,35 @@
import React, { useState } from "react";
import { useLocation } from "react-router";
import { QueryCacheKeys } from "../conf";
import useSpotifyInfiniteQuery from "../hooks/useSpotifyInfiniteQuery";
import { GenreView } from "./PlaylistGenreView";
function SearchResultPlaylistCollection() {
const location = useLocation<{ searchQuery: string }>();
const { data: searchResults, fetchNextPage, hasNextPage, isFetchingNextPage } = useSpotifyInfiniteQuery<SpotifyApi.SearchResponse>(
QueryCacheKeys.searchPlaylist,
(spotifyApi, { pageParam }) => spotifyApi.searchPlaylists(location.state.searchQuery, { limit: 20, offset: pageParam }).then((res) => res.body),
{
getNextPageParam: (lastPage) => {
return (lastPage.playlists?.offset ?? 0) + 1;
},
}
);
return (
<GenreView
heading={"Search: "+location.state.searchQuery}
playlists={
(searchResults?.pages
?.map((page) => page.playlists?.items)
.filter(Boolean)
.flat(1) as SpotifyApi.PlaylistObjectSimplified[]) ?? []
}
loadMore={() => {
fetchNextPage();
}}
isLoadable={hasNextPage || !isFetchingNextPage}
/>
);
}
export default SearchResultPlaylistCollection;

View File

@ -16,5 +16,6 @@ export enum QueryCacheKeys{
playlistTracks="playlistTracks",
userPlaylists = "user-palylists",
userSavedTracks = "user-saved-tracks",
search="search"
search = "search",
searchPlaylist="searchPlaylist"
}

View File

@ -17,7 +17,6 @@ function useSpotifyApi() {
useEffect(() => {
if (isLoggedIn && clientId && clientSecret && refreshToken) {
console.log(chalk.bgCyan.black("Setting up spotify credentials"));
spotifyApi.setClientId(clientId);
spotifyApi.setClientSecret(clientSecret);
spotifyApi.setRefreshToken(refreshToken);
@ -25,7 +24,6 @@ function useSpotifyApi() {
spotifyApi
.refreshAccessToken()
.then((token) => {
console.log(chalk.bgRedBright.yellow("Refreshing access token from useSpotifyApi"));
setAccess_token(token.body.access_token);
})
.catch((error) => {
@ -33,7 +31,6 @@ function useSpotifyApi() {
});
}
spotifyApi.setAccessToken(access_token);
console.log(chalk.bgCyan.green("Finished setting up credentials"));
}
}, [access_token, clientId, clientSecret, isLoggedIn]);

View File

@ -0,0 +1,28 @@
import { useEffect } from "react";
import { QueryFunctionContext, QueryKey, useInfiniteQuery, UseInfiniteQueryOptions, UseInfiniteQueryResult } from "react-query";
import SpotifyWebApi from "spotify-web-api-node";
import useSpotifyApi from "./useSpotifyApi";
import useSpotifyApiError from "./useSpotifyApiError";
type SpotifyQueryFn<TQueryData> = (spotifyApi: SpotifyWebApi, pageArgs: QueryFunctionContext) => Promise<TQueryData>;
function useSpotifyInfiniteQuery<TQueryData = unknown>(
queryKey: QueryKey,
queryHandler: SpotifyQueryFn<TQueryData>,
options: UseInfiniteQueryOptions<TQueryData, SpotifyApi.ErrorObject> = {}
): UseInfiniteQueryResult<TQueryData, SpotifyApi.ErrorObject> {
const spotifyApi = useSpotifyApi();
const handleSpotifyError = useSpotifyApiError(spotifyApi);
const query = useInfiniteQuery<TQueryData, SpotifyApi.ErrorObject>(queryKey, (pageArgs) => queryHandler(spotifyApi, pageArgs), options);
const { isError, error } = query;
useEffect(() => {
if (isError && error) {
handleSpotifyError(error);
}
}, [isError, error]);
return query;
}
export default useSpotifyInfiniteQuery;

View File

@ -9,6 +9,7 @@ import TabMenu from "./components/TabMenu";
import CurrentPlaylist from "./components/CurrentPlaylist";
import Library from "./components/Library";
import Search from "./components/Search";
import SearchResultPlaylistCollection from "./components/SearchResultPlaylistCollection";
function Routes() {
const { isLoggedIn } = useContext(authContext);
@ -33,13 +34,18 @@ function Routes() {
<Login />
)}
</Route>
<Route path="/search"><Search/></Route>
<Route path="/currently">
<CurrentPlaylist />
</Route>
<Route path="/library">
<Library />
</Route>
<Route exact path="/search">
<Search />
</Route>
<Route exact path="/search/playlists">
<SearchResultPlaylistCollection />
</Route>
</>
);
}

View File

@ -2,6 +2,10 @@
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"lib": [
"ES2019.Array",
"DOM"
],
"jsx": "react",
"strict": true,
"alwaysStrict": true,