Agregada vista de artista

This commit is contained in:
Daniel Cortes
2020-06-06 05:52:15 -04:00
parent 084845f347
commit 0af23a9a3a
7 changed files with 302 additions and 62 deletions

View File

@@ -1 +1 @@
Searchbar tendria que hacer callback de la query que se ejecuto, no ejecutarla el mismo
Transition para cosas a las que hago hover para hacer un efecto mas suave

71
src/components/Artist.jsx Normal file
View File

@@ -0,0 +1,71 @@
import React, {useEffect, useState} from 'react'
import {getArtist, getArtistDiscs} from "../services/entity_service";
import {CoverWithCaption} from './CoverArt';
import ReactJson from "react-json-view";
export const Discs = (props) => {
const discs = props.discs ? props.discs.discs : null;
const paginate = props.discs ? props.discs.paginate : null;
if (discs) {
return (
<div className='discs'>
<h2>Discos</h2>
<ul className={'discs-list'}>
{discs.map((disc, index) => {
return (
<div className='cover-container'>
<CoverWithCaption key={index} cover_art={disc.cover_art} alt={`Cover art del disco ${disc.title}`} caption={disc.title}/>
</div>
)
})}
</ul>
</div>
)
} else return <></>
}
export const Artist = (props) => {
const artist = props.artist;
if (artist) {
return (
<div className='artist'>
<div className='space-between'>
<div className='title'>
<h1>{artist.name}</h1>
<h4>{[artist.type, artist.country].filter(Boolean).join(' - ')}</h4>
</div>
<div>
<button className='button'>Agregar a mi Lista</button>
</div>
</div>
<ul className='tags'>
{artist.tags.map((tag, index) => (<li key={index}>{tag.name}</li>))}
</ul>
</div>
)
} else {
return <></>
}
}
export const ArtistView = (props) => {
const [artist, setArtist] = useState(null);
const [discs, setDiscs] = useState(null);
const mbid = props.match.params.mbid;
useEffect(() => {
if (mbid) {
getArtist(mbid).then((result) => setArtist(result));
getArtistDiscs(mbid, 15).then((result) => setDiscs(result));
}
}, [mbid])
return (
<div className='artist-view'>
<Artist artist={artist}/>
<Discs discs={discs}/>
{!mbid && <p>AHH</p>}
</div>
);
}

View File

@@ -0,0 +1,33 @@
import React, {Fragment, useState} from "react";
import {ReactComponent as DiscSVG} from "../svg/disc.svg";
export const CoverArt = (props) => {
const [loading, setLoading] = useState(true);
const handleLoad = () => setLoading(false)
if (props.cover_art) {
if (loading) {
return (
<Fragment>
<img src={props.cover_art.image} className={'coverart loading'} alt={props.alt} onLoad={handleLoad}/>
<div class={'coverart pulsating'}/>
</Fragment>
)
} else {
return <img src={props.cover_art.image} className={'coverart'} alt={props.alt}/>
}
} else {
return <DiscSVG className='coverart'/>
}
}
export const CoverWithCaption = (props) => {
return(
<figure className='cover-caption'>
<CoverArt cover_art={props.cover_art} alt={props.alt}/>
<figcaption>{props.caption}</figcaption>
</figure>
);
}

View File

@@ -4,31 +4,10 @@ import queryString from "query-string";
import {searchArtist, searchDisc, searchSong} from "../services/search_service";
import {SearchBar} from "./SearchBar";
import {Paginate} from "./Paginate";
import {CoverArt} from "./CoverArt";
import {Link} from "react-router-dom";
import {Tab, TabList, TabPanel, Tabs} from "react-tabs";
import {ReactComponent as DiscSVG} from "../svg/disc.svg";
const CoverArt = (props) => {
const [loading, setLoading] = useState(true);
const handleLoad = () => setLoading(false)
if (props.cover_art) {
if (loading) {
return (
<Fragment>
<img src={props.cover_art.image} className={'coverart loading'} alt={props.alt} onLoad={handleLoad}/>
<DiscSVG className='coverart'/>
</Fragment>
)
} else {
return <img src={props.cover_art.image} className={'coverart'} alt={props.alt}/>
}
} else {
return <DiscSVG className='coverart'/>
}
}
const SearchPlaceholder = (props) => {
return (
@@ -109,7 +88,9 @@ const SearchDisc = (props) => {
return (
<li className='disc'>
<Link to={`/disc/${disc.id}`}>
<CoverArt cover_art={disc.cover_art} alt={`Cover del disco: ${disc.title}`}/>
<div className={'cover-container'}>
<CoverArt cover_art={disc.cover_art} alt={`Cover del disco: ${disc.title}`}/>
</div>
<div className='description'>
<span>{disc.title}</span>
<span className='small'>{disc.artist[0].name}</span>

View File

@@ -9,6 +9,7 @@ import './styles/main.scss';
import {Nav} from "./components/Nav";
import {BrowserRouter, Switch, Route} from "react-router-dom";
import {ArtistView} from "./components/Artist";
const Main = (props) => {
const navigate = (query) => props.history.push(`/search?query=${query}`);
@@ -36,6 +37,7 @@ const App = () => (
<Nav/>
<Switch>
<Route path='/search/:who?' component={Search}/>
<Route path='/artist/:mbid?' component={ArtistView}/>
<Route exact path='/' component={Main}/>
<Route path='*' component={NoRoute}/>
</Switch>

View File

@@ -0,0 +1,16 @@
import axios from 'axios';
let baseUrl = `${process.env.REACT_APP_API_SERVER}/api/brainz`;
export async function getArtist(mbid) {
const url = `${baseUrl}/artist/${mbid}`;
const response = await axios.get(url);
return response.data
}
export async function getArtistDiscs(mbid, per_page=10) {
const url = `${baseUrl}/artist/${mbid}/discs?per_page=${per_page}`;
const response = await axios.get(url);
return response.data
}

View File

@@ -1,15 +1,21 @@
@import url('https://fonts.googleapis.com/css2?family=Work+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
/* Colorscheme */
/* Variables */
:root {
--white: hsl(0, 0%, 99%);
--gray-1: hsl(0, 0%, 95%);
--gray-2: hsl(0, 0%, 85%);
--gray-3: hsl(0, 0%, 80%);
--gray-4: hsl(0, 0%, 30%);
--black: hsl(0, 0%, 20%);
--white: hsl(20, 20%, 98%);
--gray-1: hsl(20, 20%, 95%);
--gray-2: hsl(20, 20%, 85%);
--gray-3: hsl(20, 20%, 80%);
--gray-4: hsl(20, 20%, 30%);
--black: hsl(20, 20%, 20%);
--accent: hsl(354, 81%, 56%);
--font-1: 4.209em;
--font-2: 3.157em;
--font-3: 2.369em;
--font-4: 1.777em;
--font-5: 1.333em;
--font-6: 0.75em;
}
/* Modificación básica de elementos*/
@@ -22,6 +28,7 @@ body {
overflow-y: scroll;
line-height: 1.65;
font-size: 100%;
background-color: var(--white);
}
p {
@@ -30,7 +37,7 @@ p {
input {
border: 1px var(--gray-2) solid;
padding: .3em;
padding: .3em .5em;
}
button {
@@ -60,18 +67,40 @@ h1, h2, h3, h4, h5 {
h1 {
margin-top: 0;
font-size: 4.209em;
font-size: var(--font-1);
}
h2 {font-size: 3.157em;}
h2 {font-size: var(--font-2);}
h3 {font-size: 2.369em;}
h3 {font-size: var(--font-3);}
h4 {font-size: 1.777em;}
h4 {font-size: var(--font-4);}
h5 {font-size: 1.333em;}
h5 {font-size: var(--font-5);}
small, .text_small {font-size: 0.75em;}
small, .text_small {font-size: var(--font-6);}
button.button {
border: 3px solid var(--accent);
background-color: var(--white);
padding: .5em 1em;
font-weight: 500;
font-size: var(--font-5);
color: var(--accent);
cursor: pointer;
&:hover {
background-color: var(--gray-1);
transition: .3s;
}
}
figure {
height: 100%;
width: 100%;
}
/* Navbar */
.nav {
@@ -169,6 +198,12 @@ ul.entity_list {
border-bottom: 2px var(--gray-1) solid;
margin-bottom: 1em;
.cover-container {
width: 200px;
height: 200px;
margin: 1em;
}
&:last-child {
margin-bottom: auto;
}
@@ -208,18 +243,6 @@ ul.entity_list {
height: 14em;
a {
.coverart {
object-fit: cover;
width: 200px;
height: 200px;
margin: 1em;
border: 1px solid var(--gray-2);
&.loading {
display: none;
}
}
.release-date {
display: inline-block;
font-size: .7em;
@@ -238,24 +261,126 @@ ul.entity_list {
}
}
.cover-caption {
height: 250px;
width: 250px;
figcaption {
position: relative;
display: none;
z-index: 2;
height: 0;
background-color: var(--accent);
color: var(--white);
font-weight: 600;
padding: 1em;
opacity: 0;
overflow: hidden;
}
&:hover {
figcaption{
display: block;
animation: slidein 300ms ease-in-out both;
@keyframes slidein {
to{
height: 80px;
transform: translateY(-80px);
opacity: 100;
}
}
}
}
}
.coverart {
object-fit: cover;
width: 100%;
height: 100%;
border: 1px solid var(--gray-2);
&.loading {
display: none;
}
}
/* Tabs */
ul.tabs {
display: flex;
align-items: stretch;
border-bottom: 2px var(--gray-1) solid;
margin: 1rem 0;
display: flex;
align-items: stretch;
border-bottom: 2px var(--gray-1) solid;
margin: 1rem 0;
li.tab {
padding: .5rem 1em;
margin-bottom: -2px;
cursor: pointer;
li.tab {
padding: .5rem 1em;
margin-bottom: -2px;
cursor: pointer;
&:hover {
&:hover {
border-bottom: 2px var(--gray-2) solid;
}
}
&.selected {
&.selected {
border-bottom: 2px var(--accent) solid;
}
}
}
/* Artists */
.artist-view {
.artist{
.title {
h1 { margin-bottom: 0}
h4 { margin-top: 0; margin-bottom: .5em }
max-width: 60%;
}
.button {
margin-bottom: 1em;
}
.tags {
display:flex;
flex-wrap: wrap;
li {
border-bottom: 3px solid var(--accent);
margin-right: 1em;
line-height: 1;
margin-bottom: .5em;
font-weight: 500;
}
margin-bottom: 3em;
}
}
.discs {
.discs-list {
display: grid;
grid-auto-rows: 250px;
gap: 30px;
justify-content: start;
align-content: start;
$grid-width: 250px;
$grid-gap: 16px;
@for $x from 1 to 5 {
@media (min-width: $grid-width * $x + $grid-gap * $x ) {
grid-template-columns: repeat($x, auto);
}
}
@media (min-width: $grid-width * 4 + $grid-gap * 4 ) {
justify-content: space-between;
align-content: space-between;
}
.cover-container {
width: 250px;
height: 250px;
}
}
}
}
@@ -282,3 +407,15 @@ ul.tabs {
*[hidden] {
visibility: hidden;
}
.space-between {
display: flex;
justify-content: space-between;
align-items: center;
@media (max-width: 767px) {
justify-content: flex-start;
align-items: flex-start;
flex-direction: column;
}
}