Skip to content

Commit

Permalink
Merge pull request #9 from markerikson/practical-redux-part-8-final
Browse files Browse the repository at this point in the history
Practical Redux Part 8 - final
  • Loading branch information
markerikson committed Jan 26, 2017
2 parents 13c657c + a4c2ce7 commit 4576f5d
Show file tree
Hide file tree
Showing 24 changed files with 522 additions and 47 deletions.
7 changes: 7 additions & 0 deletions src/app/reducers/editingEntitiesReducer.js
@@ -0,0 +1,7 @@
import {createReducer} from "common/utils/reducerUtils";

import schema from "app/schema";
const defaultEditingEntities = schema.getDefaultState();

export default createReducer(defaultEditingEntities, {
});
4 changes: 4 additions & 0 deletions src/app/reducers/rootReducer.js
Expand Up @@ -3,16 +3,19 @@ import {combineReducers} from "redux";
import {reduceReducers} from "common/utils/reducerUtils";

import entitiesReducer from "./entitiesReducer";
import editingEntitiesReducer from "./editingEntitiesReducer";
import tabReducer from "features/tabs/tabReducer";
import unitInfoReducer from "features/unitInfo/unitInfoReducer";
import pilotsReducer from "features/pilots/pilotsReducer";
import mechsReducer from "features/mechs/mechsReducer";

import entityCrudReducer from "features/entities/entityReducer";
import editingFeatureReducer from "features/editing/editingReducer";


const combinedReducer = combineReducers({
entities : entitiesReducer,
editingEntities : editingEntitiesReducer,
unitInfo : unitInfoReducer,
pilots : pilotsReducer,
mechs : mechsReducer,
Expand All @@ -23,6 +26,7 @@ const combinedReducer = combineReducers({
const rootReducer = reduceReducers(
combinedReducer,
entityCrudReducer,
editingFeatureReducer,
);

export default rootReducer;
5 changes: 5 additions & 0 deletions src/common/utils/modelUtils.js
@@ -0,0 +1,5 @@
export function getModelByType(session, itemType, itemID) {
const modelClass = session[itemType];
const model = modelClass.withId(itemID);
return model;
}
59 changes: 59 additions & 0 deletions src/features/editing/editingActions.js
@@ -0,0 +1,59 @@
import {
EDIT_ITEM_EXISTING,
EDIT_ITEM_UPDATE,
EDIT_ITEM_APPLY,
EDIT_ITEM_STOP,
EDIT_ITEM_RESET,
} from "./editingConstants";


export function editExistingItem(itemType, itemID) {
return {
type : EDIT_ITEM_EXISTING,
payload : {
itemType,
itemID
},
};
}

export function editItemAttributes(itemType, itemID, newItemAttributes) {
return {
type : EDIT_ITEM_UPDATE,
payload : {
itemType,
itemID,
newItemAttributes,
},
};
}

export function applyItemEdits(itemType, itemID) {
return {
type : EDIT_ITEM_APPLY,
payload : {
itemType,
itemID
},
};
}

export function stopEditingItem(itemType, itemID) {
return {
type : EDIT_ITEM_STOP,
payload : {
itemType,
itemID
},
};
}

export function resetEditedItem(itemType, itemID) {
return {
type : EDIT_ITEM_RESET,
payload : {
itemType,
itemID,
},
};
}
5 changes: 5 additions & 0 deletions src/features/editing/editingConstants.js
@@ -0,0 +1,5 @@
export const EDIT_ITEM_EXISTING = "EDIT_ITEM_EXISTING";
export const EDIT_ITEM_UPDATE = "EDIT_ITEM_UPDATE";
export const EDIT_ITEM_APPLY = "EDIT_ITEM_APPLY";
export const EDIT_ITEM_STOP = "EDIT_ITEM_STOP";
export const EDIT_ITEM_RESET = "EDIT_ITEM_RESET";
129 changes: 129 additions & 0 deletions src/features/editing/editingReducer.js
@@ -0,0 +1,129 @@
import {createReducer} from "common/utils/reducerUtils";

import schema from "app/schema";

import {
createEntity,
updateEntity,
deleteEntity
} from "features/entities/entityReducer";

import {
EDIT_ITEM_EXISTING,
EDIT_ITEM_UPDATE,
EDIT_ITEM_APPLY,
EDIT_ITEM_STOP,
EDIT_ITEM_RESET,
} from "./editingConstants";

import {getModelByType} from "common/utils/modelUtils";

import {selectEntities} from "features/entities/entitySelectors";
import {selectEditingEntities} from "./editingSelectors";
import {
readEntityData,
updateEntitiesState,
updateEditingEntitiesState,
} from "./editingUtils";



export function copyEntity(sourceEntities, destinationEntities, payload) {
const {itemID, itemType} = payload;

const newItemAttributes = readEntityData(sourceEntities, itemType, itemID);
const creationPayload = {itemType, itemID, newItemAttributes}

const updatedEntities = createEntity(destinationEntities, creationPayload);
return updatedEntities;
}



export function updateEditedEntity(sourceEntities, destinationEntities, payload) {
// Start by reading our "work-in-progress" data
const readSession = schema.from(sourceEntities);

const {itemType, itemID} = payload;

// Look up the model instance for the requested item
const model = getModelByType(readSession, itemType, itemID);


// We of course will be updating our "current" relational data
let writeSession = schema.from(destinationEntities);

const ModelClass = writeSession[itemType];

if(ModelClass.hasId(itemID)) {
// Look up the original Model instance for the top item
const existingItem = ModelClass.withId(itemID);

if(existingItem.updateFrom) {
// Each model class should know how to properly update itself and its
// relations from another model of the same type. Ask the original model to
// update itself based on the "work-in-progress" model, which queues up a
// series of immutable add/update/delete actions internally
existingItem.updateFrom(model);
}
}

// Immutably apply the changes and generate our new "current" relational data
const updatedEntities = writeSession.reduce();
return updatedEntities;
}




export function editItemExisting(state, payload) {
const entities = selectEntities(state);
const editingEntities = selectEditingEntities(state);

const updatedEditingEntities = copyEntity(entities, editingEntities, payload);

return updateEditingEntitiesState(state, updatedEditingEntities);
}

export function editItemUpdate(state, payload) {
const editingEntities = selectEditingEntities(state);

const updatedEditingEntities = updateEntity(editingEntities, payload);
return updateEditingEntitiesState(state, updatedEditingEntities);
}

export function editItemStop(state, payload) {
const editingEntities = selectEditingEntities(state);

const updatedEditingEntities = deleteEntity(editingEntities, payload);
return updateEditingEntitiesState(state, updatedEditingEntities);
}


export function editItemApply(state, payload) {
const entities = selectEntities(state);
const editingEntities = selectEditingEntities(state);

const updatedEntities = updateEditedEntity(editingEntities, entities, payload);
return updateEntitiesState(state, updatedEntities);
}


export function editItemReset(state, payload) {
const stateWithoutItem = editItemStop(state, payload);
const stateWithCurrentItem = editItemExisting(stateWithoutItem, payload);

return stateWithCurrentItem;
}



const editingFeatureReducer = createReducer({}, {
[EDIT_ITEM_EXISTING] : editItemExisting,
[EDIT_ITEM_UPDATE] : editItemUpdate,
[EDIT_ITEM_APPLY] : editItemApply,
[EDIT_ITEM_STOP] : editItemStop,
[EDIT_ITEM_RESET] : editItemReset,
});

export default editingFeatureReducer;
11 changes: 11 additions & 0 deletions src/features/editing/editingSelectors.js
@@ -0,0 +1,11 @@
import {createSelector} from "reselect";

import schema from "app/schema";


export const selectEditingEntities = state => state.editingEntities;

export const getEditingEntitiesSession = createSelector(
selectEditingEntities,
editingEntities => schema.from(editingEntities)
);
27 changes: 27 additions & 0 deletions src/features/editing/editingUtils.js
@@ -0,0 +1,27 @@
import schema from "app/schema";
import {getModelByType} from "common/utils/modelUtils";

export function updateEditingEntitiesState(state, updatedEditingEntities) {
return {
...state,
editingEntities : updatedEditingEntities,
};
}

export function updateEntitiesState(state, updatedEntities) {
return {
...state,
entities : updatedEntities,
};
}

export function readEntityData(entities, itemType, itemID) {
const readSession = schema.from(entities);

// Look up the model instance for the requested item
const model = getModelByType(readSession, itemType, itemID);
const data = model.toJSON();

return data;
}

23 changes: 22 additions & 1 deletion src/features/entities/entityActions.js
@@ -1,4 +1,8 @@
import {ENTITY_UPDATE} from "./entityConstants";
import {
ENTITY_UPDATE,
ENTITY_DELETE,
ENTITY_CREATE,
} from "./entityConstants";

export function updateEntity(itemType, itemID, newItemAttributes) {
return {
Expand All @@ -9,4 +13,21 @@ export function updateEntity(itemType, itemID, newItemAttributes) {
newItemAttributes,
},
};
}

export function deleteEntity(itemType, itemID) {
return {
type : ENTITY_DELETE,
payload : {itemType, itemID}
};
}

export function createEntity(itemType, newItemAttributes) {
return {
type : ENTITY_CREATE,
payload : {
itemType,
newItemAttributes,
},
};
}
4 changes: 3 additions & 1 deletion src/features/entities/entityConstants.js
@@ -1 +1,3 @@
export const ENTITY_UPDATE = "ENTITY_UPDATE";
export const ENTITY_UPDATE = "ENTITY_UPDATE";
export const ENTITY_CREATE = "ENTITY_CREATE";
export const ENTITY_DELETE = "ENTITY_DELETE";
42 changes: 41 additions & 1 deletion src/features/entities/entityReducer.js
@@ -1,4 +1,8 @@
import {ENTITY_UPDATE} from "./entityConstants";
import {
ENTITY_UPDATE,
ENTITY_DELETE,
ENTITY_CREATE,
} from "./entityConstants";

import {createConditionalSliceReducer} from "common/utils/reducerUtils";

Expand All @@ -23,8 +27,44 @@ export function updateEntity(state, payload) {
return newState;
}


export function deleteEntity(state, payload) {
const {itemID, itemType} = payload;

const session = schema.from(state);
const ModelClass = session[itemType];

let newState = state;

if(ModelClass.hasId(itemID)) {
const modelInstance = ModelClass.withId(itemID);

modelInstance.delete();

// Immutably apply updates and return the new entities structure
newState = session.reduce();
}

return newState;
}

export function createEntity(state, payload) {
const {itemType, newItemAttributes} = payload;

const session = schema.from(state);
const ModelClass = session[itemType];

ModelClass.parse(newItemAttributes);

const newState = session.reduce();
return newState;
}


const entityHandlers = {
[ENTITY_UPDATE] : updateEntity,
[ENTITY_CREATE] : createEntity,
[ENTITY_DELETE] : deleteEntity,
};

const entityCrudFeatureReducer = createConditionalSliceReducer("entities", entityHandlers);
Expand Down
10 changes: 10 additions & 0 deletions src/features/entities/entitySelectors.js
@@ -0,0 +1,10 @@
import {createSelector} from "reselect";

import schema from "app/schema";

export const selectEntities = state => state.entities;

export const getEntitiesSession = createSelector(
selectEntities,
entities => schema.from(entities)
);

0 comments on commit 4576f5d

Please sign in to comment.