Skip to content

Commit

Permalink
🚧 getQueue
Browse files Browse the repository at this point in the history
  • Loading branch information
Bakhaw committed Dec 27, 2023
1 parent c2c967b commit 0b87301
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 32 deletions.
18 changes: 18 additions & 0 deletions app/api/getQueue/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { NextResponse } from "next/server";

export async function GET(req: Request) {
const { searchParams } = new URL(req.url);
const accessToken = searchParams.get("accessToken");

if (!accessToken) throw new Error("getQueue: accessToken not provided");

const url = `https://api.spotify.com/v1/me/player/queue`;

const result = await fetch(url, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
}).then((res) => res.json());

return NextResponse.json(result);
}
70 changes: 57 additions & 13 deletions components/Player/Controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,85 @@ import useSpotify from "@/hooks/useSpotify";
import useTrack from "@/hooks/useTrack";

const Controls = () => {
const { currentPlaybackState, hydratePlaybackState } = usePlayerContext();
const {
currentPlaybackState,
refreshPlaybackState,
setCurrentPlaybackState,
} = usePlayerContext();
const spotifyApi = useSpotify();
const currentTrack = useTrack(currentPlaybackState?.item?.id);

const accessToken = spotifyApi.getAccessToken();

async function getQueue() {
const res = await fetch(`/api/getQueue?accessToken=${accessToken}`);
const result = await res.json();

return {
currentlyPlaying: result.currently_playing,
queue: result.queue,
};
}

// TODO handle back button using queue
async function onBackwardButtonClick() {
await spotifyApi.skipToPrevious();

// set timeout is used to make sure the previous song has finished fetching
setTimeout(async () => {
await hydratePlaybackState();
await refreshPlaybackState();
}, 500);
}

async function onForwardButtonClick() {
await spotifyApi.skipToNext();
const { currentlyPlaying, queue } = await getQueue();
const nextTrack = queue[0];

// set timeout is used to make sure the next song has finished fetching
setTimeout(async () => {
await hydratePlaybackState();
}, 500);
setCurrentPlaybackState((state) => {
if (!state) return null;

return {
...state,
item: nextTrack,
is_playing: true,
progress_ms: 0,
};
});

await spotifyApi.skipToNext();
}

async function onTogglePlay() {
if (currentPlaybackState?.is_playing) {
setCurrentPlaybackState((state) => {
if (!state) return null;

return {
...state,
is_playing: false,
};
});

spotifyApi.pause();

setTimeout(async () => {
await hydratePlaybackState();
}, 500);
// setTimeout(async () => {
// await refreshPlaybackState();
// }, 500);
} else {
setCurrentPlaybackState((state) => {
if (!state) return null;

return {
...state,
is_playing: true,
};
});

spotifyApi.play();

setTimeout(async () => {
await hydratePlaybackState();
}, 500);
// setTimeout(async () => {
// await refreshPlaybackState();
// }, 500);
}
}
const color = useDominantColor(currentTrack?.album.images[0].url);
Expand Down
3 changes: 2 additions & 1 deletion components/Track/PlaybackControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const PlaybackControls: React.FC<PlaybackControlsProps> = ({

if (!order) return null;

const isCurrentTrack = track.id === currentTrackId;
const isCurrentTrackPlaying = track.id === currentTrackId && isPlaying;

return (
Expand Down Expand Up @@ -55,7 +56,7 @@ const PlaybackControls: React.FC<PlaybackControlsProps> = ({
</div>
)}

{!isCurrentTrackPlaying && (
{!isCurrentTrack && (
<span className="block w-5 group-hover:hidden">{order}</span>
)}
</div>
Expand Down
8 changes: 4 additions & 4 deletions context/PlayerContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import useSpotify from "@/hooks/useSpotify";

interface PlayerContext {
currentPlaybackState: SpotifyApi.CurrentPlaybackResponse | null;
hydratePlaybackState: () => Promise<void>;
refreshPlaybackState: () => Promise<void>;
setCurrentPlaybackState: Dispatch<
SetStateAction<SpotifyApi.CurrentPlaybackResponse | null>
>;
Expand All @@ -29,7 +29,7 @@ function PlayerContextProvider({ children }: { children: React.ReactNode }) {
const [currentPlaybackState, setCurrentPlaybackState] =
useState<SpotifyApi.CurrentPlaybackResponse | null>(null);

const hydratePlaybackState = async () => {
const refreshPlaybackState = async () => {
const { body } = await spotifyApi.getMyCurrentPlaybackState();

if (!body || !body.item) return;
Expand All @@ -39,7 +39,7 @@ function PlayerContextProvider({ children }: { children: React.ReactNode }) {

useEffect(() => {
if (spotifyApi.getAccessToken()) {
hydratePlaybackState();
refreshPlaybackState();
}
}, [spotifyApi, session]);

Expand All @@ -48,7 +48,7 @@ function PlayerContextProvider({ children }: { children: React.ReactNode }) {
value={{
currentPlaybackState,
setCurrentPlaybackState,
hydratePlaybackState,
refreshPlaybackState,
}}
>
{children}
Expand Down
20 changes: 15 additions & 5 deletions context/TimerContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const TimerContext = createContext<TimerContext | null>(null);
function TimerContextProvider({ children }: { children: React.ReactNode }) {
const spotifyApi = useSpotify();
const { data: session } = useSession();
const { currentPlaybackState, hydratePlaybackState } = usePlayerContext();
const { currentPlaybackState, setCurrentPlaybackState } = usePlayerContext();

const [progressMs, setProgressMs] = useState<number>(
currentPlaybackState?.progress_ms ?? 0
Expand Down Expand Up @@ -46,20 +46,30 @@ function TimerContextProvider({ children }: { children: React.ReactNode }) {
if (!currentPlaybackState?.item) return;

if (progressMs > currentPlaybackState.item.duration_ms) {
hydratePlaybackState();
setProgressMs(0);
} else {
setProgressMs(progressMs + 1000);
}
}, 1000);

return () => clearInterval(intervalId);
}, [progressMs]);
}, [currentPlaybackState, progressMs]);

// used to reset the progressMs when we play a new track
useEffect(() => {
setProgressMs(0);
}, [currentPlaybackState?.item?.id]);
if (currentPlaybackState?.progress_ms === 0) {
setProgressMs(0);

setCurrentPlaybackState((state) => {
if (!state) return null;

return {
...state,
progress_ms: 1,
};
});
}
}, [currentPlaybackState?.progress_ms, setCurrentPlaybackState]);

return (
<TimerContext.Provider
Expand Down
21 changes: 12 additions & 9 deletions hooks/usePlaybackControls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@ import useSpotify from "@/hooks/useSpotify";

const usePlaybackControls = () => {
const spotifyApi = useSpotify();
const {
setCurrentPlaybackState,
currentPlaybackState,
hydratePlaybackState,
} = usePlayerContext();
const { setCurrentPlaybackState, currentPlaybackState } = usePlayerContext();

const playSong = async (track: SpotifyApi.TrackObjectFull) => {
if (!track) return;
Expand All @@ -29,6 +25,17 @@ const usePlaybackControls = () => {
});
} else {
// play a new track
setCurrentPlaybackState((state) => {
if (!state) return null;

return {
...state,
is_playing: true,
item: track,
progress_ms: 0,
};
});

const { body: devices } = await spotifyApi.getMyDevices();

spotifyApi.play({
Expand All @@ -37,10 +44,6 @@ const usePlaybackControls = () => {
context_uri: track.album.uri,
offset: { uri: track.uri },
});

setTimeout(async () => {
await hydratePlaybackState();
}, 1000);
}
};

Expand Down

0 comments on commit 0b87301

Please sign in to comment.