diff --git a/src/js/App.js b/src/js/App.js index 67096d32..2683bbc2 100755 --- a/src/js/App.js +++ b/src/js/App.js @@ -310,7 +310,7 @@ export class App extends React.Component { - + diff --git a/src/js/components/ContextMenu.js b/src/js/components/ContextMenu.js index 1c14eb23..985463c0 100755 --- a/src/js/components/ContextMenu.js +++ b/src/js/components/ContextMenu.js @@ -783,8 +783,12 @@ class ContextMenu extends React.Component { const { playlists: allPlaylists, loading_progress, + menu: { + uris, + } = {}, } = this.props; + const encodedUris = uris && uris.length > 0 ? encodeUri(uris.join(',')) : ''; let list = null; if (submenu === 'add-to-playlist') { let playlists = compact(allPlaylists.map((playlist) => { @@ -842,6 +846,16 @@ class ContextMenu extends React.Component { +
+ + + + + + + + +
{loading_progress ? (
diff --git a/src/js/locale/en.yaml b/src/js/locale/en.yaml index 66012d4b..e5ef04d4 100755 --- a/src/js/locale/en.yaml +++ b/src/js/locale/en.yaml @@ -175,6 +175,7 @@ sidebar: context_menu: add_to_playlist: title: Add to playlist + new_playlist: Create playlist no_playlists: No writable playlists play_next: Play next play_top_tracks: Play top tracks @@ -511,6 +512,7 @@ modal: edit_playlist: title: Edit playlist title_create: Create playlist + subtitle: 'And add %{count} track%{plural}' name_required: Name is required name: Name description: Description diff --git a/src/js/services/core/actions.js b/src/js/services/core/actions.js index 09796904..82dc133f 100755 --- a/src/js/services/core/actions.js +++ b/src/js/services/core/actions.js @@ -403,16 +403,12 @@ export function savePlaylist(uri, name, description = '', is_public = false, is_ } } -export function createPlaylist(scheme, name, description = '', is_public = false, is_collaborative = false) { - switch (scheme) { +export function createPlaylist(playlist) { + switch (playlist.scheme) { case 'spotify': - if (description === '') { - description = null; - } - return spotifyActions.createPlaylist(name, description, is_public, is_collaborative); - + return spotifyActions.createPlaylist(playlist); default: - return mopidyActions.createPlaylist(name, scheme); + return mopidyActions.createPlaylist(playlist); } } diff --git a/src/js/services/mopidy/actions.js b/src/js/services/mopidy/actions.js index 5889b476..58ee6de7 100755 --- a/src/js/services/mopidy/actions.js +++ b/src/js/services/mopidy/actions.js @@ -422,11 +422,10 @@ export function getImages(uris) { }; } -export function createPlaylist(name, scheme) { +export function createPlaylist(playlist) { return { type: 'MOPIDY_CREATE_PLAYLIST', - name, - scheme, + playlist, }; } diff --git a/src/js/services/mopidy/middleware.js b/src/js/services/mopidy/middleware.js index 1dde1d6b..b21b657a 100755 --- a/src/js/services/mopidy/middleware.js +++ b/src/js/services/mopidy/middleware.js @@ -1346,7 +1346,11 @@ const MopidyMiddleware = (function () { break; case 'MOPIDY_CREATE_PLAYLIST': - request(store, 'playlists.create', { name: action.name, uri_scheme: action.scheme }) + const data = { + name: action.playlist.name, + uri_scheme: action.playlist.scheme, + }; + request(store, 'playlists.create', data) .then((response) => { const playlist = formatPlaylist({ ...response, @@ -1358,6 +1362,12 @@ const MopidyMiddleware = (function () { store.dispatch(uiActions.createNotification({ content: i18n('actions.created', { name: i18n('playlist.title') }), })); + if (action.playlist.tracks_uris) { + store.dispatch(coreActions.addTracksToPlaylist( + playlist.uri, + action.playlist.tracks_uris, + )); + } store.dispatch(coreActions.addToLibrary( getProvider('playlists', 'm3u:')?.uri, playlist, diff --git a/src/js/services/spotify/actions.js b/src/js/services/spotify/actions.js index 777f0cc5..25879099 100755 --- a/src/js/services/spotify/actions.js +++ b/src/js/services/spotify/actions.js @@ -22,7 +22,8 @@ import { injectSortId, } from '../../util/format'; import URILink from '../../components/URILink'; -import { getItem, providers, getProvider } from '../../util/selectors'; +import { i18n } from '../../locale'; +import { getItem, getProvider } from '../../util/selectors'; const coreActions = require('../core/actions'); const uiActions = require('../ui/actions'); @@ -1140,13 +1141,13 @@ export function getAlbum(uri, { full, forceRefetch } = {}) { * ====================================================================================== * */ -export function createPlaylist(name, description, is_public, is_collaborative) { +export function createPlaylist(playlist) { return (dispatch, getState) => { const data = { - name, - description, - public: is_public, - collaborative: is_collaborative, + name: playlist.name, + description: playlist.description || '', + public: playlist.public, + collaborative: playlist.collaborative, }; const { spotify: { @@ -1176,7 +1177,12 @@ export function createPlaylist(name, description, is_public, is_collaborative) { response, )); - dispatch(uiActions.createNotification({ content: 'Created playlist' })); + dispatch(uiActions.createNotification({ + content: i18n('actions.created', { name: i18n('playlist.title') }), + })); + if (playlist.tracks_uris) { + dispatch(coreActions.addTracksToPlaylist(response.uri, playlist.tracks_uris)); + } }, (error) => { dispatch(coreActions.handleException( diff --git a/src/js/views/modals/CreatePlaylist.js b/src/js/views/modals/CreatePlaylist.js index 7c537b3b..bc1aa92d 100755 --- a/src/js/views/modals/CreatePlaylist.js +++ b/src/js/views/modals/CreatePlaylist.js @@ -1,49 +1,49 @@ -import React from 'react'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; +import React, { useState, useEffect } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; +import { useParams } from 'react-router-dom'; import Modal from './Modal'; import * as coreActions from '../../services/core/actions'; import * as uiActions from '../../services/ui/actions'; import { i18n, I18n } from '../../locale'; +import { decodeUri } from '../../util/format'; +import TextField from '../../components/Fields/TextField'; import Button from '../../components/Button'; -class CreatePlaylist extends React.Component { - constructor(props) { - super(props); - this.state = { - name: '', - description: '', - scheme: 'm3u', - is_public: true, - is_collaborative: false, - }; - } +const CreatePlaylist = () => { + const { uris } = useParams(); + const tracks_uris = uris ? decodeUri(uris).split(',') : undefined; + const dispatch = useDispatch(); + const [playlist, setPlaylist] = useState({ + scheme: 'm3u', + name: '', + description: '', + collaborative: false, + public: false, + }); + const spotifyAvailable = useSelector((state) => state.spotify.access_token); + useEffect( + () => { + dispatch(uiActions.setWindowTitle(i18n('modal.edit_playlist.title_create'))); + }, + [], + ); - componentDidMount() { - this.props.uiActions.setWindowTitle(i18n('modal.edit_playlist.title_create')); - } - - createPlaylist(e) { + const onChange = (updates) => setPlaylist({ ...playlist, ...updates }); + const onSubmit = (e) => { e.preventDefault(); - if (!this.state.name || this.state.name == '') { - this.setState({ error: i18n('modal.edit_playlist.name_required') }); + if (!playlist.name || playlist.name === '') { return false; } - this.props.coreActions.createPlaylist( - this.state.scheme, - this.state.name, - this.state.description, - this.state.is_public, - this.state.is_collaborative, + dispatch( + coreActions.createPlaylist({ ...playlist, tracks_uris }), ); window.history.back(); - return false; - } + }; - renderFields() { - switch (this.state.scheme) { + const fields = () => { + switch (playlist.scheme) { case 'spotify': return (
@@ -52,10 +52,9 @@ class CreatePlaylist extends React.Component {
- this.setState({ name: e.target.value })} - value={this.state.name} + onChange({ name: value })} + value={playlist.name} />
@@ -65,11 +64,9 @@ class CreatePlaylist extends React.Component {
- this.setState({ description: e.target.value })} - - value={this.state.description} + onChange({ description: value })} + value={playlist.description} />
@@ -82,9 +79,9 @@ class CreatePlaylist extends React.Component {