Links for grid items, may need to add more context options
This commit is contained in:
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
|
// Release details
|
||||||
// These are automatically injected to built HTML
|
// These are automatically injected to built HTML
|
||||||
var build = "1547510940";
|
var build = "1547579515";
|
||||||
var version = "3.31.8";
|
var version = "3.31.8";
|
||||||
|
|
||||||
// Construct the script tag
|
// Construct the script tag
|
||||||
|
|||||||
@ -36,6 +36,7 @@ import LibraryAlbums from './views/library/LibraryAlbums';
|
|||||||
import LibraryTracks from './views/library/LibraryTracks';
|
import LibraryTracks from './views/library/LibraryTracks';
|
||||||
import LibraryPlaylists from './views/library/LibraryPlaylists';
|
import LibraryPlaylists from './views/library/LibraryPlaylists';
|
||||||
import LibraryBrowse from './views/library/LibraryBrowse';
|
import LibraryBrowse from './views/library/LibraryBrowse';
|
||||||
|
import LibraryBrowseDirectory from './views/library/LibraryBrowseDirectory';
|
||||||
|
|
||||||
import EditPlaylist from './views/modals/EditPlaylist';
|
import EditPlaylist from './views/modals/EditPlaylist';
|
||||||
import CreatePlaylist from './views/modals/CreatePlaylist';
|
import CreatePlaylist from './views/modals/CreatePlaylist';
|
||||||
@ -387,30 +388,31 @@ class App extends React.Component{
|
|||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path="/" component={Queue} />
|
<Route exact path="/" component={Queue} />
|
||||||
|
|
||||||
<Route path="/queue" component={Queue} />
|
<Route exact path="/queue" component={Queue} />
|
||||||
<Route path="/queue/history" component={QueueHistory} />
|
<Route exact path="/queue/history" component={QueueHistory} />
|
||||||
<Route path="/settings/debug" component={Debug} />
|
<Route exact path="/settings/debug" component={Debug} />
|
||||||
<Route path="/settings" component={Settings} />
|
<Route exact path="/settings" component={Settings} />
|
||||||
<Route path="/settings/service/:sub_view?" component={Settings} />
|
<Route exact path="/settings/service/:sub_view?" component={Settings} />
|
||||||
|
|
||||||
<Route path="/search/:type/:term?" component={Search} />
|
<Route exact path="/search/(:type/:term)?" component={Search} />
|
||||||
<Route path="/album/:uri" component={Album} />
|
<Route exact path="/album/:uri" component={Album} />
|
||||||
<Route path="/artist/:uri/:sub_view?" component={Artist} />
|
<Route exact path="/artist/:uri/:sub_view?" component={Artist} />
|
||||||
<Route path="/playlist/:uri" component={Playlist} />
|
<Route exact path="/playlist/:uri" component={Playlist} />
|
||||||
<Route path="/user/:uri" component={User} />
|
<Route exact path="/user/:uri" component={User} />
|
||||||
<Route path="/track/:uri" component={Track} />
|
<Route exact path="/track/:uri" component={Track} />
|
||||||
|
|
||||||
<Route path="/discover/recommendations/:seeds?" component={DiscoverRecommendations} />
|
<Route exact path="/discover/recommendations/:seeds?" component={DiscoverRecommendations} />
|
||||||
<Route path="/discover/featured" component={DiscoverFeatured} />
|
<Route exact path="/discover/featured" component={DiscoverFeatured} />
|
||||||
<Route path="/discover/categories" component={DiscoverCategories} />
|
<Route exact path="/discover/categories/:id" component={DiscoverCategory} />
|
||||||
<Route path="/discover/categories/:id" component={DiscoverCategory} />
|
<Route exact path="/discover/categories" component={DiscoverCategories} />
|
||||||
<Route path="/discover/new-releases" component={DiscoverNewReleases} />
|
<Route exact path="/discover/new-releases" component={DiscoverNewReleases} />
|
||||||
|
|
||||||
<Route path="/library/artists" component={LibraryArtists} />
|
<Route exact path="/library/artists" component={LibraryArtists} />
|
||||||
<Route path="/library/albums" component={LibraryAlbums} />
|
<Route exact path="/library/albums" component={LibraryAlbums} />
|
||||||
<Route path="/library/tracks" component={LibraryTracks} />
|
<Route exact path="/library/tracks" component={LibraryTracks} />
|
||||||
<Route path="/library/playlists" component={LibraryPlaylists} />
|
<Route exact path="/library/playlists" component={LibraryPlaylists} />
|
||||||
<Route path="/library/browse/:uri?" component={LibraryBrowse} />
|
<Route exact path="/library/browse/:uri" component={LibraryBrowseDirectory} />
|
||||||
|
<Route exact path="/library/browse" component={LibraryBrowse} />
|
||||||
|
|
||||||
<Route>
|
<Route>
|
||||||
<h1>I'm lost</h1>
|
<h1>I'm lost</h1>
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export default class CategoryGrid extends React.Component{
|
|||||||
key={category.id}
|
key={category.id}
|
||||||
type="category"
|
type="category"
|
||||||
item={category}
|
item={category}
|
||||||
onClick={e => {this.props.history.push(global.baseURL+'discover/categories/'+encodeURIComponent(category.id))}}
|
link={global.baseURL+'discover/categories/'+encodeURIComponent(category.id)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -107,16 +107,26 @@ export default class GridItem extends React.Component{
|
|||||||
images = this.props.item.icons;
|
images = this.props.item.icons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.props.link){
|
||||||
|
var link = this.props.link;
|
||||||
|
} else {
|
||||||
|
var link = global.baseURL+this.props.type+'/'+encodeURIComponent(item.uri);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link className={"grid__item grid__item--"+this.props.type} to={global.baseURL+this.props.type+'/'+encodeURIComponent(item.uri)} onClick={e => helpers.scrollTo()} onContextMenu={e => this.handleContextMenu(e)}>
|
<Link
|
||||||
<Thumbnail size="medium" className="grid__item__thumbnail" images={images} />
|
className={"grid__item grid__item--"+this.props.type}
|
||||||
<div className="grid__item__name">
|
to={link}
|
||||||
{item.name ? item.name : <span className="opaque-text">{item.uri}</span>}
|
onClick={e => helpers.scrollTo()}
|
||||||
</div>
|
onContextMenu={e => this.handleContextMenu(e)}>
|
||||||
<div className="grid__item__secondary">
|
<Thumbnail size="medium" className="grid__item__thumbnail" images={images} />
|
||||||
{this.props.show_source_icon ? <Icon name={helpers.sourceIcon(item.uri)} type="fontawesome" className="source" /> : null}
|
<div className="grid__item__name">
|
||||||
{this.renderSecondary(item)}
|
{item.name ? item.name : <span className="opaque-text">{item.uri}</span>}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="grid__item__secondary">
|
||||||
|
{this.props.show_source_icon ? <Icon name={helpers.sourceIcon(item.uri)} type="fontawesome" className="source" /> : null}
|
||||||
|
{this.renderSecondary(item)}
|
||||||
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
|
|
||||||
import React, { PropTypes } from 'react'
|
import React, { PropTypes } from 'react';
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux';
|
||||||
import { bindActionCreators } from 'redux'
|
import { bindActionCreators } from 'redux';
|
||||||
|
import { Redirect } from 'react-router';
|
||||||
|
import { Route, Switch } from 'react-router-dom';
|
||||||
|
|
||||||
import Link from '../components/Link';
|
import Link from '../components/Link';
|
||||||
import LazyLoadListener from '../components/LazyLoadListener'
|
import LazyLoadListener from '../components/LazyLoadListener'
|
||||||
@ -105,18 +107,7 @@ class Artist extends React.Component{
|
|||||||
this.props.uiActions.set({artist_albums_filter: value});
|
this.props.uiActions.set({artist_albums_filter: value});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSubViewMenu(){
|
renderOverview(){
|
||||||
return (
|
|
||||||
<div className="sub-views" id="sub-views-menu">
|
|
||||||
<Link className="option" activeClassName="active" to={global.baseURL+'artist/'+this.props.uri} scrollTo="sub-views-menu"><h4>Overview</h4></Link>
|
|
||||||
{this.props.artist.related_artists_uris ? <Link className="option" activeClassName="active" to={global.baseURL+'artist/'+this.props.uri+'/related-artists'} scrollTo="sub-views-menu"><h4>Related artists</h4></Link> : null}
|
|
||||||
<Link className="option" activeClassName="active" to={global.baseURL+'artist/'+this.props.uri+'/about'} scrollTo="sub-views-menu"><h4>About</h4></Link>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderBody(){
|
|
||||||
var scheme = helpers.uriSource(this.props.uri);
|
|
||||||
var artist = helpers.collate(
|
var artist = helpers.collate(
|
||||||
this.props.artist,
|
this.props.artist,
|
||||||
{
|
{
|
||||||
@ -134,144 +125,154 @@ class Artist extends React.Component{
|
|||||||
artist.albums = helpers.applyFilter('type', this.props.filter, artist.albums);
|
artist.albums = helpers.applyFilter('type', this.props.filter, artist.albums);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (this.props.match.params.sub_view){
|
var sort_options = [
|
||||||
|
{
|
||||||
|
value: null,
|
||||||
|
label: 'Default'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'name',
|
||||||
|
label: 'Name'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'date',
|
||||||
|
label: 'Date'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'tracks_total',
|
||||||
|
label: 'Tracks'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
case 'related-artists':
|
var filter_options = [
|
||||||
return (
|
{
|
||||||
<div className="body related-artists">
|
value: null,
|
||||||
<section className="grid-wrapper no-top-padding">
|
label: 'All'
|
||||||
<ArtistGrid artists={artist.related_artists} />
|
},
|
||||||
</section>
|
{
|
||||||
</div>
|
value: 'album',
|
||||||
)
|
label: 'Albums'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'single',
|
||||||
|
label: 'Singles'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'compilation',
|
||||||
|
label: 'Compilations'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
case 'about':
|
if (!artist.tracks_uris || (artist.tracks_uris && !artist.tracks) || (artist.tracks_uris.length !== artist.tracks.length)){
|
||||||
|
var is_loading_tracks = true;
|
||||||
var thumbnails = [];
|
} else {
|
||||||
if (artist.images && artist.images.length > 0){
|
var is_loading_tracks = false;
|
||||||
for (var i = 0; i < artist.images.length; i++){
|
|
||||||
thumbnails.push(
|
|
||||||
<div className="tile thumbnail-wrapper" key={i}>
|
|
||||||
<Thumbnail size="huge" canZoom images={artist.images[i]} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="body about">
|
|
||||||
|
|
||||||
<div className="col col--w40 tiles artist-stats">
|
|
||||||
{thumbnails}
|
|
||||||
{artist.followers ? <div className="tile"><span className="content"><Icon type="fontawesome" name="users" />{artist.followers.toLocaleString()} followers</span></div> : null}
|
|
||||||
{artist.popularity ? <div className="tile"><span className="content"><Icon type="fontawesome" name="fire" />{artist.popularity }% popularity</span></div> : null}
|
|
||||||
{artist.listeners ? <div className="tile"><span className="content"><Icon type="fontawesome" name="headphones" />{ artist.listeners.toLocaleString() } listeners</span></div> : null }
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col col--w60 biography">
|
|
||||||
<section>
|
|
||||||
{ artist.biography ? <div className="biography-text"><p>{artist.biography}</p><br />
|
|
||||||
<div className="mid_grey-text">Published: { artist.biography_publish_date }</div>
|
|
||||||
<div className="mid_grey-text">Origin: <a href={ artist.biography_link } target="_blank">{ artist.biography_link }</a></div></div> : null }
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
default:
|
|
||||||
|
|
||||||
var sort_options = [
|
|
||||||
{
|
|
||||||
value: null,
|
|
||||||
label: 'Default'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'name',
|
|
||||||
label: 'Name'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'date',
|
|
||||||
label: 'Date'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'tracks_total',
|
|
||||||
label: 'Tracks'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
var filter_options = [
|
|
||||||
{
|
|
||||||
value: null,
|
|
||||||
label: 'All'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'album',
|
|
||||||
label: 'Albums'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'single',
|
|
||||||
label: 'Singles'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'compilation',
|
|
||||||
label: 'Compilations'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
if (!artist.tracks_uris || (artist.tracks_uris && !artist.tracks) || (artist.tracks_uris.length !== artist.tracks.length)){
|
|
||||||
var is_loading_tracks = true;
|
|
||||||
} else {
|
|
||||||
var is_loading_tracks = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="body overview">
|
|
||||||
<div className={"top-tracks col col--w"+(artist.related_artists && artist.related_artists.length > 0 ? "70" : "100")}>
|
|
||||||
{artist.tracks ? <h4>Top tracks</h4> : null}
|
|
||||||
<div className="list-wrapper">
|
|
||||||
<TrackList className="artist-track-list" uri={artist.uri} tracks={artist.tracks} />
|
|
||||||
<LazyLoadListener showLoader={is_loading_tracks} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col col--w5"></div>
|
|
||||||
|
|
||||||
{artist.related_artists && artist.related_artists.length > 0 ? <div className="col col--w25 related-artists"><h4>Related artists</h4><div className="list-wrapper"><RelatedArtists artists={artist.related_artists.slice(0,6)} /></div><Link to={global.baseURL+'artist/'+artist.uri+'/related-artists'} scrollTo="sub-views-menu" className="button button--default">All related artists</Link></div> : null}
|
|
||||||
|
|
||||||
<div className="cf"></div>
|
|
||||||
|
|
||||||
{artist.albums ? <div className="albums">
|
|
||||||
<h4>
|
|
||||||
Albums
|
|
||||||
<DropdownField
|
|
||||||
icon="sort"
|
|
||||||
name="Sort"
|
|
||||||
value={this.props.sort}
|
|
||||||
options={sort_options}
|
|
||||||
selected_icon={this.props.sort ? (this.props.sort_reverse ? 'keyboard_arrow_up' : 'keyboard_arrow_down') : null}
|
|
||||||
handleChange={value => {this.setSort(value); this.props.uiActions.hideContextMenu() }}
|
|
||||||
/>
|
|
||||||
<DropdownField
|
|
||||||
icon="filter_list"
|
|
||||||
name="Filter"
|
|
||||||
value={this.props.filter}
|
|
||||||
options={filter_options}
|
|
||||||
handleChange={value => {this.setFilter(value); this.props.uiActions.hideContextMenu() }}
|
|
||||||
/>
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
<section className="grid-wrapper no-top-padding">
|
|
||||||
<AlbumGrid albums={artist.albums} />
|
|
||||||
<LazyLoadListener
|
|
||||||
loadKey={artist.albums_more}
|
|
||||||
showLoader={artist.albums_more}
|
|
||||||
loadMore={() => this.loadMore()}
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
</div> : null}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="body overview">
|
||||||
|
<div className={"top-tracks col col--w"+(artist.related_artists && artist.related_artists.length > 0 ? "70" : "100")}>
|
||||||
|
{artist.tracks ? <h4>Top tracks</h4> : null}
|
||||||
|
<div className="list-wrapper">
|
||||||
|
<TrackList className="artist-track-list" uri={artist.uri} tracks={artist.tracks} />
|
||||||
|
<LazyLoadListener showLoader={is_loading_tracks} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col col--w5"></div>
|
||||||
|
|
||||||
|
{artist.related_artists && artist.related_artists.length > 0 ? <div className="col col--w25 related-artists"><h4>Related artists</h4><div className="list-wrapper"><RelatedArtists artists={artist.related_artists.slice(0,6)} /></div><Link to={global.baseURL+'artist/'+artist.uri+'/related-artists'} scrollTo="sub-views-menu" className="button button--default">All related artists</Link></div> : null}
|
||||||
|
|
||||||
|
<div className="cf"></div>
|
||||||
|
|
||||||
|
{artist.albums ? <div className="albums">
|
||||||
|
<h4>
|
||||||
|
Albums
|
||||||
|
<DropdownField
|
||||||
|
icon="sort"
|
||||||
|
name="Sort"
|
||||||
|
value={this.props.sort}
|
||||||
|
options={sort_options}
|
||||||
|
selected_icon={this.props.sort ? (this.props.sort_reverse ? 'keyboard_arrow_up' : 'keyboard_arrow_down') : null}
|
||||||
|
handleChange={value => {this.setSort(value); this.props.uiActions.hideContextMenu() }}
|
||||||
|
/>
|
||||||
|
<DropdownField
|
||||||
|
icon="filter_list"
|
||||||
|
name="Filter"
|
||||||
|
value={this.props.filter}
|
||||||
|
options={filter_options}
|
||||||
|
handleChange={value => {this.setFilter(value); this.props.uiActions.hideContextMenu() }}
|
||||||
|
/>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
<section className="grid-wrapper no-top-padding">
|
||||||
|
<AlbumGrid albums={artist.albums} />
|
||||||
|
<LazyLoadListener
|
||||||
|
loadKey={artist.albums_more}
|
||||||
|
showLoader={artist.albums_more}
|
||||||
|
loadMore={() => this.loadMore()}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</div> : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRelatedArtists(){
|
||||||
|
var artist = helpers.collate(
|
||||||
|
this.props.artist,
|
||||||
|
{
|
||||||
|
artists: this.props.artists
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="body related-artists">
|
||||||
|
<section className="grid-wrapper no-top-padding">
|
||||||
|
<ArtistGrid artists={artist.related_artists} />
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAbout(){
|
||||||
|
var artist = helpers.collate(
|
||||||
|
this.props.artist,
|
||||||
|
{
|
||||||
|
artists: this.props.artists
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
var thumbnails = [];
|
||||||
|
if (artist.images && artist.images.length > 0){
|
||||||
|
for (var i = 0; i < artist.images.length; i++){
|
||||||
|
thumbnails.push(
|
||||||
|
<div className="tile thumbnail-wrapper" key={i}>
|
||||||
|
<Thumbnail size="huge" canZoom images={artist.images[i]} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="body about">
|
||||||
|
|
||||||
|
<div className="col col--w40 tiles artist-stats">
|
||||||
|
{thumbnails}
|
||||||
|
{artist.followers ? <div className="tile"><span className="content"><Icon type="fontawesome" name="users" />{artist.followers.toLocaleString()} followers</span></div> : null}
|
||||||
|
{artist.popularity ? <div className="tile"><span className="content"><Icon type="fontawesome" name="fire" />{artist.popularity }% popularity</span></div> : null}
|
||||||
|
{artist.listeners ? <div className="tile"><span className="content"><Icon type="fontawesome" name="headphones" />{ artist.listeners.toLocaleString() } listeners</span></div> : null }
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col col--w60 biography">
|
||||||
|
<section>
|
||||||
|
{ artist.biography ? <div className="biography-text"><p>{artist.biography}</p><br />
|
||||||
|
<div className="mid_grey-text">Published: { artist.biography_publish_date }</div>
|
||||||
|
<div className="mid_grey-text">Origin: <a href={ artist.biography_link } target="_blank">{ artist.biography_link }</a></div></div> : null }
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(){
|
render(){
|
||||||
@ -316,11 +317,49 @@ class Artist extends React.Component{
|
|||||||
{is_spotify ? <FollowButton uri={this.props.uri} removeText="Remove from library" addText="Add to library" is_following={this.inLibrary()} /> : null}
|
{is_spotify ? <FollowButton uri={this.props.uri} removeText="Remove from library" addText="Add to library" is_following={this.inLibrary()} /> : null}
|
||||||
<ContextMenuTrigger className="white" onTrigger={e => this.handleContextMenu(e)} />
|
<ContextMenuTrigger className="white" onTrigger={e => this.handleContextMenu(e)} />
|
||||||
</div>
|
</div>
|
||||||
{ this.renderSubViewMenu() }
|
<div className="sub-views" id="sub-views-menu">
|
||||||
|
<Link
|
||||||
|
nav
|
||||||
|
className="option"
|
||||||
|
activeClassName="active"
|
||||||
|
to={global.baseURL+'artist/'+this.props.uri+'/overview'}
|
||||||
|
scrollTo="sub-views-menu">
|
||||||
|
<h4>Overview</h4>
|
||||||
|
</Link>
|
||||||
|
{this.props.artist.related_artists_uris ? <Link
|
||||||
|
nav
|
||||||
|
className="option"
|
||||||
|
activeClassName="active"
|
||||||
|
to={global.baseURL+'artist/'+this.props.uri+'/related-artists'}
|
||||||
|
scrollTo="sub-views-menu">
|
||||||
|
<h4>Related artists</h4>
|
||||||
|
</Link> : null}
|
||||||
|
<Link
|
||||||
|
nav
|
||||||
|
className="option"
|
||||||
|
activeClassName="active"
|
||||||
|
to={global.baseURL+'artist/'+this.props.uri+'/about'}
|
||||||
|
scrollTo="sub-views-menu">
|
||||||
|
<h4>About</h4>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="content-wrapper">
|
<div className="content-wrapper">
|
||||||
{this.renderBody()}
|
<Switch>
|
||||||
|
<Route exact path="/artist/:id/related-artists">
|
||||||
|
{this.renderRelatedArtists()}
|
||||||
|
</Route>
|
||||||
|
<Route exact path="/artist/:id/about">
|
||||||
|
{this.renderAbout()}
|
||||||
|
</Route>
|
||||||
|
<Route exact path="/artist/:id/overview">
|
||||||
|
{this.renderOverview()}
|
||||||
|
</Route>
|
||||||
|
<Route>
|
||||||
|
{this.renderOverview()}
|
||||||
|
</Route>
|
||||||
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -230,7 +230,7 @@ class Playlist extends React.Component{
|
|||||||
<TrackList
|
<TrackList
|
||||||
uri={playlist.uri}
|
uri={playlist.uri}
|
||||||
className="playlist-track-list"
|
className="playlist-track-list"
|
||||||
context={context}
|
track_context={context}
|
||||||
tracks={playlist.tracks}
|
tracks={playlist.tracks}
|
||||||
removeTracks={tracks_indexes => this.removeTracks(tracks_indexes)}
|
removeTracks={tracks_indexes => this.removeTracks(tracks_indexes)}
|
||||||
reorderTracks={(indexes, index) => this.reorderTracks(indexes, index)}
|
reorderTracks={(indexes, index) => this.reorderTracks(indexes, index)}
|
||||||
|
|||||||
@ -52,7 +52,7 @@ class Search extends React.Component{
|
|||||||
componentWillReceiveProps(nextProps){
|
componentWillReceiveProps(nextProps){
|
||||||
|
|
||||||
// Query changed
|
// Query changed
|
||||||
if (nextProps.params.term !== this.props.params.term || nextProps.params.type !== this.props.params.type){
|
if (nextProps.term !== this.props.term || nextProps.type !== this.props.type){
|
||||||
this.digestUri(nextProps);
|
this.digestUri(nextProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,13 +69,13 @@ class Search extends React.Component{
|
|||||||
// Digest the URI query property
|
// Digest the URI query property
|
||||||
// Triggered when the URL changes
|
// Triggered when the URL changes
|
||||||
digestUri(props = this.props){
|
digestUri(props = this.props){
|
||||||
if (props.params && props.params.type && props.params.term){
|
if (props.params && props.type && props.term){
|
||||||
this.setState({
|
this.setState({
|
||||||
type: props.params.type,
|
type: props.type,
|
||||||
term: props.params.term
|
term: props.term
|
||||||
});
|
});
|
||||||
|
|
||||||
this.search(props.params.type, props.params.term);
|
this.search(props.type, props.term);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -429,6 +429,8 @@ class Search extends React.Component{
|
|||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state, ownProps) => {
|
||||||
return {
|
return {
|
||||||
|
type: ownProps.match.params.type,
|
||||||
|
term: ownProps.match.params.term,
|
||||||
mopidy_connected: state.mopidy.connected,
|
mopidy_connected: state.mopidy.connected,
|
||||||
albums: (state.core.albums ? state.core.albums : []),
|
albums: (state.core.albums ? state.core.albums : []),
|
||||||
artists: (state.core.artists ? state.core.artists : []),
|
artists: (state.core.artists ? state.core.artists : []),
|
||||||
|
|||||||
@ -24,7 +24,7 @@ class DiscoverCategory extends React.Component{
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps){
|
componentWillReceiveProps(nextProps){
|
||||||
if (nextProps.params.id != this.props.params.id){
|
if (nextProps.match.params.id != this.props.match.params.id){
|
||||||
this.loadCategory();
|
this.loadCategory();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,11 +43,11 @@ class DiscoverCategory extends React.Component{
|
|||||||
|
|
||||||
loadCategory(){
|
loadCategory(){
|
||||||
if (!this.props.category){
|
if (!this.props.category){
|
||||||
this.props.spotifyActions.getCategory(this.props.params.id);
|
this.props.spotifyActions.getCategory(this.props.match.params.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.props.category.playlists_uris){
|
if (!this.props.category.playlists_uris){
|
||||||
this.props.spotifyActions.getCategoryPlaylists(this.props.params.id);
|
this.props.spotifyActions.getCategoryPlaylists(this.props.match.params.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ class DiscoverCategory extends React.Component{
|
|||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
type: 'SPOTIFY_CATEGORY_PLAYLISTS_LOADED_MORE',
|
type: 'SPOTIFY_CATEGORY_PLAYLISTS_LOADED_MORE',
|
||||||
uri: 'category:'+this.props.params.id
|
uri: 'category:'+this.props.match.params.id
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -104,18 +104,11 @@ class DiscoverCategory extends React.Component{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export our component
|
|
||||||
*
|
|
||||||
* We also integrate our global store, using connect()
|
|
||||||
**/
|
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state, ownProps) => {
|
||||||
return {
|
return {
|
||||||
load_queue: state.ui.load_queue,
|
load_queue: state.ui.load_queue,
|
||||||
playlists: state.core.playlists,
|
playlists: state.core.playlists,
|
||||||
category: (state.spotify.categories && state.spotify.categories['category:'+ownProps.params.id] !== undefined ? state.spotify.categories['category:'+ownProps.params.id] : false )
|
category: (state.spotify.categories && state.spotify.categories['category:'+ownProps.match.params.id] !== undefined ? state.spotify.categories['category:'+ownProps.match.params.id] : false )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,23 +1,23 @@
|
|||||||
|
|
||||||
import React, { PropTypes } from 'react'
|
import React, { PropTypes } from 'react';
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux';
|
||||||
import { bindActionCreators } from 'redux'
|
import { bindActionCreators } from 'redux';
|
||||||
|
import { Route, Switch } from 'react-router-dom';
|
||||||
|
|
||||||
import Link from '../../components/Link';
|
import Link from '../../components/Link';
|
||||||
|
|
||||||
import Header from '../../components/Header'
|
import Header from '../../components/Header';
|
||||||
import List from '../../components/List'
|
import List from '../../components/List';
|
||||||
import TrackList from '../../components/TrackList'
|
import TrackList from '../../components/TrackList';
|
||||||
import GridItem from '../../components/GridItem'
|
import GridItem from '../../components/GridItem';
|
||||||
import DropdownField from '../../components/Fields/DropdownField'
|
import DropdownField from '../../components/Fields/DropdownField';
|
||||||
import Icon from '../../components/Icon'
|
import Icon from '../../components/Icon';
|
||||||
import URILink from '../../components/URILink'
|
import URILink from '../../components/URILink';
|
||||||
import ErrorBoundary from '../../components/ErrorBoundary'
|
import ErrorBoundary from '../../components/ErrorBoundary';
|
||||||
|
|
||||||
import * as helpers from '../../helpers'
|
import * as helpers from '../../helpers';
|
||||||
import * as uiActions from '../../services/ui/actions'
|
import * as uiActions from '../../services/ui/actions';
|
||||||
import * as mopidyActions from '../../services/mopidy/actions'
|
import * as mopidyActions from '../../services/mopidy/actions';
|
||||||
import * as spotifyActions from '../../services/spotify/actions'
|
|
||||||
|
|
||||||
class LibraryBrowse extends React.Component{
|
class LibraryBrowse extends React.Component{
|
||||||
|
|
||||||
@ -36,182 +36,19 @@ class LibraryBrowse extends React.Component{
|
|||||||
if (!this.props.mopidy_connected && nextProps.mopidy_connected){
|
if (!this.props.mopidy_connected && nextProps.mopidy_connected){
|
||||||
this.loadDirectory(nextProps);
|
this.loadDirectory(nextProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
// our uri changes
|
|
||||||
if (nextProps.match.params.uri != this.props.match.params.uri){
|
|
||||||
this.loadDirectory(nextProps);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loadDirectory(props = this.props){
|
loadDirectory(props = this.props){
|
||||||
if (props.mopidy_connected){
|
if (props.mopidy_connected){
|
||||||
var uri = null
|
var uri = null
|
||||||
if (props.match.params.uri !== undefined){
|
if (props.uri !== undefined){
|
||||||
uri = props.match.params.uri;
|
uri = props.uri;
|
||||||
}
|
}
|
||||||
this.props.mopidyActions.getDirectory(uri);
|
this.props.mopidyActions.getDirectory(uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
playAll(e, tracks){
|
render(){
|
||||||
var tracks_uris = helpers.arrayOf('uri',tracks);
|
|
||||||
this.props.mopidyActions.playURIs(tracks_uris, "iris:browse:"+this.props.match.params.uri);
|
|
||||||
this.props.uiActions.hideContextMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
goBack(e){
|
|
||||||
window.history.back();
|
|
||||||
this.props.uiActions.hideContextMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
renderBreadcrumbs(){
|
|
||||||
|
|
||||||
if (this.props.match.params.uri){
|
|
||||||
var parent_uri = this.props.match.params.uri;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parent_uri.startsWith('file://')){
|
|
||||||
var uri = parent_uri.replace('file:///','');
|
|
||||||
var uri_elements = uri.split('/');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<h4>
|
|
||||||
{uri_elements.map((uri_element, index) => {
|
|
||||||
|
|
||||||
// Reconstruct a URL to this element
|
|
||||||
var uri = "file://";
|
|
||||||
for (var i = 0; i <= index; i++){
|
|
||||||
uri += "/"+uri_elements[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span key={uri}>
|
|
||||||
{index > 0 ? <span> <Icon type="fontawesome" name="angle-right" /> </span> : null}
|
|
||||||
<URILink type="browse" uri={uri}>
|
|
||||||
{decodeURI(uri_element)}
|
|
||||||
</URILink>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</h4>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderSubdirectories(subdirectories){
|
|
||||||
if (this.props.view == 'list'){
|
|
||||||
return (
|
|
||||||
<List
|
|
||||||
nocontext
|
|
||||||
rows={subdirectories}
|
|
||||||
className="library-local-directory-list"
|
|
||||||
link_prefix={global.baseURL+'library/browse/'}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<div className="grid category-grid">
|
|
||||||
{
|
|
||||||
subdirectories.map(subdirectory => {
|
|
||||||
return (
|
|
||||||
<GridItem
|
|
||||||
key={subdirectory.uri}
|
|
||||||
type="category"
|
|
||||||
item={subdirectory}
|
|
||||||
onClick={e => {this.props.history.push(global.baseURL+'library/browse/'+encodeURIComponent(subdirectory.uri))}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderDirectory(directory){
|
|
||||||
var title = 'Browse';
|
|
||||||
var uri_exploded = this.props.match.params.uri.split(':');
|
|
||||||
if (uri_exploded.length > 0){
|
|
||||||
title = uri_exploded[0];
|
|
||||||
title = title.charAt(0).toUpperCase() + title.slice(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.props.directory || helpers.isLoading(this.props.load_queue,['mopidy_browse'])){
|
|
||||||
return (
|
|
||||||
<div className="view library-local-view">
|
|
||||||
<Header icon="music" title={title} uiActions={this.props.uiActions} />
|
|
||||||
<div className="body-loader loading">
|
|
||||||
<div className="loader"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
var tracks = (this.props.directory.tracks && this.props.directory.tracks.length > 0 ? this.props.directory.tracks : null);
|
|
||||||
tracks = helpers.sortItems(tracks, 'name');
|
|
||||||
|
|
||||||
var subdirectories = (this.props.directory.subdirectories && this.props.directory.subdirectories.length > 0 ? this.props.directory.subdirectories : null);
|
|
||||||
subdirectories = helpers.sortItems(subdirectories, 'name');
|
|
||||||
|
|
||||||
var view_options = [
|
|
||||||
{
|
|
||||||
label: 'Thumbnails',
|
|
||||||
value: 'thumbnails'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'List',
|
|
||||||
value: 'list'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
var options = (
|
|
||||||
<span>
|
|
||||||
<DropdownField
|
|
||||||
icon="visibility"
|
|
||||||
name="View"
|
|
||||||
value={this.props.view}
|
|
||||||
options={view_options}
|
|
||||||
handleChange={value => {this.props.uiActions.set({ library_directory_view: value }); this.props.uiActions.hideContextMenu()}}
|
|
||||||
/>
|
|
||||||
{tracks ? <a className="button button--no-hover" onClick={e => {this.props.uiActions.hideContextMenu(); this.playAll(e, tracks)}}>
|
|
||||||
<Icon name="play_circle_filled" />Play all
|
|
||||||
</a> : null }
|
|
||||||
<a className="button button--no-hover" onClick={e => {this.props.uiActions.hideContextMenu(); this.goBack(e)}}>
|
|
||||||
<Icon name="keyboard_backspace" />Back
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="view library-local-view">
|
|
||||||
<Header options={options} uiActions={this.props.uiActions}>
|
|
||||||
<Icon name="folder" type="material" />
|
|
||||||
{title}
|
|
||||||
</Header>
|
|
||||||
<section className="content-wrapper">
|
|
||||||
<ErrorBoundary>
|
|
||||||
|
|
||||||
{this.renderBreadcrumbs()}
|
|
||||||
|
|
||||||
{subdirectories ? this.renderSubdirectories(subdirectories) : null}
|
|
||||||
|
|
||||||
{tracks ? <TrackList
|
|
||||||
tracks={this.props.directory.tracks}
|
|
||||||
uri={"iris:browse:"+this.props.match.params.uri}
|
|
||||||
className="library-local-track-list"
|
|
||||||
/> : null }
|
|
||||||
|
|
||||||
</ErrorBoundary>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderIndex(){
|
|
||||||
var grid_items = []
|
var grid_items = []
|
||||||
if (this.props.directory){
|
if (this.props.directory){
|
||||||
for (var subdirectory of this.props.directory.subdirectories){
|
for (var subdirectory of this.props.directory.subdirectories){
|
||||||
@ -269,7 +106,7 @@ class LibraryBrowse extends React.Component{
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="view library-local-view">
|
<div className="view library-local-view">
|
||||||
<Header uiActions={this.props.uiActions}>
|
<Header>
|
||||||
<Icon name="folder" type="material" />
|
<Icon name="folder" type="material" />
|
||||||
Browse
|
Browse
|
||||||
</Header>
|
</Header>
|
||||||
@ -284,7 +121,6 @@ class LibraryBrowse extends React.Component{
|
|||||||
item={item}
|
item={item}
|
||||||
key={index}
|
key={index}
|
||||||
type="category"
|
type="category"
|
||||||
onClick={e => this.props.history.push(item.link)}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -296,14 +132,6 @@ class LibraryBrowse extends React.Component{
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(){
|
|
||||||
if (this.props.match.params.uri !== undefined && this.props.match.params.uri){
|
|
||||||
return this.renderDirectory();
|
|
||||||
} else {
|
|
||||||
return this.renderIndex();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state, ownProps) => {
|
||||||
@ -318,8 +146,7 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
const mapDispatchToProps = (dispatch) => {
|
const mapDispatchToProps = (dispatch) => {
|
||||||
return {
|
return {
|
||||||
uiActions: bindActionCreators(uiActions, dispatch),
|
uiActions: bindActionCreators(uiActions, dispatch),
|
||||||
mopidyActions: bindActionCreators(mopidyActions, dispatch),
|
mopidyActions: bindActionCreators(mopidyActions, dispatch)
|
||||||
spotifyActions: bindActionCreators(spotifyActions, dispatch)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
235
src/js/views/library/LibraryBrowseDirectory.js
Executable file
235
src/js/views/library/LibraryBrowseDirectory.js
Executable file
@ -0,0 +1,235 @@
|
|||||||
|
|
||||||
|
import React, { PropTypes } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { bindActionCreators } from 'redux';
|
||||||
|
|
||||||
|
import Link from '../../components/Link';
|
||||||
|
import Header from '../../components/Header';
|
||||||
|
import List from '../../components/List';
|
||||||
|
import TrackList from '../../components/TrackList';
|
||||||
|
import GridItem from '../../components/GridItem';
|
||||||
|
import DropdownField from '../../components/Fields/DropdownField';
|
||||||
|
import Icon from '../../components/Icon';
|
||||||
|
import URILink from '../../components/URILink';
|
||||||
|
import ErrorBoundary from '../../components/ErrorBoundary';
|
||||||
|
|
||||||
|
import * as helpers from '../../helpers';
|
||||||
|
import * as uiActions from '../../services/ui/actions';
|
||||||
|
import * as mopidyActions from '../../services/mopidy/actions';
|
||||||
|
import * as spotifyActions from '../../services/spotify/actions';
|
||||||
|
|
||||||
|
class LibraryBrowseDirectory extends React.Component{
|
||||||
|
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount(){
|
||||||
|
this.props.uiActions.setWindowTitle("Browse");
|
||||||
|
this.loadDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps){
|
||||||
|
|
||||||
|
// mopidy goes online
|
||||||
|
if (!this.props.mopidy_connected && nextProps.mopidy_connected){
|
||||||
|
this.loadDirectory(nextProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
// our uri changes
|
||||||
|
if (nextProps.uri && nextProps.uri !== this.props.uri){
|
||||||
|
|
||||||
|
console.log(nextProps.uri,this.props.uri);
|
||||||
|
|
||||||
|
//this.loadDirectory(nextProps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadDirectory(props = this.props){
|
||||||
|
if (props.mopidy_connected){
|
||||||
|
var uri = null
|
||||||
|
if (props.uri !== undefined){
|
||||||
|
uri = props.uri;
|
||||||
|
}
|
||||||
|
this.props.mopidyActions.getDirectory(uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
playAll(e, tracks){
|
||||||
|
var tracks_uris = helpers.arrayOf('uri',tracks);
|
||||||
|
this.props.mopidyActions.playURIs(tracks_uris, "iris:browse:"+this.props.uri);
|
||||||
|
this.props.uiActions.hideContextMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
goBack(e){
|
||||||
|
window.history.back();
|
||||||
|
this.props.uiActions.hideContextMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBreadcrumbs(){
|
||||||
|
|
||||||
|
if (this.props.uri){
|
||||||
|
var parent_uri = this.props.uri;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent_uri.startsWith('file://')){
|
||||||
|
var uri = parent_uri.replace('file:///','');
|
||||||
|
var uri_elements = uri.split('/');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<h4>
|
||||||
|
{uri_elements.map((uri_element, index) => {
|
||||||
|
|
||||||
|
// Reconstruct a URL to this element
|
||||||
|
var uri = "file://";
|
||||||
|
for (var i = 0; i <= index; i++){
|
||||||
|
uri += "/"+uri_elements[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span key={uri}>
|
||||||
|
{index > 0 ? <span> <Icon type="fontawesome" name="angle-right" /> </span> : null}
|
||||||
|
<URILink type="browse" uri={uri}>
|
||||||
|
{decodeURI(uri_element)}
|
||||||
|
</URILink>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</h4>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSubdirectories(subdirectories){
|
||||||
|
if (this.props.view == 'list'){
|
||||||
|
return (
|
||||||
|
<List
|
||||||
|
nocontext
|
||||||
|
rows={subdirectories}
|
||||||
|
className="library-local-directory-list"
|
||||||
|
link_prefix={global.baseURL+'library/browse/'}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<div className="grid category-grid">
|
||||||
|
{
|
||||||
|
subdirectories.map(subdirectory => {
|
||||||
|
return (
|
||||||
|
<GridItem
|
||||||
|
key={subdirectory.uri}
|
||||||
|
type="directory"
|
||||||
|
item={subdirectory}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
var title = 'Directory';
|
||||||
|
var uri_exploded = this.props.uri.split(':');
|
||||||
|
if (uri_exploded.length > 0){
|
||||||
|
title = uri_exploded[0];
|
||||||
|
title = title.charAt(0).toUpperCase() + title.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.props.directory || helpers.isLoading(this.props.load_queue,['mopidy_browse'])){
|
||||||
|
return (
|
||||||
|
<div className="view library-local-view">
|
||||||
|
<Header icon="music" title={title} uiActions={this.props.uiActions} />
|
||||||
|
<div className="body-loader loading">
|
||||||
|
<div className="loader"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tracks = (this.props.directory.tracks && this.props.directory.tracks.length > 0 ? this.props.directory.tracks : null);
|
||||||
|
tracks = helpers.sortItems(tracks, 'name');
|
||||||
|
|
||||||
|
var subdirectories = (this.props.directory.subdirectories && this.props.directory.subdirectories.length > 0 ? this.props.directory.subdirectories : null);
|
||||||
|
subdirectories = helpers.sortItems(subdirectories, 'name');
|
||||||
|
|
||||||
|
var view_options = [
|
||||||
|
{
|
||||||
|
label: 'Thumbnails',
|
||||||
|
value: 'thumbnails'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'List',
|
||||||
|
value: 'list'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
var options = (
|
||||||
|
<span>
|
||||||
|
<DropdownField
|
||||||
|
icon="visibility"
|
||||||
|
name="View"
|
||||||
|
value={this.props.view}
|
||||||
|
options={view_options}
|
||||||
|
handleChange={value => {this.props.uiActions.set({ library_directory_view: value }); this.props.uiActions.hideContextMenu()}}
|
||||||
|
/>
|
||||||
|
{tracks ? <a className="button button--no-hover" onClick={e => {this.props.uiActions.hideContextMenu(); this.playAll(e, tracks)}}>
|
||||||
|
<Icon name="play_circle_filled" />Play all
|
||||||
|
</a> : null }
|
||||||
|
<a className="button button--no-hover" onClick={e => {this.props.uiActions.hideContextMenu(); this.goBack(e)}}>
|
||||||
|
<Icon name="keyboard_backspace" />Back
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="view library-local-view">
|
||||||
|
<Header options={options} uiActions={this.props.uiActions}>
|
||||||
|
<Icon name="folder" type="material" />
|
||||||
|
{title}
|
||||||
|
</Header>
|
||||||
|
<section className="content-wrapper">
|
||||||
|
<ErrorBoundary>
|
||||||
|
|
||||||
|
{this.renderBreadcrumbs()}
|
||||||
|
|
||||||
|
{subdirectories ? this.renderSubdirectories(subdirectories) : null}
|
||||||
|
|
||||||
|
{tracks ? <TrackList
|
||||||
|
tracks={this.props.directory.tracks}
|
||||||
|
uri={"iris:browse:"+this.props.uri}
|
||||||
|
className="library-local-track-list"
|
||||||
|
/> : null }
|
||||||
|
|
||||||
|
</ErrorBoundary>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps) => {
|
||||||
|
var uri = decodeURIComponent(ownProps.match.params.uri);
|
||||||
|
return {
|
||||||
|
uri: uri,
|
||||||
|
load_queue: state.ui.load_queue,
|
||||||
|
mopidy_connected: state.mopidy.connected,
|
||||||
|
directory: state.mopidy.directory,
|
||||||
|
view: state.ui.library_directory_view
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => {
|
||||||
|
return {
|
||||||
|
uiActions: bindActionCreators(uiActions, dispatch),
|
||||||
|
mopidyActions: bindActionCreators(mopidyActions, dispatch),
|
||||||
|
spotifyActions: bindActionCreators(spotifyActions, dispatch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(LibraryBrowseDirectory)
|
||||||
Reference in New Issue
Block a user