Phyks (Lucas Verney)
d8a7d4f66a
Full rework of webplayer. Webplayer is back to its previous working state, and ready for further improvements.
218 lines
6.4 KiB
JavaScript
218 lines
6.4 KiB
JavaScript
/**
|
|
* This implements the global entities reducer.
|
|
*/
|
|
|
|
// NPM imports
|
|
import Immutable from "immutable";
|
|
|
|
// Local imports
|
|
import { createReducer } from "../utils";
|
|
|
|
// Models
|
|
import { stateRecord } from "../models/entities";
|
|
|
|
// Actions
|
|
import {
|
|
API_REQUEST,
|
|
API_FAILURE,
|
|
PUSH_ENTITIES,
|
|
INCREMENT_REFCOUNT,
|
|
DECREMENT_REFCOUNT,
|
|
INVALIDATE_STORE,
|
|
} from "../actions";
|
|
|
|
|
|
/**
|
|
* Helper methods
|
|
*/
|
|
|
|
/**
|
|
* Update the reference counter for a given item.
|
|
*
|
|
* Do not do any garbage collection.
|
|
*
|
|
* @param state The state object to update.
|
|
* @param keyPath The keyPath to update, from the refCount key.
|
|
* @param incr The increment (or decrement) for the reference counter.
|
|
*
|
|
* @return An updated state.
|
|
*/
|
|
function updateRefCount(state, keyPath, incr) {
|
|
// Prepend refCounts to keyPath
|
|
const refCountKeyPath = Array.concat(["refCounts"], keyPath);
|
|
// Get updated value
|
|
let newRefCount = state.getIn(refCountKeyPath) + incr;
|
|
if (isNaN(newRefCount)) {
|
|
// If NaN, reference does not exist, so set it to ±1
|
|
newRefCount = Math.sign(incr);
|
|
}
|
|
// Update state
|
|
return state.setIn(refCountKeyPath, newRefCount);
|
|
}
|
|
|
|
|
|
/**
|
|
* Update the reference counter of a given entity, taking into account the
|
|
* nested objects.
|
|
*
|
|
* Do not do any garbage collection.
|
|
*
|
|
* @param state The state object to update.
|
|
* @param itemName The type of the entity object.
|
|
* @param id The id of the entity.
|
|
* @param entity The entity object, as Immutable.
|
|
* @param incr The increment (or decrement) for the reference counter.
|
|
*
|
|
* @return An updated state.
|
|
*/
|
|
function updateEntityRefCount(state, itemName, id, entity, incr) {
|
|
let newState = state;
|
|
let albums = null;
|
|
let tracks = null;
|
|
switch (itemName) {
|
|
case "artist":
|
|
// Update artist refCount
|
|
newState = updateRefCount(newState, ["artist", id], incr);
|
|
// Update nested albums refCount
|
|
albums = entity.get("albums");
|
|
if (Immutable.List.isList(albums)) {
|
|
albums.forEach(function (id) {
|
|
newState = updateRefCount(newState, ["album", id], incr);
|
|
});
|
|
}
|
|
// Update nested tracks refCount
|
|
tracks = entity.get("songs");
|
|
if (Immutable.List.isList(tracks)) {
|
|
tracks.forEach(function (id) {
|
|
newState = updateRefCount(newState, ["song", id], incr);
|
|
});
|
|
}
|
|
break;
|
|
case "album":
|
|
// Update album refCount
|
|
newState = updateRefCount(newState, ["album", id], incr);
|
|
// Update nested artist refCount
|
|
newState = updateRefCount(newState, ["artist", entity.get("artist")], incr);
|
|
// Update nested tracks refCount
|
|
tracks = entity.get("tracks");
|
|
if (Immutable.List.isList(tracks)) {
|
|
tracks.forEach(function (id) {
|
|
newState = updateRefCount(newState, ["song", id], incr);
|
|
});
|
|
}
|
|
break;
|
|
case "song":
|
|
// Update track refCount
|
|
newState = updateRefCount(newState, ["song", id], incr);
|
|
// Update nested artist refCount
|
|
newState = updateRefCount(newState, ["artist", entity.get("artist")], incr);
|
|
// Update nested album refCount
|
|
newState = updateRefCount(newState, ["album", entity.get("album")], incr);
|
|
break;
|
|
default:
|
|
// Just update the entity, no nested entities
|
|
newState = updateRefCount(newState, [itemName, id], incr);
|
|
break;
|
|
}
|
|
return newState;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
function garbageCollection(state) {
|
|
let newState = state;
|
|
state.refCounts.forEach(function (refCounts, itemName) {
|
|
refCounts.forEach(function (refCount, id) {
|
|
if (refCount < 1) {
|
|
// Garbage collection
|
|
newState = newState.deleteIn(["entities", itemName, id]);
|
|
newState = newState.deleteIn(["refCounts", itemName, id]);
|
|
}
|
|
});
|
|
});
|
|
return newState;
|
|
}
|
|
|
|
|
|
/**
|
|
* Initial state
|
|
*/
|
|
var initialState = new stateRecord();
|
|
|
|
|
|
/**
|
|
* Reducer
|
|
*/
|
|
export default createReducer(initialState, {
|
|
[API_REQUEST]: (state) => {
|
|
return (
|
|
state
|
|
.set("isFetching", true)
|
|
.set("error", null)
|
|
);
|
|
},
|
|
[API_FAILURE]: (state, payload) => {
|
|
return (
|
|
state
|
|
.set("isFetching", false)
|
|
.set("error", payload.error)
|
|
);
|
|
},
|
|
[PUSH_ENTITIES]: (state, payload) => {
|
|
let newState = state;
|
|
|
|
// Unset error and isFetching
|
|
newState = state.set("isFetching", false).set("error", payload.error);
|
|
|
|
// Merge entities
|
|
newState = newState.mergeIn(["entities"], payload.entities);
|
|
|
|
// Increment reference counter
|
|
payload.refCountType.forEach(function (itemName) {
|
|
const entities = payload.entities[itemName];
|
|
for (let id in entities) {
|
|
const entity = newState.getIn(["entities", itemName, id]);
|
|
newState = updateEntityRefCount(newState, itemName, id, entity, 1);
|
|
}
|
|
});
|
|
|
|
return newState;
|
|
},
|
|
[INCREMENT_REFCOUNT]: (state, payload) => {
|
|
let newState = state;
|
|
|
|
// Increment reference counter
|
|
for (let itemName in payload.entities) {
|
|
const entities = payload.entities[itemName];
|
|
entities.forEach(function (id) {
|
|
const entity = newState.getIn(["entities", itemName, id]);
|
|
newState = updateEntityRefCount(newState, itemName, id, entity, 1);
|
|
});
|
|
}
|
|
|
|
return newState;
|
|
},
|
|
[DECREMENT_REFCOUNT]: (state, payload) => {
|
|
let newState = state;
|
|
|
|
// Decrement reference counter
|
|
for (let itemName in payload.entities) {
|
|
const entities = payload.entities[itemName];
|
|
entities.forEach(function (id) {
|
|
const entity = newState.getIn(["entities", itemName, id]);
|
|
newState = updateEntityRefCount(newState, itemName, id, entity, -1);
|
|
});
|
|
}
|
|
|
|
// Perform garbage collection
|
|
newState = garbageCollection(newState);
|
|
|
|
return newState;
|
|
},
|
|
[INVALIDATE_STORE]: () => {
|
|
return new stateRecord();
|
|
},
|
|
});
|