@nosto/nosto-react 0.2.1 → 0.3.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.
@@ -1,5 +1,6 @@
1
1
  import * as React from "react";
2
- import React__default, { createContext, useContext, useEffect } from "react";
2
+ import React__default, { createContext, useContext, useEffect, isValidElement, useState, useRef } from "react";
3
+ import { createRoot } from "react-dom/client";
3
4
  const NostoContext = createContext({
4
5
  account: void 0,
5
6
  currentVariation: "",
@@ -49,22 +50,23 @@ const NostoFohofo = () => {
49
50
  const {
50
51
  clientScriptLoaded,
51
52
  currentVariation,
52
- renderFunction,
53
- responseMode
53
+ responseMode,
54
+ recommendationComponent,
55
+ useRenderCampaigns
54
56
  } = useNostoContext();
57
+ const {
58
+ renderCampaigns,
59
+ pageTypeUpdated
60
+ } = useRenderCampaigns("404");
55
61
  useEffect(() => {
56
- if (clientScriptLoaded) {
62
+ if (clientScriptLoaded && pageTypeUpdated) {
57
63
  window.nostojs((api) => {
58
64
  api.defaultSession().setVariation(currentVariation).setResponseMode(responseMode).viewNotFound().setPlacements(api.placements.getPlacements()).load().then((data) => {
59
- if (responseMode == "HTML") {
60
- api.placements.injectCampaigns(data.recommendations);
61
- } else {
62
- renderFunction(data.campaigns);
63
- }
65
+ renderCampaigns(data, api);
64
66
  });
65
67
  });
66
68
  }
67
- }, [clientScriptLoaded, currentVariation, renderFunction]);
69
+ }, [clientScriptLoaded, currentVariation, recommendationComponent, pageTypeUpdated]);
68
70
  return /* @__PURE__ */ jsx(Fragment, {
69
71
  children: /* @__PURE__ */ jsx("div", {
70
72
  className: "nosto_page_type",
@@ -79,22 +81,23 @@ const NostoOther = () => {
79
81
  const {
80
82
  clientScriptLoaded,
81
83
  currentVariation,
82
- renderFunction,
83
- responseMode
84
+ responseMode,
85
+ recommendationComponent,
86
+ useRenderCampaigns
84
87
  } = useNostoContext();
88
+ const {
89
+ renderCampaigns,
90
+ pageTypeUpdated
91
+ } = useRenderCampaigns("other");
85
92
  useEffect(() => {
86
- if (clientScriptLoaded) {
93
+ if (clientScriptLoaded && pageTypeUpdated) {
87
94
  window.nostojs((api) => {
88
95
  api.defaultSession().setVariation(currentVariation).setResponseMode(responseMode).viewOther().setPlacements(api.placements.getPlacements()).load().then((data) => {
89
- if (responseMode == "HTML") {
90
- api.placements.injectCampaigns(data.recommendations);
91
- } else {
92
- renderFunction(data.campaigns);
93
- }
96
+ renderCampaigns(data, api);
94
97
  });
95
98
  });
96
99
  }
97
- }, [clientScriptLoaded, currentVariation, renderFunction]);
100
+ }, [clientScriptLoaded, currentVariation, recommendationComponent, pageTypeUpdated]);
98
101
  return /* @__PURE__ */ jsx(Fragment, {
99
102
  children: /* @__PURE__ */ jsx("div", {
100
103
  className: "nosto_page_type",
@@ -109,22 +112,23 @@ const NostoCheckout = () => {
109
112
  const {
110
113
  clientScriptLoaded,
111
114
  currentVariation,
112
- renderFunction,
113
- responseMode
115
+ responseMode,
116
+ recommendationComponent,
117
+ useRenderCampaigns
114
118
  } = useNostoContext();
119
+ const {
120
+ renderCampaigns,
121
+ pageTypeUpdated
122
+ } = useRenderCampaigns("checkout");
115
123
  useEffect(() => {
116
- if (clientScriptLoaded) {
124
+ if (clientScriptLoaded && pageTypeUpdated) {
117
125
  window.nostojs((api) => {
118
126
  api.defaultSession().setVariation(currentVariation).setResponseMode(responseMode).viewCart().setPlacements(api.placements.getPlacements()).load().then((data) => {
119
- if (responseMode == "HTML") {
120
- api.placements.injectCampaigns(data.recommendations);
121
- } else {
122
- renderFunction(data.campaigns);
123
- }
127
+ renderCampaigns(data, api);
124
128
  });
125
129
  });
126
130
  }
127
- }, [clientScriptLoaded, currentVariation, renderFunction]);
131
+ }, [clientScriptLoaded, currentVariation, recommendationComponent, pageTypeUpdated]);
128
132
  return /* @__PURE__ */ jsx(Fragment, {
129
133
  children: /* @__PURE__ */ jsx("div", {
130
134
  className: "nosto_page_type",
@@ -142,20 +146,21 @@ const NostoProduct = ({
142
146
  const {
143
147
  clientScriptLoaded,
144
148
  currentVariation,
145
- renderFunction,
146
- responseMode
149
+ responseMode,
150
+ recommendationComponent,
151
+ useRenderCampaigns
147
152
  } = useNostoContext();
153
+ const {
154
+ renderCampaigns,
155
+ pageTypeUpdated
156
+ } = useRenderCampaigns("product");
148
157
  useEffect(() => {
149
- if (clientScriptLoaded) {
158
+ if (clientScriptLoaded && pageTypeUpdated) {
150
159
  window.nostojs((api) => {
151
160
  api.defaultSession().setResponseMode(responseMode).viewProduct(product).setPlacements(api.placements.getPlacements()).load().then((data) => {
152
- if (responseMode == "HTML") {
153
- api.placements.injectCampaigns(data.recommendations);
154
- } else {
155
- renderFunction(data.campaigns);
156
- }
161
+ renderCampaigns(data, api);
157
162
  });
158
- }, [clientScriptLoaded, currentVariation, product, renderFunction]);
163
+ }, [clientScriptLoaded, currentVariation, product, recommendationComponent, pageTypeUpdated]);
159
164
  }
160
165
  });
161
166
  return /* @__PURE__ */ jsxs(Fragment, {
@@ -282,22 +287,23 @@ const NostoCategory = ({
282
287
  const {
283
288
  clientScriptLoaded,
284
289
  currentVariation,
285
- renderFunction,
286
- responseMode
290
+ responseMode,
291
+ recommendationComponent,
292
+ useRenderCampaigns
287
293
  } = useNostoContext();
294
+ const {
295
+ renderCampaigns,
296
+ pageTypeUpdated
297
+ } = useRenderCampaigns("home");
288
298
  useEffect(() => {
289
- if (clientScriptLoaded) {
299
+ if (clientScriptLoaded && pageTypeUpdated) {
290
300
  window.nostojs((api) => {
291
301
  api.defaultSession().setVariation(currentVariation).setResponseMode(responseMode).viewCategory(category).setPlacements(api.placements.getPlacements()).load().then((data) => {
292
- if (responseMode == "HTML") {
293
- api.placements.injectCampaigns(data.recommendations);
294
- } else {
295
- renderFunction(data.campaigns);
296
- }
302
+ renderCampaigns(data, api);
297
303
  });
298
304
  });
299
305
  }
300
- }, [clientScriptLoaded, category, currentVariation, renderFunction]);
306
+ }, [clientScriptLoaded, category, currentVariation, recommendationComponent, pageTypeUpdated]);
301
307
  return /* @__PURE__ */ jsxs(Fragment, {
302
308
  children: [/* @__PURE__ */ jsx("div", {
303
309
  className: "nosto_page_type",
@@ -320,22 +326,23 @@ const NostoSearch = ({
320
326
  const {
321
327
  clientScriptLoaded,
322
328
  currentVariation,
323
- renderFunction,
324
- responseMode
329
+ responseMode,
330
+ recommendationComponent,
331
+ useRenderCampaigns
325
332
  } = useNostoContext();
333
+ const {
334
+ renderCampaigns,
335
+ pageTypeUpdated
336
+ } = useRenderCampaigns("search");
326
337
  useEffect(() => {
327
- if (clientScriptLoaded) {
338
+ if (clientScriptLoaded && pageTypeUpdated) {
328
339
  window.nostojs((api) => {
329
340
  api.defaultSession().setVariation(currentVariation).setResponseMode(responseMode).viewSearch(query).setPlacements(api.placements.getPlacements()).load().then((data) => {
330
- if (responseMode == "HTML") {
331
- api.placements.injectCampaigns(data.recommendations);
332
- } else {
333
- renderFunction(data.campaigns);
334
- }
341
+ renderCampaigns(data, api);
335
342
  });
336
343
  });
337
344
  }
338
- }, [clientScriptLoaded, currentVariation, query, renderFunction]);
345
+ }, [clientScriptLoaded, currentVariation, query, recommendationComponent, pageTypeUpdated]);
339
346
  return /* @__PURE__ */ jsxs(Fragment, {
340
347
  children: [/* @__PURE__ */ jsx("div", {
341
348
  className: "nosto_page_type",
@@ -379,22 +386,23 @@ const NostoOrder = ({
379
386
  const {
380
387
  clientScriptLoaded,
381
388
  currentVariation,
382
- renderFunction,
383
- responseMode
389
+ responseMode,
390
+ recommendationComponent,
391
+ useRenderCampaigns
384
392
  } = useNostoContext();
393
+ const {
394
+ renderCampaigns,
395
+ pageTypeUpdated
396
+ } = useRenderCampaigns("order");
385
397
  useEffect(() => {
386
- if (clientScriptLoaded) {
398
+ if (clientScriptLoaded && pageTypeUpdated) {
387
399
  window.nostojs((api) => {
388
400
  api.defaultSession().setVariation(currentVariation).setResponseMode(responseMode).addOrder(snakeize(order)).setPlacements(api.placements.getPlacements()).load().then((data) => {
389
- if (responseMode == "HTML") {
390
- api.placements.injectCampaigns(data.recommendations);
391
- } else {
392
- renderFunction(data.campaigns);
393
- }
401
+ renderCampaigns(data, api);
394
402
  });
395
403
  });
396
404
  }
397
- }, [clientScriptLoaded, currentVariation, renderFunction]);
405
+ }, [clientScriptLoaded, currentVariation, recommendationComponent, pageTypeUpdated]);
398
406
  return /* @__PURE__ */ jsxs(Fragment, {
399
407
  children: [/* @__PURE__ */ jsx("div", {
400
408
  className: "nosto_page_type",
@@ -415,22 +423,23 @@ const NostoHome = () => {
415
423
  const {
416
424
  clientScriptLoaded,
417
425
  currentVariation,
418
- renderFunction,
419
- responseMode
426
+ responseMode,
427
+ recommendationComponent,
428
+ useRenderCampaigns
420
429
  } = useNostoContext();
430
+ const {
431
+ renderCampaigns,
432
+ pageTypeUpdated
433
+ } = useRenderCampaigns("home");
421
434
  useEffect(() => {
422
- if (clientScriptLoaded) {
435
+ if (clientScriptLoaded && pageTypeUpdated) {
423
436
  window.nostojs((api) => {
424
437
  api.defaultSession().setVariation(currentVariation).setResponseMode(responseMode).viewFrontPage().setPlacements(api.placements.getPlacements()).load().then((data) => {
425
- if (responseMode == "HTML") {
426
- api.placements.injectCampaigns(data.recommendations);
427
- } else {
428
- renderFunction(data.campaigns);
429
- }
438
+ renderCampaigns(data, api);
430
439
  });
431
440
  });
432
441
  }
433
- }, [clientScriptLoaded, currentVariation, renderFunction]);
442
+ }, [clientScriptLoaded, currentVariation, recommendationComponent, pageTypeUpdated]);
434
443
  return /* @__PURE__ */ jsx(Fragment, {
435
444
  children: /* @__PURE__ */ jsx("div", {
436
445
  className: "nosto_page_type",
@@ -442,12 +451,13 @@ const NostoHome = () => {
442
451
  });
443
452
  };
444
453
  const NostoPlacement = ({
445
- id
454
+ id,
455
+ pageType
446
456
  }) => {
447
457
  return /* @__PURE__ */ jsx("div", {
448
458
  className: "nosto_element",
449
459
  id
450
- });
460
+ }, id + pageType);
451
461
  };
452
462
  const NostoProvider = ({
453
463
  account,
@@ -455,12 +465,51 @@ const NostoProvider = ({
455
465
  multiCurrency = false,
456
466
  host,
457
467
  children,
458
- renderFunction
468
+ recommendationComponent
459
469
  }) => {
460
470
  const [clientScriptLoadedState, setClientScriptLoadedState] = React__default.useState(false);
461
471
  const clientScriptLoaded = React__default.useMemo(() => clientScriptLoadedState, [clientScriptLoadedState]);
462
- const responseMode = typeof renderFunction == "function" ? "JSON_ORIGINAL" : "HTML";
463
472
  currentVariation = multiCurrency ? currentVariation : "";
473
+ const responseMode = isValidElement(recommendationComponent) ? "JSON_ORIGINAL" : "HTML";
474
+ function RecommendationComponentWrapper(props) {
475
+ return React__default.cloneElement(recommendationComponent, {
476
+ nostoRecommendation: props.nostoRecommendation
477
+ });
478
+ }
479
+ const [pageType, setPageType] = useState("");
480
+ const useRenderCampaigns = function(type = "") {
481
+ const placementRefs = useRef({});
482
+ useEffect(() => {
483
+ if (pageType != type) {
484
+ setPageType(type);
485
+ }
486
+ }, []);
487
+ const pageTypeUpdated = type == pageType;
488
+ function renderCampaigns(data, api) {
489
+ if (responseMode == "HTML") {
490
+ api.placements.injectCampaigns(data.recommendations);
491
+ } else {
492
+ const recommendations = data.campaigns.recommendations;
493
+ for (const key in recommendations) {
494
+ let recommendation = recommendations[key];
495
+ let placementSelector = "#" + key;
496
+ let placement = () => document.querySelector(placementSelector);
497
+ if (!!placement()) {
498
+ if (!placementRefs.current[key])
499
+ placementRefs.current[key] = createRoot(placement());
500
+ const root = placementRefs.current[key];
501
+ root.render(/* @__PURE__ */ jsx(RecommendationComponentWrapper, {
502
+ nostoRecommendation: recommendation
503
+ }));
504
+ }
505
+ }
506
+ }
507
+ }
508
+ return {
509
+ renderCampaigns,
510
+ pageTypeUpdated
511
+ };
512
+ };
464
513
  useEffect(() => {
465
514
  if (!document.querySelectorAll("[nosto-client-script]").length) {
466
515
  const script = document.createElement("script");
@@ -482,8 +531,10 @@ const NostoProvider = ({
482
531
  account,
483
532
  clientScriptLoaded,
484
533
  currentVariation,
485
- renderFunction,
486
- responseMode
534
+ responseMode,
535
+ recommendationComponent,
536
+ useRenderCampaigns,
537
+ pageType
487
538
  },
488
539
  children
489
540
  });
@@ -1,4 +1,4 @@
1
- (function(i,d){typeof exports=="object"&&typeof module!="undefined"?d(exports,require("react")):typeof define=="function"&&define.amd?define(["exports","react"],d):(i=typeof globalThis!="undefined"?globalThis:i||self,d(i["@nosto/nosto-react"]={},i.React))})(this,function(i,d){"use strict";function O(s){return s&&typeof s=="object"&&"default"in s?s:{default:s}}function F(s){if(s&&s.__esModule)return s;var e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});return s&&Object.keys(s).forEach(function(r){if(r!=="default"){var n=Object.getOwnPropertyDescriptor(s,r);Object.defineProperty(e,r,n.get?n:{enumerable:!0,get:function(){return s[r]}})}}),e.default=s,Object.freeze(e)}var w=O(d),N=F(d);const S=d.createContext({account:void 0,currentVariation:"",renderFunction:void 0});function p(){const s=d.useContext(S);if(!s)throw new Error("No nosto context found");return s}var _={exports:{}},v={};/**
1
+ (function(m,p){typeof exports=="object"&&typeof module!="undefined"?p(exports,require("react"),require("react-dom/client")):typeof define=="function"&&define.amd?define(["exports","react","react-dom/client"],p):(m=typeof globalThis!="undefined"?globalThis:m||self,p(m["@nosto/nosto-react"]={},m.React,m.ReactDOM))})(this,function(m,p,A){"use strict";function D(t){return t&&typeof t=="object"&&"default"in t?t:{default:t}}function I(t){if(t&&t.__esModule)return t;var e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});return t&&Object.keys(t).forEach(function(r){if(r!=="default"){var n=Object.getOwnPropertyDescriptor(t,r);Object.defineProperty(e,r,n.get?n:{enumerable:!0,get:function(){return t[r]}})}}),e.default=t,Object.freeze(e)}var w=D(p),S=I(p);const O=p.createContext({account:void 0,currentVariation:"",renderFunction:void 0});function h(){const t=p.useContext(O);if(!t)throw new Error("No nosto context found");return t}var P={exports:{}},j={};/**
2
2
  * @license React
3
3
  * react-jsx-runtime.production.min.js
4
4
  *
@@ -6,4 +6,4 @@
6
6
  *
7
7
  * This source code is licensed under the MIT license found in the
8
8
  * LICENSE file in the root directory of this source tree.
9
- */var E=w.default,T=Symbol.for("react.element"),V=Symbol.for("react.fragment"),x=Object.prototype.hasOwnProperty,R=E.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,A={key:!0,ref:!0,__self:!0,__source:!0};function P(s,e,r){var n,o={},l=null,t=null;r!==void 0&&(l=""+r),e.key!==void 0&&(l=""+e.key),e.ref!==void 0&&(t=e.ref);for(n in e)x.call(e,n)&&!A.hasOwnProperty(n)&&(o[n]=e[n]);if(s&&s.defaultProps)for(n in e=s.defaultProps,e)o[n]===void 0&&(o[n]=e[n]);return{$$typeof:T,type:s,key:l,ref:t,props:o,_owner:R.current}}v.Fragment=V,v.jsx=P,v.jsxs=P,_.exports=v;const c=_.exports.jsx,f=_.exports.jsxs,u=_.exports.Fragment,H=()=>{const{clientScriptLoaded:s,currentVariation:e,renderFunction:r,responseMode:n}=p();return d.useEffect(()=>{s&&window.nostojs(o=>{o.defaultSession().setVariation(e).setResponseMode(n).viewNotFound().setPlacements(o.placements.getPlacements()).load().then(l=>{n=="HTML"?o.placements.injectCampaigns(l.recommendations):r(l.campaigns)})})},[s,e,r]),c(u,{children:c("div",{className:"nosto_page_type",style:{display:"none"},children:"notfound"})})},I=()=>{const{clientScriptLoaded:s,currentVariation:e,renderFunction:r,responseMode:n}=p();return d.useEffect(()=>{s&&window.nostojs(o=>{o.defaultSession().setVariation(e).setResponseMode(n).viewOther().setPlacements(o.placements.getPlacements()).load().then(l=>{n=="HTML"?o.placements.injectCampaigns(l.recommendations):r(l.campaigns)})})},[s,e,r]),c(u,{children:c("div",{className:"nosto_page_type",style:{display:"none"},children:"other"})})},D=()=>{const{clientScriptLoaded:s,currentVariation:e,renderFunction:r,responseMode:n}=p();return d.useEffect(()=>{s&&window.nostojs(o=>{o.defaultSession().setVariation(e).setResponseMode(n).viewCart().setPlacements(o.placements.getPlacements()).load().then(l=>{n=="HTML"?o.placements.injectCampaigns(l.recommendations):r(l.campaigns)})})},[s,e,r]),c(u,{children:c("div",{className:"nosto_page_type",style:{display:"none"},children:"cart"})})},U=({product:s,tagging:e})=>{const{clientScriptLoaded:r,currentVariation:n,renderFunction:o,responseMode:l}=p();return d.useEffect(()=>{r&&window.nostojs(t=>{t.defaultSession().setResponseMode(l).viewProduct(s).setPlacements(t.placements.getPlacements()).load().then(a=>{l=="HTML"?t.placements.injectCampaigns(a.recommendations):o(a.campaigns)})},[r,n,s,o])}),f(u,{children:[c("div",{className:"nosto_page_type",style:{display:"none"},children:"product"}),f("div",{className:"nosto_product",style:{display:"none"},children:[(e==null?void 0:e.variationId)&&c("span",{className:"variation_id",children:e.variationId}),s&&c("span",{className:"product_id",children:s}),(e==null?void 0:e.name)&&c("span",{className:"name",children:e.name}),(e==null?void 0:e.url)&&c("span",{className:"url",children:e.url.toString()}),(e==null?void 0:e.imageUrl)&&c("span",{className:"image_url",children:e.imageUrl.toString()}),(e==null?void 0:e.availability)&&c("span",{className:"availability",children:e.availability}),(e==null?void 0:e.price)&&c("span",{className:"price",children:e.price}),(e==null?void 0:e.listPrice)&&c("span",{className:"list_price",children:e.listPrice}),(e==null?void 0:e.priceCurrencyCode)&&c("span",{className:"price_currency_code",children:e.priceCurrencyCode}),(e==null?void 0:e.brand)&&c("span",{className:"brand",children:e.brand}),(e==null?void 0:e.description)&&c("span",{className:"description",children:e.description}),(e==null?void 0:e.googleCategory)&&c("span",{className:"description",children:e.googleCategory}),(e==null?void 0:e.condition)&&c("span",{className:"condition",children:e.condition}),(e==null?void 0:e.gender)&&c("span",{className:"gender",children:e.gender}),(e==null?void 0:e.ageGroup)&&c("span",{className:"age_group",children:e.ageGroup}),(e==null?void 0:e.gtin)&&c("span",{className:"gtin",children:e.gtin}),(e==null?void 0:e.category)&&(e==null?void 0:e.category.map((t,a)=>c("span",{className:"category",children:t},a))),(e==null?void 0:e.tags1)&&e.tags1.map((t,a)=>c("span",{className:"tag1",children:t},a)),(e==null?void 0:e.tags2)&&e.tags2.map((t,a)=>c("span",{className:"tag2",children:t},a)),(e==null?void 0:e.tags3)&&e.tags3.map((t,a)=>c("span",{className:"tag3",children:t},a)),(e==null?void 0:e.ratingValue)&&c("span",{className:"rating_value",children:e.ratingValue}),(e==null?void 0:e.reviewCount)&&c("span",{className:"review_count",children:e.reviewCount}),(e==null?void 0:e.alternateImageUrls)&&e.alternateImageUrls.map((t,a)=>c("span",{className:"alternate_image_url",children:t.toString()},a)),(e==null?void 0:e.customFields)&&Object.keys(e.customFields).map((t,a)=>e.customFields&&e.customFields[t]&&c("span",{className:t,children:e.customFields[t]},a)),(e==null?void 0:e.skus)&&e.skus.map((t,a)=>f("span",{className:"nosto_sku",children:[(t==null?void 0:t.id)&&c("span",{className:"product_id",children:t.id}),(t==null?void 0:t.name)&&c("span",{className:"name",children:t.name}),(t==null?void 0:t.price)&&c("span",{className:"price",children:t.price}),(t==null?void 0:t.listPrice)&&c("span",{className:"list_price",children:t.listPrice}),(t==null?void 0:t.url)&&c("span",{className:"url",children:t.url.toString()}),(t==null?void 0:t.imageUrl)&&c("span",{className:"image_url",children:t.imageUrl.toString()}),(t==null?void 0:t.gtin)&&c("span",{className:"gtin",children:t.gtin}),(t==null?void 0:t.availability)&&c("span",{className:"availability",children:t.availability}),(t==null?void 0:t.customFields)&&Object.keys(t.customFields).map((y,C)=>t.customFields&&t.customFields[y]&&c("span",{className:y,children:t.customFields[y]},C))]},a))]})]})},b=({category:s})=>{const{clientScriptLoaded:e,currentVariation:r,renderFunction:n,responseMode:o}=p();return d.useEffect(()=>{e&&window.nostojs(l=>{l.defaultSession().setVariation(r).setResponseMode(o).viewCategory(s).setPlacements(l.placements.getPlacements()).load().then(t=>{o=="HTML"?l.placements.injectCampaigns(t.recommendations):n(t.campaigns)})})},[e,s,r,n]),f(u,{children:[c("div",{className:"nosto_page_type",style:{display:"none"},children:"category"}),c("div",{className:"nosto_category",style:{display:"none"},children:s})]})},z=({query:s})=>{const{clientScriptLoaded:e,currentVariation:r,renderFunction:n,responseMode:o}=p();return d.useEffect(()=>{e&&window.nostojs(l=>{l.defaultSession().setVariation(r).setResponseMode(o).viewSearch(s).setPlacements(l.placements.getPlacements()).load().then(t=>{o=="HTML"?l.placements.injectCampaigns(t.recommendations):n(t.campaigns)})})},[e,r,s,n]),f(u,{children:[c("div",{className:"nosto_page_type",style:{display:"none"},children:"search"}),c("div",{className:"nosto_search",style:{display:"none"},children:s})]})};var j=function s(e){return!e||typeof e!="object"||q(e)||B(e)?e:Array.isArray(e)?e.map(s):Object.keys(e).reduce(function(r,n){var o=n[0].toLowerCase()+n.slice(1).replace(/([A-Z]+)/g,function(l,t){return"_"+t.toLowerCase()});return r[o]=s(e[n]),r},{})},q=function(s){return Object.prototype.toString.call(s)==="[object Date]"},B=function(s){return Object.prototype.toString.call(s)==="[object RegExp]"};const G=({order:s})=>{const{clientScriptLoaded:e,currentVariation:r,renderFunction:n,responseMode:o}=p();return d.useEffect(()=>{e&&window.nostojs(l=>{l.defaultSession().setVariation(r).setResponseMode(o).addOrder(j(s)).setPlacements(l.placements.getPlacements()).load().then(t=>{o=="HTML"?l.placements.injectCampaigns(t.recommendations):n(t.campaigns)})})},[e,r,n]),f(u,{children:[c("div",{className:"nosto_page_type",style:{display:"none"},children:"order"}),c("div",{className:"nosto_order",style:{display:"none"},children:s.purchase.number})]})},J=()=>{const{clientScriptLoaded:s,currentVariation:e,renderFunction:r,responseMode:n}=p();return d.useEffect(()=>{s&&window.nostojs(o=>{o.defaultSession().setVariation(e).setResponseMode(n).viewFrontPage().setPlacements(o.placements.getPlacements()).load().then(l=>{n=="HTML"?o.placements.injectCampaigns(l.recommendations):r(l.campaigns)})})},[s,e,r]),c(u,{children:c("div",{className:"nosto_page_type",style:{display:"none"},children:"front"})})},$=({id:s})=>c("div",{className:"nosto_element",id:s}),W=({account:s,currentVariation:e="",multiCurrency:r=!1,host:n,children:o,renderFunction:l})=>{const[t,a]=w.default.useState(!1),y=w.default.useMemo(()=>t,[t]),C=typeof l=="function"?"JSON_ORIGINAL":"HTML";return e=r?e:"",d.useEffect(()=>{if(!document.querySelectorAll("[nosto-client-script]").length){const m=document.createElement("script");m.type="text/javascript",m.src="//"+(n||"connect.nosto.com")+"/include/"+s,m.async=!0,m.setAttribute("nosto-client-script",""),m.onload=()=>{console.log("Nosto client script loaded"),a(!0)},document.head.appendChild(m)}window.nostojs=m=>(window.nostojs.q=window.nostojs.q||[]).push(m),window.nostojs(m=>m.setAutoLoad(!1))},[]),c(S.Provider,{value:{account:s,clientScriptLoaded:y,currentVariation:e,renderFunction:l,responseMode:C},children:o})};var M=Object.prototype.hasOwnProperty;function L(s,e,r){for(r of s.keys())if(h(r,e))return r}function h(s,e){var r,n,o;if(s===e)return!0;if(s&&e&&(r=s.constructor)===e.constructor){if(r===Date)return s.getTime()===e.getTime();if(r===RegExp)return s.toString()===e.toString();if(r===Array){if((n=s.length)===e.length)for(;n--&&h(s[n],e[n]););return n===-1}if(r===Set){if(s.size!==e.size)return!1;for(n of s)if(o=n,o&&typeof o=="object"&&(o=L(e,o),!o)||!e.has(o))return!1;return!0}if(r===Map){if(s.size!==e.size)return!1;for(n of s)if(o=n[0],o&&typeof o=="object"&&(o=L(e,o),!o)||!h(n[1],e.get(o)))return!1;return!0}if(r===ArrayBuffer)s=new Uint8Array(s),e=new Uint8Array(e);else if(r===DataView){if((n=s.byteLength)===e.byteLength)for(;n--&&s.getInt8(n)===e.getInt8(n););return n===-1}if(ArrayBuffer.isView(s)){if((n=s.byteLength)===e.byteLength)for(;n--&&s[n]===e[n];);return n===-1}if(!r||typeof s=="object"){n=0;for(r in s)if(M.call(s,r)&&++n&&!M.call(e,r)||!(r in e)||!h(s[r],e[r]))return!1;return Object.keys(e).length===n}}return s!==s&&e!==e}function Y(s){var e=N.useRef(s),r=N.useRef(0);return h(s,e.current)||(e.current=s,r.current+=1),N.useMemo(function(){return e.current},[r.current])}function Z(s,e){return N.useEffect(s,Y(e))}const K=({cart:s,customer:e})=>{const{clientScriptLoaded:r}=p();return Z(()=>{const n=s?j(s):void 0,o=e?j(e):void 0;r&&window.nostojs(l=>{l.defaultSession().setResponseMode("HTML").setCart(n).setCustomer(o).viewOther().load()})},[r,s||[],e||{}]),c(u,{})};i.Nosto404=H,i.NostoCategory=b,i.NostoCheckout=D,i.NostoContext=S,i.NostoHome=J,i.NostoOrder=G,i.NostoOther=I,i.NostoPlacement=$,i.NostoProduct=U,i.NostoProvider=W,i.NostoSearch=z,i.NostoSession=K,i.useNostoContext=p,Object.defineProperties(i,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
9
+ */var b=w.default,z=Symbol.for("react.element"),q=Symbol.for("react.fragment"),H=Object.prototype.hasOwnProperty,B=b.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,G={key:!0,ref:!0,__self:!0,__source:!0};function E(t,e,r){var n,c={},a=null,d=null;r!==void 0&&(a=""+r),e.key!==void 0&&(a=""+e.key),e.ref!==void 0&&(d=e.ref);for(n in e)H.call(e,n)&&!G.hasOwnProperty(n)&&(c[n]=e[n]);if(t&&t.defaultProps)for(n in e=t.defaultProps,e)c[n]===void 0&&(c[n]=e[n]);return{$$typeof:z,type:t,key:a,ref:d,props:c,_owner:B.current}}j.Fragment=q,j.jsx=E,j.jsxs=E,P.exports=j;const o=P.exports.jsx,_=P.exports.jsxs,y=P.exports.Fragment,J=()=>{const{clientScriptLoaded:t,currentVariation:e,responseMode:r,recommendationComponent:n,useRenderCampaigns:c}=h(),{renderCampaigns:a,pageTypeUpdated:d}=c("404");return p.useEffect(()=>{t&&d&&window.nostojs(l=>{l.defaultSession().setVariation(e).setResponseMode(r).viewNotFound().setPlacements(l.placements.getPlacements()).load().then(i=>{a(i,l)})})},[t,e,n,d]),o(y,{children:o("div",{className:"nosto_page_type",style:{display:"none"},children:"notfound"})})},W=()=>{const{clientScriptLoaded:t,currentVariation:e,responseMode:r,recommendationComponent:n,useRenderCampaigns:c}=h(),{renderCampaigns:a,pageTypeUpdated:d}=c("other");return p.useEffect(()=>{t&&d&&window.nostojs(l=>{l.defaultSession().setVariation(e).setResponseMode(r).viewOther().setPlacements(l.placements.getPlacements()).load().then(i=>{a(i,l)})})},[t,e,n,d]),o(y,{children:o("div",{className:"nosto_page_type",style:{display:"none"},children:"other"})})},$=()=>{const{clientScriptLoaded:t,currentVariation:e,responseMode:r,recommendationComponent:n,useRenderCampaigns:c}=h(),{renderCampaigns:a,pageTypeUpdated:d}=c("checkout");return p.useEffect(()=>{t&&d&&window.nostojs(l=>{l.defaultSession().setVariation(e).setResponseMode(r).viewCart().setPlacements(l.placements.getPlacements()).load().then(i=>{a(i,l)})})},[t,e,n,d]),o(y,{children:o("div",{className:"nosto_page_type",style:{display:"none"},children:"cart"})})},Y=({product:t,tagging:e})=>{const{clientScriptLoaded:r,currentVariation:n,responseMode:c,recommendationComponent:a,useRenderCampaigns:d}=h(),{renderCampaigns:l,pageTypeUpdated:i}=d("product");return p.useEffect(()=>{r&&i&&window.nostojs(s=>{s.defaultSession().setResponseMode(c).viewProduct(t).setPlacements(s.placements.getPlacements()).load().then(u=>{l(u,s)})},[r,n,t,a,i])}),_(y,{children:[o("div",{className:"nosto_page_type",style:{display:"none"},children:"product"}),_("div",{className:"nosto_product",style:{display:"none"},children:[(e==null?void 0:e.variationId)&&o("span",{className:"variation_id",children:e.variationId}),t&&o("span",{className:"product_id",children:t}),(e==null?void 0:e.name)&&o("span",{className:"name",children:e.name}),(e==null?void 0:e.url)&&o("span",{className:"url",children:e.url.toString()}),(e==null?void 0:e.imageUrl)&&o("span",{className:"image_url",children:e.imageUrl.toString()}),(e==null?void 0:e.availability)&&o("span",{className:"availability",children:e.availability}),(e==null?void 0:e.price)&&o("span",{className:"price",children:e.price}),(e==null?void 0:e.listPrice)&&o("span",{className:"list_price",children:e.listPrice}),(e==null?void 0:e.priceCurrencyCode)&&o("span",{className:"price_currency_code",children:e.priceCurrencyCode}),(e==null?void 0:e.brand)&&o("span",{className:"brand",children:e.brand}),(e==null?void 0:e.description)&&o("span",{className:"description",children:e.description}),(e==null?void 0:e.googleCategory)&&o("span",{className:"description",children:e.googleCategory}),(e==null?void 0:e.condition)&&o("span",{className:"condition",children:e.condition}),(e==null?void 0:e.gender)&&o("span",{className:"gender",children:e.gender}),(e==null?void 0:e.ageGroup)&&o("span",{className:"age_group",children:e.ageGroup}),(e==null?void 0:e.gtin)&&o("span",{className:"gtin",children:e.gtin}),(e==null?void 0:e.category)&&(e==null?void 0:e.category.map((s,u)=>o("span",{className:"category",children:s},u))),(e==null?void 0:e.tags1)&&e.tags1.map((s,u)=>o("span",{className:"tag1",children:s},u)),(e==null?void 0:e.tags2)&&e.tags2.map((s,u)=>o("span",{className:"tag2",children:s},u)),(e==null?void 0:e.tags3)&&e.tags3.map((s,u)=>o("span",{className:"tag3",children:s},u)),(e==null?void 0:e.ratingValue)&&o("span",{className:"rating_value",children:e.ratingValue}),(e==null?void 0:e.reviewCount)&&o("span",{className:"review_count",children:e.reviewCount}),(e==null?void 0:e.alternateImageUrls)&&e.alternateImageUrls.map((s,u)=>o("span",{className:"alternate_image_url",children:s.toString()},u)),(e==null?void 0:e.customFields)&&Object.keys(e.customFields).map((s,u)=>e.customFields&&e.customFields[s]&&o("span",{className:s,children:e.customFields[s]},u)),(e==null?void 0:e.skus)&&e.skus.map((s,u)=>_("span",{className:"nosto_sku",children:[(s==null?void 0:s.id)&&o("span",{className:"product_id",children:s.id}),(s==null?void 0:s.name)&&o("span",{className:"name",children:s.name}),(s==null?void 0:s.price)&&o("span",{className:"price",children:s.price}),(s==null?void 0:s.listPrice)&&o("span",{className:"list_price",children:s.listPrice}),(s==null?void 0:s.url)&&o("span",{className:"url",children:s.url.toString()}),(s==null?void 0:s.imageUrl)&&o("span",{className:"image_url",children:s.imageUrl.toString()}),(s==null?void 0:s.gtin)&&o("span",{className:"gtin",children:s.gtin}),(s==null?void 0:s.availability)&&o("span",{className:"availability",children:s.availability}),(s==null?void 0:s.customFields)&&Object.keys(s.customFields).map((N,L)=>s.customFields&&s.customFields[N]&&o("span",{className:N,children:s.customFields[N]},L))]},u))]})]})},Z=({category:t})=>{const{clientScriptLoaded:e,currentVariation:r,responseMode:n,recommendationComponent:c,useRenderCampaigns:a}=h(),{renderCampaigns:d,pageTypeUpdated:l}=a("home");return p.useEffect(()=>{e&&l&&window.nostojs(i=>{i.defaultSession().setVariation(r).setResponseMode(n).viewCategory(t).setPlacements(i.placements.getPlacements()).load().then(s=>{d(s,i)})})},[e,t,r,c,l]),_(y,{children:[o("div",{className:"nosto_page_type",style:{display:"none"},children:"category"}),o("div",{className:"nosto_category",style:{display:"none"},children:t})]})},K=({query:t})=>{const{clientScriptLoaded:e,currentVariation:r,responseMode:n,recommendationComponent:c,useRenderCampaigns:a}=h(),{renderCampaigns:d,pageTypeUpdated:l}=a("search");return p.useEffect(()=>{e&&l&&window.nostojs(i=>{i.defaultSession().setVariation(r).setResponseMode(n).viewSearch(t).setPlacements(i.placements.getPlacements()).load().then(s=>{d(s,i)})})},[e,r,t,c,l]),_(y,{children:[o("div",{className:"nosto_page_type",style:{display:"none"},children:"search"}),o("div",{className:"nosto_search",style:{display:"none"},children:t})]})};var M=function t(e){return!e||typeof e!="object"||Q(e)||X(e)?e:Array.isArray(e)?e.map(t):Object.keys(e).reduce(function(r,n){var c=n[0].toLowerCase()+n.slice(1).replace(/([A-Z]+)/g,function(a,d){return"_"+d.toLowerCase()});return r[c]=t(e[n]),r},{})},Q=function(t){return Object.prototype.toString.call(t)==="[object Date]"},X=function(t){return Object.prototype.toString.call(t)==="[object RegExp]"};const k=({order:t})=>{const{clientScriptLoaded:e,currentVariation:r,responseMode:n,recommendationComponent:c,useRenderCampaigns:a}=h(),{renderCampaigns:d,pageTypeUpdated:l}=a("order");return p.useEffect(()=>{e&&l&&window.nostojs(i=>{i.defaultSession().setVariation(r).setResponseMode(n).addOrder(M(t)).setPlacements(i.placements.getPlacements()).load().then(s=>{d(s,i)})})},[e,r,c,l]),_(y,{children:[o("div",{className:"nosto_page_type",style:{display:"none"},children:"order"}),o("div",{className:"nosto_order",style:{display:"none"},children:t.purchase.number})]})},g=()=>{const{clientScriptLoaded:t,currentVariation:e,responseMode:r,recommendationComponent:n,useRenderCampaigns:c}=h(),{renderCampaigns:a,pageTypeUpdated:d}=c("home");return p.useEffect(()=>{t&&d&&window.nostojs(l=>{l.defaultSession().setVariation(e).setResponseMode(r).viewFrontPage().setPlacements(l.placements.getPlacements()).load().then(i=>{a(i,l)})})},[t,e,n,d]),o(y,{children:o("div",{className:"nosto_page_type",style:{display:"none"},children:"front"})})},ee=({id:t,pageType:e})=>o("div",{className:"nosto_element",id:t},t+e),te=({account:t,currentVariation:e="",multiCurrency:r=!1,host:n,children:c,recommendationComponent:a})=>{const[d,l]=w.default.useState(!1),i=w.default.useMemo(()=>d,[d]);e=r?e:"";const s=p.isValidElement(a)?"JSON_ORIGINAL":"HTML";function u(f){return w.default.cloneElement(a,{nostoRecommendation:f.nostoRecommendation})}const[N,L]=p.useState(""),oe=function(f=""){const R=p.useRef({});p.useEffect(()=>{N!=f&&L(f)},[]);const ce=f==N;function le(V,de){if(s=="HTML")de.placements.injectCampaigns(V.recommendations);else{const F=V.campaigns.recommendations;for(const C in F){let ae=F[C],ie="#"+C,x=()=>document.querySelector(ie);x()&&(R.current[C]||(R.current[C]=A.createRoot(x())),R.current[C].render(o(u,{nostoRecommendation:ae})))}}}return{renderCampaigns:le,pageTypeUpdated:ce}};return p.useEffect(()=>{if(!document.querySelectorAll("[nosto-client-script]").length){const f=document.createElement("script");f.type="text/javascript",f.src="//"+(n||"connect.nosto.com")+"/include/"+t,f.async=!0,f.setAttribute("nosto-client-script",""),f.onload=()=>{console.log("Nosto client script loaded"),l(!0)},document.head.appendChild(f)}window.nostojs=f=>(window.nostojs.q=window.nostojs.q||[]).push(f),window.nostojs(f=>f.setAutoLoad(!1))},[]),o(O.Provider,{value:{account:t,clientScriptLoaded:i,currentVariation:e,responseMode:s,recommendationComponent:a,useRenderCampaigns:oe,pageType:N},children:c})};var T=Object.prototype.hasOwnProperty;function U(t,e,r){for(r of t.keys())if(v(r,e))return r}function v(t,e){var r,n,c;if(t===e)return!0;if(t&&e&&(r=t.constructor)===e.constructor){if(r===Date)return t.getTime()===e.getTime();if(r===RegExp)return t.toString()===e.toString();if(r===Array){if((n=t.length)===e.length)for(;n--&&v(t[n],e[n]););return n===-1}if(r===Set){if(t.size!==e.size)return!1;for(n of t)if(c=n,c&&typeof c=="object"&&(c=U(e,c),!c)||!e.has(c))return!1;return!0}if(r===Map){if(t.size!==e.size)return!1;for(n of t)if(c=n[0],c&&typeof c=="object"&&(c=U(e,c),!c)||!v(n[1],e.get(c)))return!1;return!0}if(r===ArrayBuffer)t=new Uint8Array(t),e=new Uint8Array(e);else if(r===DataView){if((n=t.byteLength)===e.byteLength)for(;n--&&t.getInt8(n)===e.getInt8(n););return n===-1}if(ArrayBuffer.isView(t)){if((n=t.byteLength)===e.byteLength)for(;n--&&t[n]===e[n];);return n===-1}if(!r||typeof t=="object"){n=0;for(r in t)if(T.call(t,r)&&++n&&!T.call(e,r)||!(r in e)||!v(t[r],e[r]))return!1;return Object.keys(e).length===n}}return t!==t&&e!==e}function se(t){var e=S.useRef(t),r=S.useRef(0);return v(t,e.current)||(e.current=t,r.current+=1),S.useMemo(function(){return e.current},[r.current])}function ne(t,e){return S.useEffect(t,se(e))}const re=({cart:t,customer:e})=>{const{clientScriptLoaded:r}=h();return ne(()=>{const n=t?M(t):void 0,c=e?M(e):void 0;r&&window.nostojs(a=>{a.defaultSession().setResponseMode("HTML").setCart(n).setCustomer(c).viewOther().load()})},[r,t||[],e||{}]),o(y,{})};m.Nosto404=J,m.NostoCategory=Z,m.NostoCheckout=$,m.NostoContext=O,m.NostoHome=g,m.NostoOrder=k,m.NostoOther=W,m.NostoPlacement=ee,m.NostoProduct=Y,m.NostoProvider=te,m.NostoSearch=K,m.NostoSession=re,m.useNostoContext=h,Object.defineProperties(m,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nosto/nosto-react",
3
3
  "description": "Component library to simply implementing Nosto on React.",
4
- "version": "0.2.1",
4
+ "version": "0.3.0",
5
5
  "author": "Mridang Agarwalla, Dominik Gilg",
6
6
  "license": "ISC",
7
7
  "repository": {
@@ -2,12 +2,19 @@ import React, { useEffect } from "react";
2
2
  import { useNostoContext } from "../Provider/context.client";
3
3
 
4
4
  const NostoCategory: React.FC<{ category: string }> = ({ category }) => {
5
- const { clientScriptLoaded, currentVariation, renderFunction, responseMode } =
6
- useNostoContext();
5
+ const {
6
+ clientScriptLoaded,
7
+ currentVariation,
8
+ responseMode,
9
+ recommendationComponent,
10
+ useRenderCampaigns,
11
+ } = useNostoContext();
12
+
13
+ const { renderCampaigns, pageTypeUpdated } = useRenderCampaigns("home");
7
14
 
8
15
  useEffect(() => {
9
16
  // @ts-ignore
10
- if (clientScriptLoaded) {
17
+ if (clientScriptLoaded && pageTypeUpdated) {
11
18
  window.nostojs((api: any) => {
12
19
  api
13
20
  .defaultSession()
@@ -17,17 +24,17 @@ const NostoCategory: React.FC<{ category: string }> = ({ category }) => {
17
24
  .setPlacements(api.placements.getPlacements())
18
25
  .load()
19
26
  .then((data: object) => {
20
- if (responseMode == "HTML") {
21
- // @ts-ignore
22
- api.placements.injectCampaigns(data.recommendations);
23
- } else {
24
- // @ts-ignore
25
- renderFunction(data.campaigns);
26
- }
27
+ renderCampaigns(data, api);
27
28
  });
28
29
  });
29
30
  }
30
- }, [clientScriptLoaded, category, currentVariation, renderFunction]);
31
+ }, [
32
+ clientScriptLoaded,
33
+ category,
34
+ currentVariation,
35
+ recommendationComponent,
36
+ pageTypeUpdated,
37
+ ]);
31
38
 
32
39
  return (
33
40
  <>
@@ -2,12 +2,19 @@ import React, { useEffect } from "react";
2
2
  import { useNostoContext } from "../Provider/context.client";
3
3
 
4
4
  const NostoCheckout: React.FC = () => {
5
- const { clientScriptLoaded, currentVariation, renderFunction, responseMode } =
6
- useNostoContext();
5
+ const {
6
+ clientScriptLoaded,
7
+ currentVariation,
8
+ responseMode,
9
+ recommendationComponent,
10
+ useRenderCampaigns,
11
+ } = useNostoContext();
12
+
13
+ const { renderCampaigns, pageTypeUpdated } = useRenderCampaigns("checkout");
7
14
 
8
15
  useEffect(() => {
9
16
  // @ts-ignore
10
- if (clientScriptLoaded) {
17
+ if (clientScriptLoaded && pageTypeUpdated) {
11
18
  window.nostojs((api: any) => {
12
19
  api
13
20
  .defaultSession()
@@ -17,17 +24,16 @@ const NostoCheckout: React.FC = () => {
17
24
  .setPlacements(api.placements.getPlacements())
18
25
  .load()
19
26
  .then((data: object) => {
20
- if (responseMode == "HTML") {
21
- // @ts-ignore
22
- api.placements.injectCampaigns(data.recommendations);
23
- } else {
24
- // @ts-ignore
25
- renderFunction(data.campaigns);
26
- }
27
+ renderCampaigns(data, api);
27
28
  });
28
29
  });
29
30
  }
30
- }, [clientScriptLoaded, currentVariation, renderFunction]);
31
+ }, [
32
+ clientScriptLoaded,
33
+ currentVariation,
34
+ recommendationComponent,
35
+ pageTypeUpdated,
36
+ ]);
31
37
 
32
38
  return (
33
39
  <>
@@ -2,12 +2,19 @@ import React, { useEffect } from "react";
2
2
  import { useNostoContext } from "../Provider/context.client";
3
3
 
4
4
  const NostoFohofo: React.FC = () => {
5
- const { clientScriptLoaded, currentVariation, renderFunction, responseMode } =
6
- useNostoContext();
5
+ const {
6
+ clientScriptLoaded,
7
+ currentVariation,
8
+ responseMode,
9
+ recommendationComponent,
10
+ useRenderCampaigns,
11
+ } = useNostoContext();
12
+
13
+ const { renderCampaigns, pageTypeUpdated } = useRenderCampaigns("404");
7
14
 
8
15
  useEffect(() => {
9
16
  // @ts-ignore
10
- if (clientScriptLoaded) {
17
+ if (clientScriptLoaded && pageTypeUpdated) {
11
18
  window.nostojs((api: any) => {
12
19
  api
13
20
  .defaultSession()
@@ -17,17 +24,16 @@ const NostoFohofo: React.FC = () => {
17
24
  .setPlacements(api.placements.getPlacements())
18
25
  .load()
19
26
  .then((data: object) => {
20
- if (responseMode == "HTML") {
21
- // @ts-ignore
22
- api.placements.injectCampaigns(data.recommendations);
23
- } else {
24
- // @ts-ignore
25
- renderFunction(data.campaigns);
26
- }
27
+ renderCampaigns(data, api);
27
28
  });
28
29
  });
29
30
  }
30
- }, [clientScriptLoaded, currentVariation, renderFunction]);
31
+ }, [
32
+ clientScriptLoaded,
33
+ currentVariation,
34
+ recommendationComponent,
35
+ pageTypeUpdated,
36
+ ]);
31
37
 
32
38
  return (
33
39
  <>
@@ -2,12 +2,19 @@ import React, { useEffect } from "react";
2
2
  import { useNostoContext } from "../Provider/context.client";
3
3
 
4
4
  const NostoHome: React.FC = () => {
5
- const { clientScriptLoaded, currentVariation, renderFunction, responseMode } =
6
- useNostoContext();
5
+ const {
6
+ clientScriptLoaded,
7
+ currentVariation,
8
+ responseMode,
9
+ recommendationComponent,
10
+ useRenderCampaigns,
11
+ } = useNostoContext();
12
+
13
+ const { renderCampaigns, pageTypeUpdated } = useRenderCampaigns("home");
7
14
 
8
15
  useEffect(() => {
9
16
  // @ts-ignore
10
- if (clientScriptLoaded) {
17
+ if (clientScriptLoaded && pageTypeUpdated) {
11
18
  window.nostojs((api: any) => {
12
19
  api
13
20
  .defaultSession()
@@ -17,17 +24,16 @@ const NostoHome: React.FC = () => {
17
24
  .setPlacements(api.placements.getPlacements())
18
25
  .load()
19
26
  .then((data: object) => {
20
- if (responseMode == "HTML") {
21
- // @ts-ignore
22
- api.placements.injectCampaigns(data.recommendations);
23
- } else {
24
- // @ts-ignore
25
- renderFunction(data.campaigns);
26
- }
27
+ renderCampaigns(data, api);
27
28
  });
28
29
  });
29
30
  }
30
- }, [clientScriptLoaded, currentVariation, renderFunction]);
31
+ }, [
32
+ clientScriptLoaded,
33
+ currentVariation,
34
+ recommendationComponent,
35
+ pageTypeUpdated,
36
+ ]);
31
37
 
32
38
  return (
33
39
  <>
@@ -8,12 +8,19 @@ export interface OrderProps {
8
8
  }
9
9
 
10
10
  const NostoOrder: React.FC<{ order: OrderProps }> = ({ order }) => {
11
- const { clientScriptLoaded, currentVariation, renderFunction, responseMode } =
12
- useNostoContext();
11
+ const {
12
+ clientScriptLoaded,
13
+ currentVariation,
14
+ responseMode,
15
+ recommendationComponent,
16
+ useRenderCampaigns,
17
+ } = useNostoContext();
18
+
19
+ const { renderCampaigns, pageTypeUpdated } = useRenderCampaigns("order");
13
20
 
14
21
  useEffect(() => {
15
22
  // @ts-ignore
16
- if (clientScriptLoaded) {
23
+ if (clientScriptLoaded && pageTypeUpdated) {
17
24
  window.nostojs((api: any) => {
18
25
  api
19
26
  .defaultSession()
@@ -23,17 +30,16 @@ const NostoOrder: React.FC<{ order: OrderProps }> = ({ order }) => {
23
30
  .setPlacements(api.placements.getPlacements())
24
31
  .load()
25
32
  .then((data: object) => {
26
- if (responseMode == "HTML") {
27
- // @ts-ignore
28
- api.placements.injectCampaigns(data.recommendations);
29
- } else {
30
- // @ts-ignore
31
- renderFunction(data.campaigns);
32
- }
33
+ renderCampaigns(data, api);
33
34
  });
34
35
  });
35
36
  }
36
- }, [clientScriptLoaded, currentVariation, renderFunction]);
37
+ }, [
38
+ clientScriptLoaded,
39
+ currentVariation,
40
+ recommendationComponent,
41
+ pageTypeUpdated,
42
+ ]);
37
43
 
38
44
  return (
39
45
  <>
@@ -2,12 +2,19 @@ import React, { useEffect } from "react";
2
2
  import { useNostoContext } from "../Provider/context.client";
3
3
 
4
4
  const NostoOther: React.FC = () => {
5
- const { clientScriptLoaded, currentVariation, renderFunction, responseMode } =
6
- useNostoContext();
5
+ const {
6
+ clientScriptLoaded,
7
+ currentVariation,
8
+ responseMode,
9
+ recommendationComponent,
10
+ useRenderCampaigns,
11
+ } = useNostoContext();
12
+
13
+ const { renderCampaigns, pageTypeUpdated } = useRenderCampaigns("other");
7
14
 
8
15
  useEffect(() => {
9
16
  // @ts-ignore
10
- if (clientScriptLoaded) {
17
+ if (clientScriptLoaded && pageTypeUpdated) {
11
18
  window.nostojs((api: any) => {
12
19
  api
13
20
  .defaultSession()
@@ -17,17 +24,16 @@ const NostoOther: React.FC = () => {
17
24
  .setPlacements(api.placements.getPlacements())
18
25
  .load()
19
26
  .then((data: object) => {
20
- if (responseMode == "HTML") {
21
- // @ts-ignore
22
- api.placements.injectCampaigns(data.recommendations);
23
- } else {
24
- // @ts-ignore
25
- renderFunction(data.campaigns);
26
- }
27
+ renderCampaigns(data, api);
27
28
  });
28
29
  });
29
30
  }
30
- }, [clientScriptLoaded, currentVariation, renderFunction]);
31
+ }, [
32
+ clientScriptLoaded,
33
+ currentVariation,
34
+ recommendationComponent,
35
+ pageTypeUpdated,
36
+ ]);
31
37
 
32
38
  return (
33
39
  <>
@@ -2,10 +2,11 @@ import React from "react";
2
2
 
3
3
  export interface PlacementProps {
4
4
  id: string;
5
+ pageType: string;
5
6
  }
6
7
 
7
- const NostoPlacement: React.FC<PlacementProps> = ({ id }) => {
8
- return <div className="nosto_element" id={id} />;
8
+ const NostoPlacement: React.FC<PlacementProps> = ({ id, pageType }) => {
9
+ return <div className="nosto_element" id={id} key={id + pageType} />;
9
10
  };
10
11
 
11
12
  export default NostoPlacement;
@@ -6,12 +6,19 @@ const NostoProduct: React.FC<{ product: string; tagging: Product }> = ({
6
6
  product,
7
7
  tagging,
8
8
  }) => {
9
- const { clientScriptLoaded, currentVariation, renderFunction, responseMode } =
10
- useNostoContext();
9
+ const {
10
+ clientScriptLoaded,
11
+ currentVariation,
12
+ responseMode,
13
+ recommendationComponent,
14
+ useRenderCampaigns,
15
+ } = useNostoContext();
16
+
17
+ const { renderCampaigns, pageTypeUpdated } = useRenderCampaigns("product");
11
18
 
12
19
  useEffect(() => {
13
20
  // @ts-ignore
14
- if (clientScriptLoaded) {
21
+ if (clientScriptLoaded && pageTypeUpdated) {
15
22
  window.nostojs(
16
23
  (api: any) => {
17
24
  api
@@ -21,16 +28,16 @@ const NostoProduct: React.FC<{ product: string; tagging: Product }> = ({
21
28
  .setPlacements(api.placements.getPlacements())
22
29
  .load()
23
30
  .then((data: object) => {
24
- if (responseMode == "HTML") {
25
- // @ts-ignore
26
- api.placements.injectCampaigns(data.recommendations);
27
- } else {
28
- // @ts-ignore
29
- renderFunction(data.campaigns);
30
- }
31
+ renderCampaigns(data, api);
31
32
  });
32
33
  },
33
- [clientScriptLoaded, currentVariation, product, renderFunction]
34
+ [
35
+ clientScriptLoaded,
36
+ currentVariation,
37
+ product,
38
+ recommendationComponent,
39
+ pageTypeUpdated,
40
+ ]
34
41
  );
35
42
  }
36
43
  });
@@ -1,4 +1,4 @@
1
- import { createContext, useContext } from "react";
1
+ import { createContext, useContext, ReactComponentElement } from "react";
2
2
 
3
3
  export interface NostoInterface {
4
4
  account: string;
@@ -6,6 +6,9 @@ export interface NostoInterface {
6
6
  currentVariation: string;
7
7
  renderFunction?: Function;
8
8
  responseMode: string;
9
+ recommendationComponent?: any;
10
+ useRenderCampaigns: Function;
11
+ pageType: string;
9
12
  }
10
13
 
11
14
  /* tslint:disable:no-empty */
@@ -1,5 +1,6 @@
1
- import React, { useEffect } from "react";
1
+ import React, { useEffect, isValidElement, useState, useRef } from "react";
2
2
  import { NostoContext } from "./context.client";
3
+ import { createRoot } from "react-dom/client";
3
4
 
4
5
  interface NostoProviderProps {
5
6
  account: string;
@@ -7,7 +8,7 @@ interface NostoProviderProps {
7
8
  host: string;
8
9
  children: React.ReactElement;
9
10
  multiCurrency: boolean;
10
- renderFunction?: Function;
11
+ recommendationComponent?: any;
11
12
  }
12
13
 
13
14
  const NostoProvider: React.FC<NostoProviderProps> = ({
@@ -16,7 +17,7 @@ const NostoProvider: React.FC<NostoProviderProps> = ({
16
17
  multiCurrency = false,
17
18
  host,
18
19
  children,
19
- renderFunction,
20
+ recommendationComponent,
20
21
  }) => {
21
22
  const [clientScriptLoadedState, setClientScriptLoadedState] =
22
23
  React.useState(false);
@@ -25,13 +26,61 @@ const NostoProvider: React.FC<NostoProviderProps> = ({
25
26
  [clientScriptLoadedState]
26
27
  );
27
28
 
28
- //Set responseMode for loading campaigns:
29
- const responseMode =
30
- typeof renderFunction == "function" ? "JSON_ORIGINAL" : "HTML";
31
-
32
29
  //Pass currentVariation as empty string if multiCurrency is disabled
33
30
  currentVariation = multiCurrency ? currentVariation : "";
34
31
 
32
+ // Set responseMode for loading campaigns:
33
+ const responseMode = isValidElement(recommendationComponent)
34
+ ? "JSON_ORIGINAL"
35
+ : "HTML";
36
+
37
+ // RecommendationComponent for client-side rendering:
38
+ function RecommendationComponentWrapper(props: any) {
39
+ return React.cloneElement(recommendationComponent, {
40
+ nostoRecommendation: props.nostoRecommendation,
41
+ });
42
+ }
43
+
44
+ // custom hook for rendering campaigns (CSR/SSR):
45
+ const [pageType, setPageType] = useState("");
46
+ const useRenderCampaigns: any = function (type: string = "") {
47
+ const placementRefs: any = useRef({});
48
+ useEffect(() => {
49
+ if (pageType != type) {
50
+ setPageType(type);
51
+ }
52
+ }, []);
53
+
54
+ const pageTypeUpdated = type == pageType;
55
+
56
+ function renderCampaigns(data: any, api: any) {
57
+ if (responseMode == "HTML") {
58
+ // inject content campaigns as usual:
59
+ api.placements.injectCampaigns(data.recommendations);
60
+ } else {
61
+ // render recommendation component into placements:
62
+ const recommendations = data.campaigns.recommendations;
63
+ for (const key in recommendations) {
64
+ let recommendation = recommendations[key];
65
+ let placementSelector = "#" + key;
66
+ let placement: Function = () =>
67
+ document.querySelector(placementSelector);
68
+ if (!!placement()) {
69
+ if (!placementRefs.current[key])
70
+ placementRefs.current[key] = createRoot(placement());
71
+ const root = placementRefs.current[key];
72
+ root.render(
73
+ <RecommendationComponentWrapper
74
+ nostoRecommendation={recommendation}
75
+ ></RecommendationComponentWrapper>
76
+ );
77
+ }
78
+ }
79
+ }
80
+ }
81
+ return { renderCampaigns, pageTypeUpdated };
82
+ };
83
+
35
84
  useEffect(() => {
36
85
  if (!document.querySelectorAll("[nosto-client-script]").length) {
37
86
  const script = document.createElement("script");
@@ -58,8 +107,10 @@ const NostoProvider: React.FC<NostoProviderProps> = ({
58
107
  account,
59
108
  clientScriptLoaded,
60
109
  currentVariation,
61
- renderFunction,
62
110
  responseMode,
111
+ recommendationComponent,
112
+ useRenderCampaigns,
113
+ pageType,
63
114
  }}
64
115
  >
65
116
  {children}
@@ -2,12 +2,19 @@ import React, { useEffect } from "react";
2
2
  import { useNostoContext } from "../Provider/context.client";
3
3
 
4
4
  const NostoSearch: React.FC<{ query: string }> = ({ query }) => {
5
- const { clientScriptLoaded, currentVariation, renderFunction, responseMode } =
6
- useNostoContext();
5
+ const {
6
+ clientScriptLoaded,
7
+ currentVariation,
8
+ responseMode,
9
+ recommendationComponent,
10
+ useRenderCampaigns,
11
+ } = useNostoContext();
12
+
13
+ const { renderCampaigns, pageTypeUpdated } = useRenderCampaigns("search");
7
14
 
8
15
  useEffect(() => {
9
16
  // @ts-ignore
10
- if (clientScriptLoaded) {
17
+ if (clientScriptLoaded && pageTypeUpdated) {
11
18
  window.nostojs((api: any) => {
12
19
  api
13
20
  .defaultSession()
@@ -17,17 +24,17 @@ const NostoSearch: React.FC<{ query: string }> = ({ query }) => {
17
24
  .setPlacements(api.placements.getPlacements())
18
25
  .load()
19
26
  .then((data: object) => {
20
- if (responseMode == "HTML") {
21
- // @ts-ignore
22
- api.placements.injectCampaigns(data.recommendations);
23
- } else {
24
- // @ts-ignore
25
- renderFunction(data.campaigns);
26
- }
27
+ renderCampaigns(data, api);
27
28
  });
28
29
  });
29
30
  }
30
- }, [clientScriptLoaded, currentVariation, query, renderFunction]);
31
+ }, [
32
+ clientScriptLoaded,
33
+ currentVariation,
34
+ query,
35
+ recommendationComponent,
36
+ pageTypeUpdated,
37
+ ]);
31
38
 
32
39
  return (
33
40
  <>
@@ -13,7 +13,7 @@ interface NostoSessionProps {
13
13
  const NostoSession: React.FC<NostoSessionProps> = ({ cart, customer }) => {
14
14
  const { clientScriptLoaded } = useNostoContext();
15
15
  useDeepCompareEffect(() => {
16
- const currentCart = cart ? snakeize(cart) : undefined;
16
+ const currentCart = cart ? snakeize(cart) : undefined;
17
17
  const currentCustomer = customer ? snakeize(customer) : undefined;
18
18
 
19
19
  // @ts-ignore
@@ -34,4 +34,3 @@ export {
34
34
  } from "./components/Provider/context.client";
35
35
  // noinspection JSUnusedGlobalSymbols
36
36
  export { default as NostoSession } from "./components/Session/index.client";
37
- //