diff --git a/package-lock.json b/package-lock.json index af77eb5..c0b4192 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,6 +34,7 @@ "eslint-config-google": "^0.14.0", "eslint-plugin-simple-import-sort": "^8.0.0", "file-loader": "^6.2.0", + "ga-gtag": "^1.1.7", "hsv-rgb": "^1.0.0", "html-webpack-plugin": "^5.5.0", "i18next": "^22.4.14", @@ -4925,6 +4926,12 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "node_modules/ga-gtag": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/ga-gtag/-/ga-gtag-1.1.7.tgz", + "integrity": "sha512-fT/D87hhuNIAmEB2z9mxz88gMFYc1olpX/fETHidZ51sYJ4y6OFch8HZ0DoNWPGFw1BCHB0fqt68dUWvd8kM1Q==", + "dev": true + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -14573,6 +14580,12 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "ga-gtag": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/ga-gtag/-/ga-gtag-1.1.7.tgz", + "integrity": "sha512-fT/D87hhuNIAmEB2z9mxz88gMFYc1olpX/fETHidZ51sYJ4y6OFch8HZ0DoNWPGFw1BCHB0fqt68dUWvd8kM1Q==", + "dev": true + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", diff --git a/package.json b/package.json index 504dbe4..86d9241 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "eslint-config-google": "^0.14.0", "eslint-plugin-simple-import-sort": "^8.0.0", "file-loader": "^6.2.0", + "ga-gtag": "^1.1.7", "hsv-rgb": "^1.0.0", "html-webpack-plugin": "^5.5.0", "i18next": "^22.4.14", diff --git a/src/analytics.ts b/src/analytics.ts new file mode 100644 index 0000000..3284928 --- /dev/null +++ b/src/analytics.ts @@ -0,0 +1,28 @@ +import { AppConsole } from './ui/console'; +const gtag = require('ga-gtag'); + +export class AppAnalytics { + private _ready: boolean; + + private static _instance: AppAnalytics; + public static get Get() { + return this._instance || (this._instance = new this()); + } + + private constructor() { + this._ready = false; + } + + public static Init() { + gtag.install('G-W0SCWQ7HGJ', { 'send_page_view': true }); + gtag.gtag('config', 'G-W0SCWQ7HGJ', { 'debug_mode': true }); + this.Get._ready = true; + } + + public static Event(id: string, attributes?: any) { + if (this.Get._ready) { + console.log('[Analytics]: Tracked event', id, attributes); + gtag.gtag('event', id, Object.assign(attributes ?? {}, { 'debug_mode': true })); + } + } +} \ No newline at end of file diff --git a/src/app_context.ts b/src/app_context.ts index a737a5d..0d22c37 100644 --- a/src/app_context.ts +++ b/src/app_context.ts @@ -1,4 +1,5 @@ import '../styles.css'; +import { AppAnalytics } from './analytics'; import { FallableBehaviour } from './block_mesh'; import { ArcballCamera } from './camera'; @@ -38,6 +39,8 @@ export class AppContext { } public static async init() { + AppAnalytics.Init(); + await Localiser.Get.init(); AppConsole.info(LOC('init.initialising')); @@ -81,11 +84,13 @@ export class AppContext { private async _import(): Promise { // Gather data from the UI to send to the worker const components = UI.Get.layout.import.components; + let filetype: string; AppConsole.info(LOC('import.importing_mesh')); { // Instruct the worker to perform the job and await the result const file = components.input.getValue(); + filetype = file.type; const resultImport = await this._workerController.execute({ action: 'Import', @@ -131,6 +136,9 @@ export class AppContext { } AppConsole.success(LOC('import.rendered_mesh')); + AppAnalytics.Event('import', { + 'filetype': filetype, + }); return true; } @@ -163,6 +171,7 @@ export class AppContext { } AppConsole.success(LOC('materials.updated_materials')); + AppAnalytics.Event('materials') return true; } @@ -222,6 +231,14 @@ export class AppContext { } AppConsole.success(LOC('voxelise.rendered_voxel_mesh')); + AppAnalytics.Event('voxelise', { + constraintAxis: components.constraintAxis.getValue(), + voxeliser: components.voxeliser.getValue(), + size: components.size.getValue(), + useMultisampleColouring: components.multisampleColouring.getValue(), + enableAmbientOcclusion: components.ambientOcclusion.getValue(), + voxelOverlapRule: components.voxelOverlapRule.getValue(), + }); return true; } @@ -287,6 +304,16 @@ export class AppContext { } AppConsole.success(LOC('assign.rendered_block_mesh')); + AppAnalytics.Event('assign', { + dithering: components.dithering.getValue(), + ditheringMagnitude: components.ditheringMagnitude.getValue(), + fallable: components.fallable.getValue() as FallableBehaviour, + resolution: Math.pow(2, components.colourAccuracy.getValue()), + calculateLighting: components.calculateLighting.getValue(), + lightThreshold: components.lightThreshold.getValue(), + contextualAveraging: components.contextualAveraging.getValue(), + errorWeight: components.errorWeight.getValue() / 10, + }); return true; } @@ -327,6 +354,9 @@ export class AppContext { } AppConsole.success(LOC('export.exported_structure')); + AppAnalytics.Event('export', { + exporter: components.export.getValue(), + }); return true; } diff --git a/src/ui/components/header.ts b/src/ui/components/header.ts index 2aa0484..fa6d850 100644 --- a/src/ui/components/header.ts +++ b/src/ui/components/header.ts @@ -67,10 +67,13 @@ export class HeaderComponent extends BaseComponent { ${this._discordButton.generateHTML()} -
-
+
+
${LOC('description')}
+
+ This site may use cookies and similar tracking technologies (like web beacons) to access and store information about usage. +
`; } diff --git a/styles.css b/styles.css index 58c34e8..d74f7a4 100644 --- a/styles.css +++ b/styles.css @@ -734,4 +734,9 @@ a { .hover-text { position: relative; +} + +.privacy-disclaimer { + font-size: var(--font-size-small); + color: var(--text-dim); } \ No newline at end of file diff --git a/template.html b/template.html index 6b7e1f0..019ddd0 100644 --- a/template.html +++ b/template.html @@ -1,20 +1,6 @@ - - - ObjToSchematic Web