@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.
- package/LICENSE +2 -0
- package/README.md +2 -0
- package/dist/GridInsertionService-CEYo9pGj.js +22 -0
- package/dist/GridInsertionService-CS_bnPh0.cjs +28 -0
- package/dist/bandolier-Ble8jEa8.js +1221 -0
- package/dist/bandolier-Bm2xAt_j.cjs +1221 -0
- package/dist/carpe-Da7b-LCW.cjs +599 -0
- package/dist/carpe-W13mhRRP.js +597 -0
- package/dist/cdnService-CZ-aXcY6.cjs +23 -0
- package/dist/cdnService-zQfKk3Eb.js +18 -0
- package/dist/chatElementDisplayLocation-CX8fuNao.d.cts +239 -0
- package/dist/chatElementDisplayLocation-CwptS9tx.d.ts +239 -0
- package/dist/chunk-CUT6urMc.cjs +30 -0
- package/dist/contexts/index.cjs +13 -0
- package/dist/contexts/index.d.cts +65 -0
- package/dist/contexts/index.d.ts +66 -0
- package/dist/contexts/index.js +7 -0
- package/dist/contexts-BRjfVq_k.js +5064 -0
- package/dist/contexts-BYArqZtK.cjs +5164 -0
- package/dist/coterie-3y0D9ko4.cjs +229 -0
- package/dist/coterie-DOWcJAYv.js +229 -0
- package/dist/custservice-types-CFIFwZ-r.js +10 -0
- package/dist/custservice-types-CkfxZiHY.cjs +16 -0
- package/dist/default-C2fEZKXk.js +175 -0
- package/dist/default-CBUq6Q6G.cjs +4 -0
- package/dist/default-CGIFZK6m.js +4 -0
- package/dist/default-D_KPZdPJ.cjs +198 -0
- package/dist/divIds-Bss-btao.js +49 -0
- package/dist/divIds-DnZNd7rA.cjs +223 -0
- package/dist/dreamlandBaby-DCIsuU9R.cjs +338 -0
- package/dist/dreamlandBaby-DvSaZGrz.js +338 -0
- package/dist/entrypoints-D_JUvkgy.cjs +18 -0
- package/dist/entrypoints-YLQsbBRD.js +6 -0
- package/dist/enviveConfigContext-CUGLpPGU.js +34 -0
- package/dist/enviveConfigContext-Dfr2VH6u.cjs +48 -0
- package/dist/fiveCbd-B1SESMCO.js +605 -0
- package/dist/fiveCbd-CkOlVby_.cjs +605 -0
- package/dist/forLoveAndLemons-CfYPMnKS.cjs +660 -0
- package/dist/forLoveAndLemons-DmwYZIk0.js +658 -0
- package/dist/greenpan-Bsl3ir59.cjs +389 -0
- package/dist/greenpan-BtOi45lf.js +389 -0
- package/dist/grooveLife-6_dtYsRk.js +334 -0
- package/dist/grooveLife-Cmm1PSCL.cjs +334 -0
- package/dist/homegrownCannabis-C-kw-74X.js +400 -0
- package/dist/homegrownCannabis-CO0uY_mp.cjs +400 -0
- package/dist/hooks/index.cjs +16 -0
- package/dist/hooks/index.d.cts +357 -0
- package/dist/hooks/index.d.ts +357 -0
- package/dist/hooks/index.js +7 -0
- package/dist/jackArcher-CLVmwwpI.js +719 -0
- package/dist/jackArcher-DdYTIzAV.cjs +719 -0
- package/dist/jordanCraig-Am-Oor-O.js +1778 -0
- package/dist/jordanCraig-_u3-w4Hp.cjs +1778 -0
- package/dist/kindredBravely-CWovIDSc.cjs +482 -0
- package/dist/kindredBravely-eWp-ud_E.js +482 -0
- package/dist/kutFromTheKloth-BMV4BuGQ.js +361 -0
- package/dist/kutFromTheKloth-Q589bAOC.cjs +361 -0
- package/dist/larryAndSerges-BMUlTgI-.js +252 -0
- package/dist/larryAndSerges-CEau764j.cjs +252 -0
- package/dist/leapsAndRebounds-DGMzPO7T.js +352 -0
- package/dist/leapsAndRebounds-DHAtRTJD.cjs +352 -0
- package/dist/logger-Dln20ans.cjs +25 -0
- package/dist/logger-pdEEY8T2.js +19 -0
- package/dist/longevityrx-CZW8Hxzi.cjs +312 -0
- package/dist/longevityrx-jH2JLhNH.js +312 -0
- package/dist/lookOptic-BGXP5P_V.js +274 -0
- package/dist/lookOptic-CA6RwLbG.cjs +274 -0
- package/dist/mantraBrand-Cm9_PBCT.js +742 -0
- package/dist/mantraBrand-DByNqpnL.cjs +742 -0
- package/dist/medterra-B0wxj_PV.js +575 -0
- package/dist/medterra-DnPN2ksU.cjs +575 -0
- package/dist/modells-Bmz8Ag5M.js +476 -0
- package/dist/modells-CoYgkLSp.cjs +476 -0
- package/dist/models-DHdb7QWn.js +51 -0
- package/dist/models-ixxUsGL_.cjs +69 -0
- package/dist/pressedFloral-DSKs_oVG.js +653 -0
- package/dist/pressedFloral-DjBiSoUl.cjs +653 -0
- package/dist/skinPerfection-B_3xzVNS.cjs +326 -0
- package/dist/skinPerfection-IDrBuAPt.js +326 -0
- package/dist/snapSupplements-BJk5T5ba.js +277 -0
- package/dist/snapSupplements-BStTsdOZ.cjs +277 -0
- package/dist/socialProofClasses-Bhv2Vulz.js +9 -0
- package/dist/socialProofClasses-CrQBWdSA.cjs +39 -0
- package/dist/spanx-BYg0LE7R.js +653 -0
- package/dist/spanx-LwU1zSzq.cjs +655 -0
- package/dist/spanxStaging-CfSmuKYB.js +837 -0
- package/dist/spanxStaging-OZLV9qix.cjs +840 -0
- package/dist/suggestionBarV2-types-BllzwsBD.js +34 -0
- package/dist/suggestionBarV2-types-CaovchMP.cjs +46 -0
- package/dist/supergoop-BqPXDnKk.cjs +327 -0
- package/dist/supergoop-CIlrHND_.js +325 -0
- package/dist/types-C4T5UOIW.cjs +230 -0
- package/dist/types-CYNvLeSA.js +176 -0
- package/dist/uniqueVintage-B30mOqbH.cjs +1205 -0
- package/dist/uniqueVintage-CFueJOhO.js +1203 -0
- package/dist/venaCbd-DHGZy49P.cjs +357 -0
- package/dist/venaCbd-T0CqVD4k.js +357 -0
- package/dist/westonJonBoucher-BdMzs_Yg.cjs +414 -0
- package/dist/westonJonBoucher-b4TCQ4ev.js +414 -0
- package/dist/wineEnthusiast-BLGlOjgr.cjs +932 -0
- package/dist/wineEnthusiast-BqR0i_54.js +932 -0
- package/dist/wolfMattress-CyyO-LoC.js +362 -0
- package/dist/wolfMattress-DNGZOivg.cjs +362 -0
- package/dist/wolfTactical-3Mm2fvVF.js +341 -0
- package/dist/wolfTactical-BmXYlFjr.cjs +341 -0
- package/package.json +66 -0
- package/src/adapters/amplitude/amplitudeAdapter.ts +454 -0
- package/src/adapters/amplitude/index.ts +2 -0
- package/src/adapters/amplitude/stubAmplitudeAdapter.ts +34 -0
- package/src/adapters/spiffy/commerce/api.ts +596 -0
- package/src/adapters/spiffy/commerce/exceptions/sessionExceptions.ts +6 -0
- package/src/adapters/spiffy/commerce/exceptions/unsupportedProductExceptions.ts +6 -0
- package/src/adapters/spiffy/commerce/graphql.ts +184 -0
- package/src/application/config/generalStaticConfig.ts +37 -0
- package/src/application/logging/logger.ts +29 -0
- package/src/application/models/api/context.ts +4 -0
- package/src/application/models/api/generationParams.ts +4 -0
- package/src/application/models/api/nextMessageRequest.ts +11 -0
- package/src/application/models/api/orgAnalyticsConfig.ts +19 -0
- package/src/application/models/api/orgConfigResults.ts +40 -0
- package/src/application/models/api/organizationConfig.ts +12 -0
- package/src/application/models/api/response.ts +132 -0
- package/src/application/models/api/responseGenerics.ts +67 -0
- package/src/application/models/api/search.ts +26 -0
- package/src/application/models/api/suggestion.ts +4 -0
- package/src/application/models/api/supportedEventRequest.ts +8 -0
- package/src/application/models/api/userEvent.ts +101 -0
- package/src/application/models/cachedValue.ts +8 -0
- package/src/application/models/chatElementDisplayLocation.ts +22 -0
- package/src/application/models/clientDetails.ts +18 -0
- package/src/application/models/colorsConfig.ts +28 -0
- package/src/application/models/conversationalSearchIds.ts +5 -0
- package/src/application/models/dataLayer.ts +45 -0
- package/src/application/models/domMutationContinuation.ts +7 -0
- package/src/application/models/domObservationStrategy.ts +9 -0
- package/src/application/models/events.ts +5 -0
- package/src/application/models/featureGates.ts +23 -0
- package/src/application/models/frontendConfig.ts +14 -0
- package/src/application/models/googleAnalyticsEvents.ts +8 -0
- package/src/application/models/graphql/index.ts +2 -0
- package/src/application/models/graphql/queries/getMerchantColorsQuery.ts +37 -0
- package/src/application/models/graphql/queries/getMerchantFrontendConfigQuery.ts +103 -0
- package/src/application/models/graphql/queries/getMerchantOrgIdQuery.ts +11 -0
- package/src/application/models/guards/api/index.ts +12 -0
- package/src/application/models/guards/api/isApiFormResponse.ts +90 -0
- package/src/application/models/guards/api/isApiFormSubmittedResponseAttributes.ts +37 -0
- package/src/application/models/guards/api/isApiOrderResponseAttributes.ts +155 -0
- package/src/application/models/guards/api/isApiOrgConfigResults.ts +277 -0
- package/src/application/models/guards/api/isApiOrganizationConfig.ts +207 -0
- package/src/application/models/guards/api/isApiPDPEventAttributes.ts +21 -0
- package/src/application/models/guards/api/isApiPLPEventAttributes.ts +41 -0
- package/src/application/models/guards/api/isApiPageResponseAttributes.ts +21 -0
- package/src/application/models/guards/api/isApiProductResponseAttributes.ts +85 -0
- package/src/application/models/guards/api/isApiProductSearchAttributes.ts +23 -0
- package/src/application/models/guards/api/isApiProductSearchFilterAttributes.ts +15 -0
- package/src/application/models/guards/api/isApiQueryTypedEventAttributes.ts +4 -0
- package/src/application/models/guards/api/isApiResponse.ts +39 -0
- package/src/application/models/guards/api/isApiReviewResponseAttributes.ts +30 -0
- package/src/application/models/guards/api/isApiReviewRichInformation.ts +37 -0
- package/src/application/models/guards/api/isApiSearchEventAttributes.ts +28 -0
- package/src/application/models/guards/api/isApiSuggestion.ts +36 -0
- package/src/application/models/guards/api/isApiSuggestionClickedEventAttributes.ts +9 -0
- package/src/application/models/guards/api/isApiTextResponseAttributes.ts +9 -0
- package/src/application/models/guards/api/isApiUserEvent.ts +25 -0
- package/src/application/models/guards/graphQL/isGraphQLColorsConfig.ts +50 -0
- package/src/application/models/guards/isBaseEcommerceEvent.ts +17 -0
- package/src/application/models/guards/isGA4EcommerceEvent.ts +17 -0
- package/src/application/models/guards/isLegacyUAEcommerceEvent.ts +17 -0
- package/src/application/models/guards/isMobilePLPChatPlacementParameter.ts +11 -0
- package/src/application/models/guards/isSpanxTakeAQuizCtaParameter.ts +4 -0
- package/src/application/models/guards/isVariantInfo.ts +37 -0
- package/src/application/models/guards/utils.ts +43 -0
- package/src/application/models/index.ts +20 -0
- package/src/application/models/localStorageEventListener.ts +4 -0
- package/src/application/models/message.ts +146 -0
- package/src/application/models/mobilePLPChatPlacementParameter.ts +3 -0
- package/src/application/models/orgsEnum.ts +36 -0
- package/src/application/models/productExperiment.ts +5 -0
- package/src/application/models/spanxTakeAQuizCtaParameter.ts +4 -0
- package/src/application/models/spiffyWidgets.ts +16 -0
- package/src/application/models/supportedOrgs.ts +137 -0
- package/src/application/models/utilityTypes/camelCase.ts +87 -0
- package/src/application/models/utilityTypes/camelCasedPropertiesDeep.ts +80 -0
- package/src/application/models/utilityTypes/delimiterCase.ts +121 -0
- package/src/application/models/utilityTypes/delimiterCasedPropertiesDeep.ts +98 -0
- package/src/application/models/utilityTypes/index.ts +1 -0
- package/src/application/models/utilityTypes/internal.ts +93 -0
- package/src/application/models/utilityTypes/primitive.ts +8 -0
- package/src/application/models/utilityTypes/snakeCasedPropertiesDeep.ts +49 -0
- package/src/application/models/utilityTypes/splitWords.ts +76 -0
- package/src/application/models/utilityTypes/trim.ts +28 -0
- package/src/application/models/utilityTypes/unknownArray.ts +25 -0
- package/src/application/models/utils/snakeToCamelTransformer.ts +90 -0
- package/src/application/models/utils/stringToFulfillmentDisplayStatusEnumValue.ts +68 -0
- package/src/application/models/validators/validateGraphQLColorsConfig.ts +29 -0
- package/src/application/models/validators/validateGraphQLFrontendConfig.ts +594 -0
- package/src/application/models/validators/validateGraphQLOrgId.ts +7 -0
- package/src/application/models/validators/validateMobilePLPChatPlacementParameter.ts +14 -0
- package/src/application/models/validators/validateOrgConfigResults.ts +47 -0
- package/src/application/models/validators/validateOrganizationConfig.ts +37 -0
- package/src/application/models/validators/validateResponse.ts +187 -0
- package/src/application/models/validators/validateSuggestion.ts +16 -0
- package/src/application/models/validators/validateUserEvent.ts +110 -0
- package/src/application/models/variantInfo/index.ts +1 -0
- package/src/application/models/variantInfo/pageVisitInfo.ts +6 -0
- package/src/application/models/variantInfo/plpInfo.ts +3 -0
- package/src/application/models/variantInfo/productInfo.ts +5 -0
- package/src/application/models/variantInfo/variantInfo.ts +23 -0
- package/src/application/service/cachingService.ts +84 -0
- package/src/application/service/cdnService.ts +18 -0
- package/src/application/service/customerService/index.ts +8 -0
- package/src/application/service/customerService/providers/UnsupportedCustomerService.ts +15 -0
- package/src/application/service/customerService/types.ts +31 -0
- package/src/application/service/domMutationObserver.ts +320 -0
- package/src/application/service/domMutations/GridInsertionService.ts +123 -0
- package/src/application/service/domMutations/dataLayer/dataLayerEventsListener.ts +99 -0
- package/src/application/service/domMutations/domInsertionService.ts +90 -0
- package/src/application/service/domMutations/domMutationListener.ts +15 -0
- package/src/application/service/domMutations/domMutationListenerState.ts +52 -0
- package/src/application/service/domMutations/floatingChat/embeddedChatsPlacementsListener.ts +41 -0
- package/src/application/service/domMutations/gladly/gladlyListener.ts +61 -0
- package/src/application/service/domMutations/spiffy/orgs/common/kustomerVisibilityListener.ts +41 -0
- package/src/application/service/domMutations/spiffy/orgs/common/orgsCommonDataLayerListener.ts +119 -0
- package/src/application/service/environmentService.ts +51 -0
- package/src/application/service/featureFlagService.ts +130 -0
- package/src/application/service/kustomerIntegrationService.ts +111 -0
- package/src/application/service/localStorageService.ts +77 -0
- package/src/application/service/pageVariantService.ts +779 -0
- package/src/application/service/searchService.ts +140 -0
- package/src/application/service/sessionStorageService.ts +27 -0
- package/src/application/service/shopifyUrlService.ts +63 -0
- package/src/application/service/userIdentityService.ts +114 -0
- package/src/application/service/windowChatToggleService.ts +71 -0
- package/src/application/service/windowDataLayerService.ts +181 -0
- package/src/application/service/windowFrontendConfigService.ts +104 -0
- package/src/application/utils/__tests__/divideArrays.test.ts +14 -0
- package/src/application/utils/analyticsUtils.ts +110 -0
- package/src/application/utils/coreContextToApiContext.ts +11 -0
- package/src/application/utils/coreUserEventToApiUserEvent.ts +106 -0
- package/src/application/utils/divideArray.ts +7 -0
- package/src/application/utils/domObserver.ts +96 -0
- package/src/application/utils/elementObserver.ts +246 -0
- package/src/application/utils/imageFilter.ts +12 -0
- package/src/application/utils/index.ts +3 -0
- package/src/application/utils/merchantUtils.ts +16 -0
- package/src/application/utils/messageFromFormSubmittedEvent.ts +31 -0
- package/src/application/utils/messageFromQueryEvent.ts +38 -0
- package/src/application/utils/messageFromResponse.ts +133 -0
- package/src/application/utils/messageFromSuggestionEvent.ts +32 -0
- package/src/application/utils/mouseEventTypes.ts +1 -0
- package/src/application/utils/mutationHelper.ts +51 -0
- package/src/application/utils/nextMessageRequestToApiRequest.ts +31 -0
- package/src/application/utils/nodeSelector.ts +133 -0
- package/src/application/utils/overrides.ts +196 -0
- package/src/application/utils/stringUtils.ts +55 -0
- package/src/application/utils/supportedEventRequestToApiRequest.ts +12 -0
- package/src/application/utils/urlsParser.ts +53 -0
- package/src/application/utils/validation.ts +5 -0
- package/src/atoms/app/index.ts +57 -0
- package/src/atoms/app/variant.ts +261 -0
- package/src/atoms/atomStore.ts +34 -0
- package/src/atoms/chat/chatState.ts +44 -0
- package/src/atoms/chat/form.ts +19 -0
- package/src/atoms/chat/index.ts +38 -0
- package/src/atoms/chat/lastMessage.ts +11 -0
- package/src/atoms/chat/messageQueue.ts +65 -0
- package/src/atoms/chat/performanceMetrics.ts +84 -0
- package/src/atoms/chat/renderedWidgetRefs.ts +28 -0
- package/src/atoms/chat/replies.ts +51 -0
- package/src/atoms/chat/suggestions.ts +36 -0
- package/src/atoms/globalSearch.ts +12 -0
- package/src/atoms/index.ts +5 -0
- package/src/atoms/org/customerService.ts +13 -0
- package/src/atoms/org/graphqlConfig.ts +27 -0
- package/src/atoms/org/index.ts +7 -0
- package/src/atoms/org/merchantCss.ts +44 -0
- package/src/atoms/org/org.ts +256 -0
- package/src/atoms/org/orgAnalyticsConfig.ts +28 -0
- package/src/atoms/org/orgPageConfig.ts +38 -0
- package/src/atoms/org/orgUIConfig.ts +122 -0
- package/src/atoms/search/chatSearch.ts +293 -0
- package/src/atoms/search/index.ts +2 -0
- package/src/atoms/search/productFilters.ts +207 -0
- package/src/atoms/search/productSorter.ts +23 -0
- package/src/atoms/search/searchAPI.ts +194 -0
- package/src/atoms/search/types.ts +55 -0
- package/src/atoms/search/utils.ts +18 -0
- package/src/config/divIds.ts +27 -0
- package/src/config/locators/components/chat/entrypoints.ts +13 -0
- package/src/config/locators/components/chat/index.ts +23 -0
- package/src/config/locators/components/chat/preview.ts +13 -0
- package/src/config/locators/components/chat/variants/index.ts +16 -0
- package/src/config/locators/components/common/buttons.ts +6 -0
- package/src/config/locators/components/common/cards.ts +18 -0
- package/src/config/locators/components/common/links.ts +1 -0
- package/src/config/locators/components/common/tables.ts +2 -0
- package/src/config/locators/components/floating-button.ts +2 -0
- package/src/config/locators/components/index.ts +3 -0
- package/src/config/locators/components/report-issue.ts +21 -0
- package/src/config/locators/components/search/index.ts +5 -0
- package/src/config/locators/components/shadow-dom.ts +1 -0
- package/src/config/locators/embedded.ts +21 -0
- package/src/config/locators/index.ts +3 -0
- package/src/config/socialProofClasses.ts +17 -0
- package/src/contexts/chatContext.tsx +451 -0
- package/src/contexts/enviveConfigContext.tsx +70 -0
- package/src/contexts/index.ts +4 -0
- package/src/contexts/systemSettingsContext.tsx +61 -0
- package/src/contexts/types.ts +1059 -0
- package/src/enabled-features.ts +83 -0
- package/src/events/event-types.ts +11 -0
- package/src/events/index.ts +52 -0
- package/src/events/registerAnalyticsListeners.ts +49 -0
- package/src/extension.ts +63 -0
- package/src/hooks/index.ts +22 -0
- package/src/hooks/useBlockBackButton.ts +29 -0
- package/src/hooks/useChatToggle.ts +66 -0
- package/src/hooks/useCustomerSupportHandoff.ts +39 -0
- package/src/hooks/useDebounce.ts +17 -0
- package/src/hooks/useDynamicVariants.ts +210 -0
- package/src/hooks/useElementObserver.ts +245 -0
- package/src/hooks/useFileUpload.ts +61 -0
- package/src/hooks/useGrabAndScroll.ts +133 -0
- package/src/hooks/useHideElements.ts +82 -0
- package/src/hooks/useHorizontalScrollAnimation.ts +115 -0
- package/src/hooks/useImageResolver.ts +51 -0
- package/src/hooks/useIntersection.ts +28 -0
- package/src/hooks/useIsSmallScreen.ts +23 -0
- package/src/hooks/useMessageFilter.ts +49 -0
- package/src/hooks/useMessageScrollObserver.ts +47 -0
- package/src/hooks/useReducedMotionWithOverride.ts +15 -0
- package/src/hooks/useSearch.tsx +433 -0
- package/src/hooks/useSnapCalculator.ts +38 -0
- package/src/hooks/useSnapControl.ts +155 -0
- package/src/hooks/useSystemSettingsContext.ts +12 -0
- package/src/hooks/useTrackComponentVisibleEvent.ts +52 -0
- package/src/hooks/useUpdateAnalyticsProps.ts +56 -0
- package/src/hooks/utils.ts +153 -0
- package/src/index.ts +31 -0
- package/src/initialize.ts +163 -0
- package/src/interceptors/types.ts +6 -0
- package/src/interceptors/useFormEscalation.ts +40 -0
- package/src/interceptors/useMessageInterceptor.ts +32 -0
- package/src/main.ts +85 -0
- package/src/main.tsx +123 -0
- package/src/merchants/bandolier/bandolier.ts +1389 -0
- package/src/merchants/carpe/carpe.ts +656 -0
- package/src/merchants/coterie/coterie.ts +280 -0
- package/src/merchants/default.ts +193 -0
- package/src/merchants/dreamlandBaby/dreamlandBaby.ts +375 -0
- package/src/merchants/fiveCbd/fiveCbd.ts +697 -0
- package/src/merchants/forLoveAndLemons/forLoveAndLemons.ts +721 -0
- package/src/merchants/greenpan/greenpan.ts +440 -0
- package/src/merchants/grooveLife/grooveLife.ts +386 -0
- package/src/merchants/homegrownCannabis/homegrownCannabis.ts +468 -0
- package/src/merchants/init-merchant.sh +53 -0
- package/src/merchants/jackArcher/jackArcher.ts +974 -0
- package/src/merchants/jordanCraig/jordanCraig.ts +1927 -0
- package/src/merchants/kindredBravely/kindredBravely.ts +529 -0
- package/src/merchants/kutFromTheKloth/kutFromTheKloth.ts +418 -0
- package/src/merchants/larryAndSerges/larryAndSerges.ts +314 -0
- package/src/merchants/leapsAndRebounds/leapsAndRebounds.ts +424 -0
- package/src/merchants/longevityrx/longevityrx.ts +368 -0
- package/src/merchants/lookOptic/lookOptic.ts +323 -0
- package/src/merchants/mantraBrand/mantraBrand.ts +838 -0
- package/src/merchants/medterra/medterra.ts +670 -0
- package/src/merchants/modells/modells.ts +546 -0
- package/src/merchants/pressedFloral/pressedFloral.ts +734 -0
- package/src/merchants/skinPerfection/skinPerfection.ts +379 -0
- package/src/merchants/snapSupplements/snapSupplements.ts +325 -0
- package/src/merchants/spanx/spanx.ts +810 -0
- package/src/merchants/spanx/spanxStaging.ts +942 -0
- package/src/merchants/supergoop/supergoop.ts +376 -0
- package/src/merchants/uniqueVintage/uniqueVintage.ts +1314 -0
- package/src/merchants/uniqueVintage/views/useUniqueVintageChatSearch.ts +147 -0
- package/src/merchants/venaCbd/venaCbd.ts +410 -0
- package/src/merchants/westonJonBoucher/westonJonBoucher.ts +473 -0
- package/src/merchants/wineEnthusiast/wineEnthusiast.ts +990 -0
- package/src/merchants/wolfMattress/wolfMattress.ts +411 -0
- package/src/merchants/wolfTactical/wolfTactical.ts +389 -0
- package/src/types/custservice-types.ts +28 -0
- package/src/types/search-filter-types.ts +111 -0
- package/src/types/suggestionBarV2-types.ts +4 -0
- package/src/types/test-types.ts +3 -0
- 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
|
+
};
|
package/src/extension.ts
ADDED
|
@@ -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
|
+
};
|