Working through each Modal and updating; Addingng ErrorMessage and 'asset not found' messages

This commit is contained in:
James Barnsley
2019-01-22 21:46:56 +13:00
parent 4d7f81b685
commit 1fbd2c36a9
25 changed files with 13470 additions and 18914 deletions

View File

@ -13,6 +13,7 @@ import Dragger from './components/Dragger';
import Notifications from './components/Notifications';
import DebugInfo from './components/DebugInfo';
import ErrorBoundary from './components/ErrorBoundary';
import ErrorMessage from './components/ErrorMessage';
import Album from './views/Album';
import Artist from './views/Artist';
@ -414,7 +415,9 @@ class App extends React.Component{
<Route exact path="/library/browse/:uri" component={LibraryBrowseDirectory} />
<Route>
<h1>I'm lost</h1>
<ErrorMessage type="not-found" title="Not found">
<p>Oops, that link could not be found</p>
</ErrorMessage>
</Route>
</Switch>

View File

@ -1,5 +1,6 @@
import React from 'react';
import ErrorMessage from './ErrorMessage';
export default class ErrorBoundary extends React.Component {
@ -24,16 +25,9 @@ export default class ErrorBoundary extends React.Component {
render() {
if (this.state.hasError){
return (
<div className="error-boundary">
<h4 className="error-boundary__title">
<i className="icon icon--material">error</i>
{this.state.error ? this.state.error.toString() : "Unknown error"}
</h4>
{this.state.info ? <pre className="error-boundary__trace">{this.state.info.componentStack}</pre> : null}
</div>
<ErrorMessage type="error-boundary">
{this.state.info ? <pre className="error-message__trace">{this.state.info.componentStack}</pre> : null}
</ErrorMessage>
);
}
return this.props.children;

View File

@ -0,0 +1,28 @@
import React from 'react';
export default class ErrorMessage extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className={"error-message"+(this.props.type ? " error-message--"+this.props.type : "")}>
<i className="error-message__icon icon icon--material">error</i>
<h4 className="error-message__title">
{this.props.title ? this.props.title : "Unknown error"}
</h4>
<div className="error-message__content">
{this.props.children}
</div>
</div>
);
}
}

View File

@ -1,8 +1,8 @@
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { withRouter } from 'react-router';
import Dropzone from './Dropzone'
@ -93,4 +93,4 @@ const mapDispatchToProps = (dispatch) => {
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Dropzones)
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Dropzones));

View File

@ -3,6 +3,7 @@ import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import ErrorMessage from '../components/ErrorMessage';
import Header from '../components/Header'
import TrackList from '../components/TrackList'
import Thumbnail from '../components/Thumbnail'
@ -115,7 +116,11 @@ class Album extends React.Component{
</div>
)
} else {
return null;
return (
<ErrorMessage type="not-found" title="Not found">
<p>Could not find album with URI "{this.props.uri}"</p>
</ErrorMessage>
);
}
}

View File

@ -5,6 +5,7 @@ import { bindActionCreators } from 'redux';
import { Redirect } from 'react-router';
import { Route, Switch } from 'react-router-dom';
import ErrorMessage from '../components/ErrorMessage';
import Link from '../components/Link';
import LazyLoadListener from '../components/LazyLoadListener'
import Header from '../components/Header'
@ -286,7 +287,11 @@ class Artist extends React.Component{
</div>
)
} else {
return null;
return (
<ErrorMessage type="not-found" title="Not found">
<p>Could not find artist with URI "{this.props.uri}"</p>
</ErrorMessage>
);
}
}

View File

@ -2,9 +2,9 @@
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import ReactGA from 'react-ga';
import ErrorMessage from '../components/ErrorMessage';
import Link from '../components/Link';
import TrackList from '../components/TrackList'
import Thumbnail from '../components/Thumbnail'
@ -182,7 +182,11 @@ class Playlist extends React.Component{
</div>
)
} else {
return null;
return (
<ErrorMessage type="not-found" title="Not found">
<p>Could not find playlist with URI "{this.props.uri}"</p>
</ErrorMessage>
);
}
}

View File

@ -71,7 +71,7 @@ class Queue extends React.Component {
if (!image){
return (
<span className={this.props.radio_enabled ? 'artwork radio-enabled' : 'artwork'}>
{this.props.radio_enabled ? <img className="radio-overlay" src="assets/radio-overlay.png" /> : null}
{this.props.radio_enabled ? <img className="radio-overlay" src="/iris/assets/radio-overlay.png" /> : null}
<Thumbnail circle={this.props.radio_enabled} />
</span>
)
@ -86,7 +86,7 @@ class Queue extends React.Component {
className={this.props.radio_enabled ? 'artwork radio-enabled' : 'artwork'}
type="album"
uri={uri}>
{this.props.radio_enabled ? <img className="radio-overlay" src="assets/radio-overlay.png" /> : null}
{this.props.radio_enabled ? <img className="radio-overlay" src="/iris/assets/radio-overlay.png" /> : null}
<Thumbnail image={image} circle={this.props.radio_enabled} />
</URILink>
)

View File

@ -4,6 +4,7 @@ import { connect } from 'react-redux'
import Link from '../components/Link';
import { bindActionCreators } from 'redux'
import ErrorMessage from '../components/ErrorMessage';
import Header from '../components/Header'
import TrackList from '../components/TrackList'
import Thumbnail from '../components/Thumbnail'
@ -181,7 +182,11 @@ class Track extends React.Component{
</div>
)
} else {
return null;
return (
<ErrorMessage type="not-found" title="Not found">
<p>Could not find track with URI "{this.props.uri}"</p>
</ErrorMessage>
);
}
}

View File

@ -3,19 +3,20 @@ import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import Thumbnail from '../components/Thumbnail'
import PlaylistGrid from '../components/PlaylistGrid'
import FollowButton from '../components/Fields/FollowButton'
import LazyLoadListener from '../components/LazyLoadListener'
import Parallax from '../components/Parallax'
import ContextMenuTrigger from '../components/ContextMenuTrigger'
import Icon from '../components/Icon'
import ErrorMessage from '../components/ErrorMessage';
import Thumbnail from '../components/Thumbnail';
import PlaylistGrid from '../components/PlaylistGrid';
import FollowButton from '../components/Fields/FollowButton';
import LazyLoadListener from '../components/LazyLoadListener';
import Parallax from '../components/Parallax';
import ContextMenuTrigger from '../components/ContextMenuTrigger';
import Icon from '../components/Icon';
import * as helpers from '../helpers'
import * as coreActions from '../services/core/actions'
import * as uiActions from '../services/ui/actions'
import * as mopidyActions from '../services/mopidy/actions'
import * as spotifyActions from '../services/spotify/actions'
import * as helpers from '../helpers';
import * as coreActions from '../services/core/actions';
import * as uiActions from '../services/ui/actions';
import * as mopidyActions from '../services/mopidy/actions';
import * as spotifyActions from '../services/spotify/actions';
class User extends React.Component{
@ -75,7 +76,11 @@ class User extends React.Component{
</div>
)
} else {
return null;
return (
<ErrorMessage type="not-found" title="Not found">
<p>Could not find user with URI "{this.props.uri}"</p>
</ErrorMessage>
);
}
}

View File

@ -62,8 +62,8 @@ class AddToPlaylist extends React.Component{
return (
<div className="list__item" key={playlist.uri} onClick={ () => this.playlistSelected(playlist.uri) }>
<Thumbnail images={playlist.images} size="small" />
<h3 className="name">{ playlist.name }</h3>
<ul className="details">
<h4 className="list__item__name">{ playlist.name }</h4>
<ul className="list__item__details details">
<li><Icon type="fontawesome" className="source" name={helpers.sourceIcon(playlist.uri)} /></li>
<li>{ playlist.tracks_total ? <span className="mid_grey-text">&nbsp;{ playlist.tracks_total } tracks</span> : null }</li>
</ul>

View File

@ -1,14 +1,15 @@
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import Link from '../../components/Link'
import ReactGA from 'react-ga'
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import ReactGA from 'react-ga';
import Modal from './Modal';
import Link from '../../components/Link';
import Icon from '../../components/Icon';
import ColourField from '../../components/Fields/ColourField';
import IconField from '../../components/Fields/IconField';
import * as pusherActions from '../../services/pusher/actions';
import * as uiActions from '../../services/ui/actions';
import * as helpers from '../../helpers';
@ -176,6 +177,7 @@ class EditCommand extends React.Component{
const mapStateToProps = (state, ownProps) => {
var id = ownProps.params.id;
return {
id: id,
command: (id && state.pusher.commands && state.pusher.commands[id] !== undefined ? state.pusher.commands[id] : null)
}
}

View File

@ -39,16 +39,16 @@ class EditPlaylist extends React.Component{
collaborative: (this.props.playlist.collaborative == true)
});
} else {
switch (helpers.uriSource(this.props.match.params.uri)){
switch (helpers.uriSource(this.props.uri)){
case 'spotify':
this.props.spotifyActions.getPlaylist(this.props.match.params.uri);
this.props.spotifyActions.following(this.props.match.params.uri);
this.props.spotifyActions.getPlaylist(this.props.uri);
this.props.spotifyActions.following(this.props.uri);
break
default:
if (props.mopidy_connected){
this.props.mopidyActions.getPlaylist(this.props.match.params.uri);
if (this.props.mopidy_connected){
this.props.mopidyActions.getPlaylist(this.props.uri);
}
break
}
@ -74,7 +74,7 @@ class EditPlaylist extends React.Component{
return false
} else {
this.props.coreActions.savePlaylist(
this.props.match.params.uri,
this.props.uri,
this.state.name,
this.state.description,
this.state.public,
@ -103,7 +103,7 @@ class EditPlaylist extends React.Component{
}
renderFields(){
switch (helpers.uriSource(this.props.match.params.uri)){
switch (helpers.uriSource(this.props.uri)){
case 'spotify':
return (
@ -204,9 +204,11 @@ class EditPlaylist extends React.Component{
}
const mapStateToProps = (state, ownProps) => {
var uri = decodeURIComponent(ownProps.match.params.uri);
return {
uri: uri,
mopidy_connected: state.mopidy.connected,
playlist: (state.core.playlists[ownProps.match.params.uri] !== undefined ? state.core.playlists[ownProps.match.params.uri] : null),
playlist: (state.core.playlists[uri] !== undefined ? state.core.playlists[uri] : null),
playlists: state.core.playlists
}
}

View File

@ -190,7 +190,7 @@ class EditRadio extends React.Component{
return (
<div className="list__item" key={seed.uri}>
{seed.unresolved ? <span className="mid_grey-text">{seed.uri}</span> : <span>{seed.name}</span> }
{!seed.unresolved ? <span className="mid_grey-text">&nbsp;({seed.type})</span> : null}
{!seed.unresolved ? <span className="mid_grey-text">&nbsp;({helpers.uriType(seed.uri)})</span> : null}
<span className="button discrete remove-uri no-hover" onClick={e => this.removeSeed(seed.uri)}>
<Icon name="delete" />Remove
</span>

View File

@ -22,7 +22,7 @@ class ImageZoom extends React.Component{
render(){
return (
<Modal className="modal--image-zoom">
<img src={this.props.location.query.url} />
<img src={this.props.location.search.replace("?url=","")} />
</Modal>
)
}

View File

@ -100,11 +100,10 @@ class ShareConfiguration extends React.Component {
onChange={e => this.toggleRecipient(connection.connection_id)}
/>
<span className="label">
{ connection.username }
&nbsp;
<span className="mid_grey-text">
<div className="title">{ connection.username }</div>
<div className="description mid_grey-text">
({ connection.ip })
</span>
</div>
</span>
</label>
);
@ -123,7 +122,7 @@ class ShareConfiguration extends React.Component {
}
return (
<Modal className="modal--share-authorization">
<Modal className="modal--share-configuration">
<h1>Share configuration</h1>
<form onSubmit={e => this.handleSubmit(e)}>
<div className="field checkbox white">
@ -141,7 +140,7 @@ class ShareConfiguration extends React.Component {
checked={ this.state.ui }
onChange={ e => this.setState({ ui: !this.state.ui })} />
<span className="label">
UI customisation (theme, sorting, filters)
<span className="title">UI customisation (theme, sorting, filters)</span>
</span>
</label>
@ -152,7 +151,8 @@ class ShareConfiguration extends React.Component {
checked={this.state.spotify}
onChange={ e => this.setState({ spotify: !this.state.spotify })} />
<span className="label">
Spotify authorization <span className="mid_grey-text">&nbsp;Logged in as {this.props.spotify_me.name}</span>
<div className="title">Spotify authorization</div>
<div className="description mid_grey-text">Logged in as {this.props.spotify_me.name}</div>
</span>
</label> : null}
@ -163,7 +163,8 @@ class ShareConfiguration extends React.Component {
checked={this.state.lastfm}
onChange={ e => this.setState({ lastfm: !this.state.lastfm })} />
<span className="label">
LastFM authorization <span className="mid_grey-text">&nbsp;Logged in as {this.props.lastfm_me.name}</span>
<div className="title">LastFM authorization</div>
<div className="description mid_grey-text">Logged in as {this.props.lastfm_me.name}</div>
</span>
</label> : null}
@ -174,7 +175,8 @@ class ShareConfiguration extends React.Component {
checked={this.state.genius}
onChange={ e => this.setState({ genius: !this.state.genius })} />
<span className="label">
Genius authorization <span className="mid_grey-text">&nbsp;Logged in as {this.props.genius_me.name}</span>
<div className="title">Genius authorization</div>
<div className="description mid_grey-text">Logged in as {this.props.genius_me.name}</div>
</span>
</label> : null}
</div>

View File

@ -36,7 +36,7 @@
@import 'components/icon-field';
@import 'components/commands';
@import 'components/related-artists';
@import 'components/error-boundary';
@import 'components/error-message';
@import 'views/artist';
@import 'views/user';

View File

@ -1,20 +0,0 @@
.error-boundary {
padding: 20px;
&__title {
color: colour(red);
padding: 10px;
margin: 0;
.icon {
font-size: 1em;
padding-right: 10px;
vertical-align: middle;
}
}
&__trace {
padding: 10px;
}
}

View File

@ -0,0 +1,23 @@
.error-message {
padding: 40px 40px 40px 80px;
position: relative;
&__icon {
position: absolute;
top: 40px;
left: 40px;
font-size: 2rem;
color: colour(red);
}
&__title {
color: colour(red);
padding: 4px 0 10px 0 !important;
margin: 0;
}
&__trace {
padding: 10px;
}
}

View File

@ -1,9 +1,14 @@
.modal {
@include fadein();
position: relative;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: darken(colour(darkest_grey), 2%);
overflow-y: auto;
overflow-x: hidden;
min-height: 100%;
.light-theme & {
@ -45,9 +50,9 @@
}
.content {
padding: 80px 25%;
padding: 80px 20%;
margin: 0 auto;
width: 50%;
width: 60%;
color: colour(white);
h1 {
@ -156,8 +161,8 @@
padding-top: 40px;
.list__item {
margin: 0 0 20px 0;
padding: 5px;
margin: 5px 0;
padding: 10px;
cursor: pointer;
&:before {
@ -166,7 +171,16 @@
.thumbnail {
float: left;
margin-right: 20px;
}
&__name,
&__details {
padding-left: 70px;
}
&__name {
padding-top: 0;
margin-bottom: 5px;
}
.source {
@ -177,13 +191,13 @@
}
}
&.small .list__item {
width: 49%;
float: left;
font-size: inherit;
&.small {
display: flex;
flex-wrap: wrap;
&:nth-child(2n){
margin-left: 2%;
.list__item {
width: 50%;
font-size: inherit;
}
}
}
@ -215,6 +229,20 @@
}
}
&--share-configuration {
.field.checkbox {
.label {
.title {
font-size: 1.2rem;
}
.description {
padding-top: 4px;
}
}
}
}
&--kiosk-mode {
background: colour(black);
overflow: hidden;
@ -289,6 +317,12 @@
padding-top: 50px;
width: 60vw;
max-width: 60vh;
.slider {
&__track {
background: rgba(128, 128, 128, 0.25);
}
}
}
}
}

View File

@ -41,12 +41,13 @@
.title {
font-size: 18px;
padding-top: 20px;
padding-top: 30px;
position: relative;
z-index: 2;
}
.artist-sentence {
padding-top: 10px;
display: block;
font-size: 18px;
opacity: 0.5;