UNPKG

react-chrome-redux

Version:

A set of utilities for building Redux applications in Google Chrome Extensions.

210 lines (175 loc) 6.7 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _constants = require('../constants'); var _serialization = require('../serialization'); var _diff = require('../strategies/shallowDiff/diff'); var _diff2 = _interopRequireDefault(_diff); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * Responder for promisified results * @param {object} dispatchResult The result from `store.dispatch()` * @param {function} send The function used to respond to original message * @return {undefined} */ var promiseResponder = function promiseResponder(dispatchResult, send) { Promise.resolve(dispatchResult).then(function (res) { send({ error: null, value: res }); }).catch(function (err) { console.error('error dispatching result:', err); send({ error: err.message, value: null }); }); }; /** * Wraps a Redux store so that proxy stores can connect to it. * @param {Object} store A Redux store * @param {Object} options An object of form {portName, dispatchResponder, serializer, deserializer}, where `portName` is a required string and defines the name of the port for state transition changes, `dispatchResponder` is a function that takes the result of a store dispatch and optionally implements custom logic for responding to the original dispatch message,`serializer` is a function to serialize outgoing message payloads (default is passthrough), `deserializer` is a function to deserialize incoming message payloads (default is passthrough), and diffStrategy is one of the included diffing strategies (default is shallow diff) or a custom diffing function. */ exports.default = function (store, _ref) { var portName = _ref.portName, dispatchResponder = _ref.dispatchResponder, _ref$serializer = _ref.serializer, serializer = _ref$serializer === undefined ? _serialization.noop : _ref$serializer, _ref$deserializer = _ref.deserializer, deserializer = _ref$deserializer === undefined ? _serialization.noop : _ref$deserializer, _ref$diffStrategy = _ref.diffStrategy, diffStrategy = _ref$diffStrategy === undefined ? _diff2.default : _ref$diffStrategy; if (!portName) { throw new Error('portName is required in options'); } if (typeof serializer !== 'function') { throw new Error('serializer must be a function'); } if (typeof deserializer !== 'function') { throw new Error('deserializer must be a function'); } if (typeof diffStrategy !== 'function') { throw new Error('diffStrategy must be one of the included diffing strategies or a custom diff function'); } // set dispatch responder as promise responder if (!dispatchResponder) { dispatchResponder = promiseResponder; } /** * Respond to dispatches from UI components */ var dispatchResponse = function dispatchResponse(request, sender, sendResponse) { if (request.type === _constants.DISPATCH_TYPE && request.portName === portName) { var action = Object.assign({}, request.payload, { _sender: sender }); var dispatchResult = null; try { dispatchResult = store.dispatch(action); } catch (e) { dispatchResult = Promise.reject(e.message); console.error(e); } dispatchResponder(dispatchResult, sendResponse); return true; } }; /** * Setup for state updates */ var connectState = function connectState(port) { if (port.name !== portName) { return; } var serializedMessagePoster = (0, _serialization.withSerializer)(serializer)(function () { return port.postMessage.apply(port, arguments); }); var prevState = store.getState(); var patchState = function patchState() { var state = store.getState(); var diff = diffStrategy(prevState, state); if (diff.length) { prevState = state; serializedMessagePoster({ type: _constants.PATCH_STATE_TYPE, payload: diff }); } }; // Send patched state down connected port on every redux store state change var unsubscribe = store.subscribe(patchState); // when the port disconnects, unsubscribe the sendState listener port.onDisconnect.addListener(unsubscribe); // Send store's initial state through port serializedMessagePoster({ type: _constants.STATE_TYPE, payload: prevState }); }; var withPayloadDeserializer = (0, _serialization.withDeserializer)(deserializer); var shouldDeserialize = function shouldDeserialize(request) { return request.type === _constants.DISPATCH_TYPE && request.portName === portName; }; /** * Setup action handler */ withPayloadDeserializer(function () { var _chrome$runtime$onMes; return (_chrome$runtime$onMes = chrome.runtime.onMessage).addListener.apply(_chrome$runtime$onMes, arguments); })(dispatchResponse, shouldDeserialize); /** * Setup external action handler */ if (chrome.runtime.onMessageExternal) { withPayloadDeserializer(function () { var _chrome$runtime$onMes2; return (_chrome$runtime$onMes2 = chrome.runtime.onMessageExternal).addListener.apply(_chrome$runtime$onMes2, arguments); })(dispatchResponse, shouldDeserialize); } else { console.warn('runtime.onMessageExternal is not supported'); } /** * Setup extended connection */ chrome.runtime.onConnect.addListener(connectState); /** * Setup extended external connection */ if (chrome.runtime.onConnectExternal) { chrome.runtime.onConnectExternal.addListener(connectState); } else { console.warn('runtime.onConnectExternal is not supported'); } /** * Safety message to tabs for content scripts */ chrome.tabs.query({}, function (tabs) { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = tabs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var tab = _step.value; chrome.tabs.sendMessage(tab.id, { action: 'storeReady' }); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } }); // For non-tab based // TODO: Find use case for this. Ommiting until then. // chrome.runtime.sendMessage(null, {action: 'storeReady'}); };