Merging from develop

This commit is contained in:
James Barnsley
2020-11-12 21:13:59 +13:00
14 changed files with 633 additions and 262 deletions

View File

@ -121353,7 +121353,7 @@ var MopidyMiddleware = function () {
}; // Give a 5-second leeway for allowing Mopidy to connect, if it isn't already connected
if (store.getState().mopidy.connected) {
if (socket) {
doRequest();
} else {
console.info('Mopidy not yet connected, waiting 2 seconds');
@ -122621,21 +122621,32 @@ var MopidyMiddleware = function () {
store.dispatch({
type: 'MOPIDY_DIRECTORY_FLUSH'
});
var uri = action.uri ? decodeURIComponent(action.uri) : null;
if (action.uri) {
if (uri) {
request(store, 'library.lookup', {
uris: [action.uri]
uris: [uri]
}).then(function (response) {
if (!response[action.uri] || !response[action.uri].length) return;
var _response$uri = response[uri],
results = _response$uri === void 0 ? [] : _response$uri;
if (!results.length) return;
var result = results[0];
if (result.album) {
result = _objectSpread(_objectSpread({}, result), {}, {
name: result.album.name
});
}
store.dispatch({
type: 'MOPIDY_DIRECTORY_LOADED',
directory: Object(_util_format__WEBPACK_IMPORTED_MODULE_6__["formatSimpleObject"])(response[action.uri][0])
directory: _objectSpread({}, Object(_util_format__WEBPACK_IMPORTED_MODULE_6__["formatSimpleObject"])(result))
});
});
}
request(store, 'library.browse', {
uri: action.uri
uri: uri
}).then(function (response) {
var tracks_uris = [];
var tracks = [];
@ -122692,9 +122703,9 @@ var MopidyMiddleware = function () {
return;
}
for (var uri in response) {
if (response.hasOwnProperty(uri) && response[uri].length > 0) {
tracks.push(Object(_util_format__WEBPACK_IMPORTED_MODULE_6__["formatTrack"])(response[uri][0]));
for (var _uri in response) {
if (response.hasOwnProperty(_uri) && response[_uri].length > 0) {
tracks.push(Object(_util_format__WEBPACK_IMPORTED_MODULE_6__["formatTrack"])(response[_uri][0]));
}
}
@ -125352,7 +125363,7 @@ function reducer() {
/*!********************************************!*\
!*** ./src/js/services/spotify/actions.js ***!
\********************************************/
/*! exports provided: set, connect, authorizationGranted, revokeAuthorization, refreshingToken, tokenChanged, importAuthorization, getMe, getTrack, getFeaturedPlaylists, getCategories, getCategory, getNewReleases, getURL, getMore, getSearchResults, getAutocompleteResults, clearAutocompleteResults, following, resolveRadioSeeds, getRecommendations, getGenres, getArtist, getArtistImages, playArtistTopTracks, getUser, getAlbum, createPlaylist, savePlaylist, getPlaylist, addTracksToPlaylist, deleteTracksFromPlaylist, reorderPlaylistTracks, flushLibrary, getLibraryPlaylists, getLibraryAlbums, getLibraryArtists, getLibraryTracks */
/*! exports provided: set, connect, authorizationGranted, revokeAuthorization, refreshingToken, tokenChanged, importAuthorization, getMe, getTrack, getFeaturedPlaylists, getCategories, getCategory, getNewReleases, getURL, getMore, getSearchResults, getAutocompleteResults, clearAutocompleteResults, following, resolveRadioSeeds, getRecommendations, getGenres, getArtist, getArtistImages, playArtistTopTracks, getUser, getAlbum, createPlaylist, savePlaylist, getPlaylistTracks, getPlaylist, addTracksToPlaylist, deleteTracksFromPlaylist, reorderPlaylistTracks, flushLibrary, getLibraryPlaylists, getLibraryAlbums, getLibraryArtists, getLibraryTracks */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@ -125386,6 +125397,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getAlbum", function() { return getAlbum; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createPlaylist", function() { return createPlaylist; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "savePlaylist", function() { return savePlaylist; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getPlaylistTracks", function() { return getPlaylistTracks; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getPlaylist", function() { return getPlaylist; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "addTracksToPlaylist", function() { return addTracksToPlaylist; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "deleteTracksFromPlaylist", function() { return deleteTracksFromPlaylist; });
@ -125877,6 +125889,11 @@ function getSearchResults(_ref3) {
var offset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
var processKey = 'SPOTIFY_GET_SEARCH_RESULTS';
return function (dispatch, getState) {
var _getState = getState(),
_getState$spotify$me = _getState.spotify.me;
_getState$spotify$me = _getState$spotify$me === void 0 ? {} : _getState$spotify$me;
var meId = _getState$spotify$me.id;
dispatch(uiActions.startProcess(processKey, {
content: 'Searching Spotify'
}));
@ -125916,7 +125933,7 @@ function getSearchResults(_ref3) {
if (response.playlists !== undefined) {
var playlists = response.playlists.items.map(function (item) {
return _objectSpread(_objectSpread({}, Object(_util_format__WEBPACK_IMPORTED_MODULE_3__["formatPlaylist"])(item)), {}, {
can_edit: getState().spotify.me && item.owner.id === getState().spotify.me.id
can_edit: meId === item.owner.id
});
});
dispatch(coreActions.searchResultsLoaded({
@ -126061,8 +126078,14 @@ function following(uri) {
break;
case 'playlist':
var _getState2 = getState(),
_getState2$spotify$me = _getState2.spotify.me;
_getState2$spotify$me = _getState2$spotify$me === void 0 ? {} : _getState2$spotify$me;
var meId = _getState2$spotify$me.id;
if (method === 'GET') {
endpoint = "playlists/".concat(Object(_util_helpers__WEBPACK_IMPORTED_MODULE_2__["getFromUri"])('playlistid', uri), "/followers/contains?ids=").concat(getState().spotify.me.id);
endpoint = "playlists/".concat(Object(_util_helpers__WEBPACK_IMPORTED_MODULE_2__["getFromUri"])('playlistid', uri), "/followers/contains?ids=").concat(meId);
} else {
endpoint = "playlists/".concat(Object(_util_helpers__WEBPACK_IMPORTED_MODULE_2__["getFromUri"])('playlistid', uri), "/followers?");
}
@ -126487,7 +126510,13 @@ function createPlaylist(name, description, is_public, is_collaborative) {
"public": is_public,
collaborative: is_collaborative
};
request(dispatch, getState, "users/".concat(getState().spotify.me.id, "/playlists/"), 'POST', data).then(function (response) {
var _getState3 = getState(),
_getState3$spotify$me = _getState3.spotify.me;
_getState3$spotify$me = _getState3$spotify$me === void 0 ? {} : _getState3$spotify$me;
var meId = _getState3$spotify$me.id;
request(dispatch, getState, "users/".concat(meId, "/playlists/"), 'POST', data).then(function (response) {
dispatch(coreActions.itemLoaded(_objectSpread(_objectSpread({}, Object(_util_format__WEBPACK_IMPORTED_MODULE_3__["formatPlaylist"])(response)), {}, {
can_edit: true,
tracks: []
@ -126511,16 +126540,22 @@ function savePlaylist(uri, name, description, is_public, is_collaborative, image
description: description,
"public": is_public,
collaborative: is_collaborative
}; // Update the playlist fields
};
request(dispatch, getState, "users/".concat(getState().spotify.me.id, "/playlists/").concat(Object(_util_helpers__WEBPACK_IMPORTED_MODULE_2__["getFromUri"])('playlistid', uri)), 'PUT', data).then(function (response) {
var _getState4 = getState(),
_getState4$spotify$me = _getState4.spotify.me;
_getState4$spotify$me = _getState4$spotify$me === void 0 ? {} : _getState4$spotify$me;
var meId = _getState4$spotify$me.id; // Update the playlist fields
request(dispatch, getState, "users/".concat(meId, "/playlists/").concat(Object(_util_helpers__WEBPACK_IMPORTED_MODULE_2__["getFromUri"])('playlistid', uri)), 'PUT', data).then(function (response) {
dispatch(uiActions.createNotification({
level: 'warning',
content: 'Playlist saved'
})); // Save the image
if (image) {
request(dispatch, getState, "users/".concat(getState().spotify.me.id, "/playlists/").concat(Object(_util_helpers__WEBPACK_IMPORTED_MODULE_2__["getFromUri"])('playlistid', uri), "/images"), 'PUT', image).then(function (response) {
request(dispatch, getState, "users/".concat(meId, "/playlists/").concat(Object(_util_helpers__WEBPACK_IMPORTED_MODULE_2__["getFromUri"])('playlistid', uri), "/images"), 'PUT', image).then(function (response) {
dispatch({
type: 'PLAYLIST_UPDATED',
key: uri,
@ -126551,13 +126586,60 @@ function savePlaylist(uri, name, description, is_public, is_collaborative, image
});
};
}
function getPlaylist(uri) {
function getPlaylistTracks(uri) {
var _ref7 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
full = _ref7.full,
forceRefetch = _ref7.forceRefetch,
callbackAction = _ref7.callbackAction;
return function (dispatch, getState) {
var initialEndpoint = "playlists/".concat(Object(_util_helpers__WEBPACK_IMPORTED_MODULE_2__["getFromUri"])('playlistid', uri), "/tracks");
initialEndpoint += "?market=".concat(getState().spotify.country);
if (forceRefetch) initialEndpoint += "&refetch=".concat(Date.now());
var tracks = [];
var fetchTracks = function fetchTracks(endpoint) {
return request(dispatch, getState, endpoint).then(function (response) {
tracks = [].concat(_toConsumableArray(tracks), _toConsumableArray(Object(_util_format__WEBPACK_IMPORTED_MODULE_3__["formatTracks"])(response.items)));
if (response.next) {
fetchTracks(response.next);
} else {
dispatch(coreActions.itemLoaded({
uri: uri,
tracks: Object(_util_format__WEBPACK_IMPORTED_MODULE_3__["injectSortId"])(tracks) // only inject sort_id when we've loaded them all
}));
if (callbackAction) {
switch (callbackAction.name) {
case 'enqueue':
dispatch(mopidyActions.enqueueURIs(Object(_util_arrays__WEBPACK_IMPORTED_MODULE_1__["arrayOf"])('uri', tracks), uri, callbackAction.play_next, callbackAction.at_position, callbackAction.offset));
break;
case 'play':
dispatch(mopidyActions.playURIs(Object(_util_arrays__WEBPACK_IMPORTED_MODULE_1__["arrayOf"])('uri', tracks), uri, callbackAction.shuffle));
break;
default:
break;
}
}
}
});
};
fetchTracks(initialEndpoint);
};
}
function getPlaylist(uri, options) {
var full = options.full,
forceRefetch = options.forceRefetch;
return function (dispatch, getState) {
var _getState5 = getState(),
_getState5$spotify$me = _getState5.spotify.me;
_getState5$spotify$me = _getState5$spotify$me === void 0 ? {} : _getState5$spotify$me;
var meId = _getState5$spotify$me.id;
var endpoint = "playlists/".concat(Object(_util_helpers__WEBPACK_IMPORTED_MODULE_2__["getFromUri"])('playlistid', uri));
endpoint += "?market=".concat(getState().spotify.country);
if (forceRefetch) endpoint += "&refetch=".concat(Date.now()); // TODO
@ -126567,8 +126649,6 @@ function getPlaylist(uri) {
// 3. Update process as tracks are loaded and enqueue occurs
request(dispatch, getState, endpoint).then(function (response) {
var tracks = Object(_util_format__WEBPACK_IMPORTED_MODULE_3__["injectSortId"])(Object(_util_format__WEBPACK_IMPORTED_MODULE_3__["formatTracks"])(response.tracks.items)); // convert links in description
var description = null;
if (response.description) {
@ -126579,46 +126659,16 @@ function getPlaylist(uri) {
}
dispatch(coreActions.itemLoaded(_objectSpread(_objectSpread({}, Object(_util_format__WEBPACK_IMPORTED_MODULE_3__["formatPlaylist"])(response)), {}, {
can_edit: getState().spotify.me && getState().spotify.me.id === response.owner.id,
description: description
}, full ? {} : {
can_edit: meId === response.owner.id,
description: description,
// Remove tracks. They're handed by another query which allows our detector
// to accurately identify whether we've loaded *ALL* the tracks. Without this, it
// doesn't know if we've loaded all tracks, or just the first page.
tracks: null
})));
if (full) {
var fetchTracks = function fetchTracks(endpoint) {
return request(dispatch, getState, endpoint).then(function (response) {
tracks = [].concat(_toConsumableArray(tracks), _toConsumableArray(Object(_util_format__WEBPACK_IMPORTED_MODULE_3__["formatTracks"])(response.items)));
if (response.next) {
fetchTracks(response.next);
} else {
dispatch(coreActions.itemLoaded({
uri: uri,
tracks: Object(_util_format__WEBPACK_IMPORTED_MODULE_3__["injectSortId"])(tracks)
}));
if (callbackAction) {
switch (callbackAction.name) {
case 'enqueue':
dispatch(mopidyActions.enqueueURIs(Object(_util_arrays__WEBPACK_IMPORTED_MODULE_1__["arrayOf"])('uri', tracks), uri, callbackAction.play_next, callbackAction.at_position, callbackAction.offset));
break;
case 'play':
dispatch(mopidyActions.playURIs(Object(_util_arrays__WEBPACK_IMPORTED_MODULE_1__["arrayOf"])('uri', tracks), uri, callbackAction.shuffle));
break;
default:
break;
}
}
}
});
};
if (response.tracks.next) {
fetchTracks(response.tracks.next);
}
dispatch(getPlaylistTracks(uri, options));
}
;
@ -126694,6 +126744,11 @@ function flushLibrary() {
}
function getLibraryPlaylists(forceRefetch) {
return function (dispatch, getState) {
var _getState6 = getState(),
_getState6$spotify$me = _getState6.spotify.me;
_getState6$spotify$me = _getState6$spotify$me === void 0 ? {} : _getState6$spotify$me;
var meId = _getState6$spotify$me.id;
var processKey = 'SPOTIFY_GET_LIBRARY_PLAYLISTS';
dispatch(uiActions.startProcess(processKey, {
notification: false
@ -126717,7 +126772,7 @@ function getLibraryPlaylists(forceRefetch) {
var items = response.items.map(function (item) {
return _objectSpread(_objectSpread({}, Object(_util_format__WEBPACK_IMPORTED_MODULE_3__["formatPlaylist"])(item)), {}, {
in_library: true,
can_edit: getState().spotify.me && item.owner.id === getState().spotify.me.id
can_edit: meId === item.owner.id
});
});
libraryItems = [].concat(_toConsumableArray(libraryItems), _toConsumableArray(items));
@ -134226,54 +134281,56 @@ var Artist = /*#__PURE__*/function (_React$Component) {
if (prevUri !== uri && artist) _this.setWindowTitle(artist);
});
_defineProperty(_assertThisInitialized(_this), "onResetFilters", function () {
_this.onChangeFilter(null);
_this.onChangeSort(null);
Object(_components_Trackable__WEBPACK_IMPORTED_MODULE_26__["trackEvent"])({
category: 'Artist',
action: 'FilterAlbums',
label: 'Reset'
});
_defineProperty(_assertThisInitialized(_this), "onChangeTracksFilter", function (value) {
_this.onChangeFilter('tracks', value);
});
_defineProperty(_assertThisInitialized(_this), "onChangeFilter", function (value) {
_defineProperty(_assertThisInitialized(_this), "onChangeAlbumsFilter", function (value) {
_this.onChangeFilter('albums', value);
});
_defineProperty(_assertThisInitialized(_this), "onChangeFilter", function (type, value) {
var _this$props$uiActions = _this.props.uiActions,
set = _this$props$uiActions.set,
hideContextMenu = _this$props$uiActions.hideContextMenu;
set({
artist_albums_filter: value
});
set(_defineProperty({}, "artist_".concat(type, "_filter"), value));
hideContextMenu();
Object(_components_Trackable__WEBPACK_IMPORTED_MODULE_26__["trackEvent"])({
category: 'Artist',
action: 'FilterAlbums',
action: "Filter".concat(type),
label: value
});
});
_defineProperty(_assertThisInitialized(_this), "onChangeSort", function (value) {
_defineProperty(_assertThisInitialized(_this), "onChangeTracksSort", function (value) {
_this.onChangeSort('tracks', value);
});
_defineProperty(_assertThisInitialized(_this), "onChangeAlbumsSort", function (value) {
_this.onChangeSort('albums', value);
});
_defineProperty(_assertThisInitialized(_this), "onChangeSort", function (type, value) {
var _set2;
var _this$props2 = _this.props,
sort = _this$props2.sort,
sort_reverse = _this$props2.sort_reverse,
sort = _this$props2["".concat(type, "_sort")],
sort_reverse = _this$props2["".concat(type, "_sort_reverse")],
_this$props2$uiAction = _this$props2.uiActions,
set = _this$props2$uiAction.set,
hideContextMenu = _this$props2$uiAction.hideContextMenu;
var reverse = false;
if (value !== null && sort === value) {
reverse = !sort_reverse;
}
set({
artist_albums_sort_reverse: reverse,
artist_albums_sort: value
});
set((_set2 = {}, _defineProperty(_set2, "artist_".concat(type, "_sort_reverse"), reverse), _defineProperty(_set2, "artist_".concat(type, "_sort"), value), _set2));
hideContextMenu();
Object(_components_Trackable__WEBPACK_IMPORTED_MODULE_26__["trackEvent"])({
category: 'Artist',
action: 'SortAlbums',
action: "Sort".concat(type),
label: "".concat(value, " ").concat(reverse ? 'DESC' : 'ASC')
});
});
@ -134326,10 +134383,10 @@ var Artist = /*#__PURE__*/function (_React$Component) {
uri = _this$props5.uri,
uiActions = _this$props5.uiActions,
artist = _this$props5.artist,
sort = _this$props5.sort,
sort_reverse = _this$props5.sort_reverse,
sort = _this$props5.albums_sort,
sort_reverse = _this$props5.albums_sort_reverse,
filterType = _this$props5.filterType;
var filter = _this.state.filter;
var filter = _this.state.albumsFilter;
var tracks = artist.tracks,
related_artists = artist.related_artists;
var albums = _this.props.albums;
@ -134450,7 +134507,7 @@ var Artist = /*#__PURE__*/function (_React$Component) {
initialValue: filter,
handleChange: function handleChange(value) {
return _this.setState({
filter: value
albumsFilter: value
});
},
onSubmit: function onSubmit() {
@ -134465,7 +134522,7 @@ var Artist = /*#__PURE__*/function (_React$Component) {
valueAsLabel: true,
options: sort_options,
selected_icon: sort ? sort_reverse ? 'keyboard_arrow_up' : 'keyboard_arrow_down' : null,
handleChange: _this.onChangeSort,
handleChange: _this.onChangeAlbumsSort,
"data-qa-node": "DropdownField",
"data-qa-file": "Artist"
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_Fields_DropdownField__WEBPACK_IMPORTED_MODULE_14__["default"], {
@ -134474,7 +134531,7 @@ var Artist = /*#__PURE__*/function (_React$Component) {
value: filterType,
valueAsLabel: true,
options: filter_type_options,
handleChange: _this.onChangeFilter,
handleChange: _this.onChangeAlbumsFilter,
"data-qa-node": "DropdownField",
"data-qa-file": "Artist"
}))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("section", {
@ -134489,9 +134546,28 @@ var Artist = /*#__PURE__*/function (_React$Component) {
});
_defineProperty(_assertThisInitialized(_this), "renderTracks", function () {
var _this$props$artist = _this.props.artist,
uri = _this$props$artist.uri,
tracks = _this$props$artist.tracks;
var _this$props6 = _this.props,
uri = _this$props6.artist.uri,
sort = _this$props6.tracks_sort,
sort_reverse = _this$props6.tracks_sort_reverse;
var filter = _this.state.tracksFilter;
var tracks = _this.props.artist.tracks;
if (sort && tracks) {
tracks = Object(_util_arrays__WEBPACK_IMPORTED_MODULE_23__["sortItems"])(tracks, sort, sort_reverse);
}
if (filter && filter !== '') {
tracks = Object(_util_arrays__WEBPACK_IMPORTED_MODULE_23__["applyFilter"])('name', filter, tracks);
}
var sort_options = [{
value: 'disc_track',
label: Object(_locale__WEBPACK_IMPORTED_MODULE_24__["i18n"])('album.tracks.sort.disc_track')
}, {
value: 'name',
label: Object(_locale__WEBPACK_IMPORTED_MODULE_24__["i18n"])('album.tracks.sort.name')
}];
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
className: "body related-artists",
"data-qa-node": "div",
@ -134500,7 +134576,41 @@ var Artist = /*#__PURE__*/function (_React$Component) {
className: "list-wrapper no-top-padding",
"data-qa-node": "section",
"data-qa-file": "Artist"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_TrackList__WEBPACK_IMPORTED_MODULE_6__["default"], {
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("h4", {
className: "no-bottom-margin",
"data-qa-node": "h4",
"data-qa-file": "Artist"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_locale__WEBPACK_IMPORTED_MODULE_24__["I18n"], {
path: "artist.tracks.title",
"data-qa-node": "I18n",
"data-qa-file": "Artist"
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
className: "actions-wrapper",
"data-qa-node": "div",
"data-qa-file": "Artist"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_Fields_FilterField__WEBPACK_IMPORTED_MODULE_15__["default"], {
initialValue: filter,
handleChange: function handleChange(value) {
return _this.setState({
tracksFilter: value
});
},
onSubmit: function onSubmit() {
return _services_ui_actions__WEBPACK_IMPORTED_MODULE_19__["hideContextMenu"]();
},
"data-qa-node": "FilterField",
"data-qa-file": "Artist"
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_Fields_DropdownField__WEBPACK_IMPORTED_MODULE_14__["default"], {
icon: "swap_vert",
name: "Sort",
value: sort,
valueAsLabel: true,
options: sort_options,
selected_icon: sort ? sort_reverse ? 'keyboard_arrow_up' : 'keyboard_arrow_down' : null,
handleChange: _this.onChangeTracksSort,
"data-qa-node": "DropdownField",
"data-qa-file": "Artist"
}))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_TrackList__WEBPACK_IMPORTED_MODULE_6__["default"], {
className: "artist-track-list",
uri: uri,
tracks: tracks,
@ -134527,9 +134637,9 @@ var Artist = /*#__PURE__*/function (_React$Component) {
});
_defineProperty(_assertThisInitialized(_this), "renderAbout", function () {
var _this$props6 = _this.props,
artistProp = _this$props6.artist,
artists = _this$props6.artists;
var _this$props7 = _this.props,
artistProp = _this$props7.artist,
artists = _this$props7.artists;
var artist = Object(_util_format__WEBPACK_IMPORTED_MODULE_22__["collate"])(artistProp, {
artists: artists
});
@ -134676,11 +134786,11 @@ var Artist = /*#__PURE__*/function (_React$Component) {
});
_defineProperty(_assertThisInitialized(_this), "render", function () {
var _this$props7 = _this.props,
uri = _this$props7.uri,
loading = _this$props7.loading,
artist = _this$props7.artist,
history = _this$props7.history;
var _this$props8 = _this.props,
uri = _this$props8.uri,
loading = _this$props8.loading,
artist = _this$props8.artist,
history = _this$props8.history;
if (loading) {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_Loader__WEBPACK_IMPORTED_MODULE_17__["default"], {
@ -134875,7 +134985,8 @@ var Artist = /*#__PURE__*/function (_React$Component) {
});
_this.state = {
filter: ''
albumsFilter: '',
tracksFilter: ''
};
return _this;
}
@ -134883,9 +134994,9 @@ var Artist = /*#__PURE__*/function (_React$Component) {
_createClass(Artist, [{
key: "componentDidMount",
value: function componentDidMount() {
var _this$props8 = this.props,
uri = _this$props8.uri,
loadItem = _this$props8.coreActions.loadItem;
var _this$props9 = this.props,
uri = _this$props9.uri,
loadItem = _this$props9.coreActions.loadItem;
this.setWindowTitle();
loadItem(uri, {
full: true
@ -134915,9 +135026,11 @@ var mapStateToProps = function mapStateToProps(state, ownProps) {
loading: loadingSelector(state),
theme: state.ui.theme,
slim_mode: state.ui.slim_mode,
filterType: state.ui.artist_albums_filter ? state.ui.artist_albums_filter : null,
sort: state.ui.artist_albums_sort ? state.ui.artist_albums_sort : null,
sort_reverse: !!state.ui.artist_albums_sort_reverse,
filterType: state.ui.artist_albums_filter,
albums_sort: state.ui.artist_albums_sort,
albums_sort_reverse: !!state.ui.artist_albums_sort_reverse,
tracks_sort: state.ui.artist_tracks_sort,
tracks_sort_reverse: !!state.ui.artist_tracks_sort_reverse,
spotify_authorized: state.spotify.authorization
};
};
@ -136182,9 +136295,6 @@ var Playlist = /*#__PURE__*/function (_React$Component) {
var sort_options = [{
value: 'sort_id',
label: Object(_locale__WEBPACK_IMPORTED_MODULE_23__["i18n"])('playlist.tracks.sort.sort_id')
}, {
value: 'added_at',
label: Object(_locale__WEBPACK_IMPORTED_MODULE_23__["i18n"])('playlist.tracks.sort.added_at')
}, {
value: 'name',
label: Object(_locale__WEBPACK_IMPORTED_MODULE_23__["i18n"])('playlist.tracks.sort.name')
@ -137118,7 +137228,7 @@ var QueueHistory = /*#__PURE__*/function (_React$Component) {
type: "material",
"data-qa-node": "Icon",
"data-qa-file": "QueueHistory"
}))), tracks && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("section", {
}))), tracks.length > 0 && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("section", {
className: "content-wrapper",
"data-qa-node": "section",
"data-qa-file": "QueueHistory"
@ -142769,12 +142879,16 @@ var LibraryBrowseDirectory = /*#__PURE__*/function (_React$Component) {
}));
});
_defineProperty(_assertThisInitialized(_this), "playAll", function (tracks) {
_defineProperty(_assertThisInitialized(_this), "playAll", function () {
var _this$props3 = _this.props,
uri = _this$props3.uri,
playURIs = _this$props3.mopidyActions.playURIs,
hideContextMenu = _this$props3.uiActions.hideContextMenu;
playURIs(Object(_util_arrays__WEBPACK_IMPORTED_MODULE_17__["arrayOf"])('uri', tracks), "iris:browse:".concat(uri));
hideContextMenu = _this$props3.uiActions.hideContextMenu,
_this$props3$director = _this$props3.directory;
_this$props3$director = _this$props3$director === void 0 ? {} : _this$props3$director;
var tracks = _this$props3$director.tracks;
if (!tracks || !tracks.length) return;
playURIs(Object(_util_arrays__WEBPACK_IMPORTED_MODULE_17__["arrayOf"])('uri', Object(_util_arrays__WEBPACK_IMPORTED_MODULE_17__["sortItems"])(tracks, 'name')), "iris:browse:".concat(uri));
hideContextMenu();
});
@ -142890,8 +143004,8 @@ var LibraryBrowseDirectory = /*#__PURE__*/function (_React$Component) {
var tracks = null;
var limit_remaining = limit - subdirectories.length;
if (limit_remaining > 0) {
all_tracks = directory.tracks && directory.tracks.length > 0 ? directory.tracks : null;
if (limit_remaining > 0 && directory.tracks && directory.tracks.length) {
all_tracks = directory.tracks;
all_tracks = Object(_util_arrays__WEBPACK_IMPORTED_MODULE_17__["sortItems"])(all_tracks, 'name');
tracks = all_tracks.slice(0, limit_remaining);
}
@ -142918,11 +143032,7 @@ var LibraryBrowseDirectory = /*#__PURE__*/function (_React$Component) {
"data-qa-node": "DropdownField",
"data-qa-file": "LibraryBrowseDirectory"
}), tracks && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_Button__WEBPACK_IMPORTED_MODULE_19__["default"], {
onClick: function onClick() {
uiActions.hideContextMenu();
_this.playAll(all_tracks);
},
onClick: _this.playAll,
noHover: true,
discrete: true,
tracking: {
@ -143548,6 +143658,9 @@ var LibraryPlaylists = /*#__PURE__*/function (_React$Component) {
var mapStateToProps = function mapStateToProps(state) {
var source = state.ui.library_playlists_source || 'all';
var _state$spotify$me = state.spotify.me;
_state$spotify$me = _state$spotify$me === void 0 ? {} : _state$spotify$me;
var me_id = _state$spotify$me.id;
var libraryUris = [];
if (source === 'all' || source === 'local') libraryUris.push('mopidy:library:playlists');
if (source === 'all' || source === 'spotify') libraryUris.push('spotify:library:playlists');
@ -143561,7 +143674,7 @@ var mapStateToProps = function mapStateToProps(state) {
playlists: librarySelector(state),
loading_progress: processProgressSelector(state),
source: source,
me_id: state.spotify.me ? state.spotify.me.id : false,
me_id: me_id,
view: state.ui.library_playlists_view,
sort: state.ui.library_playlists_sort ? state.ui.library_playlists_sort : null,
sort_reverse: state.ui.library_playlists_sort_reverse ? state.ui.library_playlists_sort_reverse : false
@ -143764,11 +143877,33 @@ var LibraryTracks = /*#__PURE__*/function (_React$Component) {
});
});
_defineProperty(_assertThisInitialized(_this), "renderView", function () {
_defineProperty(_assertThisInitialized(_this), "playAll", function () {
var _this$props5 = _this.props,
sort = _this$props5.sort,
sort_reverse = _this$props5.sort_reverse,
loading_progress = _this$props5.loading_progress;
playURIs = _this$props5.mopidyActions.playURIs,
hideContextMenu = _this$props5.uiActions.hideContextMenu;
var tracks = _this.props.tracks;
var filter = _this.state.filter;
if (!tracks || !tracks.length) return;
if (sort) {
tracks = Object(_util_arrays__WEBPACK_IMPORTED_MODULE_13__["sortItems"])(tracks, sort, sort_reverse);
}
if (filter && filter !== '') {
tracks = Object(_util_arrays__WEBPACK_IMPORTED_MODULE_13__["applyFilter"])('name', filter, tracks);
}
playURIs(Object(_util_arrays__WEBPACK_IMPORTED_MODULE_13__["arrayOf"])('uri', tracks));
hideContextMenu();
});
_defineProperty(_assertThisInitialized(_this), "renderView", function () {
var _this$props6 = _this.props,
sort = _this$props6.sort,
sort_reverse = _this$props6.sort_reverse,
loading_progress = _this$props6.loading_progress;
var _this$state2 = _this.state,
limit = _this$state2.limit,
filter = _this$state2.filter;
@ -143813,13 +143948,13 @@ var LibraryTracks = /*#__PURE__*/function (_React$Component) {
});
_defineProperty(_assertThisInitialized(_this), "render", function () {
var _this$props6 = _this.props,
spotify_available = _this$props6.spotify_available,
sort = _this$props6.sort,
source = _this$props6.source,
sort_reverse = _this$props6.sort_reverse,
uiActions = _this$props6.uiActions,
loading_progress = _this$props6.loading_progress;
var _this$props7 = _this.props,
spotify_available = _this$props7.spotify_available,
sort = _this$props7.sort,
source = _this$props7.source,
sort_reverse = _this$props7.sort_reverse,
uiActions = _this$props7.uiActions,
loading_progress = _this$props7.loading_progress;
var _this$state3 = _this.state,
filter = _this$state3.filter,
per_page = _this$state3.per_page;
@ -143894,6 +144029,24 @@ var LibraryTracks = /*#__PURE__*/function (_React$Component) {
"data-qa-node": "DropdownField",
"data-qa-file": "LibraryTracks"
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_Button__WEBPACK_IMPORTED_MODULE_14__["default"], {
onClick: _this.playAll,
noHover: true,
discrete: true,
tracking: {
category: 'LibraryTracks',
action: 'Play'
},
"data-qa-node": "Button",
"data-qa-file": "LibraryTracks"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_Icon__WEBPACK_IMPORTED_MODULE_8__["default"], {
name: "play_circle_filled",
"data-qa-node": "Icon",
"data-qa-file": "LibraryTracks"
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_locale__WEBPACK_IMPORTED_MODULE_15__["I18n"], {
path: "actions.play_all",
"data-qa-node": "I18n",
"data-qa-file": "LibraryTracks"
})), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_Button__WEBPACK_IMPORTED_MODULE_14__["default"], {
noHover: true,
discrete: true,
onClick: loading ? _this.cancelRefresh : _this.refresh,
@ -143952,10 +144105,10 @@ var LibraryTracks = /*#__PURE__*/function (_React$Component) {
_createClass(LibraryTracks, [{
key: "componentDidMount",
value: function componentDidMount() {
var _this$props7 = this.props,
_this$props7$location = _this$props7.location.state,
state = _this$props7$location === void 0 ? {} : _this$props7$location,
setWindowTitle = _this$props7.uiActions.setWindowTitle; // Restore any limit defined in our location state
var _this$props8 = this.props,
_this$props8$location = _this$props8.location.state,
state = _this$props8$location === void 0 ? {} : _this$props8$location,
setWindowTitle = _this$props8.uiActions.setWindowTitle; // Restore any limit defined in our location state
if (state.limit) {
this.setState({
@ -144222,16 +144375,27 @@ var AddToPlaylist = /*#__PURE__*/function (_React$Component) {
}(react__WEBPACK_IMPORTED_MODULE_0___default.a.Component);
var mapStateToProps = function mapStateToProps(state, ownProps) {
var _state$spotify = state.spotify,
_state$spotify$me = _state$spotify.me;
_state$spotify$me = _state$spotify$me === void 0 ? {} : _state$spotify$me;
var me_id = _state$spotify$me.id,
spotify_available = _state$spotify.access_token,
load_queue = state.ui.load_queue,
_state$core = state.core,
playlists = _state$core.playlists,
items = _state$core.items,
libraries = _state$core.libraries,
mopidy_uri_schemes = state.mopidy.uri_schemes;
return {
uris: ownProps.match.params.uris ? decodeURIComponent(ownProps.match.params.uris).split(',') : [],
mopidy_uri_schemes: state.mopidy.uri_schemes,
items: state.core.items,
mopidy_library: state.core.libraries['mopidy:library:playlists'],
spotify_library: state.core.libraries['spotify:library:playlists'],
spotify_available: state.spotify.access_token,
load_queue: state.ui.load_queue,
me_id: state.spotify.me ? state.spotify.me.id : false,
playlists: state.core.playlists
mopidy_uri_schemes: mopidy_uri_schemes,
items: items,
mopidy_library: libraries['mopidy:library:playlists'],
spotify_library: libraries['spotify:library:playlists'],
spotify_available: spotify_available,
load_queue: load_queue,
me_id: me_id,
playlists: playlists
};
};
@ -147977,7 +148141,7 @@ var ShareConfiguration = /*#__PURE__*/function (_React$Component) {
return ShareConfiguration;
}(react__WEBPACK_IMPORTED_MODULE_0___default.a.Component);
var mapStateToProps = function mapStateToProps(state, ownProps) {
var mapStateToProps = function mapStateToProps(state) {
return {
spotify_authorization: state.spotify.authorization,
spotify_me: state.spotify.me,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -106,7 +106,11 @@
// Release details
// These are automatically injected to built HTML
<<<<<<< HEAD
var build = "1605168574";
=======
var build = "1605167537";
>>>>>>> develop
var version = "3.54.0";
// Construct the script tag

View File

@ -228,7 +228,7 @@ const MopidyMiddleware = (function () {
};
// Give a 5-second leeway for allowing Mopidy to connect, if it isn't already connected
if (store.getState().mopidy.connected) {
if (socket) {
doRequest();
} else {
console.info('Mopidy not yet connected, waiting 2 seconds');
@ -1606,19 +1606,35 @@ const MopidyMiddleware = (function () {
store.dispatch({
type: 'MOPIDY_DIRECTORY_FLUSH',
});
const uri = action.uri ? decodeURIComponent(action.uri) : null;
if (action.uri) {
request(store, 'library.lookup', { uris: [action.uri] })
if (uri) {
request(store, 'library.lookup', { uris: [uri] })
.then((response) => {
if (!response[action.uri] || !response[action.uri].length) return;
const {
[uri]: results = [],
} = response;
if (!results.length) return;
let result = results[0];
if (result.album) {
result = {
...result,
name: result.album.name,
};
}
store.dispatch({
type: 'MOPIDY_DIRECTORY_LOADED',
directory: formatSimpleObject(response[action.uri][0]),
directory: {
...formatSimpleObject(result),
},
});
});
}
request(store, 'library.browse', { uri: action.uri })
request(store, 'library.browse', { uri })
.then((response) => {
const tracks_uris = [];
const tracks = [];
@ -1643,8 +1659,8 @@ const MopidyMiddleware = (function () {
}
return {
...subdir,
images: images,
}
images,
};
});
store.dispatch({

View File

@ -559,6 +559,13 @@ export function getMore(url, core_action = null, custom_action = null, extra_dat
export function getSearchResults({ type, term }, limit = 50, offset = 0) {
const processKey = 'SPOTIFY_GET_SEARCH_RESULTS';
return (dispatch, getState) => {
const {
spotify: {
me: {
id: meId,
} = {},
},
} = getState();
dispatch(uiActions.startProcess(processKey, { content: 'Searching Spotify' }));
let typeString = type.replace(/s+$/, '');
@ -602,7 +609,7 @@ export function getSearchResults({ type, term }, limit = 50, offset = 0) {
if (response.playlists !== undefined) {
const playlists = response.playlists.items.map((item) => ({
...formatPlaylist(item),
can_edit: (getState().spotify.me && item.owner.id === getState().spotify.me.id),
can_edit: (meId === item.owner.id),
}));
dispatch(coreActions.searchResultsLoaded(
{ term, type },
@ -747,8 +754,15 @@ export function following(uri, method = 'GET') {
}
break;
case 'playlist':
const {
spotify: {
me: {
id: meId,
} = {},
},
} = getState();
if (method === 'GET') {
endpoint = `playlists/${getFromUri('playlistid', uri)}/followers/contains?ids=${getState().spotify.me.id}`;
endpoint = `playlists/${getFromUri('playlistid', uri)}/followers/contains?ids=${meId}`;
} else {
endpoint = `playlists/${getFromUri('playlistid', uri)}/followers?`;
}
@ -1221,8 +1235,15 @@ export function createPlaylist(name, description, is_public, is_collaborative) {
public: is_public,
collaborative: is_collaborative,
};
const {
spotify: {
me: {
id: meId,
} = {},
},
} = getState();
request(dispatch, getState, `users/${getState().spotify.me.id}/playlists/`, 'POST', data)
request(dispatch, getState, `users/${meId}/playlists/`, 'POST', data)
.then(
(response) => {
dispatch(coreActions.itemLoaded({
@ -1256,10 +1277,17 @@ export function savePlaylist(uri, name, description, is_public, is_collaborative
public: is_public,
collaborative: is_collaborative,
};
const {
spotify: {
me: {
id: meId,
} = {},
},
} = getState();
// Update the playlist fields
request(
dispatch, getState, `users/${getState().spotify.me.id}/playlists/${getFromUri('playlistid', uri)}`, 'PUT', data,
dispatch, getState, `users/${meId}/playlists/${getFromUri('playlistid', uri)}`, 'PUT', data,
)
.then(
(response) => {
@ -1267,7 +1295,7 @@ export function savePlaylist(uri, name, description, is_public, is_collaborative
// Save the image
if (image) {
request(dispatch, getState, `users/${getState().spotify.me.id}/playlists/${getFromUri('playlistid', uri)}/images`, 'PUT', image)
request(dispatch, getState, `users/${meId}/playlists/${getFromUri('playlistid', uri)}/images`, 'PUT', image)
.then(
(response) => {
@ -1314,8 +1342,65 @@ export function savePlaylist(uri, name, description, is_public, is_collaborative
};
}
export function getPlaylist(uri, { full, forceRefetch, callbackAction } = {}) {
export function getPlaylistTracks(uri, { forceRefetch, callbackAction } = {}) {
return (dispatch, getState) => {
let initialEndpoint = `playlists/${getFromUri('playlistid', uri)}/tracks`;
initialEndpoint += `?market=${getState().spotify.country}`;
if (forceRefetch) initialEndpoint += `&refetch=${Date.now()}`;
let tracks = [];
const fetchTracks = (endpoint) => request(dispatch, getState, endpoint)
.then((response) => {
tracks = [...tracks, ...formatTracks(response.items)];
if (response.next) {
fetchTracks(response.next);
} else {
dispatch(coreActions.itemLoaded({
uri,
tracks: injectSortId(tracks), // only inject sort_id when we've loaded them all
}));
if (callbackAction) {
switch (callbackAction.name) {
case 'enqueue':
dispatch(mopidyActions.enqueueURIs(
arrayOf('uri', tracks),
uri,
callbackAction.play_next,
callbackAction.at_position,
callbackAction.offset,
));
break;
case 'play':
dispatch(mopidyActions.playURIs(
arrayOf('uri', tracks),
uri,
callbackAction.shuffle,
));
break;
default:
break;
}
}
}
});
fetchTracks(initialEndpoint);
}
}
export function getPlaylist(uri, options) {
const { full, forceRefetch } = options;
return (dispatch, getState) => {
const {
spotify: {
me: {
id: meId,
} = {},
},
} = getState();
let endpoint = `playlists/${getFromUri('playlistid', uri)}`;
endpoint += `?market=${getState().spotify.country}`;
if (forceRefetch) endpoint += `&refetch=${Date.now()}`;
@ -1329,9 +1414,6 @@ export function getPlaylist(uri, { full, forceRefetch, callbackAction } = {}) {
request(dispatch, getState, endpoint)
.then(
(response) => {
let tracks = injectSortId(formatTracks(response.tracks.items));
// convert links in description
let description = null;
if (response.description) {
description = response.description;
@ -1342,54 +1424,16 @@ export function getPlaylist(uri, { full, forceRefetch, callbackAction } = {}) {
dispatch(coreActions.itemLoaded({
...formatPlaylist(response),
can_edit: (getState().spotify.me && getState().spotify.me.id === response.owner.id),
can_edit: (meId === response.owner.id),
description,
// Remove tracks unless we're looking for the full object. This allows our detector
// Remove tracks. They're handed by another query which allows our detector
// to accurately identify whether we've loaded *ALL* the tracks. Without this, it
// doesn't know if we've loaded all tracks, or just the first page.
...(full ? {} : { tracks: null }),
tracks: null,
}));
if (full) {
const fetchTracks = (endpoint) => request(dispatch, getState, endpoint)
.then((response) => {
tracks = [...tracks, ...formatTracks(response.items)];
if (response.next) {
fetchTracks(response.next);
} else {
dispatch(coreActions.itemLoaded({
uri,
tracks: injectSortId(tracks),
}));
if (callbackAction) {
switch (callbackAction.name) {
case 'enqueue':
dispatch(mopidyActions.enqueueURIs(
arrayOf('uri', tracks),
uri,
callbackAction.play_next,
callbackAction.at_position,
callbackAction.offset,
));
break;
case 'play':
dispatch(mopidyActions.playURIs(
arrayOf('uri', tracks),
uri,
callbackAction.shuffle,
));
break;
default:
break;
}
}
}
});
if (response.tracks.next) {
fetchTracks(response.tracks.next);
}
dispatch(getPlaylistTracks(uri, options));
};
},
(error) => {
@ -1495,6 +1539,13 @@ export function flushLibrary() {
export function getLibraryPlaylists(forceRefetch) {
return (dispatch, getState) => {
const {
spotify: {
me: {
id: meId,
} = {},
},
} = getState();
const processKey = 'SPOTIFY_GET_LIBRARY_PLAYLISTS';
dispatch(uiActions.startProcess(processKey, { notification: false }));
@ -1517,7 +1568,7 @@ export function getLibraryPlaylists(forceRefetch) {
(item) => ({
...formatPlaylist(item),
in_library: true,
can_edit: (getState().spotify.me && item.owner.id === getState().spotify.me.id),
can_edit: (meId === item.owner.id),
}),
);
libraryItems = [...libraryItems, ...items];

View File

@ -40,7 +40,8 @@ class Artist extends React.Component {
super(props);
this.state = {
filter: '',
albumsFilter: '',
tracksFilter: '',
};
}
@ -76,23 +77,33 @@ class Artist extends React.Component {
if (prevUri !== uri && artist) this.setWindowTitle(artist);
}
onResetFilters = () => {
this.onChangeFilter(null);
this.onChangeSort(null);
trackEvent({ category: 'Artist', action: 'FilterAlbums', label: 'Reset' });
onChangeTracksFilter = (value) => {
this.onChangeFilter('tracks', value);
}
onChangeFilter = (value) => {
onChangeAlbumsFilter = (value) => {
this.onChangeFilter('albums', value);
}
onChangeFilter = (type, value) => {
const { uiActions: { set, hideContextMenu } } = this.props;
set({ artist_albums_filter: value });
set({ [`artist_${type}_filter`]: value });
hideContextMenu();
trackEvent({ category: 'Artist', action: 'FilterAlbums', label: value });
trackEvent({ category: 'Artist', action: `Filter${type}`, label: value });
}
onChangeSort = (value) => {
onChangeTracksSort = (value) => {
this.onChangeSort('tracks', value);
}
onChangeAlbumsSort = (value) => {
this.onChangeSort('albums', value);
}
onChangeSort = (type, value) => {
const {
sort,
sort_reverse,
[`${type}_sort`]: sort,
[`${type}_sort_reverse`]: sort_reverse,
uiActions: {
set,
hideContextMenu,
@ -105,11 +116,11 @@ class Artist extends React.Component {
}
set({
artist_albums_sort_reverse: reverse,
artist_albums_sort: value,
[`artist_${type}_sort_reverse`]: reverse,
[`artist_${type}_sort`]: value,
});
hideContextMenu();
trackEvent({ category: 'Artist', action: 'SortAlbums', label: `${value} ${reverse ? 'DESC' : 'ASC'}` });
trackEvent({ category: 'Artist', action: `Sort${type}`, label: `${value} ${reverse ? 'DESC' : 'ASC'}` });
}
onPlay = () => {
@ -163,12 +174,12 @@ class Artist extends React.Component {
uri,
uiActions,
artist,
sort,
sort_reverse,
filterType
albums_sort: sort,
albums_sort_reverse: sort_reverse,
filterType,
} = this.props;
const {
filter,
albumsFilter: filter,
} = this.state;
let {
tracks,
@ -259,7 +270,7 @@ class Artist extends React.Component {
<div className="actions-wrapper">
<FilterField
initialValue={filter}
handleChange={(value) => this.setState({ filter: value })}
handleChange={(value) => this.setState({ albumsFilter: value })}
onSubmit={() => uiActions.hideContextMenu()}
/>
<DropdownField
@ -269,7 +280,7 @@ class Artist extends React.Component {
valueAsLabel
options={sort_options}
selected_icon={sort ? (sort_reverse ? 'keyboard_arrow_up' : 'keyboard_arrow_down') : null}
handleChange={this.onChangeSort}
handleChange={this.onChangeAlbumsSort}
/>
<DropdownField
icon="filter_list"
@ -277,7 +288,7 @@ class Artist extends React.Component {
value={filterType}
valueAsLabel
options={filter_type_options}
handleChange={this.onChangeFilter}
handleChange={this.onChangeAlbumsFilter}
/>
</div>
</h4>
@ -291,12 +302,62 @@ class Artist extends React.Component {
}
renderTracks = () => {
const { artist: { uri, tracks } } = this.props;
const {
artist: {
uri,
},
tracks_sort: sort,
tracks_sort_reverse: sort_reverse,
} = this.props;
const { tracksFilter: filter } = this.state;
let { artist: { tracks } } = this.props;
if (sort && tracks) {
tracks = sortItems(tracks, sort, sort_reverse);
}
if (filter && filter !== '') {
tracks = applyFilter('name', filter, tracks);
}
const sort_options = [
{
value: 'disc_track',
label: i18n('album.tracks.sort.disc_track'),
},
{
value: 'name',
label: i18n('album.tracks.sort.name'),
},
];
return (
<div className="body related-artists">
<section className="list-wrapper no-top-padding">
<TrackList className="artist-track-list" uri={uri} tracks={tracks} />
<h4 className="no-bottom-margin">
<I18n path="artist.tracks.title" />
<div className="actions-wrapper">
<FilterField
initialValue={filter}
handleChange={(value) => this.setState({ tracksFilter: value })}
onSubmit={() => uiActions.hideContextMenu()}
/>
<DropdownField
icon="swap_vert"
name="Sort"
value={sort}
valueAsLabel
options={sort_options}
selected_icon={sort ? (sort_reverse ? 'keyboard_arrow_up' : 'keyboard_arrow_down') : null}
handleChange={this.onChangeTracksSort}
/>
</div>
</h4>
<TrackList
className="artist-track-list"
uri={uri}
tracks={tracks}
/>
</section>
</div>
);
@ -547,9 +608,11 @@ const mapStateToProps = (state, ownProps) => {
loading: loadingSelector(state),
theme: state.ui.theme,
slim_mode: state.ui.slim_mode,
filterType: (state.ui.artist_albums_filter ? state.ui.artist_albums_filter : null),
sort: (state.ui.artist_albums_sort ? state.ui.artist_albums_sort : null),
sort_reverse: (!!state.ui.artist_albums_sort_reverse),
filterType: state.ui.artist_albums_filter,
albums_sort: state.ui.artist_albums_sort,
albums_sort_reverse: (!!state.ui.artist_albums_sort_reverse),
tracks_sort: state.ui.artist_tracks_sort,
tracks_sort_reverse: (!!state.ui.artist_tracks_sort_reverse),
spotify_authorized: state.spotify.authorization,
};
};

View File

@ -369,10 +369,6 @@ class Playlist extends React.Component {
value: 'sort_id',
label: i18n('playlist.tracks.sort.sort_id'),
},
{
value: 'added_at',
label: i18n('playlist.tracks.sort.added_at'),
},
{
value: 'name',
label: i18n('playlist.tracks.sort.name'),

View File

@ -61,7 +61,7 @@ class QueueHistory extends React.Component {
<Icon name="play_arrow" type="material" />
</I18n>
</Header>
{tracks && (
{tracks.length > 0 && (
<section className="content-wrapper">
<TrackList
uri="iris:queue-history"

View File

@ -86,7 +86,7 @@ class LibraryBrowseDirectory extends React.Component {
});
}
playAll = (tracks) => {
playAll = () => {
const {
uri,
mopidyActions: {
@ -95,9 +95,14 @@ class LibraryBrowseDirectory extends React.Component {
uiActions: {
hideContextMenu,
},
directory: {
tracks,
} = {},
} = this.props;
playURIs(arrayOf('uri', tracks), `iris:browse:${uri}`);
if (!tracks || !tracks.length) return;
playURIs(arrayOf('uri', sortItems(tracks, 'name')), `iris:browse:${uri}`);
hideContextMenu();
}
@ -199,8 +204,8 @@ class LibraryBrowseDirectory extends React.Component {
let all_tracks = null;
let tracks = null;
const limit_remaining = limit - subdirectories.length;
if (limit_remaining > 0) {
all_tracks = (directory.tracks && directory.tracks.length > 0 ? directory.tracks : null);
if (limit_remaining > 0 && directory.tracks && directory.tracks.length) {
all_tracks = directory.tracks;
all_tracks = sortItems(all_tracks, 'name');
tracks = all_tracks.slice(0, limit_remaining);
}
@ -228,7 +233,7 @@ class LibraryBrowseDirectory extends React.Component {
/>
{tracks && (
<Button
onClick={() => { uiActions.hideContextMenu(); this.playAll(all_tracks); }}
onClick={this.playAll}
noHover
discrete
tracking={{ category: 'Directory', action: 'Play' }}

View File

@ -354,6 +354,13 @@ class LibraryPlaylists extends React.Component {
const mapStateToProps = (state) => {
const source = state.ui.library_playlists_source || 'all';
const {
spotify: {
me: {
id: me_id,
} = {},
},
} = state;
const libraryUris = [];
if (source === 'all' || source === 'local') libraryUris.push('mopidy:library:playlists');
@ -369,7 +376,7 @@ const mapStateToProps = (state) => {
playlists: librarySelector(state),
loading_progress: processProgressSelector(state),
source,
me_id: (state.spotify.me ? state.spotify.me.id : false),
me_id,
view: state.ui.library_playlists_view,
sort: (state.ui.library_playlists_sort ? state.ui.library_playlists_sort : null),
sort_reverse: (state.ui.library_playlists_sort_reverse ? state.ui.library_playlists_sort_reverse : false),

View File

@ -12,7 +12,7 @@ import * as coreActions from '../../services/core/actions';
import * as uiActions from '../../services/ui/actions';
import * as mopidyActions from '../../services/mopidy/actions';
import * as spotifyActions from '../../services/spotify/actions';
import { sortItems, applyFilter } from '../../util/arrays';
import { sortItems, applyFilter, arrayOf } from '../../util/arrays';
import Button from '../../components/Button';
import { i18n, I18n } from '../../locale';
import Loader from '../../components/Loader';
@ -161,6 +161,34 @@ class LibraryTracks extends React.Component {
});
}
playAll = () => {
const {
sort,
sort_reverse,
mopidyActions: {
playURIs,
},
uiActions: {
hideContextMenu,
},
} = this.props;
let { tracks } = this.props;
const { filter } = this.state;
if (!tracks || !tracks.length) return;
if (sort) {
tracks = sortItems(tracks, sort, sort_reverse);
}
if (filter && filter !== '') {
tracks = applyFilter('name', filter, tracks);
}
playURIs(arrayOf('uri', tracks));
hideContextMenu();
}
renderView = () => {
const {
sort,
@ -191,7 +219,9 @@ class LibraryTracks extends React.Component {
return (
<section className="content-wrapper">
<TrackList tracks={tracks} />
<TrackList
tracks={tracks}
/>
<LazyLoadListener
loadKey={total_tracks > limit ? limit : total_tracks}
showLoader={limit < total_tracks}
@ -277,6 +307,15 @@ class LibraryTracks extends React.Component {
options={source_options}
handleChange={(val) => { uiActions.set({ library_tracks_source: val }); uiActions.hideContextMenu(); }}
/>
<Button
onClick={this.playAll}
noHover
discrete
tracking={{ category: 'LibraryTracks', action: 'Play' }}
>
<Icon name="play_circle_filled" />
<I18n path="actions.play_all" />
</Button>
<Button
noHover
discrete

View File

@ -106,17 +106,39 @@ class AddToPlaylist extends React.Component {
}
}
const mapStateToProps = (state, ownProps) => ({
uris: (ownProps.match.params.uris ? decodeURIComponent(ownProps.match.params.uris).split(',') : []),
mopidy_uri_schemes: state.mopidy.uri_schemes,
items: state.core.items,
mopidy_library: state.core.libraries['mopidy:library:playlists'],
spotify_library: state.core.libraries['spotify:library:playlists'],
spotify_available: state.spotify.access_token,
load_queue: state.ui.load_queue,
me_id: (state.spotify.me ? state.spotify.me.id : false),
playlists: state.core.playlists,
});
const mapStateToProps = (state, ownProps) => {
const {
spotify: {
me: {
id: me_id,
} = {},
access_token: spotify_available,
},
ui: {
load_queue,
},
core: {
playlists,
items,
libraries,
},
mopidy: {
uri_schemes: mopidy_uri_schemes,
}
} = state;
return {
uris: (ownProps.match.params.uris ? decodeURIComponent(ownProps.match.params.uris).split(',') : []),
mopidy_uri_schemes,
items,
mopidy_library: libraries['mopidy:library:playlists'],
spotify_library: libraries['spotify:library:playlists'],
spotify_available,
load_queue,
me_id,
playlists,
};
};
const mapDispatchToProps = (dispatch) => ({
coreActions: bindActionCreators(coreActions, dispatch),

View File

@ -297,7 +297,7 @@ class ShareConfiguration extends React.Component {
}
}
const mapStateToProps = (state, ownProps) => ({
const mapStateToProps = (state) => ({
spotify_authorization: state.spotify.authorization,
spotify_me: state.spotify.me,
genius_authorization: state.genius.authorization,