Phyks (Lucas Verney)
ab7470d618
Webplayer can now handle a basic playlist, pushing multiple songs in the playlist and passing from one song to another. Some things that are not yet working: * Using previous and next buttons and going outside of the playlist breaks things. * Random / repeat modes are not yet implemented. * Playlist is not exposed in the UI at the moment. * Seeking in a song is not exposed in the UI. * When playing a song, webplayer does not automatically play the next one when reaching the end of the song.
162 lines
4.5 KiB
JavaScript
162 lines
4.5 KiB
JavaScript
/**
|
|
* This implements the webplayer reducers.
|
|
*/
|
|
|
|
// NPM imports
|
|
import Immutable from "immutable";
|
|
|
|
// Local imports
|
|
import { createReducer } from "../utils";
|
|
|
|
// Models
|
|
import { stateRecord } from "../models/webplayer";
|
|
|
|
// Actions
|
|
import {
|
|
PLAY_PAUSE,
|
|
STOP_PLAYBACK,
|
|
SET_PLAYLIST,
|
|
PUSH_SONG,
|
|
POP_SONG,
|
|
JUMP_TO_SONG,
|
|
PLAY_PREVIOUS_SONG,
|
|
PLAY_NEXT_SONG,
|
|
TOGGLE_RANDOM,
|
|
TOGGLE_REPEAT,
|
|
TOGGLE_MUTE,
|
|
SET_VOLUME,
|
|
INVALIDATE_STORE } from "../actions";
|
|
|
|
|
|
/**
|
|
* Initial state
|
|
*/
|
|
|
|
var initialState = new stateRecord();
|
|
|
|
|
|
/**
|
|
* Helper functions
|
|
*/
|
|
|
|
/**
|
|
* Stop playback in reducer helper.
|
|
*
|
|
* @param state Current state to update.
|
|
*/
|
|
function stopPlayback(state) {
|
|
return (
|
|
state
|
|
.set("isPlaying", false)
|
|
.set("currentIndex", 0)
|
|
.set("playlist", new Immutable.List())
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Reducers
|
|
*/
|
|
|
|
export default createReducer(initialState, {
|
|
[PLAY_PAUSE]: (state, payload) => {
|
|
// Force play or pause
|
|
return state.set("isPlaying", payload.isPlaying);
|
|
},
|
|
[STOP_PLAYBACK]: (state) => {
|
|
// Clear the playlist
|
|
return stopPlayback(state);
|
|
},
|
|
[SET_PLAYLIST]: (state, payload) => {
|
|
// Set current playlist, reset playlist index
|
|
return (
|
|
state
|
|
.set("playlist", new Immutable.List(payload.playlist))
|
|
.set("currentIndex", 0)
|
|
);
|
|
},
|
|
[PUSH_SONG]: (state, payload) => {
|
|
// Push song to playlist
|
|
let newState = state;
|
|
if (payload.index) {
|
|
// If index is specified, insert it at this position
|
|
newState = newState.set(
|
|
"playlist",
|
|
newState.get("playlist").insert(payload.index, payload.song)
|
|
);
|
|
if (payload.index <= newState.get("currentIndex")) { // "<=" because insertion is made before
|
|
// If we insert before the current position in the playlist, we
|
|
// update the current position to keep the currently played
|
|
// music
|
|
newState = newState.set(
|
|
"currentIndex",
|
|
Math.min(newState.get("currentIndex") + 1, newState.get("playlist").size)
|
|
);
|
|
}
|
|
} else {
|
|
// Else, push at the end of the playlist
|
|
newState = newState.set(
|
|
"playlist",
|
|
newState.get("playlist").push(payload.song)
|
|
);
|
|
}
|
|
return newState;
|
|
},
|
|
[POP_SONG]: (state, payload) => {
|
|
// Pop song from playlist
|
|
let newState = state.deleteIn(["playlist", payload.index]);
|
|
if (payload.index < state.get("currentIndex")) {
|
|
// If we delete before the current position in the playlist, we
|
|
// update the current position to keep the currently played
|
|
// music
|
|
newState = newState.set(
|
|
"currentIndex",
|
|
Math.max(newState.get("currentIndex") - 1, 0)
|
|
);
|
|
}
|
|
return newState;
|
|
},
|
|
[JUMP_TO_SONG]: (state, payload) => {
|
|
// Set current index
|
|
const newCurrentIndex = state.get("playlist").findKey(x => x == payload.song);
|
|
return state.set("currentIndex", newCurrentIndex);
|
|
},
|
|
[PLAY_PREVIOUS_SONG]: (state) => {
|
|
const newIndex = state.get("currentIndex") - 1;
|
|
if (newIndex < 0) {
|
|
// If there is an overlow on the left of the playlist, just stop
|
|
// playback
|
|
// TODO: Behavior is not correct
|
|
return stopPlayback(state);
|
|
} else {
|
|
return state.set("currentIndex", newIndex);
|
|
}
|
|
},
|
|
[PLAY_NEXT_SONG]: (state) => {
|
|
const newIndex = state.get("currentIndex") + 1;
|
|
if (newIndex > state.get("playlist").size) {
|
|
// If there is an overflow, just stop playback
|
|
// TODO: Behavior is not correct
|
|
return stopPlayback(state);
|
|
} else {
|
|
// Else, play next item
|
|
return state.set("currentIndex", newIndex);
|
|
}
|
|
},
|
|
[TOGGLE_RANDOM]: (state) => {
|
|
return state.set("isRandom", !state.get("isRandom"));
|
|
},
|
|
[TOGGLE_REPEAT]: (state) => {
|
|
return state.set("isRepeat", !state.get("isRepeat"));
|
|
},
|
|
[TOGGLE_MUTE]: (state) => {
|
|
return state.set("isMute", !state.get("isMute"));
|
|
},
|
|
[SET_VOLUME]: (state, payload) => {
|
|
return state.set("volume", payload.volume);
|
|
},
|
|
[INVALIDATE_STORE]: () => {
|
|
return new stateRecord();
|
|
},
|
|
});
|