Part 2 of functional component upgrade
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
30
mopidy_iris/static/app/app.min.js
vendored
30
mopidy_iris/static/app/app.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -98,7 +98,7 @@
|
||||
|
||||
// Release details
|
||||
// These are automatically injected to built HTML
|
||||
var build = "1554883181";
|
||||
var build = "1554926560";
|
||||
var version = "3.34.3";
|
||||
|
||||
// Construct the script tag
|
||||
|
||||
@ -1,51 +1,45 @@
|
||||
|
||||
import React from 'react'
|
||||
import React, { memo } from 'react'
|
||||
import URILink from './URILink'
|
||||
|
||||
export default class ArtistSentence extends React.Component{
|
||||
export default memo((props) => {
|
||||
|
||||
constructor(props){
|
||||
super(props);
|
||||
if (!props.artists){
|
||||
return <span className={props.className ? props.className+" artist-sentence" : "artist-sentence" }>-</span>;
|
||||
}
|
||||
|
||||
render(){
|
||||
if (!this.props.artists){
|
||||
return <span className={this.props.className ? this.props.className+" artist-sentence" : "artist-sentence" }>-</span>;
|
||||
}
|
||||
return (
|
||||
<span className={ props.className ? props.className+" artist-sentence" : "artist-sentence" }>
|
||||
{
|
||||
props.artists.map((artist, index) => {
|
||||
|
||||
return (
|
||||
<span className={ this.props.className ? this.props.className+" artist-sentence" : "artist-sentence" }>
|
||||
{
|
||||
this.props.artists.map((artist, index) => {
|
||||
if (!artist){
|
||||
return <span>-</span>;
|
||||
}
|
||||
|
||||
if (!artist){
|
||||
return <span>-</span>;
|
||||
}
|
||||
var separator = null;
|
||||
if (index == props.artists.length - 2){
|
||||
separator = ' and ';
|
||||
} else if (index < props.artists.length - 2){
|
||||
separator = ', ';
|
||||
}
|
||||
|
||||
var separator = null;
|
||||
if (index == this.props.artists.length - 2){
|
||||
separator = ' and ';
|
||||
} else if (index < this.props.artists.length - 2){
|
||||
separator = ', ';
|
||||
}
|
||||
if (!artist.name){
|
||||
var content = <span>-</span>
|
||||
} else if (!artist.uri || props.nolinks){
|
||||
var content = <span>{ artist.name }</span>
|
||||
} else {
|
||||
var content = <URILink className="artist" type="artist" uri={artist.uri}>{ artist.name }</URILink>
|
||||
}
|
||||
|
||||
if (!artist.name){
|
||||
var content = <span>-</span>
|
||||
} else if (!artist.uri || this.props.nolinks){
|
||||
var content = <span>{ artist.name }</span>
|
||||
} else {
|
||||
var content = <URILink className="artist" type="artist" uri={artist.uri}>{ artist.name }</URILink>
|
||||
}
|
||||
|
||||
return (
|
||||
<span key={'index_'+artist.uri}>
|
||||
{ content }
|
||||
{ separator }
|
||||
</span>
|
||||
);
|
||||
})
|
||||
}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<span key={'index_'+artist.uri}>
|
||||
{ content }
|
||||
{ separator }
|
||||
</span>
|
||||
);
|
||||
})
|
||||
}
|
||||
</span>
|
||||
);
|
||||
});
|
||||
@ -1,38 +1,32 @@
|
||||
|
||||
|
||||
|
||||
import React from 'react'
|
||||
import React, { memo } from 'react'
|
||||
import GridItem from './GridItem'
|
||||
|
||||
export default class CategoryGrid extends React.Component{
|
||||
|
||||
constructor(props){
|
||||
super(props);
|
||||
export default memo((props) => {
|
||||
|
||||
if (!props.categories){
|
||||
return null;
|
||||
}
|
||||
|
||||
render(){
|
||||
if (!this.props.categories ) return null
|
||||
let className = "grid grid--tiles";
|
||||
if (props.className) className += ' '+props.className;
|
||||
if (props.mini) className += ' grid--mini';
|
||||
|
||||
var className = "grid grid--tiles";
|
||||
if (this.props.className) className += ' '+this.props.className;
|
||||
if (this.props.mini) className += ' grid--mini';
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
{
|
||||
this.props.categories.map(category => {
|
||||
return (
|
||||
<GridItem
|
||||
key={category.id}
|
||||
type="category"
|
||||
item={category}
|
||||
link={'/discover/categories/'+encodeURIComponent(category.id)}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className={className}>
|
||||
{
|
||||
props.categories.map(category => {
|
||||
return (
|
||||
<GridItem
|
||||
key={category.id}
|
||||
type="category"
|
||||
item={category}
|
||||
link={'/discover/categories/'+encodeURIComponent(category.id)}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@ -1,28 +1,21 @@
|
||||
|
||||
import React from 'react';
|
||||
import React, { memo } from 'react';
|
||||
|
||||
export default class ErrorMessage extends React.Component {
|
||||
export default memo((props) => {
|
||||
return (
|
||||
<div className={"error-message"+(props.type ? " error-message--"+props.type : "")}>
|
||||
|
||||
<i className="error-message__icon icon icon--material">error</i>
|
||||
|
||||
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>
|
||||
<h4 className="error-message__title">
|
||||
{props.title ? props.title : "Unknown error"}
|
||||
</h4>
|
||||
|
||||
<div className="error-message__content">
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import * as helpers from '../helpers';
|
||||
import Link from './Link';
|
||||
|
||||
@ -1,47 +1,34 @@
|
||||
|
||||
import React from 'react';
|
||||
import React, { memo } from 'react';
|
||||
import FontAwesome from 'react-fontawesome';
|
||||
|
||||
export default class Icon extends React.Component{
|
||||
|
||||
constructor(props){
|
||||
super(props);
|
||||
export default memo((props) => {
|
||||
if (!props.name || props.name === ""){
|
||||
return null;
|
||||
}
|
||||
|
||||
handleClick(e){
|
||||
if (this.props.onClick){
|
||||
this.props.onClick(e);
|
||||
}
|
||||
let className = "icon icon--"+(props.type ? props.type : 'material');
|
||||
if (props.className){
|
||||
className += ' '+props.className;
|
||||
}
|
||||
|
||||
render(){
|
||||
if (!this.props.name || this.props.name === ""){
|
||||
return null;
|
||||
}
|
||||
switch (props.type){
|
||||
case 'svg':
|
||||
return <img className={className} src={'/iris/assets/icons/'+props.name+'.svg'} onClick={e => (props.handleClick ? props.handleClick(e) : null)} />;
|
||||
|
||||
var className = "icon icon--"+(this.props.type ? this.props.type : 'material');
|
||||
if (this.props.className){
|
||||
className += ' '+this.props.className;
|
||||
}
|
||||
case 'gif':
|
||||
return <img className={className} src={'/iris/assets/icons/'+props.name+'.gif'} onClick={e => (props.handleClick ? props.handleClick(e) : null)} />;
|
||||
|
||||
switch (this.props.type){
|
||||
case 'svg':
|
||||
return <img className={className} src={'/iris/assets/icons/'+this.props.name+'.svg'} onClick={e => this.handleClick(e)} />;
|
||||
case 'fontawesome':
|
||||
return <FontAwesome className={className} type="fontawesome" name={props.name} onClick={e => (props.handleClick ? props.handleClick(e) : null)} />;
|
||||
|
||||
case 'gif':
|
||||
return <img className={className} src={'/iris/assets/icons/'+this.props.name+'.gif'} onClick={e => this.handleClick(e)} />;
|
||||
case 'css':
|
||||
switch (props.name){
|
||||
case 'playing':
|
||||
return <i className={className + " icon--playing"}><span></span><span></span><span></span></i>
|
||||
}
|
||||
|
||||
case 'fontawesome':
|
||||
return <FontAwesome className={className} type="fontawesome" name={this.props.name} onClick={e => this.handleClick(e)} />;
|
||||
|
||||
case 'css':
|
||||
switch (this.props.name){
|
||||
case 'playing':
|
||||
return <i className={className + " icon--playing"}><span></span><span></span><span></span></i>
|
||||
}
|
||||
|
||||
default:
|
||||
return <i className={className} onClick={e => this.handleClick(e)}>{this.props.name}</i>;
|
||||
}
|
||||
default:
|
||||
return <i className={className} onClick={e => (props.handleClick ? props.handleClick(e) : null)}>{props.name}</i>;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -1,32 +1,23 @@
|
||||
|
||||
import React from 'react'
|
||||
import Icon from './Icon'
|
||||
import * as helpers from '../helpers'
|
||||
import React, { memo } from 'react';
|
||||
|
||||
export default class Popularity extends React.Component{
|
||||
|
||||
constructor(props){
|
||||
super(props)
|
||||
export default memo((props) => {
|
||||
if (props.popularity === undefined || props.popularity === null){
|
||||
return null;
|
||||
}
|
||||
|
||||
render(){
|
||||
if (this.props.popularity === undefined || this.props.popularity === null){
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<span className="popularity">
|
||||
<span className="popularity-bars">
|
||||
<span className={"bar"+(this.props.popularity > 10 ? " filled" : "")}></span>
|
||||
<span className={"bar"+(this.props.popularity > 30 ? " filled" : "")}></span>
|
||||
<span className={"bar"+(this.props.popularity > 50 ? " filled" : "")}></span>
|
||||
<span className={"bar"+(this.props.popularity > 70 ? " filled" : "")}></span>
|
||||
<span className={"bar"+(this.props.popularity > 90 ? " filled" : "")}></span>
|
||||
</span>
|
||||
<span className="popularity-value">
|
||||
{this.props.popularity}% popularity
|
||||
</span>
|
||||
return (
|
||||
<span className="popularity">
|
||||
<span className="popularity-bars">
|
||||
<span className={"bar"+(props.popularity > 10 ? " filled" : "")}></span>
|
||||
<span className={"bar"+(props.popularity > 30 ? " filled" : "")}></span>
|
||||
<span className={"bar"+(props.popularity > 50 ? " filled" : "")}></span>
|
||||
<span className={"bar"+(props.popularity > 70 ? " filled" : "")}></span>
|
||||
<span className={"bar"+(props.popularity > 90 ? " filled" : "")}></span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
<span className="popularity-value">
|
||||
{props.popularity}% popularity
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
});
|
||||
@ -1,45 +1,40 @@
|
||||
|
||||
import React from 'react'
|
||||
import React, { memo } from 'react'
|
||||
import Thumbnail from './Thumbnail'
|
||||
import URILink from './URILink'
|
||||
|
||||
export default class RelatedArtists extends React.Component{
|
||||
|
||||
constructor(props){
|
||||
super(props)
|
||||
export default memo((props) => {
|
||||
if (!props.artists){
|
||||
return null;
|
||||
}
|
||||
|
||||
render(){
|
||||
if (!this.props.artists ) return null
|
||||
return (
|
||||
<div className="related-artists">
|
||||
{
|
||||
props.artists.map((artist, index) => {
|
||||
|
||||
return (
|
||||
<div className="related-artists">
|
||||
{
|
||||
this.props.artists.map((artist, index) => {
|
||||
var images = artist.images;
|
||||
if (Array.isArray(images)){
|
||||
images = images[0];
|
||||
}
|
||||
|
||||
var images = artist.images;
|
||||
if (Array.isArray(images)){
|
||||
images = images[0];
|
||||
}
|
||||
|
||||
if (artist.uri){
|
||||
return (
|
||||
<URILink type="artist" uri={artist.uri} key={artist.uri} className="related-artists__item related-artists__item--link">
|
||||
<Thumbnail className="related-artists__item__thumbnail" circle={true} size="small" images={images} />
|
||||
<span className="related-artists__item__name">{ artist.name }</span>
|
||||
</URILink>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<span key={artist.uri} className="related-artists__item">
|
||||
<Thumbnail className="related-artists__item__thumbnail" circle={true} size="small" images={images} />
|
||||
<span className="related-artists__item__name">{ artist.name }</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
if (artist.uri){
|
||||
return (
|
||||
<URILink type="artist" uri={artist.uri} key={artist.uri} className="related-artists__item related-artists__item--link">
|
||||
<Thumbnail className="related-artists__item__thumbnail" circle={true} size="small" images={images} />
|
||||
<span className="related-artists__item__name">{ artist.name }</span>
|
||||
</URILink>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<span key={artist.uri} className="related-artists__item">
|
||||
<Thumbnail className="related-artists__item__thumbnail" circle={true} size="small" images={images} />
|
||||
<span className="related-artists__item__name">{ artist.name }</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
@ -1,86 +1,63 @@
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createStore, bindActionCreators } from 'redux';
|
||||
import React, { memo } from 'react';
|
||||
import Link from './Link';
|
||||
import Icon from './Icon';
|
||||
|
||||
import Icon from './Icon'
|
||||
|
||||
import * as helpers from '../helpers'
|
||||
import * as uiActions from '../services/ui/actions'
|
||||
|
||||
class Thumbnail extends React.Component{
|
||||
|
||||
constructor(props){
|
||||
super(props);
|
||||
}
|
||||
|
||||
mapImageSizes(){
|
||||
export default memo((props) => {
|
||||
|
||||
const mapImageSizes = () => {
|
||||
|
||||
// Single image
|
||||
if (this.props.image){
|
||||
return this.props.image;
|
||||
|
||||
if (props.image){
|
||||
return props.image;
|
||||
|
||||
// Multiple images
|
||||
} else if (this.props.images){
|
||||
var images = this.props.images;
|
||||
|
||||
} else if (props.images){
|
||||
var images = props.images;
|
||||
|
||||
// An array of image objects (eg Artists), so just pick the first one
|
||||
if (Array.isArray(images) && images.length > 0){
|
||||
images = images[0];
|
||||
}
|
||||
|
||||
|
||||
// Default to medium-sized image, but accept size property as override
|
||||
var size = 'medium';
|
||||
if (this.props.size){
|
||||
size = this.props.size;
|
||||
if (props.size){
|
||||
size = props.size;
|
||||
}
|
||||
|
||||
|
||||
// Return the requested size
|
||||
if (images[size]){
|
||||
return images[size];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// No images
|
||||
return null;
|
||||
}
|
||||
|
||||
render(){
|
||||
var image = this.mapImageSizes();
|
||||
var class_name = 'thumbnail thumbnail--loaded';
|
||||
if (this.props.size){
|
||||
class_name += ' thumbnail--'+this.props.size;
|
||||
}
|
||||
if (this.props.circle){
|
||||
class_name += ' thumbnail--circle';
|
||||
}
|
||||
if (this.props.className){
|
||||
class_name += ' '+this.props.className;
|
||||
}
|
||||
|
||||
var zoom_icon = null;
|
||||
if (this.props.canZoom && image){
|
||||
zoom_icon = <Link className="thumbnail__zoom" to={'/image-zoom?url='+image}><Icon name="search" /></Link>;
|
||||
}
|
||||
var image = mapImageSizes();
|
||||
var class_name = 'thumbnail thumbnail--loaded';
|
||||
|
||||
return (
|
||||
<div className={class_name}>
|
||||
<div className="thumbnail__image" style={{backgroundImage: 'url("'+(image ? image : '/iris/assets/no-image.svg')+'")'}}></div>
|
||||
{zoom_icon}
|
||||
</div>
|
||||
);
|
||||
if (props.size){
|
||||
class_name += ' thumbnail--'+props.size;
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
return {}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
uiActions: bindActionCreators(uiActions, dispatch)
|
||||
if (props.circle){
|
||||
class_name += ' thumbnail--circle';
|
||||
}
|
||||
if (props.className){
|
||||
class_name += ' '+props.className;
|
||||
}
|
||||
|
||||
var zoom_icon = null;
|
||||
if (props.canZoom && image){
|
||||
zoom_icon = <Link className="thumbnail__zoom" to={'/image-zoom?url='+image}><Icon name="search" /></Link>;
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Thumbnail)
|
||||
return (
|
||||
<div className={class_name}>
|
||||
<div className="thumbnail__image" style={{backgroundImage: 'url("'+(image ? image : '/iris/assets/no-image.svg')+'")'}}></div>
|
||||
{zoom_icon}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
@ -1,79 +1,67 @@
|
||||
|
||||
import React from 'react';
|
||||
import React, { memo } from 'react';
|
||||
import Link from './Link';
|
||||
|
||||
export default class URILink extends React.Component{
|
||||
export default memo((props) => {
|
||||
|
||||
constructor(props){
|
||||
super(props);
|
||||
var to = null;
|
||||
var uri = props.uri;
|
||||
if (!props.unencoded){
|
||||
uri = encodeURIComponent(uri);
|
||||
}
|
||||
|
||||
handleContextMenu(e){
|
||||
if (this.props.onContextMenu){
|
||||
this.props.onContextMenu(e);
|
||||
}
|
||||
switch (props.type){
|
||||
case 'playlist':
|
||||
to = '/playlist/'+uri;
|
||||
break;
|
||||
|
||||
case 'artist':
|
||||
to = '/artist/'+uri;
|
||||
break;
|
||||
|
||||
case 'album':
|
||||
to = '/album/'+uri;
|
||||
break;
|
||||
|
||||
case 'track':
|
||||
to = '/track/'+uri;
|
||||
break;
|
||||
|
||||
case 'user':
|
||||
to = '/user/'+uri;
|
||||
break;
|
||||
|
||||
case 'browse':
|
||||
to = '/library/browse/'+uri;
|
||||
break;
|
||||
|
||||
case 'recommendations':
|
||||
to = '/discover/recommendations/'+uri;
|
||||
break;
|
||||
|
||||
case 'search':
|
||||
var exploded = uri.split('%3A');
|
||||
to = '/search/'+exploded[1]+'/'+exploded[2];
|
||||
break;
|
||||
|
||||
default:
|
||||
to = null;
|
||||
}
|
||||
|
||||
render(){
|
||||
var to = null;
|
||||
var uri = this.props.uri;
|
||||
if (!this.props.unencoded){
|
||||
uri = encodeURIComponent(uri);
|
||||
}
|
||||
|
||||
switch (this.props.type){
|
||||
case 'playlist':
|
||||
to = '/playlist/'+uri;
|
||||
break;
|
||||
|
||||
case 'artist':
|
||||
to = '/artist/'+uri;
|
||||
break;
|
||||
|
||||
case 'album':
|
||||
to = '/album/'+uri;
|
||||
break;
|
||||
|
||||
case 'track':
|
||||
to = '/track/'+uri;
|
||||
break;
|
||||
|
||||
case 'user':
|
||||
to = '/user/'+uri;
|
||||
break;
|
||||
|
||||
case 'browse':
|
||||
to = '/library/browse/'+uri;
|
||||
break;
|
||||
|
||||
case 'recommendations':
|
||||
to = '/discover/recommendations/'+uri;
|
||||
break;
|
||||
|
||||
case 'search':
|
||||
var exploded = uri.split('%3A');
|
||||
to = '/search/'+exploded[1]+'/'+exploded[2];
|
||||
break;
|
||||
|
||||
default:
|
||||
to = null;
|
||||
}
|
||||
|
||||
if (uri){
|
||||
return (
|
||||
<Link
|
||||
className={this.props.className ? this.props.className : null}
|
||||
to={to}
|
||||
onContextMenu={e => this.handleContextMenu(e)}>
|
||||
{this.props.children}
|
||||
</Link>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<span className={this.props.className ? this.props.className : null}>
|
||||
{this.props.children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
if (uri){
|
||||
return (
|
||||
<Link
|
||||
className={props.className ? props.className : null}
|
||||
to={to}
|
||||
onContextMenu={e => (props.handleContextMenu ? props.handleContextMenu(e) : null)}>
|
||||
{props.children}
|
||||
</Link>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<span className={props.className ? props.className : null}>
|
||||
{props.children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user