Service tiles; Start handling disabled services

This commit is contained in:
James Barnsley
2017-08-03 21:26:33 +12:00
parent 6d32e83cac
commit 123fe51289
15 changed files with 194 additions and 196 deletions

View File

@ -50,13 +50,6 @@ class App extends React.Component{
this.props.coreActions.startServices()
this.props.coreActions.getBroadcasts()
if (this.props.spotify_authorized){
// TODO: remove this so we don't tap out our API limits before we even get started
// Perhaps fire this on demand? Context menu, playlists loading or AddToPlaylistModal
this.props.spotifyActions.getAllLibraryPlaylists();
}
// when we navigate to a new route
hashHistory.listen( location => {

1
src/js/bootstrap.js vendored
View File

@ -60,6 +60,7 @@ var initialState = {
track: {}
},
spotify: {
enabled: true,
connected: false,
me: false,
autocomplete_results: {},

View File

@ -90,7 +90,7 @@ class Sidebar extends React.Component{
<Link className={this.linkClassName('settings')} to={global.baseURL+"settings"}>
<Icon name="cog" />
Settings
{ !this.props.mopidy_connected || !this.props.spotify_connected || !this.props.pusher_connected ? <FontAwesome name="exclamation-triangle" className="red-text pull-right" /> : null }
{ !this.props.mopidy_connected || (!this.props.spotify_connected && this.props.spotify_enabled) || !this.props.pusher_connected ? <FontAwesome name="exclamation-triangle" className="red-text pull-right" /> : null }
</Link>
</section>
@ -115,6 +115,7 @@ const mapStateToProps = (state, ownProps) => {
return {
mopidy_connected: state.mopidy.connected,
pusher_connected: state.pusher.connected,
spotify_enabled: state.spotify.enabled,
spotify_connected: state.spotify.connected,
spotify_authorized: state.spotify.authorized,
dragger: state.ui.dragger

View File

@ -20,7 +20,11 @@ const CoreMiddleware = (function(){
case 'CORE_START_SERVICES':
store.dispatch(mopidyActions.connect())
store.dispatch(pusherActions.connect())
store.dispatch(spotifyActions.connect())
if (store.getState().spotify.enabled){
store.dispatch(spotifyActions.connect())
}
next(action)
break

View File

@ -218,7 +218,7 @@ const MopidyMiddleware = (function(){
case 'MOPIDY_URISCHEMES':
var uri_schemes = action.data
var remove = ['http','https','mms','rtmp','rtmps','rtsp','sc','spotify']
var remove = ['http','https','mms','rtmp','rtmps','rtsp','sc']
// remove all our ignored types
for( var i = 0; i < remove.length; i++ ){
@ -279,10 +279,12 @@ const MopidyMiddleware = (function(){
// it's a spotify playlist that we haven't loaded
// we need to fetch via HTTP API to avoid timeout
} else if (helpers.uriSource(action.uri) == 'spotify'){
} else if (helpers.uriSource(action.uri) == 'spotify' && store.getState().spotify.enabled){
store.dispatch(uiActions.startProcess('MOPIDY_ENQUEUE_URIS', 'Fetching tracks'))
store.dispatch(spotifyActions.getAllPlaylistTracks(action.uri))
break
// Not in index, and Spotify HTTP not enabled, so just play it as-is
}
// fetch the playlist tracks via backend

View File

@ -109,7 +109,7 @@ const PusherMiddleware = (function(){
response.type = 'PUSHER_CONFIG'
store.dispatch(response)
if (response.config.spotify_username){
if (response.config.spotify_username && store.getState().spotify.enabled){
store.dispatch(spotifyActions.getUser('spotify:user:'+response.config.spotify_username))
}
var spotify = store.getState().spotify
@ -303,7 +303,7 @@ const PusherMiddleware = (function(){
case 'PUSHER_RADIO_STARTED':
case 'PUSHER_RADIO_CHANGED':
if (action.radio.enabled){
if (action.radio.enabled && store.getState().spotify.enabled){
store.dispatch(spotifyActions.resolveRadioSeeds(action.radio))
}
next(action)

View File

@ -171,11 +171,6 @@ function refreshToken( dispatch, getState ){
})
}
/**
* Actions and Action Creators
**/
export function setConfig( config ){
return {
type: 'SPOTIFY_SET_CONFIG',

View File

@ -16,10 +16,21 @@ const SpotifyMiddleware = (function(){
switch(action.type){
case 'SPOTIFY_CONNECT':
store.dispatch( spotifyActions.getMe() )
break
case 'SPOTIFY_CONNECTED':
var label = null
if (store.getState().spotify.me) label = store.getState().spotify.me.id
ReactGA.event({ category: 'Spotify', action: 'Connected', label: label })
// TODO: remove this so we don't tap out our API limits before we even get started
// Perhaps fire this on demand? Context menu, playlists loading or AddToPlaylistModal
if (store.getState().spotify_authorized){
store.dispatch(spotifyActions.getAllLibraryPlaylists())
}
next(action)
break
@ -56,10 +67,6 @@ const SpotifyMiddleware = (function(){
next(action)
break
case 'SPOTIFY_CONNECT':
store.dispatch( spotifyActions.getMe() )
break
case 'SPOTIFY_CREATE_PLAYLIST':
if( !store.getState().spotify.authorized ){
store.dispatch( uiActions.createNotification( "Must be logged in to Spotify to do that", 'bad' ) )

View File

@ -3,7 +3,6 @@ import ReactGA from 'react-ga'
var uiActions = require('./actions.js')
var mopidyActions = require('../mopidy/actions.js')
var spotifyActions = require('../spotify/actions.js')
var helpers = require('../../helpers.js')
const UIMiddleware = (function(){

View File

@ -141,12 +141,12 @@ class Artist extends React.Component{
return (
<div className="body about">
<div className="col w40 tiles">
<div className="col w40 tiles artist-stats">
{this.props.artist.images ? <div className="tile thumbnail-wrapper"><Thumbnail size="huge" canZoom images={this.props.artist.images} /></div> : null}
{this.props.artist.images_additional ? <div className="tile thumbnail-wrapper"><Thumbnail size="huge" canZoom images={this.props.artist.images_additional} /></div> : null}
{this.props.artist.followers ? <div className="tile"><span className="text"><FontAwesome name="users" />{this.props.artist.followers.total.toLocaleString() } followers</span></div> : null}
{this.props.artist.popularity ? <div className="tile"><span className="text"><FontAwesome name="fire" />{this.props.artist.popularity }% popularity</span></div> : null}
{this.props.artist.listeners ? <div className="tile"><span className="text"><FontAwesome name="headphones" />{ this.props.artist.listeners.toLocaleString() } listeners</span></div> : null }
{this.props.artist.followers ? <div className="tile"><span className="content"><FontAwesome name="users" />{this.props.artist.followers.total.toLocaleString() } followers</span></div> : null}
{this.props.artist.popularity ? <div className="tile"><span className="content"><FontAwesome name="fire" />{this.props.artist.popularity }% popularity</span></div> : null}
{this.props.artist.listeners ? <div className="tile"><span className="content"><FontAwesome name="headphones" />{ this.props.artist.listeners.toLocaleString() } listeners</span></div> : null }
</div>
<div className="col w60 biography">

View File

@ -88,42 +88,6 @@ class Settings extends React.Component{
this.props.pusherActions.setUsername(this.state.pusher_username)
}
renderConnectionStatus(service){
if (service == 'Spotify' && !this.props[service.toLowerCase()].authorized){
return (
<span className="red-text connection-status">
<FontAwesome name="exclamation-triangle" />
&nbsp;
Not connected
</span>
)
} else if (this.props[service.toLowerCase()].connected){
return (
<span className="green-text connection-status">
<FontAwesome name="check" />
&nbsp;
{service}
</span>
)
} else if (this.props[service.toLowerCase()].connecting){
return (
<span className="grey-text connection-status">
<FontAwesome name="circle-o-notch" spin />
&nbsp;
Connecting
</span>
)
} else {
return (
<span className="red-text connection-status">
<FontAwesome name="exclamation-triangle" />
&nbsp;
Not connected
</span>
)
}
}
renderSpotifyUser(){
var user = null
@ -186,25 +150,26 @@ class Settings extends React.Component{
var icon = null
var status = 'Unknown'
if (this.props[service].connecting){
icon = <span className="icon connecting"><FontAwesome name="plug" /></span>
status = 'Connecting'
} else if (service == 'spotify' && !this.props[service].authorized){
icon = <span className="icon warning"><FontAwesome name="check" /></span>
status = 'Connected (not authorized)'
if (this.props[service].enabled !== undefined && !this.props[service].enabled){
icon = <span className="icon"><FontAwesome fixedWidth name="power-off" /></span>
status = 'disabled'
} else if (this.props[service].connecting){
icon = <span className="icon"><FontAwesome fixedWidth name="plug" /></span>
status = 'connecting'
} else if (this.props[service].connected){
icon = <span className="icon connected"><FontAwesome name="check" /></span>
status = 'Connected'
icon = <span className="icon"><FontAwesome fixedWidth name="check" /></span>
status = 'connected'
} else {
icon = <span className="icon disconnected"><FontAwesome name="close" /></span>
status = 'Disconnected'
icon = <span className="icon disconnected"><FontAwesome fixedWidth name="close" /></span>
status = 'disconnected'
}
return (
<div className="service">
<h4>{service}</h4>
{icon}
<span className="text">{status}</span>
<div className={'service '+status}>
<span className="content">
<h4 className="title">{service}</h4>
<div className="status">{icon}&nbsp; {status}</div>
</span>
</div>
)
}
@ -276,6 +241,19 @@ class Settings extends React.Component{
<h4 className="underline">Spotify</h4>
<form>
<div className="field checkbox">
<div className="input">
<label>
<input
type="checkbox"
name="spotify_enabled"
checked={ this.props.spotify.enabled }
onChange={e => this.props.spotifyActions.setConfig({enabled: !this.props.spotify.enabled})}
/>
<span className="label">Enabled</span>
</label>
</div>
</div>
<div className="field">
<div className="name">Country</div>
<div className="input">

View File

@ -137,4 +137,93 @@
}
}
}
}
.tiles {
padding-bottom: 60px;
.tile {
color: $white;
text-align: center;
position: relative;
z-index: 1;
float: left;
overflow: hidden;
.content {
display: block;
position: absolute;
top: 50%;
left: 10%;
right: 10%;
vertical-align: top;
margin-top: -12px;
font-size: 16px;
z-index: 2;
}
&:last-child {
@include clearfix();
}
&:before {
content: '';
display: block;
width: 100%;
padding-bottom: 100%;
background: lighten($dark_grey,2%);
z-index: -2;
}
&.thumbnail-wrapper {
&:before {
display: none;
}
}
}
&.artist-stats {
.tile {
.fa {
display: block;
position: absolute;
top: 50%;
left: 10%;
right: 10%;
text-align: center;
margin-top: -27px;
font-size: 60px;
z-index: -1;
opacity: 0.1;
}
}
@include responsive( null, $bp_medium ){
.tile {
@include grid_item( 2 );
}
}
@include responsive( $bp_medium ){
padding-bottom: 20px;
.tile {
@include grid_item( 3 );
}
}
@include responsive( $bp_narrow ){
.tile {
.text {
font-size: 14px;
.fa {
font-size: 28px;
margin-top: -12px;
}
}
}
}
}
}

View File

@ -84,82 +84,3 @@
}
}
}
.tiles {
padding-bottom: 60px;
.tile {
color: $white;
text-align: center;
position: relative;
z-index: 1;
float: left;
overflow: hidden;
.text {
display: block;
position: absolute;
top: 50%;
left: 10%;
right: 10%;
vertical-align: top;
margin-top: -12px;
font-size: 16px;
z-index: 2;
.fa {
display: block;
position: absolute;
top: 50%;
left: 10%;
right: 10%;
text-align: center;
margin-top: -27px;
font-size: 60px;
z-index: -1;
opacity: 0.1;
}
}
&:before {
content: '';
display: block;
width: 100%;
padding-bottom: 100%;
background: lighten($dark_grey,2%);
z-index: -2;
}
&.thumbnail-wrapper {
&:before {
display: none;
}
}
}
@include responsive( null, $bp_medium ){
.tile {
@include grid_item( 2 );
}
}
@include responsive( $bp_medium ){
padding-bottom: 20px;
.tile {
@include grid_item( 3 );
}
}
@include responsive( $bp_narrow ){
.tile {
.text {
font-size: 14px;
.fa {
font-size: 28px;
margin-top: -12px;
}
}
}
}
}

View File

@ -47,11 +47,16 @@
background: $green;
}
&.broadcast {
&.broadcast,
&.info {
background: $yellow;
color: $black;
}
&.warning {
background: $orange;
}
&.bad {
background: $red;
}

View File

@ -1,53 +1,56 @@
.settings-view {
.services {
@include clearfix();
.service {
position: relative;
display: inline-block;
padding: 0 0 0 45px;
margin: 0 20px 10px 0;
min-width: 80px;
background: lighten($dark_grey,2%);
padding: 12px 15px;
width: 24%;
margin-right: 2%;
float: left;
border-radius: 3px;
box-sizing: border-box;
h4 {
padding: 0;
margin: 0 0 -3px 0;
&:nth-child(4n){
margin-right: 0;
}
&.disabled {
color: $grey;
}
&.connecting {
.status {
color: $grey;
}
}
&.connected {
.status {
color: $green;
}
}
&.disconnected {
.status {
color: $red;
}
}
.title {
text-transform: capitalize;
margin: 2px 0 5px 0;
}
.icon {
width: 35px;
height: 25px;
position: absolute;
top: 2px;
left: 0;
border-radius: 50%;
color: $white;
text-align: center;
padding-top: 10px;
&.connecting {
background: $mid_grey;
}
&.connected {
background: $green;
}
&.warning {
background: $orange;
}
&.disconnected {
background: $red;
}
}
.text {
.status {
text-transform: capitalize;
font-size: 14px;
}
}
}
.field.current-user {
.text {
padding: 14px 14px 14px 0px;