working redux-thunk
This commit is contained in:
parent
2a4dad3bf5
commit
7eb639297f
@ -1,4 +1,5 @@
|
||||
module.exports = {
|
||||
...require('./reducers/app').actions,
|
||||
...require('./reducers/printers').actions,
|
||||
...require('./reducers/changes').actions
|
||||
};
|
||||
|
@ -24,3 +24,26 @@ exports.fetchChanges = () => {
|
||||
return data.changes;
|
||||
});
|
||||
};
|
||||
|
||||
exports.removeChange = (id) => {
|
||||
console.log(`
|
||||
mutation {
|
||||
removeChange(id: "${id}")
|
||||
}
|
||||
`);
|
||||
return fetch(`
|
||||
mutation {
|
||||
removeChange(id: "${id}") {
|
||||
id
|
||||
}
|
||||
}
|
||||
`).then(({
|
||||
errors
|
||||
}) => {
|
||||
if (!errors) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error(errors[0].message);
|
||||
});
|
||||
};
|
||||
|
@ -2,6 +2,7 @@ const React = require('react');
|
||||
|
||||
module.exports = ({
|
||||
printers = [],
|
||||
locked = '',
|
||||
onClick
|
||||
}) => {
|
||||
const _onClick = (id) => {
|
||||
@ -12,11 +13,20 @@ module.exports = ({
|
||||
|
||||
const lis = printers.map(({
|
||||
name,
|
||||
lock,
|
||||
id
|
||||
}) => {
|
||||
const msg = (() => {
|
||||
if (!lock) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return (locked === id) ? '(Locked to you)' : `(Locked to ${lock})`;
|
||||
})();
|
||||
|
||||
return (
|
||||
<li key={id}>
|
||||
<a onClick={_onClick(id)}>{name}</a>
|
||||
<a onClick={_onClick(id)}>{name} {msg}</a>
|
||||
</li>
|
||||
);
|
||||
});
|
||||
|
@ -1,27 +1,55 @@
|
||||
const ReactRedux = require('react-redux');
|
||||
const ReactRouter = require('react-router');
|
||||
const React = require('react');
|
||||
|
||||
const NotFound = require('./not-found');
|
||||
const Home = require('./home');
|
||||
const Print = require('./print');
|
||||
|
||||
const actions = require('../actions');
|
||||
|
||||
const {
|
||||
connect
|
||||
} = ReactRedux;
|
||||
|
||||
const App = React.createClass({
|
||||
const {
|
||||
Miss,
|
||||
Match
|
||||
} = ReactRouter;
|
||||
|
||||
const {
|
||||
updateRouter
|
||||
} = actions
|
||||
|
||||
const App = connect()(React.createClass({
|
||||
componentDidMount: function() {
|
||||
require('../worker').on('action', (action) => {
|
||||
this.props.dispatch(action);
|
||||
});
|
||||
require('../worker').on('action', this.props.dispatch);
|
||||
},
|
||||
render: function() {
|
||||
const {
|
||||
children
|
||||
children,
|
||||
router,
|
||||
dispatch
|
||||
} = this.props;
|
||||
|
||||
// ugly hack needed because of a limitation of react-router api
|
||||
// that doens't pass it's instance to matched routes
|
||||
dispatch(updateRouter(router));
|
||||
|
||||
return (
|
||||
<div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
module.exports = connect()(App);
|
||||
module.exports = (props) => {
|
||||
return (
|
||||
<App {...props}>
|
||||
<Match exactly pattern='/' component={Home} />
|
||||
<Match pattern='/print' component={Print} />
|
||||
<Miss component={NotFound}/>
|
||||
</App>
|
||||
);
|
||||
};
|
||||
|
@ -11,13 +11,17 @@ const Change = require('../components/change');
|
||||
const actions = require('../actions');
|
||||
|
||||
const {
|
||||
fetchChanges
|
||||
fetchChanges,
|
||||
lockPrinter,
|
||||
print,
|
||||
transitionTo
|
||||
} = actions;
|
||||
|
||||
const {
|
||||
BrowserRouter,
|
||||
Miss,
|
||||
Match,
|
||||
Router
|
||||
} = ReactRouter;
|
||||
|
||||
const {
|
||||
@ -30,8 +34,11 @@ const Print = ({
|
||||
changes = [],
|
||||
lockPrinter,
|
||||
fetchChanges,
|
||||
onPrint,
|
||||
loaded,
|
||||
loading
|
||||
loading,
|
||||
locked,
|
||||
router
|
||||
}) => {
|
||||
const allChanges = () => {
|
||||
return (
|
||||
@ -58,6 +65,18 @@ const Print = ({
|
||||
return change.id === params.id;
|
||||
});
|
||||
|
||||
if (!change) {
|
||||
return (
|
||||
<p>Change not found</p>
|
||||
);
|
||||
}
|
||||
|
||||
const _onPrint = (id) => {
|
||||
return () => {
|
||||
return onPrint(id);
|
||||
};
|
||||
};
|
||||
|
||||
// TODO: don't load all changes
|
||||
return (
|
||||
<div>
|
||||
@ -69,6 +88,7 @@ const Print = ({
|
||||
>
|
||||
<Change {...change} />
|
||||
</Loader>
|
||||
<button onClick={_onPrint(params.id)}>Print</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -79,6 +99,8 @@ const Print = ({
|
||||
<p>Printers</p>
|
||||
<Printers
|
||||
printers={printers}
|
||||
onClick={lockPrinter}
|
||||
locked={locked}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -93,17 +115,25 @@ const mapStateToProps = (state) => {
|
||||
loaded: state.ui.changes.loaded,
|
||||
loading: state.ui.changes.loading,
|
||||
changes: state.data.changes,
|
||||
printers: state.data.printers
|
||||
printers: state.data.printers,
|
||||
locked: state.ui.printers.locked
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
lockPrinter: (id) => {},
|
||||
lockPrinter: (id) => {
|
||||
return dispatch(lockPrinter(id));
|
||||
},
|
||||
fetchChanges: () => {
|
||||
dispatch(fetchChanges());
|
||||
return dispatch(fetchChanges());
|
||||
},
|
||||
onPrint: (id) => {
|
||||
return dispatch(print(id)).then(() => {
|
||||
return dispatch(transitionTo('/print'));
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(Print);
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps, )(Print);
|
||||
|
3
spikes/stacks/redux-thunk/src/client/intl.json
Normal file
3
spikes/stacks/redux-thunk/src/client/intl.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
30
spikes/stacks/redux-thunk/src/client/reducers/app.js
Normal file
30
spikes/stacks/redux-thunk/src/client/reducers/app.js
Normal file
@ -0,0 +1,30 @@
|
||||
const ReduxActions = require('redux-actions');
|
||||
const app = require('../../../package.json').name;
|
||||
|
||||
const {
|
||||
createAction,
|
||||
handleActions
|
||||
} = ReduxActions;
|
||||
|
||||
const UPDATE_ROUTER = `${app}/changes/UPDATE_ROUTER`;
|
||||
|
||||
exports.ui = handleActions({
|
||||
[UPDATE_ROUTER]: (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
router: action.payload
|
||||
};
|
||||
}
|
||||
}, {});
|
||||
|
||||
const actions = exports.actions = {
|
||||
updateRouter: (router) => {
|
||||
return {
|
||||
type: UPDATE_ROUTER,
|
||||
payload: router
|
||||
};
|
||||
},
|
||||
transitionTo: (pathname) => (dispatch, getState) => {
|
||||
return getState().ui.app.router.transitionTo(pathname);
|
||||
}
|
||||
};
|
@ -8,10 +8,12 @@ const {
|
||||
} = ReduxActions;
|
||||
|
||||
const {
|
||||
fetchChanges
|
||||
fetchChanges,
|
||||
removeChange
|
||||
} = api;
|
||||
|
||||
const FETCH_CHANGES = `${app}/changes/FETCH_CHANGES`;
|
||||
const REMOVE_CHANGE = `${app}/changes/REMOVE_CHANGE`;
|
||||
|
||||
exports.data = handleActions({
|
||||
[`${FETCH_CHANGES}_FULFILLED`]: (state, action) => {
|
||||
@ -46,11 +48,19 @@ exports.ui = handleActions({
|
||||
loaded: false
|
||||
});
|
||||
|
||||
exports.actions = {
|
||||
const actions = exports.actions = {
|
||||
fetchChanges: () => {
|
||||
return {
|
||||
type: FETCH_CHANGES,
|
||||
payload: fetchChanges()
|
||||
};
|
||||
},
|
||||
removeChange: (id) => (dispatch) => {
|
||||
return dispatch({
|
||||
type: REMOVE_CHANGE,
|
||||
payload: removeChange(id)
|
||||
}).then(() => {
|
||||
return dispatch(actions.fetchChanges());
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
// const reduceReducers = require('reduce-reducers');
|
||||
const Redux = require('redux');
|
||||
|
||||
const app = require('./app');
|
||||
const printers = require('./printers');
|
||||
const changes = require('./changes');
|
||||
|
||||
@ -15,7 +16,9 @@ module.exports = () => {
|
||||
changes: changes.data
|
||||
}),
|
||||
ui: combineReducers({
|
||||
changes: changes.ui
|
||||
changes: changes.ui,
|
||||
printers: printers.ui,
|
||||
app: app.ui
|
||||
})
|
||||
});
|
||||
};
|
||||
|
@ -1,14 +1,23 @@
|
||||
const ReduxActions = require('redux-actions');
|
||||
const app = require('../../../package.json').name;
|
||||
const find = require('lodash.find');
|
||||
const changes = require('./changes');
|
||||
|
||||
const {
|
||||
createAction,
|
||||
handleActions
|
||||
} = ReduxActions;
|
||||
|
||||
const {
|
||||
actions: {
|
||||
removeChange
|
||||
}
|
||||
} = changes;
|
||||
|
||||
const UPDATE_PRINTERS = `${app}/printers/UPDATE_PRINTERS`;
|
||||
const UPDATE_WORKER_ID = `${app}/printers/UPDATE_WORKER_ID`;
|
||||
const LOCK_PRINTER = `${app}/printers/LOCK_PRINTER`;
|
||||
const PRINT = `${app}/printers/PRINT`;
|
||||
|
||||
exports.data = handleActions({
|
||||
[UPDATE_PRINTERS]: (state, action) => {
|
||||
@ -27,8 +36,7 @@ exports.ui = handleActions({
|
||||
const locked = (find(action.payload, (printer) => {
|
||||
return (
|
||||
printer.lock &&
|
||||
state.locked &&
|
||||
printer.lock === state.locked
|
||||
printer.lock === state.id
|
||||
);
|
||||
}) || {}).id || '';
|
||||
|
||||
@ -42,7 +50,157 @@ exports.ui = handleActions({
|
||||
locked: ''
|
||||
});
|
||||
|
||||
exports.actions = {
|
||||
updatePrinters: createAction(UPDATE_PRINTERS),
|
||||
updateWorkerId: createAction(UPDATE_WORKER_ID)
|
||||
// confirm should be an async op,
|
||||
// let's mock it that way
|
||||
const confirm = (msg) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(window.confirm(msg));
|
||||
});
|
||||
};
|
||||
|
||||
// prompt should be an async op,
|
||||
// let's mock it that way
|
||||
const prompt = (msg) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(window.prompt(msg));
|
||||
});
|
||||
};
|
||||
|
||||
// alert should be an async op,
|
||||
// let's mock it that way
|
||||
const alert = (msg) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(window.alert(msg));
|
||||
});
|
||||
};
|
||||
|
||||
const actions = exports.actions = {
|
||||
updatePrinters: createAction(UPDATE_PRINTERS),
|
||||
updateWorkerId: createAction(UPDATE_WORKER_ID),
|
||||
lockPrinter: (id) => (dispatch, getState) => {
|
||||
const {
|
||||
ui,
|
||||
data
|
||||
} = getState();
|
||||
|
||||
const {
|
||||
printers
|
||||
} = data;
|
||||
|
||||
const {
|
||||
printers: {
|
||||
locked
|
||||
}
|
||||
} = ui;
|
||||
|
||||
if (locked === id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const printer = find(printers, ['id', id]);
|
||||
|
||||
if (!printer) {
|
||||
return window.alert(`Printer ${id} not found`);
|
||||
}
|
||||
|
||||
const worker = require('../worker');
|
||||
|
||||
const lock = () => {
|
||||
return dispatch({
|
||||
type: LOCK_PRINTER,
|
||||
payload: worker.dispatch({
|
||||
type: 'LOCK_PRINTER',
|
||||
payload: id
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
const askToLock = () => {
|
||||
const msg = `Do you want to lock printer ${id}?`;
|
||||
return confirm(msg).then((yes) => {
|
||||
return yes ? lock(id) : null;
|
||||
});
|
||||
};
|
||||
|
||||
const askToOverride = () => {
|
||||
const msg = `Printer ${id} already locked! Do you want to override?`;
|
||||
return confirm(msg).then((yes) => {
|
||||
return yes ? lock(id) : null;
|
||||
});
|
||||
};
|
||||
|
||||
return printer.lock ? askToOverride() : askToLock();
|
||||
},
|
||||
print: (changeId) => (dispatch, getState) => {
|
||||
const {
|
||||
ui,
|
||||
data
|
||||
} = getState();
|
||||
|
||||
const {
|
||||
printers
|
||||
} = data;
|
||||
|
||||
const {
|
||||
printers: {
|
||||
locked
|
||||
}
|
||||
} = ui;
|
||||
|
||||
const worker = require('../worker');
|
||||
|
||||
const print = () => {
|
||||
return dispatch({
|
||||
type: PRINT,
|
||||
payload: worker.dispatch({
|
||||
type: 'PRINT',
|
||||
payload: changeId
|
||||
})
|
||||
}).then(() => {
|
||||
return dispatch(removeChange(changeId));
|
||||
});
|
||||
};
|
||||
|
||||
const lock = (printerId) => {
|
||||
return dispatch({
|
||||
type: LOCK_PRINTER,
|
||||
payload: worker.dispatch({
|
||||
type: 'LOCK_PRINTER',
|
||||
payload: printerId
|
||||
})
|
||||
}).then(print);
|
||||
};
|
||||
|
||||
const askToOverride = (printerId) => {
|
||||
const msg = `Printer ${printerId} already locked! Do you want to override?`;
|
||||
return confirm(msg).then((yes) => {
|
||||
return yes ? lock(printerId) : null;
|
||||
});
|
||||
};
|
||||
|
||||
const askToLock = () => {
|
||||
const msg = `Please select a printer to lock: ${
|
||||
printers.map(({
|
||||
id,
|
||||
name
|
||||
}) => {
|
||||
return `\n(${id}) ${name}`;
|
||||
})
|
||||
}`;
|
||||
|
||||
return prompt(msg).then((printerId) => {
|
||||
const printer = find(printers, ['id', printerId]);
|
||||
|
||||
if (!printer) {
|
||||
return alert(`Printer ${printerId} not found. Try again`).then(() => {
|
||||
return actions.print(printerId)(dispatch, getState);
|
||||
});
|
||||
}
|
||||
|
||||
return printer.lock ? askToOverride(printerId) : lock(printerId);
|
||||
});
|
||||
};
|
||||
|
||||
return !locked ? askToLock() : print();
|
||||
}
|
||||
};
|
||||
|
@ -2,11 +2,9 @@ const React = require('react');
|
||||
const ReactHotLoader = require('react-hot-loader');
|
||||
const ReactRouter = require('react-router');
|
||||
const ReactRedux = require('react-redux');
|
||||
const ReactIntl = require('react-intl');
|
||||
|
||||
const App = require('./containers/app');
|
||||
const NotFound = require('./containers/not-found');
|
||||
const Home = require('./containers/home');
|
||||
const Print = require('./containers/print');
|
||||
|
||||
const store = require('./store');
|
||||
|
||||
@ -16,25 +14,33 @@ const {
|
||||
|
||||
const {
|
||||
BrowserRouter,
|
||||
Miss,
|
||||
Match
|
||||
} = ReactRouter;
|
||||
|
||||
const {
|
||||
Provider
|
||||
} = ReactRedux;
|
||||
|
||||
const {
|
||||
IntlProvider
|
||||
} = ReactIntl;
|
||||
|
||||
// http://stackoverflow.com/a/38150585
|
||||
const locale = (
|
||||
navigator.languages && navigator.languages[0] || // Chrome / Firefox
|
||||
navigator.language || // All browsers
|
||||
navigator.userLanguage // IE <= 10
|
||||
);
|
||||
|
||||
module.exports = () => {
|
||||
return (
|
||||
<AppContainer>
|
||||
<Provider store={store()}>
|
||||
<BrowserRouter>
|
||||
<App>
|
||||
<Match exactly pattern='/' component={Home} />
|
||||
<Match pattern='/print' component={Print} />
|
||||
<Miss component={NotFound}/>
|
||||
</App>
|
||||
</BrowserRouter>
|
||||
<IntlProvider
|
||||
locale={locale}
|
||||
defaultLocale='en'
|
||||
>
|
||||
<BrowserRouter>{App}</BrowserRouter>
|
||||
</IntlProvider>
|
||||
</Provider>
|
||||
</AppContainer>
|
||||
);
|
||||
|
@ -18,7 +18,7 @@ module.exports = (state = Object.freeze({})) => {
|
||||
state,
|
||||
compose(
|
||||
applyMiddleware(
|
||||
createLogger(),
|
||||
// createLogger(),
|
||||
promiseMiddleware(),
|
||||
thunk
|
||||
)
|
||||
|
@ -9,12 +9,44 @@ let isMaster = crosstab.util.tabs['MASTER_TAB'].id === crosstab.id;
|
||||
const bridge = new Map();
|
||||
|
||||
const {
|
||||
updatePrinters
|
||||
updatePrinters,
|
||||
updateWorkerId
|
||||
} = actions;
|
||||
|
||||
const printers = {
|
||||
'1': {
|
||||
id: '1',
|
||||
name: 'Main printer',
|
||||
lock: ''
|
||||
},
|
||||
'2': {
|
||||
id: '2',
|
||||
name: 'Handled printer',
|
||||
lock: ''
|
||||
}
|
||||
};
|
||||
|
||||
const handlers = {
|
||||
'PRINT': (action, fn) => {},
|
||||
'PRINT': (action, fn) => {
|
||||
fn();
|
||||
},
|
||||
'LOCK_PRINTER': (action, fn) => {
|
||||
const alreadyLocked = values(printers).filter((printer) => {
|
||||
return printer.lock === action._origin;
|
||||
});
|
||||
|
||||
alreadyLocked.forEach((printer) => {
|
||||
printers[printer.id] = {
|
||||
...printers[printer.id],
|
||||
lock: ''
|
||||
};
|
||||
});
|
||||
|
||||
printers[action.payload] = {
|
||||
...printers[action.payload],
|
||||
lock: action._origin
|
||||
};
|
||||
|
||||
fn();
|
||||
}
|
||||
};
|
||||
@ -46,7 +78,10 @@ crosstab.util.events.on('message', ({
|
||||
return emitter.emit('action', data);
|
||||
}
|
||||
|
||||
handlers[data.type](data, (err, res) => {
|
||||
handlers[data.type]({
|
||||
...data,
|
||||
_origin: origin
|
||||
}, (err, res) => {
|
||||
crosstab.broadcast('message', {
|
||||
...data,
|
||||
error: err && err.message
|
||||
@ -66,7 +101,10 @@ const dispatch = module.exports.dispatch = (action, tab) => {
|
||||
if (isMaster && !tab) {
|
||||
if (handlers[action.type]) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
handlers[action.type](action, function(err, res) {
|
||||
handlers[action.type]({
|
||||
...action,
|
||||
_origin: crosstab.id
|
||||
}, function(err, res) {
|
||||
return err ? reject(err) : resolve(res);
|
||||
});
|
||||
});
|
||||
@ -90,20 +128,9 @@ const dispatch = module.exports.dispatch = (action, tab) => {
|
||||
return then;
|
||||
};
|
||||
|
||||
const printers = {
|
||||
'1': {
|
||||
id: '1',
|
||||
name: 'Main printer',
|
||||
lock: ''
|
||||
},
|
||||
'2': {
|
||||
id: '2',
|
||||
name: 'Handled printer',
|
||||
lock: ''
|
||||
}
|
||||
};
|
||||
|
||||
emitter.emit('action', updateWorkerId(crosstab.id));
|
||||
setTimeout(function() {
|
||||
emitter.emit('action', updateWorkerId(crosstab.id));
|
||||
}, 450);
|
||||
|
||||
setInterval(() => {
|
||||
if (!isMaster) {
|
||||
|
@ -114,7 +114,7 @@ const mutation = new GraphQLObjectType({
|
||||
}
|
||||
},
|
||||
resolve(root, args, ctx) {
|
||||
const changes = (() => {
|
||||
const ops = (() => {
|
||||
if (args.id) {
|
||||
return [args.id];
|
||||
}
|
||||
@ -128,7 +128,7 @@ const mutation = new GraphQLObjectType({
|
||||
});
|
||||
})();
|
||||
|
||||
changes.forEach((id) => {
|
||||
ops.forEach((id) => {
|
||||
delete changes[id];
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user