Skip to content

Commit

Permalink
Added Top Artist section to profile
Browse files Browse the repository at this point in the history
  • Loading branch information
egimenos committed Sep 20, 2021
1 parent a6dec45 commit e018d9b
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 2 deletions.
29 changes: 29 additions & 0 deletions client/src/components/ArtistsGrid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { StyledGrid } from "../styles";

const ArtistsGrid = ({ artists }) => (
<>
{artists && artists.length ? (
<StyledGrid type="artist">
{artists.map((artist, i) => (
<li className="grid__item" key={i}>
<div className="grid__item__inner">
{artist.images[0] && (
<div className="grid__item__img">
<img src={artist.images[0].url} alt={artist.name} />
</div>
)}
<h3 className="grid__item__name overflow-ellipsis">
{artist.name}
</h3>
<p className="grid__item__label">Artist</p>
</div>
</li>
))}
</StyledGrid>
) : (
<p className="empty-notice">No artists available</p>
)}
</>
);

export default ArtistsGrid;
36 changes: 36 additions & 0 deletions client/src/components/SectionWrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Link } from "react-router-dom";
import { StyledSection } from "../styles";

const SectionWrapper = ({ children, title, seeAllLink, breadcrumb }) => (
<StyledSection>
<div className="section__inner">
<div className="section__top">
<h2 className="section__heading">
{breadcrumb && (
<span className="section__breadcrumb">
<Link to="/">Profile</Link>
</span>
)}
{title && (
<>
{seeAllLink ? (
<Link to={seeAllLink}>{title}</Link>
) : (
<span>{title}</span>
)}
</>
)}
</h2>
{seeAllLink && (
<Link to={seeAllLink} className="section__see-all">
See All
</Link>
)}
</div>

{children}
</div>
</StyledSection>
);

export default SectionWrapper;
2 changes: 2 additions & 0 deletions client/src/components/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as ArtistsGrid } from "./ArtistsGrid";
export { default as SectionWrapper } from "./SectionWrapper";
18 changes: 17 additions & 1 deletion client/src/pages/Profile.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { useState, useEffect } from "react";
import { catchErrors } from "../utils";
import {
getCurrentUserPlaylists,
getCurrentUserProfile,
getCurrentUserPlaylists,
getTopArtists,
} from "../services/spotify";
import { StyledHeader } from "../styles";
import { SectionWrapper, ArtistsGrid } from "../components";

const Profile = () => {
const [profile, setProfile] = useState(null);
const [playlists, setPlaylists] = useState(null);
const [topArtists, setTopArtists] = useState(null);

useEffect(() => {
const fetchData = async () => {
Expand All @@ -17,6 +20,9 @@ const Profile = () => {

const userPlaylists = await getCurrentUserPlaylists();
setPlaylists(userPlaylists.data);

const userTopArtist = await getTopArtists();
setTopArtists(userTopArtist.data);
};

catchErrors(fetchData());
Expand Down Expand Up @@ -53,6 +59,16 @@ const Profile = () => {
</div>
</div>
</StyledHeader>
{topArtists && (
<main>
<SectionWrapper
title="Top artists this month"
seeAllLink="/top-artists"
>
<ArtistsGrid artists={topArtists.items.slice(0, 10)} />
</SectionWrapper>
</main>
)}
</>
)}
</>
Expand Down
10 changes: 10 additions & 0 deletions client/src/services/spotify.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,14 @@ export const getCurrentUserPlaylists = (limit = 20) => {
return axios.get(`/me/playlists?limit=${limit}`);
};

/**
* Get a User's Top Artists and Tracks
* https://developer.spotify.com/documentation/web-api/reference/#endpoint-get-users-top-artists-and-tracks
* @param {string} time_range - 'short_term' (last 4 weeks) 'medium_term' (last 6 months) or 'long_term' (calculated from several years of data and including all new data as it becomes available). Defaults to 'short_term'
* @returns {Promise}
*/
export const getTopArtists = (time_range = "short_term") => {
return axios.get(`/me/top/artists?time_range=${time_range}`);
};

export const getCurrentUserProfile = () => axios.get("/me");
79 changes: 79 additions & 0 deletions client/src/styles/StyledGrid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import styled from "styled-components/macro";

const StyledGrid = styled.ul`
list-style: none;
margin: 0;
padding: 0;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
grid-gap: var(--spacing-sm);
@media (min-width: 768px) {
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
grid-gap: var(--spacing-lg);
}
.grid__item {
background-color: var(--near-black);
border-radius: var(--border-radius-subtle);
transition: background-color 0.3s ease;
cursor: default;
&:hover,
&:focus {
background-color: var(--dark-grey);
img {
box-shadow: 0 8px 24px rgb(0 0 0 / 50%);
}
}
a {
display: block;
width: 100%;
height: 100%;
&:hover,
&:focus {
text-decoration: none;
}
}
}
.grid__item__inner {
padding: var(--spacing-sm);
@media (min-width: 768px) {
padding: var(--spacing-md);
}
}
.grid__item__img {
position: relative;
padding-top: 100%;
margin: 0 auto var(--spacing-lg);
img {
position: absolute;
top: 0;
width: 100%;
height: 100%;
object-fit: cover;
background-color: var(--dark-grey);
border-radius: ${(props) => (props.type === "artist" ? "50%" : "2px")};
}
}
.grid__item__name {
margin: 0 0 var(--spacing-xxs);
font-size: var(--fz-md);
letter-spacing: normal;
}
.grid__item__label {
font-size: var(--fz-sm);
color: var(--light-grey);
}
`;

export default StyledGrid;
65 changes: 65 additions & 0 deletions client/src/styles/StyledSection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import styled from "styled-components/macro";

const StyledSection = styled.section`
&:first-of-type {
.section__inner {
padding-top: 0;
}
}
.section__inner {
width: 100%;
max-width: var(--site-max-width);
margin: 0 auto;
position: relative;
padding: var(--spacing-lg) var(--spacing-md);
@media (min-width: 768px) {
padding: var(--spacing-xl) var(--spacing-xxl);
}
}
.section__top {
display: flex;
justify-content: space-between;
align-items: stretch;
margin-bottom: var(--spacing-xl);
}
.section__heading {
display: flex;
margin: 0;
font-size: var(--fz-xxl);
}
.section__breadcrumb {
display: flex;
color: var(--light-grey);
&::after {
content: "/";
display: block;
margin: 0 var(--spacing-sm);
}
a {
&:hover,
&:focus {
color: var(--white);
}
}
}
.section__see-all {
display: flex;
align-items: flex-end;
text-transform: uppercase;
color: var(--light-grey);
font-size: var(--fz-xxs);
font-weight: 700;
letter-spacing: 0.1em;
padding-bottom: 2px;
}
`;

export default StyledSection;
2 changes: 2 additions & 0 deletions client/src/styles/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export { default as GlobalStyle } from "./GlobalStyle";
export { default as StyledGrid } from "./StyledGrid";
export { default as StyledHeader } from "./StyledHeader";
export { default as StyledSection } from "./StyledSection";
4 changes: 3 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ app.get("/login", (req, res) => {
const state = generateRandomString(16);
res.cookie(stateKey, state);

const scope = "user-read-private user-read-email";
const scope = ["user-read-private", "user-read-email", "user-top-read"].join(
" "
);
const queryParams = querystring.stringify({
client_id: CLIENT_ID,
response_type: "code",
Expand Down

0 comments on commit e018d9b

Please sign in to comment.