diff --git a/atoms/playlistAtom.js b/atoms/playlistAtom.js new file mode 100644 index 0000000..13386c8 --- /dev/null +++ b/atoms/playlistAtom.js @@ -0,0 +1,11 @@ +import { atom } from 'recoil' + +export const playlistState = atom({ + key: 'playlistState', + default: null, +}) + +export const playlistIdState = atom({ + key: 'playlistIdState', + default: '37i9dQZF1EUMDoJuT8yJsl', +}) diff --git a/components/Center.jsx b/components/Center.jsx new file mode 100644 index 0000000..79c301c --- /dev/null +++ b/components/Center.jsx @@ -0,0 +1,80 @@ +import { useSession } from 'next-auth/react' +import { ChevronDownIcon } from '@heroicons/react/outline' +import { useEffect, useState } from 'react' +import { shuffle } from 'lodash' +import { useRecoilState, useRecoilValue } from 'recoil' +import { playlistIdState, playlistState } from '../atoms/playlistAtom' +import useSpotify from '../hooks/useSpotify' +import Songs from './Songs' + +const colors = [ + 'from-indigo-500', + 'from-blue-500', + 'from-green-500', + 'from-red-500', + 'from-yellow-500', + 'from-pink-500', + 'from-purple-500', +] + +function Center() { + const spotifyApi = useSpotify() + const { data: session } = useSession() + const [color, setColor] = useState(null) + const playlistId = useRecoilValue(playlistIdState) + const [playlist, setPlaylist] = useRecoilState(playlistState) + + console.log('Playlist - ', playlist) + + useEffect(() => { + setColor(shuffle(colors).pop()) + }, [playlistId]) + + useEffect(() => { + spotifyApi + .getPlaylist(playlistId) + .then((data) => setPlaylist(data.body)) + .catch((error) => console.log(error)) + }, [spotifyApi, playlistId]) + + return ( +
+
+
+ avatar +

{session?.user.name}

+ +
+
+
+ playlist image +
+

PLAYLIST

+

+ {playlist?.name} +

+
+
+ +
+ +
+
+ ) +} + +export default Center diff --git a/components/Sidebar.jsx b/components/Sidebar.jsx index ebd602e..1d13134 100644 --- a/components/Sidebar.jsx +++ b/components/Sidebar.jsx @@ -7,12 +7,36 @@ import { HeartIcon, } from '@heroicons/react/outline' import { signOut, useSession } from 'next-auth/react' +import { useEffect, useState } from 'react' +import { useRecoilState } from 'recoil' +import useSpotify from '../hooks/useSpotify' +import { playlistIdState } from '../atoms/playlistAtom' function Sidebar() { + const spotifyApi = useSpotify() const { data: session, status } = useSession() - console.log(session) + const [playlist, setPlaylist] = useState([]) + const [playlistId, setPlaylistId] = useRecoilState(playlistIdState) + //console.log('session sidebar - ', session) + // console.log(` YOu picked playlistId: `, playlistId) + + useEffect(() => { + if (spotifyApi.getAccessToken()) { + spotifyApi + .getUserPlaylists() + .then((data) => { + setPlaylist(data.body.items) + }) + .catch((err) => console.error(err)) + } + }, [session, spotifyApi]) + return ( -
+
) diff --git a/components/Song.jsx b/components/Song.jsx new file mode 100644 index 0000000..856b973 --- /dev/null +++ b/components/Song.jsx @@ -0,0 +1,30 @@ +import { millisToMinutesAndSeconds } from '../lib/time' + +function Song({ order, track }) { + // console.log(track) + return ( +
+
+

{order + 1}

+ track image +
+

{track.track.name}

+

{track.track.artists[0].name}

+
+
+
+

{track.track.album.name}

+

{millisToMinutesAndSeconds(track.track.duration_ms)}

+
+
+ ) +} + +export default Song diff --git a/components/Songs.jsx b/components/Songs.jsx new file mode 100644 index 0000000..6084f30 --- /dev/null +++ b/components/Songs.jsx @@ -0,0 +1,17 @@ +import { useRecoilValue } from 'recoil' +import { playlistState } from '../atoms/playlistAtom' +import Song from './Song' + +function Songs() { + const playlist = useRecoilValue(playlistState) + + return ( +
+ {playlist?.tracks.items.map((track, index) => ( + + ))} +
+ ) +} + +export default Songs diff --git a/hooks/useSpotify.js b/hooks/useSpotify.js new file mode 100644 index 0000000..5654645 --- /dev/null +++ b/hooks/useSpotify.js @@ -0,0 +1,27 @@ +import { signIn, useSession } from 'next-auth/react' +import { useEffect } from 'react' +import SpotifyWebApi from 'spotify-web-api-node' +// import spotifyApi from '../lib/spotify' + +const spotifyApi = new SpotifyWebApi({ + clientId: process.env.NEXT_PUBLIC_CLIENT_ID, + clientSecret: process.env.NEXT_PUBLIC_CLIENT_SECRET, +}) + +function useSpotify() { + const { data: session, status } = useSession() + + useEffect(() => { + if (session) { + // If refresh token attempt fails, direct user to login + if (session.error === 'RefreshAccessTokenError') { + signIn() + } + + spotifyApi.setAccessToken(session.user.accessToken) + } + }, [session]) + return spotifyApi +} + +export default useSpotify diff --git a/lib/time.js b/lib/time.js new file mode 100644 index 0000000..31c33c9 --- /dev/null +++ b/lib/time.js @@ -0,0 +1,10 @@ +import { constSelector } from 'recoil' + +export const millisToMinutesAndSeconds = (millis) => { + const minutes = Math.floor(millis / 60000) + const seconds = ((millis % 60000) / 1000).toFixed(0) + + return seconds == 60 + ? minutes + 1 + ':00' + : minutes + ':' + (seconds < 10 ? '0' : '') + seconds +} diff --git a/package-lock.json b/package-lock.json index b16bb91..73363b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,11 +6,14 @@ "": { "dependencies": { "@heroicons/react": "^1.0.5", + "lodash": "^4.17.21", "next": "latest", "next-auth": "^4.0.6", "react": "^17.0.2", "react-dom": "^17.0.2", - "spotify-web-api-node": "^5.0.2" + "recoil": "^0.5.2", + "spotify-web-api-node": "^5.0.2", + "tailwind-scrollbar-hide": "^1.1.7" }, "devDependencies": { "@types/node": "17.0.4", @@ -2071,6 +2074,11 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" }, + "node_modules/hamt_plus": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz", + "integrity": "sha1-4hwlKWjH4zsg9qGwlM2FeHomVgE=" + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -2648,6 +2656,11 @@ "node": ">=8" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -3637,6 +3650,25 @@ "node": ">=8.10.0" } }, + "node_modules/recoil": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.5.2.tgz", + "integrity": "sha512-Edibzpu3dbUMLy6QRg73WL8dvMl9Xqhp+kU+f2sJtXxsaXvAlxU/GcnDE8HXPkprXrhHF2e6SZozptNvjNF5fw==", + "dependencies": { + "hamt_plus": "1.0.2" + }, + "peerDependencies": { + "react": ">=16.13.1" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/regenerator-runtime": { "version": "0.13.4", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", @@ -4040,6 +4072,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tailwind-scrollbar-hide": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/tailwind-scrollbar-hide/-/tailwind-scrollbar-hide-1.1.7.tgz", + "integrity": "sha512-X324n9OtpTmOMqEgDUEA/RgLrNfBF/jwJdctaPZDzB3mppxJk7TLIDmOreEDm1Bq4R9LSPu4Epf8VSdovNU+iA==" + }, "node_modules/tailwindcss": { "version": "3.0.12", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.12.tgz", @@ -5990,6 +6027,11 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" }, + "hamt_plus": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz", + "integrity": "sha1-4hwlKWjH4zsg9qGwlM2FeHomVgE=" + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -6374,6 +6416,11 @@ "p-locate": "^4.1.0" } }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -7066,6 +7113,14 @@ "picomatch": "^2.2.1" } }, + "recoil": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.5.2.tgz", + "integrity": "sha512-Edibzpu3dbUMLy6QRg73WL8dvMl9Xqhp+kU+f2sJtXxsaXvAlxU/GcnDE8HXPkprXrhHF2e6SZozptNvjNF5fw==", + "requires": { + "hamt_plus": "1.0.2" + } + }, "regenerator-runtime": { "version": "0.13.4", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", @@ -7359,6 +7414,11 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, + "tailwind-scrollbar-hide": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/tailwind-scrollbar-hide/-/tailwind-scrollbar-hide-1.1.7.tgz", + "integrity": "sha512-X324n9OtpTmOMqEgDUEA/RgLrNfBF/jwJdctaPZDzB3mppxJk7TLIDmOreEDm1Bq4R9LSPu4Epf8VSdovNU+iA==" + }, "tailwindcss": { "version": "3.0.12", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.12.tgz", diff --git a/package.json b/package.json index ff027a3..0bbc6e0 100644 --- a/package.json +++ b/package.json @@ -7,11 +7,14 @@ }, "dependencies": { "@heroicons/react": "^1.0.5", + "lodash": "^4.17.21", "next": "latest", "next-auth": "^4.0.6", "react": "^17.0.2", "react-dom": "^17.0.2", - "spotify-web-api-node": "^5.0.2" + "recoil": "^0.5.2", + "spotify-web-api-node": "^5.0.2", + "tailwind-scrollbar-hide": "^1.1.7" }, "devDependencies": { "@types/node": "17.0.4", diff --git a/pages/_app.jsx b/pages/_app.jsx index 7a61480..0861324 100644 --- a/pages/_app.jsx +++ b/pages/_app.jsx @@ -1,10 +1,14 @@ import '../styles/globals.css' import { SessionProvider } from 'next-auth/react' +import { RecoilBridge, RecoilRoot } from 'recoil' function MyApp({ Component, pageProps: { session, ...pageProps } }) { + // console.log(`session _app - `, session) return ( - + + + ) } diff --git a/pages/_middleware.jsx b/pages/_middleware.jsx index a6022d9..9f35a9a 100644 --- a/pages/_middleware.jsx +++ b/pages/_middleware.jsx @@ -8,7 +8,7 @@ export async function middleware(req) { const token = await getToken({ req, secret }) const { pathname } = req.nextUrl - console.log(pathname) + //console.log(pathname) if (token && pathname === '/login') { return NextResponse.redirect('/') diff --git a/pages/index.jsx b/pages/index.jsx index 294cfef..86e84c5 100644 --- a/pages/index.jsx +++ b/pages/index.jsx @@ -1,14 +1,14 @@ import { getSession } from 'next-auth/react' import Head from 'next/head' import Sidebar from '../components/Sidebar' +import Center from '../components/Center' export default function Home() { return (
-
- {/* Side bar */} +
- {/* Center */} +
{/* Player */}
@@ -16,10 +16,10 @@ export default function Home() { ) } -// export async function getServerSideProps(context) { -// return { -// props: { -// session: await getSession(context), -// }, -// } -// } +export async function getServerSideProps(context) { + return { + props: { + session: await getSession(context), + }, + } +} diff --git a/tailwind.config.js b/tailwind.config.js index 4cd6138..880e5e7 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,10 +1,10 @@ module.exports = { - content: [ - './pages/**/*.{js,ts,jsx,tsx}', - './components/**/*.{js,ts,jsx,tsx}', - ], - theme: { - extend: {}, - }, - plugins: [], + content: [ + './pages/**/*.{js,ts,jsx,tsx}', + './components/**/*.{js,ts,jsx,tsx}', + ], + theme: { + extend: {}, + }, + plugins: [require('tailwind-scrollbar-hide')], }