@shopify/app-bridge-host
Version:
App Bridge Host contains components and middleware to be consumed by the app's host, as well as the host itself. The middleware and `Frame` component are responsible for facilitating communication between the client and host, and used to act on actions se
147 lines (144 loc) • 6.23 kB
JavaScript
import { __assign } from 'tslib';
import { combineReducers, compose, createStore as createStore$1, applyMiddleware } from 'redux';
import { isLoadReducerAction, hostLoadReducer, hostLoadCompleteReducer } from '../actions.js';
import '@shopify/app-bridge-core/actions';
import featuresReducer from './reducers/embeddedApp/features/reducer.js';
export { setFeaturesAvailable } from './reducers/embeddedApp/features/reducer.js';
import { wrapReducers, resetAppReducer } from './reducers/embeddedApp/utilities.js';
export { getMobileMiddleware } from './middlewares/mobile/middleware.js';
export { CART_PERMISSION_MESSAGE, applyPrintToLegacyButton, buildCallbackQueueItem, buildQueueItem, fixDiscountInCartResponse, isCartAction, isLegacyShopifyMobile, isLegacyShopifyPOS, isMobile, isMobileMiddlewareAvailable, isMobileMiddlewareSupported, isShopifyMobile, isShopifyPOS, isShopifyPing, traverseButtonPayload } from './middlewares/mobile/helpers.js';
export { mapAppDispatchToProps, mapAppStoreToProps } from './reducers/index.js';
export { StoreReadyAction } from './reducers/embeddedApp/appBridge/actions.js';
/**
* The constant key `appBridge`
* @public
*/
var APP_BRIDGE_KEY = 'appBridge';
/**
* Returns a combined reducer for the `appBridge` key
* Always includes the `features` reducer
* @public
* @param stateReducers - a reducer map for the dynamic app state
* @param initialState - an optional default value for the store
*/
function createReducers(stateReducers, initialState) {
var _a;
if (stateReducers === void 0) { stateReducers = {}; }
if (initialState === void 0) { initialState = {}; }
var allReducers = combineReducers((_a = {},
_a[APP_BRIDGE_KEY] = combineReducers(wrapReducers(__assign({ features: featuresReducer }, stateReducers), resetAppReducer, initialState)),
_a));
return function (state, action) {
var _a, _b, _c;
if (state === void 0) { state = (_a = {}, _a[APP_BRIDGE_KEY] = { features: {} }, _a); }
if (isLoadReducerAction(action)) {
var feature = action.payload.feature;
var currenState = state[APP_BRIDGE_KEY];
return _b = {},
_b[APP_BRIDGE_KEY] = __assign(__assign({}, currenState), (_c = {}, _c[feature] = initialState[feature], _c)),
_b;
}
return allReducers(state, action);
};
}
/**
* Creates a store containing only the default `features` reducer
* @public
*/
function createStore(middleware, initialState, debug) {
var _a;
if (middleware === void 0) { middleware = []; }
if (initialState === void 0) { initialState = { features: {} }; }
if (debug === void 0) { debug = false; }
var defaultState = (_a = {}, _a[APP_BRIDGE_KEY] = initialState, _a);
var composeEnhancers = debug && typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ name: 'App Bridge', shouldHotReload: false })
: compose;
return createStore$1(createReducers({}, initialState), defaultState, composeEnhancers(applyMiddleware.apply(void 0, middleware)));
}
/**
* Creates a method that when called, dynamically adds a reducer to
* the provided store
* @internal
* @param store - a Redux store
* @param globalInitialState - custom overrides for resolving the app state when adding a new reducer
* */
function createAddReducer(store, globalInitialState) {
if (globalInitialState === void 0) { globalInitialState = {}; }
var asyncReducers = {};
return function addReducer(_a) {
var _b;
var key = _a.key, reducer = _a.reducer, initialState = _a.initialState;
// not adding 'features' reducer to store fixes https://github.com/Shopify/app-bridge/issues/2571
if (Object.prototype.hasOwnProperty.call(asyncReducers, key) || key === 'features') {
return;
}
asyncReducers[key] = reducer;
store.replaceReducer(createReducers(asyncReducers, __assign((_b = {}, _b[key] = initialState, _b), globalInitialState)));
store.dispatch(hostLoadReducer(key));
store.dispatch(hostLoadCompleteReducer(key));
};
}
/**
* Creates an action queue for the provided store
* @internal
* */
function createActionsQueue(store) {
var queue = new Map();
var hostQueue = new Set();
return {
add: function (context, action) {
if (!queue.has(context)) {
queue.set(context, new Set());
}
var contextQueue = queue.get(context);
contextQueue.add(action);
},
addHostAction: function (action) {
hostQueue.add(action);
},
clear: function (context) {
var contextQueue = queue.get(context);
if (contextQueue) {
contextQueue.clear();
}
},
resolve: function (feature) {
hostQueue.forEach(function (action) {
var actionKey = groupToFeatureKey(action.group);
if (actionKey === feature) {
store.dispatch(action);
hostQueue.delete(action);
}
});
queue.forEach(function (contextQueue) {
contextQueue.forEach(function (action) {
var actionKey = groupToFeatureKey(action.group);
if (actionKey === feature) {
store.dispatch(action);
contextQueue.delete(action);
}
});
});
},
};
}
/**
* Returns the reducer key from a group
* @internal
*/
function groupToFeatureKey(group) {
return group[0].toLowerCase().concat(group.substr(1).replace('_', ''));
}
/**
* Predicate to determine if a reducer for the associated action is already loaded
* @internal
*/
function isReducerLoaded(state, action) {
var group = action ? action.group : undefined;
if (!group)
return false;
var key = groupToFeatureKey(action.group);
return Object.prototype.hasOwnProperty.call(state, key);
}
export { APP_BRIDGE_KEY, createActionsQueue, createAddReducer, createReducers, createStore, isReducerLoaded };