import { Action } from "../types" ; import { CLICKED_TRACK , CTRL_CLICKED_TRACK , SHIFT_CLICKED_TRACK , SELECT_ALL , SELECT_ZERO , INVERT_SELECTION , REMOVE_ALL_TRACKS , REMOVE_TRACKS , ADD_TRACK_FROM_URL , REVERSE_LIST , RANDOMIZE_LIST , SET_TRACK_ORDER , PLAY_TRACK , BUFFER_TRACK , DRAG_SELECTED , } from "../actionTypes" ; import { shuffle , moveSelected } from "../utils" ; export interface PlaylistState { trackOrder : number [ ] ; lastSelectedIndex : number | null ; currentTrack : number | null ; selectedTracks : Set < number > ; } const defaultPlaylistState = { trackOrder : [ ] , currentTrack : null , lastSelectedIndex : null , selectedTracks : new Set ( ) , } ; function toggleSetMembership < T > ( set , value ) { if ( set . has ( value ) ) { set . delete ( value ) ; } else { set . add ( value ) ; } } const playlist = ( state = defaultPlaylistState , action ) : PlaylistState => { switch ( action . type ) { case CLICKED_TRACK : return { ... state , selectedTracks : new Set ( [ state . trackOrder [ action . index ] ] ) , lastSelectedIndex : action . index , } ; case CTRL_CLICKED_TRACK : { const id = state . trackOrder [ action . index ] ; const newSelectedTracks = new Set ( state . selectedTracks ) ; toggleSetMembership ( newSelectedTracks , id ) ; return { ... state , selectedTracks : newSelectedTracks , lastSelectedIndex : action . index , } ; } case SHIFT_CLICKED_TRACK : if ( state . lastSelectedIndex == null ) { return state ; } const clickedIndex = action . index ; const start = Math . min ( clickedIndex , state . lastSelectedIndex ) ; const end = Math . max ( clickedIndex , state . lastSelectedIndex ) ; const selectedTracks = new Set ( state . trackOrder . slice ( start , end + 1 ) ) ; return { ... state , selectedTracks , } ; case SELECT_ALL : return { ... state , selectedTracks : new Set ( state . trackOrder ) , } ; case SELECT_ZERO : return { ... state , selectedTracks : new Set ( ) , } ; case INVERT_SELECTION : return { ... state , selectedTracks : new Set ( state . trackOrder . filter ( ( id ) => ! state . selectedTracks . has ( id ) ) ) , } ; case REMOVE_ALL_TRACKS : return { ... state , trackOrder : [ ] , currentTrack : null , selectedTracks : new Set ( ) , lastSelectedIndex : null , } ; case REMOVE_TRACKS : const actionIds = new Set ( action . ids . map ( Number ) ) ; const { currentTrack } = state ; return { ... state , trackOrder : state . trackOrder . filter ( ( trackId ) => ! actionIds . has ( trackId ) ) , currentTrack : actionIds . has ( Number ( currentTrack ) ) ? null : currentTrack , selectedTracks : new Set ( Array . from ( state . selectedTracks ) . filter ( ( id ) => actionIds . has ( id ) ) ) , lastSelectedIndex : null , } ; case REVERSE_LIST : return { ... state , trackOrder : [ ... state . trackOrder ] . reverse ( ) , lastSelectedIndex : null , } ; case RANDOMIZE_LIST : return { ... state , trackOrder : shuffle ( state . trackOrder ) , } ; case SET_TRACK_ORDER : const { trackOrder } = action ; return { ... state , trackOrder } ; case ADD_TRACK_FROM_URL : const atIndex = action . atIndex == null ? state . trackOrder . length : action . atIndex ; return { ... state , trackOrder : [ ... state . trackOrder . slice ( 0 , atIndex ) , Number ( action . id ) , ... state . trackOrder . slice ( atIndex ) , ] , lastSelectedIndex : null , } ; case PLAY_TRACK : case BUFFER_TRACK : return { ... state , currentTrack : action . id , } ; case DRAG_SELECTED : return { ... state , trackOrder : moveSelected ( state . trackOrder , ( i ) => state . selectedTracks . has ( state . trackOrder [ i ] ) , action . offset ) , lastSelectedIndex : null , } ; default : return state ; } } ; export default playlist ;