Drag-and-drop support for adding to new playlist, #723
This commit is contained in:
@ -43,7 +43,7 @@ export default class Dropzone extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={hover ? 'dropzone hover' : 'dropzone'}
|
className={hover ? 'dropzone hover' : 'dropzone'}
|
||||||
onMouseUp={handleMouseUp}
|
onMouseUp={() => handleMouseUp(data)}
|
||||||
>
|
>
|
||||||
<Icon name={data.icon} />
|
<Icon name={data.icon} />
|
||||||
<span className="title">{ data.title }</span>
|
<span className="title">{ data.title }</span>
|
||||||
|
|||||||
@ -1,106 +1,81 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { bindActionCreators } from 'redux';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { withRouter } from 'react-router';
|
|
||||||
|
|
||||||
import Dropzone from './Dropzone';
|
import Dropzone from './Dropzone';
|
||||||
|
|
||||||
import * as uiActions from '../../services/ui/actions';
|
import * as uiActions from '../../services/ui/actions';
|
||||||
import * as mopidyActions from '../../services/mopidy/actions';
|
import * as mopidyActions from '../../services/mopidy/actions';
|
||||||
import * as spotifyActions from '../../services/spotify/actions';
|
|
||||||
import { arrayOf } from '../../util/arrays';
|
import { arrayOf } from '../../util/arrays';
|
||||||
import { i18n } from '../../locale';
|
import { i18n } from '../../locale';
|
||||||
import { encodeUri } from '../../util/format';
|
import { encodeUri } from '../../util/format';
|
||||||
|
|
||||||
class Dropzones extends React.Component {
|
const Dropzones = () => {
|
||||||
constructor(props) {
|
const dispatch = useDispatch();
|
||||||
super(props);
|
const history = useHistory();
|
||||||
|
const {
|
||||||
|
victims,
|
||||||
|
from_uri,
|
||||||
|
active,
|
||||||
|
} = useSelector((state) => state.ui.dragger || {});
|
||||||
|
const zones = [
|
||||||
|
{
|
||||||
|
title: i18n('actions.add_to_queue'),
|
||||||
|
icon: 'play_arrow',
|
||||||
|
action: 'enqueue',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18n('actions.play_next'),
|
||||||
|
icon: 'play_arrow',
|
||||||
|
action: 'enqueue_next',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18n('actions.add_to_playlist'),
|
||||||
|
icon: 'playlist_add',
|
||||||
|
action: 'add_to_playlist',
|
||||||
|
accepts: ['tltrack', 'track', 'album', 'playlist', 'artist'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18n('modal.edit_playlist.create_playlist'),
|
||||||
|
icon: 'playlist_add',
|
||||||
|
action: 'create_playlist_and_add',
|
||||||
|
accepts: ['tltrack', 'track', 'album', 'playlist', 'artist'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
this._zones = [
|
if (!active) return null;
|
||||||
{
|
|
||||||
title: i18n('actions.add_to_queue'),
|
|
||||||
icon: 'play_arrow',
|
|
||||||
action: 'enqueue',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: i18n('actions.play_next'),
|
|
||||||
icon: 'play_arrow',
|
|
||||||
action: 'enqueue_next',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: i18n('actions.add_to_playlist'),
|
|
||||||
icon: 'playlist_add',
|
|
||||||
action: 'add_to_playlist',
|
|
||||||
accepts: ['tltrack', 'track', 'album', 'playlist', 'artist'],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
handleMouseMove = (e) => {
|
const handleMouseUp = (zone) => {
|
||||||
const { dragger = {}, uiActions: { dragMove } } = this.props;
|
|
||||||
if (!dragger || !dragger.active) return null;
|
|
||||||
return dragMove(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleMouseUp = (zone) => {
|
|
||||||
const {
|
|
||||||
dragger: {
|
|
||||||
victims = [],
|
|
||||||
from_uri,
|
|
||||||
},
|
|
||||||
mopidyActions: {
|
|
||||||
enqueueURIs,
|
|
||||||
},
|
|
||||||
history,
|
|
||||||
} = this.props;
|
|
||||||
const uris = arrayOf('uri', victims);
|
const uris = arrayOf('uri', victims);
|
||||||
|
|
||||||
switch (zone.action) {
|
switch (zone.action) {
|
||||||
case 'enqueue':
|
case 'enqueue':
|
||||||
enqueueURIs(uris, from_uri);
|
dispatch(mopidyActions.enqueueURIs(uris, from_uri));
|
||||||
break;
|
break;
|
||||||
case 'enqueue_next':
|
case 'enqueue_next':
|
||||||
enqueueURIs(uris, from_uri, true);
|
dispatch(mopidyActions.enqueueURIs(uris, from_uri, true));
|
||||||
break;
|
break;
|
||||||
case 'add_to_playlist':
|
case 'add_to_playlist':
|
||||||
history.push(`/add-to-playlist/${encodeUri(uris.join(','))}`);
|
history.push(`/add-to-playlist/${encodeUri(uris.join(','))}`);
|
||||||
// uris
|
break;
|
||||||
|
case 'create_playlist_and_add':
|
||||||
|
history.push(`/playlist/create/${encodeUri(uris.join(','))}`);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
render = () => {
|
return (
|
||||||
const {
|
<div className="dropzones">
|
||||||
dragger = {},
|
{
|
||||||
} = this.props;
|
zones.map((zone) => (
|
||||||
if (!dragger || !dragger.active) return null;
|
<Dropzone
|
||||||
|
key={zone.action}
|
||||||
|
data={zone}
|
||||||
|
handleMouseUp={handleMouseUp}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
export default Dropzones;
|
||||||
<div className="dropzones">
|
|
||||||
{
|
|
||||||
this._zones.map((zone) => (
|
|
||||||
<Dropzone
|
|
||||||
key={zone.action}
|
|
||||||
data={zone}
|
|
||||||
handleMouseUp={() => this.handleMouseUp(zone)}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapStateToProps = (state) => ({
|
|
||||||
dragger: state.ui.dragger,
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
|
||||||
uiActions: bindActionCreators(uiActions, dispatch),
|
|
||||||
spotifyActions: bindActionCreators(spotifyActions, dispatch),
|
|
||||||
mopidyActions: bindActionCreators(mopidyActions, dispatch),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Dropzones));
|
|
||||||
|
|||||||
@ -27,6 +27,7 @@ actions:
|
|||||||
stop: Stop
|
stop: Stop
|
||||||
add_to_playlist: Add to playlist
|
add_to_playlist: Add to playlist
|
||||||
add_to_library: Add to library
|
add_to_library: Add to library
|
||||||
|
create_playlist_and_add: New playlist
|
||||||
added_tracks_to: 'Added %{count} tracks to '
|
added_tracks_to: 'Added %{count} tracks to '
|
||||||
remove_from_library: Remove from library
|
remove_from_library: Remove from library
|
||||||
add_to_queue: Add to queue
|
add_to_queue: Add to queue
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
import Link from '../../components/Link';
|
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import Thumbnail from '../../components/Thumbnail';
|
import Thumbnail from '../../components/Thumbnail';
|
||||||
import LinksSentence from '../../components/LinksSentence';
|
import LinksSentence from '../../components/LinksSentence';
|
||||||
@ -12,7 +11,7 @@ import * as mopidyActions from '../../services/mopidy/actions';
|
|||||||
import * as geniusActions from '../../services/genius/actions';
|
import * as geniusActions from '../../services/genius/actions';
|
||||||
import { isLoading } from '../../util/helpers';
|
import { isLoading } from '../../util/helpers';
|
||||||
import { i18n, I18n } from '../../locale';
|
import { i18n, I18n } from '../../locale';
|
||||||
import { makeItemSelector } from '../../util/selectors';
|
import { makeItemSelector, makeLoadingSelector } from '../../util/selectors';
|
||||||
|
|
||||||
const LyricsScroller = ({
|
const LyricsScroller = ({
|
||||||
content = '',
|
content = '',
|
||||||
@ -33,17 +32,17 @@ const LyricsScroller = ({
|
|||||||
|
|
||||||
const Lyrics = ({
|
const Lyrics = ({
|
||||||
show_lyrics,
|
show_lyrics,
|
||||||
load_queue,
|
|
||||||
time_position = null,
|
time_position = null,
|
||||||
current_track,
|
current_track,
|
||||||
}) => {
|
}) => {
|
||||||
if (!show_lyrics) {
|
if (!show_lyrics) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const loadingSelector = makeLoadingSelector(['genius_(.*)']);
|
||||||
|
const loading = useSelector(loadingSelector);
|
||||||
const { lyrics, duration } = current_track || {};
|
const { lyrics, duration } = current_track || {};
|
||||||
|
|
||||||
if (isLoading(load_queue, ['genius_'])) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="lyrics">
|
<div className="lyrics">
|
||||||
<Loader body loading />
|
<Loader body loading />
|
||||||
@ -225,6 +224,6 @@ const KioskMode = () => {
|
|||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default KioskMode;
|
export default KioskMode;
|
||||||
|
|||||||
Reference in New Issue
Block a user