24
package-lock.json
generated
24
package-lock.json
generated
@@ -5461,6 +5461,11 @@
|
||||
"strip-eof": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"exenv": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
|
||||
"integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50="
|
||||
},
|
||||
"exit": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
|
||||
@@ -11347,6 +11352,17 @@
|
||||
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
||||
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
|
||||
},
|
||||
"react-modal": {
|
||||
"version": "3.11.2",
|
||||
"resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.11.2.tgz",
|
||||
"integrity": "sha512-o8gvvCOFaG1T7W6JUvsYjRjMVToLZgLIsi5kdhFIQCtHxDkA47LznX62j+l6YQkpXDbvQegsDyxe/+JJsFQN7w==",
|
||||
"requires": {
|
||||
"exenv": "^1.2.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"react-lifecycles-compat": "^3.0.0",
|
||||
"warning": "^4.0.3"
|
||||
}
|
||||
},
|
||||
"react-router": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
|
||||
@@ -13835,6 +13851,14 @@
|
||||
"makeerror": "1.0.x"
|
||||
}
|
||||
},
|
||||
"warning": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
|
||||
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"watchpack": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.2.tgz",
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
"react-dom": "^16.13.1",
|
||||
"react-icons": "^3.10.0",
|
||||
"react-json-view": "^1.19.1",
|
||||
"react-modal": "^3.11.2",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "3.4.1",
|
||||
"react-tabs": "^3.1.1",
|
||||
|
||||
@@ -1,7 +1,160 @@
|
||||
import React from "react";
|
||||
import React, {useState} from "react";
|
||||
import Modal from 'react-modal';
|
||||
import {Button} from './Button';
|
||||
import './AddToList.scss';
|
||||
import {useStateValue} from "../services/State";
|
||||
import {wait} from "../services/utils";
|
||||
import {AiFillStar, AiOutlineStar} from "react-icons/all";
|
||||
import {add_to_list} from "../services/list_service";
|
||||
|
||||
const Star = (props) => {
|
||||
return (
|
||||
<div onClick={props.onClick}>
|
||||
{props.selected ?
|
||||
<AiFillStar className={'star selected'}/> :
|
||||
<AiOutlineStar className={'star'}/>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Stars = (props) => {
|
||||
const getSelected = (i) => {
|
||||
return i <= props.stars
|
||||
}
|
||||
|
||||
const handleStarClick = (i) => () => {
|
||||
if (props.stars === i) {
|
||||
props.onChange(0);
|
||||
} else {
|
||||
props.onChange(i);
|
||||
}
|
||||
}
|
||||
|
||||
const stars = [
|
||||
<Star key={1} onClick={handleStarClick(1)} selected={getSelected(1)}/>,
|
||||
<Star key={2} onClick={handleStarClick(2)} selected={getSelected(2)}/>,
|
||||
<Star key={3} onClick={handleStarClick(3)} selected={getSelected(3)}/>,
|
||||
<Star key={4} onClick={handleStarClick(4)} selected={getSelected(4)}/>,
|
||||
<Star key={5} onClick={handleStarClick(5)} selected={getSelected(5)}/>,
|
||||
]
|
||||
|
||||
return (
|
||||
<div className={'stars'}>
|
||||
{stars}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const Opinion = (props) => {
|
||||
const handleInput = (event) => {
|
||||
props.onChange(event.target.value);
|
||||
}
|
||||
|
||||
return (
|
||||
<textarea onChange={handleInput}/>
|
||||
);
|
||||
}
|
||||
|
||||
const AddForm = (props) => {
|
||||
const context = useStateValue()[0];
|
||||
const [state, setState] = useState(0);
|
||||
const [stars, setStars] = useState(0);
|
||||
const [opinion, setOpinion] = useState('');
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
const type = props.type;
|
||||
const handleClose = props.onClose;
|
||||
|
||||
let title;
|
||||
|
||||
switch (type) {
|
||||
case 'artist':
|
||||
title = 'Comparte lo que te gusta de este artista';
|
||||
break;
|
||||
case 'release':
|
||||
title = 'Comparte lo que te gusta de este lanzamiento';
|
||||
break;
|
||||
case 'disc':
|
||||
title = 'Comparte lo que te gusta de este disco';
|
||||
break;
|
||||
case 'song':
|
||||
title = 'Comparte lo que te gusta de esta canción';
|
||||
break;
|
||||
default:
|
||||
title = 'Comparte lo que te gusta de esto';
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
add_to_list(context.user.access_token, JSON.parse(window.localStorage.getItem('user')).id,
|
||||
props.entity, props.type, []).then(() => {
|
||||
setState(1);
|
||||
}).catch((error) => {
|
||||
console.log('Request error', error.response)
|
||||
if(error.response.data.error){
|
||||
setState(2);
|
||||
setError(error.response.data.error);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const errorComponent = (<h3>{error}</h3>)
|
||||
|
||||
const doneComponent = (
|
||||
<h3>Agregado!</h3>
|
||||
)
|
||||
|
||||
const formComponent = (
|
||||
<div className={'add-form'}>
|
||||
<h3>{title}</h3>
|
||||
<div className="form">
|
||||
<div className="form-group">
|
||||
<label>Clasificacion</label>
|
||||
<Stars stars={stars} onChange={setStars}/>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label>Opinion</label>
|
||||
<Opinion opinion={opinion} onChange={setOpinion}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={'buttons'}>
|
||||
<Button className='primary' onClick={handleSave} text={'Agregar'}/>
|
||||
<Button onClick={handleClose} text={'Cerrar'}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
if(state === 0){
|
||||
return formComponent
|
||||
} else if (state === 1) {
|
||||
return doneComponent;
|
||||
} else if (state === 2) {
|
||||
return errorComponent;
|
||||
}
|
||||
}
|
||||
|
||||
export const AddToList = (props) => {
|
||||
return <Button className='add-to-list' text='Agregar a mi lista'/>
|
||||
const context = useStateValue()[0];
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
const handleOpenModal = () => setShow(true);
|
||||
const handleCloseModal = () => setShow(false);
|
||||
|
||||
Modal.setAppElement('#root');
|
||||
|
||||
let modalChildren = <AddForm type={props.type} entity={props.entity} onClose={handleCloseModal}/>
|
||||
|
||||
if (!context.user.auth) {
|
||||
modalChildren = <h3>Tienes que tener una cuenta para poder agregar a tu lista</h3>
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button className='add-to-list' onClick={handleOpenModal} text='Agregar a mi lista'/>
|
||||
<Modal className='modal' onRequestClose={handleCloseModal} isOpen={show}>
|
||||
{modalChildren}
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,61 @@
|
||||
.add-to-list {
|
||||
.modal {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 25%;
|
||||
transform: translate(-50%, -25%);
|
||||
|
||||
max-width: 75em;
|
||||
width: 90%;
|
||||
|
||||
margin: auto;
|
||||
|
||||
padding: 2em;
|
||||
background-color: var(--white);
|
||||
outline: var(--accent) var(--line-width) solid;
|
||||
}
|
||||
|
||||
.stars {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
|
||||
.star {
|
||||
margin: inherit;
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.star.selected {
|
||||
color: var(--accent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.add-form {
|
||||
.form {
|
||||
& > * {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
|
||||
textarea {
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
height: 10ch;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.buttons .button {
|
||||
margin-right: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.button.add-to-list {
|
||||
--color: var(--accent);
|
||||
--line-width: 3px;
|
||||
font-size: 1.2em;
|
||||
|
||||
@@ -5,6 +5,7 @@ export const Button = (props) => {
|
||||
const className = props.className;
|
||||
const text = props.text;
|
||||
const type = props.type ? props.type : 'button'
|
||||
const handleClick = props.onClick;
|
||||
|
||||
return <button className={`${type} ${className}`}>{text}</button>
|
||||
return <button className={`${type} ${className}`} onClick={handleClick}>{text}</button>
|
||||
}
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
&:active { --color: var(--accent) }
|
||||
}
|
||||
|
||||
.button.primary {
|
||||
--color: var(--accent)
|
||||
}
|
||||
|
||||
.link {
|
||||
display: inline;
|
||||
|
||||
@@ -22,7 +22,7 @@ export const Entity = (props) => {
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
<AddToList/>
|
||||
<AddToList type={props.type} entity={props.entity}/>
|
||||
</div>
|
||||
|
||||
{hasCover &&
|
||||
|
||||
@@ -40,7 +40,7 @@ const SocialNetwork = (props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<a href={link} target={'_blank'} className={'social_network'}>{icon}</a>
|
||||
<a href={link} rel='noopener noreferrer' target={'_blank'} className={'social_network'}>{icon}</a>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import axios from "axios";
|
||||
import {getUser} from "./user_service";
|
||||
|
||||
const current_host = `${window.location.protocol}//${window.location.host}`
|
||||
const oauth_url = `${process.env.REACT_APP_API_SERVER}/oauth`;
|
||||
@@ -99,14 +100,19 @@ export const auth = async (params) => {
|
||||
// en localstorage para ser utilizado mas tarde para renovar el access_token
|
||||
window.localStorage.clear()
|
||||
|
||||
const access_token = response.access_token;
|
||||
const refresh = response.refresh_token;
|
||||
const expires = new Date(new Date().getTime() + ((response.expires_in) * 1000))
|
||||
|
||||
window.localStorage.setItem('refresh_token', refresh);
|
||||
window.localStorage.setItem('expires', expires);
|
||||
|
||||
// Almacenar el usuario en localStorage
|
||||
const user = await getUser(access_token);
|
||||
window.localStorage.setItem('user', JSON.stringify(user))
|
||||
|
||||
// Finalmente se retorna el access_token para ser utilizado en el estado de la app
|
||||
return {status: 'done', access_token: response.access_token};
|
||||
return {status: 'done', access_token: access_token};
|
||||
}
|
||||
|
||||
export const logout = async (access_token) => {
|
||||
|
||||
19
src/services/list_service.js
Normal file
19
src/services/list_service.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import axios from 'axios';
|
||||
|
||||
let baseUrl = `${process.env.REACT_APP_API_SERVER}/api/lists`;
|
||||
|
||||
export function get_list(user_id) {
|
||||
return axios.get(`${baseUrl}/list/${user_id}/`)
|
||||
}
|
||||
|
||||
export function add_to_list(token, user_id, entity, entity_type, tags) {
|
||||
const post_data = {
|
||||
entity: entity,
|
||||
entity_type: entity_type,
|
||||
tags: tags
|
||||
};
|
||||
|
||||
return axios.post(`${baseUrl}/list/${user_id}/`, post_data, {
|
||||
headers: {'AUTHORIZATION': `Bearer ${token}`}
|
||||
});
|
||||
}
|
||||
@@ -26,3 +26,7 @@ export const toDuration = (miliseconds) => {
|
||||
return pad2(minutes) + ':' + pad2(seconds)
|
||||
}
|
||||
}
|
||||
|
||||
export const wait = (miliseconds) => {
|
||||
return new Promise(res => setTimeout(res, miliseconds))
|
||||
}
|
||||
|
||||
@@ -70,7 +70,10 @@ const Artist = (props) => {
|
||||
if (artist){
|
||||
return <Entity title={artist.name}
|
||||
subtitle={[artist.type, artist.country].filter(Boolean).join(' - ')}
|
||||
tags={artist.tags.map((tag) => (tag.name))}/>
|
||||
tags={artist.tags.map((tag) => (tag.name))}
|
||||
type={'artist'}
|
||||
entity={artist.id}
|
||||
/>
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,10 @@ const Disc = (props) => {
|
||||
)
|
||||
return <Entity title={disc.title}
|
||||
subtitle={subtitle}
|
||||
cover={<CoverArt disc={disc} popup={true} size={4}/>}/>
|
||||
cover={<CoverArt disc={disc} popup={true} size={4}/>}
|
||||
type={'disc'}
|
||||
entity={disc.id}
|
||||
/>
|
||||
}else {
|
||||
return <Fragment></Fragment>
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ const Release = (props) => {
|
||||
</Link>
|
||||
)
|
||||
return <Entity title={release.title} subtitle={subtitle}
|
||||
cover={<CoverArt release={release} popup={true} size={4}/>}/>
|
||||
cover={<CoverArt release={release} popup={true} size={4}/>} type={'release'} entity={release.id}/>
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ const Song = (props) => {
|
||||
subtitle = <span>[{toDuration(song.length)}]</span>
|
||||
}
|
||||
|
||||
return <Entity title={song.title} subtitle={subtitle}/>
|
||||
return <Entity title={song.title} subtitle={subtitle} type={'song'} entity={song.id}/>
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user