@nosto/nosto-react 2.2.2 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,7 @@ import { NostoContext, RecommendationComponent } from "../context"
3
3
  import type { ReactNode } from "react"
4
4
  import { ScriptLoadOptions } from "../hooks/scriptLoader"
5
5
  import { useLoadClientScript } from "../hooks/useLoadClientScript"
6
+ import { nostojs } from "@nosto/nosto-js"
6
7
 
7
8
  /**
8
9
  * @group Components
@@ -88,7 +89,7 @@ export function NostoProvider(props: NostoProviderProps) {
88
89
  const { clientScriptLoaded } = useLoadClientScript(props)
89
90
 
90
91
  if (clientScriptLoaded) {
91
- window.nostojs(api => {
92
+ nostojs(api => {
92
93
  api.defaultSession().setVariation(currentVariation!).setResponseMode(responseMode)
93
94
  })
94
95
  }
package/src/context.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createContext, ReactElement } from "react"
2
- import { Recommendation, RenderMode } from "./types"
2
+ import { Recommendation } from "./types"
3
+ import { RenderMode } from "@nosto/nosto-js/client"
3
4
 
4
5
  type AnyFunction = (...args: unknown[]) => unknown
5
6
 
@@ -1,11 +1,12 @@
1
1
  import { useState, useEffect } from "react"
2
- import { isNostoLoaded } from "../components/helpers"
3
- import type { NostoClient } from "../types"
4
2
  import type { NostoProviderProps } from "../components/NostoProvider"
5
3
  import scriptLoaderFn from "./scriptLoader"
4
+ import { init, initNostoStub, isNostoLoaded, nostojs } from "@nosto/nosto-js"
6
5
 
7
6
  type NostoScriptProps = Pick<NostoProviderProps, "account" | "host" | "shopifyMarkets" | "loadScript" | "scriptLoader">
8
7
 
8
+ const defaultAttributes = { "nosto-client-script": "" }
9
+
9
10
  export function useLoadClientScript(props: NostoScriptProps) {
10
11
  const {
11
12
  host = "connect.nosto.com",
@@ -18,19 +19,13 @@ export function useLoadClientScript(props: NostoScriptProps) {
18
19
 
19
20
  useEffect(() => {
20
21
  function scriptOnload() {
21
- // Override for production scripts to work in unit tests
22
- if ("nostoReactTest" in window) {
23
- window.nosto?.reload({
24
- site: "localhost"
25
- })
26
- }
27
22
  setClientScriptLoaded(true)
28
23
  }
29
24
 
30
25
  // Create and append script element
31
26
  async function injectScriptElement(urlPartial: string, extraAttributes: Record<string, string> = {}) {
32
27
  const scriptSrc = `//${host}${urlPartial}`
33
- const attributes = { "nosto-client-script": "", ...extraAttributes }
28
+ const attributes = { ...defaultAttributes, ...extraAttributes }
34
29
  await scriptLoader(scriptSrc, { attributes })
35
30
  scriptOnload()
36
31
  }
@@ -60,23 +55,27 @@ export function useLoadClientScript(props: NostoScriptProps) {
60
55
  }
61
56
  }
62
57
 
63
- // Load Nosto API stub
64
- if (!window.nostojs) {
65
- window.nostojs = (cb: (api: NostoClient) => void) => {
66
- (window.nostojs.q = window.nostojs.q || []).push(cb)
67
- }
68
- window.nostojs(api => api.setAutoLoad(false))
69
- }
58
+ initNostoStub()
70
59
 
71
60
  if (!loadScript) {
72
- window.nosto ? scriptOnload() : window.nostojs(scriptOnload)
61
+ nostojs(scriptOnload)
73
62
  return
74
63
  }
75
64
 
65
+ async function initClientScript() {
66
+ await init({
67
+ merchantId: account,
68
+ options: {
69
+ attributes: defaultAttributes
70
+ },
71
+ scriptLoader
72
+ })
73
+ scriptOnload()
74
+ }
75
+
76
76
  // Load Nosto client script if not already loaded externally
77
77
  if (!isNostoLoaded() && !shopifyMarkets) {
78
- const urlPartial = `/include/${account}`
79
- injectScriptElement(urlPartial)
78
+ initClientScript()
80
79
  }
81
80
 
82
81
  // Load Shopify Markets scripts
@@ -1,15 +1,16 @@
1
1
  import { DependencyList, useEffect } from "react"
2
2
  import { useNostoContext } from "./useNostoContext"
3
- import { NostoClient } from "../types"
4
3
  import { useDeepCompareEffect } from "./useDeepCompareEffect"
4
+ import { nostojs } from "@nosto/nosto-js"
5
+ import { API } from "@nosto/nosto-js/client"
5
6
 
6
- export function useNostoApi(cb: (api: NostoClient) => void, deps?: DependencyList, flags?: { deep?: boolean }): void {
7
+ export function useNostoApi(cb: (api: API) => void, deps?: DependencyList, flags?: { deep?: boolean }): void {
7
8
  const { clientScriptLoaded } = useNostoContext()
8
9
  const useEffectFn = flags?.deep ? useDeepCompareEffect : useEffect
9
10
 
10
11
  useEffectFn(() => {
11
12
  if (clientScriptLoaded) {
12
- window.nostojs(cb)
13
+ nostojs(cb)
13
14
  }
14
15
  }, [clientScriptLoaded, ...(deps ?? [])])
15
16
  }
@@ -1,8 +1,8 @@
1
1
  import { snakeize } from "../utils/snakeize"
2
- import { Order } from "../types"
3
2
  import { useRenderCampaigns } from "./useRenderCampaigns"
4
3
  import { useNostoApi } from "./useNostoApi"
5
4
  import { ToCamelCase } from "../utils/types"
5
+ import { WebsiteOrder as Order } from "@nosto/nosto-js/client"
6
6
 
7
7
  /**
8
8
  * @group Hooks
@@ -1,12 +1,13 @@
1
- import { Product } from "../types"
2
1
  import { useNostoApi } from "./useNostoApi"
3
2
  import { useRenderCampaigns } from "./useRenderCampaigns"
3
+ import { Product } from "@nosto/nosto-js/client"
4
4
 
5
5
  /**
6
6
  * @group Hooks
7
7
  */
8
8
  export type NostoProductProps = {
9
9
  product: string
10
+ reference?: string
10
11
  tagging?: Product
11
12
  placements?: string[]
12
13
  }
@@ -16,22 +17,27 @@ export type NostoProductProps = {
16
17
  *
17
18
  * @group Hooks
18
19
  */
19
- export function useNostoProduct({ product, tagging, placements }: NostoProductProps) {
20
+ export function useNostoProduct({ product, tagging, placements, reference }: NostoProductProps) {
20
21
  const { renderCampaigns } = useRenderCampaigns()
21
22
 
22
23
  if (tagging && !tagging.product_id) {
23
24
  throw new Error("The product object must contain a product_id property")
24
25
  }
25
26
 
27
+ const productId = tagging?.product_id ?? product
28
+
26
29
  useNostoApi(
27
30
  async api => {
28
- const data = await api
31
+ const action = api
29
32
  .defaultSession()
30
33
  .viewProduct(tagging ?? product)
31
34
  .setPlacements(placements || api.placements.getPlacements())
32
- .load()
35
+ if (reference) {
36
+ action.setRef(productId, reference)
37
+ }
38
+ const data = await action.load()
33
39
  renderCampaigns(data)
34
40
  },
35
- [product, tagging?.selected_sku_id]
41
+ [productId, tagging?.selected_sku_id]
36
42
  )
37
43
  }
@@ -1,8 +1,9 @@
1
1
  import { snakeize } from "../utils/snakeize"
2
- import { Cart as CartSnakeCase, Customer as CustomerSnakeCase } from "../types"
2
+ import { PushedCustomer as CustomerSnakeCase, Cart as CartSnakeCase } from "@nosto/nosto-js/client"
3
3
  import { ToCamelCase } from "../utils/types"
4
4
  import { useNostoContext } from "./useNostoContext"
5
5
  import { useDeepCompareEffect } from "./useDeepCompareEffect"
6
+ import { nostojs } from "@nosto/nosto-js"
6
7
 
7
8
  /**
8
9
  * @group Hooks
@@ -25,7 +26,7 @@ export function useNostoSession({ cart, customer }: NostoSessionProps = {}) {
25
26
  const currentCustomer = customer ? snakeize(customer) : undefined
26
27
 
27
28
  if (clientScriptLoaded) {
28
- window.nostojs(api => {
29
+ nostojs(api => {
29
30
  api.defaultSession().setCart(currentCart).setCustomer(currentCustomer).viewOther().load({ skipPageViews: true })
30
31
  })
31
32
  }
@@ -1,8 +1,10 @@
1
1
  import { cloneElement, useRef } from "react"
2
2
  import { createRoot, Root } from "react-dom/client"
3
- import { ActionResponse, Recommendation } from "../types"
3
+ import { Recommendation } from "../types"
4
4
  import { useNostoContext } from "./useNostoContext"
5
5
  import { RecommendationComponent } from "../context"
6
+ import { ActionResponse, API } from "@nosto/nosto-js/client"
7
+ import { nostojs } from "@nosto/nosto-js"
6
8
 
7
9
  type CampaignData = Pick<ActionResponse, "campaigns" | "recommendations">
8
10
 
@@ -17,13 +19,16 @@ function RecommendationComponentWrapper(props: {
17
19
  })
18
20
  }
19
21
 
22
+ function injectPlacements(data: Record<string, unknown>) {
23
+ nostojs(api => api.placements.injectCampaigns(data as Parameters<API['placements']['injectCampaigns']>[0]))
24
+ }
25
+
20
26
  function injectCampaigns(data: CampaignData) {
27
+ // @ts-expect-error not defined
21
28
  if (!window.nostojs) {
22
29
  throw new Error("Nosto has not yet been initialized")
23
30
  }
24
- window.nostojs(api => {
25
- api.placements.injectCampaigns(data.recommendations)
26
- })
31
+ injectPlacements(data.recommendations)
27
32
  }
28
33
 
29
34
  export function useRenderCampaigns() {
@@ -35,6 +40,9 @@ export function useRenderCampaigns() {
35
40
  }
36
41
 
37
42
  function renderCampaigns(data: CampaignData) {
43
+ // inject Onsite content campaigns directly
44
+ injectPlacements(data.campaigns?.content ?? {})
45
+
38
46
  // render recommendation component into placements:
39
47
  const recommendations = data.campaigns?.recommendations ?? {}
40
48
  for (const key in recommendations) {
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
- export type { Cart, Customer, Product, Order, Recommendation } from "./types"
1
+ export type { Product, PushedCustomer as Customer, Cart, WebsiteOrder as Order } from "@nosto/nosto-js/client"
2
+ export type { Recommendation } from "./types"
2
3
  export { type ScriptLoadOptions } from "./hooks/scriptLoader"
3
4
  export { NostoContext, type NostoContextType } from "./context"
4
5
  export { useNostoContext } from "./hooks/useNostoContext"