Part 2 of functional component upgrade

This commit is contained in:
James Barnsley
2019-04-11 08:05:53 +12:00
parent 014da60b76
commit 413d93a181
14 changed files with 962 additions and 1379 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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

View File

@ -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>
);
});

View File

@ -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>
);
});

View File

@ -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>
);
});

View File

@ -1,6 +1,5 @@
import React from 'react';
import ReactDOM from 'react-dom';
import * as helpers from '../helpers';
import Link from './Link';

View File

@ -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>;
}
}
});

View File

@ -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>
);
});

View File

@ -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>
);
});

View File

@ -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>
);
});

View File

@ -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>
);
}
}
});