Skip to content

Commit

Permalink
Merge branch 'practical-redux-part-8-form-draft-data' into 'master'
Browse files Browse the repository at this point in the history
Practical Redux, Part 8: Form Draft Data

See merge request markerikson/project-minimek-course!6
  • Loading branch information
markerikson committed Nov 11, 2017
2 parents 58bc773 + de269ed commit 6cdb605
Show file tree
Hide file tree
Showing 25 changed files with 516 additions and 45 deletions.
7 changes: 7 additions & 0 deletions src/app/reducers/editingEntitiesReducer.js
@@ -0,0 +1,7 @@
import {createReducer} from "common/utils/reducerUtils";

import orm from "app/orm";
const defaultEditingEntities = orm.getEmptyState();

export default createReducer(defaultEditingEntities, {
});
2 changes: 1 addition & 1 deletion src/app/reducers/entitiesReducer.js
@@ -1,6 +1,6 @@
import {createReducer} from "common/utils/reducerUtils";

import orm from "app/orm"
import orm from "app/orm";

import {DATA_LOADED} from "features/tools/toolConstants";

Expand Down
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 pilotsReducer from "features/pilots/pilotsReducer";
import mechsReducer from "features/mechs/mechsReducer";
import tabReducer from "features/tabs/tabsReducer";
import unitInfoReducer from "features/unitInfo/unitInfoReducer";

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


const combinedReducer = combineReducers({
entities : entitiesReducer,
editingEntities : editingEntitiesReducer,
pilots : pilotsReducer,
mechs : mechsReducer,
unitInfo : unitInfoReducer,
Expand All @@ -22,6 +25,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 applyItemEdits(itemType, itemID) {
return {
type : EDIT_ITEM_APPLY,
payload : {
itemType,
itemID
},
};
}

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

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";
126 changes: 126 additions & 0 deletions src/features/editing/editingReducer.js
@@ -0,0 +1,126 @@
import {createReducer} from "common/utils/reducerUtils";

import orm from "app/orm";

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 = orm.session(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 = orm.session(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);
}
}

// The session contains our new "current" relational data
const updatedEntities = writeSession.state;
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 orm from "app/orm";


export const selectEditingEntities = state => state.editingEntities;

export const getEditingEntitiesSession = createSelector(
selectEditingEntities,
editingEntities => orm.session(editingEntities)
);
27 changes: 27 additions & 0 deletions src/features/editing/editingUtils.js
@@ -0,0 +1,27 @@
import orm from "app/orm";
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 = orm.session(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 @@ -10,3 +14,20 @@ export function updateEntity(itemType, itemID, 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,
},
};
}
2 changes: 2 additions & 0 deletions src/features/entities/entityConstants.js
@@ -1 +1,3 @@
export const ENTITY_UPDATE = "ENTITY_UPDATE";
export const ENTITY_CREATE = "ENTITY_CREATE";
export const ENTITY_DELETE = "ENTITY_DELETE";
39 changes: 38 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,41 @@ export function updateEntity(state, payload) {
return newState;
}

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

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

let newState = state;

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

modelInstance.delete();

newState = session.state
}

return newState;
}

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

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

ModelClass.parse(newItemAttributes);

return session.state;
}


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 orm from "app/orm";

export const selectEntities = state => state.entities;

export const getEntitiesSession = createSelector(
selectEntities,
entities => orm.session(entities)
);

0 comments on commit 6cdb605

Please sign in to comment.