@envive-ai/react-hooks 0.1.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.
Files changed (385) hide show
  1. package/LICENSE +2 -0
  2. package/README.md +2 -0
  3. package/dist/GridInsertionService-CEYo9pGj.js +22 -0
  4. package/dist/GridInsertionService-CS_bnPh0.cjs +28 -0
  5. package/dist/bandolier-Ble8jEa8.js +1221 -0
  6. package/dist/bandolier-Bm2xAt_j.cjs +1221 -0
  7. package/dist/carpe-Da7b-LCW.cjs +599 -0
  8. package/dist/carpe-W13mhRRP.js +597 -0
  9. package/dist/cdnService-CZ-aXcY6.cjs +23 -0
  10. package/dist/cdnService-zQfKk3Eb.js +18 -0
  11. package/dist/chatElementDisplayLocation-CX8fuNao.d.cts +239 -0
  12. package/dist/chatElementDisplayLocation-CwptS9tx.d.ts +239 -0
  13. package/dist/chunk-CUT6urMc.cjs +30 -0
  14. package/dist/contexts/index.cjs +13 -0
  15. package/dist/contexts/index.d.cts +65 -0
  16. package/dist/contexts/index.d.ts +66 -0
  17. package/dist/contexts/index.js +7 -0
  18. package/dist/contexts-BRjfVq_k.js +5064 -0
  19. package/dist/contexts-BYArqZtK.cjs +5164 -0
  20. package/dist/coterie-3y0D9ko4.cjs +229 -0
  21. package/dist/coterie-DOWcJAYv.js +229 -0
  22. package/dist/custservice-types-CFIFwZ-r.js +10 -0
  23. package/dist/custservice-types-CkfxZiHY.cjs +16 -0
  24. package/dist/default-C2fEZKXk.js +175 -0
  25. package/dist/default-CBUq6Q6G.cjs +4 -0
  26. package/dist/default-CGIFZK6m.js +4 -0
  27. package/dist/default-D_KPZdPJ.cjs +198 -0
  28. package/dist/divIds-Bss-btao.js +49 -0
  29. package/dist/divIds-DnZNd7rA.cjs +223 -0
  30. package/dist/dreamlandBaby-DCIsuU9R.cjs +338 -0
  31. package/dist/dreamlandBaby-DvSaZGrz.js +338 -0
  32. package/dist/entrypoints-D_JUvkgy.cjs +18 -0
  33. package/dist/entrypoints-YLQsbBRD.js +6 -0
  34. package/dist/enviveConfigContext-CUGLpPGU.js +34 -0
  35. package/dist/enviveConfigContext-Dfr2VH6u.cjs +48 -0
  36. package/dist/fiveCbd-B1SESMCO.js +605 -0
  37. package/dist/fiveCbd-CkOlVby_.cjs +605 -0
  38. package/dist/forLoveAndLemons-CfYPMnKS.cjs +660 -0
  39. package/dist/forLoveAndLemons-DmwYZIk0.js +658 -0
  40. package/dist/greenpan-Bsl3ir59.cjs +389 -0
  41. package/dist/greenpan-BtOi45lf.js +389 -0
  42. package/dist/grooveLife-6_dtYsRk.js +334 -0
  43. package/dist/grooveLife-Cmm1PSCL.cjs +334 -0
  44. package/dist/homegrownCannabis-C-kw-74X.js +400 -0
  45. package/dist/homegrownCannabis-CO0uY_mp.cjs +400 -0
  46. package/dist/hooks/index.cjs +16 -0
  47. package/dist/hooks/index.d.cts +357 -0
  48. package/dist/hooks/index.d.ts +357 -0
  49. package/dist/hooks/index.js +7 -0
  50. package/dist/jackArcher-CLVmwwpI.js +719 -0
  51. package/dist/jackArcher-DdYTIzAV.cjs +719 -0
  52. package/dist/jordanCraig-Am-Oor-O.js +1778 -0
  53. package/dist/jordanCraig-_u3-w4Hp.cjs +1778 -0
  54. package/dist/kindredBravely-CWovIDSc.cjs +482 -0
  55. package/dist/kindredBravely-eWp-ud_E.js +482 -0
  56. package/dist/kutFromTheKloth-BMV4BuGQ.js +361 -0
  57. package/dist/kutFromTheKloth-Q589bAOC.cjs +361 -0
  58. package/dist/larryAndSerges-BMUlTgI-.js +252 -0
  59. package/dist/larryAndSerges-CEau764j.cjs +252 -0
  60. package/dist/leapsAndRebounds-DGMzPO7T.js +352 -0
  61. package/dist/leapsAndRebounds-DHAtRTJD.cjs +352 -0
  62. package/dist/logger-Dln20ans.cjs +25 -0
  63. package/dist/logger-pdEEY8T2.js +19 -0
  64. package/dist/longevityrx-CZW8Hxzi.cjs +312 -0
  65. package/dist/longevityrx-jH2JLhNH.js +312 -0
  66. package/dist/lookOptic-BGXP5P_V.js +274 -0
  67. package/dist/lookOptic-CA6RwLbG.cjs +274 -0
  68. package/dist/mantraBrand-Cm9_PBCT.js +742 -0
  69. package/dist/mantraBrand-DByNqpnL.cjs +742 -0
  70. package/dist/medterra-B0wxj_PV.js +575 -0
  71. package/dist/medterra-DnPN2ksU.cjs +575 -0
  72. package/dist/modells-Bmz8Ag5M.js +476 -0
  73. package/dist/modells-CoYgkLSp.cjs +476 -0
  74. package/dist/models-DHdb7QWn.js +51 -0
  75. package/dist/models-ixxUsGL_.cjs +69 -0
  76. package/dist/pressedFloral-DSKs_oVG.js +653 -0
  77. package/dist/pressedFloral-DjBiSoUl.cjs +653 -0
  78. package/dist/skinPerfection-B_3xzVNS.cjs +326 -0
  79. package/dist/skinPerfection-IDrBuAPt.js +326 -0
  80. package/dist/snapSupplements-BJk5T5ba.js +277 -0
  81. package/dist/snapSupplements-BStTsdOZ.cjs +277 -0
  82. package/dist/socialProofClasses-Bhv2Vulz.js +9 -0
  83. package/dist/socialProofClasses-CrQBWdSA.cjs +39 -0
  84. package/dist/spanx-BYg0LE7R.js +653 -0
  85. package/dist/spanx-LwU1zSzq.cjs +655 -0
  86. package/dist/spanxStaging-CfSmuKYB.js +837 -0
  87. package/dist/spanxStaging-OZLV9qix.cjs +840 -0
  88. package/dist/suggestionBarV2-types-BllzwsBD.js +34 -0
  89. package/dist/suggestionBarV2-types-CaovchMP.cjs +46 -0
  90. package/dist/supergoop-BqPXDnKk.cjs +327 -0
  91. package/dist/supergoop-CIlrHND_.js +325 -0
  92. package/dist/types-C4T5UOIW.cjs +230 -0
  93. package/dist/types-CYNvLeSA.js +176 -0
  94. package/dist/uniqueVintage-B30mOqbH.cjs +1205 -0
  95. package/dist/uniqueVintage-CFueJOhO.js +1203 -0
  96. package/dist/venaCbd-DHGZy49P.cjs +357 -0
  97. package/dist/venaCbd-T0CqVD4k.js +357 -0
  98. package/dist/westonJonBoucher-BdMzs_Yg.cjs +414 -0
  99. package/dist/westonJonBoucher-b4TCQ4ev.js +414 -0
  100. package/dist/wineEnthusiast-BLGlOjgr.cjs +932 -0
  101. package/dist/wineEnthusiast-BqR0i_54.js +932 -0
  102. package/dist/wolfMattress-CyyO-LoC.js +362 -0
  103. package/dist/wolfMattress-DNGZOivg.cjs +362 -0
  104. package/dist/wolfTactical-3Mm2fvVF.js +341 -0
  105. package/dist/wolfTactical-BmXYlFjr.cjs +341 -0
  106. package/package.json +66 -0
  107. package/src/adapters/amplitude/amplitudeAdapter.ts +454 -0
  108. package/src/adapters/amplitude/index.ts +2 -0
  109. package/src/adapters/amplitude/stubAmplitudeAdapter.ts +34 -0
  110. package/src/adapters/spiffy/commerce/api.ts +596 -0
  111. package/src/adapters/spiffy/commerce/exceptions/sessionExceptions.ts +6 -0
  112. package/src/adapters/spiffy/commerce/exceptions/unsupportedProductExceptions.ts +6 -0
  113. package/src/adapters/spiffy/commerce/graphql.ts +184 -0
  114. package/src/application/config/generalStaticConfig.ts +37 -0
  115. package/src/application/logging/logger.ts +29 -0
  116. package/src/application/models/api/context.ts +4 -0
  117. package/src/application/models/api/generationParams.ts +4 -0
  118. package/src/application/models/api/nextMessageRequest.ts +11 -0
  119. package/src/application/models/api/orgAnalyticsConfig.ts +19 -0
  120. package/src/application/models/api/orgConfigResults.ts +40 -0
  121. package/src/application/models/api/organizationConfig.ts +12 -0
  122. package/src/application/models/api/response.ts +132 -0
  123. package/src/application/models/api/responseGenerics.ts +67 -0
  124. package/src/application/models/api/search.ts +26 -0
  125. package/src/application/models/api/suggestion.ts +4 -0
  126. package/src/application/models/api/supportedEventRequest.ts +8 -0
  127. package/src/application/models/api/userEvent.ts +101 -0
  128. package/src/application/models/cachedValue.ts +8 -0
  129. package/src/application/models/chatElementDisplayLocation.ts +22 -0
  130. package/src/application/models/clientDetails.ts +18 -0
  131. package/src/application/models/colorsConfig.ts +28 -0
  132. package/src/application/models/conversationalSearchIds.ts +5 -0
  133. package/src/application/models/dataLayer.ts +45 -0
  134. package/src/application/models/domMutationContinuation.ts +7 -0
  135. package/src/application/models/domObservationStrategy.ts +9 -0
  136. package/src/application/models/events.ts +5 -0
  137. package/src/application/models/featureGates.ts +23 -0
  138. package/src/application/models/frontendConfig.ts +14 -0
  139. package/src/application/models/googleAnalyticsEvents.ts +8 -0
  140. package/src/application/models/graphql/index.ts +2 -0
  141. package/src/application/models/graphql/queries/getMerchantColorsQuery.ts +37 -0
  142. package/src/application/models/graphql/queries/getMerchantFrontendConfigQuery.ts +103 -0
  143. package/src/application/models/graphql/queries/getMerchantOrgIdQuery.ts +11 -0
  144. package/src/application/models/guards/api/index.ts +12 -0
  145. package/src/application/models/guards/api/isApiFormResponse.ts +90 -0
  146. package/src/application/models/guards/api/isApiFormSubmittedResponseAttributes.ts +37 -0
  147. package/src/application/models/guards/api/isApiOrderResponseAttributes.ts +155 -0
  148. package/src/application/models/guards/api/isApiOrgConfigResults.ts +277 -0
  149. package/src/application/models/guards/api/isApiOrganizationConfig.ts +207 -0
  150. package/src/application/models/guards/api/isApiPDPEventAttributes.ts +21 -0
  151. package/src/application/models/guards/api/isApiPLPEventAttributes.ts +41 -0
  152. package/src/application/models/guards/api/isApiPageResponseAttributes.ts +21 -0
  153. package/src/application/models/guards/api/isApiProductResponseAttributes.ts +85 -0
  154. package/src/application/models/guards/api/isApiProductSearchAttributes.ts +23 -0
  155. package/src/application/models/guards/api/isApiProductSearchFilterAttributes.ts +15 -0
  156. package/src/application/models/guards/api/isApiQueryTypedEventAttributes.ts +4 -0
  157. package/src/application/models/guards/api/isApiResponse.ts +39 -0
  158. package/src/application/models/guards/api/isApiReviewResponseAttributes.ts +30 -0
  159. package/src/application/models/guards/api/isApiReviewRichInformation.ts +37 -0
  160. package/src/application/models/guards/api/isApiSearchEventAttributes.ts +28 -0
  161. package/src/application/models/guards/api/isApiSuggestion.ts +36 -0
  162. package/src/application/models/guards/api/isApiSuggestionClickedEventAttributes.ts +9 -0
  163. package/src/application/models/guards/api/isApiTextResponseAttributes.ts +9 -0
  164. package/src/application/models/guards/api/isApiUserEvent.ts +25 -0
  165. package/src/application/models/guards/graphQL/isGraphQLColorsConfig.ts +50 -0
  166. package/src/application/models/guards/isBaseEcommerceEvent.ts +17 -0
  167. package/src/application/models/guards/isGA4EcommerceEvent.ts +17 -0
  168. package/src/application/models/guards/isLegacyUAEcommerceEvent.ts +17 -0
  169. package/src/application/models/guards/isMobilePLPChatPlacementParameter.ts +11 -0
  170. package/src/application/models/guards/isSpanxTakeAQuizCtaParameter.ts +4 -0
  171. package/src/application/models/guards/isVariantInfo.ts +37 -0
  172. package/src/application/models/guards/utils.ts +43 -0
  173. package/src/application/models/index.ts +20 -0
  174. package/src/application/models/localStorageEventListener.ts +4 -0
  175. package/src/application/models/message.ts +146 -0
  176. package/src/application/models/mobilePLPChatPlacementParameter.ts +3 -0
  177. package/src/application/models/orgsEnum.ts +36 -0
  178. package/src/application/models/productExperiment.ts +5 -0
  179. package/src/application/models/spanxTakeAQuizCtaParameter.ts +4 -0
  180. package/src/application/models/spiffyWidgets.ts +16 -0
  181. package/src/application/models/supportedOrgs.ts +137 -0
  182. package/src/application/models/utilityTypes/camelCase.ts +87 -0
  183. package/src/application/models/utilityTypes/camelCasedPropertiesDeep.ts +80 -0
  184. package/src/application/models/utilityTypes/delimiterCase.ts +121 -0
  185. package/src/application/models/utilityTypes/delimiterCasedPropertiesDeep.ts +98 -0
  186. package/src/application/models/utilityTypes/index.ts +1 -0
  187. package/src/application/models/utilityTypes/internal.ts +93 -0
  188. package/src/application/models/utilityTypes/primitive.ts +8 -0
  189. package/src/application/models/utilityTypes/snakeCasedPropertiesDeep.ts +49 -0
  190. package/src/application/models/utilityTypes/splitWords.ts +76 -0
  191. package/src/application/models/utilityTypes/trim.ts +28 -0
  192. package/src/application/models/utilityTypes/unknownArray.ts +25 -0
  193. package/src/application/models/utils/snakeToCamelTransformer.ts +90 -0
  194. package/src/application/models/utils/stringToFulfillmentDisplayStatusEnumValue.ts +68 -0
  195. package/src/application/models/validators/validateGraphQLColorsConfig.ts +29 -0
  196. package/src/application/models/validators/validateGraphQLFrontendConfig.ts +594 -0
  197. package/src/application/models/validators/validateGraphQLOrgId.ts +7 -0
  198. package/src/application/models/validators/validateMobilePLPChatPlacementParameter.ts +14 -0
  199. package/src/application/models/validators/validateOrgConfigResults.ts +47 -0
  200. package/src/application/models/validators/validateOrganizationConfig.ts +37 -0
  201. package/src/application/models/validators/validateResponse.ts +187 -0
  202. package/src/application/models/validators/validateSuggestion.ts +16 -0
  203. package/src/application/models/validators/validateUserEvent.ts +110 -0
  204. package/src/application/models/variantInfo/index.ts +1 -0
  205. package/src/application/models/variantInfo/pageVisitInfo.ts +6 -0
  206. package/src/application/models/variantInfo/plpInfo.ts +3 -0
  207. package/src/application/models/variantInfo/productInfo.ts +5 -0
  208. package/src/application/models/variantInfo/variantInfo.ts +23 -0
  209. package/src/application/service/cachingService.ts +84 -0
  210. package/src/application/service/cdnService.ts +18 -0
  211. package/src/application/service/customerService/index.ts +8 -0
  212. package/src/application/service/customerService/providers/UnsupportedCustomerService.ts +15 -0
  213. package/src/application/service/customerService/types.ts +31 -0
  214. package/src/application/service/domMutationObserver.ts +320 -0
  215. package/src/application/service/domMutations/GridInsertionService.ts +123 -0
  216. package/src/application/service/domMutations/dataLayer/dataLayerEventsListener.ts +99 -0
  217. package/src/application/service/domMutations/domInsertionService.ts +90 -0
  218. package/src/application/service/domMutations/domMutationListener.ts +15 -0
  219. package/src/application/service/domMutations/domMutationListenerState.ts +52 -0
  220. package/src/application/service/domMutations/floatingChat/embeddedChatsPlacementsListener.ts +41 -0
  221. package/src/application/service/domMutations/gladly/gladlyListener.ts +61 -0
  222. package/src/application/service/domMutations/spiffy/orgs/common/kustomerVisibilityListener.ts +41 -0
  223. package/src/application/service/domMutations/spiffy/orgs/common/orgsCommonDataLayerListener.ts +119 -0
  224. package/src/application/service/environmentService.ts +51 -0
  225. package/src/application/service/featureFlagService.ts +130 -0
  226. package/src/application/service/kustomerIntegrationService.ts +111 -0
  227. package/src/application/service/localStorageService.ts +77 -0
  228. package/src/application/service/pageVariantService.ts +779 -0
  229. package/src/application/service/searchService.ts +140 -0
  230. package/src/application/service/sessionStorageService.ts +27 -0
  231. package/src/application/service/shopifyUrlService.ts +63 -0
  232. package/src/application/service/userIdentityService.ts +114 -0
  233. package/src/application/service/windowChatToggleService.ts +71 -0
  234. package/src/application/service/windowDataLayerService.ts +181 -0
  235. package/src/application/service/windowFrontendConfigService.ts +104 -0
  236. package/src/application/utils/__tests__/divideArrays.test.ts +14 -0
  237. package/src/application/utils/analyticsUtils.ts +110 -0
  238. package/src/application/utils/coreContextToApiContext.ts +11 -0
  239. package/src/application/utils/coreUserEventToApiUserEvent.ts +106 -0
  240. package/src/application/utils/divideArray.ts +7 -0
  241. package/src/application/utils/domObserver.ts +96 -0
  242. package/src/application/utils/elementObserver.ts +246 -0
  243. package/src/application/utils/imageFilter.ts +12 -0
  244. package/src/application/utils/index.ts +3 -0
  245. package/src/application/utils/merchantUtils.ts +16 -0
  246. package/src/application/utils/messageFromFormSubmittedEvent.ts +31 -0
  247. package/src/application/utils/messageFromQueryEvent.ts +38 -0
  248. package/src/application/utils/messageFromResponse.ts +133 -0
  249. package/src/application/utils/messageFromSuggestionEvent.ts +32 -0
  250. package/src/application/utils/mouseEventTypes.ts +1 -0
  251. package/src/application/utils/mutationHelper.ts +51 -0
  252. package/src/application/utils/nextMessageRequestToApiRequest.ts +31 -0
  253. package/src/application/utils/nodeSelector.ts +133 -0
  254. package/src/application/utils/overrides.ts +196 -0
  255. package/src/application/utils/stringUtils.ts +55 -0
  256. package/src/application/utils/supportedEventRequestToApiRequest.ts +12 -0
  257. package/src/application/utils/urlsParser.ts +53 -0
  258. package/src/application/utils/validation.ts +5 -0
  259. package/src/atoms/app/index.ts +57 -0
  260. package/src/atoms/app/variant.ts +261 -0
  261. package/src/atoms/atomStore.ts +34 -0
  262. package/src/atoms/chat/chatState.ts +44 -0
  263. package/src/atoms/chat/form.ts +19 -0
  264. package/src/atoms/chat/index.ts +38 -0
  265. package/src/atoms/chat/lastMessage.ts +11 -0
  266. package/src/atoms/chat/messageQueue.ts +65 -0
  267. package/src/atoms/chat/performanceMetrics.ts +84 -0
  268. package/src/atoms/chat/renderedWidgetRefs.ts +28 -0
  269. package/src/atoms/chat/replies.ts +51 -0
  270. package/src/atoms/chat/suggestions.ts +36 -0
  271. package/src/atoms/globalSearch.ts +12 -0
  272. package/src/atoms/index.ts +5 -0
  273. package/src/atoms/org/customerService.ts +13 -0
  274. package/src/atoms/org/graphqlConfig.ts +27 -0
  275. package/src/atoms/org/index.ts +7 -0
  276. package/src/atoms/org/merchantCss.ts +44 -0
  277. package/src/atoms/org/org.ts +256 -0
  278. package/src/atoms/org/orgAnalyticsConfig.ts +28 -0
  279. package/src/atoms/org/orgPageConfig.ts +38 -0
  280. package/src/atoms/org/orgUIConfig.ts +122 -0
  281. package/src/atoms/search/chatSearch.ts +293 -0
  282. package/src/atoms/search/index.ts +2 -0
  283. package/src/atoms/search/productFilters.ts +207 -0
  284. package/src/atoms/search/productSorter.ts +23 -0
  285. package/src/atoms/search/searchAPI.ts +194 -0
  286. package/src/atoms/search/types.ts +55 -0
  287. package/src/atoms/search/utils.ts +18 -0
  288. package/src/config/divIds.ts +27 -0
  289. package/src/config/locators/components/chat/entrypoints.ts +13 -0
  290. package/src/config/locators/components/chat/index.ts +23 -0
  291. package/src/config/locators/components/chat/preview.ts +13 -0
  292. package/src/config/locators/components/chat/variants/index.ts +16 -0
  293. package/src/config/locators/components/common/buttons.ts +6 -0
  294. package/src/config/locators/components/common/cards.ts +18 -0
  295. package/src/config/locators/components/common/links.ts +1 -0
  296. package/src/config/locators/components/common/tables.ts +2 -0
  297. package/src/config/locators/components/floating-button.ts +2 -0
  298. package/src/config/locators/components/index.ts +3 -0
  299. package/src/config/locators/components/report-issue.ts +21 -0
  300. package/src/config/locators/components/search/index.ts +5 -0
  301. package/src/config/locators/components/shadow-dom.ts +1 -0
  302. package/src/config/locators/embedded.ts +21 -0
  303. package/src/config/locators/index.ts +3 -0
  304. package/src/config/socialProofClasses.ts +17 -0
  305. package/src/contexts/chatContext.tsx +451 -0
  306. package/src/contexts/enviveConfigContext.tsx +70 -0
  307. package/src/contexts/index.ts +4 -0
  308. package/src/contexts/systemSettingsContext.tsx +61 -0
  309. package/src/contexts/types.ts +1059 -0
  310. package/src/enabled-features.ts +83 -0
  311. package/src/events/event-types.ts +11 -0
  312. package/src/events/index.ts +52 -0
  313. package/src/events/registerAnalyticsListeners.ts +49 -0
  314. package/src/extension.ts +63 -0
  315. package/src/hooks/index.ts +22 -0
  316. package/src/hooks/useBlockBackButton.ts +29 -0
  317. package/src/hooks/useChatToggle.ts +66 -0
  318. package/src/hooks/useCustomerSupportHandoff.ts +39 -0
  319. package/src/hooks/useDebounce.ts +17 -0
  320. package/src/hooks/useDynamicVariants.ts +210 -0
  321. package/src/hooks/useElementObserver.ts +245 -0
  322. package/src/hooks/useFileUpload.ts +61 -0
  323. package/src/hooks/useGrabAndScroll.ts +133 -0
  324. package/src/hooks/useHideElements.ts +82 -0
  325. package/src/hooks/useHorizontalScrollAnimation.ts +115 -0
  326. package/src/hooks/useImageResolver.ts +51 -0
  327. package/src/hooks/useIntersection.ts +28 -0
  328. package/src/hooks/useIsSmallScreen.ts +23 -0
  329. package/src/hooks/useMessageFilter.ts +49 -0
  330. package/src/hooks/useMessageScrollObserver.ts +47 -0
  331. package/src/hooks/useReducedMotionWithOverride.ts +15 -0
  332. package/src/hooks/useSearch.tsx +433 -0
  333. package/src/hooks/useSnapCalculator.ts +38 -0
  334. package/src/hooks/useSnapControl.ts +155 -0
  335. package/src/hooks/useSystemSettingsContext.ts +12 -0
  336. package/src/hooks/useTrackComponentVisibleEvent.ts +52 -0
  337. package/src/hooks/useUpdateAnalyticsProps.ts +56 -0
  338. package/src/hooks/utils.ts +153 -0
  339. package/src/index.ts +31 -0
  340. package/src/initialize.ts +163 -0
  341. package/src/interceptors/types.ts +6 -0
  342. package/src/interceptors/useFormEscalation.ts +40 -0
  343. package/src/interceptors/useMessageInterceptor.ts +32 -0
  344. package/src/main.ts +85 -0
  345. package/src/main.tsx +123 -0
  346. package/src/merchants/bandolier/bandolier.ts +1389 -0
  347. package/src/merchants/carpe/carpe.ts +656 -0
  348. package/src/merchants/coterie/coterie.ts +280 -0
  349. package/src/merchants/default.ts +193 -0
  350. package/src/merchants/dreamlandBaby/dreamlandBaby.ts +375 -0
  351. package/src/merchants/fiveCbd/fiveCbd.ts +697 -0
  352. package/src/merchants/forLoveAndLemons/forLoveAndLemons.ts +721 -0
  353. package/src/merchants/greenpan/greenpan.ts +440 -0
  354. package/src/merchants/grooveLife/grooveLife.ts +386 -0
  355. package/src/merchants/homegrownCannabis/homegrownCannabis.ts +468 -0
  356. package/src/merchants/init-merchant.sh +53 -0
  357. package/src/merchants/jackArcher/jackArcher.ts +974 -0
  358. package/src/merchants/jordanCraig/jordanCraig.ts +1927 -0
  359. package/src/merchants/kindredBravely/kindredBravely.ts +529 -0
  360. package/src/merchants/kutFromTheKloth/kutFromTheKloth.ts +418 -0
  361. package/src/merchants/larryAndSerges/larryAndSerges.ts +314 -0
  362. package/src/merchants/leapsAndRebounds/leapsAndRebounds.ts +424 -0
  363. package/src/merchants/longevityrx/longevityrx.ts +368 -0
  364. package/src/merchants/lookOptic/lookOptic.ts +323 -0
  365. package/src/merchants/mantraBrand/mantraBrand.ts +838 -0
  366. package/src/merchants/medterra/medterra.ts +670 -0
  367. package/src/merchants/modells/modells.ts +546 -0
  368. package/src/merchants/pressedFloral/pressedFloral.ts +734 -0
  369. package/src/merchants/skinPerfection/skinPerfection.ts +379 -0
  370. package/src/merchants/snapSupplements/snapSupplements.ts +325 -0
  371. package/src/merchants/spanx/spanx.ts +810 -0
  372. package/src/merchants/spanx/spanxStaging.ts +942 -0
  373. package/src/merchants/supergoop/supergoop.ts +376 -0
  374. package/src/merchants/uniqueVintage/uniqueVintage.ts +1314 -0
  375. package/src/merchants/uniqueVintage/views/useUniqueVintageChatSearch.ts +147 -0
  376. package/src/merchants/venaCbd/venaCbd.ts +410 -0
  377. package/src/merchants/westonJonBoucher/westonJonBoucher.ts +473 -0
  378. package/src/merchants/wineEnthusiast/wineEnthusiast.ts +990 -0
  379. package/src/merchants/wolfMattress/wolfMattress.ts +411 -0
  380. package/src/merchants/wolfTactical/wolfTactical.ts +389 -0
  381. package/src/types/custservice-types.ts +28 -0
  382. package/src/types/search-filter-types.ts +111 -0
  383. package/src/types/suggestionBarV2-types.ts +4 -0
  384. package/src/types/test-types.ts +3 -0
  385. package/src/types.ts +66 -0
@@ -0,0 +1,83 @@
1
+ import Logger from 'src/application/logging/logger';
2
+ import { LocalStorageService } from 'src/application/service/localStorageService';
3
+ import { getOrgInfo } from 'src/application/models';
4
+ import { OrgConfig } from 'src/application/models/api/orgConfigResults';
5
+ import { FeatureFlagService } from 'src/application/service/featureFlagService';
6
+
7
+
8
+ type EnabledFeaturesValues = {
9
+ envive: boolean;
10
+ salesAgent: boolean;
11
+ };
12
+ class EnabledFeatures {
13
+ private static values: EnabledFeaturesValues | undefined = undefined;
14
+
15
+ static async get(
16
+ orgConfig: OrgConfig,
17
+ featureFlagService: FeatureFlagService,
18
+ ): Promise<EnabledFeaturesValues> {
19
+ if (this.values !== undefined) {
20
+ return this.values;
21
+ }
22
+
23
+ const isSpiffyEnabled = async (orgConfig: OrgConfig, featureFlagService: FeatureFlagService): Promise<boolean> => {
24
+ // "spiffy_on" is the highest precedence override and typically used for development/testing
25
+ // skip all other checks if it is "true"
26
+ if (LocalStorageService.getSpiffyOnFeatureFlag() === 'true') {
27
+ Logger.logDebug(`[spiffy-ai] spiffy enabled by "spiffy_on" override`);
28
+ return true;
29
+ }
30
+
31
+ if (LocalStorageService.getSpiffyOnFeatureFlag() === 'false') {
32
+ Logger.logDebug(`[spiffy-ai] envive disabled by "envive_on" override`);
33
+ return false;
34
+ }
35
+
36
+ if (window._spiffy?.show != null && !window._spiffy.show) {
37
+ Logger.logDebug(`[spiffy-ai] spiffy disabled by window._spiffy.show`);
38
+ return false;
39
+ }
40
+
41
+ if (!featureFlagService.isClientSessionEnabled()) {
42
+ Logger.logDebug(`[spiffy-ai] spiffy disabled by feature flag`);
43
+ return false;
44
+ }
45
+
46
+ // this is for when visibility is controlled by the merchant
47
+ if (window._spiffy?.show != null && window._spiffy.show) {
48
+ Logger.logDebug(`[spiffy-ai] spiffy enabled by window._spiffy.show`);
49
+ return true;
50
+ }
51
+
52
+ // otherwise, spiffy controls the visibility of the experience through a feature flag
53
+ const orgInfo = await getOrgInfo(orgConfig.org.org.shortName);
54
+ if (orgInfo.alwaysEnabledMerchants) {
55
+ Logger.logDebug(`[spiffy-ai] spiffy enabled by org config and feature flag`);
56
+ return true;
57
+ }
58
+
59
+ if (featureFlagService.isClientSessionEnabled()) {
60
+ Logger.logDebug(`[spiffy-ai] spiffy is enabled by feature flag`);
61
+ return true;
62
+ }
63
+
64
+ Logger.logDebug(`[spiffy-ai] spiffy is disabled because not all checks have passed`);
65
+ return false;
66
+ };
67
+
68
+ const isSalesAgentEnabled = isSpiffyEnabled;
69
+
70
+ this.values = {
71
+ envive: await isSpiffyEnabled(orgConfig, featureFlagService),
72
+ salesAgent: await isSalesAgentEnabled(orgConfig, featureFlagService),
73
+ };
74
+
75
+ return this.values;
76
+ }
77
+
78
+ static reset() {
79
+ this.values = undefined;
80
+ }
81
+ }
82
+
83
+ export { EnabledFeatures };
@@ -0,0 +1,11 @@
1
+ export enum SpiffyEvent {
2
+ AMPLITUDE_EVENT = 'SPIFFY_AMPLITUDE_EVENT',
3
+ APP_INITIALIZED = 'SPIFFY_APP_INITIALIZED',
4
+ CALL_MERCHANT_APP = 'SPIFFY_CALL_MERCHANT_APP',
5
+ MOUNTING_MERCHANT_APP = 'SPIFFY_MOUNTING_MERCHANT_APP',
6
+ CHAT_FACTORY_INITIALIZED = 'SPIFFY_CHAT_FACTORY_INITIALIZED',
7
+ FLOATING_CHAT_RENDERING = 'SPIFFY_FLOATING_CHAT_RENDERING',
8
+ FLOATING_CHAT_NOT_RENDERING = 'SPIFFY_FLOATING_CHAT_NOT_RENDERING',
9
+ FLOATING_CHAT_BUTTON_RENDERING = 'SPIFFY_FLOATING_CHAT_BUTTON_RENDERING',
10
+ SHADOW_DOM_CONTAINER_READY = 'SPIFFY_SHADOW_DOM_CONTAINER_READY',
11
+ }
@@ -0,0 +1,52 @@
1
+ export { SpiffyEvent } from './event-types';
2
+
3
+ /**
4
+ * A singleton class for dispatching Spiffy events to the document object.
5
+ * Provides functionality for adding default event data, managing listeners,
6
+ * and dispatching events.
7
+ *
8
+ * TODO: could add observability to the listeners, would need to track listeners
9
+ * internally.
10
+ */
11
+ export class EventsDispatcher {
12
+
13
+ static eventData: Record<string, any> = {};
14
+
15
+ /**
16
+ * Adds event data to the eventData object, which is included by default for
17
+ * every event dispatched. E.g. could add in userId, variantInfo, etc. But
18
+ * make sure it is JSON serializable.
19
+ */
20
+ static addEventData(data: Record<string, any>) {
21
+ this.eventData = { ...this.eventData, ...data };
22
+ }
23
+
24
+ /**
25
+ * Dispatches an event with the given name and detail. If includeDefault is
26
+ * false, the default event data is excluded.
27
+ * @param eventName - the name of the event to dispatch, should be all caps
28
+ * and start with SPIFFY_, e.g. "SPIFFY_FLOATING_CONTAINER_CREATED"
29
+ * @param detail - optional parameter to include extra event data
30
+ * @param includeDefault - optional parameter to exclude the default event
31
+ * data
32
+ */
33
+ static dispatch(eventName: string, detail?: Record<string, any>, includeDefault?: boolean) {
34
+ if (detail === undefined) {
35
+ detail = {};
36
+ }
37
+ if (includeDefault === undefined) {
38
+ includeDefault = true;
39
+ }
40
+ const eventData = includeDefault ? { ...this.eventData, ...detail } : detail;
41
+ const event = new CustomEvent(eventName, { detail: eventData, bubbles: true, composed: true, cancelable: false });
42
+ window.dispatchEvent(event);
43
+ }
44
+
45
+ static addListener(eventName: string, listener: (event: Event) => void) {
46
+ window.addEventListener(eventName, listener, { once: false, passive: false, capture: false });
47
+ }
48
+
49
+ static removeListener(eventName: string, listener: (event: Event) => void) {
50
+ window.removeEventListener(eventName, listener);
51
+ }
52
+ }
@@ -0,0 +1,49 @@
1
+ import { AmplitudeAdapter, SpiffyMetricsEventName } from 'src/adapters/amplitude';
2
+ import { getAtomStore } from 'src/atoms/atomStore';
3
+ import {
4
+ pageLoadOffsetTimeAtom,
5
+ performanceMetricsAtom,
6
+ resetPerformanceMetricsAtom,
7
+ } from 'src/atoms/chat';
8
+
9
+ const registerPerformanceMetricsListener = () => {
10
+ const atomStore = getAtomStore();
11
+ const performanceMetrics = atomStore.get(performanceMetricsAtom);
12
+ const pageLoadOffsetTime = atomStore.get(pageLoadOffsetTimeAtom);
13
+
14
+ const handleVisibilityChange = () => {
15
+ if (document.visibilityState === 'hidden' && performanceMetrics.size > 0) {
16
+ const perfMetricsProperties = Object.fromEntries(
17
+ Array.from(performanceMetrics.entries()).map(([key, value]) => [
18
+ `performanceMetrics.${key}`,
19
+ value,
20
+ ]),
21
+ );
22
+
23
+ AmplitudeAdapter.trackEvent({
24
+ eventName: SpiffyMetricsEventName.PerformanceMetrics,
25
+ eventProps: {
26
+ ...perfMetricsProperties,
27
+ 'performance_metrics.page_load_offset_ms': pageLoadOffsetTime,
28
+ },
29
+ });
30
+ resetPerformanceMetricsAtom();
31
+ }
32
+ };
33
+
34
+ // do not use anything other than visibilitychange to detect when a page or
35
+ // session has ended as other events may not fire in all browsers/devices
36
+ window.addEventListener('visibilitychange', handleVisibilityChange);
37
+
38
+ return () => {
39
+ window.removeEventListener('visibilitychange', handleVisibilityChange);
40
+ };
41
+ };
42
+
43
+ export const registerAnalyticsListeners = () => {
44
+ const cleanupPerformanceMetricsListener = registerPerformanceMetricsListener();
45
+
46
+ return () => {
47
+ cleanupPerformanceMetricsListener();
48
+ };
49
+ };
@@ -0,0 +1,63 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ import Logger from './application/logging/logger';
4
+
5
+ export type ExtensionInfo = {
6
+ isLoading: boolean;
7
+ isInjected: boolean;
8
+ error?: string | null;
9
+ };
10
+
11
+ enum MessageTypes {
12
+ ExtensionInfoUpdate = 'spiffy-extension-info-update',
13
+ }
14
+
15
+ export const EXTENSION_ID = 'ohickcjdalobhoohmnpjnmgaalphmdje';
16
+
17
+ const sendMessageToExtensionHost = (type: MessageTypes, payload: ExtensionInfo) => {
18
+ if (!window.chrome?.runtime) {
19
+ return Logger.logDebug(
20
+ '[spiffy-ai] Extension runtime is not exposed, so not sending message',
21
+ undefined,
22
+ );
23
+ }
24
+
25
+ return window.chrome.runtime
26
+ .sendMessage(EXTENSION_ID, { msg: type, ...payload })
27
+ .then((_response: any) => {
28
+ if (window.chrome.runtime.lastError) {
29
+ Logger.logError(window.chrome.runtime.lastError, undefined);
30
+ } else {
31
+ Logger.logDebug('[spiffy-ai] Received response from extension host');
32
+ }
33
+ })
34
+ .catch(() => {
35
+ Logger.logDebug('[spiffy-ai] Unable to log extension message', type, payload);
36
+ });
37
+ };
38
+
39
+ export const tellExtensionInjectionIsLoading = () => {
40
+ return sendMessageToExtensionHost(MessageTypes.ExtensionInfoUpdate, {
41
+ isLoading: true,
42
+ isInjected: false,
43
+ error: null,
44
+ });
45
+ };
46
+
47
+ // This is being used in the dynamic script we build and run in the browser.
48
+ export const tellExtensionAboutInjectionError = (error: Error) => {
49
+ return sendMessageToExtensionHost(MessageTypes.ExtensionInfoUpdate, {
50
+ isLoading: false,
51
+ isInjected: false,
52
+ error: error.message,
53
+ });
54
+ };
55
+
56
+ // This is being used in the dynamic script we build and run in the browser.
57
+ export const tellExtensionAboutSuccessfullInjection = () => {
58
+ return sendMessageToExtensionHost(MessageTypes.ExtensionInfoUpdate, {
59
+ isLoading: false,
60
+ isInjected: true,
61
+ error: null,
62
+ });
63
+ };
@@ -0,0 +1,22 @@
1
+ export * from "./useBlockBackButton";
2
+ export * from "./useChatToggle";
3
+ export * from "./useCustomerSupportHandoff";
4
+ export * from "./useDebounce";
5
+ export * from "./useDynamicVariants";
6
+ export * from "./useElementObserver";
7
+ export * from "./useFileUpload";
8
+ export * from "./useGrabAndScroll";
9
+ export * from "./useHideElements";
10
+ export * from "./useHorizontalScrollAnimation";
11
+ export * from "./useImageResolver";
12
+ export * from "./useIntersection";
13
+ export * from "./useIsSmallScreen";
14
+ export * from "./useMessageFilter";
15
+ export * from "./useMessageScrollObserver";
16
+ export * from "./useReducedMotionWithOverride";
17
+ export * from "./useSearch";
18
+ export * from "./useSnapCalculator";
19
+ export * from "./useSnapControl";
20
+ export * from "./useSystemSettingsContext";
21
+ export * from "./useTrackComponentVisibleEvent";
22
+ export * from "./useUpdateAnalyticsProps";
@@ -0,0 +1,29 @@
1
+ import { useEffect } from "react";
2
+
3
+ export const useBlockBackButton = (enabled: boolean, callback: () => void) => {
4
+
5
+ useEffect(() => {
6
+ if (enabled && window) {
7
+
8
+ if (window.history.scrollRestoration) {
9
+ window.history.scrollRestoration = "manual"
10
+ }
11
+
12
+ window.history.pushState(null, document.title, window.location.href);
13
+ window.onpopstate = (e) => {
14
+ e.preventDefault();
15
+ window.history.pushState(null, document.title, window.location.href);
16
+ callback?.()
17
+ };
18
+ }
19
+
20
+ return () => {
21
+ if (enabled && window) {
22
+ window.history.back()
23
+ window.onpopstate = null
24
+ window.history.scrollRestoration = "auto";
25
+ }
26
+ }
27
+ }, [enabled])
28
+
29
+ }
@@ -0,0 +1,66 @@
1
+ import { useAtomValue, useSetAtom } from "jotai";
2
+ import { ChatElementDisplayLocation } from "src/application/models/chatElementDisplayLocation";
3
+ import { chatAtom, chatOnToggleAtom } from "src/atoms/chat";
4
+ import {
5
+ AmplitudeAdapter,
6
+ SpiffyMetricsEventName,
7
+ } from "src/adapters/amplitude";
8
+
9
+ export const useChatToggle = () => {
10
+ const onToggle = useSetAtom(chatOnToggleAtom);
11
+ const { isOpen } = useAtomValue(chatAtom);
12
+
13
+ const toggle = (
14
+ triggerLocation: ChatElementDisplayLocation,
15
+ triggerId?: string
16
+ ) => {
17
+ if (!isOpen) {
18
+ AmplitudeAdapter.trackEvent({
19
+ eventName: SpiffyMetricsEventName.ChatComponentExpanded,
20
+ eventProps: {
21
+ message_metadata: {
22
+ trigger_location: triggerLocation,
23
+ trigger_id: triggerId,
24
+ },
25
+ },
26
+ });
27
+ } else {
28
+ AmplitudeAdapter.trackEvent({
29
+ eventName: SpiffyMetricsEventName.ChatComponentCollapsed,
30
+ eventProps: {
31
+ message_metadata: {
32
+ trigger_location: triggerLocation,
33
+ trigger_id: triggerId,
34
+ },
35
+ },
36
+ });
37
+ }
38
+
39
+ onToggle();
40
+ };
41
+
42
+ const openChat = (
43
+ triggerLocation: ChatElementDisplayLocation,
44
+ triggerId?: string
45
+ ) => {
46
+ if (!isOpen) {
47
+ toggle(triggerLocation, triggerId);
48
+ }
49
+ };
50
+
51
+ const closeChat = (
52
+ triggerLocation: ChatElementDisplayLocation,
53
+ triggerId?: string
54
+ ) => {
55
+ if (isOpen) {
56
+ toggle(triggerLocation, triggerId);
57
+ }
58
+ };
59
+
60
+ return {
61
+ toggle,
62
+ isOpen,
63
+ openChat,
64
+ closeChat,
65
+ };
66
+ };
@@ -0,0 +1,39 @@
1
+ import Logger from 'src/application/logging/logger';
2
+ import { useCallback } from 'react';
3
+
4
+ /**
5
+ * Hook to call the `click` method of the merchant's customer support chat widget.
6
+ *
7
+ * @param onSwitchToAgent a function to override the function returned by the hook. This is mainly to
8
+ * preserve backward compatibility for merchants not using Kustomer and will be removed when all
9
+ * CS integrations are handled.
10
+ *
11
+ * @returns a function that searches for the customer support chat widget and calls the `click` method.
12
+ */
13
+ export const useCustomerSupportHandoff = (onSwitchToAgent?: () => void) => {
14
+ // TODO handle Gorgias
15
+
16
+ const onKustomerSwitch = useCallback(() => {
17
+ const kustomerElement = document.getElementById('kustomer-ui-sdk-iframe');
18
+
19
+ if (kustomerElement == null || !(kustomerElement instanceof HTMLIFrameElement)) {
20
+ Logger.logError('[spiffy-ai] Kustomer iFrame element not found', undefined);
21
+ return;
22
+ }
23
+
24
+ const kustomerButton = kustomerElement.contentWindow?.document?.getElementById('rootChatIcon');
25
+
26
+ if (kustomerButton == null) {
27
+ Logger.logError('[spiffy-ai] Kustomer button not found', undefined);
28
+ return;
29
+ }
30
+
31
+ kustomerButton.click();
32
+ }, []);
33
+
34
+ if (onSwitchToAgent != null) {
35
+ return { onSwitch: onSwitchToAgent };
36
+ }
37
+
38
+ return { onSwitch: onKustomerSwitch };
39
+ };
@@ -0,0 +1,17 @@
1
+ import { useState, useEffect } from 'react';
2
+
3
+ export function useDebounce<T>(value: T, delay: number): T {
4
+ const [debouncedValue, setDebouncedValue] = useState<T>(value);
5
+
6
+ useEffect(() => {
7
+ const handler = setTimeout(() => {
8
+ setDebouncedValue(value);
9
+ }, delay);
10
+
11
+ return () => {
12
+ clearTimeout(handler);
13
+ };
14
+ }, [value, delay]);
15
+
16
+ return debouncedValue;
17
+ }
@@ -0,0 +1,210 @@
1
+ import Logger from 'src/application/logging/logger';
2
+ import { useEffect, useRef, useCallback, useState } from 'react';
3
+ import { useAtom, useAtomValue, useSetAtom } from 'jotai';
4
+ import { variantInfoAtom } from 'src/atoms/app';
5
+ import { PageVariantService } from 'src/application/service/pageVariantService';
6
+ import { orgPageConfigAtom } from 'src/atoms/org/orgPageConfig';
7
+ import { hasParsedVariantInfoAtom, supportedEventAtom } from 'src/atoms/app/variant';
8
+ import { SpiffyMetricsEventName, AmplitudeAdapter } from 'src/adapters/amplitude';
9
+ import { orgUIConfigAtom } from 'src/atoms/org/orgUIConfig';
10
+ import { createAppLoadedEvent, createVisitUserEvent } from 'src/hooks/utils';
11
+ import { queueUserEventAtom } from 'src/atoms/chat/messageQueue';
12
+ import { UserEvent } from 'src/application/models';
13
+ import { UserEventCategory } from '@spiffy-ai/commerce-api-client';
14
+
15
+ // consider putting this in the org config
16
+ const DEBOUNCE_DELAY_MS = 300;
17
+
18
+ // a debounced function that sets the location href to prevent rapidly changing urls from
19
+ // causing the variant info to be changed too early
20
+ const debounce = (func: () => void, delay: number): (() => void) => {
21
+ let timeoutId: NodeJS.Timeout;
22
+
23
+ return () => {
24
+ clearTimeout(timeoutId);
25
+ timeoutId = setTimeout(() => func(), delay);
26
+ };
27
+ };
28
+
29
+ const isUserEventEqual = (event1: UserEvent | null, event2: UserEvent | null) => {
30
+ if (!event1 || !event2) {
31
+ return false;
32
+ }
33
+ if (event1.category !== event2.category) {
34
+ return false;
35
+ }
36
+ if (
37
+ event1.category === UserEventCategory.Search &&
38
+ event2.category === UserEventCategory.Search
39
+ ) {
40
+ return event1.attributes.searchTerm === event2.attributes.searchTerm;
41
+ }
42
+ if (
43
+ event1.category === UserEventCategory.PdpVisit &&
44
+ event2.category === UserEventCategory.PdpVisit
45
+ ) {
46
+ return event1.attributes.productId === event2.attributes.productId;
47
+ }
48
+ if (
49
+ event1.category === UserEventCategory.PlpVisit &&
50
+ event2.category === UserEventCategory.PlpVisit &&
51
+ 'id' in event1.attributes.attributes &&
52
+ 'id' in event2.attributes.attributes
53
+ ) {
54
+ return event1.attributes.attributes.id === event2.attributes.attributes.id;
55
+ }
56
+ if (
57
+ event1.category === UserEventCategory.PageVisit &&
58
+ event2.category === UserEventCategory.PageVisit
59
+ ) {
60
+ return event1.attributes.url === event2.attributes.url;
61
+ }
62
+ return false;
63
+ };
64
+
65
+ export const useDynamicVariants = () => {
66
+ const orgUIConfig = useAtomValue(orgUIConfigAtom);
67
+ const locationHrefRef = useRef<string>(window.location.href);
68
+ const setVariantInfo = useSetAtom(variantInfoAtom);
69
+ const setSupportedEvent = useSetAtom(supportedEventAtom);
70
+ const queueUserEvent = useSetAtom(queueUserEventAtom);
71
+ const [mostRecentUserEvent, setMostRecentUserEvent] = useState<UserEvent | null>(null);
72
+
73
+ const orgPageConfig = useAtomValue(orgPageConfigAtom);
74
+ const debouncedFunc = useRef<(() => void) | null>(null);
75
+ // we need to fetch org info before we can get the variant info. this prevents the rest of the application
76
+ // logic from running before that happens. More specifically, we want to have all org and variant states
77
+ // settled before mounting the ChatContextProvider which will start the session.
78
+ const [hasParsedVariantInfo, setHasParsedVariantInfo] = useAtom(hasParsedVariantInfoAtom);
79
+ const animationFrameRef = useRef<number | null>(null);
80
+ const isBeforeUnload = useRef(false);
81
+
82
+ const handleLocationChange = () => {
83
+ if (animationFrameRef.current) {
84
+ cancelAnimationFrame(animationFrameRef.current);
85
+ }
86
+
87
+ animationFrameRef.current = requestAnimationFrame(() => {
88
+ if (locationHrefRef.current !== window.location.href) {
89
+ debouncedFunc.current?.();
90
+ }
91
+ });
92
+ };
93
+
94
+ const handlePageChange = useCallback(async () => {
95
+ if (isBeforeUnload.current) {
96
+ return;
97
+ }
98
+
99
+ if (!orgPageConfig) {
100
+ Logger.logWarn(
101
+ '[spiffy-ai] orgPageConfig not found. skipping page change handling',
102
+ undefined,
103
+ );
104
+ return;
105
+ }
106
+
107
+ const parsedVariantInfo = await PageVariantService.getVariantInfoWithPageConfig(orgPageConfig);
108
+
109
+ if (parsedVariantInfo) {
110
+ Logger.logDebug('[spiffy-ai] variantInfo changed', parsedVariantInfo);
111
+ const [newVariantInfo, pageVariantConfig] = parsedVariantInfo;
112
+
113
+ setVariantInfo(newVariantInfo);
114
+ setHasParsedVariantInfo(true);
115
+
116
+ const visitEvent = createVisitUserEvent({ variantInfo: newVariantInfo });
117
+ if (visitEvent && !isUserEventEqual(mostRecentUserEvent, visitEvent)) {
118
+ setMostRecentUserEvent(visitEvent);
119
+ queueUserEvent(createAppLoadedEvent());
120
+ queueUserEvent(visitEvent);
121
+ }
122
+
123
+ const supportedEvent = await PageVariantService.checkSupportedEvent(pageVariantConfig);
124
+ console.log('useDynamicVariants - supportedEvent', supportedEvent);
125
+ setSupportedEvent(supportedEvent);
126
+ AmplitudeAdapter.trackEvent({
127
+ eventName: SpiffyMetricsEventName.SupportedEvent,
128
+ eventProps: {
129
+ ...supportedEvent,
130
+ },
131
+ });
132
+ } else {
133
+ setHasParsedVariantInfo(false);
134
+ }
135
+ }, [
136
+ orgPageConfig,
137
+ setVariantInfo,
138
+ setHasParsedVariantInfo,
139
+ setSupportedEvent,
140
+ queueUserEvent,
141
+ mostRecentUserEvent,
142
+ setMostRecentUserEvent,
143
+ ]);
144
+
145
+ useEffect(() => {
146
+ debouncedFunc.current = debounce(() => {
147
+ locationHrefRef.current = window.location.href;
148
+ handlePageChange();
149
+ }, DEBOUNCE_DELAY_MS);
150
+
151
+ return () => {
152
+ debouncedFunc.current = null;
153
+ };
154
+ }, [handlePageChange]);
155
+
156
+ useEffect(() => {
157
+ // when navigation happens, the url is updated before the page is unloaded & reloaded
158
+ // this causes the variant info to be changed first and triggering the hook that fetches
159
+ // next responses. On non-SPA's, this handler prevents variant info from being updated
160
+ // and lets the usual initialization sequence update it instead. On SPA's, this is not called
161
+ // and handleLocationChange is called instead
162
+ const handleBeforeUnload = () => {
163
+ setSupportedEvent(undefined);
164
+ isBeforeUnload.current = true;
165
+ };
166
+
167
+ window.addEventListener('click', handleLocationChange);
168
+ window.addEventListener('popstate', handleLocationChange);
169
+ window.addEventListener('beforeunload', handleBeforeUnload);
170
+
171
+ return () => {
172
+ window.removeEventListener('click', handleLocationChange);
173
+ window.removeEventListener('popstate', handleLocationChange);
174
+ window.removeEventListener('beforeunload', handleBeforeUnload);
175
+ // TODO: this is causing the supportedEvent to be undefined for Magento customers
176
+ // Why is unclear however...
177
+ // setSupportedEvent(undefined);
178
+
179
+ if (animationFrameRef.current) {
180
+ cancelAnimationFrame(animationFrameRef.current);
181
+ }
182
+ };
183
+ }, [handlePageChange, setSupportedEvent]);
184
+
185
+ useEffect(() => {
186
+ // handle the initial page load and when the variant info is overridden
187
+ if (!hasParsedVariantInfo) {
188
+ handlePageChange();
189
+ }
190
+ }, [hasParsedVariantInfo, handlePageChange]);
191
+
192
+ useEffect(() => {
193
+ let windowInterval: number | null = null;
194
+ const { merchantVariantSettings } = orgUIConfig;
195
+
196
+ if (merchantVariantSettings?.dynamicVariants?.enabled) {
197
+ const refreshInterval = merchantVariantSettings?.dynamicVariants?.refreshInterval;
198
+ windowInterval = window.setInterval(() => {
199
+ handleLocationChange();
200
+ }, refreshInterval);
201
+ }
202
+
203
+ return () => {
204
+ if (windowInterval) {
205
+ window.clearInterval(windowInterval);
206
+ }
207
+ };
208
+ // eslint-disable-next-line react-hooks/exhaustive-deps
209
+ }, []);
210
+ };