Skip to content

Commit

Permalink
prepare ex12 with final
Browse files Browse the repository at this point in the history
  • Loading branch information
wolfgangGoedel committed Mar 26, 2019
1 parent 7e34bae commit 151f2d3
Show file tree
Hide file tree
Showing 8 changed files with 445 additions and 0 deletions.
48 changes: 48 additions & 0 deletions src/ex12/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { useEffect } from "react";
import { Switch, Route, Redirect } from "react-router-dom";

import { Header, HeaderActionItem } from "../solution/Header";
import { SearchableListView } from "../solution/SearchableList";
import { PlayerView } from "../solution/Player";
import { Person } from "../solution/EditablePerson";
import { Loading } from "../solution/Loading";

import {
withLoadPeople,
withPersonFromPersonId,
withPersonHandlers,
withFilteredPeopleIds,
withPeopleTriptych
} from "./connect";
// } from "../solution/ex12/connect";

const ConnectedList = withFilteredPeopleIds(SearchableListView);
const ConnectedPlayer = withPeopleTriptych(PlayerView);
const ConnectedPerson = withPersonFromPersonId(withPersonHandlers(Person));

export const App = withLoadPeople(({ loadPeople, loading }) => {
useEffect(() => void loadPeople(), [loadPeople]);
return (
<>
<Header>
<HeaderActionItem to="/player" icon="view_carousel" />
<HeaderActionItem to="/list" icon="view_module" />
</Header>
{loading ? (
<Loading />
) : (
<Switch>
<Route path="/list" component={ConnectedList} />
<Route path="/player" component={ConnectedPlayer} />
<Route
path="/person/:id"
render={({ match }) => (
<ConnectedPerson personId={match.params.id} />
)}
/>
<Redirect to="/list" />
</Switch>
)}
</>
);
});
59 changes: 59 additions & 0 deletions src/ex12/connect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { connect } from "react-redux";
import { savePerson, loadPeople } from "../utils";
import {
getPeopleLoading,
getFilteredPeopleIds,
getCurrent,
getTriptych,
getPersonById,
getQuery,
SetPeople,
SetPerson,
SetQuery,
SetNextPerson,
SetPrevPerson,
SetCurrentPerson
} from "./state";

export const withLoadPeople = connect(
state => ({
loading: getPeopleLoading(state)
}),
dispatch => ({
loadPeople: () => loadPeople().then(people => dispatch(SetPeople(people)))
})
);

export const withPersonFromPersonId = connect((state, { personId }) => ({
person: getPersonById(state, personId)
}));

export const withPersonHandlers = connect(
undefined,
dispatch => ({
onUpdate: person =>
savePerson(person).then(person => dispatch(SetPerson(person))),
onDisplay: personId => dispatch(SetCurrentPerson(personId))
})
);

export const withFilteredPeopleIds = connect(
state => ({
people: getFilteredPeopleIds(state),
currentId: getCurrent(state),
query: getQuery(state)
}),
dispatch => ({
setQuery: query => dispatch(SetQuery(query))
})
);

export const withPeopleTriptych = connect(
state => ({
triptych: getTriptych(state)
}),
dispatch => ({
onNext: () => dispatch(SetNextPerson()),
onPrev: () => dispatch(SetPrevPerson())
})
);
22 changes: 22 additions & 0 deletions src/ex12/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet"
/>
<link href="../styles/main.less" rel="stylesheet" />
<title>12 - Embracing Redux</title>
</head>
<body class="mdc-typography mdc-theme--background">
<div id="root"></div>
<script src="main.js"></script>
</body>
</html>
22 changes: 22 additions & 0 deletions src/ex12/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from "react";
import { render } from "react-dom";
import { createStore } from "redux";
import { devToolsEnhancer } from "redux-devtools-extension";
import { Provider } from "react-redux";

import { Config } from "../solution/Config";

import { App } from "./App";
import { reducer } from "./state";
// import { reducer } from "../solution/ex12/state";

const store = createStore(reducer, devToolsEnhancer());

render(
<Config useRouter>
<Provider store={store}>
<App />
</Provider>
</Config>,
document.getElementById("root")
);
116 changes: 116 additions & 0 deletions src/ex12/state.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { createSelector } from "reselect";
import { toRing } from "../utils";

const initialState = {
people: {
map: {},
all: [],
loading: true
},
query: "",
current: null
};

const onSetPeople = (state, { people }) => {
const map = people.reduce(
(acc, cur) => Object.assign(acc, { [cur.id]: cur }),
{}
);
const all = people.map(p => p.id);
const current =
all.length > 0
? all.includes(state.current)
? state.current
: all[0]
: null;

return {
...state,
people: { map, all, loading: false },
current
};
};

export const reducer = (state = initialState, action) => {
switch (action.type) {
case "SET_PEOPLE":
return onSetPeople(state, action);
case "SET_PERSON":
return {
...state,
people: {
...state.people,
map: {
...state.people.map,
[action.person.id]: action.person
}
}
};
case "SET_QUERY":
return {
...state,
query: action.query
};
case "SET_CURRENT_PERSON":
return state.people.all.includes(action.personId) &&
state.current !== action.personId
? {
...state,
current: action.personId
}
: state;
case "SET_NEXT_PERSON":
return {
...state,
current: toRing(state.people.all, state.current).next
};
case "SET_PREV_PERSON":
return {
...state,
current: toRing(state.people.all, state.current).prev
};
default:
return state;
}
};

export const getPersonById = (state, personId) => state.people.map[personId];

export const getPeopleIds = state => state.people.all;
export const getPeopleMap = state => state.people.map;
export const getPeopleLoading = state => state.people.loading;

export const getQuery = state => state.query;

export const getCurrent = state => state.current;
export const getTriptych = createSelector(
getCurrent,
state => toRing(state.people.all, state.current),
(curr, { next, prev }) => [prev, curr, next]
);

const nameContains = query => {
const re = new RegExp(query, "i");
return p => re.test(p.firstname) || re.test(p.lastname);
};

export const getFilteredPeopleIds = createSelector(
getPeopleIds,
getPeopleMap,
getQuery,
(pids, dict, query) =>
pids
.map(pid => dict[pid])
.filter(nameContains(query))
.map(p => p.id)
);

export const SetPeople = (people = []) => ({ type: "SET_PEOPLE", people });
export const SetPerson = person => ({ type: "SET_PERSON", person });
export const SetQuery = query => ({ type: "SET_QUERY", query });
export const SetCurrentPerson = personId => ({
type: "SET_CURRENT_PERSON",
personId
});
export const SetNextPerson = () => ({ type: "SET_NEXT_PERSON" });
export const SetPrevPerson = () => ({ type: "SET_PREV_PERSON" });
3 changes: 3 additions & 0 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ <h2>Demos</h2>
>11 - Encapsulating Redux</a
>
</li>
<li>
<a href="//localhost:1234/ex12/index.html">12 - Embracing Redux</a>
</li>
</ul>
</setion>
</body>
Expand Down
59 changes: 59 additions & 0 deletions src/solution/ex12/connect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { connect } from "react-redux";
import { savePerson, loadPeople } from "../../utils";
import {
getPeopleLoading,
getFilteredPeopleIds,
getCurrent,
getTriptych,
getPersonById,
getQuery,
SetPeople,
SetPerson,
SetQuery,
SetNextPerson,
SetPrevPerson,
SetCurrentPerson
} from "./state";

export const withLoadPeople = connect(
state => ({
loading: getPeopleLoading(state)
}),
dispatch => ({
loadPeople: () => loadPeople().then(people => dispatch(SetPeople(people)))
})
);

export const withPersonFromPersonId = connect((state, { personId }) => ({
person: getPersonById(state, personId)
}));

export const withPersonHandlers = connect(
undefined,
dispatch => ({
onUpdate: person =>
savePerson(person).then(person => dispatch(SetPerson(person))),
onDisplay: personId => dispatch(SetCurrentPerson(personId))
})
);

export const withFilteredPeopleIds = connect(
state => ({
people: getFilteredPeopleIds(state),
currentId: getCurrent(state),
query: getQuery(state)
}),
dispatch => ({
setQuery: query => dispatch(SetQuery(query))
})
);

export const withPeopleTriptych = connect(
state => ({
triptych: getTriptych(state)
}),
dispatch => ({
onNext: () => dispatch(SetNextPerson()),
onPrev: () => dispatch(SetPrevPerson())
})
);
Loading

0 comments on commit 151f2d3

Please sign in to comment.