From 69a56ad7bdd95822339a39cb4b5d1417ea0d67b5 Mon Sep 17 00:00:00 2001 From: Cyborg Viktor <307551610+vpetersson-bot@users.noreply.github.com> Date: Sat, 15 Aug 2026 13:00:12 +0000 Subject: [PATCH] feat(analytics): fire the page view once the player is known The automatic `page_view` goes out with `gtag('config')`, before `main.js` can profile the player, so it carries no player fields. `trackPlayer` runs afterwards and only its own events are attributed. This is not the client_id churn that motivated the Worker-app fix. These screens keep their identity across hundreds of loads; the loss is per LOAD. Measured 2026-08-14, Quotes sent 9,996 page views and only 3,168 carried player fields, which is why the static apps sat at 22-46% run attribution against 87-92% on the Worker apps. So the tag is configured with `send_page_view: false` and `trackPlayer` sends the page view itself, one line after the user properties, in exactly the position `player_detected` already occupies. The two settings pair: set one without the other and the app either double-counts every page view or stops counting them. The trade is that a load which never reaches `main.js` now reports nothing at all rather than an unattributed page view. That is measured, not assumed: `player_detected` lands on 99.7% to 100% of page views on every static app, so 0.3% or less of loads go silent. A Worker app has the profile before it configures, so it keeps the automatic page view instead and cannot lose one. Kit bumped to 2026.8.10, which adds the option. Co-Authored-By: Claude Opus 5 (1M context) --- assets/static/js/main.ts | 3 ++- bun.lock | 4 ++-- index.html | 5 ++++- package.json | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/assets/static/js/main.ts b/assets/static/js/main.ts index bf48870..f970024 100644 --- a/assets/static/js/main.ts +++ b/assets/static/js/main.ts @@ -124,7 +124,8 @@ const init = (): void => { // Report which player is showing this, and how it is set up. A static app can only // profile from the user agent and referrer; the Worker apps additionally read // X-Requested-With, the only signal that names an Android WebView vendor. - trackPlayer(detectPlayer(), { app: 'timer', config: timerConfig() }) + trackPlayer(detectPlayer(), { app: 'timer', sendPageView: true, + config: timerConfig() }) render() } diff --git a/bun.lock b/bun.lock index e5eecb5..85b1511 100644 --- a/bun.lock +++ b/bun.lock @@ -5,7 +5,7 @@ "": { "name": "screenly-timer-app", "dependencies": { - "@screenly-labs/signage-kit": "github:Screenly-Labs/signage-kit#2026.8.6", + "@screenly-labs/signage-kit": "github:Screenly-Labs/signage-kit#2026.8.10", }, "devDependencies": { "@biomejs/biome": "^2.5.7", @@ -146,7 +146,7 @@ "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="], - "@screenly-labs/signage-kit": ["@screenly-labs/signage-kit@github:Screenly-Labs/signage-kit#7b0d8f1", { "dependencies": { "@fontsource-variable/bricolage-grotesque": "^5.3.0", "@fontsource-variable/fraunces": "^5.3.0", "@fontsource-variable/hanken-grotesk": "^5.3.0", "@fontsource-variable/jetbrains-mono": "^5.3.0", "@fontsource-variable/newsreader": "^5.3.0", "@fontsource/space-mono": "^5.3.0" }, "peerDependencies": { "@csstools/postcss-cascade-layers": "^6", "browserslist": "^4", "esbuild": "^0.28", "lightningcss": "^1.32", "postcss": "^8" }, "optionalPeers": ["@csstools/postcss-cascade-layers", "postcss"] }, "Screenly-Labs-signage-kit-7b0d8f1", "sha512-x61h6zlMCOW0MYcuIQgNeZF3TEI02rmafrASohDN5NT/PD0YSnANfWcKbZx32ItiC3fDihpeQH5c4Iu5GiKUiw=="], + "@screenly-labs/signage-kit": ["@screenly-labs/signage-kit@github:Screenly-Labs/signage-kit#39479f0", { "dependencies": { "@fontsource-variable/bricolage-grotesque": "^5.3.0", "@fontsource-variable/fraunces": "^5.3.0", "@fontsource-variable/hanken-grotesk": "^5.3.0", "@fontsource-variable/jetbrains-mono": "^5.3.0", "@fontsource-variable/newsreader": "^5.3.0", "@fontsource/space-mono": "^5.3.0" }, "peerDependencies": { "@csstools/postcss-cascade-layers": "^6", "browserslist": "^4", "esbuild": "^0.28", "lightningcss": "^1.32", "postcss": "^8" }, "optionalPeers": ["@csstools/postcss-cascade-layers", "postcss"] }, "Screenly-Labs-signage-kit-39479f0", "sha512-oMBzxMhSDVkF0wfzfTEpPr+R3fk2lIEOfrSvVh6BqAe/lY4punKbrTBNfBHHtWTwPFG79oxBeL9J6L+Nz0DYlQ=="], "@tailwindcss/cli": ["@tailwindcss/cli@4.3.3", "", { "dependencies": { "@parcel/watcher": "2.5.1", "@tailwindcss/node": "4.3.3", "@tailwindcss/oxide": "4.3.3", "enhanced-resolve": "^5.24.1", "mri": "^1.2.0", "picocolors": "^1.1.1", "tailwindcss": "4.3.3" }, "bin": { "tailwindcss": "./dist/index.mjs" } }, "sha512-ZvS/n1ZHOBKcVlhkt8l5NNr1EDXk1NboYO5CYDOs6NUmvT9z6bzkwsosaJftY57T/3gWNzWMJzIXLodZC8ssdw=="], diff --git a/index.html b/index.html index 4b87019..42454cf 100644 --- a/index.html +++ b/index.html @@ -10,7 +10,10 @@ dataLayer.push(arguments) } gtag('js', new Date()) - gtag('config', 'G-3V92EN0SBN') + // The automatic page view is suppressed because it fires before main.js can + // profile the player, so it would carry no player fields. trackPlayer sends it + // instead, once the profile is known (sendPageView: true). Set both or neither. + gtag('config', 'G-3V92EN0SBN', { send_page_view: false }) Timer diff --git a/package.json b/package.json index e3951a8..bf0acd5 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "license": "AGPL-3.0-only", "dependencies": { - "@screenly-labs/signage-kit": "github:Screenly-Labs/signage-kit#2026.8.6" + "@screenly-labs/signage-kit": "github:Screenly-Labs/signage-kit#2026.8.10" }, "devDependencies": { "@biomejs/biome": "^2.5.7",