Skip to content

Commit

Permalink
style : added skeleton loading animation in collection, search and pl…
Browse files Browse the repository at this point in the history
…aylist page
  • Loading branch information
NayanUnni95 committed Nov 26, 2024
1 parent f97066b commit 31239d0
Show file tree
Hide file tree
Showing 14 changed files with 269 additions and 22 deletions.
37 changes: 32 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Spotify Client Web App

![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)

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.

## Features
Expand Down Expand Up @@ -64,12 +70,33 @@ npm run dev

###### This will start the development server and open the app in your default web browser.

###### Format file using Prettier
### Developer Commands

###### Prettier:

```bash
npx prettier --write <filename>
```

###### Preview:

```bash
npm run preview
```

###### Production Build:

```bash
npm run build
```

###### Run Production Build:

```bash
npm install -g http-server;
http-server dist
```

### Usage

1. Authentication
Expand All @@ -82,10 +109,6 @@ npx prettier --write <filename>
- After logging in, you will be redirected back to the app.
- You can now view your playlists, track details, and user profile information.

<!-- 3. User Differentiation
- The app will display different features based on whether you are a normal or premium Spotify user. -->

### Acknowledgements

- [Spotify Web API](https://developer.spotify.com/documentation/web-api)
Expand All @@ -95,3 +118,7 @@ npx prettier --write <filename>
### License

This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for details.

```
```
16 changes: 16 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",
"prettier": "^3.4.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^5.2.1",
Expand Down
File renamed without changes.
43 changes: 43 additions & 0 deletions src/components/RowLoadingSkeleton/RowLoading.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import styles from './RowLoading.module.css';

function RowLoading() {
const cells = [1, 2, 3];

return (
<>
{cells.map((data, index) => {
return (
<tr key={index}>
<td className={styles.songNoSection}>
<div className={styles.songNoCell} />
</td>
<td className={styles.songMainDataSection}>
<div className={styles.songDetailsSection}>
<div className={styles.songNameSection}>
<div className={styles.songNameCell} />
</div>
<div className={styles.artistNameSection}>
<div className={styles.artistNameCell} />
</div>
</div>
</td>
<td className={styles.songAlbumNameSection}>
<div className={styles.albumNameSection}>
<div className={styles.albumNameCell} />
</div>
</td>
<td className={styles.songAddedTimeSection}>
<div className={styles.songAddedTimeCell} />
</td>
<td className={styles.songDurationSection}>
<div className={styles.songDurationCell} />
</td>
</tr>
);
})}
</>
);
}

export default RowLoading;
96 changes: 96 additions & 0 deletions src/components/RowLoadingSkeleton/RowLoading.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
.songNoSection {
padding: 0.5rem;
}
.songNoCell {
width: 100%;
height: 2rem;
border-radius: 0.3rem;
animation: skeleton-loading 1s linear infinite alternate;
}
.songMainDataSection {
padding: 0.5rem;
}
.songDetailsSection {
width: 100%;
height: 2.5rem;
margin: 0;
border-radius: 0.3rem;
display: flex;
flex-direction: column;
justify-content: center;
}
.songNameSection {
width: 100%;
}
.songNameCell {
width: 95%;
height: 0.8rem;
margin: 0.2rem;
border-radius: 0.2rem;
animation: skeleton-loading 1s linear infinite alternate;
}
.artistNameSection {
margin-top: 0;
}
.artistNameCell {
width: 80%;
height: 0.6rem;
margin: 0.2rem;
border-radius: 0.2rem;
animation: skeleton-loading 1s linear infinite alternate;
}
.songAlbumNameSection {
padding: 0.5rem;
}
.albumNameSection {
width: 100%;
}
.albumNameCell {
width: 100%;
height: 2rem;
border-radius: 0.3rem;
animation: skeleton-loading 1s linear infinite alternate;
}
.songAddedTimeSection {
padding: 0.5rem;
}
.songAddedTimeCell {
width: 100%;
height: 2rem;
border-radius: 0.3rem;
animation: skeleton-loading 1s linear infinite alternate;
}
.songDurationCell {
width: 100%;
height: 2rem;
border-radius: 0.3rem;
animation: skeleton-loading 1s linear infinite alternate;
}
@media only screen and (max-width: 768px) {
.songAddedTimeSection {
display: none;
}
.songDurationSection {
display: none;
}
}
@media only screen and (max-width: 425px) {
.songNoSection {
display: none;
}
.songAlbumNameSection {
display: none;
}
}
.skeleton-load {
opacity: 0.4;
animation: skeleton-loading 1s linear infinite alternate;
}
@keyframes skeleton-loading {
0% {
background-color: hsl(0, 0%, 31%);
}
100% {
background-color: hsl(0, 0%, 15%);
}
}
9 changes: 7 additions & 2 deletions src/components/SearchCategoryGrid/SearchCategoryGrid.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { Link } from 'react-router-dom';
import styles from './SearchCategoryGrid.module.css';

function SearchCategoryGrid({ category }) {
function SearchCategoryGrid({ category, lastItemRef }) {
return (
<div className={styles.categoryContainer}>
<div className={styles.innerContainer}>
Expand All @@ -11,8 +11,13 @@ function SearchCategoryGrid({ category }) {
</div>
<div className={styles.innerCategoryContainer}>
{category?.map((data, index) => {
const isLastIndex = index === category.length - 1;
return (
<div className={styles.categoryCard} key={index}>
<div
className={styles.categoryCard}
key={index}
ref={isLastIndex ? lastItemRef : null}
>
<Link to={`/genre/${data.id}`}>
<div>
<img src={data.icons[0].url} className={styles.cardImg} />
Expand Down
5 changes: 5 additions & 0 deletions src/constants/constant.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const AUTH_URL = 'https://accounts.spotify.com/authorize';
const Token_URL = 'https://accounts.spotify.com/api/token';
const Base_URL = 'https://api.spotify.com/v1';
const Liked_Songs = '/me/tracks?limit=50';
Expand Down Expand Up @@ -26,8 +27,11 @@ const User = '/users';
const Category_Playlist =
'/playlists'; /* '/browse/categories/{category_id}/playlists'
(Several_Category + Category_Playlist) */
const scope =
'user-read-private user-read-email playlist-modify-private playlist-modify-public playlist-read-private user-top-read user-library-read user-follow-read user-read-recently-played';

export {
AUTH_URL,
Token_URL,
Base_URL,
Liked_Songs,
Expand All @@ -53,4 +57,5 @@ export {
County_Best,
User,
Category_Playlist,
scope,
};
File renamed without changes.
6 changes: 4 additions & 2 deletions src/pages/Collection/Collection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import equ from '../../assets/images/equaliser-animated-green.gif';
import { DataContext } from '../../context/DataCacheContext';
import { ThreeDots } from 'react-loader-spinner';
import { CgProfile } from 'react-icons/cg';
import RowLoading from '../../components/RowLoadingSkeleton/RowLoading';

function Collection() {
const navigate = useNavigate();
Expand Down Expand Up @@ -94,7 +95,7 @@ function Collection() {
<div style={{ display: 'flex' }}>
<div className={styles.navigationBtn}>
<button onClick={() => navigate(-1)}>
<FaArrowLeft size={20} color="black" />
<FaArrowLeft size={20} color="#ffffff" />
</button>
</div>
<div className={styles.title}>
Expand Down Expand Up @@ -131,7 +132,7 @@ function Collection() {
</div>
<div className={styles.desc}>
<h5 className={styles.name}>
{profileData.images[0] ? (
{profileData && profileData.images[0] ? (
<img
src={profileData.images[0].url}
alt={profileData && profileData.display_name}
Expand Down Expand Up @@ -233,6 +234,7 @@ function Collection() {
</tr>
);
})}
{nextEndpoint && <RowLoading />}
</tbody>
</table>
</div>
Expand Down
20 changes: 15 additions & 5 deletions src/pages/Collection/Collection.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,6 @@ button {
.type {
color: #b8b8b8e0;
}
.title {
display: none;
margin-top: 1rem;
}
.title h1 {
font-size: x-large;
font-weight: bolder;
Expand Down Expand Up @@ -215,11 +211,22 @@ button {
cursor: not-allowed;
}
@media only screen and (max-width: 426px) {
.innerSection header {
padding: 0;
justify-content: center;
flex-direction: column;
align-items: center;
}
.detailsSection {
margin: 1rem;
}
.navigationBar {
display: none;
}
.imgSection div img {
display: none;
width: 8rem;
height: 8rem;
border-radius: 0.2rem;
}
.songManageSection {
display: none;
Expand All @@ -239,6 +246,9 @@ button {
.songDuration {
display: none;
}
.title {
display: none;
}
}
@media only screen and (min-width: 426px) {
.innerSection {
Expand Down
9 changes: 4 additions & 5 deletions src/pages/Login/Login.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ import React, { useState } from 'react';
import { FaCaretUp, FaCaretDown } from 'react-icons/fa';
import styles from './Login.module.css';
import SpotifyLogo from '../../assets/images/Spotify_Logo_Black.png';
import { AUTH_URL, scope } from '../../constants/constant';

function Login() {
const [show, setShow] = useState(false);
const config = () => {
const clientId = import.meta.env.VITE_CLIENT_ID;
const redirectUri = import.meta.env.VITE_HOME_URL;
const scope =
'user-read-private user-read-email playlist-modify-private playlist-modify-public playlist-read-private user-top-read user-library-read user-follow-read user-read-recently-played';
let url = 'https://accounts.spotify.com/authorize';
const clientId = import.meta.env.VITE_CLIENT_ID || null;
const redirectUri = import.meta.env.VITE_HOME_URL || null;
let url = AUTH_URL;
url += '?response_type=token';
url += '&client_id=' + encodeURIComponent(clientId);
url += '&redirect_uri=' + encodeURIComponent(redirectUri);
Expand Down
Loading

0 comments on commit 31239d0

Please sign in to comment.