UNPKG

@benshi.ai/js-sdk

Version:

Benshi SDK

313 lines (270 loc) 9.28 kB
import ECommmercePropertiesTI from '../ECommerce/typings-ti' import OxygenPropertiesTI from '../ECommerce/oxygen.typings-ti' import BloodPropertiesTI from '../ECommerce/blood.typings-ti' import MedicalEquipmentPropertiesTI from '../ECommerce/medicalEquipment.typings-ti' import NavigationPropertiesTI from './typings-ti' import { AudioVideoType, ContentBlock, ImageType, InternalMediaProperties, MediaData, NudgeResponseProperties, RateProperties, TrackProperties, TrackTypes } from './typings' import { AppProperties, IdentifyProperties, InternalSearchProperties, MediaProperties, NavigationTypes, PageProperties, SearchProperties } from "./typings" import { injectEvent } from "../../core/injector" import { getCoreInstance } from '../../coreInstanceGetter' import { ICatalogRepository } from '../../core/repositories/catalog/CatalogRepository' import { ItemType,TypedItem } from '../ECommerce/typings' import { hasRepeatedIds } from '../../utils' let catalogRepository: ICatalogRepository; const moduleName = ContentBlock.Core /** * `title` may be overwritten by the developer * This variable stores the known title, that is, * the one provided by the BsAppStateMonitor or by developer */ let lastKnownTitle /** * @internal * * @param properties * @param sendNow */ const logAppEvent = (properties: AppProperties, sendNow = false) => { injectEvent( properties, [NavigationPropertiesTI, ECommmercePropertiesTI, BloodPropertiesTI, OxygenPropertiesTI, MedicalEquipmentPropertiesTI], NavigationTypes.App, moduleName, '', sendNow) } /** * @internal * * @param userId * @param properties * @param sendNow */ const logIdentifyEvent = (properties: IdentifyProperties, sendNow = false, ) => { injectEvent( properties, [NavigationPropertiesTI, ECommmercePropertiesTI, BloodPropertiesTI, OxygenPropertiesTI, MedicalEquipmentPropertiesTI], NavigationTypes.Identify, moduleName, '', sendNow) } /** * Whenever the user interacts with a media element (Video, Audio, Image), log that * action with this method * * @param {MediaProperties} properties * * @param {MediaData} mediaData Information related to media features to be injected * into the media catalog * * @param sendNow */ const logMediaEvent = async ( properties: MediaProperties, mediaData: MediaData, sendNow = false): Promise<void> => { properties.time = properties.type === ImageType.Image ? 0 : properties.time const internalMediaProperties: InternalMediaProperties = { ...properties, id_source: properties.id, id: `${properties.type}_${properties.id}` } injectEvent( internalMediaProperties, [NavigationPropertiesTI, ECommmercePropertiesTI, BloodPropertiesTI, OxygenPropertiesTI, MedicalEquipmentPropertiesTI], NavigationTypes.Media, properties.contentBlock || ContentBlock.Core, '', sendNow ) if (mediaData !== undefined && Object.keys(mediaData).length !== 0) { catalogRepository.injectMedia(properties.id, properties.type as AudioVideoType, mediaData) } } /** * SDK tracks automatically page changes by inspecting the URL. However, * depending on the app implementation, it may happen that the URL does not * change when a new view is presented to the user. For these cases, use this method * * @param properties * @param sendNow */ const logPageEvent = (properties: PageProperties, sendNow = false): void => { if (properties.duration === 0) { // avoid noise due internal URLs that changes so fast return } properties.title = lastKnownTitle ? lastKnownTitle : properties.title injectEvent( properties, [NavigationPropertiesTI, ECommmercePropertiesTI, BloodPropertiesTI, OxygenPropertiesTI, MedicalEquipmentPropertiesTI], NavigationTypes.Page, moduleName, '', sendNow ) lastKnownTitle = undefined } /** * Log generic action regarding navigation, like view an item or a list, that are not * included in other specific modules like `eCommerce` or `eLearning`. It also allows * to log navigation to external links * * @param properties * @param sendNow */ const logTrackEvent = async ( properties: TrackProperties, sendNow = false): Promise<void> => { const internalProperties = { ...properties, type: TrackTypes.ReferenceGuide } injectEvent( internalProperties, [NavigationPropertiesTI, ECommmercePropertiesTI, BloodPropertiesTI, OxygenPropertiesTI, MedicalEquipmentPropertiesTI], NavigationTypes.Track, moduleName, '', sendNow) } /** * @internal * * @param properties * @param sendNow */ const logPushNotificationEvent = (properties: NudgeResponseProperties, sendNow = false): void => { injectEvent( properties, [NavigationPropertiesTI, ECommmercePropertiesTI, BloodPropertiesTI, OxygenPropertiesTI, MedicalEquipmentPropertiesTI], NavigationTypes.NudgeResponse, moduleName, '', sendNow) } const logRateEvent = (properties: RateProperties, sendNow = false): void => { const RATE_MIN_VALUE = 0 const RATE_MAX_VALUE = 5 if (properties.rate_value < RATE_MIN_VALUE || properties.rate_value > RATE_MAX_VALUE) { throw new Error('invalid range for value') } injectEvent( properties, [NavigationPropertiesTI, ECommmercePropertiesTI, BloodPropertiesTI, OxygenPropertiesTI, MedicalEquipmentPropertiesTI], NavigationTypes.Rate, moduleName, '', sendNow) } /** * This function logs into the platform a new search performed by the user, * including both, the parameters and the resulting ids * * @param {SearchProperties} properties * @param {boolean} isNewSearch Whenever the user performs a new search, * call the function with `true` in this parameter; however, when the user * clicks on the links to go to next paginated page or load more items, * related to the same search, just pass `false` * * @returns Search ID, which must be included in subsequences Item events */ const logSearchEvent = (properties: SearchProperties, searchId: string, sendNow = false): void => { const { contentBlock, ...restOfProperties } = properties const ERROR_REPEATEAD_IDS = 'repeated-ids' let formattedResultsIds: TypedItem[] = [] if (!properties.results_ids) { throw new Error('missing-results-ids') } if (properties.results_ids.length !== 0 && typeof properties.results_ids[0] === 'string') { formattedResultsIds = (properties.results_ids as string[]) .map( (id: string) => ({id, type: ItemType.Drug})) } else { formattedResultsIds = properties.results_ids as TypedItem[] } if (hasRepeatedIds(formattedResultsIds)) { throw new Error(ERROR_REPEATEAD_IDS) } const internalProperties: InternalSearchProperties = { ...restOfProperties, results_list: formattedResultsIds, id: searchId, } delete internalProperties.results_ids injectEvent( internalProperties, [NavigationPropertiesTI, ECommmercePropertiesTI, BloodPropertiesTI, OxygenPropertiesTI, MedicalEquipmentPropertiesTI], NavigationTypes.Search, properties.contentBlock || ContentBlock.Core, '', sendNow ) } /** * SDK has some autonomy to trigger events automatically. For instance, * it detects URL changes and notify Benshi.ai backend about that changes * and the duration. However, it is not able to find out which block that URL * belongs to. The goal of this method is to solve that. So, call it whenever * the user goes to another section within the site. For example, if the user * jumps from the e-commerce section to e-learning, call in this way: * * ``` * Navigation.setCurrentBlock(ContentBlock.Elearning) * ``` * * @param {ContentBlock} block the new section where the user has just jumped */ const setCurrentBlock = (block: ContentBlock) => { const core = getCoreInstance() core.setCurrentBlock(block) } /** * There are scenarious where the browser page title is not available, * for instance when the web is embedded within a webview. Use this function * whenever the new page is loaded to let the SDK know the title. * * Note that only is necessary when the standard `document.title` is not * available for any reason * * * @param title */ const setTitle = (title: string) => { // the timeout is for waiting for the page-changed event, if exists, // see BsSystem for more details setTimeout(() => { lastKnownTitle = title }, 15) } /** * Private function to inject CurrencyRepository * * @ignore */ const init = ( injectedCatalogRepository: ICatalogRepository ) => { catalogRepository = injectedCatalogRepository } export default { logAppEvent, logIdentifyEvent, logMediaEvent, logPageEvent, logPushNotificationEvent, logRateEvent, logSearchEvent, logTrackEvent, setCurrentBlock, setTitle, init }