From e91a218a8cb923d8fcb2088c46dc80c792fe23b4 Mon Sep 17 00:00:00 2001 From: KRTirtho Date: Fri, 19 Mar 2021 15:44:41 +0600 Subject: [PATCH] Added: SearchResult comp enhanced, Error, loading, retry handler comp, pagination --- .vscode/settings.json | 3 + assets/loading-spinner.gif | Bin 0 -> 19705 bytes src/app.tsx | 1 - src/components/CurrentPlaylist.tsx | 22 ++- src/components/Home.tsx | 128 +----------------- src/components/Library.tsx | 83 +++++++++--- src/components/Player.tsx | 2 + src/components/PlayerProgressBar.tsx | 4 +- src/components/PlaylistGenreView.tsx | 13 +- src/components/PlaylistView.tsx | 109 ++------------- src/components/Search.tsx | 37 +++-- .../SearchResultPlaylistCollection.tsx | 19 +-- .../SearchResultSongsCollection.tsx | 58 ++++++++ src/components/shared/ErrorApplet.tsx | 24 ---- src/components/shared/PlaceholderApplet.tsx | 56 ++++++++ src/components/shared/PlaylistCard.tsx | 123 +++++++++++++++++ src/components/shared/TrackButton.tsx | 85 ++++++++++++ src/conf.ts | 3 +- src/hooks/useTrackReaction.ts | 9 +- src/icons.ts | 4 +- src/routes.tsx | 4 + 21 files changed, 487 insertions(+), 300 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 assets/loading-spinner.gif create mode 100644 src/components/SearchResultSongsCollection.tsx delete mode 100644 src/components/shared/ErrorApplet.tsx create mode 100644 src/components/shared/PlaceholderApplet.tsx create mode 100644 src/components/shared/PlaylistCard.tsx create mode 100644 src/components/shared/TrackButton.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..3662b370 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib" +} \ No newline at end of file diff --git a/assets/loading-spinner.gif b/assets/loading-spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..fb9c052f6b5104e8f99448080e98fcd2fc7195a5 GIT binary patch literal 19705 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
There is nothing being played now
`}; } + if (currentTrack && !currentPlaylist) { + + + + } + return ( {`

${currentPlaylist?.name}

`}
@@ -17,16 +24,7 @@ function CurrentPlaylist() { {currentPlaylist?.tracks.map(({ track }, index) => { - return ( - setCurrentTrack(track) }} - onTrackClick={() => {}} - /> - ); + return ; })} diff --git a/src/components/Home.tsx b/src/components/Home.tsx index 359aea05..85f9ecc1 100644 --- a/src/components/Home.tsx +++ b/src/components/Home.tsx @@ -1,21 +1,14 @@ -import React, { useContext, useMemo, useState } from "react"; -import { Button, ScrollArea, View, Text } from "@nodegui/react-nodegui"; +import React from "react"; +import { Button, ScrollArea, View } from "@nodegui/react-nodegui"; import { useHistory } from "react-router"; -import CachedImage from "./shared/CachedImage"; -import { CursorShape, QIcon, QMouseEvent } from "@nodegui/nodegui"; +import { CursorShape, QMouseEvent } from "@nodegui/nodegui"; import { QueryCacheKeys } from "../conf"; import useSpotifyQuery from "../hooks/useSpotifyQuery"; -import ErrorApplet from "./shared/ErrorApplet"; -import IconButton from "./shared/IconButton"; -import { heart, heartRegular, pause, play } from "../icons"; -import playerContext from "../context/playerContext"; -import { audioPlayer } from "./Player"; -import showError from "../helpers/showError"; -import { generateRandomColor, getDarkenForeground } from "../helpers/RandomColor"; -import usePlaylistReaction from "../hooks/usePlaylistReaction"; +import PlaceholderApplet from "./shared/PlaceholderApplet"; +import PlaylistCard from "./shared/PlaylistCard"; function Home() { - const { data: categories, isError, isRefetchError, refetch } = useSpotifyQuery( + const { data: categories, isError, refetch, isLoading } = useSpotifyQuery( QueryCacheKeys.categories, (spotifyApi) => spotifyApi.getCategories({ country: "US" }).then((categoriesReceived) => categoriesReceived.body.categories.items), { initialData: [] } @@ -24,7 +17,7 @@ function Home() { return ( - {(isError || isRefetchError) && } + {categories?.map((category, index) => { return ; })} @@ -95,110 +88,3 @@ const CategoryCard = ({ id, name }: CategoryCardProps) => { ); }; - -interface PlaylistCardProps { - playlist: SpotifyApi.PlaylistObjectSimplified; -} - -export const PlaylistCard = React.memo(({ playlist }: PlaylistCardProps) => { - const { id, description, name, images } = playlist; - const history = useHistory(); - const [hovered, setHovered] = useState(false); - const { setCurrentTrack, currentPlaylist, setCurrentPlaylist } = useContext(playerContext); - const { refetch } = useSpotifyQuery([QueryCacheKeys.playlistTracks, id], (spotifyApi) => spotifyApi.getPlaylistTracks(id).then((track) => track.body.items), { - initialData: [], - enabled: false, - }); - const { reactToPlaylist, isFavorite } = usePlaylistReaction(); - - const handlePlaylistPlayPause = async () => { - try { - const { data: tracks, isSuccess } = await refetch(); - if (currentPlaylist?.id !== id && isSuccess && tracks) { - setCurrentPlaylist({ tracks, id, name, thumbnail: images[0].url }); - setCurrentTrack(tracks[0].track); - } else { - await audioPlayer.stop(); - setCurrentTrack(undefined); - setCurrentPlaylist(undefined); - } - } catch (error) { - showError(error, "[Failed adding playlist to queue]: "); - } - }; - - function gotoPlaylist(native?: any) { - const key = new QMouseEvent(native); - if (key.button() === 1) { - history.push(`/playlist/${id}`, { name, thumbnail: images[0].url }); - } - } - - const bgColor1 = useMemo(() => generateRandomColor(), []); - const color = useMemo(() => getDarkenForeground(bgColor1), [bgColor1]); - - const playlistStyleSheet = ` - #playlist-container{ - width: 150px; - flex-direction: column; - padding: 5px; - min-height: 150px; - background-color: ${bgColor1}; - border-radius: 5px; - } - #playlist-container:hover{ - border: 1px solid green; - } - #playlist-container:clicked{ - border: 5px solid green; - } - `; - - return ( - - {/* */} - - {` -
-

${name}

-

${description}

-
- `} -
- {(hovered || currentPlaylist?.id === id) && ( - <> - - - - )} -
- ); -}); diff --git a/src/components/Library.tsx b/src/components/Library.tsx index fcf5fe3e..cce426fd 100644 --- a/src/components/Library.tsx +++ b/src/components/Library.tsx @@ -1,11 +1,14 @@ -import { ScrollArea, View } from "@nodegui/react-nodegui"; +import { Button, ScrollArea, View } from "@nodegui/react-nodegui"; import React, { useContext } from "react"; import { Redirect, Route } from "react-router"; import { QueryCacheKeys } from "../conf"; import playerContext from "../context/playerContext"; +import useSpotifyInfiniteQuery from "../hooks/useSpotifyInfiniteQuery"; import useSpotifyQuery from "../hooks/useSpotifyQuery"; -import { PlaylistCard } from "./Home"; -import { PlaylistSimpleControls, TrackButton, TrackTableIndex } from "./PlaylistView"; +import { PlaylistSimpleControls, TrackTableIndex } from "./PlaylistView"; +import PlaceholderApplet from "./shared/PlaceholderApplet"; +import PlaylistCard from "./shared/PlaylistCard"; +import { TrackButton } from "./shared/TrackButton"; import { TabMenuItem } from "./TabMenu"; function Library() { @@ -38,6 +41,7 @@ function UserPlaylists() { return ( + {userPlaylists?.map((playlist, index) => ( ))} @@ -48,10 +52,23 @@ function UserPlaylists() { function UserSavedTracks() { const userSavedPlaylistId = "user-saved-tracks"; - const { data: userTracks, isError, isLoading } = useSpotifyQuery(QueryCacheKeys.userSavedTracks, (spotifyApi) => - spotifyApi.getMySavedTracks({ limit: 50 }).then((tracks) => tracks.body.items) + const { data: userSavedTracks, fetchNextPage, hasNextPage, isFetchingNextPage, isError, isLoading, refetch } = useSpotifyInfiniteQuery( + QueryCacheKeys.userSavedTracks, + (spotifyApi, { pageParam }) => spotifyApi.getMySavedTracks({ limit: 50, offset: pageParam }).then((res) => res.body), + { + getNextPageParam(lastPage) { + if (lastPage.next) { + return lastPage.offset + lastPage.limit; + } + }, + } ); - const { currentPlaylist, setCurrentPlaylist, setCurrentTrack, currentTrack } = useContext(playerContext); + const { currentPlaylist, setCurrentPlaylist, setCurrentTrack } = useContext(playerContext); + + const userTracks = userSavedTracks?.pages + ?.map((page) => page.items) + .filter(Boolean) + .flat(1) as SpotifyApi.SavedTrackObject[] | undefined; function handlePlaylistPlayPause(index?: number) { if (currentPlaylist?.id !== userSavedPlaylistId && userTracks) { @@ -63,26 +80,58 @@ function UserSavedTracks() { } } + const playlist: SpotifyApi.PlaylistObjectFull = { + collaborative: false, + description: "User Playlist", + tracks: { + items: [userTracks ?? []].map( + (userTrack) => + (({ + ...userTrack, + added_by: "Me", + is_local: false, + added_at: Date.now(), + } as unknown) as SpotifyApi.PlaylistTrackObject) + ), + limit: 20, + href: "", + next: "", + offset: 0, + previous: "", + total: 20, + }, + external_urls: { spotify: "" }, + followers: { href: null, total: 2 }, + href: "", + id: userSavedPlaylistId, + images: [], + name: "User saved track", + owner: { external_urls: { spotify: "" }, href: "", id: "Me", type: "user", uri: "spotify:user:me", display_name: "User", followers: { href: null, total: 0 } }, + public: false, + snapshot_id: userSavedPlaylistId + "snapshot", + type: "playlist", + uri: "spotify:user:me:saved-tracks", + }; return ( - + - {userTracks?.map(({ track }, index) => ( - + {userTracks?.map(({ track }, index) => track && )} + {hasNextPage && ( +