@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
118 lines (111 loc) • 5.17 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var tslib = require('tslib');
var React = require('react');
var appBridgeCore = require('@shopify/app-bridge-core');
var Actions = require('@shopify/app-bridge-core/actions');
var compose = require('@shopify/react-compose');
var Frame = require('../Frame.js');
var withFeature = require('../withFeature.js');
var store_reducers_embeddedApp_navigation_index = require('../store/reducers/embeddedApp/navigation/index.js');
require('../store/reducers/embeddedApp/appBridge/reducer.js');
var actionCreators = require('../store/reducers/embeddedApp/appBridge/actionCreators.js');
var useHostContext = require('../hooks/useHostContext.js');
var useRouterContext = require('../hooks/useRouterContext.js');
var components_utilities_appUrl = require('./utilities/appUrl.js');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var React__default = /*#__PURE__*/_interopDefault(React);
var compose__default = /*#__PURE__*/_interopDefault(compose);
var style = {
position: 'relative',
border: 'none',
width: '100%',
flex: '1',
display: 'flex',
};
/**
* Renders a Frame component with the Context set to `Main`
* Handles updating the iframe url for all app-related Navigation actions
* @public
* @requires RouterContext
* @requires HostContext
* */
function MainFrame(props) {
var hostContext = useHostContext.useHostContext();
var routerContext = useRouterContext.useRouterContext();
var iframe = React.useRef();
var app = hostContext.app, config = hostContext.config;
var location = routerContext.location;
var pathname = location.pathname, search = location.search;
var apiKey = config.apiKey, handle = config.handle, url = config.url;
var appURLRef = React.useRef(components_utilities_appUrl.buildAppUrl({ handle: handle, apiKey: apiKey, url: url, pathname: pathname }));
var actions = props.actions, onInit = props.onInit, onLocationUpdate = props.onLocationUpdate, extraProps = tslib.__rest(props, ["actions", "onInit", "onLocationUpdate"]);
var appControlledRedirect = app.isTransportSubscribed(appBridgeCore.Context.Main, Actions.Redirect.Action.APP);
var routeChangeRef = React.useRef();
var addRouteChangeListener = hostContext.addRouteChangeListener;
React.useEffect(function () {
return addRouteChangeListener(function (options) {
routeChangeRef.current = options;
});
}, [addRouteChangeListener]);
var initHandler = React.useCallback(function (frame) {
iframe.current = frame.iframe;
if (onInit) {
onInit(frame);
}
}, [onInit]);
/**
* Reload the iframe for apps that don't handle their own redirects
* We only update the iframe url after the config change
* because we need to refetch the app url from the server
* */
React.useEffect(function () {
if (appControlledRedirect) {
return;
}
var iframeUrl = components_utilities_appUrl.buildAppUrl({ handle: handle, apiKey: apiKey, url: url, pathname: pathname });
app.dispatch(actionCreators.reset());
if (iframeUrl.href === appURLRef.current.href)
return;
updateIframeUrl(iframe.current, iframeUrl.href);
}, [apiKey, handle, url]);
/**
* Notify the app of location changes if app is handling their own redirects.
* This should not be called on the initial load.
* */
var skipInitialLocation = React.useRef(true);
React.useEffect(function () {
if (skipInitialLocation.current) {
skipInitialLocation.current = false;
return;
}
var _a = routeChangeRef.current || {}, newUrl = _a.newUrl, _b = _a.shouldNotifyClient, shouldNotifyClient = _b === void 0 ? true : _b;
var currentUrl = "".concat(pathname).concat(search);
if (newUrl === currentUrl) {
routeChangeRef.current = undefined;
}
if (!shouldNotifyClient)
return;
var iframeUrl = components_utilities_appUrl.buildAppUrl({ handle: handle, apiKey: apiKey, url: url, pathname: pathname });
if (appControlledRedirect) {
actions.handleRedirectApp({ path: "".concat(iframeUrl.pathname).concat(search) });
}
else {
onLocationUpdate === null || onLocationUpdate === void 0 ? void 0 : onLocationUpdate(location);
}
}, [pathname, search]);
return (React__default.default.createElement(Frame, tslib.__assign({}, extraProps, { config: config, style: style, context: appBridgeCore.Context.Main, app: app, title: config.name, url: appURLRef.current.href, onInit: initHandler })));
}
function updateIframeUrl(iframe, newUrl) {
if (!iframe || !iframe.contentWindow) {
return;
}
iframe.contentWindow.location.replace(newUrl);
}
/**
* The MainFrame component with the Navigation feature
* @public
* */
var MainFrame$1 = compose__default.default(withFeature.default(store_reducers_embeddedApp_navigation_index.feature))(MainFrame);
exports.MainFrame = MainFrame;
exports.default = MainFrame$1;