Improve AppPage (#3216)
* WIP - reduce rerenders and change how dashboard sends events to app * Cleanup * Refactor * Add loader behind the iframe * Fix linter
This commit is contained in:
parent
9097d4076e
commit
b9acfe6214
15 changed files with 219 additions and 109 deletions
|
@ -1,16 +1,14 @@
|
||||||
|
import { useAppDashboardUpdates } from "@dashboard/apps/components/AppFrame/useAppDashboardUpdates";
|
||||||
import {
|
import {
|
||||||
AppDetailsUrlQueryParams,
|
AppDetailsUrlQueryParams,
|
||||||
getAppDeepPathFromDashboardUrl,
|
|
||||||
prepareFeatureFlagsList,
|
prepareFeatureFlagsList,
|
||||||
resolveAppIframeUrl,
|
resolveAppIframeUrl,
|
||||||
} from "@dashboard/apps/urls";
|
} from "@dashboard/apps/urls";
|
||||||
import { useAllFlags } from "@dashboard/hooks/useFlags";
|
import { useAllFlags } from "@dashboard/hooks/useFlags";
|
||||||
import useLocale from "@dashboard/hooks/useLocale";
|
import { CircularProgress } from "@material-ui/core";
|
||||||
import useShop from "@dashboard/hooks/useShop";
|
|
||||||
import { useTheme } from "@saleor/macaw-ui";
|
import { useTheme } from "@saleor/macaw-ui";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import React, { useEffect } from "react";
|
import React from "react";
|
||||||
import { useLocation } from "react-router";
|
|
||||||
|
|
||||||
import { useStyles } from "./styles";
|
import { useStyles } from "./styles";
|
||||||
import { useAppActions } from "./useAppActions";
|
import { useAppActions } from "./useAppActions";
|
||||||
|
@ -39,46 +37,36 @@ export const AppFrame: React.FC<Props> = ({
|
||||||
onError,
|
onError,
|
||||||
refetch,
|
refetch,
|
||||||
}) => {
|
}) => {
|
||||||
const shop = useShop();
|
const frameRef = React.useRef<HTMLIFrameElement | null>(null);
|
||||||
const frameRef = React.useRef<HTMLIFrameElement>(null);
|
|
||||||
const { themeType } = useTheme();
|
const { themeType } = useTheme();
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const appOrigin = getOrigin(src);
|
const appOrigin = getOrigin(src);
|
||||||
const flags = useAllFlags();
|
const flags = useAllFlags();
|
||||||
const { postToExtension } = useAppActions(frameRef, appOrigin, appId);
|
|
||||||
const location = useLocation();
|
|
||||||
const { locale } = useLocale();
|
|
||||||
|
|
||||||
useEffect(() => {
|
/**
|
||||||
postToExtension({
|
* React on messages from App
|
||||||
type: "localeChanged",
|
*/
|
||||||
payload: {
|
const { postToExtension, handshakeDone, setHandshakeDone } = useAppActions(
|
||||||
locale,
|
frameRef.current,
|
||||||
},
|
appOrigin,
|
||||||
});
|
appId,
|
||||||
}, [locale, postToExtension]);
|
appToken,
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
/**
|
||||||
postToExtension({
|
* Listen to Dashboard context like theme or locale and inform app about it
|
||||||
type: "theme",
|
*/
|
||||||
payload: {
|
useAppDashboardUpdates(frameRef.current, appOrigin, handshakeDone, appId);
|
||||||
theme: themeType,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}, [themeType, postToExtension]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
postToExtension({
|
|
||||||
type: "redirect",
|
|
||||||
payload: {
|
|
||||||
path: getAppDeepPathFromDashboardUrl(location.pathname, appId),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}, [location.pathname]);
|
|
||||||
|
|
||||||
useTokenRefresh(appToken, refetch);
|
useTokenRefresh(appToken, refetch);
|
||||||
|
|
||||||
const handleLoad = () => {
|
const handleLoad = () => {
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*
|
||||||
|
* Move handshake to notifyReady, so app is requesting token after it's ready to receive it
|
||||||
|
* Currently handshake it 2 times, for compatibility
|
||||||
|
*/
|
||||||
postToExtension({
|
postToExtension({
|
||||||
type: "handshake",
|
type: "handshake",
|
||||||
payload: {
|
payload: {
|
||||||
|
@ -86,23 +74,19 @@ export const AppFrame: React.FC<Props> = ({
|
||||||
version: 1,
|
version: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
postToExtension({
|
|
||||||
type: "theme",
|
setHandshakeDone(true);
|
||||||
payload: {
|
|
||||||
theme: themeType,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (onLoad) {
|
if (onLoad) {
|
||||||
onLoad();
|
onLoad();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!shop?.domain.host) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<div className={classes.loader}>
|
||||||
|
<CircularProgress color="primary" />
|
||||||
|
</div>
|
||||||
<iframe
|
<iframe
|
||||||
ref={frameRef}
|
ref={frameRef}
|
||||||
src={resolveAppIframeUrl(appId, src, {
|
src={resolveAppIframeUrl(appId, src, {
|
||||||
|
@ -112,8 +96,11 @@ export const AppFrame: React.FC<Props> = ({
|
||||||
})}
|
})}
|
||||||
onError={onError}
|
onError={onError}
|
||||||
onLoad={handleLoad}
|
onLoad={handleLoad}
|
||||||
className={clsx(classes.iframe, className)}
|
className={clsx(classes.iframe, className, {
|
||||||
|
[classes.iframeHidden]: !handshakeDone,
|
||||||
|
})}
|
||||||
sandbox="allow-same-origin allow-forms allow-scripts"
|
sandbox="allow-same-origin allow-forms allow-scripts"
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { usePostToExtension } from "@dashboard/apps/components/AppFrame/usePostToExtension";
|
||||||
import { useExternalApp } from "@dashboard/apps/components/ExternalAppContext/ExternalAppContext";
|
import { useExternalApp } from "@dashboard/apps/components/ExternalAppContext/ExternalAppContext";
|
||||||
import { getAppMountUri } from "@dashboard/config";
|
import { getAppMountUri } from "@dashboard/config";
|
||||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||||
|
@ -169,19 +170,34 @@ const useHandleUpdateRoutingAction = (appId: string) => ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const useNotifyReadyAction = () => ({
|
/**
|
||||||
|
* TODO Remove prop drilling, consume context
|
||||||
|
*/
|
||||||
|
const useNotifyReadyAction = (
|
||||||
|
frameEl: HTMLIFrameElement | null,
|
||||||
|
appOrigin: string,
|
||||||
|
appToken: string,
|
||||||
|
) => {
|
||||||
|
const postToExtension = usePostToExtension(frameEl, appOrigin);
|
||||||
|
|
||||||
|
return {
|
||||||
handle(action: NotifyReady) {
|
handle(action: NotifyReady) {
|
||||||
console.debug(
|
postToExtension({
|
||||||
`Handling NotifyReady action with ID: ${action.payload.actionId}`,
|
type: "handshake",
|
||||||
);
|
payload: {
|
||||||
console.warn("Not implemented");
|
token: appToken,
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
return createResponseStatus(action.payload.actionId, true);
|
return createResponseStatus(action.payload.actionId, true);
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const AppActionsHandler = {
|
export const AppActionsHandler = {
|
||||||
useHandleNotificationAction,
|
useHandleNotificationAction,
|
||||||
useHandleUpdateRoutingAction,
|
useHandleUpdateRoutingAction,
|
||||||
useHandleRedirectAction,
|
useHandleRedirectAction,
|
||||||
useNotifyReadyAction,
|
useNotifyReadyAction,
|
||||||
|
createResponseStatus,
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,16 @@ export const useStyles = makeStyles(
|
||||||
height: "100%",
|
height: "100%",
|
||||||
border: "none",
|
border: "none",
|
||||||
},
|
},
|
||||||
|
iframeHidden: {
|
||||||
|
visibility: "hidden",
|
||||||
|
},
|
||||||
|
loader: {
|
||||||
|
position: "absolute",
|
||||||
|
top: "50%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
zIndex: -1,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
{ name: "AppFrame" },
|
{ name: "AppFrame" },
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,22 +1,32 @@
|
||||||
import { AppActionsHandler } from "@dashboard/apps/components/AppFrame/appActionsHandler";
|
import { AppActionsHandler } from "@dashboard/apps/components/AppFrame/appActionsHandler";
|
||||||
import {
|
import { usePostToExtension } from "@dashboard/apps/components/AppFrame/usePostToExtension";
|
||||||
Actions,
|
import { Actions, DispatchResponseEvent } from "@saleor/app-sdk/app-bridge";
|
||||||
DispatchResponseEvent,
|
import React, { useState } from "react";
|
||||||
Events,
|
|
||||||
} from "@saleor/app-sdk/app-bridge";
|
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
export const useAppActions = (
|
export const useAppActions = (
|
||||||
frameEl: React.MutableRefObject<HTMLIFrameElement | null>,
|
frameEl: HTMLIFrameElement | null,
|
||||||
appOrigin: string,
|
appOrigin: string,
|
||||||
appId: string,
|
appId: string,
|
||||||
|
appToken: string,
|
||||||
) => {
|
) => {
|
||||||
|
const postToExtension = usePostToExtension(frameEl, appOrigin);
|
||||||
|
|
||||||
const { handle: handleNotification } =
|
const { handle: handleNotification } =
|
||||||
AppActionsHandler.useHandleNotificationAction();
|
AppActionsHandler.useHandleNotificationAction();
|
||||||
const { handle: handleUpdateRouting } =
|
const { handle: handleUpdateRouting } =
|
||||||
AppActionsHandler.useHandleUpdateRoutingAction(appId);
|
AppActionsHandler.useHandleUpdateRoutingAction(appId);
|
||||||
const { handle: handleRedirect } =
|
const { handle: handleRedirect } =
|
||||||
AppActionsHandler.useHandleRedirectAction(appId);
|
AppActionsHandler.useHandleRedirectAction(appId);
|
||||||
|
const { handle: handleNotifyReady } = AppActionsHandler.useNotifyReadyAction(
|
||||||
|
frameEl,
|
||||||
|
appOrigin,
|
||||||
|
appToken,
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store if app has performed a handshake with Dashboard, to avoid sending events before that
|
||||||
|
*/
|
||||||
|
const [handshakeDone, setHandshakeDone] = useState(false);
|
||||||
|
|
||||||
const handleAction = (action: Actions | undefined): DispatchResponseEvent => {
|
const handleAction = (action: Actions | undefined): DispatchResponseEvent => {
|
||||||
switch (action?.type) {
|
switch (action?.type) {
|
||||||
|
@ -29,8 +39,15 @@ export const useAppActions = (
|
||||||
case "updateRouting": {
|
case "updateRouting": {
|
||||||
return handleUpdateRouting(action);
|
return handleUpdateRouting(action);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Send handshake after app informs its ready and mounted
|
||||||
|
*/
|
||||||
case "notifyReady": {
|
case "notifyReady": {
|
||||||
console.warn("Not implemented");
|
const response = handleNotifyReady(action);
|
||||||
|
|
||||||
|
setHandshakeDone(true);
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
throw new Error("Unknown action type");
|
throw new Error("Unknown action type");
|
||||||
|
@ -38,12 +55,6 @@ export const useAppActions = (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const postToExtension = (event: Events) => {
|
|
||||||
if (frameEl?.current?.contentWindow) {
|
|
||||||
frameEl.current.contentWindow.postMessage(event, appOrigin);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const handler = (event: MessageEvent<Actions>) => {
|
const handler = (event: MessageEvent<Actions>) => {
|
||||||
if (event.origin === appOrigin) {
|
if (event.origin === appOrigin) {
|
||||||
|
@ -58,9 +69,11 @@ export const useAppActions = (
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("message", handler);
|
window.removeEventListener("message", handler);
|
||||||
};
|
};
|
||||||
}, []);
|
}, [appOrigin, handleAction, postToExtension]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
handshakeDone,
|
||||||
postToExtension,
|
postToExtension,
|
||||||
|
setHandshakeDone,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
58
src/apps/components/AppFrame/useAppDashboardUpdates.ts
Normal file
58
src/apps/components/AppFrame/useAppDashboardUpdates.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import { usePostToExtension } from "@dashboard/apps/components/AppFrame/usePostToExtension";
|
||||||
|
import { getAppDeepPathFromDashboardUrl } from "@dashboard/apps/urls";
|
||||||
|
import useLocale from "@dashboard/hooks/useLocale";
|
||||||
|
import { useTheme } from "@saleor/macaw-ui";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Refactor prop-drilling, use context or some atomic state
|
||||||
|
*/
|
||||||
|
export const useAppDashboardUpdates = (
|
||||||
|
frameEl: HTMLIFrameElement | null,
|
||||||
|
appOrigin: string,
|
||||||
|
enabled: boolean,
|
||||||
|
appId: string,
|
||||||
|
) => {
|
||||||
|
const postToExtension = usePostToExtension(frameEl, appOrigin);
|
||||||
|
|
||||||
|
const { locale } = useLocale();
|
||||||
|
const { themeType } = useTheme();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
postToExtension({
|
||||||
|
type: "localeChanged",
|
||||||
|
payload: {
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, [enabled, locale, postToExtension]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
postToExtension({
|
||||||
|
type: "theme",
|
||||||
|
payload: {
|
||||||
|
theme: themeType,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, [themeType, postToExtension, enabled]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
postToExtension({
|
||||||
|
type: "redirect",
|
||||||
|
payload: {
|
||||||
|
path: getAppDeepPathFromDashboardUrl(location.pathname, appId),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, [appId, enabled, postToExtension]);
|
||||||
|
};
|
22
src/apps/components/AppFrame/usePostToExtension.ts
Normal file
22
src/apps/components/AppFrame/usePostToExtension.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { Events } from "@saleor/app-sdk/app-bridge";
|
||||||
|
import { useCallback } from "react";
|
||||||
|
|
||||||
|
export const usePostToExtension = (
|
||||||
|
iframeElement: HTMLIFrameElement | null,
|
||||||
|
appOrigin: string,
|
||||||
|
) => {
|
||||||
|
const postToExtension = useCallback(
|
||||||
|
(event: Events) => {
|
||||||
|
if (iframeElement?.contentWindow) {
|
||||||
|
try {
|
||||||
|
iframeElement.contentWindow.postMessage(event, appOrigin);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[appOrigin, iframeElement],
|
||||||
|
);
|
||||||
|
|
||||||
|
return postToExtension;
|
||||||
|
};
|
|
@ -3,7 +3,7 @@ import { storiesOf } from "@storybook/react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { appDetails } from "../../fixtures";
|
import { appDetails } from "../../fixtures";
|
||||||
import AppPage, { AppPageProps } from "./AppPage";
|
import { AppPage, AppPageProps } from "./AppPage";
|
||||||
|
|
||||||
const props: AppPageProps = {
|
const props: AppPageProps = {
|
||||||
data: appDetails,
|
data: appDetails,
|
||||||
|
|
|
@ -21,12 +21,12 @@ export const AppPage: React.FC<AppPageProps> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.iframeContainer}>
|
<div className={classes.iframeContainer}>
|
||||||
{url && (
|
{url && data.id && data.accessToken && (
|
||||||
<AppFrame
|
<AppFrame
|
||||||
src={url}
|
src={url}
|
||||||
appToken={data?.accessToken ?? ""}
|
appToken={data?.accessToken}
|
||||||
onError={onError}
|
onError={onError}
|
||||||
appId={data?.id ?? ""}
|
appId={data?.id}
|
||||||
refetch={refetch}
|
refetch={refetch}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -35,4 +35,3 @@ export const AppPage: React.FC<AppPageProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
AppPage.displayName = "AppPage";
|
AppPage.displayName = "AppPage";
|
||||||
export default AppPage;
|
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
export * from "./AppPage";
|
export * from "./AppPage";
|
||||||
export { default } from "./AppPage";
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ export const useStyles = makeStyles(
|
||||||
height: "100%",
|
height: "100%",
|
||||||
},
|
},
|
||||||
iframeContainer: {
|
iframeContainer: {
|
||||||
|
position: "relative",
|
||||||
lineHeight: 0, // It removes extra space between iframe and container
|
lineHeight: 0, // It removes extra space between iframe and container
|
||||||
height: "100%",
|
height: "100%",
|
||||||
"& > iframe": {
|
"& > iframe": {
|
||||||
|
|
|
@ -15,7 +15,7 @@ import {
|
||||||
appPath,
|
appPath,
|
||||||
appsListPath,
|
appsListPath,
|
||||||
} from "./urls";
|
} from "./urls";
|
||||||
import AppView from "./views/App";
|
import { AppView } from "./views/App";
|
||||||
import AppDetailsView from "./views/AppDetails";
|
import AppDetailsView from "./views/AppDetails";
|
||||||
import AppInstallView from "./views/AppInstall";
|
import AppInstallView from "./views/AppInstall";
|
||||||
import AppsListView from "./views/AppsList";
|
import AppsListView from "./views/AppsList";
|
||||||
|
|
|
@ -3,18 +3,18 @@ import NotFoundPage from "@dashboard/components/NotFoundPage";
|
||||||
import { useAppQuery } from "@dashboard/graphql";
|
import { useAppQuery } from "@dashboard/graphql";
|
||||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||||
import useNotifier from "@dashboard/hooks/useNotifier";
|
import useNotifier from "@dashboard/hooks/useNotifier";
|
||||||
import React from "react";
|
import React, { useCallback } from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
import { useLocation } from "react-router";
|
import { useLocation } from "react-router";
|
||||||
|
|
||||||
import AppPage from "../../components/AppPage";
|
import { AppPage } from "../../components/AppPage";
|
||||||
import { appsListPath, getAppCompleteUrlFromDashboardUrl } from "../../urls";
|
import { appsListPath, getAppCompleteUrlFromDashboardUrl } from "../../urls";
|
||||||
|
|
||||||
interface AppProps {
|
interface AppProps {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const App: React.FC<AppProps> = ({ id }) => {
|
export const AppView: React.FC<AppProps> = ({ id }) => {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { data, refetch } = useAppQuery({
|
const { data, refetch } = useAppQuery({
|
||||||
displayLoader: true,
|
displayLoader: true,
|
||||||
|
@ -27,6 +27,15 @@ export const App: React.FC<AppProps> = ({ id }) => {
|
||||||
const notify = useNotifier();
|
const notify = useNotifier();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const handleError = useCallback(
|
||||||
|
() =>
|
||||||
|
notify({
|
||||||
|
status: "error",
|
||||||
|
text: intl.formatMessage(appMessages.failedToFetchAppSettings),
|
||||||
|
}),
|
||||||
|
[intl, notify],
|
||||||
|
);
|
||||||
|
|
||||||
if (!appExists) {
|
if (!appExists) {
|
||||||
return <NotFoundPage onBack={() => navigate(appsListPath)} />;
|
return <NotFoundPage onBack={() => navigate(appsListPath)} />;
|
||||||
}
|
}
|
||||||
|
@ -37,19 +46,16 @@ export const App: React.FC<AppProps> = ({ id }) => {
|
||||||
id,
|
id,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!data || !appCompleteUrl) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppPage
|
<AppPage
|
||||||
data={data?.app || null}
|
data={data.app}
|
||||||
url={appCompleteUrl || ""}
|
url={appCompleteUrl}
|
||||||
refetch={refetch}
|
refetch={refetch}
|
||||||
onError={() =>
|
onError={handleError}
|
||||||
notify({
|
|
||||||
status: "error",
|
|
||||||
text: intl.formatMessage(appMessages.failedToFetchAppSettings),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default App;
|
|
|
@ -1,2 +1 @@
|
||||||
export * from "./App";
|
export * from "./AppView";
|
||||||
export { default } from "./App";
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import useNotifier from "@dashboard/hooks/useNotifier";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
import AppPage from "../../components/AppPage";
|
import { AppPage } from "../../components/AppPage";
|
||||||
import { appsListPath } from "../../urls";
|
import { appsListPath } from "../../urls";
|
||||||
|
|
||||||
interface AppSettingsProps {
|
interface AppSettingsProps {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import {
|
||||||
AppDetailsUrlQueryParams,
|
AppDetailsUrlQueryParams,
|
||||||
AppInstallUrlQueryParams,
|
AppInstallUrlQueryParams,
|
||||||
} from "@dashboard/apps/urls";
|
} from "@dashboard/apps/urls";
|
||||||
import AppView from "@dashboard/apps/views/App";
|
import { AppView } from "@dashboard/apps/views/App";
|
||||||
import AppDetailsView from "@dashboard/apps/views/AppDetails";
|
import AppDetailsView from "@dashboard/apps/views/AppDetails";
|
||||||
import AppInstallView from "@dashboard/apps/views/AppInstall";
|
import AppInstallView from "@dashboard/apps/views/AppInstall";
|
||||||
import { sectionNames } from "@dashboard/intl";
|
import { sectionNames } from "@dashboard/intl";
|
||||||
|
@ -26,9 +26,9 @@ const AppDetails: React.FC<RouteComponentProps<{ id: string }>> = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const App: React.FC<RouteComponentProps<{ id: string }>> = ({ match }) => (
|
const AppViewRoute: React.FC<RouteComponentProps<{ id: string }>> = ({
|
||||||
<AppView id={decodeURIComponent(match.params.id)} />
|
match,
|
||||||
);
|
}) => <AppView id={decodeURIComponent(match.params.id)} />;
|
||||||
|
|
||||||
const AppInstall: React.FC<RouteComponentProps> = props => {
|
const AppInstall: React.FC<RouteComponentProps> = props => {
|
||||||
const qs = parseQs(location.search.substr(1));
|
const qs = parseQs(location.search.substr(1));
|
||||||
|
@ -58,7 +58,7 @@ const Apps = () => {
|
||||||
path={AppPaths.resolveAppDetailsPath(":id")}
|
path={AppPaths.resolveAppDetailsPath(":id")}
|
||||||
component={AppDetails}
|
component={AppDetails}
|
||||||
/>
|
/>
|
||||||
<Route path={AppPaths.resolveAppPath(":id")} component={App} />
|
<Route path={AppPaths.resolveAppPath(":id")} component={AppViewRoute} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue