Skip to content

Commit

Permalink
Merge pull request #5 from NayanUnni95/page
Browse files Browse the repository at this point in the history
add album page
  • Loading branch information
NayanUnni95 authored Dec 3, 2024
2 parents 31239d0 + ca0e77b commit a9aa36d
Show file tree
Hide file tree
Showing 10 changed files with 556 additions and 10 deletions.
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
# Spotify Client Web App

![Website](https://img.shields.io/website?url=https%3A%2F%2Fspotify-gilt-ten.vercel.app%2F)
<!-- ![Website](https://img.shields.io/website?url=https%3A%2F%2Fspotify-gilt-ten.vercel.app%2F) -->

![GitHub repo size](https://img.shields.io/github/repo-size/NayanUnni95/Spotify)

![GitHub License](https://img.shields.io/github/license/NayanUnni95/Spotify)
![GitHub repo size](https://img.shields.io/github/repo-size/NayanUnni95/Spotify) ![GitHub License](https://img.shields.io/github/license/NayanUnni95/Spotify)

This repository contains a Spotify Clone Web App built using ReactJS and the Spotify Web API. The app is designed for fetches data through a personal Spotify account. This project focuses on the frontend functionality, including data fetching, authentication, but does not include the music playback feature.

Expand Down
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
},
"dependencies": {
"axios": "^1.7.4",
"fast-average-color": "^9.4.0",
"prettier": "^3.4.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand Down
3 changes: 2 additions & 1 deletion src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Collection from './pages/Collection/Collection';
import Playlist from './pages/Playlist/Playlist';
import Genre from './pages/Genre/Genre';
import Artist from './pages/Artist/Artist';
import Album from './pages/Album/Album';

function App() {
return (
Expand All @@ -28,7 +29,7 @@ function App() {
<Route path="/collection/tracks" element={<Collection />} />
<Route path="/playlist/:playlistId" element={<Playlist />} />
<Route path="/artist/:artistId" element={<Artist />} />
<Route path="/album/:albumId" element={'Album Page'} />
<Route path="/album/:albumId" element={<Album />} />
<Route path="/track/:trackId" element={'Track Page'} />
<Route path="/user/:userId" element={'User Page'} />
<Route path="/genre/:genreId" element={<Genre />} />
Expand Down
2 changes: 1 addition & 1 deletion src/constants/constant.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const Playlist = '/me/playlists';
const PlaylistWithId = '/playlists';
const Artists = '/me/following?type=artist&limit=50';
const Several_Category = 'browse/categories';
const Several_Albums = '/albums/{id}';
const Several_Albums = '/albums';
const New_Release = '/browse/new-releases';
const Top_Items = '/me/top/{type(artists/tracks)}';
const Featured_Playlist = '/browse/featured-playlists';
Expand Down
13 changes: 13 additions & 0 deletions src/hooks/useAuth.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Cookies from 'universal-cookie';
import { instance as axios } from '../axios/configuration';
import { FastAverageColor } from 'fast-average-color';

export const useAuth = () => {
const fetchData = async (endpoint) => {
Expand Down Expand Up @@ -103,6 +104,17 @@ export const useAuth = () => {

return `${formattedMinutes}:${formattedSeconds}`;
};
const predictColor = (url, callback) => {
const fac = new FastAverageColor();
fac
.getColorAsync(url)
.then((color) => {
callback(color);
})
.catch((e) => {
console.log(e);
});
};
return {
fetchData,
spotifyAuthReturnParams,
Expand All @@ -111,5 +123,6 @@ export const useAuth = () => {
removeToken,
DateConverter,
msToTime,
predictColor,
};
};
191 changes: 191 additions & 0 deletions src/pages/Album/Album.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import React, { useState, useEffect, useRef } from 'react';
import styles from './Album.module.css';
import { Icon } from '../../assets/icons/Icons';
import { FaArrowLeft } from 'react-icons/fa6';
import { RiMore2Line } from 'react-icons/ri';
import { LuClock3 } from 'react-icons/lu';
import { IoPlay } from 'react-icons/io5';
import NavBar from '../../components/NavBar/NavBar';
import { Link, useNavigate, useParams } from 'react-router-dom';
import { useAuth } from '../../hooks/useAuth';
import { Several_Albums, Single_Artists } from '../../constants/constant';
import equ from '../../assets/images/equaliser-animated-green.gif';
import { ThreeDots } from 'react-loader-spinner';

function Album() {
const navigate = useNavigate();
const [cardHover, setCardHover] = useState(false);
const [albumData, setAlbumData] = useState(null);
const [albumOwner, setAlbumOwner] = useState(null);
const [visibleRows, setVisibleRows] = useState({});
const [bgColor, setBgColor] = useState('black');
const { fetchData, msToTime, predictColor } = useAuth();
const { albumId } = useParams();
const inputRef = useRef(null);

const handleClick = (index) => {
setVisibleRows((prevState) => ({
[index]: !prevState[index],
}));
};
useEffect(() => {
fetchData(`${Several_Albums}/${albumId}`)
.then((res) => {
setAlbumData(res);
predictColor(res.images[0].url, (result) => setBgColor(result.rgba));
fetchData(`${Single_Artists}/${res.artists[0].id}`)
.then((res) => {
setAlbumOwner(res);
})
.catch((err) => {
console.log(err);
});
})
.catch((err) => {
console.log(err);
});
}, [albumId]);
if (!albumData) {
return (
<div className={styles.collectionSection}>
<div className={styles.loaderSection}>
<ThreeDots visible={true} height="50" width="50" color="gray" />
</div>
</div>
);
}
return (
<div className={styles.collectionSection}>
<div
className={styles.innerSection}
style={{ backgroundColor: `${bgColor}` }}
>
<div className={styles.navigationBar}>
<NavBar />
</div>
<nav>
<div className={styles.navigationBtn}>
<button onClick={() => navigate(-1)}>
<FaArrowLeft size={20} color="black" />
</button>
</div>
<div className={styles.songManageSection}>
<div className={styles.searchBar}>
<button>
<Icon name="search" size={15} />
</button>
<input type="text" placeholder="Find in liked songs" />
</div>
<div className={styles.sortBtn}>
<button>sort</button>
</div>
</div>
</nav>
<header className={styles.header}>
<div className={styles.imgSection}>
<div>
<img
src={albumData && albumData.images[0].url}
alt={albumData && albumData.name}
/>
</div>
</div>
<div className={styles.detailsSection}>
<div className={styles.type}>
<span>{albumData && albumData.type}</span>
</div>
<div className={styles.title}>
<h1>{albumData && albumData.name}</h1>
</div>
{/* <div className={styles.desc}>
<h4>{albumData && albumData.description}</h4>
</div> */}
<div className={styles.desc}>
<h5 className={styles.name}>
<img
src={albumOwner && albumOwner.images[0].url}
alt={albumOwner && albumOwner.display_name}
/>
<Link to={albumOwner && `/artist/${albumOwner.id}`}>
<h3>{albumOwner && albumOwner.name}</h3>
</Link>
</h5>
<span className={styles.songCount}>
&nbsp;• {albumData.tracks.total} songs
</span>
</div>
</div>
</header>
<div className={styles.tableContainer}>
<hr />
<table>
<thead>
<tr>
<th className={styles.tableSongNoLabel}>#</th>
<th className={styles.tableSongTitleLabel}>title</th>
<th className={styles.tableSongDurationLabel}>
<LuClock3 />
</th>
</tr>
</thead>
<tbody>
{albumData.tracks.items.map((data, index) => {
return (
<tr key={index} onClick={() => handleClick(index)}>
<td className={styles.songNo}>
{cardHover ? (
<IoPlay />
) : (
<span
style={{
display: !visibleRows[index] ? 'inline' : 'none',
}}
>
{index + 1}
</span>
)}
<img
src={equ}
className={styles.equ}
style={{
display: visibleRows[index] ? 'inline' : 'none',
}}
ref={inputRef}
/>
</td>
<td className={styles.songMainData}>
<div className={styles.songDetails}>
<div className={styles.songName}>
<Link to={`/track/${data.id}`}>
<span>{data.name}</span>
</Link>
</div>
<div className={styles.artistName}>
{data.artists.map((obj, index) => {
return (
<Link to={`/artist/${obj.id}`} key={index}>
<span>{obj.name},</span>
</Link>
);
})}
</div>
</div>
</td>
<td className={styles.songDuration}>
<span>{msToTime(data.duration_ms)}</span>
</td>
<td className={styles.moreBtn}>
<RiMore2Line />
</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
</div>
);
}

export default Album;
Loading

0 comments on commit a9aa36d

Please sign in to comment.