diff --git a/example/webapp/src/app/style.css b/example/webapp/src/app/style.css index 2575383..7bbc26e 100644 --- a/example/webapp/src/app/style.css +++ b/example/webapp/src/app/style.css @@ -12,7 +12,7 @@ margin-bottom: 40px; line-height: calc(100vh - 200px); font-size: 80vh; - color: blue; + color: red; text-align: center; } diff --git a/package.json b/package.json index 865e5c7..1a288e5 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,6 @@ "dependencies": { "bleno": "^0.4.2", "redux": "^3.7.1", - "redux-thunk": "^2.2.0", "text-encoding": "^0.6.4" }, "bundlesize": [ diff --git a/src/webapp/actions/actions.js b/src/webapp/actions/actions.js index 9491ec5..665fe83 100644 --- a/src/webapp/actions/actions.js +++ b/src/webapp/actions/actions.js @@ -4,18 +4,27 @@ export default function Actions(central, TYPES) { payload: state, }); - const syncStore = () => dispatch => central.read().then(state => dispatch(syncState(state))); + const syncStore = () => ({ + type: TYPES.BLUETOOTH_SYNC_REQUEST, + request: dispatch => central.read().then(state => dispatch(syncState(state))), + }); - const connectStore = name => (dispatch) => { - dispatch({ type: TYPES.BLUETOOTH_CONNECTING }); - return central - .connect(name) - .then(() => central.handler(state => dispatch(syncState(state)))) - .then(() => dispatch({ type: TYPES.BLUETOOTH_CONNECTED })) - .then(() => dispatch(syncStore())); - }; + const connectStore = name => ({ + type: TYPES.BLUETOOTH_CONNECT_REQUEST, + request: (dispatch) => { + dispatch({ type: TYPES.BLUETOOTH_CONNECTING }); + return central + .connect(name) + .then(() => central.handler(state => dispatch(syncState(state)))) + .then(() => dispatch({ type: TYPES.BLUETOOTH_CONNECTED })) + .then(() => dispatch(syncStore())); + }, + }); - const sendAction = action => () => central.write(action); + const sendAction = action => ({ + type: TYPES.BLUETOOTH_SEND_REQUEST, + request: () => central.write(action), + }); return { connectStore, diff --git a/src/webapp/actions/actions.test.js b/src/webapp/actions/actions.test.js index 5e260d6..b59af06 100644 --- a/src/webapp/actions/actions.test.js +++ b/src/webapp/actions/actions.test.js @@ -32,9 +32,12 @@ test('syncState', () => { test('connectStore', () => { const { connectStore } = Actions(central, TYPES); - expect.assertions(8); + const action = connectStore('mockName'); + expect.assertions(10); - const promise = connectStore('mockName')(dispatch).then(() => { + expect(action.type).toEqual(TYPES.BLUETOOTH_CONNECT_REQUEST); + + const promise = action.request(dispatch).then(() => { expect(central.connect).toBeCalled(); expect(central.handler).toBeCalled(); @@ -42,7 +45,9 @@ test('connectStore', () => { expect(dispatch.mock.calls[0][0]).toEqual({ type: TYPES.BLUETOOTH_CONNECTING }); expect(dispatch.mock.calls[1][0]).toEqual({ type: TYPES.BLUETOOTH_CONNECTED }); - return dispatch.mock.calls[2][0](dispatch); // syncStore + const syncStore = dispatch.mock.calls[2][0]; + expect(syncStore.type).toEqual(TYPES.BLUETOOTH_SYNC_REQUEST); + return syncStore.request(dispatch); }).then(() => { expect(central.read).toBeCalled(); expect(dispatch.mock.calls[3][0]).toEqual({ type: TYPES.BLUETOOTH_SYNC, payload: 'mockState' }); @@ -55,9 +60,12 @@ test('connectStore', () => { test('syncStore', () => { const { syncStore } = Actions(central, TYPES); - expect.assertions(3); + const action = syncStore(); + expect.assertions(4); - const promise = syncStore()(dispatch).then(() => { + expect(action.type).toEqual(TYPES.BLUETOOTH_SYNC_REQUEST); + + const promise = action.request(dispatch).then(() => { expect(central.read).toBeCalled(); expect(dispatch).toBeCalledWith({ type: TYPES.BLUETOOTH_SYNC, payload: 'mockState' }); @@ -69,9 +77,12 @@ test('syncStore', () => { test('sendAction', () => { const { sendAction } = Actions(central, TYPES); - expect.assertions(2); + const action = sendAction('mockAction'); + expect.assertions(3); - const promise = sendAction('mockAction')(dispatch).then(() => { + expect(action.type).toEqual(TYPES.BLUETOOTH_SEND_REQUEST); + + const promise = action.request(dispatch).then(() => { expect(central.write).toBeCalledWith('mockAction'); return true; diff --git a/src/webapp/actions/types.js b/src/webapp/actions/types.js index 1bc688a..f49941c 100644 --- a/src/webapp/actions/types.js +++ b/src/webapp/actions/types.js @@ -4,3 +4,7 @@ export const BLUETOOTH_ERROR = '@@bluetooth/ERROR'; export const BLUETOOTH_READ = '@@bluetooth/READ'; export const BLUETOOTH_SYNC = '@@bluetooth/SYNC'; export const BLUETOOTH_SEND = '@@bluetooth/SEND'; + +export const BLUETOOTH_CONNECT_REQUEST = '@@bluetooth/CONNECT_REQUEST'; +export const BLUETOOTH_SEND_REQUEST = '@@bluetooth/SEND_REQUEST'; +export const BLUETOOTH_SYNC_REQUEST = '@@bluetooth/SYNC_REQUEST'; diff --git a/src/webapp/middleware/index.js b/src/webapp/middleware/index.js index 2e061c8..30beb87 100644 --- a/src/webapp/middleware/index.js +++ b/src/webapp/middleware/index.js @@ -1,9 +1,26 @@ import ACTIONS from '../actions'; +import * as TYPES from '../actions/types'; const { sendAction } = ACTIONS; +const { + BLUETOOTH_CONNECT_REQUEST, + BLUETOOTH_SEND_REQUEST, + BLUETOOTH_SYNC_REQUEST, +} = TYPES; + +const REQUESTS = [ + BLUETOOTH_CONNECT_REQUEST, + BLUETOOTH_SEND_REQUEST, + BLUETOOTH_SYNC_REQUEST, +]; + export default (actions = []) => store => next => (action) => { + if (typeof action !== 'object') { + return next(action); + } const { type } = action; + if (REQUESTS.includes(type)) action.request(store.dispatch); if (actions.includes(type)) store.dispatch(sendAction(action)); return next(action); }; diff --git a/src/webapp/middleware/index.test.js b/src/webapp/middleware/index.test.js index 9293224..ae36d3e 100644 --- a/src/webapp/middleware/index.test.js +++ b/src/webapp/middleware/index.test.js @@ -35,3 +35,17 @@ test('actions included', () => { expect(store.dispatch).toBeCalledWith('mockAction'); expect(next).toBeCalledWith({ type: 'ACTION' }); }); + +test('actions request', () => { + const request = jest.fn(); + const action = { type: '@@bluetooth/CONNECT_REQUEST', request }; + middleware(['ACTION'])(store)(next)(action); + expect(request).toBeCalledWith(store.dispatch); + expect(next).toBeCalledWith(action); +}); + +test('actions not an object', () => { + const funcAction = jest.fn(); + middleware(['ACTION'])(store)(next)(funcAction); + expect(next).toBeCalledWith(funcAction); +}); diff --git a/src/webapp/store/index.js b/src/webapp/store/index.js index 1ad1af8..fe07081 100644 --- a/src/webapp/store/index.js +++ b/src/webapp/store/index.js @@ -1,12 +1,11 @@ /* global window */ import { createStore, applyMiddleware, compose } from 'redux'; -import thunk from 'redux-thunk'; import middleware from '../middleware'; import reducers from '../reducers'; export default (actions) => { - const middlewares = [middleware(actions), thunk]; + const middlewares = [middleware(actions)]; const enhancers = [applyMiddleware(...middlewares)]; /* eslint-disable no-underscore-dangle */