Mapping Playlist view

This commit is contained in:
James Barnsley
2020-07-08 20:15:14 +12:00
parent 6225c6a6b0
commit 26cbddc34c
6 changed files with 473 additions and 385 deletions

View File

@ -97438,10 +97438,10 @@ exports.de = de;
/*!*******************************!*\
!*** ./src/js/locale/en.yaml ***!
\*******************************/
/*! exports provided: errors, specs, actions, time, services, sidebar, now_playing, album, artist, debug, search, settings, default */
/*! exports provided: errors, specs, actions, time, services, sidebar, now_playing, album, artist, debug, playlist, search, settings, default */
/***/ (function(module) {
module.exports = JSON.parse("{\"errors\":{\"uri_not_found\":\"Could not find anything with URI %{uri}\"},\"specs\":{\"tracks\":\"%{count} tracks\",\"followers\":\"%{count} followers\",\"listeners\":\"%{count} listeners\",\"plays\":\"%{count} plays\",\"popularity\":\"%{percent}% popularity\"},\"actions\":{\"play\":\"Play\",\"pause\":\"Pause\",\"stop\":\"Stop\",\"add_to_library\":\"Add to library\",\"remove_from_library\":\"Remove from library\",\"reset\":\"Reset\",\"back\":\"Back\",\"enable\":\"Enable\",\"disable\":\"Disable\",\"send\":\"Send\"},\"time\":{\"seconds\":{\"short\":\"secs\",\"singular\":\"second\",\"plural\":\"seconds\"},\"minutes\":{\"short\":\"mins\",\"singular\":\"minute\",\"plural\":\"minutes\"},\"hours\":{\"short\":\"hrs\",\"singular\":\"hour\",\"plural\":\"hours\"},\"days\":{\"short\":\"days\",\"singular\":\"day\",\"plural\":\"days\"},\"weeks\":{\"short\":\"wks\",\"singular\":\"week\",\"plural\":\"weeks\"},\"years\":{\"short\":\"yrs\",\"singular\":\"year\",\"plural\":\"years\"}},\"services\":{\"mopidy\":{\"title\":\"Mopidy\"},\"pusher\":{\"title\":\"Pusher\"},\"snapcast\":{\"title\":\"Snapcast\"}},\"sidebar\":{\"now_playing\":\"Now playing\",\"search\":\"Search\",\"discover\":\"Discover\",\"genre\":\"Genre / Mood\",\"featured_playlists\":\"Featured playlists\",\"new_releases\":\"New releases\",\"my_music\":\"My music\",\"playlists\":\"Playlists\",\"artists\":\"Artists\",\"albums\":\"Albums\",\"tracks\":\"Tracks\",\"browse\":\"Browse\",\"settings\":\"Settings\",\"not_connected\":\"%{name} not connected\"},\"now_playing\":{\"title\":\"Now playing\",\"context_actions\":{\"radio\":\"Radio\",\"history\":\"History\",\"add_uri\":\"Add URI\"},\"current_track\":{\"playing_from\":\"Playing from\",\"shuffle\":\"Shuffle\",\"clear\":\"Clear\",\"radio\":\"Radio\"}},\"album\":{\"title\":\"Album\",\"title_window\":\"%{name} by %{artist} (album)\",\"wiki\":{\"title\":\"About\",\"published\":\"Published: %{date}\"}},\"artist\":{\"title\":\"Artist\",\"title_window\":\"%{name} (artist)\",\"overview\":{\"title\":\"Overview\",\"top_tracks\":\"Top tracks\",\"related_artists\":{\"title\":\"Related artists\",\"more\":\"All related artists\"}},\"tracks\":{\"title\":\"Tracks\"},\"related_artists\":{\"title\":\"Related artists\"},\"about\":{\"title\":\"About\",\"source\":\"%{source} artist\",\"wiki\":{\"published\":\"Published: %{date}\",\"origin\":\"Origin: \"}}},\"debug\":{\"title\":\"Debug\",\"ui\":{\"title\":\"User interface\"},\"debug\":{\"title\":\"Debug\",\"overlay\":\"Show debug overlay\"},\"logging\":{\"title\":\"Logging\",\"mopidy\":\"Log Mopidy\",\"pusher\":\"Log Pusher\",\"actions\":\"Log Actions\",\"snapcast\":\"Log Snapcast\"},\"create_notification\":\"Create notification\",\"create_process\":\"Create process notification\",\"run_test\":\"Run test process\",\"call\":\"Call\",\"data\":\"Data\",\"examples\":\"Examples\"},\"search\":{\"title\":\"Search: %{term}\",\"context_actions\":{\"sort\":\"Sort\",\"source\":\"Sources (%{count})\"},\"placeholder\":\"Search\",\"artists\":{\"title\":\"Artists\",\"more\":\"All artists (%{count})\"},\"albums\":{\"title\":\"Albums\",\"more\":\"All albums (%{count})\"},\"playlists\":{\"title\":\"Playlists\",\"more\":\"All playlists (%{count})\"},\"tracks\":{\"title\":\"Tracks\"}},\"settings\":{\"title\":\"Settings\"}}");
module.exports = JSON.parse("{\"errors\":{\"uri_not_found\":\"Could not find anything with URI %{uri}\"},\"specs\":{\"tracks\":\"%{count} tracks\",\"followers\":\"%{count} followers\",\"listeners\":\"%{count} listeners\",\"plays\":\"%{count} plays\",\"popularity\":\"%{percent}% popularity\",\"edited\":\"Edited \"},\"actions\":{\"play\":\"Play\",\"pause\":\"Pause\",\"stop\":\"Stop\",\"add_to_library\":\"Add to library\",\"remove_from_library\":\"Remove from library\",\"reset\":\"Reset\",\"back\":\"Back\",\"enable\":\"Enable\",\"disable\":\"Disable\",\"send\":\"Send\",\"edit\":\"Edit\"},\"time\":{\"seconds\":{\"short\":\"secs\",\"singular\":\"second\",\"plural\":\"seconds\"},\"minutes\":{\"short\":\"mins\",\"singular\":\"minute\",\"plural\":\"minutes\"},\"hours\":{\"short\":\"hrs\",\"singular\":\"hour\",\"plural\":\"hours\"},\"days\":{\"short\":\"days\",\"singular\":\"day\",\"plural\":\"days\"},\"weeks\":{\"short\":\"wks\",\"singular\":\"week\",\"plural\":\"weeks\"},\"years\":{\"short\":\"yrs\",\"singular\":\"year\",\"plural\":\"years\"}},\"services\":{\"mopidy\":{\"title\":\"Mopidy\"},\"pusher\":{\"title\":\"Pusher\"},\"snapcast\":{\"title\":\"Snapcast\"}},\"sidebar\":{\"now_playing\":\"Now playing\",\"search\":\"Search\",\"discover\":\"Discover\",\"genre\":\"Genre / Mood\",\"featured_playlists\":\"Featured playlists\",\"new_releases\":\"New releases\",\"my_music\":\"My music\",\"playlists\":\"Playlists\",\"artists\":\"Artists\",\"albums\":\"Albums\",\"tracks\":\"Tracks\",\"browse\":\"Browse\",\"settings\":\"Settings\",\"not_connected\":\"%{name} not connected\"},\"now_playing\":{\"title\":\"Now playing\",\"context_actions\":{\"radio\":\"Radio\",\"history\":\"History\",\"add_uri\":\"Add URI\"},\"current_track\":{\"playing_from\":\"Playing from\",\"shuffle\":\"Shuffle\",\"clear\":\"Clear\",\"radio\":\"Radio\"}},\"album\":{\"title\":\"Album\",\"title_window\":\"%{name} by %{artist} (album)\",\"wiki\":{\"title\":\"About\",\"published\":\"Published: %{date}\"}},\"artist\":{\"title\":\"Artist\",\"title_window\":\"%{name} (artist)\",\"overview\":{\"title\":\"Overview\",\"top_tracks\":\"Top tracks\",\"related_artists\":{\"title\":\"Related artists\",\"more\":\"All related artists\"}},\"tracks\":{\"title\":\"Tracks\"},\"related_artists\":{\"title\":\"Related artists\"},\"about\":{\"title\":\"About\",\"source\":\"%{source} artist\",\"wiki\":{\"published\":\"Published: %{date}\",\"origin\":\"Origin: \"}}},\"debug\":{\"title\":\"Debug\",\"ui\":{\"title\":\"User interface\"},\"debug\":{\"title\":\"Debug\",\"overlay\":\"Show debug overlay\"},\"logging\":{\"title\":\"Logging\",\"mopidy\":\"Log Mopidy\",\"pusher\":\"Log Pusher\",\"actions\":\"Log Actions\",\"snapcast\":\"Log Snapcast\"},\"create_notification\":\"Create notification\",\"create_process\":\"Create process notification\",\"run_test\":\"Run test process\",\"call\":\"Call\",\"data\":\"Data\",\"examples\":\"Examples\"},\"playlist\":{\"title\":\"Playlist\",\"title_window\":\"%{name} (playlist)\"},\"search\":{\"title\":\"Search: %{term}\",\"context_actions\":{\"sort\":\"Sort\",\"source\":\"Sources (%{count})\"},\"placeholder\":\"Search\",\"artists\":{\"title\":\"Artists\",\"more\":\"All artists (%{count})\"},\"albums\":{\"title\":\"Albums\",\"more\":\"All albums (%{count})\"},\"playlists\":{\"title\":\"Playlists\",\"more\":\"All playlists (%{count})\"},\"tracks\":{\"title\":\"Tracks\"}},\"settings\":{\"title\":\"Settings\"}}");
/***/ }),
@ -114478,6 +114478,8 @@ var _helpers = __webpack_require__(/*! ../util/helpers */ "./src/js/util/helpers
var _format = __webpack_require__(/*! ../util/format */ "./src/js/util/format.js");
var _locale = __webpack_require__(/*! ../locale */ "./src/js/locale/index.js");
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@ -114520,304 +114522,22 @@ var Playlist = function (_React$Component) {
this.setWindowTitle();
loadPlaylist(uri);
}
}, {
key: 'setWindowTitle',
value: function setWindowTitle() {
var playlist = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.props.playlist;
if (playlist) {
this.props.uiActions.setWindowTitle(playlist.name + ' (playlist)');
} else {
this.props.uiActions.setWindowTitle('Playlist');
}
}
}, {
key: 'loadMore',
value: function loadMore() {
this.props.spotifyActions.getMore(this.props.playlist.tracks_more, {
parent_type: 'playlist',
parent_key: this.props.playlist.uri,
records_type: 'track'
});
}
}, {
key: 'handleContextMenu',
value: function handleContextMenu(e) {
var data = {
e: e,
context: 'playlist',
items: [this.props.playlist],
uris: [this.props.uri]
};
this.props.uiActions.showContextMenu(data);
}
}, {
key: 'play',
value: function play() {
this.props.mopidyActions.playPlaylist(this.props.playlist.uri);
}
}, {
key: 'follow',
value: function follow() {
if (this.props.allow_reporting) {
_reactGa2.default.event({ category: 'Playlist', action: 'Follow', label: this.props.playlist.uri });
}
this.props.spotifyActions.toggleFollowingPlaylist(this.props.playlist.uri, 'PUT');
}
// TODO: Once unfollowing occurs, remove playlist from global playlists list
}, {
key: 'unfollow',
value: function unfollow() {
if (this.props.allow_reporting) {
_reactGa2.default.event({ category: 'Playlist', action: 'Unfollow', label: this.props.playlist.uri });
}
this.props.spotifyActions.toggleFollowingPlaylist(this.props.playlist.uri, 'DELETE');
}
// TODO: Once deletion occurs, remove playlist from global playlists list
}, {
key: 'delete',
value: function _delete() {
this.props.mopidyActions.deletePlaylist(this.props.playlist.uri);
}
}, {
key: 'reorderTracks',
value: function reorderTracks(indexes, index) {
this.props.coreActions.reorderPlaylistTracks(this.props.playlist.uri, indexes, index, this.props.playlist.snapshot_id);
}
}, {
key: 'removeTracks',
value: function removeTracks(tracks_indexes) {
this.props.coreActions.removeTracksFromPlaylist(this.props.playlist.uri, tracks_indexes);
}
}, {
key: 'inLibrary',
value: function inLibrary() {
var library = (0, _helpers.uriSource)(this.props.uri) + '_library_playlists';
return this.props[library] && this.props[library].indexOf(this.props.uri) > -1;
}
}, {
key: 'renderActions',
value: function renderActions() {
var _this2 = this;
switch ((0, _helpers.uriSource)(this.props.uri)) {
case 'm3u':
return _react2.default.createElement(
'div',
{ className: 'actions' },
_react2.default.createElement(
'button',
{ className: 'button button--primary', onClick: function onClick(e) {
return _this2.play();
} },
'Play'
),
_react2.default.createElement(
_Link2.default,
{ className: 'button button--default', to: '/playlist/' + encodeURIComponent(this.props.uri) + '/edit' },
'Edit'
),
_react2.default.createElement(_ContextMenuTrigger2.default, { onTrigger: function onTrigger(e) {
return _this2.handleContextMenu(e);
} })
);
case 'spotify':
if (this.props.playlist.can_edit) {
return _react2.default.createElement(
'div',
{ className: 'actions' },
_react2.default.createElement(
'button',
{ className: 'button button--primary', onClick: function onClick(e) {
return _this2.play();
} },
'Play'
),
_react2.default.createElement(
_Link2.default,
{ className: 'button button--default', to: '/playlist/' + encodeURIComponent(this.props.uri) + '/edit' },
'Edit'
),
_react2.default.createElement(_ContextMenuTrigger2.default, { onTrigger: function onTrigger(e) {
return _this2.handleContextMenu(e);
} })
);
}
return _react2.default.createElement(
'div',
{ className: 'actions' },
_react2.default.createElement(
'button',
{ className: 'button button--primary', onClick: function onClick(e) {
return _this2.play();
} },
'Play'
),
_react2.default.createElement(_FollowButton2.default, { uri: this.props.uri, addText: 'Add to library', removeText: 'Remove from library', is_following: this.inLibrary() }),
_react2.default.createElement(_ContextMenuTrigger2.default, { onTrigger: function onTrigger(e) {
return _this2.handleContextMenu(e);
} })
);
default:
return _react2.default.createElement(
'div',
{ className: 'actions' },
_react2.default.createElement(
'button',
{ className: 'button button--primary', onClick: function onClick(e) {
return _this2.play();
} },
'Play'
),
_react2.default.createElement(_ContextMenuTrigger2.default, { onTrigger: function onTrigger(e) {
return _this2.handleContextMenu(e);
} })
);
}
}
}, {
key: 'render',
value: function render() {
var _this3 = this;
var playlist_id = (0, _helpers.getFromUri)('playlistid', this.props.uri);
if (!this.props.playlist) {
if ((0, _helpers.isLoading)(this.props.load_queue, ['spotify_playlists/' + playlist_id + '?'])) {
return _react2.default.createElement(_Loader2.default, { body: true, loading: true });
}
return _react2.default.createElement(
_ErrorMessage2.default,
{ type: 'not-found', title: 'Not found' },
_react2.default.createElement(
'p',
null,
'Could not find playlist with URI "',
encodeURIComponent(this.props.uri),
'"'
)
);
}
var playlist = (0, _format.collate)(this.props.playlist, { tracks: this.props.tracks, users: this.props.users });
var context = 'playlist';
if (playlist.can_edit) {
context = 'editable-playlist';
}
if (playlist.tracks_total !== 0 && (!playlist.tracks_uris || playlist.tracks_uris && !playlist.tracks || playlist.tracks_uris.length !== playlist.tracks.length)) {
var is_loading_tracks = true;
} else {
var is_loading_tracks = false;
}
return _react2.default.createElement(
'div',
{ className: 'view playlist-view content-wrapper preserve-3d' },
_react2.default.createElement(
'div',
{ className: 'thumbnail-wrapper' },
_react2.default.createElement(_Thumbnail2.default, { size: 'large', glow: true, canZoom: true, images: playlist.images, type: 'playlist' })
),
_react2.default.createElement(
'div',
{ className: 'title' },
_react2.default.createElement(
'h1',
null,
playlist.name
),
playlist.description ? _react2.default.createElement('h2', { className: 'description', dangerouslySetInnerHTML: { __html: playlist.description } }) : null,
_react2.default.createElement(
'ul',
{ className: 'details' },
!this.props.slim_mode ? _react2.default.createElement(
'li',
{ className: 'source' },
_react2.default.createElement(_Icon2.default, { type: 'fontawesome', name: (0, _helpers.sourceIcon)(playlist.uri) })
) : null,
playlist.user_uri ? _react2.default.createElement(
'li',
null,
_react2.default.createElement(
_URILink2.default,
{ type: 'user', uri: playlist.user_uri },
playlist.user ? playlist.user.name : (0, _helpers.getFromUri)('userid', playlist.user_uri)
)
) : null,
_react2.default.createElement(
'li',
null,
playlist.tracks_total ? playlist.tracks_total : '0',
' ',
'tracks'
),
!this.props.slim_mode && playlist.tracks && playlist.tracks_total > 0 ? _react2.default.createElement(
'li',
null,
_react2.default.createElement(_Dater.Dater, { type: 'total-time', data: playlist.tracks })
) : null,
!this.props.slim_mode && playlist.followers !== undefined ? _react2.default.createElement(
'li',
null,
_react2.default.createElement(_NiceNumber2.default, { value: playlist.followers }),
' ',
'followers'
) : null,
!this.props.slim_mode && playlist.last_modified_date ? _react2.default.createElement(
'li',
null,
'Edited',
_react2.default.createElement(_Dater.Dater, { type: 'ago', data: playlist.last_modified_date })
) : null
)
),
this.renderActions(),
_react2.default.createElement(
'section',
{ className: 'list-wrapper' },
_react2.default.createElement(_TrackList2.default, {
uri: playlist.uri,
className: 'playlist-track-list',
track_context: context,
tracks: playlist.tracks,
removeTracks: function removeTracks(tracks_indexes) {
return _this3.removeTracks(tracks_indexes);
},
reorderTracks: function reorderTracks(indexes, index) {
return _this3.reorderTracks(indexes, index);
}
}),
_react2.default.createElement(_LazyLoadListener2.default, {
loadKey: playlist.tracks_more,
showLoader: is_loading_tracks || playlist.tracks_more,
loadMore: function loadMore() {
return _this3.loadMore();
}
})
)
);
}
}]);
return Playlist;
}(_react2.default.Component);
var _initialiseProps = function _initialiseProps() {
var _this4 = this;
var _this2 = this;
this.componentDidUpdate = function (_ref) {
var prevUri = _ref.uri,
prevPlaylist = _ref.playlist,
prev_mopidy_connected = _ref.mopidy_connected;
var _props2 = _this4.props,
var _props2 = _this2.props,
uri = _props2.uri,
playlist = _props2.playlist,
mopidy_connected = _props2.mopidy_connected,
@ -114837,8 +114557,287 @@ var _initialiseProps = function _initialiseProps() {
}
}
if (!prevPlaylist && playlist) _this4.setWindowTitle(playlist);
if (prevUri !== uri && playlist) _this4.setWindowTitle(playlist);
if (!prevPlaylist && playlist) _this2.setWindowTitle(playlist);
if (prevUri !== uri && playlist) _this2.setWindowTitle(playlist);
};
this.setWindowTitle = function () {
var playlist = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _this2.props.playlist;
var setWindowTitle = _this2.props.uiActions.setWindowTitle;
setWindowTitle(playlist ? (0, _locale.i18n)('playlist.title_window', { name: playlist.name }) : (0, _locale.i18n)('playlist.title'));
};
this.loadMore = function () {
var _props3 = _this2.props,
getMore = _props3.spotifyActions.getMore,
_props3$playlist = _props3.playlist,
uri = _props3$playlist.uri,
tracks_more = _props3$playlist.tracks_more;
getMore(tracks_more, {
parent_type: 'playlist',
parent_key: uri,
records_type: 'track'
});
};
this.handleContextMenu = function (e) {
var _props4 = _this2.props,
showContextMenu = _props4.uiActions.showContextMenu,
playlist = _props4.playlist,
uri = _props4.uri;
showContextMenu({
e: e,
context: 'playlist',
items: [playlist],
uris: [uri]
});
};
this.play = function () {
var _props5 = _this2.props,
playPlaylist = _props5.mopidyActions.playPlaylist,
uri = _props5.playlist.uri;
playPlaylist(uri);
};
this.delete = function () {
var _props6 = _this2.props,
deletePlaylist = _props6.mopidyActions.deletePlaylist,
uri = _props6.playlist.uri;
deletePlaylist(uri);
};
this.reorderTracks = function (indexes, index) {
var _props7 = _this2.props,
reorderPlaylistTracks = _props7.coreActions.reorderPlaylistTracks,
_props7$playlist = _props7.playlist,
uri = _props7$playlist.uri,
snapshot_id = _props7$playlist.snapshot_id;
reorderPlaylistTracks(uri, indexes, index, snapshot_id);
};
this.removeTracks = function (tracks_indexes) {
_this2.props.coreActions.removeTracksFromPlaylist(_this2.props.playlist.uri, tracks_indexes);
};
this.inLibrary = function () {
var uri = _this2.props.uri;
var libraryName = (0, _helpers.uriSource)(uri) + '_library_playlists';
var _props$libraryName = _this2.props[libraryName],
library = _props$libraryName === undefined ? [] : _props$libraryName;
return library.indexOf(uri) > -1;
};
this.renderActions = function () {
var _props8 = _this2.props,
uri = _props8.uri,
can_edit = _props8.playlist.can_edit;
switch ((0, _helpers.uriSource)(_this2.props.uri)) {
case 'm3u':
return _react2.default.createElement(
'div',
{ className: 'actions' },
_react2.default.createElement(
'button',
{ className: 'button button--primary', onClick: _this2.play },
_react2.default.createElement(_locale.I18n, { path: 'actions.play' })
),
_react2.default.createElement(
_Link2.default,
{
className: 'button button--default',
to: '/playlist/' + encodeURIComponent(uri) + '/edit'
},
_react2.default.createElement(_locale.I18n, { path: 'actions.edit' })
),
_react2.default.createElement(_ContextMenuTrigger2.default, { onTrigger: _this2.handleContextMenu })
);
case 'spotify':
if (can_edit) {
return _react2.default.createElement(
'div',
{ className: 'actions' },
_react2.default.createElement(
'button',
{ className: 'button button--primary', onClick: _this2.play },
_react2.default.createElement(_locale.I18n, { path: 'actions.play' })
),
_react2.default.createElement(
_Link2.default,
{
className: 'button button--default',
to: '/playlist/' + encodeURIComponent(uri) + '/edit'
},
_react2.default.createElement(_locale.I18n, { path: 'actions.edit' })
),
_react2.default.createElement(_ContextMenuTrigger2.default, { onTrigger: _this2.handleContextMenu })
);
}
return _react2.default.createElement(
'div',
{ className: 'actions' },
_react2.default.createElement(
'button',
{ className: 'button button--primary', onClick: _this2.play },
_react2.default.createElement(_locale.I18n, { path: 'actions.play' })
),
_react2.default.createElement(_FollowButton2.default, {
uri: uri,
is_following: _this2.inLibrary()
}),
_react2.default.createElement(_ContextMenuTrigger2.default, { onTrigger: _this2.handleContextMenu })
);
default:
return _react2.default.createElement(
'div',
{ className: 'actions' },
_react2.default.createElement(
'button',
{
className: 'button button--primary',
onClick: _this2.play
},
_react2.default.createElement(_locale.I18n, { path: 'actions.play' })
),
_react2.default.createElement(_ContextMenuTrigger2.default, { onTrigger: _this2.handleContextMenu })
);
}
};
this.render = function () {
var _props9 = _this2.props,
uri = _props9.uri,
playlistProp = _props9.playlist,
load_queue = _props9.load_queue,
tracks = _props9.tracks,
users = _props9.users,
slim_mode = _props9.slim_mode;
var playlist_id = (0, _helpers.getFromUri)('playlistid', uri);
if (!playlistProp) {
if ((0, _helpers.isLoading)(load_queue, ['spotify_playlists/' + playlist_id + '?'])) {
return _react2.default.createElement(_Loader2.default, { body: true, loading: true });
}
return _react2.default.createElement(
_ErrorMessage2.default,
{ type: 'not-found', title: 'Not found' },
_react2.default.createElement(
'p',
null,
_react2.default.createElement(_locale.I18n, { path: 'errors.uri_not_found', uri: uri })
)
);
}
var playlist = (0, _format.collate)(playlistProp, { tracks: tracks, users: users });
var context = 'playlist';
if (playlist.can_edit) {
context = 'editable-playlist';
}
var is_loading_tracks = playlist.tracks_total !== 0 && (!playlist.tracks_uris || playlist.tracks_uris && !playlist.tracks || playlist.tracks_uris.length !== playlist.tracks.length);
return _react2.default.createElement(
'div',
{ className: 'view playlist-view content-wrapper preserve-3d' },
_react2.default.createElement(
'div',
{ className: 'thumbnail-wrapper' },
_react2.default.createElement(_Thumbnail2.default, { size: 'large', glow: true, canZoom: true, images: playlist.images, type: 'playlist' })
),
_react2.default.createElement(
'div',
{ className: 'title' },
_react2.default.createElement(
'h1',
null,
playlist.name
),
playlist.description && _react2.default.createElement('h2', {
className: 'description',
dangerouslySetInnerHTML: { __html: playlist.description }
}),
_react2.default.createElement(
'ul',
{ className: 'details' },
!slim_mode && _react2.default.createElement(
'li',
{ className: 'source' },
_react2.default.createElement(_Icon2.default, { type: 'fontawesome', name: (0, _helpers.sourceIcon)(playlist.uri) })
),
playlist.user_uri && _react2.default.createElement(
'li',
null,
_react2.default.createElement(
_URILink2.default,
{
type: 'user',
uri: playlist.user_uri
},
playlist.user ? playlist.user.name : (0, _helpers.getFromUri)('userid', playlist.user_uri)
)
),
_react2.default.createElement(
'li',
null,
_react2.default.createElement(_locale.I18n, { path: 'specs.tracks', count: playlist.tracks_total || 0 })
),
!slim_mode && playlist.tracks && playlist.tracks_total > 0 && _react2.default.createElement(
'li',
null,
_react2.default.createElement(_Dater.Dater, { type: 'total-time', data: playlist.tracks })
),
!slim_mode && playlist.followers !== undefined && _react2.default.createElement(
'li',
null,
_react2.default.createElement(_locale.I18n, { path: 'specs.followers', count: (0, _NiceNumber.nice_number)(playlist.followers) })
),
!slim_mode && playlist.last_modified_date && _react2.default.createElement(
'li',
null,
_react2.default.createElement(_locale.I18n, { path: 'specs.edited', date: (0, _Dater.dater)('ago', playlist.last_modified_date) })
)
)
),
_this2.renderActions(),
_react2.default.createElement(
'section',
{ className: 'list-wrapper' },
_react2.default.createElement(_TrackList2.default, {
uri: playlist.uri,
className: 'playlist-track-list',
track_context: context,
tracks: playlist.tracks,
removeTracks: _this2.removeTracks,
reorderTracks: _this2.reorderTracks
}),
_react2.default.createElement(_LazyLoadListener2.default, {
loadKey: playlist.tracks_more,
showLoader: is_loading_tracks || playlist.tracks_more,
loadMore: _this2.loadMore
})
)
);
};
};
@ -115087,11 +115086,7 @@ var Queue = function (_React$Component) {
};
_this.renderArtwork = function (image) {
var _this$props$current_t = _this.props.current_track;
_this$props$current_t = _this$props$current_t === undefined ? {} : _this$props$current_t;
var _this$props$current_t2 = _this$props$current_t.album;
_this$props$current_t2 = _this$props$current_t2 === undefined ? {} : _this$props$current_t2;
var uri = _this$props$current_t2.uri;
var current_track_uri = _this.props.current_track_uri;
if (!image) {
@ -115110,7 +115105,7 @@ var Queue = function (_React$Component) {
{ glow: true, image: image, type: 'track' },
_react2.default.createElement(
_URILink2.default,
{ uri: uri, className: 'thumbnail__actions__item' },
{ uri: current_track_uri, className: 'thumbnail__actions__item' },
_react2.default.createElement(_Icon2.default, { name: 'art_track' })
),
_react2.default.createElement(

File diff suppressed because one or more lines are too long

View File

@ -100,7 +100,7 @@
// Release details
// These are automatically injected to built HTML
var build = "1594107635";
var build = "1594194395";
var version = "3.50.0";
// Construct the script tag

View File

@ -6,6 +6,7 @@ specs:
listeners: '%{count} listeners'
plays: '%{count} plays'
popularity: '%{percent}% popularity'
edited: 'Edited '
actions:
play: Play
pause: Pause
@ -17,6 +18,7 @@ actions:
enable: Enable
disable: Disable
send: Send
edit: Edit
time:
seconds:
short: secs
@ -119,6 +121,9 @@ debug:
call: Call
data: Data
examples: Examples
playlist:
title: Playlist
title_window: '%{name} (playlist)'
search:
title: 'Search: %{term}'
context_actions:

View File

@ -8,8 +8,8 @@ import Link from '../components/Link';
import TrackList from '../components/TrackList';
import Thumbnail from '../components/Thumbnail';
import Parallax from '../components/Parallax';
import NiceNumber from '../components/NiceNumber';
import { Dater } from '../components/Dater';
import NiceNumber, { nice_number } from '../components/NiceNumber';
import { Dater, dater } from '../components/Dater';
import LazyLoadListener from '../components/LazyLoadListener';
import FollowButton from '../components/Fields/FollowButton';
import Loader from '../components/Loader';
@ -28,6 +28,7 @@ import {
decodeMopidyUri,
} from '../util/helpers';
import { collate } from '../util/format';
import { i18n, I18n } from '../locale';
class Playlist extends React.Component {
constructor(props) {
@ -82,141 +83,213 @@ class Playlist extends React.Component {
if (prevUri !== uri && playlist) this.setWindowTitle(playlist);
}
setWindowTitle(playlist = this.props.playlist) {
if (playlist) {
this.props.uiActions.setWindowTitle(`${playlist.name} (playlist)`);
} else {
this.props.uiActions.setWindowTitle('Playlist');
}
setWindowTitle = (playlist = this.props.playlist) => {
const { uiActions: { setWindowTitle } } = this.props;
setWindowTitle(
playlist ? i18n('playlist.title_window', { name: playlist.name }) : i18n('playlist.title')
);
}
loadMore() {
this.props.spotifyActions.getMore(
this.props.playlist.tracks_more,
loadMore = () => {
const {
spotifyActions: {
getMore,
},
playlist: {
uri,
tracks_more,
},
} = this.props;
getMore(
tracks_more,
{
parent_type: 'playlist',
parent_key: this.props.playlist.uri,
parent_key: uri,
records_type: 'track',
},
);
}
handleContextMenu(e) {
const data = {
handleContextMenu = (e) => {
const {
uiActions: {
showContextMenu,
},
playlist,
uri,
} = this.props;
showContextMenu({
e,
context: 'playlist',
items: [this.props.playlist],
uris: [this.props.uri],
};
this.props.uiActions.showContextMenu(data);
items: [playlist],
uris: [uri],
});
}
play() {
this.props.mopidyActions.playPlaylist(this.props.playlist.uri);
}
play = () => {
const {
mopidyActions: {
playPlaylist,
},
playlist: {
uri,
},
} = this.props;
follow() {
if (this.props.allow_reporting) {
ReactGA.event({ category: 'Playlist', action: 'Follow', label: this.props.playlist.uri });
}
this.props.spotifyActions.toggleFollowingPlaylist(this.props.playlist.uri, 'PUT');
}
// TODO: Once unfollowing occurs, remove playlist from global playlists list
unfollow() {
if (this.props.allow_reporting) {
ReactGA.event({ category: 'Playlist', action: 'Unfollow', label: this.props.playlist.uri });
}
this.props.spotifyActions.toggleFollowingPlaylist(this.props.playlist.uri, 'DELETE');
playPlaylist(uri);
}
// TODO: Once deletion occurs, remove playlist from global playlists list
delete() {
this.props.mopidyActions.deletePlaylist(this.props.playlist.uri);
delete = () => {
const {
mopidyActions: {
deletePlaylist,
},
playlist: {
uri,
},
} = this.props;
deletePlaylist(uri);
}
reorderTracks(indexes, index) {
this.props.coreActions.reorderPlaylistTracks(this.props.playlist.uri, indexes, index, this.props.playlist.snapshot_id);
reorderTracks = (indexes, index) => {
const {
coreActions: {
reorderPlaylistTracks,
},
playlist: {
uri,
snapshot_id,
},
} = this.props;
reorderPlaylistTracks(uri, indexes, index, snapshot_id);
}
removeTracks(tracks_indexes) {
removeTracks = (tracks_indexes) => {
this.props.coreActions.removeTracksFromPlaylist(this.props.playlist.uri, tracks_indexes);
}
inLibrary() {
const library = `${uriSource(this.props.uri)}_library_playlists`;
return (this.props[library] && this.props[library].indexOf(this.props.uri) > -1);
inLibrary = () => {
const { uri } = this.props;
const libraryName = `${uriSource(uri)}_library_playlists`;
const { [libraryName]: library = [] } = this.props;
return library.indexOf(uri) > -1;
}
renderActions() {
renderActions = () => {
const {
uri,
playlist: {
can_edit,
},
} = this.props;
switch (uriSource(this.props.uri)) {
case 'm3u':
return (
<div className="actions">
<button className="button button--primary" onClick={(e) => this.play()}>Play</button>
<Link className="button button--default" to={`/playlist/${encodeURIComponent(this.props.uri)}/edit`}>Edit</Link>
<ContextMenuTrigger onTrigger={(e) => this.handleContextMenu(e)} />
<button className="button button--primary" onClick={this.play}>
<I18n path="actions.play" />
</button>
<Link
className="button button--default"
to={`/playlist/${encodeURIComponent(uri)}/edit`}
>
<I18n path="actions.edit" />
</Link>
<ContextMenuTrigger onTrigger={this.handleContextMenu} />
</div>
);
case 'spotify':
if (this.props.playlist.can_edit) {
if (can_edit) {
return (
<div className="actions">
<button className="button button--primary" onClick={(e) => this.play()}>Play</button>
<Link className="button button--default" to={`/playlist/${encodeURIComponent(this.props.uri)}/edit`}>Edit</Link>
<ContextMenuTrigger onTrigger={(e) => this.handleContextMenu(e)} />
<button className="button button--primary" onClick={this.play}>
<I18n path="actions.play" />
</button>
<Link
className="button button--default"
to={`/playlist/${encodeURIComponent(uri)}/edit`}
>
<I18n path="actions.edit" />
</Link>
<ContextMenuTrigger onTrigger={this.handleContextMenu} />
</div>
);
}
return (
<div className="actions">
<button className="button button--primary" onClick={(e) => this.play()}>Play</button>
<FollowButton uri={this.props.uri} addText="Add to library" removeText="Remove from library" is_following={this.inLibrary()} />
<ContextMenuTrigger onTrigger={(e) => this.handleContextMenu(e)} />
<button className="button button--primary" onClick={this.play}>
<I18n path="actions.play" />
</button>
<FollowButton
uri={uri}
is_following={this.inLibrary()}
/>
<ContextMenuTrigger onTrigger={this.handleContextMenu} />
</div>
);
default:
return (
<div className="actions">
<button className="button button--primary" onClick={(e) => this.play()}>Play</button>
<ContextMenuTrigger onTrigger={(e) => this.handleContextMenu(e)} />
<button
className="button button--primary"
onClick={this.play}
>
<I18n path="actions.play" />
</button>
<ContextMenuTrigger onTrigger={this.handleContextMenu} />
</div>
);
}
}
render() {
const playlist_id = getFromUri('playlistid', this.props.uri);
render = () => {
const {
uri,
playlist: playlistProp,
load_queue,
tracks,
users,
slim_mode,
} = this.props;
if (!this.props.playlist) {
if (isLoading(this.props.load_queue, [`spotify_playlists/${playlist_id}?`])) {
const playlist_id = getFromUri('playlistid', uri);
if (!playlistProp) {
if (isLoading(load_queue, [`spotify_playlists/${playlist_id}?`])) {
return <Loader body loading />
}
return (
<ErrorMessage type="not-found" title="Not found">
<p>
Could not find playlist with URI "
{encodeURIComponent(this.props.uri)}
"
<I18n path="errors.uri_not_found" uri={uri} />
</p>
</ErrorMessage>
);
}
const playlist = collate(this.props.playlist, { tracks: this.props.tracks, users: this.props.users });
const playlist = collate(playlistProp, { tracks, users });
let context = 'playlist';
if (playlist.can_edit) {
context = 'editable-playlist';
}
if (playlist.tracks_total !== 0 && (!playlist.tracks_uris || (playlist.tracks_uris && !playlist.tracks) || (playlist.tracks_uris.length !== playlist.tracks.length))) {
var is_loading_tracks = true;
} else {
var is_loading_tracks = false;
}
const is_loading_tracks = (
playlist.tracks_total !== 0
&& (
!playlist.tracks_uris
|| (playlist.tracks_uris && !playlist.tracks)
|| (playlist.tracks_uris.length !== playlist.tracks.length)
)
);
return (
<div className="view playlist-view content-wrapper preserve-3d">
@ -227,30 +300,45 @@ Could not find playlist with URI "
<div className="title">
<h1>{playlist.name}</h1>
{playlist.description ? <h2 className="description" dangerouslySetInnerHTML={{ __html: playlist.description }} /> : null }
{playlist.description && (
<h2
className="description"
dangerouslySetInnerHTML={{ __html: playlist.description }}
/>
)}
<ul className="details">
{!this.props.slim_mode ? <li className="source"><Icon type="fontawesome" name={sourceIcon(playlist.uri)} /></li> : null }
{playlist.user_uri ? <li><URILink type="user" uri={playlist.user_uri}>{playlist.user ? playlist.user.name : getFromUri('userid', playlist.user_uri)}</URILink></li> : null }
{!slim_mode && (
<li className="source">
<Icon type="fontawesome" name={sourceIcon(playlist.uri)} />
</li>
)}
{playlist.user_uri && (
<li>
<URILink
type="user"
uri={playlist.user_uri}
>
{playlist.user ? playlist.user.name : getFromUri('userid', playlist.user_uri)}
</URILink>
</li>
)}
<li>
{playlist.tracks_total ? playlist.tracks_total : '0'}
{' '}
tracks
<I18n path="specs.tracks" count={playlist.tracks_total || 0} />
</li>
{!this.props.slim_mode && playlist.tracks && playlist.tracks_total > 0 ? <li><Dater type="total-time" data={playlist.tracks} /></li> : null }
{!this.props.slim_mode && playlist.followers !== undefined ? (
{!slim_mode && playlist.tracks && playlist.tracks_total > 0 && (
<li><Dater type="total-time" data={playlist.tracks} /></li>
)}
{!slim_mode && playlist.followers !== undefined && (
<li>
<NiceNumber value={playlist.followers} />
{' '}
followers
<I18n path="specs.followers" count={nice_number(playlist.followers)} />
</li>
) : null }
{!this.props.slim_mode && playlist.last_modified_date ? (
)}
{!slim_mode && playlist.last_modified_date && (
<li>
Edited
<Dater type="ago" data={playlist.last_modified_date} />
<I18n path="specs.edited" date={dater('ago', playlist.last_modified_date)} />
</li>
) : null }
)}
</ul>
</div>
@ -262,13 +350,13 @@ Edited
className="playlist-track-list"
track_context={context}
tracks={playlist.tracks}
removeTracks={(tracks_indexes) => this.removeTracks(tracks_indexes)}
reorderTracks={(indexes, index) => this.reorderTracks(indexes, index)}
removeTracks={this.removeTracks}
reorderTracks={this.reorderTracks}
/>
<LazyLoadListener
loadKey={playlist.tracks_more}
showLoader={is_loading_tracks || playlist.tracks_more}
loadMore={() => this.loadMore()}
loadMore={this.loadMore}
/>
</section>
</div>

View File

@ -123,7 +123,7 @@ class Queue extends React.Component {
}
renderArtwork = (image) => {
const { current_track: { album: { uri } = {} } = {} } = this.props;
const { current_track_uri } = this.props;
if (!image) {
return (
@ -136,7 +136,7 @@ class Queue extends React.Component {
return (
<div className="current-track__artwork">
<Thumbnail glow image={image} type="track">
<URILink uri={uri} className="thumbnail__actions__item">
<URILink uri={current_track_uri} className="thumbnail__actions__item">
<Icon name="art_track" />
</URILink>
<Link to="/kiosk-mode" className="thumbnail__actions__item">