diff --git a/plugins/tutor-contrib-google-analytics/README.rst b/plugins/tutor-contrib-google-analytics/README.rst index 32a6018..fc13d08 100644 --- a/plugins/tutor-contrib-google-analytics/README.rst +++ b/plugins/tutor-contrib-google-analytics/README.rst @@ -2,7 +2,7 @@ Google Analytics plugin for `Tutor `__ ======================================================================= This plugin wires a Google Analytics 4 loader into every Open edX MFE that -is built through ``tutor-mfe``. It duplicates the ``GoogleAnalyticsLoader`` +is built through ``tutor-mfe``. It replaces the ``GoogleAnalyticsLoader`` that used to ship with ``@openedx/frontend-platform``. The loader reads ``GOOGLE_ANALYTICS_4_ID`` from the MFE runtime configuration diff --git a/plugins/tutor-contrib-google-analytics/tutor_google_analytics/__about__.py b/plugins/tutor-contrib-google-analytics/tutor_google_analytics/__about__.py index 3dc1f76..485f44a 100644 --- a/plugins/tutor-contrib-google-analytics/tutor_google_analytics/__about__.py +++ b/plugins/tutor-contrib-google-analytics/tutor_google_analytics/__about__.py @@ -1 +1 @@ -__version__ = "0.1.0" +__version__ = "0.1.1" diff --git a/plugins/tutor-contrib-google-analytics/tutor_google_analytics/plugin.py b/plugins/tutor-contrib-google-analytics/tutor_google_analytics/plugin.py index ce5c393..aa2326d 100644 --- a/plugins/tutor-contrib-google-analytics/tutor_google_analytics/plugin.py +++ b/plugins/tutor-contrib-google-analytics/tutor_google_analytics/plugin.py @@ -2,8 +2,14 @@ from tutormfe.hooks import EXTERNAL_SCRIPTS +# This snippet is inlined verbatim into both a .jsx and a .tsx file, so it has +# to be plain JavaScript that also survives the frontend-base type checker: +# no implicit `this` properties, no undeclared globals, no possibly-null DOM +# nodes. GOOGLE_ANALYTICS_LOADER = """ class GoogleAnalyticsLoader { + analyticsId = ''; + constructor({ config }) { this.analyticsId = config.GOOGLE_ANALYTICS_4_ID; } @@ -13,41 +19,27 @@ class GoogleAnalyticsLoader { return; } - global.googleAnalytics = global.googleAnalytics || []; - const { googleAnalytics } = global; - - // If the snippet was invoked do nothing. - if (googleAnalytics.invoked) { + // Never inject the snippet twice, in case more than one app registers + // the loader on the same page. + if (document.querySelector('script[data-google-analytics-4]')) { return; } - // Invoked flag, to make sure the snippet - // is never invoked twice. - 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}'); - `; - - // Insert our scripts next to the first script element. - const first = document.getElementsByTagName('script')[0]; - first.parentNode.insertBefore(scriptSrc, first); - first.parentNode.insertBefore(scriptGtag, first); - googleAnalytics._loadOptions = options; // eslint-disable-line no-underscore-dangle - }; - - // Load GoogleAnalytics with your key. - googleAnalytics.load(this.analyticsId); + const scriptSrc = document.createElement('script'); + scriptSrc.async = true; + scriptSrc.src = `https://www.googletagmanager.com/gtag/js?id=${this.analyticsId}`; + scriptSrc.setAttribute('data-google-analytics-4', ''); + + const scriptGtag = document.createElement('script'); + scriptGtag.innerHTML = ` + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + gtag('config', '${this.analyticsId}'); + `; + + document.head.appendChild(scriptSrc); + document.head.appendChild(scriptGtag); } } """