Files
Iris/src/js/views/Queue.js

379 lines
11 KiB
JavaScript
Raw Normal View History

2019-09-05 15:36:11 +12:00
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import Link from '../components/Link';
import Icon from '../components/Icon';
import Parallax from '../components/Parallax';
import TrackList from '../components/TrackList';
import Dater from '../components/Dater';
import ArtistSentence from '../components/ArtistSentence';
import Thumbnail from '../components/Thumbnail';
import Header from '../components/Header';
import URILink from '../components/URILink';
import LazyLoadListener from '../components/LazyLoadListener';
import * as helpers from '../helpers';
import * as coreActions from '../services/core/actions';
import * as uiActions from '../services/ui/actions';
import * as pusherActions from '../services/pusher/actions';
import * as spotifyActions from '../services/spotify/actions';
import * as mopidyActions from '../services/mopidy/actions';
2019-01-15 16:57:50 +13:00
class Queue extends React.Component {
2019-08-13 16:53:39 +12:00
constructor(props) {
super(props);
this.state = {
limit: 50,
2019-09-05 15:36:11 +12:00
per_page: 50,
2019-08-13 16:53:39 +12:00
};
}
componentWillMount() {
// Before we mount, restore any limit defined in our location state
2019-09-05 15:36:11 +12:00
const state = this.props.location.state ? this.props.location.state : {};
2019-08-13 16:53:39 +12:00
if (state.limit) {
this.setState({
2019-09-05 15:36:11 +12:00
limit: state.limit,
2019-08-13 16:53:39 +12:00
});
}
}
componentDidMount() {
2019-09-05 15:36:11 +12:00
this.props.uiActions.setWindowTitle('Now playing');
2019-08-13 16:53:39 +12:00
}
shouldComponentUpdate(nextProps) {
return nextProps !== this.props;
}
componentWillReceiveProps(nextProps) {
const { added_from_uri } = nextProps;
if (added_from_uri && this.props.added_from_uri !== added_from_uri) {
const item_type = helpers.uriType(added_from_uri);
switch (item_type) {
2019-09-05 15:36:11 +12:00
case 'album':
2019-08-13 16:53:39 +12:00
this.props.coreActions.loadAlbum(added_from_uri);
break;
2019-09-05 15:36:11 +12:00
case 'artist':
2019-08-13 16:53:39 +12:00
this.props.coreActions.loadArtist(added_from_uri);
break;
2019-09-05 15:36:11 +12:00
case 'playlist':
2019-08-13 16:53:39 +12:00
this.props.coreActions.loadPlaylist(added_from_uri);
break;
}
}
}
loadMore() {
2019-09-05 15:36:11 +12:00
const new_limit = this.state.limit + this.state.per_page;
2019-08-13 16:53:39 +12:00
this.setState({ limit: new_limit });
// Set our pagination to location state
2019-09-05 15:36:11 +12:00
const state = this.props.location && this.props.location.state
? this.props.location.state
: {};
2019-08-13 16:53:39 +12:00
state.limit = new_limit;
2019-09-05 15:36:11 +12:00
this.props.history.replace({ state });
2019-08-13 16:53:39 +12:00
}
removeTracks(track_indexes) {
2019-09-05 15:36:11 +12:00
const tlids = [];
for (let i = 0; i < track_indexes.length; i++) {
const track = this.props.queue_tracks[track_indexes[i]];
2019-08-13 16:53:39 +12:00
if (track.tlid !== undefined) {
tlids.push(track.tlid);
}
}
if (tlids.length > 0) {
this.props.mopidyActions.removeTracks(tlids);
}
}
playTrack(track) {
this.props.mopidyActions.changeTrack(track.tlid);
}
playTracks(tracks) {
this.props.mopidyActions.changeTrack(tracks[0].tlid);
}
reorderTracks(indexes, index) {
this.props.mopidyActions.reorderTracklist(indexes, index);
}
renderQueueStats() {
2019-09-05 15:36:11 +12:00
const total_time = 0;
2019-08-13 16:53:39 +12:00
return (
<div className="queue-stats mid_grey-text">
<span>
{this.props.current_tracklist.length}
{' '}
tracks
2019-09-05 15:36:11 +12:00
</span>
2019-08-13 16:53:39 +12:00
&nbsp;&nbsp;|&nbsp;&nbsp;
{this.props.current_tracklist.length > 0 ? (
<Dater type="total-time" data={this.props.current_tracklist} />
) : (
<span>0 mins</span>
)}
</div>
);
}
renderArtwork(image) {
if (!image) {
return (
<div
className={`current-track__artwork ${
this.props.radio_enabled
2019-09-05 15:36:11 +12:00
? 'current-track__artwork--radio-enabled'
: ''
2019-08-13 16:53:39 +12:00
}`}
>
{this.props.radio_enabled ? (
<img
className="radio-overlay"
src="/iris/assets/radio-overlay.png"
/>
) : null}
2019-08-30 08:32:47 +12:00
<Thumbnail glow circle={this.props.radio_enabled} />
2019-08-13 16:53:39 +12:00
</div>
);
}
2019-09-05 15:36:11 +12:00
let uri = null;
2019-08-13 16:53:39 +12:00
if (this.props.current_track.album && this.props.current_track.album.uri) {
uri = this.props.current_track.album.uri;
}
return (
<div
className={`current-track__artwork ${
this.props.radio_enabled
2019-09-05 15:36:11 +12:00
? 'current-track__artwork--radio-enabled'
: ''
2019-08-13 16:53:39 +12:00
}`}
>
<URILink type="album" uri={uri}>
{this.props.radio_enabled ? (
<img
className="radio-overlay"
src="/iris/assets/radio-overlay.png"
/>
) : null}
2019-08-30 08:32:47 +12:00
<Thumbnail glow image={image} circle={this.props.radio_enabled} />
2019-08-13 16:53:39 +12:00
</URILink>
</div>
);
}
renderAddedFrom() {
const { added_from_uri } = this.props;
if (!added_from_uri) return null;
const item_type = helpers.uriType(added_from_uri);
2019-09-05 15:36:11 +12:00
const item_library = this.props[`${item_type}s`];
2019-08-13 16:53:39 +12:00
if (!item_library) return null;
const item = item_library[added_from_uri];
if (!item) return null;
return (
<div className="current-track__added-from">
<URILink
type={item_type}
uri={item.uri}
className="current-track__added-from__thumbnail"
>
<Thumbnail
images={item.images}
size="small"
2019-09-05 15:36:11 +12:00
circle={item_type == 'artist'}
2019-08-13 16:53:39 +12:00
/>
</URILink>
<div className="current-track__added-from__text">
{`Playing from `}
2019-08-13 16:53:39 +12:00
<URILink type={item_type} uri={item.uri}>
{item.name}
</URILink>
</div>
</div>
);
}
render() {
const { current_track, queue_tracks } = this.props;
const total_queue_tracks = queue_tracks.length;
const tracks = queue_tracks.slice(0, this.state.limit);
2019-09-05 15:36:11 +12:00
let current_track_image = null;
2019-08-13 16:53:39 +12:00
if (current_track && this.props.current_track_uri) {
if (current_track.images !== undefined && current_track.images) {
current_track_image = current_track.images.large;
}
}
2019-09-05 15:36:11 +12:00
const options = (
2019-08-13 16:53:39 +12:00
<span>
{this.props.spotify_enabled ? (
2019-09-05 15:36:11 +12:00
<Link className="button button--no-hover" to="/queue/radio">
2019-08-13 16:53:39 +12:00
<Icon name="radio" />
Radio
{this.props.radio && this.props.radio.enabled ? (
<span className="flag blue">On</span>
) : null}
</Link>
) : null}
2019-09-05 15:36:11 +12:00
<Link className="button button--no-hover" to="/queue/history">
2019-08-13 16:53:39 +12:00
<Icon name="history" />
History
</Link>
<a
className="button button--no-hover"
2019-09-05 15:36:11 +12:00
onClick={(e) => {
2019-08-13 16:53:39 +12:00
this.props.mopidyActions.clearTracklist();
this.props.uiActions.hideContextMenu();
}}
>
<Icon name="delete_sweep" />
Clear
</a>
2019-09-05 15:36:11 +12:00
<Link className="button button--no-hover" to="/queue/add-uri">
2019-08-13 16:53:39 +12:00
<Icon name="playlist_add" />
Add URI
</Link>
</span>
);
return (
<div className="view queue-view preserve-3d">
<Header options={options} uiActions={this.props.uiActions}>
<Icon name="play_arrow" type="material" />
Now playing
</Header>
2019-08-21 17:02:38 +12:00
<Parallax blur image={current_track_image} />
2019-08-13 16:53:39 +12:00
<div className="content-wrapper">
<div className="current-track">
{this.renderArtwork(current_track_image)}
<div className="current-track__details">
<div className="current-track__title">
{current_track ? (
<URILink type="track" uri={current_track.uri}>
{current_track.name}
</URILink>
) : (
<span>-</span>
)}
</div>
{current_track ? (
<ArtistSentence
className="current-track__artists"
artists={current_track.artists}
/>
) : (
<ArtistSentence className="current-track__artists" />
)}
{this.renderAddedFrom()}
</div>
</div>
<section className="list-wrapper">
<TrackList
show_source_icon
track_context="queue"
className="queue-track-list"
tracks={tracks}
2019-09-05 15:36:11 +12:00
removeTracks={(track_indexes) => this.removeTracks(track_indexes)}
playTracks={(tracks) => this.playTracks(tracks)}
playTrack={(track) => this.playTrack(track)}
reorderTracks={(indexes, index) => this.reorderTracks(indexes, index)}
2019-08-13 16:53:39 +12:00
/>
</section>
<LazyLoadListener
loadKey={
total_queue_tracks > this.state.limit
? this.state.limit
: total_queue_tracks
}
showLoader={this.state.limit < total_queue_tracks}
loadMore={() => this.loadMore()}
/>
</div>
</div>
);
}
2016-10-03 14:44:27 +13:00
}
const mapStateToProps = (state, ownProps) => {
2019-09-05 15:36:11 +12:00
let { current_track } = state.core;
const queue_tracks = [];
2019-08-13 16:53:39 +12:00
if (state.core.queue && state.core.tracks) {
2019-09-05 15:36:11 +12:00
for (const queue_track of state.core.queue) {
let track = { ...queue_track };
2019-08-13 16:53:39 +12:00
// If we have the track in our index, merge it in.
// We prioritise queue track over index track as queue has unique data, like which track
// is playing and tlids.
if (state.core.tracks.hasOwnProperty(track.uri)) {
2019-09-05 15:36:11 +12:00
track = { ...state.core.tracks[track.uri], ...track, playing: current_track && current_track.tlid == track.tlid };
2019-08-13 16:53:39 +12:00
}
// Now merge in our queue metadata
2019-09-05 15:36:11 +12:00
if (state.core.queue_metadata[`tlid_${track.tlid}`] !== undefined) {
track = {
...track,
...state.core.queue_metadata[`tlid_${track.tlid}`],
};
2019-08-13 16:53:39 +12:00
}
// Siphon off this track if it's a full representation of our current track (by tlid)
if (current_track && current_track.uri == track.uri) {
current_track = track;
}
// Now add our compiled track for our tracklist
queue_tracks.push(track);
}
}
return {
theme: state.ui.theme,
spotify_enabled: state.spotify.enabled,
radio: state.core.radio,
2019-09-05 15:36:11 +12:00
radio_enabled: !!(state.core.radio && state.core.radio.enabled),
2019-08-13 16:53:39 +12:00
artists: state.core.artists,
albums: state.core.albums,
playlists: state.core.playlists,
2019-09-05 15:36:11 +12:00
queue_tracks,
2019-08-13 16:53:39 +12:00
current_track_uri: state.core.current_track_uri,
2019-09-05 15:36:11 +12:00
current_track,
2019-08-13 16:53:39 +12:00
added_from_uri:
current_track && current_track.added_from
? current_track.added_from
2019-09-05 15:36:11 +12:00
: null,
2019-08-13 16:53:39 +12:00
};
};
2019-09-05 15:36:11 +12:00
const mapDispatchToProps = (dispatch) => ({
coreActions: bindActionCreators(coreActions, dispatch),
uiActions: bindActionCreators(uiActions, dispatch),
pusherActions: bindActionCreators(pusherActions, dispatch),
spotifyActions: bindActionCreators(spotifyActions, dispatch),
mopidyActions: bindActionCreators(mopidyActions, dispatch),
});
2019-08-13 16:53:39 +12:00
export default connect(
mapStateToProps,
2019-09-05 15:36:11 +12:00
mapDispatchToProps,
2019-08-13 16:53:39 +12:00
)(Queue);