Skip to content

Commit

Permalink
Fix app configuration frame embedding (#1172)
Browse files Browse the repository at this point in the history
* Fix app configuration frame embedding

* Tidy up code
  • Loading branch information
dominik-zeglen authored Jun 21, 2021
1 parent 4056ac3 commit c8d7edf
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import Hr from "@saleor/components/Hr";
import useTheme from "@saleor/hooks/useTheme";
import { sectionNames } from "@saleor/intl";
import classNames from "classnames";
import React, { useEffect, useRef } from "react";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import urlJoin from "url-join";

import { App_app } from "../../types/App";
import { useStyles } from "./styles";
import useAppConfigLoader from "./useAppConfigLoader";
import useSettingsBreadcrumbs from "./useSettingsBreadcrumbs";

export interface AppDetailsSettingsPageProps {
Expand All @@ -30,45 +30,14 @@ export const AppDetailsSettingsPage: React.FC<AppDetailsSettingsPageProps> = ({
onBack,
onError
}) => {
const iframeRef = useRef(null);
const intl = useIntl();
const classes = useStyles({});
const { sendThemeToExtension } = useTheme();
const [breadcrumbs, onBreadcrumbClick] = useSettingsBreadcrumbs();

useEffect(() => {
if (!iframeRef.current?.innerHTML && data?.configurationUrl) {
fetch(data?.configurationUrl, {
headers: {
"x-saleor-domain": backendHost,
"x-saleor-token": data.accessToken
},
method: "GET"
})
.then(async response => {
const url = new URL(response.url);
const text = await response.text();
const content = new DOMParser().parseFromString(text, "text/html");

const iFrame = document.createElement("iframe");
iFrame.src = "about:blank";
iFrame.id = "extension-app";
iframeRef.current.innerHTML = "";
iframeRef.current.appendChild(iFrame);
const iFrameDoc =
iFrame.contentWindow && iFrame.contentWindow.document;

const documentElement = content.documentElement;
const formScript = documentElement.querySelector("script");
const formURL = new URL(documentElement.querySelector("script").src);
formScript.src = `${urlJoin(url.origin, formURL.pathname)}`;
iFrameDoc.write(content.documentElement.innerHTML);
iFrameDoc.close();
iFrame.contentWindow.onload = sendThemeToExtension;
})
.catch(() => onError());
}
}, [data]);
const { sendThemeToExtension } = useTheme();
const frameContainer = useAppConfigLoader(data, backendHost, {
onError,
onLoad: sendThemeToExtension
});

return (
<Container>
Expand Down Expand Up @@ -135,7 +104,7 @@ export const AppDetailsSettingsPage: React.FC<AppDetailsSettingsPageProps> = ({
<Hr />

<CardSpacer />
<div ref={iframeRef} className={classes.iframeContainer} />
<div ref={frameContainer} className={classes.iframeContainer} />
<CardSpacer />
</Container>
);
Expand Down
75 changes: 75 additions & 0 deletions src/apps/components/AppDetailsSettingsPage/useAppConfigLoader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { AppFragment } from "@saleor/fragments/types/AppFragment";
import { useEffect, useRef } from "react";
import urlJoin from "url-join";

export type UseAppConfigLoaderCallbacks = Record<
"onLoad" | "onError",
() => void
>;

function fixRelativeScriptSrc(origin: string) {
return (node: HTMLScriptElement) => {
// Using node.getAttribute beacuse node.src returns absolute path
const src = node.getAttribute("src");
if (src?.startsWith("/")) {
node.src = urlJoin(origin, src);
}
};
}

async function fetchAndSetContent(
frameContainer: HTMLDivElement,
data: AppFragment,
backendHostname: string,
{ onError, onLoad }: UseAppConfigLoaderCallbacks
) {
if (!frameContainer?.innerHTML && data?.configurationUrl) {
try {
const response = await fetch(data?.configurationUrl, {
headers: {
"x-saleor-domain": backendHostname,
"x-saleor-token": data.accessToken
},
method: "GET"
});

const url = new URL(response.url);
const text = await response.text();
const content = new DOMParser().parseFromString(text, "text/html");

const frame = document.createElement("iframe");
frame.src = "about:blank";
frame.id = "extension-app";
frameContainer.innerHTML = "";
frameContainer.appendChild(frame);
const frameContent = frame.contentWindow.document;

const documentElement = content.documentElement;
const scriptNodes = documentElement.querySelectorAll("script");

scriptNodes.forEach(fixRelativeScriptSrc(url.origin));
frameContent.write(content.documentElement.innerHTML);
frameContent.close();
frame.contentWindow.onload = onLoad;
} catch (error) {
console.error(error);
onError();
}
}
}

function useAppConfigLoader(
data: AppFragment,
backendHost: string,
callbacks: UseAppConfigLoaderCallbacks
) {
const frameContainer = useRef<HTMLDivElement>(null);

useEffect(() => {
fetchAndSetContent(frameContainer.current, data, backendHost, callbacks);
}, [data]);

return frameContainer;
}

export default useAppConfigLoader;

0 comments on commit c8d7edf

Please sign in to comment.