From 852ece426fa256caf9c98e653598fb23abbb2827 Mon Sep 17 00:00:00 2001 From: Jarvis Ao Ieong Date: Sun, 14 Feb 2016 00:01:21 +0800 Subject: [PATCH] init --- .babelrc | 13 +++++ .editorconfig | 12 ++++ .eslintignore | 3 + .eslintrc | 27 +++++++++ .gitignore | 9 +++ .npmrc | 1 + README.md | 5 ++ package.json | 50 +++++++++++++++++ server.js | 20 +++++++ src/components/counter/Counter.js | 27 +++++++++ src/components/counter/counterActions.js | 10 ++++ src/components/counter/counterInit.js | 1 + src/components/counter/counterReducer.js | 15 +++++ src/components/counterFancy/CounterFancy.js | 33 +++++++++++ .../counterFancy/CounterWithRemoveButton.js | 32 +++++++++++ .../counterFancy/counterFancyActions.js | 18 ++++++ .../counterFancy/counterFancyInit.js | 6 ++ .../counterFancy/counterFancyReducer.js | 46 +++++++++++++++ src/components/counterList/CounterList.js | 39 +++++++++++++ .../counterList/counterListActions.js | 17 ++++++ src/components/counterList/counterListInit.js | 6 ++ .../counterList/counterListReducer.js | 46 +++++++++++++++ src/components/counterPair/CounterPair.js | 25 +++++++++ src/components/counterPair/countPairInit.js | 6 ++ .../counterPair/counterPairActions.js | 12 ++++ .../counterPair/counterPairReducer.js | 25 +++++++++ src/components/randomGif/RandomGif.js | 31 ++++++++++ src/components/randomGif/randomGifActions.js | 11 ++++ src/components/randomGif/randomGifInit.js | 12 ++++ src/components/randomGif/randomGifReducer.js | 30 ++++++++++ src/components/randomGif/randomGifTasks.js | 16 ++++++ src/components/randomGif/waiting.gif | Bin 0 -> 8457 bytes src/components/randomGifList/RandomGifList.js | 40 +++++++++++++ .../randomGifList/randomGifListActions.js | 13 +++++ .../randomGifList/randomGifListInit.js | 20 +++++++ .../randomGifList/randomGifListReducer.js | 53 ++++++++++++++++++ src/components/randomGifPair/RandomGifPair.js | 30 ++++++++++ .../randomGifPair/randomGifPairActions.js | 12 ++++ .../randomGifPair/randomGifPairInit.js | 28 +++++++++ .../randomGifPair/randomGifPairReducer.js | 43 ++++++++++++++ src/containers/App.js | 39 +++++++++++++ src/containers/CounterContainer.js | 8 +++ src/containers/CounterFancyContainer.js | 8 +++ src/containers/CounterListContainer.js | 8 +++ src/containers/CounterPairContainer.js | 8 +++ src/containers/RandomGifContainer.js | 8 +++ src/containers/RandomGifListContainer.js | 8 +++ src/containers/RandomGifPairContainer.js | 8 +++ src/favicon.ico | Bin 0 -> 9272 bytes src/helpers/superagent.js | 6 ++ src/index.html | 13 +++++ src/index.js | 17 ++++++ src/initialState.js | 32 +++++++++++ src/reducers/index.js | 7 +++ src/store/configureStore.js | 24 ++++++++ webpack.config.js | 38 +++++++++++++ webpack.development.js | 29 ++++++++++ webpack.production.js | 28 +++++++++ 58 files changed, 1132 insertions(+) create mode 100644 .babelrc create mode 100755 .editorconfig create mode 100644 .eslintignore create mode 100644 .eslintrc create mode 100755 .gitignore create mode 100644 .npmrc create mode 100644 README.md create mode 100644 package.json create mode 100644 server.js create mode 100644 src/components/counter/Counter.js create mode 100644 src/components/counter/counterActions.js create mode 100644 src/components/counter/counterInit.js create mode 100644 src/components/counter/counterReducer.js create mode 100644 src/components/counterFancy/CounterFancy.js create mode 100644 src/components/counterFancy/CounterWithRemoveButton.js create mode 100644 src/components/counterFancy/counterFancyActions.js create mode 100644 src/components/counterFancy/counterFancyInit.js create mode 100644 src/components/counterFancy/counterFancyReducer.js create mode 100644 src/components/counterList/CounterList.js create mode 100644 src/components/counterList/counterListActions.js create mode 100644 src/components/counterList/counterListInit.js create mode 100644 src/components/counterList/counterListReducer.js create mode 100644 src/components/counterPair/CounterPair.js create mode 100644 src/components/counterPair/countPairInit.js create mode 100644 src/components/counterPair/counterPairActions.js create mode 100644 src/components/counterPair/counterPairReducer.js create mode 100644 src/components/randomGif/RandomGif.js create mode 100644 src/components/randomGif/randomGifActions.js create mode 100644 src/components/randomGif/randomGifInit.js create mode 100644 src/components/randomGif/randomGifReducer.js create mode 100644 src/components/randomGif/randomGifTasks.js create mode 100644 src/components/randomGif/waiting.gif create mode 100644 src/components/randomGifList/RandomGifList.js create mode 100644 src/components/randomGifList/randomGifListActions.js create mode 100644 src/components/randomGifList/randomGifListInit.js create mode 100644 src/components/randomGifList/randomGifListReducer.js create mode 100644 src/components/randomGifPair/RandomGifPair.js create mode 100644 src/components/randomGifPair/randomGifPairActions.js create mode 100644 src/components/randomGifPair/randomGifPairInit.js create mode 100644 src/components/randomGifPair/randomGifPairReducer.js create mode 100644 src/containers/App.js create mode 100644 src/containers/CounterContainer.js create mode 100644 src/containers/CounterFancyContainer.js create mode 100644 src/containers/CounterListContainer.js create mode 100644 src/containers/CounterPairContainer.js create mode 100644 src/containers/RandomGifContainer.js create mode 100644 src/containers/RandomGifListContainer.js create mode 100644 src/containers/RandomGifPairContainer.js create mode 100644 src/favicon.ico create mode 100644 src/helpers/superagent.js create mode 100644 src/index.html create mode 100644 src/index.js create mode 100644 src/initialState.js create mode 100644 src/reducers/index.js create mode 100644 src/store/configureStore.js create mode 100644 webpack.config.js create mode 100644 webpack.development.js create mode 100644 webpack.production.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..2027edd --- /dev/null +++ b/.babelrc @@ -0,0 +1,13 @@ +{ + "presets": ["react", "es2015", "stage-0"], + "plugins": [ + "transform-runtime", + "transform-decorators-legacy", + "lodash" + ], + "env": { + "development": { + "presets": ["react-hmre"] + } + } +} diff --git a/.editorconfig b/.editorconfig new file mode 100755 index 0000000..4a7ea30 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..059e50b --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +**/node_modules +server.js +webpack.*.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..f6daa6e --- /dev/null +++ b/.eslintrc @@ -0,0 +1,27 @@ +{ + "ecmaFeatures": { + "jsx": true, + "modules": true + }, + "env": { + "browser": true, + "node": true + }, + "parser": "babel-eslint", + "rules": { + "quotes": [2, "single"], + "strict": [2, "never"], + "babel/generator-star-spacing": 1, + "babel/new-cap": 1, + "babel/object-shorthand": 1, + "babel/arrow-parens": 1, + "babel/no-await-in-loop": 1, + "react/jsx-uses-react": 2, + "react/jsx-uses-vars": 2, + "react/react-in-jsx-scope": 2 + }, + "plugins": [ + "babel", + "react" + ] +} diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..1d64ba7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.DS_Store + +npm-debug.log + +bower_components +node_modules + +config.js +dist diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..cffe8cd --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +save-exact=true diff --git a/README.md b/README.md new file mode 100644 index 0000000..f3163c7 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# redux-architecture + +> elm architecture in redux + +More information is coming. diff --git a/package.json b/package.json new file mode 100644 index 0000000..32c12ab --- /dev/null +++ b/package.json @@ -0,0 +1,50 @@ +{ + "name": "redux-architecture", + "version": "0.0.0", + "private": true, + "main": "server.js", + "scripts": { + "start": "node server", + "build": "webpack --config webpack.production.js --progress && npm run clean && mv _dist dist", + "clean": "rimraf dist" + }, + "devDependencies": { + "@ersinfotech/merge": "1.0.0", + "babel-core": "6.4.5", + "babel-eslint": "5.0.0-beta6", + "babel-loader": "6.2.1", + "babel-plugin-lodash": "2.0.1", + "babel-plugin-transform-decorators-legacy": "1.3.4", + "babel-plugin-transform-runtime": "6.3.13", + "babel-preset-es2015": "6.3.13", + "babel-preset-react": "6.3.13", + "babel-preset-react-hmre": "1.0.1", + "babel-preset-stage-0": "6.3.13", + "babel-runtime": "6.3.19", + "css-loader": "0.15.5", + "eslint": "1.10.3", + "eslint-plugin-babel": "3.0.0", + "eslint-plugin-react": "3.13.1", + "express": "4.13.3", + "extract-text-webpack-plugin": "0.8.2", + "file-loader": "0.8.4", + "html-webpack-plugin": "2.0.3", + "redux-logger": "0.0.3", + "rimraf": "2.4.3", + "style-loader": "0.12.3", + "webpack": "1.12.12", + "webpack-dev-middleware": "1.5.1", + "webpack-hot-middleware": "2.6.4" + }, + "dependencies": { + "@jarvisaoieong/redux-logger": "2.5.0", + "@jarvisaoieong/redux-loop": "1.0.8", + "bluebird": "2.9.34", + "lodash": "4.0.1", + "react": "0.14.3", + "react-dom": "0.14.3", + "react-redux": "4.2.1", + "redux": "3.2.1", + "superagent": "jarvisaoieong/superagent#845d2eff7910f051f93420edb0c898038f3a33e5" + } +} diff --git a/server.js b/server.js new file mode 100644 index 0000000..daf8b1e --- /dev/null +++ b/server.js @@ -0,0 +1,20 @@ +var express = require('express'); +var webpack = require('webpack'); +var webpackConfig = require('./webpack.development'); + +var app = express(); +var compiler = webpack(webpackConfig); + +app.use(require('webpack-dev-middleware')(compiler, { + stats: { + colors: true, + }, +})); + +app.use(require('webpack-hot-middleware')(compiler)); + +app.listen(process.env.PORT, function(err) { + if (err) { + console.log(err); + } +}); diff --git a/src/components/counter/Counter.js b/src/components/counter/Counter.js new file mode 100644 index 0000000..49f9836 --- /dev/null +++ b/src/components/counter/Counter.js @@ -0,0 +1,27 @@ +import React from 'react'; +import {inc, dec} from './counterActions'; + +export default (props) => { + const {model, dispatch} = props; + return ( +
+ + + {model} + + +
+ ); +} diff --git a/src/components/counter/counterActions.js b/src/components/counter/counterActions.js new file mode 100644 index 0000000..7851171 --- /dev/null +++ b/src/components/counter/counterActions.js @@ -0,0 +1,10 @@ +export const INC = 'INC'; +export const DEC = 'DEC'; + +export const inc = () => ({ + type: INC, +}); + +export const dec = () => ({ + type: DEC, +}); diff --git a/src/components/counter/counterInit.js b/src/components/counter/counterInit.js new file mode 100644 index 0000000..a071b85 --- /dev/null +++ b/src/components/counter/counterInit.js @@ -0,0 +1 @@ +export default (count = 0) => count diff --git a/src/components/counter/counterReducer.js b/src/components/counter/counterReducer.js new file mode 100644 index 0000000..e7bfc23 --- /dev/null +++ b/src/components/counter/counterReducer.js @@ -0,0 +1,15 @@ +import {INC, DEC} from './counterActions'; + +export const initialState = 0; + +export default (state = initialState, action) => { + if (action.type === INC) { + return state + 1; + } + + if (action.type === DEC) { + return state - 1; + } + + return state; +} diff --git a/src/components/counterFancy/CounterFancy.js b/src/components/counterFancy/CounterFancy.js new file mode 100644 index 0000000..fe10845 --- /dev/null +++ b/src/components/counterFancy/CounterFancy.js @@ -0,0 +1,33 @@ +import React, {Component} from 'react'; +import _ from 'lodash'; + +import CounterWithRemoveButton from './CounterWithRemoveButton' +import {add, modify} from './counterFancyActions'; + +export default class CounterList extends Component { + + render() { + const {model, dispatch} = this.props; + + return ( +
+ + { + _.map(model.counters, (counter) => + + ) + } +
+ ); + } + +} diff --git a/src/components/counterFancy/CounterWithRemoveButton.js b/src/components/counterFancy/CounterWithRemoveButton.js new file mode 100644 index 0000000..f131903 --- /dev/null +++ b/src/components/counterFancy/CounterWithRemoveButton.js @@ -0,0 +1,32 @@ +import React, {Component} from 'react'; + +import Counter from 'components/counter/Counter' +import {remove, modify} from './counterFancyActions'; + +export default class CounterWithRemoveButton extends Component { + + render() { + const {model, dispatch} = this.props; + + return ( +
+
+ dispatch(modify(model.id, action)), + }} /> +
+
+ +
+
+
+ ); + } + +} diff --git a/src/components/counterFancy/counterFancyActions.js b/src/components/counterFancy/counterFancyActions.js new file mode 100644 index 0000000..d743d5b --- /dev/null +++ b/src/components/counterFancy/counterFancyActions.js @@ -0,0 +1,18 @@ +export const ADD = 'ADD_FANCY'; +export const REMOVE = 'REMOVE_FANCY'; +export const MODIFY = 'MODIFY_FANCY'; + +export const add = () => ({ + type: ADD, +}); + +export const remove = (id) => ({ + type: REMOVE, + id, +}); + +export const modify = (id, action) => ({ + type: MODIFY, + id, + action, +}); diff --git a/src/components/counterFancy/counterFancyInit.js b/src/components/counterFancy/counterFancyInit.js new file mode 100644 index 0000000..55e10ab --- /dev/null +++ b/src/components/counterFancy/counterFancyInit.js @@ -0,0 +1,6 @@ +import counterInit from 'components/counter/counterInit'; + +export default (count) => ({ + counters: [{id: 0, data: counterInit(count)}], + nextId: 1, +}); diff --git a/src/components/counterFancy/counterFancyReducer.js b/src/components/counterFancy/counterFancyReducer.js new file mode 100644 index 0000000..c6ce626 --- /dev/null +++ b/src/components/counterFancy/counterFancyReducer.js @@ -0,0 +1,46 @@ +import {ADD, REMOVE, MODIFY} from './counterFancyActions'; +import _ from 'lodash'; +import counterReducer, {initialState as counterInitialState} from 'components/counter/counterReducer'; +import counterInit from 'components/counter/counterInit'; + +export const initialState = { + counters: [{id: 0, data: counterInitialState}], + nextId: 1, +} + +export default (state = initialState, action) => { + if (action.type === ADD) { + return { + ...state, + counters: [ + ...state.counters, + {id: state.nextId, data: counterInit()}, + ], + nextId: state.nextId + 1, + }; + }; + + if (action.type === REMOVE) { + return { + ...state, + counters: _.reject(state.counters, (counter) => counter.id === action.id), + }; + }; + + if (action.type === MODIFY) { + return { + ...state, + counters: _.map(state.counters, (counter) => { + if (counter.id !== action.id) { + return counter; + }; + return { + ...counter, + data: counterReducer(counter.data, action.action), + }; + }), + }; + }; + + return state; +} diff --git a/src/components/counterList/CounterList.js b/src/components/counterList/CounterList.js new file mode 100644 index 0000000..1ab092a --- /dev/null +++ b/src/components/counterList/CounterList.js @@ -0,0 +1,39 @@ +import React, {Component} from 'react'; +import _ from 'lodash'; + +import Counter from 'components/counter/Counter' +import {add, modify, remove} from './counterListActions'; + +export default class CounterList extends Component { + + render() { + const {model, dispatch} = this.props; + + return ( +
+ + + { + _.map(model.counters, (counter) => + dispatch(modify(counter.id, action)), + }} /> + ) + } +
+ ); + } + +} diff --git a/src/components/counterList/counterListActions.js b/src/components/counterList/counterListActions.js new file mode 100644 index 0000000..fb96824 --- /dev/null +++ b/src/components/counterList/counterListActions.js @@ -0,0 +1,17 @@ +export const ADD = 'ADD_LIST'; +export const REMOVE = 'REMOVE_LIST'; +export const MODIFY = 'MODIFY_LIST'; + +export const add = () => ({ + type: ADD, +}); + +export const remove = () => ({ + type: REMOVE, +}); + +export const modify = (id, action) => ({ + type: MODIFY, + id, + action, +}); diff --git a/src/components/counterList/counterListInit.js b/src/components/counterList/counterListInit.js new file mode 100644 index 0000000..55e10ab --- /dev/null +++ b/src/components/counterList/counterListInit.js @@ -0,0 +1,6 @@ +import counterInit from 'components/counter/counterInit'; + +export default (count) => ({ + counters: [{id: 0, data: counterInit(count)}], + nextId: 1, +}); diff --git a/src/components/counterList/counterListReducer.js b/src/components/counterList/counterListReducer.js new file mode 100644 index 0000000..1fe9e68 --- /dev/null +++ b/src/components/counterList/counterListReducer.js @@ -0,0 +1,46 @@ +import {ADD, REMOVE, MODIFY} from './counterListActions'; +import _ from 'lodash'; +import counterReducer, {initialState as counterInitialState} from 'components/counter/counterReducer'; +import counterInit from 'components/counter/counterInit'; + +export const initialState = { + counters: [{id: 0, data: counterInitialState}], + nextId: 1, +} + +export default (state = initialState, action) => { + if (action.type === ADD) { + return { + ...state, + counters: [ + ...state.counters, + {id: state.nextId, data: counterInit()}, + ], + nextId: state.nextId + 1, + }; + }; + + if (action.type === REMOVE) { + return { + ...state, + counters: _.drop(state.counters), + }; + }; + + if (action.type === MODIFY) { + return { + ...state, + counters: _.map(state.counters, (counter) => { + if (counter.id !== action.id) { + return counter; + }; + return { + ...counter, + data: counterReducer(counter.data, action.action), + }; + }), + }; + }; + + return state; +} diff --git a/src/components/counterPair/CounterPair.js b/src/components/counterPair/CounterPair.js new file mode 100644 index 0000000..c14437b --- /dev/null +++ b/src/components/counterPair/CounterPair.js @@ -0,0 +1,25 @@ +import React, {Component} from 'react'; + +import Counter from 'components/counter/Counter' +import {modifyFirst, modifySecond} from './counterPairActions'; + +export default class CounterPair extends Component { + + render() { + const {model, dispatch} = this.props; + + return ( +
+ dispatch(modifyFirst(action)), + }} /> + dispatch(modifySecond(action)), + }} /> +
+ ); + } + +} diff --git a/src/components/counterPair/countPairInit.js b/src/components/counterPair/countPairInit.js new file mode 100644 index 0000000..f4e6a86 --- /dev/null +++ b/src/components/counterPair/countPairInit.js @@ -0,0 +1,6 @@ +import counterInit from 'components/counter/counterInit'; + +export default (first, second) => ({ + first: counterInit(first), + second: counterInit(second), +}) diff --git a/src/components/counterPair/counterPairActions.js b/src/components/counterPair/counterPairActions.js new file mode 100644 index 0000000..1c44ea0 --- /dev/null +++ b/src/components/counterPair/counterPairActions.js @@ -0,0 +1,12 @@ +export const MODIFY_FIRST = 'MODIFY_FIRST_COUNTER'; +export const MODIFY_SECOND = 'MODIFY_SECOND_COUNTER'; + +export const modifyFirst = (action) => ({ + type: MODIFY_FIRST, + action, +}); + +export const modifySecond = (action) => ({ + type: MODIFY_SECOND, + action, +}); diff --git a/src/components/counterPair/counterPairReducer.js b/src/components/counterPair/counterPairReducer.js new file mode 100644 index 0000000..c571588 --- /dev/null +++ b/src/components/counterPair/counterPairReducer.js @@ -0,0 +1,25 @@ +import {MODIFY_FIRST, MODIFY_SECOND} from './counterPairActions'; +import counterReducer, {initialState as counterInitialState} from 'components/counter/counterReducer'; + +export const initialState = { + first: counterInitialState, + second: counterInitialState, +}; + +export default (state = initialState, action) => { + if (action.type === MODIFY_FIRST) { + return { + ...state, + first: counterReducer(state.first, action.action), + }; + }; + + if (action.type === MODIFY_SECOND) { + return { + ...state, + second: counterReducer(state.second, action.action), + }; + }; + + return state; +} diff --git a/src/components/randomGif/RandomGif.js b/src/components/randomGif/RandomGif.js new file mode 100644 index 0000000..e988003 --- /dev/null +++ b/src/components/randomGif/RandomGif.js @@ -0,0 +1,31 @@ +import React, {Component} from 'react'; +import _ from 'lodash'; + +import {requestMore} from './randomGifActions'; + +export default class RandomGif extends Component { + + render() { + const {model, dispatch} = this.props; + return ( +
+ {model.topic} + + +
+ ); + } + +} diff --git a/src/components/randomGif/randomGifActions.js b/src/components/randomGif/randomGifActions.js new file mode 100644 index 0000000..6742a52 --- /dev/null +++ b/src/components/randomGif/randomGifActions.js @@ -0,0 +1,11 @@ +export const REQUEST_MORE = 'REQUEST_MORE'; +export const NEW_GIF = 'NEW_GIF'; + +export const requestMore = () => ({ + type: REQUEST_MORE, +}); + +export const newGif = (url) => ({ + type: NEW_GIF, + url, +}); diff --git a/src/components/randomGif/randomGifInit.js b/src/components/randomGif/randomGifInit.js new file mode 100644 index 0000000..ed850d4 --- /dev/null +++ b/src/components/randomGif/randomGifInit.js @@ -0,0 +1,12 @@ +import {loop, Effects} from '@jarvisaoieong/redux-loop'; +import {fetchRandomGif} from './randomGifTasks'; +import {newGif} from './randomGifActions'; + +export default (topic) => { + return loop({ + topic, + gifUrl: require('./waiting.gif'), + }, + Effects.promise(fetchRandomGif, topic) + ); +} diff --git a/src/components/randomGif/randomGifReducer.js b/src/components/randomGif/randomGifReducer.js new file mode 100644 index 0000000..15ae0b4 --- /dev/null +++ b/src/components/randomGif/randomGifReducer.js @@ -0,0 +1,30 @@ +import {loop, Effects} from '@jarvisaoieong/redux-loop'; + +import {REQUEST_MORE, NEW_GIF, newGif} from './randomGifActions'; +import {fetchRandomGif} from './randomGifTasks'; + +export const initialState = { + topic: '', + gifUrl: require('./waiting.gif'), +}; + +export default (state = initialState, action) => { + if (action.type === REQUEST_MORE) { + return loop( + state + , + Effects.promise(fetchRandomGif, state.topic) + ); + }; + + if (action.type === NEW_GIF) { + return loop({ + ...state, + gifUrl: action.url, + }, + Effects.none() + ); + }; + + return loop(state, Effects.none()); +} diff --git a/src/components/randomGif/randomGifTasks.js b/src/components/randomGif/randomGifTasks.js new file mode 100644 index 0000000..8d0ee65 --- /dev/null +++ b/src/components/randomGif/randomGifTasks.js @@ -0,0 +1,16 @@ +import request from 'helpers/superagent'; +import _ from 'lodash'; +import {newGif} from './randomGifActions'; + +export const fetchRandomGif = (topic) => { + return request.get('http://api.giphy.com/v1/gifs/random') + .query({ + api_key: 'dc6zaTOxFJmzC', + tag: topic, + }) + .endAsync() + .then((res) => { + const url = _.get(res, 'body.data.image_url'); + return newGif(url); + }); +} diff --git a/src/components/randomGif/waiting.gif b/src/components/randomGif/waiting.gif new file mode 100644 index 0000000000000000000000000000000000000000..fe8fc55251269e645df1284824bd27b252cac60c GIT binary patch literal 8457 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 literal 0 HcmV?d00001 diff --git a/src/components/randomGifList/RandomGifList.js b/src/components/randomGifList/RandomGifList.js new file mode 100644 index 0000000..9111c84 --- /dev/null +++ b/src/components/randomGifList/RandomGifList.js @@ -0,0 +1,40 @@ +import React, {Component} from 'react'; +import _ from 'lodash'; + +import RandomGif from 'components/randomGif/RandomGif' +import {create, modify} from './randomGifListActions'; + +export default class RandomGifList extends Component { + + handleSubmit = (e) => { + e.preventDefault(); + const {dispatch} = this.props; + dispatch(create(this.refs.text.value)); + this.refs.text.value = ''; + }; + + render() { + const {model, dispatch} = this.props; + + return ( +
+
+ + +
+ { + _.map(model.gifList, (gif) => +
+ dispatch(modify(gif.id, action)), + }} /> +
+ ) + } +
+
+ ); + } + +} diff --git a/src/components/randomGifList/randomGifListActions.js b/src/components/randomGifList/randomGifListActions.js new file mode 100644 index 0000000..51f5054 --- /dev/null +++ b/src/components/randomGifList/randomGifListActions.js @@ -0,0 +1,13 @@ +export const CREATE = 'CREATE_RANDOMGIFLIST'; +export const MODIFY = 'MODIFY_RANDOMGIFLIST'; + +export const create = (topic) => ({ + type: CREATE, + topic +}); + +export const modify = (id, action) => ({ + type: MODIFY, + id, + action, +}); diff --git a/src/components/randomGifList/randomGifListInit.js b/src/components/randomGifList/randomGifListInit.js new file mode 100644 index 0000000..788a11a --- /dev/null +++ b/src/components/randomGifList/randomGifListInit.js @@ -0,0 +1,20 @@ +import _ from 'lodash'; +import {loop, Effects} from '@jarvisaoieong/redux-loop'; +import randomGifInit from 'components/randomGif/randomGifInit'; +import {modify} from './randomGifListActions'; + +export default (topicList = []) => { + const gifLoopList = _.map(topicList, randomGifInit); + + return loop({ + gifList: _.map(gifLoopList, (gifLoop, index) => ({ + id: index, + data: gifLoop.model, + })), + nextId: topicList.length + 1, + }, + Effects.batch(_.map(gifLoopList, (gifLoop, index) => + Effects.map(gifLoop.effect, modify, index) + )) + ); +}; diff --git a/src/components/randomGifList/randomGifListReducer.js b/src/components/randomGifList/randomGifListReducer.js new file mode 100644 index 0000000..59bd3a2 --- /dev/null +++ b/src/components/randomGifList/randomGifListReducer.js @@ -0,0 +1,53 @@ +import _ from 'lodash'; +import {loop, Effects} from '@jarvisaoieong/redux-loop'; +import randomGifReducer, {initialState as randomGifInitialState} from 'components/randomGif/randomGifReducer'; +import {CREATE, MODIFY, modify} from './randomGifListActions'; +import randomGifInit from 'components/randomGif/randomGifInit'; + +export const initialState = { + gifList: [{ + id: 0, + data: randomGifInitialState, + }], + nextId: 1, +}; + +export default (state = initialState, action) => { + if (action.type === CREATE) { + const {model, effect} = randomGifInit(action.topic); + return loop({ + ...state, + gifList: [ + ...state.gifList, + {id: state.nextId, data: model}, + ], + nextId: state.nextId + 1, + }, + Effects.map(effect, modify, state.nextId) + ); + }; + + if (action.type === MODIFY) { + const gifLoopList = _.map(state.gifList, (gif) => { + if (gif.id !== action.id) { + return loop(gif, Effects.none()); + }; + const {model, effect} = randomGifReducer(gif.data, action.action); + return loop({ + id: gif.id, + data: model, + }, + Effects.map(effect, modify, gif.id) + ); + }); + + return loop({ + ...state, + gifList: _.map(gifLoopList, 'model'), + }, + Effects.batch(_.map(gifLoopList, 'effect')) + ); + }; + + return loop(state, Effects.none()); +} diff --git a/src/components/randomGifPair/RandomGifPair.js b/src/components/randomGifPair/RandomGifPair.js new file mode 100644 index 0000000..5f55f55 --- /dev/null +++ b/src/components/randomGifPair/RandomGifPair.js @@ -0,0 +1,30 @@ +import React, {Component} from 'react'; + +import RandomGif from 'components/randomGif/RandomGif' +import {modifyFirst, modifySecond} from './randomGifPairActions'; + +export default class RandomGifPair extends Component { + + render() { + const {model, dispatch} = this.props; + + return ( +
+
+ dispatch(modifyFirst(action)), + }} /> +
+
+ dispatch(modifySecond(action)), + }} /> +
+
+
+ ); + } + +} diff --git a/src/components/randomGifPair/randomGifPairActions.js b/src/components/randomGifPair/randomGifPairActions.js new file mode 100644 index 0000000..ae3e5cf --- /dev/null +++ b/src/components/randomGifPair/randomGifPairActions.js @@ -0,0 +1,12 @@ +export const MODIFY_FIRST = 'MODIFY_FIRST_RANDOM_GIF'; +export const MODIFY_SECOND = 'MODIFY_SECOND_RANDOM_GIF'; + +export const modifyFirst = (action) => ({ + type: MODIFY_FIRST, + action, +}); + +export const modifySecond = (action) => ({ + type: MODIFY_SECOND, + action, +}); diff --git a/src/components/randomGifPair/randomGifPairInit.js b/src/components/randomGifPair/randomGifPairInit.js new file mode 100644 index 0000000..fc3576f --- /dev/null +++ b/src/components/randomGifPair/randomGifPairInit.js @@ -0,0 +1,28 @@ +import {loop, Effects} from '@jarvisaoieong/redux-loop'; + +import randomGifInit from 'components/randomGif/randomGifInit'; +import {modifyFirst, modifySecond} from './randomGifPairActions'; + +export default (firstTopic, secondTopic) => { + const { + model: firstModel, + effect: firstEffect, + } = randomGifInit(firstTopic); + + + const { + model: secondModel, + effect: secondEffect, + } = randomGifInit(secondTopic); + + return loop({ + first: firstModel, + second: secondModel, + }, + Effects.batch([ + Effects.map(firstEffect, modifyFirst), + Effects.map(firstEffect, modifySecond), + ]) + ); + +} diff --git a/src/components/randomGifPair/randomGifPairReducer.js b/src/components/randomGifPair/randomGifPairReducer.js new file mode 100644 index 0000000..2c9cf8d --- /dev/null +++ b/src/components/randomGifPair/randomGifPairReducer.js @@ -0,0 +1,43 @@ +import {loop, Effects} from '@jarvisaoieong/redux-loop'; + +import { + MODIFY_FIRST, + MODIFY_SECOND, + modifyFirst, + modifySecond, +} from './randomGifPairActions'; + +import randomGifReducer, { + initialState as randomGifInitialState, +} from 'components/randomGif/randomGifReducer'; + +const initialState = { + first: randomGifInitialState, + second: randomGifInitialState, +}; + +export default (state = initialState, action) => { + if (action.type === MODIFY_FIRST) { + const {model, effect} = randomGifReducer(state.first, action.action); + + return loop({ + ...state, + first: model, + }, + Effects.map(effect, modifyFirst), + ); + }; + + if (action.type === MODIFY_SECOND) { + const {model, effect} = randomGifReducer(state.second, action.action); + + return loop({ + ...state, + second: model, + }, + Effects.map(effect, modifySecond), + ); + }; + + return loop(state, Effects.none()); +} diff --git a/src/containers/App.js b/src/containers/App.js new file mode 100644 index 0000000..c184262 --- /dev/null +++ b/src/containers/App.js @@ -0,0 +1,39 @@ +import React, {Component} from 'react'; + +import CounterContainer from './CounterContainer'; +import CounterPairContainer from './CounterPairContainer'; +import CounterListContainer from './CounterListContainer'; +import CounterFancyContainer from './CounterFancyContainer'; +import RandomGifContainer from './RandomGifContainer'; +import RandomGifPairContainer from './RandomGifPairContainer'; +import RandomGifListContainer from './RandomGifListContainer'; + +export default class App extends Component { + + render() { + return ( +
+

1. Counter

+ +
+

2. CounterPair

+ +
+

3. CounterList

+ +
+

4. CounterFancy

+ +
+

5. RandomGif

+ +
+

6. RandomGifPair

+ +
+

7. RandomGifList

+ +
+ ); + } +} diff --git a/src/containers/CounterContainer.js b/src/containers/CounterContainer.js new file mode 100644 index 0000000..7c97b1f --- /dev/null +++ b/src/containers/CounterContainer.js @@ -0,0 +1,8 @@ +import {connect} from 'react-redux'; +import Counter from 'components/counter/Counter'; + +export default connect( + (state) => ({ + model: state.counter, + }) +)(Counter); diff --git a/src/containers/CounterFancyContainer.js b/src/containers/CounterFancyContainer.js new file mode 100644 index 0000000..1b1df36 --- /dev/null +++ b/src/containers/CounterFancyContainer.js @@ -0,0 +1,8 @@ +import {connect} from 'react-redux'; +import CounterFancy from 'components/counterFancy/CounterFancy'; + +export default connect( + (state) => ({ + model: state.counterFancy, + }) +)(CounterFancy); diff --git a/src/containers/CounterListContainer.js b/src/containers/CounterListContainer.js new file mode 100644 index 0000000..fb873bd --- /dev/null +++ b/src/containers/CounterListContainer.js @@ -0,0 +1,8 @@ +import {connect} from 'react-redux'; +import CounterList from 'components/counterList/CounterList'; + +export default connect( + (state) => ({ + model: state.counterList, + }) +)(CounterList); diff --git a/src/containers/CounterPairContainer.js b/src/containers/CounterPairContainer.js new file mode 100644 index 0000000..74a8c74 --- /dev/null +++ b/src/containers/CounterPairContainer.js @@ -0,0 +1,8 @@ +import {connect} from 'react-redux'; +import CounterPair from 'components/counterPair/CounterPair'; + +export default connect( + (state) => ({ + model: state.counterPair, + }) +)(CounterPair); diff --git a/src/containers/RandomGifContainer.js b/src/containers/RandomGifContainer.js new file mode 100644 index 0000000..c16cbd3 --- /dev/null +++ b/src/containers/RandomGifContainer.js @@ -0,0 +1,8 @@ +import {connect} from 'react-redux'; +import RandomGif from 'components/randomGif/RandomGif'; + +export default connect( + (state) => ({ + model: state.randomGif, + }) +)(RandomGif); diff --git a/src/containers/RandomGifListContainer.js b/src/containers/RandomGifListContainer.js new file mode 100644 index 0000000..711525d --- /dev/null +++ b/src/containers/RandomGifListContainer.js @@ -0,0 +1,8 @@ +import {connect} from 'react-redux'; +import RandomGifList from 'components/randomGifList/RandomGifList'; + +export default connect( + (state) => ({ + model: state.randomGifList, + }) +)(RandomGifList); diff --git a/src/containers/RandomGifPairContainer.js b/src/containers/RandomGifPairContainer.js new file mode 100644 index 0000000..6090ee7 --- /dev/null +++ b/src/containers/RandomGifPairContainer.js @@ -0,0 +1,8 @@ +import {connect} from 'react-redux'; +import RandomGifPair from 'components/randomGifPair/RandomGifPair'; + +export default connect( + (state) => ({ + model: state.randomGifPair, + }) +)(RandomGifPair); diff --git a/src/favicon.ico b/src/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c3d64eecc91a58c8cb3ce9ec7de64d2834b46d4f GIT binary patch literal 9272 zcmeHMOKTKC5biZ<)ToIDH9iwjL=X>xqFyxOgP@2;@O4m6BB=O2hzD~}JSr#%3X0&t zUm^a62niY=f5E?C&GuJc?@qdBdxqX53rTjms_R`fJ=-pD}BV zF+1fFq8XMeWE1McRW@I*!g|^AWYt{LSh?A#T0dhMCHB~2%=eJi zL(IE6Qgg?wL3W1vw5ImsvA37Bb|X*eU^}rkRu?yuU3c4jy0t_wfa<&j#NDMCo zGRN1CU>7WR^0kG3K3gc`Zene$V%t~Z0cH_{^iimM&S=raf~!3mQ=yf&7p z1z&v8w2?sFV8Gn8@kHBW_{AEoet1Eg`an%0f4>lYXtCd)NC_bbSlGo8RN!`Ntuko+ z6K=$uVO6jnsq6MkY(u|3QnMv8{+uGNuS*mw+W5vQu|D~FjoL60d1g+<_S`q$3Tn6x z5bdOJoLsTJ3S1mhG}p+pp7hs9##zBv=ja}^p-!)gQj`yW>bfH;me+-E>< zb`qG0?4XsGv5Qy@Z=Y^yj){Rmxq9Wwx}q+*Y7Lj5b>XUGR#mZBAG0{bOT(z)Xbr}M IC!b94KO@n>$p8QV literal 0 HcmV?d00001 diff --git a/src/helpers/superagent.js b/src/helpers/superagent.js new file mode 100644 index 0000000..945c1f1 --- /dev/null +++ b/src/helpers/superagent.js @@ -0,0 +1,6 @@ +import request from 'superagent'; +import Promise from 'bluebird'; + +Promise.promisifyAll(request); + +export default request; diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..d3f6683 --- /dev/null +++ b/src/index.html @@ -0,0 +1,13 @@ + + + + + redux-architecture + + + + +
+ + + diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..015dab7 --- /dev/null +++ b/src/index.js @@ -0,0 +1,17 @@ +import React from 'react'; +import {render} from 'react-dom'; +import {Provider} from 'react-redux'; + +import configureStore from './store/configureStore'; +import initialState from './initialState'; +import App from 'containers/App'; + +const store = configureStore({initialState}); + +render( + + + +, + document.getElementById('app') +); diff --git a/src/initialState.js b/src/initialState.js new file mode 100644 index 0000000..3b7fdd1 --- /dev/null +++ b/src/initialState.js @@ -0,0 +1,32 @@ +import {loop, Effects} from '@jarvisaoieong/redux-loop'; + +import randomGifInit from 'components/randomGif/randomGifInit'; +import randomGifPairInit from 'components/randomGifPair/randomGifPairInit'; +import randomGifListInit from 'components/randomGifList/randomGifListInit'; + +const { + model: randomGifModel, + effect: randomGifEffect, +} = randomGifInit('funny cats'); + +const { + model: randomGifPairModel, + effect: randomGifPairEffect, +} = randomGifPairInit('funny cats', 'funny dogs'); + +const { + model: randomGifListModel, + effect: randomGifListEffect, +} = randomGifListInit(['hello', 'world']); + +export default loop({ + randomGif: randomGifModel, + randomGifPair: randomGifPairModel, + randomGifList: randomGifListModel, +}, + Effects.batch([ + randomGifEffect, + randomGifPairEffect, + randomGifListEffect, + ]) +); diff --git a/src/reducers/index.js b/src/reducers/index.js new file mode 100644 index 0000000..5d49674 --- /dev/null +++ b/src/reducers/index.js @@ -0,0 +1,7 @@ +export {default as counter} from 'components/counter/counterReducer'; +export {default as counterPair} from 'components/counterPair/counterPairReducer'; +export {default as counterList} from 'components/counterList/counterListReducer'; +export {default as counterFancy} from 'components/counterFancy/counterFancyReducer'; +export {default as randomGif} from 'components/randomGif/randomGifReducer'; +export {default as randomGifPair} from 'components/randomGifPair/randomGifPairReducer'; +export {default as randomGifList} from 'components/randomGifList/randomGifListReducer'; diff --git a/src/store/configureStore.js b/src/store/configureStore.js new file mode 100644 index 0000000..c4015ad --- /dev/null +++ b/src/store/configureStore.js @@ -0,0 +1,24 @@ +import {createStore, applyMiddleware, compose} from 'redux'; +import {combineReducers, install} from '@jarvisaoieong/redux-loop'; +import createLogger from '@jarvisaoieong/redux-logger'; + +import * as reducers from '../reducers'; + +export default function configureStore({initialState}) { + const reducer = combineReducers(reducers); + + const store = createStore(reducer, initialState, compose( + install(), + applyMiddleware(createLogger({collapsed: true})) + )); + + if (process.env.NODE_ENV === 'development' && module.hot) { + module.hot.accept('../reducers', () => { + const nextReducers = require('../reducers'); + const nextReducer = combineReducers(nextReducers); + store.replaceReducer(nextReducer); + }); + } + + return store; +} diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..0235400 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,38 @@ +var HtmlWebpackPlugin = require('html-webpack-plugin'); + +module.exports = { + entry: [__dirname + '/src/index'], + output: { + path: __dirname + '/_dist', + filename: '[hash].js', + }, + resolve: { + root: [ + __dirname + '/src', + __dirname + '/node_modules', + ], + extensions: ['', '.js'], + }, + module: { + loaders: [{ + test: /\.js$/, + loaders: ['babel'], + include: /src/, + exclude: /node_modules/, + }, { + test: /\.css$/, + loaders: ['style', 'css'], + include: /components/, + }, { + test: /\.(jpe?g|png|gif|svg|ico)/i, + loader: 'file', + }], + }, + plugins: [ + new HtmlWebpackPlugin({ + template: __dirname + '/src/index.html', + favicon: __dirname + '/src/favicon.ico', + inject: false, + }), + ], +}; diff --git a/webpack.development.js b/webpack.development.js new file mode 100644 index 0000000..7f02db3 --- /dev/null +++ b/webpack.development.js @@ -0,0 +1,29 @@ +var webpack = require('webpack'); +var merge = require('@ersinfotech/merge'); + +var webpackConfig = require('./webpack.config'); + +process.env.NODE_ENV = 'development'; + +module.exports = merge(webpackConfig, { + devtool: 'eval', + debug: true, + entry: ['webpack-hot-middleware/client'], + module: { + loaders: [{ + test: /\.css$/, + loaders: ['style', 'css'], + exclude: /components/, + }], + }, + plugins: [ + new webpack.optimize.OccurenceOrderPlugin(), + new webpack.HotModuleReplacementPlugin(), + new webpack.NoErrorsPlugin(), + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: '"development"', + }, + }), + ], +}); diff --git a/webpack.production.js b/webpack.production.js new file mode 100644 index 0000000..7f34be8 --- /dev/null +++ b/webpack.production.js @@ -0,0 +1,28 @@ +var webpack = require('webpack'); +var merge = require('@ersinfotech/merge'); +var ExtractTextPlugin = require('extract-text-webpack-plugin'); + +var webpackConfig = require('./webpack.config'); + +process.env.NODE_ENV = 'production'; + +module.exports = merge(webpackConfig, { + module: { + loaders: [{ + test: /\.css$/, + loader: ExtractTextPlugin.extract('style-loader', 'css-loader'), + exclude: /components/, + }], + }, + plugins: [ + new ExtractTextPlugin('[contenthash].css', { + allChunks: true, + }), + new webpack.optimize.UglifyJsPlugin(), + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: '"production"', + }, + }), + ], +});