24
package-lock.json
generated
24
package-lock.json
generated
@@ -5461,6 +5461,11 @@
|
|||||||
"strip-eof": "^1.0.0"
|
"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": {
|
"exit": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
||||||
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
|
"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": {
|
"react-router": {
|
||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
|
||||||
@@ -13835,6 +13851,14 @@
|
|||||||
"makeerror": "1.0.x"
|
"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": {
|
"watchpack": {
|
||||||
"version": "1.7.2",
|
"version": "1.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.2.tgz",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
"react-dom": "^16.13.1",
|
"react-dom": "^16.13.1",
|
||||||
"react-icons": "^3.10.0",
|
"react-icons": "^3.10.0",
|
||||||
"react-json-view": "^1.19.1",
|
"react-json-view": "^1.19.1",
|
||||||
|
"react-modal": "^3.11.2",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-scripts": "3.4.1",
|
"react-scripts": "3.4.1",
|
||||||
"react-tabs": "^3.1.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 {Button} from './Button';
|
||||||
import './AddToList.scss';
|
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) => {
|
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);
|
--color: var(--accent);
|
||||||
--line-width: 3px;
|
--line-width: 3px;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export const Button = (props) => {
|
|||||||
const className = props.className;
|
const className = props.className;
|
||||||
const text = props.text;
|
const text = props.text;
|
||||||
const type = props.type ? props.type : 'button'
|
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) }
|
&:active { --color: var(--accent) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button.primary {
|
||||||
|
--color: var(--accent)
|
||||||
|
}
|
||||||
|
|
||||||
.link {
|
.link {
|
||||||
display: inline;
|
display: inline;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export const Entity = (props) => {
|
|||||||
</ul>
|
</ul>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<AddToList/>
|
<AddToList type={props.type} entity={props.entity}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{hasCover &&
|
{hasCover &&
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const SocialNetwork = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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 axios from "axios";
|
||||||
|
import {getUser} from "./user_service";
|
||||||
|
|
||||||
const current_host = `${window.location.protocol}//${window.location.host}`
|
const current_host = `${window.location.protocol}//${window.location.host}`
|
||||||
const oauth_url = `${process.env.REACT_APP_API_SERVER}/oauth`;
|
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
|
// en localstorage para ser utilizado mas tarde para renovar el access_token
|
||||||
window.localStorage.clear()
|
window.localStorage.clear()
|
||||||
|
|
||||||
|
const access_token = response.access_token;
|
||||||
const refresh = response.refresh_token;
|
const refresh = response.refresh_token;
|
||||||
const expires = new Date(new Date().getTime() + ((response.expires_in) * 1000))
|
const expires = new Date(new Date().getTime() + ((response.expires_in) * 1000))
|
||||||
|
|
||||||
window.localStorage.setItem('refresh_token', refresh);
|
window.localStorage.setItem('refresh_token', refresh);
|
||||||
window.localStorage.setItem('expires', expires);
|
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
|
// 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) => {
|
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)
|
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){
|
if (artist){
|
||||||
return <Entity title={artist.name}
|
return <Entity title={artist.name}
|
||||||
subtitle={[artist.type, artist.country].filter(Boolean).join(' - ')}
|
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 {
|
}else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,10 @@ const Disc = (props) => {
|
|||||||
)
|
)
|
||||||
return <Entity title={disc.title}
|
return <Entity title={disc.title}
|
||||||
subtitle={subtitle}
|
subtitle={subtitle}
|
||||||
cover={<CoverArt disc={disc} popup={true} size={4}/>}/>
|
cover={<CoverArt disc={disc} popup={true} size={4}/>}
|
||||||
|
type={'disc'}
|
||||||
|
entity={disc.id}
|
||||||
|
/>
|
||||||
}else {
|
}else {
|
||||||
return <Fragment></Fragment>
|
return <Fragment></Fragment>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ const Release = (props) => {
|
|||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
return <Entity title={release.title} subtitle={subtitle}
|
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 {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const Song = (props) => {
|
|||||||
subtitle = <span>[{toDuration(song.length)}]</span>
|
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