From ceb87c2bb097387d1bf475d0b8b41e8f41c37fd7 Mon Sep 17 00:00:00 2001 From: Kimmo Puputti Date: Wed, 25 Oct 2017 17:03:11 +0300 Subject: [PATCH] Add analytics middleware and pluggable handlers --- src/analytics/analytics.js | 18 ++++++++++++++++++ src/analytics/handlers.js | 19 +++++++++++++++++++ src/index.js | 20 +++++++++++++++++++- src/store.js | 5 +++-- 4 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 src/analytics/analytics.js create mode 100644 src/analytics/handlers.js diff --git a/src/analytics/analytics.js b/src/analytics/analytics.js new file mode 100644 index 00000000..abfc75da --- /dev/null +++ b/src/analytics/analytics.js @@ -0,0 +1,18 @@ +import { LOCATION_CHANGED } from '../ducks/Routing.duck'; + +// Create a Redux middleware from the given analytics handlers. Each +// handler should have the following methods: +// +// - trackPageView(url): called when the URL is changed +export const createMiddleware = handlers => () => next => action => { + const { type, payload } = action; + + if (type === LOCATION_CHANGED) { + const { canonicalUrl } = payload; + handlers.forEach(handler => { + handler.trackPageView(canonicalUrl); + }); + } + + next(action); +}; diff --git a/src/analytics/handlers.js b/src/analytics/handlers.js new file mode 100644 index 00000000..f96a92a5 --- /dev/null +++ b/src/analytics/handlers.js @@ -0,0 +1,19 @@ +export class LoggingAnalyticsHandler { + trackPageView(url) { + console.log('Analytics page view:', url); + } +} + +export class GoogleAnalyticsHandler { + constructor(ga) { + if (typeof ga !== 'function') { + throw new Error('Variable `ga` missing for Google Analytics'); + } + this.ga = ga; + } + trackPageView(url) { + // https://developers.google.com/analytics/devguides/collection/analyticsjs/single-page-applications#tracking_virtual_pageviews + this.ga('set', 'page', url); + this.ga('send', 'pageview'); + } +} diff --git a/src/index.js b/src/index.js index be5c452e..3ef4df4d 100644 --- a/src/index.js +++ b/src/index.js @@ -24,6 +24,7 @@ import { authInfo } from './ducks/Auth.duck'; import { fetchCurrentUser } from './ducks/user.duck'; import routeConfiguration from './routeConfiguration'; import * as log from './util/log'; +import { LoggingAnalyticsHandler, GoogleAnalyticsHandler } from './analytics/handlers'; import './marketplaceIndex.css'; @@ -53,6 +54,22 @@ const setupStripe = () => { window.Stripe.setPublishableKey(config.stripe.publishableKey); }; +const setupAnalyticsHandlers = () => { + let handlers = []; + + // Log analytics page views and events in dev mode + if (config.dev) { + handlers.push(new LoggingAnalyticsHandler()); + } + + // Add Google Analytics handler if tracker ID is found + if (process.env.REACT_APP_GOOGLE_ANALYTICS_ID) { + handlers.push(new GoogleAnalyticsHandler(window.ga)); + } + + return handlers; +}; + // If we're in a browser already, render the client application. if (typeof window !== 'undefined') { // set up logger with Sentry DSN client key and environment @@ -74,7 +91,8 @@ if (typeof window !== 'undefined') { }, ], }); - const store = configureStore(sdk, initialState); + const analyticsHandlers = setupAnalyticsHandlers(); + const store = configureStore(initialState, sdk, analyticsHandlers); setupStripe(); render(store); diff --git a/src/store.js b/src/store.js index 3180affb..b667e8f5 100644 --- a/src/store.js +++ b/src/store.js @@ -2,13 +2,14 @@ import { createStore, applyMiddleware, compose } from 'redux'; import thunk from 'redux-thunk'; import createReducer from './reducers'; +import * as analytics from './analytics/analytics'; /** * Create a new store with the given initial state. Adds Redux * middleware and enhancers. */ -export default function configureStore(sdk, initialState = {}) { - const middlewares = [thunk.withExtraArgument(sdk)]; +export default function configureStore(initialState = {}, sdk = null, analyticsHandlers = []) { + const middlewares = [thunk.withExtraArgument(sdk), analytics.createMiddleware(analyticsHandlers)]; // Enable Redux Devtools in client side dev mode. const composeEnhancers =