fixed useTrackReaction & usePlaylistReaction mutation bugs... handleSpotifyError bug fixed

This commit is contained in:
KRTirtho 2021-03-20 11:59:38 +06:00
parent 5a1da660a1
commit 2d42bbd23d
5 changed files with 109 additions and 56 deletions

View File

@ -5,10 +5,11 @@ import { QueryCacheKeys } from "../conf";
import playerContext from "../context/playerContext"; import playerContext from "../context/playerContext";
import useSpotifyInfiniteQuery from "../hooks/useSpotifyInfiniteQuery"; import useSpotifyInfiniteQuery from "../hooks/useSpotifyInfiniteQuery";
import useSpotifyQuery from "../hooks/useSpotifyQuery"; import useSpotifyQuery from "../hooks/useSpotifyQuery";
import { GenreView } from "./PlaylistGenreView";
import { PlaylistSimpleControls, TrackTableIndex } from "./PlaylistView"; import { PlaylistSimpleControls, TrackTableIndex } from "./PlaylistView";
import PlaceholderApplet from "./shared/PlaceholderApplet"; import PlaceholderApplet from "./shared/PlaceholderApplet";
import PlaylistCard from "./shared/PlaylistCard"; import PlaylistCard from "./shared/PlaylistCard";
import { TrackButton } from "./shared/TrackButton"; import { TrackButton, TrackButtonPlaylistObject } from "./shared/TrackButton";
import { TabMenuItem } from "./TabMenu"; import { TabMenuItem } from "./TabMenu";
function Library() { function Library() {
@ -32,21 +33,36 @@ function Library() {
export default Library; export default Library;
function UserPlaylists() { function UserPlaylists() {
const { data: userPlaylists, isError, isLoading, refetch } = useSpotifyQuery<SpotifyApi.PlaylistObjectSimplified[]>(QueryCacheKeys.userPlaylists, (spotifyApi) => const { data: userPagedPlaylists, isError, isLoading, refetch, isFetchingNextPage, hasNextPage, fetchNextPage } = useSpotifyInfiniteQuery<SpotifyApi.ListOfUsersPlaylistsResponse>(
spotifyApi.getUserPlaylists().then((userPlaylists) => { QueryCacheKeys.userPlaylists,
return userPlaylists.body.items; (spotifyApi, { pageParam }) =>
}) spotifyApi.getUserPlaylists({ limit: 20, offset: pageParam }).then((userPlaylists) => {
return userPlaylists.body;
}),
{
getNextPageParam(lastPage) {
if (lastPage.next) {
return lastPage.offset + lastPage.limit;
}
},
}
); );
const userPlaylists = userPagedPlaylists?.pages
?.map((playlist) => playlist.items)
.filter(Boolean)
.flat(1) as SpotifyApi.PlaylistObjectSimplified[];
return ( return (
<ScrollArea style="flex: 1; border: none;"> <GenreView
<View style="flex: 1; flex-direction: 'row'; flex-wrap: 'wrap'; justify-content: 'space-evenly'; width: 330px; align-items: 'center';"> heading="User Playlists"
<PlaceholderApplet error={isError} loading={isLoading} message="Failed querying spotify" reload={refetch} /> isError={isError}
{userPlaylists?.map((playlist, index) => ( isLoading={isLoading}
<PlaylistCard key={index + playlist.id} playlist={playlist} /> playlists={userPlaylists ?? []}
))} isLoadable={!isFetchingNextPage}
</View> refetch={refetch}
</ScrollArea> loadMore={hasNextPage ? ()=>fetchNextPage() : undefined}
/>
); );
} }
@ -80,19 +96,11 @@ function UserSavedTracks() {
} }
} }
const playlist: SpotifyApi.PlaylistObjectFull = { const playlist: TrackButtonPlaylistObject = {
collaborative: false, collaborative: false,
description: "User Playlist", description: "User Playlist",
tracks: { tracks: {
items: [userTracks ?? []].map( items: userTracks ?? [],
(userTrack) =>
(({
...userTrack,
added_by: "Me",
is_local: false,
added_at: Date.now(),
} as unknown) as SpotifyApi.PlaylistTrackObject)
),
limit: 20, limit: 20,
href: "", href: "",
next: "", next: "",
@ -101,10 +109,9 @@ function UserSavedTracks() {
total: 20, total: 20,
}, },
external_urls: { spotify: "" }, external_urls: { spotify: "" },
followers: { href: null, total: 2 },
href: "", href: "",
id: userSavedPlaylistId, id: userSavedPlaylistId,
images: [], images: [{ url: "https://facebook.com/img.jpeg" }],
name: "User saved track", 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 } }, owner: { external_urls: { spotify: "" }, href: "", id: "Me", type: "user", uri: "spotify:user:me", display_name: "User", followers: { href: null, total: 0 } },
public: false, public: false,

View File

@ -5,26 +5,27 @@ import playerContext from "../../context/playerContext";
import { msToMinAndSec } from "../../helpers/msToMin:sec"; import { msToMinAndSec } from "../../helpers/msToMin:sec";
import useTrackReaction from "../../hooks/useTrackReaction"; import useTrackReaction from "../../hooks/useTrackReaction";
import { heart, heartRegular, pause, play } from "../../icons"; import { heart, heartRegular, pause, play } from "../../icons";
import { audioPlayer } from "../Player";
import IconButton from "./IconButton"; import IconButton from "./IconButton";
export interface TrackButtonPlaylistObject extends SpotifyApi.PlaylistBaseObject {
follower?: SpotifyApi.FollowersObject;
tracks: SpotifyApi.PagingObject<SpotifyApi.SavedTrackObject | SpotifyApi.PlaylistTrackObject>;
}
export interface TrackButtonProps { export interface TrackButtonProps {
track: SpotifyApi.TrackObjectFull; track: SpotifyApi.TrackObjectFull;
playlist?: SpotifyApi.PlaylistObjectFull; playlist?: TrackButtonPlaylistObject;
index: number; index: number;
} }
export const TrackButton: FC<TrackButtonProps> = ({ track, index, playlist }) => { export const TrackButton: FC<TrackButtonProps> = ({ track, index, playlist }) => {
const { reactToTrack, isFavorite } = useTrackReaction(); const { reactToTrack, isFavorite } = useTrackReaction();
const { currentPlaylist, setCurrentPlaylist, setCurrentTrack, currentTrack } = useContext(playerContext); const { currentPlaylist, setCurrentPlaylist, setCurrentTrack, currentTrack } = useContext(playerContext);
const handlePlaylistPlayPause = (index?: number) => { const handlePlaylistPlayPause = (index: number) => {
if (currentPlaylist?.id !== playlist?.id && playlist?.tracks) { if (playlist && currentPlaylist?.id !== playlist.id) {
setCurrentPlaylist({ id: playlist.id, name: playlist.name, thumbnail: playlist.images[0].url, tracks: playlist.tracks.items }); const globalPlaylistObj = { id: playlist.id, name: playlist.name, thumbnail: playlist.images[0].url, tracks: playlist.tracks.items };
setCurrentTrack(playlist.tracks.items[index ?? 0].track); setCurrentPlaylist(globalPlaylistObj);
} else { setCurrentTrack(playlist.tracks.items[index].track);
audioPlayer.stop().catch((error) => console.error("Failed to stop audio player: ", error));
setCurrentTrack(undefined);
setCurrentPlaylist(undefined);
} }
}; };

View File

@ -1,21 +1,44 @@
import { useQueryClient } from "react-query"; import { InfiniteData, useQueryClient } from "react-query";
import { QueryCacheKeys } from "../conf"; import { QueryCacheKeys } from "../conf";
import useSpotifyInfiniteQuery from "./useSpotifyInfiniteQuery";
import useSpotifyMutation from "./useSpotifyMutation"; import useSpotifyMutation from "./useSpotifyMutation";
import useSpotifyQuery from "./useSpotifyQuery";
function usePlaylistReaction() { function usePlaylistReaction() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { data: favoritePlaylists } = useSpotifyQuery<SpotifyApi.PlaylistObjectSimplified[]>(QueryCacheKeys.userPlaylists, (spotifyApi) => const { data: favoritePagedPlaylists } = useSpotifyInfiniteQuery<SpotifyApi.ListOfUsersPlaylistsResponse>(QueryCacheKeys.userPlaylists, (spotifyApi, { pageParam }) =>
spotifyApi.getUserPlaylists().then((userPlaylists) => userPlaylists.body.items) spotifyApi.getUserPlaylists({ limit: 20, offset: pageParam }).then((userPlaylists) => {
return userPlaylists.body;
})
); );
const favoritePlaylists = favoritePagedPlaylists?.pages
.map((playlist) => playlist.items)
.filter(Boolean)
.flat(1) as SpotifyApi.PlaylistObjectSimplified[];
function updateFunction(playlist: SpotifyApi.PlaylistObjectSimplified, old?: InfiniteData<SpotifyApi.ListOfUsersPlaylistsResponse>): InfiniteData<SpotifyApi.ListOfUsersPlaylistsResponse> {
const obj: typeof old = {
pageParams: old?.pageParams ?? [],
pages:
old?.pages.map(
(oldPage, index): SpotifyApi.ListOfUsersPlaylistsResponse => {
const isPlaylistFavorite = isFavorite(playlist.id);
if (index === 0 && !isPlaylistFavorite) {
return { ...oldPage, items: [...oldPage.items, playlist] };
} else if (isPlaylistFavorite) {
return { ...oldPage, items: oldPage.items.filter((oldPlaylist) => oldPlaylist.id !== playlist.id) };
}
return oldPage;
}
) ?? [],
};
return obj;
}
const { mutate: reactToPlaylist } = useSpotifyMutation<{}, SpotifyApi.PlaylistObjectSimplified>( const { mutate: reactToPlaylist } = useSpotifyMutation<{}, SpotifyApi.PlaylistObjectSimplified>(
(spotifyApi, { id }) => spotifyApi[isFavorite(id) ? "unfollowPlaylist" : "followPlaylist"](id).then((res) => res.body), (spotifyApi, { id }) => spotifyApi[isFavorite(id) ? "unfollowPlaylist" : "followPlaylist"](id).then((res) => res.body),
{ {
onSuccess(_, playlist) { onSuccess(_, playlist) {
queryClient.setQueryData<SpotifyApi.PlaylistObjectSimplified[]>( queryClient.setQueryData<InfiniteData<SpotifyApi.ListOfUsersPlaylistsResponse>>(QueryCacheKeys.userPlaylists, (old) => updateFunction(playlist, old));
QueryCacheKeys.userPlaylists,
isFavorite(playlist.id) ? (old) => (old ?? []).filter((oldPlaylist) => oldPlaylist.id !== playlist.id) : (old) => [...(old ?? []), playlist]
);
}, },
} }
); );

View File

@ -7,17 +7,23 @@ import showError from "../helpers/showError";
function useSpotifyApiError(spotifyApi: SpotifyWebApi) { function useSpotifyApiError(spotifyApi: SpotifyWebApi) {
const { setAccess_token, isLoggedIn } = useContext(authContext); const { setAccess_token, isLoggedIn } = useContext(authContext);
return async (error: any | Error | TypeError) => { return async (error: any | Error | TypeError) => {
if ((error.message === "Unauthorized" && error.status === 401 && isLoggedIn) || (error.body.error.message === "No token provided" && error.body.error.status===401)) { const isUnauthorized = error.message === "Unauthorized";
const status401 = error.status === 401;
const bodyStatus401 = error.body.error.status === 401;
const noToken = error.body.error.message === "No token provided";
const expiredToken = error.body.error.message === "The access token expired";
if ((isUnauthorized && isLoggedIn && status401) || ((noToken || expiredToken) && bodyStatus401)) {
try { try {
console.log(chalk.bgYellow.blackBright("Refreshing Access token")) console.log(chalk.bgYellow.blackBright("Refreshing Access token"));
const { body:{access_token: refreshedAccessToken}} = await spotifyApi.refreshAccessToken(); const {
body: { access_token: refreshedAccessToken },
} = await spotifyApi.refreshAccessToken();
setAccess_token(refreshedAccessToken); setAccess_token(refreshedAccessToken);
} catch (error) { } catch (error) {
showError(error, "[Authorization Failure]: ") showError(error, "[Authorization Failure]: ");
} }
} }
}; };
} }
export default useSpotifyApiError; export default useSpotifyApiError;

View File

@ -1,8 +1,7 @@
import { useQueryClient } from "react-query"; import { InfiniteData, useQueryClient } from "react-query";
import { QueryCacheKeys } from "../conf"; import { QueryCacheKeys } from "../conf";
import useSpotifyInfiniteQuery from "./useSpotifyInfiniteQuery"; import useSpotifyInfiniteQuery from "./useSpotifyInfiniteQuery";
import useSpotifyMutation from "./useSpotifyMutation"; import useSpotifyMutation from "./useSpotifyMutation";
import useSpotifyQuery from "./useSpotifyQuery";
function useTrackReaction() { function useTrackReaction() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@ -13,14 +12,31 @@ function useTrackReaction() {
?.map((page) => page.items) ?.map((page) => page.items)
.filter(Boolean) .filter(Boolean)
.flat(1) as SpotifyApi.SavedTrackObject[] | undefined; .flat(1) as SpotifyApi.SavedTrackObject[] | undefined;
function updateFunction(track: SpotifyApi.SavedTrackObject, old?: InfiniteData<SpotifyApi.UsersSavedTracksResponse>): InfiniteData<SpotifyApi.UsersSavedTracksResponse> {
const obj: typeof old = {
pageParams: old?.pageParams ?? [],
pages:
old?.pages.map(
(oldPage, index): SpotifyApi.UsersSavedTracksResponse => {
const isTrackFavorite = isFavorite(track.track.id);
if (index === 0 && !isTrackFavorite) {
return { ...oldPage, items: [...oldPage.items, track] };
} else if (isTrackFavorite) {
return { ...oldPage, items: oldPage.items.filter((oldTrack) => oldTrack.track.id !== track.track.id) };
}
return oldPage;
}
) ?? [],
};
return obj;
}
const { mutate: reactToTrack } = useSpotifyMutation<{}, SpotifyApi.SavedTrackObject>( const { mutate: reactToTrack } = useSpotifyMutation<{}, SpotifyApi.SavedTrackObject>(
(spotifyApi, { track }) => spotifyApi[isFavorite(track.id) ? "removeFromMySavedTracks" : "addToMySavedTracks"]([track.id]).then((res) => res.body), (spotifyApi, { track }) => spotifyApi[isFavorite(track.id) ? "removeFromMySavedTracks" : "addToMySavedTracks"]([track.id]).then((res) => res.body),
{ {
onSuccess(_, track) { onSuccess(_, track) {
queryClient.setQueryData<SpotifyApi.SavedTrackObject[]>( queryClient.setQueryData<InfiniteData<SpotifyApi.UsersSavedTracksResponse>>(QueryCacheKeys.userSavedTracks, (old) => updateFunction(track, old));
QueryCacheKeys.userSavedTracks,
isFavorite(track.track.id) ? (old) => (old ?? []).filter((oldTrack) => oldTrack.track.id !== track.track.id) : (old) => [...(old ?? []), track]
);
}, },
} }
); );