Lazy loading cleanup

This commit is contained in:
James Barnsley
2018-09-03 08:24:49 +12:00
parent dbb5effbb9
commit 3344cf5db4
18 changed files with 10931 additions and 14038 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -98,7 +98,7 @@
// Release details
// These are automatically injected to built HTML
var build = "1535658713";
var build = "1535848770";
var version = "3.25.1";
// Construct the script tag

View File

@ -1,14 +1,17 @@
import React, { PropTypes } from 'react'
import React, { PropTypes } from 'react';
import * as helpers from '../helpers';
export default class LazyLoadListener extends React.Component{
constructor(props){
super(props);
this.state = {
loading: false
listening: false,
loadKey: this.props.loadKey
}
this.handleScroll = this.handleScroll.bind(this);
this.handleScroll = helpers.throttle(this.handleScroll.bind(this), 50);
}
componentWillMount(){
@ -19,21 +22,37 @@ export default class LazyLoadListener extends React.Component{
window.removeEventListener("scroll", this.handleScroll, false);
}
componentWillReceiveProps(nextProps){
if (nextProps.loadKey !== this.state.loadKey){
this.setState({
loadKey: nextProps.loadKey,
listening: true
});
}
}
handleScroll(e){
if ((window.innerHeight + window.scrollY) >= (document.body.offsetHeight - 80)){
if (!this.state.loading && this.props.loading){
this.setState({ loading: true })
this.props.loadMore();
}
}else if (this.state.loading){
this.setState({ loading: false })
}
if (this.state.listening){
// At, or nearly at the bottom of the page
if ((window.innerHeight + window.scrollY) >= (document.body.offsetHeight - 80)){
// Immediately stop listening to avoid duplicating pagination requests
this.setState(
{listening: false},
() => {
this.props.loadMore();
}
);
}
}
}
render(){
return (
<div className={"lazy-loader body-loader"+(this.state.loading || this.props.forceLoader ? ' loading' : '')}>
{this.state.loading || this.props.forceLoader ? <div className="loader"></div> : null}
<div className={"lazy-loader body-loader"+(this.props.showLoader ? ' loading' : '')}>
{this.props.showLoader ? <div className="loader"></div> : null}
</div>
)
}

View File

@ -337,6 +337,9 @@ export let formatAlbum = function(data){
if (data.date && !album.date) album.release_date = data.date;
// Actively overwrite "type" with "album_type"
if (data.album_type) album.type = data.album_type;
return album;
}
@ -364,7 +367,11 @@ export let formatArtist = function(data){
'biography_publish_date',
'related_artists_uris',
'albums_uris',
'tracks_uris'
'albums_total',
'albums_more',
'tracks_uris',
'tracks_total',
'tracks_more'
];
// Loop fields and import from data
@ -414,7 +421,9 @@ export let formatPlaylist = function(data){
'last_modified_date',
'can_edit',
'user_uri',
'tracks_uris'
'tracks_uris',
'tracks_total',
'tracks_more'
];
// Loop fields and import from data
@ -445,7 +454,10 @@ export let formatUser = function(data){
'provider',
'name',
'images',
'followers'
'followers',
'playlists_uris',
'playlists_total',
'playlists_more'
];
// Loop fields and import from data
@ -563,11 +575,12 @@ export let collate = function(obj, indexes = {}){
// Setup empty arrays for the appropriate reference objects
// This helps create a consistent object structure
if (obj.artists_uris !== undefined) obj.artists = [];
if (obj.albums_uris !== undefined) obj.albums = [];
if (obj.tracks_uris !== undefined) obj.tracks = [];
if (obj.users_uris !== undefined) obj.users = [];
if (obj.playlists_uris !== undefined) obj.playlists = [];
if (obj.artists_uris !== undefined) obj.artists = [];
if (obj.albums_uris !== undefined) obj.albums = [];
if (obj.tracks_uris !== undefined) obj.tracks = [];
if (obj.users_uris !== undefined) obj.users = [];
if (obj.playlists_uris !== undefined) obj.playlists = [];
if (obj.related_artists_uris !== undefined) obj.related_artists = [];
if (indexes.artists){
if (obj.artists_uris){
@ -577,6 +590,13 @@ export let collate = function(obj, indexes = {}){
}
}
}
if (obj.related_artists_uris){
for (var uri of obj.related_artists_uris){
if (indexes.artists[uri]){
obj.related_artists.push(indexes.artists[uri]);
}
}
}
if (obj.artist_uri){
if (indexes.artists[obj.artist_uri]){
obj.artist = indexes.artists[obj.artist_uri];

View File

@ -461,13 +461,10 @@ const CoreMiddleware = (function(){
break;
case 'LOAD_USER':
console.log(action);
if (
!action.force_reload &&
store.getState().core.playlists[action.uri] &&
store.getState().core.playlists[action.uri].albums_uris ){
store.getState().core.users[action.uri] &&
store.getState().core.users[action.uri].playlists_uris ){
console.info('Loading "'+action.uri+'" from index');
break;
}
@ -487,6 +484,14 @@ const CoreMiddleware = (function(){
break;
case 'LOAD_USER_PLAYLISTS':
if (
!action.force_reload &&
store.getState().core.users[action.uri] &&
store.getState().core.users[action.uri].playlists_uris ){
console.info('Loading "'+action.uri+'" playlists from index');
break;
}
switch (helpers.uriSource(action.uri)){
case 'spotify':
store.dispatch(spotifyActions.getUserPlaylists(action.uri));

View File

@ -1273,7 +1273,7 @@ export function getUserPlaylists(uri){
playlists.push(playlist);
}
dispatch(coreActions.userPlaylistsLoaded(uri, playlists, response.more, response.total));
dispatch(coreActions.userPlaylistsLoaded(uri, playlists, response.next, response.total));
},
error => {
dispatch(coreActions.handleException(

View File

@ -155,7 +155,11 @@ class Album extends React.Component{
<section className="list-wrapper">
<TrackList className="album-track-list" tracks={album.tracks} uri={this.props.params.uri} />
<LazyLoadListener loading={album.tracks_more} forceLoader={is_loading_tracks} loadMore={() => this.loadMore()}/>
<LazyLoadListener
loadKey={album.tracks_more}
showLoader={is_loading_tracks}
loadMore={() => this.loadMore()}
/>
</section>
</div>

View File

@ -115,34 +115,21 @@ class Artist extends React.Component{
renderBody(){
var scheme = helpers.uriSource(this.props.params.uri);
var related_artists = []
if (this.props.artist.related_artists_uris){
for (var i = 0; i < this.props.artist.related_artists_uris.length; i++){
var uri = this.props.artist.related_artists_uris[i]
if (this.props.artists.hasOwnProperty(uri)){
related_artists.push(this.props.artists[uri])
}
var artist = helpers.collate(
this.props.artist,
{
artists: this.props.artists,
albums: this.props.albums,
tracks: this.props.tracks
}
);
if (this.props.sort && artist.albums){
artist.albums = helpers.sortItems(artist.albums, this.props.sort, this.props.sort_reverse);
}
var albums = [];
if (this.props.artist.albums_uris){
var albums_uris = helpers.removeDuplicates(this.props.artist.albums_uris);
for (var i = 0; i < albums_uris.length; i++){
var uri = albums_uris[i];
if (this.props.albums.hasOwnProperty(uri)){
albums.push(this.props.albums[uri]);
}
}
if (this.props.sort){
albums = helpers.sortItems(albums, this.props.sort, this.props.sort_reverse);
}
if (this.props.filter){
albums = helpers.applyFilter('album_type', this.props.filter, albums);
}
if (this.props.filter && artist.albums){
artist.albums = helpers.applyFilter('type', this.props.filter, artist.albums);
}
switch (this.props.params.sub_view){
@ -151,7 +138,7 @@ class Artist extends React.Component{
return (
<div className="body related-artists">
<section className="grid-wrapper no-top-padding">
<ArtistGrid artists={related_artists} />
<ArtistGrid artists={artist.related_artists} />
</section>
</div>
)
@ -161,18 +148,18 @@ class Artist extends React.Component{
<div className="body about">
<div className="col w40 tiles artist-stats">
{this.props.artist.images ? <div className="tile thumbnail-wrapper"><Thumbnail size="huge" canZoom images={this.props.artist.images} /></div> : null}
{this.props.artist.images_additional ? <div className="tile thumbnail-wrapper"><Thumbnail size="huge" canZoom images={this.props.artist.images_additional} /></div> : null}
{this.props.artist.followers ? <div className="tile"><span className="content"><Icon type="fontawesome" name="users" />{this.props.artist.followers.toLocaleString()} followers</span></div> : null}
{this.props.artist.popularity ? <div className="tile"><span className="content"><Icon type="fontawesome" name="fire" />{this.props.artist.popularity }% popularity</span></div> : null}
{this.props.artist.listeners ? <div className="tile"><span className="content"><Icon type="fontawesome" name="headphones" />{ this.props.artist.listeners.toLocaleString() } listeners</span></div> : null }
{artist.images ? <div className="tile thumbnail-wrapper"><Thumbnail size="huge" canZoom images={artist.images} /></div> : null}
{artist.images_additional ? <div className="tile thumbnail-wrapper"><Thumbnail size="huge" canZoom images={artist.images_additional} /></div> : null}
{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 w60 biography">
<section>
{ this.props.artist.biography ? <div className="biography-text"><p>{this.props.artist.biography}</p><br />
<div className="grey-text">Published: { this.props.artist.biography_publish_date }</div>
<div className="grey-text">Origin: <a href={ this.props.artist.biography_link } target="_blank">{ this.props.artist.biography_link }</a></div></div> : null }
{ artist.biography ? <div className="biography-text"><p>{artist.biography}</p><br />
<div className="grey-text">Published: { artist.biography_publish_date }</div>
<div className="grey-text">Origin: <a href={ artist.biography_link } target="_blank">{ artist.biography_link }</a></div></div> : null }
</section>
</div>
</div>
@ -218,17 +205,7 @@ class Artist extends React.Component{
}
];
var tracks = [];
if (this.props.artist.tracks_uris && this.props.tracks){
for (var i = 0; i < this.props.artist.tracks_uris.length; i++){
var uri = this.props.artist.tracks_uris[i]
if (this.props.tracks.hasOwnProperty(uri)){
tracks.push(this.props.tracks[uri])
}
}
}
if (tracks.length <= 0 && helpers.isLoading(this.props.load_queue,['spotify_artists/'+helpers.getFromUri('artistid',this.props.params.uri)+'/top-tracks'])){
if (artist.tracks && artist.tracks.length <= 0 && helpers.isLoading(this.props.load_queue,['spotify_artists/'+helpers.getFromUri('artistid',artist.uri)+'/top-tracks'])){
var is_loading_tracks = true;
} else {
var is_loading_tracks = false;
@ -236,17 +213,17 @@ class Artist extends React.Component{
return (
<div className="body overview">
<div className={"top-tracks col w"+(related_artists.length > 0 ? "70" : "100")}>
<div className={"top-tracks col w"+(artist.related_artists && artist.related_artists.length > 0 ? "70" : "100")}>
<h4>Top tracks</h4>
<div className="list-wrapper">
<TrackList className="artist-track-list" uri={this.props.params.uri} tracks={tracks} />
<TrackList className="artist-track-list" uri={artist.uri} tracks={artist.tracks} />
<LazyLoadListener forceLoader={is_loading_tracks} />
</div>
</div>
<div className="col w5"></div>
{related_artists.length > 0 ? <div className="col w25 related-artists"><h4>Related artists</h4><div className="list-wrapper"><RelatedArtists artists={related_artists.slice(0,6)} /></div><Link to={global.baseURL+'artist/'+this.props.params.uri+'/related-artists'} className="button grey">All related artists</Link></div> : null}
{artist.related_artists ? <div className="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'} className="button grey">All related artists</Link></div> : null}
<div className="cf"></div>
@ -271,8 +248,12 @@ class Artist extends React.Component{
</h4>
<section className="grid-wrapper no-top-padding">
<AlbumGrid albums={albums} />
<LazyLoadListener loading={this.props.artist.albums_more} loadMore={() => this.loadMore()} />
<AlbumGrid albums={artist.albums} />
<LazyLoadListener
loadKey={artist.albums_more}
showLoader={artist.albums_more}
loadMore={() => this.loadMore()}
/>
</section>
</div>
</div>

View File

@ -213,7 +213,11 @@ class Playlist extends React.Component{
<section className="list-wrapper">
<TrackList uri={playlist.uri} className="playlist-track-list" context={context} tracks={playlist.tracks} removeTracks={ tracks_indexes => this.removeTracks(tracks_indexes) } reorderTracks={ (indexes, index) => this.reorderTracks(indexes, index) } />
<LazyLoadListener loading={playlist.tracks_more} forceLoader={is_loading_tracks} loadMore={() => this.loadMore()}/>
<LazyLoadListener
loadKey={playlist.tracks_more}
showLoader={is_loading_tracks}
loadMore={() => this.loadMore()}
/>
</section>
</div>
)

View File

@ -185,7 +185,8 @@ class Queue extends React.Component{
removeTracks={track_indexes => this.removeTracks(track_indexes)}
playTracks={tracks => this.playTracks(tracks)}
playTrack={track => this.playTrack(track)}
reorderTracks={(indexes, index) => this.reorderTracks(indexes, index)} />
reorderTracks={(indexes, index) => this.reorderTracks(indexes, index)}
/>
</section>
</div>

View File

@ -110,7 +110,11 @@ class User extends React.Component{
<section className="grid-wrapper">
<h4>Playlists</h4>
<PlaylistGrid playlists={user.playlists} />
<LazyLoadListener loading={user.playlists_more} loadMore={() => this.loadMore()} />
<LazyLoadListener
loadKey={user.playlists_more}
showLoader={user.playlists_more}
loadMore={() => this.loadMore()}
/>
</section>
</div>
</div>

View File

@ -97,7 +97,11 @@ class DiscoverCategory extends React.Component{
<section className="grid-wrapper">
<PlaylistGrid playlists={playlists} />
</section>
<LazyLoadListener loading={this.props.category.playlists_more} loadMore={ () => this.loadMore() }/>
<LazyLoadListener
loadKey={this.props.category.playlists_more}
showLoader={this.props.category.playlists_more}
loadMore={() => this.loadMore()}
/>
</div>
</div>
);

View File

@ -32,6 +32,11 @@ class DiscoverNewReleases extends React.Component{
}
loadMore(){
// TODO: Figure out how to check for duplicated requests
// perhaps we catch this at Spotify actions, by checking for
// pending requests on the same URL, and invalidate the Promise?
this.props.spotifyActions.getMore(
this.props.new_releases_more,
null,
@ -138,7 +143,11 @@ class DiscoverNewReleases extends React.Component{
<section className="content-wrapper grid-wrapper">
<AlbumGrid albums={albums} />
</section>
<LazyLoadListener loading={this.props.new_releases_more} loadMore={ () => this.loadMore() }/>
<LazyLoadListener
loadKey={this.props.new_releases_more}
showLoader={this.props.new_releases_more}
loadMore={() => this.loadMore()}
/>
</div>
);
}

View File

@ -183,7 +183,11 @@ class LibraryAlbums extends React.Component{
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})} />
<LazyLoadListener
loadKey={total_albums}
showLoader={this.state.limit < total_albums}
loadMore={() => this.setState({limit: this.state.limit + this.state.per_page})}
/>
</section>
)
} else {
@ -192,7 +196,11 @@ class LibraryAlbums extends React.Component{
<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})} />
<LazyLoadListener
loadKey={total_albums}
showLoader={this.state.limit < total_albums}
loadMore={() => this.setState({limit: this.state.limit + this.state.per_page})}
/>
</section>
)
}

View File

@ -159,7 +159,11 @@ class LibraryArtists extends React.Component{
columns={columns}
className="artist-list"
link_prefix={global.baseURL+"artist/"} />
<LazyLoadListener loading={this.state.limit < total_artists} loadMore={() => this.loadMore()} />
<LazyLoadListener
loadKey={total_artists}
showLoader={this.state.limit < total_artists}
loadMore={() => this.loadMore()}
/>
</section>
)
} else {
@ -168,7 +172,11 @@ class LibraryArtists extends React.Component{
<ArtistGrid
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
artists={artists} />
<LazyLoadListener loading={this.state.limit < total_artists} loadMore={() => this.loadMore()} />
<LazyLoadListener
loadKey={total_artists}
showLoader={this.state.limit < total_artists}
loadMore={() => this.loadMore()}
/>
</section>
)
}

View File

@ -155,7 +155,11 @@ class LibraryPlaylists extends React.Component{
columns={columns}
className="playlist-list"
link_prefix={global.baseURL+"playlist/"} />
<LazyLoadListener loading={this.state.limit < total_playlists} loadMore={() => this.setState({limit: this.state.limit + this.state.per_page})} />
<LazyLoadListener
loadKey="playlists"
loading={this.state.limit < total_playlists}
loadMore={() => this.setState({limit: this.state.limit + this.state.per_page})}
/>
</section>
)
} else {
@ -164,7 +168,11 @@ class LibraryPlaylists extends React.Component{
<PlaylistGrid
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
playlists={playlists} />
<LazyLoadListener loading={this.state.limit < total_playlists} loadMore={() => this.setState({limit: this.state.limit + this.state.per_page})} />
<LazyLoadListener
loadKey="playlists"
loading={this.state.limit < total_playlists}
loadMore={() => this.setState({limit: this.state.limit + this.state.per_page})}
/>
</section>
)
}

View File

@ -85,7 +85,11 @@ class LibraryTracks extends React.Component{
</Header>
<section className="content-wrapper">
<TrackList tracks={tracks} />
<LazyLoadListener loading={this.props.library_tracks_more} loadMore={() => this.loadMore()}/>
<LazyLoadListener
loadKey={this.props.library_tracks_more}
loading={this.props.library_tracks_more}
loadMore={() => this.loadMore()}
/>
</section>
</div>
);