Send authorization via pusher (partial), need to fetch me on acceptance

This commit is contained in:
James Barnsley
2016-12-11 21:09:50 +13:00
parent c1ea65666c
commit ba5bd5c91d
11 changed files with 126 additions and 22 deletions

View File

@ -25,11 +25,11 @@ export default class AddToPlaylistModal extends React.Component{
return (
<div>
<h4>Add to playlist</h4>
<div className="playlists">
<div className="list playlists">
{
playlists.map( playlist => {
return (
<div className="playlist" key={playlist.uri} onClick={ () => this.playlistSelected(playlist.uri) }>
<div className="list-item playlist" key={playlist.uri} onClick={ () => this.playlistSelected(playlist.uri) }>
<FontAwesome className="source" name={helpers.sourceIcon(playlist.uri)} />
&nbsp;
<span className="name">{ playlist.name }</span>

View File

@ -8,10 +8,12 @@ import Icon from '../Icon'
import AddToPlaylistModal from './AddToPlaylistModal'
import CreatePlaylistModal from './CreatePlaylistModal'
import EditPlaylistModal from './EditPlaylistModal'
import SendAuthorizationModal from './SendAuthorizationModal'
import * as uiActions from '../../services/ui/actions'
import * as mopidyActions from '../../services/mopidy/actions'
import * as spotifyActions from '../../services/spotify/actions'
import * as pusherActions from '../../services/pusher/actions'
class Modal extends React.Component{
@ -32,6 +34,7 @@ class Modal extends React.Component{
{ this.props.modal.name == 'add_to_playlist' ? <AddToPlaylistModal uiActions={this.props.uiActions} playlists={this.props.playlists} tracks_uris={this.props.modal.data.tracks_uris} /> : null }
{ this.props.modal.name == 'create_playlist' ? <CreatePlaylistModal uiActions={this.props.uiActions} /> : null }
{ this.props.modal.name == 'edit_playlist' ? <EditPlaylistModal uiActions={this.props.uiActions} data={this.props.modal.data} /> : null }
{ this.props.modal.name == 'send_authorization' ? <SendAuthorizationModal uiActions={this.props.uiActions} pusherActions={this.props.pusherActions} data={this.props.modal.data} /> : null }
</div>
</div>
@ -52,6 +55,7 @@ const mapStateToProps = (state, ownProps) => {
const mapDispatchToProps = (dispatch) => {
return {
uiActions: bindActionCreators(uiActions, dispatch),
pusherActions: bindActionCreators(pusherActions, dispatch),
spotifyActions: bindActionCreators(spotifyActions, dispatch),
mopidyActions: bindActionCreators(mopidyActions, dispatch)
}

View File

@ -0,0 +1,65 @@
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { Link } from 'react-router'
import { createStore, bindActionCreators } from 'redux'
import * as uiActions from '../../services/ui/actions'
import * as helpers from '../../helpers'
import Icon from '../Icon'
class SendAuthorizationModal extends React.Component{
constructor(props){
super(props)
}
handleClick(e, connectionid){
e.preventDefault()
var data = {
recipients: [connectionid],
action: 'send_authorization',
data: this.props.authorization
}
this.props.pusherActions.instruct( 'broadcast', data )
this.props.uiActions.closeModal()
return false;
}
render(){
return (
<div>
<h4>Share Spotify authentication</h4>
<div className="list pusher-connection-list">
{
this.props.connections.map( (connection, index) => {
return (
<div className='list-item connection' key={connection.connectionid} onClick={ e => this.handleClick(e, connection.connectionid) }>
{ connection.username }
&nbsp;
<span className="grey-text">({ connection.ip })</span>
</div>
);
})
}
</div>
</div>
)
}
}
const mapStateToProps = (state, ownProps) => {
return {
authorization: state.spotify.authorization,
connections: state.pusher.connections
}
}
const mapDispatchToProps = (dispatch) => {
return {
uiActions: bindActionCreators(uiActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SendAuthorizationModal)

View File

@ -99,12 +99,12 @@ class SpotifyAuthenticationFrame extends React.Component{
render(){
return (
<div>
<span>
{ this.renderAuthorizeButton() }
&nbsp;&nbsp;
{ this.renderRefreshButton() }
<iframe src={this.state.frameUrl} style={{ display: 'none' }}></iframe>
</div>
</span>
);
}
}

View File

@ -13,21 +13,21 @@ export default class Thumbnail extends React.Component{
shouldComponentUpdate(nextProps, nextState){
// no images at all, and we already know it
if( !this.props.image &&
!this.props.images &&
!nextProps.image &&
!nextProps.images ){
return false
}
if(
typeof(nextProps.image) == 'undefined' &&
typeof(this.props.image) == 'undefined' &&
typeof(nextProps.images) == 'undefined' &&
typeof(this.props.images) == 'undefined'
) return false
// image changed
if( this.props.image && !nextProps.image ) return true
if( !this.props.image && nextProps.image ) return true
if( this.props.image && nextProps.image ) return true
if( this.props.image != nextProps.image ) return true
// images array changed
if( this.props.images && !nextProps.images ) return true
if( !this.props.images && nextProps.images ) return true
if( typeof(this.props.images) === 'undefined' && nextProps.images ) return true
if( this.props.images && typeof(nextProps.images) === 'undefined' ) return true
if( this.props.images.length != nextProps.images.length ) return true
// image item changed

View File

@ -12,7 +12,7 @@ const localstorageMiddleware = (function(){
// append our state to a global variable. This gives us access to debug the store at any point
window._store = store
//console.log(action)
console.log(action)
switch( action.type ){
@ -70,6 +70,7 @@ const localstorageMiddleware = (function(){
Object.assign(
spotify,{
authorized: true,
authorization: action.data,
access_token: action.data.access_token,
refresh_token: action.data.refresh_token,
token_expiry: action.data.token_expiry

View File

@ -43,10 +43,10 @@ export function getConnectionList(){
}
}
export function instruct( action, data = null ){
export function instruct( message_type, data = null ){
return {
type: 'PUSHER_INSTRUCT',
action: action,
message_type: message_type,
data: data
}
}

View File

@ -9,6 +9,7 @@ const PusherMiddleware = (function(){
// handle all manner of socket messages
const handleMessage = (ws, store, message) => {
console.log(message)
switch( message.action ){
default:
var name = 'unspecified'
@ -87,7 +88,7 @@ const PusherMiddleware = (function(){
break;
case 'PUSHER_INSTRUCT':
switch( action.action ){
switch( action.message_type ){
case 'query':
makeRequest( action.data )
break
@ -150,6 +151,15 @@ const PusherMiddleware = (function(){
var notification = new notification( title, options );
break;
case 'PUSHER_SEND_AUTHORIZATION':
if( window.confirm('Spotify authorization received. Do you want to import?') ){
store.dispatch({ type: 'SPOTIFY_AUTHORIZATION_GRANTED', data: action.data })
window.location.reload(true)
}else{
console.log('Authorization ignored')
}
break
// This action is irrelevant to us, pass it on to the next middleware
default:
return next(action);

View File

@ -13,6 +13,7 @@ import VersionManager from '../components/VersionManager'
import Header from '../components/Header'
import Thumbnail from '../components/Thumbnail'
import * as uiActions from '../services/ui/actions'
import * as pusherActions from '../services/pusher/actions'
import * as mopidyActions from '../services/mopidy/actions'
import * as spotifyActions from '../services/spotify/actions'
@ -108,6 +109,18 @@ class Settings extends React.Component{
}
}
renderSendAuthorizationButton(){
if( !this.props.spotify.authorized ) return null
return (
<button onClick={e => this.props.uiActions.openModal('send_authorization', {}) }>
<FontAwesome name="share-square-o" />
&nbsp;
Share authentication
</button>
)
}
render(){
return (
<div className="view settings-view">
@ -214,6 +227,8 @@ class Settings extends React.Component{
<div className="name">Authentication</div>
<div className="input">
<SpotifyAuthenticationFrame />
&nbsp;
{ this.renderSendAuthorizationButton() }
</div>
</div>
</form>
@ -278,6 +293,7 @@ const mapStateToProps = (state, ownProps) => {
const mapDispatchToProps = (dispatch) => {
return {
uiActions: bindActionCreators(uiActions, dispatch),
pusherActions: bindActionCreators(pusherActions, dispatch),
mopidyActions: bindActionCreators(mopidyActions, dispatch),
spotifyActions: bindActionCreators(spotifyActions, dispatch)

View File

@ -8,6 +8,7 @@ import Thumbnail from '../components/Thumbnail'
import PlaylistGrid from '../components/PlaylistGrid'
import FollowButton from '../components/FollowButton'
import LazyLoadListener from '../components/LazyLoadListener'
import Header from '../components/Header'
import * as helpers from '../helpers'
import * as mopidyActions from '../services/mopidy/actions'
@ -51,6 +52,9 @@ class User extends React.Component{
return (
<div className="view user-view">
<Header icon="play" title="User" />
<div className="intro">
<Thumbnail circle={true} size="medium" images={ this.props.user.images } />

View File

@ -66,18 +66,22 @@
}
}
.playlists {
.list {
@include clearfix();
.playlist {
.list-item {
float: left;
box-sizing: border-box;
display: block;
width: 49%;
padding: 16px 20px 16px 30px;
padding: 16px 18px;
border-bottom: 1px solid $mid_grey;
position: relative;
&.playlist {
padding-left: 30px;
}
.source {
position: absolute;
top: 18px;
@ -108,8 +112,8 @@
padding: 50px;
width: 80%;
.playlists {
.playlist {
.list {
.list-item {
float: none !important;
width: auto;
}