webamp/js/reducers/playlist.ts
Jordan Eldredge 646753526b Fix lints
2019-11-30 13:42:53 -08:00

169 lines
4.6 KiB
TypeScript

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: PlaylistState = {
trackOrder: [],
currentTrack: null,
lastSelectedIndex: null,
selectedTracks: new Set(),
};
function toggleSetMembership<T>(set: Set<T>, value: T): void {
if (set.has(value)) {
set.delete(value);
} else {
set.add(value);
}
}
const playlist = (
state: PlaylistState = defaultPlaylistState,
action: 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,
// Using this as the lastClickedIndex is kinda funny, since you
// may have just _un_selected the track. However, this is what
// Winamp 2 does, so we'll copy it.
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:
// TODO: Consider disposing of ObjectUrls
return {
...state,
trackOrder: [],
currentTrack: null,
selectedTracks: new Set(),
lastSelectedIndex: null,
};
case REMOVE_TRACKS:
// TODO: Consider disposing of ObjectUrls
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))
),
// TODO: This could probably be made to work, but we clear it just to be safe.
lastSelectedIndex: null,
};
case REVERSE_LIST:
return {
...state,
trackOrder: [...state.trackOrder].reverse(),
// TODO: This could probably be made to work, but we clear it just to be safe.
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),
],
// TODO: This could probably be made to work, but we clear it just to be safe.
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
),
// TODO: This could probably be made to work, but we clear it just to be safe.
lastSelectedIndex: null,
};
default:
return state;
}
};
export default playlist;