Lazy-rendering large library lists for faster rendering and filtering
This commit is contained in:
@ -12,6 +12,7 @@ import TrackList from '../../components/TrackList'
|
|||||||
import ArtistSentence from '../../components/ArtistSentence'
|
import ArtistSentence from '../../components/ArtistSentence'
|
||||||
import DropdownField from '../../components/DropdownField'
|
import DropdownField from '../../components/DropdownField'
|
||||||
import FilterField from '../../components/FilterField'
|
import FilterField from '../../components/FilterField'
|
||||||
|
import LazyLoadListener from '../../components/LazyLoadListener'
|
||||||
|
|
||||||
import * as helpers from '../../helpers'
|
import * as helpers from '../../helpers'
|
||||||
import * as coreActions from '../../services/core/actions'
|
import * as coreActions from '../../services/core/actions'
|
||||||
@ -25,7 +26,9 @@ class LibraryAlbums extends React.Component{
|
|||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
filter: ''
|
filter: '',
|
||||||
|
limit: 50,
|
||||||
|
per_page: 50
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,75 +117,7 @@ class LibraryAlbums extends React.Component{
|
|||||||
this.props.uiActions.set(data)
|
this.props.uiActions.set(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderView(albums){
|
renderView(){
|
||||||
if (!albums || albums.length <= 0) return null
|
|
||||||
|
|
||||||
if (this.props.view == 'list'){
|
|
||||||
var columns = [
|
|
||||||
{
|
|
||||||
label: 'Name',
|
|
||||||
name: 'name'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Artists',
|
|
||||||
name: 'artists'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Added',
|
|
||||||
name: 'added_at'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Tracks',
|
|
||||||
name: 'tracks_total'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Source',
|
|
||||||
name: 'source'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
return (
|
|
||||||
<List
|
|
||||||
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
|
|
||||||
rows={albums}
|
|
||||||
columns={columns}
|
|
||||||
className="album-list"
|
|
||||||
link_prefix={global.baseURL+"album/"} />
|
|
||||||
)
|
|
||||||
}else if (this.props.view == 'detail'){
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{
|
|
||||||
albums.map(album => {
|
|
||||||
return (
|
|
||||||
<div className="album" key={album.uri}>
|
|
||||||
<Link to={global.baseURL+'album/'+album.uri}>
|
|
||||||
<Thumbnail size="medium" images={album.images} />
|
|
||||||
</Link>
|
|
||||||
<div className="detail">
|
|
||||||
<Link to={global.baseURL+'album/'+album.uri}>
|
|
||||||
<h2>{ album.name }</h2>
|
|
||||||
</Link>
|
|
||||||
<h3><ArtistSentence className="grey-text" artists={album.artists} /></h3>
|
|
||||||
</div>
|
|
||||||
<div className="list-wrapper">
|
|
||||||
<TrackList tracks={album.tracks} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<AlbumGrid
|
|
||||||
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
|
|
||||||
albums={albums} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render(){
|
|
||||||
var albums = []
|
var albums = []
|
||||||
|
|
||||||
// Spotify library items
|
// Spotify library items
|
||||||
@ -222,6 +157,57 @@ class LibraryAlbums extends React.Component{
|
|||||||
albums = helpers.applyFilter('name', this.state.filter, albums)
|
albums = helpers.applyFilter('name', this.state.filter, albums)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply our lazy-load-rendering
|
||||||
|
var total_albums = albums.length;
|
||||||
|
albums = albums.slice(0, this.state.limit);
|
||||||
|
|
||||||
|
if (this.props.view == 'list'){
|
||||||
|
var columns = [
|
||||||
|
{
|
||||||
|
label: 'Name',
|
||||||
|
name: 'name'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Artists',
|
||||||
|
name: 'artists'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Added',
|
||||||
|
name: 'added_at'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Tracks',
|
||||||
|
name: 'tracks_total'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Source',
|
||||||
|
name: 'source'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return (
|
||||||
|
<section className="content-wrapper">
|
||||||
|
<List
|
||||||
|
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
|
||||||
|
rows={albums}
|
||||||
|
columns={columns}
|
||||||
|
className="album-list"
|
||||||
|
link_prefix={global.baseURL+"album/"} />
|
||||||
|
<LazyLoadListener loading={this.state.limit < total_albums} loadMore={() => this.setState({limit: this.state.limit + this.state.per_page})} />
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<section className="content-wrapper">
|
||||||
|
<AlbumGrid
|
||||||
|
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
|
||||||
|
albums={albums} />
|
||||||
|
<LazyLoadListener loading={this.state.limit < total_albums} loadMore={() => this.setState({limit: this.state.limit + this.state.per_page})} />
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
var source_options = [
|
var source_options = [
|
||||||
{
|
{
|
||||||
value: 'all',
|
value: 'all',
|
||||||
@ -273,7 +259,7 @@ class LibraryAlbums extends React.Component{
|
|||||||
|
|
||||||
var options = (
|
var options = (
|
||||||
<span>
|
<span>
|
||||||
<FilterField handleChange={value => this.setState({filter: value})} />
|
<FilterField handleChange={value => this.setState({filter: value, limit: this.state.per_page})} />
|
||||||
<DropdownField icon="sort" name="Sort" value={this.props.sort} options={sort_options} reverse={this.props.sort_reverse} handleChange={val => {this.setSort(val); this.props.uiActions.hideContextMenu() }} />
|
<DropdownField icon="sort" name="Sort" value={this.props.sort} options={sort_options} reverse={this.props.sort_reverse} handleChange={val => {this.setSort(val); this.props.uiActions.hideContextMenu() }} />
|
||||||
<DropdownField icon="eye" name="View" value={this.props.view} options={view_options} handleChange={val => {this.props.uiActions.set({ library_albums_view: val }); this.props.uiActions.hideContextMenu() }} />
|
<DropdownField icon="eye" name="View" value={this.props.view} options={view_options} handleChange={val => {this.props.uiActions.set({ library_albums_view: val }); this.props.uiActions.hideContextMenu() }} />
|
||||||
<DropdownField icon="database" name="Source" value={this.props.source} options={source_options} handleChange={val => {this.props.uiActions.set({ library_albums_source: val}); this.props.uiActions.hideContextMenu() }} />
|
<DropdownField icon="database" name="Source" value={this.props.source} options={source_options} handleChange={val => {this.props.uiActions.set({ library_albums_source: val}); this.props.uiActions.hideContextMenu() }} />
|
||||||
@ -282,13 +268,8 @@ class LibraryAlbums extends React.Component{
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="view library-albums-view">
|
<div className="view library-albums-view">
|
||||||
|
|
||||||
<Header icon="cd" title="My albums" options={options} uiActions={this.props.uiActions} />
|
<Header icon="cd" title="My albums" options={options} uiActions={this.props.uiActions} />
|
||||||
|
{this.renderView()}
|
||||||
<section className="content-wrapper">
|
|
||||||
{this.renderView(albums)}
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,12 +4,12 @@ import { connect } from 'react-redux'
|
|||||||
import { bindActionCreators } from 'redux'
|
import { bindActionCreators } from 'redux'
|
||||||
import { Link } from 'react-router'
|
import { Link } from 'react-router'
|
||||||
|
|
||||||
import LazyLoadListener from '../../components/LazyLoadListener'
|
|
||||||
import Header from '../../components/Header'
|
import Header from '../../components/Header'
|
||||||
import ArtistGrid from '../../components/ArtistGrid'
|
import ArtistGrid from '../../components/ArtistGrid'
|
||||||
import List from '../../components/List'
|
import List from '../../components/List'
|
||||||
import DropdownField from '../../components/DropdownField'
|
import DropdownField from '../../components/DropdownField'
|
||||||
import FilterField from '../../components/FilterField'
|
import FilterField from '../../components/FilterField'
|
||||||
|
import LazyLoadListener from '../../components/LazyLoadListener'
|
||||||
|
|
||||||
import * as helpers from '../../helpers'
|
import * as helpers from '../../helpers'
|
||||||
import * as uiActions from '../../services/ui/actions'
|
import * as uiActions from '../../services/ui/actions'
|
||||||
@ -22,7 +22,9 @@ class LibraryArtists extends React.Component{
|
|||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
filter: ''
|
filter: '',
|
||||||
|
limit: 50,
|
||||||
|
per_page: 50
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,44 +87,7 @@ class LibraryArtists extends React.Component{
|
|||||||
this.props.uiActions.set(data)
|
this.props.uiActions.set(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderView(artists){
|
renderView(){
|
||||||
if (this.props.view == 'list'){
|
|
||||||
var columns = [
|
|
||||||
{
|
|
||||||
label: 'Name',
|
|
||||||
name: 'name'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Followers',
|
|
||||||
name: 'followers.total'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Popularity',
|
|
||||||
name: 'popularity'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
return (
|
|
||||||
<section className="content-wrapper">
|
|
||||||
<List
|
|
||||||
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
|
|
||||||
rows={artists}
|
|
||||||
columns={columns}
|
|
||||||
className="artist-list"
|
|
||||||
link_prefix={global.baseURL+"artist/"} />
|
|
||||||
</section>
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<section className="content-wrapper">
|
|
||||||
<ArtistGrid
|
|
||||||
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
|
|
||||||
artists={artists} />
|
|
||||||
</section>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render(){
|
|
||||||
var artists = []
|
var artists = []
|
||||||
|
|
||||||
// Mopidy library items
|
// Mopidy library items
|
||||||
@ -162,6 +127,49 @@ class LibraryArtists extends React.Component{
|
|||||||
artists = helpers.applyFilter('name', this.state.filter, artists)
|
artists = helpers.applyFilter('name', this.state.filter, artists)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply our lazy-load-rendering
|
||||||
|
var total_artists = artists.length;
|
||||||
|
artists = artists.slice(0, this.state.limit);
|
||||||
|
|
||||||
|
if (this.props.view == 'list'){
|
||||||
|
var columns = [
|
||||||
|
{
|
||||||
|
label: 'Name',
|
||||||
|
name: 'name'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Followers',
|
||||||
|
name: 'followers.total'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Popularity',
|
||||||
|
name: 'popularity'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return (
|
||||||
|
<section className="content-wrapper">
|
||||||
|
<List
|
||||||
|
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
|
||||||
|
rows={artists}
|
||||||
|
columns={columns}
|
||||||
|
className="artist-list"
|
||||||
|
link_prefix={global.baseURL+"artist/"} />
|
||||||
|
<LazyLoadListener loading={this.state.limit < total_artists} loadMore={() => this.setState({limit: this.state.limit + this.state.per_page})} />
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<section className="content-wrapper">
|
||||||
|
<ArtistGrid
|
||||||
|
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
|
||||||
|
artists={artists} />
|
||||||
|
<LazyLoadListener loading={this.state.limit < total_artists} loadMore={() => this.setState({limit: this.state.limit + this.state.per_page})} />
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
var source_options = [
|
var source_options = [
|
||||||
{
|
{
|
||||||
value: 'all',
|
value: 'all',
|
||||||
@ -205,7 +213,7 @@ class LibraryArtists extends React.Component{
|
|||||||
|
|
||||||
var options = (
|
var options = (
|
||||||
<span>
|
<span>
|
||||||
<FilterField handleChange={value => this.setState({filter: value})} />
|
<FilterField handleChange={value => this.setState({filter: value, limit: this.state.per_page})} />
|
||||||
<DropdownField icon="sort" name="Sort" value={ this.props.sort } options={sort_options} reverse={this.props.sort_reverse} handleChange={value => {this.setSort(value); this.props.uiActions.hideContextMenu() }} />
|
<DropdownField icon="sort" name="Sort" value={ this.props.sort } options={sort_options} reverse={this.props.sort_reverse} handleChange={value => {this.setSort(value); this.props.uiActions.hideContextMenu() }} />
|
||||||
<DropdownField icon="eye" name="View" value={ this.props.view } options={view_options} handleChange={value => {this.props.uiActions.set({ library_artists_view: value }); this.props.uiActions.hideContextMenu()}} />
|
<DropdownField icon="eye" name="View" value={ this.props.view } options={view_options} handleChange={value => {this.props.uiActions.set({ library_artists_view: value }); this.props.uiActions.hideContextMenu()}} />
|
||||||
<DropdownField icon="database" name="Source" value={this.props.source} options={source_options} handleChange={val => {this.props.uiActions.set({ library_artists_source: val}); this.props.uiActions.hideContextMenu() }} />
|
<DropdownField icon="database" name="Source" value={this.props.source} options={source_options} handleChange={val => {this.props.uiActions.set({ library_artists_source: val}); this.props.uiActions.hideContextMenu() }} />
|
||||||
@ -214,8 +222,8 @@ class LibraryArtists extends React.Component{
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="view library-artists-view">
|
<div className="view library-artists-view">
|
||||||
<Header icon="mic" title="My artists" options={options} uiActions={this.props.uiActions} />
|
<Header icon="mic" title="My artists" options={options} uiActions={this.props.uiActions} />
|
||||||
{ this.renderView(artists) }
|
{this.renderView()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import List from '../../components/List'
|
|||||||
import DropdownField from '../../components/DropdownField'
|
import DropdownField from '../../components/DropdownField'
|
||||||
import Header from '../../components/Header'
|
import Header from '../../components/Header'
|
||||||
import FilterField from '../../components/FilterField'
|
import FilterField from '../../components/FilterField'
|
||||||
|
import LazyLoadListener from '../../components/LazyLoadListener'
|
||||||
|
|
||||||
import * as helpers from '../../helpers'
|
import * as helpers from '../../helpers'
|
||||||
import * as coreActions from '../../services/core/actions'
|
import * as coreActions from '../../services/core/actions'
|
||||||
@ -23,7 +24,9 @@ class LibraryPlaylists extends React.Component{
|
|||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
filter: ''
|
filter: '',
|
||||||
|
limit: 50,
|
||||||
|
per_page: 50
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +90,7 @@ class LibraryPlaylists extends React.Component{
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderView(){
|
renderView(){
|
||||||
var playlists = []
|
var playlists = [];
|
||||||
|
|
||||||
// Spotify library items
|
// Spotify library items
|
||||||
if (this.props.spotify_library_playlists && (this.props.source == 'all' || this.props.source == 'spotify')){
|
if (this.props.spotify_library_playlists && (this.props.source == 'all' || this.props.source == 'spotify')){
|
||||||
@ -109,13 +112,17 @@ class LibraryPlaylists extends React.Component{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
playlists = helpers.sortItems(playlists, this.props.sort, this.props.sort_reverse)
|
playlists = helpers.sortItems(playlists, this.props.sort, this.props.sort_reverse);
|
||||||
playlists = helpers.removeDuplicates(playlists)
|
playlists = helpers.removeDuplicates(playlists);
|
||||||
|
|
||||||
if (this.state.filter !== ''){
|
if (this.state.filter !== ''){
|
||||||
playlists = helpers.applyFilter('name', this.state.filter, playlists)
|
playlists = helpers.applyFilter('name', this.state.filter, playlists);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply our lazy-load-rendering
|
||||||
|
var total_playlists = playlists.length;
|
||||||
|
playlists = playlists.slice(0, this.state.limit);
|
||||||
|
|
||||||
if (this.props.view == 'list'){
|
if (this.props.view == 'list'){
|
||||||
if (this.props.slim_mode){
|
if (this.props.slim_mode){
|
||||||
var columns = [
|
var columns = [
|
||||||
@ -161,6 +168,7 @@ class LibraryPlaylists extends React.Component{
|
|||||||
columns={columns}
|
columns={columns}
|
||||||
className="playlist-list"
|
className="playlist-list"
|
||||||
link_prefix={global.baseURL+"playlist/"} />
|
link_prefix={global.baseURL+"playlist/"} />
|
||||||
|
<LazyLoadListener loading={this.state.limit < total_playlists} loadMore={() => this.setState({limit: this.state.limit + this.state.per_page})} />
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -169,13 +177,13 @@ class LibraryPlaylists extends React.Component{
|
|||||||
<PlaylistGrid
|
<PlaylistGrid
|
||||||
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
|
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
|
||||||
playlists={playlists} />
|
playlists={playlists} />
|
||||||
|
<LazyLoadListener loading={this.state.limit < total_playlists} loadMore={() => this.setState({limit: this.state.limit + this.state.per_page})} />
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render(){
|
render(){
|
||||||
|
|
||||||
var source_options = [
|
var source_options = [
|
||||||
{
|
{
|
||||||
value: 'all',
|
value: 'all',
|
||||||
|
|||||||
Reference in New Issue
Block a user