-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix app configuration frame embedding (#1172)
* Fix app configuration frame embedding * Tidy up code
- Loading branch information
1 parent
4056ac3
commit c8d7edf
Showing
2 changed files
with
83 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
src/apps/components/AppDetailsSettingsPage/useAppConfigLoader.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |