Browse Source

Added playlist selecting in queue and other features to queue list.

KrisVos130 7 năm trước cách đây
mục cha
commit
8510a96971

+ 23 - 0
frontend/app/js/actions/playlistQueue.js

@@ -0,0 +1,23 @@
+export const SELECT_PLAYLIST = "SELECT_PLAYLIST";
+export const DESELECT_PLAYLISTS = "DESELECT_PLAYLISTS";
+export const ADD_SONG = "ADD_SONG";
+
+export function selectPlaylist(playlistId) {
+	return {
+		type: SELECT_PLAYLIST,
+		playlistId,
+	};
+}
+
+export function deselectPlaylists() {
+	return {
+		type: DESELECT_PLAYLISTS,
+	};
+}
+
+export function addSong(songId) {
+	return {
+		type: ADD_SONG,
+		songId,
+	};
+}

+ 2 - 0
frontend/app/js/reducers/index.js

@@ -4,6 +4,7 @@ import volume from "reducers/volume";
 import songPlayer from "reducers/songPlayer";
 import station from "reducers/station";
 import stationOverlay from "reducers/stationOverlay";
+import playlistQueue from "reducers/playlistQueue";
 
 export default combineReducers({
 	user,
@@ -11,4 +12,5 @@ export default combineReducers({
 	songPlayer,
 	station,
 	stationOverlay,
+	playlistQueue,
 });

+ 36 - 0
frontend/app/js/reducers/playlistQueue.js

@@ -0,0 +1,36 @@
+import { Map } from "immutable";
+
+import {
+	SELECT_PLAYLIST,
+	DESELECT_PLAYLISTS,
+	ADD_SONG,
+} from "actions/playlistQueue";
+
+const initialState = Map({
+	playlistSelected: null,
+	addedSongId: null,
+});
+
+const actionsMap = {
+	[SELECT_PLAYLIST]: (state, action) => {
+		return state.merge({
+			playlistSelected: action.playlistId,
+		});
+	},
+	[DESELECT_PLAYLISTS]: (state, action) => {
+		return state.merge({
+			playlistSelected: null,
+			addedSongId: null,
+		});
+	},
+	[ADD_SONG]: (state, action) => {
+		return state.merge({
+			addedSongId: action.songId,
+		});
+	},
+};
+
+export default function reducer(state = initialState, action = {}) {
+	const fn = actionsMap[action.type];
+	return fn ? fn(state, action) : state;
+}

+ 58 - 4
frontend/app/js/views/Station/Views/QueueList.jsx

@@ -6,10 +6,10 @@ import CustomErrors from "components/CustomMessages.jsx";
 
 import { connect } from "react-redux";
 
-import { closeOverlay1, openOverlay2 } from "actions/stationOverlay";
+import { closeOverlay1, openOverlay2, closeOverlay2 } from "actions/stationOverlay";
+import { selectPlaylist, deselectPlaylists } from "actions/playlistQueue";
 
 import io from "io";
-import {closeOverlay2} from "../../../actions/stationOverlay";
 
 @connect(state => ({
 	user: {
@@ -24,6 +24,7 @@ import {closeOverlay2} from "../../../actions/stationOverlay";
 	songDuration: state.songPlayer.get("duration"),
 	simpleSong: state.songPlayer.get("simple"),
 	songExists: state.songPlayer.get("exists"),
+	playlistSelectedId: state.playlistQueue.get("playlistSelected"),
 }))
 export default class Settings extends Component {
 	constructor(props) {
@@ -33,6 +34,7 @@ export default class Settings extends Component {
 			queue: [],
 			userIdMap: {},
 			userIdMapLoading: [],
+			playlists: [],
 		};
 
 		io.getSocket((socket) => {
@@ -55,6 +57,27 @@ export default class Settings extends Component {
 					this.checkUserId(song.requestedBy);
 				});
 			});
+
+			socket.emit('playlists.indexForUser', res => {
+				if (res.status === 'success') this.setState({
+					playlists: res.data,
+				});
+			});
+
+			socket.on('event:playlist.create', () => {
+				socket.emit('playlists.indexForUser', res => {
+					if (res.status === 'success') this.setState({
+						playlists: res.data,
+					});
+				});
+			});
+			socket.on('event:playlist.delete', () => {
+				socket.emit('playlists.indexForUser', res => {
+					if (res.status === 'success') this.setState({
+						playlists: res.data,
+					});
+				});
+			});
 		});
 	}
 
@@ -113,6 +136,20 @@ export default class Settings extends Component {
 		this.props.dispatch(openOverlay2("searchYouTube", null, this.addSongToQueueCallback));
 	};
 
+	getPlaylistAction = (playlistId) => {
+		if (playlistId === this.props.playlistSelectedId) {
+			return <span>SELECTED</span>;
+		} else return <span onClick={ () => { this.selectPlaylist(playlistId); } }>SELECT</span>;
+	}
+
+	selectPlaylist = (playlistId) => {
+		this.props.dispatch(selectPlaylist(playlistId));
+	}
+
+	deselectAll = () => {
+		this.props.dispatch(deselectPlaylists());
+	}
+
 	close = () => {
 		this.props.dispatch(closeOverlay1());
 	};
@@ -160,8 +197,25 @@ export default class Settings extends Component {
 
 				<button onClick={ this.addSongToQueue }>Add song to queue</button>
 
-				<button onClick={ () => {io.getSocket((socket) => {socket.emit("stations.addToQueue", this.props.stationId, "A1PAO3jgmXY", (data) => {console.log(data);});});} }>Add temp</button>
-				<button onClick={ () => {io.getSocket((socket) => {socket.emit('stations.updatePartyMode', this.props.stationId, true, res => {console.log(res);});})}}>Enable party</button>
+				<hr/>
+
+				<ul>
+					{
+						this.state.playlists.map((playlist) => {
+							return (
+								<li key={ playlist._id }>
+									{ playlist.displayName } - { this.getPlaylistAction(playlist._id) }
+								</li>
+							)
+						})
+					}
+				</ul>
+
+				{
+					(this.props.playlistSelectedId)
+						? <button onClick={ this.deselectAll }>Deselect all playlists</button>
+					: null
+				}
 			</div>
 		);
 	}

+ 106 - 1
frontend/app/js/views/Station/index.jsx

@@ -13,6 +13,7 @@ import { changeVolume } from "actions/volume";
 import { changeSong, setTimeElapsed, timePaused, receivedRatings, receivedOwnRatings } from "actions/songPlayer";
 import { pauseStation, resumeStation } from "actions/station";
 import { openOverlay1 } from "actions/stationOverlay";
+import { addSong } from "actions/playlistQueue";
 
 import { connect } from "react-redux";
 
@@ -43,6 +44,8 @@ const formatTime = (duration) => {
 	songDisliked: state.songPlayer.get("disliked"),
 	simpleSong: state.songPlayer.get("simple"),
 	songExists: state.songPlayer.get("exists"),
+	queueLocked: state.station.get("locked"),
+	partyEnabled: state.station.get("partyMode"),
 	station: {
 		stationId: state.station.get("id"),
 		name: state.station.get("name"),
@@ -51,6 +54,10 @@ const formatTime = (duration) => {
 		pausedAt: state.station.get("pausedAt"),
 		ownerId: state.station.get("ownerId"),
 	},
+	selectedPlaylistObject: {
+		addedSongId: state.playlistQueue.get("addedSongId"),
+		selectedPlaylistId: state.playlistQueue.get("playlistSelected"),
+	},
 }))
 
 @translate(["station"], { wait: true })
@@ -66,6 +73,10 @@ export default class Station extends Component {
 	constructor(props) {
 		super();
 
+		this.state = {
+			mode: this.getModeTemp(props.partyEnabled, props.queueLocked),
+		};
+
 		io.getSocket(socket => {
 			socket.emit("stations.join", props.station.name, res => {
 				if (res.status === 'success') {
@@ -79,6 +90,7 @@ export default class Station extends Component {
 				}
 
 				socket.on('event:songs.next', data => {
+					this.addTopToQueue();
 					if (data.currentSong) {
 						data.currentSong.startedAt = data.startedAt;
 						data.currentSong.timePaused = data.timePaused;
@@ -96,7 +108,6 @@ export default class Station extends Component {
 					this.props.dispatch(resumeStation());
 				});
 
-
 				socket.on('event:song.like', data => {
 					console.log("LIKE");
 					if (this.props.songExists) {
@@ -146,6 +157,100 @@ export default class Station extends Component {
 		}, 1000);
 	}
 
+	isInQueue = (songId, cb) => {
+		io.getSocket((socket) => {
+			socket.emit('stations.getQueue', this.props.stationId, data => {
+				if (data.status === 'success') {
+					data.queue.forEach((song) => {
+						if (song._id === songId) {
+							return cb(true);
+						}
+					});
+				}
+
+				return cb(false);
+			});
+		});
+	};
+
+	checkIfCanAdd = (cb) => {
+		if (this.state.mode === "normal") return cb(false);
+		let playlistId = this.props.selectedPlaylistObject.selectedPlaylistId;
+		let songId = this.props.selectedPlaylistObject.addedSongId;
+		console.log(playlistId, songId, this.props.songId);
+		if (playlistId) {
+			if (songId === this.props.songId) return cb(true);
+			else if (songId === null) return cb(true);
+			else {
+				this.isInQueue(songId, (res) => {
+					return cb(res);
+				});
+			}
+		}
+	}
+
+	addTopToQueue = () => {
+		console.log("ADD TOP TO QUEUE!!!");
+		this.checkIfCanAdd((can) => {
+			if (!can) return;
+			let playlistId = this.props.selectedPlaylistObject.selectedPlaylistId;
+			console.log(can);
+			io.getSocket((socket) => {
+				socket.emit('playlists.getFirstSong', this.props.selectedPlaylistObject.selectedPlaylistId, data => {
+					if (data.status === 'success') {
+						let songId = data.song.songId;
+						if (data.song.duration < 15 * 60) {
+							this.props.dispatch(addSong(songId));
+
+							socket.emit('stations.addToQueue', this.props.station.stationId, songId, data2 => {
+								if (data2.status === 'success') {
+									this.moveToBottom(playlistId, songId, (data3) => {
+										if (data3.status === 'success') {
+										}
+									});
+								} else {
+									this.messages.clearAddError("Could not automatically add top song of playlist to queue.", data2.message);
+								}
+							});
+						} else {
+							this.messages.clearAddError("Top song in playlist was too long to be added. Moving to the next song.");
+							this.moveToBottom(playlistId, songId, (data3) => {
+								if (data3.status === 'success') {
+									setTimeout(() => {
+										this.addTopToQueue();
+									}, 2000);
+								}
+							});
+						}
+					}
+				});
+			});
+		});
+	};
+
+	moveToBottom = (playlistId, songId, cb) => {
+		io.getSocket((socket) => {
+			socket.emit('playlists.moveSongToBottom', playlistId, songId, data => {
+				cb(data);
+			});
+		});
+	}
+
+	getModeTemp = (partyEnabled, queueLocked) => {
+		// If party enabled
+		// If queue locked
+		// Mode is DJ
+		// If queue not locked
+		// Mode party
+		// If party not enabled
+		// Mode is normal
+
+		if (partyEnabled) {
+			if (queueLocked) return "dj";
+			else return "party";
+		} else return "normal";
+	}
+
 	getOwnRatings = () => {
 		io.getSocket((socket) => {
 			if (!this.props.songExists) return;