diff --git a/site.config.build.tsx b/site.config.build.tsx index f909cf6..21ef60d 100644 --- a/site.config.build.tsx +++ b/site.config.build.tsx @@ -3,6 +3,8 @@ import { authnApp } from '@openedx/frontend-app-authn'; import { instructorDashboardApp } from '@openedx/frontend-app-instructor-dashboard'; import { learnerDashboardApp } from '@openedx/frontend-app-learner-dashboard'; +import { googleAnalyticsApp } from './src/googleAnalytics'; + import './src/site.scss'; const siteConfig: SiteConfig = { @@ -21,6 +23,12 @@ const siteConfig: SiteConfig = { authnApp, learnerDashboardApp, instructorDashboardApp, + { + ...googleAnalyticsApp, + config: { + GOOGLE_ANALYTICS_4_ID: 'G-TEST123', + }, + }, ], externalRoutes: [ { diff --git a/site.config.dev.tsx b/site.config.dev.tsx index 6138b62..2af027d 100644 --- a/site.config.dev.tsx +++ b/site.config.dev.tsx @@ -3,6 +3,8 @@ import { authnApp } from '@openedx/frontend-app-authn'; import { instructorDashboardApp } from '@openedx/frontend-app-instructor-dashboard'; import { learnerDashboardApp } from '@openedx/frontend-app-learner-dashboard'; +import { googleAnalyticsApp } from './src/googleAnalytics'; + import './src/site.scss'; const siteConfig: SiteConfig = { @@ -21,6 +23,12 @@ const siteConfig: SiteConfig = { authnApp, learnerDashboardApp, instructorDashboardApp, + { + ...googleAnalyticsApp, + config: { + GOOGLE_ANALYTICS_4_ID: 'G-TEST123', + }, + }, ], externalRoutes: [ { diff --git a/src/googleAnalytics/GoogleAnalyticsLoader.ts b/src/googleAnalytics/GoogleAnalyticsLoader.ts new file mode 100644 index 0000000..af3b93b --- /dev/null +++ b/src/googleAnalytics/GoogleAnalyticsLoader.ts @@ -0,0 +1,50 @@ +import { AppConfig, ExternalScriptLoader } from '@openedx/frontend-base'; + +export default class GoogleAnalyticsLoader implements ExternalScriptLoader { + analyticsId: string; + + constructor({ config }: { config: AppConfig }) { + this.analyticsId = config.GOOGLE_ANALYTICS_4_ID as string; + } + + loadScript() { + if (!this.analyticsId) { + return; + } + + global.googleAnalytics = global.googleAnalytics || []; + // @ts-expect-error We just added googleAnalytics to global, it's there. + const { googleAnalytics } = global; + + if (googleAnalytics.invoked) { + return; + } + + googleAnalytics.invoked = true; + + googleAnalytics.load = (key, options) => { + const scriptSrc = document.createElement('script'); + scriptSrc.type = 'text/javascript'; + scriptSrc.async = true; + scriptSrc.src = `https://www.googletagmanager.com/gtag/js?id=${key}`; + + const scriptGtag = document.createElement('script'); + scriptGtag.innerHTML = ` + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + gtag('config', '${key}'); + `; + + const first = document.getElementsByTagName('script')[0]; + if (first?.parentNode === null) { + throw new Error('No script to insert Google analytics script before.'); + } + first.parentNode.insertBefore(scriptSrc, first); + first.parentNode.insertBefore(scriptGtag, first); + googleAnalytics._loadOptions = options; + }; + + googleAnalytics.load(this.analyticsId); + } +} diff --git a/src/googleAnalytics/app.ts b/src/googleAnalytics/app.ts new file mode 100644 index 0000000..8d6effd --- /dev/null +++ b/src/googleAnalytics/app.ts @@ -0,0 +1,9 @@ +import { App } from '@openedx/frontend-base'; +import GoogleAnalyticsLoader from './GoogleAnalyticsLoader'; + +const app: App = { + appId: 'org.openedx.frontend.app.googleAnalytics', + externalScripts: [GoogleAnalyticsLoader], +}; + +export default app; diff --git a/src/googleAnalytics/index.ts b/src/googleAnalytics/index.ts new file mode 100644 index 0000000..5270cf5 --- /dev/null +++ b/src/googleAnalytics/index.ts @@ -0,0 +1,2 @@ +export { default as GoogleAnalyticsLoader } from './GoogleAnalyticsLoader'; +export { default as googleAnalyticsApp } from './app';