NPM issue with basetag; Basic styles

This commit is contained in:
James Barnsley
2016-10-24 21:26:01 +13:00
parent a89d87a960
commit 3f055df3f2
18 changed files with 251 additions and 93 deletions

40
npm-debug.log Normal file
View File

@ -0,0 +1,40 @@
0 info it worked if it ends with ok
1 verbose cli [ '/usr/bin/nodejs', '/usr/bin/npm', 'run', 'dev' ]
2 info using npm@2.15.8
3 info using node@v4.4.7
4 verbose run-script [ 'predev', 'dev', 'postdev' ]
5 info predev iris@1.0.0
6 info dev iris@1.0.0
7 verbose unsafe-perm in lifecycle true
8 info iris@1.0.0 Failed to exec dev script
9 verbose stack Error: iris@1.0.0 dev: `mkdir production & cp src/index.html production/index.html & cp -R src/fonts production/fonts & NODE_ENV=development webpack & sass src/scss/app.scss production/app.css`
9 verbose stack Exit status 65
9 verbose stack at EventEmitter.<anonymous> (/usr/lib/node_modules/npm/lib/utils/lifecycle.js:217:16)
9 verbose stack at emitTwo (events.js:87:13)
9 verbose stack at EventEmitter.emit (events.js:172:7)
9 verbose stack at ChildProcess.<anonymous> (/usr/lib/node_modules/npm/lib/utils/spawn.js:24:14)
9 verbose stack at emitTwo (events.js:87:13)
9 verbose stack at ChildProcess.emit (events.js:172:7)
9 verbose stack at maybeClose (internal/child_process.js:827:16)
9 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:211:5)
10 verbose pkgid iris@1.0.0
11 verbose cwd /var/www/iris
12 error Linux 3.16.0-4-amd64
13 error argv "/usr/bin/nodejs" "/usr/bin/npm" "run" "dev"
14 error node v4.4.7
15 error npm v2.15.8
16 error code ELIFECYCLE
17 error iris@1.0.0 dev: `mkdir production & cp src/index.html production/index.html & cp -R src/fonts production/fonts & NODE_ENV=development webpack & sass src/scss/app.scss production/app.css`
17 error Exit status 65
18 error Failed at the iris@1.0.0 dev script 'mkdir production & cp src/index.html production/index.html & cp -R src/fonts production/fonts & NODE_ENV=development webpack & sass src/scss/app.scss production/app.css'.
18 error This is most likely a problem with the iris package,
18 error not with npm itself.
18 error Tell the author that this fails on your system:
18 error mkdir production & cp src/index.html production/index.html & cp -R src/fonts production/fonts & NODE_ENV=development webpack & sass src/scss/app.scss production/app.css
18 error You can get information on how to open an issue for this project with:
18 error npm bugs iris
18 error Or if that isn't available, you can get their info via:
18 error
18 error npm owner ls iris
18 error There is likely additional logging output above.
19 verbose exit [ 1, true ]

View File

@ -5,7 +5,7 @@
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link href="https://fonts.googleapis.com/css?family=Archivo+Narrow:300,400,700" rel="stylesheet">
<link href="app.css" rel="stylesheet" />
<link href="/app.css" rel="stylesheet" />
</head>
<body>
@ -16,6 +16,6 @@
</body>
<script type="text/javascript" src="app.js"></script>
<script type="text/javascript" src="/app.js"></script>
</html>

View File

@ -9,7 +9,9 @@ export default class Icon extends React.Component{
}
render(){
var className = 'icon';
if( this.props.className ) className += ' '+this.props.className;
var src = require('../../images/icons/'+this.props.name+'.svg');
return <img className="icon" src={src} />;
return <img className={className} src={src} />;
}
}

View File

@ -6,6 +6,7 @@ import { bindActionCreators } from 'redux'
import FontAwesome from 'react-fontawesome'
import VolumeSlider from './VolumeSlider'
import ArtistList from './ArtistList'
import Thumbnail from './Thumbnail'
import * as mopidyActions from '../services/mopidy/actions'
@ -16,63 +17,74 @@ class Player extends React.Component{
}
renderTrackInFocus(){
if( this.props.mopidy && this.props.mopidy.trackInFocus ){
if( this.props.mopidy && this.props.mopidy.currentTlTrack ){
return (
<div className="track-in-focus">
<div className="title">{ this.props.mopidy.trackInFocus.track.name }</div>
<ArtistList artists={ this.props.mopidy.trackInFocus.track.artists } />
{ this.props.spotify.track && !this.props.mini ? <Thumbnail size="large" images={this.props.spotify.track.album.images} /> : null }
<div className="title">{ this.props.mopidy.currentTlTrack.track.name }</div>
<ArtistList artists={ this.props.mopidy.currentTlTrack.track.artists } />
</div>
);
}
return null;
}
renderControls(){
var playButton = <a onClick={() => this.props.mopidyActions.play()}><FontAwesome name="play" /> </a>
renderPlayButton(){
var button = <a onClick={() => this.props.mopidyActions.play()}><FontAwesome name="play" /> </a>
if( this.props.mopidy.state == 'playing' ){
playButton = <a onClick={() => this.props.mopidyActions.pause()}><FontAwesome name="pause" /> </a>
button = <a onClick={() => this.props.mopidyActions.pause()}><FontAwesome name="pause" /> </a>
}
return button;
}
var consumeButton = <a onClick={() => this.props.mopidyActions.instruct('tracklist.setConsume', [true])}>Consume </a>
renderConsumeButton(){
if( this.props.mini ) return null;
var button = <a onClick={() => this.props.mopidyActions.instruct('tracklist.setConsume', [true])}><FontAwesome name="fire" /></a>
if( this.props.mopidy.consume ){
consumeButton = <a onClick={() => this.props.mopidyActions.instruct('tracklist.setConsume', [false])}>Un-Consume </a>
button = <a className="active" onClick={() => this.props.mopidyActions.instruct('tracklist.setConsume', [false])}><FontAwesome name="fire" /></a>
}
return button;
}
var randomButton = <a onClick={() => this.props.mopidyActions.instruct('tracklist.setRandom', [true])}>Random </a>
renderRandomButton(){
if( this.props.mini ) return null;
var button = <a onClick={() => this.props.mopidyActions.instruct('tracklist.setRandom', [true])}><FontAwesome name="random" /></a>
if( this.props.mopidy.random ){
randomButton = <a onClick={() => this.props.mopidyActions.instruct('tracklist.setRandom', [false])}>Un-Random </a>
button = <a className="active" onClick={() => this.props.mopidyActions.instruct('tracklist.setRandom', [false])}><FontAwesome name="random" /></a>
}
return button;
}
var repeatButton = <a onClick={() => this.props.mopidyActions.instruct('tracklist.setRepeat', [true])}>Repeat </a>
renderRepeatButton(){
if( this.props.mini ) return null;
var button = <a onClick={() => this.props.mopidyActions.instruct('tracklist.setRepeat', [true])}><FontAwesome name="repeat" /></a>
if( this.props.mopidy.repeat ){
repeatButton = <a onClick={() => this.props.mopidyActions.instruct('tracklist.setRepeat', [false])}>Un-Repeat </a>
button = <a className="active" onClick={() => this.props.mopidyActions.instruct('tracklist.setRepeat', [false])}><FontAwesome name="repeat" /></a>
}
return (
<div className="player">
{ this.renderTrackInFocus() }
{ playButton }
<a onClick={() => this.props.mopidyActions.previous()}>
<FontAwesome name="step-backward" />
</a>&nbsp;
<a onClick={() => this.props.mopidyActions.next()}>
<FontAwesome name="step-forward" />
</a>&nbsp;
{ consumeButton }
{ randomButton }
{ repeatButton }
<VolumeSlider
volume={ this.props.mopidy.volume }
onChange={(volume) => this.props.mopidyActions.instruct('playback.setVolume', { volume: volume })} />
</div>
);
return button;
}
render(){
return (
<div>
{ this.renderControls() }
<div className="player">
{ this.renderTrackInFocus() }
<div className="controls">
{ this.renderPlayButton() }
<a onClick={() => this.props.mopidyActions.previous()}>
<FontAwesome name="step-backward" />
</a>&nbsp;
<a onClick={() => this.props.mopidyActions.next()}>
<FontAwesome name="step-forward" />
</a>&nbsp;
{ this.renderConsumeButton() }
{ this.renderRandomButton() }
{ this.renderRepeatButton() }
<VolumeSlider
volume={ this.props.mopidy.volume }
onChange={(volume) => this.props.mopidyActions.instruct('playback.setVolume', { volume: volume })} />
</div>
</div>
);
}

View File

@ -5,6 +5,8 @@ import { bindActionCreators } from 'redux'
import { Link } from 'react-router'
import Player from '../components/Player'
import Icon from '../components/Icon'
import Thumbnail from '../components/Thumbnail'
import FontAwesome from 'react-fontawesome'
import * as mopidyActions from '../services/mopidy/actions'
@ -22,11 +24,16 @@ class Sidebar extends React.Component{
render(){
return (
<aside>
{ this.props.spotify && this.props.spotify.track ? <Thumbnail size="large" images={this.props.spotify.track.album.images} /> : null }
<nav>
<section>
<Link activeClassName="active" to="/queue">Now playing</Link>
<Link activeClassName="active" to="/queue">
<Icon name="play" className="white" />
Now playing
</Link>
</section>
<section>
@ -35,19 +42,34 @@ class Sidebar extends React.Component{
<section>
<title>My Music</title>
<Link activeClassName="active" to="/library/playlists">Playlists</Link>
<Link activeClassName="active" to="/library/artists">Artists</Link>
<Link activeClassName="active" to="/library/albums">Albums</Link>
<Link activeClassName="active" to="/library/tracks">Tracks</Link>
<Link activeClassName="active" to="/library/playlists">
<Icon name="playlist" className="white" />
Playlists
</Link>
<Link activeClassName="active" to="/library/artists">
<Icon name="mic" className="white" />
Artists
</Link>
<Link activeClassName="active" to="/library/albums">
<Icon name="cd" className="white" />
Albums
</Link>
<Link activeClassName="active" to="/library/tracks">
<Icon name="music" className="white" />
Tracks
</Link>
</section>
<section>
<Link activeClassName="active" to="/settings">Settings</Link>
<Link activeClassName="active" to="/settings">
<Icon name="cog" className="white" />
Settings
</Link>
</section>
</nav>
<Player />
<Player mini={ true } />
</aside>
);

View File

@ -5,22 +5,41 @@ export default class Thumbnail extends React.Component{
constructor(props) {
super(props);
this.state = {
small: false,
medium: false,
large: false,
huge: false
}
}
sizedImageURL(){
var images = this.props.images;
switch( this.props.size ){
case 'small':
break;
componentDidMount(){
this.mapImageSizes();
}
default:
return images[images.length-1].url
componentWillReceiveProps( nextProps ){
this.mapImageSizes();
}
mapImageSizes(){
var images = this.props.images;
for( var i = 0; i < images.length; i++ ){
if( images[i].height > 800 ){
this.setState({ huge: images[i].url });
}else if( images[i].height > 600 ){
this.setState({ large: images[i].url });
}else if( images[i].height > 280 ){
this.setState({ medium: images[i].url });
}else{
this.setState({ small: images[i].url });
}
}
}
render(){
return (
<img src={ this.sizedImageURL() } />
<img className={ 'thumbnail '+ this.props.size } src={ this.state[this.props.size] } />
);
}
}

View File

@ -30,10 +30,12 @@ const MopidyMiddleware = (function(){
instruct( ws, store, 'tracklist.getTlTracks' );
break;
//case 'event:trackPlaybackEnded':
case 'event:playbackStateChanged':
case 'event:trackPlaybackStarted':
instruct( ws, store, 'playback.getState' );
break;
//case 'event:trackPlaybackEnded':
case 'event:trackPlaybackStarted':
instruct( ws, store, 'playback.getCurrentTlTrack' );
break;

View File

@ -66,7 +66,7 @@ export default function reducer(mopidy = {}, action){
case 'MOPIDY_CURRENTTLTRACK':
return Object.assign({}, mopidy, {
trackInFocus: action.data
currentTlTrack: action.data
});
case 'MOPIDY_VOLUME':

View File

@ -22,13 +22,6 @@ export function disconnect(){
}
}
export function loadAlbum( uri ){
return {
type: 'SPOTIFY_LOAD_ALBUM',
uri: uri
}
}
/**
* Send an ajax request to the Spotify API
@ -55,7 +48,7 @@ const sendRequest = ( dispatch, getState, endpoint, method = 'GET', data = false
}).then(
response => resolve(response),
error => {
console.error('sendRequest', error)
console.error('Could not send request to '+endpoint, error, data)
reject(error)
}
)
@ -96,17 +89,12 @@ function getToken( dispatch, getState ){
resolve(getState().spotify.access_token);
return
}
// token is expiring/expired, so go get a new one and resolve that
refreshToken( dispatch, getState )
.then(
response => {
resolve(response.access_token);
},
error => {
console.error('getToken', error)
reject(error)
}
response => resolve(response.access_token),
error => reject(error)
);
});
}
@ -129,17 +117,17 @@ function refreshToken( dispatch, getState ){
resolve(response);
},
error => {
console.error('refreshToken', error)
console.error('Could not refresh token', error)
reject(error)
}
);
})
}
export function refreshToken(){
export function refreshingToken(){
return (dispatch, getState) => {
dispatch({ type: 'SPOTIFY_TOKEN_REFRESHING' });
doRefreshToken( dispatch, getState );
refreshToken( dispatch, getState );
}
}
@ -164,6 +152,29 @@ export function getMe(){
}
/**
* Get a single track
*
* @param uri string
**/
export function getTrack( uri ){
return (dispatch, getState) => {
// flush out the previous store value
dispatch({ type: 'SPOTIFY_TRACK_LOADED', data: false });
sendRequest( dispatch, getState, 'tracks/'+ getFromUri('trackid', uri) )
.then( response => {
dispatch({
type: 'SPOTIFY_TRACK_LOADED',
data: response
});
}
);
}
}
/**
* Get a single artist
*

View File

@ -1,5 +1,5 @@
import actions from './actions'
var actions = require('./actions.js')
const SpotifyMiddleware = (function(){
@ -7,26 +7,25 @@ const SpotifyMiddleware = (function(){
* The actual middleware inteceptor
**/
return store => next => action => {
return next(action);
/*
var state = store.getState();
switch(action.type){
case 'SPOTIFY_AUTHORIZATION_GRANTEDXXX':
// when our mopidy server current track changes
case 'MOPIDY_CURRENTTLTRACK':
// proceed as usual
// proceed as usual so we don't inhibit default functionality
next(action)
// now we've been granted,
store.dispatch({ type: 'SPOTIFY_AUTHORIZATION_COMPLETE', data: data })
break;
// if the current track is a spotify track
if( action.data.track.uri.substring(0,14) == 'spotify:track:' ){
store.dispatch( actions.getTrack( action.data.track.uri ) )
}
// This action is irrelevant to us, pass it on to the next middleware
default:
return next(action);
}
*/
}
})();

View File

@ -43,6 +43,9 @@ export default function reducer(spotify = {}, action){
case 'SPOTIFY_ME_LOADED':
return Object.assign({}, spotify, { me: action.data });
case 'SPOTIFY_TRACK_LOADED':
return Object.assign({}, spotify, { track: action.data });
case 'SPOTIFY_PLAYLIST_LOADED':
return Object.assign({}, spotify, { playlist: action.data });

View File

@ -3,6 +3,7 @@ import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import Header from '../components/Header'
import TrackList from '../components/TrackList'
import AlbumGrid from '../components/AlbumGrid'
@ -30,10 +31,7 @@ class Artist extends React.Component{
if( this.props.spotify.artist ){
return (
<div>
<Header
icon="mic"
title={ this.props.spotify.artist.name }
/>
<Header icon="mic" title={ this.props.spotify.artist.name } />
<p>{ this.props.spotify.artist.followers.total.toLocaleString() } followers</p>
{ this.props.spotify.artist.tracks ? <TrackList tracks={ this.props.spotify.artist.tracks } /> : null }
{ this.props.spotify.artist_albums ? <AlbumGrid items={ this.props.spotify.artist_albums } /> : null }

View File

@ -31,10 +31,7 @@ class Playlist extends React.Component{
if( this.props.spotify.playlist ){
return (
<div>
<Header
icon="playlist"
title={ this.props.spotify.playlist.name }
/>
<Header icon="playlist" title={ this.props.spotify.playlist.name } />
<TrackList tracks={this.props.spotify.playlist.tracks.items} />
</div>
);

View File

@ -1,4 +1,5 @@
@import 'vendor/font-awesome/index';
@import 'global/variables';
@ -6,8 +7,8 @@
@import 'global/reset';
@import 'global/forms';
@import 'components/icon';
@import 'components/sidebar';
@import 'components/lists';
@import 'components/track';
@import 'components/header';

8
src/scss/components/_icon.scss Executable file
View File

@ -0,0 +1,8 @@
.icon {
&.white{
-webkit-filter: invert(1);
-moz-filter: invert(1);
filter: invert(1);
}
}

View File

@ -32,7 +32,7 @@
}
&:not(.header):hover {
background: #F4F4F4;
background: #EEEEEE;
cursor: pointer;
}

View File

@ -7,6 +7,15 @@ aside{
bottom: 0;
background: #333333;
width: 200px;
overflow: hidden;
.thumbnail {
position: absolute;
top: 0;
height: 100%;
z-index: -1;
opacity: 0.1;
}
nav {
@ -18,6 +27,13 @@ aside{
padding: 6px 18px 6px 14px;
border-left: 4px solid transparent;
.icon {
height: 14px;
padding-right: 8px;
vertical-align: top;
margin-top: 4px;
}
&.active {
border-color: $turquoise;
background: rgba( 0,0,0,0.1 );
@ -44,6 +60,12 @@ aside{
}
.player {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 14px;
.track-in-focus {
font-size: 16px;
text-transform: uppercase;
@ -56,6 +78,7 @@ aside{
.artist-list {
opacity: 0.5;
display: block;
@include one_line_text
.artist a {
@ -69,5 +92,26 @@ aside{
}
}
}
.controls {
text-align: center;
a {
color: #FFFFFF;
cursor: pointer;
font-size: 14px;
padding: 8px;
display: inline-block;
opacity: 0.8;
&.active {
color: $turquoise;
}
&:hover {
opacity: 1.0;
}
}
}
}
}