diff --git a/src/components/NostoProvider.tsx b/src/components/NostoProvider.tsx index 19ebc27..ce9789d 100644 --- a/src/components/NostoProvider.tsx +++ b/src/components/NostoProvider.tsx @@ -1,7 +1,7 @@ -import React, { useEffect, isValidElement } from "react" +import { isValidElement } from "react" import { NostoContext, RecommendationComponent } from "../context" -import { NostoClient } from "../types" -import { isNostoLoaded } from "./helpers" +import { useLoadClientScript } from "../hooks" +import type { ReactElement } from "react" /** * @group Components @@ -22,7 +22,7 @@ export interface NostoProviderProps { /** * children */ - children: React.ReactElement | React.ReactElement[] + children: ReactElement | ReactElement[] /** * Indicates if merchant uses multiple currencies */ @@ -65,13 +65,9 @@ export default function NostoProvider(props: NostoProviderProps) { const { account, multiCurrency = false, - host, children, recommendationComponent, - shopifyMarkets } = props - const [clientScriptLoadedState, setClientScriptLoadedState] = React.useState(false) - const clientScriptLoaded = React.useMemo(() => clientScriptLoadedState, [clientScriptLoadedState]) // Pass currentVariation as empty string if multiCurrency is disabled const currentVariation = multiCurrency ? props.currentVariation : "" @@ -79,74 +75,7 @@ export default function NostoProvider(props: NostoProviderProps) { // Set responseMode for loading campaigns: const responseMode = isValidElement(recommendationComponent) ? "JSON_ORIGINAL" : "HTML" - useEffect(() => { - if (!window.nostojs) { - window.nostojs = (cb: (api: NostoClient) => void) => { - (window.nostojs.q = window.nostojs.q || []).push(cb) - } - window.nostojs(api => api.setAutoLoad(false)) - } - - if (!isNostoLoaded() && !shopifyMarkets) { - const script = document.createElement("script") - script.type = "text/javascript" - script.src = "//" + (host || "connect.nosto.com") + "/include/" + account - script.async = true - script.setAttribute("nosto-client-script", "") - - script.onload = () => { - if ("nostoReactTest" in window) { - window.nosto?.reload({ - site: "localhost", - }) - } - setClientScriptLoadedState(true) - } - document.body.appendChild(script) - } - - // Enable Shopify markets functionality: - if (shopifyMarkets) { - const existingScript = document.querySelector("[nosto-client-script]") - const nostoSandbox = document.querySelector("#nosto-sandbox") - - if ( - !existingScript || - existingScript?.getAttribute("nosto-language") !== shopifyMarkets?.language || - existingScript?.getAttribute("nosto-market-id") !== shopifyMarkets?.marketId - ) { - if (clientScriptLoadedState) { - setClientScriptLoadedState(false) - } - - existingScript?.parentNode?.removeChild(existingScript) - nostoSandbox?.parentNode?.removeChild(nostoSandbox) - - const script = document.createElement("script") - script.type = "text/javascript" - script.src = - "//" + - (host || "connect.nosto.com") + - `/script/shopify/market/nosto.js?merchant=${account}&market=${ - shopifyMarkets.marketId || "" - }&locale=${shopifyMarkets?.language?.toLowerCase() || ""}` - script.async = true - script.setAttribute("nosto-client-script", "") - script.setAttribute("nosto-language", shopifyMarkets?.language || "") - script.setAttribute("nosto-market-id", String(shopifyMarkets?.marketId)) - - script.onload = () => { - if ("nostoReactTest" in window) { - window.nosto?.reload({ - site: "localhost", - }) - } - setClientScriptLoadedState(true) - } - document.body.appendChild(script) - } - } - }, [clientScriptLoadedState, shopifyMarkets]) + const { clientScriptLoaded } = useLoadClientScript(props) return ( + +export function useLoadClientScript(props: NostoScriptProps) { + const { host, account, shopifyMarkets } = props + const [clientScriptLoadedState, setClientScriptLoadedState] = useState(false) + const clientScriptLoaded = useMemo(() => clientScriptLoadedState, [clientScriptLoadedState]) + + useEffect(() => { + const scriptOnload = () => { + if ("nostoReactTest" in window) { + window.nosto?.reload({ + site: "localhost" + }) + } + setClientScriptLoadedState(true) + } + + // Create script element + function createScriptElement(urlPartial: string) { + const scriptEl = document.createElement("script") + scriptEl.type = "text/javascript" + scriptEl.src = `//${(host || "connect.nosto.com")}${urlPartial}` + scriptEl.async = true + scriptEl.setAttribute("nosto-client-script", "") + return scriptEl + } + + // Load Nosto API stub + if (!window.nostojs) { + window.nostojs = (cb: (api: NostoClient) => void) => { + (window.nostojs.q = window.nostojs.q || []).push(cb) + } + window.nostojs(api => api.setAutoLoad(false)) + } + + // Load Nosto client script if not already loaded externally + if (!isNostoLoaded() && !shopifyMarkets) { + const urlPartial = `/include/${account}` + const script = createScriptElement(urlPartial) + script.onload = scriptOnload + document.body.appendChild(script) + } + + // Load Shopify Markets scripts + if (shopifyMarkets) { + const existingScript = document.querySelector("[nosto-client-script]") + const marketId = String(shopifyMarkets?.marketId || "") + const language = shopifyMarkets?.language || "" + + const existingScriptAttributes = + existingScript?.getAttribute("nosto-language") !== language || + existingScript?.getAttribute("nosto-market-id") !== marketId + + if (!existingScript || existingScriptAttributes) { + if (clientScriptLoadedState) { + setClientScriptLoadedState(false) + } + + const nostoSandbox = document.querySelector("#nosto-sandbox") + + existingScript?.parentNode?.removeChild(existingScript) + nostoSandbox?.parentNode?.removeChild(nostoSandbox) + + const urlPartial = + `/script/shopify/market/nosto.js?merchant=${account}&market=${marketId}&locale=${language.toLowerCase()}` + const script = createScriptElement(urlPartial) + script.setAttribute("nosto-language", language) + script.setAttribute("nosto-market-id", marketId) + script.onload = scriptOnload + document.body.appendChild(script) + } + } + + }, [clientScriptLoadedState, shopifyMarkets]) + + return { clientScriptLoaded } +} \ No newline at end of file