add and delete playlists, spotify auth
This commit is contained in:
parent
e3615d0ccf
commit
84bbcc21fc
5
README.md
Normal file
5
README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[playlist manager](https://spotify.sarsoo.xyz)
|
||||||
|
==================
|
||||||
|
|
||||||
|
playlist managing web app acting as a front-end for the [pyfmframework](https://github.com/Sarsoo/pyspotframework) playlist engine
|
||||||
|
|
@ -27,15 +27,15 @@ def get_playlists():
|
|||||||
return jsonify({'error': 'not logged in'}), 401
|
return jsonify({'error': 'not logged in'}), 401
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/playlist', methods=['GET', 'POST', 'PUT'])
|
@blueprint.route('/playlist', methods=['GET', 'POST', 'PUT', 'DELETE'])
|
||||||
def get_playlist():
|
def playlist():
|
||||||
|
|
||||||
if 'username' in session:
|
if 'username' in session:
|
||||||
|
|
||||||
user_ref = database.get_user_doc_ref(session['username'])
|
user_ref = database.get_user_doc_ref(session['username'])
|
||||||
playlists = database.get_user_playlists_collection(user_ref.id)
|
playlists = database.get_user_playlists_collection(user_ref.id)
|
||||||
|
|
||||||
if request.method == 'GET':
|
if request.method == 'GET' or request.method == 'DELETE':
|
||||||
playlist_name = request.args.get('name', None)
|
playlist_name = request.args.get('name', None)
|
||||||
|
|
||||||
if playlist_name:
|
if playlist_name:
|
||||||
@ -47,8 +47,16 @@ def get_playlist():
|
|||||||
elif len(queried_playlist) > 1:
|
elif len(queried_playlist) > 1:
|
||||||
return jsonify({'error': 'multiple playlists found'}), 500
|
return jsonify({'error': 'multiple playlists found'}), 500
|
||||||
|
|
||||||
|
if request.method == "GET":
|
||||||
|
|
||||||
return jsonify(queried_playlist[0].to_dict()), 200
|
return jsonify(queried_playlist[0].to_dict()), 200
|
||||||
|
|
||||||
|
elif request.method == 'DELETE':
|
||||||
|
|
||||||
|
playlists.document(queried_playlist[0].id).delete()
|
||||||
|
|
||||||
|
return jsonify({"message": 'playlist deleted', "status": "success"}), 200
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return jsonify({"error": 'no name requested'}), 400
|
return jsonify({"error": 'no name requested'}), 400
|
||||||
|
|
||||||
@ -62,6 +70,7 @@ def get_playlist():
|
|||||||
playlist_name = request_json['name']
|
playlist_name = request_json['name']
|
||||||
playlist_parts = request_json.get('parts', None)
|
playlist_parts = request_json.get('parts', None)
|
||||||
playlist_id = request_json.get('id', None)
|
playlist_id = request_json.get('id', None)
|
||||||
|
playlist_shuffle = request_json.get('shuffle', None)
|
||||||
|
|
||||||
queried_playlist = [i for i in playlists.where(u'name', u'==', playlist_name).stream()]
|
queried_playlist = [i for i in playlists.where(u'name', u'==', playlist_name).stream()]
|
||||||
|
|
||||||
@ -70,18 +79,19 @@ def get_playlist():
|
|||||||
if len(queried_playlist) != 0:
|
if len(queried_playlist) != 0:
|
||||||
return jsonify({'error': 'playlist already exists'}), 400
|
return jsonify({'error': 'playlist already exists'}), 400
|
||||||
|
|
||||||
if playlist_parts is None or playlist_id is None:
|
# if playlist_id is None or playlist_shuffle is None:
|
||||||
return jsonify({'error': 'parts and id required'}), 400
|
# return jsonify({'error': 'parts and id required'}), 400
|
||||||
|
|
||||||
playlists.add({
|
playlists.add({
|
||||||
'name': playlist_name,
|
'name': playlist_name,
|
||||||
'parts': playlist_parts,
|
'parts': playlist_parts,
|
||||||
'playlist_id': playlist_id
|
'playlist_id': playlist_id,
|
||||||
|
'shuffle': playlist_shuffle
|
||||||
})
|
})
|
||||||
|
|
||||||
return jsonify({"message": 'playlist added', "status": "success"}), 200
|
return jsonify({"message": 'playlist added', "status": "success"}), 200
|
||||||
|
|
||||||
else:
|
elif request.method == 'POST':
|
||||||
|
|
||||||
if len(queried_playlist) == 0:
|
if len(queried_playlist) == 0:
|
||||||
return jsonify({'error': "playlist doesn't exist"}), 400
|
return jsonify({'error': "playlist doesn't exist"}), 400
|
||||||
@ -89,7 +99,7 @@ def get_playlist():
|
|||||||
if len(queried_playlist) > 1:
|
if len(queried_playlist) > 1:
|
||||||
return jsonify({'error': "multiple playlists exist"}), 500
|
return jsonify({'error': "multiple playlists exist"}), 500
|
||||||
|
|
||||||
if playlist_parts is None and playlist_id is None:
|
if playlist_parts is None and playlist_id is None and playlist_shuffle is None:
|
||||||
return jsonify({'error': "no chnages to make"}), 400
|
return jsonify({'error': "no chnages to make"}), 400
|
||||||
|
|
||||||
playlist_doc = playlists.document(queried_playlist[0].id)
|
playlist_doc = playlists.document(queried_playlist[0].id)
|
||||||
@ -102,10 +112,15 @@ def get_playlist():
|
|||||||
if playlist_id:
|
if playlist_id:
|
||||||
dic['playlist_id'] = playlist_id
|
dic['playlist_id'] = playlist_id
|
||||||
|
|
||||||
|
if playlist_shuffle is not None:
|
||||||
|
dic['shuffle'] = playlist_shuffle
|
||||||
|
|
||||||
playlist_doc.update(dic)
|
playlist_doc.update(dic)
|
||||||
|
|
||||||
return jsonify({"message": 'playlist updated', "status": "success"}), 200
|
return jsonify({"message": 'playlist updated', "status": "success"}), 200
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return jsonify({'error': 'not logged in'}), 401
|
return jsonify({'error': 'not logged in'}), 401
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ def auth():
|
|||||||
|
|
||||||
return redirect(urllib.parse.urlunparse(['https', 'accounts.spotify.com', 'authorize', '', params, '']))
|
return redirect(urllib.parse.urlunparse(['https', 'accounts.spotify.com', 'authorize', '', params, '']))
|
||||||
|
|
||||||
return redirect('/')
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/spotify/token')
|
@blueprint.route('/spotify/token')
|
||||||
@ -116,7 +116,7 @@ def token():
|
|||||||
|
|
||||||
return redirect('/app/settings/spotify')
|
return redirect('/app/settings/spotify')
|
||||||
|
|
||||||
return redirect('/')
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/spotify/deauth')
|
@blueprint.route('/spotify/deauth')
|
||||||
@ -134,4 +134,4 @@ def deauth():
|
|||||||
|
|
||||||
return redirect('/app/settings/spotify')
|
return redirect('/app/settings/spotify')
|
||||||
|
|
||||||
return redirect('/')
|
return redirect(url_for('index'))
|
||||||
|
@ -23,9 +23,5 @@
|
|||||||
|
|
||||||
<div id="react"></div>
|
<div id="react"></div>
|
||||||
<script src="{{ url_for('static', filename='js/app.bundle.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/app.bundle.js') }}"></script>
|
||||||
|
|
||||||
<footer>
|
|
||||||
<a href="https://github.com/Sarsoo/spotify-web">view source code</a>
|
|
||||||
</footer>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -30,7 +30,7 @@
|
|||||||
<p class="center-text">password<br><input type="password" name="password" class="full-width"></p>
|
<p class="center-text">password<br><input type="password" name="password" class="full-width"></p>
|
||||||
<p id="status" style="display: none; color: red" class="center-text"></p>
|
<p id="status" style="display: none; color: red" class="center-text"></p>
|
||||||
|
|
||||||
<button class="full-width" onclick="handleLogin()" type="submit">go</button>
|
<button class="button full-width" onclick="handleLogin()" type="submit">go</button>
|
||||||
</form>
|
</form>
|
||||||
<script src="{{ url_for('static', filename='js/login.bundle.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/login.bundle.js') }}"></script>
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,7 +22,7 @@ class Index extends Component{
|
|||||||
}
|
}
|
||||||
|
|
||||||
render(){
|
render(){
|
||||||
return <h1 className="center-text">welcome to playlist manager!</h1>;
|
return <h1 className="center-text text-no-select">welcome to playlist manager!</h1>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
93
src/js/Playlist/NewPlaylist.js
Normal file
93
src/js/Playlist/NewPlaylist.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
class NewPlaylist extends Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
name: '',
|
||||||
|
type: 'normal',
|
||||||
|
error: false,
|
||||||
|
errorText: null
|
||||||
|
}
|
||||||
|
this.handleInputChange = this.handleInputChange.bind(this);
|
||||||
|
this.handleSubmit = this.handleSubmit.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInputChange(event){
|
||||||
|
this.setState({
|
||||||
|
[event.target.name]: event.target.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit(event){
|
||||||
|
axios.get('/api/playlists')
|
||||||
|
.then((response) => {
|
||||||
|
var sameName = response.data.playlists.filter((i) => {i.name == this.state.name ? true : false});
|
||||||
|
if(sameName.length == 0){
|
||||||
|
axios.put('/api/playlist', {
|
||||||
|
name: this.state.name,
|
||||||
|
parts: [],
|
||||||
|
shuffle: false,
|
||||||
|
type: this.state.type,
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
this.setState({
|
||||||
|
error: true,
|
||||||
|
errorText: 'name already exists'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log(this.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
return (
|
||||||
|
<table className="app-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colSpan="2">
|
||||||
|
<h1 className="ui-text center-text">new playlist</h1>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<select className="full-width" name="type" onChange={this.handleInputChange}>
|
||||||
|
<option value="normal">normal</option>
|
||||||
|
<option value="recents">recents</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input
|
||||||
|
className="full-width"
|
||||||
|
name="name"
|
||||||
|
type="text"
|
||||||
|
value={this.state.name}
|
||||||
|
onChange={this.handleInputChange}
|
||||||
|
placeholder="name"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colSpan="2">
|
||||||
|
<button className="button full-width" onClick={this.handleSubmit}>create</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{ this.state.error &&
|
||||||
|
<tr>
|
||||||
|
<td colSpan="2">
|
||||||
|
<p className="full-width" style={{color: 'red'}}>{this.state.errorText}</p>
|
||||||
|
</td>
|
||||||
|
</tr>}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NewPlaylist;
|
@ -5,16 +5,19 @@ class PlaylistView extends Component{
|
|||||||
|
|
||||||
constructor(props){
|
constructor(props){
|
||||||
super(props);
|
super(props);
|
||||||
console.log(this.props.match.params.name);
|
|
||||||
this.state = {
|
this.state = {
|
||||||
name: this.props.match.params.name,
|
name: this.props.match.params.name,
|
||||||
parts: [],
|
parts: [],
|
||||||
error: false,
|
error: false,
|
||||||
error_text: null,
|
error_text: null,
|
||||||
add_part_value: ''
|
newPlaylistName: '',
|
||||||
|
shuffle: false
|
||||||
}
|
}
|
||||||
this.handleAddPart = this.handleAddPart.bind(this);
|
this.handleAddPart = this.handleAddPart.bind(this);
|
||||||
this.handleAddPartChange = this.handleAddPartChange.bind(this);
|
this.handleAddPartChange = this.handleAddPartChange.bind(this);
|
||||||
|
this.handleRemoveRow = this.handleRemoveRow.bind(this);
|
||||||
|
|
||||||
|
this.handleShuffleChange = this.handleShuffleChange.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(){
|
componentDidMount(){
|
||||||
@ -35,21 +38,46 @@ class PlaylistView extends Component{
|
|||||||
|
|
||||||
handleAddPartChange(event){
|
handleAddPartChange(event){
|
||||||
this.setState({
|
this.setState({
|
||||||
add_part_value: event.target.value
|
newPlaylistName: event.target.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleShuffleChange(event) {
|
||||||
|
this.setState({
|
||||||
|
shuffle: event.target.checked
|
||||||
|
});
|
||||||
|
axios.post('/api/playlist', {
|
||||||
|
name: this.state.name,
|
||||||
|
shuffle: event.target.checked
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAddPart(event){
|
handleAddPart(event){
|
||||||
var parts = this.state.parts;
|
var parts = this.state.parts;
|
||||||
parts.push(this.state.add_part_value);
|
parts.push(this.state.newPlaylistName);
|
||||||
|
this.setState({
|
||||||
|
parts: parts,
|
||||||
|
add_part_value: ''
|
||||||
|
});
|
||||||
|
axios.post('/api/playlist', {
|
||||||
|
name: this.state.name,
|
||||||
|
parts: parts
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleRemoveRow(id, event){
|
||||||
|
var parts = this.state.parts;
|
||||||
|
parts = parts.filter(e => e !== id);
|
||||||
this.setState({
|
this.setState({
|
||||||
parts: parts
|
parts: parts
|
||||||
});
|
});
|
||||||
axios.post('/api/playlist', {
|
axios.post('/api/playlist', {
|
||||||
name: this.state.name,
|
name: this.state.name,
|
||||||
parts: parts
|
parts: parts
|
||||||
}).then((response) => {
|
|
||||||
console.log(reponse);
|
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
});
|
});
|
||||||
@ -61,19 +89,33 @@ class PlaylistView extends Component{
|
|||||||
<table className="app-table max-width">
|
<table className="app-table max-width">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colSpan="2"><h1>{ this.state.name }</h1></th>
|
<th colSpan="2"><h1 className="text-no-select">{ this.state.name }</h1></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{ this.state.parts.map((part) => <Row part={ part } key={ part }/>) }
|
{ this.state.parts.map((part) => <Row part={ part } key={ part } handler={this.handleRemoveRow}/>) }
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" className="full-width" value={this.state.add_part_value} onChange={this.handleAddPartChange}></input>
|
<input type="text"
|
||||||
|
className="full-width"
|
||||||
|
value={this.state.newPlaylistName}
|
||||||
|
onChange={this.handleAddPartChange}
|
||||||
|
placeholder="new playlist"></input>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button className="button full-width" onClick={this.handleAddPart}>add</button>
|
<button className="button full-width" onClick={this.handleAddPart}>add</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="center-text ui-text text-no-select">
|
||||||
|
shuffle output?
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox"
|
||||||
|
checked={this.state.shuffle}
|
||||||
|
onChange={this.handleShuffleChange}></input>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
);
|
);
|
||||||
@ -88,8 +130,8 @@ class PlaylistView extends Component{
|
|||||||
function Row (props) {
|
function Row (props) {
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
<td className="ui-text center-text">{ props.part }</td>
|
<td className="ui-text center-text text-no-select">{ props.part }</td>
|
||||||
<td className="ui-text center-text">remove</td>
|
<td><button className="ui-text center-text button full-width" onClick={(e) => props.handler(props.part, e)}>remove</button></td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,22 @@
|
|||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
|
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
|
|
||||||
import PlaylistsView from "./PlaylistsView.js"
|
import PlaylistsView from "./PlaylistsView.js"
|
||||||
|
import NewPlaylist from "./NewPlaylist.js";
|
||||||
|
|
||||||
class Playlists extends Component {
|
class Playlists extends Component {
|
||||||
render(){
|
render(){
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ul className="navbar" style={{width: "100%"}}>
|
<ul className="navbar" style={{width: "100%"}}>
|
||||||
<li><Link to={`${this.props.match.url}/add`}>add</Link></li>
|
<li><Link to={`${this.props.match.url}/new`}>new</Link></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<Route path={`${this.props.match.url}/`} component={PlaylistsView} />
|
<Switch>
|
||||||
|
<Route exact path={`${this.props.match.url}/`} component={PlaylistsView} />
|
||||||
|
<Route path={`${this.props.match.url}/new`} component={NewPlaylist} />
|
||||||
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,8 @@ class PlaylistsView extends Component {
|
|||||||
isLoading: true
|
isLoading: true
|
||||||
}
|
}
|
||||||
this.getPlaylists();
|
this.getPlaylists();
|
||||||
|
this.handleRunPlaylist = this.handleRunPlaylist.bind(this);
|
||||||
|
this.handleDeletePlaylist = this.handleDeletePlaylist.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPlaylists(){
|
getPlaylists(){
|
||||||
@ -23,9 +25,27 @@ class PlaylistsView extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleRunPlaylist(name, event){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDeletePlaylist(name, event){
|
||||||
|
axios.delete('/api/playlist', { params: { name: name } })
|
||||||
|
.then((response) => {
|
||||||
|
this.getPlaylists();
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
const table = <div><Table playlists={this.state.playlists}/></div>;
|
const table = <div>
|
||||||
|
<Table playlists={this.state.playlists}
|
||||||
|
handleRunPlaylist={this.handleRunPlaylist}
|
||||||
|
handleDeletePlaylist={this.handleDeletePlaylist}/>
|
||||||
|
</div>;
|
||||||
|
|
||||||
const loadingMessage = <p className="center-text">loading...</p>;
|
const loadingMessage = <p className="center-text">loading...</p>;
|
||||||
|
|
||||||
return this.state.isLoading ? loadingMessage : table;
|
return this.state.isLoading ? loadingMessage : table;
|
||||||
@ -36,7 +56,10 @@ function Table(props){
|
|||||||
return (
|
return (
|
||||||
<table className="app-table max-width">
|
<table className="app-table max-width">
|
||||||
<tbody>
|
<tbody>
|
||||||
{ props.playlists.map((playlist) => <Row playlist={ playlist } key={ playlist.name }/>) }
|
{ props.playlists.map((playlist) => <Row playlist={ playlist }
|
||||||
|
handleRunPlaylist={props.handleRunPlaylist}
|
||||||
|
handleDeletePlaylist={props.handleDeletePlaylist}
|
||||||
|
key={ playlist.name }/>) }
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
);
|
);
|
||||||
@ -46,6 +69,8 @@ function Row(props){
|
|||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
<PlaylistLink playlist={props.playlist}/>
|
<PlaylistLink playlist={props.playlist}/>
|
||||||
|
<td style={{width: "100px"}}><button className="button" style={{width: "100px"}} onClick={(e) => props.handleRunPlaylist(props.playlist.name, e)}>run</button></td>
|
||||||
|
<td style={{width: "100px"}}><button className="button" style={{width: "100px"}} onClick={(e) => props.handleDeletePlaylist(props.playlist.name, e)}>delete</button></td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -62,15 +62,17 @@ class PlaylistManager extends Component {
|
|||||||
<div className="pad-9">
|
<div className="pad-9">
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/app" exact component={Index} />
|
<Route path="/app" exact component={Index} />
|
||||||
<Route path="/app/playlists" exact component={Playlists} />
|
<Route path="/app/playlists" component={Playlists} />
|
||||||
<Route path="/app/settings" render={(props) => <Settings {...props} spotify_linked={this.state.spotify_linked}/>} />
|
<Route path="/app/settings" component={Settings} />
|
||||||
{ this.state.type == 'admin' && <Route path="/app/admin" component={Admin} /> }
|
{ this.state.type == 'admin' && <Route path="/app/admin" component={Admin} /> }
|
||||||
<Route path='/app/playlist/:name' component={PlaylistView} />
|
<Route path='/app/playlist/:name' component={PlaylistView} />
|
||||||
<Route component={NotFound} />
|
<Route component={NotFound} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<footer>
|
||||||
|
<a href="https://github.com/Sarsoo/spotify-web">view source code</a>
|
||||||
|
</footer>
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -84,12 +84,12 @@ class ChangePassword extends Component {
|
|||||||
<table className="app-table max-width">
|
<table className="app-table max-width">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colSpan="2"><h1>change password</h1></th>
|
<th colSpan="2"><h1 className="text-no-select">change password</h1></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td className="ui-text center-text">current:</td>
|
<td className="ui-text center-text text-no-select">current:</td>
|
||||||
<td><input
|
<td><input
|
||||||
type="password"
|
type="password"
|
||||||
name="current"
|
name="current"
|
||||||
@ -98,7 +98,7 @@ class ChangePassword extends Component {
|
|||||||
className="full-width" /></td>
|
className="full-width" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td className="ui-text center-text">new:</td>
|
<td className="ui-text center-text text-no-select">new:</td>
|
||||||
<td><input
|
<td><input
|
||||||
type="password"
|
type="password"
|
||||||
name="new1"
|
name="new1"
|
||||||
@ -107,7 +107,7 @@ class ChangePassword extends Component {
|
|||||||
className="full-width" /></td>
|
className="full-width" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td className="ui-text center-text">new again:</td>
|
<td className="ui-text center-text text-no-select">new again:</td>
|
||||||
<td><input
|
<td><input
|
||||||
type="password"
|
type="password"
|
||||||
name="new2"
|
name="new2"
|
||||||
|
@ -15,7 +15,7 @@ class Settings extends Component {
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<Route path={`${this.props.match.url}/password`} component={ChangePassword} />
|
<Route path={`${this.props.match.url}/password`} component={ChangePassword} />
|
||||||
<Route path={`${this.props.match.url}/spotify`} render={(props) => <SpotifyLink {...props} spotify_linked={this.props.spotify_linked}/>} />
|
<Route path={`${this.props.match.url}/spotify`} component={SpotifyLink} />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -6,25 +6,33 @@ class SpotifyLink extends Component {
|
|||||||
constructor(props){
|
constructor(props){
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
spotify_linked: props.spotify_linked
|
spotify_linked: null,
|
||||||
|
isLoading: true
|
||||||
}
|
}
|
||||||
|
this.getUserInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
getUserInfo(){
|
getUserInfo(){
|
||||||
|
axios.get('/api/user')
|
||||||
|
.then((response) => {
|
||||||
|
this.setState({
|
||||||
|
spotify_linked: response.data.spotify_linked,
|
||||||
|
isLoading: false
|
||||||
|
})
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render(){
|
render(){
|
||||||
return (
|
const table =
|
||||||
<table className="app-table max-width">
|
<table className="app-table max-width">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><h1 className="ui-text center-text">spotify link status</h1></th>
|
<th><h1 className="ui-text center-text text-no-select">spotify link status</h1></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td className="ui-text center-text">
|
<td className="ui-text center-text text-no-select">
|
||||||
status: { this.state.spotify_linked ? "linked" : "unlinked" }
|
status: { this.state.spotify_linked ? "linked" : "unlinked" }
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -34,8 +42,11 @@ class SpotifyLink extends Component {
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>;
|
||||||
);
|
|
||||||
|
const loadingMessage = <p className="center-text text-no-select">loading...</p>;
|
||||||
|
|
||||||
|
return this.state.isLoading ? loadingMessage : table;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +70,14 @@ p {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type=text], input[type=password], select {
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #505050;
|
||||||
|
border: black;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
margin: 30px;
|
margin: 30px;
|
||||||
}
|
}
|
||||||
@ -273,7 +281,7 @@ ul.navbar {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
td {
|
td {
|
||||||
padding: $pad-px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,6 +289,14 @@ ul.navbar {
|
|||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-no-select {
|
||||||
|
cursor: default;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
p {
|
p {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
Loading…
Reference in New Issue
Block a user