@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
108 lines (105 loc) • 4.54 kB
JavaScript
import { __rest, __assign } from 'tslib';
import React, { useRef, useEffect, useCallback } from 'react';
import { Context } from '@shopify/app-bridge-core';
import { Redirect } from '@shopify/app-bridge-core/actions';
import compose from '@shopify/react-compose';
import Frame from '../Frame.js';
import withFeature from '../withFeature.js';
import { feature } from '../store/reducers/embeddedApp/navigation/index.js';
import '../store/reducers/embeddedApp/appBridge/reducer.js';
import { reset } from '../store/reducers/embeddedApp/appBridge/actionCreators.js';
import { useHostContext } from '../hooks/useHostContext.js';
import { useRouterContext } from '../hooks/useRouterContext.js';
import { buildAppUrl } from './utilities/appUrl.js';
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();
var routerContext = useRouterContext();
var iframe = 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 = useRef(buildAppUrl({ handle: handle, apiKey: apiKey, url: url, pathname: pathname }));
var actions = props.actions, onInit = props.onInit, onLocationUpdate = props.onLocationUpdate, extraProps = __rest(props, ["actions", "onInit", "onLocationUpdate"]);
var appControlledRedirect = app.isTransportSubscribed(Context.Main, Redirect.Action.APP);
var routeChangeRef = useRef();
var addRouteChangeListener = hostContext.addRouteChangeListener;
useEffect(function () {
return addRouteChangeListener(function (options) {
routeChangeRef.current = options;
});
}, [addRouteChangeListener]);
var initHandler = 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
* */
useEffect(function () {
if (appControlledRedirect) {
return;
}
var iframeUrl = buildAppUrl({ handle: handle, apiKey: apiKey, url: url, pathname: pathname });
app.dispatch(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 = useRef(true);
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 = 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.createElement(Frame, __assign({}, extraProps, { config: config, style: style, context: 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(withFeature(feature))(MainFrame);
export { MainFrame, MainFrame$1 as default };