All views done this time; Language file mapping for modals.. Will need to do destructuring in second sweep
This commit is contained in:
@ -11,43 +11,53 @@ import * as spotifyActions from '../../services/spotify/actions';
|
||||
import {
|
||||
isLoading,
|
||||
} from '../../util/helpers';
|
||||
import { i18n, I18n } from '../../locale';
|
||||
import { indexToArray } from '../../util/arrays';
|
||||
|
||||
class DiscoverCategories extends React.Component {
|
||||
componentDidMount() {
|
||||
const {
|
||||
categories,
|
||||
spotifyActions: {
|
||||
getCategories,
|
||||
},
|
||||
uiActions: {
|
||||
setWindowTitle,
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
// Check for an empty category index, or where we've only got one loaded
|
||||
// This would be the case if you've refreshed from within a category and only loaded
|
||||
// the single record.
|
||||
if (!this.props.categories || Object.keys(this.props.categories).length <= 1) {
|
||||
this.props.spotifyActions.getCategories();
|
||||
if (!categories || Object.keys(categories).length <= 1) {
|
||||
getCategories();
|
||||
}
|
||||
this.props.uiActions.setWindowTitle('Genre / Mood');
|
||||
setWindowTitle(i18n('discover.categories.title'));
|
||||
}
|
||||
|
||||
render() {
|
||||
if (isLoading(this.props.load_queue, ['spotify_browse/categories'])) {
|
||||
render = () => {
|
||||
const {
|
||||
load_queue,
|
||||
categories: categoriesProp,
|
||||
uiActions,
|
||||
} = this.props;
|
||||
|
||||
if (isLoading(load_queue, ['spotify_browse/categories'])) {
|
||||
return (
|
||||
<div className="view discover-categories-view">
|
||||
<Header icon="grid" title="Genre / Mood" />
|
||||
<Header icon="grid" title={i18n('discover.categories.title')} />
|
||||
<Loader body loading />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// convert categories object into simple array
|
||||
const categories = [];
|
||||
if (this.props.categories) {
|
||||
for (const key in this.props.categories) {
|
||||
if (this.props.categories.hasOwnProperty(key)) {
|
||||
categories.push(this.props.categories[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
const categories = indexToArray(categoriesProp);
|
||||
|
||||
return (
|
||||
<div className="view discover-categories-view">
|
||||
<Header uiActions={this.props.uiActions}>
|
||||
<Header uiActions={uiActions}>
|
||||
<Icon name="mood" type="material" />
|
||||
Genre / Mood
|
||||
<I18n path="discover.categories.title" />
|
||||
</Header>
|
||||
<section className="content-wrapper grid-wrapper">
|
||||
<CategoryGrid categories={categories} />
|
||||
@ -57,14 +67,7 @@ class DiscoverCategories extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Export our component
|
||||
*
|
||||
* We also integrate our global store, using connect()
|
||||
* */
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({
|
||||
const mapStateToProps = (state) => ({
|
||||
categories: state.spotify.categories,
|
||||
load_queue: state.ui.load_queue,
|
||||
});
|
||||
|
||||
@ -11,6 +11,7 @@ import * as uiActions from '../../services/ui/actions';
|
||||
import * as spotifyActions from '../../services/spotify/actions';
|
||||
import { isLoading } from '../../util/helpers';
|
||||
import { collate } from '../../util/format';
|
||||
import { i18n } from '../../locale';
|
||||
|
||||
class DiscoverCategory extends React.Component {
|
||||
componentDidMount() {
|
||||
@ -39,57 +40,93 @@ class DiscoverCategory extends React.Component {
|
||||
if (!prevCategory && category) this.setWindowTitle(category);
|
||||
}
|
||||
|
||||
setWindowTitle(category = this.props.category) {
|
||||
setWindowTitle = (category = this.props.category) => {
|
||||
const {
|
||||
uiActions: { setWindowTitle },
|
||||
} = this.props;
|
||||
|
||||
if (category) {
|
||||
this.props.uiActions.setWindowTitle(category.name);
|
||||
setWindowTitle(category.name);
|
||||
} else {
|
||||
this.props.uiActions.setWindowTitle('Genre / Mood');
|
||||
setWindowTitle(i18n('discover.category.title'));
|
||||
}
|
||||
}
|
||||
|
||||
loadCategory() {
|
||||
if (!this.props.category) {
|
||||
this.props.spotifyActions.getCategory(this.props.match.params.id);
|
||||
loadCategory = () => {
|
||||
const {
|
||||
category,
|
||||
match: {
|
||||
params: { id },
|
||||
},
|
||||
spotifyActions: {
|
||||
getCategory,
|
||||
getCategoryPlaylists,
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
if (!category) {
|
||||
getCategory(id);
|
||||
}
|
||||
|
||||
if (!this.props.category.playlists_uris) {
|
||||
this.props.spotifyActions.getCategoryPlaylists(this.props.match.params.id);
|
||||
if (!category.playlists_uris) {
|
||||
getCategoryPlaylists(id);
|
||||
}
|
||||
}
|
||||
|
||||
loadMore() {
|
||||
this.props.spotifyActions.getMore(
|
||||
this.props.category.playlists_more,
|
||||
loadMore = () => {
|
||||
const {
|
||||
spotifyActions: {
|
||||
getMore,
|
||||
},
|
||||
category: {
|
||||
playlists_more,
|
||||
},
|
||||
match: {
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
getMore(
|
||||
playlists_more,
|
||||
null,
|
||||
{
|
||||
type: 'SPOTIFY_CATEGORY_PLAYLISTS_LOADED_MORE',
|
||||
uri: `category:${this.props.match.params.id}`,
|
||||
uri: `category:${id}`,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (isLoading(this.props.load_queue, ['spotify_browse/categories/'])) {
|
||||
render = () => {
|
||||
const {
|
||||
category: categoryProp,
|
||||
playlists,
|
||||
load_queue,
|
||||
uiActions,
|
||||
} = this.props;
|
||||
|
||||
if (isLoading(load_queue, ['spotify_browse/categories/'])) {
|
||||
return (
|
||||
<div className="view discover-categories-view">
|
||||
<Header>
|
||||
<Icon name="mood" type="material" />
|
||||
{(this.props.category ? this.props.category.name : 'Category')}
|
||||
{(categoryProp ? categoryProp.name : i18n('discover.category.category'))}
|
||||
</Header>
|
||||
<Loader body loading />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.props.category) {
|
||||
if (!categoryProp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const category = collate(this.props.category, { playlists: this.props.playlists });
|
||||
const category = collate(categoryProp, { playlists });
|
||||
|
||||
return (
|
||||
<div className="view discover-categories-view">
|
||||
<Header uiActions={this.props.uiActions}>
|
||||
<Header uiActions={uiActions}>
|
||||
<Icon name="mood" type="material" />
|
||||
{category.name}
|
||||
</Header>
|
||||
@ -100,7 +137,7 @@ class DiscoverCategory extends React.Component {
|
||||
<LazyLoadListener
|
||||
loadKey={category.playlists_more}
|
||||
showLoader={category.playlists_more}
|
||||
loadMore={() => this.loadMore()}
|
||||
loadMore={this.loadMore}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -10,6 +10,7 @@ import * as uiActions from '../../services/ui/actions';
|
||||
import * as mopidyActions from '../../services/mopidy/actions';
|
||||
import * as spotifyActions from '../../services/spotify/actions';
|
||||
import { isLoading } from '../../util/helpers';
|
||||
import { i18n, I18n } from '../../locale';
|
||||
|
||||
class DiscoverFeatured extends React.Component {
|
||||
componentDidMount() {
|
||||
@ -23,7 +24,7 @@ class DiscoverFeatured extends React.Component {
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
setWindowTitle('Featured playlists');
|
||||
setWindowTitle(i18n('discover.featured.title'));
|
||||
if (!featured_playlists) getFeaturedPlaylists();
|
||||
}
|
||||
|
||||
@ -40,7 +41,7 @@ class DiscoverFeatured extends React.Component {
|
||||
getFeaturedPlaylists();
|
||||
}
|
||||
|
||||
handleContextMenu(e, item) {
|
||||
handleContextMenu = (e, item) => {
|
||||
const {
|
||||
uiActions: {
|
||||
showContextMenu,
|
||||
@ -70,7 +71,7 @@ class DiscoverFeatured extends React.Component {
|
||||
<div className="view discover-featured-view preserve-3d">
|
||||
<Header className="overlay" uiActions={uiActions}>
|
||||
<Icon name="star" type="material" />
|
||||
Featured playlists
|
||||
<I18n path="discover.featured.title" />
|
||||
</Header>
|
||||
<Loader body loading />
|
||||
</div>
|
||||
@ -90,7 +91,7 @@ class DiscoverFeatured extends React.Component {
|
||||
const options = (
|
||||
<a className="button button--no-hover" onClick={this.onRefresh}>
|
||||
<Icon name="refresh" />
|
||||
Refresh
|
||||
<I18n path="actions.refresh" />
|
||||
</a>
|
||||
);
|
||||
|
||||
@ -98,7 +99,7 @@ class DiscoverFeatured extends React.Component {
|
||||
<div className="view discover-featured-view preserve-3d">
|
||||
<Header uiActions={uiActions} options={options}>
|
||||
<Icon name="star" type="material" />
|
||||
Featured playlists
|
||||
<I18n path="discover.featured.title" />
|
||||
</Header>
|
||||
<section className="content-wrapper grid-wrapper">
|
||||
{items && <PlaylistGrid playlists={items} />}
|
||||
|
||||
@ -12,20 +12,37 @@ import * as uiActions from '../../services/ui/actions';
|
||||
import * as mopidyActions from '../../services/mopidy/actions';
|
||||
import * as spotifyActions from '../../services/spotify/actions';
|
||||
import { isLoading } from '../../util/helpers';
|
||||
import { collate } from '../../util/format';
|
||||
import { i18n, I18n } from '../../locale';
|
||||
|
||||
class DiscoverNewReleases extends React.Component {
|
||||
componentDidMount() {
|
||||
this.props.uiActions.setWindowTitle('New releases');
|
||||
const {
|
||||
new_releases,
|
||||
uiActions: {
|
||||
setWindowTitle,
|
||||
},
|
||||
spotifyActions: {
|
||||
getNewReleases,
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
if (!this.props.new_releases) {
|
||||
this.props.spotifyActions.getNewReleases();
|
||||
setWindowTitle(i18n('discover.new_releases.title'));
|
||||
|
||||
if (!new_releases) {
|
||||
getNewReleases();
|
||||
}
|
||||
}
|
||||
|
||||
loadMore() {
|
||||
this.props.spotifyActions.getMore(
|
||||
this.props.new_releases_more,
|
||||
loadMore = () => {
|
||||
const {
|
||||
new_releases_more,
|
||||
spotifyActions: {
|
||||
getMore,
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
getMore(
|
||||
new_releases_more,
|
||||
null,
|
||||
{
|
||||
type: 'SPOTIFY_NEW_RELEASES_LOADED',
|
||||
@ -33,19 +50,36 @@ class DiscoverNewReleases extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
playAlbum(e, album) {
|
||||
this.props.mopidyActions.playURIs([album.uri], album.uri);
|
||||
playAlbum = (album) => {
|
||||
const { mopidyActions: { playURIs } } = this.props;
|
||||
|
||||
playURIs([album.uri], album.uri);
|
||||
}
|
||||
|
||||
refresh = () => {
|
||||
const {
|
||||
uiActions: {
|
||||
hideContextMenu,
|
||||
},
|
||||
spotifyActions: {
|
||||
getNewReleases,
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
hideContextMenu();
|
||||
getNewReleases();
|
||||
}
|
||||
|
||||
handleContextMenu(e, item) {
|
||||
const { uriActions: { showContextMenu } } = this.props;
|
||||
|
||||
e.preventDefault();
|
||||
const data = {
|
||||
showContextMenu({
|
||||
e,
|
||||
context: 'album',
|
||||
uris: [item.uri],
|
||||
items: [item],
|
||||
};
|
||||
this.props.uiActions.showContextMenu(data);
|
||||
});
|
||||
}
|
||||
|
||||
renderIntro = ({ images: { large } = {} } = {}) => (
|
||||
@ -54,13 +88,21 @@ class DiscoverNewReleases extends React.Component {
|
||||
</div>
|
||||
);
|
||||
|
||||
render() {
|
||||
if (isLoading(this.props.load_queue, ['spotify_browse/new-releases'])) {
|
||||
render = () => {
|
||||
const {
|
||||
load_queue,
|
||||
new_releases,
|
||||
albums: albumsProp,
|
||||
new_releases_more,
|
||||
uiActions,
|
||||
} = this.props;
|
||||
|
||||
if (isLoading(load_queue, ['spotify_browse/new-releases'])) {
|
||||
return (
|
||||
<div className="view discover-new-releases-view">
|
||||
<Header>
|
||||
<Icon name="new_releases" type="material" />
|
||||
New releases
|
||||
<I18n path="discover.new_releases.title" />
|
||||
</Header>
|
||||
<Loader body loading />
|
||||
</div>
|
||||
@ -68,41 +110,41 @@ class DiscoverNewReleases extends React.Component {
|
||||
}
|
||||
|
||||
const albums = [];
|
||||
if (this.props.new_releases) {
|
||||
for (const uri of this.props.new_releases) {
|
||||
if (this.props.albums.hasOwnProperty(uri)) {
|
||||
albums.push(this.props.albums[uri]);
|
||||
if (new_releases) {
|
||||
for (const uri of new_releases) {
|
||||
if (albumsProp.hasOwnProperty(uri)) {
|
||||
albums.push(albumsProp[uri]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const options = (
|
||||
<a className="button button--no-hover" onClick={(e) => { this.props.uiActions.hideContextMenu(); this.props.spotifyActions.getNewReleases(); }}>
|
||||
<a className="button button--no-hover" onClick={this.refresh}>
|
||||
<Icon name="refresh" />
|
||||
Refresh
|
||||
<I18n path="actions.refresh" />
|
||||
</a>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="view discover-new-releases-view preserve-3d">
|
||||
<Header options={options} uiActions={this.props.uiActions}>
|
||||
<Header options={options} uiActions={uiActions}>
|
||||
<Icon name="new_releases" type="material" />
|
||||
New releases
|
||||
<I18n path="discover.new_releases.title" />
|
||||
</Header>
|
||||
<section className="content-wrapper grid-wrapper">
|
||||
<AlbumGrid albums={albums} />
|
||||
</section>
|
||||
<LazyLoadListener
|
||||
loadKey={this.props.new_releases_more}
|
||||
showLoader={this.props.new_releases_more}
|
||||
loadMore={() => this.loadMore()}
|
||||
loadKey={new_releases_more}
|
||||
showLoader={new_releases_more}
|
||||
loadMore={this.loadMore}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({
|
||||
const mapStateToProps = (state) => ({
|
||||
theme: state.ui.theme,
|
||||
load_queue: state.ui.load_queue,
|
||||
artists: state.core.artists,
|
||||
|
||||
@ -22,7 +22,8 @@ import {
|
||||
uriType,
|
||||
titleCase,
|
||||
} from '../../util/helpers';
|
||||
import { arrayOf } from '../../util/arrays';
|
||||
import { arrayOf, indexToArray } from '../../util/arrays';
|
||||
import { i18n, I18n } from '../../locale';
|
||||
|
||||
class Discover extends React.Component {
|
||||
constructor(props) {
|
||||
@ -150,15 +151,25 @@ class Discover extends React.Component {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.uiActions.setWindowTitle('Discover');
|
||||
const {
|
||||
uiActions: {
|
||||
setWindowTitle,
|
||||
},
|
||||
match: {
|
||||
params: {
|
||||
seeds,
|
||||
},
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
// We have seeds provided in the URL
|
||||
if (this.props.match.params.seeds) {
|
||||
this.handleURLSeeds(this.props.match.params.seeds);
|
||||
setWindowTitle(i18n('discover.recommendations.title'));
|
||||
|
||||
if (seeds) {
|
||||
this.handleURLSeeds();
|
||||
}
|
||||
}
|
||||
|
||||
componentDiUpdate = ({
|
||||
componentDidUpdate = ({
|
||||
match: {
|
||||
params: {
|
||||
seeds: prevSeeds,
|
||||
@ -172,63 +183,92 @@ class Discover extends React.Component {
|
||||
},
|
||||
},
|
||||
} = this.props;
|
||||
if (prevSeeds !== seeds) this.handleURLSeeds(seeds);
|
||||
if (prevSeeds !== seeds) this.handleURLSeeds();
|
||||
}
|
||||
|
||||
handleContextMenu(e) {
|
||||
var uri = 'iris:discover';
|
||||
if (this.state.seeds) {
|
||||
handleContextMenu = (e) => {
|
||||
const {
|
||||
recommendations: {
|
||||
tracks_uris,
|
||||
},
|
||||
uiActions: {
|
||||
showContextMenu,
|
||||
},
|
||||
tracks: tracksProp,
|
||||
} = this.props;
|
||||
const {
|
||||
seeds,
|
||||
} = this.state;
|
||||
|
||||
let uri = 'iris:discover';
|
||||
if (seeds) {
|
||||
uri += ':';
|
||||
for (var i = 0; i < this.state.seeds.length; i++) {
|
||||
for (let i = 0; i < seeds.length; i++) {
|
||||
if (i > 0) {
|
||||
uri += ',';
|
||||
}
|
||||
uri += this.state.seeds[i].split(':').join('_');
|
||||
uri += seeds[i].split(':').join('_');
|
||||
}
|
||||
}
|
||||
|
||||
const tracks = [];
|
||||
if (this.props.recommendations.tracks_uris && this.props.tracks) {
|
||||
for (var i = 0; i < this.props.recommendations.tracks_uris.length; i++) {
|
||||
var uri = this.props.recommendations.tracks_uris[i];
|
||||
if (this.props.tracks.hasOwnProperty(uri)) {
|
||||
tracks.push(this.props.tracks[uri]);
|
||||
}
|
||||
}
|
||||
}
|
||||
const tracks = indexToArray(tracksProp, tracks_uris);
|
||||
|
||||
const data = {
|
||||
showContextMenu({
|
||||
e,
|
||||
context: 'track',
|
||||
items: tracks,
|
||||
uris: arrayOf('uri', tracks),
|
||||
};
|
||||
this.props.uiActions.showContextMenu(data);
|
||||
});
|
||||
}
|
||||
|
||||
handleURLSeeds(seeds_string = this.props.match.params.seeds) {
|
||||
handleURLSeeds = () => {
|
||||
const {
|
||||
spotifyActions: {
|
||||
getArtist,
|
||||
getTrack,
|
||||
},
|
||||
match: {
|
||||
params: {
|
||||
seeds: seedsProp,
|
||||
},
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
// Rejoin if we've had to uri-encode these as strings
|
||||
// We'd need to do this if our URL has been encoded so the whole URL can become
|
||||
// it's own URI (eg iris:discover:spotify_artist_1234) where we can't use ":"
|
||||
const seeds = seeds_string.split('_').join(':').split(',');
|
||||
const seeds = seedsProp.split('_').join(':').split(',');
|
||||
|
||||
for (let i = 0; i < seeds.length; i++) {
|
||||
switch (uriType(seeds[i])) {
|
||||
case 'artist':
|
||||
this.props.spotifyActions.getArtist(seeds[i]);
|
||||
getArtist(seeds[i]);
|
||||
break;
|
||||
|
||||
case 'track':
|
||||
this.props.spotifyActions.getTrack(seeds[i]);
|
||||
getTrack(seeds[i]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ seeds });
|
||||
this.getRecommendations(seeds);
|
||||
this.setState(
|
||||
{ seeds },
|
||||
() => this.getRecommendations(),
|
||||
);
|
||||
}
|
||||
|
||||
getRecommendations(seeds = this.state.seeds, tunabilities = this.state.tunabilities) {
|
||||
getRecommendations = () => {
|
||||
const {
|
||||
seeds,
|
||||
tunabilities,
|
||||
} = this.state;
|
||||
const {
|
||||
spotifyActions: {
|
||||
getRecommendations: doGetRecommendations,
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
if (seeds.length > 0) {
|
||||
const digested_tunabilities = {};
|
||||
for (const key in tunabilities) {
|
||||
@ -247,37 +287,53 @@ class Discover extends React.Component {
|
||||
digested_tunabilities[`${key}_min`] = min.toString();
|
||||
}
|
||||
}
|
||||
this.props.spotifyActions.getRecommendations(seeds, 50, digested_tunabilities);
|
||||
doGetRecommendations(seeds, 50, digested_tunabilities);
|
||||
}
|
||||
}
|
||||
|
||||
playTracks() {
|
||||
this.props.mopidyActions.playURIs(this.props.recommendations.tracks_uris);
|
||||
playTracks = () => {
|
||||
const {
|
||||
mopidyActions: {
|
||||
playURIs,
|
||||
},
|
||||
recommendations: {
|
||||
tracks_uris,
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
playURIs(tracks_uris);
|
||||
}
|
||||
|
||||
removeSeed(index) {
|
||||
removeSeed = (index) => {
|
||||
const { seeds } = this.state;
|
||||
seeds.splice(index, 1);
|
||||
this.setState({ seeds });
|
||||
}
|
||||
|
||||
handleSelect(e, uri) {
|
||||
handleSelect = (e, uri) => {
|
||||
const { seeds } = this.state;
|
||||
seeds.push(uri);
|
||||
this.setState({ seeds });
|
||||
}
|
||||
|
||||
renderSeeds() {
|
||||
renderSeeds = () => {
|
||||
const {
|
||||
tracks,
|
||||
artists,
|
||||
} = this.props;
|
||||
const {
|
||||
seeds,
|
||||
} = this.state;
|
||||
const seeds_objects = [];
|
||||
|
||||
if (this.state.seeds.length > 0) {
|
||||
for (let i = 0; i < this.state.seeds.length; i++) {
|
||||
const uri = this.state.seeds[i];
|
||||
if (seeds.length > 0) {
|
||||
for (let i = 0; i < seeds.length; i++) {
|
||||
const uri = seeds[i];
|
||||
|
||||
switch (uriType(uri)) {
|
||||
case 'track':
|
||||
if (typeof (this.props.tracks[uri]) !== 'undefined') {
|
||||
seeds_objects.push(this.props.tracks[uri]);
|
||||
if (typeof (tracks[uri]) !== 'undefined') {
|
||||
seeds_objects.push(tracks[uri]);
|
||||
} else {
|
||||
seeds_objects.push({
|
||||
name: 'Loading...',
|
||||
@ -285,10 +341,9 @@ class Discover extends React.Component {
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'artist':
|
||||
if (typeof (this.props.artists[uri]) !== 'undefined') {
|
||||
seeds_objects.push(this.props.artists[uri]);
|
||||
if (typeof (artists[uri]) !== 'undefined') {
|
||||
seeds_objects.push(artists[uri]);
|
||||
} else {
|
||||
seeds_objects.push({
|
||||
name: 'Loading...',
|
||||
@ -296,7 +351,6 @@ class Discover extends React.Component {
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'genre':
|
||||
var name = getFromUri('genreid', uri);
|
||||
seeds_objects.push({
|
||||
@ -304,6 +358,8 @@ class Discover extends React.Component {
|
||||
uri,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -311,22 +367,26 @@ class Discover extends React.Component {
|
||||
return (
|
||||
<div className="seeds">
|
||||
{
|
||||
seeds_objects.map((seed, index) => {
|
||||
const type = uriType(seed.uri);
|
||||
let images = null;
|
||||
if (seed.images) {
|
||||
if (type == 'artist') {
|
||||
if (seed.images.length > 0) {
|
||||
images = seed.images[0];
|
||||
}
|
||||
} else {
|
||||
images = seed.images;
|
||||
}
|
||||
}
|
||||
seeds_objects.map((seed, index) => {
|
||||
const type = uriType(seed.uri);
|
||||
let images = null;
|
||||
if (seed.images) {
|
||||
if (type === 'artist') {
|
||||
if (seed.images.length > 0) {
|
||||
images = seed.images[0];
|
||||
}
|
||||
} else {
|
||||
images = seed.images;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`seed${seed.images ? ' has-thumbnail' : ''}`} key={seed.uri}>
|
||||
{images ? <URILink className="thumbnail-wrapper" type={type} uri={seed.uri}><Thumbnail images={images} circle={seed.type == 'artist'} size="small" /></URILink> : null}
|
||||
{images && (
|
||||
<URILink className="thumbnail-wrapper" type={type} uri={seed.uri}>
|
||||
<Thumbnail images={images} circle={seed.type === 'artist'} size="small" />
|
||||
</URILink>
|
||||
)}
|
||||
<div className="seed__details">
|
||||
<div className="seed__label">
|
||||
<span className="seed__label__text">{titleCase(type)}</span>
|
||||
@ -342,19 +402,19 @@ class Discover extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
setTunability(name, value) {
|
||||
setTunability = (name, value) => {
|
||||
const { tunabilities } = this.state;
|
||||
tunabilities[name].value = value;
|
||||
this.setState({ tunabilities });
|
||||
}
|
||||
|
||||
toggleTunability(name) {
|
||||
toggleTunability = (name) => {
|
||||
const { tunabilities } = this.state;
|
||||
tunabilities[name].enabled = !tunabilities[name].enabled;
|
||||
this.setState({ tunabilities });
|
||||
}
|
||||
|
||||
renderTunabilities() {
|
||||
renderTunabilities = () => {
|
||||
const addable_tunabilities = [];
|
||||
const enabled_tunabilities = [];
|
||||
for (const key in this.state.tunabilities) {
|
||||
@ -382,7 +442,7 @@ class Discover extends React.Component {
|
||||
<div className="field tunability range" key={tunability.name}>
|
||||
<div className="tunability__label">
|
||||
{titleCase(tunability.name)}
|
||||
<span className="remove" onClick={(e) => this.toggleTunability(tunability.name)}>
|
||||
<span className="remove" onClick={() => this.toggleTunability(tunability.name)}>
|
||||
<Icon name="close" />
|
||||
</span>
|
||||
</div>
|
||||
@ -401,38 +461,51 @@ class Discover extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderResults() {
|
||||
// Results not in
|
||||
if (!this.props.recommendations || this.props.recommendations.albums_uris === undefined || this.props.recommendations.artists_uris === undefined) {
|
||||
renderResults = () => {
|
||||
const {
|
||||
recommendations: {
|
||||
tracks_uris,
|
||||
albums_uris,
|
||||
artists_uris,
|
||||
} = {},
|
||||
tracks: tracksProp,
|
||||
artists: artistsProp,
|
||||
albums: albumsProp,
|
||||
} = this.props;
|
||||
const {
|
||||
seeds,
|
||||
} = this.state;
|
||||
|
||||
if (!albums_uris === undefined || artists_uris === undefined) {
|
||||
return <div className="content-wrapper recommendations-results" />;
|
||||
}
|
||||
|
||||
const tracks = [];
|
||||
if (this.props.recommendations.tracks_uris && this.props.tracks) {
|
||||
for (var i = 0; i < this.props.recommendations.tracks_uris.length; i++) {
|
||||
var uri = this.props.recommendations.tracks_uris[i];
|
||||
if (this.props.tracks.hasOwnProperty(uri)) {
|
||||
tracks.push(this.props.tracks[uri]);
|
||||
if (tracks_uris && tracksProp) {
|
||||
for (var i = 0; i < tracks_uris.length; i++) {
|
||||
var uri = tracks_uris[i];
|
||||
if (tracksProp.hasOwnProperty(uri)) {
|
||||
tracks.push(tracksProp[uri]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const artists = [];
|
||||
if (this.props.recommendations.artists_uris && this.props.artists) {
|
||||
for (var i = 0; i < this.props.recommendations.artists_uris.length; i++) {
|
||||
var uri = this.props.recommendations.artists_uris[i];
|
||||
if (this.props.artists.hasOwnProperty(uri)) {
|
||||
artists.push(this.props.artists[uri]);
|
||||
if (artists_uris && artistsProp) {
|
||||
for (var i = 0; i < artists_uris.length; i++) {
|
||||
var uri = artists_uris[i];
|
||||
if (artistsProp.hasOwnProperty(uri)) {
|
||||
artists.push(artistsProp[uri]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const albums = [];
|
||||
if (this.props.recommendations.albums_uris && this.props.albums) {
|
||||
for (var i = 0; i < this.props.recommendations.albums_uris.length; i++) {
|
||||
var uri = this.props.recommendations.albums_uris[i];
|
||||
if (this.props.albums.hasOwnProperty(uri)) {
|
||||
albums.push(this.props.albums[uri]);
|
||||
if (albums_uris && albumsProp) {
|
||||
for (var i = 0; i < albums_uris.length; i++) {
|
||||
var uri = albums_uris[i];
|
||||
if (albumsProp.hasOwnProperty(uri)) {
|
||||
albums.push(albumsProp[uri]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -443,13 +516,13 @@ class Discover extends React.Component {
|
||||
}
|
||||
|
||||
var uri = 'iris:discover';
|
||||
if (this.state.seeds) {
|
||||
if (seeds) {
|
||||
uri += ':';
|
||||
for (var i = 0; i < this.state.seeds.length; i++) {
|
||||
for (var i = 0; i < seeds.length; i++) {
|
||||
if (i > 0) {
|
||||
uri += ',';
|
||||
}
|
||||
uri += this.state.seeds[i].split(':').join('_');
|
||||
uri += seeds[i].split(':').join('_');
|
||||
}
|
||||
}
|
||||
|
||||
@ -458,10 +531,12 @@ class Discover extends React.Component {
|
||||
|
||||
<section className="col col--w70 tracks">
|
||||
<h4>
|
||||
Tracks
|
||||
<I18n path="discover.recommendations.tracks" />
|
||||
<div className="pull-right">
|
||||
<ContextMenuTrigger onTrigger={(e) => this.handleContextMenu(e)} />
|
||||
<button className="button button--primary" onClick={(e) => this.playTracks(e)}>Play all</button>
|
||||
<ContextMenuTrigger onTrigger={this.handleContextMenu} />
|
||||
<button className="button button--primary" onClick={this.playTracks} type="button">
|
||||
<I18n path="actions.play_all" />
|
||||
</button>
|
||||
</div>
|
||||
</h4>
|
||||
<TrackList className="discover-track-list" uri={uri} tracks={tracks} />
|
||||
@ -471,13 +546,13 @@ class Discover extends React.Component {
|
||||
|
||||
<div className="col col--w25 others">
|
||||
<section>
|
||||
<h4>Artists</h4>
|
||||
<RelatedArtists artists={artists} uiActions={this.props.uiActions} />
|
||||
<h4><I18n path="discover.recommendations.artists" /></h4>
|
||||
<RelatedArtists artists={artists} uiActions={uiActions} />
|
||||
</section>
|
||||
<br />
|
||||
<br />
|
||||
<section>
|
||||
<h4>Albums</h4>
|
||||
<h4><I18n path="discover.recommendations.albums" /></h4>
|
||||
<AlbumGrid className="grid--mini" albums={albums} />
|
||||
</section>
|
||||
</div>
|
||||
@ -486,14 +561,22 @@ class Discover extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const is_loading = isLoading(this.props.load_queue, ['spotify_recommendations']);
|
||||
render = () => {
|
||||
const {
|
||||
load_queue,
|
||||
theme,
|
||||
} = this.props;
|
||||
const {
|
||||
tunabilities,
|
||||
seeds,
|
||||
} = this.state;
|
||||
|
||||
const is_loading = isLoading(load_queue, ['spotify_recommendations']);
|
||||
const addable_tunabilities = [];
|
||||
for (const key in this.state.tunabilities) {
|
||||
if (this.state.tunabilities.hasOwnProperty(key)) {
|
||||
if (tunabilities.hasOwnProperty(key)) {
|
||||
const tunability = {
|
||||
|
||||
...this.state.tunabilities[key],
|
||||
...tunabilities[key],
|
||||
name: key,
|
||||
};
|
||||
|
||||
@ -509,30 +592,39 @@ class Discover extends React.Component {
|
||||
return (
|
||||
<div className="view discover-view preserve-3d">
|
||||
<div className="intro preserve-3d">
|
||||
|
||||
{this.props.theme == 'dark' && <Parallax image="/iris/assets/backgrounds/discover.jpg" />}
|
||||
|
||||
{theme === 'dark' && <Parallax image="/iris/assets/backgrounds/discover.jpg" />}
|
||||
<div className="intro__liner">
|
||||
<h1>Explore new music</h1>
|
||||
<h2>Add seeds and musical properties below to build your sound</h2>
|
||||
<h1><I18n path="discover.recommendations.body_title" /></h1>
|
||||
<h2><I18n path="discover.recommendations.body_subtitle" /></h2>
|
||||
<div className="intro__parameters">
|
||||
|
||||
{this.renderSeeds()}
|
||||
{this.renderTunabilities()}
|
||||
{this.state.seeds.length > 5 ? <p className="message error">Too many seeds! You can use up to a total of 5 seed tracks, artists and genres.</p> : null}
|
||||
|
||||
{seeds.length > 5 && (
|
||||
<p className="message error">
|
||||
<I18n path="discover.recommendations.too_many_seeds" />
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="intro__actions">
|
||||
|
||||
<AddSeedField onSelect={(e, uri) => this.handleSelect(e, uri)} />
|
||||
<DropdownField className="add-properties" name="Properties" options={addable_tunabilities} no_status_icon button="default" handleChange={(val) => { this.toggleTunability(val); }} />
|
||||
<DropdownField
|
||||
className="add-properties"
|
||||
name="Properties"
|
||||
options={addable_tunabilities}
|
||||
no_status_icon
|
||||
button="default"
|
||||
handleChange={this.toggleTunability}
|
||||
/>
|
||||
<div className="intro__actions__separator" />
|
||||
<span className={`submit button button--primary button--large${is_loading ? ' button--working' : ''}`} onClick={(e) => this.getRecommendations()}>
|
||||
<span
|
||||
className={`submit button button--primary button--large${is_loading ? ' button--working' : ''}`}
|
||||
onClick={this.getRecommendations}
|
||||
>
|
||||
<Icon name="explore" />
|
||||
|
||||
Find recommendations
|
||||
<I18n path="discover.recommendations.find_recommendations" afterContent>
|
||||
{' '}
|
||||
</I18n>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -545,7 +637,7 @@ class Discover extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({
|
||||
const mapStateToProps = (state) => ({
|
||||
theme: state.ui.theme,
|
||||
albums: state.core.albums,
|
||||
artists: state.core.artists,
|
||||
|
||||
Reference in New Issue
Block a user