diff options
Diffstat (limited to 'packages/instant/src/util')
-rw-r--r-- | packages/instant/src/util/analytics.ts | 64 | ||||
-rw-r--r-- | packages/instant/src/util/heap.ts | 113 | ||||
-rw-r--r-- | packages/instant/src/util/provider_state_factory.ts | 2 |
3 files changed, 178 insertions, 1 deletions
diff --git a/packages/instant/src/util/analytics.ts b/packages/instant/src/util/analytics.ts new file mode 100644 index 000000000..2ffaac1dd --- /dev/null +++ b/packages/instant/src/util/analytics.ts @@ -0,0 +1,64 @@ +import { ObjectMap } from '@0x/types'; + +import { heapUtil } from './heap'; + +let isDisabled = false; +export const disableAnalytics = (shouldDisableAnalytics: boolean) => { + isDisabled = shouldDisableAnalytics; +}; +export const evaluateIfEnabled = (fnCall: () => void) => { + if (isDisabled) { + return; + } + fnCall(); +}; + +enum EventNames { + INSTANT_OPENED = 'Instant - Opened', + WALLET_READY = 'Wallet - Ready', +} +const track = (eventName: EventNames, eventData: ObjectMap<string | number> = {}): void => { + evaluateIfEnabled(() => { + heapUtil.evaluateHeapCall(heap => heap.track(eventName, eventData)); + }); +}; +function trackingEventFnWithoutPayload(eventName: EventNames): () => void { + return () => { + track(eventName); + }; +} +// tslint:disable-next-line:no-unused-variable +function trackingEventFnWithPayload<T extends ObjectMap<string | number>>( + eventName: EventNames, +): (eventDataProperties: T) => void { + return (eventDataProperties: T) => { + track(eventName, eventDataProperties); + }; +} + +export interface AnalyticsUserOptions { + ethAddress?: string; + ethBalanceInUnitAmount?: string; +} +export interface AnalyticsEventOptions { + embeddedHost?: string; + embeddedUrl?: string; + networkId?: number; + providerName?: string; + gitSha?: string; + npmVersion?: string; +} +export const analytics = { + addUserProperties: (properties: AnalyticsUserOptions): void => { + evaluateIfEnabled(() => { + heapUtil.evaluateHeapCall(heap => heap.addUserProperties(properties)); + }); + }, + addEventProperties: (properties: AnalyticsEventOptions): void => { + evaluateIfEnabled(() => { + heapUtil.evaluateHeapCall(heap => heap.addEventProperties(properties)); + }); + }, + trackWalletReady: trackingEventFnWithoutPayload(EventNames.WALLET_READY), + trackInstantOpened: trackingEventFnWithoutPayload(EventNames.INSTANT_OPENED), +}; diff --git a/packages/instant/src/util/heap.ts b/packages/instant/src/util/heap.ts new file mode 100644 index 000000000..78ec3b3cc --- /dev/null +++ b/packages/instant/src/util/heap.ts @@ -0,0 +1,113 @@ +import { ObjectMap } from '@0x/types'; +import { logUtils } from '@0x/utils'; +import * as _ from 'lodash'; + +import { HEAP_ANALYTICS_ID } from '../constants'; + +import { AnalyticsEventOptions, AnalyticsUserOptions } from './analytics'; + +export interface HeapAnalytics { + loaded: boolean; + appid: string; + identify(id: string, idType: string): void; + track(eventName: string, eventProperties?: ObjectMap<string | number>): void; + resetIdentity(): void; + addUserProperties(properties: AnalyticsUserOptions): void; + addEventProperties(properties: AnalyticsEventOptions): void; + removeEventProperty(property: string): void; + clearEventProperties(): void; +} +interface ModifiedWindow { + heap?: HeapAnalytics; + zeroExInstantLoadedHeap?: boolean; +} +const getWindow = (): ModifiedWindow => { + return window as ModifiedWindow; +}; + +const setupZeroExInstantHeap = () => { + if (_.isUndefined(HEAP_ANALYTICS_ID)) { + return; + } + + const curWindow = getWindow(); + // Set property to specify that this is zeroEx's heap + curWindow.zeroExInstantLoadedHeap = true; + + // Typescript-compatible version of https://docs.heapanalytics.com/docs/installation + /* tslint:disable */ + ((window as any).heap = (window as any).heap || []), + ((window as any).heap.load = function(e: any, t: any) { + ((window as any).heap.appid = e), ((window as any).heap.config = t = t || {}); + var r = t.forceSSL || 'https:' === (document.location as Location).protocol, + a = document.createElement('script'); + (a.type = 'text/javascript'), + (a.async = !0), + (a.src = (r ? 'https:' : 'http:') + '//cdn.heapanalytics.com/js/heap-' + e + '.js'); + var n = document.getElementsByTagName('script')[0]; + (n.parentNode as Node).insertBefore(a, n); + for ( + var o = function(e: any) { + return function() { + (window as any).heap.push([e].concat(Array.prototype.slice.call(arguments, 0))); + }; + }, + p = [ + 'addEventProperties', + 'addUserProperties', + 'clearEventProperties', + 'identify', + 'resetIdentity', + 'removeEventProperty', + 'setEventProperties', + 'track', + 'unsetEventProperty', + ], + c = 0; + c < p.length; + c++ + ) + (window as any).heap[p[c]] = o(p[c]); + }); + (window as any).heap.load(HEAP_ANALYTICS_ID); + /* tslint:enable */ + + return curWindow.heap as HeapAnalytics; +}; + +export const heapUtil = { + getHeap: (): HeapAnalytics | undefined => { + const curWindow = getWindow(); + const hasOtherExistingHeapIntegration = curWindow.heap && !curWindow.zeroExInstantLoadedHeap; + if (hasOtherExistingHeapIntegration) { + return undefined; + } + + const zeroExInstantHeapIntegration = curWindow.zeroExInstantLoadedHeap && curWindow.heap; + if (zeroExInstantHeapIntegration) { + return zeroExInstantHeapIntegration; + } + + return setupZeroExInstantHeap(); + }, + evaluateHeapCall: (heapFunctionCall: (heap: HeapAnalytics) => void): void => { + if (_.isUndefined(HEAP_ANALYTICS_ID)) { + return; + } + + const curHeap = heapUtil.getHeap(); + if (curHeap) { + try { + if (curHeap.appid !== HEAP_ANALYTICS_ID) { + // Integrator has included heap after us and reset the app id + return; + } + heapFunctionCall(curHeap); + } catch (e) { + // We never want analytics to crash our React component + // TODO(sk): error reporter here + logUtils.log('Analytics error', e); + } + } + }, +}; diff --git a/packages/instant/src/util/provider_state_factory.ts b/packages/instant/src/util/provider_state_factory.ts index 452a71460..7c788dff2 100644 --- a/packages/instant/src/util/provider_state_factory.ts +++ b/packages/instant/src/util/provider_state_factory.ts @@ -56,7 +56,7 @@ export const providerStateFactory = { getInitialProviderStateFallback: (orderSource: OrderSource, network: Network): ProviderState => { const provider = providerFactory.getFallbackNoSigningProvider(network); const providerState: ProviderState = { - name: envUtil.getProviderName(provider), + name: 'Fallback', provider, web3Wrapper: new Web3Wrapper(provider), assetBuyer: assetBuyerFactory.getAssetBuyer(provider, orderSource, network), |