mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 22:57:21 +01:00
Set up analytics scripts on demand post user load (#15501)
* fix(analytics): can't get consented user during main,js load * fix(race): don't let gtag load twice also refactor to avoid unnecessary _getConsentedUser() calls * fix(lint): need user ID for gtag config * fix(analytics): adjust script loads and refs * fix(vue): try moving plugin to most relevant file * fix(amplitude): correct event fn * fix(analytics): direct load gtag from uri * fix(ga): use ga-gtag for loading google * fix(lint): import order * refactor(analytics): remove superfluous setUser fn * fix(amplitude): return to Javascript SDK syntax * refactor(misc): remove unneeded asyncs * refactor(analytics): slim down if checks
This commit is contained in:
12
website/client/package-lock.json
generated
12
website/client/package-lock.json
generated
@@ -23,6 +23,7 @@
|
|||||||
"eslint-config-habitrpg": "6.2.0",
|
"eslint-config-habitrpg": "6.2.0",
|
||||||
"eslint-plugin-mocha": "5.3.0",
|
"eslint-plugin-mocha": "5.3.0",
|
||||||
"eslint-plugin-vue": "7.20.0",
|
"eslint-plugin-vue": "7.20.0",
|
||||||
|
"ga-gtag": "^1.2.0",
|
||||||
"habitica-markdown": "^3.0.0",
|
"habitica-markdown": "^3.0.0",
|
||||||
"hellojs": "^1.20.0",
|
"hellojs": "^1.20.0",
|
||||||
"intro.js": "^7.2.0",
|
"intro.js": "^7.2.0",
|
||||||
@@ -42,7 +43,6 @@
|
|||||||
"vue": "^2.7.10",
|
"vue": "^2.7.10",
|
||||||
"vue-fragment": "^1.6.0",
|
"vue-fragment": "^1.6.0",
|
||||||
"vue-mugen-scroll": "^0.2.6",
|
"vue-mugen-scroll": "^0.2.6",
|
||||||
"vue-plugin-load-script": "^1.3.6",
|
|
||||||
"vue-router": "^3.6.5",
|
"vue-router": "^3.6.5",
|
||||||
"vuedraggable": "^2.24.3",
|
"vuedraggable": "^2.24.3",
|
||||||
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker.git#153d339e4dbebb73733658aeda1d5b7fcc55b0a0"
|
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker.git#153d339e4dbebb73733658aeda1d5b7fcc55b0a0"
|
||||||
@@ -5161,6 +5161,11 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ga-gtag": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ga-gtag/-/ga-gtag-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-j9gxutMdpGMdwaX1SzOG31Ddm+IGFjeNf+N3Z5g+BBpS8FSXOALlrM+ORIGc/QKszGJEDlw+6PfIsJZICsqsGQ=="
|
||||||
|
},
|
||||||
"node_modules/gensync": {
|
"node_modules/gensync": {
|
||||||
"version": "1.0.0-beta.2",
|
"version": "1.0.0-beta.2",
|
||||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||||
@@ -8818,11 +8823,6 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vue-plugin-load-script": {
|
|
||||||
"version": "1.3.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/vue-plugin-load-script/-/vue-plugin-load-script-1.3.6.tgz",
|
|
||||||
"integrity": "sha512-O+mpw32dXY3tMBBNKm7/qIByJV1QbHJ+0CXI4GeXx4NHU/Rg+bv7bvzugkEWnYcB/43WvR8ZD2K9KQIeVng1bA=="
|
|
||||||
},
|
|
||||||
"node_modules/vue-router": {
|
"node_modules/vue-router": {
|
||||||
"version": "3.6.5",
|
"version": "3.6.5",
|
||||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.6.5.tgz",
|
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.6.5.tgz",
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
"eslint-config-habitrpg": "6.2.0",
|
"eslint-config-habitrpg": "6.2.0",
|
||||||
"eslint-plugin-mocha": "5.3.0",
|
"eslint-plugin-mocha": "5.3.0",
|
||||||
"eslint-plugin-vue": "7.20.0",
|
"eslint-plugin-vue": "7.20.0",
|
||||||
|
"ga-gtag": "^1.2.0",
|
||||||
"habitica-markdown": "^3.0.0",
|
"habitica-markdown": "^3.0.0",
|
||||||
"hellojs": "^1.20.0",
|
"hellojs": "^1.20.0",
|
||||||
"intro.js": "^7.2.0",
|
"intro.js": "^7.2.0",
|
||||||
@@ -46,7 +47,6 @@
|
|||||||
"vue": "^2.7.10",
|
"vue": "^2.7.10",
|
||||||
"vue-fragment": "^1.6.0",
|
"vue-fragment": "^1.6.0",
|
||||||
"vue-mugen-scroll": "^0.2.6",
|
"vue-mugen-scroll": "^0.2.6",
|
||||||
"vue-plugin-load-script": "^1.3.6",
|
|
||||||
"vue-router": "^3.6.5",
|
"vue-router": "^3.6.5",
|
||||||
"vuedraggable": "^2.24.3",
|
"vuedraggable": "^2.24.3",
|
||||||
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker.git#153d339e4dbebb73733658aeda1d5b7fcc55b0a0"
|
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker.git#153d339e4dbebb73733658aeda1d5b7fcc55b0a0"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import isEqual from 'lodash/isEqual';
|
|||||||
import keys from 'lodash/keys';
|
import keys from 'lodash/keys';
|
||||||
import pick from 'lodash/pick';
|
import pick from 'lodash/pick';
|
||||||
import amplitude from 'amplitude-js';
|
import amplitude from 'amplitude-js';
|
||||||
|
import { gtag, install } from 'ga-gtag';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import getStore from '@/store';
|
import getStore from '@/store';
|
||||||
|
|
||||||
@@ -10,9 +11,11 @@ const AMPLITUDE_KEY = import.meta.env.AMPLITUDE_KEY;
|
|||||||
const DEBUG_ENABLED = import.meta.env.DEBUG_ENABLED === 'true';
|
const DEBUG_ENABLED = import.meta.env.DEBUG_ENABLED === 'true';
|
||||||
const GA_ID = import.meta.env.GA_ID;
|
const GA_ID = import.meta.env.GA_ID;
|
||||||
const IS_PRODUCTION = import.meta.env.NODE_ENV === 'production';
|
const IS_PRODUCTION = import.meta.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
const REQUIRED_FIELDS = ['eventCategory', 'eventAction'];
|
const REQUIRED_FIELDS = ['eventCategory', 'eventAction'];
|
||||||
|
|
||||||
|
let analyticsLoading = false;
|
||||||
|
let analyticsReady = false;
|
||||||
|
|
||||||
function _getConsentedUser () {
|
function _getConsentedUser () {
|
||||||
const store = getStore();
|
const store = getStore();
|
||||||
const user = store.state.user.data;
|
const user = store.state.user.data;
|
||||||
@@ -63,15 +66,22 @@ function _gatherUserStats (properties) {
|
|||||||
if (user.purchased.plan.planId) properties.subscription = user.purchased.plan.planId;
|
if (user.purchased.plan.planId) properties.subscription = user.purchased.plan.planId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setUser () {
|
export function safeSetup (userId) {
|
||||||
const user = _getConsentedUser();
|
if (analyticsLoading || analyticsReady) return;
|
||||||
if (!user) return;
|
analyticsLoading = true;
|
||||||
amplitude.getInstance().setUserId(user._id);
|
install(GA_ID, {
|
||||||
|
debug_mode: DEBUG_ENABLED || !IS_PRODUCTION,
|
||||||
|
user_id: userId,
|
||||||
|
});
|
||||||
|
amplitude.getInstance().init(AMPLITUDE_KEY, userId);
|
||||||
|
analyticsReady = true;
|
||||||
|
analyticsLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function track (properties, options = {}) {
|
export function track (properties, options = {}) {
|
||||||
const user = _getConsentedUser();
|
const user = _getConsentedUser();
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
|
safeSetup(user._id);
|
||||||
// Use nextTick to avoid blocking the UI
|
// Use nextTick to avoid blocking the UI
|
||||||
Vue.nextTick(() => {
|
Vue.nextTick(() => {
|
||||||
if (_doesNotHaveRequiredFields(properties)) return;
|
if (_doesNotHaveRequiredFields(properties)) return;
|
||||||
@@ -80,9 +90,7 @@ export function track (properties, options = {}) {
|
|||||||
// Track events on the server by default
|
// Track events on the server by default
|
||||||
if (trackOnClient === true) {
|
if (trackOnClient === true) {
|
||||||
amplitude.getInstance().logEvent(properties.eventAction, properties);
|
amplitude.getInstance().logEvent(properties.eventAction, properties);
|
||||||
if (window.gtag) {
|
gtag('event', properties.eventAction, properties);
|
||||||
window.gtag('event', properties.eventAction, properties);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const store = getStore();
|
const store = getStore();
|
||||||
store.dispatch('analytics:trackEvent', properties);
|
store.dispatch('analytics:trackEvent', properties);
|
||||||
@@ -93,26 +101,14 @@ export function track (properties, options = {}) {
|
|||||||
export function updateUser (properties = {}) {
|
export function updateUser (properties = {}) {
|
||||||
const user = _getConsentedUser();
|
const user = _getConsentedUser();
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
|
safeSetup(user._id);
|
||||||
// Use nextTick to avoid blocking the UI
|
// Use nextTick to avoid blocking the UI
|
||||||
Vue.nextTick(() => {
|
Vue.nextTick(() => {
|
||||||
_gatherUserStats(properties);
|
_gatherUserStats(properties);
|
||||||
if (window.gtag) {
|
gtag('set', 'user_properties', properties);
|
||||||
window.gtag('set', 'user_properties', properties);
|
|
||||||
}
|
|
||||||
forEach(properties, (value, key) => {
|
forEach(properties, (value, key) => {
|
||||||
const identify = new amplitude.Identify().set(key, value);
|
const identify = new amplitude.Identify().set(key, value);
|
||||||
amplitude.getInstance().identify(identify);
|
amplitude.getInstance().identify(identify);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setup () {
|
|
||||||
const user = _getConsentedUser();
|
|
||||||
if (!user) return;
|
|
||||||
await Vue.loadScript(`https://www.googletagmanager.com/gtag/js?id=${GA_ID}`);
|
|
||||||
window.gtag('config', GA_ID, {
|
|
||||||
debug_mode: DEBUG_ENABLED || !IS_PRODUCTION,
|
|
||||||
user_id: user._id,
|
|
||||||
});
|
|
||||||
amplitude.getInstance().init(AMPLITUDE_KEY);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -12,11 +12,7 @@ import {
|
|||||||
CollapsePlugin,
|
CollapsePlugin,
|
||||||
} from 'bootstrap-vue';
|
} from 'bootstrap-vue';
|
||||||
import Fragment from 'vue-fragment';
|
import Fragment from 'vue-fragment';
|
||||||
import LoadScript from 'vue-plugin-load-script';
|
|
||||||
import AppComponent from './app';
|
import AppComponent from './app';
|
||||||
import {
|
|
||||||
setup as setupAnalytics,
|
|
||||||
} from '@/libs/analytics';
|
|
||||||
import { setUpLogging } from '@/libs/logging';
|
import { setUpLogging } from '@/libs/logging';
|
||||||
import router from './router/index';
|
import router from './router/index';
|
||||||
import getStore from './store';
|
import getStore from './store';
|
||||||
@@ -49,10 +45,8 @@ Vue.use(TooltipPlugin);
|
|||||||
Vue.use(NavbarPlugin);
|
Vue.use(NavbarPlugin);
|
||||||
Vue.use(CollapsePlugin);
|
Vue.use(CollapsePlugin);
|
||||||
Vue.use(Fragment.Plugin);
|
Vue.use(Fragment.Plugin);
|
||||||
Vue.use(LoadScript);
|
|
||||||
|
|
||||||
setUpLogging();
|
setUpLogging();
|
||||||
setupAnalytics();
|
|
||||||
const store = getStore();
|
const store = getStore();
|
||||||
|
|
||||||
if (import.meta.env.TIME_TRAVEL_ENABLED === 'true') {
|
if (import.meta.env.TIME_TRAVEL_ENABLED === 'true') {
|
||||||
|
|||||||
@@ -263,8 +263,6 @@ export default {
|
|||||||
this.$store.dispatch('tasks:fetchUserTasks'),
|
this.$store.dispatch('tasks:fetchUserTasks'),
|
||||||
]).then(() => {
|
]).then(() => {
|
||||||
this.$store.state.isUserLoaded = true;
|
this.$store.state.isUserLoaded = true;
|
||||||
Analytics.setUser();
|
|
||||||
Analytics.updateUser();
|
|
||||||
const analyticsConsent = localStorage.getItem('analyticsConsent');
|
const analyticsConsent = localStorage.getItem('analyticsConsent');
|
||||||
if (analyticsConsent !== null
|
if (analyticsConsent !== null
|
||||||
&& analyticsConsent !== this.user.preferences.analyticsConsent
|
&& analyticsConsent !== this.user.preferences.analyticsConsent
|
||||||
@@ -281,6 +279,7 @@ export default {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Analytics.updateUser();
|
||||||
return axios.get(
|
return axios.get(
|
||||||
'/api/v4/i18n/browser-script',
|
'/api/v4/i18n/browser-script',
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user